Files
erp/plans/fluffy-painting-bachman.md
iven 841766b168
Some checks failed
CI / rust-check (push) Has been cancelled
CI / rust-test (push) Has been cancelled
CI / frontend-build (push) Has been cancelled
CI / security-audit (push) Has been cancelled
fix(用户管理): 修复用户列表页面加载失败问题
修复用户列表页面加载失败导致测试超时的问题,确保页面元素正确渲染
2026-04-19 08:46:28 +08:00

8.8 KiB
Raw Blame History

CRM 插件平台标杆 — P0 基础能力设计

Context

CRM 插件作为 ERP 平台的第一个行业插件,目前暴露了插件平台的多项基础能力缺口。本次设计的定位不是"CRM 功能最全",而是"插件平台能力最扎实"——通过 CRM 验证的每个能力都应被所有未来插件inventory、生产、财务等零改动复用。

对标一流 CRMSalesforce/HubSpot/Pipedrive的差距分析表明当前 CRM 更接近"客户通讯录+标签+图谱",距离可用 CRM 有显著差距。但这些差距中,最优先补的是平台基础设施,而非 CRM 业务功能。

设计决策记录

决策点 选择 理由
目标定位 插件平台标杆 打磨通用能力,所有插件受益
推进节奏 P0 基础先行 基础不扎实,上层的业务功能无法可靠运行
实体关系复杂度 全覆盖 (1:N/N:1/N:N/自引用) CRM 和 inventory 都需要,一次到位
字段校验范围 完整套件 (6种) 数据质量是所有插件的生命线
前端硬编码 全部通用化 第二个插件必须零改动可用

P0-1: 实体关系声明 + ref_entity + 级联策略

Manifest Schema 扩展

在 entity 下新增 [[relations]] 段:

[[schema.entities.relations]]
name = "contacts"                    # 关系名
target_entity = "contact"            # 目标实体
type = "one_to_many"                 # one_to_many | many_to_one | many_to_many
foreign_key = "customer_id"          # FK 字段
on_delete = "cascade_soft_delete"    # cascade_soft_delete | set_null | restrict
display_field = "name"               # 下拉框显示字段

# N:N 需要中间表
[[schema.entities.relations]]
name = "related_customers"
target_entity = "customer"
type = "many_to_many"
through_entity = "customer_relationship"
through_source_field = "from_customer_id"
through_target_field = "to_customer_id"

后端实现 (crates/erp-plugin/)

关键文件:

  • src/manifest.rs — ManifestParser 新增 relations 解析
  • src/dynamic_table.rs — 安装时存储关系到 entity metadata
  • src/data_service.rs — 删除时执行级联策略,创建/更新时验证 FK
  • src/handler/data_handler.rs — 错误响应格式

级联策略执行流程:

DELETE /plugins/{id}/{entity}/{rid}
  → 查询 entity 的所有 incoming relations (被引用的关系)
  → for each relation:
      cascade_soft_delete → UPDATE child SET deleted_at=now() WHERE fk=rid
      set_null            → UPDATE child SET fk=NULL WHERE fk=rid
      restrict            → SELECT COUNT children, if >0 return 409 Conflict
  → 软删除目标记录

FK 存在性校验:

POST/PUT /plugins/{id}/{entity}
  → for each field with ref_entity:
      SELECT EXISTS(SELECT 1 FROM plugin_xxx_{ref_entity} WHERE id=field_value AND deleted_at IS NULL)
      → 不存在则返回 400 + 具体字段错误

前端实现 (apps/web/)

关键文件:

  • src/pages/PluginCRUDPage.tsx — 自动为 ref_entity 字段渲染 EntitySelect
  • src/pages/PluginDetailPage — 自动渲染关联子实体内嵌列表
  • src/components/EntitySelect.tsx — 增强支持 display_field 配置
  • src/api/plugins.ts — schema 类型新增 relations

详情页自动关联渲染:

  • 读取 entity 的 outgoing relations (one_to_many)
  • 为每个 relation 渲染内嵌 CRUD 表格compact 模式,带 filter=fk:parent_id
  • 级联删除前弹出确认("将同时删除 3 条联系人"

CRM plugin.toml 改造

为 5 个实体补充 relations 声明:

  • customer → contacts (1:N, cascade_soft_delete)
  • customer → communications (1:N, cascade_soft_delete)
  • customer → tags (1:N, cascade_soft_delete)
  • customer → parent (N:1, set_null, 自引用)
  • contact → communications (1:N, cascade_soft_delete)

P0-2: 字段校验层

Manifest Schema 扩展

在 field 下新增 [validation] 子结构:

[[schema.entities.fields]]
name = "phone"
field_type = "string"

[schema.entities.fields.validation]
pattern = "^1[3-9]\\d{9}$"
message = "请输入有效的手机号码"
min_length = 11
max_length = 11

[[schema.entities.fields]]
name = "email"
field_type = "string"

[schema.entities.fields.validation]
pattern = "^[\\w.-]+@[\\w.-]+\\.\\w+$"
message = "请输入有效的邮箱地址"
max_length = 254

后端校验器 (crates/erp-plugin/src/validation.rs — 新文件)

6 种校验器统一执行:

校验器 触发条件 错误格式
required field.required = true {field}: 不能为空
unique field.unique = true {field}: 该值已存在
pattern validation.pattern regex match {field}: {validation.message}
ref_exists field.ref_entity FK 查询 {field}: 引用的{entity}不存在
min_length / max_length validation.min_length / max_length {field}: 长度必须在 {min}-{max} 之间
min_value / max_value validation.min_value / max_value {field}: 值必须在 {min}-{max} 之间

执行位置: data_service.rs 的 create/update 方法中,数据写入前统一调用。

错误响应:

{
  "success": false,
  "error": "数据验证失败",
  "details": [
    { "field": "phone", "message": "请输入有效的手机号码" },
    { "field": "customer_id", "message": "引用的客户不存在" }
  ]
}

前端校验生成

从 schema 自动生成 Ant Design Form rules

  • required{ required: true, message: "..." }
  • pattern{ pattern: /regex/, message: "..." }
  • min_length / max_length{ min: n, max: n, message: "..." }

CRM plugin.toml 补充校验

  • phone: pattern 手机号
  • email: pattern 邮箱
  • credit_code: pattern 统一社会信用代码 (18位)
  • website: pattern URL
  • customer_id: ref_entity = "customer" (FK 校验)

P0-3: 前端去硬编码

Dashboard 通用化

文件: apps/web/src/pages/dashboard/dashboardConstants.tsx, PluginDashboardPage.tsx

改造方案:

  • 移除 ENTITY_COLORSENTITY_ICONS 硬编码映射
  • 颜色自动分配: 8 色调色板按 entity 顺序循环
  • 图标从 page schema 的 icon 字段读取
  • 标题: {manifest.name} 统计概览,副标题: {manifest.description}
  • Widget 定义从 page schema 的 widgets 数组读取

Graph 通用化

文件: apps/web/src/pages/plugins/graph/graphConstants.ts

改造方案:

  • 移除 RELATIONSHIP_COLORS 硬编码
  • 关系类型标签从 field.options 读取 (已有 label 映射)
  • 颜色用调色板按 option 顺序循环分配
  • 未知类型 fallback 到灰色 + 原始 label

CRUD 表格列可配置

文件: PluginCRUDPage.tsx

改造方案:

  • manifest page 新增 table_columns: ["name", "customer_type", "level", "status", "owner_id"]
  • 不声明则默认取前 8 个非 hidden 非 FK 字段
  • 移除 fields.slice(0, 5) 硬编码

验证标准

换成 inventory 插件Dashboard/Graph/CRUD 应该零改动正确渲染。


关键文件清单

文件 改动类型 说明
crates/erp-plugin/src/manifest.rs 修改 新增 relations + validation 解析
crates/erp-plugin/src/validation.rs 新建 校验引擎
crates/erp-plugin/src/data_service.rs 修改 集成级联策略 + 校验
crates/erp-plugin/src/dynamic_table.rs 修改 安装时存储关系元数据
crates/erp-plugin/src/handler/data_handler.rs 修改 FK 校验错误格式
crates/erp-plugin-crm/plugin.toml 修改 补充 relations + validation
apps/web/src/pages/dashboard/dashboardConstants.tsx 修改 去硬编码,通用调色板
apps/web/src/pages/dashboard/DashboardWidgets.tsx 修改 schema 驱动
apps/web/src/pages/PluginDashboardPage.tsx 修改 通用标题/副标题
apps/web/src/pages/plugins/graph/graphConstants.ts 修改 关系类型从 options 读取
apps/web/src/pages/PluginCRUDPage.tsx 修改 可配置列数
apps/web/src/api/plugins.ts 修改 类型定义更新

验证方案

  1. 编译检查: cargo check 全 workspace 通过
  2. 单元测试: validation.rs 每种校验器独立测试
  3. 集成测试: Testcontainers 验证级联删除/FK 校验/unique 冲突
  4. 功能验证:
    • 重新安装 CRM 插件,确认 5 个 relation 正确注册
    • 删除客户 → 联系人/沟通记录/标签级联软删除
    • 创建联系人 → customer_id 不存在时返回 400
    • 手机号/邮箱格式不正确时返回校验错误
    • Dashboard 切换 inventory 插件时正确渲染
  5. 前端验证: pnpm dev 启动后手动测试所有页面

输出产物

  1. 设计规格文档: docs/superpowers/specs/2026-04-18-crm-plugin-platform-p0-design.md
  2. 实施计划: 通过 writing-plans skill 生成
  3. 知识库文档: 记录讨论过程和决策理由