feat(ops): add CleanOrderConfirmEventHandler

This commit is contained in:
lzh
2026-01-19 14:50:09 +08:00
parent 48ab863fda
commit d933bbf92c

View File

@@ -0,0 +1,161 @@
package com.viewsh.module.ops.environment.integration.consumer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.viewsh.module.iot.api.device.IotDeviceControlApi;
import com.viewsh.module.iot.api.device.dto.IotDeviceServiceInvokeReqDTO;
import com.viewsh.module.ops.core.lifecycle.OrderLifecycleManager;
import com.viewsh.module.ops.core.lifecycle.model.OrderTransitionRequest;
import com.viewsh.module.ops.dal.dataobject.workorder.OpsOrderDO;
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.integration.dto.CleanOrderConfirmEventDTO;
import com.viewsh.module.ops.infrastructure.log.context.BusinessLogContext;
import com.viewsh.module.ops.infrastructure.log.enumeration.LogType;
import com.viewsh.module.ops.infrastructure.log.publisher.BusinessLogPublisher;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.annotation.ConsumeMode;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* 保洁工单确认事件消费者
* <p>
* 订阅 IoT 模块发布的工单确认事件(如:工牌按键确认)
*
* @author AI
*/
@Slf4j
@Component
@RocketMQMessageListener(
topic = "ops.order.confirm",
consumerGroup = "ops-clean-order-confirm-group",
consumeMode = ConsumeMode.CONCURRENTLY
)
public class CleanOrderConfirmEventHandler implements RocketMQListener<String> {
private static final String DEDUP_KEY_PATTERN = "ops:clean:dedup:confirm:%s";
private static final int DEDUP_TTL_SECONDS = 300;
@Resource
private ObjectMapper objectMapper;
@Resource
private StringRedisTemplate stringRedisTemplate;
@Resource
private OpsOrderMapper opsOrderMapper;
@Resource
private OrderLifecycleManager orderLifecycleManager;
@Resource
private BusinessLogPublisher businessLogPublisher;
@Resource
private IotDeviceControlApi iotDeviceControlApi;
@Override
public void onMessage(String message) {
try {
// 1. JSON 反序列化
CleanOrderConfirmEventDTO event = objectMapper.readValue(message, CleanOrderConfirmEventDTO.class);
String eventId = event.getEventId();
// 2. 幂等性检查
String dedupKey = String.format(DEDUP_KEY_PATTERN, eventId);
Boolean firstTime = stringRedisTemplate.opsForValue()
.setIfAbsent(dedupKey, "1", DEDUP_TTL_SECONDS, TimeUnit.SECONDS);
if (!Boolean.TRUE.equals(firstTime)) {
log.debug("[CleanOrderConfirmEventHandler] 重复事件,跳过处理: eventId={}", eventId);
return;
}
// 3. 加载工单
Long orderId = event.getOrderId();
OpsOrderDO order = opsOrderMapper.selectById(orderId);
if (order == null) {
log.warn("[CleanOrderConfirmEventHandler] 工单不存在: orderId={}", orderId);
return;
}
WorkOrderStatusEnum currentStatus = WorkOrderStatusEnum.fromStatus(order.getStatus());
log.info("[CleanOrderConfirmEventHandler] 收到确认事件: eventId={}, orderId={}, currentStatus={}",
eventId, orderId, currentStatus);
// 4. 状态检查
// 如果已在进行中 (CONFIRMED or ARRIVED),提示"工单已在进行中"
if (currentStatus == WorkOrderStatusEnum.CONFIRMED || currentStatus == WorkOrderStatusEnum.ARRIVED) {
sendTTS(event.getDeviceId(), "工单已在进行中");
return;
}
// 检查是否可以确认
if (!currentStatus.canConfirm()) {
log.warn("[CleanOrderConfirmEventHandler] 当前状态无法确认工单: orderId={}, status={}", orderId, currentStatus);
sendTTS(event.getDeviceId(), "当前状态无法确认工单");
return;
}
// 5. 状态流转 -> CONFIRMED
OrderTransitionRequest request = OrderTransitionRequest.builder()
.orderId(orderId)
.targetStatus(WorkOrderStatusEnum.CONFIRMED)
.reason("工牌按键确认")
.operatorType(OperatorTypeEnum.CLEANER)
.operatorId(order.getAssigneeId())
.build();
orderLifecycleManager.transition(request);
// 6. 记录业务日志
BusinessLogContext logContext = BusinessLogContext.forOrder(
LogType.TRANSITION,
"工单已确认 (工牌按键)",
orderId
);
businessLogPublisher.publishSuccess(logContext);
// 7. 发送 TTS 通知
// "工单已确认,请前往{AreaName}开始作业"
String ttsText = "工单已确认,请前往作业区域开始作业";
sendTTS(event.getDeviceId(), ttsText);
} catch (Exception e) {
log.error("[CleanOrderConfirmEventHandler] 消息处理失败: message={}", message, e);
throw new RuntimeException("保洁工单确认事件处理失败", e);
}
}
/**
* 发送 TTS 语音播报
*/
private void sendTTS(Long deviceId, String text) {
if (deviceId == null) {
return;
}
try {
Map<String, Object> params = new HashMap<>();
params.put("text", text);
IotDeviceServiceInvokeReqDTO req = IotDeviceServiceInvokeReqDTO.builder()
.deviceId(deviceId)
.identifier("TTS")
.params(params)
.timeoutSeconds(10)
.build();
iotDeviceControlApi.invokeService(req);
} catch (Exception e) {
log.error("[CleanOrderConfirmEventHandler] TTS 发送失败: deviceId={}", deviceId, e);
}
}
}