From d70705e7fe9d71f95486a5b70169d046e39d33ab Mon Sep 17 00:00:00 2001 From: lookerzz <1031516901@qq.com> Date: Wed, 23 Sep 2026 20:56:36 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E6=B7=BB=E5=8A=A0statistics=20tag?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/score.py | 6 +++--- dao/statistics.py | 4 ++-- main.py | 10 +++++++++- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/api/score.py b/api/score.py index a87e4c1..f610b3a 100644 --- a/api/score.py +++ b/api/score.py @@ -32,7 +32,7 @@ def list_score( return {"total": total, "data": data_list} -@scores_router.put("{score_id}", summary="更新成绩接口") +@scores_router.put("/{score_id}", summary="更新成绩接口") def modify_score(score_id: int, req: ScoreUpdate, db: Session = Depends(get_db)): update_dict = req.model_dump(exclude_unset=True) if not update_dict: @@ -43,9 +43,9 @@ def modify_score(score_id: int, req: ScoreUpdate, db: Session = Depends(get_db)) return {"msg": "修改成功"} -@scores_router.delete("{score_id}", summary="删除成绩接口") +@scores_router.delete("/{score_id}", summary="删除成绩接口") def remove_score(score_id: int, db: Session = Depends(get_db)): ok = delete_score_dao(score_id, db) if not ok: raise HTTPException(status_code=400, detail="删除失败,记录不存在或已删除") - return {"msg": "逻辑删除成功"} \ No newline at end of file + return {"msg": "逻辑删除成功"} diff --git a/dao/statistics.py b/dao/statistics.py index eb7db16..d2566c7 100644 --- a/dao/statistics.py +++ b/dao/statistics.py @@ -3,7 +3,6 @@ from sqlalchemy import func #`GET /statistics/students/age-over-30` #查询所有超过 30 岁的学员信息 - def age_over_thirty(Student,db): student_list=db.query(Student).filter(Student.age>30,Student.is_deleted==0).all() if not student_list : @@ -30,7 +29,7 @@ def age_over_thirty(Student,db): "is_deleted": student.is_deleted, }) return result - +#统计男、女、总人数 def gender_count(Student,db): result=db.query(Student.class_id,Student.gender,func.count(Student.id)).filter(Student.is_deleted==0).group_by(Student.class_id,Student.gender).all() if not result : @@ -43,6 +42,7 @@ def gender_count(Student,db): dict1[i]['总人数']+=k return dict1 #查询每次考试成绩都在 80 分以上的学生编号、姓名和成绩 +#首先,拿到最低值>80的人的列表,row。然后,遍历元素拿到符合条件的id的属性。 def score_over_eighty(Score,Student,db): student_list=db.query(Student.id, Student.student_name)\ .join(Score,Score.student_id==Student.id).filter(Student.is_deleted==0,Score.is_deleted==0).group_by(Student.id,Student.student_name)\ diff --git a/main.py b/main.py index abf63e5..d9ec4ed 100644 --- a/main.py +++ b/main.py @@ -1,4 +1,7 @@ from fastapi import FastAPI +from fastapi.responses import RedirectResponse +from fastapi.staticfiles import StaticFiles +from pathlib import Path from api.example import example_router from middleware import log_middleware from api.statistics import statistics_router @@ -8,6 +11,12 @@ from api.teacher import router as teacher_router,class_teacher_router from api.magic import magic_router app = FastAPI(title='学生管理系统') +app.mount("/ui", StaticFiles(directory=Path(__file__).resolve().parent / "frontend", html=True), name="frontend") + +@app.get("/", include_in_schema=False) +def home(): + return RedirectResponse("/ui/") + app.middleware("http")(log_middleware) app.include_router(magic_router) @@ -25,7 +34,6 @@ from api import consultant app.include_router(consultant.router) #-------------张昕浩--------------- app.include_router(example_router) -app.include_router(magic_router) app.include_router(statistics_router, prefix="/statistics") #-------------曾凯--------------- From a90c240b5a49ec2bb91b2171612b94bd3e4766a2 Mon Sep 17 00:00:00 2001 From: lookerzz <1031516901@qq.com> Date: Thu, 24 Sep 2026 10:26:22 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E6=B7=BB=E5=8A=A0statistics=20tag?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/statistics.py | 29 +++++++++++++++-------------- dao/statistics.py | 34 ++++++++++++++++++++-------------- schemas/statistics.py | 11 ++++++++++- 3 files changed, 45 insertions(+), 29 deletions(-) diff --git a/api/statistics.py b/api/statistics.py index 4b7ee40..1c2af3b 100644 --- a/api/statistics.py +++ b/api/statistics.py @@ -3,6 +3,7 @@ from core.database import get_db from models.employment import Employment from models.student import Student from models.score import Score +from schemas.statistics import * from dao.statistics import * from schemas.common import SuccessResponse from sqlalchemy.orm import Session @@ -11,34 +12,34 @@ from sqlalchemy.orm import Session statistics_router=APIRouter(tags=['统计接口']) -@statistics_router.get("/students/age-over-30",response_model=SuccessResponse) -def api_age_over_thirty(db:Session=Depends(get_db)): +@statistics_router.post("/students/age-over-30",response_model=SuccessResponse) +def api_age_over_thirty(input_age:GenderCount,db:Session=Depends(get_db)): try: - age_over_thirty_list=age_over_thirty(Student,db) + age_over_thirty_list=age_over_thirty(Student,input_age.input_class,db) return SuccessResponse(status_code=200,msg=age_over_thirty_list) except NoExist as e: raise HTTPException(status_code=404,detail=str(e)) # 这边功能函数返回的是一个字典 @statistics_router.get("/classes/gender-count",response_model=SuccessResponse) -def api_gender_count(db:Session=Depends(get_db)): +def api_gender_count(input_class:int|None= None,db:Session=Depends(get_db)): try: - result = gender_count(Student,db) + result = gender_count(Student,db,input_class) return SuccessResponse(status_code=200,msg=result) except NoExist as e: raise HTTPException(status_code=404,detail=str(e)) -@statistics_router.get("/scores/all-above-80",response_model=SuccessResponse) -def all_above_eighty(db:Session=Depends(get_db)): +@statistics_router.post("/scores/all-above-80",response_model=SuccessResponse) +def all_above_eighty(page:int,score:StudentScore,db:Session=Depends(get_db)): try: - result = score_over_eighty(Score,Student,db) + result = score_over_eighty(score.score,page,Score,Student,db) return SuccessResponse(status_code=200,msg=result) except NoExist as e: raise HTTPException(status_code=404,detail=str(e)) @statistics_router.get("/scores/fail-more-than-two",response_model=SuccessResponse) -def fail_two(db:Session=Depends(get_db)): +def fail_two(num:int,offset_set:int,db:Session=Depends(get_db)): try: - result = twice_error(Score,Student,db) + result = twice_error(num,offset_set,Score,Student,db) return SuccessResponse(status_code=200,msg=result) except NoExist as e: raise HTTPException(status_code=404,detail=str(e)) @@ -53,17 +54,17 @@ def class_average(db:Session=Depends(get_db)): @statistics_router.get("/employments/top5-salary",response_model=SuccessResponse) -def top_salary(db:Session=Depends(get_db)): +def top_salary(limit_set:int,db:Session=Depends(get_db)): try: - result=max_salary(Student,Employment,db) + result=max_salary(limit_set,Student,Employment,db) return SuccessResponse(status_code=200,msg=result) except NoExist as e: raise HTTPException(status_code=404,detail=str(e)) @statistics_router.get("/employments/student-duration",response_model=SuccessResponse) -def employ_time(db:Session=Depends(get_db)): +def employ_time(limit_set,db:Session=Depends(get_db)): try: - result = student_market(Employment,db) + result = student_market(limit_set,Employment,db) return SuccessResponse(status_code=200,msg=result) except NoExist as e: raise HTTPException(status_code=404,detail=str(e)) diff --git a/dao/statistics.py b/dao/statistics.py index d2566c7..c403ab8 100644 --- a/dao/statistics.py +++ b/dao/statistics.py @@ -3,8 +3,8 @@ from sqlalchemy import func #`GET /statistics/students/age-over-30` #查询所有超过 30 岁的学员信息 -def age_over_thirty(Student,db): - student_list=db.query(Student).filter(Student.age>30,Student.is_deleted==0).all() +def age_over_thirty(Student,input_age,db): + student_list=db.query(Student).filter(Student.age>input_age,Student.is_deleted==0).all() if not student_list : raise NoExist('无目标年龄大于30岁') # 把每个学生对象需要返回的字段,明确放进普通字典。 @@ -30,8 +30,12 @@ def age_over_thirty(Student,db): }) return result #统计男、女、总人数 -def gender_count(Student,db): - result=db.query(Student.class_id,Student.gender,func.count(Student.id)).filter(Student.is_deleted==0).group_by(Student.class_id,Student.gender).all() +def gender_count(Student,db,input_class=None): + if input_class : + result = db.query(Student.class_id, Student.gender, func.count(Student.id)).filter( + Student.class_id == input_class, Student.is_deleted == 0).group_by(Student.class_id, Student.gender).all() + else: + result=db.query(Student.class_id,Student.gender,func.count(Student.id)).filter(Student.is_deleted==0).group_by(Student.class_id,Student.gender).all() if not result : raise NoExist('班里招点人吧,要倒闭了') dict1 = {} @@ -43,10 +47,10 @@ def gender_count(Student,db): return dict1 #查询每次考试成绩都在 80 分以上的学生编号、姓名和成绩 #首先,拿到最低值>80的人的列表,row。然后,遍历元素拿到符合条件的id的属性。 -def score_over_eighty(Score,Student,db): +def score_over_eighty(score,offset_set,Score,Student,db): student_list=db.query(Student.id, Student.student_name)\ .join(Score,Score.student_id==Student.id).filter(Student.is_deleted==0,Score.is_deleted==0).group_by(Student.id,Student.student_name)\ - .having(func.min(Score.score)>80).all() + .having(func.min(Score.score)>score).all() if not student_list : raise NoExist('全是学渣') list1 = [] @@ -59,10 +63,11 @@ def score_over_eighty(Score,Student,db): "student_name": student_name, "score": float(score) if score is not None else None, }) - return list1 + list2=list1[5*(offset_set-1):5*offset_set] + return list2 #查询有两次以上不及格的学生姓名、班级和不及格成绩 -def twice_error(Score,Student,db): - student_list=db.query(Score.student_id).filter(Score.score<60,Score.is_deleted==0).group_by(Score.student_id).having(func.count(Score.id)>2).all() +def twice_error(num,offset_set,Score,Student,db): + student_list=db.query(Score.student_id).filter(Score.score<60,Score.is_deleted==0).group_by(Score.student_id).having(func.count(Score.id)>num).all() if not student_list : raise NoExist('全是学霸') list1=[] @@ -75,7 +80,8 @@ def twice_error(Score,Student,db): "class_id": class_id, "score": float(score) if score is not None else None, }) - return list1 + list2 = list1[5 * (offset_set - 1):5 * offset_set] + return list2 #统计每次考试每个班级的平均分,并按照平均分从高到低排序 def avg_class(Score,Student,db): @@ -92,8 +98,8 @@ def avg_class(Score,Student,db): return result #统计就业薪资最高的前五名学生姓名、班级、就业时间、就业公司 -def max_salary(Student,Employment,db): - student_list = db.query(Student.student_name,Student.class_id,Employment.offer_date,Employment.company_name).join(Employment, Student.id == Employment.student_id).filter(Student.is_deleted==0,Employment.is_deleted==0).order_by(Employment.salary.desc()).limit(5).all() +def max_salary(limit_set,Student,Employment,db): + student_list = db.query(Student.student_name,Student.class_id,Employment.offer_date,Employment.company_name).join(Employment, Student.id == Employment.student_id).filter(Student.is_deleted==0,Employment.is_deleted==0).order_by(Employment.salary.desc()).limit(limit_set).all() if not student_list : raise NoExist('没就业的啊兄弟?') result = [] @@ -108,8 +114,8 @@ def max_salary(Student,Employment,db): # - 统计每个学生的就业时长 # - 就业时长 = `offer_date - employment_open_date` -def student_market(Employment,db): - student_list = db.query(Employment.student_id,func.datediff(Employment.offer_date,Employment.employment_open_date)).filter(Employment.is_deleted==0).all() +def student_market(limit_set,Employment,db): + student_list = db.query(Employment.student_id,func.datediff(Employment.offer_date,Employment.employment_open_date)).filter(Employment.is_deleted==0).limit(limit_set).all() result = [] for student_id, duration_days in student_list: result.append({ diff --git a/schemas/statistics.py b/schemas/statistics.py index cdd636f..4b67013 100644 --- a/schemas/statistics.py +++ b/schemas/statistics.py @@ -1,4 +1,13 @@ +from pydantic import BaseModel,Field + class NoExist(Exception): def __init__(self,value): self.value = value - pass \ No newline at end of file + pass + + +class GenderCount(BaseModel): + input_class:int|None = None + +class StudentScore(BaseModel): + score:int = Field(default = 80, ge = 0, le = 100) From e80d463bbce1e56294eb700cb16f94f989304d7c Mon Sep 17 00:00:00 2001 From: lookerzz <1031516901@qq.com> Date: Thu, 24 Sep 2026 10:28:00 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E6=B7=BB=E5=8A=A0statistics=20tag?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/score.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/api/score.py b/api/score.py index f610b3a..31d1ed8 100644 --- a/api/score.py +++ b/api/score.py @@ -1,7 +1,7 @@ from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.orm import Session from core.database import get_db -from dao.score import post_score_dao, update_score_dao, get_score_dao, delete_score_dao, exist_student_exam_dao +from dao.score import post_score_dao, update_score_dao, get_score_dao,delete_score_dao, exist_student_exam_dao from schemas.score import ScoreCreate, ScoreUpdate, ScorePageResp scores_router = APIRouter(prefix="/scores", tags=["成绩接口"]) @@ -12,14 +12,13 @@ def create_score(req: ScoreCreate, db: Session = Depends(get_db)): # 业务校验:联合唯一 if exist_student_exam_dao(req.student_id, req.exam_seq, db): raise HTTPException(status_code=400, detail="该学生本次考试成绩已存在,不能重复录入") - ok = post_score_dao(req.model_dump(), db) if not ok: raise HTTPException(status_code=400, detail="新增成绩失败,请检查学生id是否存在") return {"msg": "新增成功"} -@scores_router.get("", response_model=ScorePageResp, summary="查询成绩接口") +@scores_router.get("", response_model=ScorePageResp, summary="个人成绩查询接口") def list_score( score_id: int|None=None, student_id: int |None=None, @@ -32,7 +31,9 @@ def list_score( return {"total": total, "data": data_list} -@scores_router.put("/{score_id}", summary="更新成绩接口") + + +@scores_router.put("{score_id}", summary="更新成绩接口") def modify_score(score_id: int, req: ScoreUpdate, db: Session = Depends(get_db)): update_dict = req.model_dump(exclude_unset=True) if not update_dict: @@ -43,9 +44,9 @@ def modify_score(score_id: int, req: ScoreUpdate, db: Session = Depends(get_db)) return {"msg": "修改成功"} -@scores_router.delete("/{score_id}", summary="删除成绩接口") +@scores_router.delete("{score_id}", summary="删除成绩接口") def remove_score(score_id: int, db: Session = Depends(get_db)): ok = delete_score_dao(score_id, db) if not ok: raise HTTPException(status_code=400, detail="删除失败,记录不存在或已删除") - return {"msg": "逻辑删除成功"} + return {"msg": "逻辑删除成功"} \ No newline at end of file