Files
track/PROJECT_ANALYSIS_REPORT.md
duxingchen d32a3dce49 chore: UniApp OTA静默更新 + 项目分析报告
1. UniApp OTA更新优化
   - 移除进度条和下载中Toast(静默后台下载)
   - 安装成功后3秒自动重启,无用户感知
   - 失败时仅console.error,不弹窗打扰用户

2. 项目分析报告
   - 新增PROJECT_ANALYSIS_REPORT.md
   - 涵盖技术栈、数据模型、核心业务流程、潜在痛点和架构建议
2026-08-12 12:04:51 +08:00

397 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 自动生成,基于对代码库的静态分析。建议结合实际运行数据进一步验证上述发现。*