from fastapi import APIRouter, Depends from sqlalchemy.orm import Session from sqlalchemy import func from typing import List, Optional from database import get_db from models import Student, Class, Score, Employment router = APIRouter(prefix="/api/stats", tags=["stats"]) @router.get("/classes") def class_stats(db: Session = Depends(get_db)): """按班级统计学生人数和性别分布""" results = ( db.query( Student.class_id, Class.name.label("class_name"), func.count(Student.id).label("total"), func.sum(func.if_(Student.sex == 1, 1, 0)).label("male"), func.sum(func.if_(Student.sex == 2, 1, 0)).label("female"), ) .join(Class, Student.class_id == Class.id) .filter(Student.is_deleted == False, Class.is_deleted == False) .group_by(Student.class_id, Class.name) .all() ) return [ { "class_id": r.class_id, "class_name": r.class_name, "total_students": r.total, "male_count": r.male, "female_count": r.female, } for r in results ] @router.get("/scores") def score_stats(class_id: Optional[int] = None, score_num: Optional[str] = None, db: Session = Depends(get_db)): """成绩统计:平均分、最高分、最低分、人数""" query = ( db.query( Score.num.label("score_num"), Student.class_id, func.avg(Score.score).label("avg_score"), func.max(Score.score).label("max_score"), func.min(Score.score).label("min_score"), func.count(Score.id).label("count"), ) .join(Student, Score.sid == Student.id) .filter(Student.is_deleted == False, Score.is_deleted == False) .group_by(Score.num, Student.class_id) ) if class_id: query = query.filter(Student.class_id == class_id) if score_num: query = query.filter(Score.num == score_num) results = query.all() return [ { "score_num": r.score_num, "class_id": r.class_id, "avg_score": float(r.avg_score), "max_score": float(r.max_score), "min_score": float(r.min_score), "count": r.count, } for r in results ] @router.get("/employment") def employment_stats(class_id: Optional[int] = None, db: Session = Depends(get_db)): """就业统计:已就业人数、总人数、平均薪资、平均签约周期""" from sqlalchemy import cast, Integer query = ( db.query( Employment.class_id, Class.name.label("class_name"), func.count(Employment.id).label("employed_count"), func.count(Student.id).label("total_count"), func.avg(Employment.employment_salary).label("avg_salary"), ) .join(Class, Employment.class_id == Class.id) .join(Student, Employment.sid == Student.id) .filter(Employment.is_deleted == False, Student.is_deleted == False, Class.is_deleted == False) .group_by(Employment.class_id, Class.name) ) if class_id: query = query.filter(Employment.class_id == class_id) results = query.all() output = [] for r in results: # 计算平均签约周期(天数) durations = ( db.query( func.ceil( func.cast(func.julianday(Employment.offer_recived_time) - func.julianday(Employment.employment_open_time), Integer) ) ) .filter( Employment.class_id == r.class_id, Employment.is_deleted == False, Employment.employment_open_time.isnot(None), Employment.offer_recived_time.isnot(None), ) .all() ) avg_days = sum(d[0] for d in durations if d[0] is not None) / len(durations) if durations else None output.append({ "class_id": r.class_id, "class_name": r.class_name, "employed_count": r.employed_count, "total_count": r.total_count, "avg_salary": float(r.avg_salary) if r.avg_salary else None, "avg_duration_days": round(avg_days, 1) if avg_days is not None else None, }) return output