14 KiB
空木箱出入库实现计划
面向 AI 代理的工作者: 必需子技能:使用 superpowers:subagent-driven-development(推荐)或 superpowers:executing-plans 逐任务实现此计划。步骤使用复选框(
- [ ])语法来跟踪进度。
目标: 实现基于木箱规格、仓位优先级和任务回调的空木箱入库与先进先出出库能力,并向其他模块开放创建接口。
架构: WMS API 模块定义跨模块契约,WMS Server 的 EmptyBoxService 统一负责选位、条件抢锁、木箱状态和任务生命周期;EmptyBoxTask 只将完成、取消事件转发给 Service。候选排与候选木箱由 Mapper XML 限量筛选,优先级和并发重试由 Java 控制。
技术栈: Java 17、Spring Boot、OpenFeign、MyBatis/MyBatis-Plus、MySQL、现有 TransportTaskApi 与 CodeGenApi。
文件结构
- 创建
sql/mysql/wms_empty_box.sql:木箱规格表、木箱实例状态字段、锁字典和BOX_CODE编码规则变更。 - 创建
nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/api/emptybox/EmptyBoxApi.java:跨模块创建接口。 - 创建
nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/api/emptybox/dto/*.java:入库、出库请求与响应对象。 - 创建
nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/dataobject/boxtype/BoxTypeDO.java:木箱规格数据对象。 - 创建
nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/dataobject/boxinfo/BoxInfoDO.java:木箱实例数据对象。 - 创建
nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/mysql/boxtype/BoxTypeMapper.java与对应 XML:按规格编码查询。 - 扩展
nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/mysql/boxinfo/BoxInfoMapper.java与对应 XML:插入、状态更新、先进先出候选查询。 - 扩展
StrucAttrMapper.java与StrucAttrMapper.xml:同规格候选排、空排、条件锁定、任务绑定、完成和取消。 - 创建
nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/emptybox/EmptyBoxService.java和EmptyBoxServiceImpl.java:领域业务。 - 创建
nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/api/emptybox/EmptyBoxApiImpl.java:跨模块接口实现。 - 创建
nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/emptybox/EmptyBoxController.java:管理端接口。 - 创建
nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/task/EmptyBoxTask.java:任务事件转发。 - 修改木箱查询 VO/XML:展示状态、入库时间和出库时间。
任务 1:数据库与枚举基础
文件:
-
创建:
sql/mysql/wms_empty_box.sql -
创建:
nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/enums/emptybox/EmptyBoxStatusEnum.java -
步骤 1:编写幂等数据库脚本
脚本创建 wms_boxtype,向 wmw_boxinfo 增加 status varchar(1) 和 out_time varchar(25),向 wms_lock_type 增加值 8/9,并幂等创建 BOX_CODE 编码规则。material_code 建唯一索引;wmw_boxinfo 增加 (material_code, status, insert_time) 普通索引以支持 FIFO。
- 步骤 2:增加木箱状态枚举
public enum EmptyBoxStatusEnum {
PENDING_INBOUND("0"), IN_STOCK("1"), OUTBOUND("2");
private final String code;
}
- 步骤 3:校验脚本和 Java 格式
运行:git diff --check -- sql/mysql/wms_empty_box.sql nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/enums/emptybox/EmptyBoxStatusEnum.java
预期:无输出,退出码 0。
任务 2:跨模块 API 契约
文件:
-
创建:
nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/api/emptybox/EmptyBoxApi.java -
创建:
nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/api/emptybox/dto/EmptyBoxInboundCreateReq.java -
创建:
nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/api/emptybox/dto/EmptyBoxInboundCreateResp.java -
创建:
nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/api/emptybox/dto/EmptyBoxOutboundCreateReq.java -
创建:
nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/api/emptybox/dto/EmptyBoxOutboundCreateResp.java -
步骤 1:定义带注解校验的请求对象
入库请求包含必填 deviceCode、materialCode;出库请求包含必填 materialCode。
- 步骤 2:定义响应对象
入库响应包含 taskId、boxNo、targetStructCode;出库响应包含 taskId、boxNo、destinationPoint。
- 步骤 3:定义 Feign API
@PostMapping(PREFIX + "/createInbound")
CommonResult<EmptyBoxInboundCreateResp> createInbound(
@Valid @RequestBody EmptyBoxInboundCreateReq req);
@PostMapping(PREFIX + "/createOutbound")
CommonResult<EmptyBoxOutboundCreateResp> createOutbound(
@Valid @RequestBody EmptyBoxOutboundCreateReq req);
- 步骤 4:编译 API 模块
运行:./mvnw -pl nl-module-wms/nl-module-wms-api -am -DskipTests compile
预期:BUILD SUCCESS。
任务 3:规格与木箱持久化
文件:
-
创建:
nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/dataobject/boxtype/BoxTypeDO.java -
创建:
nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/dataobject/boxinfo/BoxInfoDO.java -
创建:
nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/mysql/boxtype/BoxTypeMapper.java -
创建:
nl-module-wms/nl-module-wms-server/src/main/resources/mapper/boxtype/BoxTypeMapper.xml -
修改:
nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/mysql/boxinfo/BoxInfoMapper.java -
修改:
nl-module-wms/nl-module-wms-server/src/main/resources/mapper/boxinfo/BoxInfoMapper.xml -
步骤 1:映射规格表和木箱实例表
BoxTypeDO 精确对应 wms_boxtype;BoxInfoDO 对应 wmw_boxinfo,包含新增状态和出库时间。
- 步骤 2:在 XML 中实现规格查询
SELECT id, material_code, material_name, lash_num,
box_length, box_width, box_height
FROM wms_boxtype
WHERE material_code = #{materialCode}
- 步骤 3:在 BoxInfo XML 实现实例写入和状态迁移
增加 insertPendingInbound、completeInbound、deletePendingInbound、completeOutbound;完成更新均同时校验原状态,避免重复回调静默成功。
- 步骤 4:实现 FIFO 候选查询
关联 wms_structattr.storagevehicle_code = wmw_boxinfo.box_no,筛选在库、仓位可用且未锁定的数据,按 insert_time ASC, box_id ASC 排序并限制候选数量。
- 步骤 5:检查 XML 可解析性
运行:xmllint --noout nl-module-wms/nl-module-wms-server/src/main/resources/mapper/boxtype/BoxTypeMapper.xml nl-module-wms/nl-module-wms-server/src/main/resources/mapper/boxinfo/BoxInfoMapper.xml
预期:无输出,退出码 0。
任务 4:仓位候选与条件锁
文件:
-
创建:
nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/dataobject/structAttr/EmptyBoxRowCandidate.java -
修改:
nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/mysql/structAttr/StrucAttrMapper.java -
修改:
nl-module-wms/nl-module-wms-server/src/main/resources/mapper/structAttr/StrucAttrMapper.xml -
步骤 1:增加同规格候选排查询
按仓库、库区、块、排、层分组。候选排必须至少存在一个同规格在库空木箱,不存在其他规格木箱或组盘载具,并至少存在一个未锁定空仓位;结果限制 32 排。
- 步骤 2:增加整排为空候选查询
复用现有排维度,要求整排载具为空且至少一个可用未锁定仓位;结果限制 32 排。
- 步骤 3:增加入库条件锁和任务绑定
以 struct_id、未锁定、载具为空为条件将锁类型改为 8 并写预占码;创建任务后仅在预占码匹配时替换为任务 ID。
- 步骤 4:增加出库条件锁
以 struct_id、未锁定、指定木箱编码仍绑定、木箱仍为在库为条件改为锁类型 9 并写预占码。
- 步骤 5:增加完成和取消 SQL
入库完成绑定木箱并解锁;出库完成清空载具并解锁;取消只恢复锁和任务码。所有更新按 task_code 与锁类型精确匹配。
- 步骤 6:检查 Mapper XML
运行:xmllint --noout nl-module-wms/nl-module-wms-server/src/main/resources/mapper/structAttr/StrucAttrMapper.xml
预期:无输出,退出码 0。
任务 5:空木箱领域服务
文件:
-
创建:
nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/emptybox/EmptyBoxService.java -
创建:
nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/emptybox/EmptyBoxServiceImpl.java -
步骤 1:实现入库选位和预占
先遍历同规格候选排,再遍历空排;每排查询一个空位并条件锁定。全部失败时抛出“未找到符合条件的空木箱入库仓位”。
- 步骤 2:生成木箱编码并创建待入库记录
调用 CodeGenApi.generate(new CodeGenerateReqDTO().setRuleCode("BOX_CODE")),检查远程调用结果;复制 wms_boxtype 规格属性到 wmw_boxinfo,状态设为待入库。
- 步骤 3:创建并下发入库任务
任务起点为设备号,终点为仓位,handleCode 为 EMPTYBOXTASK,bizType 为 EMPTY_BOX_IN。绑定任务 ID 后调用任务下发回滚接口。
- 步骤 4:实现 FIFO 出库预占
遍历 FIFO 候选并条件抢锁;全部失败时抛出“没有可出库的该规格空木箱”。
- 步骤 5:实现临时出库点负载选择
定义 ZXQ_01、ZXQ_02 常量,分别计算未完成任务数并加点位库存数。增加中文扩展注释,说明后续由 LMS 一次性返回出库点及占用库存;当前库存数查询方法返回 WMS 可获取的点位占用数量。相同负载固定选择 ZXQ_01。
- 步骤 6:创建并下发出库任务
任务起点为仓位,终点为选定出库点,bizType 为 EMPTY_BOX_OUT;响应返回任务 ID、木箱号和目的点。
- 步骤 7:实现完成与取消事务
完成时先根据任务锁确定方向:入库绑定仓位并把木箱改为在库,出库清空仓位并把木箱改为已出库;取消入库时删除待入库记录,取消出库时保留在库记录。每项更新必须恰好影响一行。
任务 6:接口与任务回调接入
文件:
-
创建:
nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/api/emptybox/EmptyBoxApiImpl.java -
创建:
nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/emptybox/EmptyBoxController.java -
创建:
nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/task/EmptyBoxTask.java -
步骤 1:实现内部 API
使用 @Resource 注入 EmptyBoxService,将创建结果包装为 CommonResult.success。
- 步骤 2:实现管理端接口
提供 /wms/empty-box/createInbound 和 /wms/empty-box/createOutbound,返回 CommonResult,请求使用 @Valid。
- 步骤 3:实现任务处理器
@Component("EMPTYBOXTASK")
public class EmptyBoxTask extends AbstractTask {
@Resource
private EmptyBoxService emptyBoxService;
@Override
public void doHandleFinish(TaskExecuteDTO dto) {
emptyBoxService.completeTask(dto.getTaskId());
}
@Override
public void doHandleCancel(TaskExecuteDTO dto) {
emptyBoxService.cancelTask(dto.getTaskId());
}
}
- 步骤 4:确认任务工厂自动发现处理器
检查 WmsTaskStatusChangeConsumer 仍通过现有 TaskFactory 按 handleCode 分发,无需加入业务分支。
任务 7:木箱查询展示状态
文件:
-
修改:
nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/boxinfo/vo/BoxInfoRespVO.java -
修改:
nl-module-wms/nl-module-wms-server/src/main/resources/mapper/boxinfo/BoxInfoMapper.xml -
修改:
nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/api/wms/boxinfo/model.ts -
修改:
nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/boxinfo/data.ts -
步骤 1:后端返回状态和出库时间
分页 SQL 增加 status、out_time,响应对象增加对应字段。
- 步骤 2:前端增加只读列
列表展示状态、入库时间、出库时间;状态使用明确文本“待入库/在库/已出库”。
- 步骤 3:运行前端静态检查
运行项目现有 ESLint 命令,仅检查本次修改的木箱信息文件。
预期:退出码 0。
任务 8:整体验证
文件:
-
检查本计划涉及的全部文件。
-
步骤 1:检查注解 SQL 和格式问题
运行:rg -n "@(Select|Update|Insert|Delete)" nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/{service/emptybox,dal/mysql/boxtype,dal/mysql/boxinfo,dal/mysql/structAttr}
预期:没有本功能新增的注解 SQL。
- 步骤 2:编译 WMS 模块
运行:./mvnw -pl nl-module-wms/nl-module-wms-server -am -DskipTests clean compile
预期:BUILD SUCCESS。
- 步骤 3:检查 XML 与差异
运行:xmllint --noout nl-module-wms/nl-module-wms-server/src/main/resources/mapper/{boxtype/BoxTypeMapper.xml,boxinfo/BoxInfoMapper.xml,structAttr/StrucAttrMapper.xml}
运行:git diff --check
预期:两条命令均退出码 0。
- 步骤 4:人工核对关键路径
确认同规格同排优先、空排回退、FIFO、条件抢锁、任务 ID 绑定、完成和取消状态迁移,以及出库响应目的点全部在代码中形成闭环。