Files
erp/.claude/skills/plugin-development/SKILL.md
iven b482230a07 docs(crm): 更新架构快照 + 提炼插件开发 Skill
- CLAUDE.md §12 新增 CRM 插件完成记录和 erp-plugin-crm 模块
- §13 新增动态表 SQL 注入防护和插件权限注册反模式
- §10 scope 表补充 plugin/crm 范围
- §11 设计文档索引补充 CRM 插件设计和实施计划
- 新建 .claude/skills/plugin-development/SKILL.md 可复用插件开发流程
2026-04-16 19:23:54 +08:00

186 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.

# 插件开发 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 前缀