Files
erp/docs/superpowers/specs/2026-04-17-platform-maturity-roadmap-design.md
iven 432eb2f9f5 docs: 添加平台全面成熟度提升路线图设计规格
覆盖安全地基、架构强化、测试覆盖、插件生态四个维度,
按 Q2-Q4 三季度分层推进的全面改进计划。
2026-04-17 15:58:31 +08:00

16 KiB
Raw Blame History

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 个季度的分层改进,将平台从"功能完整"推进到"生产就绪"

  • Q24-5月:消除安全风险,建立自动化质量门
  • Q36-8月:强化架构,提升前端工程化水平
  • Q49-11月:补齐测试覆盖,扩展插件生态

1.4 约束

  • 独立开发者 + Claude 辅助 — 每季度聚焦单一维度
  • SaaS 优先部署 — 多租户安全是硬性要求
  • 不破坏现有功能 — 所有改进必须向后兼容

2. Q2安全地基 + CI/CD4-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__AUTH__ADMIN_PASSWORD 读取初始密码,未设置则拒绝初始化(移除 fallback 到硬编码值的逻辑)
  4. 清理 .test_token:加入 .gitignore,从 git 历史中移除
  5. default.toml 占位符:敏感字段改为 "__MUST_SET_VIA_ENV__" 之类的明显占位值

验证标准:

  • 默认配置启动时服务拒绝运行
  • 环境变量设置后正常启动
  • .test_token 不再出现在仓库中

2.2 Gitea Actions CI/CD

流水线设计:

name: CI
on: [push, pull_request]

jobs:
  rust-check:
    runs-on: ubuntu-latest
    steps:
      - cargo fmt --check --all
      - 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:
      - cargo test --workspace

  frontend-build:
    runs-on: ubuntu-latest
    steps:
      - cd apps/web && pnpm install && pnpm build

  security-audit:
    runs-on: ubuntu-latest
    steps:
      - cargo audit
      - cd apps/web && pnpm audit

关键决策:

  • 使用 Gitea Actions service 容器提供 PostgreSQL
  • 四个 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)
缺少 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 find_by_id 添加 .filter(user::Column::TenantId.eq(claims.tenant_id))
内存级 tenant_id 过滤 改为数据库级 .filter(Column::TenantId.eq(tenant_id)) 查询

涉及文件:

  • 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

改进方案:

重构 ErpModule trait新增路由注册和权限声明

pub trait ErpModule: Send + Sync + 'static {
    fn name(&self) -> &str;
    fn version(&self) -> &str;
    fn dependencies(&self) -> Vec<&str> { vec![] }

    // 新增:路由注册自动化
    fn register_routes(&self, router: Router) -> Router { router }

    // 重构:事件订阅真正生效
    fn register_event_handlers(&self, bus: &EventBus) {}

    // 新增:模块权限声明
    fn permissions(&self) -> Vec<PermissionDef> { vec![] }

    // 保留已有生命周期钩子
    async fn on_startup(&self, _state: &AppState) -> AppResult<()> { Ok(()) }
    async fn on_shutdown(&self) -> AppResult<()> { Ok(()) }
    async fn on_tenant_created(&self, _tenant_id: Uuid, _state: &AppState) -> AppResult<()> { Ok(()) }
    async fn on_tenant_deleted(&self, _tenant_id: Uuid, _state: &AppState) -> AppResult<()> { Ok(()) }
    async fn health_check(&self) -> AppResult<()> { Ok(()) }
}

ModuleRegistry::build() 自动收集路由、事件处理器和权限:

let (registry, router) = ModuleRegistry::new()
    .register(auth_module)
    .register(config_module)
    .register(workflow_module)
    .register(message_module)
    .register(plugin_module)
    .build(router);

迁移策略: 逐模块迁移 — 每个模块从静态函数改为 trait 方法实现,main.rs 逐步简化。

3.2 错误映射修正 + N+1 查询优化

错误映射修正:

当前 DbErr 统一映射为 AuthError::Validation,掩盖了连接失败和约束冲突。

改为语义化映射:

  • DbErr::ConnectionErrAppError::Internal("数据库连接失败")
  • DbErr::RecordNotFoundAppError::NotFound
  • DbErr + 消息含 "duplicate key"AppError::Conflict("记录已存在")
  • DbErr + 消息含 "foreign key"AppError::Validation("关联数据不存在")

移除 From<AppError> for AuthError 的反向映射lossy wrapping

N+1 查询优化:

user_service.rslist() 方法改为批量查询:

  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): stringapi/errors.ts
  • 插件 Schema 类型定义集中到 types/plugin.ts
  • 移除 api/client.ts 中已废弃的 CancelToken,改用 AbortController

4. Q4测试覆盖 + 插件生态9-11月

4.1 数据库集成测试框架

方案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 → 增量 DDLADD COLUMN 等) → 热替换 WASM 模块
  • 数据安全:tenant_id 数据不丢失
  • 版本兼容性检查:新版本必须向后兼容或提供迁移脚本

4.5 文档更新与清理

项目 改进
Wiki 文档 全面更新到当前状态(前端路由、测试数量、模块能力、插件系统)
CLAUDE.md 版本号修正React 19 / Ant Design 6
根目录清理 删除未跟踪的开发临时文件截图、heap dump、perf trace、agent plan 文件)
integration-tests/ 纳入 Cargo workspace 或合并到 crates/erp-server/tests/
erp-common 引用 从文档中移除不存在的 crate 引用

5. 风险与缓解

风险 概率 影响 缓解措施
安全修复引入新 bug 每个修复配有对应的测试用例
ErpModule trait 重构影响所有模块 逐模块迁移,每步验证 cargo test
i18n 迁移工作量大 增量式,不追求一次性完成
Testcontainers 在 CI 环境不稳定 本地开发可跳过集成测试CI 用 service container 兜底
进销存插件实体设计变更 先完成最小实体集,后续迭代扩展

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 文档与代码同步