From a5b8cdffec5b5f014eaa30f7a3d3ca49bc304c65 Mon Sep 17 00:00:00 2001 From: DragonWenLong <552045633@qq.com> Date: Tue, 26 Aug 2025 17:12:36 +0800 Subject: [PATCH] =?UTF-8?q?refactor(device):=20=E4=BC=98=E5=8C=96=E8=AE=BE?= =?UTF-8?q?=E5=A4=87=E5=8F=91=E9=80=81=E5=91=8A=E8=AD=A6=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 批量查询设备,减少数据库交互次数 - 优化设备状态检查逻辑,提高效率 - 封装单个设备发送告警信息的逻辑,提高代码可读性- 使用 Redis 和 MQTT 时增加异常处理,提高系统稳定性 - 优化日志记录和异常提示,便于问题排查 --- .../rule/xinghan/XinghanBootLogoRule.java | 12 ++ .../xinghan/XinghanSendAlarmMessageRule.java | 2 +- .../device/DeviceXinghanBizService.java | 164 +++++++++++------- 3 files changed, 116 insertions(+), 62 deletions(-) diff --git a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/xinghan/XinghanBootLogoRule.java b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/xinghan/XinghanBootLogoRule.java index 96faf308..9109b942 100644 --- a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/xinghan/XinghanBootLogoRule.java +++ b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/xinghan/XinghanBootLogoRule.java @@ -114,13 +114,24 @@ public class XinghanBootLogoRule implements MqttMessageRule { private static final int CHUNK_SIZE = 256; + /** + * 计算数据的CRC32校验值,并将结果转换为整数列表 + * + * @param data 需要计算CRC32校验值的字节数组 + * @return 包含CRC32校验值的整数列表,每个字节对应一个无符号整数 + */ private static ArrayList crc32AsList(byte[] data) { + // 计算CRC32校验值 CRC32 crc = new CRC32(); crc.update(data); + + // 将CRC32值转换为字节数组 byte[] crcBytes = ByteBuffer.allocate(4) .order(ByteOrder.BIG_ENDIAN) .putInt((int) crc.getValue()) .array(); + + // 将字节数组转换为无符号整数列表 ArrayList list = new ArrayList<>(4); for (byte b : crcBytes) { list.add(Byte.toUnsignedInt(b)); @@ -128,6 +139,7 @@ public class XinghanBootLogoRule implements MqttMessageRule { return list; } + /* ---------- DTO ---------- */ @Data diff --git a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/xinghan/XinghanSendAlarmMessageRule.java b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/xinghan/XinghanSendAlarmMessageRule.java index 4134e783..21f9c124 100644 --- a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/xinghan/XinghanSendAlarmMessageRule.java +++ b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/xinghan/XinghanSendAlarmMessageRule.java @@ -79,7 +79,7 @@ public class XinghanSendAlarmMessageRule implements MqttMessageRule { ArrayList intData = new ArrayList<>(); intData.add(blockIndex); // 获取块原内容 转成GBK 再转成无符号十进制整数 - String blockTxt = data.split(",")[blockIndex-1]; + String blockTxt = data.split("\\|")[blockIndex-1]; // 再按 GBK 编码把字符串转成字节数组,并逐个转为无符号十进制整数 for (byte b : blockTxt.getBytes(GBK)) { intData.add(b & 0xFF); // b & 0xFF 得到 0~255 的整数 diff --git a/fys-admin/src/main/java/com/fuyuanshen/web/service/device/DeviceXinghanBizService.java b/fys-admin/src/main/java/com/fuyuanshen/web/service/device/DeviceXinghanBizService.java index fc69d57b..aadd4148 100644 --- a/fys-admin/src/main/java/com/fuyuanshen/web/service/device/DeviceXinghanBizService.java +++ b/fys-admin/src/main/java/com/fuyuanshen/web/service/device/DeviceXinghanBizService.java @@ -201,88 +201,130 @@ public class DeviceXinghanBizService { * @return */ public int sendAlarmMessage(AppDeviceSendMsgBo bo) { + List deviceIds = bo.getDeviceIds(); + + // 1. 简化非空检查和抛出异常 + if (deviceIds == null || deviceIds.isEmpty()) { + throw new ServiceException("请选择设备"); + } + QueryWrapper queryWrapper = new QueryWrapper<>(); + // 使用 in 语句根据id集合查询 + queryWrapper.in("id", deviceIds); + // 2. 将批量查询设备,减少数据库交互次数 + List devices = deviceMapper.selectList(queryWrapper); + if (devices.size() != deviceIds.size()) { + // 如果查询回来的设备数量不一致,说明有设备不存在,此处可以优化为更详细的提示 + throw new ServiceException("部分设备不存在"); + } + try { - List deviceIds = bo.getDeviceIds(); - if (deviceIds == null || deviceIds.isEmpty()) { - throw new ServiceException("请选择设备"); + for (Device device : devices) { + String deviceImei = device.getDeviceImei(); + String deviceName = device.getDeviceName(); + + // 3. 在循环中进行设备状态检查,快速失败 + if (isDeviceOffline(deviceImei)) { + // 如果设备离线,可以选择继续处理下一个设备,或者抛出异常。这里选择抛出异常。 + throw new ServiceException(deviceName + ", 设备已断开连接"); + } + + // 4. 将Redis和MQTT操作封装在一个方法中,提高可读性 + sendSingleAlarmMessage(device, bo.getSendMsg()); + + // 5. 批量更新设备状态,提高效率 + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + updateWrapper.eq("id", device.getId()) + .eq("binding_user_id", AppLoginHelper.getUserId()) + .set("send_msg", bo.getSendMsg()); + deviceMapper.update(updateWrapper); + + // 6. 记录操作日志 + recordDeviceLog(device.getId(), deviceName, "发送紧急通知", bo.getSendMsg(), AppLoginHelper.getUserId()); } - - for (Long deviceId : deviceIds) { - Device device = deviceMapper.selectById(deviceId); - if (device == null) { - throw new ServiceException("设备不存在" + deviceId); - } - if(isDeviceOffline(device.getDeviceImei())){ - throw new ServiceException(device.getDeviceName()+",设备已断开连接"); - } - try { - - RedisUtils.setCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + device.getDeviceImei() + DEVICE_ALARM_MESSAGE_KEY_PREFIX, bo.getSendMsg(),Duration.ofSeconds(5 * 60L)); - - Map payload = Map.of("ins_BreakNews", - Collections.singletonList(0)); - String topic = MqttConstants.GLOBAL_PUB_KEY + device.getDeviceImei(); - String json = JsonUtils.toJsonString(payload); - - try { - mqttGateway.sendMsgToMqtt(topic, 1, json); - } catch (Exception e) { - log.error("发送紧急通知失败, topic={}, payload={}", topic, json, e); - throw new ServiceException("发送紧急通知失败:" + e.getMessage()); - } - log.info("发送紧急通知=>topic:{},payload:{}", MqttConstants.GLOBAL_PUB_KEY+device.getDeviceImei(),json); - - UpdateWrapper updateWrapper = new UpdateWrapper<>(); - updateWrapper.eq("id", deviceId) - .eq("binding_user_id", AppLoginHelper.getUserId()) - .set("send_msg", bo.getSendMsg()); - deviceMapper.update(updateWrapper); - recordDeviceLog(device.getId(), device.getDeviceName(), "发送紧急通知", bo.getSendMsg(), AppLoginHelper.getUserId()); - } catch (Exception e) { - log.info("设备发送告警信息信息失败:{}" ,deviceId); - throw new ServiceException("设备发送告警信息信息失败"); - } - - } - } catch (Exception e){ - e.printStackTrace(); + } catch (ServiceException e) { + // 捕获并重新抛出自定义异常,避免内层异常被外层泛化捕获 + log.error("发送告警信息指令失败: {}", e.getMessage(), e); + throw e; + } catch (Exception e) { + log.error("发送告警信息指令发生未知错误", e); throw new ServiceException("发送告警信息指令失败"); } + return 1; } - /* ---------------------------------- 私有通用方法 ---------------------------------- */ - private void sendCommand(DeviceInstructDto dto, - String payloadKey,String deviceAction) { - long deviceId = dto.getDeviceId(); - Device device = deviceMapper.selectById(deviceId); - if (device == null) { - throw new ServiceException("设备不存在"); - } - if (isDeviceOffline(device.getDeviceImei())) { - throw new ServiceException("设备已断开连接:" + device.getDeviceName()); - } + /** + * 封装单个设备发送告警信息的逻辑 + */ + private void sendSingleAlarmMessage(Device device, String message) { + String deviceImei = device.getDeviceImei(); - Integer value = Integer.parseInt(dto.getInstructValue()); + // 缓存告警消息到Redis + RedisUtils.setCacheObject(GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX + deviceImei + DEVICE_ALARM_MESSAGE_KEY_PREFIX, message, Duration.ofSeconds(5 * 60L)); - Map payload = Map.of(payloadKey, - Collections.singletonList(value)); - - String topic = MqttConstants.GLOBAL_PUB_KEY + device.getDeviceImei(); + // 构建并发送MQTT消息 + Map payload = Map.of("ins_BreakNews", Collections.singletonList(0)); + String topic = MqttConstants.GLOBAL_PUB_KEY + deviceImei; String json = JsonUtils.toJsonString(payload); try { mqttGateway.sendMsgToMqtt(topic, 1, json); + log.info("发送紧急通知成功 => topic:{}, payload:{}", topic, json); + } catch (Exception e) { + log.error("发送紧急通知失败, topic={}, payload={}", topic, json, e); + throw new ServiceException("发送紧急通知失败:" + e.getMessage()); + } + } + + /** + * 发送设备控制指令 + * + * @param dto 设备指令数据传输对象,包含设备ID和指令值等信息 + * @param payloadKey 指令负载数据的键名 + * @param deviceAction 设备操作类型描述 + */ + private void sendCommand(DeviceInstructDto dto, String payloadKey, String deviceAction) { + long deviceId = dto.getDeviceId(); + + // 1. 使用Optional简化空值检查,使代码更简洁 + Device device = Optional.ofNullable(deviceMapper.selectById(deviceId)) + .orElseThrow(() -> new ServiceException("设备不存在")); + + String deviceImei = device.getDeviceImei(); + String deviceName = device.getDeviceName(); + + // 2. 提前进行设备状态检查,逻辑更清晰 + if (isDeviceOffline(deviceImei)) { + throw new ServiceException("设备已断开连接:" + deviceName); + } + + // 3. 统一处理类型转换异常,避免在业务逻辑中混杂try-catch + int value; + try { + value = Integer.parseInt(dto.getInstructValue()); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("指令值格式不正确,必须为整数。", e); + } + + // 4. 使用Map.of()或Map.ofEntries()创建不可变Map,更简洁且线程安全 + Map> payload = Map.of(payloadKey, List.of(value)); + + String topic = MqttConstants.GLOBAL_PUB_KEY + deviceImei; + String json = JsonUtils.toJsonString(payload); + + try { + mqttGateway.sendMsgToMqtt(topic, 1, json); + log.info("发送指令成功 => topic:{}, payload:{}", topic, json); } catch (Exception e) { log.error("发送指令失败, topic={}, payload={}", topic, json, e); throw new ServiceException("发送指令失败:" + e.getMessage()); } - log.info("发送指令成功 => topic:{}, payload:{}", topic, json); + // 5. 将日志记录和描述解析放在try-catch块之外,确保无论是否成功发送指令都能执行 String content = resolveGradeDesc("ins_DetectGrade", value); recordDeviceLog(device.getId(), - device.getDeviceName(), + deviceName, deviceAction, content, AppLoginHelper.getUserId());