Compare commits

..
Author SHA1 Message Date
geyuan b67e3cd547 修更新的bud 2026-09-23 00:50:59 +08:00
chen 079a7cf351 模块解释 2026-09-22 23:02:42 +08:00
Coffee 2dca7c7f76 更新debug 2026-09-22 23:02:42 +08:00
Orangeeee 566747b320 更新Student的README_students.md模块解释文件。 2026-09-22 23:02:42 +08:00
Orangeeee 699867b0af 优化了students_dao里add_student_dao的报错逻辑。 2026-09-22 23:02:42 +08:00
Orangeeee fa571a13da 优化了students_dao里add_student_dao的报错逻辑。 2026-09-22 23:02:42 +08:00
Coffee 5fddb78c27 更新db.refresh 2026-09-22 23:02:42 +08:00
Coffee b5883e450d 更新debug 2026-09-22 23:02:39 +08:00
Coffee dc243fb33f 更新外键 2026-09-22 23:01:59 +08:00
Anhoa 6e189d0c65 更改新增dao bug 2026-09-22 23:01:59 +08:00
chen f2e525b5a5 统计 2026-09-22 23:01:59 +08:00
Orangeeee a1d7adba94 全部更新确认,无基础错误,各自测试各自端口bug,暂时没有自动导入数据。 2026-09-22 23:01:59 +08:00
chen 7d5f352c69 统计 2026-09-22 23:01:56 +08:00
Orangeeee ffcb40db41 统计分析模块更新,待测试,统计分析模块更新,就业3个模块更新。 2026-09-22 23:01:21 +08:00
chen 75f23e39be dao改动 2026-09-22 23:01:21 +08:00
longtrying c1813ae700 删除 .env
以免频繁重新覆盖修改。
2026-09-22 23:01:21 +08:00
Anhoa 65d67e7cf6 增加了num的唯一键,修改了dao和api 2026-09-22 23:01:18 +08:00
Orangeeee c0880f9a44 统计分析模块更新,待测试,优化所有模块main.py文件,修复bug。 2026-09-22 22:59:50 +08:00
geyuan 9c9e51d68f 改提交不上的bud 2026-09-22 22:55:17 +08:00
geyuan 334bb237b2 改提交不上的bud 2026-09-22 22:32:44 +08:00
16 changed files with 264 additions and 713 deletions
+13 -9
View File
@@ -11,7 +11,7 @@ tea_api = APIRouter()
@tea_api.post('/tea',response_model=TeaResponse,summary='新增老师',description='输入新增的老师信息,除去姓名外可以为空')
def add_tea(tea: NewTeaRequest=Depends(),db=Depends(get_db)):
r = add_tea_dao(tea.model_dump(),db)
r = add_tea_dao(tea.model_dump(exclude_unset=True),db)
if not r:
raise HTTPException(status_code=500,detail='添加失败!')
return TeaResponse(totals=1,data=tea.model_dump())
@@ -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='输入任意信息删除')
@@ -48,7 +52,7 @@ def delete_other_tea_dao1(t:TeaRequest=Depends(),db=Depends(get_db)):
@tea_api.post('/subject',summary='新增科目',description='输入新增的科目')
def add_sub(t:SubRequest=Depends(),db=Depends(get_db)): #开启会话窗口
r = add_subject_dao(t.model_dump(),db=db)
if not r:
if r=='已存在':
raise HTTPException(status_code=500,detail='科目已存在')
return TeaResponse(totals=len(r), data=r)
+25 -16
View File
@@ -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):
@@ -50,8 +57,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 +74,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 +105,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'):
@@ -128,15 +135,17 @@ def add_subject_dao(t,db):
try:
t1 = db.query(Sub).filter(Sub.name == t.get('name')).first()
if t1:
print('科目已存在')
raise Exception('科目已存在')
return'已存在'
s1 = Sub(**t)
db.add(s1)
db.commit()
return '添加成功'
except Exception as e:
return '[s1]'
except InterruptedError as e:
db.rollback()
print('失败的原因',e)
return False
except Exception as e:
db.rollback()
return f'{e}'
+1
View File
@@ -24,4 +24,5 @@ def get_db():
yield db
finally:
db.close()
#建表
Base.metadata.create_all(engine1)
+1 -1
View File
@@ -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
-3
View File
@@ -16,17 +16,14 @@ class Tea(Base_all):
,autoincrement=True
,comment='id')
name = Column(String(50)
,nullable=False
,comment='姓名')
sex = Column(Enum(Gender),default='未知'
,comment='性别')
class_id = Column(Integer
,nullable=False
,comment='班级id')
phone = Column(String(50))
subject_id = Column(Integer
, ForeignKey('subject.id')
,nullable=False
,comment='教学科目')
home_place = Column(String(50)
,comment='家乡')
+4 -4
View File
@@ -7,7 +7,7 @@ class TeaRequest(BaseModel):
name: str|None = None
sex: Gender|None = None
class_id: int | None = None
sub: str|None = None
subject_id: str|None = None
phone: str | None
home_place: str|None = None
collage: str|None = None
@@ -22,7 +22,7 @@ class NewTeaRequest(BaseModel):
name: str | None = None
sex: Gender|None = None
class_id: int | None = None
sub: str | None = None
subject_id: int | None = None
home_place: str | None = None
phone: str | None = Field(default=None, min_length=11, max_length=11)
collage: str | None = None
@@ -35,7 +35,7 @@ class NewTeaRequest1(BaseModel):
name: str | None = None
sex: Gender|None = None
class_id: int | None = None
sub: str | None = None
subject_id: int | None = None
home_place: str | None = None
phone: str | None = Field(default=None, min_length=11, max_length=11)
collage: str | None = None
@@ -45,4 +45,4 @@ class NewTeaRequest1(BaseModel):
specialty: str | None = None
class SubRequest(BaseModel):
name: str | None = None
name: str
+2 -2
View File
@@ -11,9 +11,9 @@ 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()
+6 -5
View File
@@ -3,16 +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__':
# 自测接口
app = FastAPI()
app.include_router(class_api)
Base.metadata.create_all(engine)
import uvicorn
+33
View File
@@ -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是主程序文件,将配置文件的接口导入,创建接口运行
+4 -4
View File
@@ -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( #调用函数
-377
View File
@@ -1,377 +0,0 @@
# 学生就业信息管理系统 —— 就业模块项目文档
## 一、模块概述
本模块是「学生就业信息管理系统」的核心业务模块,负责管理学生就业状态的全生命周期。围绕就业信息(Employments)、公司信息(Company)、地址信息(Address)三张核心表,并与学生表(students)、班级表(classes)建立外键关联,提供增删改查(CRUD)与统计分析接口。
**技术栈:**
| 项 | 选型 |
| ------ | --------------------- |
| Web 框架 | FastAPI |
| ORM | SQLAlchemy 2.x |
| 数据库 | MySQL 8.x(驱动 PyMySQL) |
| 数据校验 | Pydantic v2 |
| 服务启动 | Uvicorn |
## 二、目录结构
```
student\_manage\_system/
├── main.py # 程序入口:注册全模块路由、建表(\_\_main\_\_ 中执行)
├── databases.py # 根数据库配置:engine\_all / Base\_all / Session / get\_db
└── stu\_jiuye/ # 就业模块业务包
  ├── main.py # 路由组装(Shtudent\_Jiuye);独立自测 app
  ├── database.py # 模块级数据库配置(engine / Base / Session / get\_db)
  ├── model.py # ORM 模型:Employments / Company / Address
  ├── request.py # Pydantic 请求体 / 响应体模型
  ├── dao.py # 数据访问层(纯数据库操作)
  └── CURD\_api.py # 路由层(API 接口定义)
```
**分层职责:**
| 层 | 文件 | 职责 |
| ----- | ------------ | --------------------------- |
| 路由层 | CURD\_api.py | 接收 HTTP 请求,参数校验,调用 DAO,返回响应 |
| 数据访问层 | dao.py | 封装 SQLAlchemy 查询与事务,向上提供函数 |
| 模型层 | model.py | 定义表结构与外键关系 |
| 校验层 | request.py | 定义请求体 / 响应体的字段与类型 |
| 配置层 | database.py | 数据库 URL、引擎、Session 工厂 |
## 三、数据表设计
### 1. Employments(就业信息表)
| 字段 | 类型 | 约束 | 说明 |
| ---------------------- | -------- | ------------------------- | ------------------ |
| id | Integer | PK, 自增 | 主键 |
| stuid | Integer | FK → students.id | 学生 ID |
| class\_id | Integer | FK → classes.id | 班级 ID |
| company\_id | Integer | FK → Company.id, 可空 | 就业公司 |
| address\_id | Integer | FK → Address.id, NOT NULL | 就业地址 |
| employment\_salary | Integer | 可空 | 就业薪资 |
| employment\_open\_time | DATE | 可空 | 就业开放时间 |
| offer\_recived\_time | DATE | 可空 | Offer 下发时间 |
| create\_time | DateTime | default=now | 创建时间 |
| update\_time | DateTime | default=now, onupdate=now | 更新时间 |
| is\_deleted | Integer | default=0 | 软删除标记(0 正常 / 1 已删) |
### 2. Company(公司表)
| 字段 | 类型 | 约束 | 说明 |
| ------------------- | ---------- | ------------------------- | ----- |
| id | Integer | PK, 自增 | 主键 |
| employment\_company | String(50) | NOT NULL | 公司名称 |
| create\_time | DateTime | default=now | 创建时间 |
| update\_time | DateTime | default=now, onupdate=now | 更新时间 |
| is\_deleted | Integer | default=0 | 软删除标记 |
### 3. Address(地址表)
| 字段 | 类型 | 约束 | 说明 |
| ------------ | ---------- | ------------------------- | --------------- |
| id | Integer | PK, 自增 | 主键 |
| somewhere | String(50) | NOT NULL | 地址区域,如 "深圳市南山区" |
| create\_time | DateTime | default=now | 创建时间 |
| update\_time | DateTime | default=now, onupdate=now | 更新时间 |
| is\_deleted | Integer | default=0 | 软删除标记 |
### 表关系图
```
students (1) ───< (N) Employments (N) >─── (1) Company
classes (1) ───< (N) | (N) >─── (1) Address
```
* 一个学生可有多个就业记录,一条就业记录对应一个学生(通过 stuid 关联学生表)
* 一条就业记录对应一个公司(company\_id)与一个地址(address\_id)
* 通过多表 JOIN 可从就业记录直接获取学生姓名、班级名称、公司名称与地址信息
**设计说明:姓名 / 班级不冗余,走 JOIN 获取**
与 "在就业表冗余学生姓名" 的方案不同,本模块选择通过 `stuid`、`class_id` 外键关联学生表与班级表,查询时动态 JOIN 获取姓名与班级名。代价是查询多一次 JOIN;好处是学生姓名 / 班级变更后无需同步,数据天然一致。
## 四、API 功能清单(CRUD)
接口定义于 `CURD_api.py` 的 `CURD` 路由,由根 `../main.py` 以 `Shtudent_Jiuye` 挂载。
### 📌 新增(Create)
| 方法 | 路径 | 功能 | 请求体 |
| ---- | -------------------- | -------- | ----------- |
| POST | /emp/company/address | 新增地址区域 | AddRequest |
| POST | /emp/company | 新增公司 | CompRequest |
| POST | /emp | 新增学生就业信息 | PostRequest |
> 注意:由于外键依赖,建议按 Address → Company → Employments 的顺序添加。
### 📌 查询(Read)
| 方法 | 路径 | 功能 | 查询参数 |
| --- | ---- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| GET | /emp | 多条件查询就业信息 | sname、class\_id、employment\_company、min\_salary、max\_salary、somewhere、employment\_open\_start、employment\_open\_end、offer\_recived\_start、offer\_recived\_end(全部可选,均不传则查询全部) |
> 就业薪资 Top5、就业时长、平均就业时长等统计接口已迁移至统计分析模块(StatCalc):
>
> `/statcalc/top`
>
> 、
>
> `/statcalc/job`
>
> 、
>
> `/statcalc/avgjob`
>
> 。
### 📌 更新(Update)
| 方法 | 路径 | 功能 | 参数 |
| --- | --------- | ------ | ----------------------------------- |
| PUT | /emp?id=X | 修改就业信息 | Query:id;Body:PostRequest(支持部分字段更新) |
### 📌 删除(Delete)
| 方法 | 路径 | 功能 | 参数 |
| ------ | --------- | ------- | ------- |
| DELETE | /emp/{id} | 软删除就业信息 | Path:id |
**删除说明**:本模块采用软删除(`is_deleted = 1`),数据不会从物理表中移除,便于审计与恢复。
## 五、核心流程示例
### 查询流程(GET /emp)
```
前端 → CURD\_api.get\_stuinfo()
&#x20; ↓ 收集 10 个可选查询条件
&#x20; ↓ 调用 get\_emp\_dao()
&#x20; ↓ DAO 五表 OUTER JOIN:Employments + Students + ClassInfo + Company + Address
&#x20; ↓ 动态拼接 filter(姓名/班级/公司/薪资范围/地址/时间范围)
&#x20; ↓ 统一过滤 is\_deleted = 0
&#x20; ↓ 返回 list\[dict](含学生姓名、班级名称、公司、区域、薪资、时间)
&#x20; ↓ 结果为空 → 404「名单不存在」
```
### 添加流程(POST /emp)
```
前端 → CURD\_api.add\_stuinfo()
&#x20; ↓ p.model\_dump() 转 dict
&#x20; ↓ add\_employment\_dao()
&#x20; ↓ o.pop('id' / 'create\_time' / 'update\_time' / 'is\_deleted') 去掉自增主键与自动字段
&#x20; ↓ Employments(\*\*o) 实例化
&#x20; ↓ db.add → db.commit → db.refresh
&#x20; ↓ 失败 → db.rollback → 返回 500
```
### 更新流程(PUT /emp?id=X)
```
前端 → CURD\_api.updata\_stuinfo()
&#x20; ↓ 请求体排除未传字段(exclude\_unset)
&#x20; ↓ 为空 → 400「没有需要更新的字段」
&#x20; ↓ update\_emp\_dao() 按 id 执行 update
&#x20; ↓ 影响 0 行 → 404「未找到对应记录」
```
### 删除流程(DELETE /emp/{id})
```
前端 → CURD\_api.delete\_stuinfo()
&#x20; ↓ delete\_emp\_dao() 按 id 执行 UPDATE is\_deleted = 1(软删除)
&#x20; ↓ 影响 0 行 → 500「没有删除」
```
## 六、项目亮点
1. **清晰的分层架构**
路由(CURD\_api.py)、数据访问(dao.py)、模型(model.py)、校验(request.py)各司其职,便于单元测试与维护。
2. **全链路软删除**
Employments、Company、Address 三张表均有 `is_deleted` 字段,删除统一走 `UPDATE is_deleted = 1`,数据可追溯、可恢复;查询时统一过滤 `is_deleted == 0`。
3. **动态多条件组合查询**
`get_emp_dao` 使用链式 filter 动态拼接查询条件,任一条件为空自动跳过,一个接口覆盖 10 种查询维度(姓名 / 班级 / 公司 / 薪资区间 / 地址 / 开放时间区间 / Offer 时间区间)。
4. **统一时间戳审计**
所有表均带 `create_time` / `update_time`,`onupdate=datetime.now` 让修改自动记录时间,为审计、报表提供数据基础。
5. **请求体与响应体分离**
request.py 为每类资源定义独立请求体(PostRequest / CompRequest / AddRequest)与响应体(EmpResponse / CompResponse / AddResponse),前端契约清晰,Swagger 自动生成接口文档。
6. **多表 JOIN 关联查询**
就业记录与学生、班级、公司、地址四张表通过外键关联,一次查询即可返回完整就业信息,无需前端二次请求。
7. **外键约束保证数据完整性**
stuid /class\_id/company\_id /address\_id 全部建立外键,杜绝 "就业记录指向不存在学生 / 公司" 的脏数据。
## 七、待添加的新功能 与 已知问题
### 🎯 短期(可快速落地)
| 功能 | 说明 | 涉及改动 |
| ------- | ------------------------------------------------ | ---------------------------------------------- |
| 分页查询 | /emp 支持 page /page\_size,返回 total 与 items | dao.py 加 .offset ().limit (),request.py 加分页请求体 |
| 薪资区间校验 | 确保 min\_salary <= max\_salary | request.py 加 @model\_validator |
| 数据去重 | 同一学生同一公司只保留一条有效就业记录 | add\_employment\_dao 前置查询 + 唯一约束 |
| 级联删除 | 删公司 / 地址时同步软删其下就业记录 | dao.py 加级联更新逻辑 |
| 外键存在性校验 | 新增时校验 stuid /company\_id/address\_id 真实存在,给出明确错误 | dao.py 前置查询 |
### 🚀 中期(需要设计)
| 功能 | 说明 |
| ---------- | --------------------------------- |
| 批量导入 | 支持 Excel/CSV 批量导入就业信息 |
| 数据导出 | 查询结果一键导出为 Excel |
| JWT 鉴权 | 管理员 / 普通用户角色分离,保护写接口 |
| 操作日志 | 记录每一次增删改操作的操作人与时间 |
| Alembic 迁移 | 用 Alembic 管理表结构变更,告别手动 DROP TABLE |
### 🌟 长期(数据价值)
| 功能 | 说明 |
| ------ | ----------------------- |
| 就业数据看板 | 按班级 / 公司 / 薪资维度统计,前端可视化 |
| 薪资分布分析 | 直方图、分位数、同比环比 |
| 就业率追踪 | 结合学生表计算班级 / 专业就业率 |
| 缓存层 | Redis 缓存 Top5、统计类接口 |
| 异步任务 | Celery 处理导入 / 导出等耗时任务 |
### ⚠️ 当前已知问题(开发中待修复)
| 问题 | 位置 | 影响 | 建议修复 |
| ----------------- | -------------------------------------------------------- | -------------------- | ------------------------- |
| 查询结果按 5 变量解包 3 元组 | dao.py get\_emp\_dao | 查到数据即 ValueError 崩溃 | select 补齐 5 列,或解包变量与列数一致 |
| 空日期直接 strftime | dao.py 返回段 | 时间为空时 AttributeError | 判空后再格式化 |
| 删除 0 行返回 500 | CURD\_api.py delete\_stuinfo | 状态码语义错误 | 改 404 |
| 查询空结果返回 404 | CURD\_api.py get\_stuinfo | 正常空结果被当异常 | 改 200 + 空列表 + totals |
| 更新未过滤已删除记录 | dao.py update\_emp\_dao | 可修改已软删数据 | filter 加 is\_deleted == 0 |
| SQL 全量打印 | database.py echo=True | 开发噪音 / 性能损耗 | 生产环境关闭 |
| 命名不统一 | Shtudent\_Jiuye / updata\_stuinfo / offer\_recived\_time | 可读性差 | 统一规范命名 |
## 八、运行方式
```
\# 1. 安装依赖
pip install fastapi uvicorn sqlalchemy pymysql pydantic python-dotenv
\# 2. 配置数据库(项目根目录 .env 中 DB\_USER / DB\_PASSWORD / DB\_HOST / DB\_PORT / DB\_NAME)
\# 3. 启动服务(根入口,含全模块)
python main.py
\# 或
uvicorn main:app --host 127.0.0.1 --port 59000 --reload
\# 4. 单独调试就业模块
uvicorn stu\_jiuye.main:app --host 127.0.0.1 --port 12395 --reload
\# 5. 访问 Swagger 文档
http://127.0.0.1:59000/docs
```
## 九、注意事项
1. **表结构变更需删表重建**:`Base.metadata.create_all(engine)` 只建不改,模型加字段后需 DROP TABLE 或引入 Alembic。
2. **建表时机**:当前建表逻辑在根 `../main.py` 的 `__main__` 块中执行;直接用 `uvicorn main:app` 启动时不会建表,全新环境需先执行一次 `python main.py`(或后续将建表移入 FastAPI lifespan)。
3. **添加顺序有外键依赖**:先建学生 / 班级 / 公司 / 地址,再添加就业记录;Employments 的 stuid /class\_id/company\_id /address\_id 均受外键约束。
4. **软删除后查询自动过滤**:`get_emp_dao` 已统一过滤 `is_deleted == 0`,被删除的记录不会再出现在列表里。
5. **删除接口用 Path 参数**:`DELETE /emp/{id}`,不要写成 `/emp?id=X`。
6. **echo=True 仅限开发**:生产环境请关掉,避免刷屏与性能损耗。
-270
View File
@@ -1,270 +0,0 @@
# 学生就业信息管理系统 —— 就业模块项目文档
## 一、模块概述
本模块是「学生就业信息管理系统」的核心业务模块,负责管理学生就业状态的全生命周期。围绕就业信息(Employments)、公司信息(Company)、地址信息(Address)三张核心表,并与学生表(students)、班级表(classes)建立外键关联,提供增删改查(CRUD)与统计分析接口。
**技术栈:**
| 项 | 选型 |
|---|---|
| Web 框架 | FastAPI |
| ORM | SQLAlchemy 2.x |
| 数据库 | MySQL 8.x(驱动 PyMySQL) |
| 数据校验 | Pydantic v2 |
| 服务启动 | Uvicorn |
## 二、目录结构
```
student_manage_system/
├── main.py # 程序入口:注册全模块路由、建表(__main__ 中执行)
├── databases.py # 根数据库配置:engine_all / Base_all / Session / get_db
└── stu_jiuye/ # 就业模块业务包
├── main.py # 路由组装(Shtudent_Jiuye);独立自测 app
├── database.py # 模块级数据库配置(engine / Base / Session / get_db)
├── model.py # ORM 模型:Employments / Company / Address
├── request.py # Pydantic 请求体 / 响应体模型
├── dao.py # 数据访问层(纯数据库操作)
└── CURD_api.py # 路由层(API 接口定义)
```
**分层职责:**
| 层 | 文件 | 职责 |
|---|---|---|
| 路由层 | CURD_api.py | 接收 HTTP 请求,参数校验,调用 DAO,返回响应 |
| 数据访问层 | dao.py | 封装 SQLAlchemy 查询与事务,向上提供函数 |
| 模型层 | model.py | 定义表结构与外键关系 |
| 校验层 | request.py | 定义请求体 / 响应体的字段与类型 |
| 配置层 | database.py | 数据库 URL、引擎、Session 工厂 |
## 三、数据表设计
### 1. Employments(就业信息表)
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
| id | Integer | PK, 自增 | 主键 |
| stuid | Integer | FK → students.id | 学生 ID |
| class_id | Integer | FK → classes.id | 班级 ID |
| company_id | Integer | FK → Company.id, 可空 | 就业公司 |
| address_id | Integer | FK → Address.id, NOT NULL | 就业地址 |
| employment_salary | Integer | 可空 | 就业薪资 |
| employment_open_time | DATE | 可空 | 就业开放时间 |
| offer_recived_time | DATE | 可空 | Offer 下发时间 |
| create_time | DateTime | default=now | 创建时间 |
| update_time | DateTime | default=now, onupdate=now | 更新时间 |
| is_deleted | Integer | default=0 | 软删除标记(0 正常 / 1 已删) |
### 2. Company(公司表)
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
| id | Integer | PK, 自增 | 主键 |
| employment_company | String(50) | NOT NULL | 公司名称 |
| create_time | DateTime | default=now | 创建时间 |
| update_time | DateTime | default=now, onupdate=now | 更新时间 |
| is_deleted | Integer | default=0 | 软删除标记 |
### 3. Address(地址表)
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
| id | Integer | PK, 自增 | 主键 |
| somewhere | String(50) | NOT NULL | 地址区域,如"深圳市南山区" |
| create_time | DateTime | default=now | 创建时间 |
| update_time | DateTime | default=now, onupdate=now | 更新时间 |
| is_deleted | Integer | default=0 | 软删除标记 |
### 表关系图
```
students (1) ───< (N) Employments (N) >─── (1) Company
classes (1) ───< (N) | (N) >─── (1) Address
```
- 一个学生可有多个就业记录,一条就业记录对应一个学生(通过 stuid 关联学生表)
- 一条就业记录对应一个公司(company_id)与一个地址(address_id)
- 通过多表 JOIN 可从就业记录直接获取学生姓名、班级名称、公司名称与地址信息
**设计说明:姓名/班级不冗余,走 JOIN 获取**
与"在就业表冗余学生姓名"的方案不同,本模块选择通过 `stuid`、`class_id` 外键关联学生表与班级表,查询时动态 JOIN 获取姓名与班级名。代价是查询多一次 JOIN;好处是学生姓名/班级变更后无需同步,数据天然一致。
## 四、API 功能清单(CRUD)
接口定义于 `stu_jiuye/CURD_api.py` 的 `CURD` 路由,由根 `main.py` 以 `Shtudent_Jiuye` 挂载。
### 📌 新增(Create)
| 方法 | 路径 | 功能 | 请求体 |
|---|---|---|---|
| POST | /emp/company/address | 新增地址区域 | AddRequest |
| POST | /emp/company | 新增公司 | CompRequest |
| POST | /emp | 新增学生就业信息 | PostRequest |
> 注意:由于外键依赖,建议按 Address → Company → Employments 的顺序添加。
### 📌 查询(Read)
| 方法 | 路径 | 功能 | 查询参数 |
|---|---|---|---|
| GET | /emp | 多条件查询就业信息 | sname、class_id、employment_company、min_salary、max_salary、somewhere、employment_open_start、employment_open_end、offer_recived_start、offer_recived_end(全部可选,均不传则查询全部) |
> 就业薪资 Top5、就业时长、平均就业时长等统计接口已迁移至统计分析模块(StatCalc):`/statcalc/top`、`/statcalc/job`、`/statcalc/avgjob`。
### 📌 更新(Update)
| 方法 | 路径 | 功能 | 参数 |
|---|---|---|---|
| PUT | /emp?id=X | 修改就业信息 | Query:id;Body:PostRequest(支持部分字段更新) |
### 📌 删除(Delete)
| 方法 | 路径 | 功能 | 参数 |
|---|---|---|---|
| DELETE | /emp/{id} | 软删除就业信息 | Path:id |
**删除说明**:本模块采用软删除(`is_deleted = 1`),数据不会从物理表中移除,便于审计与恢复。
## 五、核心流程示例
### 查询流程(GET /emp)
```
前端 → CURD_api.get_stuinfo()
↓ 收集 10 个可选查询条件
↓ 调用 get_emp_dao()
↓ DAO 五表 OUTER JOIN:Employments + Students + ClassInfo + Company + Address
↓ 动态拼接 filter(姓名/班级/公司/薪资范围/地址/时间范围)
↓ 统一过滤 is_deleted = 0
↓ 返回 list[dict](含学生姓名、班级名称、公司、区域、薪资、时间)
↓ 结果为空 → 404「名单不存在」
```
### 添加流程(POST /emp)
```
前端 → CURD_api.add_stuinfo()
↓ p.model_dump() 转 dict
↓ add_employment_dao()
↓ o.pop('id' / 'create_time' / 'update_time' / 'is_deleted') 去掉自增主键与自动字段
↓ Employments(**o) 实例化
↓ db.add → db.commit → db.refresh
↓ 失败 → db.rollback → 返回 500
```
### 更新流程(PUT /emp?id=X)
```
前端 → CURD_api.updata_stuinfo()
↓ 请求体排除未传字段(exclude_unset)
↓ 为空 → 400「没有需要更新的字段」
↓ update_emp_dao() 按 id 执行 update
↓ 影响 0 行 → 404「未找到对应记录」
```
### 删除流程(DELETE /emp/{id})
```
前端 → CURD_api.delete_stuinfo()
↓ delete_emp_dao() 按 id 执行 UPDATE is_deleted = 1(软删除)
↓ 影响 0 行 → 500「没有删除」
```
## 六、项目亮点
1. **清晰的分层架构**
路由(CURD_api.py)、数据访问(dao.py)、模型(model.py)、校验(request.py)各司其职,便于单元测试与维护。
2. **全链路软删除**
Employments、Company、Address 三张表均有 `is_deleted` 字段,删除统一走 `UPDATE is_deleted = 1`,数据可追溯、可恢复;查询时统一过滤 `is_deleted == 0`。
3. **动态多条件组合查询**
`get_emp_dao` 使用链式 filter 动态拼接查询条件,任一条件为空自动跳过,一个接口覆盖 10 种查询维度(姓名/班级/公司/薪资区间/地址/开放时间区间/Offer 时间区间)。
4. **统一时间戳审计**
所有表均带 `create_time` / `update_time`,`onupdate=datetime.now` 让修改自动记录时间,为审计、报表提供数据基础。
5. **请求体与响应体分离**
request.py 为每类资源定义独立请求体(PostRequest / CompRequest / AddRequest)与响应体(EmpResponse / CompResponse / AddResponse),前端契约清晰,Swagger 自动生成接口文档。
6. **多表 JOIN 关联查询**
就业记录与学生、班级、公司、地址四张表通过外键关联,一次查询即可返回完整就业信息,无需前端二次请求。
7. **外键约束保证数据完整性**
stuid / class_id / company_id / address_id 全部建立外键,杜绝"就业记录指向不存在学生/公司"的脏数据。
## 七、待添加的新功能 与 已知问题
### 🎯 短期(可快速落地)
| 功能 | 说明 | 涉及改动 |
|---|---|---|
| 分页查询 | /emp 支持 page / page_size,返回 total 与 items | dao.py 加 .offset().limit(),request.py 加分页请求体 |
| 薪资区间校验 | 确保 min_salary <= max_salary | request.py 加 @model_validator |
| 数据去重 | 同一学生同一公司只保留一条有效就业记录 | add_employment_dao 前置查询 + 唯一约束 |
| 级联删除 | 删公司/地址时同步软删其下就业记录 | dao.py 加级联更新逻辑 |
| 外键存在性校验 | 新增时校验 stuid / company_id / address_id 真实存在,给出明确错误 | dao.py 前置查询 |
### 🚀 中期(需要设计)
| 功能 | 说明 |
|---|---|
| 批量导入 | 支持 Excel/CSV 批量导入就业信息 |
| 数据导出 | 查询结果一键导出为 Excel |
| JWT 鉴权 | 管理员 / 普通用户角色分离,保护写接口 |
| 操作日志 | 记录每一次增删改操作的操作人与时间 |
| Alembic 迁移 | 用 Alembic 管理表结构变更,告别手动 DROP TABLE |
### 🌟 长期(数据价值)
| 功能 | 说明 |
|---|---|
| 就业数据看板 | 按班级/公司/薪资维度统计,前端可视化 |
| 薪资分布分析 | 直方图、分位数、同比环比 |
| 就业率追踪 | 结合学生表计算班级/专业就业率 |
| 缓存层 | Redis 缓存 Top5、统计类接口 |
| 异步任务 | Celery 处理导入/导出等耗时任务 |
###
| | | | |
|---|---|---|---|
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
## 八、运行方式
```bash
# 1. 安装依赖
pip install fastapi uvicorn sqlalchemy pymysql pydantic python-dotenv
# 2. 配置数据库(项目根目录 .env 中 DB_USER / DB_PASSWORD / DB_HOST / DB_PORT / DB_NAME)
# 3. 启动服务(根入口,含全模块)
python main.py
# 或
uvicorn main:app --host 127.0.0.1 --port 59000 --reload
# 4. 单独调试就业模块
uvicorn stu_jiuye.main:app --host 127.0.0.1 --port 12395 --reload
# 5. 访问 Swagger 文档
http://127.0.0.1:59000/docs
```
## 九、注意事项
1. **表结构变更需删表重建**:`Base.metadata.create_all(engine)` 只建不改,模型加字段后需 DROP TABLE 或引入 Alembic。
2. **建表时机**:当前建表逻辑在根 `main.py` 的 `__main__` 块中执行;直接用 `uvicorn main:app` 启动时不会建表,全新环境需先执行一次 `python main.py`(或后续将建表移入 FastAPI lifespan)。
3. **添加顺序有外键依赖**:先建学生/班级/公司/地址,再添加就业记录;Employments 的 stuid / class_id / company_id / address_id 均受外键约束。
4. **软删除后查询自动过滤**:`get_emp_dao` 已统一过滤 `is_deleted == 0`,被删除的记录不会再出现在列表里。
5. **删除接口用 Path 参数**:`DELETE /emp/{id}`,不要写成 `/emp?id=X`。
6. **echo=True 仅限开发**:生产环境请关掉,避免刷屏与性能损耗。
+161 -14
View File
@@ -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 即可使用。
+1 -1
View File
@@ -30,7 +30,7 @@ def get_students( stu:StudentsQuery = Depends()
f'注意事项:\n'
f'时间格式:YYYY-MM-DD\n'
f'班级ID和顾问ID必须已经存在才可以绑定'
,response_model=StudentsResponse )
)
def add_student( stu:StudentsResponse = Depends()
, db=Depends(get_db)
):
+11 -5
View File
@@ -66,7 +66,13 @@ def add_student_dao( s , db):
if not phone:
raise ValueError("手机号不能为空")
if not s.get("class_id") or not s.get("advisor_id"):
raise ValueError("班级ID和顾问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")
@@ -92,16 +98,16 @@ def add_student_dao( s , db):
return s1_deleted
s1 = Students( **s )
db.add(s1)
db.commit()
except ValueError as e:
# 业务校验异常:回滚并返回错误信息
db.rollback()
return {"code": 400, "msg": str(e)}
except Exception:
except Exception as e:
# 这里可以加日志
db.rollback()
return {"code": 500, "msg": f"系统异常: {str(e)}"}
else:
db.commit()
db.refresh(s1)
return s1
@@ -147,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
+2 -2
View File
@@ -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)