400 lines
14 KiB
Markdown
400 lines
14 KiB
Markdown
# Project 领域重构指南
|
||
|
||
## 1. 范围
|
||
|
||
本文档只做 Project 领域的项目侧梳理。
|
||
|
||
必须覆盖两类字段:
|
||
|
||
- 集团侧 `enum_config` 对应到 Project 的字段:`project_type`、`project_subtype`、`syxs`、`status`
|
||
- depm 本地已经做成 enum / 类 enum,或者已经承担初始化、创建校验、业务分支职责的字段:`progress_type`、`key_constraints`、`build_methods`
|
||
|
||
本文重点只回答四件事:
|
||
|
||
- 当前有几种实现
|
||
- 模型层怎么存
|
||
- 是否真的用到了
|
||
- 创建、校验、初始化时怎么取值,以及下一步怎么改
|
||
|
||
## 2. 字段盘点
|
||
|
||
### 2.1 `project_type`
|
||
|
||
当前实现:
|
||
|
||
- `app/common/enums.py` 有一套 `ProjectType`
|
||
- `app/modules/project/enums.py` 又有一套 `ProjectType`
|
||
- `Project` 模型里是 `String`
|
||
- `ProjectCreate` / `ProjectUpdate` 里是 `str`
|
||
|
||
当前使用:
|
||
|
||
- `ProjectService.init_project_data)` 用它作为初始化路由键,去取 `preparation_{project_type}`、`production_{project_type}`、`power_{project_type}` 等配置
|
||
- construction 模块的部分接口和工具也直接复用 `ProjectType`
|
||
|
||
当前问题:
|
||
|
||
- 定义重复
|
||
- 模型层不是 enum
|
||
- 创建 / 更新入口没有真正按 enum 校验
|
||
|
||
项目侧取值范围:
|
||
|
||
- 当前代码里的正式候选项是:`photovoltaic`、`onshore_wind`、`offshore_wind`、`pumped_storage`、`hydropower`、`thermal_power`、`gas_turbine`
|
||
- `app/common/enums.py`、`app/modules/project/enums.py`、初始化 `dict_data.csv` 当前三处是一致的
|
||
|
||
集团侧差异:
|
||
|
||
- 当前未看到和项目侧不同的取值项
|
||
- 这个字段目前可视为两边一致
|
||
- 但同步 schema 里的 `project_type` 字段当前仍写成 `str`,并没有直接收紧成 `ProjectType`
|
||
|
||
结论:
|
||
|
||
- 保留为代码 enum
|
||
- 它是核心路由字段,不迁到 `field_config`
|
||
|
||
### 2.2 `project_subtype`
|
||
|
||
当前实现:
|
||
|
||
- 模型里是 `String`
|
||
- `ProjectCreate` / `ProjectUpdate` 里是 `str`
|
||
- 没看到本地正式 enum
|
||
|
||
当前使用:
|
||
|
||
- 当前主要是项目主表字段
|
||
- 在 Project 模块内没看到稳定初始化使用点
|
||
|
||
当前问题:
|
||
|
||
- 没有统一候选项来源
|
||
- 没有统一校验
|
||
- 现在只是“字符串字段”
|
||
|
||
项目侧取值范围:
|
||
|
||
- 目前列不出权威范围
|
||
- 当前项目侧没有正式 enum、没有 `field_config`、没有 schema 白名单
|
||
- 从代码看,它现在理论上就是任意字符串
|
||
|
||
项目侧可举例范围:
|
||
|
||
- 目前只能借集团侧样例来猜测,例如光伏下可能是 `centralized`、`distributed`
|
||
|
||
集团侧差异:
|
||
|
||
- 集团侧至少给出了光伏下的两个候选项:`centralized`、`distributed`
|
||
- 项目侧目前一个正式值都没有落下来
|
||
- 其它项目类型在集团侧也基本还是空占位,所以当前只能确认“项目侧缺正式范围,集团侧只给了局部样例”
|
||
- 同步 schema 对应字段名是 `sub_type`,当前类型是 `Optional[str]`,也还没有 enum 化
|
||
|
||
困难:
|
||
|
||
- 项目侧没有正式定义源,无法从代码中反推出完整候选项集合
|
||
- 集团侧除光伏外也没有给出有效值,导致现在只能列出局部范围,不能写成全项目类型完整表
|
||
|
||
结论:
|
||
|
||
- 改为 `field_config`
|
||
- 建议 key:`project.project_subtype`
|
||
|
||
### 2.3 `syxs`
|
||
|
||
当前实现:
|
||
|
||
- 模型里是 `String`
|
||
- 当前 `ProjectCreate` / `ProjectUpdate` 里没有这个字段
|
||
- 没看到本地正式 enum
|
||
|
||
当前使用:
|
||
|
||
- 在 Project 模块内暂时没看到明确创建、初始化或校验使用点
|
||
- 当前更像是“模型里已有字段,但项目侧链路尚未收敛”的状态
|
||
|
||
当前问题:
|
||
|
||
- 有字段,但没有统一入口
|
||
- 没有统一候选项来源
|
||
- 没有统一校验
|
||
|
||
项目侧取值范围:
|
||
|
||
- 目前列不出权威范围
|
||
- 项目模型里有这个字段,但 Project 创建 schema 里没有该字段
|
||
- 代码中也没找到项目侧正式 enum 或统一校验入口
|
||
|
||
项目侧可举例范围:
|
||
|
||
- 当前只能参考集团侧海风场景,例如 `hssy`、`lssy`
|
||
|
||
集团侧差异:
|
||
|
||
- 集团侧至少给出了海风下的两个候选项:`hssy`、`lssy`
|
||
- 项目侧目前还没有正式范围、正式入口和正式校验
|
||
- 其它项目类型在集团侧也基本还是空占位,所以当前差异主要是“集团侧有局部定义,项目侧还没落下来”
|
||
- 同步 schema 中 `syxs` 当前也是 `Optional[str]`,还没有 enum 化
|
||
|
||
困难:
|
||
|
||
- 项目侧没有正式输入入口,也没有权威候选项定义
|
||
- 集团侧只有海风场景给了有效值,因此暂时只能列局部范围
|
||
|
||
结论:
|
||
|
||
- 改为 `field_config`
|
||
- 建议 key:`project.syxs`
|
||
|
||
### 2.4 `status`
|
||
|
||
当前实现:
|
||
|
||
- `app/modules/project/enums.py` 里有 `ProjectStatus`
|
||
- `app/common/enums.py` 里也有一套 `ProjectStatus`
|
||
- 模型里是 `String`
|
||
- 响应 schema 里是 `str`
|
||
|
||
当前使用:
|
||
|
||
- `ProjectService.change_project_status()` 会根据 `investment_decision_date`、`actual_construction_date`、`actual_production_date`、`actual_full_production_date` 计算并写回 `status`
|
||
|
||
当前问题:
|
||
|
||
- 定义重复
|
||
- 模型层不是 enum
|
||
- 当前状态值由服务逻辑推导,但接口层没有统一约束
|
||
- 当前正式状态集合必须补上 `early`
|
||
- 当前库里还存在 `NULL`,这说明状态机和历史数据都还没收口干净
|
||
|
||
项目侧取值范围:
|
||
|
||
- 目标正式值应为:`early`、`preparation`、`started`、`partially_connected`、`completed`
|
||
- 当前代码定义和服务推导还没有完整覆盖这 5 个值
|
||
- 当前实际数据里还存在 `NULL`,这应视为待清理异常值,而不是正式取值
|
||
|
||
集团侧差异:
|
||
|
||
- 项目侧最终也要纳入 `early`
|
||
- 因此这个字段的目标差异不是“是否要 early”,而是“项目侧何时补齐 early 并清掉 NULL”
|
||
- 同步 schema 里的 `status` 当前也是 `Optional[str]`,没有正式 enum 类型
|
||
|
||
结论:
|
||
|
||
- 保留为代码 enum
|
||
- 正式状态集合按 `early`、`preparation`、`started`、`partially_connected`、`completed` 收口
|
||
- `NULL` 不是正式值,必须通过数据修复和状态回填消除
|
||
|
||
### 2.5 `progress_type`
|
||
|
||
当前实现:
|
||
|
||
- `app/common/enums.py` 和 `app/modules/project/enums.py` 都定义了 `ProgressType`
|
||
- 模型里是 `SQLEnum(ProgressType)`
|
||
- `ProjectInfoResponse` 里使用 `ProgressType`
|
||
- `ProjectCreate` 里仍是 `str`
|
||
|
||
当前使用:
|
||
|
||
- 是 Project 主表正式字段
|
||
- `project_detail_data` 初始化时会写入 `progress_type`
|
||
- 列表筛选也会直接按它查询
|
||
|
||
当前问题:
|
||
|
||
- 定义重复
|
||
- 模型层已收紧,但创建入口还没收紧
|
||
|
||
项目侧取值范围:
|
||
|
||
- 当前正式值是:`A`、`B`、`C`
|
||
- `app/common/enums.py`、`app/modules/project/enums.py`、初始化 `dict_data.csv` 当前是一致的
|
||
|
||
集团侧差异:
|
||
|
||
- 集团侧同步 schema 中,`progress_type` 也是正式 enum
|
||
- 同步 schema 的取值项是:`A`、`B`、`C`
|
||
- 这一点和项目侧代码 enum 一致
|
||
- 当前真正的差异不在取值范围,而在项目侧创建入口仍是 `str`,还没有像同步 schema 一样收紧到 enum 类型
|
||
|
||
困难:
|
||
|
||
- 这个字段属于 depm 本地已枚举化字段,不是集团侧 `Project` 段直出字段
|
||
- 因此这里只能写“项目侧权威范围明确,集团侧暂无直连字段”
|
||
|
||
结论:
|
||
|
||
- 保留为代码 enum
|
||
- 不迁到 `field_config`
|
||
|
||
### 2.6 `key_constraints`
|
||
|
||
当前实现:
|
||
|
||
- `app/common/enums.py` 和 `app/modules/project/enums.py` 都定义了 `KeyConstraints`
|
||
- 两套定义已分叉:common 里有 `OTHER`,project 模块里没有
|
||
- 模型里是 `SQLEnum(KeyConstraints)`
|
||
- `ProjectInfoResponse` 里使用 `KeyConstraints`
|
||
- `ProjectSquareUpdate` 里还是 `str`
|
||
|
||
当前使用:
|
||
|
||
- 是 Project 主表正式字段
|
||
- `project_detail_data` 初始化时会和 `progress_type` 一起写入
|
||
- `project_detail_data` 审批回写时也会更新 Project 主表
|
||
|
||
当前问题:
|
||
|
||
- 定义分叉
|
||
- 主表是 enum,但部分接口入参仍是字符串
|
||
- 项目侧正式口径应保留 `OTHER`
|
||
|
||
项目侧取值范围:
|
||
|
||
- 项目侧正式值应为:`NONE`、`LAND`、`TRANSMISSION`、`BOTH`、`OTHER`
|
||
- 当前 `app/common/enums.py` 和初始化 `dict_data.csv` 已包含这 5 个值
|
||
- 当前 `app/modules/project/enums.py` 还少 `OTHER`
|
||
|
||
集团侧差异:
|
||
|
||
- 集团侧同步 schema 中,`key_constraints` 也是正式 enum
|
||
- 同步 schema 的取值项是:`NONE`、`LAND`、`TRANSMISSION`、`BOTH`、`QT`
|
||
- 项目侧正式口径保留 `OTHER`
|
||
- 这里需要备注的差异只有一条:集团侧用 `QT`,项目侧用 `OTHER`
|
||
|
||
结论:
|
||
|
||
- 保留为代码 enum
|
||
- 项目侧按 `OTHER` 收口
|
||
- 在同步和推送链路上单独备注 `OTHER/QT` 差异
|
||
- 同时补齐 project 模块那套缺失的 `OTHER`
|
||
|
||
### 2.7 `build_methods`
|
||
|
||
当前实现:
|
||
|
||
- `app/modules/project/enums.py` 里有 `BuildMethod`
|
||
- 模型里是 `String`
|
||
- `ProjectCreate` 里是 `str`
|
||
- 响应 schema 通过 `enum_to_build_method_name` 做名称映射
|
||
|
||
当前使用:
|
||
|
||
- construction 服务里确实使用了 `project.build_methods`
|
||
- 它会影响施工分支判断,例如 `power`、`mixed`、`none`、`self`
|
||
|
||
当前问题:
|
||
|
||
- 模型层不是 enum
|
||
- 创建入口不是 enum
|
||
- 已经参与业务分支,但没有统一校验
|
||
|
||
项目侧取值范围:
|
||
|
||
- 当前代码 enum 给出的值是:`self`、`power`、`mixed`、`none`
|
||
- construction 分支判断当前也确实在使用这 4 个值
|
||
|
||
集团侧差异:
|
||
|
||
- 集团侧同步 schema 里没有 `build_methods` 这个名字,对应字段是 `scgc_type`
|
||
- `scgc_type` 当前在同步 schema 里是 `Optional[str]`
|
||
- 目前没有看到同步 schema 中为 `scgc_type` 定义正式 enum
|
||
|
||
也就是说:
|
||
|
||
- 语义上两边大概率是在说同一个字段
|
||
- 但集团侧当前还没有把它收紧成正式 enum
|
||
- 因此这个字段更像“项目侧本地先有 enum,集团侧 schema 还停留在字符串字段”
|
||
|
||
困难:
|
||
|
||
- 这是项目侧本地业务字段,不是集团侧 `Project` 段直出字段
|
||
- 项目侧虽然已有 enum,但模型和 schema 还没完全收紧
|
||
|
||
结论:
|
||
|
||
- 保留为代码 enum
|
||
- 不迁到 `field_config`
|
||
- 需要把模型层和 schema 层一起收紧
|
||
|
||
## 3. 重点结论
|
||
|
||
- `project_type` 是初始化路由键,必须保留为代码 enum。
|
||
- `project_subtype`、`syxs` 是典型的按项目类型变化字段,应迁到 `field_config`。
|
||
- `progress_type`、`key_constraints`、`build_methods` 都已经在项目侧真实使用,不是可忽略字段;它们应保留为代码 enum,但当前实现没有收紧干净。
|
||
- `status` 也应保留为代码 enum,正式集合必须包含 `early`,并且要清掉历史 `NULL`。
|
||
- `key_constraints` 项目侧正式口径保留 `OTHER`,只需要在文档和同步链路上备注集团侧使用 `QT`。
|
||
|
||
## 4. 待修改项清单
|
||
|
||
1. 统一 `ProjectType` 定义,只保留一套正式 enum。
|
||
2. 统一 `ProjectStatus` 定义,并把 `early` 纳入正式状态集合。
|
||
3. 统一 `ProgressType` 定义,只保留一套正式 enum。
|
||
4. 统一 `KeyConstraints` 定义,项目侧正式保留 `OTHER`。
|
||
5. 将 `project.project_subtype` 改为字段级配置,放入 `field_config`。
|
||
6. 将 `project.syxs` 改为字段级配置,放入 `field_config`。
|
||
7. 为 `project_type` 在 schema 层增加正式 enum 校验,不再使用裸 `str`。
|
||
8. 为 `progress_type` 在创建入口增加正式 enum 校验。
|
||
9. 为 `build_methods` 在 schema 层增加正式 enum 校验。
|
||
10. 为 `key_constraints` 在仍使用字符串入参的接口上补 enum 校验。
|
||
11. 清理 `Project.status` 的历史 `NULL` 数据,并补状态回填逻辑,确保 `NULL` 不再出现。
|
||
12. 评估 `Project` 模型中的 `project_type`、`status`、`build_methods` 是否要收紧为数据库 enum;如果暂不收紧,也要先统一 schema 校验。
|
||
13. 为 `project_subtype`、`syxs` 增加统一读取入口:`get_field_options()` / `validate_field_value()`。
|
||
14. 明确 `status` 的唯一来源:是继续由 `change_project_status()` 推导,还是允许手动写入。
|
||
|
||
## 5. 推荐落地顺序
|
||
|
||
1. 先统一 `ProjectType`、`ProgressType`、`KeyConstraints`、`ProjectStatus` 的定义源。
|
||
2. 再补 schema 层校验,把 `project_type`、`progress_type`、`build_methods`、`key_constraints` 的裸字符串入口收紧。
|
||
3. 先补 `status=early` 和历史 `NULL` 清理,再把 `project_subtype`、`syxs` 迁到 `field_config`。
|
||
|
||
## 6. 按本轮要求的改造落点
|
||
|
||
project 领域这轮要区分两类字段:
|
||
|
||
- 固定语义枚举:继续保留代码 enum
|
||
- 按项目类型变化的候选项:迁到 field config
|
||
|
||
### 6.1 初始化
|
||
|
||
Project 本身不直接批量初始化子表候选项,但 `project_type` 是整个初始化流程的路由键。
|
||
|
||
因此本轮在 project 领域的初始化要求是:
|
||
|
||
- `project_type` 继续保留代码 enum,作为 config 路由键
|
||
- `project_subtype`、`syxs` 如果进入初始化或默认值链路,应直接从 field config 读取
|
||
|
||
### 6.2 Create / Update 校验
|
||
|
||
这轮 project 领域要分开处理:
|
||
|
||
- `project_type`、`status`、`progress_type`、`key_constraints`、`build_methods`:继续按代码 enum 收紧
|
||
- `project_subtype`、`syxs`:通过新的 config 工具校验
|
||
|
||
也就是说,project 领域不是“全部配置化”,而是“固定 enum 和 field config 分流后分别收紧”。
|
||
|
||
### 6.3 下拉列表
|
||
|
||
当前 project 领域里:
|
||
|
||
- `project_type`、`progress_type`、`key_constraints`、`build_methods` 更适合继续由代码 enum 提供候选项
|
||
- `project_subtype`、`syxs` 后续如果提供前端下拉,应直接走 field config
|
||
|
||
本轮目标是明确边界,避免把本地固定枚举误迁到 config,也避免把按项目类型变化字段继续留成自由字符串。
|
||
|
||
### 6.4 清理项
|
||
|
||
project 领域本轮改造完成后,应清理:
|
||
|
||
- 重复的 enum 定义源
|
||
- 仍以裸字符串接收固定 enum 字段的入口
|
||
- `status` 历史 `NULL` 值
|
||
|
||
## 7. 测试关注点
|
||
|
||
project 领域 service 层集成测试至少覆盖:
|
||
|
||
1. `project_type`、`progress_type`、`key_constraints`、`build_methods` 的合法/非法值校验。
|
||
2. `project_subtype`、`syxs` 的 field config 读取与校验。
|
||
3. `status` 状态推导补齐 `early` 且不再产生 `NULL`。
|