fix(ops): 手动完单走完整责任链,补全队列同步、设备状态、事件发布等缺失环节
原手动完单逻辑直接更新 DB 状态,绕过了 OrderLifecycleManager 责任链, 导致队列未清理、工牌设备状态未恢复 IDLE、自动派发下一任务未触发、 事件表缺少操作人信息等问题。 改动: - CleanWorkOrderServiceImpl.manualCompleteOrder 改为委托 OrderLifecycleManager - OrderLifecycleManager 新增 completeOrder(orderId, operatorId, operatorType, remark) 重载 - Controller 注入 SecurityFrameworkUtils.getLoginUserId() 填充操作人 - EventPublishHandler 修复 OrderCompletedEvent.assigneeId 兜底逻辑 - 新增已完成幂等返回和已取消拒绝校验 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -21,4 +21,7 @@ public class ManualCompleteOrderReqDTO {
|
||||
|
||||
@Schema(description = "备注", example = "管理员手动完成")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "操作人ID(由后端自动填充)", hidden = true)
|
||||
private Long operatorId;
|
||||
}
|
||||
|
||||
@@ -2,10 +2,13 @@ package com.viewsh.module.ops.environment.service.cleanorder;
|
||||
|
||||
import com.viewsh.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import com.viewsh.module.ops.api.clean.OrderTimelineRespDTO;
|
||||
import com.viewsh.module.ops.core.lifecycle.OrderLifecycleManager;
|
||||
import com.viewsh.module.ops.dal.dataobject.workorder.OpsOrderDO;
|
||||
import com.viewsh.module.ops.dal.dataobject.workorder.OpsOrderEventDO;
|
||||
import com.viewsh.module.ops.dal.mysql.workorder.OpsOrderEventMapper;
|
||||
import com.viewsh.module.ops.dal.mysql.workorder.OpsOrderMapper;
|
||||
import com.viewsh.module.ops.enums.OperatorTypeEnum;
|
||||
import com.viewsh.module.ops.enums.WorkOrderStatusEnum;
|
||||
import com.viewsh.module.ops.environment.dal.dataobject.ManualCompleteOrderReqDTO;
|
||||
import com.viewsh.module.ops.environment.dal.dataobject.UpgradePriorityReqDTO;
|
||||
import com.viewsh.module.ops.service.event.OpsOrderEventService;
|
||||
@@ -40,6 +43,9 @@ public class CleanWorkOrderServiceImpl implements CleanWorkOrderService {
|
||||
@Resource
|
||||
private OpsOrderEventService opsOrderEventService;
|
||||
|
||||
@Resource
|
||||
private OrderLifecycleManager orderLifecycleManager;
|
||||
|
||||
/**
|
||||
* 事件类型名称映射
|
||||
*/
|
||||
@@ -120,42 +126,31 @@ public class CleanWorkOrderServiceImpl implements CleanWorkOrderService {
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void manualCompleteOrder(ManualCompleteOrderReqDTO req) {
|
||||
try {
|
||||
Long orderId = req.getOrderId();
|
||||
Long orderId = req.getOrderId();
|
||||
|
||||
// 1. 查询工单
|
||||
OpsOrderDO order = opsOrderMapper.selectById(orderId);
|
||||
if (order == null) {
|
||||
log.warn("[manualCompleteOrder] 工单不存在: orderId={}", orderId);
|
||||
return;
|
||||
}
|
||||
|
||||
String fromStatus = order.getStatus();
|
||||
|
||||
// 2. 更新工单状态为已完成
|
||||
OpsOrderDO updateDO = new OpsOrderDO();
|
||||
updateDO.setId(orderId);
|
||||
updateDO.setStatus("COMPLETED");
|
||||
updateDO.setEndTime(LocalDateTime.now());
|
||||
opsOrderMapper.updateById(updateDO);
|
||||
|
||||
// 3. 记录事件
|
||||
opsOrderEventService.recordEvent(
|
||||
orderId,
|
||||
fromStatus,
|
||||
"COMPLETED",
|
||||
"COMPLETE",
|
||||
"ADMIN",
|
||||
null, // 管理员操作,当前未传递具体ID
|
||||
req.getRemark() != null ? req.getRemark() : "管理员手动完成"
|
||||
);
|
||||
|
||||
log.info("[manualCompleteOrder] 手动完成工单成功: orderId={}, remark={}", orderId, req.getRemark());
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("[manualCompleteOrder] 手动完成工单失败: orderId={}", req.getOrderId(), e);
|
||||
throw e;
|
||||
// 1. 查询工单
|
||||
OpsOrderDO order = opsOrderMapper.selectById(orderId);
|
||||
if (order == null) {
|
||||
throw new IllegalArgumentException("工单不存在: orderId=" + orderId);
|
||||
}
|
||||
|
||||
// 2. 校验:已完成的工单幂等返回
|
||||
if (WorkOrderStatusEnum.COMPLETED.getStatus().equals(order.getStatus())) {
|
||||
log.info("[manualCompleteOrder] 工单已处于完成状态,幂等返回: orderId={}", orderId);
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. 校验:已取消的工单不能完成
|
||||
if (WorkOrderStatusEnum.CANCELLED.getStatus().equals(order.getStatus())) {
|
||||
throw new IllegalStateException("已取消的工单不能完成: orderId=" + orderId);
|
||||
}
|
||||
|
||||
// 4. 通过 OrderLifecycleManager 走完整责任链(状态校验→队列同步→事件发布)
|
||||
String remark = req.getRemark() != null ? req.getRemark() : "管理员手动完成";
|
||||
orderLifecycleManager.completeOrder(orderId, req.getOperatorId(), OperatorTypeEnum.ADMIN, remark);
|
||||
|
||||
log.info("[manualCompleteOrder] 手动完成工单成功: orderId={}, operatorId={}, remark={}",
|
||||
orderId, req.getOperatorId(), remark);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -140,6 +140,18 @@ public interface OrderLifecycleManager {
|
||||
*/
|
||||
void completeOrder(Long orderId, Long operatorId, String remark);
|
||||
|
||||
/**
|
||||
* 完成工单(指定操作人类型)
|
||||
* <p>
|
||||
* 用于管理员手动完单等场景,允许指定操作人类型
|
||||
*
|
||||
* @param orderId 工单ID
|
||||
* @param operatorId 操作人ID
|
||||
* @param operatorType 操作人类型
|
||||
* @param remark 完成备注
|
||||
*/
|
||||
void completeOrder(Long orderId, Long operatorId, OperatorTypeEnum operatorType, String remark);
|
||||
|
||||
/**
|
||||
* 取消工单
|
||||
* <p>
|
||||
|
||||
@@ -268,13 +268,20 @@ public class OrderLifecycleManagerImpl implements OrderLifecycleManager {
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void completeOrder(Long orderId, Long operatorId, String remark) {
|
||||
log.info("开始完成工单: orderId={}, operatorId={}, remark={}", orderId, operatorId, remark);
|
||||
completeOrder(orderId, operatorId, OperatorTypeEnum.CLEANER, remark);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void completeOrder(Long orderId, Long operatorId, OperatorTypeEnum operatorType, String remark) {
|
||||
log.info("开始完成工单: orderId={}, operatorId={}, operatorType={}, remark={}",
|
||||
orderId, operatorId, operatorType, remark);
|
||||
|
||||
// 构建请求
|
||||
OrderTransitionRequest request = OrderTransitionRequest.builder()
|
||||
.orderId(orderId)
|
||||
.targetStatus(WorkOrderStatusEnum.COMPLETED)
|
||||
.operatorType(OperatorTypeEnum.CLEANER)
|
||||
.operatorType(operatorType)
|
||||
.operatorId(operatorId)
|
||||
.reason(remark)
|
||||
.build();
|
||||
@@ -287,8 +294,8 @@ public class OrderLifecycleManagerImpl implements OrderLifecycleManager {
|
||||
}
|
||||
|
||||
// 注意:IoT 触发的自动完成在 CleanOrderCompleteEventHandler 中记录日志
|
||||
// 这里只记录手动完成的日志(operatorId 不为空的情况)
|
||||
if (operatorId != null) {
|
||||
// 管理员手动完成时记录日志
|
||||
if (operatorType == OperatorTypeEnum.ADMIN) {
|
||||
recordStatusChangeLog(orderId, result, "ORDER_COMPLETED_MANUAL", "工单手动完成");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,11 +64,16 @@ public class EventPublishHandler extends TransitionHandler {
|
||||
|
||||
// 如果是完成状态,额外发布完成事件
|
||||
if (targetStatus == WorkOrderStatusEnum.COMPLETED) {
|
||||
// assigneeId 优先从 request 获取,兜底从工单对象获取(手动完单等场景 request 中无 assigneeId)
|
||||
Long assigneeId = request.getAssigneeId() != null
|
||||
? request.getAssigneeId()
|
||||
: context.getOrder().getAssigneeId();
|
||||
|
||||
OrderCompletedEvent completedEvent = OrderCompletedEvent.builder()
|
||||
.orderId(context.getOrder().getId())
|
||||
.orderCode(context.getOrder().getOrderCode())
|
||||
.orderType(context.getOrder().getOrderType())
|
||||
.assigneeId(request.getAssigneeId())
|
||||
.assigneeId(assigneeId)
|
||||
.workDuration(calculateWorkDuration(context))
|
||||
.build();
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.viewsh.module.ops.controller.admin.clean;
|
||||
|
||||
import com.viewsh.framework.common.pojo.CommonResult;
|
||||
import com.viewsh.framework.security.core.util.SecurityFrameworkUtils;
|
||||
import com.viewsh.module.ops.api.clean.OrderTimelineRespDTO;
|
||||
import com.viewsh.module.ops.environment.dal.dataobject.ManualCompleteOrderReqDTO;
|
||||
import com.viewsh.module.ops.environment.dal.dataobject.UpgradePriorityReqDTO;
|
||||
@@ -49,6 +50,8 @@ public class CleanWorkOrderController {
|
||||
public CommonResult<Boolean> manualCompleteOrder(
|
||||
@Valid @RequestBody ManualCompleteOrderReqDTO req) {
|
||||
|
||||
// 注入当前登录用户ID作为操作人
|
||||
req.setOperatorId(SecurityFrameworkUtils.getLoginUserId());
|
||||
cleanWorkOrderService.manualCompleteOrder(req);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user