Files
shanxia2.0/docs/crm-migration/step-04-backend.md
T
2026-06-01 08:21:21 +08:00

121 lines
5.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 步骤四:后端 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)。