回库代码编写

liweidong
绫Umbrella 1 month ago
parent c2433c360b
commit d893b06989
  1. 21
      blade-service-api/blade-desk-api/src/main/java/org/springblade/desk/logistics/pojo/dto/ReturnToWarehouseDto.java
  2. 4
      blade-service-api/blade-desk-api/src/main/java/org/springblade/desk/logistics/pojo/entity/Station.java
  3. 12
      blade-service-api/blade-desk-api/src/main/java/org/springblade/desk/logistics/pojo/vo/BsWorkCenterVO.java
  4. 7
      blade-service/blade-desk/src/main/java/org/springblade/desk/dashboard/mapper/BsWorkCenterMapper.java
  5. 16
      blade-service/blade-desk/src/main/java/org/springblade/desk/dashboard/mapper/BsWorkCenterMapper.xml
  6. 2
      blade-service/blade-desk/src/main/java/org/springblade/desk/dashboard/service/IBsWorkCenterService.java
  7. 5
      blade-service/blade-desk/src/main/java/org/springblade/desk/dashboard/service/impl/BsWorkCenterServiceImpl.java
  8. 21
      blade-service/blade-desk/src/main/java/org/springblade/desk/logistics/controller/OrderBoxController.java
  9. 17
      blade-service/blade-desk/src/main/java/org/springblade/desk/logistics/service/IOrderBoxService.java
  10. 18
      blade-service/blade-desk/src/main/java/org/springblade/desk/logistics/service/IPipelineOrderBoxService.java
  11. 291
      blade-service/blade-desk/src/main/java/org/springblade/desk/logistics/service/impl/IOrderBoxServiceImpl.java
  12. 123
      blade-service/blade-desk/src/main/java/org/springblade/desk/logistics/service/impl/IPipelineOrderBoxServiceImpl.java
  13. 6
      blade-service/blade-desk/src/main/java/org/springblade/desk/logistics/service/impl/PipelineServiceImpl.java
  14. 36
      blade-service/blade-desk/src/main/java/org/springblade/desk/logistics/utils/CollectionCheckUtil.java

@ -0,0 +1,21 @@
package org.springblade.desk.logistics.pojo.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
@Data
@Schema(description = "出库操作")
public class ReturnToWarehouseDto {
//箱条码
@NotBlank(message = "箱条码不能为空")
private String boxBarcode;
//站点id
@NotBlank(message = "站点编号不能为空")
private String stationCode;
//终点id
private Long endLocationId ;
//终点名称
private String endName;
}

@ -46,6 +46,10 @@ public class Station extends BaseEntity {
* 放货输送线 站点类型常量
*/
public static final String DROPOFF_CONVEYOR_LINE = "1001";
/**
* 放货输送线 站点类型常量
*/
public static final String DROPOFF_CONVEYOR_LINE_NAME = "输送线终点";
/**

@ -0,0 +1,12 @@
package org.springblade.desk.logistics.pojo.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "wslist返回对象")
public class BsWorkCenterVO {
private Long id;
private String wcName;
}

@ -63,4 +63,11 @@ public interface BsWorkCenterMapper extends BaseMapper<BsWorkCenterEntity> {
List<BsWorkCenterVO> getList();
BsWorkCenterEntity selectBsWorkCenterByWcCode(@Param("wcCode")String wcCode);
/**
* 根据ID集合查询工作中心
* @param ids ID集合
* @return 工作中心列表
*/
List<org.springblade.desk.logistics.pojo.vo.BsWorkCenterVO> selectByIds(@Param("ids") List<Long> ids);
}

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.springblade.desk.dashboard.mapper.BsWorkCenterMapper">
<!-- 根据ID集合查询工作中心 -->
<select id="selectByIds" parameterType="java.util.List" resultType="org.springblade.desk.logistics.pojo.vo.BsWorkCenterVO">
SELECT id, WC_NAME
FROM BS_WORK_CENTER
WHERE id IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select>
</mapper>

@ -70,4 +70,6 @@ public interface IBsWorkCenterService extends BaseService<BsWorkCenterEntity> {
* @return
*/
BsWorkCenterEntity selectBsWorkCenterByWcCode(String wcCode);
List<org.springblade.desk.logistics.pojo.vo.BsWorkCenterVO> getByIds(List<Long> list);
}

@ -74,4 +74,9 @@ public class BsWorkCenterServiceImpl extends BaseServiceImpl<BsWorkCenterMapper,
return workCenterMapper.selectBsWorkCenterByWcCode(wcCode);
}
@Override
public List<org.springblade.desk.logistics.pojo.vo.BsWorkCenterVO> getByIds(List<Long> list) {
return workCenterMapper.selectByIds(list);
}
}

@ -8,6 +8,7 @@ import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
import jodd.util.StringUtil;
import lombok.AllArgsConstructor;
import org.springblade.core.boot.ctrl.BladeController;
import org.springblade.core.mp.support.Condition;
@ -18,6 +19,7 @@ import org.springblade.desk.dashboard.service.IBsWorkCenterService;
import org.springblade.desk.device.pojo.entity.EquipmentEntity;
import org.springblade.desk.device.pojo.vo.EquipmentVO;
import org.springblade.desk.logistics.pojo.dto.BoxBindingDto;
import org.springblade.desk.logistics.pojo.dto.ReturnToWarehouseDto;
import org.springblade.desk.logistics.pojo.dto.TaskDto;
import org.springblade.desk.logistics.pojo.vo.TaskVO;
import org.springblade.desk.logistics.service.IOrderBoxService;
@ -110,5 +112,24 @@ public class OrderBoxController extends BladeController {
IPage<TaskVO> pages = taskService.selectPage(Condition.getPage(query), taskDto);
return R.data(pages);
}
/**
* 回库
*/
@PostMapping("/return-warehouse")
@ApiOperationSupport(order = 5)
@Operation(summary = "回库", description = "调用回库")
public R inventoryReturnToWarehouse(@RequestParam ReturnToWarehouseDto returnToWarehouseDto){
return iOrderBoxService.inventoryReturnToWarehouse(returnToWarehouseDto);
}
/**
* 回调作业中心list
*/
@PostMapping("/return-warehouse-list")
@ApiOperationSupport(order = 6)
@Operation(summary = "回调作业中心list", description = "回调作业中心list")
public R inventoryReturnToWarehouseList(){
return iOrderBoxService.returnToWarehouseList();
}
}

@ -2,6 +2,7 @@ package org.springblade.desk.logistics.service;
import org.springblade.core.tool.api.R;
import org.springblade.desk.logistics.pojo.dto.BoxBindingDto;
import org.springblade.desk.logistics.pojo.dto.ReturnToWarehouseDto;
import java.math.BigDecimal;
@ -37,15 +38,15 @@ public interface IOrderBoxService {
*/
R boxBinding(BoxBindingDto boxBinding);
/**
* 箱条码与订单解绑
* 功能解除指定箱条码对应的所有订单绑定关系支持箱的重新绑定或回收
*
* @param boxBarcode 箱条码唯一标识需要解绑的箱子
* @return R<?> 通用返回结果
* - 成功R.success()携带解绑成功的提示或解绑的订单数量
* - 失败R.fail()携带失败原因如箱条码不存在解绑数据异常等
* 释放站点并调用AGV小车接口优化后的核心方法
* 业务场景任务结束后释放站点触发AGV小车相关操作
* @param ReturnToWarehouseDto returnToWarehouseDto
* @return 统一返回结果
*/
R boxOrderUnbind(String boxBarcode);
R inventoryReturnToWarehouse(ReturnToWarehouseDto returnToWarehouseDto);
R returnToWarehouseList();
}

@ -0,0 +1,18 @@
package org.springblade.desk.logistics.service;
import org.springblade.core.tool.api.R;
import org.springblade.desk.logistics.pojo.entity.Task;
public interface IPipelineOrderBoxService {
/**
* 箱条码与订单解绑
* 功能解除指定箱条码对应的所有订单绑定关系支持箱的重新绑定或回收
*
* @param boxBarcode 箱条码唯一标识需要解绑的箱子
* @return R<?> 通用返回结果
* - 成功R.success()携带解绑成功的提示或解绑的订单数量
* - 失败R.fail()携带失败原因如箱条码不存在解绑数据异常等
*/
R boxOrderUnbind(String boxBarcode);
}

@ -2,23 +2,32 @@ package org.springblade.desk.logistics.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import groovy.lang.Lazy;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tool.api.R;
import org.springblade.desk.dashboard.service.IBsWorkCenterService;
import org.springblade.desk.logistics.pojo.dto.BoxBindingDto;
import org.springblade.desk.logistics.pojo.dto.ReturnToWarehouseDto;
import org.springblade.desk.logistics.pojo.entity.*;
import org.springblade.desk.logistics.pojo.vo.BsWorkCenterVO;
import org.springblade.desk.logistics.service.*;
import org.springblade.desk.logistics.utils.AgvTaskTypeUtil;
import org.springblade.desk.order.pojo.entity.YieldOrder;
import org.springblade.desk.order.service.IYieldOrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
import static org.springblade.desk.logistics.constant.AgvConstant.EQUIPMENT_TYPE_AGV;
import static org.springblade.desk.logistics.pojo.entity.OrderBind.STATUS_UNBINDED;
import static org.springblade.desk.logistics.pojo.entity.Station.STATUS_OCCUPIED;
import static org.springblade.desk.logistics.pojo.entity.Station.*;
import static org.springblade.desk.logistics.utils.CollectionCheckUtil.isFieldInCollection;
/**
* 订单箱业务实现类
@ -51,6 +60,11 @@ public class IOrderBoxServiceImpl implements IOrderBoxService {
*/
private final ILocationService iLocationService;
private final AgvTaskTypeUtil agvTaskTypeUtil;
private final IBsWorkCenterService bsWorkCenterService;
private final IPipelineService iPipelineOrderBoxService;
/**
* 任务运行中状态集合包含任务从启动到待入库的所有中间状态
* 用于判断箱条码是否存在未完成的运行任务
@ -69,6 +83,17 @@ public class IOrderBoxServiceImpl implements IOrderBoxService {
RUNNING_STATUSES.add(Task.STATUS_BACK_TO_STORAGE);// 返库状态
}
public IOrderBoxServiceImpl(IYieldOrderService iYieldOrderService, ITaskService iTaskService, IOrderBindService iOrderBindService, IStationService iStationService, ILocationService iLocationService, AgvTaskTypeUtil agvTaskTypeUtil, IBsWorkCenterService bsWorkCenterService, IPipelineService iPipelineOrderBoxService) {
this.iYieldOrderService = iYieldOrderService;
this.iTaskService = iTaskService;
this.iOrderBindService = iOrderBindService;
this.iStationService = iStationService;
this.iLocationService = iLocationService;
this.agvTaskTypeUtil = agvTaskTypeUtil;
this.bsWorkCenterService = bsWorkCenterService;
this.iPipelineOrderBoxService = iPipelineOrderBoxService;
}
/**
* 构造器注入依赖服务替代@Autowired符合Spring最佳实践
*
@ -78,13 +103,7 @@ public class IOrderBoxServiceImpl implements IOrderBoxService {
* @param iStationService 站点服务
* @param iLocationService 库位服务
*/
public IOrderBoxServiceImpl(IYieldOrderService iYieldOrderService, ITaskService iTaskService, IOrderBindService iOrderBindService, IStationService iStationService, ILocationService iLocationService) {
this.iYieldOrderService = iYieldOrderService;
this.iTaskService = iTaskService;
this.iOrderBindService = iOrderBindService;
this.iStationService = iStationService;
this.iLocationService = iLocationService;
}
/**
* 维护订单配件实际重量
@ -98,7 +117,7 @@ public class IOrderBoxServiceImpl implements IOrderBoxService {
*/
@Override
public R upholdOrderPartWeight(String cardNo, BigDecimal actualWeight) {
log.info("接收到实际重量:{},对应的流程卡号:{}",actualWeight,cardNo);
log.info("接收到实际重量:{},对应的流程卡号:{}", actualWeight, cardNo);
// 根据流程卡号查询工单(按更新时间倒序,取最新记录)
List<YieldOrder> list = iYieldOrderService.list(new QueryWrapper<YieldOrder>().eq("CARD_NO", cardNo).orderByDesc("UPDATE_TIME"));
@ -106,7 +125,7 @@ public class IOrderBoxServiceImpl implements IOrderBoxService {
list.get(0).setActualWeighing(actualWeight);
// 执行更新并返回结果
return iYieldOrderService.updateById(list.get(0))? R.success():R.fail("实际称重维护:卡号维护失败");
return iYieldOrderService.updateById(list.get(0)) ? R.success() : R.fail("实际称重维护:卡号维护失败");
}
/**
@ -154,7 +173,7 @@ public class IOrderBoxServiceImpl implements IOrderBoxService {
}
// 6. 完善任务信息并保存
task= (Task) location.getData();
task = (Task) location.getData();
task.setTaskStatus(Task.STATUS_START); // 设置任务初始状态为启动
task.setCreateTime(new Date()); // 设置任务创建时间
task.setCreateUser(AuthUtil.getUserId());
@ -162,7 +181,7 @@ public class IOrderBoxServiceImpl implements IOrderBoxService {
boolean orderBool = boxBinding.getOrderIdList() == null || boxBinding.getOrderIdList().size() == 0;
if (orderBool) {
task.setWeight(new BigDecimal(0));
}else {
} else {
task.setWeight(getWeightByOrderIdList(boxBinding.getOrderIdList()));
}
// 8. 重量校验(小于50kg才允许绑定,避免超重)
@ -178,64 +197,234 @@ public class IOrderBoxServiceImpl implements IOrderBoxService {
// 10. 无订单则直接返回成功,有订单则执行订单绑定
if (orderBool) {
return R.success();
}else {
return saveOrderBindingList(task.getId(),boxBinding.getOrderIdList());
} else {
return saveOrderBindingList(task.getId(), boxBinding.getOrderIdList());
}
}
/**
* 箱条码与订单解绑
* 流程参数校验 查询运行中任务 重置站点/库位状态 解绑订单 结束任务
* 释放站点并调用AGV小车接口
* <p>
* 业务场景任务结束后释放指定站点根据终点类型触发不同的AGV小车调度逻辑
* 核心流程
* 1. 校验入参和基础数据站点任务终点信息
* 2. 分两种场景处理AGV调度
* - 场景1仅指定终点名称下料输送线 直接调度AGV到下料线
* - 场景2指定终点站点ID 调度AGV到目标空闲站点
* 3. AGV调用失败时自动回滚站点状态保证数据一致性
*
* @param boxBarcode 箱条码
* @return R<?> 解绑结果
* - 成功R.success()
* - 失败R.fail()携带具体失败原因如箱条码为空
* @param returnToWarehouseDto 回库请求参数DTO
* @return 统一返回结果
* @throws IllegalArgumentException 入参无效基础数据不存在时抛出
*/
@Override
public R boxOrderUnbind(String boxBarcode) {
// 1. 参数非空校验
if (boxBarcode.isEmpty()) {
log.warn("箱绑定参数为空或箱条码缺失");
return R.fail("箱条码不能为空");
public R inventoryReturnToWarehouse(ReturnToWarehouseDto returnToWarehouseDto) {
// ========== 1. 入参和基础数据校验(前置校验,提前失败) ==========
// 校验DTO非空
Assert.notNull(returnToWarehouseDto, "回库请求参数不能为空");
String stationCode = returnToWarehouseDto.getStationCode();
Assert.hasText(stationCode, "站点编号不能为空");
// 查询站点信息(封装为方法,提升可读性)
Station targetStation = getStationByCode(stationCode);
// 查询有效任务列表
List<Task> validTaskList = getValidTaskList(stationCode);
// 校验站点状态
checkStationStatus(targetStation);
// 校验终点信息
checkEndLocationInfo(returnToWarehouseDto);
// ========== 2. 分场景处理AGV调度 ==========
try {
if (isDropoffConveyorLineScenario(returnToWarehouseDto)) {
// 场景1:仅指定下料输送线名称 → 调度AGV到下料线
return handleDropoffConveyorLineScenario(targetStation, validTaskList);
} else {
// 场景2:指定终点站点ID → 调度AGV到目标站点
return handleTargetStationScenario(targetStation, returnToWarehouseDto, validTaskList);
}
} catch (Exception e) {
log.error("库存回库-AGV调度异常,站点编号:{}", stationCode, e);
return R.fail("库存回库操作失败:" + e.getMessage());
}
}
// 2. 查询该箱条码对应的运行中任务
List<Task> taskList = iTaskService.list(
new LambdaQueryWrapper<Task>().eq(Task::getBoxBarcode, boxBarcode).in(false,Task::getTaskStatus,RUNNING_STATUSES)
);
/**
* 根据站点编号查询站点信息不存在则抛异常
*/
private Station getStationByCode(String stationCode) {
LambdaQueryWrapper<Station> stationQuery = new LambdaQueryWrapper<Station>()
.eq(Station::getStationCode, stationCode);
List<Station> stationList = iStationService.list(stationQuery);
// 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);
if (stationList == null || stationList.isEmpty()) {
log.warn("站点编号不存在,入参:{}", stationCode);
throw new IllegalArgumentException("输入站点编号不存在");
}
return stationList.get(0);
}
// 3.2 重置库位状态为占用(释放库位)
if (task.getLocationId()!=null&&task.getLocationId()!=0) {
Location location = iLocationService.getById(task.getLocationId());
location.setLocationStatus(STATUS_OCCUPIED);
iLocationService.updateById(location);
/**
* 查询有效任务列表运行中状态不存在则抛异常
*/
private List<Task> getValidTaskList(String stationCode) {
LambdaQueryWrapper<Task> taskQuery = new LambdaQueryWrapper<Task>()
.eq(Task::getBoxBarcode, stationCode)
.in(Task::getTaskStatus, RUNNING_STATUSES); // RUNNING_STATUSES建议抽取为常量
List<Task> taskList = iTaskService.list(taskQuery);
if (taskList == null || taskList.isEmpty()) {
log.warn("箱条码数据异常,站点编号:{}", stationCode);
throw new IllegalArgumentException("箱条码数据异常");
}
return taskList;
}
// 3.3 解绑订单(更新绑定状态为未绑定)
List<OrderBind> orderBindList = iOrderBindService.list(new LambdaQueryWrapper<OrderBind>().eq(OrderBind::getTaskId, task.getId()));
if (!CollectionUtils.isEmpty(orderBindList)) {
for (OrderBind orderBind : orderBindList) {
orderBind.setBindingStatus(STATUS_UNBINDED);
iOrderBindService.updateById(orderBind);
/**
* 校验站点状态必须为空闲
*/
private void checkStationStatus(Station station) {
if (!STATUS_FREE.equals(station.getStationStatus())) {
log.warn("站点被占用,站点编号:{},当前状态:{}", station.getStationCode(), station.getStationStatus());
throw new IllegalArgumentException("该站点正在被使用,请使用其他站点");
}
}
// // 3.4 结束任务(更新任务状态为已完成)
// task.setTaskStatus(STATUS_FINISHED);
// iTaskService.updateById(task);
/**
* 校验终点信息终点ID/名称不能为空
*/
private void checkEndLocationInfo(ReturnToWarehouseDto dto) {
Long endLocationId = dto.getEndLocationId();
String endName = dto.getEndName();
// 终点ID和名称都为空 → 异常
if ((endLocationId == null || endLocationId == 0) && (endName == null || endName.isBlank())) {
throw new IllegalArgumentException("请检查运送线终点,运送线终点为空");
}
// 仅终点ID为空 → 校验终点名称是否为下料线
if ((endLocationId == null || endLocationId == 0) && !DROPOFF_CONVEYOR_LINE_NAME.equals(endName)) {
throw new IllegalArgumentException("请检查运送线终点位置异常");
}
}
/**
* 判断是否为下料输送线场景
*/
private boolean isDropoffConveyorLineScenario(ReturnToWarehouseDto dto) {
Long endLocationId = dto.getEndLocationId();
return endLocationId == null || endLocationId == 0;
}
/**
* 处理下料输送线场景的AGV调度
*/
private R<?> handleDropoffConveyorLineScenario(Station targetStation, List<Task> taskList) {
String stationCode = targetStation.getStationCode();
// 1. 占用站点
updateStationStatus(targetStation, STATUS_OCCUPIED);
try {
// 2. 获取AGV任务类型并调用调度接口
String taskType = agvTaskTypeUtil.getTaskType(Integer.valueOf(targetStation.getStationPosition()), true);
boolean agvResult = iPipelineOrderBoxService.genAgvSchedulingTask(
taskType, stationCode, DROPOFF_CONVEYOR_LINE, EQUIPMENT_TYPE_AGV, taskList.get(0)
);
if (agvResult) {
log.info("AGV调度成功(下料线场景),站点编号:{}", stationCode);
return R.success();
} else {
throw new RuntimeException("调用AGV小车异常");
}
} catch (Exception e) {
// 3. AGV调用失败 → 回滚站点状态
updateStationStatus(targetStation, STATUS_FREE);
throw e;
}
}
/**
* 处理目标站点场景的AGV调度
*/
private R<?> handleTargetStationScenario(Station targetStation, ReturnToWarehouseDto dto, List<Task> taskList) {
String sourceStationCode = targetStation.getStationCode();
Long endLocationId = dto.getEndLocationId();
// 1. 查询终点空闲站点
Station endStation = getFreeStationByWcId(endLocationId);
// 2. 占用源站点 + 预占用终点站点
updateStationStatus(targetStation, STATUS_OCCUPIED);
updateStationStatus(endStation, PRE_STATUS_OCCUPIED);
try {
// 3. 获取AGV任务类型并调用调度接口
String taskType = agvTaskTypeUtil.getTaskType(Integer.valueOf(targetStation.getStationPosition()), false);
boolean agvResult = iPipelineOrderBoxService.genAgvSchedulingTask(
taskType, sourceStationCode, endStation.getStationCode(), EQUIPMENT_TYPE_AGV, taskList.get(0)
);
if (agvResult) {
log.info("AGV调度成功(目标站点场景),源站点:{},终点站点:{}", sourceStationCode, endStation.getStationCode());
return R.success();
} else {
throw new RuntimeException("调用AGV小车异常");
}
} catch (Exception e) {
// 4. AGV调用失败 → 回滚两个站点状态
updateStationStatus(targetStation, STATUS_FREE);
updateStationStatus(endStation, STATUS_FREE);
throw e;
}
}
/**
* 根据WCID查询空闲的终点站点不存在则抛异常
*/
private Station getFreeStationByWcId(Long wcId) {
LambdaQueryWrapper<Station> endStationQuery = new LambdaQueryWrapper<Station>()
.eq(Station::getWcId, wcId)
.eq(Station::getStationStatus, STATUS_FREE);
List<Station> endStationList = iStationService.list(endStationQuery);
if (endStationList == null || endStationList.isEmpty()) {
log.warn("结束站点异常,WCID:{}", wcId);
throw new IllegalArgumentException("结束站点异常");
}
return endStationList.get(0);
}
/**
* 统一更新站点状态封装重复逻辑
*/
private void updateStationStatus(Station station, Integer newStatus) {
station.setStationStatus(newStatus);
boolean updateResult = iStationService.updateById(station);
if (!updateResult) {
log.error("站点状态更新失败,站点编号:{},目标状态:{}", station.getStationCode(), newStatus);
throw new RuntimeException("站点状态更新失败");
}
log.debug("站点状态更新成功,站点编号:{},状态:{}", station.getStationCode(), newStatus);
}
@Override
public R returnToWarehouseList() {
List<Station> stationList = iStationService.list(new LambdaQueryWrapper<Station>().eq(Station::getStationStatus, STATUS_FREE));
ArrayList<BsWorkCenterVO> bsWorkCenterVOList = new ArrayList<>();
BsWorkCenterVO bsWorkCenterVO = new BsWorkCenterVO();
bsWorkCenterVO.setWcName(DROPOFF_CONVEYOR_LINE_NAME);
bsWorkCenterVOList.add(bsWorkCenterVO);
if (!stationList.isEmpty()) {
List<Long> list = stationList.stream().map(Station::getWcId).distinct().collect(Collectors.toList());
List<BsWorkCenterVO> bwList = bsWorkCenterService.getByIds(list);
if (!bwList.isEmpty()) {
bwList.forEach(s->{bsWorkCenterVOList.add(s);});
}
}
return R.data(bsWorkCenterVOList);
}
/**
@ -262,7 +451,7 @@ public class IOrderBoxServiceImpl implements IOrderBoxService {
// 批量插入绑定记录,失败则删除任务
if (iOrderBindService.saveBatch(orderBindList)) {
return R.success();
}else {
} else {
iTaskService.removeById(taskId); // 回滚:删除已创建的任务
return R.fail("订单绑定箱条码异常");
}

@ -0,0 +1,123 @@
package org.springblade.desk.logistics.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.tool.api.R;
import org.springblade.desk.logistics.pojo.entity.Location;
import org.springblade.desk.logistics.pojo.entity.OrderBind;
import org.springblade.desk.logistics.pojo.entity.Station;
import org.springblade.desk.logistics.pojo.entity.Task;
import org.springblade.desk.logistics.service.*;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static org.springblade.desk.logistics.pojo.entity.OrderBind.STATUS_UNBINDED;
import static org.springblade.desk.logistics.pojo.entity.Station.STATUS_OCCUPIED;
@Service
@Slf4j
public class IPipelineOrderBoxServiceImpl implements IPipelineOrderBoxService {
/**
* 任务服务处理箱绑定任务的创建状态更新删除等
*/
private final ITaskService iTaskService;
/**
* 站点服务处理站点状态占用/空闲管理
*/
private final IStationService iStationService;
/**
* 库位服务处理库位状态占用/空闲管理
*/
private final ILocationService iLocationService;
/**
* 订单绑定服务处理订单与任务的绑定关系维护
*/
private final IOrderBindService iOrderBindService;
/**
* 任务运行中状态集合包含任务从启动到待入库的所有中间状态
* 用于判断箱条码是否存在未完成的运行任务
*/
private static final Set<Integer> 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);// 返库状态
}
public IPipelineOrderBoxServiceImpl(ITaskService iTaskService, IStationService iStationService, ILocationService iLocationService, IOrderBindService iOrderBindService) {
this.iTaskService = iTaskService;
this.iStationService = iStationService;
this.iLocationService = iLocationService;
this.iOrderBindService = iOrderBindService;
}
/**
* 箱条码与订单解绑
* 流程参数校验 查询运行中任务 重置站点/库位状态 解绑订单 结束任务
*
* @param boxBarcode 箱条码
* @return R<?> 解绑结果
* - 成功R.success()
* - 失败R.fail()携带具体失败原因如箱条码为空
*/
@Override
public R boxOrderUnbind(String boxBarcode) {
// 1. 参数非空校验
if (boxBarcode.isEmpty()) {
log.warn("箱绑定参数为空或箱条码缺失");
return R.fail("箱条码不能为空");
}
// 2. 查询该箱条码对应的运行中任务
List<Task> taskList = iTaskService.list(
new LambdaQueryWrapper<Task>().eq(Task::getBoxBarcode, boxBarcode).in(false, 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<OrderBind> orderBindList = iOrderBindService.list(new LambdaQueryWrapper<OrderBind>().eq(OrderBind::getTaskId, task.getId()));
if (!CollectionUtils.isEmpty(orderBindList)) {
for (OrderBind orderBind : orderBindList) {
orderBind.setBindingStatus(STATUS_UNBINDED);
iOrderBindService.updateById(orderBind);
}
}
// // 3.4 结束任务(更新任务状态为已完成)
// task.setTaskStatus(STATUS_FINISHED);
// iTaskService.updateById(task);
}
return R.success();
}
}

@ -24,6 +24,7 @@
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.desk.logistics.service.impl;
import groovy.lang.Lazy;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.tool.api.R;
import org.springblade.desk.logistics.constant.AgvConstant;
@ -60,7 +61,8 @@ public class PipelineServiceImpl implements IPipelineService {
ILocationService locationService;
@Autowired
IOrderBoxService orderBoxService;
IPipelineOrderBoxService iPipelineOrderBoxService;
// IOrderBoxService orderBoxService;
@Autowired
ITaskExecuteRecordService taskExecuteRecordService;
@ -80,7 +82,7 @@ public class PipelineServiceImpl implements IPipelineService {
taskService.savePipelineWeigh(boxBarcode,actualWeight, Task.STATUS_RETURNED);
// 3.超重处理,解绑
R ret = orderBoxService.boxOrderUnbind(boxBarcode);
R ret = iPipelineOrderBoxService.boxOrderUnbind(boxBarcode);
if(!ret.isSuccess()){
throw new ServiceException("解绑失败: "+boxBarcode);
}

@ -0,0 +1,36 @@
package org.springblade.desk.logistics.utils;
import java.util.Collection;
import java.util.Map;
public class CollectionCheckUtil {
/**
* 判断指定字段的值是否存在于目标集合中
*
* @param fieldValue 要校验的字段参数值
* @param targetCollection 目标集合ListSetMap等
* @param <T> 字段值的类型泛型适配任意类型
* @return 存在返回true不存在返回false
* @throws IllegalArgumentException 当目标集合为空或类型不支持时抛出异常
*/
public static <T> boolean isFieldInCollection(T fieldValue, Object targetCollection) {
// 健壮性校验
if (targetCollection == null) {
throw new IllegalArgumentException("目标集合不能为空");
}
// 处理Collection类型(List、Set等)
if (targetCollection instanceof Collection) {
return ((Collection<?>) targetCollection).contains(fieldValue);
}
// 处理Map类型(判断key是否存在)
if (targetCollection instanceof Map) {
return ((Map<?, ?>) targetCollection).containsKey(fieldValue);
}
// 不支持的集合类型
throw new IllegalArgumentException("不支持的集合类型:" + targetCollection.getClass().getName());
}
}
Loading…
Cancel
Save