27 KiB
搬运任务操作路由表重构 + PC 端操作接口 实现计划
面向 AI 代理的工作者: 必需子技能:使用 superpowers:subagent-driven-development(推荐)或 superpowers:executing-plans 逐任务实现此计划。步骤使用复选框(
- [ ])语法来跟踪进度。
目标: 消灭 ACS 反馈的 if-else 分发,改为 TransportTaskServiceImpl 内的枚举路由表;新增 PC 端统一操作接口(完成/取消/强制完成)并打通前端按钮。
架构: TaskOperationTypeEnum 增加 acsAllowed/pcAllowed 属性与 getByCode 解析;TransportTaskServiceImpl 持有 EnumMap<操作类型, BiConsumer> 路由表,handle* 业务方法原样迁入;TransportTaskFeedbackServiceImpl 瘦身为 ACS 前置处理层后委托分发;PC 入口 POST /task/transport-task/operate 走同一路由表,差异仅在前置校验(抛异常)与记录 finishedType。
技术栈: Java 17 / Spring Boot / MyBatis-Plus(后端);Vue 3 + Vben + antdv-next(前端)
规格: docs/superpowers/specs/2026-07-15-task-operation-refactor-design.md
约定(来自 CLAUDE.md 与 nl-java-style): 注释/日志用中文;注入用 @Resource;不写测试;不频繁 commit(最后统一一次,需用户同意);枚举解析方法命名 getByCode。
文件结构
| 文件 | 操作 | 职责 |
|---|---|---|
nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/enums/TaskOperationTypeEnum.java |
修改 | 操作类型枚举:增加入口允许标记与 getByCode 解析 |
nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/enums/ErrorCodeConstants.java |
修改 | 新增 PC 操作两个错误码 |
nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/controller/admin/transporttask/vo/TransportTaskOperateReqVO.java |
创建 | PC 操作请求 VO |
nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/service/transporttask/TransportTaskService.java |
修改 | 接口新增 operateTransportTask / dispatchOperation |
nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/service/transporttask/TransportTaskServiceImpl.java |
修改 | 路由表 + handle* 迁入 + PC 入口 |
nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/service/transporttask/TransportTaskFeedbackServiceImpl.java |
修改 | 瘦身为 ACS 前置处理层 |
nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/controller/admin/transporttask/TransportTaskController.java |
修改 | 新增 POST /operate |
nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/api/task/transporttask/index.ts |
修改 | 新增 operateTransportTask API |
nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/task/transporttask/index.vue |
修改 | 三个按钮绑定 handleOperate |
任务 1:枚举增强 TaskOperationTypeEnum
文件:
-
修改:
nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/enums/TaskOperationTypeEnum.java -
步骤 1:整体替换为以下内容
package cn.code.nl.module.task.enums;
import lombok.Getter;
/**
* 任务操作类型枚举
* <p>
* acsAllowed:是否允许 ACS 反馈触发;pcAllowed:是否允许 PC 端触发
*
* @Author: liyongde
* @Date: 2026/7/15 19:11
*/
@Getter
public enum TaskOperationTypeEnum {
EXECUTING("EXECUTING", "执行中", true, false),
PICKED("PICKED", "取货完成", true, false),
FINISHED("FINISHED", "完成任务", true, true),
CANCELLED("CANCELLED", "取消任务", true, true),
APPLY_AGAIN("APPLY-AGAIN", "二次请求", true, false),
FORCE_FINISH("FORCE-FINISH", "强制完成", false, true);
/** 操作编码 */
private final String code;
/** 操作名称 */
private final String name;
/** 是否允许 ACS 反馈触发 */
private final boolean acsAllowed;
/** 是否允许 PC 端触发 */
private final boolean pcAllowed;
TaskOperationTypeEnum(String code, String name, boolean acsAllowed, boolean pcAllowed) {
this.code = code;
this.name = name;
this.acsAllowed = acsAllowed;
this.pcAllowed = pcAllowed;
}
/**
* 根据编码解析枚举(忽略大小写),找不到返回 null
*/
public static TaskOperationTypeEnum getByCode(String code) {
for (TaskOperationTypeEnum type : values()) {
if (type.getCode().equalsIgnoreCase(code)) {
return type;
}
}
return null;
}
}
任务 2:新增错误码
文件:
-
修改:
nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/enums/ErrorCodeConstants.java -
步骤 1:在
TRANSPORT_TASK_RUNNING_ALREADY_EXIST之后追加两行
ErrorCode TRANSPORT_TASK_OPERATION_NOT_SUPPORTED = new ErrorCode(5006, "不支持的任务操作类型");
ErrorCode TRANSPORT_TASK_ALREADY_FINAL = new ErrorCode(5007, "任务已处于终态,不允许操作");
任务 3:新增 TransportTaskOperateReqVO
文件:
-
创建:
nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/controller/admin/transporttask/vo/TransportTaskOperateReqVO.java -
步骤 1:创建文件
package cn.code.nl.module.task.controller.admin.transporttask.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/**
* PC 端搬运任务操作 Request VO
*
* @Author: liyongde
* @Date: 2026/7/15
*/
@Schema(description = "管理后台 - PC 端搬运任务操作 Request VO")
@Data
public class TransportTaskOperateReqVO {
@Schema(description = "任务ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "任务ID不能为空")
private Long taskId;
@Schema(description = "操作类型:FINISHED/CANCELLED/FORCE-FINISH", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "操作类型不能为空")
private String operationType;
}
任务 4:TransportTaskService 接口新增方法
文件:
-
修改:
nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/service/transporttask/TransportTaskService.java -
步骤 1:新增 import
在现有 import 区追加(vo.* 通配已覆盖新 VO,无需单独引入):
import cn.code.nl.module.task.dto.AcsFeedbackReqDTO;
import cn.code.nl.module.task.enums.TaskOperationTypeEnum;
- 步骤 2:在
getTransportTaskPage方法之后新增两个方法声明
/**
* PC 端操作搬运任务(完成/取消/强制完成)
*
* @param reqVO 操作请求
*/
void operateTransportTask(@Valid TransportTaskOperateReqVO reqVO);
/**
* 按操作类型分发处理任务(ACS 反馈与 PC 端操作共用路由表)
*
* @param task 任务
* @param type 操作类型
* @param reqDTO 反馈请求(PC 端为构造的精简对象)
*/
void dispatchOperation(TransportTaskDO task, TaskOperationTypeEnum type, AcsFeedbackReqDTO reqDTO);
任务 5:TransportTaskServiceImpl 路由表 + handle* 迁入 + PC 入口
文件:
- 修改:
nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/service/transporttask/TransportTaskServiceImpl.java
说明: handleExecuting/handlePicked/handleFinished/handleCancelled/publishEvent/buildCallbackReq 六个方法从 TransportTaskFeedbackServiceImpl 原样迁入(方法体一字不改),本任务只新增代码,删除动作在任务 6。
- 步骤 1:补充 import
在现有 import 区追加:
import cn.code.nl.framework.execute.biz.api.TaskCommonApi;
import cn.code.nl.framework.execute.core.TaskCommonApiFactory;
import cn.code.nl.module.task.dto.AcsFeedbackReqDTO;
import cn.code.nl.module.task.dto.TaskStatusCallbackReqDTO;
import cn.code.nl.module.task.enums.TaskEventTypeEnum;
import cn.code.nl.module.task.enums.TaskOperationTypeEnum;
import cn.code.nl.module.task.message.TaskEventMessage;
import cn.code.nl.module.task.mq.producer.TaskEventProducer;
import cn.hutool.core.util.StrUtil;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;
import java.util.EnumMap;
import java.util.Map;
import java.util.function.BiConsumer;
import static cn.code.nl.module.task.enums.ErrorCodeConstants.TRANSPORT_TASK_ALREADY_FINAL;
import static cn.code.nl.module.task.enums.ErrorCodeConstants.TRANSPORT_TASK_OPERATION_NOT_SUPPORTED;
import static cn.code.nl.module.task.framework.common.util.TaskUtil.isAllowedFrom;
注意:TRANSPORT_TASK_NOT_EXISTS 的静态导入已存在,不要重复。
- 步骤 2:类上补充
@Slf4j注解
@Slf4j
@Service
@Validated
public class TransportTaskServiceImpl implements TransportTaskService {
- 步骤 3:新增依赖字段与路由表字段(放在
transportTaskMapper字段之后)
@Resource
private TaskEventProducer taskEventProducer;
@Resource
private TaskCommonApiFactory taskCommonApiFactory;
/**
* 操作类型 -> 处理方法 路由表(ACS 反馈与 PC 端操作共用)
*/
private final Map<TaskOperationTypeEnum, BiConsumer<TransportTaskDO, AcsFeedbackReqDTO>> operationHandlers =
new EnumMap<>(TaskOperationTypeEnum.class);
/**
* 初始化操作类型路由表
*/
@PostConstruct
public void initOperationHandlers() {
operationHandlers.put(TaskOperationTypeEnum.EXECUTING, (task, reqDTO) -> handleExecuting(task));
operationHandlers.put(TaskOperationTypeEnum.PICKED, this::handlePicked);
operationHandlers.put(TaskOperationTypeEnum.FINISHED, this::handleFinished);
operationHandlers.put(TaskOperationTypeEnum.CANCELLED, this::handleCancelled);
// todo: 二次请求业务未开发
operationHandlers.put(TaskOperationTypeEnum.APPLY_AGAIN,
(task, reqDTO) -> log.info("二次请求业务未开发, taskId={}", task.getTaskId()));
// todo: 强制完成业务未开发
operationHandlers.put(TaskOperationTypeEnum.FORCE_FINISH,
(task, reqDTO) -> log.info("强制完成业务未开发, taskId={}", task.getTaskId()));
}
- 步骤 4:新增 PC 入口与分发方法(放在
getTransportTaskPage方法之后)
/**
* PC 端操作搬运任务(完成/取消/强制完成):先校验再走统一路由表分发
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void operateTransportTask(TransportTaskOperateReqVO reqVO) {
// 1. 校验任务存在
TransportTaskDO task = transportTaskMapper.selectById(reqVO.getTaskId());
if (task == null) {
throw exception(TRANSPORT_TASK_NOT_EXISTS);
}
// 2. 校验操作类型允许 PC 端触发
TaskOperationTypeEnum type = TaskOperationTypeEnum.getByCode(reqVO.getOperationType());
if (type == null || !type.isPcAllowed()) {
throw exception(TRANSPORT_TASK_OPERATION_NOT_SUPPORTED);
}
// 3. 终态校验:已完成/已取消不允许再操作
if (TransportTaskStatusEnum.FINISHED.getCode().equals(task.getTaskStatus())
|| TransportTaskStatusEnum.CANCELLED.getCode().equals(task.getTaskStatus())) {
throw exception(TRANSPORT_TASK_ALREADY_FINAL);
}
// 4. PC 端与 ACS 的差异点:记录完成类型
if (type == TaskOperationTypeEnum.FINISHED) {
task.setFinishedType("MANUAL");
} else if (type == TaskOperationTypeEnum.FORCE_FINISH) {
task.setFinishedType("FORCE");
}
// 5. 构造精简反馈对象,走统一路由表分发
AcsFeedbackReqDTO reqDTO = new AcsFeedbackReqDTO();
reqDTO.setTaskId(task.getTaskId());
reqDTO.setStatus(type.getCode());
dispatchOperation(task, type, reqDTO);
}
/**
* 按操作类型查路由表分发(消灭 if-else)
*/
@Override
public void dispatchOperation(TransportTaskDO task, TaskOperationTypeEnum type, AcsFeedbackReqDTO reqDTO) {
BiConsumer<TransportTaskDO, AcsFeedbackReqDTO> handler = operationHandlers.get(type);
if (handler == null) {
log.warn("操作类型未注册处理器, taskId={}, type={}", task.getTaskId(), type.getCode());
return;
}
handler.accept(task, reqDTO);
}
- 步骤 5:迁入六个业务方法(放在类末尾,方法体与原
TransportTaskFeedbackServiceImpl完全一致)
// ==================== 各状态处理方法(自 TransportTaskFeedbackServiceImpl 迁入,逻辑未改动) ====================
/**
* 处理执行中(60):直接更新状态,不做业务回调
*/
private void handleExecuting(TransportTaskDO task) {
if (!isAllowedFrom(task.getTaskStatus(), "40", "50")) {
log.warn("执行中反馈状态校验不通过, taskId={}, currentStatus={}",
task.getTaskId(), task.getTaskStatus());
return;
}
task.setTaskStatus(TransportTaskStatusEnum.EXECUTING.getCode());
transportTaskMapper.updateById(task);
log.info("任务执行中, taskId={}", task.getTaskId());
}
/**
* 处理取货完成(61):更新状态 + 同步 HTTP 回调 LMS/WMS
* ownerService: 服务提供商
*/
private void handlePicked(TransportTaskDO task, AcsFeedbackReqDTO reqDTO) {
if (!isAllowedFrom(task.getTaskStatus(), "60", "61")) {
log.warn("取货完成反馈状态校验不通过, taskId={}, currentStatus={}",
task.getTaskId(), task.getTaskStatus());
return;
}
// 更新状态
task.setTaskStatus(TransportTaskStatusEnum.PICKED.getCode());
transportTaskMapper.updateById(task);
log.info("任务已取货, taskId={}", task.getTaskId());
// 同步请求业务
TaskCommonApi byServerName = taskCommonApiFactory.getByServerName("lms-server");
byServerName.doHandlePicked();
}
/**
* 处理完成(67):更新状态 + 异步 MQ 通知 LMS/WMS
*/
private void handleFinished(TransportTaskDO task, AcsFeedbackReqDTO reqDTO) {
if (!isAllowedFrom(task.getTaskStatus(), "10", "40", "45", "50", "60", "61", "67")) {
log.warn("完成反馈状态校验不通过, taskId={}, currentStatus={}",
task.getTaskId(), task.getTaskStatus());
return;
}
// 已是 67 则只发 MQ,不改状态
if (!TransportTaskStatusEnum.FINISHED_CALLBACK_PENDING.getCode().equals(task.getTaskStatus())) {
task.setTaskStatus(TransportTaskStatusEnum.FINISHED_CALLBACK_PENDING.getCode());
}
task.setCallbackStatus(CallbackStatusEnum.PENDING.getCode());
transportTaskMapper.updateById(task);
publishEvent(task, TaskEventTypeEnum.TASK_FINISHED, reqDTO.getPayload());
}
/**
* 处理取消(69):更新状态 + 异步 MQ 通知 LMS/WMS
*/
private void handleCancelled(TransportTaskDO task, AcsFeedbackReqDTO reqDTO) {
if (!isAllowedFrom(task.getTaskStatus(), "10", "40", "45", "50", "60", "61", "67", "69")) {
log.warn("取消反馈状态校验不通过, taskId={}, currentStatus={}",
task.getTaskId(), task.getTaskStatus());
return;
}
if (!TransportTaskStatusEnum.CANCEL_CALLBACK_PENDING.getCode().equals(task.getTaskStatus())) {
task.setTaskStatus(TransportTaskStatusEnum.CANCEL_CALLBACK_PENDING.getCode());
}
task.setCallbackStatus(CallbackStatusEnum.PENDING.getCode());
transportTaskMapper.updateById(task);
publishEvent(task, TaskEventTypeEnum.TASK_CANCELLED, reqDTO.getPayload());
}
// ==================== 工具方法 ====================
/**
* 发布 MQ 事件。发送失败时记录 callbackStatus=FAILED
*/
private void publishEvent(TransportTaskDO task, TaskEventTypeEnum eventType,
Map<String, Object> payload) {
TaskEventMessage msg = new TaskEventMessage();
msg.setEventType(eventType.getCode());
msg.setTaskId(task.getTaskId());
msg.setTaskCode(task.getTaskCode());
msg.setOwnerService(task.getOwnerService());
msg.setBizType(task.getBizType());
msg.setBizId(task.getBizId());
msg.setHandleCode(task.getHandleCode());
msg.setPayload(payload);
try {
taskEventProducer.publishEvent(msg);
} catch (Exception e) {
log.error("MQ 发布失败, taskId={}, eventType={}",
task.getTaskId(), eventType.getCode(), e);
task.setCallbackStatus(CallbackStatusEnum.FAILED.getCode());
task.setCallbackErrorMsg(StrUtil.maxLength(e.getMessage(), 500));
transportTaskMapper.updateById(task);
}
}
/**
* 构建同步回调请求 DTO(当前无调用方,随迁保留)
*/
private TaskStatusCallbackReqDTO buildCallbackReq(TransportTaskDO task,
AcsFeedbackReqDTO reqDTO) {
TaskStatusCallbackReqDTO req = new TaskStatusCallbackReqDTO();
req.setTaskId(task.getTaskId());
req.setTaskCode(task.getTaskCode());
req.setStatus(TransportTaskStatusEnum.PICKED.getCode());
req.setOwnerService(task.getOwnerService());
req.setBizType(task.getBizType());
req.setBizId(task.getBizId());
req.setHandleCode(task.getHandleCode());
req.setPayload(reqDTO.getPayload());
return req;
}
任务 6:TransportTaskFeedbackServiceImpl 瘦身
文件:
-
修改:
nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/service/transporttask/TransportTaskFeedbackServiceImpl.java -
步骤 1:整体替换为以下内容
package cn.code.nl.module.task.service.transporttask;
import cn.code.nl.module.task.dal.dataobject.transporttask.TransportTaskDO;
import cn.code.nl.module.task.dal.mysql.transporttask.TransportTaskMapper;
import cn.code.nl.module.task.dto.AcsFeedbackReqDTO;
import cn.code.nl.module.task.enums.TaskOperationTypeEnum;
import cn.code.nl.module.task.enums.TransportTaskStatusEnum;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import jakarta.annotation.Resource;
/**
* ACS 反馈处理服务实现
* <p>
* 只做 ACS 前置处理(查任务/幂等/保存反馈参数),具体操作业务统一由 {@link TransportTaskService} 路由表分发
*
* @author 诺力管理员
*/
@Slf4j
@Service
@Validated
public class TransportTaskFeedbackServiceImpl implements TransportTaskFeedbackService {
@Resource
private TransportTaskMapper transportTaskMapper;
@Resource
private TransportTaskService transportTaskService;
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
@Override
@Transactional(rollbackFor = Exception.class)
public void receiveAcsFeedback(AcsFeedbackReqDTO reqDTO) {
// 1. 查询任务
TransportTaskDO task = transportTaskMapper.selectById(reqDTO.getTaskId());
if (task == null) {
log.warn("ACS 反馈 taskId 不存在, taskId={}, 直接返回成功", reqDTO.getTaskId());
return;
}
// 2. 幂等判断:已经是终态(完成/取消)则不重复处理
String currentStatus = task.getTaskStatus();
if (TransportTaskStatusEnum.FINISHED.getCode().equals(currentStatus)
|| TransportTaskStatusEnum.CANCELLED.getCode().equals(currentStatus)) {
log.info("任务已处终态, taskId={}, currentStatus={}, 忽略反馈",
reqDTO.getTaskId(), currentStatus);
return;
}
// 3. 保存 resultParam
if (reqDTO.getPayload() != null) {
try {
task.setResultParam(OBJECT_MAPPER.writeValueAsString(reqDTO.getPayload()));
} catch (Exception e) {
log.warn("resultParam 序列化失败, taskId={}", reqDTO.getTaskId(), e);
}
}
// 4. 解析操作类型并校验允许 ACS 触发,走统一路由表分发
TaskOperationTypeEnum type = TaskOperationTypeEnum.getByCode(reqDTO.getStatus());
if (type == null || !type.isAcsAllowed()) {
log.warn("未知或不允许的 ACS 反馈状态, taskId={}, status={}",
reqDTO.getTaskId(), reqDTO.getStatus());
return;
}
transportTaskService.dispatchOperation(task, type, reqDTO);
}
}
注意:原类中 handle*、publishEvent、buildCallbackReq 及其相关 import(TaskCommonApi、TaskCommonApiFactory、TaskEventMessage、TaskStatusCallbackReqDTO、CallbackStatusEnum、TaskEventTypeEnum、TaskEventProducer、StrUtil、isAllowedFrom、Map)随整体替换一并移除,已在任务 5 迁入 TransportTaskServiceImpl。
任务 7:TransportTaskController 新增 operate 接口
文件:
-
修改:
nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/controller/admin/transporttask/TransportTaskController.java -
步骤 1:在
updateTransportTask方法之后新增(vo.*通配 import 已覆盖新 VO)
@PostMapping("/operate")
@Operation(summary = "PC 端操作搬运任务(完成/取消/强制完成)")
@PreAuthorize("@ss.hasPermission('task:transport-task:operate')")
public CommonResult<Boolean> operateTransportTask(@Valid @RequestBody TransportTaskOperateReqVO reqVO) {
transportTaskService.operateTransportTask(reqVO);
return success(true);
}
任务 8:后端编译验证
- 步骤 1:编译 task 模块
运行(仓库根目录):
mvn -f nl-module-task/pom.xml compile -q
预期:BUILD SUCCESS,无编译错误。若因兄弟模块依赖未安装到本地仓库而失败,改用:
mvn -pl nl-module-task -am compile -q
任务 9:前端 API 新增 operateTransportTask
文件:
-
修改:
nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/api/task/transporttask/index.ts -
步骤 1:在
exportTransportTask之后新增
/** PC 端操作搬运任务(完成/取消/强制完成) */
export function operateTransportTask(data: {
operationType: string;
taskId: number | string;
}) {
return requestClient.post('/task/transport-task/operate', data);
}
任务 10:index.vue 三个按钮绑定操作类型
文件:
-
修改:
nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/task/transporttask/index.vue -
步骤 1:import 块补充
operateTransportTask
import {
deleteTransportTask,
deleteTransportTaskList,
exportTransportTask,
getTransportTaskPage,
operateTransportTask,
} from '#/api/task/transporttask';
- 步骤 2:在
handleDelete之前新增操作函数
/** PC 端操作搬运任务(完成/取消/强制完成) */
async function handleOperate(
row: TaskTransportTaskApi.TransportTask,
operationType: string,
label: string,
) {
await confirm(`确认要${label}【${row.taskCode}】吗?`);
const hideLoading = message.loading({
content: `正在${label}...`,
duration: 0,
});
try {
await operateTransportTask({ taskId: row.taskId!, operationType });
message.success(`${label}成功`);
handleRefresh();
} finally {
hideLoading();
}
}
- 步骤 3:替换三个按钮的
onClick(原来错绑handleEdit.bind(null, row))
{
label: '完成任务',
type: 'link',
icon: ACTION_ICON.SEND,
auth: ['task:transport-task:finish'],
disabled: ['79', '89', '99'].includes(row.taskStatus),
onClick: handleOperate.bind(null, row, 'FINISHED', '完成任务'),
},
{
label: '取消任务',
type: 'link',
icon: ACTION_ICON.CANCEL,
auth: ['task:transport-task:cancel'],
disabled: ['79', '89', '99'].includes(row.taskStatus),
onClick: handleOperate.bind(null, row, 'CANCELLED', '取消任务'),
},
{
label: '强制完成任务',
type: 'link',
icon: ACTION_ICON.CANCEL,
auth: ['task:transport-task:cancel'],
disabled: ['79', '89', '99'].includes(row.taskStatus),
onClick: handleOperate.bind(null, row, 'FORCE-FINISH', '强制完成任务'),
},
说明:auth/icon 维持现状(强制完成的独立权限属规格范围外)。
任务 11:前端验证
- 步骤 1:类型检查(可选,monorepo 全量较慢)
运行(nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next 目录):
pnpm typecheck
预期:本次改动的两个文件无新增类型错误(仓库既有错误不属本次范围)。
任务 12:统一提交(可选,需用户同意)
- 步骤 1:确认用户同意后提交
git add docs/superpowers/specs/2026-07-15-task-operation-refactor-design.md \
docs/superpowers/plans/2026-07-15-task-operation-refactor.md \
nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/enums/TaskOperationTypeEnum.java \
nl-module-task/nl-module-task-api/src/main/java/cn/code/nl/module/task/enums/ErrorCodeConstants.java \
nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/controller/admin/transporttask/vo/TransportTaskOperateReqVO.java \
nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/controller/admin/transporttask/TransportTaskController.java \
nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/service/transporttask/TransportTaskService.java \
nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/service/transporttask/TransportTaskServiceImpl.java \
nl-module-task/nl-module-task-server/src/main/java/cn/code/nl/module/task/service/transporttask/TransportTaskFeedbackServiceImpl.java \
nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/api/task/transporttask/index.ts \
nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/task/transporttask/index.vue
git commit -m "feat:任务操作路由表重构 + PC 端完成/取消/强制完成操作"
风险与注意事项
- 循环依赖:依赖方向为
FeedbackServiceImpl → TransportTaskService单向,TransportTaskServiceImpl不得反向注入TransportTaskFeedbackService。 - 事务:
receiveAcsFeedback的@Transactional经跨 Bean 调用按 REQUIRED 传播覆盖dispatchOperation;PC 入口自带@Transactional。 - FORCE-FINISH 的 finishedType:PC 入口会 set 但 handler 为 todo 不落库,属规格已确认的预期行为。
- 工作区有未提交改动:本计划涉及的部分文件已有本地修改(见 git status),实现时在现有基础上叠加,不要回退已有改动。