fix(beacon): 修复信标检测重复触发到岗事件bug - determineState优先检查本地arrivedTime判断状态 - 离岗警告阶段保持IN_AREA状态不清除arrivedTime - arrivedTime仅在工单完成时由SignalLossRuleProcessor清除
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user