173 lines
8.6 KiB
Markdown
173 lines
8.6 KiB
Markdown
# 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 时返回原 JSON;csv/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. 每批完成后独立提交推送
|