Commit Graph

196 Commits

Author SHA1 Message Date
cjw eb0a4005e1 fix(db): celery 角色改用 NullPool——连接跨事件循环复用致任务全挂
celery_tasks 经 asyncio.run 每任务新建事件循环,而 asyncpg 连接绑定
创建它的循环。db_manager 引擎只连一次(is_connected 短路),QueuePool
把上一循环的连接缓存着流入新循环:任务报
"got Future attached to a different loop" 全部 failed,连接销毁时再报
"Event loop is closed"(ForkPoolWorker 日志连续复现)。

celery_tasks 对 redis 已有同因处理(每任务 reconnect()),DB 侧漏了。
改法:connect(role="celery") 用 NullPool——连接不缓存,每次 checkout
在当前循环新建、用完即关。web 入口(单循环 uvicorn)QueuePool 不变。

已实测:同进程三个连续 asyncio.run 循环共享引擎,全部查询成功。

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-26 23:43:54 +08:00
cjw 73de137779 fix(processing): process_file_core 缺 timeout_seconds 参数——解析必挂 NameError
process_file_core 内 4 处引用 timeout_seconds(run_occ / 网格 / 型腔 /
设计分析的步骤超时),但该变量定义在外层 process_file_with_storage
(按文件大小计算超时处)。拆方法重构时漏传,解析第一步
run_occ("parse_stp") 即 NameError,任务标 failed,页面报
"name 'timeout_seconds' is not defined"。

修复:核心方法显式接收 timeout_seconds 参数(默认 300 兜底),
调用方传入。pyflakes 全仓库 F821 扫描确认无同类未定义名。

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-26 23:27:32 +08:00
cjw aaaac95b53 fix(init_db): await 优先级漏括号——启动迁移自诞生起从未执行成功(事故根因)
has_alembic = await conn.execute(...).scalar()  因 await 优先级低于
属性访问,实际解析为 await (conn.execute(...).scalar())——在协程对象
上调 .scalar() 必抛 AttributeError('coroutine' object has no attribute
'scalar')。启动迁移每次容器启动都在第一行炸掉,再被 except 吞成一行
日志(4a5dc61 已改为 logger.exception)。两行均改为
(await conn.execute(...)).scalar()。

已于生产库实测:主库路径(has_alembic=True→upgrade no-op)与临时
历史库路径(stamp+upgrade)全部跑通,临时库已删除。

至此 2026-09-26 事故根因链闭合:启动迁移从未生效 → prod schema 长期
落后于模型 → 新镜像上线即上传 500(缺 product_id)。

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-26 23:17:33 +08:00
cjw 4a5dc61a02 fix(init_db): 启动迁移失败不再静默——logger.exception 带 traceback + schema 覆盖校验
2026-09-26 事故复盘:AUTO_MIGRATE=true 但启动迁移在 stamp 之前就抛异常,
except 里 logger.error(f"{e}") 丢掉了 traceback,docker logs 只剩一句
"数据库初始化失败",prod 缺 stp_files.product_id 等列导致上传全挂,
无从定位。两个修复:

- 初始化异常改用 logger.exception,完整 traceback 落日志
- 新增启动期 _verify_schema_coverage:模型声明的 32 张表全部列必须真实
  存在于 DB,缺失即 error 点名(只查缺不查多,遗留列不报);迁移后自动
  执行,只读、自身异常不影响启动

已对生产库实跑验证:32 张表全部通过。

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-26 23:11:30 +08:00
cjw f1d6a78f8a fix(auth): 登录 500——asyncpg 拒绝 aware datetime 写入 naive TIMESTAMP 列
批次5(64dc85b)datetime 弃用清零把 utcnow() 换成 aware 的
now(timezone.utc),而 users.last_login 列是 TIMESTAMP WITHOUT TIME
ZONE(sa.DateTime()),asyncpg 编码时抛 DataError → 未捕获 →
Starlette 纯文本 500。症状:密码错误正常 401,密码正确反而 500
(错误密码在 commit 前已 return)。

已在生产库实测确认(事务回滚零写入):aware 写入 REJECTED、
naive ACCEPTED、过去 1 小时 last_login 零记录。

- auth_service: last_login 改存 naive UTC(.replace(tzinfo=None)),
  JWT exp 不受影响仍用 aware
- experience_feedback_service: expires_at 同为 naive 列,提交/查询
  两处 aware now 一并修掉(线上尚无此表,属前瞻性修复)

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-26 22:29:17 +08:00
cjw 505f3591ab D17 批 2:算法接缝(OCC payload 通道 + hints 透传到分模评分)—— 闭环通
让批 1 沉淀的老师傅经验 hints 真接入分模方案生成:
processing_service 拉同指纹 hints 装进 OCC worker payload,
planner 透传到 candidate_generator(axis 优先级加成)和
scheme_scorer(score_breakdown 新字段 + total_score 加成),
写入即消费闭环通。

变更内容:
- src/moldinsight/core/parting_candidate_generator.py
  generate_candidates(..., hints=None):_build_axis_metrics 末尾按 hints
  加成(priority_score += weight × 20 上限;sample_count ≥ 2 + weight ≥ 0.5
  → method 标签升级 "human_experience_primary")
- src/moldinsight/core/parting_scheme_scorer.py
  score_schemes(schemes, *, hints=None) keyword-only:_score_scheme 新增
  human_hint_bonus 字段(weight × 12 上限;sample_count < 2 时 ×0.5 折半
  防信号不足过度影响);_compute_human_hint_bonus 静态方法解析 axis
  (parting.axis → axis → Z);bonus 纳入 total_score
- src/moldinsight/core/multi_scheme_planner.py
  generate_plan(..., hints=None):透传 hints 到 candidate_generator 与
  scheme_scorer;global_summary.applied_hints 注入返回供前端 ResultView
  渲染经验角标
- src/moldinsight/services/processing_service.py
  _step_generate_cavity 加 db_session 形参;调用
  experience_feedback_service.resolve_for_process_params 拿同指纹 hints,
  装进 run_occ payload 顶层 experience_hints;解析失败回退空 list
  不阻塞主流程(旧任务不因 receives 闭包退化)
- src/moldinsight/core/occ_worker.py
  _op_generate_cavity:payload.get("experience_hints") or {} 透传给
  planner.generate_plan(..., hints=...);普通 dict 跨进程 pickle 安全
  (满足 occ_worker.py:7-8 硬规则)
- tests/test_experience_feedback_algorithm.py(new)11 例:
  - candidate_generator 3 例(无 hints 默认 / hints 加成 / sample_count < 2 不升级)
  - scheme_scorer 4 例(无 hints 无 bonus / bonus 加成 / sample_count 折半 /
    weight=0 不加成)
  - multi_scheme_planner 2 例 OCC-gated(透传 / applied_hints 默认空)
  - processing_service 2 例 OCC-gated(payload 含 experience_hints /
    解析失败回退空 list)

设计取舍:
- keyword-only hints:避免与位置参数混淆
- weight 仅正向上有效:max(0, (adopted-rejected)/total),老师傅拒绝
  的不扣分老算法,只让采纳的加分
- signal-noise 控制:sample_count < 2 时 bonus ×0.5,但 priority_score
  仍加成(候选方向仍偏向,避免完全无信号)
- graceful degradation:hints 解析失败回退空 list,主流程继续

- docs/STATUS.md 顶部加 2026-09-23 批 2 日志条目
- docs/TECH_DEBT.md D17 追加批 2 已完成描述 + 缩减剩余工作(仅剩批 3 / 4)

测试基线:192 passed, 13 skipped(净增 7 通过 + 4 OCC-gated skip)。

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-23 16:21:33 +08:00
cjw 2c9ba9d6b3 D17 批 1:Human-in-Loop 老师傅经验反馈(数据 + 权限 + 写入 API)
新增 experience_feedback 表(32 表迁移,alembic head b7d1f4a92c3e),
老师傅对系统推荐方案给出"采纳 / 调整 / 拒绝"反馈,按"产品指纹 +
工艺参数"为键跨任务匹配,下次同指纹产品分析自动消费。

变更内容:
- src/moldinsight/models/experience_feedback.py(new)ORM:Base 单点来源、
  跨模块裸 FK(user_id / processing_task_id / stp_file_id)、不建 ORM
  relationship;fingerprint JSON 列存跨任务匹配键
- src/moldinsight/models/__init__.py 导出 ExperienceFeedback
- migrations/versions/b7d1f4a92c3e_add_experience_feedback.py(new)32 表
  迁移;fingerprint 列 PG 下加 GIN 索引(jsonb_path_query 支持)
- src/shared/database/init_db.py 加 3 个权限码(view_experience_feedback /
  feedback_experience_hint / manage_experience_feedback)+ 新角色
  process_engineer;admin 角色 permissions 同步补齐;init_permissions /
  init_roles 改为按 code 比对(新增保留已有 id,避免 FK 引用失效)——
  修复既有 DB 启动期漏掉新权限的幂等 bug
- src/moldinsight/services/experience_feedback_service.py(new)service:
  compute_fingerprint 分桶(bbox_aspect / volume_bucket / face_bucket /
  undercut_class / material_family / is_foam)/ record_feedback(D9 边界:
  service.flush + 路由 commit;D17 衰减:同 stp_file_id 整体续期 90 天 TTL,
  无 celery beat 依赖)/ list_hints_for_task / resolve_for_process_params
- src/moldinsight/api/experience_feedback_router.py(new)路由:Pydantic
  模型写在路由文件内(项目硬规则);POST /api/tasks/{task_id}/experience-feedback
  + GET /api/tasks/{task_id}/experience-hints;归属 TaskQueryService.ensure_task_access
  + User.has_permission 全仓首次调用点
- src/moldinsight/api/__init__.py ROUTE_MODULES 注册新路由
- tests/test_model_ownership.py EXPECTED_TABLES 加 experience_feedback
  (31→32)
- tests/test_experience_feedback_fingerprint.py(new)分桶参数化覆盖
  bbox / volume / face / undercut / material / is_foam 各边界值
- tests/test_experience_feedback_router.py(new)API 契约 9 例
  (401/403/422/200 路径 + 衰减续期 + 任务归属校验 + ORM 注册收口)
- docs/STATUS.md 顶部加 2026-09-23 批 1 日志条目
- docs/TECH_DEBT.md D17 加批 1 已完成描述 + 剩余工作清单
- docs/API_CONTRACT.md §3.2 加 D17 端点表格

测试基线:185 passed, 9 skipped(净增 59 测试)。

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-23 16:11:17 +08:00
cjw 64dc85bd14 批次5:inventory服务下沉收口 + Pydantic v2 / datetime弃用清零
- inventory业务层下沉(薄路由+service orchestration模式):
  - customer/supplier/warehouse -> master_data_service
  - material_routes(价格历史/趋势/供应商关联)-> material_service
  - product_routes(CRUD/BOM/from-task跨模块桥接)-> product_service
  - dashboard_routes(首页统计/低库存预警)-> dashboard_service
- inventory侧新增service回归覆盖(dashboard 2 / master_data 10 /
  material 10 / product 14),含跨模块桥接测试种子
- Pydantic v2弃用清零:全仓14处 class Config 全部迁移到
  model_config = ConfigDict(from_attributes=True)(含 shared auth)
- datetime.utcnow() 弃用清零:auth_service 3处统一改 datetime.now(timezone.utc)
- 同步文档:STATUS / ROADMAP / TECH_DEBT(D12清偿)/ AGENTS 代码地图

测试基线:126 passed, 4 skipped(无deprecation warning)

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-22 16:13:45 +08:00
cjw c51e6b793a x 2026-09-18 18:21:01 +08:00
cjw e728dcd226 批次4后续专项完成:D11清偿 + OCC方案B实施 + 部署参数 + D2诚实标注 + CI门禁
① D11 HTML 报告 RustFS 单源化(TECH_DEBT P2 清偿):可视化产物写任务临时目录后
   裸传报告键 html/reports/{filename}(文件名寻址),/html StaticFiles 挂载删除,
   新增 html_report_router 根路径代理(报告键→遗留 JSON 包装→本地卷兜底→404,
   防穿越);URL 形状 /html/{filename} 不变,持久化引用零迁移;celery 摘除
   html_data 卷,镜像不再烤入陈旧报告;顺带删除 get_stp_file_with_data 死数据块
② OCC 方案 B(D10 清偿):run_occ(op_name, payload) 契约 + 常驻工作进程池
   (occ_process_pool + occ_worker 操作注册表),超时/崩溃 terminate 换新补位、
   任务级超时 recover 整体重建,残留线程泄漏根治;TopoDS 不跨进程(generate_cavity
   分模 + 方案 STEP 持久化全在子进程内,返回 export_manifest);删除内存形状缓存链、
   CADExporter.export_mold_results、shape_loader(→ stp_materializer)
③ OCC 方案 A 部署参数:CELERY_CONCURRENCY / CELERY_MAX_TASKS_PER_CHILD 进
   Dockerfile.celery + compose + .env.example
④ D2 诚实标注:铝价响应带 source: "simulated",前端按来源渲染标注(原硬编码
   "上海期货交易所"属虚假声明),死代码 getAluminumPrice 删除
⑤ CI 门禁:.gitea/workflows/ci.yml 三 job(pytest / 前端构建含 vue-tsc /
   openapi 漂移检测)

接口变更三件套随批完成(openapi 76→77 paths + gen:api + 前端构建通过;方案 B
接口面零变化)。测试基线 143 passed, 0 skipped(新增 16 项)。文档六处同步。

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-18 17:22:01 +08:00
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
cjw 4537faf2c4 优化 2026-09-16 17:55:04 +08:00
cjw 5e531ffe1e 文档目录结构简洁化 2026-09-01 18:05:18 +08:00
cjw bee439cf34 x 2026-08-31 18:01:34 +08:00
cjw a330757946 xx 2026-07-30 11:01:00 +08:00
cjw 853c478657 xxx 2026-07-30 10:30:50 +08:00
cjw cf6d708566 xxx 2026-07-27 15:54:25 +08:00
cjw 00ca81287e feat(db): 引入 Alembic 取代裸 DDL 迁移
- alembic init + env.py 接 settings/models metadata(asyncpg->psycopg2 同步 URL)
- 补 3 个 CheckConstraint 到 models(原只在裸 DDL)
- 离线生成初始迁移(31 表+约束+95 索引,全 sa.* 通用类型)
- init_db 用 _run_alembic_migrations(自动基线+upgrade head)替换 create_tables+ensure_schema_updates(删 92 行裸 DDL)
- 删破坏性 migrate_db.py(drop_all)
- 既有 DB 首次启动自动 stamp 基线,无需手动

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-20 11:04:23 +08:00
cjw 66ba6e00f5 refactor(moldinsight): 引入可插拔注册表(MoldGenerator + FeatureDetector)
- 新增 MoldGeneratorRegistry:multi_scheme_planner 消除 if-else,按 mold_type 选生成器
- 新增 FeatureDetectorRegistry:geometry_analyzer._detect_features 消除 6 个检测器硬编码
- 新增模具类型/特征检测器只需 register 一行
- 移除 OCC ThreadPoolExecutor(注册表本身串行执行)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-20 11:03:16 +08:00
cjw f3d2951dc3 x 2026-07-20 10:16:27 +08:00
cjw 8493f0df03 init 2026-07-13 17:44:50 +08:00
cjw 975fa1ffac init 2026-07-13 17:44:37 +08:00
cjw 6b3188e693 x 2026-06-23 09:38:31 +08:00
cjw f6d8829071 x 2026-06-16 11:15:03 +08:00
cjw f71a48d3b8 x 2026-06-12 13:55:38 +08:00
cjw fdbde938f9 x 2026-06-09 16:19:15 +08:00
cjw 4151e4963b x 2026-06-09 15:44:17 +08:00
cjw fa2178fbd8 x 2026-06-09 13:44:53 +08:00
cjw 8cca6f9d32 x 2026-06-02 09:55:06 +08:00
cjw b22431d3cb x 2026-06-01 10:44:08 +08:00
cjw 77bdf56adf 重写独立dockerfile 2026-05-29 18:19:30 +08:00
cjw 823a387118 后端模块拆分 2026-05-29 18:10:08 +08:00
cjw 5bb9bc84ea 前端重构 Vite 2026-05-29 17:30:26 +08:00
cjw 3443763fbf x 2026-05-29 09:47:21 +08:00
cjw 44902207c9 增加celery任务队列,任务持久化(服务重启不丢)、支持多 Worker 水平扩展、失败自动重试 2026-05-28 17:59:39 +08:00
cjw 6ad22e4466 优化整体任务性能 2026-05-28 17:41:02 +08:00
cjw 2ffe8ecbb3 x 2026-05-27 16:15:01 +08:00
cjw 9882d3becc 导出step文件合并为整体,方便后续修改 2026-05-27 14:57:03 +08:00
cjw 26613676f8 x 2026-05-25 18:35:33 +08:00
cjw 63bee26ab8 x 2026-05-25 10:16:05 +08:00
cjw 08d5a45c80 x 2026-05-18 16:45:17 +08:00
cjw 224330598a x 2026-05-14 16:56:18 +08:00
cjw fd1f43f278 x 2026-05-12 10:23:58 +08:00
cjw 74bbca90e3 x 2026-05-11 18:05:27 +08:00
cjw c9672d9685 fix: 恢复 init_db.py 等全部缺失文件 + LLM 集成 2026-05-11 15:35:54 +08:00
cjw 4667346669 x 2026-05-11 14:46:20 +08:00
cjw eaf37b9a03 x 2026-05-11 14:15:39 +08:00
cjw a68b9ca57f x 2026-05-11 11:35:15 +08:00
cjw 5e1d475a22 x 2026-05-11 09:34:50 +08:00
cjw 5d2ca3f782 x 2026-05-07 16:51:21 +08:00