feat(freelance): 编译 WASM 并验证安装 — 10 实体/20 权限已创建
This commit is contained in:
@@ -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(自由职业者工作台)和 itops(IT 运维服务台)两个 WASM 插件,覆盖其全部 12 条经营范围。
|
||||
|
||||
**Architecture:** 两个独立的 WASM 插件 crate,每个包含 Cargo.toml(cdylib)、src/lib.rs(Guest 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.toml(freelance)
|
||||
|
||||
**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.3(10 个实体)+ 2.4(编号规则)+ 2.5(页面声明)的所有 TOML 内容,合并为完整的 `plugin.toml` 文件。
|
||||
|
||||
文件结构:
|
||||
1. `[metadata]` 段
|
||||
2. `[[permissions]]` × 20
|
||||
3. `[[schema.entities]]` × 10(client, opportunity, quote, quote_line, contract, project, task, time_entry, invoice, expense),每个实体包含 fields 和 relations
|
||||
4. `[[numbering]]` × 3(quote_number, contract_number, invoice_number)
|
||||
5. `[[ui.pages]]` × 7(dashboard, 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: < 100KB(CRM 约 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]]` × 4(service_contract, ticket, check_plan, check_record),每个实体包含 fields 和 relations
|
||||
4. `[[numbering]]` × 1(contract_number)
|
||||
5. `[[ui.pages]]` × 4(crud+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: 测试跨插件引用**
|
||||
|
||||
场景 A(freelance 已安装):创建工单时 client_id 字段显示为下拉选择器,可搜索 freelance.client
|
||||
场景 B(freelance 未安装):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` | 插件制作完整流程 |
|
||||
Reference in New Issue
Block a user