Files
fastAPI/docs/plans/2026-05-24-accessory-module.md
2026-05-24 17:25:47 +08:00

647 lines
22 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.
# PRD · 模块三:附件登记
**版本:** v1.0
**状态:** 待实现
**适用端:** 安卓扫码枪 Flutter 应用 + FastAPI 后端
---
## 1. 功能概述
附件登记模块是为成品库房中**无执行卡的订单附件**提供信息化管理的独立模块。
在实际作业中,产品的附件(安装配件、说明书、包装材料等)可能在主产品生产完成之前就运抵库房,需要临时存放。这些附件不携带执行卡,无法通过扫码获取总排号,因此需要通过手动输入排产号、选择工令号的方式定位到具体总排号,完成上架和装箱登记。
本模块覆盖附件的**上架登记、下架操作、装箱处理**全流程,与现有的上架登记和装箱编号模块并行存在于首页导航。
---
## 2. 用户与使用场景
**使用人员:** 成品库房工人
**典型场景:**
| 场景 | 描述 |
|---|---|
| 附件到货上架 | 附件先于产品到达库房,工人根据送货单上的排产号,手动输入后查询工令号,选择后登记到货位 |
| 附件下架 | 已上架的附件需要转移到转运区域,与产品下架逻辑一致 |
| 附件与产品混装 | 装箱时将附件与主产品装入同一箱号 |
| 附件单独装箱 | 装箱时将附件单独装箱,关联到同一排产号 |
| 附件类型管理 | 管理员在系统中预设附件类型列表,工人操作时从中点选 |
---
## 3. 设备能力约定
与现有模块一致:
| 项目 | 说明 |
|---|---|
| 扫码输出方式 | 广播输出Android Intent |
| 屏幕尺寸 | 3.5 英寸,宽度 640px高度 960px |
| 键盘 | 31 键实体键盘 |
| 网络 | WiFi / 蓝牙 / 蜂窝 |
---
## 4. 码值识别规则
本模块中,货位号仍通过扫码获取,复用现有码值解析规则。排产号和工令号通过手动输入和选择操作。
| 码值类型 | 获取方式 | 识别规则 |
|---|---|---|
| 排产号 | 手动输入 | 正则 `^[A-Z]{1,2}\d{5}(-J)?$` |
| 工令号 | 从列表选择 | 由 ERP 查询返回 |
| 总排号 | 系统自动关联 | 由 排产号+工令号 从 ERP 关联得出 |
| 普通货位号 | 扫码或手动输入 | 正则 `^[A-Z]+\d+-\d+-\d+$` |
| 转运特殊货位号 | 扫码或手动输入 | 以 `TRANS-` 开头 |
| 附件类型 | 预设点选或自由输入 | 字符串,最长 64 字符 |
---
## 5. 数据模型
### 5.1 新增表:附件记录表
```sql
CREATE TABLE "CargoTrace".finished_goods_accessory (
id SERIAL PRIMARY KEY,
paichan_no VARCHAR(64) NOT NULL,
zongpai_no VARCHAR(64) NOT NULL,
accessory_type VARCHAR(64) NOT NULL,
quantity INT NOT NULL,
location_code VARCHAR(64) NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_fga_paichan_no ON "CargoTrace".finished_goods_accessory (paichan_no);
CREATE INDEX idx_fga_zongpai_no ON "CargoTrace".finished_goods_accessory (zongpai_no);
CREATE INDEX idx_fga_location_code ON "CargoTrace".finished_goods_accessory (location_code);
```
| 字段 | 类型 | 说明 |
|---|---|---|
| id | SERIAL | 主键 |
| paichan_no | VARCHAR(64) | 排产号,工人手动输入 |
| zongpai_no | VARCHAR(64) | 总排号,由排产号+工令号从 ERP 关联得出 |
| accessory_type | VARCHAR(64) | 附件类型名称,来自预设列表或自由输入 |
| quantity | INT | 附件数量,精确计量 |
| location_code | VARCHAR(64) | 货位号,上架后填入;未上架时为 NULL |
| created_at | TIMESTAMP | 创建时间 |
**设计说明:**
- 无唯一约束:同一总排号可登记多种附件,每种附件也可分多次登记。
- `location_code` 为 NULL 表示未上架,有值表示已上架或已下架到转运区域。
- 下架逻辑复用现有模式:`location_code``TRANS-` 开头即为已下架。
### 5.2 新增表:附件类型预设表
```sql
CREATE TABLE "CargoTrace".accessory_type (
id SERIAL PRIMARY KEY,
name VARCHAR(64) NOT NULL,
sort_order INT NOT NULL DEFAULT 0,
CONSTRAINT uk_accessory_type_name UNIQUE (name)
);
```
| 字段 | 类型 | 说明 |
|---|---|---|
| id | SERIAL | 主键 |
| name | VARCHAR(64) | 类型名称,唯一 |
| sort_order | INT | 排序权重,值越小越靠前 |
### 5.3 现有表变更:装箱明细表
`finished_goods_box_item` 表新增 `item_type` 字段:
```sql
ALTER TABLE "CargoTrace".finished_goods_box_item
ADD COLUMN item_type VARCHAR(16) NOT NULL DEFAULT 'product';
COMMENT ON COLUMN "CargoTrace".finished_goods_box_item.item_type
IS '条目类型product = 产品accessory = 附件';
```
---
## 6. 页面设计
### 6.1 首页更新
在现有首页功能卡片列表中,在"装箱编号"卡片下方新增"附件登记"卡片:
```
┌──────────────────────────────┐
│ 📦 上架登记 │ ← 已上线
│ ──────────────────────── │
│ 📋 装箱编号 │ ← 已上线
│ ──────────────────────── │
│ 🔧 附件登记 │ ← 新增,开发中
│ 扫码或手动登记订单附件 │
│ 开发中 ○ │
│ ──────────────────────── │
│ 🔍 货架查询 │ ← 规划中(已并入总览)
│ 规划中 ○ │
└──────────────────────────────┘
```
### 6.2 附件登记主页面
页面分为上下两个区域:上方为操作区,下方为已登记附件列表。整体可滚动。
**初始状态(等待输入):**
```
┌──────────────────────────────────┐
│ 附件登记 │
├──────────────────────────────────┤
│ ▌ 操作区 │
│ │
│ 排产号 [ ] [查询] │ ← 手动输入框 + 查询按钮
│ │
│ 工令号 (请先查询排产号) │ ← 置灰,查询后填充下拉列表
│ 总排号 (请先选择工令号) │ ← 置灰,选择工令号后自动填入
│ 附件类型 ▼ [ ] │ ← 置灰,选择总排号后激活
│ 数量 [ ] │ ← 置灰,选择附件类型后激活
│ 货位号 [ ] │ ← 置灰,输入数量后激活,支持扫码
│ │
│ ┌────────────────────────┐ │
│ │ 确 认 上 架 │ │ ← 置灰,全部填入后激活
│ └────────────────────────┘ │
│ │
├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│ ▌ 已登记附件 │ ← 初始隐藏,查询排产号后展示
│ (请先查询排产号) │
│ │
├──────────────────────────────────┤
│ ● 等待输入排产号… │
└──────────────────────────────────┘
```
**查询排产号后的状态:**
```
┌──────────────────────────────────┐
│ 附件登记 │
├──────────────────────────────────┤
│ ▌ 操作区 │
│ │
│ 排产号 [ W00009 ] [查询] │
│ │
│ 工令号 ▼ [ 6-1(7) ] │ ← 下拉展示该排产号下所有工令号
│ │ │ 可点选
│ 总排号 26BW0011 │ ← 自动填入(工令号选定后关联)
│ │
│ 附件类型 ▼ [ 安装配件 ] │ ← 下拉预设 + 自由输入
│ 数量 [ 5 ] │
│ 货位号 [ A01-02-03 ] │ ← 扫码或手动输入
│ │
│ ┌────────────────────────┐ │
│ │ 确 认 上 架 │ │
│ └────────────────────────┘ │
│ │
├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│ ▌ 已登记附件 · W00009 │
│ ┌────────┬──────────┬────┬──────┬──────────┐
│ │总排号 │附件类型 │数量│货位号│操作 │
│ ├────────┼──────────┼────┼──────┼──────────┤
│ │26BW0011│安装配件 │ 5 │A01-02│ [改][删] │
│ │26BW0011│说明书 │ 10 │A01-02│ [改][删] │
│ │26BW0010│包装材料 │ 2 │— │ [改][删] │ ← 未上架,货位显示 —
│ └────────┴──────────┴────┴──────┴──────────┘
│ │
├──────────────────────────────────┤
│ ● 已填入,请确认或继续编辑 │
└──────────────────────────────────┘
```
### 6.3 字段联动规则
操作区采用**逐步解锁**模式,每一步完成后才激活下一步:
| 步骤 | 操作 | 激活条件 | 说明 |
|---|---|---|---|
| 1 | 输入排产号并查询 | 无 | 手动输入排产号,点击查询或按回车 |
| 2 | 选择工令号 | 排产号查询成功 | 下拉列表展示该排产号下所有去重工令号 |
| 3 | 总排号自动填入 | 工令号选定 | 系统根据排产号+工令号从 ERP 关联总排号若只有1个则自动填入若多个则展示下拉供选择 |
| 4 | 选择附件类型 | 总排号已填入 | 下拉展示预设类型列表,支持手动输入自定义类型 |
| 5 | 输入数量 | 附件类型已填入 | 手动输入正整数 |
| 6 | 输入货位号 | 数量已填入 | 扫码或手动输入,复用现有货位号格式校验 |
| 7 | 确认上架 | 全部字段有效 | 提交到后端 |
### 6.4 已登记列表
已登记列表在首次查询排产号成功后展示,展示该排产号下所有已登记的附件记录。
**列定义:**
| 列 | 内容 | 说明 |
|---|---|---|
| 总排号 | zongpai_no | 该附件关联的总排号 |
| 附件类型 | accessory_type | 类型名称 |
| 数量 | quantity | 登记的附件数量 |
| 货位号 | location_code | 已上架显示货位号,未上架显示 "—",转运显示 TRANS-xx |
| 操作 | [改] [删] | 行内编辑和删除 |
**行内编辑(点击 [改]**
```
│ 26BW0011 │ [安装配件] │ [5] │ A01-02 │ [✓][✗] │
```
- 仅附件类型和数量可编辑,总排号和货位号不可修改
- 点 [✓] 调用 PATCH 接口保存
- 点 [✗] 取消恢复原值
**行内删除(点击 [删]**
```
│ 确认删除此条附件记录? [是] [否]
```
- 确认后调用 DELETE 接口删除
**已装箱附件标记:** 已完成装箱的附件记录在列表中显示 `[已装箱]` 标签,不支持编辑和删除。
---
## 7. 功能详细说明
### 7.1 操作流程图
```mermaid
flowchart TD
A([进入附件登记页]) --> B[等待输入排产号]
B --> C[手动输入排产号\n点击查询或回车]
C --> D{排产号格式校验}
D -->|无效| E[底部红条提示:排产号格式无效]
E --> B
D -->|有效| F[调用后端查询工令号列表]
F --> G{查询结果}
G -->|未找到| H[底部红条提示:未找到该排产号信息]
H --> B
G -->|成功| I[工令号下拉列表填充\n已登记列表刷新]
I --> J[选择工令号]
J --> K{工令号关联的总排号}
K -->|仅1个| L[自动填入总排号]
K -->|多个| M[总排号下拉供选择]
L --> N[选择/输入附件类型]
M --> N
N --> O[输入数量]
O --> P[扫描或输入货位号]
P --> Q{全部字段有效?}
Q -->|否| R[相应字段标红提示]
Q -->|是| S[确认按钮激活]
S --> T[点击确认或回车提交]
T --> U[调用后端保存接口]
U --> V{结果}
V -->|成功| W[成功反馈\n已登记列表刷新\n操作区部分重置\n保留排产号和工令号选择]
V -->|失败| X[保留数据\n底部状态栏显示错误]
W --> Y{是否继续登记?}
Y -->|同排产号继续| N
Y -->|换排产号| B
P --> P2{货位号类型判断}
P2 -->|现有记录在普通货架\n且目标为转运货架| DOWN[下架操作\n后端更新记录]
P2 -->|现有记录在转运货架| ERR[错误:已下架不可重新上架]
```
### 7.2 下架逻辑
附件的下架逻辑与产品完全一致:
| 现有状态 | 目标货位 | 结果 |
|---|---|---|
| 未上架location_code 为 NULL | 普通货架 | 上架成功,新建 location_code |
| 未上架 | 转运货架 | 直接转运成功 |
| 普通货架 | 普通货架 | 错误:该附件已有货位记录 |
| 普通货架 | 转运货架 | 下架成功,更新 location_code |
| 转运货架 | 任何 | 错误:已下架不可重新上架 |
### 7.3 已登记列表刷新策略
| 时机 | 行为 |
|---|---|
| 排产号查询成功 | 加载该排产号下全部附件记录 |
| 上架/下架提交成功 | 刷新列表 |
| 编辑/删除操作完成 | 刷新列表 |
| 切换排产号 | 替换为新排产号的记录 |
---
## 8. 装箱集成
附件的装箱操作在**现有装箱编号模块**中完成,附件登记模块只负责上架和下架。
### 8.1 装箱模块改动
#### 8.1.1 接口响应扩展
`GET /CargoTrace/box/info` 接口的响应新增 `pending_accessories` 字段:
```json
{
"zongpai_no": "26BW0011",
"paichan_no": "W00009",
"work_order_no": "6-1(7)",
"quantity": 80,
"current_zongpai_boxes": [...],
"existing_boxes": [...],
"max_box_no": 5,
"suggested_box_no": 6,
"pending_accessories": [
{
"accessory_id": 1,
"accessory_type": "安装配件",
"quantity": 5,
"location_code": "A01-02-03"
},
{
"accessory_id": 2,
"accessory_type": "说明书",
"quantity": 10,
"location_code": "TRANS-01"
}
]
}
```
#### 8.1.2 装箱页面展示
在装箱页面已分配列表下方,新增"待装箱附件"区域:
```
┌──────────────────────────────┐
│ ...已分配产品列表... │
├──────────────────────────────┤
│ 待装箱附件2 项) │
│ 安装配件 × 5 [加入本箱] │
│ 说明书 × 10 [加入本箱] │
└──────────────────────────────┘
```
- 工人点击 [加入本箱],将附件作为一条装箱明细写入 `finished_goods_box_item``item_type``accessory`
- 附件在装箱详情页中通过不同的行样式或标签区分
#### 8.1.3 装箱记录保存
`POST /CargoTrace/box` 接口的请求体新增可选字段:
```json
{
"zongpai_no": "26BW0011",
"box_no": 3,
"quantity": 80,
"item_type": "accessory",
"accessory_id": 1
}
```
- `item_type`:默认为 `product`,附件装箱时传 `accessory`
- `accessory_id`:当 `item_type``accessory` 时必填,关联附件记录
---
## 9. 接口依赖
### 9.1 工令号查询接口(新增)
**GET** `/CargoTrace/accessory/work-orders?paichan_no={paichan_no}`
根据排产号查询该排产号下所有去重的工令号及其对应的总排号。
**请求参数:**
| 参数 | 类型 | 位置 | 说明 |
|---|---|---|---|
| paichan_no | string | Query | 排产号 |
**成功响应 200**
```json
{
"paichan_no": "W00009",
"work_orders": [
{
"work_order_no": "6-1(7)",
"zongpai_nos": ["26BW0010", "26BW0011", "26BW0012"]
},
{
"work_order_no": "6-2(3)",
"zongpai_nos": ["26BW0013", "26BW0014"]
}
]
}
```
**失败响应:**
| HTTP 状态码 | 错误码 | 含义 |
|---|---|---|
| 400 | INVALID_PAICHAN | 排产号格式不合法 |
| 404 | PAICHA_NOT_FOUND | 未找到该排产号 |
### 9.2 附件登记接口(新增)
**POST** `/CargoTrace/accessory`
**请求体:**
```json
{
"paichan_no": "W00009",
"zongpai_no": "26BW0011",
"accessory_type": "安装配件",
"quantity": 5,
"location_code": "A01-02-03"
}
```
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| paichan_no | string | 是 | 排产号 |
| zongpai_no | string | 是 | 总排号 |
| accessory_type | string | 是 | 附件类型名称 |
| quantity | int | 是 | 数量,必须为正整数 |
| location_code | string | 否 | 货位号,为空表示仅登记不上架 |
**成功响应 200**
```json
{
"id": 1,
"paichan_no": "W00009",
"zongpai_no": "26BW0011",
"accessory_type": "安装配件",
"quantity": 5,
"location_code": "A01-02-03",
"created_at": "2026-05-24T10:30:00"
}
```
### 9.3 附件记录查询接口(新增)
**GET** `/CargoTrace/accessory?paichan_no={paichan_no}`
查询指定排产号下所有附件记录。
**成功响应 200**
```json
{
"paichan_no": "W00009",
"items": [
{
"id": 1,
"zongpai_no": "26BW0011",
"accessory_type": "安装配件",
"quantity": 5,
"location_code": "A01-02-03",
"is_boxed": false,
"created_at": "2026-05-24T10:30:00"
}
]
}
```
### 9.4 附件记录更新接口(新增)
**PATCH** `/CargoTrace/accessory/{id}`
**请求体:**
```json
{
"accessory_type": "安装配件",
"quantity": 8,
"location_code": "A01-02-04"
}
```
支持部分更新,仅传需要修改的字段。
### 9.5 附件记录删除接口(新增)
**DELETE** `/CargoTrace/accessory/{id}`
已装箱的附件不可删除,返回 `409 ALREADY_BOXED`
### 9.6 附件类型管理接口(新增)
**GET** `/CargoTrace/accessory-type**
返回所有预设附件类型,按 sort_order 排序。
```json
{
"types": [
{"id": 1, "name": "安装配件", "sort_order": 0},
{"id": 2, "name": "说明书", "sort_order": 1},
{"id": 3, "name": "包装材料", "sort_order": 2}
]
}
```
**POST** `/CargoTrace/accessory-type` — 新增类型
**PATCH** `/CargoTrace/accessory-type/{id}` — 修改类型名称或排序
**DELETE** `/CargoTrace/accessory-type/{id}` — 删除类型
---
## 10. 异常与错误处理
| 异常情况 | 触发条件 | 系统表现 |
|---|---|---|
| 排产号格式无效 | 不符合正则 | 底部红条提示"排产号格式无效" |
| 排产号未找到 | ERP 无记录 | 底部红条提示"未找到该排产号信息" |
| 附件类型为空 | 未选择或输入 | 提示"请选择或输入附件类型" |
| 数量无效 | 非正整数 | 提示"数量必须为正整数" |
| 货位号格式无效 | 不符合货位号规则 | 底部红条提示"无效的货位号" |
| 附件已有货位 | 重复上架普通货架 | 错误弹窗,显示已有货位信息 |
| 已下架附件 | 转运货架→任何 | 底部红条提示"该附件已下架不可重新上架" |
| 已装箱附件 | 尝试编辑/删除已装箱记录 | 底部红条提示"该附件已装箱不可操作" |
| 网络异常 | 超时或断网 | 底部黄条提示"网络异常,请检查网络连接" |
---
## 11. 反馈机制
复用现有统一反馈标准:
| 事件 | 屏幕 | 声音 | 振动 |
|---|---|---|---|
| 排产号查询成功 | 工令号下拉填充,绿色高亮 | 短促提示音 | 短震 |
| 上架成功 | 底部状态栏绿色提示 1.5 秒 | 成功音 | 长震 |
| 下架成功 | 底部状态栏绿色提示"下架成功" | 成功音 | 长震 |
| 操作失败 | 底部状态栏红色提示 2 秒 | 错误音 | 短震 |
| 网络异常 | 底部状态栏黄色提示 | 失败音 | 短震 |
---
## 12. 非功能需求
| 项目 | 要求 |
|---|---|
| 工令号查询响应时间 | ≤ 500ms |
| 附件保存响应时间 | ≤ 500ms |
| 扫码到货位号填入延迟 | ≤ 100ms |
| 离线处理策略 | 断网时提示检查网络,数据保留在页面 |
| 软键盘控制 | 附件类型选择弹出选择面板时使用应用内 UI不依赖系统软键盘 |
---
## 13. 项目结构变更
### 13.1 Flutter 端
```
lib/
├── pages/
│ ├── accessory_page.dart # 新增:附件登记主页面
│ ├── boxing_page.dart # 修改:增加附件装箱展示
│ └── boxing_detail_page.dart # 修改:附件装箱明细展示
├── services/
│ └── api_service.dart # 修改:新增附件相关 API 调用
└── widgets/
└── accessory_type_selector.dart # 新增:附件类型选择器组件
```
### 13.2 FastAPI 端
```
app/
├── api/v1/
│ ├── accessory.py # 新增:附件登记 API
│ ├── accessory_type.py # 新增:附件类型管理 API
│ └── box.py # 修改:装箱接口支持附件
├── models/
│ ├── finished_goods.py # 修改:新增附件模型
│ └── accessory_type.py # 新增:附件类型模型
├── schemas/
│ ├── accessory.py # 新增:附件请求/响应 Schema
│ └── box.py # 修改:装箱 Schema 支持附件
└── services/
├── accessory_service.py # 新增:附件业务逻辑
└── box_service.py # 修改:装箱逻辑支持附件
```
---
## 14. 超出当前版本范围
以下内容在当前版本中不实现:
- 附件到货数量与 ERP BOM 对比校验(附件数量是否与订单需求匹配)
- 附件条码标签生成(为附件打印临时标签)
- 附件库存盘点功能
- 附件出入库历史查询