fix(ops): 统一 Redis 序列化为 StringRedisTemplate,修复跨模块/跨路径数据不兼容
Some checks failed
Java CI with Maven / build (11) (push) Has been cancelled
Java CI with Maven / build (17) (push) Has been cancelled
Java CI with Maven / build (8) (push) Has been cancelled

BadgeDeviceStatusServiceImpl 和 RedisOrderQueueServiceImpl 原使用
RedisTemplate<String, Object>(Jackson JSON 序列化),但 Pipeline、
Lua 脚本写入的裸字符串与 Jackson 格式不兼容,导致:
- IoT 模块 StringRedisTemplate 读取工单状态比对失败(按键无法确认)
- 队列 entries() 反序列化失败(REMOVED 记录无法清理,持续报错)

修改内容:
- BadgeDeviceStatusServiceImpl: RedisTemplate → StringRedisTemplate
- RedisOrderQueueServiceImpl: RedisTemplate → StringRedisTemplate
- 所有写入值显式 String.valueOf(),读取用 parse 替代强转
- 修复 null 值写入 StringRedisSerializer 导致 NPE 的隐患
- application.yaml: TTS 播报间隔 6000ms → 3000ms

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
lzh
2026-02-27 10:50:03 +08:00
parent c21c77c758
commit 7c22fe998e
3 changed files with 124 additions and 101 deletions

View File

@@ -10,7 +10,7 @@ import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
@@ -40,7 +40,7 @@ import java.util.stream.Collectors;
public class BadgeDeviceStatusServiceImpl implements BadgeDeviceStatusService, InitializingBean {
@Resource
private RedisTemplate<String, Object> redisTemplate;
private StringRedisTemplate stringRedisTemplate;
@Resource
private AreaDeviceService areaDeviceService;
@@ -90,23 +90,35 @@ public class BadgeDeviceStatusServiceImpl implements BadgeDeviceStatusService, I
}
}
// 更新状态
Map<String, Object> statusMap = new HashMap<>();
statusMap.put("deviceId", deviceId);
// 更新状态(使用 StringRedisTemplate所有值显式转 String确保跨模块兼容
Map<String, String> statusMap = new HashMap<>();
statusMap.put("deviceId", String.valueOf(deviceId));
statusMap.put("status", status.getCode());
statusMap.put("statusChangeTime", LocalDateTime.now().toString());
if (currentStatus != null) {
statusMap.put("deviceCode", currentStatus.getDeviceCode());
statusMap.put("batteryLevel", currentStatus.getBatteryLevel());
statusMap.put("currentAreaId", currentStatus.getCurrentAreaId());
statusMap.put("currentAreaName", currentStatus.getCurrentAreaName());
statusMap.put("currentOpsOrderId", currentStatus.getCurrentOpsOrderId());
statusMap.put("lastHeartbeatTime", currentStatus.getLastHeartbeatTime());
if (currentStatus.getDeviceCode() != null) {
statusMap.put("deviceCode", currentStatus.getDeviceCode());
}
if (currentStatus.getBatteryLevel() != null) {
statusMap.put("batteryLevel", String.valueOf(currentStatus.getBatteryLevel()));
}
if (currentStatus.getCurrentAreaId() != null) {
statusMap.put("currentAreaId", String.valueOf(currentStatus.getCurrentAreaId()));
}
if (currentStatus.getCurrentAreaName() != null) {
statusMap.put("currentAreaName", currentStatus.getCurrentAreaName());
}
if (currentStatus.getCurrentOpsOrderId() != null) {
statusMap.put("currentOpsOrderId", String.valueOf(currentStatus.getCurrentOpsOrderId()));
}
if (currentStatus.getLastHeartbeatTime() != null) {
statusMap.put("lastHeartbeatTime", String.valueOf(currentStatus.getLastHeartbeatTime()));
}
}
redisTemplate.opsForHash().putAll(key, statusMap);
redisTemplate.expire(key, STATUS_EXPIRE_HOURS, TimeUnit.HOURS);
stringRedisTemplate.opsForHash().putAll(key, statusMap);
stringRedisTemplate.expire(key, STATUS_EXPIRE_HOURS, TimeUnit.HOURS);
log.info("更新工牌设备状态: deviceId={}, status={}, operatorId={}, reason={}",
deviceId, status, operatorId, reason);
@@ -140,7 +152,7 @@ public class BadgeDeviceStatusServiceImpl implements BadgeDeviceStatusService, I
try {
String key = BADGE_STATUS_KEY_PREFIX + deviceId;
Map<Object, Object> map = redisTemplate.opsForHash().entries(key);
Map<Object, Object> map = stringRedisTemplate.opsForHash().entries(key);
if (map.isEmpty()) {
return null;
@@ -221,7 +233,7 @@ public class BadgeDeviceStatusServiceImpl implements BadgeDeviceStatusService, I
@Override
public List<BadgeDeviceStatusDTO> listActiveBadges() {
try {
Set<String> keys = redisTemplate.keys(BADGE_STATUS_KEY_PREFIX + "*");
Set<String> keys = stringRedisTemplate.keys(BADGE_STATUS_KEY_PREFIX + "*");
if (keys == null || keys.isEmpty()) {
return Collections.emptyList();
@@ -255,7 +267,7 @@ public class BadgeDeviceStatusServiceImpl implements BadgeDeviceStatusService, I
String key = BADGE_STATUS_KEY_PREFIX + deviceId;
// 获取当前状态
Map<Object, Object> currentMap = redisTemplate.opsForHash().entries(key);
Map<Object, Object> currentMap = stringRedisTemplate.opsForHash().entries(key);
// 设备离线时,检查是否有正在执行的工单,发布事件处理
if (status == BadgeDeviceStatusEnum.OFFLINE && currentMap.containsKey("currentOpsOrderId")) {
@@ -290,18 +302,26 @@ public class BadgeDeviceStatusServiceImpl implements BadgeDeviceStatusService, I
Long now = System.currentTimeMillis();
// 构建状态数据
Map<String, Object> statusMap = new HashMap<>();
statusMap.put("deviceId", deviceId);
statusMap.put("deviceCode", deviceCode != null ? deviceCode : currentMap.get("deviceCode"));
statusMap.put("nickname", nickname != null ? nickname : currentMap.get("nickname"));
// 构建状态数据(使用 StringRedisTemplate所有值显式转 String
Map<String, String> statusMap = new HashMap<>();
statusMap.put("deviceId", String.valueOf(deviceId));
String effectiveDeviceCode = deviceCode != null ? deviceCode : (String) currentMap.get("deviceCode");
if (effectiveDeviceCode != null) {
statusMap.put("deviceCode", effectiveDeviceCode);
}
String effectiveNickname = nickname != null ? nickname : (String) currentMap.get("nickname");
if (effectiveNickname != null) {
statusMap.put("nickname", effectiveNickname);
}
statusMap.put("status", status.getCode());
statusMap.put("statusChangeTime", LocalDateTime.now().toString());
statusMap.put("statusChangeReason", reason);
if (reason != null) {
statusMap.put("statusChangeReason", reason);
}
// 如果是上线(非 OFFLINE 状态),更新心跳时间(此处作为活跃时间使用)
if (status.isActive()) {
statusMap.put("lastHeartbeatTime", now);
statusMap.put("lastHeartbeatTime", String.valueOf(now));
}
// 更新区域信息
@@ -318,22 +338,22 @@ public class BadgeDeviceStatusServiceImpl implements BadgeDeviceStatusService, I
// 无工单:不填充 currentAreaId保持为空
}
// 保持当前工单相关字段(如果存在)
// 保持当前工单相关字段(如果存在,值已是 String
if (currentMap.containsKey("currentOpsOrderId")) {
statusMap.put("currentOpsOrderId", currentMap.get("currentOpsOrderId"));
statusMap.put("currentOpsOrderId", (String) currentMap.get("currentOpsOrderId"));
}
if (currentMap.containsKey("currentOrderStatus")) {
statusMap.put("currentOrderStatus", currentMap.get("currentOrderStatus"));
statusMap.put("currentOrderStatus", (String) currentMap.get("currentOrderStatus"));
}
if (currentMap.containsKey("beaconMac")) {
statusMap.put("beaconMac", currentMap.get("beaconMac"));
statusMap.put("beaconMac", (String) currentMap.get("beaconMac"));
}
if (currentMap.containsKey("batteryLevel")) {
statusMap.put("batteryLevel", currentMap.get("batteryLevel"));
statusMap.put("batteryLevel", (String) currentMap.get("batteryLevel"));
}
redisTemplate.opsForHash().putAll(key, statusMap);
redisTemplate.expire(key, STATUS_EXPIRE_HOURS, TimeUnit.HOURS);
stringRedisTemplate.opsForHash().putAll(key, statusMap);
stringRedisTemplate.expire(key, STATUS_EXPIRE_HOURS, TimeUnit.HOURS);
log.info("[updateBadgeOnlineStatus] 更新工牌在线状态: deviceId={}, deviceCode={}, status={}, reason={}",
deviceId, deviceCode, status, reason);
@@ -357,7 +377,7 @@ public class BadgeDeviceStatusServiceImpl implements BadgeDeviceStatusService, I
try {
String key = BADGE_STATUS_KEY_PREFIX + deviceId;
redisTemplate.opsForHash().put(key, "currentOpsOrderId", orderId);
stringRedisTemplate.opsForHash().put(key, "currentOpsOrderId", String.valueOf(orderId));
log.debug("设置工牌设备当前工单: deviceId={}, orderId={}", deviceId, orderId);
} catch (Exception e) {
log.error("设置工牌设备当前工单失败: deviceId={}, orderId={}", deviceId, orderId, e);
@@ -374,19 +394,19 @@ public class BadgeDeviceStatusServiceImpl implements BadgeDeviceStatusService, I
String key = BADGE_STATUS_KEY_PREFIX + deviceId;
// 使用 Redis Pipeline 保证多个字段的原子性设置
redisTemplate.opsForHash().put(key, "currentOpsOrderId", orderId);
stringRedisTemplate.opsForHash().put(key, "currentOpsOrderId", String.valueOf(orderId));
if (orderStatus != null) {
redisTemplate.opsForHash().put(key, "currentOrderStatus", orderStatus);
stringRedisTemplate.opsForHash().put(key, "currentOrderStatus", orderStatus);
}
if (areaId != null) {
redisTemplate.opsForHash().put(key, "currentAreaId", areaId);
stringRedisTemplate.opsForHash().put(key, "currentAreaId", String.valueOf(areaId));
}
if (beaconMac != null) {
redisTemplate.opsForHash().put(key, "beaconMac", beaconMac);
stringRedisTemplate.opsForHash().put(key, "beaconMac", beaconMac);
}
// 刷新过期时间
redisTemplate.expire(key, STATUS_EXPIRE_HOURS, TimeUnit.HOURS);
stringRedisTemplate.expire(key, STATUS_EXPIRE_HOURS, TimeUnit.HOURS);
log.info("设置工牌设备工单信息: deviceId={}, orderId={}, orderStatus={}, areaId={}, beaconMac={}",
deviceId, orderId, orderStatus, areaId, beaconMac);
@@ -403,7 +423,7 @@ public class BadgeDeviceStatusServiceImpl implements BadgeDeviceStatusService, I
try {
String key = BADGE_STATUS_KEY_PREFIX + deviceId;
redisTemplate.opsForHash().put(key, "currentOrderStatus", orderStatus);
stringRedisTemplate.opsForHash().put(key, "currentOrderStatus", orderStatus);
log.debug("更新工单状态: deviceId={}, orderStatus={}", deviceId, orderStatus);
} catch (Exception e) {
log.error("更新工单状态失败: deviceId={}, orderStatus={}", deviceId, orderStatus, e);
@@ -419,9 +439,9 @@ public class BadgeDeviceStatusServiceImpl implements BadgeDeviceStatusService, I
try {
String key = BADGE_STATUS_KEY_PREFIX + deviceId;
// 更新工单状态和信标MACorderId 和 areaId 已在 DISPATCHED 时设置,不需要重复)
redisTemplate.opsForHash().put(key, "currentOrderStatus", orderStatus);
stringRedisTemplate.opsForHash().put(key, "currentOrderStatus", orderStatus);
if (beaconMac != null) {
redisTemplate.opsForHash().put(key, "beaconMac", beaconMac);
stringRedisTemplate.opsForHash().put(key, "beaconMac", beaconMac);
}
log.debug("更新工单状态和信标: deviceId={}, orderId={}, orderStatus={}, beaconMac={}",
deviceId, orderId, orderStatus, beaconMac);
@@ -439,7 +459,7 @@ public class BadgeDeviceStatusServiceImpl implements BadgeDeviceStatusService, I
try {
String key = BADGE_STATUS_KEY_PREFIX + deviceId;
// 清除工单相关字段currentOpsOrderId、currentOrderStatus、currentAreaId、currentAreaName、beaconMac
redisTemplate.opsForHash().delete(key, "currentOpsOrderId", "currentOrderStatus", "currentAreaId", "currentAreaName", "beaconMac");
stringRedisTemplate.opsForHash().delete(key, "currentOpsOrderId", "currentOrderStatus", "currentAreaId", "currentAreaName", "beaconMac");
log.info("清除工牌设备当前工单: deviceId={}", deviceId);
} catch (Exception e) {
log.error("清除工牌设备当前工单失败: deviceId={}", deviceId, e);
@@ -449,7 +469,7 @@ public class BadgeDeviceStatusServiceImpl implements BadgeDeviceStatusService, I
@Override
public List<BadgeDeviceStatusDTO> listBadgesWithCurrentOrder() {
try {
Set<String> keys = redisTemplate.keys(BADGE_STATUS_KEY_PREFIX + "*");
Set<String> keys = stringRedisTemplate.keys(BADGE_STATUS_KEY_PREFIX + "*");
if (keys == null || keys.isEmpty()) {
return Collections.emptyList();
@@ -501,8 +521,8 @@ public class BadgeDeviceStatusServiceImpl implements BadgeDeviceStatusService, I
// 直接写 status 字段,避免 updateBadgeStatus 内部回写已清除的 currentOpsOrderId
if (deviceStatus.getStatus() != null && deviceStatus.getStatus() != BadgeDeviceStatusEnum.IDLE) {
String key = BADGE_STATUS_KEY_PREFIX + deviceId;
redisTemplate.opsForHash().put(key, "status", BadgeDeviceStatusEnum.IDLE.getCode());
redisTemplate.opsForHash().put(key, "statusChangeTime", LocalDateTime.now().toString());
stringRedisTemplate.opsForHash().put(key, "status", BadgeDeviceStatusEnum.IDLE.getCode());
stringRedisTemplate.opsForHash().put(key, "statusChangeTime", LocalDateTime.now().toString());
}
return true;
@@ -521,16 +541,16 @@ public class BadgeDeviceStatusServiceImpl implements BadgeDeviceStatusService, I
try {
String key = BADGE_STATUS_KEY_PREFIX + deviceId;
Map<String, Object> statusMap = new HashMap<>();
Map<String, String> statusMap = new HashMap<>();
if (areaId != null) {
statusMap.put("currentAreaId", areaId);
statusMap.put("currentAreaId", String.valueOf(areaId));
}
if (areaName != null) {
statusMap.put("currentAreaName", areaName);
}
if (!statusMap.isEmpty()) {
redisTemplate.opsForHash().putAll(key, statusMap);
stringRedisTemplate.opsForHash().putAll(key, statusMap);
}
log.debug("更新工牌设备区域: deviceId={}, areaId={}, areaName={}", deviceId, areaId, areaName);
@@ -549,7 +569,7 @@ public class BadgeDeviceStatusServiceImpl implements BadgeDeviceStatusService, I
try {
String key = BADGE_STATUS_KEY_PREFIX + deviceId;
redisTemplate.delete(key);
stringRedisTemplate.delete(key);
log.info("删除工牌设备状态: deviceId={}", deviceId);
} catch (Exception e) {
log.error("删除工牌设备状态失败: deviceId={}", deviceId, e);
@@ -559,7 +579,7 @@ public class BadgeDeviceStatusServiceImpl implements BadgeDeviceStatusService, I
@Override
public void clearOfflineBadges() {
try {
Set<String> keys = redisTemplate.keys(BADGE_STATUS_KEY_PREFIX + "*");
Set<String> keys = stringRedisTemplate.keys(BADGE_STATUS_KEY_PREFIX + "*");
if (keys == null || keys.isEmpty()) {
return;
@@ -567,10 +587,10 @@ public class BadgeDeviceStatusServiceImpl implements BadgeDeviceStatusService, I
int count = 0;
for (String key : keys) {
Map<Object, Object> map = redisTemplate.opsForHash().entries(key);
Map<Object, Object> map = stringRedisTemplate.opsForHash().entries(key);
String statusStr = (String) map.get("status");
if (BadgeDeviceStatusEnum.OFFLINE.getCode().equals(statusStr)) {
redisTemplate.delete(key);
stringRedisTemplate.delete(key);
count++;
}
}