docs(crm): 更新架构快照 + 提炼插件开发 Skill
- CLAUDE.md §12 新增 CRM 插件完成记录和 erp-plugin-crm 模块 - §13 新增动态表 SQL 注入防护和插件权限注册反模式 - §10 scope 表补充 plugin/crm 范围 - §11 设计文档索引补充 CRM 插件设计和实施计划 - 新建 .claude/skills/plugin-development/SKILL.md 可复用插件开发流程
This commit is contained in:
185
.claude/skills/plugin-development/SKILL.md
Normal file
185
.claude/skills/plugin-development/SKILL.md
Normal file
@@ -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<u8>) -> 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 前缀
|
||||
@@ -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 页面) | ✅ 完成 |
|
||||
|
||||
<!-- ARCH-SNAPSHOT-END -->
|
||||
|
||||
@@ -494,6 +499,8 @@ chore(docker): 添加 PostgreSQL 健康检查
|
||||
- ❌ **不要**假设只有单租户 — 从第一天就按多租户设计
|
||||
- ❌ **不要**提前实现远期功能 — 严格按 Phase 计划推进
|
||||
- ❌ **不要**忽略 `version` 字段 — 所有更新操作必须检查乐观锁
|
||||
- ❌ **不要**在动态表 SQL 中拼接用户输入 — 使用 `sanitize_identifier` 防注入
|
||||
- ❌ **不要**在插件 crate 中直接依赖 erp-auth — 权限注册用 raw SQL,保持模块边界
|
||||
|
||||
### 场景化指令
|
||||
|
||||
|
||||
Reference in New Issue
Block a user