# dao/scores_dao.py # 成绩表的数据访问层(录入、修改、删除、查询 + 60分红线预警) from typing import List, Optional, Tuple from sqlalchemy.orm import Session from model.scores import Score from model.students import Student from scheme.scores import ScoreAdd class ScoreDAO: WARNING_LINE = 60 # 成绩红线 @staticmethod def get_one(db: Session, stu_id: int, exam_id: int, include_deleted: bool = False) -> Optional[Score]: """获取指定学生某次考核的成绩(复合主键查询)""" query = db.query(Score).filter(Score.stu_id == stu_id, Score.exam_id == exam_id) if not include_deleted: query = query.filter(Score.is_deleted == 0) return query.first() @staticmethod def add_score(db: Session, score_data: ScoreAdd) -> Score: """录入成绩(调用方需先校验学生存在与复合主键冲突)""" db_score = Score(**score_data.model_dump(), is_deleted=0) db.add(db_score) db.commit() db.refresh(db_score) return db_score @staticmethod def update_score(db: Session, stu_id: int, exam_id: int, new_score: float) -> Optional[Score]: """修改指定学生的某次成绩""" db_score = ScoreDAO.get_one(db, stu_id, exam_id) if db_score is None: return None db_score.score = new_score db_score.is_deleted = 0 # 若曾被软删除,修改视为恢复 db.commit() db.refresh(db_score) return db_score @staticmethod def delete_score(db: Session, stu_id: int, exam_id: int) -> bool: """ 逻辑删除指定学生的某次成绩 :return: True 成功 / False 不存在或已删除 """ db_score = ScoreDAO.get_one(db, stu_id, exam_id) if db_score is None: return False db_score.is_deleted = 1 db.commit() return True @staticmethod def get_by_student(db: Session, stu_id: int) -> List[Score]: """获取学生的全部成绩(按考核序次排序)""" return ( db.query(Score) .filter(Score.stu_id == stu_id, Score.is_deleted == 0) .order_by(Score.exam_id) .all() ) @staticmethod def get_by_exam(db: Session, exam_id: int, skip: int = 0, limit: int = 100) -> Tuple[int, List[Score]]: """按考核序次查询成绩列表(分页)""" query = db.query(Score).filter(Score.exam_id == exam_id, Score.is_deleted == 0) total = query.count() items = query.order_by(Score.stu_id).offset(skip).limit(limit).all() return total, items @staticmethod def attach_student_name(db: Session, score: Score) -> None: """把学生姓名附加到 Score 对象上(供响应模型冗余展示)""" student = db.query(Student).filter(Student.stu_id == score.stu_id).first() score.stu_name = student.stu_name if student else None @staticmethod def build_score_item(score: Score, stu_name: Optional[str] = None) -> dict: """组装成绩响应字典:附带红线预警标记""" return { "stu_id": score.stu_id, "exam_id": score.exam_id, "score": score.score, "stu_name": stu_name, "is_warning": score.score is not None and score.score < ScoreDAO.WARNING_LINE, }