关键修正: - ErpModule trait 基于实际签名设计,使用双路由模式 - CI/CD 添加 checkout/cache 步骤 - 环境变量名与现有代码一致 (ERP__SUPER_ADMIN_PASSWORD) - .test_token 标记为需 BFG 清理 - Q4 拆分为 Q4a(测试) + Q4b(插件) - 热更新添加回滚策略 - 添加 Windows Testcontainers 兼容性风险
457 lines
20 KiB
Markdown
457 lines
20 KiB
Markdown
# ERP 平台底座 — 全面成熟度提升路线图
|
||
|
||
> 创建日期:2026-04-17
|
||
> 状态:审查修订完成
|
||
> 范围:安全、架构、测试、前端体验、插件生态 — 3 季度分层推进
|
||
|
||
---
|
||
|
||
## 1. 背景与目标
|
||
|
||
### 1.1 项目现状
|
||
|
||
ERP 平台底座已完成 Phase 1-6 基础设施建设 + WASM 插件系统集成 + CRM 客户管理插件。当前具备:
|
||
|
||
- 6 个业务模块(auth, config, workflow, message, plugin, server)
|
||
- 36 个数据库迁移
|
||
- 完整的 WASM 插件运行时
|
||
- Schema 驱动的动态前端(6 种页面类型)
|
||
- React 19 + Ant Design 6 + Zustand 5 前端 SPA
|
||
|
||
### 1.2 分析发现摘要
|
||
|
||
| 维度 | 评分 | 关键问题 |
|
||
|------|------|---------|
|
||
| 架构健壮性 | 8/10 | ErpModule trait 死代码、路由注册未自动化 |
|
||
| 代码质量 | 7/10 | N+1 查询、错误映射过宽、 oversized 组件 |
|
||
| 安全性 | 5/10 | 3 个 CRITICAL(硬编码密钥/密码)、4 个 HIGH |
|
||
| 测试覆盖 | 4/10 | 零数据库集成测试、关键流程未覆盖 |
|
||
| 前端体验 | 7/10 | 无 i18n、无 Error Boundary、无虚拟滚动 |
|
||
| 基础设施 | 4/10 | 无 CI/CD、Wiki 过时、大量未跟踪文件 |
|
||
|
||
### 1.3 目标
|
||
|
||
通过 3 个季度的分层改进,将平台从"功能完整"推进到"生产就绪":
|
||
|
||
- **Q2(4-5月)**:消除安全风险,建立自动化质量门
|
||
- **Q3(6-8月)**:强化架构,提升前端工程化水平
|
||
- **Q4(9-11月)**:补齐测试覆盖,扩展插件生态
|
||
|
||
### 1.4 约束
|
||
|
||
- **独立开发者** + Claude 辅助 — 每季度聚焦单一维度
|
||
- **SaaS 优先**部署 — 多租户安全是硬性要求
|
||
- **不破坏现有功能** — 所有改进必须向后兼容
|
||
|
||
---
|
||
|
||
## 2. Q2:安全地基 + CI/CD(4-5月)
|
||
|
||
### 2.1 密钥外部化与启动强制检查
|
||
|
||
**问题:**
|
||
- JWT 密钥 `"change-me-in-production"` 硬编码在 `crates/erp-server/config/default.toml`
|
||
- 管理员密码 `"Admin@2026"` 硬编码 + fallback
|
||
- 数据库凭据 `postgres://erp:erp_dev_2024@...` 硬编码
|
||
- `.test_token` 含有效 admin JWT 提交到仓库
|
||
|
||
**方案:**
|
||
|
||
1. **配置强制化**:`default.toml` 只保留开发环境默认值。生产敏感值通过环境变量 `ERP__` 前缀注入(已有机制)
|
||
2. **启动检查**:服务启动时检测 JWT 密钥是否为默认值,若是则 **拒绝启动**(返回错误退出码,不只是警告)
|
||
3. **密码初始化**:`seed_tenant_auth` 从环境变量 `ERP__SUPER_ADMIN_PASSWORD` 读取初始密码(与现有 `module.rs:149` 中的变量名一致),未设置则拒绝初始化(移除 fallback 到硬编码值的逻辑)
|
||
4. **清理 `.test_token`**:立即加入 `.gitignore`。验证该文件是否曾被提交到 git 历史 — 如果曾提交,需使用 BFG Repo-Cleaner 清理历史(因包含用硬编码密钥签名的 admin JWT,等同于密钥泄露)
|
||
5. **`default.toml` 占位符**:敏感字段改为 `"__MUST_SET_VIA_ENV__"` 之类的明显占位值
|
||
|
||
**验证标准:**
|
||
- 默认配置启动时服务拒绝运行
|
||
- 环境变量设置后正常启动
|
||
- `.test_token` 不再出现在仓库中
|
||
|
||
### 2.2 Gitea Actions CI/CD
|
||
|
||
**流水线设计:**
|
||
|
||
```yaml
|
||
name: CI
|
||
on: [push, pull_request]
|
||
|
||
jobs:
|
||
rust-check:
|
||
runs-on: ubuntu-latest
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
- uses: dtolnay/rust-toolchain@stable
|
||
- uses: Swatinem/rust-cache@v2
|
||
- run: cargo fmt --check --all
|
||
- run: cargo clippy -- -D warnings
|
||
|
||
rust-test:
|
||
runs-on: ubuntu-latest
|
||
services:
|
||
postgres:
|
||
image: postgres:16
|
||
env: { POSTGRES_DB: erp_test, POSTGRES_USER: test, POSTGRES_PASSWORD: test }
|
||
ports: ["5432:5432"]
|
||
options: >-
|
||
--health-cmd pg_isready
|
||
--health-interval 10s
|
||
--health-timeout 5s
|
||
--health-retries 5
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
- uses: dtolnay/rust-toolchain@stable
|
||
- uses: Swatinem/rust-cache@v2
|
||
- run: cargo test --workspace
|
||
|
||
frontend-build:
|
||
runs-on: ubuntu-latest
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
- uses: actions/setup-node@v4
|
||
with: { node-version: 20 }
|
||
- run: cd apps/web && pnpm install && pnpm build
|
||
|
||
security-audit:
|
||
runs-on: ubuntu-latest
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
- uses: dtolnay/rust-toolchain@stable
|
||
- run: cargo audit
|
||
- uses: actions/setup-node@v4
|
||
with: { node-version: 20 }
|
||
- run: cd apps/web && pnpm audit
|
||
```
|
||
|
||
**关键决策:**
|
||
- 使用 Gitea Actions(与 GitHub Actions 语法兼容)
|
||
- 每个 job 包含 `actions/checkout@v4` + 对应语言 toolchain setup
|
||
- Rust 使用 `Swatinem/rust-cache@v2` 缓存编译产物,避免每次全量编译
|
||
- PostgreSQL 通过 service 容器提供
|
||
- 四个 job 并行运行,互不依赖
|
||
- 后续可扩展:Redis service、Playwright E2E、Docker 镜像构建推送
|
||
|
||
### 2.3 审计日志补全
|
||
|
||
**当前缺口与改进:**
|
||
|
||
| 缺口 | 改进方案 |
|
||
|------|---------|
|
||
| 登录/登出只发 DomainEvent,不写审计日志 | 在 `auth_service` 的 login/logout/change_password 中调用 `audit_service::record()` |
|
||
| 审计日志缺少 `old_value`/`new_value` | 关键实体(user/role/permission/org)的 update 操作添加 `.with_changes(old, new)`。序列化完整的旧模型和新模型为 JSON,由审计日志消费者计算 diff — 比应用层计算细粒度 diff 更简单健壮 |
|
||
| 缺少 IP 地址和 User-Agent | `AuditLogBuilder::with_request_info()` 在 handler 层传入请求上下文 |
|
||
| 插件 CRUD 无审计 | `data_service` 的 create/update/delete 操作添加审计日志记录 |
|
||
| 登录失败无记录 | 添加失败登录审计(含尝试的用户名/IP),用于入侵检测 |
|
||
|
||
**验证标准:**
|
||
- 登录成功/失败均写入审计日志
|
||
- 用户更新操作记录变更前后值
|
||
- 审计日志包含 IP 和 User-Agent
|
||
|
||
### 2.4 Docker 生产化
|
||
|
||
| 改进项 | 当前 | 目标 |
|
||
|--------|------|------|
|
||
| PostgreSQL 端口 | `ports: "5432:5432"` 暴露到宿主机 | 移除 `ports:`,使用 Docker 网络内部通信 |
|
||
| Redis 端口 | `ports: "6379:6379"` 无认证 | 移除 `ports:`,添加 `--requirepass` |
|
||
| 容器资源限制 | 无 | CPU 1核 / 内存 512MB |
|
||
| 应用镜像 | 无 Dockerfile | 多阶段构建:Rust build → 精简 runtime 镜像 |
|
||
| Redis 宕机时限流 | fail-open(无限流) | fail-closed(拒绝请求) |
|
||
|
||
**限流 fail-closed 改动:**
|
||
`crates/erp-server/src/middleware/rate_limit.rs` 中 Redis 不可用时,返回 `429 Too Many Requests` 而非放行。
|
||
|
||
### 2.5 多租户安全加固
|
||
|
||
| 问题 | 改进方案 |
|
||
|------|---------|
|
||
| 登录使用硬编码 `default_tenant_id` | 登录接口增加租户解析(从子域名/请求头 `X-Tenant-ID`) |
|
||
| `auth_service::refresh()` 用户查询缺少 tenant_id(`auth_service.rs:177`) | `find_by_id` 添加 `.filter(user::Column::TenantId.eq(claims.tenant_id))` |
|
||
| 内存级 tenant_id 过滤(`user_service.rs` 的 `get_by_id`/`update`/`delete`) | 改为数据库级 `.filter(Column::TenantId.eq(tenant_id))` 查询。注意:`login`/`list`/`assign_roles` 已正确使用数据库级过滤,无需修改 |
|
||
|
||
**涉及文件:**
|
||
- `crates/erp-auth/src/handler/auth_handler.rs`
|
||
- `crates/erp-auth/src/service/auth_service.rs`
|
||
- `crates/erp-auth/src/service/user_service.rs`
|
||
- `crates/erp-auth/src/middleware/jwt_auth.rs`
|
||
|
||
---
|
||
|
||
## 3. Q3:架构强化 + 前端体验(6-8月)
|
||
|
||
### 3.1 ErpModule Trait 重构
|
||
|
||
**当前问题:**
|
||
- `register_event_handlers` 是死代码 — 所有模块实现为空操作
|
||
- 路由注册需在 `main.rs` 手动编辑两处
|
||
- 事件订阅在 `main.rs` 中手动调用,绕过 trait
|
||
|
||
**改进方案:**
|
||
|
||
基于当前 trait 签名(`erp-core/src/module.rs`),新增双路由注册和权限声明。保持与现有 `ModuleContext` 参数一致,不引入 `AppState` 依赖(避免 `erp-core` → `erp-server` 反向依赖):
|
||
|
||
```rust
|
||
pub trait ErpModule: Send + Sync + 'static {
|
||
// 保留已有方法
|
||
fn name(&self) -> &str;
|
||
fn version(&self) -> &str;
|
||
fn module_type(&self) -> &str { "business" }
|
||
fn dependencies(&self) -> Vec<&str> { vec![] }
|
||
fn id(&self) -> Uuid { /* 默认实现 */ }
|
||
fn as_any(&self) -> &dyn Any;
|
||
|
||
// 新增:双路由注册(匹配现有 public/protected 分离模式)
|
||
fn register_public_routes(&self, router: Router) -> Router { router }
|
||
fn register_protected_routes(&self, router: Router) -> Router { router }
|
||
|
||
// 重构:事件订阅真正生效(当前所有模块实现为空操作)
|
||
fn register_event_handlers(&self, bus: &EventBus) {}
|
||
|
||
// 新增:模块权限声明
|
||
fn permissions(&self) -> Vec<PermissionDef> { vec![] }
|
||
|
||
// 保留已有生命周期钩子(保持 ModuleContext 参数签名)
|
||
async fn on_startup(&self, _ctx: &ModuleContext) -> AppResult<()> { Ok(()) }
|
||
async fn on_shutdown(&self) -> AppResult<()> { Ok(()) }
|
||
async fn on_tenant_created(&self, _tenant_id: Uuid, _db: &DatabaseConnection, _bus: &EventBus) -> AppResult<()> { Ok(()) }
|
||
async fn on_tenant_deleted(&self, _tenant_id: Uuid, _db: &DatabaseConnection, _bus: &EventBus) -> AppResult<()> { Ok(()) }
|
||
async fn health_check(&self) -> AppResult<serde_json::Value> { Ok(serde_json::json!({})) }
|
||
}
|
||
```
|
||
|
||
`ModuleRegistry::build()` 自动收集路由、事件处理器和权限,`main.rs` 简化为:
|
||
|
||
```rust
|
||
let (registry, public_routes, protected_routes) = ModuleRegistry::new()
|
||
.register(auth_module)
|
||
.register(config_module)
|
||
.register(workflow_module)
|
||
.register(message_module)
|
||
.register(plugin_module)
|
||
.build();
|
||
|
||
// 自动组合:public_routes 直接挂载,protected_routes 包裹 JWT 中间件
|
||
let app = Router::new()
|
||
.merge(public_routes)
|
||
.merge(protected_routes.layer(jwt_middleware));
|
||
```
|
||
|
||
**迁移策略:** 逐模块迁移 — 每个模块从静态 `public_routes()`/`protected_routes()` 函数改为 trait 方法实现,`main.rs` 逐步简化。
|
||
|
||
**已知例外:** PluginModule 的两阶段初始化(先注册再启动事件监听器)在初期保持独立处理,不强行纳入自动化。`MessageModule::start_event_listener`、`WorkflowModule::start_timeout_checker`、`outbox::start_outbox_relay` 等独立生命周期钩子作为范围排除项,后续迭代再统一。
|
||
|
||
**迁移策略:** 逐模块迁移 — 每个模块从静态函数改为 trait 方法实现,`main.rs` 逐步简化。
|
||
|
||
### 3.2 错误映射修正 + N+1 查询优化
|
||
|
||
**错误映射修正:**
|
||
|
||
当前 `erp-auth` 服务中直接 `.map_err(|e| AuthError::Validation(e.to_string()))` 将所有 `DbErr` 映射为 `Validation`,绕过了 `erp-core` 中已有的 `From<DbErr> for AppError` 语义映射(该映射已正确处理 `RecordNotFound` → `NotFound`、重复键 → `Conflict`)。
|
||
|
||
**修复策略:** `erp-auth` 服务层停止手动包装 `DbErr`,改为通过 `?` 操作符依赖 `DbErr → AppError` 的核心映射,通过现有的 `From<AuthError> for AppError` 转换传播。这样数据库连接错误会正确显示为 `Internal`,唯一约束冲突会正确显示为 `Conflict`。
|
||
|
||
移除 `From<AppError> for AuthError` 的反向映射(当前是 lossy wrapping — `AppError::NotFound` 变为 `AuthError::Validation`,丢失语义信息)。
|
||
|
||
**N+1 查询优化:**
|
||
|
||
`user_service.rs` 的 `list()` 方法改为批量查询:
|
||
1. 先查询当前页用户列表
|
||
2. 收集所有 `user_id`
|
||
3. 一次 `WHERE user_id IN (...)` 查询 `user_role` + `role`
|
||
4. 内存中按 `user_id` 分组组装
|
||
|
||
从 N+1 查询降为 3 次固定查询(用户列表 + 角色关联 + 角色详情)。
|
||
|
||
### 3.3 前端 Error Boundary + hooks 提取
|
||
|
||
**Error Boundary:**
|
||
- `App.tsx` 根组件包裹全局 Error Boundary(捕获未预期崩溃)
|
||
- 每个懒加载页面外包裹页面级 Error Boundary(隔离单页面崩溃)
|
||
- 失败时展示友好错误页面 + 重试按钮
|
||
|
||
**hooks 提取:**
|
||
|
||
| Hook | 提取来源 | 用途 |
|
||
|------|---------|------|
|
||
| `usePaginatedData<T>` | 6+ 页面的分页加载逻辑 | 统一分页/搜索/加载状态 |
|
||
| `useDarkMode` | 8+ 文件的 `token.colorBgContainer` 字符串比较 | 提供可靠的 boolean 暗色模式判断 |
|
||
| `useCountUp` | Home.tsx + DashboardWidgets 重复实现 | 计数动画复用 |
|
||
| `useDebouncedValue` | Users.tsx 等搜索输入 | 防抖搜索,避免每次按键触发 API |
|
||
| `useApiRequest` | 所有页面的 try/catch + message.error | 统一 API 错误处理和消息提示 |
|
||
|
||
### 3.4 i18n 基础设施搭建
|
||
|
||
**方案:react-i18next**
|
||
|
||
- 安装 `react-i18next` + `i18next`
|
||
- 创建 `locales/zh-CN.json`,提取所有硬编码中文为 key
|
||
- 配置 i18next 初始化,默认 `zh-CN`
|
||
- 用 `useTranslation()` hook 替换硬编码字符串
|
||
|
||
**实施策略:** 增量式 — 新页面强制使用 i18n,旧页面按模块逐步迁移。不强求一次性替换。
|
||
|
||
**命名规范:**
|
||
- 页面文案:`{module}.{page}.{element}` 如 `auth.login.username`
|
||
- 通用文案:`common.{action}` 如 `common.save`, `common.cancel`
|
||
- 错误消息:`error.{type}` 如 `error.network`, `error.unauthorized`
|
||
|
||
### 3.5 行级数据权限接线
|
||
|
||
**当前状态:** 数据库列、SQL 条件构建器、manifest 声明已就绪,handler 层有 TODO 未实现。
|
||
|
||
**完成步骤:**
|
||
1. JWT 中间件注入 `department_ids`(完成 `jwt_auth.rs:50` 的 TODO)
|
||
2. `data_handler` 查询接口注入 data scope 条件
|
||
3. 前端角色权限编辑页添加 `data_scope` 选择控件
|
||
4. 端到端验证:创建测试角色 → 设置数据范围 → 验证查询过滤
|
||
|
||
### 3.6 前端共享类型统一
|
||
|
||
- `PaginatedResponse<T>` 从 `users.ts` 提取到 `api/types.ts`
|
||
- 错误提取工具函数 `extractErrorMessage(err: unknown): string` → `api/errors.ts`
|
||
- 插件 Schema 类型定义集中到 `types/plugin.ts`
|
||
- 移除 `api/client.ts` 中已废弃的 `CancelToken`,改用 `AbortController`
|
||
|
||
---
|
||
|
||
## 4. Q4:测试覆盖 + 插件生态(9-11月)
|
||
|
||
### 4.1 Q4 范围调整说明
|
||
|
||
Q4 原始范围较大(Testcontainers + Playwright + 进销存插件 + 热更新 + 文档清理)。调整为两个子阶段:
|
||
|
||
- **Q4a(9-10月)**:测试基础设施 — Testcontainers 集成测试框架 + Playwright E2E + 文档清理
|
||
- **Q4b(11月+)**:插件生态 — 进销存插件 + 热更新
|
||
|
||
热更新功能可视 Q4a 进度推迟到 Q1 2027,避免在单季度内承载过多工作。
|
||
|
||
### 4.2 数据库集成测试框架
|
||
|
||
**方案:Testcontainers + PostgreSQL**
|
||
|
||
创建 `crates/erp-server/tests/integration/` 目录,使用 `testcontainers` crate 启动真实 PostgreSQL 容器。
|
||
|
||
**测试基座:**
|
||
- 每个测试套件共享一个 PostgreSQL 容器
|
||
- 自动运行所有迁移
|
||
- 提供 `setup_test_db()` 辅助函数返回连接池
|
||
- 测试结束自动清理
|
||
|
||
**覆盖优先级:**
|
||
|
||
| 优先级 | 模块 | 测试场景 |
|
||
|--------|------|---------|
|
||
| P0 | erp-auth | 用户 CRUD、角色权限分配、登录/JWT 完整流程 |
|
||
| P0 | erp-auth | 多租户隔离 — 租户 A 数据对租户 B 不可见 |
|
||
| P0 | erp-plugin | 插件生命周期(install→enable→disable→uninstall) |
|
||
| P1 | erp-auth | 乐观锁并发冲突、软删除恢复 |
|
||
| P1 | erp-plugin | 行级数据权限过滤、JSONB 查询/索引 |
|
||
| P1 | erp-plugin | 动态表 DDL 正确性(generated column、pg_trgm 索引) |
|
||
| P1 | erp-workflow | 流程实例启动、任务完成、网关分支 |
|
||
| P1 | erp-core | 事件总线发布/订阅端到端、outbox relay 补偿 |
|
||
|
||
### 4.2 核心流程 E2E 测试
|
||
|
||
**方案:Playwright**
|
||
|
||
放在 `apps/web/e2e/` 目录,CI 中作为独立 job 运行。
|
||
|
||
**覆盖场景(4 个关键旅程):**
|
||
|
||
| 场景 | 步骤 |
|
||
|------|------|
|
||
| 完整登录流程 | 打开登录页 → 输入密码 → 验证 token → 刷新 token → 登出 → 验证跳转 |
|
||
| 用户管理闭环 | 创建用户 → 分配角色 → 搜索用户 → 编辑 → 软删除 → 验证列表不显示 |
|
||
| 插件安装流程 | 上传 WASM → 安装 → 验证菜单出现 → 数据 CRUD → 卸载 → 验证菜单消失 |
|
||
| 多租户隔离 | 租户 A 创建用户 → 切换租户 B → 验证查询结果为空 |
|
||
|
||
### 4.3 第二个行业插件 — 进销存(Inventory)
|
||
|
||
**选择理由:**
|
||
- 与 CRM 有天然关联(客户 → 订单 → 出库)
|
||
- 实体数量适中(5-8 个),复杂度可控
|
||
- 能验证插件系统的复用性和跨实体关联能力
|
||
- 为后续财务模块铺垫
|
||
|
||
**实体设计:**
|
||
|
||
| 实体 | 字段 | 关联 |
|
||
|------|------|------|
|
||
| product 商品 | 名称/编码/规格/单位/分类/售价/成本价 | — |
|
||
| warehouse 仓库 | 名称/地址/负责人/状态 | — |
|
||
| stock 库存 | 商品/仓库/数量/成本/预警线 | → product, warehouse |
|
||
| purchase_order 采购单 | 供应商/总金额/状态/日期 | → supplier(CRM), stock |
|
||
| sales_order 销售单 | 客户/总金额/状态/日期 | → customer(CRM), stock |
|
||
| supplier 供应商 | 名称/编码/联系方式/地址 | — |
|
||
|
||
**需要验证的插件能力:**
|
||
- 跨实体关联(订单 → 商品 → 库存联动)
|
||
- 事务性事件(库存扣减在订单确认时原子执行)
|
||
- 页面间导航(从订单跳转客户详情)
|
||
- 报表/统计页面(库存汇总、进销存明细)
|
||
|
||
### 4.4 插件热更新能力
|
||
|
||
**当前限制:** 更新插件需要完整 uninstall/reinstall。
|
||
|
||
**改进方案:**
|
||
- 新增 `POST /api/v1/admin/plugins/{id}/upgrade` 端点
|
||
- 升级流程:上传新 WASM → 对比 manifest schema → 增量 DDL(ADD COLUMN 等) → 热替换 WASM 模块
|
||
- 数据安全:`tenant_id` 数据不丢失
|
||
- 版本兼容性检查:新版本必须向后兼容或提供迁移脚本
|
||
|
||
**回滚策略:** 升级前创建 schema 备份点。升级流程分两步执行:
|
||
1. 先暂存新 WASM 并尝试验证初始化(不应用 DDL)
|
||
2. 初始化成功后,在单事务中执行 DDL 变更 + 状态转换
|
||
3. 如果新 WASM 初始化失败,保持旧 WASM 继续运行,回滚暂存状态
|
||
4. DDL 已应用但 WASM 运行异常时,保留旧 WASM 可加载作为 fallback
|
||
|
||
### 4.5 文档更新与清理
|
||
|
||
| 项目 | 改进 |
|
||
|------|------|
|
||
| Wiki 文档 | 全面更新到当前状态(前端路由、测试数量、模块能力、插件系统) |
|
||
| CLAUDE.md | 版本号修正(React 19 / Ant Design 6) |
|
||
| 根目录清理 | 删除未跟踪的开发临时文件(截图、heap dump、perf trace、agent plan 文件) |
|
||
| integration-tests/ | 验证现有测试是否能编译。若已失效则删除,用新的 Testcontainers 框架替代;若仍有效则纳入 Cargo workspace |
|
||
| N+1 查询(plugin) | `plugin_service.rs` 的列表查询也存在 N+1 问题(每条插件单独查询 entities),需一并优化 |
|
||
|
||
---
|
||
|
||
## 5. 风险与缓解
|
||
|
||
| 风险 | 概率 | 影响 | 缓解措施 |
|
||
|------|------|------|---------|
|
||
| 安全修复引入新 bug | 中 | 高 | 每个修复配有对应的测试用例 |
|
||
| ErpModule trait 重构影响所有模块 | 高 | 中 | 逐模块迁移,每步验证 `cargo test` |
|
||
| i18n 迁移工作量大 | 中 | 低 | 增量式,不追求一次性完成 |
|
||
| Testcontainers 在 CI 环境不稳定 | 低 | 中 | 本地开发可跳过集成测试,CI 用 service container 兜底 |
|
||
| Testcontainers 在 Windows (WSL2) 上兼容性 | 中 | 中 | 主开发环境为 Windows 11,Testcontainers 对 Windows 支持有限。本地开发可依赖 CI service container 运行集成测试,或使用 WSL2 环境 |
|
||
| 进销存插件实体设计变更 | 中 | 低 | 先完成最小实体集,后续迭代扩展 |
|
||
|
||
---
|
||
|
||
## 6. 成功标准
|
||
|
||
**Q2 完成标准:**
|
||
- [ ] 3 个 CRITICAL 安全问题全部修复
|
||
- [ ] Gitea Actions CI/CD 流水线运行通过
|
||
- [ ] 默认配置启动被拒绝
|
||
- [ ] 登录/登出写入审计日志
|
||
- [ ] Docker 生产化配置就绪
|
||
|
||
**Q3 完成标准:**
|
||
- [ ] ErpModule trait 路由注册自动化
|
||
- [ ] N+1 查询优化,用户列表查询次数固定为 3
|
||
- [ ] 前端 Error Boundary 覆盖全局 + 页面级
|
||
- [ ] 5 个自定义 hooks 提取完成
|
||
- [ ] i18n 基础设施可用,至少 1 个页面完成迁移
|
||
- [ ] 行级数据权限端到端验证通过
|
||
|
||
**Q4 完成标准:**
|
||
- [ ] 集成测试覆盖 auth + plugin 核心流程
|
||
- [ ] 4 个 E2E 测试场景通过
|
||
- [ ] 进销存插件 6 个实体可用
|
||
- [ ] 插件热更新功能可用
|
||
- [ ] Wiki 文档与代码同步
|