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,172 @@
# P1-P4 审计修复实施计划
## Context
对 P1-P4 审计发现 8 项高/中优先级缺失Excel/CSV 导入导出、市场后端 API、对账扫描、运行时监控、通知规则、编号 reset_rule。本计划按优先级分 3 批推进,每批独立可提交。
---
## 第一批高优先级Excel/CSV + 市场后端 + 对账扫描)
### 1.1 Excel/CSV 导入导出
**思路**: 后端新增 `csv` + `rust_xlsxwriter` 依赖export handler 支持 format 参数输出 CSV/XLSX前端同时支持。
**后端改动**:
1. `Cargo.toml` (workspace): 新增 `csv = "1"``rust_xlsxwriter = "0.82"`
2. `crates/erp-plugin/Cargo.toml`: 添加 `csv``rust_xlsxwriter` 依赖
3. `crates/erp-plugin/src/data_service.rs`:
- `export()` 签名增加 `format: Option<String>` 参数
- 内部新增 `export_csv()``export_xlsx()` 私有方法,返回 `Vec<u8>` bytes
- format 为空/json 时返回原 JSONcsv/xlsx 时返回二进制
- 返回类型改为 enum `ExportPayload { Json(Vec<Value>), Csv(Vec<u8>), Xlsx(Vec<u8>) }`
4. `crates/erp-plugin/src/data_dto.rs`: ExportParams 的 format 字段已有
5. `crates/erp-plugin/src/handler/data_handler.rs`:
- `export_plugin_data` 根据 format 参数返回不同 Content-Type:
- JSON: `application/json`
- CSV: `text/csv` + `Content-Disposition: attachment`
- XLSX: `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`
- 返回类型改为 `axum::response::Response`(不是 Json<>
6. 前端 `pluginData.ts`: `exportPluginData` 支持 format 参数CSV/XLSX 时用 `responseType: 'blob'`
7. 前端 `PluginCRUDPage.tsx`: 导出按钮增加下拉菜单选择格式JSON/CSV/Excel
**注意**: 导入仍保持 JSON复杂度低模板生成和导入历史不在本批范围。
### 1.2 P4 市场后端 API
**思路**: 新建 `market_service.rs` + `market_handler.rs`,复用 DB 迁移已建好的 `plugin_market_entries``plugin_market_reviews` 表。
**新增文件**:
- `crates/erp-plugin/src/service/market_service.rs`: 市场业务逻辑
- `crates/erp-plugin/src/handler/market_handler.rs`: 市场 API handler
- `crates/erp-plugin/src/entity/market_entry.rs`: SeaORM Entity
- `crates/erp-plugin/src/entity/market_review.rs`: SeaORM Entity
**后端 API**:
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/api/v1/market/entries` | 浏览市场目录(分类/搜索/分页) |
| GET | `/api/v1/market/entries/{id}` | 市场条目详情 |
| POST | `/api/v1/market/entries/{id}/install` | 从市场一键安装 |
| GET | `/api/v1/market/entries/{id}/reviews` | 查看评论 |
| POST | `/api/v1/market/entries/{id}/reviews` | 提交评分/评论 |
**一键安装逻辑**: 从 `plugin_market_entries``wasm_binary` + `manifest_toml`,调用已有的 `PluginService::upload` + `PluginService::install` + `PluginService::enable`
**依赖提示**: install 时检查 manifest.dependencies若目标插件未安装则返回警告软提示不阻塞
**前端改动**:
- `apps/web/src/api/plugins.ts`: 新增市场 API 函数
- `apps/web/src/pages/PluginMarket.tsx`: 对接真实 API替换 mock 数据;增加评分提交 UI安装按钮对接真实 API
### 1.3 P1 对账扫描
**思路**: 新增 `reconcile` service 方法和 handler在插件重新启用时扫描悬空引用。
**后端改动**:
- `crates/erp-plugin/src/data_service.rs`: 新增 `reconcile_references()` 方法
- 查找所有指向目标插件的 `ref_entity` 字段(从 plugin_entities schema_json 解析)
- 扫描这些字段的 UUID 值,验证目标表中是否存在
- 返回 `ReconciliationReport { valid: N, dangling: M, details: Vec<DanglingRef> }`
- `crates/erp-plugin/src/data_dto.rs`: 新增 DTO
- `crates/erp-plugin/src/handler/data_handler.rs`: 新增 `reconcile_refs` handler
- `crates/erp-plugin/src/module.rs`: 注册路由 `POST /plugins/{plugin_id}/reconcile`
**前端**: 暂不实现完整对账 UI低优先级仅提供 API 供后续使用。
---
## 第二批:中优先级(运行时监控 + 通知规则 + 编号 reset
### 2.1 P3 运行时监控
**后端改动**:
1. 新建迁移 `m20260420_000041_plugin_runtime_metrics.rs`:
- `plugin_runtime_metrics` 表: plugin_id, tenant_id, error_count, total_invocations, avg_response_ms, fuel_consumption_avg, memory_peak_bytes, last_error, updated_at
2. `crates/erp-plugin/src/engine.rs`:
- `LoadedPlugin` 新增 `metrics: Arc<RwLock<RuntimeMetrics>>` 字段
- `execute_wasm` 中采集指标: 记录开始时间、成功/失败计数、fuel 消耗
- 定期持久化到 DB每 10 次调用或 60 秒)
3. `crates/erp-plugin/src/handler/plugin_handler.rs`:
- 扩展 `health_check` 返回 RuntimeMetrics
- 新增 `GET /admin/plugins/{id}/metrics` 端点
### 2.2 P2 通知规则引擎
**思路**: 复用 EventBus 的 `subscribe_filtered` + erp-message 的 `send_system`,在 plugin 模块启动时监听 `plugin.trigger.*` 前缀事件。
**后端改动**:
- `crates/erp-plugin/src/module.rs`: 启动事件监听(参考 erp-message 的 `start_event_listener` 模式)
- 新建 `crates/erp-plugin/src/notification.rs`:
- 订阅 `plugin.trigger.*` 事件
- 查询 trigger_events 声明,匹配事件名
- 调用 erp-message 的系统消息发送(通过 EventBus 发布 `message.send` 事件,或直接调用 message service 的 REST API
- 通知对象: 通过 manifest 声明扩展(当前简化为通知所有管理员)
### 2.3 P2 编号 reset_rule
**思路**: 参考 erp-config 的 `numbering_service.rs``maybe_reset_sequence` 模式,替换 PostgreSQL 序列为表行 + advisory lock。
**后端改动**:
- `crates/erp-plugin/src/host.rs`: 重写 `numbering_generate`
- 改用 `pg_advisory_xact_lock` + 表行序列(而非 PostgreSQL SEQUENCE
- 在事务内: 读序列行 → 检查 reset_rule 是否需要重置 → 递增/重置 → 写回
- 序列表: 使用已有的动态表模式,或新建 `plugin_numbering_sequences`
- `crates/erp-plugin/src/engine.rs`: `NumberingRule` 中 reset_rule 字段已被传递但未使用,直接在 host.rs 中消费
---
## 第三批:低优先级(配置变更通知 + 自定义视图)
### 3.1 P2 配置变更通知
**后端改动**:
- `crates/erp-plugin/src/service.rs`: `update_config` 增加 `event_bus: &EventBus` 参数,更新成功后发布 `plugin.config.updated` 事件
- `crates/erp-plugin/src/handler/plugin_handler.rs`: `update_plugin_config` handler 从 state 获取 event_bus 传入
- `crates/erp-plugin/src/engine.rs`: 订阅 `plugin.config.updated` 事件,刷新内存中的 `plugin_config`
### 3.2 P2 自定义视图
**后端改动**:
1. 新建迁移 `plugin_user_views`
2. 新建 `crates/erp-plugin/src/service/view_service.rs`: CRUD user views
3. 新建 handler: `GET/POST/PUT/DELETE /plugins/{plugin_id}/{entity}/views`
4. **前端**: PluginCRUDPage 增加视图保存/加载 UI
---
## 关键文件清单
| 文件 | 改动类型 |
|------|---------|
| `Cargo.toml` (workspace) | 新增 csv, rust_xlsxwriter 依赖 |
| `crates/erp-plugin/Cargo.toml` | 新增依赖 |
| `crates/erp-plugin/src/data_service.rs` | export format 支持, reconcile 方法 |
| `crates/erp-plugin/src/data_dto.rs` | ExportPayload enum, ReconciliationReport |
| `crates/erp-plugin/src/handler/data_handler.rs` | export 返回 Response, reconcile handler |
| `crates/erp-plugin/src/handler/market_handler.rs` | **新建** 市场 API |
| `crates/erp-plugin/src/service/market_service.rs` | **新建** 市场业务逻辑 |
| `crates/erp-plugin/src/entity/market_entry.rs` | **新建** SeaORM Entity |
| `crates/erp-plugin/src/entity/market_review.rs` | **新建** SeaORM Entity |
| `crates/erp-plugin/src/notification.rs` | **新建** 通知规则引擎 |
| `crates/erp-plugin/src/engine.rs` | LoadedPlugin 增加 metrics, 配置热更新 |
| `crates/erp-plugin/src/host.rs` | numbering_generate 重写 |
| `crates/erp-plugin/src/service.rs` | update_config 增加 event_bus |
| `crates/erp-plugin/src/module.rs` | 注册新路由, 启动通知监听 |
| `crates/erp-plugin/src/lib.rs` | 导出新模块 |
| `crates/erp-server/migration/src/m20260420_*.rs` | **新建** metrics 表迁移 |
| `apps/web/src/api/plugins.ts` | 市场前端 API |
| `apps/web/src/api/pluginData.ts` | export format 支持 |
| `apps/web/src/pages/PluginMarket.tsx` | 对接真实 API |
| `apps/web/src/pages/PluginCRUDPage.tsx` | 导出格式选择 |
## 验证计划
1. `cargo check` — 全 workspace 编译通过
2. `pnpm build` — 前端构建通过
3. 启动后端 + 前端,浏览器中验证:
- CRM customer 导出 CSV/Excel 下载
- 市场 API 返回数据curl 测试)
- 插件 health 接口返回 metrics
4. 每批完成后独立提交推送