commit 70783dd3500ed24206c9d8c64793323fc9aade63
Author: mister-ye <17608588+yewenwei2002@user.noreply.gitee.com>
Date: Mon Sep 21 18:03:04 2026 +0800
feat: 初始化学生管理系统代码
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..b6b1ecf
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,10 @@
+# 默认忽略的文件
+/shelf/
+/workspace.xml
+# 已忽略包含查询文件的默认文件夹
+/queries/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
+# 基于编辑器的 HTTP 客户端请求
+/httpRequests/
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..7e7ac73
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..105ce2d
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..9de2865
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..6e86413
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/沃林学生管理系统.iml b/.idea/沃林学生管理系统.iml
new file mode 100644
index 0000000..8437fe6
--- /dev/null
+++ b/.idea/沃林学生管理系统.iml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/api/__init__.py b/api/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/api/advisor_api.py b/api/advisor_api.py
new file mode 100644
index 0000000..f989887
--- /dev/null
+++ b/api/advisor_api.py
@@ -0,0 +1,60 @@
+from fastapi import APIRouter, Depends, HTTPException, Query
+from sqlalchemy.orm import Session
+from dao import advisor_dao
+from database import get_db
+from scheme.advisor_scheme import (
+ AdvisorCreate,
+ AdvisorResp,
+ AdvisorStudentResp,
+ AdvisorUpdate,
+)
+
+# router = APIRouter(prefix="/advisor", tags=["顾问管理模块"])
+router = APIRouter()
+
+@router.post("/", response_model=AdvisorResp, summary="新增顾问")
+def add_advisor(advisor: AdvisorCreate, db: Session = Depends(get_db)):
+ if advisor_dao.get_advisor_by_no(db, advisor.advisor_no):
+ raise HTTPException(status_code=400, detail=f"顾问编号 {advisor.advisor_no} 已存在")
+ return advisor_dao.create_advisor(db, advisor)
+
+@router.get("/", response_model=list[AdvisorResp], summary="顾问列表/搜索")
+def get_advisor_list(
+ search_keyword: str | None = Query(default=None, description="按顾问编号或姓名模糊搜索"),
+ db: Session = Depends(get_db),
+):
+ return advisor_dao.get_advisor_list(db, search_keyword)
+
+
+
+@router.get("/{advisor_no}", response_model=AdvisorResp, summary="按编号查询顾问")
+def get_advisor(advisor_no: str, db: Session = Depends(get_db)):
+ advisor = advisor_dao.get_advisor_by_no(db, advisor_no)
+ if not advisor:
+ raise HTTPException(status_code=404, detail=f"顾问 {advisor_no} 不存在")
+ return advisor
+
+
+# 查该顾问名下所有学生
+@router.get("/{advisor_no}/students", response_model=list[AdvisorStudentResp], summary="查询顾问名下学生")
+def get_advisor_students(advisor_no: str, db: Session = Depends(get_db)):
+ if not advisor_dao.get_advisor_by_no(db, advisor_no):
+ raise HTTPException(status_code=404, detail=f"顾问 {advisor_no} 不存在")
+ return advisor_dao.get_students_by_advisor_no(db, advisor_no)
+
+
+@router.put("/{advisor_no}", response_model=AdvisorResp, summary="更新顾问")
+def update_advisor(advisor_no: str, update: AdvisorUpdate, db: Session = Depends(get_db)):
+ advisor = advisor_dao.update_advisor(db, advisor_no, update.model_dump(exclude_unset=True))
+ if not advisor:
+ raise HTTPException(status_code=404, detail=f"顾问 {advisor_no} 不存在")
+ return advisor
+
+# 请求:DELETE /advisor/A001
+@router.delete("/{advisor_no}", response_model=AdvisorResp, summary="删除顾问")
+def delete_advisor(advisor_no: str, db: Session = Depends(get_db)):
+ advisor = advisor_dao.delete_advisor(db, advisor_no)
+ if not advisor:
+ raise HTTPException(status_code=404, detail=f"顾问 {advisor_no} 不存在")
+ return advisor
+
diff --git a/api/api_analysis.py b/api/api_analysis.py
new file mode 100644
index 0000000..b4baa45
--- /dev/null
+++ b/api/api_analysis.py
@@ -0,0 +1,90 @@
+from fastapi import APIRouter, Query, Depends
+from sqlalchemy.orm import Session
+
+from dao.age_analysis import (
+ analysis_student_age,
+ analysis_class_gender,
+ analysis_score_over,
+ analysis_score_not_qualified,
+ analysis_avg_score_order,
+ analysis_employment_salary_order,
+ analysis_student_employment_time,
+ avg_employment_time,
+)
+from database import get_db
+from scheme.schema_analysis import (
+ StudentOut,
+ StudentGenderOut,
+ StudentRecordOut1,
+ StudentRecordOut2,
+ StudentRecordOut3,
+ StudentRecordOut4,
+ StudentRecordOut5,
+ StudentRecordOut6,
+)
+
+router = APIRouter()
+
+
+# 动态年龄范围查询:支持输入年龄阈值(大于等于/小于等于)动态查询学员信息
+@router.get("/analysis_age", response_model=list[StudentOut])
+async def analysis_age(
+ db: Session = Depends(get_db),
+ min_age: int | None = Query(None, ge=0, description="请输入查询年龄大于等于"),
+ max_age: int | None = Query(None, ge=0, description="请输入查询年龄小于等于"),
+):
+ return analysis_student_age(db, min_age, max_age)
+
+
+# 统计每个班级的总人数,以及按性别(男、女)细分的人数分布
+@router.get("/analysis_gender", response_model=list[StudentGenderOut])
+async def analysis_gender(db: Session = Depends(get_db)):
+ return analysis_class_gender(db)
+
+
+# 查询每次考试成绩都在输入分数线(如80分)以上的学生的编号、姓名和成绩
+@router.get("/analysis_scores", response_model=list[StudentRecordOut1])
+async def analysis_scores(
+ db: Session = Depends(get_db),
+ score: float = Query(0, ge=0, le=100, description="请输入查询学生每次考试分数在多少以上"),
+):
+ return analysis_score_over(db, score)
+
+
+# 查询有输入指定次数(如两次)以上不及格的学生的姓名、班级和不及格成绩明细
+@router.get("/analysis_not_qualified", response_model=list[StudentRecordOut2])
+async def analysis_not_qualified(
+ db: Session = Depends(get_db),
+ n: int = Query(1, ge=1, description="请输入不及格次数下限"),
+):
+ return analysis_score_not_qualified(db, n)
+
+
+# 统计每次考试每个班级的平均分,支持按分数从高到低或从低到高动态排序
+@router.get("/analysis_avg_score", response_model=list[StudentRecordOut3])
+async def analysis_avg_score(
+ db: Session = Depends(get_db),
+ sort: str = Query("asc", description="请输入排序类型 asc/desc"),
+):
+ return analysis_avg_score_order(db, sort)
+
+
+# 统计就业薪资排名 Top N(动态输入 N)的学生的姓名、班级、就业时间和就业公司
+@router.get("/analysis_salary_top", response_model=list[StudentRecordOut4])
+async def analysis_salary_top(
+ db: Session = Depends(get_db),
+ n: int = Query(1, ge=1, description="请输入取前几名薪资"),
+):
+ return analysis_employment_salary_order(db, n)
+
+
+# 统计每个学生的就业时长(offer下发时间 - 就业开放时间,单位秒)
+@router.get("/analysis_employment_time", response_model=list[StudentRecordOut5])
+async def analysis_employment_time(db: Session = Depends(get_db)):
+ return analysis_student_employment_time(db)
+
+
+# 统计每个班级的平均就业时长(仅统计进入就业阶段,即有就业开放时间的学生)
+@router.get("/analysis_avg_employment_time", response_model=list[StudentRecordOut6])
+async def analysis_avg_employment_time(db: Session = Depends(get_db)):
+ return avg_employment_time(db)
diff --git a/api/classinfo_api.py b/api/classinfo_api.py
new file mode 100644
index 0000000..890e371
--- /dev/null
+++ b/api/classinfo_api.py
@@ -0,0 +1,45 @@
+# 路由接口
+from typing import List
+
+from fastapi import APIRouter,Depends,HTTPException
+from sqlalchemy.orm import Session
+
+from database import get_db
+from dao.classinfo_dao import create_class,get_class,get_all_class,update_class,delete_class
+from scheme.classinfo_scheme import ClassCreate,ClassUpdate,ClassIn
+
+
+router=APIRouter()
+# 新增班级
+@router.post("/",response_model=ClassIn)
+def add_class(class_data:ClassCreate,db:Session=Depends(get_db)):
+ return create_class(db=db,obj=class_data)
+
+# 条件查询(id / 名称筛选)
+@router.get("/search",response_model=List[ClassIn])
+def get_cls(class_id:int=None,class_name:str=None,db:Session=Depends(get_db)):
+ res_list=get_class(db=db,class_id=class_id,class_name=class_name)
+ if not res_list:
+ raise HTTPException(status_code=404,detail="班级不存在")
+ return res_list
+
+# 查询全部有效班级
+@router.get("/",response_model=List[ClassIn])
+def get_all(db:Session=Depends(get_db)):
+ return get_all_class(db=db)
+
+# 修改班级
+@router.put("/{class_id}",response_model=ClassIn)
+def up_class(class_id:int,class_data:ClassUpdate,db:Session=Depends(get_db)):
+ res=update_class(db=db,class_id=class_id,obj=class_data)
+ if not res:
+ raise HTTPException(status_code=404,detail="班级不存在")
+ return res
+
+# 逻辑删除
+@router.delete("/{class_id}")
+def del_class(class_id:int,db:Session=Depends(get_db)):
+ res=delete_class(db=db,class_id=class_id)
+ if not res:
+ raise HTTPException(status_code=404,detail="班级不存在")
+ return {"msg":"逻辑删除成功"}
diff --git a/api/employment_api.py b/api/employment_api.py
new file mode 100644
index 0000000..4656041
--- /dev/null
+++ b/api/employment_api.py
@@ -0,0 +1,114 @@
+from fastapi import APIRouter, HTTPException, Depends, Query
+from sqlalchemy import and_
+from sqlalchemy.orm import Session
+from model.employment import Employment
+
+from dao import employment_dao
+from database import get_db
+from model.student import Student
+# from model.student import Student
+from scheme.employment_scheme import EmploymentOut, EmploymentCreate
+
+router=APIRouter()
+
+def stu_id(db:Session,stu_id:int):
+ student=(db.query(Student).filter(Student.id==stu_id,Student.statu==1).first())
+ if not student:
+ raise HTTPException(status_code=404,detail="该学生不存在")
+ return student
+@router.get("/",response_model=list[EmploymentOut])
+def set_employment(
+ stu_id:int = None,
+ name:str = None,
+ min_salary: float = None,
+ max_salary: float = None,
+ db:Session = Depends(get_db),
+):
+ obj=employment_dao.set_employment(db
+ ,stu_id
+ ,name
+ ,min_salary
+ ,max_salary)
+ emp_list=[]
+ for i in obj:
+ data={
+ "id":i.id,
+ "stu_id":i.stu_id,
+ "name":i.name,
+ "employment_open_time":i.employment_open_time,
+ "offer_time":i.offer_time,
+ "salary":i.salary
+ }
+ emp_list.append(data)
+ return emp_list
+@router.post("/",response_model=EmploymentCreate)
+def update_emp(
+ stu_id:int = Query(...,description="学生ID"),
+ data:EmploymentCreate=None,
+ db:Session=Depends(get_db)
+):
+ student=(db.query(Student).filter(and_(Student.id==stu_id,Student.statu==1)).first())
+ if not student:
+ raise HTTPException(status_code=404,detail="该学生不存在")
+ try:
+ obj=employment_dao.update_employment(db,stu_id,data)
+ except ValueError as e:
+ raise HTTPException(status_code=400,detail=str(e))
+ emp_dict={
+ "id":obj.id,
+ "stu_id":obj.stu_id,
+ "name":obj.name,
+ "employment_open_time":obj.employment_open_time,
+ "offer_time":obj.offer_time,
+ "salary":obj.salary
+ }
+ return emp_dict
+@router.post("/create", response_model=EmploymentOut)
+def create_employment(
+ stu_id: int = Query(..., description="学生ID"),
+ data: EmploymentCreate = None,
+ db: Session = Depends(get_db),
+):
+ student = (
+ db.query(Student)
+ .filter(and_(Student.id == stu_id, Student.statu == 1))
+ .first()
+ )
+ if not student:
+ raise HTTPException(status_code=404, detail="该学生不存在")
+ exist = employment_dao.get_by_stu_id(db, stu_id)
+ if exist:
+ raise HTTPException(status_code=400, detail="该学生已存在就业信息,请使用更新接口")
+ open_time = data.employment_open_time
+ offer_time = data.offer_time
+ if open_time is not None and offer_time is not None:
+ if offer_time < open_time:
+ raise HTTPException(status_code=400, detail="offer下发时间必须晚于就业开放时间")
+ obj = Employment()
+ obj.stu_id = stu_id
+ obj.name = data.name
+ obj.employment_open_time = open_time
+ obj.offer_time = offer_time
+ obj.company_name = data.company_name
+ obj.salary = data.salary
+ db.add(obj)
+ db.commit()
+ db.refresh(obj)
+ emp_dict = {
+ "id": obj.id,
+ "stu_id": obj.stu_id,
+ "name": obj.name,
+ "employment_open_time": obj.employment_open_time,
+ "offer_time": obj.offer_time,
+ "salary": obj.salary,
+ }
+ return emp_dict
+
+@router.delete("/")
+def delete_emp(emp_id:int,db:Session = Depends(get_db)):
+ del_emp=employment_dao.delete_emp(db,emp_id)
+ if not del_emp:
+ raise HTTPException(status_code=404,detail="该就业信息不存在")
+ return {"code":0,"message":"删除成功"}
+
+
diff --git a/api/score_api.py b/api/score_api.py
new file mode 100644
index 0000000..5c85587
--- /dev/null
+++ b/api/score_api.py
@@ -0,0 +1,75 @@
+# api/score.py
+from fastapi import APIRouter, Depends, HTTPException, Query
+from sqlalchemy.orm import Session
+from dao import score_dao
+from database import get_db
+
+# 创建路由器
+router = APIRouter()
+
+
+
+# 不及格学生表
+
+@router.get("/warning/list")
+async def warning_list(db: Session = Depends(get_db)):
+ return score_dao.get_warning(db)
+
+
+
+# 查某学生所有成绩
+
+
+@router.get("/{stu_id}")
+def get_scores(stu_id: int, db: Session = Depends(get_db)):
+ return score_dao.get_score(db, stu_id)
+
+
+# 增
+
+@router.post("/")
+def add_score(
+ student_id: int = Query(...),
+ exam_seq: int = Query(...),
+ score: float = Query(...),
+ db: Session = Depends(get_db),
+):
+ obj, err = score_dao.add_score(db, student_id, exam_seq, score)
+ if err:
+ raise HTTPException(status_code=400, detail=err)
+ return obj
+
+
+
+
+# 改
+
+
+@router.put("/update")
+def update_score(
+ student_id: int = Query(...),
+ exam_seq: int = Query(...),
+ score: float = Query(...),
+ db: Session = Depends(get_db),
+):
+ obj, err = score_dao.update_score(db, student_id, exam_seq, score)
+ if err:
+ raise HTTPException(status_code=404, detail=err)
+ return obj
+
+
+# 逻辑删除
+
+
+@router.post("/delete")
+def delete_score(
+ student_id: int = Query(...),
+ exam_seq: int = Query(...),
+ db: Session = Depends(get_db),
+):
+ obj, err = score_dao.delete_score(db, student_id, exam_seq)
+ if err:
+ raise HTTPException(status_code=404, detail=err)
+ return {"message": "已删除,数据仍保留在后台"}
+
+
diff --git a/api/student_api.py b/api/student_api.py
new file mode 100644
index 0000000..00d9294
--- /dev/null
+++ b/api/student_api.py
@@ -0,0 +1,41 @@
+from typing import Optional
+from fastapi import APIRouter, HTTPException,Depends,Query
+from sqlalchemy.orm import Session
+from dao import student_dao
+from database import get_db
+from scheme.student_scheme import Create_Stu_Mod
+
+# 创建路由器
+router = APIRouter()
+
+@router.get("/")
+async def get_students(db: Session = Depends(get_db)):
+ # 把db作为参数传给dao函数
+ return student_dao.get_stu(db)
+
+@router.get("/all_stu")
+def get_all_stu(db: Session = Depends(get_db)):
+ return student_dao.get_all_stu(db)
+
+@router.get("/stu_id")
+def get_sid(stu_id:Optional[int]=Query(None,gt=0,description="输入学生编号精确查找"),
+ class_id:Optional[int]=Query(None,description="同时输入班级和姓名进行查找"),
+ stu_name:Optional[str]=Query(None,description="同时输入班级和姓名进行查找"),
+ db:Session=Depends(get_db)):
+ if stu_id is not None:
+ return student_dao.query_sid(db,stu_id)
+ elif class_id is not None and stu_name is not None:
+ return student_dao.query_cid(db,class_id,stu_name)
+ return {"message":""}
+
+@router.get("/delete/{stu_id}")
+def delete_sid(stu_id:int,db:Session = Depends(get_db)):
+ return student_dao.delete_sid(db,stu_id)
+#新增学生
+@router.post("/create_stu")
+def create_stu(stu:Create_Stu_Mod,db:Session = Depends(get_db)):
+ return student_dao.create_stu(db,stu)
+# 修改学生
+@router.post("/update_stu/{stu_id}")
+def update_stu(stu_id:int,stu:Create_Stu_Mod,db:Session = Depends(get_db)):
+ return student_dao.update_stu(stu_id,stu,db)
diff --git a/api/teacher_api.py b/api/teacher_api.py
new file mode 100644
index 0000000..d382c9b
--- /dev/null
+++ b/api/teacher_api.py
@@ -0,0 +1,55 @@
+# api/teacher.py
+# 本文件定义教师相关的所有 API 路由(Controller 层)
+# dao/users_dao.py
+# 本文件封装对 Teacher表的所有数据库操作(增、删、改、查)
+# api/teacher.py
+# 本文件定义教师相关的所有 API 路由(Controller 层)
+from fastapi import APIRouter, Depends, HTTPException
+from sqlalchemy.orm import Session
+from scheme.teacher_scheme import TeacherCreate, TeacherUpdate
+from dao.teacher_dao import add_teacher, get_teacher_by_id, delete_teacher_logic, update_teacher_logic
+from database import get_db
+
+#创建路由
+router=APIRouter()
+
+#新增老师 POST
+@router.post("/")
+def create_teacher(teacher_in: TeacherCreate, db: Session = Depends(get_db)):
+ return add_teacher(db, teacher_in.teacher_no, teacher_in.name, teacher_in.phone)
+
+#根据id查询老师 GET
+@router.get("/{teacher_id}")
+def get_one_teacher(teacher_id: int, db: Session = Depends(get_db)):
+ teacher = get_teacher_by_id(db, teacher_id)
+ if not teacher:
+ raise HTTPException(status_code=404, detail="老师不存在")
+ return {
+ "id": teacher.id,
+ "teacher_no": teacher.teacher_no,
+ "name": teacher.name,
+ "phone": teacher.phone
+ }
+
+#修改老师 PUT
+@router.put("/{teacher_id}")
+def update_teacher(teacher_id: int, teacher_in: TeacherUpdate, db: Session = Depends(get_db)):
+ teacher = update_teacher_logic(db, teacher_id, teacher_in.name, teacher_in.phone)
+ if not teacher:
+ raise HTTPException(status_code=404, detail="老师不存在")
+ return {
+ "id": teacher.id,
+ "teacher_no": teacher.teacher_no,
+ "name": teacher.name,
+ "phone": teacher.phone
+ }
+
+#删除老师 DELETE(逻辑删除)
+@router.delete("/{teacher_id}")
+def delete_teacher(teacher_id: int, db: Session = Depends(get_db)):
+ teacher = delete_teacher_logic(db, teacher_id)
+ if not teacher:
+ return {"success": False, "msg": "数据不存在"}
+ return {"success": True, "msg": "删除成功"}
+
+
diff --git a/dao/__init__.py b/dao/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/dao/advisor_dao.py b/dao/advisor_dao.py
new file mode 100644
index 0000000..126169e
--- /dev/null
+++ b/dao/advisor_dao.py
@@ -0,0 +1,69 @@
+from sqlalchemy import and_, or_
+from sqlalchemy.orm import Session
+from model.advisor import Advisor
+from model.student import Student
+from scheme.advisor_scheme import AdvisorCreate
+# 新增顾问信息(前端传过来的、且已经 scheme 校验好的数据)
+
+def create_advisor(db: Session, advisor: AdvisorCreate) -> Advisor:
+ db_advisor = Advisor(
+ advisor_no=advisor.advisor_no,
+ name=advisor.name,
+ gender=advisor.gender,
+ phone=advisor.phone,
+ remark=advisor.remark,
+ )
+ db.add(db_advisor)
+ db.commit()
+ db.refresh(db_advisor)
+ return db_advisor
+
+# 顾问信息查询:支持全部查询 / 模糊搜索(匹配顾问编号、姓名)
+def get_advisor_list(db: Session, search_keyword: str = None):
+ q = db.query(Advisor).filter(Advisor.statu == 1)
+ if search_keyword:
+ q = q.filter(
+ or_(
+ Advisor.advisor_no.like(f"%{search_keyword}%"),
+ Advisor.name.like(f"%{search_keyword}%"),
+ )
+ )
+ return q.all()
+
+
+# 根据顾问编号查询单个存在的顾问
+def get_advisor_by_no(db: Session, advisor_no: str):
+ return (
+ db.query(Advisor)
+ .filter(and_(Advisor.advisor_no == advisor_no, Advisor.statu == 1))
+ .first()
+ )
+
+# 查询该顾问名下所有学生
+def get_students_by_advisor_no(db: Session, advisor_no: str):
+ return (
+ db.query(Student)
+ .filter(and_(Student.advisor_no == advisor_no, Student.statu == 1))
+ .all()
+ )
+
+# 更新顾问信息(setattr(obj, "k", "v") 动态赋值)
+def update_advisor(db: Session, advisor_no: str, update_data: dict):
+ advisor = get_advisor_by_no(db, advisor_no)
+ if not advisor:
+ return None
+ update_data.pop("advisor_no", None)
+ for k, v in update_data.items():
+ setattr(advisor, k, v)
+ db.commit()
+ db.refresh(advisor)
+ return advisor
+
+# 删除顾问:逻辑删除
+def delete_advisor(db: Session, advisor_no: str):
+ advisor = get_advisor_by_no(db, advisor_no)
+ if not advisor:
+ return None
+ advisor.statu = 0
+ db.commit()
+ return advisor
diff --git a/dao/age_analysis.py b/dao/age_analysis.py
new file mode 100644
index 0000000..33bf92a
--- /dev/null
+++ b/dao/age_analysis.py
@@ -0,0 +1,135 @@
+from fastapi import HTTPException
+
+from model.classinfo import ClassInfo
+from model.employment import Employment
+from model.score import Score
+from model.student import Student
+from sqlalchemy.orm import Session
+
+from sqlalchemy import case, func, text, and_
+
+
+def analysis_student_age(db: Session, min_age: int | None = None, max_age: int | None = None):
+ #按年龄区间(>=min 且 <=max)查询学生,区间可缺省。
+ if min_age is not None and min_age < 0:
+ raise HTTPException(status_code=400, detail="最小年龄不能小于0")
+ elif min_age is not None and max_age is not None and min_age > max_age:
+ raise HTTPException(status_code=400, detail="最小年龄不能大于最大年龄")
+ elif min_age is None and max_age is None :
+ data_age= db.query(Student).all()
+ elif min_age is not None and max_age is None:
+ data_age = db.query(Student).filter(Student.age >= min_age).all()
+ elif max_age is not None and min_age is None:
+ data_age = db.query(Student).filter(Student.age <= max_age).all()
+ elif min_age is not None and max_age is not None:
+ data_age = db.query(Student).filter(and_(Student.age >= min_age, Student.age <= max_age)).all()
+ return data_age
+
+
+def analysis_class_gender(db: Session):
+ #每个班级总人数及男、女人数。
+ data= (db.query(
+ Student.class_id,
+ func.count(Student.id).label("total"),
+ func.sum(case((Student.gender == "男", 1), else_=0)).label("gender_male"),
+ func.sum(case((Student.gender == "女", 1), else_=0)).label("gender_female"),
+ ).group_by(Student.class_id).all())
+ return data
+
+def analysis_score_over(db: Session, score_over: float = 0):
+ #每次考试成绩都在分数线以上的学生的编号、姓名和成绩。
+ qualified_student_ids = (
+ db.query(Score.student_id)
+ .group_by(Score.student_id)
+ .having(func.min(Score.score) >= score_over)
+ )
+ data=(
+ db.query(Student.id, Student.name, Score.score)
+ .join(Score, Student.id == Score.student_id)
+ .filter(Student.id.in_(qualified_student_ids))
+ .all()
+ )
+ return data
+
+def analysis_score_not_qualified(db: Session, not_qualified_count: int = 1):
+ #有指定次数以上不及格(<60)的学生的姓名、班级和不及格成绩明细。
+ not_qualified_students = (db.query(Score.student_id)
+ .filter(Score.score < 60)
+ .group_by(Score.student_id)
+ .having(func.count(Score.student_id) >= not_qualified_count))
+ data=(db.query(Student.id, Student.name, Student.class_id, Score.score)
+ .join(Score, Student.id == Score.student_id)
+ .filter(Student.id.in_(not_qualified_students), Score.score < 60).all())
+ return data
+
+
+def analysis_avg_score_order(db: Session, sort_type: str = "asc"):
+ #每次考试每个班级的平均分,asc/desc 排序。
+ if sort_type not in ("asc", "desc"):
+ raise HTTPException(status_code=400, detail="sort 参数只能为 asc 或 desc")
+ avg_score = func.avg(Score.score).label("avg_score")
+ query = (
+ db.query(Score.exam_seq, ClassInfo.class_name, avg_score)
+ .join(Student, Score.student_id == Student.id)
+ .join(ClassInfo, Student.class_id == ClassInfo.id)
+ .group_by(Score.exam_seq, ClassInfo.class_name)
+ )
+ if sort_type == "asc":
+ data = query.order_by(avg_score.asc()).all()
+ else:
+ data = query.order_by(avg_score.desc()).all()
+ return data
+
+
+def analysis_employment_salary_order(db: Session, n: int = 1):
+ #就业薪资排名 Top N 的学生姓名、班级、就业时间和就业公司
+ data=(db.query(
+ Student.name,
+ Student.class_id,
+ Employment.employment_open_time,
+ Employment.company_name,
+ Employment.salary)
+ .join(Employment, Employment.stu_id == Student.id)
+ .order_by(Employment.salary.desc()).limit(n).all())
+ return data
+
+def analysis_student_employment_time(db: Session):
+ #每个学生的就业时长(offer下发时间 - 就业开放时间,单位秒)。
+ data=(
+ db.query(
+ Employment.stu_id,
+ func.timestampdiff(
+ text("SECOND"),
+ Employment.employment_open_time,
+ Employment.offer_time,
+ ).label("time_diff"),
+ )
+ .filter(
+ Employment.employment_open_time.isnot(None),
+ Employment.offer_time.isnot(None),
+ )
+ .all()
+ )
+ return data
+
+def avg_employment_time(db: Session):
+ #每个班级的平均就业时长(仅统计有就业开放时间的学生,单位秒)
+ return (
+ db.query(
+ Student.class_id,
+ func.avg(
+ func.timestampdiff(
+ text("SECOND"),
+ Employment.employment_open_time,
+ Employment.offer_time,
+ )
+ ).label("time_diff"),
+ )
+ .join(Employment, Employment.stu_id == Student.id)
+ .filter(
+ Employment.employment_open_time.isnot(None),
+ Employment.offer_time.isnot(None),
+ )
+ .group_by(Student.class_id)
+ .all()
+ )
diff --git a/dao/classinfo_dao.py b/dao/classinfo_dao.py
new file mode 100644
index 0000000..0c8a8dd
--- /dev/null
+++ b/dao/classinfo_dao.py
@@ -0,0 +1,45 @@
+from model.classinfo import ClassInfo
+from scheme.classinfo_scheme import ClassCreate, ClassUpdate
+
+# 增删改查
+def create_class(db,obj:ClassCreate):
+ new_class=ClassInfo(class_name=obj.class_name,start_time=obj.start_time,statu=1)
+ db.add(new_class)
+ db.commit()
+ db.refresh(new_class)
+ return new_class
+# 查询单个班级
+def get_class(db,class_id=None,class_name=None):
+ u1=db.query(ClassInfo).filter(ClassInfo.statu==1)
+ if class_id is not None:
+ u1=u1.filter(ClassInfo.id==class_id)
+ if class_name is not None:
+ u1=u1.filter(ClassInfo.class_name.like(f"%{class_name}%"))
+ if class_id is not None:
+ return u1.first()
+ else:
+ return u1.all()
+# 查询所有班级
+def get_all_class(db):
+ return db.query(ClassInfo).filter(ClassInfo.statu==1).all()
+# 修改班级`
+def update_class(db,class_id:int,obj:ClassUpdate):
+ u2=get_class(db,class_id)
+ if u2:
+ u2.class_name=obj.class_name
+ u2.start_time=obj.start_time
+ db.commit()
+ db.refresh(u2)
+ return u2
+ else:
+ return None
+# 删除班级
+def delete_class(db,class_id:int):
+ u3=get_class(db,class_id)
+ if u3:
+ u3.statu=0
+ db.commit()
+ db.refresh(u3)
+ return u3
+ else:
+ return None
diff --git a/dao/employment_dao.py b/dao/employment_dao.py
new file mode 100644
index 0000000..5b9a27b
--- /dev/null
+++ b/dao/employment_dao.py
@@ -0,0 +1,86 @@
+from sqlalchemy import and_
+from sqlalchemy.orm import Session
+
+from scheme import employment_scheme
+from scheme.employment_scheme import EmploymentCreate
+from model.employment import Employment
+from model.student import Student
+
+def update_employment(db:Session,stu_id:int,data:employment_scheme.EmploymentCreate):
+ obj=get_by_stu_id(db,stu_id)
+ open_time=data.employment_open_time
+ offer_time=data.offer_time
+ if open_time is not None and offer_time is not None :
+ if offer_time< open_time:
+ raise ValueError("offer下发时间必须晚于及就业开放时间")
+ emp_dict={
+ "stu_id":stu_id,
+ "name":data.name,
+ "employment_open_time":open_time,
+ "offer_time":offer_time,
+ "company_name":data.company_name,
+ "salary":data.salary
+ }
+ if obj:
+ obj.stu_id = stu_id
+ obj.name = data.name
+ obj.employment_open_time = open_time
+ obj.offer_time = offer_time
+ obj.company_name = data.company_name
+ obj.salary = data.salary
+ else:
+ obj = Employment()
+ obj.stu_id = stu_id
+ obj.employment_open_time = open_time
+ obj.offer_time = offer_time
+ obj.company_name = data.company_name
+ obj.salary = data.salary
+ db.add(obj)
+ db.commit()
+ db.refresh(obj)
+ return obj
+def get_employment(db:Session,emp_id: int):
+ employment= (db.query(Employment).filter(Employment.id == emp_id, Employment.is_deleted == False).first())
+ return employment
+def get_by_stu_id(db:Session,stu_id:int):
+ employment=(db.query(Employment).filter(Employment.stu_id == stu_id, Employment.is_deleted == 0).first())
+ return employment
+def set_employment(db:Session,
+ stu_id: int = None,
+ name: str = None,
+ min_salary: float = None,
+ max_salary: float = None):
+ s=db.query(Employment).filter(Employment.is_deleted == False)
+ if stu_id is not None:
+ s = s.filter(Employment.stu_id == stu_id)
+ if name is not None:
+ s = s.filter(Employment.name.like(f'%{name}%'))
+ if min_salary is not None:
+ s = s.filter(Employment.salary >= min_salary)
+ if max_salary is not None:
+ s = s.filter(Employment.salary <= max_salary)
+ return s
+def create_employment(db: Session, stu_id: int, data: EmploymentCreate):
+ obj = Employment()
+ obj.stu_id = stu_id
+ obj.name = data.name
+ obj.employment_open_time = data.employment_open_time
+ obj.offer_time = data.offer_time
+ obj.company_name = data.company_name
+ obj.salary = data.salary
+ db.add(obj)
+ db.commit()
+ return obj
+def delete_emp(db:Session,emp_id:int):
+ obj=get_employment(db,emp_id)
+ if not obj:
+ return False
+ obj.is_deleted=1
+ db.commit()
+ return True
+
+
+
+
+
+
diff --git a/dao/score_dao.py b/dao/score_dao.py
new file mode 100644
index 0000000..8a7cbc3
--- /dev/null
+++ b/dao/score_dao.py
@@ -0,0 +1,115 @@
+# dao/score_dao.py
+from sqlalchemy.orm import Session
+from model.score import Score
+from model.student import Student
+
+
+ # 查
+# 查一个学生所有成绩
+
+def get_score(db: Session, student_id: int):
+ return db.query(Score).filter(
+ Score.student_id == student_id,
+ Score.is_deleted == 0,
+ ).order_by(Score.exam_seq.asc()).all()
+
+
+
+# 查某学生的某次成绩
+def get_one_score(db: Session, student_id: int, exam_seq: int):
+
+ return db.query(Score).filter(
+ Score.student_id == student_id,
+ Score.exam_seq == exam_seq,
+ Score.is_deleted == 0,
+ ).first()
+
+
+
+# 查不及格的
+def get_warning(db: Session):
+ return db.query(Score).filter(
+ Score.is_warning == 1,
+ Score.is_deleted == 0,
+ ).all()
+
+
+
+
+
+
+# 录
+
+
+# 录信息
+def add_score(db: Session, student_id: int, exam_seq: int, score: float):
+
+ stu = db.query(Student).filter(Student.id == student_id).first()
+ if not stu:
+ return None, "学生不存在"
+
+
+ exists = get_one_score(db, student_id, exam_seq)
+ if exists:
+ return None, "该学生这次成绩已录过"
+
+
+
+ new_score = Score(
+ student_id=student_id,
+ exam_seq=exam_seq,
+ score=score,
+ is_warning=1 if score < 60 else 0,
+ is_deleted=0,
+ )
+ db.add(new_score)
+ db.commit()
+ db.refresh(new_score)
+ return new_score, None
+
+
+
+
+
+# 改
+
+ # 改成绩
+
+def update_score(db: Session, student_id: int, exam_seq: int, new_score: float):
+
+
+ obj = get_one_score(db, student_id, exam_seq)
+ if not obj:
+ return None, "成绩不存在或已删除"
+
+ obj.score = new_score
+
+ # 如果不及格 预警重置
+ obj.is_warning = 1 if new_score < 60 else 0
+
+ db.commit()
+ db.refresh(obj)
+ return obj, None
+
+
+
+
+
+# 逻辑删除
+
+
+def delete_score(db: Session, student_id: int, exam_seq: int):
+ # 把删除的学生0改成1
+
+ obj = get_one_score(db, student_id, exam_seq)
+
+ if not obj:
+ return None, "成绩不存在或已删除"
+
+
+ # 0 → 1
+ obj.is_deleted = 1
+ db.commit()
+ return obj, None
+
+
diff --git a/dao/student_dao.py b/dao/student_dao.py
new file mode 100644
index 0000000..cd37b22
--- /dev/null
+++ b/dao/student_dao.py
@@ -0,0 +1,109 @@
+from fastapi import HTTPException
+from sqlalchemy import and_
+from sqlalchemy.orm import Session
+from model.advisor import Advisor
+from model.student import Student
+from model.classinfo import ClassInfo
+from datetime import date
+import scheme
+import api
+
+def get_stu(db:Session):
+ res=db.query(Student).all()
+ l1=list()
+ for i in res :
+ l1.append(i.name)
+ return {"message":l1}
+
+def get_all_stu(db:Session):
+ rs=db.query(Student).filter(Student.statu==1).all()
+ return {"message":"查询成功","data":rs}
+
+def get_stu_id(stu_id:int,db:Session):
+ res = db.query(Student).filter(Student.id==stu_id).all()
+ l1 = list()
+ for i in res:
+ l1.append(i.name)
+ return {"message": l1}
+
+def query_sid(db:Session,stu_id):
+ res=db.query(Student).filter(Student.id==stu_id).first()
+ return res
+#按班级姓名查
+def query_cid(db:Session,class_id,stu_name):
+ res=db.query(Student).filter(and_(Student.class_id==class_id,Student.name==stu_name)).first()
+ return {"data":res}
+
+def query_stu_name(db:Session,stu_name):
+ res=db.query(Student).filter(Student.name==stu_name).all()
+ return res
+# 逻辑删除学生
+def delete_sid(db:Session,stu_id):
+ rs=db.query(Student).filter(Student.id==stu_id).first()
+ if not rs:
+ raise HTTPException(status_code=404, detail="学生不存在")
+ rs.statu=0
+ db.commit()
+ return {"message":f"已删除编号为:{rs.stu_id}的学生:{rs.name}"}#逻辑删除
+
+def create_stu(db:Session,stu):
+ cid=db.query(ClassInfo.id).filter(ClassInfo.class_name==stu.class_name).first()
+ if cid:
+ cid=cid.id
+ else:
+ raise HTTPException(status_code=404, detail="班级不存在")
+ vid=None
+ if stu.advisor_name:
+ vid=db.query(Advisor.id).filter(Advisor.name==stu.advisor_name).first()
+ if vid:
+ vid=vid.id
+ else:
+ raise HTTPException(status_code=404, detail="顾问不存在")
+ s_new=Student(
+ name=stu.name,
+ hometown=stu.hometown,
+ school = stu.school,
+ major = stu.major,
+ enroll_time = stu.enroll_time,
+ graduate_time = stu.graduate_time,
+ education = stu.education,
+ age = stu.age,
+ gender = stu.gender,
+ statu = 1,
+ advisor_id = vid,
+ class_id = cid
+ )
+ db.add(s_new)
+ db.commit()
+ return {"message":"学生增加成功"}
+
+def update_stu(stu_id,stu,db:Session):
+ cid = db.query(ClassInfo.id).filter(ClassInfo.class_name == stu.class_name).first()
+ if cid is None:
+ raise HTTPException(status_code=400, detail="找不到该班级名称")
+ else:
+ cid=cid.id
+ if stu.advisor_name:
+ vid = db.query(Advisor.id).filter(Advisor.name == stu.advisor_name).first()
+ if vid:
+ vid=vid.id
+ else:
+ raise HTTPException(status_code=404, detail="顾问不存在")
+ s=db.query(Student).filter(Student.id==stu_id).first()
+ if not s:
+ raise HTTPException(status_code=404, detail="该学生不存在")
+ s.name=stu.name
+ s.hometown = stu.hometown
+ s.school = stu.school
+ s.major = stu.major
+ s.enroll_time = stu.enroll_time
+ s.graduate_time = stu.graduate_time
+ s.education = stu.education
+ s.age = stu.age
+ s.gender = stu.gender
+ s.advisor_id = vid
+ s.class_id = cid
+ db.commit()
+ return {"message":"学生信息修改成功"}
+
+
diff --git a/dao/teacher_dao.py b/dao/teacher_dao.py
new file mode 100644
index 0000000..c9804a8
--- /dev/null
+++ b/dao/teacher_dao.py
@@ -0,0 +1,53 @@
+# dao/users_dao.py
+# 本文件封装对 Teacher表的所有数据库操作(增、删、改、查)
+# dao/teacher_dao.py
+# 本文件封装对 Teacher表的所有数据库操作(增、删、改、查)
+# dao/teacher_dao.py
+# 本文件封装对 Teacher表的所有数据库操作(增、删、改、查)
+# dao/teacher_dao.py
+# 本文件封装对 Teacher表的所有数据库操作(增、删、改、查)
+# dao/teacher_dao.py
+# 本文件封装对 Teacher表的所有数据库操作(增、删、改、查)
+from sqlalchemy.orm import Session
+from model.teacher import Teacher
+
+#新增
+def add_teacher(db:Session, teacher_no:str, name:str, phone:str):
+ new_teacher=Teacher(
+ teacher_no=teacher_no,
+ name=name,
+ phone=phone
+ )
+ db.add(new_teacher)
+ db.commit()
+ db.refresh(new_teacher)
+ return new_teacher
+
+#查 根据id
+def get_teacher_by_id(db:Session,id:int):
+ return db.query(Teacher).filter(Teacher.id == id, Teacher.is_deleted == 0).first()
+
+#逻辑删除
+def delete_teacher_logic(db:Session,id:int):
+ teacher=get_teacher_by_id(db,id)
+ if not teacher:
+ return None
+ teacher.is_deleted=1
+ db.commit()
+ return teacher
+
+#修改
+def update_teacher_logic(db:Session, id:int, new_name:str=None, new_phone:str=None):
+ teacher = get_teacher_by_id(db, id)
+ if not teacher:
+ return None
+ if new_name is not None:
+ teacher.name = new_name
+ if new_phone is not None:
+ teacher.phone = new_phone
+ db.commit()
+ db.refresh(teacher)
+ return teacher
+
+
+
diff --git a/database.py b/database.py
new file mode 100644
index 0000000..57030a7
--- /dev/null
+++ b/database.py
@@ -0,0 +1,18 @@
+from sqlalchemy import create_engine
+from sqlalchemy.ext.declarative import declarative_base
+from sqlalchemy.orm import sessionmaker
+
+
+
+# 数据库引擎
+engine = create_engine("mysql+pymysql://root:123456@localhost/fastapi0914")
+# 会话工厂
+SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
+Base=declarative_base()
+
+def get_db():
+ db = SessionLocal()
+ try:
+ yield db
+ finally:
+ db.close()
\ No newline at end of file
diff --git a/main.py b/main.py
new file mode 100644
index 0000000..26db624
--- /dev/null
+++ b/main.py
@@ -0,0 +1,18 @@
+import uvicorn
+from fastapi import FastAPI
+from api import student_api,employment_api,advisor_api,teacher_api,score_api,classinfo_api,api_analysis
+from database import engine
+from model import Base
+Base.metadata.create_all(engine)
+app = FastAPI(title="沃林学生管理系统", version="1.0.0")
+app.include_router(student_api.router, prefix="/students", tags=["学生模块"])
+app.include_router(employment_api.router,prefix="/employment",tags=["就业模块"])
+app.include_router(advisor_api.router,prefix="/advisor",tags=["顾问模块"])
+app.include_router(teacher_api.router,prefix="/teacher",tags=["教师模块"])
+app.include_router(score_api.router,prefix="/score",tags=["成绩模块"])
+app.include_router(classinfo_api.router,prefix="/classinfo",tags=["班级模块"])
+app.include_router(api_analysis.router,prefix="/analysis",tags=["统计分析模块"])
+
+if __name__=="__main__":
+ Base.metadata.create_all(bind=engine)
+ uvicorn.run("main:app",host="localhost",port=8051,reload=True)
diff --git a/model/__init__.py b/model/__init__.py
new file mode 100644
index 0000000..95ebc0c
--- /dev/null
+++ b/model/__init__.py
@@ -0,0 +1,3 @@
+from sqlalchemy.orm import declarative_base
+
+Base = declarative_base()
\ No newline at end of file
diff --git a/model/advisor.py b/model/advisor.py
new file mode 100644
index 0000000..75eea4c
--- /dev/null
+++ b/model/advisor.py
@@ -0,0 +1,17 @@
+from sqlalchemy import Column, Integer, String
+from sqlalchemy.orm import declarative_base, relationship
+from database import Base
+
+class Advisor(Base):
+ __tablename__ = 'advisor'
+ id = Column(Integer, primary_key=True, autoincrement=True)
+ advisor_no = Column(String(32), unique=True, nullable=False)
+ name = Column(String(50), nullable=False)
+ gender = Column(String(2), nullable=False)
+ phone = Column(String(20), nullable=False)
+ remark=Column(String(500), nullable=True)
+ statu = Column(Integer, default=1, nullable=False)
+ students=relationship("Student", back_populates="advisor")
+
+
+
diff --git a/model/classinfo.py b/model/classinfo.py
new file mode 100644
index 0000000..3823db5
--- /dev/null
+++ b/model/classinfo.py
@@ -0,0 +1,17 @@
+from sqlalchemy import Column, Integer, String, Date
+from sqlalchemy.orm import relationship
+from database import Base
+from model.teacher_class import teacher_class
+from model.teacher import Teacher
+from model.student import Student
+# 导入中间关联表
+
+# 班级信息表
+class ClassInfo(Base):
+ __tablename__="classinfo"
+ id=Column(Integer,primary_key=True,autoincrement=True)
+ class_name=Column(String(50),nullable=False,unique=True)
+ start_time=Column(Date)
+ statu=Column(Integer,default=1) # 逻辑删除表字段 1代表存在
+ teachers=relationship(Teacher,secondary=teacher_class,back_populates="classinfo")
+ students= relationship(Student,back_populates="classinfo")
\ No newline at end of file
diff --git a/model/employment.py b/model/employment.py
new file mode 100644
index 0000000..d783567
--- /dev/null
+++ b/model/employment.py
@@ -0,0 +1,20 @@
+from sqlalchemy import Column, Integer, String, Float, ForeignKey, DateTime, Date
+from sqlalchemy.orm import relationship
+from database import Base
+
+class Employment(Base):
+ __tablename__="employment"
+ id=Column(Integer,primary_key=True, autoincrement=True)
+ name=Column(String(50))
+ stu_id=Column(Integer,ForeignKey("student.id"),unique=True,nullable=False,index=True)
+ employment_open_time=Column(DateTime,nullable=True)
+ offer_time=Column(DateTime,nullable=True)
+ company_name=Column(String(50),nullable=True)
+ salary=Column(Float,nullable=True)
+ is_deleted = Column(Integer, default=0, nullable=False, comment="0未删除 1已删除")
+ student=relationship("Student",back_populates="employment")
+
+
+
+
+
diff --git a/model/score.py b/model/score.py
new file mode 100644
index 0000000..424a8b0
--- /dev/null
+++ b/model/score.py
@@ -0,0 +1,17 @@
+# model/score.py
+from sqlalchemy import Column, Integer, Float, ForeignKey, DateTime
+from sqlalchemy.orm import relationship
+from datetime import datetime
+from database import Base
+
+
+class Score(Base):
+ __tablename__ = "score"
+ id = Column(Integer, primary_key=True, autoincrement=True) # 主键
+ student_id = Column(Integer, ForeignKey("student.id"), nullable=False) #外键
+ exam_seq = Column(Integer, nullable=False)
+ score = Column(Float, nullable=False)
+ is_warning = Column(Integer, default=0) #低分预警 0正常 1不及格
+ is_deleted = Column(Integer, default=0) #是否被逻辑删除
+ create_time = Column(DateTime, default=datetime.now)
+ student = relationship("Student", back_populates="scores")
\ No newline at end of file
diff --git a/model/student.py b/model/student.py
new file mode 100644
index 0000000..e9204da
--- /dev/null
+++ b/model/student.py
@@ -0,0 +1,29 @@
+from sqlalchemy import create_engine, Column, Integer, String, Float, ForeignKey, Table, Date
+from sqlalchemy.orm import relationship
+from model import Base
+
+class Student(Base):
+ __tablename__="student"
+ id = Column(Integer, primary_key=True, autoincrement=True)
+ stu_id=Column(String(50),unique=True)
+ name = Column(String(50), nullable=False)
+ hometown=Column(String(100))
+ school = Column(String(100))
+ major= Column(String(100))
+ enroll_time=Column(String(100))
+ graduate_time=Column(String(100))
+ education=Column(String(100))
+ age=Column(Integer)
+ gender=Column(String(10))
+ statu=Column(Integer,nullable=False)
+ advisor_id = Column(Integer, ForeignKey("advisor.id"), nullable=True)
+ class_id = Column(Integer, ForeignKey("classinfo.id"))
+ advisor = relationship("Advisor", back_populates="students")#关联顾问
+ scores = relationship("Score", back_populates="student")#关联成绩
+ employment=relationship("Employment", back_populates="student")#关联就业
+ classinfo = relationship("ClassInfo", back_populates="students")#关联班级
+
+
+
+
+
diff --git a/model/teacher.py b/model/teacher.py
new file mode 100644
index 0000000..d5769d3
--- /dev/null
+++ b/model/teacher.py
@@ -0,0 +1,17 @@
+from sqlalchemy import Column, Integer, String, ForeignKey,Date,DateTime
+from sqlalchemy.orm import relationship
+from sqlalchemy.sql import func
+from model import Base
+from model.teacher_class import teacher_class
+
+
+class Teacher(Base):
+ __tablename__="teacher"
+ id=Column(Integer,primary_key=True,autoincrement=True)
+ teacher_no=Column(String(50),unique=True,nullable=False)
+ name=Column(String(50),nullable=False)
+ phone=Column(String(50),nullable=False)
+ is_deleted=Column(Integer,default=0)
+ created_at=Column(DateTime,server_default=func.now())
+ updated_at=Column(DateTime,onupdate=func.now())
+ classinfo = relationship("ClassInfo",secondary=teacher_class, back_populates="teachers")
\ No newline at end of file
diff --git a/model/teacher_class.py b/model/teacher_class.py
new file mode 100644
index 0000000..e8dec89
--- /dev/null
+++ b/model/teacher_class.py
@@ -0,0 +1,12 @@
+from sqlalchemy import create_engine, Column, Integer, String, Float, ForeignKey, Table, Date
+from sqlalchemy.orm import relationship
+from model import Base
+
+teacher_class = Table(
+ "teacher_class", # 中间表的名字
+ Base.metadata, # 绑定到 Base 的元数据
+ Column("class_id", Integer, ForeignKey("classinfo.id"), primary_key=True),
+ Column("teacher_id", Integer, ForeignKey("teacher.id"), primary_key=True) ,
+ Column("role",String(50))
+
+)
\ No newline at end of file
diff --git a/scheme/__init__.py b/scheme/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/scheme/advisor_scheme.py b/scheme/advisor_scheme.py
new file mode 100644
index 0000000..2532d48
--- /dev/null
+++ b/scheme/advisor_scheme.py
@@ -0,0 +1,40 @@
+from typing import Optional
+from pydantic import BaseModel, ConfigDict, Field
+
+# 新增顾问信息
+class AdvisorCreate(BaseModel):
+ advisor_no: str = Field(description="顾问编号")
+ name: str = Field(description="顾问姓名")
+ gender: Optional[str] = Field(description="顾问性别")
+ phone: Optional[str] = Field(None, description="联系电话")
+ remark: Optional[str] = Field(None, description="备注")
+
+
+# 修改顾问信息
+class AdvisorUpdate(BaseModel):
+ name: Optional[str] = Field(None, description="顾问姓名")
+ gender: Optional[str] = Field(None, description="顾问性别")
+ phone: Optional[str] = Field(None, description="联系电话")
+ remark: Optional[str] = Field(None, description="备注")
+
+# 顾问信息响应体
+class AdvisorResp(BaseModel):
+ id: int
+ advisor_no: str
+ name: str
+ gender: str
+ phone: Optional[str] = None
+ remark: Optional[str] = None
+
+ # Pydantic v2 写法:允许直接从 ORM 对象取值
+ model_config = ConfigDict(from_attributes=True)
+
+
+# 某个顾问名下的学生信息
+class AdvisorStudentResp(BaseModel):
+ id: int = Field(description="学生ID")
+ name: str = Field(description="学生姓名")
+ class_id: Optional[int] = Field(None, description="班级ID")
+ advisor_no: Optional[str] = Field(None, description="顾问编号")
+
+ model_config = ConfigDict(from_attributes=True)
diff --git a/scheme/classinfo_scheme.py b/scheme/classinfo_scheme.py
new file mode 100644
index 0000000..5387f38
--- /dev/null
+++ b/scheme/classinfo_scheme.py
@@ -0,0 +1,25 @@
+# 请求和响应模型
+from datetime import date
+from pydantic import BaseModel,Field
+from typing import Optional
+
+# 新增班级
+class ClassCreate(BaseModel):
+ class_name: str=Field(...,min_length=2,max_length=20,description="班级名称")
+ start_time: Optional[date]=Field(None,description="开课时间")
+
+# 修改班级
+class ClassUpdate(BaseModel):
+ class_name: Optional[str]=Field(...,min_length=2,max_length=20,description="班级名称")
+ start_time: Optional[date]=Field(None,description="开课时间")
+ statu: Optional[int]=Field(None,ge=0,le=1,description="判断存不存在")
+
+# 响应模型
+class ClassIn(BaseModel):
+ id: int
+ class_name: str
+ start_time: Optional[date]
+ statu: int
+
+ class Config:
+ from_attributes=True
\ No newline at end of file
diff --git a/scheme/employment_scheme.py b/scheme/employment_scheme.py
new file mode 100644
index 0000000..0e06441
--- /dev/null
+++ b/scheme/employment_scheme.py
@@ -0,0 +1,38 @@
+from datetime import datetime
+from pydantic import BaseModel, Field, field_validator
+
+
+
+class EmploymentCreate(BaseModel):
+ stu_id: int = Field(..., description="学生编号ID")
+ name: str | None = None
+ employment_open_time: datetime | None = None
+ offer_time: datetime | None = None
+ company_name: str | None = None
+ salary: float | None = None
+
+
+
+
+class EmploymentSet(BaseModel):
+ name: str | None = None
+ stu_id: int | None = None
+ min_salary: float | None = None
+ max_salary: float | None = None
+
+
+class EmploymentUpdate(BaseModel):
+ name: str | None = None
+ employment_open_time: datetime | None = None
+ offer_time: datetime | None = None
+ company_name: str | None = None
+ salary: float | None = None
+
+class EmploymentOut(BaseModel):
+ id:int
+ stu_id:int
+ name: str | None = None
+ employment_open_time:datetime | None = None
+ offer_time: datetime | None = None
+ company_name:str | None = None
+ salary:float | None = None
\ No newline at end of file
diff --git a/scheme/schema_analysis.py b/scheme/schema_analysis.py
new file mode 100644
index 0000000..5eddfab
--- /dev/null
+++ b/scheme/schema_analysis.py
@@ -0,0 +1,62 @@
+from datetime import datetime
+
+from pydantic import BaseModel, ConfigDict
+
+
+class StudentOut(BaseModel):
+ model_config = ConfigDict(from_attributes=True)
+ id: int
+ name: str
+ age: int | None = None
+ class_id: int | None = None
+
+
+class StudentGenderOut(BaseModel):
+ model_config = ConfigDict(from_attributes=True)
+ class_id: int | None = None
+ total: int
+ gender_male: int
+ gender_female: int
+
+
+class StudentRecordOut1(BaseModel):
+ model_config = ConfigDict(from_attributes=True)
+ id: int
+ name: str
+ score: float
+
+
+class StudentRecordOut2(BaseModel):
+ model_config = ConfigDict(from_attributes=True)
+ id: int
+ name: str
+ class_id: int | None = None
+ score: float
+
+
+class StudentRecordOut3(BaseModel):
+ model_config = ConfigDict(from_attributes=True)
+ exam_seq: int
+ class_name: str
+ avg_score: float
+
+
+class StudentRecordOut4(BaseModel):
+ model_config = ConfigDict(from_attributes=True)
+ name: str
+ class_id: int | None = None
+ employment_open_time: datetime | None = None
+ company_name: str | None = None
+ salary: float | None = None
+
+
+class StudentRecordOut5(BaseModel):
+ model_config = ConfigDict(from_attributes=True)
+ stu_id: int
+ time_diff: float
+
+
+class StudentRecordOut6(BaseModel):
+ model_config = ConfigDict(from_attributes=True)
+ class_id: int | None = None
+ time_diff: float
diff --git a/scheme/score_scheme.py b/scheme/score_scheme.py
new file mode 100644
index 0000000..90215f8
--- /dev/null
+++ b/scheme/score_scheme.py
@@ -0,0 +1,28 @@
+# scheme/score_scheme.py
+from pydantic import BaseModel, Field
+
+
+# 录入成绩
+class ScoreCreate(BaseModel):
+ student_id: int = Field(..., description="ID")
+ exam_seq: int = Field(..., ge=1, description="第几次")
+ score: float = Field(..., ge=0, le=100, description="分数")
+
+# 修改成绩
+class ScoreUpdate(BaseModel):
+ student_id: int = Field(..., description="ID")
+ exam_seq: int = Field(..., ge=1, description="第几次")
+ score: float = Field(..., ge=0, le=100, description="分数")
+
+
+# 返回前端
+class ScoreOut(BaseModel):
+ id: int
+ student_id: int
+ exam_seq: int
+ score: float
+ is_warning: int
+ create_time: str | None = None
+
+ class Config:
+ from_attributes = True
\ No newline at end of file
diff --git a/scheme/student_scheme.py b/scheme/student_scheme.py
new file mode 100644
index 0000000..c694ea8
--- /dev/null
+++ b/scheme/student_scheme.py
@@ -0,0 +1,22 @@
+from pydantic import BaseModel,Field,field_validator
+from typing import Optional
+from model import student
+
+class Create_Stu_Mod(BaseModel):
+ name: str = Field(min_length=3, max_length=15,description="姓名")
+ hometown :Optional[str]=Field(min_length=2)
+ school :str= Field(min_length=2,description="毕业院校")
+ major : str=Field(description="专业")
+ enroll_time:Optional[str]=Field(None,description="入学时间")
+ graduate_time:Optional[str] = Field(None,description="毕业时间")
+ education :Optional[str] = Field(description="学历")
+ age :int=Field(gt=14,lt=60,description="年龄")
+ gender : str = Field(max_length=10,description="性别")
+ advisor_name:Optional[str]=Field(description="顾问名字")
+ class_name:str=Field(description="班级名称")
+ # 自定义校验器
+ @field_validator('gender')
+ def gender_validator(cls, gender):
+ if gender not in ['男','女']:
+ raise ValueError('性别输入有误')
+ return gender
diff --git a/scheme/teacher_scheme.py b/scheme/teacher_scheme.py
new file mode 100644
index 0000000..1a401f7
--- /dev/null
+++ b/scheme/teacher_scheme.py
@@ -0,0 +1,14 @@
+from pydantic import BaseModel, Field
+from typing import Optional
+
+class TeacherBase(BaseModel):
+ teacher_no: str = Field(..., max_length=50, description="教师编号")
+ name: str = Field(..., max_length=50, description="教师姓名")
+ phone: str = Field(..., max_length=50, description="手机号")
+
+class TeacherCreate(TeacherBase):
+ pass
+
+class TeacherUpdate(BaseModel):
+ name: Optional[str] = Field(None, max_length=50, description="教师姓名")
+ phone: Optional[str] = Field(None, max_length=50, description="手机号")
diff --git a/test.py b/test.py
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/test.py
@@ -0,0 +1 @@
+