# 一袋一码破损袋码替换 - 前端联调方案 ## 涉及页面 - 赋码管理:袋码列表、袋码详情、扫码追溯 - 批次生产管理:批次详情及有效袋码数量展示 - 托盘码管理:托盘详情及绑定袋码列表 本次只新增后端接口和字段约定,不修改前端项目代码。 ## 业务流程与数据带入 1. 生产人员在袋码列表中选择同一批次下包装破损的袋码,记录其数据库编号。 2. 前端调用破损替换接口,传入批次编号、旧袋码编号列表和破损原因。 3. 后端在一个事务内将旧袋码改为“已作废”,生成等量新袋码,并返回旧码与新码一对一映射。 4. 如果旧袋码已绑定启用托盘,新袋码自动继承原托盘;旧码从托盘当前有效袋码中移除,托盘已绑定数量保持不变。 5. 前端根据响应中的 `replaceUuid` 或新袋码列表刷新袋码列表,并调用现有袋码导出接口完成新标签打印。 6. 新袋码导出后状态由“已生成”变为“已导出印刷”;旧袋码不参与打印。 7. 扫描旧码仍可查询原批次和作废信息;扫描新码可查询当前批次、托盘和替换来源。 ## API ### 1. 破损袋码替换 | 方法 | 路径 | 说明 | |---|---|---| | POST | `/mes/pro/bag-code/replace` | 作废破损旧袋码并在原批次生成等量新袋码 | 权限:`mes:pro-bag-code:replace` **请求参数:** | 参数 | 类型 | 必填 | 说明 | |---|---|---|---| | `batchId` | Long | 是 | 生产批次编号 | | `oldIds` | Array | 是 | 待作废袋码编号,去重后至少 1 个,单次最多 200 个 | | `reason` | String | 是 | 替换原因,例如“包装破损”,最长 255 个字符 | **请求示例:** ```json { "batchId": 1001, "oldIds": [5001, 5002], "reason": "包装破损" } ``` **响应字段:** | 字段 | 类型 | 说明 | |---|---|---| | `replaceUuid` | String | 本次替换组 UUID,可用于识别本次新码集合 | | `batchId` | Long | 生产批次编号 | | `batchCode` | String | 生产批次号 | | `count` | Integer | 本次替换数量 | | `items` | Array | 新旧袋码映射 | | `items.oldBagCodeId` | Long | 旧袋码编号 | | `items.oldCode` | String | 旧袋码 | | `items.oldBagNo` | Integer | 旧袋序号 | | `items.oldStatus` | Integer | 作废前状态 | | `items.oldPalletId` | Long | 旧袋码原托盘编号,可为空 | | `items.oldPalletCode` | String | 旧袋码原托盘码,可为空 | | `items.newBagCodeId` | Long | 新袋码编号 | | `items.newCode` | String | 新袋码 | | `items.newBagNo` | Integer | 新袋序号 | | `items.newPalletId` | Long | 新袋码当前托盘编号,可为空 | | `items.newPalletCode` | String | 新袋码当前托盘码,可为空 | | `items.reason` | String | 替换原因 | | `items.replaceTime` | String | 替换时间 | **响应示例:** ```json { "code": 0, "data": { "replaceUuid": "9f6e4c4b2b2c4d5b8a8a8c0a0b0c0d0e", "batchId": 1001, "batchCode": "PC20260821L01ITEM001", "count": 2, "items": [ { "oldBagCodeId": 5001, "oldCode": "PC20260821L01ITEM001-0001", "oldBagNo": 1, "oldStatus": 20, "oldPalletId": 3001, "oldPalletCode": "PC20260821L01ITEM001-P01", "newBagCodeId": 6001, "newCode": "PC20260821L01ITEM001-1201", "newBagNo": 1201, "newPalletId": 3001, "newPalletCode": "PC20260821L01ITEM001-P01", "reason": "包装破损", "replaceTime": "2026-08-21 15:30:00" } ] } } ``` ## 字段展示规则 | 字段 | 展示位置 | 说明 | |---|---|---| | `status` | 袋码列表、袋码详情、扫码追溯 | 10 已生成、20 已导出印刷、30 已作废 | | `cancelTime` | 袋码详情、扫码追溯 | 仅作废袋码返回 | | `cancelReason` | 袋码详情、扫码追溯 | 展示破损或其他替换原因 | | `replacementUuid` | 袋码详情、扫码追溯 | 标识替换操作 | | `replacedByCode` | 作废旧码追溯 | 展示替换后的新袋码 | | `replacementOfCode` | 新码追溯 | 展示被替换的旧袋码 | | `bagQuantity` | 批次列表、批次详情 | 按当前有效袋码数量统计,不包含已作废袋码 | | `palletCode` | 袋码列表、托盘详情、扫码追溯 | 已绑定启用托盘的替换新码继承原托盘 | ## 业务规则说明 | 场景 | 规则 | |---|---| | 普通生成 | 仍仅允许生产中批次,使用 `/mes/pro/bag-code/generate` | | 破损替换 | 允许生产中、已完成批次;草稿和已作废批次不允许 | | 旧码状态 | 仅已生成、已导出印刷可以替换;已作废不能重复替换 | | 批次归属 | `oldIds` 必须全部属于请求中的 `batchId` | | 新码序号 | 从批次历史最大袋序号继续递增,已作废和逻辑删除序号也不复用 | | 数量 | 作废 N 个并生成 N 个,批次有效袋码数量保持不变 | | 未装托盘 | 旧码作废,新码保持未绑定 | | 已装托盘 | 原托盘必须存在且启用;旧码解绑并作废,新码自动继承原托盘 | | 托盘数量 | 替换前后托盘有效绑定数量保持不变 | | 打印 | 新码初始为已生成;导出后变为已导出印刷;旧码不得导出打印 | | 追溯 | 旧码记录保留并显示已作废,不返回袋码不存在;新旧码可互相追溯 | | 原子性 | 作废、生成、托盘继承、替换记录写入任一步失败,全部事务回滚 | ## 接口调用时序 1. 获取袋码分页,按批次和状态筛选有效袋码。 2. 用户勾选破损袋码并填写原因。 3. 调用 `/mes/pro/bag-code/replace`。 4. 成功后刷新袋码列表和批次数量。 5. 依据响应的新袋码集合,按 `replaceUuid` 查询或导出新码进行打印。 6. 打印成功后刷新状态;如需扫码确认,分别扫描旧码和新码核对替换关系。 ## 注意事项 - `oldIds` 不要混入不同批次的袋码;服务端会再次校验。 - 不要调用普通 `/generate` 实现破损补码,普通生成会增加袋码数量且不保存替换关系。 - 新袋码不复用旧袋码的 code 或袋序号,1200 袋替换 2 袋时新码通常从 1201、1202 继续生成。 - 已作废旧码仍可追溯,但不可绑定托盘、不可再次替换、不可打印。 - 原托盘已作废或不存在时,已绑定袋码的替换会失败,需要先处理托盘状态。 - 批次追溯中的袋码对象包含状态字段;展示作废标识时不要只显示 `code` 字符串。 - 替换接口返回的新码初始未打印,导出成功后才变为“已导出印刷”。