diff --git a/stu_jiuye/就业模块文档.md b/stu_jiuye/就业模块文档.md new file mode 100644 index 0000000..b421a09 --- /dev/null +++ b/stu_jiuye/就业模块文档.md @@ -0,0 +1,377 @@ +# 学生就业信息管理系统 —— 就业模块项目文档 + +## 一、模块概述 + +本模块是「学生就业信息管理系统」的核心业务模块,负责管理学生就业状态的全生命周期。围绕就业信息(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() + + ↓ 收集 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 处理导入 / 导出等耗时任务 | + +### ⚠️ 当前已知问题(开发中待修复) + + + +| 问题 | 位置 | 影响 | 建议修复 | +| ----------------- | -------------------------------------------------------- | -------------------- | ------------------------- | +| 查询结果按 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 仅限开发**:生产环境请关掉,避免刷屏与性能损耗。 \ No newline at end of file