feat(freelance): 编译 WASM 并验证安装 — 10 实体/20 权限已创建

This commit is contained in:
iven
2026-04-19 23:59:03 +08:00
parent 813df3688c
commit fc76793d6d
13 changed files with 4140 additions and 203 deletions

View File

@@ -0,0 +1,484 @@
# 汕头市智界科技 IT 服务插件 — 实施计划
> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 为汕头市智界科技有限公司创建 freelance自由职业者工作台和 itopsIT 运维服务台)两个 WASM 插件,覆盖其全部 12 条经营范围。
**Architecture:** 两个独立的 WASM 插件 crate每个包含 Cargo.tomlcdylib、src/lib.rsGuest trait 实现、plugin.toml声明式 schema。通过插件安装 API 上传到系统平台自动创建动态表、注册权限、生成前端页面。itops 通过 ref_plugin 跨插件引用 freelance 的 client 实体。
**Tech Stack:** Rust (wit-bindgen 0.55, cdylib → WASM Component)、TOML manifest、Axum Host API
**Spec:** `docs/superpowers/specs/2026-04-19-shantou-zhijie-it-services-plugins-design.md`
---
## Chunk 1: freelance 插件
### Task 1: 创建 crate 目录和 Cargo.toml
**Files:**
- Create: `crates/erp-plugin-freelance/Cargo.toml`
- Create: `crates/erp-plugin-freelance/src/lib.rs`(空文件占位)
- [ ] **Step 1: 创建目录结构**
```bash
mkdir -p crates/erp-plugin-freelance/src
```
- [ ] **Step 2: 编写 Cargo.toml**
创建 `crates/erp-plugin-freelance/Cargo.toml`
```toml
[package]
name = "erp-plugin-freelance"
version = "0.1.0"
edition = "2024"
description = "自由职业者工作台 WASM 插件"
[lib]
crate-type = ["cdylib"]
[dependencies]
wit-bindgen = "0.55"
```
- [ ] **Step 3: 编写 src/lib.rs**
创建 `crates/erp-plugin-freelance/src/lib.rs`
```rust
//! 自由职业者工作台 WASM 插件
wit_bindgen::generate!({
path: "../erp-plugin-prototype/wit/plugin.wit",
world: "plugin-world",
});
use crate::exports::erp::plugin::plugin_api::Guest;
struct FreelancePlugin;
impl Guest for FreelancePlugin {
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!(FreelancePlugin);
```
- [ ] **Step 4: 注册到 workspace**
编辑根 `Cargo.toml`,在 `members` 数组末尾添加:
```toml
"crates/erp-plugin-freelance",
```
- [ ] **Step 5: 验证编译**
```bash
cargo check -p erp-plugin-freelance
```
Expected: 编译通过,无错误
- [ ] **Step 6: Commit**
```bash
git add crates/erp-plugin-freelance/ Cargo.toml
git commit -m "feat(freelance): 创建插件 crate 骨架"
```
---
### Task 2: 编写 plugin.tomlfreelance
**Files:**
- Create: `crates/erp-plugin-freelance/plugin.toml`
- [ ] **Step 1: 从设计规格文档复制完整 plugin.toml 内容**
从设计规格 `docs/superpowers/specs/2026-04-19-shantou-zhijie-it-services-plugins-design.md` 中提取 2.1(元数据)+ 2.2(权限)+ 2.310 个实体)+ 2.4(编号规则)+ 2.5(页面声明)的所有 TOML 内容,合并为完整的 `plugin.toml` 文件。
文件结构:
1. `[metadata]`
2. `[[permissions]]` × 20
3. `[[schema.entities]]` × 10client, opportunity, quote, quote_line, contract, project, task, time_entry, invoice, expense每个实体包含 fields 和 relations
4. `[[numbering]]` × 3quote_number, contract_number, invoice_number
5. `[[ui.pages]]` × 7dashboard, tabs+detail+kanban for client, crud+detail for project, tabs for finance, crud for expense
注意要点:
- client 实体必须标记 `is_public = true`(被 itops 跨插件引用)
- quote 到 quote_line 有 cascade 关系
- project 到 task 和 time_entry 有 cascade 关系
- 所有 uuid 引用字段使用 `ui_widget = "entity_select"` + `ref_label_field` + `ref_search_fields`
- 所有 select 字段使用 `options = [{ label = "X", value = "x" }]` 格式
- 长文本使用 `field_type = "string"` + `ui_widget = "textarea"`
- 金额使用 `field_type = "decimal"`
- 时间戳使用 `field_type = "date_time"`
- [ ] **Step 2: 验证 TOML 格式**
```bash
cargo check -p erp-plugin-freelance
```
- [ ] **Step 3: Commit**
```bash
git add crates/erp-plugin-freelance/plugin.toml
git commit -m "feat(freelance): 添加 plugin.toml — 10 实体/20 权限/7 页面"
```
---
### Task 3: 编译 WASM 并安装
- [ ] **Step 1: 编译为 WASM**
```bash
cargo build -p erp-plugin-freelance --target wasm32-unknown-unknown --release
```
Expected: 编译成功,产出 `target/wasm32-unknown-unknown/release/erp_plugin_freelance.wasm`
- [ ] **Step 2: 转换为 Component**
```bash
wasm-tools component new target/wasm32-unknown-unknown/release/erp_plugin_freelance.wasm -o target/erp_plugin_freelance.component.wasm
```
- [ ] **Step 3: 检查产物大小**
```bash
ls -la target/erp_plugin_freelance.component.wasm
```
Expected: < 100KBCRM 22KB
- [ ] **Step 4: 启动后端服务**
```bash
cd crates/erp-server && cargo run
```
等待服务启动完成看到 "listening on 0.0.0.0:3000" 日志
- [ ] **Step 5: 登录获取 Token**
```bash
curl -s -X POST http://localhost:3000/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"admin123"}' | jq -r '.data.access_token'
```
保存输出的 token
- [ ] **Step 6: 上传安装插件**
```bash
TOKEN="<上一步的 token>"
MANIFEST=$(cat crates/erp-plugin-freelance/plugin.toml)
curl -s -X POST http://localhost:3000/api/v1/admin/plugins/upload \
-H "Authorization: Bearer $TOKEN" \
-F "wasm=@target/erp_plugin_freelance.component.wasm" \
-F "manifest=$MANIST"
```
Expected: 返回插件 ID状态为 `installed`
- [ ] **Step 7: 启用插件**
使用上一步返回的插件 ID
```bash
PLUGIN_ID="<返回的插件 ID>"
curl -s -X POST "http://localhost:3000/api/v1/admin/plugins/$PLUGIN_ID/enable" \
-H "Authorization: Bearer $TOKEN"
```
Expected: 状态变为 `running`
- [ ] **Step 8: Commit**
```bash
git add -A
git commit -m "feat(freelance): 编译 WASM 并验证安装"
```
---
### Task 4: 浏览器验证 freelance 插件
- [ ] **Step 1: 打开浏览器访问 http://localhost:5174**
- [ ] **Step 2: 登录后检查侧边栏**
Expected: 看到"自由职业者工作台"菜单组包含工作台客户管理商机看板项目管理项目详情财务中心支出管理
- [ ] **Step 3: 测试客户 CRUD**
进入客户管理 新增客户填写名称联系人电话行业等)→ 保存 列表中可见
- [ ] **Step 4: 测试项目 → 任务级联**
进入项目管理 新增项目 进入项目详情 新增任务 验证任务关联到项目
- [ ] **Step 5: 测试报价 → 报价明细级联**
进入财务中心 报价管理 tab 新增报价 验证明细行可添加
- [ ] **Step 6: 测试商机看板**
进入商机看板 新增商机 拖拽改变阶段 验证数据更新
- [ ] **Step 7: 验证数据库表创建**
```bash
PGPASSWORD=123123 "D:\postgreSQL\bin\psql.exe" -U postgres -h localhost -d erp -c "\dt plugin_erp_freelance_*"
```
Expected: 看到 10 张动态表
---
## Chunk 2: itops 插件
### Task 5: 创建 itops 插件 crate
**Files:**
- Create: `crates/erp-plugin-itops/Cargo.toml`
- Create: `crates/erp-plugin-itops/src/lib.rs`
- Create: `crates/erp-plugin-itops/plugin.toml`
- [ ] **Step 1: 创建目录结构**
```bash
mkdir -p crates/erp-plugin-itops/src
```
- [ ] **Step 2: 编写 Cargo.toml**
创建 `crates/erp-plugin-itops/Cargo.toml`
```toml
[package]
name = "erp-plugin-itops"
version = "0.1.0"
edition = "2024"
description = "IT 运维服务台 WASM 插件"
[lib]
crate-type = ["cdylib"]
[dependencies]
wit-bindgen = "0.55"
```
- [ ] **Step 3: 编写 src/lib.rs**
创建 `crates/erp-plugin-itops/src/lib.rs`
```rust
//! IT 运维服务台 WASM 插件
wit_bindgen::generate!({
path: "../erp-plugin-prototype/wit/plugin.wit",
world: "plugin-world",
});
use crate::exports::erp::plugin::plugin_api::Guest;
struct ItopsPlugin;
impl Guest for ItopsPlugin {
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!(ItopsPlugin);
```
- [ ] **Step 4: 编写 plugin.toml**
从设计规格文档 Section 3 提取完整内容
1. `[metadata]` id="erp-itops" dependencies松耦合
2. `[[permissions]]` × 8
3. `[[schema.entities]]` × 4service_contract, ticket, check_plan, check_record每个实体包含 fields relations
4. `[[numbering]]` × 1contract_number
5. `[[ui.pages]]` × 4crud+detail for service_contract, tabs for ticket center
关键注意点
- 4 个实体的 `client_id` 字段都使用 `ref_plugin = "erp-freelance"` + `ref_fallback_label = "外部客户"`
- `filterable` 只用于 string 类型的 status/type/category 字段不用于 uuid 字段
- `check_items` `items_data` 使用 `field_type = "json"`
- `responded_at` / `resolved_at` / `closed_at` 使用 `field_type = "date_time"`
- [ ] **Step 5: 注册到 workspace**
编辑根 `Cargo.toml` members 数组末尾添加
```toml
"crates/erp-plugin-itops",
```
- [ ] **Step 6: 验证编译**
```bash
cargo check -p erp-plugin-itops
```
- [ ] **Step 7: Commit**
```bash
git add crates/erp-plugin-itops/ Cargo.toml
git commit -m "feat(itops): 创建 IT 运维服务台插件 — 4 实体/8 权限/4 页面"
```
---
### Task 6: 编译 WASM 并安装 itops
- [ ] **Step 1: 编译为 WASM**
```bash
cargo build -p erp-plugin-itops --target wasm32-unknown-unknown --release
```
- [ ] **Step 2: 转换为 Component**
```bash
wasm-tools component new target/wasm32-unknown-unknown/release/erp_plugin_itops.wasm -o target/erp_plugin_itops.component.wasm
```
- [ ] **Step 3: 上传安装插件**
```bash
TOKEN="<之前获取的 token>"
MANIFEST=$(cat crates/erp-plugin-itops/plugin.toml)
curl -s -X POST http://localhost:3000/api/v1/admin/plugins/upload \
-H "Authorization: Bearer $TOKEN" \
-F "wasm=@target/erp_plugin_itops.component.wasm" \
-F "manifest=$MANIFEST"
```
- [ ] **Step 4: 启用插件**
```bash
PLUGIN_ID="<返回的插件 ID>"
curl -s -X POST "http://localhost:3000/api/v1/admin/plugins/$PLUGIN_ID/enable" \
-H "Authorization: Bearer $TOKEN"
```
---
### Task 7: 浏览器验证 itops 插件
- [ ] **Step 1: 检查侧边栏**
Expected: 看到"IT 运维服务台"菜单组包含合同管理合同详情工单中心
- [ ] **Step 2: 测试维保合同 CRUD**
进入合同管理 新增维保合同选择客户时验证 freelance 已安装客户下拉显示 freelance 的客户列表
- [ ] **Step 3: 测试跨插件引用**
场景 Afreelance 已安装创建工单时 client_id 字段显示为下拉选择器可搜索 freelance.client
场景 Bfreelance 未安装client_id 降级为文本输入显示"外部客户"
- [ ] **Step 4: 测试合同 → 工单 → 巡检级联**
进入合同详情 工单 tab 新增工单 巡检计划 tab 新增巡检计划 巡检记录 tab 新增巡检记录
- [ ] **Step 5: 验证数据库表**
```bash
PGPASSWORD=123123 "D:\postgreSQL\bin\psql.exe" -U postgres -h localhost -d erp -c "\dt plugin_erp_itops_*"
```
Expected: 看到 4 张动态表
---
## Chunk 3: 集成验证
### Task 8: 全链路端到端验证
- [ ] **Step 1: 创建客户**
freelance 客户管理 新增客户"汕头市XX科技有限公司"
- [ ] **Step 2: 创建商机**
商机看板 新增商机 选择客户 填写"官网开发"→ 拖拽到"成交"阶段
- [ ] **Step 3: 创建报价单**
财务中心 报价管理 新增报价 选择客户 添加明细行 保存
- [ ] **Step 4: 创建合同**
财务中心 合同管理 新增合同 选择客户 填写金额和日期 保存
- [ ] **Step 5: 创建项目**
项目管理 新增项目 选择客户和合同 填写"官网开发项目" 添加任务 记录工时
- [ ] **Step 6: 创建发票**
财务中心 发票/收款 新增发票 选择客户和项目 填写金额 标记已收款
- [ ] **Step 7: 创建运维工单**
itops 合同管理 新增维保合同 选择客户验证跨插件引用)→ 保存
itops 工单中心 新增工单 选择客户和合同 保存
- [ ] **Step 8: 记录支出**
freelance 支出管理 新增支出 选择类别"云服务" 填写金额 保存
- [ ] **Step 9: 提交并推送**
```bash
git add -A
git commit -m "feat(freelance,itops): 汕头市智界科技 IT 服务行业插件验证通过"
git push
```
---
## 关键参考文件
| 文件 | 用途 |
|------|------|
| `crates/erp-plugin-crm/Cargo.toml` | Cargo.toml 模板参考 |
| `crates/erp-plugin-crm/src/lib.rs` | lib.rs 代码模式参考 |
| `crates/erp-plugin-crm/plugin.toml` | plugin.toml 格式参考同插件内引用 |
| `crates/erp-plugin-inventory/plugin.toml` | 跨插件引用格式参考ref_plugin |
| `crates/erp-plugin/src/manifest.rs` | PluginField/PluginFieldType 完整定义 |
| `crates/erp-plugin-prototype/wit/plugin.wit` | WIT 接口定义 |
| `wiki/infrastructure.md` | 数据库连接端口登录凭据 |
| `wiki/wasm-plugin.md` | 插件制作完整流程 |