Files
geMoldInsight/docs/TECH_DEBT.md
T
cjw 2fd1b3da21 🐛 fix(deploy): 修干净机器首次构建两处必挂——.dockerignore 排除 deploy/ + celery 并行构建依赖
部署机首次 docker compose up -d 实测暴露:

- .dockerignore 自"重写独立dockerfile"起排除整个 deploy/,而
  Dockerfile.frontend COPY deploy/nginx/frontend.conf、Dockerfile.moldinsight
  COPY deploy/requirements-*.txt → COPY not found。历史一直有旧镜像兜底未暴露;
  BuildKit 不支持重包含被排除目录的子文件,直接移除该行
- Dockerfile.celery FROM gemold-backend:latest 在 compose 并行构建下引用
  尚不存在的本地镜像必挂 → 删除 Dockerfile.celery,moldinsight-celery 改为
  与 API 服务同一 build 声明 + 同一 gemold-backend:latest tag(compose 去重
  只构建一次),celery 仅以 command: 覆盖启动 worker,参数语义不变
- build.sh/.bat 移除 gemold-celery 构建步骤;OPERATIONS / OCC_THROUGHPUT /
  TECH_DEBT / .env.example 的 Dockerfile.celery 指向同步改写;STATUS 补录

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-24 16:18:44 +08:00

27 KiB
Raw Blame History

geMoldInsight 技术债与治理计划(TECH_DEBT)

文档定位:当前活跃技术债与治理计划的权威文档。 本文回答“现在还有哪些重要债务、优先级如何、下一步怎么处理”;不负责维护当前实现状态,当前状态见 STATUS.md。架构边界见 ARCHITECTURE.md,未来路线见 ROADMAP.md。 本文由归档文档 archive/MOLDINSIGHT_TECH_DEBT_PLAN.md 收敛整理而来,保留活跃债务与治理结论,弱化详细实施流水账。


1. 当前技术债概览

当前最主要的技术债集中在三个区域:

  • moldinsight API 与处理链路的结构收口
  • inventory 复杂业务域的 service 继续下沉
  • 文档 / 部署 / 历史语义与当前代码现状未完全一致

已经完成的高优先级治理不再作为持续待办反复展开,当前重点聚焦在“还没完成、且值得继续推进”的部分。


2. 已完成的重要治理(主题摘要)

按主题归类的高价值治理已完成项。每项的具体修复清单 / 迁移号 / 回归测试 / 测试基线见归档:

2.1 安全与权限

  • debug / history 路由补鉴权;任务访问控制收紧;无主数据不再默认放行
  • /api/status/{task_id} 补 JWT 鉴权与归属校验(原 D5 → §3 D5)
  • bcrypt 超 72 字节显式拒绝 + 截断比较;SECRET_KEY / RUSTFS_* 缺失明确报错

2.2 静默失败与可用性

  • detect-undercuts 改为基于真实 shape 分析
  • OCC 超时后重建 executor(短期)→ D10 方案 B 进程化彻底替换
  • 后台任务统一分派,补强引用与并发控制

2.3 状态存储与缓存

  • Redis 任务状态改为 Hash 字段级更新,兼容旧格式
  • 内存回退彻底删除,PG 为任务状态单一事实源(原 D7)
  • 完成态任务视图缓存;导出缓存与持久化链路收口

2.4 架构与代码清理

  • 删除旧单体入口与死代码(db_manager.create_tables / log_user_activity / CADExporter.export_mold_results / getAluminumPrice 等)
  • 惰性配置校验,提升可测试性
  • Generator 公共接口提取 + 契约测试
  • 共享 ORM 按模块拆分,跨模块桥接收敛为裸 FK 硬规则(ARCHITECTURE §5.1)

3. 当前活跃技术债

D1. advanced_router 过大,职责混杂 —— 已清偿(2026-09-17,批次 3)

修复内容:

  • 592 行的 advanced_router 按职责拆为四个子路由,端点路径全部不变:design_router.py(布局/冷浇/模架/倒扣)、cost_router.py、machining_router.py(CAM/碰撞/刀路/电极/仿真)、export_router.py(导出/下载/建议)
  • 全部请求体改 Pydantic 模型(request.json() 手动解析退役),校验失败统一 422;_get_cached_import 上提为 core_modules.py 共用
  • 契约测试:tests/test_advanced_split_contract.py(路径不丢、鉴权不丢、422 语义、纯计算端点冒烟)
  • openapi.json 重导出 + 前端 gen:api(接口变更三件套随批完成)

原现状 / 影响:导出/估算/设计接口混在单文件,边界不清晰、OpenAPI 可读性差、参数校验不统一。

D2. 铝价模拟数据未显式标注来源 —— 已清偿(2026-09-18,批次 4 后续专项)

修复内容(保留编号以维持引用稳定):

  • src/moldinsight/services/aluminum_price_service.py:get_aluminum_current_price 响应补 source: "simulated",get_aluminum_price_history 逐项补同字段
  • frontend/src/modules/home/HomeView.vue:按 source 字段渲染"模拟数据 · 参考走势,非实时行情"标注(不再硬编码"上海期货交易所"等虚假来源)
  • 死代码 getAluminumPrice 删除(前端此前保留了一份本地硬编码函数,已无调用方)

原现状 / 影响:铝价接口返回走势数据但无来源声明,前端原硬编码"上海期货交易所"字样,与实际模拟数据不一致,属虚假来源声明。

D3. shared/platform 边界仍需继续收敛 —— 主体已清偿(2026-09-17 批次 4 + 2026-09-18 后续)

已完成部分(修复文件清单见 archive/2026-09_governance_batches.md 批次 4 / 后续专项):

  • 共享 ORM(原最强耦合点)按模块拆分:base / identity(shared)+ moldinsight/models + inventory/models;跨模块只允许裸 FK,单模块部署 mapper 可独立配置(详见 ARCHITECTURE.md §6.1)
  • 旧 shared/models/database.py 物理删除,无兼容 facade;归属边界由 tests/test_model_ownership.py 锁定
  • app_factory 组合职责收敛(2026-09-18):平台工厂只做纯平台引导,connect_rustfs 参数移除;moldinsight 专属接线(RustFS 启动钩子 init_storage.py rustfs_startup_hook、路由单点聚合 moldinsight/api/__init__.py register_moldinsight_routers)收敛回模块层,入口退化为纯组装(ARCHITECTURE §6.2)

仍保留的收敛方向(低优先级,随实际重构推进):

  • identity / platform 的边界语义(ROADMAP §2.1):平台表与模块表的命名/注释口径随实际重构推进

优先级:P3(仅剩 identity/platform 语义注释口径)

D4. 文档现状 / 规划 / 历史混放 —— 已清偿(2026-09-22)

修复内容(保留编号以维持引用稳定):

  • 已建立 STATUS / ARCHITECTURE / ROADMAP / TECH_DEBT / DEPLOYMENT 主骨架,每类信息单一归属;历史材料归档至 docs/archive/
  • 本文档 §2 由"按批次回顾"精简为"按主题摘要",修复文件清单 / 迁移号 / 回归测试 / 测试基线等详细流水账整体迁入 archive/2026-09_governance_batches.md(避免与 §3 重复膨胀)
  • §3 中对历史批次的引用(如 D3 → §2.8)改为 archive 指针;D2 等已清偿项补齐时间戳

原现状 / 影响:TECH_DEBT §2 与 §3 内容重复膨胀,文档目录结构清晰度受新成员评估影响。

D5. /api/status/{task_id} 未鉴权(安全缺口)—— 已清偿(2026-09-16,批次 0)

修复内容(保留编号以维持 D6–D14 引用稳定):

  • 端点补 Depends(get_current_active_user);归属校验收敛为 TaskQueryService.ensure_task_access,task_router 与 advanced_router 共用(advanced_router 原私有 _ensure_task_access 改为委托)
  • 语义:无 token 401、他人/无主任务 403(无主不等于公共)、任务不存在 404
  • 回归测试:tests/test_status_endpoint_auth.py
  • 接口行为变化已同步 API_CONTRACT.md §3.2

原现状 / 影响:端点未挂鉴权,匿名可枚举任务号拉取完整分析视图。

D6. 主处理链路依赖节点本地文件路径 —— 已清偿(2026-09-16,批次 1)

修复内容(保留编号以维持引用稳定):

  • 分派入参收敛为 stp_file_id(dispatch_processing 与 Celery 任务签名同步变更):处理方按 PG 元数据从 RustFS 下载源文件到任务专属临时目录(保留原始文件名,下游产物命名不变),任务结束即清理(processing_service.py _materialize_source_file)
  • RustFS 不可用时回退 STPFile.file_path 节点本地路径;compose 为 backend / celery 增加共享卷 uploads_data / html_data 作过渡兜底(HTML 产物跨容器写读同源问题一并兜住,正式修复在 D11)

原现状 / 影响:worker 直读 API 节点本地路径,双容器部署必然 FileNotFoundError。

D7. Redis 降级为进程内 dict,多副本状态不一致 —— 已清偿(2026-09-16,批次 2)

修复内容(比原建议更彻底:完全删除内存回退,而非仅限 DEBUG):

  • redis_task_manager.py 删除全部 _fallback_* 进程内存存储:Redis 不可用时写 no-op、读返回 None(Redis 仅热缓存,任务状态事实源在 PG,缓存缺失不影响正确性)
  • 批量元数据入库:processing_tasks 新增 batch_id 列(迁移 a3f8c2d91e47),GET /api/batch/{batch_id} 改为按列聚合查询 + STPFile.user_id 归属校验,删除 Redis batch key 与进程内 dict 双通道
  • TaskQueryService 的 PG 组装视图补 progress / current_step(Redis 不可用时前端轮询仍能看到进度);batch 聚合响应同步补 current_step

原现状 / 影响:Redis 故障时状态静默降级各进程内存,多副本互不可见、同任务不同副本读到不同状态。

D8. 型腔生成失败被静默标记为 completed —— 已清偿(2026-09-16,批次 2)

修复内容:

  • processing_service.py _step_generate_cavity 不再吞异常:分模失败直接向编排层传播 → 任务 failed(error_message 说明型腔阶段失败);已提交的几何/网格数据保留,用户可凭失败原因重新分析
  • 未采用 completed_with_fallback:多一个状态值会扩散到前端所有状态分支,failed + 明确错误更诚实且成本低

原现状 / 影响:型腔失败被吞掉继续主流程,最终 completed,"完成"状态不可信。

D9. 持久化事务边界破碎 —— 已清偿(2026-09-16,批次 2)

修复内容(进度可见性与原子性折中设计):

  • 数据本体写方法只 flush 不 commit:save_stp_file / save_geometry_data / save_mesh_data / save_mold_cavity_data / save_html_file / save_features_and_recommendations / update_task_parameters / update_stp_file_analysis_summary / _save_analysis_metrics / _save_verification_metrics
  • 编排层分阶段收口(processing_service.py):阶段 A = 几何+网格(解析后确定成果,原子提交);阶段 B = 型腔+HTML+特征+指标+摘要+验证(结果包原子提交);完成时先 flush 任务参数、完成状态提交时一并落库(completed 即完整)
  • 失败路径先 rollback 再置 failed:未提交半成品回滚,失败状态单独提交,不出现"completed 但数据残缺"
  • 保留即时 commit:update_task_status / update_stp_file_status(处理中进度需跨事务对外可见,分钟级长任务不能憋在一个大事务里)
  • 调用方补显式 commit:upload_router / batch_router(分派前置事务,STPFile + ProcessingTask 原子,消除孤儿文件记录)、advanced_router 导出两处

原现状 / 影响:各存储方法内部自行 commit,型腔保存失败留半成品数据且任务仍 completed。

D10. OCC 全局单线程串行 + 超时重建泄漏线程 —— 已清偿(2026-09-18,方案 B 实施)

方案 B(常驻 OCC 进程池,kill-on-timeout 根治泄漏)已实施(2026-09-18):

  • run_occ(fn, *args) → run_occ(op_name, payload);执行器由进程内线程池替换为常驻工作进程池 occ_process_pool.py + 操作注册表 occ_worker.py(新增;详见 topics/performance/OCC_THROUGHPUT.md §5)
  • 超时/崩溃 = terminate() 换新补位——残留线程泄漏根治(C++ 栈由进程边界回收);OCC segfault 不再波及 API/worker 主进程
  • 调用点全部迁移(解析/网格/型腔/分析/倒扣/STEP 转换),TopoDS 形状不跨进程(generate_cavity 的方案形状 STEP 导出改在子进程内持久化,返回 export_manifest)
  • 顺带删除:内存形状缓存链(_cache_export_shapes / get_export_shapes / _persist_step_exports)、CADExporter.export_mold_results(零调用方)、shape_loader(→ stp_materializer.py)
  • 回归测试:tests/test_occ_process_pool.py(OCC-gated,6 例含真实盒体 STP 端到端)

方案 A 部署参数落地(2026-09-18):CELERY_CONCURRENCY / CELERY_MAX_TASKS_PER_CHILD 进 compose + .env.example(--max-tasks-per-child 仍保留为进程回收兜底;2026-09-24 起 worker 与后端共用 gemold-backend 镜像,Dockerfile.celery 已移除,参数经 compose command: 覆盖与环境变量传递)。

保留为已知约束(非待修缺陷):

  • 单进程内 OCC 串行是正确性要求(OCC 非线程安全),吞吐扩展走多进程(方案 A/B)
  • 每个操作从 STP 原件重新加载形状(STEP 重载成本秒级)——进程隔离的设计取舍,见 OCC_THROUGHPUT §1.2/§5

优先级:P3 已清偿

D11. HTML 报告本地磁盘与 RustFS 双写双读 —— 已清偿(2026-09-18)

修复内容:

  • 写侧:可视化产物不再落节点本地 html_output/——HTMLGenerator 每任务写临时目录(processing_service.py),.html / _summary.json / _data.json 三件统一裸传 RustFS 报告键 html/reports/{filename}(文件名寻址,同源同秒重复分析即覆盖刷新);HTMLFile 表仅存元数据
  • 读侧:/html StaticFiles 挂载删除,新增代理路由 html_report_router.py(挂根路径保持 URL 形状——持久化 cavity JSON 与前端 iframe 均引用 /html/{filename}):RustFS 报告键直取 → 遗留 html/{hash}.json JSON 包装解析 → 本地卷存量兜底 → 404,防路径穿越(单段文件名校验)
  • 部署:celery 服务摘除 html_data 卷(不再写本地);Dockerfile.moldinsight 删除 COPY html_output/(构建机陈旧报告不再烤进镜像)
  • 已知约束(沿用 StaticFiles 时代既定姿态,非新引入):报告路由不做认证——iframe 无法携带 Authorization 头
  • 回归测试:tests/test_html_report_router.py(8 例:四链路命中、新旧格式记录区分、媒体类型、404、穿越拒绝)

原现状 / 影响:可视化 HTML/摘要同时写本地与 RustFS,多副本下 /html 命中结果取决于负载均衡,跨副本文件不共享。

D12. 应用启动时自动执行 alembic 迁移

D12. 应用启动时自动执行 alembic 迁移 —— 已清偿(2026-09-16,批次 1)

修复内容:

  • 新增 AUTO_MIGRATE 开关(settings / .env.example / compose 透传):默认 true 保持单机开发行为;多副本部署设 false,由部署流程单点执行 alembic CLI 或 python -m shared.database.init_db
  • 连带发现并修复两个使自动迁移从未真正生效的缺陷:
    1. 迁移目录 alembic/ 与 alembic 包重名——应用内 import alembic 命中本地目录(namespace package)遮蔽真实包,启动期迁移异常被 init_database 吞掉只打日志;已改名 migrations/(alembic.ini script_location 与 4 处文档引用同步)
    2. 镜像未打包迁移脚本与 alembic.ini,容器内迁移必然失败——Dockerfile.base / Dockerfile.moldinsight 已补 COPY migrations/ + COPY alembic.ini

D12. Pydantic v2 弃用项清理 —— 已清偿(2026-09-22)

修复内容(保留编号以维持引用稳定):

D13. PythonOCC 镜像引入方式脆弱 + 依赖无版本锁(镜像引入已清偿;锁文件流程已固化,待首次构建落盘)

现状:

  • 从 conda env 拷贝 site-packages 进 python:3.12-slim(2026-09-16 已修正:Dockerfile.moldinsight 改为 conda 运行时原生执行,不再跨镜像拷贝;基础镜像 tag 锁定 continuumio/miniconda3:24.7.1-0、python:3.12-slim-bookworm;tag 可用性随下次镜像构建验证)
  • 锁文件流程已固化(2026-09-22):
  • 遗留:锁文件本身尚未落盘——本机 Miniforge 装的是跨项目开发栈混装环境,污染严重不能直接用 pip freeze;须等 CI / 生产机器首次构建 moldinsight 镜像后,按流程跑 bash deploy/generate_lockfiles.sh 落锁并提交。已存在护栏:CI 镜像构建 job 跑 --run-lockfile-check 后若未落盘会 fail-fast,强制流程走通

优先级:P2(流程已固化,剩"首次构建后落盘"一次性产物)

D14. 配置漂移:弱默认 / 死配置 / 重复解析 —— 已清偿(2026-09-16 ~ 09-17,批次 1 / 3)

修复内容:

  • RUSTFS_* 弱默认(批次 0 代码侧去除);compose 侧 SECRET_KEY / ADMIN_PASSWORD 弱默认(批次 1 改 ${VAR:?} 强制显式配置)
  • MAX_FILE_SIZE 死配置(批次 3):upload/batch 路由的 FileHandler 接 settings.UPLOAD_DIR / settings.MAX_FILE_SIZE(此前处理器硬编码 50MB;接线后默认上限变为 100MB,以 .env 为准)
  • celery_app 重复拼装(批次 3):删除自行 load_dotenv + 手拼 REDIS URL,broker/backend 复用 Settings.redis_url(新增 property,连接串唯一拼装点)

优先级:已清偿

D15. 前端 npm run build 因既有 TS 错误失败(批次 3 连带发现)—— 已清偿(2026-09-17,批次 4)

修复内容:

  • vite.config.ts 删除未使用的回调参数 mode(TS6133 源头,一行修复);vue-tsc -b 实测通过,生产构建链路恢复

原现状 / 影响:vue-tsc -b(npm run build 的类型检查步)因既有 TS6133 失败,前端无法出生产包(与批次 3 改动无关的既有问题)。

D17. 算法成熟度距"老师傅经验"差距 + Human-in-Loop 闭环 —— 批 1 已清偿(2026-09-23)

背景:现有算法(分模 / 倒扣 / 评分 / DFM 校验)是 OCC BREP 上的工程启发式,距模具师傅"看完就知道该咋改"的实战经验仍有结构性差距——倒扣邻接聚类缺失、滑块 / 斜顶设计是纯几何启发、DFM 规则库仅 4 条、评分权重拍脑袋(详见 2026-09-22 用户对话评估)。

方案:引入 Human-in-Loop 闭环——老师傅对系统推荐方案给出"采纳 / 调整 / 拒绝"反馈,以"产品指纹 + 工艺参数"为索引跨任务匹配,下次同指纹产品分析自动消费这些经验(OCC worker payload 透传 → MultiSchemeMoldPlanner → PartingSchemeScorer 加成)。老师傅的经验以结构化数据沉淀,避免成为"知识库坟墓"。

批 1 已完成(数据 + 权限 + 写入 API):

  • 新增 experience_feedback 表(alembic head b7d1f4a92c3e���32 表迁移),含 fingerprint JSON 列(PG 下 GIN 索引支持 jsonb_path_query)
  • 3 个权限码(view_experience_feedback / feedback_experience_hint / manage_experience_feedback)+ 新角色 process_engineer;admin 角色 permissions 同步补齐
  • init_db.py 幂等 bug 修复——既有 DB 启动期不再跳过新增权限 / 角色补登(init_permissions / init_roles 改为按 code 比对,新增保留已有 id,避免 FK 引用失效)
  • 新增端点 POST /api/tasks/{task_id}/experience-feedback(提交方案级反馈;TaskQueryService.ensure_task_access 归属校验 + User.has_permission 全仓首次调用)+ GET /api/tasks/{task_id}/experience-hints(按 material_family + is_foam 锚定的历史聚合)
  • D9 边界遵守:service.flush + 路由 commit;D17 衰减机制:写新反馈时同 stp_file_id 整体续期 90 天 TTL(无 celery beat 依赖)
  • 测试基线:185 passed, 9 skipped(批 1 净增 59 测试)

批 2 已完成(算法接缝 + OCC payload 通道)—— 闭环通:

  • parting_candidate_generator.py generate_candidates(..., hints=None):priority_score += weight × 20,sample_count ≥ 2 + weight ≥ 0.5 时 method 标签升级 human_experience_primary
  • parting_scheme_scorer.py score_schemes(..., *, hints=None):新增 score_breakdown["human_hint_bonus"](weight × 12,sample_count < 2 时 ×0.5 折半),纳入 total_score;keyword-only 防与位置参数混淆
  • multi_scheme_planner.py generate_plan(..., hints=None):透传 hints 到下两层,global_summary.applied_hints 注入返回供前端展示
  • processing_service.py _step_generate_cavity:调 experience_feedback_service.resolve_for_process_params 拿同指纹 hints,装进 run_occ payload 顶层 experience_hints;解析失败回退空 list 不阻塞主流程
  • occ_worker.py _op_generate_cavity:payload.get("experience_hints") or {} 透传给 planner.generate_plan,普通 dict 跨进程 pickle 安全
  • 测试基线:192 passed, 13 skipped(批 2 净增 7 通过 + 4 OCC-gated skip)

批 3 已完成(前端按钮 + Dialog + 经验角标)—— 闭环可视:

  • ResultView.vue:35-47 方案卡片 summary-header 加 t-tag 经验角标(currentAxisHint computed 按 scheme_axis 索引 hintsByAxis,无 hints 不渲染)
  • ResultView.vue:131-138 export-buttons-bar 加 👍 老师傅反馈 按钮(v-if="canGiveFeedback" 角色门控:admin 或 process_engineer)
  • HumanFeedbackDialog.vue 新组件:t-dialog + t-form + t-radio-group 三选一 + t-textarea;走 moldinsightApi.submitExperienceFeedback,成功后 emit submitted 让父组件重拉 hints
  • shared/api-client.ts:407-444 moldinsightApi 新增 getExperienceHints / submitExperienceFeedback
  • 接口变更三件套随批完成:openapi.json 重导出(2 新 path)→ npm run gen:api → npm run build 通过

剩余工作(按需排期):

  • 批 4:衰减机制完善(与 DB 一致性定期核查)+ DFM 规则库独立模块化 + 经验冲突仲裁 UI

原现状 / 影响:算法生成的方案与真实工程决策有差距,老师傅每次都要推翻系统建议重来,沉淀经验无结构化路径。


4. 当前推荐治理顺序

注:2026-09-15 后端设计审查后,治理执行顺序以 ROADMAP.md §3.1 批次计划为准(批次 0–4);D5–D14 的批次归属见该表。本节保留原有优先项作为补充说明。

第一优先级

  1. advanced_router 拆分(2026-09-17 批次 3 完成,见 D1)
  2. 高优先级接口补 Pydantic 请求模型(2026-09-17 批次 3 完成)
  3. 文档主骨架收口并减少重复说明

第二优先级

  1. 铝价模拟数据来源显式化
  2. 部署历史文档归档
  3. shared/platform 语义继续收敛(共享 ORM 归属已于批次 4 清偿,剩余为 app_factory 组合职责等,见 D3)
  4. inventory 服务继续下沉(2026-09-21 已完成第一批主数据 CRUD 收口:customer / supplier / warehouse → master_data_service;剩余复杂域如 product / material / dashboard)

5. 治理原则

5.1 先收口接口与边界,再做更大结构调整

当前最值得继续投入的,不是大规模目录重写,而是:

  • 先把接口边界、文档边界、部署边界收清楚
  • 再逐步推进 shared/platform 的后续调整

5.2 优先做“降低长期维护成本”的改动

优先处理:

  • 重复逻辑
  • 模糊边界
  • 静态契约缺失
  • 文档漂移风险

5.3 已解决问题不再长期占据主文档中心

已经完成且稳定的问题,只在本文保留摘要结论;详细实施流水账后续归档,不继续作为主文档主体。


6. 与相关文档的边界