Files
stu_teacher/api/scores.py
T

124 lines
5.5 KiB
Python

# api/scores.py
# 成绩模块接口:录入(含 60 分红线预警)、修改、删除、查询
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from core.deps import get_current_user, require_roles, get_teacher_class_ids
from dao.scores_dao import ScoreDAO
from dao.students_dao import StudentDAO
from database import get_db
from model.user import User
from scheme.scores import (
ScoreAdd,
ScoreUpdate,
ScoreDelete,
ScoreResponse,
ScoreListResponse,
ScoreAddResponse,
)
router = APIRouter()
def _can_manage_student_scores(db: Session, user: User, student) -> bool:
"""教师只能管理自己所带班级学生的成绩;管理员不受限"""
if user.role == "admin":
return True
if user.role == "teacher":
return student.class_id in get_teacher_class_ids(db, user.teacher_id)
return False
# ---------- 录入成绩 ----------
@router.post("/add", response_model=ScoreAddResponse, summary="录入成绩(低于60分触发预警)")
async def add_score(
score_data: ScoreAdd,
db: Session = Depends(get_db),
current_user: User = Depends(require_roles("admin", "teacher")),
):
# 外键校验:学生必须存在
student = StudentDAO.get_active(db, score_data.stu_id)
if student is None:
raise HTTPException(status_code=404, detail="学生不存在,或已被软删除")
if not _can_manage_student_scores(db, current_user, student):
raise HTTPException(status_code=403, detail="教师只能管理自己所带班级学生的成绩")
# 复合主键冲突校验
existing = ScoreDAO.get_one(db, score_data.stu_id, score_data.exam_id, include_deleted=True)
if existing:
raise HTTPException(status_code=409, detail="该学生此考核序次的成绩已存在,请使用修改接口")
db_score = ScoreDAO.add_score(db, score_data)
item = ScoreDAO.build_score_item(db_score, stu_name=student.stu_name)
warning = None
if item["is_warning"]:
warning = f"预警:学生 {student.stu_name}(学号 {student.stu_id})第 {score_data.exam_id} 次考核成绩 {score_data.score} 分低于 {ScoreDAO.WARNING_LINE} 分红线,需要多加关注!"
return ScoreAddResponse(score=ScoreResponse(**item), warning=warning)
# ---------- 查询学生成绩 ----------
@router.get("/query", response_model=ScoreListResponse, summary="查询成绩(按学号或考核序次)")
async def get_scores(
stu_id: int = Query(None, ge=1, description="按学号查询(学生角色只能查自己)"),
exam_id: int = Query(None, ge=1, description="按考核序次查询"),
skip: int = Query(0, ge=0),
limit: int = Query(100, ge=1, le=200),
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
# 学生角色只能查自己的成绩
if current_user.role == "student":
stu_id = current_user.stu_id
if stu_id is None and exam_id is None:
raise HTTPException(status_code=400, detail="stu_id 与 exam_id 至少提供一个")
if stu_id is not None:
rows = ScoreDAO.get_by_student(db, stu_id)
student = StudentDAO.get_active(db, stu_id)
stu_name = student.stu_name if student else None
items = [ScoreResponse(**ScoreDAO.build_score_item(s, stu_name)) for s in rows]
return ScoreListResponse(total=len(items), items=items)
total, rows = ScoreDAO.get_by_exam(db, exam_id, skip=skip, limit=limit)
items = []
for s in rows:
student = StudentDAO.get_active(db, s.stu_id)
items.append(
ScoreResponse(**ScoreDAO.build_score_item(s, student.stu_name if student else None))
)
return ScoreListResponse(total=total, items=items)
# ---------- 修改成绩 ----------
@router.put("/update", response_model=ScoreResponse, summary="修改指定学生的某次成绩")
async def update_score(
score_data: ScoreUpdate,
stu_id: int = Query(..., ge=1, description="学生编号"),
exam_id: int = Query(..., ge=1, description="考核序次"),
db: Session = Depends(get_db),
current_user: User = Depends(require_roles("admin", "teacher")),
):
student = StudentDAO.get_active(db, stu_id)
if student is None:
raise HTTPException(status_code=404, detail="学生不存在,或已被软删除")
if not _can_manage_student_scores(db, current_user, student):
raise HTTPException(status_code=403, detail="教师只能管理自己所带班级学生的成绩")
db_score = ScoreDAO.update_score(db, stu_id, exam_id, score_data.score)
if db_score is None:
raise HTTPException(status_code=404, detail="该学生此考核序次的成绩不存在")
return ScoreResponse(**ScoreDAO.build_score_item(db_score, student.stu_name))
# ---------- 删除成绩 ----------
@router.post("/delete", status_code=204, summary="删除指定学生的某次成绩(逻辑删除)")
async def delete_score(
body: ScoreDelete,
db: Session = Depends(get_db),
current_user: User = Depends(require_roles("admin", "teacher")),
):
student = StudentDAO.get_active(db, body.stu_id)
if student is None:
raise HTTPException(status_code=404, detail="学生不存在,或已被软删除")
if not _can_manage_student_scores(db, current_user, student):
raise HTTPException(status_code=403, detail="教师只能管理自己所带班级学生的成绩")
success = ScoreDAO.delete_score(db, body.stu_id, body.exam_id)
if not success:
raise HTTPException(status_code=404, detail="该成绩不存在,或已被删除")
return None