refactor(iot): 重构配置模型与实现双层缓存优化

- 将 buttonEvent 配置从区域级移至设备级(iot_device.config)
- 添加 getConfigWrapperByDeviceId 方法支持一对一设备查询
- 实现设备索引缓存 + 区域配置缓存的双层缓存设计
- 添加设备类型常量 DEVICE_RELATION_TYPES
- 完善 JavaDoc 注释说明配置层级划分

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
lzh
2026-02-01 00:57:03 +08:00
parent b73ef4f39f
commit 89e68eceb2
5 changed files with 396 additions and 238 deletions

View File

@@ -7,6 +7,9 @@ import lombok.Data;
* <p> * <p>
* 这是 IoT 设备与保洁工单集成的总配置类,包含所有子配置 * 这是 IoT 设备与保洁工单集成的总配置类,包含所有子配置
* 存储在 Ops 模块的 {@code ops_area_device_relation.config_data} 字段中 * 存储在 Ops 模块的 {@code ops_area_device_relation.config_data} 字段中
* <p>
* 注意:此配置仅包含区域级配置(如客流阈值、信标检测),
* 设备级配置(如按键映射)应存储在 {@code iot_device.config} 字段中
* *
* @author AI * @author AI
*/ */
@@ -26,11 +29,4 @@ public class CleanOrderIntegrationConfig {
* 用于基于工牌蓝牙信标检测自动确认到岗/离岗 * 用于基于工牌蓝牙信标检测自动确认到岗/离岗
*/ */
private BeaconPresenceConfig beaconPresence; private BeaconPresenceConfig beaconPresence;
/**
* 按键事件配置
* <p>
* 用于工牌按键事件的处理
*/
private ButtonEventConfig buttonEvent;
} }

View File

@@ -13,15 +13,30 @@ import com.viewsh.module.iot.dal.dataobject.integration.clean.CleanOrderIntegrat
public interface CleanOrderIntegrationConfigService { public interface CleanOrderIntegrationConfigService {
/** /**
* 根据设备ID查询配置包装器(包含完整信息) * 根据设备ID查询配置包装器
* <p> * <p>
* 返回包含设备ID、区域ID、关联类型等完整信息的配置包装器 * 适用于一对一关系的设备(如 TRAFFIC_COUNTER设备只关联一个区域
* <p>
* 内部实现:先通过 deviceId 获取 areaId再查询配置
* *
* @param deviceId 设备ID * @param deviceId 设备ID
* @return 配置包装器,如果不存在或未启用返回 null * @return 配置包装器,如果不存在或未启用返回 null
*/ */
AreaDeviceConfigWrapper getConfigWrapperByDeviceId(Long deviceId); AreaDeviceConfigWrapper getConfigWrapperByDeviceId(Long deviceId);
/**
* 根据设备ID和区域ID查询配置包装器包含完整信息
* <p>
* 返回包含设备ID、区域ID、关联类型等完整信息的配置包装器
* <p>
* 缓存Key: ops:area:{areaId}:type:{relationType}
*
* @param deviceId 设备ID
* @param areaId 区域ID
* @return 配置包装器,如果不存在或未启用返回 null
*/
AreaDeviceConfigWrapper getConfigWrapperByDeviceIdAndAreaId(Long deviceId, Long areaId);
/** /**
* 根据区域ID查询所有启用的配置带缓存 * 根据区域ID查询所有启用的配置带缓存
* <p> * <p>
@@ -72,6 +87,16 @@ public interface CleanOrderIntegrationConfigService {
*/ */
void evictAreaCache(Long areaId); void evictAreaCache(Long areaId);
/**
* 转换配置数据 Map -> CleanOrderIntegrationConfig
* <p>
* 公开方法,供其他模块(如 TrafficThresholdRuleProcessor调用
*
* @param configData 配置数据 Map
* @return 集成配置对象,如果输入为空返回 null
*/
CleanOrderIntegrationConfig convertConfig(java.util.Map<String, Object> configData);
/** /**
* 区域设备配置包装类 * 区域设备配置包装类
* <p> * <p>

View File

@@ -1,209 +1,320 @@
package com.viewsh.module.iot.service.integration.clean; package com.viewsh.module.iot.service.integration.clean;
import com.viewsh.framework.common.pojo.CommonResult; import com.viewsh.framework.common.pojo.CommonResult;
import com.viewsh.framework.common.util.json.JsonUtils; import com.viewsh.framework.common.util.json.JsonUtils;
import com.viewsh.module.iot.dal.dataobject.integration.clean.CleanOrderIntegrationConfig; 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.AreaDeviceApi;
import com.viewsh.module.ops.api.area.AreaDeviceDTO; import com.viewsh.module.ops.api.area.AreaDeviceDTO;
import com.viewsh.module.ops.api.area.DeviceRelationDTO; import com.viewsh.module.ops.api.area.DeviceRelationDTO;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.List; import java.util.HashMap;
import java.util.stream.Collectors; import java.util.List;
import java.util.Map;
/** import java.util.concurrent.TimeUnit;
* 保洁工单集成配置 Service 实现类 import java.util.stream.Collectors;
*
* @author AI /**
*/ * 保洁工单集成配置 Service 实现类
@Service *
@Slf4j * @author AI
public class CleanOrderIntegrationConfigServiceImpl implements CleanOrderIntegrationConfigService { */
@Service
/** @Slf4j
* Redis Key 前缀(与 Ops 模块保持一致) public class CleanOrderIntegrationConfigServiceImpl implements CleanOrderIntegrationConfigService {
*/
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"; * Redis Key 前缀
private static final String NULL_CACHE = "NULL"; */
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";
* Ops 模块区域设备 API Client private static final String NULL_CACHE = "NULL";
*/
@Resource /**
private AreaDeviceApi areaDeviceApi; * 设备关联类型枚举
* <p>
@Resource * 用于遍历查找设备配置
private StringRedisTemplate stringRedisTemplate; */
private static final String[] DEVICE_RELATION_TYPES = {"BADGE", "BEACON", "TRAFFIC_COUNTER"};
@Override
public List<AreaDeviceConfigWrapper> getConfigsByAreaId(Long areaId) { /**
log.debug("[CleanOrderConfig] 查询区域配置areaId={}", areaId); * 缓存过期时间
*/
// 通过 Feign 调用 Ops 模块获取所有关联设备 private static final long DEVICE_INDEX_CACHE_TTL_SECONDS = 3600L; // 1小时
CommonResult<List<AreaDeviceDTO>> result = areaDeviceApi.getDevicesByAreaAndType(areaId, null);
/**
if (result == null || !result.isSuccess() || result.getData() == null) { * Ops 模块区域设备 API Client
log.warn("[CleanOrderConfig] 调用 Ops 模块获取区域配置失败areaId={}", areaId); */
return List.of(); @Resource
} private AreaDeviceApi areaDeviceApi;
return result.getData().stream() @Resource
.map(this::wrapDto) private StringRedisTemplate stringRedisTemplate;
.collect(Collectors.toList());
} @Override
public List<AreaDeviceConfigWrapper> getConfigsByAreaId(Long areaId) {
@Override log.debug("[CleanOrderConfig] 查询区域配置areaId={}", areaId);
public List<AreaDeviceConfigWrapper> getConfigsByAreaIdAndRelationType(Long areaId, String relationType) {
log.debug("[CleanOrderConfig] 查询区域配置areaId={}, relationType={}", areaId, relationType); // 通过 Feign 调用 Ops 模块获取所有关联设备
CommonResult<List<AreaDeviceDTO>> result = areaDeviceApi.getDevicesByAreaAndType(areaId, null);
// 通过 Feign 调用 Ops 模块获取关联设备
CommonResult<List<AreaDeviceDTO>> result = areaDeviceApi.getDevicesByAreaAndType(areaId, relationType); if (result == null || !result.isSuccess() || result.getData() == null) {
log.warn("[CleanOrderConfig] 调用 Ops 模块获取区域配置失败areaId={}", areaId);
if (result == null || !result.isSuccess() || result.getData() == null) { return List.of();
log.warn("[CleanOrderConfig] 调用 Ops 模块获取区域配置失败areaId={}, relationType={}", areaId, relationType); }
return List.of();
} return result.getData().stream()
.map(this::wrapDto)
return result.getData().stream() .collect(Collectors.toList());
.map(this::wrapDto) }
.collect(Collectors.toList());
} @Override
public List<AreaDeviceConfigWrapper> getConfigsByAreaIdAndRelationType(Long areaId, String relationType) {
@Override log.debug("[CleanOrderConfig] 查询区域配置areaId={}, relationType={}", areaId, relationType);
public AreaDeviceConfigWrapper getConfigByAreaIdAndRelationType(Long areaId, String relationType) {
log.debug("[CleanOrderConfig] 查询单个区域配置areaId={}, relationType={}", areaId, relationType); // 通过 Feign 调用 Ops 模块获取关联设备
CommonResult<List<AreaDeviceDTO>> result = areaDeviceApi.getDevicesByAreaAndType(areaId, relationType);
// 1. 先读 Redis 缓存
String cacheKey = String.format(AREA_TYPE_CACHE_KEY_PREFIX, areaId, relationType); if (result == null || !result.isSuccess() || result.getData() == null) {
try { log.warn("[CleanOrderConfig] 调用 Ops 模块获取区域配置失败areaId={}, relationType={}", areaId, relationType);
String cached = stringRedisTemplate.opsForValue().get(cacheKey); return List.of();
if (cached != null) { }
if (NULL_CACHE.equals(cached)) {
log.debug("[CleanOrderConfig] 命中空值缓存areaId={}, relationType={}", areaId, relationType); return result.getData().stream()
return null; .map(this::wrapDto)
} .collect(Collectors.toList());
log.debug("[CleanOrderConfig] 命中 Redis 缓存areaId={}, relationType={}", areaId, relationType); }
AreaDeviceDTO dto = JsonUtils.parseObject(cached, AreaDeviceDTO.class);
return wrapDto(dto); @Override
} public AreaDeviceConfigWrapper getConfigByAreaIdAndRelationType(Long areaId, String relationType) {
} catch (Exception e) { log.debug("[CleanOrderConfig] 查询单个区域配置areaId={}, relationType={}", areaId, relationType);
log.warn("[CleanOrderConfig] 读取 Redis 缓存失败areaId={}, relationType={}", areaId, relationType, e);
} // 1. 先读 Redis 缓存
String cacheKey = String.format(AREA_TYPE_CACHE_KEY_PREFIX, areaId, relationType);
// 2. 缓存未命中,调用 Ops 模块 try {
CommonResult<List<AreaDeviceDTO>> result = areaDeviceApi.getDevicesByAreaAndType(areaId, relationType); String cached = stringRedisTemplate.opsForValue().get(cacheKey);
if (cached != null) {
if (result == null || !result.isSuccess() || result.getData() == null) { if (NULL_CACHE.equals(cached)) {
log.warn("[CleanOrderConfig] 调用 Ops 模块获取区域配置失败areaId={}, relationType={}", areaId, relationType); log.debug("[CleanOrderConfig] 命中空值缓存areaId={}, relationType={}", areaId, relationType);
return null; return null;
} }
log.debug("[CleanOrderConfig] 命中 Redis 缓存areaId={}, relationType={}", areaId, relationType);
// 返回第一个启用的配置 AreaDeviceDTO dto = JsonUtils.parseObject(cached, AreaDeviceDTO.class);
return result.getData().stream() return wrapDto(dto);
.filter(dto -> dto.getEnabled() != null && dto.getEnabled()) }
.findFirst() } catch (Exception e) {
.map(this::wrapDto) log.warn("[CleanOrderConfig] 读取 Redis 缓存失败areaId={}, relationType={}", areaId, relationType, e);
.orElse(null); }
}
// 2. 缓存未命中,调用 Ops 模块
@Override CommonResult<List<AreaDeviceDTO>> result = areaDeviceApi.getDevicesByAreaAndType(areaId, relationType);
public AreaDeviceConfigWrapper getConfigWrapperByDeviceId(Long deviceId) {
log.debug("[CleanOrderConfig] 查询设备完整配置deviceId={}", deviceId); if (result == null || !result.isSuccess() || result.getData() == null) {
log.warn("[CleanOrderConfig] 调用 Ops 模块获取区域配置失败areaId={}, relationType={}", areaId, relationType);
// 1. 先读 Redis 缓存 return null;
String cacheKey = DEVICE_CACHE_KEY_PREFIX + deviceId; }
try {
String cached = stringRedisTemplate.opsForValue().get(cacheKey); // 返回第一个启用的配置
if (cached != null) { return result.getData().stream()
if (NULL_CACHE.equals(cached)) { .filter(dto -> dto.getEnabled() != null && dto.getEnabled())
log.debug("[CleanOrderConfig] 命中空值缓存deviceId={}", deviceId); .findFirst()
return null; .map(this::wrapDto)
} .orElse(null);
log.debug("[CleanOrderConfig] 命中 Redis 缓存deviceId={}", deviceId); }
AreaDeviceDTO dto = JsonUtils.parseObject(cached, AreaDeviceDTO.class);
return wrapDto(dto); @Override
} public AreaDeviceConfigWrapper getConfigWrapperByDeviceId(Long deviceId) {
} catch (Exception e) { if (deviceId == null) {
log.warn("[CleanOrderConfig] 读取 Redis 缓存失败deviceId={}", deviceId, e); return null;
} }
// 2. 缓存未命中,调用 Ops 模块 log.debug("[CleanOrderConfig] 查询设备配置deviceId={}", deviceId);
CommonResult<DeviceRelationDTO> result = areaDeviceApi.getDeviceRelationWithConfig(deviceId);
// 1. 查询设备反向索引缓存
if (result == null || !result.isSuccess() || result.getData() == null) { String indexKey = String.format(DEVICE_INDEX_KEY_PREFIX, deviceId);
log.warn("[CleanOrderConfig] 调用 Ops 模块获取设备配置失败deviceId={}", deviceId); try {
return null; Map<Object, Object> indexMap = stringRedisTemplate.opsForHash().entries(indexKey);
}
Long areaId = null;
DeviceRelationDTO dto = result.getData(); String relationType = null;
// 检查是否启用 if (indexMap != null && !indexMap.isEmpty()) {
if (dto.getEnabled() == null || !dto.getEnabled()) { // 命中缓存
return null; Object areaIdObj = indexMap.get("areaId");
} Object relationTypeObj = indexMap.get("relationType");
Object enabledObj = indexMap.get("enabled");
return wrapDto(dto);
} if (enabledObj != null && Boolean.FALSE.equals(Boolean.parseBoolean(enabledObj.toString()))) {
log.debug("[CleanOrderConfig] 设备未启用缓存deviceId={}", deviceId);
@Override return null;
public void evictCache(Long deviceId) { }
// 缓存已由 Ops 模块管理,无需操作
log.debug("[CleanOrderConfig] evictCache 由 Ops 模块管理deviceId={}", deviceId); if (areaIdObj != null) {
} areaId = Long.parseLong(areaIdObj.toString());
}
@Override if (relationTypeObj != null) {
public void evictAreaCache(Long areaId) { relationType = relationTypeObj.toString();
// 缓存已由 Ops 模块管理,无需操作 }
log.debug("[CleanOrderConfig] evictAreaCache 由 Ops 模块管理areaId={}", areaId);
} log.debug("[CleanOrderConfig] 命中设备索引缓存deviceId={}, areaId={}, relationType={}",
deviceId, areaId, relationType);
/** }
* 包装配置数据
*/ // 2. 缓存未命中,调用 Ops API 获取设备关联
private AreaDeviceConfigWrapper wrapDto(AreaDeviceDTO dto) { if (areaId == null || relationType == null) {
if (dto == null) { CommonResult<DeviceRelationDTO> result = areaDeviceApi.getDeviceRelation(deviceId);
return null;
} if (result == null || !result.isSuccess() || result.getData() == null) {
return new AreaDeviceConfigWrapper( log.debug("[CleanOrderConfig] 未找到设备关联deviceId={}", deviceId);
dto.getDeviceId(), return null;
dto.getDeviceKey(), }
dto.getProductId(),
dto.getProductKey(), DeviceRelationDTO dto = result.getData();
dto.getAreaId(), if (dto.getEnabled() == null || !dto.getEnabled()) {
dto.getRelationType(), log.debug("[CleanOrderConfig] 设备未启用deviceId={}", deviceId);
convertConfig(dto.getConfigData()) return null;
); }
}
areaId = dto.getAreaId();
/** relationType = dto.getRelationType();
* 包装配置数据
*/ if (areaId == null || relationType == null) {
private AreaDeviceConfigWrapper wrapDto(DeviceRelationDTO dto) { log.debug("[CleanOrderConfig] 设备关联信息不完整deviceId={}, areaId={}, relationType={}",
if (dto == null) { deviceId, areaId, relationType);
return null; return null;
} }
return new AreaDeviceConfigWrapper(
dto.getDeviceId(), // 3. 回写设备索引缓存
dto.getDeviceKey(), writeDeviceIndexCache(deviceId, areaId, relationType, dto.getEnabled());
dto.getProductId(),
dto.getProductKey(), log.debug("[CleanOrderConfig] 从Ops获取设备关联并写入缓存deviceId={}, areaId={}, relationType={}",
dto.getAreaId(), deviceId, areaId, relationType);
dto.getRelationType(), }
convertConfig(dto.getConfigData())
); // 4. 使用 areaId + relationType 查询配置(带缓存)
} return getConfigByAreaIdAndRelationType(areaId, relationType);
/** } catch (Exception e) {
* 转换配置数据 Map -> CleanOrderIntegrationConfig log.error("[CleanOrderConfig] 查询设备配置失败deviceId={}", deviceId, e);
*/ return null;
private CleanOrderIntegrationConfig convertConfig(java.util.Map<String, Object> configData) { }
if (configData == null || configData.isEmpty()) { }
return null;
} /**
return JsonUtils.parseObject(JsonUtils.toJsonString(configData), CleanOrderIntegrationConfig.class); * 写入设备索引缓存
} */
} private void writeDeviceIndexCache(Long deviceId, Long areaId, String relationType, Boolean enabled) {
try {
String cacheKey = String.format(DEVICE_INDEX_KEY_PREFIX, deviceId);
Map<String, String> 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<70><65><EFBFBD>需要遍历所有类型查找匹配的设备
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<String, Object> 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())
);
}
}

View File

@@ -132,17 +132,44 @@ public class BadgeDeviceStatusEventListener {
var waitingTasks = orderQueueService.getWaitingTasksByUserIdFromDb(deviceId); var waitingTasks = orderQueueService.getWaitingTasksByUserIdFromDb(deviceId);
switch (newStatus) { switch (newStatus) {
case DISPATCHED: case DISPATCHED:
// 工单已推送到工牌,设置工单关联,设备状态转为 BUSY // 工单已推送到工牌,设置工单关联和区域信息
badgeDeviceStatusService.setCurrentOrder(deviceId, orderId); // currentAreaId 应该设置为工单所属区域,而非设备物理位置
badgeDeviceStatusService.updateBadgeStatus(deviceId, BadgeDeviceStatusEnum.BUSY, null, "新工单已推送"); 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; break;
case CONFIRMED: case CONFIRMED:
// 设备按键确认,设备保持 BUSY // 设备按键确认,更新工单关联和区域信息
badgeDeviceStatusService.setCurrentOrder(deviceId, orderId); // currentAreaId 应该设置为工单所属区域,而非设备物理位置
log.debug("[BadgeDeviceStatusEventListener] 工单已确认: 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.debug("[BadgeDeviceStatusEventListener] 工单已确认,更新区域信息: deviceId={}, orderId={}, areaId={}",
deviceId, orderId, order.getAreaId());
} else {
badgeDeviceStatusService.setCurrentOrder(deviceId, orderId);
log.debug("[BadgeDeviceStatusEventListener] 工单已确认: deviceId={}, orderId={}", deviceId, orderId);
}
break; break;
case ARRIVED: case ARRIVED:

View File

@@ -279,20 +279,19 @@ public class BadgeDeviceStatusServiceImpl implements BadgeDeviceStatusService, I
statusMap.put("lastHeartbeatTime", now); statusMap.put("lastHeartbeatTime", now);
} }
// 更新实时物理区域信息 (Key2) // 更新区域信息
// 注意currentAreaId 应该表示当前工单所属区域,而非设备物理位置
// 只有在没有工单时,才同步 IoT 上报的物理位置
if (areaId != null) { if (areaId != null) {
statusMap.put("currentAreaId", areaId); // 检查是否有正在执行的工单
// 同时更新区域设备索引缓存 (这里注释掉了,保持原样) Object currentOrderId = currentMap.get("currentOpsOrderId");
// addToAreaIndex(deviceId, areaId); if (currentOrderId != null) {
} else { // 有工单:保持 currentAreaId 不变(用工单区域),不覆盖
// 保持现有实时物理区域信息 log.debug("设备 {} 有执行中工单 {},保持 currentAreaId 不变,忽略 IoT 上报的物理位置 {}",
Object existingAreaId = currentMap.get("currentAreaId"); deviceId, currentOrderId, areaId);
if (existingAreaId != null) { } else {
statusMap.put("currentAreaId", existingAreaId); // 无工单:用 IoT 上报的物理位置
} statusMap.put("currentAreaId", areaId);
Object existingAreaName = currentMap.get("currentAreaName");
if (existingAreaName != null) {
statusMap.put("currentAreaName", existingAreaName);
} }
} }