121 lines
5.1 KiB
Markdown
121 lines
5.1 KiB
Markdown
# 步骤四:后端 API 实现
|
||
|
||
> 前置条件:step-03 已完成。
|
||
> 实施依据:`docs/crm-migration/plans/{module}/06-steps.md` 中的后端实现步骤。
|
||
> 同步参考:[ref-database-rules.md](ref-database-rules.md)、[ref-lessons-learned.md](ref-lessons-learned.md)
|
||
> 模块方案:`docs/crm-migration/plans/{module}/04-api-spec.md`(API规格)、`docs/crm-migration/plans/{module}/02-database.md`(数据库)
|
||
|
||
---
|
||
|
||
## 核心原则
|
||
|
||
**先理解 Java 业务逻辑,再写 Python 代码。** 每个接口先读 Java 源码(Controller → Service → Mapper),用中文写出业务逻辑说明,再编码。
|
||
|
||
### Java 快照(按需,SHALL)
|
||
|
||
**写到哪一步,快照哪一步的 Java**——不在方案阶段一次性拷贝整个模块。
|
||
|
||
1. 打开 `plans/{module}/06-steps.md`,找到当前步骤的「Sub-step 0 快照」清单
|
||
2. 执行 `docs/crm-migration/scripts/snapshot-java.sh {module} <files...>`
|
||
3. 只加载快照目录中**本步文件** + `05-special.md`,再写 Python
|
||
|
||
权威源:`../shanxia/crm/backend/ruoyi-modules/ruoyi-crm/`。快照目录:`docs/crm-migration/reference/java/`。
|
||
|
||
## 操作流程
|
||
|
||
### 1. 数据库先行
|
||
|
||
按 [ref-database-rules.md](ref-database-rules.md) 规范:
|
||
|
||
1. 编写迁移后 DDL(业务字段与 sx.sql 一致,公共字段按适配规则调整)
|
||
2. 编写 SQL 增量脚本保存到 `docs/sql/`
|
||
3. 更新 SQLAlchemy Model(`backend/app/models/crm_models.py`)
|
||
4. 导入字典数据(如需新建,从 sx.sql 筛选 `crm_%` 字典)
|
||
|
||
### 2. Schema 层
|
||
|
||
创建 `backend/app/schemas/crm/{module}.py`:
|
||
|
||
- CreateBody、UpdateBody、ListBody 等 Pydantic v2 模型
|
||
- 使用 `Field(..., alias="camelCase")` 实现命名转换
|
||
- 参考 `backend/app/schemas/crm/product.py`
|
||
|
||
### 3. CRUD 层
|
||
|
||
创建 `backend/app/crud/crm/{module}.py`:
|
||
|
||
- `_row_to_dict`:ORM → camelCase dict(所有可读字段的**单点真相**)
|
||
- `list_xxx`:分页查询(租户隔离 + 软删除过滤 + create_time DESC)
|
||
- `create_xxx`:新增(UUID主键 + 编号生成 + 唯一校验 + 冗余字段写入)
|
||
- `update_xxx`:更新
|
||
- `soft_delete_xxx`:软删除(del_flag=1)
|
||
- 参考 `backend/app/crud/crm/product.py`
|
||
|
||
### 4. API 路由层
|
||
|
||
在 `backend/app/api/crm.py` 中追加路由:
|
||
|
||
- 每个接口一个路由函数
|
||
- `Depends(require_admin_or_super_admin)` 认证
|
||
- 路由路径与前端 API 调用一致
|
||
|
||
### 5. API 路径约定
|
||
|
||
| 操作 | 路径格式 | 示例 |
|
||
|------|---------|------|
|
||
| 列表 | `/web/crm/{module}/list` | POST,含 page/pageSize |
|
||
| 新增 | `/web/crm/{module}/add` | POST |
|
||
| 编辑 | `/web/crm/{module}/update` | POST |
|
||
| 删除 | `/web/crm/{module}/delete` | POST |
|
||
| 详情 | `/web/crm/{module}/detail` | POST |
|
||
| 下拉 | `/web/crm/{module}/options` | POST |
|
||
| 导出 | `/web/crm/{module}/export` | POST,返回 xlsx 流(非 JSON) |
|
||
| 导入模板 | `/web/crm/{module}/importTemplate` 或 `/import-template` | POST,返回 xlsx 流 |
|
||
|
||
### 7. Excel 导出(CRM 集中字典翻译)
|
||
|
||
对齐 Java `ExcelUtil.exportExcel` + `*Vo` 的 `@ExcelDictFormat`:
|
||
|
||
1. **列定义**:在 `backend/app/utils/crm_export_columns.py` 增加 `XXX_EXPORT_COLUMNS`(`ExportColumn(title, field, dict_type=...)`)。
|
||
2. **字典对照**:打开原 CRM `domain/vo/CrmXxxVo.java`,逐字段核对 `dictType`(如线索 `crm_clue_source`、`crm_clue_level`)。
|
||
3. **路由实现**:查询列表数据后 **只调用** `export_rows_to_xlsx_response(db, tenant_no=..., columns=..., rows=..., filename_stem=...)`。
|
||
4. **禁止**:在 `crm.py` 内重复 openpyxl 表头/循环;禁止导出原始 dict code(除非该列 Java 也无 `@ExcelDictFormat`)。
|
||
5. **依赖**:`openpyxl` 须在运行 venv 中安装(见 `ref-lessons-learned.md` §30)。
|
||
|
||
参考实现:`backend/app/utils/excel_export.py`;已接入:线索/客户/产品/联系人/跟进/商机。
|
||
|
||
### 8. 定时任务 & 特殊场景
|
||
|
||
- 检查原 CRM 是否有定时任务,在 `backend/app/tasks/` 中用 APScheduler 实现
|
||
- 在 API 路由中处理触发器逻辑(如统计字段同步更新)
|
||
|
||
## 常见业务场景处理
|
||
|
||
| 场景 | 处理方式 |
|
||
|------|---------|
|
||
| 编号自动生成 | CRUD 层 create_xxx 中实现,参考 customer.py |
|
||
| 唯一性校验 | CRUD 层校验,`del_flag=0` 排除软删除 |
|
||
| 关联统计更新 | 新增/删除关联记录时同步更新主表 |
|
||
| 冗余字段写入 | create/update 时从主表获取写入 |
|
||
| 租户隔离 | 所有查询 WHERE 加 `tenant_no` |
|
||
| 数据权限 | 列表查询根据角色数据范围过滤 |
|
||
|
||
## 字段一致性
|
||
|
||
- `_row_to_dict` 是所有可读字段的**单点真相**
|
||
- CreateBody/UpdateBody 的字段必须与 `_row_to_dict` 中可写字段一一对应
|
||
- 前端 `formData` 初始化必须包含 CreateBody 中的所有字段
|
||
- 可用 `python scripts/check_field_consistency.py` 验证
|
||
|
||
## 完成标准
|
||
|
||
- [ ] DDL 已编写并保存到 docs/sql/
|
||
- [ ] Model、Schema、CRUD、API 路由全部实现
|
||
- [ ] 每个接口的业务逻辑与 Java 源码一致
|
||
- [ ] 字段一致性检查通过
|
||
- [ ] 若有导出:已在 `crm_export_columns.py` 声明列与 `dict_type`,并走 `export_rows_to_xlsx_response`
|
||
|
||
## 下一步
|
||
|
||
完成后加载 [step-05-verify.md](step-05-verify.md)。
|