# 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 对比校验(附件数量是否与订单需求匹配) - 附件条码标签生成(为附件打印临时标签) - 附件库存盘点功能 - 附件出入库历史查询