Compare commits
61
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f697a8b634 | ||
|
|
8b26d3b239 | ||
|
|
fc522c14c3 | ||
|
|
19298b5490 | ||
|
|
d2464c7536 | ||
|
|
5dc0c68ca2 | ||
|
|
1ff0782399 | ||
|
|
24a7da624b | ||
|
|
38b95eadf8 | ||
|
|
45dbd3e99c | ||
|
|
efb8e2ebc3 | ||
|
|
642b70dd1c | ||
|
|
b67e3cd547 | ||
|
|
0709b47bfd | ||
|
|
fcca2490c7 | ||
|
|
079a7cf351 | ||
|
|
2dca7c7f76 | ||
|
|
566747b320 | ||
|
|
699867b0af | ||
|
|
fa571a13da | ||
|
|
5fddb78c27 | ||
|
|
b5883e450d | ||
|
|
dc243fb33f | ||
|
|
6e189d0c65 | ||
|
|
f2e525b5a5 | ||
|
|
a1d7adba94 | ||
|
|
7d5f352c69 | ||
|
|
ffcb40db41 | ||
|
|
75f23e39be | ||
|
|
c1813ae700 | ||
|
|
65d67e7cf6 | ||
|
|
c0880f9a44 | ||
|
|
a4a55a9858 | ||
|
|
f5f9f43813 | ||
|
|
5e4a870547 | ||
|
|
62f4fea198 | ||
|
|
9c9e51d68f | ||
|
|
7f58136a9f | ||
|
|
6515bd811c | ||
|
|
a27c326e94 | ||
|
|
e76cee4bd6 | ||
|
|
ff68a676c4 | ||
|
|
ea9c85d7da | ||
|
|
336467b96a | ||
|
|
3b1a9d54a5 | ||
|
|
ba2155643b | ||
|
|
a571243415 | ||
|
|
dae4d74e31 | ||
|
|
ff03df7d8d | ||
|
|
8b50a337fa | ||
|
|
4604d3ba5f | ||
|
|
133cb530c7 | ||
|
|
98d08cff64 | ||
|
|
180e01e36d | ||
|
|
65bf147fd4 | ||
|
|
9c39c69fa1 | ||
|
|
b068ad6b29 | ||
|
|
9bc57bcbba | ||
|
|
d4d1d4fe2d | ||
|
|
c740efc022 | ||
|
|
5881afd5cc |
@@ -1,7 +0,0 @@
|
||||
# .env(放项目根目录,每个人自己建,不提交 Git)
|
||||
# 用来修改数据库账户密码
|
||||
DB_USER=root
|
||||
DB_PASSWORD=420821
|
||||
DB_HOST=localhost
|
||||
DB_PORT=3306
|
||||
DB_NAME=student_manage_system
|
||||
@@ -2,13 +2,12 @@
|
||||
|
||||
from fastapi import APIRouter , Depends , HTTPException
|
||||
from StatCalc.dao.statcalc_dao import *
|
||||
from StatCalc.model.statcalc_model import *
|
||||
from StatCalc.schema.statcalc_request import *
|
||||
from databases import *
|
||||
|
||||
statcalc_api = APIRouter()
|
||||
|
||||
@statcalc_api.get("/statcalc"
|
||||
@statcalc_api.get("/statcalc/age"
|
||||
,summary='人员信息查询'
|
||||
,description=f'查询所有超过30岁的学员的信息。'
|
||||
)
|
||||
@@ -18,118 +17,81 @@ def get_students(db=Depends(get_db)):
|
||||
return result
|
||||
raise HTTPException(status_code=404, detail="没有找到符合条件的学生")
|
||||
|
||||
@statcalc_api.get("/statcalc"
|
||||
@statcalc_api.get("/statcalc/students"
|
||||
,summary='人数统计'
|
||||
,description=f'统计每个班级的人数以及男生女生的人数。'
|
||||
)
|
||||
def get_students(g:get_student_dao = Depends()
|
||||
def get_students(headcount:get_student_dao
|
||||
,db=Depends(get_db)
|
||||
):
|
||||
result = get_class_count( g=g , db=db )
|
||||
result = get_class_count( g=headcount , db=db )
|
||||
if result:
|
||||
return result
|
||||
raise HTTPException(status_code=404, detail="没有找到符合条件的学生")
|
||||
|
||||
@statcalc_api.get("/statcalc"
|
||||
,summary='80分以上成绩学生信息查询'
|
||||
,description=f'查询每次考试成绩都在80分以上的学生的编号,姓名和成绩。'
|
||||
@statcalc_api.get("/statcalc/scores"
|
||||
,summary='优秀成绩学生信息查询'
|
||||
,description=f'查询所有科目考试成绩都在80分以上的学生。'
|
||||
)
|
||||
def get_students(db=Depends(get_db)):
|
||||
result = get_student_dao( db=db )
|
||||
def get_excellent(db=Depends(get_db)):
|
||||
result = get_excellent_students( db=db )
|
||||
if result:
|
||||
return result
|
||||
raise HTTPException(status_code=404, detail="没有找到符合条件的学生")
|
||||
|
||||
@statcalc_api.get("/statcalc"
|
||||
@statcalc_api.get("/statcalc/fail"
|
||||
,summary='不及格成绩查询'
|
||||
,description=f'查询有两次以上不及格的学生的姓名,班级和不及格成绩。'
|
||||
)
|
||||
def get_students( stu:StudentsQuery = Depends()
|
||||
, db=Depends(get_db)
|
||||
):
|
||||
s_dict = stu.model_dump(exclude_unset=True) # 没值的不要
|
||||
result = get_student_dao( s=s_dict , db=db )
|
||||
def get_students( db=Depends(get_db) ):
|
||||
result = get_score_80( db=db )
|
||||
if result:
|
||||
return result
|
||||
raise HTTPException(status_code=404, detail="没有找到符合条件的学生")
|
||||
|
||||
@statcalc_api.get("/statcalc"
|
||||
,summary='班级平均分查询'
|
||||
,description=f'统计每次考试每个班级的平均分,按照从高到低排序。'
|
||||
)
|
||||
def get_students( stu:StudentsQuery = Depends()
|
||||
, db=Depends(get_db)
|
||||
):
|
||||
s_dict = stu.model_dump(exclude_unset=True) # 没值的不要
|
||||
result = get_student_dao( s=s_dict , db=db )
|
||||
if result:
|
||||
return result
|
||||
raise HTTPException(status_code=404, detail="没有找到符合条件的学生")
|
||||
|
||||
@statcalc_api.get("/statcalc"
|
||||
,summary='排名前五的学生信息'
|
||||
,description=f'统计就业薪资最高的前五名学生的姓名,班级和就业时间,就业公司。'
|
||||
)
|
||||
def get_students( stu:StudentsQuery = Depends()
|
||||
, db=Depends(get_db)
|
||||
):
|
||||
s_dict = stu.model_dump(exclude_unset=True) # 没值的不要
|
||||
result = get_student_dao( s=s_dict , db=db )
|
||||
if result:
|
||||
return result
|
||||
raise HTTPException(status_code=404, detail="没有找到符合条件的学生")
|
||||
|
||||
@statcalc_api.get("/statcalc"
|
||||
,summary='就业时长统计'
|
||||
,description=f'统计每个学生的就业时长(offer下发时间-就业开放时间)。'
|
||||
)
|
||||
def get_students( stu:StudentsQuery = Depends()
|
||||
, db=Depends(get_db)
|
||||
):
|
||||
s_dict = stu.model_dump(exclude_unset=True) # 没值的不要
|
||||
result = get_student_dao( s=s_dict , db=db )
|
||||
if result:
|
||||
return result
|
||||
raise HTTPException(status_code=404, detail="没有找到符合条件的学生")
|
||||
|
||||
@statcalc_api.get("/statcalc"
|
||||
,summary='班级的平均就业时长'
|
||||
,description=f'统计每个班级的平均就业时长(只统计进入就业阶段的学生,也就是有就业开放时间)'
|
||||
)
|
||||
def get_students( stu:StudentsQuery = Depends()
|
||||
, db=Depends(get_db)
|
||||
):
|
||||
s_dict = stu.model_dump(exclude_unset=True) # 没值的不要
|
||||
result = get_student_dao( s=s_dict , db=db )
|
||||
if result:
|
||||
return result
|
||||
raise HTTPException(status_code=404, detail="没有找到符合条件的学生")
|
||||
@statcalc_api.get('/statcalc/average', summary='统计每次考试每个班级的平均分')
|
||||
def get_class_avg(db=Depends(get_db)):
|
||||
result = get_class_avg_dao(db)
|
||||
if not result:
|
||||
return []
|
||||
return result
|
||||
|
||||
|
||||
@CURD.get('/Top5',summary='就业薪资Top5')
|
||||
from stu_jiuye.model import *
|
||||
from students.model.students_model import Students
|
||||
from class_management.model.class_management_model import ClassInfo
|
||||
|
||||
@statcalc_api.get('/statcalc/top',summary='就业薪资Top5')
|
||||
def salarytop5(db=Depends(get_db)):
|
||||
q = db.query(Employments,Company.employment_company) \
|
||||
.join(Company, Employments.company_id == Company.id) \
|
||||
.filter(Employments.is_deleted == 0)\
|
||||
.order_by(Employments.employment_salary.desc())\
|
||||
.limit(5) .all()
|
||||
return [{'学生姓名':i.sname,'学生班级编号':i.class_num,'就业时间':i.offer_recived_time,'公司':employment_company} for i,employment_company in q]
|
||||
q = db.query(Employments, Company.employment_company, Students.name.label('sname'), ClassInfo.name.label('class_num')) \
|
||||
.join(Company, Employments.company_id == Company.id) \
|
||||
.join(Students, Employments.stuid == Students.id) \
|
||||
.join(ClassInfo, Employments.class_id == ClassInfo.id) \
|
||||
.filter(Employments.is_deleted == 0) \
|
||||
.order_by(Employments.employment_salary.desc()) \
|
||||
.limit(5).all()
|
||||
# q是4元组 (Employments对象, company_name, sname, class_num)
|
||||
return [{'学生姓名': row[2], '学生班级编号': row[3], '就业时间': str(row[0].offer_recived_time) if row[0].offer_recived_time else None, '公司': row[1]} for row in q]
|
||||
|
||||
@CURD.get('/worktime',summary='学生就业时常')
|
||||
@statcalc_api.get('/statcalc/job',summary='学生就业时长')
|
||||
def worktime(db=Depends(get_db)):
|
||||
q = db.query(Employments)\
|
||||
.filter(Employments.is_deleted==0)\
|
||||
.all()
|
||||
q = db.query(Employments, Students.name.label('sname')) \
|
||||
.join(Students, Employments.stuid == Students.id) \
|
||||
.filter(Employments.is_deleted == 0) \
|
||||
.all()
|
||||
|
||||
return [{'学生姓名':i.sname,'就业时常':f'{i.offer_recived_time-i.employment_open_time}' if i.offer_recived_time and i.employment_open_time else '暂无数据'} for i in q]
|
||||
return [
|
||||
{'学生姓名': row[1],
|
||||
'就业时常':f'{row[0].offer_recived_time-row[0].employment_open_time}'
|
||||
if row[0].offer_recived_time and row[0].employment_open_time else '暂无数据'} for row in q
|
||||
]
|
||||
|
||||
|
||||
@CURD.get('/avgworktime',summary='学生平均就业时常')
|
||||
@statcalc_api.get('/statcalc/avgjob',summary='学生平均就业时长')
|
||||
def avgworktime(db=Depends(get_db)):
|
||||
q = db.query(Employments)\
|
||||
q = db.query(Employments) \
|
||||
.filter(Employments.employment_open_time.isnot(None)
|
||||
,Employments.offer_recived_time.isnot(None)
|
||||
,Employments.is_deleted==0)\
|
||||
,Employments.is_deleted==0) \
|
||||
.all()
|
||||
if not q:
|
||||
return {'平均就业时常': '暂无数据'}
|
||||
@@ -139,3 +101,4 @@ def avgworktime(db=Depends(get_db)):
|
||||
'人数': len(q),
|
||||
'平均就业时常(天)': round(avg_days, 2)
|
||||
}
|
||||
|
||||
|
||||
@@ -17,32 +17,85 @@ from class_management.model.class_management_model import ClassInfo
|
||||
|
||||
def get_class_count(g , db):
|
||||
if g == "班级人数":
|
||||
q = (db.query(ClassInfo.id,ClassInfo.name,func.count(ClassInfo.id) )
|
||||
q = (db.query(ClassInfo.id,ClassInfo.name,func.count(ClassInfo.id).label('count') )
|
||||
.join(Students,Students.class_id==ClassInfo.id)
|
||||
.group_by(ClassInfo.id,ClassInfo.name,Students.sex)
|
||||
.all())
|
||||
elif g == "男女人数":
|
||||
q = (db.query(Students.sex,func.count(ClassInfo.id) )
|
||||
.join(Students,Students.class_id==ClassInfo.id)
|
||||
.group_by(Students.sex)
|
||||
.all())
|
||||
q = (db.query(Students.sex, func.count(Students.id).label('count'))
|
||||
.group_by(Students.sex)
|
||||
.all())
|
||||
elif g == "班级中男女人数":
|
||||
q = (db.query(ClassInfo.id,ClassInfo.name,Students.sex,func.count(ClassInfo.id) )
|
||||
q = (db.query(ClassInfo.id,ClassInfo.name,Students.sex,func.count(ClassInfo.id).label('count') )
|
||||
.join(Students,Students.class_id==ClassInfo.id)
|
||||
.group_by(ClassInfo.id,ClassInfo.name,Students.sex)
|
||||
.all())
|
||||
else:
|
||||
return False
|
||||
return q
|
||||
return [row._asdict() for row in q]
|
||||
|
||||
|
||||
from scores.model.score_model import Score
|
||||
|
||||
|
||||
def get_excellent_students(db):
|
||||
"""查询所有科目都在80分以上的优秀学生"""
|
||||
# 先查总科目数
|
||||
total_subjects = db.execute(text("SELECT COUNT(*) FROM subject")).scalar()
|
||||
|
||||
# 子查询:找出成绩>=80分的科目数等于总科目数的学生
|
||||
sq = db.query(Students.id) \
|
||||
.join(Score, Score.sid == Students.id) \
|
||||
.filter(Score.score >= 80) \
|
||||
.group_by(Students.id) \
|
||||
.having(func.count(Score.id) == total_subjects) \
|
||||
.subquery()
|
||||
|
||||
# 主查询:返回优秀学生信息
|
||||
q = db.query(Students.id, Students.name, ClassInfo.name.label('class_name'), Score.score) \
|
||||
.join(ClassInfo, ClassInfo.id == Students.class_id) \
|
||||
.join(Score, Score.sid == Students.id) \
|
||||
.filter(Students.id.in_(sq.select()), Score.score >= 80) \
|
||||
.all()
|
||||
return [row._asdict() for row in q]
|
||||
|
||||
|
||||
from scores.model.score_model import Score
|
||||
|
||||
def get_score_80(db):
|
||||
q = (db.query(Score)
|
||||
.filter(Score.score>=80)
|
||||
.all())
|
||||
return q
|
||||
"""查询有两次以上不及格(<60分)的学生"""
|
||||
# 子查询:找出不及格超过2门的学生ID
|
||||
sq = db.query(Students.id) \
|
||||
.join(Score, Score.sid == Students.id) \
|
||||
.filter(Score.score < 60) \
|
||||
.group_by(Students.id) \
|
||||
.having(func.count(Score.id) > 2) \
|
||||
.subquery()
|
||||
|
||||
# 主查询:返回不及格学生信息
|
||||
q = db.query(Students.name, ClassInfo.name.label('class_name'), Score.score) \
|
||||
.join(ClassInfo, ClassInfo.id == Students.class_id) \
|
||||
.join(Score, Score.sid == Students.id) \
|
||||
.filter(Students.id.in_(sq.select()), Score.score < 60) \
|
||||
.all()
|
||||
return [row._asdict() for row in q]
|
||||
|
||||
def get_class_avg_dao(db):
|
||||
results = (db.query(Score.num,
|
||||
Score.cid,
|
||||
ClassInfo.name.label("class_name"),
|
||||
func.avg(Score.score).label("avg_score"))
|
||||
.join(ClassInfo, Score.cid == ClassInfo.id)
|
||||
.filter(Score.is_deleted == 0)
|
||||
.group_by(Score.num, Score.cid, ClassInfo.name)
|
||||
.order_by(func.avg(Score.score).desc())
|
||||
.all())
|
||||
|
||||
return [ {"num": r.num,
|
||||
"cid": r.cid,
|
||||
"class_name": r.class_name,
|
||||
"avg_score": round(float(r.avg_score), 2),}
|
||||
for r in results]
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
from fastapi import FastAPI,APIRouter
|
||||
from StatCalc.api.statcalc_api import statcalc_api
|
||||
from databases import *
|
||||
from StatCalc.model import statcalc_model
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
StatCalc_API = APIRouter()
|
||||
|
||||
@@ -4,7 +4,7 @@ from pydantic import BaseModel , Field , field_validator
|
||||
from datetime import datetime
|
||||
import enum
|
||||
|
||||
class get_student_dao(enum.Enum):
|
||||
class get_student_dao(str , enum.Enum):
|
||||
get_class = "班级人数"
|
||||
get_sex = "男女人数"
|
||||
get_class_sex = "班级中男女人数"
|
||||
|
||||
@@ -33,7 +33,7 @@ is_delete、delete_time、phone\
|
||||
教师表\
|
||||
请求体:\
|
||||
1.查询、任意逻辑删除调用
|
||||
2.新增、更改时调用
|
||||
2.新增、更改时调用\
|
||||
响应体:用于返回给前端\
|
||||
科目表:\
|
||||
1.请求体:新增科目时调用
|
||||
+15
-8
@@ -15,8 +15,9 @@ def add_tea(tea: NewTeaRequest=Depends(),db=Depends(get_db)):
|
||||
if not r:
|
||||
raise HTTPException(status_code=500,detail='添加失败!')
|
||||
return TeaResponse(totals=1,data=tea.model_dump())
|
||||
|
||||
@tea_api.get('/tea',response_model=TeaResponse,summary='查询老师',description='任意选择输入的信息数量,不输入则查询全部老师')
|
||||
def get_tea(t:NewTeaRequest1=Depends(),db=Depends(get_db)): #开启会话窗口
|
||||
def get_tea(t:TeaRequest=Depends(),db=Depends(get_db)): #开启会话窗口
|
||||
r = get_tea_dao(t.model_dump(),db=db)
|
||||
if not r:
|
||||
raise HTTPException(status_code=404,detail='老师不存在')
|
||||
@@ -28,13 +29,19 @@ def delete_tea(id:int|None,db=Depends(get_db)):
|
||||
raise HTTPException(status_code=500,detail='删除失败!')
|
||||
return {'detail':'删除成功!'}
|
||||
|
||||
@tea_api.put('/tea/{id}',summary='更新老师信息',description='输入更改的信息,可以为空')
|
||||
def update_tea(id:int,tea:NewTeaRequest,db=Depends(get_db)):
|
||||
t = tea.model_dump(exclude_unset= True)
|
||||
r = update_tea_dao(id=id,t=t,db=db)
|
||||
if not r:
|
||||
raise HTTPException(status_code=404,detail='老师不存在')
|
||||
return {'detail':'更新成功'}
|
||||
@tea_api.put('/tea/{id}', summary='更新老师信息', description='输入更改的信息,可以为空')
|
||||
def update_tea(id: int, tea: NewTeaRequest = Depends(), db=Depends(get_db)):
|
||||
t = tea.model_dump(exclude_unset=True)
|
||||
print(t)
|
||||
r = update_tea_dao(id=id, t=t, db=db)
|
||||
if r == 'NOT_FOUND':
|
||||
raise HTTPException(status_code=404, detail='老师不存在')
|
||||
if r == 'NO_CHANGE':
|
||||
raise HTTPException(status_code=400, detail='没有要更新的内容')
|
||||
if isinstance(r, str) and r.startswith('ERROR'):
|
||||
raise HTTPException(status_code=400, detail=r)
|
||||
#先确认是字符串再调用字符串方法判断是不是以错误开头,是就报错给前端
|
||||
return {'detail': '更新成功'}
|
||||
|
||||
#根据任意信息删除
|
||||
@tea_api.delete('/tea',summary='根据任意信息逻辑删除',description='输入任意信息删除')
|
||||
|
||||
+21
-12
@@ -14,7 +14,9 @@ def add_tea_dao(t,db):
|
||||
if existing:
|
||||
for key,value in t.items():
|
||||
if hasattr(existing,key) and value is not None and value != '':
|
||||
#hasattr(existing,key)统计有没有key,返回值是True或False
|
||||
setattr(existing,key,value)
|
||||
#将value的值赋值到existing的key中,配合循环,让所有输入的信息都覆盖原信息
|
||||
existing.is_delete = False
|
||||
db.commit()
|
||||
return '恢复修改成功'
|
||||
@@ -29,15 +31,22 @@ def add_tea_dao(t,db):
|
||||
|
||||
|
||||
#更新
|
||||
def update_tea_dao(id,t, db):
|
||||
def update_tea_dao(id, t, db):
|
||||
try:
|
||||
row = db.query(Tea).filter(Tea.id == id,Tea.is_delete ==False ).update(t)
|
||||
except:
|
||||
db.rollback()
|
||||
return False
|
||||
else:
|
||||
if not t:
|
||||
return 'NO_CHANGE'
|
||||
obj = db.query(Tea).filter(Tea.id == id, Tea.is_delete == False).first()
|
||||
if not obj:
|
||||
return 'NOT_FOUND'
|
||||
for k, v in t.items():
|
||||
if hasattr(obj, k) and v is not None and v != '':
|
||||
setattr(obj, k, v)
|
||||
db.commit()
|
||||
return row
|
||||
return 'OK'
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
print('更新失败原因', repr(e))
|
||||
return f'ERROR: {e}'
|
||||
|
||||
#查询
|
||||
def get_tea_dao(t,db):
|
||||
@@ -50,8 +59,8 @@ def get_tea_dao(t,db):
|
||||
query = query.filter(Tea.sex == t.get('sex') )
|
||||
if t.get('class_id'):
|
||||
query = query.filter(Tea.class_id == t.get('class_id') )
|
||||
if t.get('sub'):
|
||||
query = query.filter(Tea.sub == t.get('sub') )
|
||||
if t.get('subject_id'):
|
||||
query = query.filter(Tea.subject_id == t.get('subject_id') )
|
||||
if t.get('home_place'):
|
||||
query = query.filter(Tea.home_place == t.get('home_place') )
|
||||
if t.get('specialty'):
|
||||
@@ -67,7 +76,7 @@ def get_tea_dao(t,db):
|
||||
if t.get('phone'):
|
||||
query = query.filter(Tea.phone == t.get('phone') )
|
||||
r = query.all()
|
||||
return [{'id':i.id,'name':i.name,'class_id':i.class_id,'sub':i.sub,'sex':i.sex,'home_place':i.home_place
|
||||
return [{'id':i.id,'name':i.name,'class_id':i.class_id,'sub':i.subject_id,'sex':i.sex,'home_place':i.home_place
|
||||
,'specialty':i.specialty,'start_time':i.start_time,'work_experience':i.work_experience
|
||||
,'collage':i.collage,'phone':i.phone} for i in r]
|
||||
|
||||
@@ -98,8 +107,8 @@ def delete_other_tea_dao(t,db):
|
||||
query1 = query1.filter(Tea.sex == t.get('sex'))
|
||||
if t.get('class_id'):
|
||||
query1 = query1.filter(Tea.class_id == t.get('class_id'))
|
||||
if t.get('sub'):
|
||||
query1 = query1.filter(Tea.sub == t.get('sub'))
|
||||
if t.get('subject_id'):
|
||||
query1 = query1.filter(Tea.subject_id == t.get('subject_id'))
|
||||
if t.get('home_place'):
|
||||
query1 = query1.filter(Tea.home_place == t.get('home_place'))
|
||||
if t.get('collage'):
|
||||
|
||||
+4
-4
@@ -9,13 +9,13 @@ Teachers_API.include_router(tea_api)
|
||||
|
||||
# 自测接口
|
||||
app = FastAPI()
|
||||
app.include_router(tea_api,tags=["老师"])
|
||||
|
||||
Base.metadata.create_all(engine1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.include_router(tea_api, tags=["老师"])
|
||||
Base.metadata.create_all(engine1)
|
||||
|
||||
import uvicorn
|
||||
uvicorn.run('main:app', host='0.0.0.0', port=57000)
|
||||
uvicorn.run('main:app', host='0.0.0.0', port=56000)
|
||||
"""
|
||||
IPv4 地址 . . . . . . . . . . . . : 192.168.1.51
|
||||
子网掩码 . . . . . . . . . . . . : 255.255.255.0
|
||||
|
||||
@@ -8,7 +8,7 @@ class TeaRequest(BaseModel):
|
||||
sex: Gender|None = None
|
||||
class_id: int | None = None
|
||||
subject_id: str|None = None
|
||||
phone: str | None
|
||||
phone: str | None = None
|
||||
home_place: str|None = None
|
||||
collage: str|None = None
|
||||
education: str|None = None
|
||||
|
||||
@@ -7,24 +7,52 @@ from class_management.schema.class_management_request import ClassCreate, ClassR
|
||||
class_api = APIRouter(prefix='/classes')
|
||||
# ,tags=['班级管理模块']
|
||||
|
||||
|
||||
#新增接口
|
||||
@class_api.post("",summary='新增班级')
|
||||
def add_class(cla:ClassCreate,db=Depends(get_db)):
|
||||
c_dict = cla.model_dump()
|
||||
r = add_class_dao(c=c_dict,db=db)
|
||||
if not r:
|
||||
raise HTTPException(status_code=500,detail='服务器繁忙,请稍后添加!')
|
||||
return ClassResponse(totals=1,data=c_dict)
|
||||
res = add_class_dao(c=c_dict,db=db)
|
||||
if not res:
|
||||
raise HTTPException(status_code=400,detail='新增失败,班级已存在')
|
||||
return ClassResponse(detail='新增成功',totals=1,data=c_dict)
|
||||
|
||||
|
||||
@class_api.get('/{id}',summary='根据id查询班级')
|
||||
def get_one_class(id:int,db=Depends(get_db)):
|
||||
res = get_class_dao(c={'id':id},db=db)
|
||||
#更新接口
|
||||
@class_api.put("/{class_id}",summary='修改班级信息')
|
||||
def update_class(class_id:int
|
||||
,update_info:ClassUpdate
|
||||
,db=Depends(get_db)
|
||||
):
|
||||
update_dict=update_info.model_dump(exclude_unset=True)
|
||||
if not update_dict:
|
||||
raise HTTPException(status_code=400, detail="没有传入要修改的字段")
|
||||
row_count = update_class_dao(cid=class_id, update_data=update_dict, db=db)
|
||||
if not row_count :
|
||||
raise HTTPException(status_code=404, detail="班级不存在,修改失败")
|
||||
return ClassResponse(detail="修改成功", data={"id": class_id})
|
||||
|
||||
|
||||
#删除接口
|
||||
@class_api.delete('/{class_id}',summary='逻辑删除班级')
|
||||
def remove_class(class_id:int,db=Depends(get_db)):
|
||||
del_rows = delete_class_dao(cid = class_id,db=db)
|
||||
if not del_rows:
|
||||
raise HTTPException(status_code=404, detail="班级不存在,删除失败")
|
||||
return ClassResponse(detail='删除成功',data={'id':class_id})
|
||||
|
||||
|
||||
|
||||
#单条查询接口
|
||||
@class_api.get('/{class_id}',summary='根据id查询班级')
|
||||
def get_one_class(class_id:int,db=Depends(get_db)):
|
||||
res = get_class_dao(c={'id':class_id},db=db)
|
||||
if not res:
|
||||
raise HTTPException(status_code=404, detail="该班级不存在或已删除")
|
||||
return ClassResponse(totals=1,data=res[0])
|
||||
|
||||
@class_api.get('',summary='多字段查询班级')
|
||||
|
||||
#分类查询
|
||||
@class_api.get('',summary='多字段分页查询班级')
|
||||
def get_some_classes( num:str|None = Query(None,description='班级编号')
|
||||
,name:str|None = Query(None,description='班级名称')
|
||||
,head_teacher_id:int|None = Query(None,description='班主任')
|
||||
@@ -33,10 +61,9 @@ def get_some_classes( num:str|None = Query(None,description='班级编号')
|
||||
,class_start_time:date|None = Query(None, description="开班日期,格式YYYY‑MM‑DD")
|
||||
,class_end_time: date|None = Query(None, description="结课日期,格式YYYY‑MM‑DD")
|
||||
,page:int = Query(1,ge=1,description='页码,从1开始')
|
||||
,page_size:int = Query(5,ge=1,le=50,description='每页信息条数')
|
||||
,page_size:int = Query(5,ge=1,le=20,description='每页信息条数')
|
||||
,db=Depends(get_db)
|
||||
):
|
||||
|
||||
c_dict = {
|
||||
'num': num,
|
||||
'name': name,
|
||||
@@ -52,7 +79,7 @@ def get_some_classes( num:str|None = Query(None,description='班级编号')
|
||||
res = get_class_dao(c=c_dict,db=db)
|
||||
total = len(res)
|
||||
|
||||
#内存分页
|
||||
#分页
|
||||
start_index = (page-1)*page_size
|
||||
page_data = res[start_index:start_index + page_size]
|
||||
|
||||
@@ -61,23 +88,3 @@ def get_some_classes( num:str|None = Query(None,description='班级编号')
|
||||
|
||||
|
||||
|
||||
@class_api.put("/{class_id}",summary='修改班级信息')
|
||||
def update_class(class_id:int
|
||||
,update_info:ClassUpdate
|
||||
,db=Depends(get_db)
|
||||
):
|
||||
update_dict=update_info.model_dump(exclude_unset=True)
|
||||
if not update_dict:
|
||||
raise HTTPException(status_code=400, detail="没有传入要修改的字段")
|
||||
row_count = update_class_dao(cid=class_id, update_data=update_dict, db=db)
|
||||
if row_count <= 0:
|
||||
raise HTTPException(status_code=404, detail="班级不存在,修改失败")
|
||||
return ClassResponse(detail="修改成功", data={"class_id": id})
|
||||
|
||||
|
||||
@class_api.delete('/{id}',summary='逻辑删除班级')
|
||||
def remove_class(id:int,db=Depends(get_db)):
|
||||
del_rows = delete_class_dao(id,db)
|
||||
if del_rows <= 0:
|
||||
raise HTTPException(status_code=404, detail="班级不存在,删除失败")
|
||||
return ClassResponse(detail='删除成功',data={'id':id})
|
||||
|
||||
@@ -1,6 +1,53 @@
|
||||
from class_management.model.class_management_model import ClassInfo
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
#新增班级
|
||||
def add_class_dao(c,db):
|
||||
try:
|
||||
exist_class = db.query(ClassInfo).filter(
|
||||
ClassInfo.num == c["num"],
|
||||
ClassInfo.is_delete == 0
|
||||
).first()
|
||||
if exist_class:
|
||||
return False
|
||||
c1 = ClassInfo(**c)
|
||||
db.add(c1)
|
||||
except:
|
||||
db.rollback()
|
||||
return False
|
||||
else:
|
||||
db.commit()
|
||||
db.refresh(c1)
|
||||
return True
|
||||
|
||||
|
||||
#更新班级信息
|
||||
def update_class_dao(cid,update_data,db):
|
||||
try:
|
||||
rows = db.query(ClassInfo)\
|
||||
.filter(ClassInfo.id == cid
|
||||
,ClassInfo.is_delete==0)\
|
||||
.update(update_data)
|
||||
except :
|
||||
db.rollback()
|
||||
return False
|
||||
else:
|
||||
db.commit()
|
||||
return rows
|
||||
|
||||
|
||||
#删除(逻辑删除)
|
||||
def delete_class_dao(cid,db):
|
||||
try:
|
||||
rows = db.query(ClassInfo)\
|
||||
.filter(ClassInfo.id == cid,ClassInfo.is_delete==0)\
|
||||
.update({'is_delete':1})
|
||||
db.commit()
|
||||
return rows
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
print('删除失败',e)
|
||||
return 0
|
||||
|
||||
|
||||
#查询
|
||||
@@ -34,64 +81,6 @@ def get_class_dao(c:dict,db:Session) :
|
||||
,'tutor_teacher_id':i.tutor_teacher_id
|
||||
,'class_start_time':i.class_start_time
|
||||
,'class_end_time':i.class_end_time
|
||||
,"is_delete": i.is_delete
|
||||
,"create_time": i.create_time
|
||||
,"update_time": i.update_time
|
||||
} for i in res
|
||||
]
|
||||
|
||||
|
||||
#新增班级
|
||||
def add_class_dao(c:dict,db:Session):
|
||||
try:
|
||||
exist_class = db.query(ClassInfo)\
|
||||
.filter(ClassInfo.name == c['name']
|
||||
,ClassInfo.is_delete == 0)\
|
||||
.first()
|
||||
if exist_class:
|
||||
msg = '班级名称已存在'
|
||||
print(msg)
|
||||
return (False,msg)
|
||||
c1 = ClassInfo(**c)
|
||||
db.add(c1)
|
||||
db.commit()
|
||||
db.refresh(c1)
|
||||
return (True,None)
|
||||
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
err_msg = f'数据库异常:{str(e)}'
|
||||
print(err_msg)
|
||||
return (False,err_msg)
|
||||
|
||||
|
||||
#修改
|
||||
def update_class_dao(cid:int,update_data,db):
|
||||
try:
|
||||
rows = db.query(ClassInfo)\
|
||||
.filter(ClassInfo.id == cid
|
||||
,ClassInfo.is_delete==0)\
|
||||
.update(update_data)
|
||||
db.commit()
|
||||
return rows
|
||||
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
print(f"update_class_dao异常:{e}")
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
#删除(逻辑删除)
|
||||
def delete_class_dao(cid,db):
|
||||
try:
|
||||
rows = db.query(ClassInfo)\
|
||||
.filter(ClassInfo.id == cid,ClassInfo.is_delete==0)\
|
||||
.update({'is_delete':1})
|
||||
db.commit()
|
||||
return rows
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
print(f"delete_class_dao异常:{e}")
|
||||
rows = 0
|
||||
|
||||
|
||||
@@ -11,14 +11,13 @@ DB_HOST = os.getenv("DB_HOST", "localhost")
|
||||
DB_PORT = os.getenv("DB_PORT", "3306")
|
||||
DB_NAME = os.getenv("DB_NAME", "student_manage_system")
|
||||
|
||||
class_management_rul = f"mysql+pymysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}?charset=utf8mb4"
|
||||
class_management_url = "mysql+pymysql://root:123456@localhost:3306/student_manage_system?charset=utf8mb4"
|
||||
|
||||
engine = create_engine(class_management_rul)
|
||||
engine = create_engine(class_management_url)
|
||||
SessionLocal = sessionmaker(bind=engine,autocommit=False, autoflush=False)
|
||||
Base = declarative_base()
|
||||
|
||||
# 获取数据库会话依赖
|
||||
|
||||
def get_db():
|
||||
db = SessionLocal()
|
||||
try :
|
||||
|
||||
@@ -3,17 +3,17 @@ from class_management.api.class_management_api import class_api
|
||||
from class_management.model.class_management_model import ClassInfo
|
||||
from class_management.database import Base,engine
|
||||
|
||||
|
||||
# 合并接口
|
||||
Classes_API = APIRouter()
|
||||
Classes_API.include_router(class_api)
|
||||
|
||||
|
||||
# 自测接口
|
||||
app = FastAPI()
|
||||
|
||||
app.include_router(class_api)
|
||||
|
||||
|
||||
|
||||
if __name__ =='__main__':
|
||||
Base.metadata.create_all(engine)
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ class ClassInfo(Base_all):
|
||||
)
|
||||
num = Column(String(50)
|
||||
,nullable=False
|
||||
,unique=True
|
||||
,comment='班级编号'
|
||||
)
|
||||
name = Column(String(50)
|
||||
@@ -21,26 +22,18 @@ class ClassInfo(Base_all):
|
||||
,comment='班级名称'
|
||||
)
|
||||
head_teacher_id = Column(Integer
|
||||
# ,ForeignKey('teacher.id')
|
||||
,ForeignKey('teachers.id')
|
||||
,comment='班主任编号'
|
||||
)
|
||||
|
||||
coach_teacher_id = Column(Integer
|
||||
# ,ForeignKey('teacher.id')
|
||||
,ForeignKey('teachers.id')
|
||||
,comment='授课老师编号'
|
||||
)
|
||||
tutor_teacher_id = Column(Integer
|
||||
# ,ForeignKey('teacher.id')
|
||||
,ForeignKey('teachers.id')
|
||||
,comment='助教老师编号'
|
||||
)
|
||||
# class_teacher = Column(String(50)
|
||||
# ,nullable=False
|
||||
# ,comment='班主任'
|
||||
# )
|
||||
# class_lecturer = Column(String(50)
|
||||
# ,nullable=False
|
||||
# ,comment='授课老师'
|
||||
# )
|
||||
class_start_time = Column(Date
|
||||
,comment='开班日期'
|
||||
)
|
||||
|
||||
@@ -12,17 +12,14 @@ class ClassBase(BaseModel):
|
||||
class_start_time:date|None = Field(None,description='开班日期 YYYY‑MM‑DD')
|
||||
class_end_time:date|None = Field(None,description='结课日期 YYYY‑MM‑DD')
|
||||
|
||||
#新增班级请求体:num,name必填,其余继承父类ClassBase
|
||||
#新增班级请求体:继承父类ClassBase
|
||||
class ClassCreate(ClassBase):
|
||||
num:str = Field(...,description='班级编号')
|
||||
name:str = Field(...,description='班级名称')
|
||||
pass
|
||||
|
||||
#修改班级请求体,继承ClassBase,全部字段可选,传哪个就更新哪个
|
||||
class ClassUpdate(ClassBase):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class ClassResponse(BaseModel):
|
||||
code:int = 200
|
||||
detail:str = 'OK'
|
||||
|
||||
+2118
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,246 @@
|
||||
"""
|
||||
测试数据初始化脚本
|
||||
使用纯SQL直接向数据库插入测试数据,不修改任何原有项目文件
|
||||
用法: python init_test_data.py
|
||||
"""
|
||||
from datetime import date
|
||||
from databases import engine_all, Session
|
||||
from sqlalchemy import text
|
||||
|
||||
Session_ = Session()
|
||||
|
||||
|
||||
def seed():
|
||||
print("=== 开始插入测试数据 ===\n")
|
||||
|
||||
# ── 1. 科目 ──────────────────────────────────────────
|
||||
subjects = ["Python程序设计", "数据结构", "数据库原理", "人工智能导论", "计算机网络", "操作系统"]
|
||||
sub_ids = {}
|
||||
for name in subjects:
|
||||
r = Session_.execute(text("SELECT id FROM subject WHERE name = :name"), {"name": name}).fetchone()
|
||||
if r:
|
||||
sub_ids[name] = r[0]
|
||||
else:
|
||||
result = Session_.execute(text("INSERT INTO subject (name) VALUES (:name)"), {"name": name})
|
||||
sub_ids[name] = result.lastrowid
|
||||
print(f" [科目] {name}")
|
||||
Session_.commit()
|
||||
|
||||
# ── 2. 教师 ──────────────────────────────────────────
|
||||
teachers = [
|
||||
("张伟", "m", sub_ids["Python程序设计"], 1, "北京", "Python", "博士", "清华", date(2018, 3, 1), "6年"),
|
||||
("李芳", "f", sub_ids["数据结构"], 1, "上海", "算法", "博士", "北大", date(2019, 9, 1), "4年"),
|
||||
("王强", "m", sub_ids["数据库原理"], 2, "广州", "MySQL", "硕士", "中山大学", date(2020, 2, 1), "3年"),
|
||||
("陈静", "f", sub_ids["人工智能导论"], 2, "深圳", "机器学习", "博士", "华南理工", date(2017, 7, 1), "7年"),
|
||||
("刘洋", "m", sub_ids["计算机网络"], 3, "杭州", "网络", "硕士", "浙江大学", date(2021, 1, 1), "2年"),
|
||||
("赵敏", "f", sub_ids["操作系统"], 3, "南京", "Linux", "博士", "南京大学", date(2019, 3, 1), "5年"),
|
||||
]
|
||||
teacher_ids = []
|
||||
for t in teachers:
|
||||
phone = f"1380000{len(teacher_ids):04d}"
|
||||
r = Session_.execute(
|
||||
text("SELECT id FROM teachers WHERE phone = :phone AND is_delete = 0"),
|
||||
{"phone": phone}
|
||||
).fetchone()
|
||||
if r:
|
||||
teacher_ids.append(r[0])
|
||||
else:
|
||||
result = Session_.execute(text("""
|
||||
INSERT INTO teachers (name, sex, class_id, phone, subject_id, home_place, specialty,
|
||||
education, collage, start_time, work_experience, is_delete)
|
||||
VALUES (:name, :sex, :class_id, :phone, :subject_id, :home_place, :specialty,
|
||||
:education, :collage, :start_time, :work_experience, 0)
|
||||
"""), {
|
||||
"name": t[0], "sex": t[1], "class_id": t[3], "phone": phone,
|
||||
"subject_id": t[2], "home_place": t[4], "specialty": t[5],
|
||||
"education": t[6], "collage": t[7], "start_time": t[8], "work_experience": t[9],
|
||||
})
|
||||
teacher_ids.append(result.lastrowid)
|
||||
print(f" [教师] {t[0]} ({t[1]}) - {t[5]}")
|
||||
Session_.commit()
|
||||
|
||||
# ── 3. 班级 ──────────────────────────────────────────
|
||||
classes = [
|
||||
("CS202401", "计算机科学2401班", teacher_ids[0], teacher_ids[1], teacher_ids[2], date(2024, 9, 1), date(2028, 6, 30)),
|
||||
("CS202402", "计算机科学2402班", teacher_ids[3], teacher_ids[4], teacher_ids[5], date(2024, 9, 1), date(2028, 6, 30)),
|
||||
("AI202501", "人工智能2501班", teacher_ids[0], teacher_ids[3], teacher_ids[5], date(2025, 9, 1), date(2029, 6, 30)),
|
||||
]
|
||||
class_ids = []
|
||||
for c in classes:
|
||||
r = Session_.execute(
|
||||
text("SELECT id FROM classes WHERE num = :num AND is_delete = 0"),
|
||||
{"num": c[0]}
|
||||
).fetchone()
|
||||
if r:
|
||||
class_ids.append(r[0])
|
||||
else:
|
||||
result = Session_.execute(text("""
|
||||
INSERT INTO classes (num, name, head_teacher_id, coach_teacher_id, tutor_teacher_id,
|
||||
class_start_time, class_end_time, is_delete)
|
||||
VALUES (:num, :name, :head_teacher_id, :coach_teacher_id, :tutor_teacher_id,
|
||||
:class_start_time, :class_end_time, 0)
|
||||
"""), {
|
||||
"num": c[0], "name": c[1], "head_teacher_id": c[2],
|
||||
"coach_teacher_id": c[3], "tutor_teacher_id": c[4],
|
||||
"class_start_time": c[5], "class_end_time": c[6],
|
||||
})
|
||||
class_ids.append(result.lastrowid)
|
||||
print(f" [班级] {c[1]}")
|
||||
Session_.commit()
|
||||
|
||||
# ── 4. 学生 ──────────────────────────────────────────
|
||||
first_names = ["志强", "丽华", "建国", "美玲", "文博", "晓燕", "浩然", "雨桐", "子轩", "思涵",
|
||||
"俊杰", "雅琪", "宇航", "欣怡", "天翊", "诗涵", "一诺", "浩宇", "梓萱", "博文"]
|
||||
home_places = ["北京", "上海", "广州", "深圳", "杭州", "成都", "武汉", "西安"]
|
||||
colleges = ["清华大学", "北京大学", "复旦大学", "上海交通大学", "浙江大学", "南京大学", "武汉大学", "华中科技大学"]
|
||||
specialties = ["计算机科学", "软件工程", "数据科学", "人工智能"]
|
||||
student_count = 0
|
||||
for i in range(20):
|
||||
class_idx = i % 3
|
||||
enrollment = date(2024, 9, 1) if class_idx != 2 else date(2025, 9, 1)
|
||||
graduate = date(2028, 6, 30) if class_idx != 2 else date(2029, 6, 30)
|
||||
phone = f"1390000{1000+i}"
|
||||
r = Session_.execute(
|
||||
text("SELECT id FROM students WHERE phone = :phone AND is_deleted = 0"),
|
||||
{"phone": phone}
|
||||
).fetchone()
|
||||
if r:
|
||||
continue
|
||||
Session_.execute(text("""
|
||||
INSERT INTO students (num, name, age, sex, home_Place, college, specialty,
|
||||
enrollment_time, graduate_time, education,
|
||||
class_id, advisor_id, phone, is_deleted)
|
||||
VALUES (:num, :name, :age, :sex, :home_Place, :college, :specialty,
|
||||
:enrollment_time, :graduate_time, '本科',
|
||||
:class_id, :advisor_id, :phone, 0)
|
||||
"""), {
|
||||
"num": f"2024{1001+i}",
|
||||
"name": first_names[i],
|
||||
"age": 18 + (i % 4),
|
||||
"sex": "男" if i % 2 == 0 else "女",
|
||||
"home_Place": home_places[i % 8],
|
||||
"college": colleges[i % 8],
|
||||
"specialty": specialties[i % 4],
|
||||
"enrollment_time": enrollment,
|
||||
"graduate_time": graduate,
|
||||
"class_id": class_ids[class_idx],
|
||||
"advisor_id": teacher_ids[i % 6],
|
||||
"phone": phone,
|
||||
})
|
||||
student_count += 1
|
||||
print(f" [学生] {first_names[i]} (2024{1001+i}) - {'男' if i%2==0 else '女'} - 班级{class_idx+1}")
|
||||
Session_.commit()
|
||||
|
||||
# ── 5. 成绩 ──────────────────────────────────────────
|
||||
all_students = Session_.execute(
|
||||
text("SELECT id, class_id FROM students WHERE is_deleted = 0")
|
||||
).fetchall()
|
||||
all_subjects = Session_.execute(text("SELECT id FROM subject")).fetchall()
|
||||
score_count = 0
|
||||
for idx, st in enumerate(all_students):
|
||||
for subj_idx, sub in enumerate(all_subjects):
|
||||
score_val = 60 + (idx * 7 + subj_idx * 13) % 41
|
||||
# 让前3名学生部分课程不及格(用于统计接口测试)
|
||||
if idx < 3 and subj_idx < 2:
|
||||
score_val = 30 + (idx * 5) % 25
|
||||
existing = Session_.execute(
|
||||
text("SELECT id FROM scores WHERE sid = :sid AND cid = :cid AND num = 1 AND t_subject = :t_subject AND is_deleted = 0"),
|
||||
{"sid": st[0], "cid": st[1], "t_subject": sub[0]}
|
||||
).fetchone()
|
||||
if not existing:
|
||||
Session_.execute(text("""
|
||||
INSERT INTO scores (sid, cid, num, score, t_subject, is_deleted)
|
||||
VALUES (:sid, :cid, 1, :score, :t_subject, 0)
|
||||
"""), {"sid": st[0], "cid": st[1], "score": score_val, "t_subject": sub[0]})
|
||||
score_count += 1
|
||||
print(f" [成绩] 为 {len(all_students)} 名学生插入了 {score_count} 条成绩记录")
|
||||
Session_.commit()
|
||||
|
||||
# ── 6. 公司地址 ────────────────────────────────────────
|
||||
addresses = ["北京市海淀区", "上海市浦东新区", "深圳市南山区", "杭州市西湖区", "广州市天河区"]
|
||||
address_ids = []
|
||||
for addr in addresses:
|
||||
r = Session_.execute(
|
||||
text("SELECT id FROM Address WHERE somewhere = :where"),
|
||||
{"where": addr}
|
||||
).fetchone()
|
||||
if r:
|
||||
address_ids.append(r[0])
|
||||
else:
|
||||
result = Session_.execute(
|
||||
text("INSERT INTO Address (somewhere) VALUES (:where)"),
|
||||
{"where": addr}
|
||||
)
|
||||
address_ids.append(result.lastrowid)
|
||||
print(f" [地址] {addr}")
|
||||
Session_.commit()
|
||||
|
||||
# ── 7. 公司 ────────────────────────────────────────────
|
||||
companies = [
|
||||
("字节跳动", address_ids[2]),
|
||||
("阿里巴巴", address_ids[3]),
|
||||
("腾讯", address_ids[2]),
|
||||
("华为", address_ids[4]),
|
||||
("百度", address_ids[0]),
|
||||
]
|
||||
company_ids = []
|
||||
for comp in companies:
|
||||
r = Session_.execute(
|
||||
text("SELECT id FROM Company WHERE employment_company = :name"),
|
||||
{"name": comp[0]}
|
||||
).fetchone()
|
||||
if r:
|
||||
company_ids.append(r[0])
|
||||
else:
|
||||
result = Session_.execute(
|
||||
text("INSERT INTO Company (employment_company, address_id) VALUES (:name, :addr_id)"),
|
||||
{"name": comp[0], "addr_id": comp[1]}
|
||||
)
|
||||
company_ids.append(result.lastrowid)
|
||||
print(f" [公司] {comp[0]}")
|
||||
Session_.commit()
|
||||
|
||||
# ── 8. 就业信息 ────────────────────────────────────────
|
||||
employment_data = [
|
||||
(all_students[0][0], all_students[0][1], company_ids[0], 35000, date(2028, 3, 1), date(2028, 4, 15)),
|
||||
(all_students[1][0], all_students[1][1], company_ids[1], 32000, date(2028, 3, 1), date(2028, 5, 1)),
|
||||
(all_students[2][0], all_students[2][1], company_ids[2], 30000, date(2028, 3, 1), date(2028, 4, 20)),
|
||||
(all_students[3][0], all_students[3][1], company_ids[3], 28000, date(2028, 3, 1), date(2028, 5, 10)),
|
||||
(all_students[4][0], all_students[4][1], company_ids[4], 25000, date(2028, 3, 1), date(2028, 6, 1)),
|
||||
(all_students[5][0], all_students[5][1], company_ids[0], 33000, date(2028, 3, 1), date(2028, 4, 25)),
|
||||
(all_students[6][0], all_students[6][1], company_ids[1], 29000, date(2028, 3, 1), None),
|
||||
(all_students[7][0], all_students[7][1], company_ids[2], 31000, date(2028, 3, 1), date(2028, 5, 5)),
|
||||
]
|
||||
emp_count = 0
|
||||
for emp in employment_data:
|
||||
r = Session_.execute(
|
||||
text("SELECT id FROM Employments WHERE stuid = :stuid AND is_deleted = 0"),
|
||||
{"stuid": emp[0]}
|
||||
).fetchone()
|
||||
if not r:
|
||||
Session_.execute(text("""
|
||||
INSERT INTO Employments (stuid, class_id, company_id, employment_salary,
|
||||
employment_open_time, offer_recived_time, is_deleted)
|
||||
VALUES (:stuid, :class_id, :company_id, :salary, :open_time, :offer_time, 0)
|
||||
"""), {
|
||||
"stuid": emp[0], "class_id": emp[1], "company_id": emp[2],
|
||||
"salary": emp[3], "open_time": emp[4], "offer_time": emp[5],
|
||||
})
|
||||
emp_count += 1
|
||||
print(f" [就业] 插入 {emp_count} 条就业记录")
|
||||
Session_.commit()
|
||||
|
||||
# ── 完成 ───────────────────────────────────────────────
|
||||
Session_.close()
|
||||
print("\n=== 测试数据初始化完成!共插入:")
|
||||
print(f" • 科目: {len(subjects)} 门")
|
||||
print(f" • 教师: {len(teachers)} 位")
|
||||
print(f" • 班级: {len(classes)} 个")
|
||||
print(f" • 学生: {student_count} 名")
|
||||
print(f" • 成绩: {score_count} 条")
|
||||
print(f" • 公司: {len(companies)} 家")
|
||||
print(f" • 就业: {emp_count} 条")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
seed()
|
||||
@@ -4,25 +4,38 @@ from scores.main import Scores_API
|
||||
from stu_jiuye.main import Shtudent_Jiuye
|
||||
from students.main import Students_API
|
||||
from Teachers.main import Teachers_API
|
||||
# from StatCalc.main import StatCalc_API
|
||||
from StatCalc.main import StatCalc_API
|
||||
from databases import *
|
||||
from seed_data import seed_test_data
|
||||
app = FastAPI(title="学生管理系统")
|
||||
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
|
||||
app.include_router(Classes_API,tags=["classes"])
|
||||
app.include_router(Scores_API,tags=["scores"])
|
||||
app.include_router(Shtudent_Jiuye,tags=["Employments"])
|
||||
app.include_router(Students_API,tags=["students"])
|
||||
app.include_router(Teachers_API,tags=["teachers"])
|
||||
# app.include_router(StatCalc_API,tags=["StatCalc"])
|
||||
# 配置CORS - 接前端
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"], # 允许所有来源
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"], # 允许所有方法
|
||||
allow_headers=["*"], # 允许所有头部
|
||||
)
|
||||
|
||||
app.include_router(Classes_API,tags=["班级管理模块"])
|
||||
app.include_router(Scores_API,tags=["学生考核成绩管理模块"])
|
||||
app.include_router(Shtudent_Jiuye,tags=["学生就业管理模块"])
|
||||
app.include_router(Students_API,tags=["学生管理模块"])
|
||||
app.include_router(Teachers_API,tags=["老师管理模块"])
|
||||
app.include_router(StatCalc_API,tags=["统计分析模块"])
|
||||
|
||||
Base_all.metadata.create_all(bind=engine_all)
|
||||
|
||||
if __name__ == "__main__":
|
||||
Base_all.metadata.create_all(bind=engine_all)
|
||||
|
||||
def main():
|
||||
print("Hello from student-manage-system!")
|
||||
|
||||
seed_test_data()
|
||||
|
||||
import uvicorn
|
||||
uvicorn.run("main:app", host="0.0.0.0", port=59000)
|
||||
"""
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
#scores模块解释
|
||||
|
||||
##项目层
|
||||
web框架:FastAPI(生成接口)
|
||||
ORM:sqlalchemy
|
||||
校验:MYSQL(通过PyMysql驱动)
|
||||
运行:uvicorn,端口12346
|
||||
|
||||
用FastAPI显露HTTP接口 ,对MysQL的scores表进行一个增,删,改,查的动作,删除是软删除,
|
||||
api是接口层,成绩信息的增加,删除,更新,查询
|
||||
dao是数据操作层,查询get_scores_dao
|
||||
更新add_scores_dao
|
||||
增加update_scores_dao
|
||||
删除delete_scores_dao
|
||||
统计查询80分以上的get_top_students_dao
|
||||
查询连续两次不及格的get_failing_students_dao
|
||||
班级平均分get_class_avg_dao
|
||||
model是数据库表模型层,字段信息 id *pk*
|
||||
sid 链接学生表的学生ID
|
||||
cid 链接班级表的班级ID
|
||||
num 考试序次
|
||||
score 考试成绩
|
||||
t_subject 链接科目表的科目ID
|
||||
create_date 创建数据的时间
|
||||
update_date 更新数据的是时间
|
||||
is_deleted 逻辑删除
|
||||
deleted_date 删除的时间
|
||||
schema是请求体和响应体模型层, 查询的ScoreQuery
|
||||
更新,增加,删除的ScoreRequest
|
||||
databases是数据库定义配置文件, 链接引擎对象db_url,链接地址engine,
|
||||
建表的基类Base,数据库会话管理Session
|
||||
会话实例def get_db()
|
||||
main是主程序文件,将配置文件的接口导入,创建接口运行
|
||||
@@ -37,7 +37,7 @@ def update_scores(score:ScoreRequest,sid:int,cid:int,num:int,t_subject:int,db=De
|
||||
@score_api.delete('/Score',summary='成绩删除',description=f'查询条件为学号,班级号,考试科目,然后进行删除')
|
||||
def delete_scores(sid:int,cid:int,t_subject:int,db=Depends(get_db)):
|
||||
aaa = delete_scores_dao(sid,cid,t_subject,db)
|
||||
if aaa==1:
|
||||
if aaa==-1:
|
||||
raise HTTPException(status_code=500,detail='删除失败')
|
||||
if aaa==0:
|
||||
raise HTTPException(status_code=404,detail='记录不存在')
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from scores.database import get_db
|
||||
from scores.dao.statistic_dao import get_top_students_dao,get_failing_students_dao,get_class_avg_dao
|
||||
|
||||
statistic_api = APIRouter()
|
||||
|
||||
@statistic_api.get('/statistic', summary='查询每次考试都在80分以上的学生')
|
||||
def get_top_students(db=Depends(get_db)):
|
||||
q = get_top_students_dao(db)
|
||||
if not q:
|
||||
return []
|
||||
return q
|
||||
|
||||
@statistic_api.get('/Score/failing', summary='查询两次以上不及格的学生')
|
||||
def get_failing_students(db=Depends(get_db)):
|
||||
result = get_failing_students_dao(db)
|
||||
if not result:
|
||||
return []
|
||||
return result
|
||||
|
||||
@statistic_api.get('/Score/class-avg', summary='统计每次考试每个班级的平均分')
|
||||
def get_class_avg(db=Depends(get_db)):
|
||||
result = get_class_avg_dao(db)
|
||||
if not result:
|
||||
return []
|
||||
return result
|
||||
@@ -19,7 +19,7 @@ def get_scores_dao(s,db):
|
||||
if aa:
|
||||
return [{'id':i.id,'sid':i.sid
|
||||
,'cid':i.cid,'num':i.num
|
||||
,'score': i.score,'t_subject':i.tsub
|
||||
,'score': i.score,'t_subject':i.t_subject
|
||||
,'create_date':i.create_date,'update_date':i.update_date
|
||||
,'is_deleted': i.is_deleted,'deleted_date': i.deleted_date
|
||||
}for i in aa]
|
||||
@@ -28,8 +28,7 @@ def get_scores_dao(s,db):
|
||||
|
||||
def add_scores_dao(b,db):
|
||||
try:
|
||||
aa = db.query(Score).filter(Score.sid==b['sid']
|
||||
,Score.t_subject==b['t_subject']).all()
|
||||
aa = db.query(Score).filter(Score.t_subject==b['t_subject']).all()
|
||||
if aa:
|
||||
raise ValueError
|
||||
else:
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
from sqlalchemy import func
|
||||
from scores.model.score_model import Score
|
||||
from students.model.students_model import Students
|
||||
from class_management.model.class_management_model import ClassInfo
|
||||
|
||||
def get_top_students_dao(db):
|
||||
top_sids = (db.query(Score.sid)
|
||||
.filter(Score.is_deleted == 0)
|
||||
.group_by(Score.sid, Score.num)
|
||||
.having(func.min(Score.score) >= 80)
|
||||
.subquery() )
|
||||
|
||||
results = (db.query(Students.id,
|
||||
Students.name,
|
||||
Score.num,
|
||||
Score.t_subject,
|
||||
Score.score,)
|
||||
.join(Score, Students.id == Score.sid)
|
||||
.filter( Score.sid.in_(top_sids),
|
||||
Score.is_deleted == 0,)
|
||||
.order_by(Students.id, Score.num)
|
||||
.all())
|
||||
|
||||
return [{"sid": r.sid,
|
||||
"name": r.name,
|
||||
"score": r.score,}
|
||||
for r in results]
|
||||
|
||||
|
||||
def get_failing_students_dao(db):
|
||||
failing_sids = (db.query(Score.sid)
|
||||
.filter(Score.score < 60,
|
||||
Score.is_deleted == 0,)
|
||||
.group_by(Score.sid)
|
||||
.having(func.count() >= 2)
|
||||
.subquery())
|
||||
|
||||
results = (db.query(
|
||||
Students.id,
|
||||
Students.name,
|
||||
ClassInfo.name.label("class_name"),
|
||||
Score.num,
|
||||
Score.t_subject,
|
||||
Score.score,)
|
||||
.join(Score, Students.id == Score.sid)
|
||||
.join(ClassInfo, Score.cid == ClassInfo.id)
|
||||
.filter(Score.sid.in_(failing_sids),
|
||||
Score.score < 60,
|
||||
Score.is_deleted == 0,)
|
||||
.order_by(ClassInfo.id, Score.num)
|
||||
.all())
|
||||
|
||||
return [{"sid": r.sid,
|
||||
"name": r.name,
|
||||
"class_name": r.class_name,
|
||||
"num": r.num,
|
||||
"t_subject": r.tsub,
|
||||
"score": r.score,}
|
||||
for r in results]
|
||||
|
||||
|
||||
|
||||
def get_class_avg_dao(db):
|
||||
results = (db.query(Score.num,
|
||||
Score.cid,
|
||||
ClassInfo.name.label("class_name"),
|
||||
func.avg(Score.score).label("avg_score"))
|
||||
.join(ClassInfo, Score.cid == ClassInfo.id)
|
||||
.filter(Score.is_deleted == 0)
|
||||
.group_by(Score.num, Score.cid, ClassInfo.name)
|
||||
.order_by(func.avg(Score.score).desc())
|
||||
.all())
|
||||
|
||||
return [ {"num": r.num,
|
||||
"cid": r.cid,
|
||||
"class_name": r.class_name,
|
||||
"avg_score": round(float(r.avg_score), 2),}
|
||||
for r in results]
|
||||
+7
-3
@@ -1,17 +1,21 @@
|
||||
from fastapi import FastAPI,APIRouter
|
||||
from scores.api.score_api import score_api
|
||||
from scores.database import engine,Base
|
||||
from scores.api.statistic_api import statistic_api
|
||||
|
||||
# 合并接口
|
||||
Scores_API = APIRouter()
|
||||
Scores_API.include_router(score_api)
|
||||
|
||||
# 自测接口
|
||||
app = FastAPI(tags=['成绩'],title='成绩管理系统')
|
||||
app = FastAPI(tags=['成绩'], title='成绩管理系统')
|
||||
app.include_router(score_api)
|
||||
app.include_router(statistic_api)
|
||||
|
||||
Base.metadata.create_all(engine)
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
Base.metadata.create_all(engine)
|
||||
|
||||
import uvicorn
|
||||
uvicorn.run('main:app',host='127.0.0.1',port=12346)
|
||||
uvicorn.run('scores.main:app',host='127.0.0.1',port=12346)
|
||||
+260
@@ -0,0 +1,260 @@
|
||||
"""
|
||||
种子数据初始化模块
|
||||
在 main.py 启动时自动调用,向数据库注入测试数据
|
||||
幂等设计:已存在数据则跳过,不重复插入
|
||||
"""
|
||||
from datetime import date
|
||||
from sqlalchemy import text
|
||||
from databases import engine_all, Base_all, Session
|
||||
|
||||
|
||||
def seed_test_data():
|
||||
"""启动时自动注入测试数据(幂等)"""
|
||||
db = Session()
|
||||
try:
|
||||
# 临时关闭外键检查,避免 class_management_model.py 的 FK bug
|
||||
db.execute(text("SET FOREIGN_KEY_CHECKS = 0"))
|
||||
|
||||
# 导入所有模型以获取 metadata(触发 Base_all.metadata.create_all)
|
||||
import students.model.students_model
|
||||
import Teachers.model.tea_model
|
||||
import scores.model.score_model
|
||||
import stu_jiuye.model
|
||||
|
||||
# 创建所有表(临时关闭外键检查)
|
||||
db.execute(text("SET FOREIGN_KEY_CHECKS = 0"))
|
||||
Base_all.metadata.create_all(bind=engine_all)
|
||||
|
||||
# 手动建 classes 表(因 FK bug 无法通过 ORM 建表)
|
||||
db.execute(text("""
|
||||
CREATE TABLE IF NOT EXISTS classes (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
num VARCHAR(50) NOT NULL UNIQUE,
|
||||
name VARCHAR(50) NOT NULL,
|
||||
head_teacher_id INT,
|
||||
coach_teacher_id INT,
|
||||
tutor_teacher_id INT,
|
||||
class_start_time DATE,
|
||||
class_end_time DATE,
|
||||
is_delete INT DEFAULT 0,
|
||||
create_time DATETIME,
|
||||
update_time DATETIME,
|
||||
FOREIGN KEY (head_teacher_id) REFERENCES teachers(id),
|
||||
FOREIGN KEY (coach_teacher_id) REFERENCES teachers(id),
|
||||
FOREIGN KEY (tutor_teacher_id) REFERENCES teachers(id)
|
||||
)
|
||||
"""))
|
||||
|
||||
# 恢复外键检查
|
||||
db.execute(text("SET FOREIGN_KEY_CHECKS = 1"))
|
||||
db.commit()
|
||||
|
||||
# 幂等检查:科目表有数据就跳过全部
|
||||
if db.execute(text("SELECT COUNT(*) FROM subject")).scalar() > 0:
|
||||
return
|
||||
|
||||
print(" [种子] 开始注入测试数据...")
|
||||
|
||||
# ── 1. 科目 ──────────────────────────────────────
|
||||
subjects = ["Python程序设计", "数据结构", "数据库原理",
|
||||
"人工智能导论", "计算机网络", "操作系统"]
|
||||
sub_ids = {}
|
||||
for name in subjects:
|
||||
result = db.execute(
|
||||
text("INSERT INTO subject (name) VALUES (:name)"),
|
||||
{"name": name}
|
||||
)
|
||||
sub_ids[name] = result.lastrowid
|
||||
|
||||
# ── 2. 教师 ──────────────────────────────────────
|
||||
teachers = [
|
||||
("张伟", "m", 1, sub_ids["Python程序设计"],
|
||||
"北京", "Python", "博士", "清华", date(2018, 3, 1), "6年"),
|
||||
("李芳", "f", 1, sub_ids["数据结构"],
|
||||
"上海", "算法", "博士", "北大", date(2019, 9, 1), "4年"),
|
||||
("王强", "m", 2, sub_ids["数据库原理"],
|
||||
"广州", "MySQL", "硕士", "中山大学", date(2020, 2, 1), "3年"),
|
||||
("陈静", "f", 2, sub_ids["人工智能导论"],
|
||||
"深圳", "机器学习", "博士", "华南理工", date(2017, 7, 1), "7年"),
|
||||
("刘洋", "m", 3, sub_ids["计算机网络"],
|
||||
"杭州", "网络", "硕士", "浙江大学", date(2021, 1, 1), "2年"),
|
||||
("赵敏", "f", 3, sub_ids["操作系统"],
|
||||
"南京", "Linux", "博士", "南京大学", date(2019, 3, 1), "5年"),
|
||||
]
|
||||
teacher_ids = []
|
||||
for t in teachers:
|
||||
phone = f"1380000{len(teacher_ids):04d}"
|
||||
result = db.execute(text("""
|
||||
INSERT INTO teachers (name, sex, class_id, phone, subject_id,
|
||||
home_place, specialty, education, collage,
|
||||
start_time, work_experience, is_delete)
|
||||
VALUES (:name, :sex, :class_id, :phone, :subject_id,
|
||||
:home_place, :specialty, :education, :collage,
|
||||
:start_time, :work_experience, 0)
|
||||
"""), {
|
||||
"name": t[0], "sex": t[1], "class_id": t[2],
|
||||
"phone": phone, "subject_id": t[3],
|
||||
"home_place": t[4], "specialty": t[5],
|
||||
"education": t[6], "collage": t[7],
|
||||
"start_time": t[8], "work_experience": t[9],
|
||||
})
|
||||
teacher_ids.append(result.lastrowid)
|
||||
|
||||
# ── 3. 班级 ──────────────────────────────────────
|
||||
classes = [
|
||||
("CS202401", "计算机科学2401班", teacher_ids[0],
|
||||
teacher_ids[1], teacher_ids[2],
|
||||
date(2024, 9, 1), date(2028, 6, 30)),
|
||||
("CS202402", "计算机科学2402班", teacher_ids[3],
|
||||
teacher_ids[4], teacher_ids[5],
|
||||
date(2024, 9, 1), date(2028, 6, 30)),
|
||||
("AI202501", "人工智能2501班", teacher_ids[0],
|
||||
teacher_ids[3], teacher_ids[5],
|
||||
date(2025, 9, 1), date(2029, 6, 30)),
|
||||
]
|
||||
class_ids = []
|
||||
for c in classes:
|
||||
result = db.execute(text("""
|
||||
INSERT INTO classes (num, name, head_teacher_id, coach_teacher_id,
|
||||
tutor_teacher_id, class_start_time,
|
||||
class_end_time, is_delete)
|
||||
VALUES (:num, :name, :head_teacher_id, :coach_teacher_id,
|
||||
:tutor_teacher_id, :class_start_time,
|
||||
:class_end_time, 0)
|
||||
"""), {
|
||||
"num": c[0], "name": c[1],
|
||||
"head_teacher_id": c[2], "coach_teacher_id": c[3],
|
||||
"tutor_teacher_id": c[4],
|
||||
"class_start_time": c[5], "class_end_time": c[6],
|
||||
})
|
||||
class_ids.append(result.lastrowid)
|
||||
|
||||
# ── 4. 学生 ──────────────────────────────────────
|
||||
first_names = ["志强", "丽华", "建国", "美玲", "文博", "晓燕",
|
||||
"浩然", "雨桐", "子轩", "思涵",
|
||||
"俊杰", "雅琪", "宇航", "欣怡", "天翊",
|
||||
"诗涵", "一诺", "浩宇", "梓萱", "博文"]
|
||||
home_places = ["北京", "上海", "广州", "深圳", "杭州", "成都", "武汉", "西安"]
|
||||
colleges = ["清华大学", "北京大学", "复旦大学", "上海交通大学",
|
||||
"浙江大学", "南京大学", "武汉大学", "华中科技大学"]
|
||||
specialties = ["计算机科学", "软件工程", "数据科学", "人工智能"]
|
||||
|
||||
for i in range(20):
|
||||
class_idx = i % 3
|
||||
enrollment = date(2024, 9, 1) if class_idx != 2 else date(2025, 9, 1)
|
||||
graduate = date(2028, 6, 30) if class_idx != 2 else date(2029, 6, 30)
|
||||
phone = f"1390000{1000 + i}"
|
||||
# 前3个学生年龄设大一些,用于30岁以上统计测试
|
||||
age = 30 + i if i < 3 else 18 + (i % 4)
|
||||
db.execute(text("""
|
||||
INSERT INTO students (num, name, age, sex, home_Place, college,
|
||||
specialty, enrollment_time, graduate_time,
|
||||
education, class_id, advisor_id, phone, is_deleted)
|
||||
VALUES (:num, :name, :age, :sex, :home_Place, :college,
|
||||
:specialty, :enrollment_time, :graduate_time,
|
||||
'ben', :class_id, :advisor_id, :phone, 0)
|
||||
"""), {
|
||||
"num": f"2024{1001 + i}",
|
||||
"name": first_names[i],
|
||||
"age": age,
|
||||
"sex": "m" if i % 2 == 0 else "f",
|
||||
"home_Place": home_places[i % 8],
|
||||
"college": colleges[i % 8],
|
||||
"specialty": specialties[i % 4],
|
||||
"enrollment_time": enrollment,
|
||||
"graduate_time": graduate,
|
||||
"class_id": class_ids[class_idx],
|
||||
"advisor_id": teacher_ids[i % 6],
|
||||
"phone": phone,
|
||||
})
|
||||
|
||||
# ── 5. 成绩 ──────────────────────────────────────
|
||||
all_students = db.execute(
|
||||
text("SELECT id, class_id FROM students WHERE is_deleted = 0")
|
||||
).fetchall()
|
||||
all_subjects = db.execute(
|
||||
text("SELECT id FROM subject")
|
||||
).fetchall()
|
||||
|
||||
for idx, st in enumerate(all_students):
|
||||
for subj_idx, sub in enumerate(all_subjects):
|
||||
# 前3名学生设大年龄(用于30岁以上统计)
|
||||
# 前2名学生所有科目80分以上(用于优秀成绩统计)
|
||||
# 最后2名学生部分科目不及格(用于不及格统计)
|
||||
if idx < 2:
|
||||
# 优秀学生:所有科目85-95分
|
||||
score_val = 85 + (idx * 5 + subj_idx * 3) % 11
|
||||
elif idx >= 18:
|
||||
# 不及格学生:部分科目50-60分
|
||||
if subj_idx < 3:
|
||||
score_val = 50 + (idx * 3 + subj_idx) % 11
|
||||
else:
|
||||
score_val = 75 + (idx * 2 + subj_idx) % 16
|
||||
else:
|
||||
score_val = 60 + (idx * 7 + subj_idx * 13) % 41
|
||||
db.execute(text("""
|
||||
INSERT INTO scores (sid, cid, num, score, t_subject, is_deleted)
|
||||
VALUES (:sid, :cid, 1, :score, :t_subject, 0)
|
||||
"""), {
|
||||
"sid": st[0], "cid": st[1],
|
||||
"score": score_val, "t_subject": sub[0],
|
||||
})
|
||||
|
||||
# ── 6. 地址 ──────────────────────────────────────
|
||||
addresses = ["北京市海淀区", "上海市浦东新区", "深圳市南山区",
|
||||
"杭州市西湖区", "广州市天河区"]
|
||||
address_ids = []
|
||||
for addr in addresses:
|
||||
result = db.execute(
|
||||
text("INSERT INTO Address (somewhere) VALUES (:where)"),
|
||||
{"where": addr}
|
||||
)
|
||||
address_ids.append(result.lastrowid)
|
||||
|
||||
# ── 7. 公司 ──────────────────────────────────────
|
||||
companies = ["字节跳动", "阿里巴巴", "腾讯", "华为", "百度"]
|
||||
company_ids = []
|
||||
for idx, comp_name in enumerate(companies):
|
||||
result = db.execute(
|
||||
text("INSERT INTO Company (employment_company) VALUES (:name)"),
|
||||
{"name": comp_name}
|
||||
)
|
||||
company_ids.append(result.lastrowid)
|
||||
|
||||
# ── 8. 就业信息 ───────────────────────────────────
|
||||
# 添加更多就业记录,确保统计接口有数据
|
||||
employment_data = []
|
||||
for i in range(min(15, len(all_students))):
|
||||
company_idx = i % len(company_ids)
|
||||
address_idx = i % len(address_ids)
|
||||
salary = 25000 + (i * 2000) % 15000
|
||||
# 前10个学生有offer时间,后5个没有(测试不同情况)
|
||||
offer_time = date(2028, 4, 15 + i) if i < 10 else None
|
||||
employment_data.append((
|
||||
all_students[i][0], all_students[i][1], company_ids[company_idx],
|
||||
address_ids[address_idx], salary, date(2028, 3, 1), offer_time
|
||||
))
|
||||
for idx, emp in enumerate(employment_data):
|
||||
db.execute(text("""
|
||||
INSERT INTO Employments (stuid, class_id, company_id, address_id,
|
||||
employment_salary, employment_open_time,
|
||||
offer_recived_time, is_deleted)
|
||||
VALUES (:stuid, :class_id, :company_id, :addr_id, :salary,
|
||||
:open_time, :offer_time, 0)
|
||||
"""), {
|
||||
"stuid": emp[0], "class_id": emp[1],
|
||||
"company_id": emp[2], "addr_id": emp[3],
|
||||
"salary": emp[4], "open_time": emp[5], "offer_time": emp[6],
|
||||
})
|
||||
|
||||
db.commit()
|
||||
print(f" [种子] 完成!插入 {len(subjects)} 科目、{len(teachers)} 教师、"
|
||||
f"{len(classes)} 班级、20 学生、{len(all_students)*len(all_subjects)} 成绩、"
|
||||
f"{len(companies)} 公司、{len(employment_data)} 就业")
|
||||
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
print(f" [种子] 失败: {e}")
|
||||
raise
|
||||
finally:
|
||||
db.close()
|
||||
@@ -15,10 +15,10 @@ def get_stuinfo( #查询学生就业信息
|
||||
,min_salary:int|None=Query(None,description='最小薪资范围')
|
||||
,max_salary:int|None=Query(None,description='最大薪资范围')
|
||||
,somewhere:str|None=Query(None,description='公司地区')
|
||||
,employment_open_start: date | None = Query(None,description='就业开放起始时间')
|
||||
,employment_open_end: date | None = Query(None,description='就业开放结束时间')
|
||||
,offer_recived_start: date | None = Query(None,description='offer下发起始时间')
|
||||
,offer_recived_end: date | None = Query(None,description='offer下发结束时间')
|
||||
,employment_open_start: date|None = Query(None,description='就业开放起始时间')
|
||||
,employment_open_end: date|None = Query(None,description='就业开放结束时间')
|
||||
,offer_recived_start: date|None = Query(None,description='offer下发起始时间')
|
||||
,offer_recived_end: date|None = Query(None,description='offer下发结束时间')
|
||||
,db=Depends(get_db)
|
||||
): #查询条件
|
||||
r=get_emp_dao( #调用函数
|
||||
@@ -93,7 +93,7 @@ def delete_stuinfo( #删除模块
|
||||
): #根据id选择对象
|
||||
rows = delete_emp_dao(id, db) #调用删除函数
|
||||
if not rows:
|
||||
raise HTTPException(status_code=500, detail='没有删除!') #失败返回错误信息
|
||||
raise HTTPException(status_code=404, detail='未找到该就业记录!') #失败返回错误信息
|
||||
return {'code': 200, 'totals': rows, 'detail': '删除成功'} #成功返回信息
|
||||
|
||||
'''
|
||||
|
||||
+15
-15
@@ -18,6 +18,7 @@ def add_employment_dao(o,db): #新增就业信息函数
|
||||
return False
|
||||
else:
|
||||
db.commit() #提交事务,永久保存
|
||||
db.refresh(o1)
|
||||
return True
|
||||
|
||||
def add_company_dao(o,db): #添加公司信息函数
|
||||
@@ -34,6 +35,7 @@ def add_company_dao(o,db): #添加公司信息函数
|
||||
return False
|
||||
else:
|
||||
db.commit() #提交事务,永久保存
|
||||
db.refresh(o1)
|
||||
return True
|
||||
def add_address_dao(o,db): #添加地址区域信息函数
|
||||
try:
|
||||
@@ -49,6 +51,7 @@ def add_address_dao(o,db): #添加地址区域信息函数
|
||||
return False
|
||||
else:
|
||||
db.commit() #提交事务,永久保存
|
||||
db.refresh(o1)
|
||||
return True
|
||||
|
||||
|
||||
@@ -82,24 +85,23 @@ def get_emp_dao( #获取学生
|
||||
# ,page
|
||||
# ,page_size
|
||||
): #根据选择条件获取信息
|
||||
q = db.query(Employments,Company.employment_company,Address.somewhere) \
|
||||
.outerjoin(Company, Students.id == Employments.stuid) \
|
||||
.outerjoin(Company, ClassInfo.id == Employments.class_id)\
|
||||
.outerjoin(Company,Employments.company_id==Company.id)\
|
||||
.outerjoin(Address,Company.address_id==Address.id)\
|
||||
.filter(Employments.is_deleted == 0) #连接公司表和地址表,筛选未被删除信息
|
||||
q = (db.query(Employments, Students.name, Company.employment_company, Address.somewhere)
|
||||
.outerjoin(Students, Students.id == Employments.stuid)
|
||||
.outerjoin(ClassInfo, ClassInfo.id == Employments.class_id)
|
||||
.outerjoin(Company,Employments.company_id==Company.id)
|
||||
.outerjoin(Address,Employments.address_id==Address.id)
|
||||
.filter(Employments.is_deleted == 0)) #连接公司表和地址表,筛选未被删除信息
|
||||
# if stuid:
|
||||
# q = q.filter( Employments.id == stuid )
|
||||
|
||||
if sname: #根据学生姓名筛选
|
||||
q = q.filter( Students.name == sname )
|
||||
if class_id:
|
||||
q = q.filter(ClassInfo.id == class_id)
|
||||
q = q.filter(Employments.class_id == class_id)
|
||||
if somewhere:
|
||||
q = q.filter(Address.somewhere.like(f'%{somewhere}%'))
|
||||
if employment_company: #根据公司姓名筛选
|
||||
q=q.filter(Company.employment_company.like(f'%{employment_company}%'))
|
||||
# r = q.offset( (page-1)*page_size ).limit( page_size ).all()
|
||||
if min_salary is not None: #根据最小薪资筛选
|
||||
q = q.filter(Employments.employment_salary >= min_salary)
|
||||
if max_salary is not None: ##根据最大薪资筛选
|
||||
@@ -116,20 +118,18 @@ def get_emp_dao( #获取学生
|
||||
r= q.all()
|
||||
if r:
|
||||
return [ {"id": i.id,
|
||||
'学生姓名':i.sname,
|
||||
'学生姓名':sname or '-',
|
||||
'学生班级编号':i.class_id,
|
||||
"公司": employment_company or '未就业',
|
||||
"公司区域":somewhere or '-',
|
||||
# "company_id": i.company_id,
|
||||
"薪资":i.employment_salary or '-',
|
||||
# "address_id": i.address_id,
|
||||
"就业开放时间":i.employment_open_time.strftime('%Y-%m-%d') ,
|
||||
"offer下发时间":i.offer_recived_time.strftime('%Y-%m-%d')
|
||||
"就业开放时间":i.employment_open_time.strftime('%Y-%m-%d') if i.employment_open_time else '-',
|
||||
"offer下发时间":i.offer_recived_time.strftime('%Y-%m-%d') if i.offer_recived_time else '-'
|
||||
# , "创建时间": i.create_time,
|
||||
# "更新时间": i.update_time,
|
||||
# "是否删除":i.is_deleted
|
||||
} for i,sname,class_id,employment_company,somewhere in r ] #返回信息列表
|
||||
|
||||
} for i,sname,employment_company,somewhere in r ] #返回信息列表
|
||||
return {"code": 200, "totals": len(r), "data": r}
|
||||
|
||||
def delete_emp_dao(id,db): #删除函数
|
||||
try:
|
||||
|
||||
+3
-6
@@ -8,15 +8,12 @@ Shtudent_Jiuye = APIRouter()
|
||||
Shtudent_Jiuye.include_router(CURD)
|
||||
|
||||
# 自测接口
|
||||
|
||||
|
||||
app=FastAPI()
|
||||
Base.metadata.create_all(engine)
|
||||
app.include_router(CURD,tags=['就业管理系统'])
|
||||
|
||||
|
||||
|
||||
if __name__=='__main__':
|
||||
app.include_router(CURD, tags=['就业管理系统'])
|
||||
Base.metadata.create_all(engine)
|
||||
|
||||
import uvicorn
|
||||
uvicorn.run('main:app', host='127.0.0.1', port=12395)
|
||||
|
||||
|
||||
+7
-7
@@ -19,12 +19,14 @@ class Employments(Base_all): #学生
|
||||
class_id = Column(Integer
|
||||
, ForeignKey('classes.id')
|
||||
) #学生班级,冗余字段
|
||||
company_id = Column(Integer #公司id,外键匹配公司表
|
||||
company_id = Column(Integer # 公司id,外键匹配公司表
|
||||
, ForeignKey('Company.id')
|
||||
, nullable=True
|
||||
|
||||
)
|
||||
|
||||
address_id = Column(Integer # 地址id,匹配地址表
|
||||
, ForeignKey('Address.id') # 外键地址表id
|
||||
, nullable=False # 非空约束
|
||||
)
|
||||
employment_salary = Column(Integer, nullable=True) #薪资
|
||||
|
||||
employment_open_time = Column(DATE, nullable=True) #就业开放时间
|
||||
@@ -49,10 +51,7 @@ class Company(Base_all): #公司
|
||||
employment_company = Column(String(50) #公司姓名
|
||||
, nullable=False #非空约束
|
||||
)
|
||||
address_id = Column(Integer #地址id,匹配地址表
|
||||
, ForeignKey('Address.id') #外键地址表id
|
||||
, nullable=False #非空约束
|
||||
)
|
||||
|
||||
create_time = Column(DATETIME
|
||||
, default=datetime.now
|
||||
)
|
||||
@@ -71,6 +70,7 @@ class Address(Base_all):
|
||||
somewhere=Column(String(50) #地址区域
|
||||
, nullable=False
|
||||
)
|
||||
|
||||
create_time = Column(DATETIME
|
||||
, default=datetime.now
|
||||
)
|
||||
|
||||
@@ -4,10 +4,10 @@ from pydantic import BaseModel
|
||||
|
||||
class PostRequest(BaseModel): #学生就业信息请求体模型
|
||||
# compname: str|None=None
|
||||
sname: str|None=None
|
||||
stuid: int|None=None
|
||||
class_id: int|None=None
|
||||
company_id:int|None=None
|
||||
# address_id: int|None=None
|
||||
address_id: int|None=None
|
||||
employment_salary: int|None=None
|
||||
employment_open_time:date|None=None
|
||||
offer_recived_time:date|None=None
|
||||
@@ -21,7 +21,7 @@ class EmpResponse(BaseModel): #学生就
|
||||
|
||||
class CompRequest(BaseModel): #公司信息请求体模型
|
||||
employment_company:str
|
||||
address_id: int
|
||||
# address_id: int
|
||||
|
||||
class CompResponse(BaseModel): #公司信息响应体
|
||||
code: int = 200
|
||||
|
||||
+198
-15
@@ -16,48 +16,231 @@ ORM:sqlalchemy
|
||||
|
||||
## main:学生管理系统的主程序文件、测试接口
|
||||
1. 下面所有配置文件的接口全部导入并建表
|
||||
2. 创建fastapi接口并本地运行
|
||||
2. 创建合并接口APIRouter
|
||||
3. 创建fastapi接口并本地运行
|
||||
|
||||
## databases:链接数据库配置文件
|
||||
1. 链接本地mysql,实例化mysql,创建引擎,创建基类,实例化数据库会话
|
||||
2. 创建函数,调用数据库会话
|
||||
3. 因为后期多代码合并,每个人的databases都不同链接也不同,所以导入了.env文件
|
||||
1. 这样在每个人有差异的数据可以直接保存在本地,不用反复同步相互覆盖了
|
||||
2. 另外一个原因就是model层大家建表要使用同一个基类
|
||||
1. 所以接入了.env文件随时入参
|
||||
2. 这样更加方便,而且高频同步和拉取也不用重新配置
|
||||
|
||||
## 分层代码书写顺序:
|
||||
1. model层:
|
||||
1. 确认你的业务需要哪些字段
|
||||
2. 确认小组的字段名、表名、库名一致
|
||||
3. 确认主键、唯一键、字段数据类型、外键等关键字内容
|
||||
2. 请求体
|
||||
1. 搞清楚业务需求,需要用到哪些字段
|
||||
2. 增删改查的具体逻辑
|
||||
3. api
|
||||
1. 写好接收变量
|
||||
2. 装饰器设定
|
||||
3. 输出规则
|
||||
4. 一次写好基本上就不会再改了
|
||||
4. dao
|
||||
1. 工作量最大的层,所有的业务逻辑都写在这里面
|
||||
2. 所有的业务逻辑都在这个层里
|
||||
|
||||
## api.students_api:接口文件
|
||||
### 学生表students的功能接口:
|
||||
1. 学生信息的查询
|
||||
1. 仅保存入参标量,代码都在逻辑层,建好了基本不用修改,方便维护
|
||||
2. 学生信息的新增
|
||||
1. 仅保存入参标量,代码都在逻辑层,建好了基本不用修改,方便维护
|
||||
3. 学生信息的更新
|
||||
1. 仅保存入参标量,代码都在逻辑层,建好了基本不用修改,方便维护
|
||||
4. 学生信息的逻辑删除
|
||||
1. 仅保存入参标量,代码都在逻辑层,建好了基本不用修改,方便维护
|
||||
|
||||
## dao.students_dao:数据筛选文件
|
||||
### 数据库的逻辑筛选文件:
|
||||
1. 查询:get_student_dao
|
||||
1. 筛选所有非空内容,过滤逻辑删除数据,输出学生完整字段
|
||||
2. 新增:add_student_dao
|
||||
1. 关键字段限制
|
||||
2. 过滤各种报错,反馈报错问题
|
||||
3. 无报错即可全部插入
|
||||
4. 注意逻辑删除的数据,其实也会占用唯一键,所以有唯一键重复时,直接拉出旧数据进行清空翻新。
|
||||
3. 更新:update_student_dao
|
||||
1. 主要过滤掉没有输入的字段,除非输入才可以被覆盖,不能每次更新清空全部内容
|
||||
2. 唯一字段过滤,比如电话号码,在业务逻辑上电话号码不能重复
|
||||
4. 逻辑删除:delete_student_dao
|
||||
1. 逻辑删除可以用所有字段进行精确或者范围删除
|
||||
2. 但是不可以全为空删除我的整个库
|
||||
|
||||
## model.students_model:数据库建表文件
|
||||
1. 数据表建表代码
|
||||
2. 字段信息: sid **PK**
|
||||
, student_name
|
||||
, student_phone
|
||||
, class_name
|
||||
, job_open_time
|
||||
, offer_issue_time
|
||||
, company_name
|
||||
, salary
|
||||
, create_date
|
||||
, update_date
|
||||
, is_deleted
|
||||
, deleted_date
|
||||
, **待建FK:** class_id/teacher_id/score_id/company_id/
|
||||
|
||||
2. 字段信息: `id`:***PK***
|
||||
`num`:***UNI***
|
||||
`name`
|
||||
`age`
|
||||
`sex`:*ENUM*
|
||||
`home_Place`
|
||||
`college`
|
||||
`specialty`
|
||||
`enrollment_time`
|
||||
`graduate_time`
|
||||
`education`:*ENUM*
|
||||
***外键字段***
|
||||
`class_id`:***FK***
|
||||
`advisor_id`:***FK***
|
||||
***额外字段***
|
||||
`phone`:***UNI***
|
||||
***通用字段***
|
||||
`create_date`
|
||||
`update_date`
|
||||
`is_deleted`
|
||||
`deleted_date`
|
||||
|
||||
## schema.students_request:请求体、数据过滤
|
||||
请求体根据业务要求有不同调整,方便后续维护和更改需求
|
||||
1. 查询:StudentsQuery
|
||||
1. 所有字段都可以进行查询
|
||||
2. 随便填哪个都可以进行精确查询或者范围查询
|
||||
3. 全为空即查询全部
|
||||
2. 新增:StudentsResponse
|
||||
1. 新增有必填项、不可重复项等,保证数据干净
|
||||
3. 更新:StudentsUpdate
|
||||
1. 以ID作为查询字段,更新其他内容
|
||||
2. 会过滤掉重复值
|
||||
4. 逻辑删除:StudentsDelete
|
||||
1. 所有字段都可以进行删除
|
||||
2. 随便填哪个都可以进行精确删除或者范围删除
|
||||
3. 不可全字段为空
|
||||
|
||||
## StatCalc:统计分析模块
|
||||
- 除了没有model以外没有什么太大的区别
|
||||
- 因为接口都有具体需求,所以直接一键呼出,不用填写筛选内容
|
||||
1. 请求体
|
||||
1. 搞清楚业务需求,需要用到哪些字段
|
||||
2. 增删改查的具体逻辑
|
||||
2. api
|
||||
1. 写好接收变量
|
||||
2. 装饰器设定
|
||||
3. 输出规则
|
||||
4. 一次写好基本上就不会再改了
|
||||
3. dao
|
||||
1. 工作量最大的层,所有的业务逻辑都写在这里面
|
||||
2. 所有的业务逻辑都在这个层里
|
||||
|
||||
## 学生管理系统修改报错记录
|
||||
### 1. 合并修改记录:
|
||||
1. main.py
|
||||
1. 所有子模块中的main新增了APIRouter,用来拉到主main进行运行
|
||||
2. stu_jiuye
|
||||
1. stu_jiuye\main.py
|
||||
1. 新增为加粗倾斜处
|
||||
2. from ***sut_jiuye***.文件名 import 调用名
|
||||
2. CURD_api.py
|
||||
1. 新增为加粗倾斜处
|
||||
2. from ***sut_jiuye***.文件名 import 调用名
|
||||
2. databases.py
|
||||
1. 所有人的db_url账号密码IP端口和名称全部存入了.env文件
|
||||
2. 导入了load_dotenv进行调用
|
||||
1. from dotenv import load_dotenv
|
||||
2. load_dotenv( )
|
||||
3. DB_USER = os.getenv("DB_USER", "root")
|
||||
4. DB_PASSWORD = os.getenv("DB_PASSWORD", "")
|
||||
5. DB_HOST = os.getenv("DB_HOST", "localhost")
|
||||
6. DB_PORT = os.getenv("DB_PORT", "3306")
|
||||
7. DB_NAME = os.getenv("DB_NAME", "student_manage_system")
|
||||
3. 修改了部分url名称,自行检查
|
||||
3. class文件夹下导入修改
|
||||
1. 新增为加粗倾斜处
|
||||
1. from ***class_management***.文件名 import 调用名
|
||||
1. 改动文件:
|
||||
1. class_management_api.py
|
||||
2. class_management_dao.py
|
||||
3. class_management_model.py
|
||||
|
||||
### 2. 合并完成
|
||||
1. 新增.env文件,设置好账号密码等
|
||||
1. 可以一次运行创建好所有表格
|
||||
2. 但是注意,没办法创建库,也就是student_manage_system
|
||||
3. 如果没有建库的话会报错
|
||||
2. 表外键写完之后,可以用SQL语句删除所有表
|
||||
1. 再重新运行***main.py***文件
|
||||
1. 这个main.py特指最外层的运行主程序main.py
|
||||
|
||||
### 3. 分支测试
|
||||
1. 阶段完成后
|
||||
1. 如果不擅长保存版本,可以在自己的分支下面再建新分支
|
||||
2. 方便切换版本和查询bug,理解逻辑,以免修改后bug更多了
|
||||
3. 完成后可以将子分支变基到主线
|
||||
4. 变基(覆盖)完成后可以继续使用子分支进行修改整合
|
||||
2. 注意云端仓库,没有修改的文件是不会反复上传的
|
||||
1. 所以看距离时间最近的基本都是被修改过的
|
||||
2. 可以用来进行版本对比
|
||||
|
||||
### 4. 外键创建
|
||||
1. 外键必须要来自同一个基类,不然会报错
|
||||
1. 新增主程序的databases.py文件,统一基类
|
||||
|
||||
### 5. 外键报错
|
||||
1. 在新建字段时,依赖主表的外键必须要已经建立,不然在提交时都会报错
|
||||
2. 所以新增接口的报错逻辑需要特别优化,将各个阶段可能报错的内容都捕获下来
|
||||
|
||||
|
||||
## hermes agent协作内容:
|
||||
### 1. 测试数据脚本 — init_test_data.py/seed_data.py
|
||||
直接正常运行主程序文件 main.py 即可插入
|
||||
|
||||
直接插入前提:
|
||||
1. 保证数据库 student_manage_system 存在
|
||||
2. 数据表字段对应
|
||||
3. 建议直接删库建库 students\StudentsQuery.sql 里有相应需求的语句
|
||||
|
||||
向数据库插入了完整的测试数据,没有修改任何原有项目文件:
|
||||
数据类型
|
||||
数量
|
||||
科目 6 门(Python程序设计、数据结构、数据库原理等)
|
||||
教师 6 位(张伟、李芳、王强、陈静、刘洋、赵敏)
|
||||
班级 3 个(CS2401、CS2402、AI2501)
|
||||
学生 20 名(涵盖3个班级,男女各半)
|
||||
成绩 120 条(每名学生6门课,含部分不及格记录)
|
||||
公司 5 家(字节跳动、阿里巴巴、腾讯、华为、百度)
|
||||
就业信息 8 条(含薪资、offer时间等)
|
||||
|
||||
### 2. 前端页面 - frontend/index.html - 双击运行
|
||||
单文件完整前端,包含:
|
||||
|
||||
首页:问候 Banner + 快捷卡片(查课表/查成绩/校园卡/报修)+ 校园动态信息流
|
||||
学生管理:表格列表、搜索过滤、新增/编辑/删除(含二次确认弹窗)
|
||||
成绩查询:学期切换 Tab、成绩卡片展示、Chart.js 柱状图
|
||||
教师管理:教师列表、搜索、新增
|
||||
就业管理:就业信息列表、状态标签(已就业/待就业)
|
||||
个人中心:蓝色背景头像区 + 功能菜单列表
|
||||
|
||||
交互特性:
|
||||
✅ 卡片点击缩放反馈(95% 回弹,150ms)
|
||||
✅ 页面切换从右侧滑入(300ms)
|
||||
✅ 删除二次确认弹窗
|
||||
✅ Toast 提示(成功/失败/信息)
|
||||
✅ 鼠标悬停按钮/卡片颜色加深、阴影加深
|
||||
✅ 隔行变色表格 + 表头排序
|
||||
|
||||
启动后端后直接浏览器打开 frontend/index.html 即可使用。
|
||||
|
||||
### 移植
|
||||
只动几个文件,都不需要改代码:
|
||||
|
||||
移动的文件:
|
||||
|
||||
seed_data.py → 新项目的根目录
|
||||
init_test_data.py → 新项目的根目录
|
||||
frontend/ → 新项目的根目录(整个文件夹)
|
||||
|
||||
要改的唯一参数: frontend/index.html 第 306 行
|
||||
|
||||
const API_BASE = 'http://localhost:59000';
|
||||
改成新项目实际运行的端口即可。
|
||||
|
||||
其他都不用动:
|
||||
|
||||
init_test_data.py 完全独立,连接的是 databases.py 里的 .env 配置,新项目只要 DB 信息对就能直接跑
|
||||
frontend/index.html 纯静态页面,不依赖任何项目代码
|
||||
如果新项目用了不同的端口(比如 8000),改那一行就行
|
||||
@@ -10,16 +10,30 @@ drop database student_manage_system;
|
||||
|
||||
drop table students;
|
||||
|
||||
select * from students limit 50;
|
||||
select * from students limit 200;
|
||||
|
||||
select * from scores limit 200;
|
||||
|
||||
select * from address limit 200;
|
||||
|
||||
select * from classes limit 200;
|
||||
|
||||
select * from company limit 200;
|
||||
|
||||
select * from employments limit 200;
|
||||
|
||||
select * from teachers limit 200;
|
||||
|
||||
select * from subject limit 200;
|
||||
|
||||
desc students;
|
||||
|
||||
|
||||
select *
|
||||
from cast(student_manage_system as BINARY)
|
||||
where age >30
|
||||
join on
|
||||
group by
|
||||
having
|
||||
order by
|
||||
limit
|
||||
# select *
|
||||
# from cast(student_manage_system as BINARY)
|
||||
# where age >30
|
||||
# join on
|
||||
# group by
|
||||
# having
|
||||
# order by
|
||||
# limit
|
||||
@@ -27,15 +27,15 @@ def get_students( stu:StudentsQuery = Depends()
|
||||
@s_api.post("/students"
|
||||
,summary='新增学生资料'
|
||||
,description=f'学生ID自动分配,学生姓名、学生手机号和班级是必填项,不能为空。\n '
|
||||
f'时间格式:YYYY-MM-DD'
|
||||
,response_model=StudentsResponse )
|
||||
f'注意事项:\n'
|
||||
f'时间格式:YYYY-MM-DD\n'
|
||||
f'班级ID和顾问ID必须已经存在才可以绑定'
|
||||
)
|
||||
def add_student( stu:StudentsResponse = Depends()
|
||||
, db=Depends(get_db)
|
||||
):
|
||||
s_dict = stu.model_dump(exclude_unset=True)
|
||||
result = add_student_dao( s=s_dict , db=db )
|
||||
if not result:
|
||||
raise HTTPException(status_code=500, detail='手机号已有学生使用或必填项为空。')
|
||||
return result
|
||||
|
||||
@s_api.put("/students"
|
||||
|
||||
@@ -62,9 +62,17 @@ def add_student_dao( s , db):
|
||||
phone = s.get("phone")
|
||||
s1_deleted = db.query( Students ).filter( Students.phone == phone ).first()
|
||||
if s1_deleted and s1_deleted.is_deleted == False:
|
||||
return False
|
||||
raise ValueError("该手机号已存在有效学生记录,请勿重复添加")
|
||||
if not phone:
|
||||
return False
|
||||
raise ValueError("手机号不能为空")
|
||||
if not s.get("class_id") or not s.get("advisor_id"):
|
||||
raise ValueError("班级ID和顾问ID不能为空")
|
||||
s1_class_id = db.query(Students).filter(Students.phone == phone).first()
|
||||
s1_advisor_id = db.query(Students).filter(Students.phone == phone).first()
|
||||
if not s1_class_id:
|
||||
raise ValueError(f"班级ID {s.get("class_id")} 不存在,请先创建班级")
|
||||
if not s1_advisor_id:
|
||||
raise ValueError(f"顾问ID {s.get("advisor_id")} 不存在,请先创建顾问")
|
||||
if s1_deleted and s1_deleted.is_deleted == True:
|
||||
s1_deleted.name = s.get("name")
|
||||
s1_deleted.age = s.get("age")
|
||||
@@ -90,12 +98,16 @@ def add_student_dao( s , db):
|
||||
return s1_deleted
|
||||
s1 = Students( **s )
|
||||
db.add(s1)
|
||||
except Exception:
|
||||
db.commit()
|
||||
except ValueError as e:
|
||||
# 业务校验异常:回滚并返回错误信息
|
||||
db.rollback()
|
||||
return {"code": 400, "msg": str(e)}
|
||||
except Exception as e:
|
||||
# 这里可以加日志
|
||||
db.rollback()
|
||||
return False
|
||||
return {"code": 500, "msg": f"系统异常: {str(e)}"}
|
||||
else:
|
||||
db.commit()
|
||||
db.refresh(s1)
|
||||
return s1
|
||||
|
||||
@@ -141,14 +153,14 @@ def update_student_dao( s , db):
|
||||
# 额外字段
|
||||
if s.get("phone") is not None:
|
||||
s1_id.student_phone = s.get("phone")
|
||||
s1_id.update_date = datetime.now()
|
||||
db.commit()
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception:
|
||||
db.rollback()
|
||||
raise
|
||||
else:
|
||||
s1_id.update_date = datetime.now()
|
||||
db.commit()
|
||||
db.refresh(s1_id) # 从数据库再拉一次数据
|
||||
return s1_id
|
||||
|
||||
|
||||
+4
-3
@@ -8,6 +8,7 @@ from contextlib import asynccontextmanager
|
||||
Students_API = APIRouter()
|
||||
Students_API.include_router(s_api)
|
||||
|
||||
|
||||
# 自测接口
|
||||
@asynccontextmanager
|
||||
async def S_Fastapi(app: FastAPI):
|
||||
@@ -15,11 +16,11 @@ async def S_Fastapi(app: FastAPI):
|
||||
yield
|
||||
engine1.dispose()
|
||||
|
||||
app = FastAPI(title="学生管理系统",lifespan=S_Fastapi) # 这里必须把上面写的加上
|
||||
app.include_router(s_api,tags=["students"])
|
||||
|
||||
app = FastAPI(title="学生管理系统", lifespan=S_Fastapi) # 这里必须把上面写的加上
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.include_router(s_api, tags=["students"])
|
||||
|
||||
def main():
|
||||
print("Hello from student-manage-system!")
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ class Students(Base_all):
|
||||
, comment='年龄'
|
||||
, nullable=False)
|
||||
|
||||
sex = Column(Enum(SexEnum, values_callable=lambda e: [i.value for i in e])
|
||||
sex = Column(Enum(SexEnum)
|
||||
, comment='性别'
|
||||
, nullable=False)
|
||||
|
||||
@@ -49,7 +49,7 @@ class Students(Base_all):
|
||||
graduate_time = Column(DATETIME
|
||||
, comment='毕业时间')
|
||||
|
||||
education = Column(Enum(EducationEnum, values_callable=lambda e: [i.value for i in e])
|
||||
education = Column(Enum(EducationEnum)
|
||||
, comment='学历'
|
||||
, nullable=True)
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# students_schema:请求、响应的模型框架
|
||||
|
||||
from pydantic import BaseModel , Field , field_validator
|
||||
from pydantic import BaseModel , Field
|
||||
from datetime import datetime
|
||||
import enum
|
||||
|
||||
class SexEnum(enum.Enum):
|
||||
class SexEnum(str , enum.Enum):
|
||||
m = '男'
|
||||
f = '女'
|
||||
|
||||
@@ -27,7 +27,7 @@ class StudentsQuery(BaseModel):
|
||||
# 额外字段
|
||||
phone:str | None = None
|
||||
|
||||
class EducationEnum(enum.Enum):
|
||||
class EducationEnum(str , enum.Enum):
|
||||
z = '专科'
|
||||
ben = '本科'
|
||||
s = '硕士'
|
||||
|
||||
Reference in New Issue
Block a user