From 4207a6582f9ced7b759f49cccb0b1c424db345f1 Mon Sep 17 00:00:00 2001 From: tianyongbao Date: Wed, 1 Jul 2026 23:02:48 +0800 Subject: [PATCH] =?UTF-8?q?fix:=E5=A4=AA=E9=98=B3=E8=83=BD=E7=BD=91?= =?UTF-8?q?=E6=8E=A7=E4=B8=8B=E5=8F=91=E7=9B=90=E5=BA=A6=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=EF=BC=8C=E5=A2=9E=E5=8A=A030=E5=88=86=E9=92=9F=E5=86=BB?= =?UTF-8?q?=E7=BB=93=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../intc/iot/controller/IotController.java | 25 +++++++++ .../intc/iot/handler/DeviceDataHandler.java | 54 +++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/intc-modules/intc-iot/src/main/java/com/intc/iot/controller/IotController.java b/intc-modules/intc-iot/src/main/java/com/intc/iot/controller/IotController.java index 63897e4..3c1cc72 100644 --- a/intc-modules/intc-iot/src/main/java/com/intc/iot/controller/IotController.java +++ b/intc-modules/intc-iot/src/main/java/com/intc/iot/controller/IotController.java @@ -2084,6 +2084,7 @@ public class IotController extends BaseController { return R.fail("盐度缓存失败,请稍后重试"); } updateDeviceSalinityCompensation(device.getId(), request.getSalinityCompensation()); + setSalinityFreeze(device.getSerialNum(), request.getSalinityCompensation()); log.info("太阳能网控盐度已缓存: userId={}, deviceId={}, deviceName={}, serialNum={}, salinityCompensation={}‰", userId, device.getId(), device.getDeviceName(), device.getSerialNum(), request.getSalinityCompensation()); return R.ok("设置成功"); @@ -2147,6 +2148,24 @@ public class IotController extends BaseController { ); } + /** + * 设置盐度冻结标记,30分钟内阻止设备上报的旧值回写 DB + * + * @param serialNum 设备序列号 + * @param salinityCompensation 用户设定的盐度值 + */ + private void setSalinityFreeze(String serialNum, Double salinityCompensation) { + if (serialNum != null && !serialNum.isBlank() && salinityCompensation != null) { + com.intc.common.redis.utils.RedisUtils.setCacheObject( + DeviceDataHandler.SALINITY_SET_FREEZE_KEY_PREFIX + serialNum, + salinityCompensation, + Duration.ofMinutes(DeviceDataHandler.SALINITY_FREEZE_MINUTES) + ); + log.info("[盐度冻结] 已设置冻结标记: serialNum={}, value={}, freezeMinutes={}", + serialNum, salinityCompensation, DeviceDataHandler.SALINITY_FREEZE_MINUTES); + } + } + @Operation(summary = "太阳能网控属性下发") @SaCheckPermission("fishery:solarControl:command") @PostMapping("/device/solar/property-command") @@ -2203,6 +2222,9 @@ public class IotController extends BaseController { return R.fail("指令缓存失败,请稍后重试"); } syncDeviceFieldsAfterSolarCommand(device.getId(), properties); + if (properties.containsKey("salinitySet")) { + setSalinityFreeze(device.getSerialNum(), toDouble(properties.get("salinitySet"))); + } result.put("mode", "cached"); result.put("cached", true); result.put("sent", false); @@ -2232,6 +2254,9 @@ public class IotController extends BaseController { syncDeviceFieldsAfterSolarCommand(device.getId(), properties); removePendingSolarProperties(device, properties.keySet()); + if (properties.containsKey("salinitySet")) { + setSalinityFreeze(device.getSerialNum(), toDouble(properties.get("salinitySet"))); + } result.put("mode", "direct"); result.put("cached", false); diff --git a/intc-modules/intc-iot/src/main/java/com/intc/iot/handler/DeviceDataHandler.java b/intc-modules/intc-iot/src/main/java/com/intc/iot/handler/DeviceDataHandler.java index de3b53e..1a1ec23 100644 --- a/intc-modules/intc-iot/src/main/java/com/intc/iot/handler/DeviceDataHandler.java +++ b/intc-modules/intc-iot/src/main/java/com/intc/iot/handler/DeviceDataHandler.java @@ -72,6 +72,18 @@ public class DeviceDataHandler { */ private static final long SOLAR_PROPERTY_DELAY_MS = 5000L; + /** + * 盐度设定防回写冻结标记 Redis key 前缀 + * value = 用户设定的盐度值,TTL = {@link #SALINITY_FREEZE_MINUTES} 分钟 + * 用户修改盐度后写入此标记,冻结期内设备上报的旧值不会回写 DB,避免客户误判修改未生效 + */ + public static final String SALINITY_SET_FREEZE_KEY_PREFIX = "device:salinity:set:freeze:"; + + /** + * 盐度设定冻结时长(分钟),与设备上报周期匹配,超时后恢复正常回写逻辑 + */ + public static final int SALINITY_FREEZE_MINUTES = 30; + /** * TDengine 服务,用于存储设备时序数据 */ @@ -1058,6 +1070,8 @@ public class DeviceDataHandler { } syncDeviceFieldsAfterPropertySet(pendingProperty.deviceId, pendingProperty.properties); + // 补发成功后,如果包含盐度设定,写入冻结标记 + setSalinityFreezeIfNeeded(deviceName, pendingProperty.properties); clearPendingSolarProperty(pendingProperty); log.info("[太阳能属性] {}补发成功并清除缓存: deviceName={}, cacheKey={}, deviceId={}, properties={}", trigger, deviceName, pendingProperty.redisKey, pendingProperty.deviceId, pendingProperty.properties); @@ -1204,6 +1218,27 @@ public class DeviceDataHandler { } } + /** + * 如果属性中包含 salinitySet,写入盐度冻结标记到 Redis + * 冻结期内设备上报的旧值不会被回写 DB + * + * @param deviceName 设备名称(serialNum) + * @param properties 下发的属性 + */ + private void setSalinityFreezeIfNeeded(String deviceName, Map properties) { + if (StrUtil.isBlank(deviceName) || properties == null || !properties.containsKey("salinitySet")) { + return; + } + Double value = toDouble(properties.get("salinitySet")); + if (value == null) { + return; + } + String freezeKey = SALINITY_SET_FREEZE_KEY_PREFIX + deviceName; + RedisUtils.setCacheObject(freezeKey, value, Duration.ofMinutes(SALINITY_FREEZE_MINUTES)); + log.info("[盐度冻结] 已设置冻结标记: deviceName={}, value={}, freezeMinutes={}", + deviceName, value, SALINITY_FREEZE_MINUTES); + } + private static class PendingSolarProperty { private final String redisKey; private final Long deviceId; @@ -2877,6 +2912,25 @@ public class DeviceDataHandler { if ("salinitySet".equals(k)) { Double salinitySet = params.getDouble(k); if (salinitySet != null) { + // === 盐度冻结检查:用户修改盐度后30分钟内,忽略设备上报的旧值回写 === + String freezeKey = SALINITY_SET_FREEZE_KEY_PREFIX + deviceName; + Object freezeObj = RedisUtils.getCacheObject(freezeKey); + if (freezeObj != null) { + Double frozenValue = toDouble(freezeObj); + if (frozenValue != null && Math.abs(salinitySet - frozenValue) < 0.01) { + // 上报值与冻结值一致,设备已跟上新设置,提前解冻 + RedisUtils.deleteObject(freezeKey); + log.info("[盐度冻结] 设备上报值={} 与冻结值={} 一致,提前解冻: deviceName={}", + salinitySet, frozenValue, deviceName); + } else { + // 冻结期内上报值与设定值不一致,跳过回写避免误导 + log.debug("[盐度冻结] 冻结期内跳过设备旧值回写: deviceName={}, reportValue={}, frozenValue={}", + deviceName, salinitySet, frozenValue); + return; + } + } + // === 冻结检查结束 === + try { Device device = deviceMapper.selectOne( new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper()