From 143dcd8e634422dcc2aae09c5835daa967781ba5 Mon Sep 17 00:00:00 2001 From: tianyongbao Date: Sat, 11 Jul 2026 19:16:18 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E7=BB=91=E5=AE=9A=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=AE=8C=E5=96=84=EF=BC=8C=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E6=B8=85=E9=9B=B6=E5=8A=9F=E8=83=BD=E6=8E=A5=E5=8F=A3=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- intc-modules/intc-fishery/pom.xml | 8 ++ .../fishery/controller/DeviceController.java | 112 ++++++++++++++++++ .../service/impl/DeviceServiceImpl.java | 10 +- .../intc/iot/controller/IotController.java | 26 ++-- .../mapper/DeviceSensorDataMapper.java | 9 ++ .../service/IDeviceSensorDataService.java | 7 ++ .../service/impl/DeviceSensorDataService.java | 23 ++++ .../tdengine/DeviceSensorDataMapper.xml | 4 + 8 files changed, 187 insertions(+), 12 deletions(-) diff --git a/intc-modules/intc-fishery/pom.xml b/intc-modules/intc-fishery/pom.xml index 076046b..022ba7f 100644 --- a/intc-modules/intc-fishery/pom.xml +++ b/intc-modules/intc-fishery/pom.xml @@ -107,6 +107,14 @@ intc-system + + + com.intc + intc-tdengine + 5.5.0 + compile + + diff --git a/intc-modules/intc-fishery/src/main/java/com/intc/fishery/controller/DeviceController.java b/intc-modules/intc-fishery/src/main/java/com/intc/fishery/controller/DeviceController.java index 81160ed..eea8720 100644 --- a/intc-modules/intc-fishery/src/main/java/com/intc/fishery/controller/DeviceController.java +++ b/intc-modules/intc-fishery/src/main/java/com/intc/fishery/controller/DeviceController.java @@ -30,18 +30,25 @@ import com.intc.fishery.domain.vo.PublicDeviceLinkedCtrl; import com.intc.fishery.domain.vo.PublicDeviceDeadInfo; import com.intc.fishery.domain.vo.PublicDeviceTypeAndSerialNum; import com.intc.fishery.domain.Device; +import com.intc.fishery.domain.DeviceBindRecord; import com.intc.fishery.domain.DeviceSwitch; +import com.intc.fishery.domain.DeviceErrorCode; import com.intc.fishery.domain.Pond; import com.intc.fishery.domain.LinkedCtrl; import com.intc.fishery.domain.DeviceCorrectRecord; import com.intc.fishery.mapper.DeviceMapper; import com.intc.fishery.mapper.DeviceSwitchMapper; +import com.intc.fishery.mapper.DeviceBindRecordMapper; +import com.intc.fishery.mapper.DeviceErrorCodeMapper; import com.intc.fishery.mapper.PondMapper; import com.intc.fishery.mapper.LinkedCtrlMapper; import com.intc.fishery.mapper.DeviceCorrectRecordMapper; import com.intc.fishery.constant.DefineDeviceWarnCode; import com.intc.fishery.utils.MessageOpRecordUtil; import com.intc.common.core.config.properties.DeviceTypeProperties; +import com.intc.common.redis.utils.RedisUtils; +import com.intc.tdengine.service.IDeviceSensorDataService; +import org.springframework.beans.factory.annotation.Autowired; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import org.springframework.transaction.annotation.Transactional; @@ -69,8 +76,13 @@ public class DeviceController extends BaseController { private final PondMapper pondMapper; private final LinkedCtrlMapper linkedCtrlMapper; private final DeviceCorrectRecordMapper deviceCorrectRecordMapper; + private final DeviceBindRecordMapper deviceBindRecordMapper; + private final DeviceErrorCodeMapper deviceErrorCodeMapper; private final DeviceTypeProperties deviceTypeProperties; + @Autowired(required = false) + private IDeviceSensorDataService deviceSensorDataService; + /** * 查询设备管理列表 */ @@ -148,6 +160,106 @@ public class DeviceController extends BaseController { return toAjax(deviceService.deleteWithValidByIds(List.of(ids), true)); } + /** + * 清零设备测试数据(出厂测试用) + *

清理范围: + *

+ * 清零后,设备重新绑定时会自动重新计算365天有效期。

+ * + * @param id 设备ID + * @return 操作结果 + */ + @SaCheckPermission("fishery:device:edit") + @Log(title = "设备管理", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping("/resetTestData/{id}") + public R resetTestData(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + Device device = deviceMapper.selectById(id); + if (device == null) { + return R.fail("设备不存在"); + } + + String serialNum = device.getSerialNum(); + + // 1. 清零设备表:有效期、告警状态、实时传感器值、最后上数时间 + deviceMapper.update(null, + new LambdaUpdateWrapper() + .eq(Device::getId, id) + .set(Device::getDeadTime, null) + .set(Device::getWarnCode, DefineDeviceWarnCode.None) + .set(Device::getLastReportTime, null) + .set(Device::getValueDissolvedOxygen, null) + .set(Device::getValueTemperature, null) + .set(Device::getValueSaturability, null) + .set(Device::getValuePh, null) + .set(Device::getValueSalinity, null) + .set(Device::getValueBattery, null) + .set(Device::getIsOxygenWarnExist, 0) + .set(Device::getIsTempWarnExist, 0) + ); + + // 2. 删除TDengine时序数据(设备子表,含所有历史传感器数据) + if (deviceSensorDataService != null && serialNum != null && !serialNum.isBlank()) { + try { + deviceSensorDataService.dropSubTable(serialNum); + } catch (Exception e) { + // TDengine清理失败不阻断主流程 + org.slf4j.LoggerFactory.getLogger(DeviceController.class) + .warn("清理TDengine数据失败: deviceId={}, serialNum={}, error={}", id, serialNum, e.getMessage()); + } + } + + // 3. 删除设备绑定/解绑记录 + deviceBindRecordMapper.delete( + new LambdaQueryWrapper() + .eq(DeviceBindRecord::getSerialNum, serialNum) + ); + + // 4. 删除设备校准记录 + deviceCorrectRecordMapper.delete( + new LambdaQueryWrapper() + .eq(DeviceCorrectRecord::getDeviceId, id) + ); + + // 5. 删除设备故障码记录 + deviceErrorCodeMapper.delete( + new LambdaQueryWrapper() + .eq(DeviceErrorCode::getDeviceId, id) + ); + + // 6. 清除Redis告警标记 + if (serialNum != null && !serialNum.isBlank()) { + try { + RedisUtils.deleteObject("device:offline:wait:" + serialNum); + RedisUtils.deleteObject("device:detector:oxy:offline:wait:" + serialNum); + } catch (Exception e) { + // Redis清理失败不影响主流程 + } + } + + // 7. 记录操作日志 + if (device.getUserId() != null) { + MessageOpRecordUtil.addMessageOpRecordUser( + device.getUserId(), + "设备管理", + String.format("%s(%s),清零设备测试数据。", device.getDeviceName(), serialNum) + ); + } else { + org.slf4j.LoggerFactory.getLogger(DeviceController.class) + .info("清零设备测试数据(设备未绑定用户): deviceId={}, serialNum={}", id, serialNum); + } + + return R.ok(); + } + /** * 查询用户所有设备简化列表 * type=1: 返回完整信息(包含塘口信息和开关列表) diff --git a/intc-modules/intc-fishery/src/main/java/com/intc/fishery/service/impl/DeviceServiceImpl.java b/intc-modules/intc-fishery/src/main/java/com/intc/fishery/service/impl/DeviceServiceImpl.java index 59d711c..8e38c4d 100644 --- a/intc-modules/intc-fishery/src/main/java/com/intc/fishery/service/impl/DeviceServiceImpl.java +++ b/intc-modules/intc-fishery/src/main/java/com/intc/fishery/service/impl/DeviceServiceImpl.java @@ -402,6 +402,9 @@ public class DeviceServiceImpl implements IDeviceService { try { AquUser user = aquUserMapper.selectById(device.getUserId()); if (user != null) { + // 管理员(出厂测试人员)解绑时清空有效期,普通用户解绑时保留有效期(防止解绑续命) + boolean isManager = user.getIsManager() != null && user.getIsManager() == 1L; + Device newDevice = new Device(); newDevice.setUserId(null); newDevice.setIsOxygenUsed(device.getIsOxygenUsed()); @@ -411,7 +414,8 @@ public class DeviceServiceImpl implements IDeviceService { newDevice.setDeviceType(device.getDeviceType()); newDevice.setIccId(device.getIccId()); newDevice.setBindTime(device.getBindTime()); - newDevice.setDeadTime(device.getDeadTime()); + // 管理员解绑时清空有效期(出厂测试),普通用户解绑时保留有效期 + newDevice.setDeadTime(isManager ? null : device.getDeadTime()); newDevice.setPondId(null); newDevice.setSalinityCompensation(0.0); newDevice.setOxyWarnLower(device.getOxyWarnLower()); @@ -423,8 +427,8 @@ public class DeviceServiceImpl implements IDeviceService { newDevice.setWarnCode(DefineDeviceWarnCode.None); newDevice.setCategory(device.getCategory()); baseMapper.insert(newDevice); - log.info("已为用户重建无绑定设备: oldDeviceId={}, newDeviceId={}, serialNum={}", - deviceId, newDevice.getId(), device.getSerialNum()); + log.info("已为用户重建无绑定设备: oldDeviceId={}, newDeviceId={}, serialNum={}, isManager={}, deadTime={}", + deviceId, newDevice.getId(), device.getSerialNum(), isManager, newDevice.getDeadTime()); } } catch (Exception e) { log.error("重建无绑定设备失败: deviceId={}, error={}", deviceId, e.getMessage(), e); 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 179fbc4..ae31828 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 @@ -1151,11 +1151,12 @@ public class IotController extends BaseController { device.setIotId(iotId); device.setSerialNum(bo.getSerialNum()); device.setDeviceType(2); // 2-测控一体机 - device.setDeadTime(new Date(now.getTime() + 365L * 24 * 60 * 60 * 1000)); // 一年后到期 + device.setDeadTime(new Date(now.getTime() + 365L * 24 * 60 * 60 * 1000)); } else { - // 检查是否已过期 - if (device.getDeadTime() != null && now.after(device.getDeadTime())) { - warnCode |= DefineDeviceWarnCode.DeviceTimeDead; + // 管理员解绑会清空有效期(null),普通用户解绑会保留有效期。仅null时才设置365天 + if (device.getDeadTime() == null) { + log.info("测控一体机重新绑定,有效期已被管理员解绑清空,重新设置: serialNum={}", bo.getSerialNum()); + device.setDeadTime(new Date(now.getTime() + 365L * 24 * 60 * 60 * 1000)); } } @@ -1575,12 +1576,13 @@ public class IotController extends BaseController { device.setIotId(iotId); device.setSerialNum(bo.getSerialNum()); device.setDeviceType(1); // 1-水质检测仪 - device.setDeadTime(new Date(now.getTime() + 365L * 24 * 60 * 60 * 1000)); // 一年后到期 device.setIsOxygenUsed(1); + device.setDeadTime(new Date(now.getTime() + 365L * 24 * 60 * 60 * 1000)); } else { - // 检查是否已过期 - if (device.getDeadTime() != null && now.after(device.getDeadTime())) { - warnCode |= DefineDeviceWarnCode.DeviceTimeDead; + // 管理员解绑会清空有效期(null),普通用户解绑会保留有效期。仅null时才设置365天 + if (device.getDeadTime() == null) { + log.info("水质检测仪重新绑定,有效期已被管理员解绑清空,重新设置: serialNum={}", bo.getSerialNum()); + device.setDeadTime(new Date(now.getTime() + 365L * 24 * 60 * 60 * 1000)); } } @@ -1832,7 +1834,13 @@ public class IotController extends BaseController { device.setIotId(iotId); device.setSerialNum(bo.getSerialNum()); device.setDeviceType(3); // 3-太阳能网控 - device.setDeadTime(new Date(now.getTime() + 365L * 24 * 60 * 60 * 1000)); // 一年后到期 + device.setDeadTime(new Date(now.getTime() + 365L * 24 * 60 * 60 * 1000)); + } else { + // 管理员解绑会清空有效期(null),普通用户解绑会保留有效期。仅null时才设置365天 + if (device.getDeadTime() == null) { + log.info("太阳能网控重新绑定,有效期已被管理员解绑清空,重新设置: serialNum={}", bo.getSerialNum()); + device.setDeadTime(new Date(now.getTime() + 365L * 24 * 60 * 60 * 1000)); + } } // 设置基本信息 diff --git a/intc-modules/intc-tdengine/src/main/java/com/intc/tdengine/mapper/DeviceSensorDataMapper.java b/intc-modules/intc-tdengine/src/main/java/com/intc/tdengine/mapper/DeviceSensorDataMapper.java index f4c3a48..97ded7b 100644 --- a/intc-modules/intc-tdengine/src/main/java/com/intc/tdengine/mapper/DeviceSensorDataMapper.java +++ b/intc-modules/intc-tdengine/src/main/java/com/intc/tdengine/mapper/DeviceSensorDataMapper.java @@ -87,4 +87,13 @@ public interface DeviceSensorDataMapper { @InterceptorIgnore(tenantLine = "true") DeviceSensorData getLatestReportTime(@Param("serialNum") String serialNum); + /** + * 删除设备子表(清除该设备的所有时序数据) + * + * @param serialNum 设备序列号 + */ + @DS("taos") + @InterceptorIgnore(tenantLine = "true") + void dropSubTable(@Param("serialNum") String serialNum); + } diff --git a/intc-modules/intc-tdengine/src/main/java/com/intc/tdengine/service/IDeviceSensorDataService.java b/intc-modules/intc-tdengine/src/main/java/com/intc/tdengine/service/IDeviceSensorDataService.java index 3fc2210..f6bd46f 100644 --- a/intc-modules/intc-tdengine/src/main/java/com/intc/tdengine/service/IDeviceSensorDataService.java +++ b/intc-modules/intc-tdengine/src/main/java/com/intc/tdengine/service/IDeviceSensorDataService.java @@ -46,4 +46,11 @@ public interface IDeviceSensorDataService { * @return 最新上数时间字符串,格式 yyyy-MM-dd HH:mm:ss;无数据时返回 null */ String getLatestReportTime(String serialNum); + + /** + * 删除设备子表(清除该设备的所有时序数据) + * + * @param serialNum 设备序列号 + */ + void dropSubTable(String serialNum); } diff --git a/intc-modules/intc-tdengine/src/main/java/com/intc/tdengine/service/impl/DeviceSensorDataService.java b/intc-modules/intc-tdengine/src/main/java/com/intc/tdengine/service/impl/DeviceSensorDataService.java index 698400a..4267301 100644 --- a/intc-modules/intc-tdengine/src/main/java/com/intc/tdengine/service/impl/DeviceSensorDataService.java +++ b/intc-modules/intc-tdengine/src/main/java/com/intc/tdengine/service/impl/DeviceSensorDataService.java @@ -296,4 +296,27 @@ public class DeviceSensorDataService implements IDeviceSensorDataService { default: return "未知"; } } + + /** + * 删除设备子表(清除该设备的所有时序数据) + */ + @Override + public void dropSubTable(String serialNum) { + if (serialNum == null || serialNum.isBlank()) { + return; + } + try { + deviceSensorDataMapper.dropSubTable(serialNum); + // 清除建表缓存,下次插入数据时会重新建表 + CREATED_TABLES.remove(serialNum); + log.info("已删除设备TDengine子表: serialNum={}", serialNum); + } catch (Exception e) { + String errorMsg = e.getMessage(); + if (errorMsg != null && errorMsg.toLowerCase().contains("not exist")) { + log.debug("设备TDengine子表不存在,无需删除: serialNum={}", serialNum); + } else { + log.warn("删除设备TDengine子表失败: serialNum={}, error={}", serialNum, errorMsg); + } + } + } } diff --git a/intc-modules/intc-tdengine/src/main/resources/mapper/tdengine/DeviceSensorDataMapper.xml b/intc-modules/intc-tdengine/src/main/resources/mapper/tdengine/DeviceSensorDataMapper.xml index c2a811f..a04a6c9 100644 --- a/intc-modules/intc-tdengine/src/main/resources/mapper/tdengine/DeviceSensorDataMapper.xml +++ b/intc-modules/intc-tdengine/src/main/resources/mapper/tdengine/DeviceSensorDataMapper.xml @@ -120,4 +120,8 @@ ORDER BY `time` DESC LIMIT 1 + + + DROP TABLE IF EXISTS `fishery`.`t_${serialNum}` +