# Material 领域重构指南

## 1. 范围

本文档只做 Material 主表梳理。

当前优先关注：

- `material.material_type`
- `material.status`
- `material.supply_status`
- `material.approval_party`

## 2. 字段盘点

### 2.1 `material_type`

当前实现：

- 本地没有独立 enum 类
- 主要来源是 `contract_product_type_list`
- 模型里是 `String`
- 创建 schema 里是 `str`
- 同步 schema 里也是 `str`

当前使用：

- 是物资主类型字段
- schema 示例和业务逻辑都直接依赖 `contract_product_type_list`

项目侧取值范围：

- 当前本地明确值：`TY`、`ZJ`、`NB`、`XB`、`ZB`、`GS`、`CN`、`DL`、`XT`、`CBJDB`、`SX`、`ECXT`、`QT`

集团侧差异：

- 集团侧 `enum_config.Material.material_type` 是按项目类型变化的
- 光伏下有：`ZJ`、`TY`、`NB`、`XB`、`ZB`、`GS`、`CN`、`QT`
- 陆风/海风下有：`FJ`、`TT`、`XB`、`ZB`、`GS`、`CN`、`QT`
- 项目侧当前使用的本地列表明显偏光伏，且代码里还存在命名差异：例如本地用 `XT` / `CBJDB` / `SX` / `ECXT`，集团侧常见的是 `35KV` / `CYJD` / `SVG` / `ECPG`
- 同步 schema 里 `material_type` 仍是 `str`

结论：

- 应迁到 `field_config`
- 不能继续只靠本地 `contract_product_type_list`

### 2.2 `status`

当前实现：

- 模型里是 `SmallInteger`
- 同步 schema 里是 `Optional[int]`

项目侧取值范围：

- 当前未看到单独 enum 类
- 现有注释和使用都表明它是数值状态字段

集团侧差异：

- 集团侧 `enum_config.Material.status` 给的是 `-1`、`0`、`1`
- 项目侧没有在 material 模块里沉成统一常量

结论：

- 应统一正式状态定义源

### 2.3 `supply_status`

当前实现：

- 模型里是 `SmallInteger`
- 本地有 `SupplyStatus` enum：`-1`、`0`、`1`、`None`

项目侧取值范围：

- `-1`
- `0`
- `1`
- `None`

集团侧差异：

- 集团侧 Material.status 也是按 `-1`、`0`、`1` 表达进度状态
- 项目侧存在 `status` 和 `supply_status` 两个相近字段，语义有重叠

结论：

- 需要明确 `status` 和 `supply_status` 哪个才是正式推送字段

### 2.4 `approval_party`

当前实现：

- 模型里是 `String`
- schema 里是 `str`
- 当前示例值是 `jiagong`、`yigong`

项目侧取值范围：

- `jiagong`
- `yigong`

集团侧差异：

- 当前没看到集团侧 `enum_config` 对这个字段给出正式候选项
- 同步 schema 里也没有这个字段

结论：

- 先保留本地代码级候选项
- 后续如需推送再决定是否外提

## 3. 重点结论

- `material_type` 是标准的 `field_config` 候选字段，不能再继续绑死在本地光伏型列表上。
- `status` 与 `supply_status` 当前语义重叠，必须先定正式推送字段。
- `approval_party` 暂时还是本地专有字段，不需要现在就硬并到集团侧。

## 4. 待修改项清单

1. 将 `material_type` 正式迁到 `field_config`。
2. 按项目类型重建 `material_type` 候选项，不再只用本地光伏型列表。
3. 统一 `35KV/XT`、`CYJD/CBJDB`、`SVG/SX`、`ECPG/ECXT` 这类命名差异。
4. 明确 `status` 与 `supply_status` 的职责边界。
5. 为 `material_type` 在 schema 层增加统一校验入口。

## 5. 按本轮要求的改造落点

material 是本轮最需要针对性改造的领域之一，因为它当前同时存在：

- 初始化来源分叉
- create 校验分叉
- 下拉来源分叉

### 5.1 初始化

当前 material 初始化不是单源：

- 候选项来自 `contract_config` 里的 product 合同
- 单位和部分展示信息来自 `material_contract_{project_type}` dict

而且 `unit` 现在还挂在 dict `remark` 上。

本轮目标是：

- 明确 `material.material_type` 的正式配置来源
- 把 `label`、`unit`、`parts` 等附属字段纳入正式 config 结构
- 去掉“从 dict remark 补单位”这种不适当用法

### 5.2 Create 校验

当前 create 路径里：

- `material_type` 入参本身没有正式 config 校验
- `show_name`、`unit` 由 `contract_product_type_list` 反查
- 随后又会调用合同创建，由合同 service 再做一层 code 白名单校验

这说明 material create 现在不是单一入口校验。

本轮目标是：

- 在 `MaterialService.create()` 开头基于 config 校验 `material_type`
- 用 `get_field_option_map()` 之类的方法直接拿 `label`、`unit`、`parts`
- 不再依赖 `contract_product_type_list` 和默认兜底值

### 5.3 下拉列表

当前物资下拉已经部分走 config：

- `/material/material-types` 读 `material_types`
- `/material-detail/contract-product-type/list` 也读 `material_types`

但模块里仍保留了旧的硬编码路径：

- `get_material_info_by_code()`
- `get_kit_options()`
- `contract_product_type_list`

本轮目标是：

- 让物资类型和部件列表统一从新的 config 工具返回
- 下拉、初始化、create 校验全部围绕同一套 `material_type` 配置

### 5.4 清理项

material 领域这轮改造完成后，应优先清理：

- `material_contract_*` 中用 `remark` 承载单位的旧逻辑
- `contract_product_type_list` 里只服务于 `material_type` 真源的旧定义
- `get_material_info_by_code()` 这类旧反查入口

## 6. 测试关注点

material 领域 service 层集成测试至少覆盖：

1. 初始化：项目初始化后 material 的 `material_type`、`show_name`、`unit` 是否来自目标 config。
2. 下拉：物资类型和部件列表是否来自同一 config，且与初始化同源。
3. Create：合法 `material_type` 能创建，非法 `material_type` 明确报错，不再依赖默认兜底。

