From b482230a072196a2d3af732cd4a8853456ff89d8 Mon Sep 17 00:00:00 2001 From: iven Date: Thu, 16 Apr 2026 19:23:54 +0800 Subject: [PATCH] =?UTF-8?q?docs(crm):=20=E6=9B=B4=E6=96=B0=E6=9E=B6?= =?UTF-8?q?=E6=9E=84=E5=BF=AB=E7=85=A7=20+=20=E6=8F=90=E7=82=BC=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E5=BC=80=E5=8F=91=20Skill?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - CLAUDE.md §12 新增 CRM 插件完成记录和 erp-plugin-crm 模块 - §13 新增动态表 SQL 注入防护和插件权限注册反模式 - §10 scope 表补充 plugin/crm 范围 - §11 设计文档索引补充 CRM 插件设计和实施计划 - 新建 .claude/skills/plugin-development/SKILL.md 可复用插件开发流程 --- .claude/skills/plugin-development/SKILL.md | 185 +++++++++++++++++++++ CLAUDE.md | 9 +- 2 files changed, 193 insertions(+), 1 deletion(-) create mode 100644 .claude/skills/plugin-development/SKILL.md diff --git a/.claude/skills/plugin-development/SKILL.md b/.claude/skills/plugin-development/SKILL.md new file mode 100644 index 0000000..7158a16 --- /dev/null +++ b/.claude/skills/plugin-development/SKILL.md @@ -0,0 +1,185 @@ +# 插件开发 Skill + +基于 CRM 客户管理插件的开发经验,提炼可复用的插件开发流程和模式。 + +## 触发场景 + +- 用户说"开发一个新插件"、"新建行业模块"、"创建插件" +- 用户提到需要在 ERP 平台上扩展新的业务模块 + +## 插件开发流程 + +### 第一步:需求分析 → 数据模型 + +1. 确定插件 ID(如 `erp-crm`、`erp-inventory`) +2. 列出实体及其字段,为每个字段标注: + - `field_type`: String/Integer/Float/Boolean/Date/DateTime/Uuid/Decimal/Json + - `required` / `unique` / `searchable` / `filterable` / `sortable` + - `visible_when`: 条件显示表达式(如 `type == 'enterprise'`) + - `ui_widget`: 表单控件(input/select/textarea/datepicker) + - `options`: select 类型的选项列表 + +### 第二步:编写 plugin.toml manifest + +```toml +[metadata] +id = "erp-xxx" +name = "模块名称" +version = "0.1.0" +description = "描述" +author = "ERP Team" +min_platform_version = "0.1.0" + +# 权限:{entity}.{list|manage} +[[permissions]] +code = "entity.list" +name = "查看 XX" + +[[permissions]] +code = "entity.manage" +name = "管理 XX" + +# 实体定义 +[[schema.entities]] +name = "entity" +display_name = "实体名" + +[[schema.entities.fields]] +name = "field_name" +field_type = "String" +required = true +display_name = "字段名" +searchable = true + +# 页面声明 +[[ui.pages]] +type = "crud" +entity = "entity" +label = "页面标题" +icon = "icon-name" +enable_search = true +``` + +### 第三步:创建 Rust crate + +```bash +mkdir -p crates/erp-plugin-xxx/src +``` + +**Cargo.toml**: +```toml +[package] +name = "erp-plugin-xxx" +version = "0.1.0" +edition = "2024" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +wit-bindgen = "0.55" +serde = { workspace = true } +serde_json = { workspace = true } +``` + +**src/lib.rs**: +```rust +wit_bindgen::generate!({ + path: "../erp-plugin-prototype/wit/plugin.wit", + world: "plugin-world", +}); + +use crate::exports::erp::plugin::plugin_api::Guest; + +struct XxxPlugin; + +impl Guest for XxxPlugin { + fn init() -> Result<(), String> { Ok(()) } + fn on_tenant_created(_tenant_id: String) -> Result<(), String> { Ok(()) } + fn handle_event(_event_type: String, _payload: Vec) -> Result<(), String> { Ok(()) } +} + +export!(XxxPlugin); +``` + +### 第四步:注册到 workspace + +根 `Cargo.toml` 的 `[workspace] members` 添加 `"crates/erp-plugin-xxx"`。 + +### 第五步:编译和转换 + +```bash +cargo build -p erp-plugin-xxx --target wasm32-unknown-unknown --release +wasm-tools component new target/wasm32-unknown-unknown/release/erp_plugin_xxx.wasm -o target/erp_plugin_xxx.component.wasm +``` + +### 第六步:上传和测试 + +PluginAdmin 页面上传 `.component.wasm` + `plugin.toml`:上传 → 安装 → 启用。 + +## 可用页面类型 + +| 类型 | 说明 | 必填配置 | +|------|------|----------| +| `crud` | 增删改查表格 | `entity`, `label` | +| `tree` | 树形展示 | `entity`, `label`, `id_field`, `parent_field`, `label_field` | +| `detail` | 详情 Drawer | `entity`, `label`, `sections` | +| `tabs` | 标签页容器 | `label`, `tabs`(子页面列表) | + +## detail section 类型 + +| 类型 | 说明 | 配置 | +|------|------|------| +| `fields` | 字段描述列表 | `label`, `fields`(字段名数组) | +| `crud` | 嵌套 CRUD 表格 | `label`, `entity`, `filter_field` | + +## 字段属性速查 + +| 属性 | 说明 | +|------|------| +| `searchable` | 可搜索,自动创建 B-tree 索引 | +| `filterable` | 可筛选,前端渲染 Select | +| `sortable` | 可排序,表格列头排序图标 | +| `visible_when` | 条件显示,格式 `field == 'value'` | +| `unique` | 唯一约束,CREATE UNIQUE INDEX | +| `ui_widget` | 控件:select / textarea | +| `options` | select 选项 `[{label, value}]` | + +## 权限规则 + +- 格式:`{entity}.{list|manage}` +- 安装时自动加 manifest_id 前缀 +- REST API 动态检查,无精细权限时回退 `plugin.list` / `plugin.admin` + +## REST API + +| 方法 | 路径 | 说明 | +|------|------|------| +| GET | `/api/v1/plugins/{id}/{entity}` | 列表(filter/search/sort) | +| POST | `/api/v1/plugins/{id}/{entity}` | 创建(required 校验) | +| GET | `/api/v1/plugins/{id}/{entity}/{rid}` | 详情 | +| PUT | `/api/v1/plugins/{id}/{entity}/{rid}` | 更新(乐观锁) | +| DELETE | `/api/v1/plugins/{id}/{entity}/{rid}` | 软删除 | +| GET | `/api/v1/plugins/{id}/{entity}/count` | 统计 | +| GET | `/api/v1/plugins/{id}/{entity}/aggregate` | 聚合 | + +## 测试检查清单 + +- [ ] `cargo check --workspace` 通过 +- [ ] `cargo test --workspace` 通过 +- [ ] WASM 编译 + Component 转换成功 +- [ ] 上传 → 安装 → 启用流程正常 +- [ ] CRUD 完整可用 +- [ ] 唯一字段重复插入返回冲突 +- [ ] filter/search/sort 查询正常 +- [ ] visible_when 条件字段动态显示 +- [ ] 侧边栏菜单正确生成 + +## 常见陷阱 + +1. 表名格式:`plugin_{sanitized_id}_{sanitized_entity}`,连字符变下划线 +2. edition 必须是 "2024" +3. WIT 路径:`../erp-plugin-prototype/wit/plugin.wit`,不是 `erp-plugin` +4. JSONB 无外键约束,Uuid 字段不自动校验引用完整性 +5. Fuel 限制 1000 万,简单逻辑足够,避免重计算循环 +6. manifest 中只写 `entity.action`,安装时自动加 manifest_id 前缀 diff --git a/CLAUDE.md b/CLAUDE.md index 0c8f756..856bd5c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -413,7 +413,8 @@ cargo test -p erp-plugin-prototype # 运行插件集成测试 | `message` | erp-message | | `config` | erp-config | | `server` | erp-server | -| `plugin` | erp-plugin-prototype / erp-plugin-test-sample | +| `plugin` | erp-plugin / erp-plugin-prototype / erp-plugin-test-sample | +| `crm` | erp-plugin-crm | | `web` | Web 前端 | | `ui` | React 组件 | | `db` | 数据库迁移 | @@ -439,6 +440,8 @@ chore(docker): 添加 PostgreSQL 健康检查 | `docs/superpowers/plans/2026-04-10-erp-platform-base-plan.md` | 平台底座实施计划 | | `docs/superpowers/specs/2026-04-13-wasm-plugin-system-design.md` | WASM 插件系统设计规格 | | `docs/superpowers/plans/2026-04-13-wasm-plugin-system-plan.md` | WASM 插件原型验证计划 | +| `docs/superpowers/specs/2026-04-16-crm-plugin-design.md` | CRM 客户管理插件设计规格 | +| `docs/superpowers/plans/2026-04-16-crm-plugin-plan.md` | CRM 插件实施计划 | 所有设计决策以设计规格文档为准。实施计划按阶段拆分,每阶段开始前细化。 @@ -461,6 +464,7 @@ chore(docker): 添加 PostgreSQL 健康检查 | Phase 6 | 整合与打磨 | ✅ 完成 | | - | WASM 插件原型 (V1-V6) | ✅ 验证通过 | | - | 插件系统集成到主服务 | ✅ 已集成 | +| - | CRM 插件 (Phase 1-3) | ✅ 完成 | ### 已实现模块 @@ -476,6 +480,7 @@ chore(docker): 添加 PostgreSQL 健康检查 | erp-plugin | 插件管理 (WASM 运行时/生命周期/动态表/数据CRUD) | ✅ 已集成 | | erp-plugin-prototype | WASM 插件 Host 运行时 (Wasmtime + bindgen + Host API) | ✅ 原型验证 | | erp-plugin-test-sample | WASM 测试插件 (Guest trait + Host API 回调) | ✅ 原型验证 | +| erp-plugin-crm | CRM 客户管理插件 (5 实体/9 权限/6 页面) | ✅ 完成 | @@ -494,6 +499,8 @@ chore(docker): 添加 PostgreSQL 健康检查 - ❌ **不要**假设只有单租户 — 从第一天就按多租户设计 - ❌ **不要**提前实现远期功能 — 严格按 Phase 计划推进 - ❌ **不要**忽略 `version` 字段 — 所有更新操作必须检查乐观锁 +- ❌ **不要**在动态表 SQL 中拼接用户输入 — 使用 `sanitize_identifier` 防注入 +- ❌ **不要**在插件 crate 中直接依赖 erp-auth — 权限注册用 raw SQL,保持模块边界 ### 场景化指令