diff --git a/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/manual/ManualOrderActionFacade.java b/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/manual/ManualOrderActionFacade.java index af19d86..8b71bd6 100644 --- a/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/manual/ManualOrderActionFacade.java +++ b/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/manual/ManualOrderActionFacade.java @@ -58,9 +58,10 @@ public class ManualOrderActionFacade { public void dispatch(DispatchOrderCommand cmd) { // 1. 查单 + 校验 OpsOrderDO order = getOrderAndValidateType(cmd.getOrderId(), cmd.getBusinessType()); - WorkOrderStatusEnum status = WorkOrderStatusEnum.valueOf(order.getStatus()); - if (status != WorkOrderStatusEnum.PENDING) { - throw new IllegalStateException("当前工单状态不允许手动派单,仅 PENDING 状态可操作"); + String beforeStatus = order.getStatus(); + WorkOrderStatusEnum status = WorkOrderStatusEnum.valueOf(beforeStatus); + if (status != WorkOrderStatusEnum.PENDING && status != WorkOrderStatusEnum.QUEUED) { + throw new IllegalStateException("当前工单状态不允许手动派单,仅 PENDING/QUEUED 状态可操作"); } // 2. 条线前置校验 @@ -73,33 +74,26 @@ public class ManualOrderActionFacade { .targetStatus(WorkOrderStatusEnum.DISPATCHED) .assigneeId(cmd.getAssigneeId()) .assigneeName(cmd.getAssigneeName()) + .assigneePhone(cmd.getAssigneePhone()) .operatorType(cmd.getOperator().getOperatorType()) .operatorId(cmd.getOperator().getOperatorId()) - .reason(cmd.getReason() != null ? cmd.getReason() : "管理员手动派单") + .operatorName(cmd.getOperator().getOperatorName()) + .reason(cmd.getReason() != null ? cmd.getReason() : "手动派单") .build(); OrderTransitionResult result = orderLifecycleManager.transition(request); if (!result.isSuccess()) { throw new IllegalStateException("手动派单失败: " + result.getMessage()); } - // 4. 更新主表执行人 - order.setAssigneeId(cmd.getAssigneeId()); - order.setAssigneeName(cmd.getAssigneeName()); - opsOrderMapper.updateById(order); + // 4. 更新主表执行人(只更新 assignee 字段,避免覆盖状态机已写入的 status) + OpsOrderDO assigneeUpdate = new OpsOrderDO(); + assigneeUpdate.setId(cmd.getOrderId()); + assigneeUpdate.setAssigneeId(cmd.getAssigneeId()); + assigneeUpdate.setAssigneeName(cmd.getAssigneeName()); + opsOrderMapper.updateById(assigneeUpdate); - // 5. 审计 - Map payload = new HashMap<>(); - payload.put(OrderAuditPayloadKeys.ASSIGNEE_ID, cmd.getAssigneeId()); - payload.put(OrderAuditPayloadKeys.ASSIGNEE_NAME, cmd.getAssigneeName()); - if (cmd.getReason() != null) { - payload.put(OrderAuditPayloadKeys.REASON, cmd.getReason()); - } - String message = String.format("管理员手动派单给 %s", - cmd.getAssigneeName() != null ? cmd.getAssigneeName() : cmd.getAssigneeId()); - orderAuditService.record(order, ManualActionTypeEnum.MANUAL_DISPATCH, - cmd.getOperator(), message, payload); - - // 6. 条线后置 + // 5. 条线后置 + // 注:业务日志由生命周期事件 → 条线 EventListener 统一记录,此处不重复写 strategy.afterDispatch(cmd, order); log.info("[ManualOrderActionFacade] 手动派单完成: orderId={}, assigneeId={}", cmd.getOrderId(), cmd.getAssigneeId()); @@ -111,7 +105,8 @@ public class ManualOrderActionFacade { public void upgradePriority(UpgradePriorityCommand cmd) { // 1. 查单 + 校验 OpsOrderDO order = getOrderAndValidateType(cmd.getOrderId(), cmd.getBusinessType()); - WorkOrderStatusEnum status = WorkOrderStatusEnum.valueOf(order.getStatus()); + String beforeStatus = order.getStatus(); + WorkOrderStatusEnum status = WorkOrderStatusEnum.valueOf(beforeStatus); if (status == WorkOrderStatusEnum.COMPLETED || status == WorkOrderStatusEnum.CANCELLED) { throw new IllegalStateException("已完成或已取消的工单不允许升级优先级"); } @@ -139,8 +134,10 @@ public class ManualOrderActionFacade { if (cmd.getReason() != null) { payload.put(OrderAuditPayloadKeys.REASON, cmd.getReason()); } - String message = String.format("升级优先级 P%d → P%d", - oldPriority != null ? oldPriority : 2, cmd.getNewPriority()); + String opName = cmd.getOperator().getOperatorName() != null + ? cmd.getOperator().getOperatorName() : "操作人"; + String message = String.format("%s 将优先级从 P%d 升级为 P%d", + opName, oldPriority != null ? oldPriority : 2, cmd.getNewPriority()); orderAuditService.record(order, ManualActionTypeEnum.MANUAL_UPGRADE_PRIORITY, cmd.getOperator(), message, payload); @@ -157,7 +154,8 @@ public class ManualOrderActionFacade { public void cancel(CancelOrderCommand cmd) { // 1. 查单 + 校验 OpsOrderDO order = getOrderAndValidateType(cmd.getOrderId(), cmd.getBusinessType()); - WorkOrderStatusEnum status = WorkOrderStatusEnum.valueOf(order.getStatus()); + String beforeStatus = order.getStatus(); + WorkOrderStatusEnum status = WorkOrderStatusEnum.valueOf(beforeStatus); // 幂等 if (status == WorkOrderStatusEnum.CANCELLED) { @@ -172,19 +170,23 @@ public class ManualOrderActionFacade { OrderBusinessStrategy strategy = resolveStrategy(cmd.getBusinessType()); strategy.validateCancel(cmd, order); - // 3. 状态变更 - orderLifecycleManager.cancelOrder(cmd.getOrderId(), - cmd.getOperator().getOperatorId(), - cmd.getOperator().getOperatorType(), - cmd.getReason()); + // 3. 状态变更(走 transition 以透传 operatorName 到领域事件) + OrderTransitionRequest cancelRequest = OrderTransitionRequest.builder() + .orderId(cmd.getOrderId()) + .targetStatus(WorkOrderStatusEnum.CANCELLED) + .operatorType(cmd.getOperator().getOperatorType()) + .operatorId(cmd.getOperator().getOperatorId()) + .operatorName(cmd.getOperator().getOperatorName()) + .reason(cmd.getReason()) + .assigneeId(order.getAssigneeId()) + .build(); + OrderTransitionResult cancelResult = orderLifecycleManager.transition(cancelRequest); + if (!cancelResult.isSuccess()) { + throw new IllegalStateException("手动取消失败: " + cancelResult.getMessage()); + } - // 4. 审计 - Map payload = new HashMap<>(); - payload.put(OrderAuditPayloadKeys.REASON, cmd.getReason()); - orderAuditService.record(order, ManualActionTypeEnum.MANUAL_CANCEL, - cmd.getOperator(), "管理员手动取消: " + cmd.getReason(), payload); - - // 5. 条线后置 + // 4. 条线后置 + // 注:业务日志由生命周期事件 → 条线 EventListener 统一记录,此处不重复写 strategy.afterCancel(cmd, order); log.info("[ManualOrderActionFacade] 手动取消完成: orderId={}", cmd.getOrderId()); @@ -196,7 +198,8 @@ public class ManualOrderActionFacade { public void complete(CompleteOrderCommand cmd) { // 1. 查单 + 校验 OpsOrderDO order = getOrderAndValidateType(cmd.getOrderId(), cmd.getBusinessType()); - WorkOrderStatusEnum status = WorkOrderStatusEnum.valueOf(order.getStatus()); + String beforeStatus = order.getStatus(); + WorkOrderStatusEnum status = WorkOrderStatusEnum.valueOf(beforeStatus); // 幂等 if (status == WorkOrderStatusEnum.COMPLETED) { @@ -211,25 +214,27 @@ public class ManualOrderActionFacade { OrderBusinessStrategy strategy = resolveStrategy(cmd.getBusinessType()); strategy.validateComplete(cmd, order); - // 3. 状态变更 - String remark = cmd.getReason() != null ? cmd.getReason() : "管理员手动完成"; - orderLifecycleManager.completeOrder(cmd.getOrderId(), - cmd.getOperator().getOperatorId(), - cmd.getOperator().getOperatorType(), - remark); - - // 4. 审计 - Map payload = new HashMap<>(); - if (cmd.getReason() != null) { - payload.put(OrderAuditPayloadKeys.REASON, cmd.getReason()); + // 3. 状态变更(走 transition 以透传 operatorName 到领域事件) + String completeOpName = cmd.getOperator().getOperatorName() != null + ? cmd.getOperator().getOperatorName() : "操作人"; + String remark = cmd.getReason() != null ? cmd.getReason() + : String.format("%s 手动完成工单", completeOpName); + OrderTransitionRequest completeRequest = OrderTransitionRequest.builder() + .orderId(cmd.getOrderId()) + .targetStatus(WorkOrderStatusEnum.COMPLETED) + .operatorType(cmd.getOperator().getOperatorType()) + .operatorId(cmd.getOperator().getOperatorId()) + .operatorName(cmd.getOperator().getOperatorName()) + .reason(remark) + .assigneeId(order.getAssigneeId()) + .build(); + OrderTransitionResult completeResult = orderLifecycleManager.transition(completeRequest); + if (!completeResult.isSuccess()) { + throw new IllegalStateException("手动完单失败: " + completeResult.getMessage()); } - if (cmd.getResult() != null) { - payload.put(OrderAuditPayloadKeys.RESULT, cmd.getResult()); - } - orderAuditService.record(order, ManualActionTypeEnum.MANUAL_COMPLETE, - cmd.getOperator(), remark, payload); - // 5. 条线后置 + // 4. 条线后置 + // 注:业务日志由生命周期事件 → 条线 EventListener 统一记录,此处不重复写 strategy.afterComplete(cmd, order); log.info("[ManualOrderActionFacade] 手动完单完成: orderId={}", cmd.getOrderId()); diff --git a/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/manual/audit/OrderAuditService.java b/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/manual/audit/OrderAuditService.java index b881e80..f0c26ba 100644 --- a/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/manual/audit/OrderAuditService.java +++ b/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/core/manual/audit/OrderAuditService.java @@ -9,107 +9,52 @@ import com.viewsh.module.ops.infrastructure.log.enumeration.LogModule; import com.viewsh.module.ops.infrastructure.log.enumeration.LogType; import com.viewsh.module.ops.infrastructure.log.recorder.EventLogRecord; import com.viewsh.module.ops.infrastructure.log.recorder.EventLogRecorder; -import com.viewsh.module.ops.service.event.OpsOrderEventService; import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.util.HashMap; import java.util.Map; /** - * 统一手动动作审计服务 + * 统一手动动作审计服务。 *

- * 所有手动动作通过此服务落库,确保 ops_order_event(时间轴)和 - * ops_business_event_log(审计检索)双写一致。 + * 用于 create / upgradePriority 这类不经过状态机的手动动作, + * 写入 ops_business_event_log。 *

- * personId 永远表示真实操作者,被操作目标放入 payload。 - * - * @author lzh + * ops_order_event 只由状态机写,此服务不写时间轴。 + * dispatch / cancel / complete 走生命周期 → 条线 EventListener, + * 由 listener 统一记录业务日志,不在此服务重复写。 */ -@Slf4j @Service public class OrderAuditService { - @Resource - private OpsOrderEventService opsOrderEventService; - @Resource private EventLogRecorder eventLogRecorder; - /** - * 记录手动动作审计(双写:时间轴 + 业务日志) - * - * @param order 工单 - * @param actionType 手动动作类型 - * @param operator 操作人上下文 - * @param message 可读消息 - * @param payload 审计扩展数据 - */ public void record(OpsOrderDO order, ManualActionTypeEnum actionType, - OperatorContext operator, String message, - Map payload) { - Long orderId = order.getId(); - - // 1. 时间轴事件(ops_order_event) - String eventType = mapToEventType(actionType); - try { - opsOrderEventService.recordEvent( - orderId, - order.getStatus(), - order.getStatus(), - eventType, - operator.getOperatorType().getType(), - operator.getOperatorId(), - message - ); - } catch (Exception e) { - log.warn("[OrderAuditService] 记录时间轴事件失败: orderId={}, actionType={}", orderId, actionType, e); + OperatorContext operator, + String message, Map payload) { + Map auditPayload = new HashMap<>(); + auditPayload.put(OrderAuditPayloadKeys.BUSINESS_TYPE, order.getOrderType()); + auditPayload.put(OrderAuditPayloadKeys.ACTION_TYPE, actionType.getType()); + auditPayload.put(OrderAuditPayloadKeys.MANUAL, true); + auditPayload.put(OrderAuditPayloadKeys.SOURCE, operator.getSource().getSource()); + if (payload != null) { + auditPayload.putAll(payload); } - // 2. 业务审计日志(ops_business_event_log) - try { - Map auditPayload = new HashMap<>(); - auditPayload.put(OrderAuditPayloadKeys.BUSINESS_TYPE, order.getOrderType()); - auditPayload.put(OrderAuditPayloadKeys.ACTION_TYPE, actionType.getType()); - auditPayload.put(OrderAuditPayloadKeys.MANUAL, true); - auditPayload.put(OrderAuditPayloadKeys.SOURCE, operator.getSource().getSource()); - if (payload != null) { - auditPayload.putAll(payload); - } - - String module = LogModule.fromOrderType(order.getOrderType()); - eventLogRecorder.record(EventLogRecord.builder() - .module(module) - .domain(EventDomain.AUDIT) - .eventType(mapToLogType(actionType).getCode()) - .message(message) - .targetId(orderId) - .targetType("order") - .personId(operator.getOperatorId()) - .payload(auditPayload) - .build()); - } catch (Exception e) { - log.warn("[OrderAuditService] 记录审计日志失败: orderId={}, actionType={}", orderId, actionType, e); - } + eventLogRecorder.record(EventLogRecord.builder() + .module(LogModule.fromOrderType(order.getOrderType())) + .domain(EventDomain.AUDIT) + .eventType(mapToLogType(actionType).getCode()) + .message(message) + .targetId(order.getId()) + .targetType("order") + .personId(operator.getOperatorId()) + .payload(auditPayload) + .build()); } - /** - * ManualActionTypeEnum → ops_order_event eventType - */ - private String mapToEventType(ManualActionTypeEnum actionType) { - return switch (actionType) { - case MANUAL_CREATE -> "CREATE"; - case MANUAL_DISPATCH -> "DISPATCH"; - case MANUAL_UPGRADE_PRIORITY -> "UPGRADE_PRIORITY"; - case MANUAL_CANCEL -> "CANCEL"; - case MANUAL_COMPLETE -> "COMPLETE"; - }; - } - - /** - * ManualActionTypeEnum → LogType - */ private LogType mapToLogType(ManualActionTypeEnum actionType) { return switch (actionType) { case MANUAL_CREATE -> LogType.ORDER_CREATED;