diff --git a/viewsh-module-iot/viewsh-module-iot-server/src/main/java/com/viewsh/module/iot/dal/dataobject/integration/clean/CleanOrderIntegrationConfig.java b/viewsh-module-iot/viewsh-module-iot-server/src/main/java/com/viewsh/module/iot/dal/dataobject/integration/clean/CleanOrderIntegrationConfig.java index c6e3f23..da78be8 100644 --- a/viewsh-module-iot/viewsh-module-iot-server/src/main/java/com/viewsh/module/iot/dal/dataobject/integration/clean/CleanOrderIntegrationConfig.java +++ b/viewsh-module-iot/viewsh-module-iot-server/src/main/java/com/viewsh/module/iot/dal/dataobject/integration/clean/CleanOrderIntegrationConfig.java @@ -7,6 +7,9 @@ import lombok.Data; *

* 这是 IoT 设备与保洁工单集成的总配置类,包含所有子配置 * 存储在 Ops 模块的 {@code ops_area_device_relation.config_data} 字段中 + *

+ * 注意:此配置仅包含区域级配置(如客流阈值、信标检测), + * 设备级配置(如按键映射)应存储在 {@code iot_device.config} 字段中 * * @author AI */ @@ -26,11 +29,4 @@ public class CleanOrderIntegrationConfig { * 用于基于工牌蓝牙信标检测自动确认到岗/离岗 */ private BeaconPresenceConfig beaconPresence; - - /** - * 按键事件配置 - *

- * 用于工牌按键事件的处理 - */ - private ButtonEventConfig buttonEvent; } diff --git a/viewsh-module-iot/viewsh-module-iot-server/src/main/java/com/viewsh/module/iot/service/integration/clean/CleanOrderIntegrationConfigService.java b/viewsh-module-iot/viewsh-module-iot-server/src/main/java/com/viewsh/module/iot/service/integration/clean/CleanOrderIntegrationConfigService.java index 51007de..c5b8d86 100644 --- a/viewsh-module-iot/viewsh-module-iot-server/src/main/java/com/viewsh/module/iot/service/integration/clean/CleanOrderIntegrationConfigService.java +++ b/viewsh-module-iot/viewsh-module-iot-server/src/main/java/com/viewsh/module/iot/service/integration/clean/CleanOrderIntegrationConfigService.java @@ -13,15 +13,30 @@ import com.viewsh.module.iot.dal.dataobject.integration.clean.CleanOrderIntegrat public interface CleanOrderIntegrationConfigService { /** - * 根据设备ID查询配置包装器(包含完整信息) + * 根据设备ID查询配置包装器 *

- * 返回包含设备ID、区域ID、关联类型等完整信息的配置包装器 + * 适用于一对一关系的设备(如 TRAFFIC_COUNTER),设备只关联一个区域 + *

+ * 内部实现:先通过 deviceId 获取 areaId,再查询配置 * * @param deviceId 设备ID * @return 配置包装器,如果不存在或未启用返回 null */ AreaDeviceConfigWrapper getConfigWrapperByDeviceId(Long deviceId); + /** + * 根据设备ID和区域ID查询配置包装器(包含完整信息) + *

+ * 返回包含设备ID、区域ID、关联类型等完整信息的配置包装器 + *

+ * 缓存Key: ops:area:{areaId}:type:{relationType} + * + * @param deviceId 设备ID + * @param areaId 区域ID + * @return 配置包装器,如果不存在或未启用返回 null + */ + AreaDeviceConfigWrapper getConfigWrapperByDeviceIdAndAreaId(Long deviceId, Long areaId); + /** * 根据区域ID查询所有启用的配置(带缓存) *

@@ -72,6 +87,16 @@ public interface CleanOrderIntegrationConfigService { */ void evictAreaCache(Long areaId); + /** + * 转换配置数据 Map -> CleanOrderIntegrationConfig + *

+ * 公开方法,供其他模块(如 TrafficThresholdRuleProcessor)调用 + * + * @param configData 配置数据 Map + * @return 集成配置对象,如果输入为空返回 null + */ + CleanOrderIntegrationConfig convertConfig(java.util.Map configData); + /** * 区域设备配置包装类 *

diff --git a/viewsh-module-iot/viewsh-module-iot-server/src/main/java/com/viewsh/module/iot/service/integration/clean/CleanOrderIntegrationConfigServiceImpl.java b/viewsh-module-iot/viewsh-module-iot-server/src/main/java/com/viewsh/module/iot/service/integration/clean/CleanOrderIntegrationConfigServiceImpl.java index 3b190b5..81db962 100644 --- a/viewsh-module-iot/viewsh-module-iot-server/src/main/java/com/viewsh/module/iot/service/integration/clean/CleanOrderIntegrationConfigServiceImpl.java +++ b/viewsh-module-iot/viewsh-module-iot-server/src/main/java/com/viewsh/module/iot/service/integration/clean/CleanOrderIntegrationConfigServiceImpl.java @@ -1,209 +1,320 @@ -package com.viewsh.module.iot.service.integration.clean; - -import com.viewsh.framework.common.pojo.CommonResult; -import com.viewsh.framework.common.util.json.JsonUtils; -import com.viewsh.module.iot.dal.dataobject.integration.clean.CleanOrderIntegrationConfig; -import com.viewsh.module.ops.api.area.AreaDeviceApi; -import com.viewsh.module.ops.api.area.AreaDeviceDTO; -import com.viewsh.module.ops.api.area.DeviceRelationDTO; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.springframework.data.redis.core.StringRedisTemplate; -import org.springframework.stereotype.Service; - -import java.util.List; -import java.util.stream.Collectors; - -/** - * 保洁工单集成配置 Service 实现类 - * - * @author AI - */ -@Service -@Slf4j -public class CleanOrderIntegrationConfigServiceImpl implements CleanOrderIntegrationConfigService { - - /** - * Redis Key 前缀(与 Ops 模块保持一致) - */ - private static final String DEVICE_CACHE_KEY_PREFIX = "ops:area:device:"; - private static final String AREA_TYPE_CACHE_KEY_PREFIX = "ops:area:%s:type:%s"; - private static final String NULL_CACHE = "NULL"; - - /** - * Ops 模块区域设备 API Client - */ - @Resource - private AreaDeviceApi areaDeviceApi; - - @Resource - private StringRedisTemplate stringRedisTemplate; - - @Override - public List getConfigsByAreaId(Long areaId) { - log.debug("[CleanOrderConfig] 查询区域配置:areaId={}", areaId); - - // 通过 Feign 调用 Ops 模块获取所有关联设备 - CommonResult> result = areaDeviceApi.getDevicesByAreaAndType(areaId, null); - - if (result == null || !result.isSuccess() || result.getData() == null) { - log.warn("[CleanOrderConfig] 调用 Ops 模块获取区域配置失败:areaId={}", areaId); - return List.of(); - } - - return result.getData().stream() - .map(this::wrapDto) - .collect(Collectors.toList()); - } - - @Override - public List getConfigsByAreaIdAndRelationType(Long areaId, String relationType) { - log.debug("[CleanOrderConfig] 查询区域配置:areaId={}, relationType={}", areaId, relationType); - - // 通过 Feign 调用 Ops 模块获取关联设备 - CommonResult> result = areaDeviceApi.getDevicesByAreaAndType(areaId, relationType); - - if (result == null || !result.isSuccess() || result.getData() == null) { - log.warn("[CleanOrderConfig] 调用 Ops 模块获取区域配置失败:areaId={}, relationType={}", areaId, relationType); - return List.of(); - } - - return result.getData().stream() - .map(this::wrapDto) - .collect(Collectors.toList()); - } - - @Override - public AreaDeviceConfigWrapper getConfigByAreaIdAndRelationType(Long areaId, String relationType) { - log.debug("[CleanOrderConfig] 查询单个区域配置:areaId={}, relationType={}", areaId, relationType); - - // 1. 先读 Redis 缓存 - String cacheKey = String.format(AREA_TYPE_CACHE_KEY_PREFIX, areaId, relationType); - try { - String cached = stringRedisTemplate.opsForValue().get(cacheKey); - if (cached != null) { - if (NULL_CACHE.equals(cached)) { - log.debug("[CleanOrderConfig] 命中空值缓存:areaId={}, relationType={}", areaId, relationType); - return null; - } - log.debug("[CleanOrderConfig] 命中 Redis 缓存:areaId={}, relationType={}", areaId, relationType); - AreaDeviceDTO dto = JsonUtils.parseObject(cached, AreaDeviceDTO.class); - return wrapDto(dto); - } - } catch (Exception e) { - log.warn("[CleanOrderConfig] 读取 Redis 缓存失败:areaId={}, relationType={}", areaId, relationType, e); - } - - // 2. 缓存未命中,调用 Ops 模块 - CommonResult> result = areaDeviceApi.getDevicesByAreaAndType(areaId, relationType); - - if (result == null || !result.isSuccess() || result.getData() == null) { - log.warn("[CleanOrderConfig] 调用 Ops 模块获取区域配置失败:areaId={}, relationType={}", areaId, relationType); - return null; - } - - // 返回第一个启用的配置 - return result.getData().stream() - .filter(dto -> dto.getEnabled() != null && dto.getEnabled()) - .findFirst() - .map(this::wrapDto) - .orElse(null); - } - - @Override - public AreaDeviceConfigWrapper getConfigWrapperByDeviceId(Long deviceId) { - log.debug("[CleanOrderConfig] 查询设备完整配置:deviceId={}", deviceId); - - // 1. 先读 Redis 缓存 - String cacheKey = DEVICE_CACHE_KEY_PREFIX + deviceId; - try { - String cached = stringRedisTemplate.opsForValue().get(cacheKey); - if (cached != null) { - if (NULL_CACHE.equals(cached)) { - log.debug("[CleanOrderConfig] 命中空值缓存:deviceId={}", deviceId); - return null; - } - log.debug("[CleanOrderConfig] 命中 Redis 缓存:deviceId={}", deviceId); - AreaDeviceDTO dto = JsonUtils.parseObject(cached, AreaDeviceDTO.class); - return wrapDto(dto); - } - } catch (Exception e) { - log.warn("[CleanOrderConfig] 读取 Redis 缓存失败:deviceId={}", deviceId, e); - } - - // 2. 缓存未命中,调用 Ops 模块 - CommonResult result = areaDeviceApi.getDeviceRelationWithConfig(deviceId); - - if (result == null || !result.isSuccess() || result.getData() == null) { - log.warn("[CleanOrderConfig] 调用 Ops 模块获取设备配置失败:deviceId={}", deviceId); - return null; - } - - DeviceRelationDTO dto = result.getData(); - - // 检查是否启用 - if (dto.getEnabled() == null || !dto.getEnabled()) { - return null; - } - - return wrapDto(dto); - } - - @Override - public void evictCache(Long deviceId) { - // 缓存已由 Ops 模块管理,无需操作 - log.debug("[CleanOrderConfig] evictCache 由 Ops 模块管理:deviceId={}", deviceId); - } - - @Override - public void evictAreaCache(Long areaId) { - // 缓存已由 Ops 模块管理,无需操作 - log.debug("[CleanOrderConfig] evictAreaCache 由 Ops 模块管理:areaId={}", areaId); - } - - /** - * 包装配置数据 - */ - private AreaDeviceConfigWrapper wrapDto(AreaDeviceDTO dto) { - if (dto == null) { - return null; - } - return new AreaDeviceConfigWrapper( - dto.getDeviceId(), - dto.getDeviceKey(), - dto.getProductId(), - dto.getProductKey(), - dto.getAreaId(), - dto.getRelationType(), - convertConfig(dto.getConfigData()) - ); - } - - /** - * 包装配置数据 - */ - private AreaDeviceConfigWrapper wrapDto(DeviceRelationDTO dto) { - if (dto == null) { - return null; - } - return new AreaDeviceConfigWrapper( - dto.getDeviceId(), - dto.getDeviceKey(), - dto.getProductId(), - dto.getProductKey(), - dto.getAreaId(), - dto.getRelationType(), - convertConfig(dto.getConfigData()) - ); - } - - /** - * 转换配置数据 Map -> CleanOrderIntegrationConfig - */ - private CleanOrderIntegrationConfig convertConfig(java.util.Map configData) { - if (configData == null || configData.isEmpty()) { - return null; - } - return JsonUtils.parseObject(JsonUtils.toJsonString(configData), CleanOrderIntegrationConfig.class); - } -} +package com.viewsh.module.iot.service.integration.clean; + +import com.viewsh.framework.common.pojo.CommonResult; +import com.viewsh.framework.common.util.json.JsonUtils; +import com.viewsh.module.iot.dal.dataobject.integration.clean.CleanOrderIntegrationConfig; +import com.viewsh.module.ops.api.area.AreaDeviceApi; +import com.viewsh.module.ops.api.area.AreaDeviceDTO; +import com.viewsh.module.ops.api.area.DeviceRelationDTO; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +/** + * 保洁工单集成配置 Service 实现类 + * + * @author AI + */ +@Service +@Slf4j +public class CleanOrderIntegrationConfigServiceImpl implements CleanOrderIntegrationConfigService { + + /** + * Redis Key 前缀 + */ + private static final String AREA_TYPE_CACHE_KEY_PREFIX = "ops:area:%s:type:%s"; + private static final String DEVICE_INDEX_KEY_PREFIX = "ops:device:index:%s"; + private static final String NULL_CACHE = "NULL"; + + /** + * 设备关联类型枚举 + *

+ * 用于遍历查找设备配置 + */ + private static final String[] DEVICE_RELATION_TYPES = {"BADGE", "BEACON", "TRAFFIC_COUNTER"}; + + /** + * 缓存过期时间 + */ + private static final long DEVICE_INDEX_CACHE_TTL_SECONDS = 3600L; // 1小时 + + /** + * Ops 模块区域设备 API Client + */ + @Resource + private AreaDeviceApi areaDeviceApi; + + @Resource + private StringRedisTemplate stringRedisTemplate; + + @Override + public List getConfigsByAreaId(Long areaId) { + log.debug("[CleanOrderConfig] 查询区域配置:areaId={}", areaId); + + // 通过 Feign 调用 Ops 模块获取所有关联设备 + CommonResult> result = areaDeviceApi.getDevicesByAreaAndType(areaId, null); + + if (result == null || !result.isSuccess() || result.getData() == null) { + log.warn("[CleanOrderConfig] 调用 Ops 模块获取区域配置失败:areaId={}", areaId); + return List.of(); + } + + return result.getData().stream() + .map(this::wrapDto) + .collect(Collectors.toList()); + } + + @Override + public List getConfigsByAreaIdAndRelationType(Long areaId, String relationType) { + log.debug("[CleanOrderConfig] 查询区域配置:areaId={}, relationType={}", areaId, relationType); + + // 通过 Feign 调用 Ops 模块获取关联设备 + CommonResult> result = areaDeviceApi.getDevicesByAreaAndType(areaId, relationType); + + if (result == null || !result.isSuccess() || result.getData() == null) { + log.warn("[CleanOrderConfig] 调用 Ops 模块获取区域配置失败:areaId={}, relationType={}", areaId, relationType); + return List.of(); + } + + return result.getData().stream() + .map(this::wrapDto) + .collect(Collectors.toList()); + } + + @Override + public AreaDeviceConfigWrapper getConfigByAreaIdAndRelationType(Long areaId, String relationType) { + log.debug("[CleanOrderConfig] 查询单个区域配置:areaId={}, relationType={}", areaId, relationType); + + // 1. 先读 Redis 缓存 + String cacheKey = String.format(AREA_TYPE_CACHE_KEY_PREFIX, areaId, relationType); + try { + String cached = stringRedisTemplate.opsForValue().get(cacheKey); + if (cached != null) { + if (NULL_CACHE.equals(cached)) { + log.debug("[CleanOrderConfig] 命中空值缓存:areaId={}, relationType={}", areaId, relationType); + return null; + } + log.debug("[CleanOrderConfig] 命中 Redis 缓存:areaId={}, relationType={}", areaId, relationType); + AreaDeviceDTO dto = JsonUtils.parseObject(cached, AreaDeviceDTO.class); + return wrapDto(dto); + } + } catch (Exception e) { + log.warn("[CleanOrderConfig] 读取 Redis 缓存失败:areaId={}, relationType={}", areaId, relationType, e); + } + + // 2. 缓存未命中,调用 Ops 模块 + CommonResult> result = areaDeviceApi.getDevicesByAreaAndType(areaId, relationType); + + if (result == null || !result.isSuccess() || result.getData() == null) { + log.warn("[CleanOrderConfig] 调用 Ops 模块获取区域配置失败:areaId={}, relationType={}", areaId, relationType); + return null; + } + + // 返回第一个启用的配置 + return result.getData().stream() + .filter(dto -> dto.getEnabled() != null && dto.getEnabled()) + .findFirst() + .map(this::wrapDto) + .orElse(null); + } + + @Override + public AreaDeviceConfigWrapper getConfigWrapperByDeviceId(Long deviceId) { + if (deviceId == null) { + return null; + } + + log.debug("[CleanOrderConfig] 查询设备配置:deviceId={}", deviceId); + + // 1. 查询设备反向索引缓存 + String indexKey = String.format(DEVICE_INDEX_KEY_PREFIX, deviceId); + try { + Map indexMap = stringRedisTemplate.opsForHash().entries(indexKey); + + Long areaId = null; + String relationType = null; + + if (indexMap != null && !indexMap.isEmpty()) { + // 命中缓存 + Object areaIdObj = indexMap.get("areaId"); + Object relationTypeObj = indexMap.get("relationType"); + Object enabledObj = indexMap.get("enabled"); + + if (enabledObj != null && Boolean.FALSE.equals(Boolean.parseBoolean(enabledObj.toString()))) { + log.debug("[CleanOrderConfig] 设备未启用(缓存):deviceId={}", deviceId); + return null; + } + + if (areaIdObj != null) { + areaId = Long.parseLong(areaIdObj.toString()); + } + if (relationTypeObj != null) { + relationType = relationTypeObj.toString(); + } + + log.debug("[CleanOrderConfig] 命中设备索引缓存:deviceId={}, areaId={}, relationType={}", + deviceId, areaId, relationType); + } + + // 2. 缓存未命中,调用 Ops API 获取设备关联 + if (areaId == null || relationType == null) { + CommonResult result = areaDeviceApi.getDeviceRelation(deviceId); + + if (result == null || !result.isSuccess() || result.getData() == null) { + log.debug("[CleanOrderConfig] 未找到设备关联:deviceId={}", deviceId); + return null; + } + + DeviceRelationDTO dto = result.getData(); + if (dto.getEnabled() == null || !dto.getEnabled()) { + log.debug("[CleanOrderConfig] 设备未启用:deviceId={}", deviceId); + return null; + } + + areaId = dto.getAreaId(); + relationType = dto.getRelationType(); + + if (areaId == null || relationType == null) { + log.debug("[CleanOrderConfig] 设备关联信息不完整:deviceId={}, areaId={}, relationType={}", + deviceId, areaId, relationType); + return null; + } + + // 3. 回写设备索引缓存 + writeDeviceIndexCache(deviceId, areaId, relationType, dto.getEnabled()); + + log.debug("[CleanOrderConfig] 从Ops获取设备关联并写入缓存:deviceId={}, areaId={}, relationType={}", + deviceId, areaId, relationType); + } + + // 4. 使用 areaId + relationType 查询配置(带缓存) + return getConfigByAreaIdAndRelationType(areaId, relationType); + + } catch (Exception e) { + log.error("[CleanOrderConfig] 查询设备配置失败:deviceId={}", deviceId, e); + return null; + } + } + + /** + * 写入设备索引缓存 + */ + private void writeDeviceIndexCache(Long deviceId, Long areaId, String relationType, Boolean enabled) { + try { + String cacheKey = String.format(DEVICE_INDEX_KEY_PREFIX, deviceId); + Map indexData = new HashMap<>(); + indexData.put("areaId", String.valueOf(areaId)); + indexData.put("relationType", relationType); + indexData.put("enabled", String.valueOf(enabled)); + + stringRedisTemplate.opsForHash().putAll(cacheKey, indexData); + stringRedisTemplate.expire(cacheKey, DEVICE_INDEX_CACHE_TTL_SECONDS, TimeUnit.SECONDS); + + log.debug("[CleanOrderConfig] 写入设备索引缓存:deviceId={}, areaId={}, relationType={}", + deviceId, areaId, relationType); + } catch (Exception e) { + log.warn("[CleanOrderConfig] 写入设备索引缓存失败:deviceId={}", deviceId, e); + } + } + + /** + * 清除设备索引缓存 + */ + private void evictDeviceIndexCache(Long deviceId) { + try { + String cacheKey = String.format(DEVICE_INDEX_KEY_PREFIX, deviceId); + stringRedisTemplate.delete(cacheKey); + log.debug("[CleanOrderConfig] 清除设备索引缓存:deviceId={}", deviceId); + } catch (Exception e) { + log.warn("[CleanOrderConfig] 清除设备索引缓存失败:deviceId={}", deviceId, e); + } + } + + @Override + public AreaDeviceConfigWrapper getConfigWrapperByDeviceIdAndAreaId(Long deviceId, Long areaId) { + if (deviceId == null || areaId == null) { + return null; + } + + log.debug("[CleanOrderConfig] 查询设备配置:deviceId={}, areaId={}", deviceId, areaId); + + // 由于不知道 relationType���需要遍历所有类型查找匹配的设备 + String[] relationTypes = DEVICE_RELATION_TYPES; + + for (String relationType : relationTypes) { + AreaDeviceConfigWrapper wrapper = getConfigByAreaIdAndRelationType(areaId, relationType); + if (wrapper != null && wrapper.getDeviceId() != null + && wrapper.getDeviceId().equals(deviceId)) { + log.debug("[CleanOrderConfig] 找到设备配置:deviceId={}, areaId={}, type={}", + deviceId, areaId, relationType); + return wrapper; + } + } + + log.debug("[CleanOrderConfig] 未找到设备配置:deviceId={}, areaId={}", deviceId, areaId); + return null; + } + + @Override + public void evictCache(Long deviceId) { + evictDeviceIndexCache(deviceId); + log.debug("[CleanOrderConfig] 已清除设备缓存:deviceId={}", deviceId); + } + + @Override + public void evictAreaCache(Long areaId) { + // 区域配置缓存由 Ops 模块管理 + log.debug("[CleanOrderConfig] evictAreaCache 由 Ops 模块管理:areaId={}", areaId); + } + + @Override + public CleanOrderIntegrationConfig convertConfig(java.util.Map configData) { + if (configData == null || configData.isEmpty()) { + return null; + } + return JsonUtils.parseObject(JsonUtils.toJsonString(configData), CleanOrderIntegrationConfig.class); + } + + /** + * 包装配置数据 + */ + private AreaDeviceConfigWrapper wrapDto(AreaDeviceDTO dto) { + if (dto == null) { + return null; + } + return new AreaDeviceConfigWrapper( + dto.getDeviceId(), + dto.getDeviceKey(), + dto.getProductId(), + dto.getProductKey(), + dto.getAreaId(), + dto.getRelationType(), + convertConfig(dto.getConfigData()) + ); + } + + /** + * 包装配置数据 + */ + private AreaDeviceConfigWrapper wrapDto(DeviceRelationDTO dto) { + if (dto == null) { + return null; + } + return new AreaDeviceConfigWrapper( + dto.getDeviceId(), + dto.getDeviceKey(), + dto.getProductId(), + dto.getProductKey(), + dto.getAreaId(), + dto.getRelationType(), + convertConfig(dto.getConfigData()) + ); + } +} diff --git a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/integration/listener/BadgeDeviceStatusEventListener.java b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/integration/listener/BadgeDeviceStatusEventListener.java index 3eed733..90792b7 100644 --- a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/integration/listener/BadgeDeviceStatusEventListener.java +++ b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/integration/listener/BadgeDeviceStatusEventListener.java @@ -132,17 +132,44 @@ public class BadgeDeviceStatusEventListener { var waitingTasks = orderQueueService.getWaitingTasksByUserIdFromDb(deviceId); switch (newStatus) { case DISPATCHED: - // 工单已推送到工牌,设置工单关联,设备状态转为 BUSY - badgeDeviceStatusService.setCurrentOrder(deviceId, orderId); + // 工单已推送到工牌,设置工单关联和区域信息 + // currentAreaId 应该设置为工单所属区域,而非设备物理位置 badgeDeviceStatusService.updateBadgeStatus(deviceId, BadgeDeviceStatusEnum.BUSY, null, "新工单已推送"); - log.info("[BadgeDeviceStatusEventListener] 工单已推送,设备状态转为 BUSY: deviceId={}, orderId={}", deviceId, - orderId); + + OpsOrderDO order = opsOrderMapper.selectById(orderId); + if (order != null && order.getAreaId() != null) { + badgeDeviceStatusService.setCurrentOrderInfo( + deviceId, orderId, + event.getNewStatus().getStatus(), + order.getAreaId(), + null + ); + log.info("[BadgeDeviceStatusEventListener] 工单已推送,设备状态转为 BUSY: deviceId={}, orderId={}, areaId={}", + deviceId, orderId, order.getAreaId()); + } else { + badgeDeviceStatusService.setCurrentOrder(deviceId, orderId); + log.info("[BadgeDeviceStatusEventListener] 工单已推送,设备状态转为 BUSY: deviceId={}, orderId={}", deviceId, + orderId); + } break; case CONFIRMED: - // 设备按键确认,设备保持 BUSY - badgeDeviceStatusService.setCurrentOrder(deviceId, orderId); - log.debug("[BadgeDeviceStatusEventListener] 工单已确认: deviceId={}, orderId={}", deviceId, orderId); + // 设备按键确认,更新工单关联和区域信息 + // currentAreaId 应该设置为工单所属区域,而非设备物理位置 + OpsOrderDO order = opsOrderMapper.selectById(orderId); + if (order != null && order.getAreaId() != null) { + badgeDeviceStatusService.setCurrentOrderInfo( + deviceId, orderId, + event.getNewStatus().getStatus(), + order.getAreaId(), + null + ); + log.debug("[BadgeDeviceStatusEventListener] 工单已确认,更新区域信息: deviceId={}, orderId={}, areaId={}", + deviceId, orderId, order.getAreaId()); + } else { + badgeDeviceStatusService.setCurrentOrder(deviceId, orderId); + log.debug("[BadgeDeviceStatusEventListener] 工单已确认: deviceId={}, orderId={}", deviceId, orderId); + } break; case ARRIVED: diff --git a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/badge/BadgeDeviceStatusServiceImpl.java b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/badge/BadgeDeviceStatusServiceImpl.java index 769759b..3c2ae2d 100644 --- a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/badge/BadgeDeviceStatusServiceImpl.java +++ b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/badge/BadgeDeviceStatusServiceImpl.java @@ -279,20 +279,19 @@ public class BadgeDeviceStatusServiceImpl implements BadgeDeviceStatusService, I statusMap.put("lastHeartbeatTime", now); } - // 更新实时物理区域信息 (Key2) + // 更新区域信息 + // 注意:currentAreaId 应该表示当前工单所属区域,而非设备物理位置 + // 只有在没有工单时,才同步 IoT 上报的物理位置 if (areaId != null) { - statusMap.put("currentAreaId", areaId); - // 同时更新区域设备索引缓存 (这里注释掉了,保持原样) - // addToAreaIndex(deviceId, areaId); - } else { - // 保持现有实时物理区域信息 - Object existingAreaId = currentMap.get("currentAreaId"); - if (existingAreaId != null) { - statusMap.put("currentAreaId", existingAreaId); - } - Object existingAreaName = currentMap.get("currentAreaName"); - if (existingAreaName != null) { - statusMap.put("currentAreaName", existingAreaName); + // 检查是否有正在执行的工单 + Object currentOrderId = currentMap.get("currentOpsOrderId"); + if (currentOrderId != null) { + // 有工单:保持 currentAreaId 不变(用工单区域),不覆盖 + log.debug("设备 {} 有执行中工单 {},保持 currentAreaId 不变,忽略 IoT 上报的物理位置 {}", + deviceId, currentOrderId, areaId); + } else { + // 无工单:用 IoT 上报的物理位置 + statusMap.put("currentAreaId", areaId); } }