diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..06d7405 Binary files /dev/null and b/__init__.py differ diff --git a/statistics.py b/statistics.py new file mode 100644 index 0000000..74618b2 --- /dev/null +++ b/statistics.py @@ -0,0 +1,110 @@ +from typing import List + +from fastapi import APIRouter, Depends, HTTPException, Path, Query +from sqlalchemy.orm import Session + +from dao.wl_statistics_dao import StatisticDao +from database import get_db +from dao import wl_statistics_dao +from scheme.wl_statistics_scheme import (AgeCompareOp, ClassAvgEmpTimeOut, + ClassAvgScoreOut, ClassGenderStatOut, + EmpTopOut, FailCountModel, + ScoreOutLine, SortOrder, EmpTimeOut) +from scheme.wl_student_scheme import StudentOut + +router = APIRouter(prefix='/statistics',tags=["统计分析模块"]) + + +# ---- 2.6.1.1 动态年龄范围查询 ---- +@router.get( + "/students/age", + response_model=list[StudentOut], + summary="动态年龄范围查询", + responses={400: {"description": "查询参数不合法,例如区间查询缺少上界 value2"}}, +) +def query_age_range( + op: AgeCompareOp = Query(AgeCompareOp.gt, description="比较条件:gt/ge/lt/le/eq/between"), + value: int = Query(..., description="年龄阈值"), + value2: int | None = Query(None, description="年龄上界,仅 op=between 时必填"), + db: Session = Depends(get_db), +): + try: + return StatisticDao.search_students_by_age(db, op, value, value2) + except ValueError as e: + raise HTTPException(status_code=400, detail=str(e)) + +# ---- 2.6.1.2 多维度班级统计 ---- +@router.get( + '/classes/count', + summary="多维度班级统计", + response_model=list[ClassGenderStatOut] +) +def query_classes_count(db: Session = Depends(get_db)): + return StatisticDao.statistic_classes_count(db) + +# 查询每次考试成绩都在输入分数线(如80分)以上的学生的编号、姓名和成绩 +@router.get( + '/scores/out_score_line/{score_line}', + summary='统计每次超分数线的学生', + response_model=list[ScoreOutLine] +) +def query_out_score_line(score_line:float,db:Session=Depends(get_db)): + return StatisticDao.query_score_by_line(db, score_line) + +# 查询有输入指定次数(如两次)以上不及格的学生的姓名、班级和不及格成绩明细 +@router.get( + '/scores/fail_score/{fail_count}', + summary='统计不及格次数超过指定次数的学生信息', + response_model=list[FailCountModel] +) +def query_info_by_fail_count(fail_count:int,db: Session = Depends(get_db)): + return StatisticDao.query_by_fail_count(db,fail_count) + +# 统计每次考试每个班级的平均分,并支持按分数从高到低或从低到高动态排序 +@router.get( + '/scores/get_class_avg_score', + summary='统计每次考试每个班级的平均分,并支持按分数从高到低或从低到高动态排序', + response_model=list[ClassAvgScoreOut] +) +def get_class_avg_score( + sort_order: SortOrder | None = Query( + None, description="排序方向:asc 从低到高,desc 从高到低;不传则按班级+考试序次"), + db: Session = Depends(get_db), +): + return StatisticDao.get_class_avg_score(db, sort_order) + +# 统计就业薪资排名Top N(动态输入N)的学生的姓名、班级、就业时间和就业公司 +@router.get( + '/emp/top_rank/{rank}', + summary='统计就业薪资排名Top N', + response_model=list[EmpTopOut] +) +def get_top_rank( + rank: int = Path(..., gt=0, description="取薪资排名前 N 名"), + db: Session = Depends(get_db), +): + return StatisticDao.get_info_by_rank(db, rank) + +# 统计每个学生的就业时长(计算公式:offer下发时间-就业开放时间) +@router.get( + '/emp/emp_time', + summary='统计每个学生的就业时长', + response_model=list[EmpTimeOut] +) +def get_emp_time(db: Session = Depends(get_db)): + return StatisticDao.get_emp_time(db) + +# 统计每个班级的平均就业时长(仅统计已进入就业阶段且已拿到 offer 的学生) +@router.get( + '/emp/class_avg_emp_time', + summary='统计每个班级的平均就业时长', + response_model=list[ClassAvgEmpTimeOut] +) +def get_class_avg_emp_time( + sort_order: SortOrder | None = Query( + None, description="排序方向:asc 从低到高,desc 从高到低;不传则按班级编号"), + db: Session = Depends(get_db), +): + return StatisticDao.get_class_avg_emp_time(db, sort_order) + + diff --git a/wl_advisor_api.py b/wl_advisor_api.py new file mode 100644 index 0000000..ec84023 --- /dev/null +++ b/wl_advisor_api.py @@ -0,0 +1,57 @@ +from fastapi import APIRouter, Depends, HTTPException, Query +from sqlalchemy.orm import Session + +from dao import wl_advisor_dao +from database import get_db +from scheme.wl_advisor_scheme import AdvisorCreate, AdvisorOut, AdvisorUpdate + +router = APIRouter(prefix="/advisor", tags=["顾问管理"]) + + +# 查询顾问列表(支持姓名/手机号/区域/性别模糊筛选 + 分页) +# 注意:本路由必须写在 /{adv_id} 前面,否则 "/advisor/" 会被当成 adv_id 去解析 +@router.get("/", response_model=list[AdvisorOut]) +def query_list( + adv_name: str = Query(None), + adv_phone: str = Query(None), + adv_region: str = Query(None), + adv_gender: str = Query(None), + skip: int = 0, + limit: int = 100, + db: Session = Depends(get_db), +): + return wl_advisor_dao.list_advisors( + db, adv_name, adv_phone, adv_region, adv_gender, skip, limit + ) + + +# 新增顾问 +@router.post("/", response_model=AdvisorOut, status_code=201) +def add_advisor(body: AdvisorCreate, db: Session = Depends(get_db)): + return wl_advisor_dao.create_advisor(db, body) + + +# 根据编号查询单个顾问 +@router.get("/{adv_id}", response_model=AdvisorOut) +def query_one(adv_id: int, db: Session = Depends(get_db)): + advisor = wl_advisor_dao.get_advisor_by_id(db, adv_id) + if not advisor: + raise HTTPException(status_code=404, detail="顾问不存在") + return advisor + + +# 修改顾问 +@router.put("/{adv_id}", response_model=AdvisorOut) +def edit_advisor(adv_id: int, body: AdvisorUpdate, db: Session = Depends(get_db)): + advisor = wl_advisor_dao.update_advisor(db, adv_id, body) + if not advisor: + raise HTTPException(status_code=404, detail="顾问不存在") + return advisor + + +# 逻辑删除顾问 +@router.delete("/{adv_id}") +def remove_advisor(adv_id: int, db: Session = Depends(get_db)): + if not wl_advisor_dao.delete_advisor(db, adv_id): + raise HTTPException(status_code=404, detail="顾问不存在") + return {"code": 200, "msg": "删除成功"} diff --git a/wl_class_api.py b/wl_class_api.py new file mode 100644 index 0000000..8aa6b7e --- /dev/null +++ b/wl_class_api.py @@ -0,0 +1,49 @@ +from fastapi import APIRouter, Depends +from sqlalchemy.orm import Session +from typing import List +from database import get_db +from scheme.wl_class_scheme import ClassCreate,ClassUpdate,ClassOut +from dao import wl_class_dao +from fastapi import HTTPException + +# 创建班级模块的路由,统一前缀 /classes +router = APIRouter(prefix="/classes", tags=["班级管理"]) + +# 1. 新增班级 +@router.post("/", response_model=ClassOut) +def create_class(class_data: ClassCreate, db: Session = Depends(get_db)): + return wl_class_dao.create_class(db, class_data.model_dump()) + +#为了合并先注释的部分 +# @router.post("/", response_model=ClassOut) +# def create_class(class_data: ClassCreate, db: Session = Depends(get_db)): + # 把 pydantic 对象转成字典,再传给 dao + # if not db_class: + # raise HTTPException(status_code=404, detail="班级不存在") + +# 2. 获取班级列表 +@router.get("/", response_model=List[ClassOut]) +def read_classes(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): + return wl_class_dao.get_classes(db, skip, limit) + +# 3. 获取单个班级详情 +@router.get("/{class_id}", response_model=ClassOut) +def read_class(class_id: int, db: Session = Depends(get_db)): + db_class = wl_class_dao.get_class_by_id(db, class_id) + if not db_class: + raise HTTPException(status_code=404, detail="班级不存在") + return db_class + +# 4. 修改班级 +@router.put("/{class_id}", response_model=ClassOut) +def update_class(class_id: int, class_data: ClassUpdate, db: Session = Depends(get_db)): + db_class = wl_class_dao.update_class(db, class_id, class_data.model_dump(exclude_unset=True)) + if not db_class: + raise HTTPException(status_code=404, detail="班级不存在") + return db_class + +# 5. 删除班级(逻辑删除) +@router.delete("/{class_id}") +def delete_class(class_id: int, db: Session = Depends(get_db)): + # 直接把 dao 的结果返回给前端,前端根据 code 判断是否成功 + return wl_class_dao.delete_class(db, class_id) \ No newline at end of file diff --git a/wl_emp_api.py b/wl_emp_api.py new file mode 100644 index 0000000..65e6708 --- /dev/null +++ b/wl_emp_api.py @@ -0,0 +1,56 @@ +from typing import Optional + +from fastapi import APIRouter, Depends, HTTPException +from sqlalchemy.orm import Session + +from dao.wl_emp_dao import EmpDAO +from database import get_db +from scheme.wl_emp_scheme import EmpCreate, EmpOut, EmpUpdate + +router = APIRouter(prefix="/emp", tags=["就业管理"]) + + +# 查询就业列表 +@router.get("/", response_model=list[EmpOut]) +def query_list( + skip: int = 0, + limit: int = 100, + company_name: Optional[str] = None, + db: Session = Depends(get_db), +): + return EmpDAO.list_emps(db, skip, limit, company_name) + + +# 根据学号查询就业信息 +@router.get("/{stu_no}", response_model=EmpOut) +def query_one(stu_no: str, db: Session = Depends(get_db)): + emp = EmpDAO.get_emp(db, stu_no) + if not emp: + raise HTTPException(status_code=404, detail="就业信息不存在") + return emp + + +# 新增就业信息 +@router.post("/", response_model=EmpOut, status_code=201) +def add_emp(body: EmpCreate, db: Session = Depends(get_db)): + try: + return EmpDAO.create_emp(db, body) + except ValueError as e: + raise HTTPException(status_code=400, detail=str(e)) + + +# 修改就业信息 +@router.put("/{stu_no}", response_model=EmpOut) +def edit_emp(stu_no: str, body: EmpUpdate, db: Session = Depends(get_db)): + emp = EmpDAO.update_emp(db, stu_no, body) + if not emp: + raise HTTPException(status_code=404, detail="就业信息不存在") + return emp + + +# 逻辑删除就业信息 +@router.delete("/{stu_no}") +def remove_emp(stu_no: str, db: Session = Depends(get_db)): + if not EmpDAO.delete_emp(db, stu_no): + raise HTTPException(status_code=404, detail="就业信息不存在") + return {"code": 200, "msg": "删除成功"}