From 8493f0df030fcfaa81934375ec1689ccea7014f9 Mon Sep 17 00:00:00 2001 From: chenjw28 <792430652@qq.com> Date: Mon, 13 Jul 2026 17:44:50 +0800 Subject: [PATCH] init --- deploy/docker-compose.yml | 2 + docs/EVOLUTION_ROADMAP.md | 162 ++++ .../inventory/components/DashboardTab.vue | 2 +- .../inventory/components/FinanceTab.vue | 46 +- .../components/PurchaseOrdersTab.vue | 4 +- src/celery_tasks.py | 26 +- src/inventory/api/customer_routes.py | 2 +- src/inventory/api/finance_routes.py | 664 +--------------- src/inventory/api/inventory_routes.py | 2 +- src/inventory/api/material_routes.py | 2 +- src/inventory/api/product_routes.py | 2 +- src/inventory/api/purchase_order_routes.py | 4 +- src/inventory/api/sales_order_routes.py | 4 +- src/inventory/api/stock_movement_routes.py | 4 +- src/inventory/api/supplier_routes.py | 2 +- src/inventory/api/warehouse_routes.py | 2 +- src/inventory/schemas/finance_schemas.py | 1 + src/inventory/services/__init__.py | 5 + src/inventory/services/finance_service.py | 742 ++++++++++++++++++ src/inventory/utils.py | 21 + src/moldinsight/api/__init__.py | 1 + src/moldinsight/core/geometry_analyzer.py | 2 +- src/moldinsight/services/llm_service.py | 2 +- src/shared/services/auth_routes.py | 68 +- src/shared/services/redis_task_manager.py | 12 + 25 files changed, 1050 insertions(+), 734 deletions(-) create mode 100644 docs/EVOLUTION_ROADMAP.md create mode 100644 src/inventory/services/__init__.py create mode 100644 src/inventory/services/finance_service.py create mode 100644 src/inventory/utils.py diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index f27791d..53de53c 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -106,9 +106,11 @@ services: DB_PASSWORD: ${DB_PASSWORD:-moldinsight_password} REDIS_HOST: redis REDIS_PORT: "6379" + REDIS_PASSWORD: ${REDIS_PASSWORD:-} RUSTFS_ENDPOINT: http://minio:9000 RUSTFS_ACCESS_KEY: ${MINIO_ACCESS_KEY:-minioadmin} RUSTFS_SECRET_KEY: ${MINIO_SECRET_KEY:-minioadmin} + RUSTFS_TIMEOUT: ${RUSTFS_TIMEOUT:-30} depends_on: - moldinsight restart: unless-stopped diff --git a/docs/EVOLUTION_ROADMAP.md b/docs/EVOLUTION_ROADMAP.md new file mode 100644 index 0000000..07dfe66 --- /dev/null +++ b/docs/EVOLUTION_ROADMAP.md @@ -0,0 +1,162 @@ +# geMoldInsight 演进路线图 + +> 本文档是代码与功能演进的执行清单,基于 2026-07-13 的全量代码体检。每项含「现象 / 证据 / 修法 / 验证」,按 P0→P3 推进,完成后勾选。 + +## 诊断 + +代码已演进到「双应用模块化」形态(`entrypoints/` + `moldinsight/` + `inventory/` + `shared/`),但有 **三处结构性缺失** 让快速迭代变贵,外加 **一批静默 bug** 正在让功能"看起来在跑其实没跑": + +- **缺中间层**:业务逻辑堆在路由/编排函数里(进销存无 service 层、`process_file_core` 280 行线性函数) +- **缺契约**:前后端靠手写类型,字段已大面积漂移(财务页整页是 0) +- **缺连接**:模具分析与进销存是两个孤立产品(`STPFile` 没有 `product_id`) + +--- + +## P0 止血:正在静默失效的功能(1–2 周) + +这些不是技术债,是**现在就在坏**的东西,先修。 + +### P0-1 Celery worker 不连 Redis/RustFS,异步任务全坏 +- **现象**:任务进度写进 Celery 私有内存,web 端永远读不到;首次上传 RustFS 直接抛 `RuntimeError("RustFS 未连接")`。 +- **证据**:`redis_task_manager.connect()` / `rustfs_manager.connect()` 只在 FastAPI startup 调用(`entrypoints/moldinsight.py:42,48`),Celery 进程不跑 startup;`processing_service.py` 在 celery 内调 `update_task` 时 `is_connected=False` 走 `_fallback_set`;`rustfs_storage.py:125-126` 未连接直接抛错。`deploy/docker-compose.yml` 的 `moldinsight-celery` 服务块缺 `REDIS_PASSWORD`。 +- **修法**:`celery_tasks.py` 加 `@worker_process_init` 信号,显式 `connect()` redis 与 rustfs;补齐 celery 服务的 `REDIS_PASSWORD`/`SECRET_KEY` 等环境变量,与主应用对齐。 +- **验证**:上传一个 STP,Celery 路径下任务进度能从 web 端 `/api/status/{task_id}` 读到;上传后 RustFS 中能看到对象。 +- **状态**:- [ ] + +### P0-2 LLM 设计报告 NameError,静默失效 +- **现象**:`LLM_ENABLED=true` 时设计报告功能直接没有。 +- **证据**:`llm_service.py:339` 用未定义变量 `trimmed`(应为 `features`,`trimmed` 只在 `_build_side_action_prompt` 中定义),外层 `try/except` 吞掉 `NameError` 返回 `None`。 +- **修法**:`trimmed` -> `features`。 +- **验证**:启用 LLM 后设计报告字段非空。 +- **状态**:- [ ] + +### P0-3 前端财务页全字段错配 +- **现象**:FinanceTab 整页 0/空;用户管理菜单永不显示(`is_superuser` 后端不返回);dashboard 成品数恒 0。 +- **证据**:16 处字段名对不上,如 `total_receivable` vs `receivable_total`(`finance_schemas.py:66`)、`order_no` vs `txn_no`(`finance_schemas.py:48`)等;`App.vue:91` 读 `is_superuser` 但 `UserResponse` 无此字段。 +- **修法**:短期按映射手改前端字段;长期靠 P1-3 OpenAPI 契约生成根治。 +- **验证**:财务页卡片与表格显示真实数据;用户管理菜单对管理员可见。 +- **状态**:- [ ] + +### P0-4 OCC 线程安全自相矛盾 +- **现象**:偶发崩溃,外层 `max_workers=1` 保护形同虚设。 +- **证据**:`processing_service.py:50-51` 用单线程池序列化 OCC,但 `geometry_analyzer._detect_features`(`geometry_analyzer.py:81`)内部又开 `ThreadPoolExecutor(max_workers=4)` 并行操作 OCC `TopoDS_Shape`。 +- **修法**:特征检测器改串行;或预处理阶段把面特征抽成纯数值,检测器只处理数值不碰 OCC。 +- **验证**:压测大模型反复分析无崩溃。 +- **状态**:- [ ] + +### P0-5 `.env` 进了 git 历史,真实密钥泄露 +- **现象**:DB/Redis/SECRET_KEY/LLM key 已进入仓库历史。 +- **证据**:`git ls-files --error-unmatch .env` 命中;`git log -- .env` 有 10+ 次提交;`.env` 内含真实凭据。 +- **修法**:`git rm --cached .env`(停止跟踪,保留本地,后续不再提交);`SECRET_KEY` 从默认占位符轮换为强随机值。 +- **用户决策(2026-07-13)**:私有仓库,不轮换其他密钥、不重写 git 历史。 +- **验证**:`git status` 显示 `.env` 不再被跟踪(`D .env`)。 +- **状态**:- [x] + +### P0-6 铝价路由模块化部署后丢失 +- **现象**:模块化部署后 `/api/aluminum-price/*` 直接 404。 +- **证据**:单体 `main.py:47,155` 挂了 `aluminum_price_router`,但 `moldinsight/api/__init__.py` 的 `_safe_include` 列表不含 `aluminum_price_routes`。 +- **修法**:把 `aluminum_price_routes` 加入 `_safe_include`。 +- **验证**:模块化部署下 `/api/aluminum-price/*` 可访问。 +- **状态**:- [ ] + +### P0-7 导出缓存无持久化回退 +- **现象**:多 worker 或重启后导出返回 409。 +- **证据**:`processing_service._export_shapes_cache` 是进程内 dict,`get_export_shapes` 只查内存;`_persist_step_exports` 已写磁盘 manifest 但无回读逻辑。 +- **修法**:`get_export_shapes` 缓存未命中时从磁盘 manifest 回读。 +- **验证**:重启后导出仍可用。 +- **状态**:- [ ] + +--- + +### P0 执行结果(2026-07-13) + +- ✅ **P0-1 Celery 连接**:`celery_tasks.py` 在任务内显式 `redis_task_manager.reconnect()` + `rustfs_manager.connect()`(Redis 客户端绑定事件循环,每任务 reconnect;RustFS 同步客户端连一次复用);`deploy/docker-compose.yml` celery 服务补 `REDIS_PASSWORD`/`RUSTFS_TIMEOUT` +- ✅ **P0-2 LLM NameError**:`llm_service.py:339` `trimmed` -> `features` +- ✅ **P0-3 前端字段错配**:FinanceTab 全字段对齐 schema(summary/statement/product-statement/transaction 共 16 处);`UserResponse` 加 `is_superuser` + 统一 `_build_user_response` 构造(修用户管理菜单不显示);DashboardTab `product_count`->`finished_product_count`;PurchaseOrdersTab `received_at/paid_at`->`received_date/paid_date`;后端 `FinanceTransactionResponse` 补 `partner_name` 并批量查询客户/供应商名称 +- ✅ **P0-4 OCC 线程安全**:`geometry_analyzer._detect_features` `max_workers` 4->1 +- ✅ **P0-5 .env 泄露**:`git rm --cached .env` 已取消跟踪(后续不再提交);`SECRET_KEY` 从默认占位符轮换为强随机值(现有登录 token 失效)。用户决策:私有仓库,不轮换其他密钥、不重写 git 历史 +- ✅ **P0-6 铝价路由**:`moldinsight/api/__init__.py` `_safe_include` 加入 `aluminum_price_routes` +- ℹ️ **P0-7 导出缓存**:经排查**非 bug**——`export_artifacts` 已写 PG+Redis(`processing_service.py:342,361`),导出端点先走 `_select_persisted_files` 从 task_data 读取(`advanced_router.py:436`),重启后正常工作;409 仅在持久化也失败时出现,"请重新分析"提示为正确行为。内存 re-export 缓存的可靠性优化归入 P1-2 + +**未做验证**:前端未跑 vue-tsc 构建(字段重命名属机械改动,低风险);后端未跑 pytest(需 DB/Redis 环境)。建议下次在完整环境验证。 + +--- + +## P1 结构性地基:让后续迭代不再昂贵(持续) + +### P1-1 进销存抽 service 层 +- **现状**:`finance_routes.py` 751 行、`sales_order_routes.py` 777 行,事务编排/库存原子更新/流水写入全耦合在 endpoint;`shared/services/` 仅 auth+redis。 +- **目标**:新建 `inventory/services/`,`PurchaseOrderService.receive()`、`SalesOrderService.issue_materials()`、`FinanceService.settle()`,route 只做校验+组装。 +- **状态**:- [ ] + +### P1-2 moldinsight 可插拔注册表 + Stage 流水线 +- **现状**:模具类型硬编码 if-else(`multi_scheme_planner.py:40`);特征检测器硬编码 6 个(`geometry_analyzer.py:76-99`);`process_file_core` 280 行。 +- **目标**:`FeatureDetectorRegistry` + `MoldGeneratorRegistry`(`@register` 装饰器);`process_file_core` 拆成 Stage 链。 +- **解锁**:新增模具类型、IGES/BREP、批量分析。 +- **状态**:- [ ] + +### P1-3 前端 OpenAPI 契约生成 +- **现状**:前端 40+ 处 `any`,字段全手写已大面积错配。 +- **目标**:`openapi-typescript` 从 `/openapi.json` 生成 TS 类型替换 `any`;`api.ts` 加 baseURL/拦截器/超时,按域封装 `inventoryApi`/`moldinsightApi`/`authApi`。 +- **状态**:- [ ] + +### P1-4 引入 Alembic,废除裸 DDL +- **现状**:无 `alembic.ini`;`init_db.py` 22 条 `ALTER TABLE ADD COLUMN IF NOT EXISTS`,无版本/无回滚;`migrate_db.py` 是 `drop_all` 破坏性脚本;两应用 startup 并发跑 DDL 争锁。 +- **目标**:`alembic init`,固化版本化迁移,启动只 `upgrade head`;删 `migrate_db.py`。 +- **状态**:- [ ] + +### P1-5 统一材料属性源 +- **现状**:材料字典在 4 处重复定义且冲突(PE 收缩率 `material_service` 0.020 vs `aluminum_foam_mold.py:92` 0.025)。 +- **目标**:`MaterialService` 作为唯一源,其他模块查询。 +- **状态**:- [ ] + +--- + +### P1 执行结果(进行中) + +- 🚧 **P1-1 进销存抽 service 层**(进行中): + - ✅ 建立 `inventory/services/` 层,抽出 `FinanceService`(`finance_routes` 767→123 行,业务逻辑全下沉;路由仅做参数校验+响应组装) + - ✅ 将 `schemas/` 与 `utils.py` 从 `inventory/api/` 移至 `inventory/` 顶层,打破 service↔api 循环导入(schemas 是共享 DTO、utils 是纯函数,本不应嵌在 api 层;这是正确分层) + - ✅ import 测试通过:9 finance 路由 + 55 inventory 路由全部正常加载 + - ⏳ 待办:抽 `sales_order_service` / `purchase_order_service` / `inventory`+`stock_movement` service;清理死代码 `api/utils.py`(待 `git rm`) + +--- + +## P2 功能演进:把两个产品变成一个 + +### P2-1 打通模具分析 -> 进销存(最高产品价值) +- **现状**:`STPFile` 无 `product_id`,moldinsight 与 inventory 零数据关联。 +- **目标**:`STPFile` 加 `product_id` 外键(可空),分析完成后一键创建 `Product(finished)` 并回写。 +- **状态**:- [ ] + +### P2-2 真 AI 落地,砍掉假 AI +- **现状**:`ai_mold_assistant.py` 209 行纯 stub 从未被调用;`ai_parting_detector.py` GNN 框架完整但无权重;`llm_service` 是唯一真接 AI(且有 P0-2 bug)。 +- **目标**:聚焦一个能跑通的 AI 能力(LLM 扩到成本估算/工艺对话);GNN 要么真训权重,要么移除 stub。 +- **状态**:- [ ] + +### P2-3 模具成本估算 + 批量分析 +- 依赖 P1-2 完成后才有性价比。 +- **状态**:- [ ] + +--- + +## P3 工程治理(穿插顺手做) + +- [ ] `create_app()` 工厂消除两入口重复引导,废弃单体 `main.py` +- [ ] 删死依赖/死代码:Kafka(零引用)、`templates/` legacy、`ai_mold_assistant` stub、`ProcessingService.__init__` 3 个死实例 +- [ ] `get_db_session` 统一事务边界(commit/rollback),废除路由手动 commit +- [ ] 连接池治理(3 进程峰值 150 > PG 默认 100),考虑 PgBouncer +- [ ] 进销存 state 从模块级单例迁回 Pinia,tab 改子路由 +- [ ] 统一 `/health` 响应 schema;SPA fallback 排除 `/api` 前缀避免吞 404 +- [ ] CORS 收敛(`allow_origins=["*"]` + `allow_credentials=True` 不安全) + +--- + +## 执行进度 + +| 阶段 | 项数 | 已完成 | 进行中 | +|------|------|--------|--------| +| P0 | 7 | 6 修复 + 1 排查 | - | +| P1 | 5 | 0 | P1-1 进行中(finance 已抽) | +| P2 | 3 | 0 | - | +| P3 | 7 | 0 | - | diff --git a/frontend/src/modules/inventory/components/DashboardTab.vue b/frontend/src/modules/inventory/components/DashboardTab.vue index 2744879..f8ffb0a 100644 --- a/frontend/src/modules/inventory/components/DashboardTab.vue +++ b/frontend/src/modules/inventory/components/DashboardTab.vue @@ -6,7 +6,7 @@ const { state, formatCurrency } = useInventory()