diff --git a/api/class_api.py b/api/class_api.py new file mode 100644 index 0000000..6f5143c --- /dev/null +++ b/api/class_api.py @@ -0,0 +1,78 @@ +from fastapi import APIRouter,Depends,HTTPException,Query +from dao.class_dao import add_class_dao, update_class_dao, delete_class_dao, hard_delete_class_dao,class_no_exist,get_class_list_dao,get_class_by_id_dao +from schemas.class_request import ClassCreate,ClassResponse,ClassUpdate +from database import get_db +from typing import Optional +from sqlalchemy.orm import Session +class_app = APIRouter() +@class_app.post('',response_model=ClassResponse) +def add_class(c:ClassCreate,db=Depends(get_db)): + if class_no_exist(c.class_id,db): + raise HTTPException(status_code=404,detail='该编号已经存在') + ok,result = add_class_dao(c.model_dump(),db) + if not ok: + raise HTTPException(status_code=500,detail='服务器繁忙或指定的老师不存在') + return ClassResponse(totals=1,data=result) +@class_app.put('{id}') +def update_class(id:int,c:ClassUpdate,db=Depends(get_db)): + d = c.model_dump() + r = update_class_dao(id=id ,update_data=d,db=db) + if not r: + raise HTTPException(status_code=500,detail='更新失败') + return{'code':200,'total':1,'detail':'更新成功'} +''' +@class_app.get('') +def get_class(id:int,db=Depends(get_db),page:int=1,page_size:int=10): + rows = get_class_dao(id=id,db=db,page=page,page_size=page_size) + if rows: + return rows + raise HTTPException(status_code=404,detail='该班级不存在') +''' +@class_app.get('') +def get_classes( + id: Optional[int] = Query(None, description="班级ID"), + class_id: Optional[str] = Query(None, description="班级编号"), + class_name: Optional[str] = Query(None, description="班级名称(支持模糊搜索)"), + ht_id: Optional[int] = Query(None, description="班主任ID"), + t_id: Optional[int] = Query(None, description="教师ID"), + page: int = Query(1, ge=1, description="页码"), + page_size: int = Query(10, ge=1, le=100, description="每页条数"), + db: Session = Depends(get_db) +): + # 如果明确传了 id,当作单条查询处理 + if id is not None: + single_class = get_class_by_id_dao(id=id, db=db) + if not single_class: + raise HTTPException(status_code=404, detail="该班级不存在") + return single_class + + # 多条件列表查询 + total, items = get_class_list_dao( + db=db, + class_no=class_id, + class_name=class_name, + ht_id=ht_id, + t_id=t_id, + page=page, + page_size=page_size + ) + + return { + "total": total, + "page": page, + "page_size": page_size, + "items": items + } +@class_app.delete('/{id}') #软删除 +def delete_class(id:int,db=Depends(get_db)): + rows = delete_class_dao(id=id,db=db) + if not rows: + raise HTTPException(status_code=404,detail='该班级不存在或已删除') + return{'code':200,'total':rows,'detail':'删除成功'} + +@class_app.delete('{id}/hard') +def hard_delete_class(id:int,db=Depends(get_db)): + rows = hard_delete_class_dao(id=id,db=db) + if not rows: + raise HTTPException(status_code=404,detail='该班级不存在') + return{'code':200,'total':rows,'detail':'物理删除成功'} diff --git a/api/e_api.py b/api/e_api.py new file mode 100644 index 0000000..68db826 --- /dev/null +++ b/api/e_api.py @@ -0,0 +1,37 @@ +from fastapi import APIRouter,HTTPException,Depends +from database import Session +from model.e_model import Employment +from schema.e_schema import EmploymentRequest +from dao.e_dao import get_employment_dao,wq_employment_dao,upd_employment_dao,del_employment_dao +employment_api = APIRouter() + + +def get_db(): + db = Session() + try: + yield db + finally: + db.close() +@employment_api.get('/',summary='查询就业信息') +def get_employment(db=Depends(get_db)): + r = get_employment_dao(db) + return {'code':200,'detail':'查询成功!','data':r} + + +@employment_api.post('/',summary='新增就业信息') +def add_employment(e:EmploymentRequest,db=Depends(get_db)): + d= e.model_dump() + r = wq_employment_dao(d,db) + return {'code':200,'detail':'添加成功!','total':r} +# +# +@employment_api.put('/',summary='更新就业信息') +def update_employment(e:EmploymentRequest,db=Depends(get_db)): + e1 = e.model_dump() + r = upd_employment_dao(e1,db) + return {'code':200,'msg':'更新成功','total':r} +# +@employment_api.delete('/',summary='删除就业信息') +def delete_employment(stu_id:str,db=Depends(get_db)): + r =del_employment_dao(stu_id,db) + return {'code':200,'totals':r,'detail':'删除成功!'} diff --git a/api/scores_api.py b/api/scores_api.py new file mode 100644 index 0000000..0884242 --- /dev/null +++ b/api/scores_api.py @@ -0,0 +1,45 @@ +from dao import get_db +from schema import Test +from fastapi import APIRouter,Depends +from dao import a,a1,a2,a3,a4,a5 + +ScoreAPI=APIRouter() +ScoreAPI1=APIRouter() +@ScoreAPI.post('/scores',tags=['增']) +def scores(s:Test,db=Depends(get_db)): + a(s,db) + return {'code':200,'detail':'添加成功!',} +@ScoreAPI.delete('/scores',tags=['删']) +def scores1(stu_id:int,db=Depends(get_db)): + r = a1(stu_id,db) + if r==0: + return '没有该学生' + else: + return f'已删除学生编号为{stu_id}的学生' +@ScoreAPI.put('/scores',tags=['改']) +def scores2(stu_id:int,exam_seq:int,s:Test,db=Depends(get_db)): + r=a2(stu_id,exam_seq,s,db) + if r!=0: + return '更新成功' + else: + return '没有该数据' +@ScoreAPI.get('/scores',tags=['查']) +def Scores3(stu_id:int,exam_seq:int,db=Depends(get_db)): + r=a3(stu_id,exam_seq,db) + try: + return {'成绩编号':r.id,'学生编号':r.stu_id,f'第{exam_seq}次成绩:':r.score,'创建日期':r.create_date,'更新日期':r.update_date} + except: + return '没有该学生' +@ScoreAPI1.put('/scores/{stu_id}',tags=['软删除']) +def scores4(stu_id:int,exam_seq:int,db=Depends(get_db)): + r=a4(stu_id, exam_seq, db) + if r ==0: + return '没有该数据' + else: + return '已删除' +@ScoreAPI1.get('/scores/{stu_id}',tags=['总分,平均分,最大值,最小值']) +def scores5(stu_id:int,db=Depends(get_db)): + s,r,a=a5(stu_id,db) + + return f'总分:{s},平均分:{s/len(r)},最大值:{max(a)},最小值:{min(a)}' + diff --git a/api/student_api.py b/api/student_api.py new file mode 100644 index 0000000..f47a83d --- /dev/null +++ b/api/student_api.py @@ -0,0 +1,168 @@ +from fastapi import APIRouter, Depends, HTTPException +from sqlalchemy.orm import Session +from database import get_db +from dao.student_dao import create_student, get_student_by_id, get_student_list, update_student, delete_student_logic +from schema.student_schema import ( + StudentCreateRequest, + StudentUpdateRequest, + StudentQuery, + StudentResponse, + StudentPageResponse +) + +# 创建路由对象 API Router1 +router = APIRouter( tags=["学生管理模块"]) +@router.post("/", response_model=StudentResponse,summary="新增学生") +def add_student( + student_req: StudentCreateRequest, + db: Session = Depends(get_db) +): + db_stu = create_student(db, student_req) + # 手动转字典:model的stu_id映射响应体的id + stu_dict = { + "id": db_stu.id, + "stu_id": db_stu.stu_id, + "class_id": db_stu.class_id, + "stu_name": db_stu.stu_name, + "native_place": db_stu.native_place, + "graduate_school": db_stu.graduate_school, + "major": db_stu.major, + "enroll_date": db_stu.enroll_date, + "graduate_date": db_stu.graduate_date, + "education": db_stu.education, + "advisor_no": db_stu.advisor_no, + "age": db_stu.age, + "gender": db_stu.gender, + "is_deleted": db_stu.is_deleted + } + return StudentResponse(**stu_dict) + + +@router.get("/", response_model=StudentPageResponse,summary="根据学号姓名班级查询") +def get_student_page( + query: StudentQuery = Depends(), + db: Session = Depends(get_db) +): + + total, db_stu_list = get_student_list( + db, + stu_id=query.stu_id, + stu_name=query.stu_name, + class_id=query.class_id, + page=query.page, + page_size=query.page_size + ) + item_list = [] + for db_stu in db_stu_list: + stu_dict = { + "id": db_stu.id, + "stu_id": db_stu.stu_id, + "class_id": db_stu.class_id, + "stu_name": db_stu.stu_name, + "native_place": db_stu.native_place, + "graduate_school": db_stu.graduate_school, + "major": db_stu.major, + "enroll_date": db_stu.enroll_date, + "graduate_date": db_stu.graduate_date, + "education": db_stu.education, + "advisor_no": db_stu.advisor_no, + "age": db_stu.age, + "gender": db_stu.gender, + "is_deleted": db_stu.is_deleted + } + item_list.append(StudentResponse(**stu_dict)) + + return StudentPageResponse( + total=total, + page=query.page, + page_size=query.page_size, + items=item_list + ) + + +@router.get("/{stu_id}", response_model=StudentResponse,summary="根据id查询") +def get_one_student( + stu_id: int, + db: Session = Depends(get_db) +): + #根据主键stu_id查询单个学生 + db_stu = get_student_by_id(db, stu_id) + if db_stu is None: + raise HTTPException(status_code=404, detail="该学生不存在") + + stu_dict = { + "id": db_stu.id, + "stu_id": db_stu.stu_id, + "class_id": db_stu.class_id, + "stu_name": db_stu.stu_name, + "native_place": db_stu.native_place, + "graduate_school": db_stu.graduate_school, + "major": db_stu.major, + "enroll_date": db_stu.enroll_date, + "graduate_date": db_stu.graduate_date, + "education": db_stu.education, + "advisor_no": db_stu.advisor_no, + "age": db_stu.age, + "gender": db_stu.gender, + "is_deleted": db_stu.is_deleted + } + return StudentResponse(**stu_dict) + + +@router.put("/{stu_id}", response_model=StudentResponse,summary="修改学生信息") +def edit_student( + stu_id: int, + update_req: StudentUpdateRequest, + db: Session = Depends(get_db) +): + #修改学生信息 + db_stu = update_student(db, stu_id, update_req) + if db_stu is None: + raise HTTPException(status_code=404, detail="该学生不存在") + + stu_dict = { + "id": db_stu.id, + "stu_id": db_stu.stu_id, + "class_id": db_stu.class_id, + "stu_name": db_stu.stu_name, + "native_place": db_stu.native_place, + "graduate_school": db_stu.graduate_school, + "major": db_stu.major, + "enroll_date": db_stu.enroll_date, + "graduate_date": db_stu.graduate_date, + "education": db_stu.education, + "advisor_no": db_stu.advisor_no, + "age": db_stu.age, + "gender": db_stu.gender, + "is_deleted": db_stu.is_deleted + } + return StudentResponse(**stu_dict) + + +@router.delete("/{stu_id}", response_model=StudentResponse,summary="删除学生信息") +def remove_student( + stu_id: int, + db: Session = Depends(get_db) +): + #逻辑删除学生 + db_stu = delete_student_logic(db, stu_id) + if db_stu is None: + raise HTTPException(status_code=404, detail="该学生不存在") + + stu_dict = { + "id": db_stu.id, + "stu_id": db_stu.stu_id, + "stu_name": db_stu.stu_name, + "class_id": db_stu.class_id, + "native_place": db_stu.native_place, + "graduate_school": db_stu.graduate_school, + "major": db_stu.major, + "enroll_date": db_stu.enroll_date, + "graduate_date": db_stu.graduate_date, + "education": db_stu.education, + "advisor_no": db_stu.advisor_no, + "age": db_stu.age, + "gender": db_stu.gender, + "is_deleted": db_stu.is_deleted + } + return StudentResponse(**stu_dict) diff --git a/api/tj_api.py b/api/tj_api.py new file mode 100644 index 0000000..04bd933 --- /dev/null +++ b/api/tj_api.py @@ -0,0 +1,56 @@ +from fastapi import APIRouter, Depends,FastAPI +from model.teaModel import Session +from dao.dao1 import fun1,fun2,fun3,fun4,fun5,fun6, fun7 ,fun8 + +StatsAPI = APIRouter(prefix='/stats', tags=['统计分析']) +app = FastAPI() +app.include_router(StatsAPI) +def get_db(): + db=Session() + try: + yield db + finally: + db.close() + +@StatsAPI.get('/age-over-30', summary='超过30岁学员') +def stat_age(db= Depends(get_db)): + rows = fun1(db) + return {'code': 200, 'data':rows } + +@StatsAPI.get('/class-gender', summary='各班人数及男女比') +def stat_class_gender(db= Depends(get_db)): + rows = fun2(db) + return {'code': 200, 'data': rows} + +@StatsAPI.get('/all-pass-80', summary='每次考试都≥80分') +def stat_pass80(db= Depends(get_db)): + rows = fun3(db) + return {'code': 200, 'data': rows} + +@StatsAPI.get('/two-fail', summary='两次以上不及格') +def stat_fail(db= Depends(get_db)): + rows = fun4(db) + return {'code': 200, 'data': rows} + +@StatsAPI.get('/avg-score', summary='各班每次考试平均分') +def stat_avg(db= Depends(get_db)): + rows = fun5(db) + return {'code': 200, 'data': rows} + +@StatsAPI.get('/top5-salary', summary='薪资前五名') +def stat_top5(db= Depends(get_db)): + rows = fun6(db) + return rows +@StatsAPI.get('/duration', summary='每个学生就业时长') +def stat_duration(db= Depends(get_db)): + rows = fun7(db) + return {'code': 200, 'data': rows} + +@StatsAPI.get('/avg-duration', summary='各班平均就业时长') +def stat_avg_duration(db= Depends(get_db)): + rows = fun8(db) + return {'code': 200, 'data': rows} + +if __name__ == '__main__': + import uvicorn + uvicorn.run('cxjk:app',host='127.0.0.1',port=9999) \ No newline at end of file diff --git a/dao/class_dao.py b/dao/class_dao.py new file mode 100644 index 0000000..1c90c18 --- /dev/null +++ b/dao/class_dao.py @@ -0,0 +1,118 @@ +from pymysql import IntegrityError +from models.class_model import Class,Teacher +from sqlalchemy.orm import Session +def add_class_dao(c,db): + try: + head_teacher = db.query(Teacher).filter(Teacher.id == c['ht_id']).first() + if head_teacher is None: + return False, '指定的班主任不存在' + class_teacher = db.query(Teacher).filter(Teacher.id == c['t_id']).first() + if class_teacher is None: + return False, '指定的授课老师不存在' + if db.query(Class).filter( + Class.class_id == c['class_id'],#如果请求体传进来的classid在表里找的到 + Class.is_deleted==0 #且没有处于软删除状态 + ).first(): + return False, '班级编号已存在' + new_class = Class(**c) + db.add(new_class) + db.commit() + return True,new_class + except Exception as e: + db.rollback() + print('创建失败:',repr(e)) + return False,str(e) +def update_class_dao(id,update_data,db): + try: + rows = db.query(Class).filter(Class.id == id).update(update_data) + except Exception as e: + db.rollback() + print('更新失败:', repr(e)) + return False, str(e) + else: + db.commit() + return rows +''' +def get_class_dao(id,db,page,page_size): + q = db.query(Class) + if id: + q = q.filter(Class.id == id,Class.is_deleted==0) + rows = q.offset((page-1)*page_size ).limit(page_size).all() + if rows: + return[{'class_id':i.id,'class_no':i.class_no,'class_name':i.class_name,'ht_id':i.ht_id,'t_id':i.t_id,'start_time':i.start_time}for i in rows] +''' +def get_class_list_dao(db: Session, page: int, page_size: int, **filters): + #软删除过滤 + q = db.query(Class).filter(Class.is_deleted == 0) + + # 精确匹配字段 + if filters.get("class_no"): + q = q.filter(Class.class_id == filters["class_id"]) + if filters.get("ht_id") is not None: + q = q.filter(Class.ht_id == filters["ht_id"]) + if filters.get("t_id") is not None: + q = q.filter(Class.t_id == filters["t_id"]) + + # 模糊匹配字段(例如班级名称) + if filters.get("class_name"): + q = q.filter(Class.class_name.like(f"%{filters['class_name']}%")) + + # 获取满足条件的总记录数(分页必备) + total = q.count() + + # 分页查询 + rows = q.offset((page - 1) * page_size).limit(page_size).all() + + items = [ #如果匹配不到,则会返回空列表 + { + 'id': i.id, + 'class_id': i.class_id, + 'class_name': i.class_name, + 'ht_id': i.ht_id, + 't_id': i.t_id, + 'start_time': i.start_time + } + for i in rows + ] + return total, items +def get_class_by_id_dao(id: int, db: Session): + i = db.query(Class).filter(Class.id == id, Class.is_deleted == 0).first() + if not i: + return None + return { + 'id': i.id, + 'class_id': i.class_id, + 'class_name': i.class_name, + 'ht_id': i.ht_id, + 't_id': i.t_id, + 'start_time': i.start_time + } +def delete_class_dao(id,db): #软删除 + try: + rows = (db.query(Class) + .filter(Class.id == id,Class.is_deleted==0)#只匹配还没处于软删除状态的行 + .update({Class.is_deleted:1},synchronize_session=False))#软删除就是把这个字段改成True + except Exception as e: + db.rollback() + print('删除失败:', repr(e)) + return False, str(e) + else: + db.commit() + return rows +def hard_delete_class_dao(id,db): #物理删除 + try: + rows = (db.query(Class).filter(Class.id == id).delete(synchronize_session=False)) + db.commit() + return rows + except IntegrityError: + db.rollback() + return False, '该班级被其他数据引用,无法彻底删除' + except Exception as e: + db.rollback() + print('物理删除失败', repr(e)) + return False, str(e) +def class_no_exist(class_id,db): + return (db.query(Class) + .filter(Class.class_id == class_id,Class.is_deleted==0) + .first() is not None + ) diff --git a/dao/e_dao.py b/dao/e_dao.py new file mode 100644 index 0000000..015e65d --- /dev/null +++ b/dao/e_dao.py @@ -0,0 +1,52 @@ +from http.client import HTTPException + +from model.e_model import Employment +from fastapi import HTTPException + +def get_employment_dao(db): + try: + r = db.query(Employment).all() + except Exception as e: + db.rollback() + raise HTTPException(status_code=500, detail=f'查询失败:str(e)') + else: + db.commit() + return [{'stu_id':i.stu_id,'class_name':i.class_name} for i in r ] + + +def wq_employment_dao(o,db): + try: + o1 = Employment(**o) + db.add(o1) + except Exception as e: + db.rollback() + raise HTTPException(status_code=500, detail=f'添加失败:str(e)') + else: + db.commit() + return 1 + +def upd_employment_dao(e,db): + try: + rows = db.query(Employment).filter(Employment.stu_id == e['stu_id']).update(e) + except: + db.rollback() + raise HTTPException(status_code=500, detail='更新异常,请稍后再执行!') + else: + db.commit() + return 1 + +def del_employment_dao(stu_id,db): + try: + row = db.query(Employment).filter(Employment.stu_id == stu_id).all() + if not row: + raise HTTPException(status_code=404,detail='记录不存在') + for i in row: + if i.deleted == 0: + db.query(Employment).filter(Employment.stu_id == i.stu_id).update({'deleted':1}) + except Exception as e: + db.rollback() + raise HTTPException(status_code=500,detail=f'删除失败:{str(e)}') + else: + db.commit() + return 1 + diff --git a/dao/scores_dao.py b/dao/scores_dao.py new file mode 100644 index 0000000..7c29f10 --- /dev/null +++ b/dao/scores_dao.py @@ -0,0 +1,48 @@ +from model import Session +from model import Scores +def get_db(): + db = Session() + try: + yield db + finally: + db.close() +# @ScoreAPI.post('/scores') +def a(s,db): + d = s.model_dump() + o = Scores(**d) + db.add(o) + db.commit() +# @ScoreAPI.delete('/scores') +def a1(stu_id,db): + r=db.query(Scores).filter(Scores.stu_id==stu_id).delete() + db.commit() + return r +# @ScoreAPI.put('/scores') +def a2(stu_id,exam_seq,s,db): + r = db.query(Scores).filter((Scores.stu_id == stu_id) & (Scores.exam_seq == exam_seq)).update( + s.model_dump(exclude={'stu_id', 'exam_seq'})) + db.commit() + return r +#@ScoreAPI.get('/scores') +def a3(stu_id,exam_seq,db): + r=db.query(Scores).filter((Scores.stu_id==stu_id)&(Scores.exam_seq==exam_seq)).first() + return r +# @ScoreAPI1.put('/scores/{stu_id}',tags=['软删除']) +def a4(stu_id, exam_seq, db): + r=db.query(Scores).filter((Scores.stu_id==stu_id)&(Scores.exam_seq==exam_seq)).update({Scores.deleted:0}) + db.commit() + return r +# @ScoreAPI1.get('/scores/{stu_id}',tags=['总分,平均分,最大值,最小值']) +def a5(stu_id,db): + r = db.query(Scores).filter((Scores.stu_id == stu_id)).all() + s = 0 + a = [] + for i in r: + s += i.score + a.append(i.score) + return s,r,a + + + + + diff --git a/dao/sj_dao.py b/dao/sj_dao.py new file mode 100644 index 0000000..5fde35b --- /dev/null +++ b/dao/sj_dao.py @@ -0,0 +1,123 @@ +from sqlalchemy import func, desc +from sqlalchemy.exc import SQLAlchemyError +from model.Employment import Employment +from fastapi import HTTPException +from model.Students import Student +from model.Score import Scores + +# ① 超30岁学员 +def fun1(db): + try: + rows = db.query(Student).filter(Student.age>30).all() + return [{'id': i.stu_id, 'name': i.name} for i in rows] + except SQLAlchemyError as e: + db.rollback() + raise HTTPException(status_code=400,detail=f'查询超三十岁学员信息失败:{str(e)}') + +# ② 各班人数 + 男女比 +def fun2(db): + try: + rows = db.query( + Student.stu_class, + Student.gender, + func.count(1).label('num') + ).group_by(Student.stu_class, Student.gender).all() + return [{'班级': r.stu_class, '性别': r.gender, '人数':r.num } for r in rows] + except Exception as e: + db.rollback() + raise HTTPException(status_code=400,detail=f'查询各班人数与男女比例失败:{str(e)}') + +# ③ 每次考试都≥80分的学生 +def fun3(db): + try: + print('1111111111-----') + rows = db.query( + Scores.stu_id, + Student.name, + func.min(Scores.score).label('min_score') + ).join(Student, Student.stu_id == Scores.stu_id) \ + .group_by(Scores.stu_id, Student.name) \ + .having(func.min(Scores.score) >= 80).all() + t = ['stu_id', 'name', 'min_score'] + l = [dict(zip(t, row)) for row in rows] + return l + except Exception as e: + db.rollback() + raise HTTPException(status_code=400,detail=f'查询成绩大于80的学生失败:{str(e)}') + +# ④ 两次以上不及格的学生 +def fun4(db): + try: + rows = db.query( + Student.name, + Student.stu_class, + ).join(Scores, Scores.stu_id == Student.stu_id) \ + .filter(Scores.score < 60) \ + .group_by(Scores.stu_id,Student.name,Student.stu_class) \ + .having(func.count(Scores.id) >= 2).all() + t = ['name', 'stu_class'] + l = [dict(zip(t, row)) for row in rows] + return l + except Exception as e: + db.rollback() + raise HTTPException(status_code=400,detail=f'查询两次不及格的学生失败:{str(e)}') + +# ⑤ 每次考试各班平均分(降序) +def fun5(db): + try: + print('5555555555') + rows = db.query( + Scores.exam_seq, + Student.stu_class, + func.avg(Scores.score).label('avg_score') + ).join(Student, Student.stu_id == Scores.stu_id) \ + .group_by(Scores.exam_seq, Student.stu_class) \ + .order_by(desc('avg_score')).all() + t = ['exam_seq', 'stu_class','avg_score'] + l = [dict(zip(t, row)) for row in rows] + return l + except Exception as e: + db.rollback() + raise HTTPException(status_code=400, detail=f'查询各班平均分失败:{str(e)}') + +# ⑥ 薪资前五名 +def fun6(db): + try: + rows = db.query(Student.name, Student.stu_class,Employment.work_date,Employment.company,Employment.salary).join(Student, Student.stu_id == Employment.s_id).order_by(Employment.salary).limit(5).all() + t = ['s_name','stu_class','work_date','company','salary'] + l = [dict(zip(t, row)) for row in rows] + print(l) + return {'code': 200, 'data': l} + except Exception as e: + db.rollback() + raise HTTPException(status_code=400,detail=f'查询薪资前五名失败失败:{str(e)}') + +# ⑦ 每个学生就业时长 +def fun7(db): + try: + rows = db.query(Employment.s_id,func.datediff(Employment.offer_date,Employment.open_date).label('offer_s')).group_by(Employment.s_id,Employment.offer_date,Employment.open_date).all() + t = ['s_id', 'offer_s'] + l = [dict(zip(t, row)) for row in rows] + return {'code': 200, 'data': l} + except Exception as e: + db.rollback() + raise HTTPException(status_code=400,detail=f'查询每个学生的就业时长失败:{str(e)}') + +# ⑧ 各班平均就业时长 +def fun8(db): + try: + rows = db.query( + Student.stu_class, + func.avg(func.datediff(Employment.offer_date, Employment.open_date)).label('avg_duration') + ).join(Student, Student.stu_id == Employment.s_id).filter( + Employment.open_date.isnot(None) + ).group_by(Student.stu_class).all() + t = ['stu_class', 'avg_duration'] + l = [dict(zip(t, row)) for row in rows] + return l + except Exception as e: + db.rollback() + raise HTTPException(status_code=400,detail=f'查询各班平均就业时长失败:{str(e)}') + + + diff --git a/dao/student_dao.py b/dao/student_dao.py new file mode 100644 index 0000000..366ad79 --- /dev/null +++ b/dao/student_dao.py @@ -0,0 +1,57 @@ +from sqlalchemy.orm import Session +from model.student_model import Student +from schema.student_schema import StudentCreateRequest,StudentUpdateRequest +def create_student(db: Session, student_create: StudentCreateRequest): + """ + 新增学生 + : db: 数据库会话 + :student_c reate: 前端传来的新增请求体 + """ + db_student = Student(**student_create.model_dump())#请求体转为字典,再解包创建实体对象 + db.add(db_student)#添加 + db.commit()#提交事务 + new_student=db.query(Student).filter( + Student.stu_id==student_create.stu_id, + Student.is_deleted==False).first() + #查询 Student 表,取第一条,没有则返回 None + return new_student +def get_student_by_id(db: Session, stu_id: int):#根据stu_id查询单个学生 + query = db.query(Student).filter( + Student.stu_id == stu_id, + Student.is_deleted == False) + return query.first() +def get_student_list(db: Session, stu_id:None, + class_id:str=None, page:int=1, page_size:int=10, stu_name:str=None):#查询学生列表,支持按照编号/姓名/班级删选 + # 基础查询:只查未逻辑删除的学生 + query = db.query(Student).filter(Student.is_deleted == False) + if stu_id: + query = query.filter(Student.stu_id == stu_id) + if stu_name: + query = query.filter(Student.stu_name == stu_name) + if class_id: + query = query.filter(Student.class_id == class_id) + total = query.count()#统计满足条件的记录 + offset = (page - 1) * page_size# 计算分页偏移量:跳过前面 (page-1)*page_size 条 + db_student_list = query.offset(offset).limit(page_size).all()# 执行分页查询:跳过offset条,取page_size条 + return total, db_student_list +def update_student(db: Session, stu_id:int,student_update: StudentUpdateRequest): + """修改学生信息 +db: 数据库会话stu_id: 要修改的学生主键student_update: 前端传来的修改请求体: +""" + db_student = get_student_by_id(db=db,stu_id=stu_id) + if not db_student: + return None#先查学生,查不到报错 + update_data=student_update.model_dump(exclude_unset=True)#只解析前端输入的键值对,默认值不会解析 + for k,v in update_data.items(): + setattr(db_student,k,v)#把 value 赋给 db_student 的 key 属性。 + db.commit() + updated_student=get_student_by_id(db, stu_id) + return updated_student +def delete_student_logic(db: Session, stu_id:int): + db_student = get_student_by_id(db, stu_id) + if not db_student: + return None + db_student.is_deleted = True#把逻辑删除标记改成True + db.commit()#重新查询,返回标记已删除的这条记录 + deleted_student = db.query(Student).filter(Student.stu_id == stu_id).first() + return deleted_student diff --git a/model/class_model.py b/model/class_model.py new file mode 100644 index 0000000..02acdf1 --- /dev/null +++ b/model/class_model.py @@ -0,0 +1,35 @@ +from database import DATETIME,Base,Column,Integer,String,ForeignKey,Boolean,Enum as SQLEnum,DateTime,DATETIME +from datetime import datetime +from database import engine +from enum import Enum +class Class(Base): + ''' + 班级信息数据库类模型 + ''' + __tablename__ = "s_class" + id = Column(Integer, + primary_key=True, + autoincrement=True, + index=True,#为该列创建索引,加快查询速度 + comment='班级主键id') + class_id = Column(String(32), + nullable=False, + index=True, + comment='班级编号') + class_name = Column(String(32), + nullable=False, + index=True, + comment='班级名字') + ht_id = Column(Integer, + ForeignKey('s_teacher.id'), + nullable=False, + comment='班主任ID') + t_id = Column(Integer, + ForeignKey('s_teacher.id'), + nullable=False, + comment='授课老师ID') + start_time = Column(DATETIME,default=datetime.now,comment='开课时间') + update_time = Column(DATETIME,default=datetime.now,onupdate=datetime.now) + is_deleted = Column(Integer,default=0,index=True) +Base.metadata.create_all(engine) + diff --git a/model/e_model.py b/model/e_model.py new file mode 100644 index 0000000..580afd2 --- /dev/null +++ b/model/e_model.py @@ -0,0 +1,45 @@ +from sqlalchemy import * +from datetime import datetime +from sqlalchemy.orm import declarative_base,sessionmaker + +db_url = "mysql+pymysql://root:123456@127.0.0.1:3306/ai0824_stu?charset=utf8" + +engine = create_engine(db_url, pool_size=100,echo=False) + +Base = declarative_base() + +class Employment(Base): + __tablename__ = 's_employment' + id = Column(Integer + , primary_key=True + , autoincrement=True + , comment='编号,自增主键') + stu_id = Column(String(20) + # , ForeingKey('s_student.stu_id') + , unique=True + , nullable=True + , comment='学生学号,唯一') + class_name = Column(String(32)) + company = Column(String(100)) + salary = Column(Integer + , default=0) + open_date = Column(DATETIME + , default=datetime.now()) + offer_date = Column(DATETIME + , default=datetime.now()) + work_date = Column(DATETIME + , default=datetime.now() + , onupdate=datetime.now) + is_delete = Column(Integer + , default=0 + ) + + +Session = sessionmaker( bind = engine, autoflush = False, autocommit = False ) + +def get_db(): + db = Session() + try: + yield db + finally: + db.close() \ No newline at end of file diff --git a/model/scores_model.py b/model/scores_model.py new file mode 100644 index 0000000..d8f4b9b --- /dev/null +++ b/model/scores_model.py @@ -0,0 +1,45 @@ +from sqlalchemy import * +from datetime import datetime +from sqlalchemy.orm import declarative_base,sessionmaker +db_url='mysql+pymysql://root:123456@127.0.0.1:3306/ai0824?charset=utf8' +engine=create_engine(db_url , pool_size=50,echo=True) +Base=declarative_base() +class Scores(Base): + __tablename__ = 's_scores' + id=Column( Integer + , primary_key=True + ,autoincrement=True + ,comment='成绩编号' + ) + stu_id=Column(Integer + ,comment='学生编号' + , nullable=False + ) + exam_seq=Column(Integer + + , nullable=True + ,comment='考试序次' + ) + score=Column(Float + ,comment='分数' + ) + create_date=Column(DATETIME + , default=datetime.now + ,comment='创建时间' + ) + update_date=Column(DATETIME + , default=datetime.now + ,onupdate=datetime.now + ,comment='更新时间' + ) + deleted=Column(Integer + ,default=1 + , comment='软删除' + ) +Base.metadata.create_all(engine) +Session = sessionmaker( bind=engine + ,autoflush=False + ,autocommit = False + ) + + diff --git a/model/student_model.py b/model/student_model.py new file mode 100644 index 0000000..9b2c8bf --- /dev/null +++ b/model/student_model.py @@ -0,0 +1,25 @@ +from database import Base +from sqlalchemy import Column,Integer,String,Date,Boolean, Enum as SQLEnum +from enum import Enum +class Sex(Enum): + m= "男" + w= "女" +class Student(Base): + __tablename__ = 's_student' + id=Column(Integer, primary_key=True,autoincrement=True,comment="数据库主键id") + stu_id=Column(String(50),unique=True,nullable=False,comment='学生id') + class_id=Column(String(50),#ForeignKey=("s_class.class_id"), + comment='学生班级') + stu_name=Column(String(30),nullable=False,comment='学生姓名') + native_place = Column(String(100), comment="籍贯") + graduate_school = Column(String(100), comment="毕业院校") + major = Column(String(50), comment="专业") + enroll_date = Column(Date, comment="入学时间") + graduate_date = Column(Date, comment="毕业时间") + education = Column(String(30), comment="学历") + advisor_no = Column(String(50), comment="顾问编号") + age = Column(Integer, comment="年龄") + gender = Column( + SQLEnum(Sex, values_callable=lambda x: [e.value for e in x]), + comment="性 别") + is_deleted = Column(Boolean, default=False, comment="逻辑删除标记:False未删除,True已删除") \ No newline at end of file diff --git a/schema/class_request.py b/schema/class_request.py new file mode 100644 index 0000000..748388e --- /dev/null +++ b/schema/class_request.py @@ -0,0 +1,22 @@ +from pydantic import BaseModel,Field,ConfigDict +from datetime import datetime +class ClassRequest(BaseModel): #定义更新班级信息的请求体 + class_id: str = Field(...,) + class_name: str = Field(...,) + ht_id: int = Field(...) + t_id: int = Field(...) + start_time: datetime = Field(...) + model_config = ConfigDict(from_attributes=True) +class ClassCreate(ClassRequest): + pass +class ClassUpdate(ClassRequest): + pass +class ClassResponse(BaseModel): #定义响应体 + code:int = 200 + detail:str ='OK' + totals:int = 0 + data:ClassRequest | None = None #原来的写法str | dict | list | tuple 这几种类型里, + #一个都匹配不上路由里传的ORM对象result(Class类的实例),所以报错500 + + + diff --git a/schema/e_schema.py b/schema/e_schema.py new file mode 100644 index 0000000..f0f122b --- /dev/null +++ b/schema/e_schema.py @@ -0,0 +1,13 @@ +from pydantic import BaseModel +from typing import Optional + +class EmploymentRequest(BaseModel): + stu_id: str|None = None + class_name: str|None = None + company: Optional[str] = None + salary: Optional[int] = None + + + + + diff --git a/schema/scores_schema.py b/schema/scores_schema.py new file mode 100644 index 0000000..f1b810f --- /dev/null +++ b/schema/scores_schema.py @@ -0,0 +1,5 @@ +from pydantic import BaseModel +class Test(BaseModel): + stu_id:int + exam_seq:int + score:float diff --git a/schema/student_schema.py b/schema/student_schema.py new file mode 100644 index 0000000..c892209 --- /dev/null +++ b/schema/student_schema.py @@ -0,0 +1,58 @@ +from typing import Optional,List +from pydantic import BaseModel,Field +from datetime import date +from model.student_model import Sex +##================ =====请求体=============== +class StudentCreateRequest(BaseModel): + stu_id:str=Field(...,description="学生id") + class_id:Optional[str]=Field(None,description="学生班级id") + stu_name: str = Field(..., description="学生姓名") + native_place: Optional[str] = Field(None, description="籍贯") + graduate_school: Optional[str] = Field(None, description="毕业院校") + major: Optional[str] = Field(None, description="专业") + enroll_date: Optional[date] = Field(None, description="入学时间") + graduate_date: Optional[date] = Field(None, description="毕业时间") + education: Optional[str] = Field(None, description="学历") + advisor_no: Optional[str] = Field(None, description="顾问编号") + age: Optional[int] = Field(None, ge=0, description="年龄,不能负数") + gender: Optional[Sex] = Field(None, description="性别") +class StudentUpdateRequest(BaseModel): + stu_id:Optional[str]=None#学生id + class_id: Optional[str] = None # 学生班级 + stu_name: Optional[str] = None # 学生姓名 + native_place: Optional[str] = None # 籍贯 + graduate_school: Optional[str] = None # 毕业院校 + major: Optional[str] = None # 专业 + enroll_date: Optional[date] = None # 入学时间 + graduate_date: Optional[date] = None # 毕业时间 + education: Optional[str] = None # 学历 + advisor_no: Optional[str] = None # 顾问编号 + age: Optional[int] = Field(None, ge=0) # 年龄,不能负数 + gender: Optional[Sex] = Field(None, description="性别") # 性别''' +class StudentQuery(BaseModel): + stu_id: Optional[str] = None # 学生编号筛选 + stu_name: Optional[str] = None # 学生姓名筛选 + class_id: Optional[str] = None # 班级筛选 + page: int = Field(1, ge=1, description="页码,默认第1页") + page_size: int = Field(10, ge=1, le=100, description="每页条数,默认10条") +##=====================响应体=============== +class StudentResponse(BaseModel): + stu_id: str# 学生编号 + class_id: Optional[str]# 学生班级 + stu_name: str# 学生姓名 + native_place: Optional[str] # 籍贯 + graduate_school: Optional[str] # 毕业院校 + major: Optional[str]# 专业 + enroll_date: Optional[date] # 入学时间 + graduate_date: Optional[date] # 毕业时间 + education: Optional[str] # 学历 + advisor_no: Optional[str]# 顾问编号 + age: Optional[int]# 年龄 + gender: Optional[Sex] = Field(None, description="性别")# 性别 + is_deleted: bool# 逻辑删除标记 +# 分页列表响应体:查询学生列表时返回 +class StudentPageResponse(BaseModel): + total: int# 符合条件的总记录数 + page: int# 当前页码 + page_size: int# 每页数据条数 + items: List[StudentResponse] # 当前页学生数据列表 \ No newline at end of file