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 d485097..8b60985 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 @@ -318,6 +318,7 @@ public class DeviceController extends BaseController { data.setSalinityCompensation(device.getSalinityCompensation()); data.setInputVoltage(device.getInputVoltage()); data.setVoltageWarnOpen(device.getVoltageWarnOpen()); + data.setControllerFaultCallOpen(device.getControllerFaultCallOpen() == null ? 1 : device.getControllerFaultCallOpen()); data.setBatteryWarnCallOpen(device.getBatteryWarnCallOpen()); data.setBatteryWarnCallNoDis(device.getBatteryWarnCallNoDis()); data.setBatteryWarnLower(device.getBatteryWarnLower()); @@ -1040,6 +1041,62 @@ public class DeviceController extends BaseController { return R.ok(); } + /** + * 设置控制器故障电话报警开关。 + * 仅控制缺相、过压、欠压、开关空载/欠流/过流等故障的电话通知;断电电话通知不受此开关限制。 + * + * @param rootUserId 用户ID + * @param request 请求对象(包含设备ID和开关状态) + * @return 操作结果 + */ + @PutMapping("/controller_fault_call_open") + public R updateControllerFaultCallOpen( + @RequestParam("rootUserId") Long rootUserId, + @Validated @RequestBody ReqTurnOpen request) { + + Device device = deviceMapper.selectOne( + new LambdaQueryWrapper() + .eq(Device::getId, request.getId()) + .select(Device::getId, Device::getUserId, Device::getSerialNum, + Device::getDeviceName, Device::getDeviceType, + Device::getControllerFaultCallOpen, Device::getDeadTime) + ); + + if (device == null || device.getUserId() == null || !device.getUserId().equals(rootUserId) + || device.getDeviceType() == null || device.getDeviceType() != 2) { + return R.fail("设备不存在或无权限操作"); + } + + if (device.getDeadTime() != null && device.getDeadTime().before(new Date())) { + return R.fail("设备服务已过期"); + } + + Integer currentOpen = device.getControllerFaultCallOpen() == null ? 1 : device.getControllerFaultCallOpen(); + if (currentOpen.equals(request.getIsOpen())) { + return R.ok(); + } + + boolean updated = deviceMapper.update(null, + new LambdaUpdateWrapper() + .eq(Device::getId, request.getId()) + .set(Device::getControllerFaultCallOpen, request.getIsOpen()) + ) > 0; + + if (!updated) { + return R.fail("更新失败"); + } + + String op = request.getIsOpen() == 1 ? "开启" : "关闭"; + MessageOpRecordUtil.addMessageOpRecordUser( + rootUserId, + "设备告警设置", + String.format("%s(%s) 设置控制器故障电话报警开关:%s", + device.getDeviceName(), device.getSerialNum(), op) + ); + + return R.ok(); + } + /** * 设置测控一体机溶解氧启用/禁用 * 启用时:同时启用溶解氧和温度告警及免打扰 diff --git a/intc-modules/intc-fishery/src/main/java/com/intc/fishery/domain/Device.java b/intc-modules/intc-fishery/src/main/java/com/intc/fishery/domain/Device.java index ece9db0..1f5660a 100644 --- a/intc-modules/intc-fishery/src/main/java/com/intc/fishery/domain/Device.java +++ b/intc-modules/intc-fishery/src/main/java/com/intc/fishery/domain/Device.java @@ -208,6 +208,11 @@ public class Device extends TenantEntity { */ private Integer voltageWarnOpen; + /** + * 控制器故障电话报警开关(断电不受此开关限制) + */ + private Integer controllerFaultCallOpen; + /** * 备注 */ diff --git a/intc-modules/intc-fishery/src/main/java/com/intc/fishery/domain/bo/DeviceBo.java b/intc-modules/intc-fishery/src/main/java/com/intc/fishery/domain/bo/DeviceBo.java index aec419b..4bbbcb7 100644 --- a/intc-modules/intc-fishery/src/main/java/com/intc/fishery/domain/bo/DeviceBo.java +++ b/intc-modules/intc-fishery/src/main/java/com/intc/fishery/domain/bo/DeviceBo.java @@ -240,6 +240,11 @@ public class DeviceBo extends BaseEntity { // @NotNull(message = "电压告警开关不能为空", groups = { AddGroup.class, EditGroup.class }) private Integer voltageWarnOpen; + /** + * 控制器故障电话报警开关(断电不受此开关限制) + */ + private Integer controllerFaultCallOpen; + /** * 备注 */ diff --git a/intc-modules/intc-fishery/src/main/java/com/intc/fishery/domain/vo/DeviceVo.java b/intc-modules/intc-fishery/src/main/java/com/intc/fishery/domain/vo/DeviceVo.java index 5405e29..fc14128 100644 --- a/intc-modules/intc-fishery/src/main/java/com/intc/fishery/domain/vo/DeviceVo.java +++ b/intc-modules/intc-fishery/src/main/java/com/intc/fishery/domain/vo/DeviceVo.java @@ -270,6 +270,12 @@ public class DeviceVo implements Serializable { @ExcelProperty(value = "电压告警开关") private Integer voltageWarnOpen; + /** + * 控制器故障电话报警开关(断电不受此开关限制) + */ + @ExcelProperty(value = "控制器故障电话报警开关") + private Integer controllerFaultCallOpen; + /** * 备注 */ diff --git a/intc-modules/intc-fishery/src/main/java/com/intc/fishery/domain/vo/PublicDeviceBaseData.java b/intc-modules/intc-fishery/src/main/java/com/intc/fishery/domain/vo/PublicDeviceBaseData.java index 25d77db..1982b74 100644 --- a/intc-modules/intc-fishery/src/main/java/com/intc/fishery/domain/vo/PublicDeviceBaseData.java +++ b/intc-modules/intc-fishery/src/main/java/com/intc/fishery/domain/vo/PublicDeviceBaseData.java @@ -109,6 +109,11 @@ public class PublicDeviceBaseData implements Serializable { */ private Integer voltageWarnOpen; + /** + * 控制器故障电话报警开关(断电不受此开关限制) + */ + private Integer controllerFaultCallOpen; + /** * 低电量电话告警开关(太阳能网控专用) */ 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 a188e7c..80e1319 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 @@ -373,6 +373,7 @@ public class DeviceServiceImpl implements IDeviceService { newDevice.setOxyWarnCallNoDis(0); newDevice.setTempWarnCallOpen(0); newDevice.setTempWarnCallNoDis(0); + newDevice.setControllerFaultCallOpen(1); newDevice.setWarnCode(DefineDeviceWarnCode.None); newDevice.setCategory(device.getCategory()); baseMapper.insert(newDevice); 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 971f4b1..20e82ff 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 @@ -1654,7 +1654,8 @@ public class DeviceDataHandler { new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper() .eq(Device::getSerialNum, deviceName) .select(Device::getId, Device::getSerialNum, Device::getDeviceName, Device::getWarnCode, - Device::getPondId, Device::getUserId, Device::getDeadTime) + Device::getPondId, Device::getUserId, Device::getDeadTime, + Device::getControllerFaultCallOpen) .last("LIMIT 1") ); if (device == null) { @@ -1732,6 +1733,7 @@ public class DeviceDataHandler { int switchIndex = 0; // 告警类型:默认为控制器告警(3),开关相关故障使用开关告警(4) int warnType = WARN_TYPE_CONTROLLER; + boolean powerOffError = false; if (errorCode >= DefineDeviceErrorCode.Three_InputLosePhaseA && errorCode <= DefineDeviceErrorCode.Three_InputLosePhaseC) { @@ -1785,12 +1787,10 @@ public class DeviceDataHandler { String phaseName = DefineDeviceErrorCode.getPhaseName(tempIndex % 3); switchIndex = tempIndex / 3 + 1; DeviceSwitch sw = getDeviceSwitchByIndex(deviceId, switchIndex); - boolean electricWarnOpen = sw != null && sw.getElectricWarnOpen() != null && sw.getElectricWarnOpen() == 1; - warnTrigger = warnTrigger && electricWarnOpen; if (warnTrigger) { - String swPondName = (sw.getPondId() != null) ? getPondName(sw.getPondId()) : pondName; + String swPondName = (sw != null && sw.getPondId() != null) ? getPondName(sw.getPondId()) : pondName; messagePondName = swPondName; - String swName = sw.getSwitchName() != null ? sw.getSwitchName() : ("开关" + switchIndex); + String swName = sw != null && sw.getSwitchName() != null ? sw.getSwitchName() : ("开关" + switchIndex); title = "控制器开关输出过流"; message = formatPondWarnMessage(messagePondName, String.format("%s(%s)的开关%d(%s)输出%s相过流", @@ -1804,12 +1804,10 @@ public class DeviceDataHandler { String phaseName = DefineDeviceErrorCode.getPhaseName(tempIndex % 3); switchIndex = tempIndex / 3 + 1; DeviceSwitch sw = getDeviceSwitchByIndex(deviceId, switchIndex); - boolean electricWarnOpen = sw != null && sw.getElectricWarnOpen() != null && sw.getElectricWarnOpen() == 1; - warnTrigger = warnTrigger && electricWarnOpen; if (warnTrigger) { - String swPondName = (sw.getPondId() != null) ? getPondName(sw.getPondId()) : pondName; + String swPondName = (sw != null && sw.getPondId() != null) ? getPondName(sw.getPondId()) : pondName; messagePondName = swPondName; - String swName = sw.getSwitchName() != null ? sw.getSwitchName() : ("开关" + switchIndex); + String swName = sw != null && sw.getSwitchName() != null ? sw.getSwitchName() : ("开关" + switchIndex); title = "控制器开关输出欠流"; message = formatPondWarnMessage(messagePondName, String.format("%s(%s)的开关%d(%s)输出%s相欠流", @@ -1821,12 +1819,10 @@ public class DeviceDataHandler { warnType = WARN_TYPE_SWITCH; switchIndex = errorCode - DefineDeviceErrorCode.Three_Switch1ElectricEmpty + 1; DeviceSwitch sw = getDeviceSwitchByIndex(deviceId, switchIndex); - boolean electricWarnOpen = sw != null && sw.getElectricWarnOpen() != null && sw.getElectricWarnOpen() == 1; - warnTrigger = warnTrigger && electricWarnOpen; if (warnTrigger) { - String swPondName = (sw.getPondId() != null) ? getPondName(sw.getPondId()) : pondName; + String swPondName = (sw != null && sw.getPondId() != null) ? getPondName(sw.getPondId()) : pondName; messagePondName = swPondName; - String swName = sw.getSwitchName() != null ? sw.getSwitchName() : ("开关" + switchIndex); + String swName = sw != null && sw.getSwitchName() != null ? sw.getSwitchName() : ("开关" + switchIndex); title = "控制器开关输出空载"; message = formatPondWarnMessage(messagePondName, String.format("%s(%s)的开关%d(%s)输出空载", @@ -1838,12 +1834,10 @@ public class DeviceDataHandler { warnType = WARN_TYPE_SWITCH; switchIndex = errorCode - DefineDeviceErrorCode.One_Switch1OverElectric + 1; DeviceSwitch sw = getDeviceSwitchByIndex(deviceId, switchIndex); - boolean electricWarnOpen = sw != null && sw.getElectricWarnOpen() != null && sw.getElectricWarnOpen() == 1; - warnTrigger = warnTrigger && electricWarnOpen; if (warnTrigger) { - String swPondName = (sw.getPondId() != null) ? getPondName(sw.getPondId()) : pondName; + String swPondName = (sw != null && sw.getPondId() != null) ? getPondName(sw.getPondId()) : pondName; messagePondName = swPondName; - String swName = sw.getSwitchName() != null ? sw.getSwitchName() : ("开关" + switchIndex); + String swName = sw != null && sw.getSwitchName() != null ? sw.getSwitchName() : ("开关" + switchIndex); title = "控制器开关输出过流"; message = formatPondWarnMessage(messagePondName, String.format("%s(%s)的开关%d(%s)输出过流", @@ -1855,12 +1849,10 @@ public class DeviceDataHandler { warnType = WARN_TYPE_SWITCH; switchIndex = errorCode - DefineDeviceErrorCode.One_Switch1UnderElectric + 1; DeviceSwitch sw = getDeviceSwitchByIndex(deviceId, switchIndex); - boolean electricWarnOpen = sw != null && sw.getElectricWarnOpen() != null && sw.getElectricWarnOpen() == 1; - warnTrigger = warnTrigger && electricWarnOpen; if (warnTrigger) { - String swPondName = (sw.getPondId() != null) ? getPondName(sw.getPondId()) : pondName; + String swPondName = (sw != null && sw.getPondId() != null) ? getPondName(sw.getPondId()) : pondName; messagePondName = swPondName; - String swName = sw.getSwitchName() != null ? sw.getSwitchName() : ("开关" + switchIndex); + String swName = sw != null && sw.getSwitchName() != null ? sw.getSwitchName() : ("开关" + switchIndex); title = "控制器开关输出欠流"; message = formatPondWarnMessage(messagePondName, String.format("%s(%s)的开关%d(%s)输出欠流", @@ -1872,18 +1864,17 @@ public class DeviceDataHandler { warnType = WARN_TYPE_SWITCH; switchIndex = errorCode - DefineDeviceErrorCode.One_Switch1ElectricEmpty + 1; DeviceSwitch sw = getDeviceSwitchByIndex(deviceId, switchIndex); - boolean electricWarnOpen = sw != null && sw.getElectricWarnOpen() != null && sw.getElectricWarnOpen() == 1; - warnTrigger = warnTrigger && electricWarnOpen; if (warnTrigger) { - String swPondName = (sw.getPondId() != null) ? getPondName(sw.getPondId()) : pondName; + String swPondName = (sw != null && sw.getPondId() != null) ? getPondName(sw.getPondId()) : pondName; messagePondName = swPondName; - String swName = sw.getSwitchName() != null ? sw.getSwitchName() : ("开关" + switchIndex); + String swName = sw != null && sw.getSwitchName() != null ? sw.getSwitchName() : ("开关" + switchIndex); title = "控制器开关输出空载"; message = formatPondWarnMessage(messagePondName, String.format("%s(%s)的开关%d(%s)输出空载", displayName, device.getSerialNum(), switchIndex, swName)); } } else if (errorCode == DefineDeviceErrorCode.PowerOff) { + powerOffError = true; // 断电:已存在离线状态则不触发断电告警 boolean alreadyOffline = device.getWarnCode() != null && (device.getWarnCode() & DefineDeviceWarnCode.DeviceOffline) != 0; @@ -1905,9 +1896,15 @@ public class DeviceDataHandler { if (userId != null) { MessageWarn warn = createMessageWarn(deviceId, userId, messagePondName, title, warnType, message); messageWarnMapper.insert(warn); - // 创建电话通知记录(全局开关控制) - if (aliyunIotProperties.isCallNoticeEnabled()) { + boolean controllerFaultCallOpen = device.getControllerFaultCallOpen() == null + || device.getControllerFaultCallOpen() == 1; + boolean shouldCreateCallNotice = powerOffError || controllerFaultCallOpen; + // 创建电话通知记录:断电不受控制器故障电话开关限制,其他故障受该开关限制。 + if (aliyunIotProperties.isCallNoticeEnabled() && shouldCreateCallNotice) { createCallNotices(device, messagePondName, warn); + } else if (!shouldCreateCallNotice) { + log.info("[故障码] 设备={} 控制器故障电话报警开关关闭,仅记录报警消息: title={}", + device.getSerialNum(), title); } } } diff --git a/intc-modules/intc-iot/src/main/java/com/intc/iot/task/DeviceExpirationWarnTask.java b/intc-modules/intc-iot/src/main/java/com/intc/iot/task/DeviceExpirationWarnTask.java index 83659ad..360b0b7 100644 --- a/intc-modules/intc-iot/src/main/java/com/intc/iot/task/DeviceExpirationWarnTask.java +++ b/intc-modules/intc-iot/src/main/java/com/intc/iot/task/DeviceExpirationWarnTask.java @@ -10,6 +10,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; +import java.time.LocalTime; import java.util.Date; import java.util.List; @@ -35,6 +36,13 @@ public class DeviceExpirationWarnTask { @Scheduled(fixedDelay = 7200000) public void checkDeviceExpirationAndNotify() { try { + // 只在 8:00 ~ 21:00 之间拨打电话通知,避免夜间打扰用户 + LocalTime currentTime = LocalTime.now(); + if (currentTime.isBefore(LocalTime.of(8, 0)) || !currentTime.isBefore(LocalTime.of(21, 0))) { + log.debug("[到期告警] 当前时间 {} 不在电话通知时段(08:00~21:00),跳过本次扫描", currentTime); + return; + } + Date now = new Date(); // 24小时后的时间点 Date deadline = new Date(now.getTime() + 24L * 60 * 60 * 1000);