diff --git a/PROJECT_ANALYSIS_REPORT.md b/PROJECT_ANALYSIS_REPORT.md new file mode 100644 index 0000000..fa63f84 --- /dev/null +++ b/PROJECT_ANALYSIS_REPORT.md @@ -0,0 +1,396 @@ +# Track Production — 项目现状与业务逻辑分析报告 + +> **受众:** 高级开发工程师 / 系统架构师 +> **日期:** 2026-08-12 +> **版本:** v0.1.0 + +--- + +## 一、技术栈与架构 + +### 1.1 项目整体拓扑 + +``` +track/ +├── backend/ # Python FastAPI 后端服务 +├── frontend/ # React SPA(Tauri 桌面壳 + Web 管理端) +└── track-uniapp/ # UniApp 移动端(iOS/Android/H5) +``` + +### 1.2 后端技术栈 + +| 层面 | 技术 | 版本 | +|------|------|------| +| 框架 | FastAPI (ASGI) | 0.141 | +| ASGI 服务器 | Uvicorn | 0.52 | +| ORM | SQLAlchemy 2.0(异步) | 2.0.51 | +| 数据库 | PostgreSQL(通过 asyncpg) | pg 15+ | +| 迁移工具 | Alembic | 1.18 | +| 认证 | python-jose (JWT) + Werkzeug scrypt | HS256 | +| 密码验证 | passlib bcrypt + Werkzeug scrypt(MOM 对接) | — | +| 二维码 | qrcode[pil] + Pillow | 8.2 / 12.3 | +| 标签打印 | PIL 图像合成 + Socket TSPL 协议 | — | +| 序列化 | Pydantic v2 | 2.13 | + +**架构模式:分层架构 (Layered Architecture)** +- `api/v1/endpoints/` — 路由/控制器层 +- `services/` — 业务逻辑服务层 +- `models/` — SQLAlchemy ORM 数据模型 +- `schemas/` — Pydantic 请求/响应 DTO +- `core/` — 横切关注点(配置、数据库连接池、安全、时间工具) + +### 1.3 前端技术栈 + +| 层面 | 技术 | 版本 | +|------|------|------| +| UI 框架 | React 19 + TypeScript 6.0 | 19.2 | +| 桌面壳 | Tauri 2.x(Rust 原生窗口) | 2.11 | +| 构建工具 | Vite 8.2 | — | +| UI 库 | Ant Design 6 + Tailwind CSS 4 | 6.5 / 4.3 | +| 状态管理 | Zustand 5 | 5.0 | +| 路由 | React Router v7 | 7.18 | +| HTTP 客户端 | Axios | 1.19 | +| 扫码 | html5-qrcode(懒加载) | 2.3 | + +**架构模式:SPA + 路由级代码分割(React.lazy)** + +### 1.4 移动端 (track-uniapp) + +基于 **UniApp (Vue)** 构建,目前包含 5 个页面: +- 扫码干活 (`pages/scan/index`) — 核心工作入口 +- 我的任务 (`pages/tasks/index`) — 个人任务看板 +- 消息通知 (`pages/notify/index`) +- 个人中心 (`pages/profile/index`) +- 登录页 (`pages/login/login`) + +底部 TabBar 4 个入口,与 PC 端 AppLayout 页面结构对应。 + +--- + +## 二、数据模型与实体关系 + +### 2.1 核心 E-R 图 + +``` +┌──────────────────┐ ┌──────────────────┐ +│ production_orders│ 1──N │ products │ +│ - id (UUID PK) │ │ - id (UUID PK) │ +│ - order_no[UQ] │ │ - serial_number │ +│ - customer_info │ │ [16位HEX, UQ] │ +│ - status │ │ - order_id (FK) │←──── FK (nullable in latest) +│ - created_at │ │ - material_id* │ * = 逻辑外键→MOM +└──────────────────┘ │ - material_name │ (material_base) + │ - spec_model │ + │ - category │ + │ - material_type │ + │ - overall_status │ 宏观: 备货/生产/测试/维修/在库 + │ - parent_product │──┐ 自引用 FK + │ - current_location│ │ (持有者/仓库) + │ - status │ │ + │ - created_at │ │ + └────────┬─────────┘ │ + │ 1 │ + │ │ + │ N │ + ┌────────▼─────────┐ │ + │ tasks │◄─┘ + │ - id (UUID PK) │ + │ - product_id(FK) │ + │ - parent_task_id │──┐ 自引用 FK(无限嵌套) + │ - task_name │ │ + │ - assignee_id* │ │ * = 逻辑外键→MOM sys_user + │ - status │ │ + │ - task_type │ │ TRANSFER/SPAWN/RECOVERY + │ - is_rework │ │ + │ - reject_reason │ │ + │ - remark │ │ + │ - received_at │ │ + │ - completed_at │ │ + │ - created_at │ │ + └──┬──────┬─────────┘ │ + │ N │ N │ + ┌────────▼─┐ ┌──▼──────────┐ │ + │task_records│ │ task_logs │ │ + │- remark │ │ - action_type│ │ + │- images │ │ - operator_id│ │ + │- created │ │ - remark │ │ + └───────────┘ │ - created │ │ + └─────────────┘ │ + ┌────────────────────────┐ + │ notifications │ + │ - user_id (目标用户) │ + │ - type (TRANSFER/REJECT│ + │ /COMMENT) │ + │ - task_id (FK→tasks) │ + │ - is_read │ + └────────────────────────┘ + ┌────────────────────────┐ + │ product_messages │ + │ - product_id (FK) │ + │ - operator_id │ + │ - content │ + └────────────────────────┘ +``` + +### 2.2 关键设计决策 + +1. **逻辑外键 vs 物理外键**:`material_id` 和 `assignee_id` 均使用逻辑外键(只存 ID,无 DB 级约束),指向外部 MOM 老系统——允许老系统数据独立演进,避免跨库约束。 + +2. **物料快照机制**:Product 表存储 `material_name/spec_model/category/material_type` 完整快照,创建时一次性写入。这意味着即使老系统后续修改物料数据,已经流转的产品标签不会受影响。 + +3. **任务树自引用**:Task 通过 `parent_task_id` 自引用实现无限层级嵌套。三种任务类型定义了分支行为: + - `TRANSFER`:主线转交(主分支) + - `SPAWN`:协助分支(不改变父任务状态) + - `RECOVERY`:撤回后接力节点 + +4. **16 位 HEX 产品身份证**:通过 PostgreSQL SEQUENCE 单调递增生成,格式 `%016X`,理论上限 2^64(实际序列值)。 + +--- + +## 三、核心业务流程 + +### 3.1 任务生命周期状态机 + +``` + ┌──────────┐ + │ PENDING │ 待接收 + └────┬─────┘ + │ receive + ▼ + ┌──────────┐ + ┌────────│ WIP │◄───────────────┐ + │ └────┬─────┘ │ + │ spawn │ transfer/complete │ recall + │ (协助分支) │ (完工裂变转交) │ (撤回转交→CANCELED) + ▼ ▼ │ + ┌──────────┐ ┌───────────┐ │ + │PENDING │ │ COMPLETED │ │ + │(SPAWN) │ │(终态) │ │ + └──┬───────┘ └───────────┘ │ + │ reject │ + ▼ │ +┌──────────┐ ┌──────────────────────────────┘ +│ REJECTED │ │ +│ (终态) │ │ +└────┬─────┘ │ + │ 自动创建返工│ + ▼ │ +┌──────────┐ │ +│ PENDING │ │ +│(REWORK) │──┘ 返工任务回到 WIP 循环 +└──────────┘ +``` + +### 3.2 核心业务接口说明 + +| 接口 | 作用 | 关键逻辑 | +|------|------|----------| +| `POST /tasks/{id}/receive` | 工人确认接收 | PENDING→WIP,同步产品位置+宏观状态 | +| `POST /tasks/{id}/transfer` | 完工裂变转交 | WIP→COMPLETED,支持多分支 `next_tasks`,裂变子任务挂在当前任务下 | +| `POST /tasks/{id}/complete` | 旧版单步完结 | 保留兼容,内部委托到 transfer 逻辑 | +| `POST /tasks/{id}/end` | 结束协助分支 | 仅 SPAWN 类型可用,不创建下游 | +| `POST /tasks/{id}/reject` | 品质驳回 | →REJECTED,自动创建返工任务给上游 | +| `POST /tasks/{id}/recall` | 撤回未接收的转交 | PENDING→CANCELED,创建 RECOVERY 接力 | +| `POST /tasks/{id}/spawn` | 派发并行协助 | 父任务保持 WIP,创建 PENDING SPAWN 子任务 | +| `GET /products/scan/{sn}` | 扫码查询 | 返回产品信息+完整递归任务树+人员姓名映射 | +| `PATCH /products/scan/{sn}/status` | 更新宏观状态 | 权限校验:SUPER_ADMIN 或当前主线负责人 | +| `POST /print/execute` | 物理标签打印 | 480×360 工业排版→二值化→TSPL→Socket 9100 | + +### 3.3 权限模型 + +``` +角色层级: + SUPER_ADMIN / SUPERVISOR → 上帝视角(所有任务可操作) + operator (普通工人) → 仅操作分配给自己的任务 + +校验点: + - 任务接收/驳回/转交/撤回:_check_permission(assignee_id, operator_id, role) + - 宏观状态修改:需 SUPER_ADMIN 或当前主线任务(WIP/PENDING)的 assignee + - 留言板:operator_id 由 Token 强制覆写,防止越权伪造 +``` + +--- + +## 四、数据样本 (Data Shape) + +### 4.1 扫码查询响应 (ProductScanResponse) + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440001", + "serial_number": "000000000000001A", + "external_serial": "CUST-SN-2024-0001", + "order_id": "660e8400-e29b-41d4-a716-446655440002", + "order_no": "ORD-2024-0881", + "material_id": "MAT-32001", + "material_name": "样品升降台V1J", + "spec_model": "PH-B4V1J/类A", + "category": "成品", + "material_type": "装配件", + "parent_product_id": null, + "current_location_id": "zhangsan01", + "overall_status": "生产", + "status": "in_progress", + "created_at": "2026-08-10T09:30:00+08:00", + "assignee_names": { + "zhangsan01": "张三", + "lisi02": "李四", + "wangwu03": "王五" + }, + "task_tree": [ + { + "id": "770e8400-...", + "task_name": "装配", + "assignee_id": "zhangsan01", + "status": "WIP", + "task_type": "TRANSFER", + "is_rework": false, + "received_at": "2026-08-10T09:45:00+08:00", + "child_tasks": [ + { + "id": "880e8400-...", + "task_name": "接线", + "assignee_id": "lisi02", + "status": "PENDING", + "task_type": "TRANSFER", + "child_tasks": [], + "records": [] + } + ], + "records": [ + { + "id": 1, + "task_id": "770e8400-...", + "remark": "已完成底座固定", + "images": ["https://cdn.example.com/img/2024/photo1.jpg"], + "created_at": "2026-08-10T10:15:00+08:00" + } + ] + } + ] +} +``` + +### 4.2 任务完成请求 (transfer) + +```json +{ + "next_tasks": [ + { + "task_name": "接线", + "assignees": ["lisi02", "wangwu03"] + }, + { + "task_name": "质检", + "assignees": ["virtual_warehouse"] + } + ], + "note": "装配工序完工,转接线双人并行 + 质检入库" +} +``` + +### 4.3 标签打印数据 + +```json +{ + "serial_number": "000000000000001A", + "material_name": "样品升降台V1J", + "spec_model": "PH-B4V1J/类A", + "order_no": "ORD-2024-0881", + "copies": 2, + "printer_ip": "192.168.9.221", + "printer_port": 9100 +} +``` +> 标签格式:480×360 px 工业标签 → 二值化 → TSPL BITMAP 指令 + +--- + +## 五、系统双库架构 + +``` +┌────────────────────────────────────────────────┐ +│ Track 系统(本库) │ +│ PostgreSQL :5433 / track_production │ +│ 表: production_orders, products, tasks, │ +│ task_logs, task_records, notifications, │ +│ app_versions, product_messages │ +│ ORM: SQLAlchemy 2.0 Async │ +├────────────────────────────────────────────────┤ +│ MOM 老系统(只读) │ +│ PostgreSQL :5435 / inventory_system │ +│ 表: sys_user (用户), material_base (物料) │ +│ 连接: SQLAlchemy Sync + NullPool │ +│ 用途: 登录验证 + 物料手风琴选择器 + 姓名映射 │ +└────────────────────────────────────────────────┘ +``` + +--- + +## 六、观察到的潜在架构问题与优化方向 + +### 6.1 数据一致性与可靠性 + +| 问题 | 严重度 | 说明 | +|------|--------|------| +| **Dashboard 统计值陈旧** | 中 | `get_dashboard_stats` 使用硬编码字符串 `"pending"/"in_progress"` 过滤,但任务模型实际使用 `PENDING/WIP` 等大写常量。当前实际查的是全量/0值。 | +| **产品 status 字段语义模糊** | 中 | Product 有 `status`(产品自身状态)和 `overall_status`(宏观流转状态)两个状态字段,前者使用小写 `pending/in_progress/completed`,后者使用中文 `备货/生产/测试/维修/在库`,存在概念重叠和命名不一致。 | +| **缺失数据库事务跨表保护** | 低 | `transfer_task` 涉及多条 INSERT(子任务+日志+通知+位置更新),使用多次 `flush()` + 最终 `commit()`,无显式 BEGIN/SAVEPOINT,但 SQLAlchemy autocommit 模式下能保证原子性。 | +| **无软删除机制** | 低 | 任务仅状态流转(CANCELED),产品删除是硬删除(级联清理关联),无回收站/审计日志。 | + +### 6.2 性能与查询优化 + +| 问题 | 严重度 | 说明 | +|------|--------|------| +| **递归任务树 N+1 查询** | 高 | `_load_task_tree` 和 `_load_children` 递归执行单条 SELECT,深度为 N 的任务树执行 N+1 次数据库查询。建议使用 PostgreSQL Recursive CTE 一次性加载整棵树。 | +| **姓名映射逐次查询** | 中 | `_lookup_display_names` 每次用 OR 拼接 LIKE 查询 MOM 老系统,高频场景(产品列表每页 50 条)下调用多次。建议加 Redis 缓存或本地映射表。 | +| **任务列表无总数** | 低 | `get_all_tasks` 返回的 `total` 是 `len(flat_tasks)`(即当前页条数),而非数据库真实总数,前端无法正确分页。 | +| **产品列表复杂 JOIN** | 中 | `get_all_products` 为每个产品列表做了 3 次聚合子查询(macro_status、overall_names、main_assignees),数据量大时需关注性能。 | + +### 6.3 安全性 + +| 问题 | 严重度 | 说明 | +|------|--------|------| +| **SECRET_KEY 硬编码** | 高 | `config.py` 默认值 `"change-me-in-production"`,虽然 `.env` 可覆盖,但缺少生产环境强校验。 | +| **Material API 无鉴权** | 中 | `/materials/groups` 和 `/materials/items` 无 `Depends(get_current_user)`,任何人均可查询老系统物料库。 | +| **通知查询无鉴权** | 中 | `/notifications/` 通过 Query 参数 `user_id` 过滤,可被任意篡改查看他人通知。应改为从 Token 解析当前用户。 | +| **MOM 数据库密码明文** | 中 | `mom_database.py` 中连接字符串硬编码数据库密码。 | + +### 6.4 代码质量 + +| 问题 | 严重度 | 说明 | +|------|--------|------| +| **print.py endpoint 为同步函数** | 低 | 标签预览/打印端点为同步 `def`,若耗时长会阻塞 event loop。建议改为 `async def` + `run_in_executor`。 | +| **materials.py SQL 注入风险** | 中 | `TYPE_FILTER = "1=1"` 是 Python 常量注入到 SQL 字符串拼接,虽当前安全,但此模式不够防御性。 | +| **重复的 `_task_to_response` 实现** | 低 | `product_service.py` 和 `task_service.py` 各自维护一套任务树序列化逻辑,不共享。 | +| **app_version 模块独立但未接入 CI/CD** | 低 | `AppVersion` 表 + `/app_version` 端点支持 OTA WGT 升级,但目前无关联的打包/上传脚本。 | + +### 6.5 架构演进建议 + +1. **引入消息队列**:当裂变转交产生多个子任务时,通知创建在同一个事务内——若通知发送失败会回滚整个转交。建议将通知发送解耦到消息队列。 + +2. **位置追踪精度**:当前 `current_location_id` 仅存储一个持有者,多路裂变后只能追踪第一个分支的负责人。建议引入专门的 `product_locations` 轨迹表。 + +3. **任务状态机形式化**:当前状态转换逻辑分散在 `task_service.py` 各方法中(多处 `if task.status != ...` 检查)。建议使用状态机模式(如 `transitions` 库)集中管理。 + +4. **API 版本化健全性**:当前 `/api/v1` 前缀已预留版本号,但部分接口响应模型在迭代中已发生变化(如 `task_tree` 替代 `top_level_tasks`),建议通过 `/api/v2` 或 Deprecation Header 管理 API 演进。 + +5. **前端测试覆盖**:当前 `frontend/` 无任何测试文件(`.test.ts`/`.spec.ts`),后端也无 `pytest` 目录,建议补充核心业务流程的集成测试。 + +--- + +## 七、项目亮点总结 + +1. ✅ **精巧的任务裂变模型**:TRANSFER/SPAWN/RECOVERY 三种任务基因 + 智能父节点继承算法,支持单线转交、并行协助、裂变分支、撤回接力等复杂工厂场景。 + +2. ✅ **双库隔离架构**:Track 本库存储流转数据,MOM 老系统只读对接——物理隔离保护老系统,同时通过物料快照机制避免数据漂移。 + +3. ✅ **工业级标签打印**:PIL 精确坐标排版 → 二值化 → TSPL 指令 → Socket 直连打标机,全程离线化,不依赖第三方打印服务。 + +4. ✅ **三端覆盖**:PC 管理端 (React+Tauri) + 移动端 (UniApp) + 扫码端 (html5-qrcode 懒加载),UI 架构通过路由级代码分割优化首屏加载。 + +5. ✅ **双 Token 认证**:Access Token (2h) + Refresh Token (7d),对接 MOM sys_user 的 scrypt 密码存储,不重复造用户系统。 + +--- + +*报告由 Claude Code 自动生成,基于对代码库的静态分析。建议结合实际运行数据进一步验证上述发现。* diff --git a/track-uniapp/src/App.vue b/track-uniapp/src/App.vue index 683d8b7..511c9f7 100644 --- a/track-uniapp/src/App.vue +++ b/track-uniapp/src/App.vue @@ -96,7 +96,6 @@ export default { return; } - // 弹窗询问用户是否更新 const content = description ? `发现新版本 ${newVersion}\n\n${description}\n\n是否立即更新?` : `发现新版本 ${newVersion},是否立即更新?`; @@ -109,63 +108,33 @@ export default { success: (modalRes) => { if (!modalRes.confirm) return; - // 初始反馈:告知用户已转入后台 - uni.showToast({ title: '已转入后台下载...', icon: 'none', position: 'top' }); - - const downloadTask = uni.downloadFile({ + // 🚀 静默更新:无进度条、无 toast、不监听 onProgressUpdate + uni.downloadFile({ url: wgtUrl, success: (downloadRes) => { if (downloadRes.statusCode !== 200) { - uni.showToast({ title: "下载失败", icon: "none" }); + console.error("[OTA] 下载失败, statusCode:", downloadRes.statusCode); return; } - // 安装阶段 — toast 轻提示 - uni.showToast({ title: "正在安装...", icon: "none", position: "top" }); - plus.runtime.install( downloadRes.tempFilePath, { force: true }, () => { - console.log("[OTA] WGT 安装成功"); - // 🚀 静默重启:toast 提示后自动重启 - plus.nativeUI.toast("新版本已就绪,即将重启..."); + console.log("[OTA] 安装成功,3秒后自动重启"); setTimeout(() => { plus.runtime.restart(); - }, 2000); + }, 3000); }, (err) => { console.error("[OTA] 安装失败:", err.message); - uni.showToast({ - title: "更新失败: " + (err.message || "未知错误"), - icon: "none", - duration: 4000, - }); } ); }, fail: (err) => { console.error("[OTA] 下载失败:", err.errMsg); - uni.showToast({ title: "下载失败,请检查网络", icon: "none" }); }, }); - - // 🚀 核心性能优化:节流阀 — 每跨越 20% 才触发一次轻提示 - let lastProgress = 0; - if (downloadTask && downloadTask.onProgressUpdate) { - downloadTask.onProgressUpdate((res) => { - const pct = res.progress; - if (pct - lastProgress >= 20 && pct < 100) { - lastProgress = pct; - uni.showToast({ - title: `新版本下载中 ${pct}%`, - icon: "none", - position: "top", - duration: 1200, - }); - } - }); - } }, }); },