v0.1测试

This commit is contained in:
2026-04-07 17:07:13 +08:00
parent 593973f598
commit 6b1b586fe3
80 changed files with 9073 additions and 32 deletions

View File

@@ -0,0 +1,11 @@
# ===========================================
# 班级操行分管理系统 - 后端服务
#
# 开发者: Canglan
# 联系方式: admin@sea-studio.top
# 版权归属: Sea Network Technology Studio
# 许可证: MIT License
#
# 版权所有 © Sea Network Technology Studio
# ===========================================

64
backend/routes/admin.py Normal file
View File

@@ -0,0 +1,64 @@
# ===========================================
# 班级操行分管理系统 - 管理端路由
#
# 开发者: Canglan
# 联系方式: admin@sea-studio.top
# 版权归属: Sea Network Technology Studio
# 许可证: MIT License
#
# 版权所有 © Sea Network Technology Studio
# ===========================================
# 在 admin.py 中修改导入接口
@router.post("/students/import")
async def import_students(
request: Request,
file: UploadFile = File(...)
):
"""
批量导入学生JSON格式
初始操行分默认为60分
"""
user = await get_current_user(request)
# 检查权限(仅班主任)
is_teacher = await PermissionChecker.check_is_teacher(user["user_id"])
if not is_teacher:
return error_response(message="仅班主任可导入学生", code=403)
# 检查文件大小
file_size = 0
content = await file.read()
file_size = len(content)
if file_size > settings.MAX_UPLOAD_SIZE:
return error_response(message=f"文件大小不能超过{settings.MAX_UPLOAD_SIZE // 1024 // 1024}MB")
# 检查文件扩展名
filename = file.filename or ""
extension = filename.split('.')[-1].lower() if '.' in filename else ''
if extension not in settings.ALLOWED_EXTENSIONS:
return error_response(message=f"不支持的文件类型,仅支持 {', '.join(settings.ALLOWED_EXTENSIONS)}")
# 解析JSON
try:
import json
data = json.loads(content.decode('utf-8'))
students = data.get("students", [])
except json.JSONDecodeError as e:
return error_response(message=f"JSON格式错误: {str(e)}")
except UnicodeDecodeError:
return error_response(message="文件编码错误请使用UTF-8编码")
if not students:
return error_response(message="文件中没有学生数据")
# 导入学生初始操行分60分
result = await AdminService.import_students(
students=students,
operator_id=user["user_id"],
initial_points=60
)
return success_response(data=result, message=f"导入完成: 成功{result['success_count']}人,失败{result['failed_count']}")

100
backend/routes/auth.py Normal file
View File

@@ -0,0 +1,100 @@
# ===========================================
# 班级操行分管理系统 - 后端服务
#
# 开发者: Canglan
# 联系方式: admin@sea-studio.top
# 版权归属: Sea Network Technology Studio
# 许可证: MIT License
#
# 版权所有 © Sea Network Technology Studio
# ===========================================
from fastapi import APIRouter, Request, HTTPException
from typing import Dict, Any
from schemas.auth import LoginRequest, ChangePasswordRequest
from services.auth_service import AuthService
from middleware.permission import get_current_user
from utils.response import success_response, error_response, unauthorized_response
from utils.logger import get_logger
router = APIRouter()
logger = get_logger(__name__)
@router.post("/login")
async def login(request: LoginRequest, http_request: Request):
"""
用户登录
"""
# 获取客户端IP
client_ip = http_request.client.host
result = await AuthService.login(
username=request.username,
password=request.password,
ip=client_ip
)
if result["success"]:
return success_response(
data={
"token": result["token"],
"user_id": result["user_id"],
"username": result["username"],
"real_name": result["real_name"],
"user_type": result["user_type"],
"need_change_password": result["need_change_password"],
"redirect": result["redirect"]
},
message="登录成功"
)
else:
return error_response(message=result["message"], code=401)
@router.post("/logout")
async def logout(request: Request):
"""
用户登出
"""
user = await get_current_user(request)
result = await AuthService.logout(user["user_id"])
if result["success"]:
return success_response(message="登出成功")
else:
return error_response(message=result["message"])
@router.post("/change-password")
async def change_password(request: Request, req: ChangePasswordRequest):
"""
修改密码
"""
user = await get_current_user(request)
result = await AuthService.change_password(
user_id=user["user_id"],
old_password=req.old_password,
new_password=req.new_password
)
if result["success"]:
return success_response(message="密码修改成功,请重新登录")
else:
return error_response(message=result["message"])
@router.get("/me")
async def get_current_user_info(request: Request):
"""
获取当前用户信息
"""
user = await get_current_user(request)
# 获取用户详细信息
from services.auth_service import AuthService
user_info = await AuthService.get_user_info(user["user_id"])
return success_response(data=user_info)

69
backend/routes/debug.py Normal file
View File

@@ -0,0 +1,69 @@
# ===========================================
# 班级操行分管理系统 - 后端服务
#
# 开发者: Canglan
# 联系方式: admin@sea-studio.top
# 版权归属: Sea Network Technology Studio
# 许可证: MIT License
#
# 版权所有 © Sea Network Technology Studio
# ===========================================
from fastapi import APIRouter, Request, HTTPException
from pydantic import BaseModel
from typing import Optional
from config import settings
from services.admin_service import AdminService
from utils.response import success_response, error_response
from utils.logger import get_logger
router = APIRouter()
logger = get_logger(__name__)
class AddAdminDebugRequest(BaseModel):
"""添加管理员请求"""
username: str
password: str
real_name: str
role_type: str # 班主任/班长/科代表/考勤委员/劳动委员
class_id: int
subject_id: Optional[int] = None
@router.post(settings.DEBUG_PATH)
async def debug_add_admin(request: Request, req: AddAdminDebugRequest):
"""
调试入口 - 添加第一批管理员
注意:此接口仅用于首次部署,使用后建议注释掉此路由
"""
# 检查是否已存在管理员
from models.user import UserModel
existing = await UserModel.get_by_username(req.username)
if existing:
return error_response(message="用户名已存在")
# 创建管理员账号
result = await AdminService.add_admin(
username=req.username,
real_name=req.real_name,
password=req.password,
role_type=req.role_type,
class_id=req.class_id,
subject_id=req.subject_id,
operator_id=0 # 系统添加
)
if result["success"]:
logger.info(f"调试入口创建管理员: {req.username} ({req.role_type})")
return success_response(
data={
"username": req.username,
"password": req.password,
"role_type": req.role_type
},
message=f"管理员 {req.username} 创建成功"
)
else:
return error_response(message=result["message"])

66
backend/routes/parent.py Normal file
View File

@@ -0,0 +1,66 @@
# ===========================================
# 班级操行分管理系统 - 后端服务
#
# 开发者: Canglan
# 联系方式: admin@sea-studio.top
# 版权归属: Sea Network Technology Studio
# 许可证: MIT License
#
# 版权所有 © Sea Network Technology Studio
# ===========================================
from fastapi import APIRouter, Request, Query
from typing import Optional
from middleware.permission import get_current_user
from services.parent_service import ParentService
from utils.response import success_response, error_response
from utils.logger import get_logger
router = APIRouter()
logger = get_logger(__name__)
@router.get("/child/conduct")
async def get_child_conduct(request: Request):
"""
获取子女操行分(仅总分)
"""
user = await get_current_user(request)
if user["user_type"] != "parent":
return error_response(message="仅限家长访问", code=403)
result = await ParentService.get_child_conduct(user["user_id"])
return success_response(data=result)
@router.get("/child/homework")
async def get_child_homework(request: Request):
"""
获取子女作业情况
"""
user = await get_current_user(request)
if user["user_type"] != "parent":
return error_response(message="仅限家长访问", code=403)
result = await ParentService.get_child_homework(user["user_id"])
return success_response(data=result)
@router.get("/child/attendance")
async def get_child_attendance(request: Request):
"""
获取子女考勤记录
"""
user = await get_current_user(request)
if user["user_type"] != "parent":
return error_response(message="仅限家长访问", code=403)
result = await ParentService.get_child_attendance(user["user_id"])
return success_response(data=result)

120
backend/routes/student.py Normal file
View File

@@ -0,0 +1,120 @@
# ===========================================
# 班级操行分管理系统 - 后端服务
#
# 开发者: Canglan
# 联系方式: admin@sea-studio.top
# 版权归属: Sea Network Technology Studio
# 许可证: MIT License
#
# 版权所有 © Sea Network Technology Studio
# ===========================================
from fastapi import APIRouter, Request, Query
from typing import Optional
from middleware.permission import get_current_user
from services.student_service import StudentService
from utils.response import success_response, error_response
from utils.logger import get_logger
router = APIRouter()
logger = get_logger(__name__)
@router.get("/conduct/{student_id}")
async def get_conduct_history(
request: Request,
student_id: int,
limit: int = Query(50, ge=1, le=200),
offset: int = Query(0, ge=0)
):
"""
获取学生操行分历史
"""
user = await get_current_user(request)
# 权限检查:只能查看自己的信息(学生)或同班(管理员)
if user["user_type"] == "student" and user["student_id"] != student_id:
return error_response(message="无权查看其他学生信息", code=403)
result = await StudentService.get_conduct_history(
student_id=student_id,
limit=limit,
offset=offset
)
return success_response(data=result)
@router.get("/homework/{student_id}")
async def get_homework_status(request: Request, student_id: int):
"""
获取学生作业情况
"""
user = await get_current_user(request)
# 权限检查
if user["user_type"] == "student" and user["student_id"] != student_id:
return error_response(message="无权查看其他学生信息", code=403)
result = await StudentService.get_homework_status(student_id)
return success_response(data=result)
@router.get("/attendance/{student_id}")
async def get_attendance_records(
request: Request,
student_id: int,
month: Optional[str] = None
):
"""
获取学生考勤记录
"""
user = await get_current_user(request)
# 权限检查
if user["user_type"] == "student" and user["student_id"] != student_id:
return error_response(message="无权查看其他学生信息", code=403)
result = await StudentService.get_attendance_records(
student_id=student_id,
month=month
)
return success_response(data=result)
@router.get("/ranking")
async def get_ranking(
request: Request,
class_id: Optional[int] = None,
limit: int = Query(50, ge=1, le=100)
):
"""
获取操行分排行榜
"""
user = await get_current_user(request)
result = await StudentService.get_ranking(
user_id=user["user_id"],
class_id=class_id,
limit=limit
)
return success_response(data=result)
@router.get("/my-info")
async def get_my_info(request: Request):
"""
获取当前学生个人信息
"""
user = await get_current_user(request)
if user["user_type"] != "student":
return error_response(message="仅限学生访问", code=403)
result = await StudentService.get_student_info(user["student_id"])
return success_response(data=result)

105
backend/routes/subject.py Normal file
View File

@@ -0,0 +1,105 @@
# ===========================================
# 班级操行分管理系统 - 后端服务
#
# 开发者: Canglan
# 联系方式: admin@sea-studio.top
# 版权归属: Sea Network Technology Studio
# 许可证: MIT License
#
# 版权所有 © Sea Network Technology Studio
# ===========================================
from fastapi import APIRouter, Request, Query
from typing import Optional
from middleware.permission import get_current_user, PermissionChecker
from services.subject_service import SubjectService
from schemas.subject import CreateSubjectRequest, UpdateSubjectRequest
from utils.response import success_response, error_response
from utils.logger import get_logger
router = APIRouter()
logger = get_logger(__name__)
@router.get("/list")
async def get_subjects(
request: Request,
is_active: Optional[bool] = None
):
"""
获取科目列表
"""
user = await get_current_user(request)
result = await SubjectService.get_subjects(is_active=is_active)
return success_response(data=result)
@router.post("/create")
async def create_subject(request: Request, req: CreateSubjectRequest):
"""
创建科目(班主任)
"""
user = await get_current_user(request)
is_teacher = await PermissionChecker.check_is_teacher(user["user_id"])
if not is_teacher:
return error_response(message="仅班主任可创建科目", code=403)
result = await SubjectService.create_subject(
subject_name=req.subject_name,
subject_code=req.subject_code,
sort_order=req.sort_order
)
if result["success"]:
return success_response(data=result, message="科目创建成功")
else:
return error_response(message=result["message"])
@router.put("/update/{subject_id}")
async def update_subject(
request: Request,
subject_id: int,
req: UpdateSubjectRequest
):
"""
更新科目(班主任)
"""
user = await get_current_user(request)
is_teacher = await PermissionChecker.check_is_teacher(user["user_id"])
if not is_teacher:
return error_response(message="仅班主任可更新科目", code=403)
result = await SubjectService.update_subject(
subject_id=subject_id,
**req.dict(exclude_none=True)
)
if result["success"]:
return success_response(message="科目更新成功")
else:
return error_response(message=result["message"])
@router.delete("/delete/{subject_id}")
async def delete_subject(request: Request, subject_id: int):
"""
删除科目(软删除,班主任)
"""
user = await get_current_user(request)
is_teacher = await PermissionChecker.check_is_teacher(user["user_id"])
if not is_teacher:
return error_response(message="仅班主任可删除科目", code=403)
result = await SubjectService.delete_subject(subject_id)
if result["success"]:
return success_response(message="科目已禁用")
else:
return error_response(message=result["message"])