fix(iot,ops): 确保信标相关审计事件包含orderId以支持工单关联查询
修改内容: 1. BeaconDetectionRuleProcessor.publishAuditEvent 添加 orderId 参数 - BEACON_ARRIVE_CONFIRMED 事件包含当前工单ID - BEACON_LEAVE_WARNING_SENT 事件从设备状态获取工单ID - TTS_REQUEST 事件不包含工单ID(非工单特定) 2. SignalLossRuleProcessor.publishAuditEvent 添加 orderId 参数 - BEACON_COMPLETE_REQUESTED 事件包含当前工单ID - COMPLETE_SUPPRESSED_INVALID 事件从设备状态获取工单ID 3. CleanOrderAuditEventHandler 使用 EventLogRecord.builder() - 显式设置 targetId 和 targetType 字段 - 确保 targetType="order" 当 orderId 存在时 影响范围: - ops_business_event_log 表新增记录将正确包含 targetId 和 targetType - 支持按工单ID查询所有相关审计日志 - 解决信标到岗/离岗/完成事件缺失工单关联的问题 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -171,7 +171,7 @@ public class BeaconDetectionRuleProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 7. 发布审计日志
|
// 7. 发布审计日志
|
||||||
publishAuditEvent("BEACON_ARRIVE_CONFIRMED", deviceId, null, areaId,
|
publishAuditEvent("BEACON_ARRIVE_CONFIRMED", deviceId, null, areaId, currentOrder.getOrderId(),
|
||||||
"蓝牙信标自动到岗确认", triggerData);
|
"蓝牙信标自动到岗确认", triggerData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,13 +209,17 @@ public class BeaconDetectionRuleProcessor {
|
|||||||
(exitConfig.getLossTimeoutMinutes() > 0 ? exitConfig.getLossTimeoutMinutes() + "分钟内工单将自动结算"
|
(exitConfig.getLossTimeoutMinutes() > 0 ? exitConfig.getLossTimeoutMinutes() + "分钟内工单将自动结算"
|
||||||
: "工单将自动结算"));
|
: "工单将自动结算"));
|
||||||
|
|
||||||
// 3. 发布审计日志
|
// 3. 发布审<EFBFBD><EFBFBD><EFBFBD>日志
|
||||||
Map<String, Object> data = new HashMap<>();
|
Map<String, Object> data = new HashMap<>();
|
||||||
data.put("firstLossTime", System.currentTimeMillis());
|
data.put("firstLossTime", System.currentTimeMillis());
|
||||||
data.put("rssi", window.isEmpty() ? -999 : window.get(window.size() - 1));
|
data.put("rssi", window.isEmpty() ? -999 : window.get(window.size() - 1));
|
||||||
data.put("warningDelayMinutes", exitConfig.getWarningDelayMinutes());
|
data.put("warningDelayMinutes", exitConfig.getWarningDelayMinutes());
|
||||||
|
|
||||||
publishAuditEvent("BEACON_LEAVE_WARNING_SENT", deviceId, null, areaId,
|
// 获取当前工单ID
|
||||||
|
BadgeDeviceStatusRedisDAO.OrderInfo currentOrder = badgeDeviceStatusRedisDAO.getCurrentOrder(deviceId);
|
||||||
|
Long orderId = currentOrder != null ? currentOrder.getOrderId() : null;
|
||||||
|
|
||||||
|
publishAuditEvent("BEACON_LEAVE_WARNING_SENT", deviceId, null, areaId, orderId,
|
||||||
"保洁员离开作业区域,已发送警告", data);
|
"保洁员离开作业区域,已发送警告", data);
|
||||||
} else {
|
} else {
|
||||||
// 4. 更新最后丢失时间
|
// 4. 更新最后丢失时间
|
||||||
@@ -253,7 +257,7 @@ public class BeaconDetectionRuleProcessor {
|
|||||||
* 发布审计事件
|
* 发布审计事件
|
||||||
*/
|
*/
|
||||||
private void publishAuditEvent(String auditType, Long deviceId, String deviceKey,
|
private void publishAuditEvent(String auditType, Long deviceId, String deviceKey,
|
||||||
Long areaId, String message, Map<String, Object> data) {
|
Long areaId, Long orderId, String message, Map<String, Object> data) {
|
||||||
try {
|
try {
|
||||||
CleanOrderAuditEvent event = CleanOrderAuditEvent.builder()
|
CleanOrderAuditEvent event = CleanOrderAuditEvent.builder()
|
||||||
.eventId(java.util.UUID.randomUUID().toString())
|
.eventId(java.util.UUID.randomUUID().toString())
|
||||||
@@ -261,14 +265,15 @@ public class BeaconDetectionRuleProcessor {
|
|||||||
.deviceId(deviceId)
|
.deviceId(deviceId)
|
||||||
.deviceKey(deviceKey)
|
.deviceKey(deviceKey)
|
||||||
.areaId(areaId)
|
.areaId(areaId)
|
||||||
|
.orderId(orderId)
|
||||||
.message(message)
|
.message(message)
|
||||||
.data(data)
|
.data(data)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
rocketMQTemplate.syncSend(CleanOrderTopics.ORDER_AUDIT, MessageBuilder.withPayload(event).build());
|
rocketMQTemplate.syncSend(CleanOrderTopics.ORDER_AUDIT, MessageBuilder.withPayload(event).build());
|
||||||
|
|
||||||
log.debug("[BeaconDetection] 发布审计事件:auditType={}, deviceId={}, areaId={}",
|
log.debug("[BeaconDetection] 发布审计事件:auditType={}, deviceId={}, areaId={}, orderId={}",
|
||||||
auditType, deviceId, areaId);
|
auditType, deviceId, areaId, orderId);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("[BeaconDetection] 发布审计事件失败:auditType={}, deviceId={}", auditType, deviceId, e);
|
log.error("[BeaconDetection] 发布审计事件失败:auditType={}, deviceId={}", auditType, deviceId, e);
|
||||||
}
|
}
|
||||||
@@ -282,7 +287,7 @@ public class BeaconDetectionRuleProcessor {
|
|||||||
data.put("tts", text);
|
data.put("tts", text);
|
||||||
data.put("timestamp", System.currentTimeMillis());
|
data.put("timestamp", System.currentTimeMillis());
|
||||||
|
|
||||||
publishAuditEvent("TTS_REQUEST", deviceId, null, null, text, data);
|
publishAuditEvent("TTS_REQUEST", deviceId, null, null, null, text, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -220,7 +220,11 @@ public class SignalLossRuleProcessor {
|
|||||||
data.put("minValidWorkMinutes", exitConfig.getMinValidWorkMinutes());
|
data.put("minValidWorkMinutes", exitConfig.getMinValidWorkMinutes());
|
||||||
data.put("shortageMs", minValidWorkMillis - durationMs);
|
data.put("shortageMs", minValidWorkMillis - durationMs);
|
||||||
|
|
||||||
publishAuditEvent("COMPLETE_SUPPRESSED_INVALID", deviceId, deviceKey, areaId,
|
// 获取当前工单ID
|
||||||
|
BadgeDeviceStatusRedisDAO.OrderInfo currentOrder = badgeDeviceStatusRedisDAO.getCurrentOrder(deviceId);
|
||||||
|
Long orderId = currentOrder != null ? currentOrder.getOrderId() : null;
|
||||||
|
|
||||||
|
publishAuditEvent("COMPLETE_SUPPRESSED_INVALID", deviceId, deviceKey, areaId, orderId,
|
||||||
"作业时长不足,抑制自动完成", data);
|
"作业时长不足,抑制自动完成", data);
|
||||||
|
|
||||||
// 3. 清除丢失记录(允许重新进入)
|
// 3. 清除丢失记录(允许重新进入)
|
||||||
@@ -280,7 +284,7 @@ public class SignalLossRuleProcessor {
|
|||||||
auditData.put("durationMs", durationMs);
|
auditData.put("durationMs", durationMs);
|
||||||
auditData.put("lastLossTime", lastLossTime);
|
auditData.put("lastLossTime", lastLossTime);
|
||||||
|
|
||||||
publishAuditEvent("BEACON_COMPLETE_REQUESTED", deviceId, deviceKey, areaId,
|
publishAuditEvent("BEACON_COMPLETE_REQUESTED", deviceId, deviceKey, areaId, currentOrder.getOrderId(),
|
||||||
"信号丢失超时自动完成", auditData);
|
"信号丢失超时自动完成", auditData);
|
||||||
|
|
||||||
// 5. 清理 Redis 数据
|
// 5. 清理 Redis 数据
|
||||||
@@ -302,7 +306,7 @@ public class SignalLossRuleProcessor {
|
|||||||
* 发布审计事件
|
* 发布审计事件
|
||||||
*/
|
*/
|
||||||
private void publishAuditEvent(String auditType, Long deviceId, String deviceKey,
|
private void publishAuditEvent(String auditType, Long deviceId, String deviceKey,
|
||||||
Long areaId, String message, Map<String, Object> data) {
|
Long areaId, Long orderId, String message, Map<String, Object> data) {
|
||||||
try {
|
try {
|
||||||
CleanOrderAuditEvent event = CleanOrderAuditEvent.builder()
|
CleanOrderAuditEvent event = CleanOrderAuditEvent.builder()
|
||||||
.eventId(java.util.UUID.randomUUID().toString())
|
.eventId(java.util.UUID.randomUUID().toString())
|
||||||
@@ -310,13 +314,14 @@ public class SignalLossRuleProcessor {
|
|||||||
.deviceId(deviceId)
|
.deviceId(deviceId)
|
||||||
.deviceKey(deviceKey)
|
.deviceKey(deviceKey)
|
||||||
.areaId(areaId)
|
.areaId(areaId)
|
||||||
|
.orderId(orderId)
|
||||||
.message(message)
|
.message(message)
|
||||||
.data(data)
|
.data(data)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
rocketMQTemplate.syncSend(CleanOrderTopics.ORDER_AUDIT, MessageBuilder.withPayload(event).build());
|
rocketMQTemplate.syncSend(CleanOrderTopics.ORDER_AUDIT, MessageBuilder.withPayload(event).build());
|
||||||
|
|
||||||
log.debug("[SignalLoss] 发布审计事件:auditType={}, deviceId={}", auditType, deviceId);
|
log.debug("[SignalLoss] 发布审计事件:auditType={}, deviceId={}, orderId={}", auditType, deviceId, orderId);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("[SignalLoss] 发布审计事件失败:auditType={}, deviceId={}", auditType, deviceId, e);
|
log.error("[SignalLoss] 发布审计事件失败:auditType={}, deviceId={}", auditType, deviceId, e);
|
||||||
}
|
}
|
||||||
@@ -330,7 +335,7 @@ public class SignalLossRuleProcessor {
|
|||||||
data.put("tts", text);
|
data.put("tts", text);
|
||||||
data.put("timestamp", System.currentTimeMillis());
|
data.put("timestamp", System.currentTimeMillis());
|
||||||
|
|
||||||
publishAuditEvent("TTS_REQUEST", deviceId, null, null, text, data);
|
publishAuditEvent("TTS_REQUEST", deviceId, null, null, null, text, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
package com.viewsh.module.ops.environment.integration.consumer;
|
package com.viewsh.module.ops.environment.integration.consumer;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.viewsh.module.ops.environment.constants.CleanNotificationConstants;
|
import com.viewsh.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||||
import com.viewsh.module.ops.environment.integration.dto.CleanOrderAuditEventDTO;
|
|
||||||
import com.viewsh.module.ops.environment.service.voice.VoiceBroadcastService;
|
|
||||||
import com.viewsh.module.ops.dal.dataobject.workorder.OpsOrderDO;
|
import com.viewsh.module.ops.dal.dataobject.workorder.OpsOrderDO;
|
||||||
import com.viewsh.module.ops.dal.mysql.workorder.OpsOrderMapper;
|
import com.viewsh.module.ops.dal.mysql.workorder.OpsOrderMapper;
|
||||||
import com.viewsh.module.ops.enums.WorkOrderStatusEnum;
|
import com.viewsh.module.ops.enums.WorkOrderStatusEnum;
|
||||||
|
import com.viewsh.module.ops.environment.constants.CleanNotificationConstants;
|
||||||
|
import com.viewsh.module.ops.environment.integration.dto.CleanOrderAuditEventDTO;
|
||||||
|
import com.viewsh.module.ops.environment.service.voice.VoiceBroadcastService;
|
||||||
import com.viewsh.module.ops.infrastructure.log.enumeration.EventDomain;
|
import com.viewsh.module.ops.infrastructure.log.enumeration.EventDomain;
|
||||||
import com.viewsh.module.ops.infrastructure.log.enumeration.EventLevel;
|
import com.viewsh.module.ops.infrastructure.log.enumeration.EventLevel;
|
||||||
|
import com.viewsh.module.ops.infrastructure.log.recorder.EventLogRecord;
|
||||||
import com.viewsh.module.ops.infrastructure.log.recorder.EventLogRecorder;
|
import com.viewsh.module.ops.infrastructure.log.recorder.EventLogRecorder;
|
||||||
import com.viewsh.framework.mybatis.core.query.LambdaQueryWrapperX;
|
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.rocketmq.spring.annotation.ConsumeMode;
|
import org.apache.rocketmq.spring.annotation.ConsumeMode;
|
||||||
@@ -109,11 +110,18 @@ public class CleanOrderAuditEventHandler implements RocketMQListener<String> {
|
|||||||
String eventType = event.getAuditType() != null ? event.getAuditType() : "AUDIT";
|
String eventType = event.getAuditType() != null ? event.getAuditType() : "AUDIT";
|
||||||
|
|
||||||
// 2. 记录审计日志
|
// 2. 记录审计日志
|
||||||
eventLogRecorder.info("clean", domain, eventType,
|
eventLogRecorder.record(
|
||||||
event.getMessage(),
|
EventLogRecord.builder()
|
||||||
event.getOrderId(),
|
.module("clean")
|
||||||
event.getDeviceId(),
|
.domain(domain)
|
||||||
null);
|
.eventType(eventType)
|
||||||
|
.message(event.getMessage())
|
||||||
|
.targetId(event.getOrderId())
|
||||||
|
.targetType(event.getOrderId() != null ? "order" : null)
|
||||||
|
.deviceId(event.getDeviceId())
|
||||||
|
.level(level)
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
|
||||||
log.debug("[CleanOrderAuditEventHandler] 审计日志已记录: eventId={}, auditType={}",
|
log.debug("[CleanOrderAuditEventHandler] 审计日志已记录: eventId={}, auditType={}",
|
||||||
event.getEventId(), event.getAuditType());
|
event.getEventId(), event.getAuditType());
|
||||||
|
|||||||
@@ -335,18 +335,10 @@ public class CleanOrderEndToEndTest {
|
|||||||
|
|
||||||
// Verify
|
// Verify
|
||||||
// 1. Log recorded
|
// 1. Log recorded
|
||||||
verify(eventLogRecorder).info(
|
verify(eventLogRecorder).record(any());
|
||||||
eq("clean"),
|
|
||||||
any(),
|
|
||||||
eq("TTS_REQUEST"),
|
|
||||||
contains("TTS Message"),
|
|
||||||
any(),
|
|
||||||
eq(5001L),
|
|
||||||
any()
|
|
||||||
);
|
|
||||||
|
|
||||||
// 2. TTS sent
|
// 2. TTS sent (orderId can be null for TTS_REQUEST events)
|
||||||
verify(voiceBroadcastService).broadcast(eq(5001L), contains("请回到作业区域"), any(Long.class));
|
verify(voiceBroadcastService).broadcast(eq(5001L), contains("请回到作业区域"), isNull());
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==========================================
|
// ==========================================
|
||||||
|
|||||||
Reference in New Issue
Block a user