From 7eb5e6095a926f3e03035b2d2428bfac25400196 Mon Sep 17 00:00:00 2001 From: DragonWenLong <552045633@qq.com> Date: Thu, 25 Sep 2025 08:31:32 +0800 Subject: [PATCH] =?UTF-8?q?feat(device):=20=E6=96=B0=E5=A2=9E=E6=8A=A5?= =?UTF-8?q?=E8=AD=A6=E7=B1=BB=E5=9E=8B=E6=9E=9A=E4=B8=BE=E5=B9=B6=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E8=AE=BE=E5=A4=87=E6=8C=87=E4=BB=A4=E5=A4=84=E7=90=86?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 AlarmTypeEnum 枚举类,定义 SOS 和静止报警类型 -优化 AppAuthController 中版本信息解析逻辑,增强空值处理 - 统一设备指令接口参数类型为 DeviceXinghanInstructDto - 在 DeviceXinghanBizService 中实现 SOS 报警创建逻辑 -重构 XinghanDeviceDataRule 报警处理流程,使用统一枚举类型- 添加蓝牙模式下 SOS 指令的特殊处理逻辑- 完善报警 Redis 缓存键构建和续期机制 --- .../app/controller/AppAuthController.java | 23 +++--- .../device/AppDeviceXinghanController.java | 9 ++- .../rule/xinghan/XinghanDeviceDataRule.java | 24 ++---- .../device/DeviceXinghanController.java | 9 ++- .../domain/Dto/DeviceXinghanInstructDto.java | 15 ++++ .../fuyuanshen/web/enums/AlarmTypeEnum.java | 17 +++++ .../device/DeviceXinghanBizService.java | 74 +++++++++++++++++-- 7 files changed, 129 insertions(+), 42 deletions(-) create mode 100644 fys-admin/src/main/java/com/fuyuanshen/web/domain/Dto/DeviceXinghanInstructDto.java create mode 100644 fys-admin/src/main/java/com/fuyuanshen/web/enums/AlarmTypeEnum.java diff --git a/fys-admin/src/main/java/com/fuyuanshen/app/controller/AppAuthController.java b/fys-admin/src/main/java/com/fuyuanshen/app/controller/AppAuthController.java index 33c19f6d9..5e9a0ab30 100644 --- a/fys-admin/src/main/java/com/fuyuanshen/app/controller/AppAuthController.java +++ b/fys-admin/src/main/java/com/fuyuanshen/app/controller/AppAuthController.java @@ -263,18 +263,21 @@ public class AppAuthController { @GetMapping("/version") public R> getAppVersion() { List list = dictTypeService.selectDictDataByType("app_version"); + list.forEach(d -> { - String[] arr = d.getRemark().split("\\|"); - d.setDictLabel(d.getDictLabel()); // ios/android - d.setDictValue(arr[0]); // 版本号 - d.setRemark(arr[1]); // 下载地址 + // 1. 安全拆分 + String[] arr = d.getRemark() == null ? new String[0] : d.getRemark().split("\\|"); + if (arr.length < 2) { // 格式不对 + log.warn("字典数据 app_version 格式非法:dictLabel={}, remark={}", d.getDictLabel(), d.getRemark()); + d.setDictValue(""); // 或者 d.setDictValue(d.getDictValue()); + d.setRemark(""); // 下载地址留空 + return; // 跳过 + } + // 2. 正常赋值 + d.setDictValue(arr[0].trim()); // 版本号 + d.setRemark(arr[1].trim()); // 下载地址 }); - // 只保留方法体:筛选 label=ios 且版本号 ≥ 2.5.0 的列表 -// List result = list.stream() -// .peek(d -> { String[] a = d.getRemark().split("\\|"); d.setDictValue(a[0]); d.setRemark(a[1]); }) -// .filter(d -> "ios".equalsIgnoreCase(d.getDictLabel())) -// .filter(d -> VersionComparator.INSTANCE.compare(d.getDictValue(), "2.5.0") >= 0) -// .toList(); + return R.ok(list); } diff --git a/fys-admin/src/main/java/com/fuyuanshen/app/controller/device/AppDeviceXinghanController.java b/fys-admin/src/main/java/com/fuyuanshen/app/controller/device/AppDeviceXinghanController.java index 15d9df5df..3bb702892 100644 --- a/fys-admin/src/main/java/com/fuyuanshen/app/controller/device/AppDeviceXinghanController.java +++ b/fys-admin/src/main/java/com/fuyuanshen/app/controller/device/AppDeviceXinghanController.java @@ -10,6 +10,7 @@ import com.fuyuanshen.common.ratelimiter.annotation.FunctionAccessAnnotation; import com.fuyuanshen.common.ratelimiter.annotation.FunctionAccessBatcAnnotation; import com.fuyuanshen.common.web.core.BaseController; import com.fuyuanshen.equipment.domain.dto.AppDeviceSendMsgBo; +import com.fuyuanshen.web.domain.Dto.DeviceXinghanInstructDto; import com.fuyuanshen.web.service.device.DeviceBJQBizService; import com.fuyuanshen.web.service.device.DeviceXinghanBizService; import lombok.RequiredArgsConstructor; @@ -70,7 +71,7 @@ public class AppDeviceXinghanController extends BaseController { */ @Log(title = "xinghan指令-静电预警档位") @PostMapping("/DetectGradeSettings") - public R DetectGradeSettings(@RequestBody DeviceInstructDto params) { + public R DetectGradeSettings(@RequestBody DeviceXinghanInstructDto params) { // params 转 JSONObject appDeviceService.upDetectGradeSettings(params); return R.ok(); @@ -82,7 +83,7 @@ public class AppDeviceXinghanController extends BaseController { */ @Log(title = "xinghan指令-照明档位") @PostMapping("/LightGradeSettings") - public R LightGradeSettings(@RequestBody DeviceInstructDto params) { + public R LightGradeSettings(@RequestBody DeviceXinghanInstructDto params) { // params 转 JSONObject appDeviceService.upLightGradeSettings(params); return R.ok(); @@ -94,7 +95,7 @@ public class AppDeviceXinghanController extends BaseController { */ @Log(title = "xinghan指令-SOS档位s") @PostMapping("/SOSGradeSettings") - public R SOSGradeSettings(@RequestBody DeviceInstructDto params) { + public R SOSGradeSettings(@RequestBody DeviceXinghanInstructDto params) { // params 转 JSONObject appDeviceService.upSOSGradeSettings(params); return R.ok(); @@ -106,7 +107,7 @@ public class AppDeviceXinghanController extends BaseController { */ @Log(title = "xinghan指令-静止报警状态") @PostMapping("/ShakeBitSettings") - public R ShakeBitSettings(@RequestBody DeviceInstructDto params) { + public R ShakeBitSettings(@RequestBody DeviceXinghanInstructDto params) { // params 转 JSONObject appDeviceService.upShakeBitSettings(params); return R.ok(); diff --git a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/xinghan/XinghanDeviceDataRule.java b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/xinghan/XinghanDeviceDataRule.java index 6fc2f5f5a..cbcf2eab7 100644 --- a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/xinghan/XinghanDeviceDataRule.java +++ b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/rule/xinghan/XinghanDeviceDataRule.java @@ -23,6 +23,7 @@ import com.fuyuanshen.global.mqtt.base.MqttXinghanJson; import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants; import com.fuyuanshen.global.mqtt.constants.XingHanCommandTypeConstants; import com.fuyuanshen.global.mqtt.listener.domain.FunctionAccessStatus; +import com.fuyuanshen.web.enums.AlarmTypeEnum; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -127,18 +128,18 @@ public class XinghanDeviceDataRule implements MqttMessageRule { // 1. 处理 SOS 报警 handleSingleAlarm(deviceImei, sos > 0, - AlarmType.SOS); + AlarmTypeEnum.SOS); int shake = Optional.ofNullable(status.getStaShakeBit()).orElse(0); // 2. 处理 Shake 报警 handleSingleAlarm(deviceImei, shake > 0, - AlarmType.SHAKE); + AlarmTypeEnum.SHAKE); } /** * 通用:对单个报警源的“开始/结束”生命周期管理 */ - private void handleSingleAlarm(String deviceImei, boolean nowAlarming, AlarmType type) { + private void handleSingleAlarm(String deviceImei, boolean nowAlarming, AlarmTypeEnum type) { String redisKey = buildAlarmRedisKey(deviceImei, type); Long alarmId = RedisUtils.getCacheObject(redisKey); @@ -184,7 +185,7 @@ public class XinghanDeviceDataRule implements MqttMessageRule { /** * 新建报警 Bo */ - private DeviceAlarmBo createAlarmBo(String deviceImei, AlarmType type) { + private DeviceAlarmBo createAlarmBo(String deviceImei, AlarmTypeEnum type) { Device device = deviceService.selectDeviceByImei(deviceImei); if (device == null) { return null; @@ -212,24 +213,11 @@ public class XinghanDeviceDataRule implements MqttMessageRule { /** * key 构建 */ - private String buildAlarmRedisKey(String deviceImei, AlarmType type) { + private String buildAlarmRedisKey(String deviceImei, AlarmTypeEnum type) { return StrUtil.format("{}{}{}{}:alarm_id", GLOBAL_REDIS_KEY, DEVICE_KEY_PREFIX, deviceImei, type.getSuffix()); } - /** - * 报警类型枚举 - */ - @AllArgsConstructor - @Getter - enum AlarmType { - SOS("_sos", "SOS报警"), - SHAKE("_shake", "静止报警"); - - private final String suffix; - private final String desc; - } - /** * 异步发送位置信息到Redis(使用CompletableFuture) * diff --git a/fys-admin/src/main/java/com/fuyuanshen/web/controller/device/DeviceXinghanController.java b/fys-admin/src/main/java/com/fuyuanshen/web/controller/device/DeviceXinghanController.java index 4cc51d87d..2f1c32405 100644 --- a/fys-admin/src/main/java/com/fuyuanshen/web/controller/device/DeviceXinghanController.java +++ b/fys-admin/src/main/java/com/fuyuanshen/web/controller/device/DeviceXinghanController.java @@ -10,6 +10,7 @@ import com.fuyuanshen.common.ratelimiter.annotation.FunctionAccessAnnotation; import com.fuyuanshen.common.ratelimiter.annotation.FunctionAccessBatcAnnotation; import com.fuyuanshen.common.web.core.BaseController; import com.fuyuanshen.equipment.domain.dto.AppDeviceSendMsgBo; +import com.fuyuanshen.web.domain.Dto.DeviceXinghanInstructDto; import com.fuyuanshen.web.domain.vo.DeviceXinghanDetailVo; import com.fuyuanshen.web.service.device.DeviceXinghanBizService; import jakarta.validation.constraints.NotNull; @@ -80,7 +81,7 @@ public class DeviceXinghanController extends BaseController { * 3,2,1,0,分别表示高档/中档/低挡/关闭 */ @PostMapping("/DetectGradeSettings") - public R DetectGradeSettings(@RequestBody DeviceInstructDto params) { + public R DetectGradeSettings(@RequestBody DeviceXinghanInstructDto params) { // params 转 JSONObject deviceXinghanBizService.upDetectGradeSettings(params); return R.ok(); @@ -91,7 +92,7 @@ public class DeviceXinghanController extends BaseController { * 照明档位,2,1,0,分别表示弱光/强光/关闭 */ @PostMapping("/LightGradeSettings") - public R LightGradeSettings(@RequestBody DeviceInstructDto params) { + public R LightGradeSettings(@RequestBody DeviceXinghanInstructDto params) { // params 转 JSONObject deviceXinghanBizService.upLightGradeSettings(params); return R.ok(); @@ -102,7 +103,7 @@ public class DeviceXinghanController extends BaseController { * SOS档位,2,1,0, 分别表示红蓝模式/爆闪模式/关闭 */ @PostMapping("/SOSGradeSettings") - public R SOSGradeSettings(@RequestBody DeviceInstructDto params) { + public R SOSGradeSettings(@RequestBody DeviceXinghanInstructDto params) { // params 转 JSONObject deviceXinghanBizService.upSOSGradeSettings(params); return R.ok(); @@ -113,7 +114,7 @@ public class DeviceXinghanController extends BaseController { * 静止报警状态,0-未静止报警,1-正在静止报警。 */ @PostMapping("/ShakeBitSettings") - public R ShakeBitSettings(@RequestBody DeviceInstructDto params) { + public R ShakeBitSettings(@RequestBody DeviceXinghanInstructDto params) { // params 转 JSONObject deviceXinghanBizService.upShakeBitSettings(params); return R.ok(); diff --git a/fys-admin/src/main/java/com/fuyuanshen/web/domain/Dto/DeviceXinghanInstructDto.java b/fys-admin/src/main/java/com/fuyuanshen/web/domain/Dto/DeviceXinghanInstructDto.java new file mode 100644 index 000000000..0d3b598f5 --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/web/domain/Dto/DeviceXinghanInstructDto.java @@ -0,0 +1,15 @@ +package com.fuyuanshen.web.domain.Dto; + +import lombok.Data; + +@Data +public class DeviceXinghanInstructDto { + private Long deviceId; + + private String deviceImei; + /** + * 下发指令 + */ + private String instructValue; + private Boolean isBluetooth = false; +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/web/enums/AlarmTypeEnum.java b/fys-admin/src/main/java/com/fuyuanshen/web/enums/AlarmTypeEnum.java new file mode 100644 index 000000000..bde6fdcab --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/web/enums/AlarmTypeEnum.java @@ -0,0 +1,17 @@ +package com.fuyuanshen.web.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 报警类型枚举 + */ +@AllArgsConstructor +@Getter +public enum AlarmTypeEnum { + SOS("_sos", "SOS报警"), + SHAKE("_shake", "静止报警"); + + private final String suffix; + private final String desc; +} 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 322545929..617458cc6 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 @@ -1,6 +1,7 @@ package com.fuyuanshen.web.service.device; import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.StrUtil; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; @@ -28,17 +29,21 @@ import com.fuyuanshen.common.redis.utils.RedisUtils; import com.fuyuanshen.common.satoken.utils.AppLoginHelper; import com.fuyuanshen.equipment.domain.Device; import com.fuyuanshen.equipment.domain.DeviceType; +import com.fuyuanshen.equipment.domain.bo.DeviceAlarmBo; import com.fuyuanshen.equipment.domain.dto.AppDeviceSendMsgBo; import com.fuyuanshen.equipment.enums.LightModeEnum; import com.fuyuanshen.equipment.mapper.DeviceLogMapper; import com.fuyuanshen.equipment.mapper.DeviceMapper; import com.fuyuanshen.equipment.mapper.DeviceTypeMapper; +import com.fuyuanshen.equipment.service.IDeviceAlarmService; import com.fuyuanshen.global.mqtt.base.MqttXinghanJson; import com.fuyuanshen.global.mqtt.config.MqttGateway; import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants; import com.fuyuanshen.global.mqtt.constants.MqttConstants; import com.fuyuanshen.web.domain.Dto.DeviceDebugLogoUploadDto; +import com.fuyuanshen.web.domain.Dto.DeviceXinghanInstructDto; import com.fuyuanshen.web.domain.vo.DeviceXinghanDetailVo; +import com.fuyuanshen.web.enums.AlarmTypeEnum; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -68,6 +73,7 @@ public class DeviceXinghanBizService { private final MqttGateway mqttGateway; private final DeviceLogMapper deviceLogMapper; private final AppPersonnelInfoRecordsMapper appPersonnelInfoRecordsMapper; + private final IDeviceAlarmService deviceAlarmService; @Autowired private ObjectMapper objectMapper; @@ -95,28 +101,39 @@ public class DeviceXinghanBizService { /** * 设置静电预警档位 */ - public void upDetectGradeSettings(DeviceInstructDto dto) { + public void upDetectGradeSettings(DeviceXinghanInstructDto dto) { sendCommand(dto, "ins_DetectGrade","静电预警档位"); } /** * 设置照明档位 */ - public void upLightGradeSettings(DeviceInstructDto dto) { + public void upLightGradeSettings(DeviceXinghanInstructDto dto) { sendCommand(dto, "ins_LightGrade","照明档位"); } /** * 设置SOS档位 */ - public void upSOSGradeSettings(DeviceInstructDto dto) { - sendCommand(dto, "ins_SOSGrade","SOS档位"); + public void upSOSGradeSettings(DeviceXinghanInstructDto dto) { + if(dto.getIsBluetooth()){ + long deviceId = dto.getDeviceId(); + + // 1. 使用Optional简化空值检查,使代码更简洁 + Device device = Optional.ofNullable(deviceMapper.selectById(deviceId)) + .orElseThrow(() -> new ServiceException("设备不存在")); + int sosGrade = Integer.parseInt(dto.getInstructValue()); + // 6. 新建报警信息 + createAlarm(device.getId(),device.getDeviceImei(),"ins_SOSGrade",sosGrade); + }else { + sendCommand(dto, "ins_SOSGrade","SOS档位"); + } } /** * 设置强制报警 */ - public void upShakeBitSettings(DeviceInstructDto dto) { + public void upShakeBitSettings(DeviceXinghanInstructDto dto) { sendCommand(dto, "ins_ShakeBit","强制报警"); } @@ -465,7 +482,7 @@ public class DeviceXinghanBizService { * @param payloadKey 指令负载数据的键名 * @param deviceAction 设备操作类型描述 */ - private void sendCommand(DeviceInstructDto dto, String payloadKey, String deviceAction) { + private void sendCommand(DeviceXinghanInstructDto dto, String payloadKey, String deviceAction) { long deviceId = dto.getDeviceId(); // 1. 使用Optional简化空值检查,使代码更简洁 @@ -509,6 +526,9 @@ public class DeviceXinghanBizService { deviceAction, content, AppLoginHelper.getUserId()); + + // 6. 新建报警信息 + createAlarm(device.getId(),deviceImei,payloadKey,value); } // private boolean isDeviceOffline(String imei) { @@ -516,6 +536,48 @@ public class DeviceXinghanBizService { // return getDeviceStatus(imei); // } + /** + * 新建报警 Bo + */ + private void createAlarm(Long deviceId, String deviceImei, + String payloadKey, int value) { + // 这里直接放你原来的 createAlarmBo 全部逻辑 + if (!"ins_SOSGrade".equals(payloadKey) || value == 0) { + return; + } + AlarmTypeEnum type = value == 1 ? AlarmTypeEnum.SOS : AlarmTypeEnum.SHAKE; + String redisKey = buildAlarmRedisKey(deviceImei, type); + Long alarmId = RedisUtils.getCacheObject(redisKey); + // 已存在未结束报警 -> 什么都不做(同一条报警) + if (alarmId != null) { + // key 还在 -> 同一条报警,只续期 + RedisUtils.setCacheObject(redisKey, alarmId, Duration.ofMinutes(10)); + return; + } + // 不存在 -> 新建 + DeviceAlarmBo bo = new DeviceAlarmBo(); + bo.setDeviceId(deviceId); + bo.setDeviceImei(deviceImei); + bo.setDeviceAction(0); // 强制报警 + bo.setStartTime(new Date()); + bo.setTreatmentState(1); // 未处理 + bo.setContent("强制报警:" + type.getDesc()); + String location = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX + deviceImei + DEVICE_LOCATION_KEY_PREFIX); + if (StrUtil.isNotBlank(location)) { + bo.setLocation(JSONObject.parseObject(location).getString("address")); + } + deviceAlarmService.insertByBo(bo); + RedisUtils.setCacheObject(redisKey, bo.getId(), Duration.ofMinutes(10)); + } + + /** + * key 构建 + */ + private String buildAlarmRedisKey(String deviceImei, AlarmTypeEnum type) { + return StrUtil.format("{}{}{}{}:alarm_id", + GLOBAL_REDIS_KEY, DEVICE_KEY_PREFIX, deviceImei, type.getSuffix()); + } + /** * 记录设备操作日志 * @param deviceId 设备ID