From 8891a55c0d3d9ed36fe6d2865bf734c6acdad67b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BB=ABUmbrella?= <2539020564@qq.com> Date: Wed, 4 Mar 2026 17:00:19 +0800 Subject: [PATCH] =?UTF-8?q?=E7=AE=B1=E6=9D=A1=E7=A0=81=E4=B8=8E=E8=AE=A2?= =?UTF-8?q?=E5=8D=95=E5=8F=B7=E7=9A=84=E7=BB=91=E5=AE=9A=E4=B8=8E=E8=A7=A3?= =?UTF-8?q?=E7=BB=91=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../logistics/service/IOrderBoxService.java | 40 +++- .../service/impl/IOrderBoxServiceImpl.java | 221 ++++++++++++++---- 2 files changed, 213 insertions(+), 48 deletions(-) diff --git a/blade-service/blade-desk/src/main/java/org/springblade/desk/logistics/service/IOrderBoxService.java b/blade-service/blade-desk/src/main/java/org/springblade/desk/logistics/service/IOrderBoxService.java index e20a4c50..2987a5ce 100644 --- a/blade-service/blade-desk/src/main/java/org/springblade/desk/logistics/service/IOrderBoxService.java +++ b/blade-service/blade-desk/src/main/java/org/springblade/desk/logistics/service/IOrderBoxService.java @@ -5,11 +5,47 @@ import org.springblade.desk.logistics.pojo.entity.BoxBinding; import java.math.BigDecimal; +/** + * 订单箱管理服务接口 + * 功能说明:处理订单与箱条码的绑定、解绑,以及订单配件重量维护等核心物流业务逻辑 + * + * @author (可补充作者名) + * @date (可补充创建日期) + */ public interface IOrderBoxService { + + /** + * 维护订单配件重量 + * 功能:根据卡号(cardNo)更新对应订单配件的实际重量信息 + * + * @param cardNo 配件卡号/标识,唯一标识需要更新重量的订单配件 + * @param actualWeight 实际称重的重量值,精度由业务场景决定(如千克/克) + * @return R 通用返回结果 + * - 成功:R.success(),可携带更新成功的提示信息或数据 + * - 失败:R.fail(),携带具体的失败原因(如卡号不存在、重量非法等) + */ R upholdOrderPartWeight(String cardNo, BigDecimal actualWeight); + /** + * 箱条码与订单绑定 + * 功能:将指定的箱条码与订单列表建立绑定关系,核心物流绑定业务 + * + * @param boxBinding 箱绑定实体类,包含箱条码、订单ID列表、任务ID等核心绑定信息 + * @return R 通用返回结果 + * - 成功:R.success(),携带绑定成功的提示或绑定记录数 + * - 失败:R.fail(),携带失败原因(如箱条码不存在、订单已绑定、唯一约束冲突等) + */ R boxBinding(BoxBinding boxBinding); - R boxOrderUnbind(String boxBarcode); + /** + * 箱条码与订单解绑 + * 功能:解除指定箱条码对应的所有订单绑定关系,支持箱的重新绑定或回收 + * + * @param boxBarcode 箱条码,唯一标识需要解绑的箱子 + * @return R 通用返回结果 + * - 成功:R.success(),携带解绑成功的提示或解绑的订单数量 + * - 失败:R.fail(),携带失败原因(如箱条码不存在、解绑数据异常等) + */ + R boxOrderUnbind(String boxBarcode); -} +} \ No newline at end of file diff --git a/blade-service/blade-desk/src/main/java/org/springblade/desk/logistics/service/impl/IOrderBoxServiceImpl.java b/blade-service/blade-desk/src/main/java/org/springblade/desk/logistics/service/impl/IOrderBoxServiceImpl.java index 1872ce62..9fb736b8 100644 --- a/blade-service/blade-desk/src/main/java/org/springblade/desk/logistics/service/impl/IOrderBoxServiceImpl.java +++ b/blade-service/blade-desk/src/main/java/org/springblade/desk/logistics/service/impl/IOrderBoxServiceImpl.java @@ -20,7 +20,8 @@ import static org.springblade.desk.logistics.pojo.entity.Station.STATUS_OCCUPIED import static org.springblade.desk.logistics.pojo.entity.Task.STATUS_FINISHED; /** - * 订单箱子 控制器 + * 订单箱业务实现类 + * 核心职责:处理订单与箱条码的绑定/解绑、订单配件重量维护、任务/站点/库位状态管理等物流核心业务 * * @author qyl * @since 2026-01-08 @@ -28,26 +29,54 @@ import static org.springblade.desk.logistics.pojo.entity.Task.STATUS_FINISHED; @Service @Slf4j public class IOrderBoxServiceImpl implements IOrderBoxService { + /** + * 工单服务:处理工单重量、订单信息查询等 + */ private final IYieldOrderService iYieldOrderService; + /** + * 任务服务:处理箱绑定任务的创建、状态更新、删除等 + */ private final ITaskService iTaskService; + /** + * 订单绑定服务:处理订单与任务的绑定关系维护 + */ private final IOrderBindService iOrderBindService; + /** + * 站点服务:处理站点状态(占用/空闲)管理 + */ private final IStationService iStationService; + /** + * 库位服务:处理库位状态(占用/空闲)管理 + */ private final ILocationService iLocationService; - - + /** + * 任务运行中状态集合:包含任务从启动到待入库的所有中间状态 + * 用于判断箱条码是否存在未完成的运行任务 + */ private static final Set RUNNING_STATUSES = new HashSet<>(); + static { - RUNNING_STATUSES.add(Task.STATUS_START); - RUNNING_STATUSES.add(Task.STATUS_CONVEYOR_START); - RUNNING_STATUSES.add(Task.STATUS_CONVEYOR_END); - RUNNING_STATUSES.add(Task.STATUS_STATION); - RUNNING_STATUSES.add(Task.STATUS_LOCATION); - RUNNING_STATUSES.add(Task.STATUS_WAITING); - RUNNING_STATUSES.add(Task.STATUS_STATION_RECEIVE); - RUNNING_STATUSES.add(Task.STATUS_BACK_TO_STORAGE); + // 初始化运行中任务状态 + RUNNING_STATUSES.add(Task.STATUS_START); // 任务启动 + RUNNING_STATUSES.add(Task.STATUS_CONVEYOR_START); // 输送机启动 + RUNNING_STATUSES.add(Task.STATUS_CONVEYOR_END); // 输送机结束 + RUNNING_STATUSES.add(Task.STATUS_STATION); // 站点状态 + RUNNING_STATUSES.add(Task.STATUS_LOCATION); // 库位状态 + RUNNING_STATUSES.add(Task.STATUS_WAITING); // 等待状态 + RUNNING_STATUSES.add(Task.STATUS_STATION_RECEIVE);// 站点接收 + RUNNING_STATUSES.add(Task.STATUS_BACK_TO_STORAGE);// 返库状态 } + /** + * 构造器注入依赖服务(替代@Autowired,符合Spring最佳实践) + * + * @param iYieldOrderService 工单服务 + * @param iTaskService 任务服务 + * @param iOrderBindService 订单绑定服务 + * @param iStationService 站点服务 + * @param iLocationService 库位服务 + */ public IOrderBoxServiceImpl(IYieldOrderService iYieldOrderService, ITaskService iTaskService, IOrderBindService iOrderBindService, IStationService iStationService, ILocationService iLocationService) { this.iYieldOrderService = iYieldOrderService; this.iTaskService = iTaskService; @@ -56,18 +85,38 @@ public class IOrderBoxServiceImpl implements IOrderBoxService { this.iLocationService = iLocationService; } + /** + * 维护订单配件实际重量 + * 功能:根据流程卡号更新对应工单的实际称重数据 + * + * @param cardNo 流程卡号(唯一标识工单) + * @param actualWeight 实际称重值(单位:业务约定,如千克) + * @return R 操作结果 + * - 成功:R.success() + * - 失败:R.fail(),携带具体失败原因 + */ @Override public R upholdOrderPartWeight(String cardNo, BigDecimal actualWeight) { log.info("接收到实际重量:{},对应的流程卡号:{}",actualWeight,cardNo); - //获取流程卡号 + // 根据流程卡号查询工单(按更新时间倒序,取最新记录) List list = iYieldOrderService.list(new QueryWrapper().eq("CARD_NO", cardNo).orderByDesc("UPDATE_TIME")); - //修改重量 + // 更新最新工单的实际重量 list.get(0).setActualWeighing(actualWeight); + // 执行更新并返回结果 return iYieldOrderService.updateById(list.get(0))? R.success():R.fail("实际称重维护:卡号维护失败"); } + /** + * 箱条码与订单绑定核心方法 + * 流程:参数校验 → 任务状态校验 → 订单绑定状态校验 → 站点/库位分配 → 任务创建 → 订单绑定 + * + * @param boxBinding 箱绑定参数(包含箱条码、订单ID列表、工位ID等) + * @return R 绑定结果 + * - 成功:R.success() + * - 失败:R.fail(),携带具体失败原因(如参数为空、订单已绑定、重量超限等) + */ @Override public R boxBinding(BoxBinding boxBinding) { log.info("接收到箱绑定实际参数:{}", boxBinding); @@ -79,78 +128,109 @@ public class IOrderBoxServiceImpl implements IOrderBoxService { } String boxBarcode = boxBinding.getBoxBarcode(); - // 2. 校验箱条码是否存在运行中的任务(封装为独立方法,提升可读性) + // 2. 校验箱条码是否存在运行中的任务(避免重复绑定) R taskCheckResult = checkBoxBarcodeRunningTask(boxBarcode); if (!taskCheckResult.isSuccess()) { return taskCheckResult; } - // 3. 校验订单是否已绑定(封装为独立方法) + // 3. 校验订单是否已绑定(避免订单重复绑定) R orderCheckResult = checkOrderIdBoundStatus(boxBinding.getOrderIdList()); if (!orderCheckResult.isSuccess()) { return orderCheckResult; } + + // 4. 构建任务基础信息 Long wcId = boxBinding.getWcId(); Task task = new Task(); - task.setBoxBarcode(boxBinding.getBoxBarcode()); + task.setBoxBarcode(boxBarcode); task.setWcId(wcId); - // 4. 获取当前站点位置 + + // 5. 获取当前可用的站点/库位并分配 R location = getSiteLocation(task); if (!location.isSuccess()) { return location; } - // 5. 保存记录到数据库中 + + // 6. 完善任务信息并保存 task= (Task) location.getData(); - task.setTaskStatus(Task.STATUS_START); - task.setCreateTime(new Date()); + task.setTaskStatus(Task.STATUS_START); // 设置任务初始状态为启动 + task.setCreateTime(new Date()); // 设置任务创建时间 + + // 7. 计算订单总重量(无订单则重量为0) boolean orderBool = boxBinding.getOrderIdList() == null || boxBinding.getOrderIdList().size() == 0; if (orderBool) { task.setWeight(new BigDecimal(0)); }else { task.setWeight(getWeightByOrderIdList(boxBinding.getOrderIdList())); } + + // 8. 重量校验(小于50kg才允许绑定,避免超重) if (task.getWeight().compareTo(BigDecimal.valueOf(50)) < 0) { - R.fail("箱条码绑定的订单重量过重,请重新进行绑定"); + return R.fail("箱条码绑定的订单重量过重,请重新进行绑定"); } + + // 9. 保存任务记录 if (!iTaskService.save(task)) { - R.fail("保存绑定箱条码异常"); + return R.fail("保存绑定箱条码异常"); } + + // 10. 无订单则直接返回成功,有订单则执行订单绑定 if (orderBool) { return R.success(); }else { return saveOrderBindingList(task.getId(),boxBinding.getOrderIdList()); - } } + /** + * 箱条码与订单解绑 + * 流程:参数校验 → 查询运行中任务 → 重置站点/库位状态 → 解绑订单 → 结束任务 + * + * @param boxBarcode 箱条码 + * @return R 解绑结果 + * - 成功:R.success() + * - 失败:R.fail(),携带具体失败原因(如箱条码为空) + */ @Override public R boxOrderUnbind(String boxBarcode) { + // 1. 参数非空校验 if (boxBarcode.isEmpty()) { log.warn("箱绑定参数为空或箱条码缺失"); return R.fail("箱条码不能为空"); } - // 1. 根据箱条码查询数据 + + // 2. 查询该箱条码对应的运行中任务 List taskList = iTaskService.list( new LambdaQueryWrapper().eq(Task::getBoxBarcode, boxBarcode).in(Task::getTaskStatus,RUNNING_STATUSES) ); + + // 3. 遍历任务,重置站点/库位状态 + 解绑订单 + 结束任务 for (Task task : taskList) { + // 3.1 重置站点状态为占用(释放站点) if (task.getStationId()!=null&&task.getStationId()!=0) { Station station = iStationService.getById(task.getStationId()); station.setStationStatus(STATUS_OCCUPIED); iStationService.updateById(station); } + + // 3.2 重置库位状态为占用(释放库位) if (task.getLocationId()!=null&&task.getLocationId()!=0) { Location location = iLocationService.getById(task.getLocationId()); location.setLocationStatus(STATUS_OCCUPIED); iLocationService.updateById(location); } + + // 3.3 解绑订单(更新绑定状态为未绑定) List orderBindList = iOrderBindService.list(new LambdaQueryWrapper().eq(OrderBind::getTaskId, task.getId())); - if (orderBindList!=null&&orderBindList.size()!=0) { + if (!CollectionUtils.isEmpty(orderBindList)) { for (OrderBind orderBind : orderBindList) { orderBind.setBindingStatus(STATUS_UNBINDED); iOrderBindService.updateById(orderBind); } } + + // 3.4 结束任务(更新任务状态为已完成) task.setTaskStatus(STATUS_FINISHED); iTaskService.updateById(task); } @@ -158,56 +238,101 @@ public class IOrderBoxServiceImpl implements IOrderBoxService { return R.success(); } + /** + * 批量保存订单与任务的绑定关系 + * 核心:创建订单绑定记录并批量插入,插入失败则回滚任务(删除已创建的任务) + * + * @param taskId 任务ID(关联箱条码) + * @param orderIdList 订单ID列表 + * @return R 保存结果 + * - 成功:R.success() + * - 失败:R.fail(),删除任务并返回异常信息 + */ private R saveOrderBindingList(Long taskId, ArrayList orderIdList) { ArrayList orderBindList = new ArrayList<>(); + // 构建订单绑定记录 for (Long orderId : orderIdList) { OrderBind orderBind = new OrderBind(); - orderBind.setBindingStatus(OrderBind.STATUS_BOUND); - orderBind.setTaskId(taskId); - orderBind.setOrderId(orderId); + orderBind.setBindingStatus(OrderBind.STATUS_BOUND); // 绑定状态:已绑定 + orderBind.setTaskId(taskId); // 关联任务ID + orderBind.setOrderId(orderId); // 关联订单ID orderBindList.add(orderBind); } + + // 批量插入绑定记录,失败则删除任务 if (iOrderBindService.saveBatch(orderBindList)) { return R.success(); }else { - iTaskService.removeById(taskId); + iTaskService.removeById(taskId); // 回滚:删除已创建的任务 return R.fail("订单绑定箱条码异常"); } } + /** + * 根据订单ID列表计算订单总重量 + * 逻辑:查询订单对应的工单 → 累加实际称重值(过滤null值) + * + * @param orderIdList 订单ID列表 + * @return BigDecimal 订单总重量(单位:业务约定,如千克) + */ private BigDecimal getWeightByOrderIdList(ArrayList orderIdList) { + // 查询订单对应的工单列表 List orderList = iYieldOrderService.list(new LambdaQueryWrapper().in(YieldOrder::getId, orderIdList)); + + // 流式累加实际称重值(过滤null,初始值为0) return orderList.stream() .map(YieldOrder::getActualWeighing) .filter(Objects::nonNull) .reduce(BigDecimal.ZERO, BigDecimal::add); } + /** + * 获取当前工位可用的站点/库位并分配 + * 优先级:先分配站点 → 站点无可用则分配库位 → 均无则返回失败 + * + * @param task 任务对象(包含工位ID) + * @return R 分配结果 + * - 成功:R.data(task),任务对象已填充站点/库位ID + * - 失败:R.fail(),提示库位繁忙 + */ private R getSiteLocation(Task task) { + // 1. 查询当前工位可用的站点(状态为占用的站点) List list = iStationService.list(new LambdaQueryWrapper().eq(Station::getWcId, task.getWcId()).eq(Station::getStationStatus, STATUS_OCCUPIED)); - if (list!=null&&list.size()!=0) { - task.setStationId(list.get(0).getId()); + if (!CollectionUtils.isEmpty(list)) { + task.setStationId(list.get(0).getId()); // 分配第一个可用站点 + // 更新站点状态为预占用(锁定站点) Station station = list.get(0); station.setStationStatus(Station.PRE_STATUS_OCCUPIED); iStationService.updateById(station); return R.data(task); } + + // 2. 站点无可用则查询可用库位(状态为占用的库位) List locationList = iLocationService.list(new LambdaQueryWrapper().eq(Location::getLocationStatus, Location.STATUS_OCCUPIED)); - if (locationList!=null&&locationList.size()!=0) { - task.setLocationId(locationList.get(0).getId()); + if (!CollectionUtils.isEmpty(locationList)) { + task.setLocationId(locationList.get(0).getId()); // 分配第一个可用库位 + // 更新库位状态为空闲(锁定库位) Location location = locationList.get(0); location.setLocationStatus(Location.STATUS_FREE); iLocationService.updateById(location); return R.data(task); } + + // 3. 站点/库位均无可用 return R.fail("当前班次库位繁忙,请空闲后再试"); } /** * 校验箱条码是否存在运行中的任务 + * 核心:避免同一箱条码同时存在多个未完成的绑定任务 + * + * @param boxBarcode 箱条码 + * @return R 校验结果 + * - 成功:R.success()(无运行中任务) + * - 失败:R.fail()(存在运行中任务) */ private R checkBoxBarcodeRunningTask(String boxBarcode) { - // 查询箱条码对应的任务(MyBatis-Plus的list方法返回空列表,非null,无需判null) + // 查询箱条码对应的所有任务 List taskList = iTaskService.list( new LambdaQueryWrapper().eq(Task::getBoxBarcode, boxBarcode) ); @@ -217,10 +342,10 @@ public class IOrderBoxServiceImpl implements IOrderBoxService { return R.success(); } - // 判断是否有运行中的任务(流式操作简化,无冗余map) + // 判断是否有运行中的任务(流式操作,简化逻辑) boolean hasRunningTask = taskList.stream() .map(Task::getTaskStatus) - .filter(status -> status != null) // 过滤null状态,避免NPE + .filter(status -> status != null) // 过滤null状态,避免空指针 .anyMatch(RUNNING_STATUSES::contains); if (hasRunningTask) { @@ -230,9 +355,14 @@ public class IOrderBoxServiceImpl implements IOrderBoxService { return R.success(); } - /** - * 校验订单号是否已绑定(核心优化:减少遍历次数,流式收集数据) + * 校验订单是否已绑定 + * 核心:避免同一订单被重复绑定到不同箱条码 + * + * @param orderIdList 订单ID列表 + * @return R 校验结果 + * - 成功:R.success()(无已绑定订单) + * - 失败:R.fail()(存在已绑定订单,返回对应流程卡号) */ private R checkOrderIdBoundStatus(List orderIdList) { // 无订单号,直接通过校验 @@ -240,7 +370,7 @@ public class IOrderBoxServiceImpl implements IOrderBoxService { return R.success(); } - // 查询订单绑定记录 + // 查询订单对应的绑定记录 List orderBindList = iOrderBindService.list( new LambdaQueryWrapper().in(OrderBind::getOrderId, orderIdList) ); @@ -248,12 +378,12 @@ public class IOrderBoxServiceImpl implements IOrderBoxService { return R.success(); } - // 步骤1:过滤出已绑定的订单ID(流式收集,替代循环) + // 过滤出已绑定的订单ID(去重,避免重复提示) List boundOrderIds = orderBindList.stream() .filter(orderBind -> OrderBind.STATUS_BOUND.equals(orderBind.getBindingStatus())) .map(OrderBind::getOrderId) - .filter(orderId -> orderId != null) // 过滤null订单ID - .distinct() // 去重,避免重复查询 + .filter(orderId -> orderId != null) + .distinct() .collect(Collectors.toList()); // 无已绑定订单,直接通过 @@ -261,17 +391,16 @@ public class IOrderBoxServiceImpl implements IOrderBoxService { return R.success(); } - // 步骤2:查询已绑定订单对应的流程卡号(流式收集,替代循环) + // 查询已绑定订单对应的流程卡号,构造异常信息 List boundCardNos = iYieldOrderService.list( new LambdaQueryWrapper().in(YieldOrder::getId, boundOrderIds) ).stream() .map(YieldOrder::getCardNo) - .filter(cardNo -> cardNo != null) // 过滤null卡号 + .filter(cardNo -> cardNo != null) .collect(Collectors.toList()); - // 构造异常信息并返回 String errorMsg = String.format("以下流程卡号处于绑定状态,绑定异常:{%s}", String.join(",", boundCardNos)); log.warn(errorMsg); return R.fail(errorMsg); } -} +} \ No newline at end of file