Files
geMoldInsight/docs/TECH_DEBT.md
T
cjw 0e6b3b1811 后端设计治理:批次 0-4 全部完成(安全/部署/一致性/结构/架构)
按 ROADMAP §3.1 治理批次推进的后端设计审查整改:

- 批次 0(安全):/api/status/{task_id} 补 JWT 鉴权与任务归属校验;
  pythonocc_available 真实探测;bcrypt 超 72 字节显式拒绝;
  SECRET_KEY/RUSTFS_* 惰性校验,代码侧弱默认移除
- 批次 1(部署正确性):主处理链路改走 RustFS(分派入参 stp_file_id 化,
  worker 按 object_key 下载);AUTO_MIGRATE 开关 + 迁移目录 alembic/→migrations/
  修复包遮蔽(自动迁移此前从未真正生效);OCC 镜像改 conda 原生执行 +
  基础镜像 tag 锁定;compose 关键项改 ${VAR:?} 强制显式配置
- 批次 2(任务一致性):删除 Redis 进程内存回退,PG 为任务状态单一事实源;
  批量元数据入库(processing_tasks.batch_id,迁移 a3f8c2d91e47);
  型腔失败任务标 failed 不再静默 completed;事务边界收口
  (数据本体写 flush-only、失败先回滚再置 failed、进度更新保留即时 commit)
- 批次 3(API 与代码结构):592 行 advanced_router 拆为 design/cost/machining/
  export 四子路由,请求体全量 Pydantic 化;ROUTE_MODULES + route_registry
  (/api/health 呈现 degraded,DEBUG fail fast);纯计算端点统一 to_thread;
  StorageIntegrationService 按职责三拆;MAX_FILE_SIZE 接线生效、
  celery 复用 Settings.redis_url;管理员重置密码改 JSON body(端到端断裂修复);
  openapi.json 重导出(76 paths)+ 前端 gen:api
- 批次 4(架构演进):共享 ORM 按模块拆分(shared/models/base.py + identity.py、
  moldinsight/models/、inventory/models/,删除三条无使用方的跨模块
  relationship,跨模块桥接收敛为裸 FK 硬规则,无兼容 facade);
  OCC executor 重建补 cancel_futures=True(消除旧队列被慢恢复线程
  并行消化的数据竞争);OCC 吞吐方案设计先行
  (docs/topics/performance/OCC_THROUGHPUT.md);顺手清偿 D15
  (vite.config.ts 未用参数致 npm run build 失败)

测试基线:125 passed, 2 skipped(pytest + sqlite+aiosqlite;归属边界、
路由契约、配置治理、鉴权回归等随批新增)
文档同步:STATUS / TECH_DEBT / ROADMAP / ARCHITECTURE / API_CONTRACT /
OPERATIONS / AGENTS

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-17 16:15:49 +08:00

19 KiB
Raw Blame History

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

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


1. 当前技术债概览

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

  • moldinsight API 与处理链路的结构收口
  • 文档 / 部署 / 历史语义与当前代码现状未完全一致

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


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

以下高价值治理已完成:

2.1 安全与权限

  • debug/history 路由补鉴权
  • 任务访问控制收紧
  • 无主数据不再默认放行
  • /api/status/{task_id} 补 JWT 鉴权与归属校验(原 D5,2026-09-16 清偿,见 D5 条目)
  • bcrypt 创建口令超 72 字节显式拒绝、验证侧截断比较;SECRET_KEY / RUSTFS_* 缺失时明确报错,代码侧弱默认移除(D14 部分,2026-09-16)

2.2 静默失败与可用性

  • detect-undercuts 改为基于真实 shape 分析
  • OCC 超时后重建 executor,避免全队列永久堵死
  • 后台任务统一分派,补强引用与并发控制

2.3 状态存储与缓存

  • Redis 任务状态改为 Hash 字段级更新,兼容旧格式
  • 完成态任务视图增加缓存
  • 导出缓存与持久化链路收口,支持重启后再导出

2.4 架构与代码清理

  • 删除旧单体入口与死代码
  • 设置惰性配置校验,提升可测试性
  • Generator 公共接口提取完成,补充契约测试

2.5 部署正确性(2026-09-16,批次 0/1)

  • /api/status/{task_id} 补鉴权与归属校验(原 D5)
  • 主处理链路改走 RustFS:分派入参 stp_file_id 化,源文件按 object_key 下载;compose 共享卷过渡兜底(原 D6)
  • AUTO_MIGRATE 开关 + 迁移脚本随镜像分发 + alembic/→migrations/ 改名修复包遮蔽(原 D12)
  • OCC 镜像改 conda 运行时原生执行、基础镜像 tag 锁定(D13 主体);compose 关键项去弱默认(D14 部分)

2.6 任务一致性模型(2026-09-16,批次 2)

  • Redis 内存回退彻底删除,PG 为任务状态单一事实源(原 D7);批量元数据入库(processing_tasks.batch_id,迁移 a3f8c2d91e47)
  • 型腔生成失败任务标 failed,不再静默 completed(原 D8)
  • 持久化事务边界收口:数据本体分阶段原子提交、失败先回滚再置 failed(原 D9)
  • D11(HTML 双写双读)本批未动:正确性已由共享卷兜底,RustFS 单一来源留待后续批次

2.7 API 与代码结构(2026-09-17,批次 3)

  • advanced_router 按职责拆为 design / cost / machining / export 四个子路由,端点路径不变,请求体全量 Pydantic 化(原 D1)
  • 路由装载失败显式化:ROUTE_MODULES 清单 + route_registry,失败经 /api/health 呈现 degraded(含真实 pythonocc 探测),DEBUG 下 fail fast
  • 纯 Python 重计算端点(设计/加工/CAM 打包)统一 asyncio.to_thread 投放线程池,不再阻塞事件循环;OCC 操作仍走单线程 executor(D10 不变,批次 4)
  • StorageIntegrationService(867 行)按职责拆为 TaskStorage / AnalysisStorage / FileHistory 三服务;无调用方的 log_user_activity 死代码删除
  • 配置治理收尾:MAX_FILE_SIZE 接线生效、celery_app 复用 Settings.redis_url(原 D14)
  • 连带修复:管理员重置密码改 JSON body(原裸 str 参数被解析为 query param,前端发 body 必 422,功能端到端断裂);Dockerfile.celery 的 FROM tag 与 compose/build.sh 实际构建的 gemold-backend:latest 对齐(此前干净环境 celery 镜像必构建失败)
  • 接口变更三件套随批完成:openapi.json 重导出(76 paths)+ 前端 gen:api

2.8 架构演进(2026-09-17,批次 4)

  • 共享 ORM 按模块拆分(原 D3 主体):891 行 shared/models/database.py(31 模型类三类同居)拆为 shared/models/base.py(唯一 Base + 归属约定)/ shared/models/identity.py(7 表)/ moldinsight/models/(9 表)/ inventory/models/(catalog/warehouse/trading/finance 15 表);三条跨模块 ORM relationship(User.stp_files、STPFile.user、STPFile.product)经全仓核实均无使用方,直接删除——跨模块桥接收敛为裸 FK 硬规则(ARCHITECTURE §5.1),单模块部署不再依赖另一侧模型注册;约 45 处 import 全量改写,无兼容 facade;全量注册点收敛为 migrations/env.py 与 tests/conftest.py;零调用方的死方法 db_manager.create_tables 一并删除(拆分后会静默建残缺 schema)
  • OCC 泄漏治理 + 吞吐方案设计先行(原 D10):_reset_occ_executor 补 cancel_futures=True——不止卫生问题:旧实现下"慢恢复"的旧线程会继续消化旧队列,与新 executor 并发操作非线程安全的 OCC(数据竞争);吞吐路线定稿于 topics/performance/OCC_THROUGHPUT.md(短期 A:celery prefork 伸缩 + max-tasks-per-child 兜底;中期 B:run_occ 接口进程化 + kill-on-timeout 根治)
  • 归属边界回归测试:tests/test_model_ownership.py(31 表全量注册、单模块独立 mapper 配置、旧模块无 facade)

详细历史过程保留在原始技术债文档中,后续将转入归档。


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. 铝价模拟数据未显式标注来源

现状:

  • 铝价服务返回的是模拟/参考数据,但接口层未明确表达

影响:

  • 容易误导前端与业务使用者,把模拟数据理解为实时行情

建议:

  • 响应增加 source: "simulated"
  • 前端界面同步标注“模拟/参考数据”

优先级:P2

D3. shared/platform 边界仍需继续收敛 —— ORM 归属已清偿(2026-09-17,批次 4)

已完成部分:

  • 共享 ORM(原最强耦合点)按模块拆分:base / identity(shared)+ moldinsight/models + inventory/models;跨模块只允许裸 FK,单模块部署 mapper 可独立配置(详见 §2.8 与 ARCHITECTURE.md §6.1)
  • 旧 shared/models/database.py 物理删除,无兼容 facade;归属边界由 tests/test_model_ownership.py 锁定

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

  • app_factory.py 组合职责偏重(ARCHITECTURE §6.2)
  • identity / platform 的边界语义(ROADMAP §2.1)

优先级:P3(剩余部分)

D4. 文档现状 / 规划 / 历史混放

现状:

  • 文档存在部署说明重叠、计划/总结/权威文档混放
  • README 承担过多职责

影响:

  • 新成员难以判断“哪篇才是当前有效说法”
  • 状态、部署、规划容易发生漂移

建议:

  • 建立 STATUS / ARCHITECTURE / ROADMAP / DEPLOYMENT 主骨架
  • 历史材料迁入 docs/archive/

优先级:P1

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-17,批次 4)

已完成:

  • _reset_occ_executor 补 cancel_futures=True:排队任务随重建丢弃——旧实现下"慢恢复"的旧线程会继续消化旧队列,与新 executor 并发操作非线程安全的 OCC;修复后残留收敛为"运行中线程滞留 1 个"(C++ 栈 Python 层不可杀,属客观边界)
  • 吞吐与隔离方案定稿:topics/performance/OCC_THROUGHPUT.md——短期方案 A(celery --concurrency 伸缩 + --max-tasks-per-child 进程回收兜底,零新代码,启动参数见 OPERATIONS.md §3);中期方案 B(run_occ 改操作名+payload 契约、常驻 OCC 进程池 kill-on-timeout 根治泄漏,待独立批次)

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

  • 单进程内 OCC 串行是正确性要求(OCC 非线程安全),吞吐扩展走多进程(方案 A/B)
  • 线程级超时的滞留线程由进程边界回收,根治依赖方案 B 落地

优先级:P3(中期方案 B 实施前维持观察)

D11. HTML 报告本地磁盘与 RustFS 双写双读

现状:

  • 可视化 HTML/摘要同时写本地 html_output/(/html 静态挂载)与 RustFS

影响:

  • 多副本下 /html 命中结果取决于负载均衡,跨副本文件不共享;同一份报告两套来源

建议:

  • 统一 RustFS 为唯一来源,本地仅作按需缓存

优先级:P2

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

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 可用性随下次镜像构建验证)
  • requirements.txt 全部为 >= 下限,无锁文件(遗留:首次镜像构建成功后 pip freeze 生成锁文件,命令已注释在 Dockerfile 内)

优先级: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 改动无关的既有问题)。


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)

5. 治理原则

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

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

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

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

优先处理:

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

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

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


6. 与相关文档的边界