docs: 添加 Q4 测试覆盖 + 插件生态实施计划
15 个 Task 覆盖:Testcontainers 集成测试框架、Playwright E2E、 进销存插件(6实体/6页面)、插件热更新、Wiki 文档更新
This commit is contained in:
706
docs/superpowers/plans/2026-04-17-platform-maturity-q4-plan.md
Normal file
706
docs/superpowers/plans/2026-04-17-platform-maturity-q4-plan.md
Normal file
@@ -0,0 +1,706 @@
|
|||||||
|
# Q4 测试覆盖 + 插件生态 实施计划
|
||||||
|
|
||||||
|
> **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:** 建立 Testcontainers 集成测试框架覆盖核心模块;Playwright E2E 覆盖关键用户旅程;开发进销存插件验证插件系统扩展性;实现插件热更新能力。
|
||||||
|
|
||||||
|
**Architecture:** Testcontainers 启动真实 PostgreSQL 容器运行迁移后执行集成测试;Playwright 驱动浏览器完成端到端验证;进销存插件复用 CRM 插件的 manifest + dynamic_table 模式;热更新通过版本对比 + 增量 DDL + 两阶段提交实现。
|
||||||
|
|
||||||
|
**Tech Stack:** Rust (testcontainers, testcontainers-modules), Playwright, WASM (wit-bindgen), SeaORM Migration
|
||||||
|
|
||||||
|
**Spec:** `docs/superpowers/specs/2026-04-17-platform-maturity-roadmap-design.md` §4
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## File Structure
|
||||||
|
|
||||||
|
| 操作 | 文件 | 职责 |
|
||||||
|
|------|------|------|
|
||||||
|
| Create | `crates/erp-server/tests/integration/mod.rs` | 集成测试入口 |
|
||||||
|
| Create | `crates/erp-server/tests/integration/test_db.rs` | Testcontainers 测试基座 |
|
||||||
|
| Create | `crates/erp-server/tests/integration/auth_tests.rs` | Auth 模块集成测试 |
|
||||||
|
| Create | `crates/erp-server/tests/integration/plugin_tests.rs` | Plugin 模块集成测试 |
|
||||||
|
| Create | `crates/erp-server/tests/integration/workflow_tests.rs` | Workflow 模块集成测试 |
|
||||||
|
| Create | `crates/erp-server/tests/integration/event_tests.rs` | EventBus 端到端测试 |
|
||||||
|
| Create | `apps/web/e2e/login.spec.ts` | 登录流程 E2E |
|
||||||
|
| Create | `apps/web/e2e/users.spec.ts` | 用户管理 E2E |
|
||||||
|
| Create | `apps/web/e2e/plugins.spec.ts` | 插件安装 E2E |
|
||||||
|
| Create | `apps/web/e2e/tenant-isolation.spec.ts` | 多租户隔离 E2E |
|
||||||
|
| Create | `apps/web/playwright.config.ts` | Playwright 配置 |
|
||||||
|
| Create | `crates/erp-plugin-inventory/` | 进销存插件 crate |
|
||||||
|
| Modify | `Cargo.toml` | workspace 添加新 crate |
|
||||||
|
| Modify | `crates/erp-plugin/src/engine.rs` | 热更新 upgrade 端点支持 |
|
||||||
|
| Modify | `crates/erp-plugin/src/service.rs` | upgrade 生命周期 |
|
||||||
|
| Modify | `crates/erp-plugin/src/handler/plugin_handler.rs` | upgrade 路由 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Chunk 1: 集成测试框架
|
||||||
|
|
||||||
|
### Task 1: 添加 Testcontainers 依赖
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `crates/erp-server/Cargo.toml`(dev-dependencies)
|
||||||
|
|
||||||
|
- [ ] **Step 1: 在 `erp-server` 的 `[dev-dependencies]` 中添加**
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[dev-dependencies]
|
||||||
|
testcontainers = "0.23"
|
||||||
|
testcontainers-modules = { version = "0.11", features = ["postgres"] }
|
||||||
|
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
|
||||||
|
```
|
||||||
|
|
||||||
|
注意:版本号需与 workspace 已有依赖兼容。如果 workspace 已有 `testcontainers`,使用 workspace 引用。
|
||||||
|
|
||||||
|
- [ ] **Step 2: 验证编译**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo check -p erp-server
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add crates/erp-server/Cargo.toml Cargo.lock
|
||||||
|
git commit -m "chore(server): 添加 testcontainers 开发依赖"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 2: 创建测试基座
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `crates/erp-server/tests/integration/mod.rs`
|
||||||
|
- Create: `crates/erp-server/tests/integration/test_db.rs`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 创建测试模块入口**
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// crates/erp-server/tests/integration/mod.rs
|
||||||
|
mod test_db;
|
||||||
|
mod auth_tests;
|
||||||
|
mod plugin_tests;
|
||||||
|
```
|
||||||
|
|
||||||
|
注意:需要确保 `erp-server` 的 `Cargo.toml` 中有 `[[test]]` 配置或集成测试自动发现。
|
||||||
|
|
||||||
|
- [ ] **Step 2: 创建 Testcontainers 测试基座**
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// crates/erp-server/tests/integration/test_db.rs
|
||||||
|
use testcontainers_modules::postgres::Postgres;
|
||||||
|
use testcontainers::runners::AsyncRunner;
|
||||||
|
use sea_orm::{Database, DatabaseConnection};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
/// 测试数据库容器 — 使用 once_cell 确保每进程一个容器
|
||||||
|
pub struct TestDb {
|
||||||
|
pub db: DatabaseConnection,
|
||||||
|
pub container: testcontainers::ContainerAsync<Postgres>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestDb {
|
||||||
|
pub async fn new() -> Self {
|
||||||
|
let postgres = Postgres::default()
|
||||||
|
.with_db_name("erp_test")
|
||||||
|
.with_user("test")
|
||||||
|
.with_password("test");
|
||||||
|
|
||||||
|
let container = postgres.start().await
|
||||||
|
.expect("Failed to start PostgreSQL container");
|
||||||
|
|
||||||
|
let host_port = container.get_host_port_ipv4(5432).await
|
||||||
|
.expect("Failed to get port");
|
||||||
|
|
||||||
|
let url = format!("postgres://test:test@localhost:{}/erp_test", host_port);
|
||||||
|
let db = Database::connect(&url).await
|
||||||
|
.expect("Failed to connect to test database");
|
||||||
|
|
||||||
|
// 运行所有迁移
|
||||||
|
run_migrations(&db).await;
|
||||||
|
|
||||||
|
Self { db, container }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn run_migrations(db: &DatabaseConnection) {
|
||||||
|
use migration::{Migrator, MigratorTrait};
|
||||||
|
Migrator::up(db, None).await.expect("Failed to run migrations");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
注意:需要确保 `migration` crate 可被测试引用。可能需要调整 `Cargo.toml` 的依赖。
|
||||||
|
|
||||||
|
- [ ] **Step 3: 验证编译**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo check -p erp-server
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 4: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add crates/erp-server/tests/
|
||||||
|
git commit -m "test(server): 创建 Testcontainers 集成测试基座"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 3: Auth 模块集成测试
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `crates/erp-server/tests/integration/auth_tests.rs`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 编写用户 CRUD 测试**
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// crates/erp-server/tests/integration/auth_tests.rs
|
||||||
|
use super::test_db::TestDb;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_user_crud() {
|
||||||
|
let test_db = TestDb::new().await;
|
||||||
|
let db = &test_db.db;
|
||||||
|
let tenant_id = uuid::Uuid::new_v4();
|
||||||
|
|
||||||
|
// 创建用户
|
||||||
|
let user = erp_auth::service::UserService::create(
|
||||||
|
tenant_id,
|
||||||
|
uuid::Uuid::new_v4(),
|
||||||
|
erp_auth::dto::CreateUserReq {
|
||||||
|
username: "testuser".to_string(),
|
||||||
|
password: "TestPass123".to_string(),
|
||||||
|
email: Some("test@example.com".to_string()),
|
||||||
|
phone: None,
|
||||||
|
display_name: Some("测试用户".to_string()),
|
||||||
|
},
|
||||||
|
db,
|
||||||
|
&erp_core::events::EventBus::new(100),
|
||||||
|
).await.expect("Failed to create user");
|
||||||
|
|
||||||
|
assert_eq!(user.username, "testuser");
|
||||||
|
|
||||||
|
// 查询用户
|
||||||
|
let found = erp_auth::service::UserService::get_by_id(user.id, tenant_id, db)
|
||||||
|
.await.expect("Failed to get user");
|
||||||
|
assert_eq!(found.username, "testuser");
|
||||||
|
|
||||||
|
// 列表查询
|
||||||
|
let (users, total) = erp_auth::service::UserService::list(
|
||||||
|
tenant_id, erp_core::types::Pagination { page: 1, page_size: 10 }, None, db,
|
||||||
|
).await.expect("Failed to list users");
|
||||||
|
assert_eq!(total, 1);
|
||||||
|
assert_eq!(users[0].username, "testuser");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: 编写多租户隔离测试**
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_tenant_isolation() {
|
||||||
|
let test_db = TestDb::new().await;
|
||||||
|
let db = &test_db.db;
|
||||||
|
let tenant_a = uuid::Uuid::new_v4();
|
||||||
|
let tenant_b = uuid::Uuid::new_v4();
|
||||||
|
|
||||||
|
// 租户 A 创建用户
|
||||||
|
let user_a = erp_auth::service::UserService::create(
|
||||||
|
tenant_a,
|
||||||
|
uuid::Uuid::new_v4(),
|
||||||
|
erp_auth::dto::CreateUserReq {
|
||||||
|
username: "user_a".to_string(),
|
||||||
|
password: "Pass123!".to_string(),
|
||||||
|
email: None, phone: None, display_name: None,
|
||||||
|
},
|
||||||
|
db,
|
||||||
|
&erp_core::events::EventBus::new(100),
|
||||||
|
).await.unwrap();
|
||||||
|
|
||||||
|
// 租户 B 查询不应看到租户 A 的用户
|
||||||
|
let (users_b, total_b) = erp_auth::service::UserService::list(
|
||||||
|
tenant_b, erp_core::types::Pagination { page: 1, page_size: 10 }, None, db,
|
||||||
|
).await.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(total_b, 0);
|
||||||
|
assert!(users_b.is_empty());
|
||||||
|
|
||||||
|
// 租户 B 通过 ID 查询租户 A 的用户应返回 NotFound
|
||||||
|
let result = erp_auth::service::UserService::get_by_id(user_a.id, tenant_b, db).await;
|
||||||
|
assert!(result.is_err());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: 运行测试验证**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo test -p erp-server --test integration auth_tests
|
||||||
|
```
|
||||||
|
|
||||||
|
注意:需要 Docker 运行。Windows 上可能需要 WSL2。
|
||||||
|
|
||||||
|
- [ ] **Step 4: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add crates/erp-server/tests/integration/auth_tests.rs
|
||||||
|
git commit -m "test(auth): 添加用户 CRUD 和多租户隔离集成测试"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 4: Plugin 模块集成测试
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `crates/erp-server/tests/integration/plugin_tests.rs`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 编写插件生命周期测试**
|
||||||
|
|
||||||
|
测试 install → enable → data CRUD → disable → uninstall 完整流程。
|
||||||
|
|
||||||
|
- [ ] **Step 2: 编写 JSONB 查询测试**
|
||||||
|
|
||||||
|
验证 dynamic_table 的 generated column、pg_trgm 索引是否正确创建。
|
||||||
|
|
||||||
|
- [ ] **Step 3: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add crates/erp-server/tests/integration/plugin_tests.rs
|
||||||
|
git commit -m "test(plugin): 添加插件生命周期和 JSONB 集成测试"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 5: Workflow + EventBus 集成测试
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `crates/erp-server/tests/integration/workflow_tests.rs`
|
||||||
|
- Create: `crates/erp-server/tests/integration/event_tests.rs`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Workflow 测试 — 流程实例启动和任务完成**
|
||||||
|
|
||||||
|
- [ ] **Step 2: EventBus 测试 — 发布/订阅端到端 + outbox relay**
|
||||||
|
|
||||||
|
- [ ] **Step 3: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add crates/erp-server/tests/integration/
|
||||||
|
git commit -m "test: 添加 workflow 和 EventBus 集成测试"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Chunk 2: E2E 测试
|
||||||
|
|
||||||
|
### Task 6: Playwright 环境搭建
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `apps/web/playwright.config.ts`
|
||||||
|
- Modify: `apps/web/package.json`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 安装 Playwright**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd apps/web && pnpm add -D @playwright/test && pnpm exec playwright install chromium
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: 创建 Playwright 配置**
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// apps/web/playwright.config.ts
|
||||||
|
import { defineConfig } from '@playwright/test';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
testDir: './e2e',
|
||||||
|
timeout: 30000,
|
||||||
|
retries: 1,
|
||||||
|
use: {
|
||||||
|
baseURL: 'http://localhost:5173',
|
||||||
|
headless: true,
|
||||||
|
screenshot: 'only-on-failure',
|
||||||
|
trace: 'on-first-retry',
|
||||||
|
},
|
||||||
|
webServer: {
|
||||||
|
command: 'pnpm dev',
|
||||||
|
port: 5173,
|
||||||
|
reuseExistingServer: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add apps/web/playwright.config.ts apps/web/package.json
|
||||||
|
git commit -m "test(web): 搭建 Playwright E2E 测试环境"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 7: 登录流程 E2E 测试
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `apps/web/e2e/login.spec.ts`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 编写登录 E2E 测试**
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// apps/web/e2e/login.spec.ts
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
|
||||||
|
test('完整登录流程', async ({ page }) => {
|
||||||
|
await page.goto('/#/login');
|
||||||
|
await expect(page.locator('h2, .ant-card-head-title')).toContainText('登录');
|
||||||
|
|
||||||
|
// 输入凭据
|
||||||
|
await page.fill('input[placeholder*="用户名"]', 'admin');
|
||||||
|
await page.fill('input[placeholder*="密码"]', 'Admin@2026');
|
||||||
|
await page.click('button:has-text("登录")');
|
||||||
|
|
||||||
|
// 验证跳转到首页
|
||||||
|
await page.waitForURL('**/'),
|
||||||
|
await expect(page).toHaveURL(/\/$/);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
注意:此测试需要后端服务运行。可在 CI 中使用 service container 或手动启动。
|
||||||
|
|
||||||
|
- [ ] **Step 2: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add apps/web/e2e/login.spec.ts
|
||||||
|
git commit -m "test(web): 添加登录流程 E2E 测试"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 8: 用户管理 E2E 测试
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `apps/web/e2e/users.spec.ts`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 编写用户管理闭环测试**
|
||||||
|
|
||||||
|
创建 → 搜索 → 编辑 → 软删除 → 验证列表不显示。
|
||||||
|
|
||||||
|
- [ ] **Step 2: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add apps/web/e2e/users.spec.ts
|
||||||
|
git commit -m "test(web): 添加用户管理 E2E 测试"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 9: 插件安装 + 多租户 E2E 测试
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `apps/web/e2e/plugins.spec.ts`
|
||||||
|
- Create: `apps/web/e2e/tenant-isolation.spec.ts`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 插件安装 E2E 测试**
|
||||||
|
|
||||||
|
上传 → 安装 → 验证菜单 → 数据 CRUD → 卸载。
|
||||||
|
|
||||||
|
- [ ] **Step 2: 多租户隔离 E2E 测试**
|
||||||
|
|
||||||
|
租户 A 创建数据 → 切换租户 B → 验证不可见。
|
||||||
|
|
||||||
|
- [ ] **Step 3: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add apps/web/e2e/
|
||||||
|
git commit -m "test(web): 添加插件安装和多租户 E2E 测试"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Chunk 3: 进销存插件
|
||||||
|
|
||||||
|
### Task 10: 创建插件 crate 骨架
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `crates/erp-plugin-inventory/Cargo.toml`
|
||||||
|
- Create: `crates/erp-plugin-inventory/src/lib.rs`
|
||||||
|
- Create: `crates/erp-plugin-inventory/manifest.toml`
|
||||||
|
- Modify: `Cargo.toml`(workspace members)
|
||||||
|
|
||||||
|
- [ ] **Step 1: 创建 Cargo.toml**
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[package]
|
||||||
|
name = "erp-plugin-inventory"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
wit-bindgen = "0.38"
|
||||||
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
serde_json = "1"
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: 创建 manifest.toml**
|
||||||
|
|
||||||
|
定义 6 个实体(product, warehouse, stock, supplier, purchase_order, sales_order)的完整 schema,包括字段、关系、页面、权限声明。参考 CRM 插件的 `crates/erp-plugin-crm/manifest.toml`。
|
||||||
|
|
||||||
|
- [ ] **Step 3: 创建 lib.rs(Guest trait 实现)**
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// crates/erp-plugin-inventory/src/lib.rs
|
||||||
|
wit_bindgen::generate!({
|
||||||
|
path: "../erp-plugin-prototype/wit",
|
||||||
|
world: "plugin-world",
|
||||||
|
});
|
||||||
|
|
||||||
|
struct InventoryPlugin;
|
||||||
|
|
||||||
|
impl Guest for InventoryPlugin {
|
||||||
|
fn init() -> Result<(), String> { Ok(()) }
|
||||||
|
fn on_tenant_created(_tenant_id: String) -> Result<(), String> { Ok(()) }
|
||||||
|
fn handle_event(_event_type: String, _event_data: String) -> Result<(), String> { Ok(()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
export_plugin!(InventoryPlugin);
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 4: 添加到 workspace**
|
||||||
|
|
||||||
|
在根 `Cargo.toml` 的 `members` 中添加 `"crates/erp-plugin-inventory"`。
|
||||||
|
|
||||||
|
- [ ] **Step 5: 验证编译**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo check -p erp-plugin-inventory
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 6: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add crates/erp-plugin-inventory/ Cargo.toml
|
||||||
|
git commit -m "feat(inventory): 创建进销存插件 crate 骨架"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 11: 定义实体 Schema
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `crates/erp-plugin-inventory/manifest.toml`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 定义 6 个实体**
|
||||||
|
|
||||||
|
参考 CRM 插件 manifest 格式,定义:
|
||||||
|
|
||||||
|
| 实体 | 关键字段 | 关联 | 页面类型 |
|
||||||
|
|------|---------|------|---------|
|
||||||
|
| product | code, name, spec, unit, category, price, cost | — | CRUD |
|
||||||
|
| warehouse | code, name, address, manager, status | — | CRUD |
|
||||||
|
| stock | product_id, warehouse_id, qty, cost, alert_line | → product, warehouse | CRUD |
|
||||||
|
| supplier | code, name, contact, phone, address | — | CRUD |
|
||||||
|
| purchase_order | supplier_id, total_amount, status, date | → supplier, stock | CRUD + Dashboard |
|
||||||
|
| sales_order | customer_id, total_amount, status, date | → customer(CRM), stock | CRUD + Kanban |
|
||||||
|
|
||||||
|
- [ ] **Step 2: 定义 6 个页面**(4 CRUD + 1 Dashboard 库存汇总 + 1 Kanban 销售看板)
|
||||||
|
|
||||||
|
- [ ] **Step 3: 定义 9 个权限**(每个实体 list/create/update/delete + 全局 manage)
|
||||||
|
|
||||||
|
- [ ] **Step 4: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add crates/erp-plugin-inventory/manifest.toml
|
||||||
|
git commit -m "feat(inventory): 定义 6 实体/6 页面/9 权限 manifest"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 12: 编译 WASM 并测试安装
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Build output: `apps/web/public/inventory.wasm`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 编译为 WASM Component**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo build -p erp-plugin-inventory --target wasm32-unknown-unknown --release
|
||||||
|
wasm-tools component new target/wasm32-unknown-unknown/release/erp_plugin_inventory.wasm -o target/erp_plugin_inventory.component.wasm
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: 复制到前端 public 目录**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp target/erp_plugin_inventory.component.wasm apps/web/public/inventory.wasm
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: 通过 API 安装插件并验证**
|
||||||
|
|
||||||
|
使用 curl 或前端插件管理页面上传 `inventory.wasm`,验证动态表创建成功,CRUD 页面正常工作。
|
||||||
|
|
||||||
|
- [ ] **Step 4: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add apps/web/public/inventory.wasm
|
||||||
|
git commit -m "feat(inventory): 编译并部署进销存插件 WASM"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Chunk 4: 插件热更新
|
||||||
|
|
||||||
|
### Task 13: 添加 upgrade 端点
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `crates/erp-plugin/src/handler/plugin_handler.rs`
|
||||||
|
- Modify: `crates/erp-plugin/src/service.rs`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 在 plugin_handler 中添加 upgrade 路由**
|
||||||
|
|
||||||
|
```rust
|
||||||
|
pub fn protected_routes() -> Router<AppState> {
|
||||||
|
Router::new()
|
||||||
|
// ... 现有路由 ...
|
||||||
|
.route("/admin/plugins/:plugin_id/upgrade", post(upgrade_plugin))
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: 实现 upgrade handler**
|
||||||
|
|
||||||
|
接收新 WASM 文件,调用 service 层执行升级。
|
||||||
|
|
||||||
|
- [ ] **Step 3: 在 service 中实现升级逻辑**
|
||||||
|
|
||||||
|
```rust
|
||||||
|
pub async fn upgrade_plugin(
|
||||||
|
plugin_id: Uuid,
|
||||||
|
tenant_id: Uuid,
|
||||||
|
new_wasm_bytes: Vec<u8>,
|
||||||
|
db: &DatabaseConnection,
|
||||||
|
) -> PluginResult<()> {
|
||||||
|
// 1. 解析新 manifest
|
||||||
|
let new_manifest = parse_manifest_from_wasm(&new_wasm_bytes)?;
|
||||||
|
|
||||||
|
// 2. 获取当前插件信息
|
||||||
|
let current = find_by_id(plugin_id, tenant_id, db).await?;
|
||||||
|
|
||||||
|
// 3. 对比 schema 变更,生成增量 DDL
|
||||||
|
let schema_diff = compare_schemas(¤t.manifest, &new_manifest)?;
|
||||||
|
|
||||||
|
// 4. 暂存新 WASM,尝试验证初始化
|
||||||
|
// 5. 初始化成功后,在事务中执行 DDL + 状态更新
|
||||||
|
// 6. 失败时保持旧 WASM 继续运行
|
||||||
|
// 详见 spec §4.4 回滚策略
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 4: 验证编译**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo check -p erp-plugin
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 5: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add crates/erp-plugin/src/
|
||||||
|
git commit -m "feat(plugin): 添加插件热更新 upgrade 端点"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Chunk 5: 文档更新与清理
|
||||||
|
|
||||||
|
### Task 14: 更新 Wiki 文档
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `wiki/frontend.md`
|
||||||
|
- Modify: `wiki/database.md`
|
||||||
|
- Modify: `wiki/testing.md`
|
||||||
|
- Modify: `wiki/index.md`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 更新 `wiki/frontend.md`**
|
||||||
|
|
||||||
|
更新为反映当前 16 条路由、6 种插件页面类型、Zustand stores 等实际状态。
|
||||||
|
|
||||||
|
- [ ] **Step 2: 更新 `wiki/testing.md`**
|
||||||
|
|
||||||
|
更新测试数量、添加 Testcontainers 集成测试和 Playwright E2E 描述。
|
||||||
|
|
||||||
|
- [ ] **Step 3: 更新 `wiki/index.md`**
|
||||||
|
|
||||||
|
添加进销存插件到模块导航树,更新开发进度表。
|
||||||
|
|
||||||
|
- [ ] **Step 4: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add wiki/
|
||||||
|
git commit -m "docs: 更新 Wiki 文档到当前状态"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 15: CLAUDE.md 版本号修正 + 根目录清理
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `CLAUDE.md`
|
||||||
|
- Cleanup: 根目录未跟踪文件
|
||||||
|
|
||||||
|
- [ ] **Step 1: 修正 CLAUDE.md 版本号**
|
||||||
|
|
||||||
|
将 `React 18 + Ant Design 5` 改为 `React 19 + Ant Design 6`。
|
||||||
|
|
||||||
|
- [ ] **Step 2: 清理根目录未跟踪文件**
|
||||||
|
|
||||||
|
删除开发临时文件:截图、heap dump、perf trace、agent plan 文件。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rm -f current-page.png home-full.png home-improved.png docs/debug-*.png
|
||||||
|
rm -f docs/memory-snapshot-*.heapsnapshot docs/perf-trace-*.json
|
||||||
|
rm -f test_api_auth.py test_users.py
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: 处理 integration-tests/ 目录**
|
||||||
|
|
||||||
|
验证 `integration-tests/` 中的测试是否能编译。若已失效则删除(新的集成测试在 `crates/erp-server/tests/integration/`)。若仍有效则添加到 workspace。
|
||||||
|
|
||||||
|
- [ ] **Step 4: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add CLAUDE.md
|
||||||
|
git commit -m "docs: 修正 CLAUDE.md 版本号 (React 19 / AD 6) 并清理临时文件"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 验证清单
|
||||||
|
|
||||||
|
- [ ] **V1: 全 workspace 编译和测试**
|
||||||
|
```bash
|
||||||
|
cargo check && cargo test --workspace
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **V2: 集成测试通过**
|
||||||
|
```bash
|
||||||
|
cargo test -p erp-server --test integration
|
||||||
|
```
|
||||||
|
注意:需要 Docker 运行
|
||||||
|
|
||||||
|
- [ ] **V3: 前端构建**
|
||||||
|
```bash
|
||||||
|
cd apps/web && pnpm build
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **V4: E2E 测试**
|
||||||
|
```bash
|
||||||
|
cd apps/web && pnpm exec playwright test
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **V5: 进销存插件安装验证**
|
||||||
|
|
||||||
|
通过 API 安装 inventory.wasm,验证动态表和 CRUD 页面正常。
|
||||||
|
|
||||||
|
- [ ] **V6: Wiki 文档同步**
|
||||||
|
|
||||||
|
确认 Wiki 描述与代码实际状态一致。
|
||||||
Reference in New Issue
Block a user