Files
cjw 6baa6b0d0a docs:ARCHITECTURE / ROADMAP 同步 D17 Human-in-Loop 完成
更新两个权威文档与 D17 端到端闭环对齐:

- docs/ROADMAP.md §2.2 主线二"moldinsight 工程化增强"重点方向
  加一条 D17 已完成条目(2026-09-23~24,3 个 commit:数据 + 权限 +
  写入 API / 算法接缝 + OCC payload / 前端按钮 + Dialog + 经验角标;
  详见 TECH_DEBT.md D17),与既有 ~~XXX~~(YYYY-MM-DD 完成)格式一致

- docs/ARCHITECTURE.md 新增 §6.4 D17 Human-in-Loop 老师傅经验反馈闭环
  —— 已完成段:用 ASCII 数据流图展示老师傅点反馈按钮 → 路由层
  → service 写入 → 续期衰减 → 上传新 STP 触发 resolve_for_process_params
  → OCC payload 透传 → planner 算法加成 → ResultView 渲染的端到端链路

  段内列出"硬规则遵守"(跨模块 FK 守 §5.1、OCC payload 守
  occ_worker.py:7-8、D9 边界不破、init_db.py 幂等修复已落)和
  "重量级约束"(weight 仅正向、sample_count<2 时 ×0.5、graceful 退化、
  角色门控),最后给测试基线指针

  放在 §6.3"文档与结构尚未完全同步"之后作为"已完成端到端闭环"
  对照示例,便于新成员理解 D17 在系统中的位置

文档侧仅变更,无代码改动;按 AGENTS.md §4.1 映射表,模块边界
(ARCHITECTURE.md)/ 演进路线(ROADMAP.md)相关变更同步。

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-24 10:15:36 +08:00

313 lines
15 KiB
Markdown
Raw Permalink 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.
# geMoldInsight 架构与边界(ARCHITECTURE)
> 文档定位:**当前架构、模块边界与结构原则的权威文档**。
> 本文描述“现在的系统结构是什么、边界如何划分、目标形态是什么”;不负责维护当前实现进度,当前状态见 [STATUS.md](STATUS.md),部署见 [DEPLOYMENT.md](DEPLOYMENT.md),演进路线见 [ROADMAP.md](ROADMAP.md)。
> 如需追溯模块化设计蓝图与扩展讨论,见 [archive/BACKEND_MODULARIZATION_BLUEPRINT.md](archive/BACKEND_MODULARIZATION_BLUEPRINT.md)。
---
## 1. 架构目标
geMoldInsight 的目标架构不是微服务,也不是继续维持历史单体,而是:
> **单仓库 + 单数据库 + 多模块 + 可独立部署的 modular monolith**
这意味着:
- 保持同一 Git 仓库
- 保持同一 PostgreSQL 数据库
- 按模块组织业务代码与部署入口
- 在不拆库、不拆仓的前提下,明确业务边界与部署边界
---
## 2. 当前模块划分
### 2.1 moldinsight
目录:
- [src/moldinsight/](../src/moldinsight/)
职责:
- STEP/STP 上传与任务管理
- 几何分析与特征识别
- 模具方案生成
- 批量分析
- 成本估算
- 导出与结果查询
- 结合 Celery 执行异步分析链路
### 2.2 inventory
目录:
- [src/inventory/](../src/inventory/)
职责:
- 成品/物料管理
- BOM 管理
- 库存与库存流水
- 采购订单 / 销售订单
- 财务与对账
- 采购建议推导
### 2.3 frontend
目录:
- [frontend/](../frontend/)
职责:
- 模具分析页面
- 进销存页面
- 登录/用户管理
- 统一路由与状态管理
- 基于 OpenAPI 类型生成的前端调用
### 2.4 shared(当前平台层)
目录:
- [src/shared/](../src/shared/)
职责:
- 配置
- 数据库连接与 session
- 认证与权限
- 日志与 request_id
- 应用工厂与共用中间件
说明:
- `shared` 的定位已明确为**平台层(跨模块基础能力)**;模块专属接线(RustFS 启动 / HTML 报告挂载 / 路由聚合)已收敛回模块层(见 §6.2),当前仓库结构仍以 `shared` 为事实名称
- 剩余语义收敛(identity 平台表 vs 模块表的命名与注释口径)随实际重构继续推进
---
## 3. 当前代码结构
当前核心结构如下:
```text
geMoldInsight/
├── src/
│ ├── entrypoints/
│ │ ├── moldinsight.py
│ │ └── inventory.py
│ ├── shared/
│ ├── moldinsight/
│ ├── inventory/
│ ├── celery_app.py
│ └── celery_tasks.py
├── frontend/
├── migrations/
├── deploy/
├── docs/
└── tests/
```
这反映的是**当前实际代码组织**,不是历史单体结构。
---
## 4. 部署边界
当前设计上支持三种部署模式:
### 4.1 unified
一个统一后端同时承载 moldinsight + inventory,并作为前端默认反代目标。
适合:
- 本地开发
- 集成环境
- 小团队统一部署
### 4.2 moldinsight-only
只部署模具分析后端。
适合:
- 单独开放分析能力
- 异步任务与文件处理独立扩容
入口:
- [src/entrypoints/moldinsight.py](../src/entrypoints/moldinsight.py)
### 4.3 inventory-only
只部署进销存后端。
适合:
- 独立使用 ERP / 库存能力
- 与 moldinsight 分开部署节奏
入口:
- [src/entrypoints/inventory.py](../src/entrypoints/inventory.py)
部署操作与当前推荐方案见 [DEPLOYMENT.md](DEPLOYMENT.md)。
---
## 5. 关键架构原则
### 5.1 单数据库是刻意设计
项目不是把 moldinsight 与 inventory 强拆成两个数据库,而是保留共享数据库,以支撑完整业务闭环:
- 分析结果
- 创建成品
- 成品 BOM
- 销售 / 采购 / 库存
典型桥接关系示例:
- `STPFile.product_id -> Product.id`
桥接只允许**裸 FK 列**(字符串表名),**不允许跨模块 ORM relationship**——单模块部署下另一模块的模型类可能未注册,跨模块 relationship 会让 mapper 配置直接失败(2026-09-17 批次 4 起为硬规则,原三条跨模块 relationship 均无使用方,已删除;对象化查询由使用方显式 select)。
### 5.2 模块边界优先于“临时方便”
新增逻辑时,应优先放入对应业务模块,而不是继续堆进 `shared`。
原则上:
- moldinsight 业务进入 `src/moldinsight/`
- inventory 业务进入 `src/inventory/`
- 只有真正跨模块复用的基础能力才进入 `src/shared/`
### 5.3 前端是独立工程,不是后端静态附属
前端已是独立 Vite/Vue 工程,部署上可与后端组合,但在代码组织上应视为独立模块,而不是后端 `static/` 的扩展。
---
## 6. 当前主要耦合点
虽然模块化已经成型,但仍有几个关键耦合点需要持续关注:
### 6.1 共享 ORM 模型 —— 已按模块拆分(2026-09-17,批次 4)
历史上的 `shared/models/database.py`(31 个模型类三类同居)已拆除,现为按归属分置:
- [src/shared/models/base.py](../src/shared/models/base.py):唯一 `Base` + 归属约定与全量注册点说明
- [src/shared/models/identity.py](../src/shared/models/identity.py):用户/角色/权限/审计(平台层,所有部署形态共用)
- [src/moldinsight/models/](../src/moldinsight/models/):STEP 分析域 9 表(stp_files 及各阶段产物、processing_tasks)
- [src/inventory/models/](../src/inventory/models/):进销存 15 表(catalog / warehouse / trading / finance 四域文件)
跨模块只允许裸 FK(规则见 §5.1);全量模型注册点收敛为 `migrations/env.py` 与 `tests/conftest.py`;归属边界由 [tests/test_model_ownership.py](../tests/test_model_ownership.py) 锁定(含单模块独立 mapper 配置与旧模块无 facade 断言)。
### 6.2 app factory 组合职责 —— 已收敛(2026-09-18,D3 剩余)
平台层与模块专属接线的边界已明确(`shared` = 跨模块基础能力,模块专属接线归属模块层):
- **平台工厂 [src/shared/app_factory.py](../src/shared/app_factory.py) 只做纯平台引导**:CORS / 请求日志 / 目录准备 / 静态托管 / 数据库与 Redis 启动 / auth 路由 / /health / SPA fallback。`startup_hooks` 参数承载模块专属启动接线——原 `connect_rustfs` 参数(平台工厂持有 moldinsight 依赖)已移除。
- **moldinsight 专属接线收敛回 moldinsight 层**:
- RustFS 启动 → [init_storage.py](../src/moldinsight/storage/init_storage.py) 的 `rustfs_startup_hook`(moldinsight/unified 入口经 `startup_hooks` 注入)
- /api 路由聚合 + HTML 报告根路径挂载 → [moldinsight/api/__init__.py](../src/moldinsight/api/__init__.py) 的 `register_moldinsight_routers`(入口只做单点调用,不再重复 include html_report)
- **入口 [src/entrypoints/](../src/entrypoints/) 退化为纯组装**:sys.path 修正 + 调 create_app + 传 startup_hooks / register_routers。
### 6.3 文档与结构尚未完全同步
代码结构已明显模块化,但历史文档中仍保留不少阶段性叙述、旧部署语义与重复说明,这也是本轮文档整理要解决的问题之一。
### 6.4 D17 Human-in-Loop 老师傅经验反馈闭环 —— 已完成(2026-09-23~24,3 个 commit)
算法演进由老师傅经验驱动:通过方案级整体反馈(采纳 / 建议调整 / 拒绝)按"产品指纹 + 工艺参数"为键跨任务匹配,下次同指纹产品分析自动消费老师傅沉淀的经验。这是少数"算法层由用户在线学习样本持续校准"的端到端闭环。
**端到端数据流**:
```
┌─────────────────────────────────────────────────────────────┐
│ 老师傅在 ResultView 点 👍 老师傅反馈按钮 │
│ → HumanFeedbackDialog 三选一(采纳 / 建议调整 / 拒绝) │
└──────────────────────────────┬──────────────────────────────┘
│ POST /api/tasks/{id}/experience-feedback
▼
┌──────────────────────────────────────────────────────────────┐
│ experience_feedback_router (src/moldinsight/api/) │
│ - ensure_task_access 归属校验 │
│ - current_user.has_permission("feedback_experience_hint") │
│ - service.record_feedback (flush; commit + invalidate) │
└──────────────────────────────┬───────────────────────────────┘
▼
┌──────────────────────────────────────────────────────────────┐
│ experience_feedback_service.record_feedback │
│ - compute_fingerprint (bbox_aspect / volume_bucket / │
│ face_bucket / undercut_class / material_family / is_foam) │
│ - 写 experience_feedback 表(D9 边界 / D17 衰减 90d TTL) │
│ - 同 stp_file_id 整体续期 expires_at │
└──────────────────────────────┬───────────────────────────────┘
│ 同 stp_file_id 上传新 STP 自动消费
▼
┌──────────────────────────────────────────────────────────────┐
│ processing_service._step_generate_cavity │
│ - experience_feedback_service.resolve_for_process_params │
│ → hints (List[{scheme_axis, weight, sample_count, ...}]) │
│ - hints 装进 run_occ payload 顶层 experience_hints │
└──────────────────────────────┬───────────────────────────────┘
│ OCC 子进程(spawn 隔离)
▼
┌──────────────────────────────────────────────────────────────┐
│ occ_worker._op_generate_cavity │
│ - payload.get("experience_hints") or {} → planner.generate_plan(hints=...) │
└──────────────────────────────┬───────────────────────────────┘
▼
┌──────────────────────────────────────────────────────────────┐
│ MultiSchemeMoldPlanner.generate_plan(..., hints=None) │
│ - candidate_generator.generate_candidates(..., hints) │
│ * priority_score += weight × 20 │
│ * sample_count ≥ 2 + weight ≥ 0.5 → method="human_experience_primary" │
│ - scheme_scorer.score_schemes(schemes, *, hints) │
│ * score_breakdown["human_hint_bonus"] = weight × 12 │
│ (sample_count < 2 时 ×0.5 折半) │
│ - global_summary.applied_hints 注入返回 │
└──────────────────────────────┬───────────────────────────────┘
▼
┌──────────────────────────────────────────────────────────────┐
│ ResultView 渲染: │
│ - summary-header 加 t-tag theme="success" 📚 历史经验 N 条 │
│ - 反馈提交后 onFeedbackSubmitted → loadExperienceHints 即刷 │
└──────────────────────────────────────────────────────────────┘
```
**硬规则遵守**:
- 跨模块 FK 仍守 §5.1(`experience_feedback.user_id` / `processing_task_id` / `stp_file_id` 全用字符串表名,无 ORM relationship)
- OCC 跨进程守 [occ_worker.py:7-8](../src/moldinsight/core/occ_worker.py#L7-L8) "杜绝 pickle OCC 对象"——payload 普通 dict 透传
- D9 边界不破:service.flush + 路由 commit(无 service 内 commit)
- 现有 `init_db.py` 幂等修复:按 code 补登权限/角色
**重量级约束**:
- `weight = max(0, (adopted-rejected)/total)`:仅正向有效,老师傅拒绝不"扣分"老算法
- `sample_count < 2` 时 bonus ×0.5:信号不足折半,但 priority_score 仍加成(候选方向仍偏向)
- 解析失败回退空 list:graceful,主流程不因下游错误退化
- `canGiveFeedback` 角色门控:`is_superuser || roles 含 process_engineer`
**测试基线**:192 passed, 13 skipped(批 3 净增 +4 OCC-gated:candidate_generator 3 / scheme_scorer 4 / multi_scheme_planner 2 / processing_service 2);前端 vue-tsc + vite 通过。
详见 [TECH_DEBT.md](TECH_DEBT.md) D17 + [STATUS.md](STATUS.md) 2026-09-23~24 日志。
---
## 7. 专题文档与主骨架的关系
以下文档仍可作为专题补充参考,但不再承担默认入口职责:
- 存储方向:
- [topics/storage/RUSTFS_STORAGE.md](topics/storage/RUSTFS_STORAGE.md)
- [topics/storage/STORAGE_SETUP.md](topics/storage/STORAGE_SETUP.md)
- 性能方向:
- [topics/performance/OCC_THROUGHPUT.md](topics/performance/OCC_THROUGHPUT.md)(OCC 吞吐与隔离方案设计,TECH_DEBT D10 归属)
AI、铝泡沫等更偏历史设计/规划性质的专题材料已迁入 [archive/README.md](archive/README.md)。
阶段性任务清单、迁移计划、历史总结等文档会逐步迁入 [archive/README.md](archive/README.md)。
---
## 8. 与相关文档的边界
- 想看“当前做到哪一步”:看 [STATUS.md](STATUS.md)
- 想看“后面还要往哪演进”:看 [ROADMAP.md](ROADMAP.md)
- 想看“当前有哪些活跃技术债”:看 [TECH_DEBT.md](TECH_DEBT.md)
- 想看“配置怎么给、服务怎么起”:看 [OPERATIONS.md](OPERATIONS.md)
- 想看“前后端接口契约”:看 [API_CONTRACT.md](API_CONTRACT.md)
- 想看“怎么部署”:看 [DEPLOYMENT.md](DEPLOYMENT.md)
- 想看“模块化蓝图与更完整设计讨论”:看 [archive/BACKEND_MODULARIZATION_BLUEPRINT.md](archive/BACKEND_MODULARIZATION_BLUEPRINT.md)