Files
nj/wiki/architecture.md
iven ab58186ab3 docs(wiki): 全量项目健康度评估 + 技术债全景更新
- 新增 project-health.md — 项目评分/技术债全景/风险矩阵/改进路线图
- 更新 index.md — 代码量分布表/新发现技术债统计/新增症状条目
- 更新 architecture.md — Feature Flag 未实现状态/超大文件发现
- 更新 frontend.md — 状态管理不统一/SSE 端口/测试缺失等 11 项问题
- 更新 erp-diary.md — 代码量分布参考/班级码硬编码问题

基于 4 代理并行深度分析: 后端 Rust 51,459 行 + 前端 Flutter 18,398 行
2026-06-01 18:33:38 +08:00

126 lines
5.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: 架构决策
updated: 2026-06-01
status: active
tags: [architecture, base, multi-tenant, security]
---
# 架构决策
> 从 [[index]] 导航。关联: [[data-layer]] [[erp-diary]] [[frontend]]
## 1. 设计决策
### Q: 为什么从 HMS 剥离而不是 fork
独立 `base.git` 仓库https://git.stableeasy.com/iven/base.git作为通用 ERP 基座多个项目可独立克隆后添加业务模块。fork 会将两个项目的 git 历史绑定,无法独立演进。
### Q: 为什么用 Feature Flag 组装模块?
`cargo build --features diary` 按需引入暖记模块。基座 crateauth/config/message/workflow/plugin不需要 diary 功能时零开销。
### Q: 为什么选 Flutter 而不是原生?
Android + iOS 跨平台首发。CustomPainter + Listener 手写性能满足 <16ms 目标,一套代码双端覆盖。
### Q: 为什么选 BLoC 不是 Provider/Riverpod
编辑器、日历、同步引擎等复杂交互需要 Event/State 显式建模。BLoC 的单向数据流在复杂场景下比 Provider 更可控。
### 基座剥离 — 7 个耦合点
从 HMS 剥离到 base.git 时遇到的耦合及解决方案:
| 耦合点 | 问题 | 解决 |
|--------|------|------|
| main.rs 业务初始化 | 硬编码 health 模块注册 | 改为 Feature Flag 动态注册 |
| AppState 业务字段 | 嵌入 HealthState 等 | 改为 trait object / AnyMap |
| 迁移文件混合 | 基座 + 业务迁移在同一目录 | 按 prefix 分类,业务迁移独立 |
| 路由注册 | 业务路由写死在基座 | Module trait 的 routes() 方法 |
| 事件定义 | 业务事件在基座 crate | 各模块自定义 event.rs |
| 权限 seed | 业务权限码在基座迁移 | 各模块自带 seed 迁移 |
| Cargo.toml | 业务依赖在 workspace 根 | 各业务 crate 独立管理 |
## 2. 关键文件 + 数据流
### 仓库拓扑
```
HMS (G:\hms) [只读]
└─复制→ base.git (https://git.stableeasy.com/iven/base.git)
└─克隆→ nj.git (暖记 = 基座 + erp-diary + Flutter)
```
### Cargo Workspace
```
nj/crates/
├── erp-core/ # 基座 — 模块系统/事件/加密/错误
├── erp-auth/ # 基座 — JWT/RBAC/用户/角色
├── erp-config/ # 基座 — 字典/菜单/设置
├── erp-message/ # 基座 — 消息/通知/SSE
├── erp-workflow/ # 基座 — BPMN 工作流
├── erp-plugin/ # 基座 — WASM 插件运行时
├── erp-server/ # Axum 入口 + 迁移 + 路由组装
└── erp-diary/ # 暖记业务模块 (~5,500 行新增)
```
### 集成契约
| 方向 | 模块 | 接口 | 触发时机 |
|------|------|------|---------|
| 被调用 ← | erp-server | `DiaryModule::new()` | 启动时 feature=diary |
| 调用 → | erp-core | `EventBus` | diary.created 等事件 |
| 调用 → | erp-auth | `require_permission()` | handler 层权限守卫 |
| 提供 → | erp-server | `routes()` + `entities()` | Axum 路由注册 |
## 3. 代码逻辑
### 不变量
**G:\hms 只读** — 绝不修改 HMS 源码,所有操作在 nj 仓库进行
**业务 crate 间无直接依赖** — erp-diary 不 import erp-health只通过 EventBus 通信
**所有查询带 tenant_id** — 多租户中间件自动注入API 路径不含 tenant_id
**软删除不硬删** — 所有 delete 操作设置 deleted_at不做 DELETE FROM
**version 乐观锁** — 所有 Entity 的 version 字段用于同步冲突检测
### 多租户隔离策略
中间件从 JWT 提取 tenant_id → 注入请求扩展 → handler/service 层自动过滤。API 路径不含 tenant_id`/api/v1/diary/...` 而非 `/{tenant}/diary/...`)。
### PIPL 合规
- 未满 14 岁必须家长授权
- 最小必要数据(昵称 + 年级,无需真实姓名)
- AES-256-GCM 加密 + TLS 传输
- 30 天内注销删除所有关联数据
- 班级码6 位混合码5 次错误锁定 30 分钟
## 4. 活跃问题 + 陷阱
| 问题 | 级别 | 状态 | 说明 |
|------|------|------|------|
| Feature Flag 未实现 | HIGH | 待做 | erp-server/Cargo.toml 无 `[features]` 段,所有模块无条件编译 |
| Docker 部署未验证 | HIGH | 待做 | docker/ 配置完善但 Dockerfile 不存在,未实际运行 |
| 上下文窗口耗尽 | MEDIUM | 已缓解 | CLAUDE.md §8 会话交接机制 |
| Windows Defender 锁定 exe | MEDIUM | 需手动 | 排除 target/ 目录 |
| erp-plugin 超大文件 | LOW | 待重构 | manifest.rs (1809行) + data_service.rs (1907行) 超过 800 行限制 |
| erp-message module.rs 过大 | LOW | 待重构 | 1283 行,事件监听逻辑可拆分 |
### 历史教训
- 基座剥离耗时比预期长7 个耦合点需逐一解耦)
- Isar 3.x 扩展方法不随传递 import 传播,必须显式 import
- Feature Flag 在基座剥离规划中设计完善,但实际实施时未落地到 Cargo.toml
## 5. 变更记录
| 日期 | 变更 |
|------|------|
| 2026-06-01 | 补充 Feature Flag 状态、超大文件发现 |
| 2026-06-01 | 初始创建 — 架构决策、基座剥离记录、集成契约 |