fix(beacon): 修复信标检测重复触发到岗事件bug - determineState优先检查本地arrivedTime判断状态 - 离岗警告阶段保持IN_AREA状态不清除arrivedTime - arrivedTime仅在工单完成时由SignalLossRuleProcessor清除

This commit is contained in:
lzh
2026-01-30 00:27:53 +08:00
parent 260d4b8220
commit 6ff40b61d7

View File

@@ -58,9 +58,9 @@ public class BeaconDetectionRuleProcessor {
* <p>
* 在设备属性上报处理流程中调用此方法
*
* @param deviceId 设备ID
* @param identifier 属性标识符bluetoothDevices
* @param propertyValue 属性值(蓝牙设备数组)
* @param deviceId 设备ID
* @param identifier 属性标识符bluetoothDevices
* @param propertyValue 属性值(蓝牙设备数组)
*/
public void processPropertyChange(Long deviceId, String identifier, Object propertyValue) {
// 1. 检查是否是蓝牙属性
@@ -71,8 +71,8 @@ public class BeaconDetectionRuleProcessor {
log.debug("[BeaconDetection] 收到蓝牙属性deviceId={}", deviceId);
// 2. 获取工牌设备的配置包含区域ID
CleanOrderIntegrationConfigService.AreaDeviceConfigWrapper badgeConfigWrapper =
configService.getConfigWrapperByDeviceId(deviceId);
CleanOrderIntegrationConfigService.AreaDeviceConfigWrapper badgeConfigWrapper = configService
.getConfigWrapperByDeviceId(deviceId);
if (badgeConfigWrapper == null || badgeConfigWrapper.getAreaId() == null) {
log.debug("[BeaconDetection] 工牌设备无区域配置deviceId={}", deviceId);
@@ -82,8 +82,8 @@ public class BeaconDetectionRuleProcessor {
Long areaId = badgeConfigWrapper.getAreaId();
// 3. 获取该区域的信标配置(从 BEACON 类型的设备获取)
CleanOrderIntegrationConfigService.AreaDeviceConfigWrapper beaconConfigWrapper =
configService.getConfigByAreaIdAndRelationType(areaId, "BEACON");
CleanOrderIntegrationConfigService.AreaDeviceConfigWrapper beaconConfigWrapper = configService
.getConfigByAreaIdAndRelationType(areaId, "BEACON");
if (beaconConfigWrapper == null || beaconConfigWrapper.getConfig() == null) {
log.debug("[BeaconDetection] 区域无信标配置areaId={}", areaId);
@@ -110,19 +110,17 @@ public class BeaconDetectionRuleProcessor {
List<Integer> window = windowRedisDAO.getWindow(deviceId, areaId);
// 6. 获取设备当前工单状态
BadgeDeviceStatusRedisDAO.OrderInfo currentOrder =
badgeDeviceStatusRedisDAO.getCurrentOrder(deviceId);
BadgeDeviceStatusRedisDAO.OrderInfo currentOrder = badgeDeviceStatusRedisDAO.getCurrentOrder(deviceId);
// 7. 确定当前状态
RssiSlidingWindowDetector.AreaState currentState = determineState(currentOrder, areaId);
RssiSlidingWindowDetector.AreaState currentState = determineState(currentOrder, areaId, deviceId);
// 8. 执行检测
RssiSlidingWindowDetector.DetectionResult result = detector.detect(
window,
beaconConfig.getEnter(),
beaconConfig.getExit(),
currentState
);
currentState);
// 9. 处理检测结果
switch (result) {
@@ -142,8 +140,8 @@ public class BeaconDetectionRuleProcessor {
* 处理到达确认
*/
private void handleArriveConfirmed(Long deviceId, Long areaId, List<Integer> window,
BeaconPresenceConfig beaconConfig,
CleanOrderIntegrationConfigService.AreaDeviceConfigWrapper badgeConfigWrapper) {
BeaconPresenceConfig beaconConfig,
CleanOrderIntegrationConfigService.AreaDeviceConfigWrapper badgeConfigWrapper) {
log.info("[BeaconDetection] 到达确认deviceId={}, areaId={}, window={}",
deviceId, areaId, window);
@@ -177,10 +175,13 @@ public class BeaconDetectionRuleProcessor {
* 处理离开确认
*/
private void handleLeaveConfirmed(Long deviceId, Long areaId, List<Integer> window,
BeaconPresenceConfig beaconConfig) {
BeaconPresenceConfig beaconConfig) {
log.info("[BeaconDetection] 离开确认deviceId={}, areaId={}, window={}",
deviceId, areaId, window);
// 注意离岗警告阶段不清除arrivedTime保持IN_AREA状态
// arrivedTime在工单完成时由SignalLossRuleProcessor.cleanupRedisData清除
// P0 插队校验:检查当前工单是否属于正在检查的区域
if (isSwitchingOrder(deviceId, areaId)) {
log.debug("[BeaconDetection][P0Interrupt] 检测到工单切换,跳过区域 {} 的离岗处理",
@@ -201,8 +202,8 @@ public class BeaconDetectionRuleProcessor {
// 2. 发送警告
publishTtsEvent(deviceId, "你已离开当前区域," +
(exitConfig.getLossTimeoutMinutes() > 0 ?
exitConfig.getLossTimeoutMinutes() + "分钟内工单将自动结算" : "工单将自动结算"));
(exitConfig.getLossTimeoutMinutes() > 0 ? exitConfig.getLossTimeoutMinutes() + "分钟内工单将自动结算"
: "工单将自动结算"));
// 3. 发布审计日志
Map<String, Object> data = new HashMap<>();
@@ -225,9 +226,17 @@ public class BeaconDetectionRuleProcessor {
*/
private void publishArriveEvent(Long deviceId, String deviceKey, Long areaId, Map<String, Object> triggerData) {
try {
// 获取当前工单信息,没有工单不发布事件
BadgeDeviceStatusRedisDAO.OrderInfo currentOrder = badgeDeviceStatusRedisDAO.getCurrentOrder(deviceId);
if (currentOrder == null || currentOrder.getOrderId() == null) {
log.warn("[BeaconDetection] 设备无当前工单,跳过到岗事件: deviceId={}, areaId={}", deviceId, areaId);
return;
}
CleanOrderArriveEvent event = CleanOrderArriveEvent.builder()
.eventId(java.util.UUID.randomUUID().toString())
.orderType("CLEAN")
.orderId(currentOrder.getOrderId())
.deviceId(deviceId)
.deviceKey(deviceKey)
.areaId(areaId)
@@ -237,8 +246,8 @@ public class BeaconDetectionRuleProcessor {
rocketMQTemplate.syncSend(CleanOrderTopics.ORDER_ARRIVE, MessageBuilder.withPayload(event).build());
log.info("[BeaconDetection] 发布到岗事件eventId={}, deviceId={}, areaId={}",
event.getEventId(), deviceId, areaId);
log.info("[BeaconDetection] 发布到岗事件eventId={}, deviceId={}, areaId={}, orderId={}",
event.getEventId(), deviceId, areaId, currentOrder.getOrderId());
} catch (Exception e) {
log.error("[BeaconDetection] 发布到岗事件失败deviceId={}, areaId={}", deviceId, areaId, e);
}
@@ -248,7 +257,7 @@ public class BeaconDetectionRuleProcessor {
* 发布审计事件
*/
private void publishAuditEvent(String auditType, Long deviceId, String deviceKey,
Long areaId, String message, Map<String, Object> data) {
Long areaId, String message, Map<String, Object> data) {
try {
CleanOrderAuditEvent event = CleanOrderAuditEvent.builder()
.eventId(java.util.UUID.randomUUID().toString())
@@ -284,14 +293,24 @@ public class BeaconDetectionRuleProcessor {
* 确定当前状态
*/
private RssiSlidingWindowDetector.AreaState determineState(
BadgeDeviceStatusRedisDAO.OrderInfo currentOrder, Long areaId) {
BadgeDeviceStatusRedisDAO.OrderInfo currentOrder, Long areaId, Long deviceId) {
if (currentOrder == null) {
return RssiSlidingWindowDetector.AreaState.OUT_AREA;
// 优先检查IoT本地到岗记录避免依赖跨模块异步同步
Long arrivedTime = arrivedTimeRedisDAO.getArrivedTime(deviceId, areaId);
if (arrivedTime != null) {
log.debug("[BeaconDetection] 本地状态:已到岗, deviceId={}, areaId={}, arrivedTime={}",
deviceId, areaId, arrivedTime);
return RssiSlidingWindowDetector.AreaState.IN_AREA;
}
// 检查工单状态和区域是否匹配
if ("ARRIVED".equals(currentOrder.getStatus()) && areaId.equals(currentOrder.getAreaId())) {
// 降级检查Ops模块的工单状态向后兼容
if (currentOrder != null
&& "ARRIVED".equals(currentOrder.getStatus())
&& areaId.equals(currentOrder.getAreaId())) {
// 同步本地状态(修复历史数据)
arrivedTimeRedisDAO.recordArrivedTime(deviceId, areaId, System.currentTimeMillis());
log.info("[BeaconDetection] 从Ops状态同步本地到岗记录deviceId={}, areaId={}",
deviceId, areaId);
return RssiSlidingWindowDetector.AreaState.IN_AREA;
}
@@ -308,8 +327,7 @@ public class BeaconDetectionRuleProcessor {
* @return true-工单切换场景false-正常离岗场景
*/
private boolean isSwitchingOrder(Long deviceId, Long areaId) {
BadgeDeviceStatusRedisDAO.OrderInfo currentOrder =
badgeDeviceStatusRedisDAO.getCurrentOrder(deviceId);
BadgeDeviceStatusRedisDAO.OrderInfo currentOrder = badgeDeviceStatusRedisDAO.getCurrentOrder(deviceId);
return currentOrder != null && !currentOrder.getAreaId().equals(areaId);
}
}