From 49e9066033a27176695efacf54a3d17bc7209107 Mon Sep 17 00:00:00 2001 From: DragonWenLong <552045633@qq.com> Date: Tue, 23 Sep 2025 11:31:11 +0800 Subject: [PATCH 1/4] =?UTF-8?q?feat(equipment):=20=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E8=AE=BE=E5=A4=87=E7=BB=B4=E4=BF=AE=E8=AE=B0=E5=BD=95=E5=9B=BE?= =?UTF-8?q?=E7=89=87=E7=AE=A1=E7=90=86=E5=92=8C=E6=8A=A5=E8=AD=A6=E5=A4=84?= =?UTF-8?q?=E7=90=86=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -重构文件哈希工具类路径并优化上传逻辑,支持秒传 - 新增维修记录图片ID列表字段及删除旧图片逻辑- 设备维修记录查询增加设备名称模糊搜索条件 -日期查询条件添加格式化注解支持 yyyy-MM-dd- MQTT规则中新增SOS与静止报警处理机制 - 实现报警生命周期管理(开始/结束)及Redis缓存控制 - 添加报警信息入库和位置解析功能 - 优化设备状态数据解析与经纬度异步存储逻辑 --- .../global/mqtt/base/MqttXinghanJson.java | 5 + .../rule/xinghan/XinghanDeviceDataRule.java | 132 +++++++++++++++++- .../service/device/DeviceDebugService.java | 5 +- .../domain/bo/DeviceRepairRecordsBo.java | 4 + .../DeviceRepairRecordsQueryCriteria.java | 3 + .../impl/DeviceRepairRecordsServiceImpl.java | 26 +++- .../equipment/utils}/FileHashUtil.java | 2 +- .../equipment/DeviceRepairRecordsMapper.xml | 3 + 8 files changed, 168 insertions(+), 12 deletions(-) rename {fys-admin/src/main/java/com/fuyuanshen/web/util => fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/utils}/FileHashUtil.java (95%) diff --git a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/base/MqttXinghanJson.java b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/base/MqttXinghanJson.java index 3e0737f..4d9513f 100644 --- a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/base/MqttXinghanJson.java +++ b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/base/MqttXinghanJson.java @@ -61,5 +61,10 @@ public class MqttXinghanJson { */ @JsonProperty("sta_latitude") public String stalatitude; + /** + * 第十二键值对,系统现状,0关机,1仅充电,2开机未充电,3,开机且充电 + */ + @JsonProperty("sta_system") + public String stasystem; } 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 cf4fbd0..17dce0e 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 @@ -1,33 +1,41 @@ package com.fuyuanshen.global.mqtt.rule.xinghan; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.StrUtil; import com.alibaba.fastjson2.JSONObject; import com.fasterxml.jackson.databind.ObjectMapper; import com.fuyuanshen.common.core.constant.GlobalConstants; import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.core.utils.date.DurationUtils; import com.fuyuanshen.common.json.utils.JsonUtils; import com.fuyuanshen.common.redis.utils.RedisUtils; +import com.fuyuanshen.equipment.domain.Device; +import com.fuyuanshen.equipment.domain.bo.DeviceAlarmBo; +import com.fuyuanshen.equipment.domain.vo.DeviceAlarmVo; +import com.fuyuanshen.equipment.mapper.DeviceAlarmMapper; +import com.fuyuanshen.equipment.service.DeviceService; +import com.fuyuanshen.equipment.service.IDeviceAlarmService; import com.fuyuanshen.equipment.utils.map.GetAddressFromLatUtil; import com.fuyuanshen.equipment.utils.map.LngLonUtil; import com.fuyuanshen.global.mqtt.base.MqttMessageRule; import com.fuyuanshen.global.mqtt.base.MqttRuleContext; import com.fuyuanshen.global.mqtt.base.MqttXinghanJson; import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants; -import com.fuyuanshen.global.mqtt.constants.LightingCommandTypeConstants; import com.fuyuanshen.global.mqtt.constants.XingHanCommandTypeConstants; import com.fuyuanshen.global.mqtt.listener.domain.FunctionAccessStatus; +import lombok.AllArgsConstructor; +import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.springframework.beans.factory.annotation.Autowired; import java.time.Duration; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; +import java.util.*; import java.util.concurrent.CompletableFuture; import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_KEY; +import static com.fuyuanshen.common.core.constant.GlobalConstants.GLOBAL_REDIS_KEY; import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.*; import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX; @@ -57,6 +65,9 @@ public class XinghanDeviceDataRule implements MqttMessageRule { @Autowired private ObjectMapper objectMapper; + private final IDeviceAlarmService deviceAlarmService; + private final DeviceService deviceService; + private final DeviceAlarmMapper deviceAlarmMapper; @Override public void execute(MqttRuleContext context) { @@ -99,12 +110,123 @@ public class XinghanDeviceDataRule implements MqttMessageRule { // 异步发送经纬度到Redis asyncSendLocationToRedisWithFuture(deviceImei, deviceStatus.getStalatitude(), deviceStatus.getStalongitude()); + // 保存报警信息 + saveAlarm(deviceImei,deviceStatus); + } catch (Exception e) { log.error("异步发送设备信息到Redis时出错: device={}, error={}", deviceImei, e.getMessage(), e); } }); } + /** + * 入口:保存报警(SOS 与 Shake 完全独立) + */ + public void saveAlarm(String deviceImei, MqttXinghanJson status) { + int sos = Optional.ofNullable(status.getStaSOSGrade()).orElse(0); + // 1. 处理 SOS 报警 + handleSingleAlarm(deviceImei, + sos > 0, + AlarmType.SOS); + int shake = Optional.ofNullable(status.getStaShakeBit()).orElse(0); + // 2. 处理 Shake 报警 + handleSingleAlarm(deviceImei, + shake > 0, + AlarmType.SHAKE); + } + + /** + * 通用:对单个报警源的“开始/结束”生命周期管理 + */ + private void handleSingleAlarm(String deviceImei, boolean nowAlarming, AlarmType type) { + String redisKey = buildAlarmRedisKey(deviceImei, type); + + Long alarmId = RedisUtils.getCacheObject(redisKey); + + // ---------- 情况 1:当前正在报警 ---------- + if (nowAlarming) { + // 已存在未结束报警 -> 什么都不做(同一条报警) + if (alarmId != null) { + // key 还在 -> 同一条报警,只续期 + RedisUtils.setCacheObject(redisKey, alarmId, Duration.ofMinutes(10)); + return; + } + // 不存在 -> 新建 + DeviceAlarmBo bo = createAlarmBo(deviceImei, type); + deviceAlarmService.insertByBo(bo); + RedisUtils.setCacheObject(redisKey, bo.getId(), Duration.ofMinutes(10)); // 5分钟后结束过期 + return; + } + + // ---------- 情况 2:当前不报警 ---------- + if (alarmId != null) { + // 结束它 + finishAlarm(alarmId); + RedisUtils.deleteObject(redisKey); + } + } + + /** + * 结束报警:写库 + */ + private void finishAlarm(Long alarmId) { + DeviceAlarmVo vo = deviceAlarmService.queryById(alarmId); + if (vo == null || vo.getTreatmentState() == 0) { + return; // 已处理或已被删 + } + DeviceAlarmBo bo = BeanUtil.toBean(vo, DeviceAlarmBo.class); + bo.setFinishTime(new Date()); + bo.setDurationTime(DurationUtils.getDurationBetween(vo.getStartTime(), bo.getFinishTime())); + bo.setTreatmentState(0); // 已处理 + deviceAlarmService.updateByBo(bo); + } + + /** + * 新建报警 Bo + */ + private DeviceAlarmBo createAlarmBo(String deviceImei, AlarmType type) { + Device device = deviceService.selectDeviceByImei(deviceImei); + DeviceAlarmBo bo = new DeviceAlarmBo(); + bo.setDeviceId(device.getId()); + bo.setDeviceImei(deviceImei); + bo.setDeviceAction(2); // 自动报警 + bo.setStartTime(new Date()); + bo.setTreatmentState(1); // 未处理 + bo.setTenantId(device.getTenantId()); + + // 报警内容 + 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")); + } + return bo; + } + + /** + * key 构建 + */ + private String buildAlarmRedisKey(String deviceImei, AlarmType 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/service/device/DeviceDebugService.java b/fys-admin/src/main/java/com/fuyuanshen/web/service/device/DeviceDebugService.java index 2f2e6b8..d4b875a 100644 --- a/fys-admin/src/main/java/com/fuyuanshen/web/service/device/DeviceDebugService.java +++ b/fys-admin/src/main/java/com/fuyuanshen/web/service/device/DeviceDebugService.java @@ -10,18 +10,15 @@ import com.fuyuanshen.app.service.IAppBusinessFileService; import com.fuyuanshen.app.service.IAppOperationVideoService; import com.fuyuanshen.common.core.exception.ServiceException; import com.fuyuanshen.common.satoken.utils.AppLoginHelper; -import com.fuyuanshen.equipment.mapper.DeviceMapper; import com.fuyuanshen.equipment.service.DeviceService; import com.fuyuanshen.system.domain.vo.SysOssVo; import com.fuyuanshen.system.service.ISysOssService; import com.fuyuanshen.web.domain.vo.DeviceInfoVo; -import com.fuyuanshen.web.util.FileHashUtil; -import io.swagger.v3.oas.annotations.Operation; +import com.fuyuanshen.equipment.utils.FileHashUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/bo/DeviceRepairRecordsBo.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/bo/DeviceRepairRecordsBo.java index e134543..e463172 100644 --- a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/bo/DeviceRepairRecordsBo.java +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/bo/DeviceRepairRecordsBo.java @@ -11,6 +11,8 @@ import lombok.Data; import lombok.EqualsAndHashCode; import jakarta.validation.constraints.*; import java.util.Date; +import java.util.List; + import com.fasterxml.jackson.annotation.JsonFormat; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.web.multipart.MultipartFile; @@ -70,4 +72,6 @@ public class DeviceRepairRecordsBo extends BaseEntity { @Schema(title = "维修后图片") @JsonIgnore private MultipartFile afterFile; + + private List imageIds; } diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/query/DeviceRepairRecordsQueryCriteria.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/query/DeviceRepairRecordsQueryCriteria.java index 4df5ec6..bf9ba0f 100644 --- a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/query/DeviceRepairRecordsQueryCriteria.java +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/query/DeviceRepairRecordsQueryCriteria.java @@ -7,6 +7,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; import org.springframework.web.multipart.MultipartFile; import java.util.Date; @@ -51,8 +52,10 @@ public class DeviceRepairRecordsQueryCriteria extends BaseEntity { private String repairPerson; @Schema(title = "维修开始时间") + @DateTimeFormat(pattern = "yyyy-MM-dd") private Date repairBeginTime; @Schema(title = "维修结束时间") + @DateTimeFormat(pattern = "yyyy-MM-dd") private Date repairEndTime; @Schema(title = "所属客户") diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/impl/DeviceRepairRecordsServiceImpl.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/impl/DeviceRepairRecordsServiceImpl.java index 3b838d8..80ff0fe 100644 --- a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/impl/DeviceRepairRecordsServiceImpl.java +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/impl/DeviceRepairRecordsServiceImpl.java @@ -18,6 +18,7 @@ import com.fuyuanshen.equipment.domain.vo.DeviceRepairImagesVo; import com.fuyuanshen.equipment.enums.RepairImageType; import com.fuyuanshen.equipment.mapper.DeviceMapper; import com.fuyuanshen.equipment.mapper.DeviceRepairImagesMapper; +import com.fuyuanshen.equipment.utils.FileHashUtil; import com.fuyuanshen.system.domain.vo.SysOssVo; import com.fuyuanshen.system.service.ISysOssService; import lombok.RequiredArgsConstructor; @@ -32,6 +33,7 @@ import com.fuyuanshen.equipment.service.IDeviceRepairRecordsService; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; +import java.io.IOException; import java.util.*; /** @@ -178,6 +180,10 @@ public class DeviceRepairRecordsServiceImpl extends ServiceImpl images = new ArrayList<>(2); @@ -200,12 +206,28 @@ public class DeviceRepairRecordsServiceImpl extends ServiceImpl + + and d.device_name like concat('%', TRIM(#{criteria.searchValue}), '%') + and dr.device_id = #{criteria.deviceId} From baa341c2bf7c4749edf348462b84bc6f50b1d23d Mon Sep 17 00:00:00 2001 From: DragonWenLong <552045633@qq.com> Date: Tue, 23 Sep 2025 14:21:05 +0800 Subject: [PATCH 2/4] =?UTF-8?q?feat(equipment):=20=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E8=AE=BE=E5=A4=87=E7=BB=B4=E4=BF=AE=E8=AE=B0=E5=BD=95=E5=9B=BE?= =?UTF-8?q?=E7=89=87=E7=AE=A1=E7=90=86=E5=92=8C=E6=8A=A5=E8=AD=A6=E5=A4=84?= =?UTF-8?q?=E7=90=86=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -重构文件哈希工具类路径并优化上传逻辑,支持秒传 - 新增维修记录图片ID列表字段及删除旧图片逻辑- 设备维修记录查询增加设备名称模糊搜索条件 -日期查询条件添加格式化注解支持 yyyy-MM-dd- MQTT规则中新增SOS与静止报警处理机制 - 实现报警生命周期管理(开始/结束)及Redis缓存控制 - 添加报警信息入库和位置解析功能 - 优化设备状态数据解析与经纬度异步存储逻辑 --- .../global/mqtt/rule/xinghan/XinghanDeviceDataRule.java | 3 +++ 1 file changed, 3 insertions(+) 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 17dce0e..b2e89a4 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 @@ -186,6 +186,9 @@ public class XinghanDeviceDataRule implements MqttMessageRule { */ private DeviceAlarmBo createAlarmBo(String deviceImei, AlarmType type) { Device device = deviceService.selectDeviceByImei(deviceImei); + if (device == null) { + return null; + } DeviceAlarmBo bo = new DeviceAlarmBo(); bo.setDeviceId(device.getId()); bo.setDeviceImei(deviceImei); From 91f024118120b2e640afdfac29ba9436ec64eed8 Mon Sep 17 00:00:00 2001 From: DragonWenLong <552045633@qq.com> Date: Tue, 23 Sep 2025 15:55:01 +0800 Subject: [PATCH 3/4] =?UTF-8?q?feat(mqtt):=20=E6=B7=BB=E5=8A=A0=E7=94=B5?= =?UTF-8?q?=E6=B1=A0=E7=94=B5=E9=87=8F=E7=99=BE=E5=88=86=E6=AF=94=E5=AD=97?= =?UTF-8?q?=E6=AE=B5=E5=B9=B6=E9=80=82=E9=85=8D=E6=8E=A7=E5=88=B6=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 MqttXinghanJson 中新增 batteryPercentage 字段- 在 XinghanDeviceDataRule 中设置 batteryPercentage 值 - 适配控制百分比列表显示电池电量信息 --- .../java/com/fuyuanshen/global/mqtt/base/MqttXinghanJson.java | 4 ++++ .../global/mqtt/rule/xinghan/XinghanDeviceDataRule.java | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/base/MqttXinghanJson.java b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/base/MqttXinghanJson.java index 4d9513f..a487d0d 100644 --- a/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/base/MqttXinghanJson.java +++ b/fys-admin/src/main/java/com/fuyuanshen/global/mqtt/base/MqttXinghanJson.java @@ -66,5 +66,9 @@ public class MqttXinghanJson { */ @JsonProperty("sta_system") public String stasystem; + /** + * 电量百分比(适配控制列表显示) + */ + public String batteryPercentage; } 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 b2e89a4..6fc2f5f 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 @@ -76,7 +76,7 @@ public class XinghanDeviceDataRule implements MqttMessageRule { // Latitude, longitude //主灯档位,激光灯档位,电量百分比,充电状态,电池剩余续航时间 MqttXinghanJson deviceStatus = objectMapper.convertValue(context.getPayloadDict(), MqttXinghanJson.class); - + deviceStatus.setBatteryPercentage(deviceStatus.getStaPowerPercent().toString()); // 发送设备状态和位置信息到Redis asyncSendDeviceDataToRedisWithFuture(context.getDeviceImei(),deviceStatus); RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.OK.getCode(), Duration.ofSeconds(20)); 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 4/4] =?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 33c19f6..5e9a0ab 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 15d9df5..3bb7028 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 6fc2f5f..cbcf2ea 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 4cc51d8..2f1c324 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 0000000..0d3b598 --- /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 0000000..bde6fdc --- /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 3225459..617458c 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