step-04-backend.md 5.1 KB

步骤四:后端 API 实现

前置条件:step-03 已完成。 实施依据:docs/crm-migration/plans/{module}/06-steps.md 中的后端实现步骤。 同步参考:ref-database-rules.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 规范:

  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。