Compare commits
10
Commits
f2e525b5a5
...
b67e3cd547
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b67e3cd547 | ||
|
|
079a7cf351 | ||
|
|
2dca7c7f76 | ||
|
|
566747b320 | ||
|
|
699867b0af | ||
|
|
fa571a13da | ||
|
|
5fddb78c27 | ||
|
|
b5883e450d | ||
|
|
dc243fb33f | ||
|
|
6e189d0c65 |
+11
-7
@@ -28,13 +28,17 @@ 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)
|
||||
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='输入任意信息删除')
|
||||
|
||||
+14
-7
@@ -29,15 +29,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):
|
||||
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):
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ if __name__ == "__main__":
|
||||
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
|
||||
|
||||
@@ -4,6 +4,12 @@ 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:
|
||||
@@ -11,6 +17,7 @@ def add_class_dao(c,db):
|
||||
return False
|
||||
else:
|
||||
db.commit()
|
||||
db.refresh(c1)
|
||||
return True
|
||||
|
||||
|
||||
|
||||
@@ -22,16 +22,16 @@ 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_start_time = Column(Date
|
||||
|
||||
@@ -9,12 +9,12 @@ from databases import *
|
||||
app = FastAPI(title="学生管理系统")
|
||||
|
||||
|
||||
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"])
|
||||
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=["统计分析模块"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -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是主程序文件,将配置文件的接口导入,创建接口运行
|
||||
@@ -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': '删除成功'} #成功返回信息
|
||||
|
||||
'''
|
||||
|
||||
+13
-13
@@ -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,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,
|
||||
'学生班级编号':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.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:
|
||||
|
||||
+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
|
||||
|
||||
+161
-14
@@ -16,48 +16,195 @@ 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. 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
|
||||
向数据库插入了完整的测试数据,没有修改任何原有项目文件:
|
||||
数据类型
|
||||
数量
|
||||
科目 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 即可使用。
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user