fix: 任务并发控制

This commit is contained in:
2026-07-27 10:44:51 +08:00
parent ed0b18a4af
commit 25ea2a248f
27 changed files with 333 additions and 36 deletions

View File

@@ -14,6 +14,7 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import jakarta.validation.Valid;
import java.util.List;
/**
* Task 服务 RPC API 接口
@@ -45,4 +46,14 @@ public interface TransportTaskApi {
@GetMapping(PREFIX + "/getTaskByCode")
@Operation(summary = "根据 taskCode 查询任务")
CommonResult<TaskInfoDTO> getTaskByCode(@RequestParam("taskCode") String taskCode);
@GetMapping(PREFIX + "/getRunningTaskByMaterialId")
@Operation(summary = "根据 materialId 和 ownerService 查询运行中任务")
CommonResult<List<TaskInfoDTO>> getRunningTaskByMaterialId(@RequestParam("materialId") Long materialId,
@RequestParam("ownerService") String ownerService);
@GetMapping(PREFIX + "/getRunningTaskByMaterialCode")
@Operation(summary = "根据 materialCode 和 ownerService 查询运行中任务")
CommonResult<List<TaskInfoDTO>> getRunningTaskByMaterialCode(@RequestParam("materialCode") String materialCode,
@RequestParam("ownerService") String ownerService);
}

View File

@@ -91,6 +91,14 @@ public class TaskInfoDTO implements Serializable {
* 载具编码2
*/
private String vehicleCode2;
/**
* 物料id
*/
private Long materialId;
/**
* 物料编码
*/
private String materialCode;
/**
* 车号
*/

View File

@@ -21,14 +21,6 @@ public class TransportTaskCreateReqDTO {
@NotEmpty(message = "业务归属服务不能为空")
private String ownerService;
@Schema(description = "业务类型", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "业务类型不能为空")
private String bizType;
@Schema(description = "业务侧标识", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "业务侧标识不能为空")
private String bizId;
@Schema(description = "业务回调处理器编码", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "业务回调处理器编码不能为空")
private String handleCode;
@@ -59,6 +51,12 @@ public class TransportTaskCreateReqDTO {
@Schema(description = "载具编码2")
private String vehicleCode2;
@Schema(description = "物料id")
private Long materialId;
@Schema(description = "物料编码")
private String materialCode;
@Schema(description = "优先级")
private String priority;

View File

@@ -0,0 +1,12 @@
package cn.code.nl.module.task.message;
/**
* 全局锁的key 或 前缀
* @Author: liyongde
* @Date: 2026/7/27 10:16
*/
public interface LockKeyConstants {
/** 任务状态变更消费锁前缀 */
String TASK_STATUS_CHANGE_LOCK_KEY = "task:task-status-change:";
}

View File

@@ -10,6 +10,8 @@ import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import static cn.code.nl.framework.common.pojo.CommonResult.success;
/**
@@ -51,4 +53,14 @@ public class TransportTaskApiImpl implements TransportTaskApi {
public CommonResult<TaskInfoDTO> getTaskByCode(String taskCode) {
return success(transportTaskService.getTaskInfoByCode(taskCode));
}
@Override
public CommonResult<List<TaskInfoDTO>> getRunningTaskByMaterialId(Long materialId, String ownerService) {
return success(transportTaskService.getRunningTaskInfoByMaterialId(materialId, ownerService));
}
@Override
public CommonResult<List<TaskInfoDTO>> getRunningTaskByMaterialCode(String materialCode, String ownerService) {
return success(transportTaskService.getRunningTaskInfoByMaterialCode(materialCode, ownerService));
}
}

View File

@@ -5,6 +5,8 @@ import cn.code.nl.module.task.dto.TaskInfoDTO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
/**
* 搬运任务 Convert
*
@@ -20,4 +22,9 @@ public interface TransportTaskConvert {
* DO 转 RPC 全量信息 DTO字段同名零配置映射
*/
TaskInfoDTO convert(TransportTaskDO bean);
/**
* DO 列表转 RPC 全量信息 DTO 列表
*/
List<TaskInfoDTO> convertList(List<TransportTaskDO> list);
}

View File

@@ -40,11 +40,11 @@ public class TransportTaskDO extends BaseDO {
*/
private String ownerService;
/**
* 业务类型
* 业务类型 todo: 暂时不用
*/
private String bizType;
/**
* 业务侧标识
* 业务侧标识 todo: 暂时不用
*/
private String bizId;
/**
@@ -103,6 +103,14 @@ public class TransportTaskDO extends BaseDO {
* 载具编码2
*/
private String vehicleCode2;
/**
* 物料id
*/
private Long materialId;
/**
* 物料编码
*/
private String materialCode;
/**
* 车号
*/

View File

@@ -87,6 +87,28 @@ public interface TransportTaskMapper extends BaseMapperX<TransportTaskDO> {
.lt(TransportTaskDO::getTaskStatus, TransportTaskStatusEnum.FINISHED_CALLBACK_PENDING.getCode()));
}
/**
* 根据物料ID和业务归属查询运行中任务
*/
default List<TransportTaskDO> selectRunningListByMaterialId(Long materialId, String ownerService) {
return selectList(new LambdaQueryWrapperX<TransportTaskDO>()
.eq(TransportTaskDO::getMaterialId, materialId)
.eq(TransportTaskDO::getOwnerService, ownerService)
.lt(TransportTaskDO::getTaskStatus, TransportTaskStatusEnum.FINISHED_CALLBACK_PENDING.getCode())
.orderByDesc(TransportTaskDO::getTaskId));
}
/**
* 根据物料编码和业务归属查询运行中任务
*/
default List<TransportTaskDO> selectRunningListByMaterialCode(String materialCode, String ownerService) {
return selectList(new LambdaQueryWrapperX<TransportTaskDO>()
.eq(TransportTaskDO::getMaterialCode, materialCode)
.eq(TransportTaskDO::getOwnerService, ownerService)
.lt(TransportTaskDO::getTaskStatus, TransportTaskStatusEnum.FINISHED_CALLBACK_PENDING.getCode())
.orderByDesc(TransportTaskDO::getTaskId));
}
/**
* 条件更新任务状态CAS 抢占)
*/

View File

@@ -0,0 +1,16 @@
package cn.code.nl.module.task.enums;
/**
* 请求ACS 接口 定义常量
* @Author: liyongde
* @Date: 2026/7/27 9:45
*/
public interface AcsApiConstants {
/** 下发任务 */
String ACS_TASK_API = "/acs-api/wms/issue-task";
/** 检测任务 */
String ACS_OPERATE_CHECK_API = "/acs-api/wms/check-enable-operate";
}

View File

@@ -0,0 +1,6 @@
/**
* 任务模块自身使用的枚举
* @Author: liyongde
* @Date: 2026/7/27 9:44
*/
package cn.code.nl.module.task.enums;

View File

@@ -24,6 +24,8 @@ import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import static cn.code.nl.module.task.enums.AcsApiConstants.ACS_TASK_API;
/**
* 搬运任务下发管理器
*/
@@ -31,7 +33,6 @@ import java.util.stream.Collectors;
@Component
public class TransportTaskIssueManager {
private static final String ACS_TASK_API = "/acs-api/wms/task";
private static final String ACS_SERVER_ADDRESS_CONFIG_SUFFIX = "-acs-server-address";
@Resource

View File

@@ -14,6 +14,8 @@ import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import static cn.code.nl.module.task.enums.AcsApiConstants.ACS_OPERATE_CHECK_API;
/**
* PC 端任务操作 ACS 校验管理器
*/
@@ -23,7 +25,6 @@ public class TransportTaskOperateCheckManager {
private static final String TASK_OPERATE_ENABLE_CONFIG_KEY = "task-operate-enable";
private static final String TASK_OPERATE_ENABLE_VALUE = "1";
private static final String ACS_OPERATE_CHECK_API = "/acs-api/wms/check-enable-operate";
private static final String ACS_SERVER_ADDRESS_CONFIG_SUFFIX = "-acs-server-address";
@Resource

View File

@@ -31,6 +31,8 @@ import static cn.code.nl.module.task.enums.ErrorCodeConstants.TRANSPORT_TASK_AUT
import static cn.code.nl.module.task.enums.ErrorCodeConstants.TRANSPORT_TASK_STATUS_NOT_ALLOW;
import static cn.code.nl.module.task.framework.common.util.TaskUtil.isAllowedFrom;
import static cn.code.nl.module.task.message.LockKeyConstants.TASK_STATUS_CHANGE_LOCK_KEY;
/**
* 搬运任务状态操作分发管理器
*/
@@ -70,7 +72,7 @@ public class TransportTaskOperationManager {
* 按操作类型查路由表分发
*/
public void dispatchOperation(TransportTaskDO task, TaskOperationTypeEnum type, AcsFeedbackReqDTO reqDTO) {
RLock lock = redissonClient.getLock(String.valueOf(task.getTaskId()));
RLock lock = redissonClient.getLock(TASK_STATUS_CHANGE_LOCK_KEY + task.getTaskId());
if (lock.tryLock()) {
try {
BiConsumer<TransportTaskDO, AcsFeedbackReqDTO> handler = operationHandlers.get(type);

View File

@@ -31,10 +31,7 @@ public class TaskEventProducer {
* @param message 事件消息
*/
public void publishEvent(TaskEventMessage message) {
// 补齐 eventId
if (message.getEventId() == null || message.getEventId().isEmpty()) {
message.setEventId(UUID.randomUUID().toString());
}
message.setEventId(message.getTaskId().toString());
String destination = message.getOwnerService() + "_" + topic;
try {

View File

@@ -99,6 +99,24 @@ public interface TransportTaskService {
*/
TaskInfoDTO getTaskInfoByCode(String taskCode);
/**
* 根据物料ID和业务归属查询运行中任务状态小于75
*
* @param materialId 物料ID
* @param ownerService 业务归属服务
* @return 运行中任务列表
*/
List<TaskInfoDTO> getRunningTaskInfoByMaterialId(Long materialId, String ownerService);
/**
* 根据物料编码和业务归属查询运行中任务状态小于75
*
* @param materialCode 物料编码
* @param ownerService 业务归属服务
* @return 运行中任务列表
*/
List<TaskInfoDTO> getRunningTaskInfoByMaterialCode(String materialCode, String ownerService);
/**
* 自动下发待下发任务到 ACS
*/

View File

@@ -2,6 +2,7 @@ package cn.code.nl.module.task.service.transporttask;
import cn.code.nl.framework.common.pojo.PageResult;
import cn.code.nl.framework.common.util.object.BeanUtils;
import cn.code.nl.framework.security.core.util.SecurityFrameworkUtils;
import cn.code.nl.module.base.api.classstandard.ClassStandardApi;
import cn.code.nl.module.task.controller.admin.transporttask.vo.TransportTaskOperateReqVO;
import cn.code.nl.module.task.controller.admin.transporttask.vo.TransportTaskPageReqVO;
@@ -21,6 +22,8 @@ import cn.code.nl.module.task.manage.TransportTaskOperateCheckManager;
import cn.code.nl.module.task.manage.TransportTaskOperationManager;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.mzt.logapi.context.LogRecordContext;
import com.mzt.logapi.starter.annotation.LogRecord;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -30,6 +33,7 @@ import org.springframework.validation.annotation.Validated;
import java.util.List;
import static cn.code.nl.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.code.nl.module.system.enums.LogRecordConstants.*;
import static cn.code.nl.module.task.enums.ErrorCodeConstants.TRANSPORT_TASK_ALREADY_FINAL;
import static cn.code.nl.module.task.enums.ErrorCodeConstants.TRANSPORT_TASK_NOT_EXISTS;
import static cn.code.nl.module.task.enums.ErrorCodeConstants.TRANSPORT_TASK_OPERATION_NOT_SUPPORTED;
@@ -130,6 +134,10 @@ public class TransportTaskServiceImpl implements TransportTaskService {
*/
@Override
@Transactional(rollbackFor = Exception.class)
@LogRecord(type = TASK_INFO,
subType = TASK_INFO_OPERATE_TYPE,
bizNo = "{{#reqVO.taskId}}",
success = TASK_INFO_OPERATE_SUCCESS)
public void operateTransportTask(TransportTaskOperateReqVO reqVO) {
TransportTaskDO task = transportTaskMapper.selectById(reqVO.getTaskId());
if (task == null) {
@@ -155,6 +163,8 @@ public class TransportTaskServiceImpl implements TransportTaskService {
reqDTO.setTaskId(task.getTaskId());
reqDTO.setStatus(type.getCode());
transportTaskOperationManager.dispatchOperation(task, type, reqDTO);
LogRecordContext.putVariable("loginUserNickname", SecurityFrameworkUtils.getLoginUserNickname());
LogRecordContext.putVariable("operateName", type.getName());
}
/**
@@ -186,6 +196,18 @@ public class TransportTaskServiceImpl implements TransportTaskService {
return TransportTaskConvert.INSTANCE.convert(task);
}
@Override
public List<TaskInfoDTO> getRunningTaskInfoByMaterialId(Long materialId, String ownerService) {
List<TransportTaskDO> tasks = transportTaskMapper.selectRunningListByMaterialId(materialId, ownerService);
return TransportTaskConvert.INSTANCE.convertList(tasks);
}
@Override
public List<TaskInfoDTO> getRunningTaskInfoByMaterialCode(String materialCode, String ownerService) {
List<TransportTaskDO> tasks = transportTaskMapper.selectRunningListByMaterialCode(materialCode, ownerService);
return TransportTaskConvert.INSTANCE.convertList(tasks);
}
/**
* 查询待下发任务,并委托下发管理器按生产区域下发到 ACS
*/

View File

@@ -80,7 +80,7 @@ spring:
rocketmq:
name-server: 192.168.81.193:9876 # RocketMQ Namesrv
producer:
group: task-producer-dev-group # 生产者分组
group: task_producer_dev_group # 生产者分组
consumer:
task:
topic: task-status-change-dev-topic