From 6ff40b61d7f4080f175de4eeb39a8743556719aa Mon Sep 17 00:00:00 2001 From: lzh Date: Fri, 30 Jan 2026 00:27:53 +0800 Subject: [PATCH] =?UTF-8?q?fix(beacon):=20=E4=BF=AE=E5=A4=8D=E4=BF=A1?= =?UTF-8?q?=E6=A0=87=E6=A3=80=E6=B5=8B=E9=87=8D=E5=A4=8D=E8=A7=A6=E5=8F=91?= =?UTF-8?q?=E5=88=B0=E5=B2=97=E4=BA=8B=E4=BB=B6bug=20-=20determineState?= =?UTF-8?q?=E4=BC=98=E5=85=88=E6=A3=80=E6=9F=A5=E6=9C=AC=E5=9C=B0arrivedTi?= =?UTF-8?q?me=E5=88=A4=E6=96=AD=E7=8A=B6=E6=80=81=20-=20=E7=A6=BB=E5=B2=97?= =?UTF-8?q?=E8=AD=A6=E5=91=8A=E9=98=B6=E6=AE=B5=E4=BF=9D=E6=8C=81IN=5FAREA?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E4=B8=8D=E6=B8=85=E9=99=A4arrivedTime=20-=20?= =?UTF-8?q?arrivedTime=E4=BB=85=E5=9C=A8=E5=B7=A5=E5=8D=95=E5=AE=8C?= =?UTF-8?q?=E6=88=90=E6=97=B6=E7=94=B1SignalLossRuleProcessor=E6=B8=85?= =?UTF-8?q?=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BeaconDetectionRuleProcessor.java | 72 ++++++++++++------- 1 file changed, 45 insertions(+), 27 deletions(-) diff --git a/viewsh-module-iot/viewsh-module-iot-server/src/main/java/com/viewsh/module/iot/service/rule/clean/processor/BeaconDetectionRuleProcessor.java b/viewsh-module-iot/viewsh-module-iot-server/src/main/java/com/viewsh/module/iot/service/rule/clean/processor/BeaconDetectionRuleProcessor.java index 58e6122..ee1bd2a 100644 --- a/viewsh-module-iot/viewsh-module-iot-server/src/main/java/com/viewsh/module/iot/service/rule/clean/processor/BeaconDetectionRuleProcessor.java +++ b/viewsh-module-iot/viewsh-module-iot-server/src/main/java/com/viewsh/module/iot/service/rule/clean/processor/BeaconDetectionRuleProcessor.java @@ -58,9 +58,9 @@ public class BeaconDetectionRuleProcessor { *

* 在设备属性上报处理流程中调用此方法 * - * @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 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 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 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 data = new HashMap<>(); @@ -225,9 +226,17 @@ public class BeaconDetectionRuleProcessor { */ private void publishArriveEvent(Long deviceId, String deviceKey, Long areaId, Map 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 data) { + Long areaId, String message, Map 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); } }