1. UniApp OTA更新优化 - 移除进度条和下载中Toast(静默后台下载) - 安装成功后3秒自动重启,无用户感知 - 失败时仅console.error,不弹窗打扰用户 2. 项目分析报告 - 新增PROJECT_ANALYSIS_REPORT.md - 涵盖技术栈、数据模型、核心业务流程、潜在痛点和架构建议
19 KiB
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 请求/响应 DTOcore/— 横切关注点(配置、数据库连接池、安全、时间工具)
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 关键设计决策
-
逻辑外键 vs 物理外键:
material_id和assignee_id均使用逻辑外键(只存 ID,无 DB 级约束),指向外部 MOM 老系统——允许老系统数据独立演进,避免跨库约束。 -
物料快照机制:Product 表存储
material_name/spec_model/category/material_type完整快照,创建时一次性写入。这意味着即使老系统后续修改物料数据,已经流转的产品标签不会受影响。 -
任务树自引用:Task 通过
parent_task_id自引用实现无限层级嵌套。三种任务类型定义了分支行为:TRANSFER:主线转交(主分支)SPAWN:协助分支(不改变父任务状态)RECOVERY:撤回后接力节点
-
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)
{
"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)
{
"next_tasks": [
{
"task_name": "接线",
"assignees": ["lisi02", "wangwu03"]
},
{
"task_name": "质检",
"assignees": ["virtual_warehouse"]
}
],
"note": "装配工序完工,转接线双人并行 + 质检入库"
}
4.3 标签打印数据
{
"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 架构演进建议
-
引入消息队列:当裂变转交产生多个子任务时,通知创建在同一个事务内——若通知发送失败会回滚整个转交。建议将通知发送解耦到消息队列。
-
位置追踪精度:当前
current_location_id仅存储一个持有者,多路裂变后只能追踪第一个分支的负责人。建议引入专门的product_locations轨迹表。 -
任务状态机形式化:当前状态转换逻辑分散在
task_service.py各方法中(多处if task.status != ...检查)。建议使用状态机模式(如transitions库)集中管理。 -
API 版本化健全性:当前
/api/v1前缀已预留版本号,但部分接口响应模型在迭代中已发生变化(如task_tree替代top_level_tasks),建议通过/api/v2或 Deprecation Header 管理 API 演进。 -
前端测试覆盖:当前
frontend/无任何测试文件(.test.ts/.spec.ts),后端也无pytest目录,建议补充核心业务流程的集成测试。
七、项目亮点总结
-
✅ 精巧的任务裂变模型:TRANSFER/SPAWN/RECOVERY 三种任务基因 + 智能父节点继承算法,支持单线转交、并行协助、裂变分支、撤回接力等复杂工厂场景。
-
✅ 双库隔离架构:Track 本库存储流转数据,MOM 老系统只读对接——物理隔离保护老系统,同时通过物料快照机制避免数据漂移。
-
✅ 工业级标签打印:PIL 精确坐标排版 → 二值化 → TSPL 指令 → Socket 直连打标机,全程离线化,不依赖第三方打印服务。
-
✅ 三端覆盖:PC 管理端 (React+Tauri) + 移动端 (UniApp) + 扫码端 (html5-qrcode 懒加载),UI 架构通过路由级代码分割优化首屏加载。
-
✅ 双 Token 认证:Access Token (2h) + Refresh Token (7d),对接 MOM sys_user 的 scrypt 密码存储,不重复造用户系统。
报告由 Claude Code 自动生成,基于对代码库的静态分析。建议结合实际运行数据进一步验证上述发现。