Compare commits

49 Commits

Author SHA1 Message Date
dyf
740a638444 Merge pull request 'fix(device):修复SOS报警类型判断逻辑' (#17) from liwenlong/fys-Multi-tenant:jingquan into jingquan
Reviewed-on: #17
2025-10-11 13:35:08 +08:00
a3d44a157a fix(device):修复SOS报警类型判断逻辑
- 简化报警类型判断,固定为SOS类型
- 移除不必要的shake类型判断逻辑
2025-10-11 13:23:51 +08:00
cc23f082a6 Merge remote-tracking branch 'upstream/6170' into 6170 2025-10-11 11:06:15 +08:00
1bc7dc676e 控制中心查询 2025-10-11 11:06:07 +08:00
5cdacad468 获取设备使用频次统计 2025-10-11 10:32:16 +08:00
62af38cbf0 Merge branch 'dyf-device' into 6170 2025-10-11 09:05:38 +08:00
04a21567aa 设备所属分组 2025-10-11 09:04:48 +08:00
5b4fa38dbd 设备告警 2025-10-10 15:46:25 +08:00
2c3effa683 历史轨迹1 2025-10-10 09:26:52 +08:00
7b8c626cb6 Merge branch 'jingquan' into dyf-device 2025-10-09 14:45:15 +08:00
dyf
9642908035 Merge pull request 'feat(app): 实现系统版本管理功能-Dto 新增 SystemVersion 数据传输对象- 修改 AppAuthController 的 getAppVersion 接口,从 Redis 获取版本信息' (#16) from liwenlong/fys-Multi-tenant:jingquan into jingquan
Reviewed-on: #16
2025-10-09 14:44:18 +08:00
85b2e2b976 设备告警1 2025-10-09 13:31:37 +08:00
88ac0236bd feat(app): 实现系统版本管理功能-Dto 新增 SystemVersion 数据传输对象- 修改 AppAuthController 的 getAppVersion 接口,从 Redis 获取版本信息
- 新增 DeviceXinghanController 的 VersionSettings 接口,用于更新版本信息到 Redis- 引入 FastJSON 和 Redis 工具类支持版本信息的序列化与缓存
- 添加权限注解控制版本更新接口访问
- 定义全局 Redis 键常量用于版本信息存储
2025-10-09 11:09:07 +08:00
609a3e4058 设备在线状态2 2025-10-09 09:44:48 +08:00
68cf78f3f2 设备在线状态5 2025-10-08 17:27:43 +08:00
c9857a679c 设备在线状态4 2025-10-08 15:08:31 +08:00
6439a96e3d 设备在线状态3 2025-10-08 15:07:54 +08:00
9f0155a6e3 Merge branch 'dyf-device' into 6170 2025-10-08 14:12:09 +08:00
6c99cef65d 解除告警 2025-10-08 14:11:11 +08:00
840c4fd68f 设备在线状态2 2025-10-08 13:53:03 +08:00
dc0fe96652 设备在线状态 2025-10-08 13:38:07 +08:00
cf883bfa00 Merge remote-tracking branch 'upstream/6170' into 6170 2025-10-08 11:48:16 +08:00
6a6397da23 控制查询条件加模糊查询 2025-10-08 11:48:05 +08:00
d701a834b3 设备IMEI 模糊查询 2025-10-08 09:37:18 +08:00
e1ac87e5f2 在线状态 2025-10-07 16:11:06 +08:00
5c3a35c83e 在线状态 2025-10-07 16:09:59 +08:00
2c0eff2b2c Merge remote-tracking branch 'origin/6170' into 6170 2025-10-07 15:50:26 +08:00
37e07d2706 控制中心优化2 2025-10-07 15:50:16 +08:00
dyf
5c0c4e40e4 Merge pull request 'fix(device):优化Redis缓存更新逻辑' (#15) from liwenlong/fys-Multi-tenant:jingquan into jingquan
Reviewed-on: #15
2025-10-07 15:46:56 +08:00
eceedae676 fix(device):优化Redis缓存更新逻辑
- 删除原有的直接设置缓存列表逻辑
- 新增先删除后写入的缓存更新策略- 统一使用变量存储Redis键值避免重复拼接- 保持原有缓存过期时间设置为5分钟
2025-10-07 15:29:19 +08:00
dfa5b446f9 控制中心优化 2025-10-07 09:50:49 +08:00
9afc0222d5 app批量上传logo 2025-09-30 17:53:28 +08:00
2cfcea65f1 Merge branch '6170' into dyf-device 2025-09-30 16:10:13 +08:00
dyf
9b72a6ebd2 Merge pull request 'feat(device): 新增设备类型查询与设备添加功能' (#14) from liwenlong/fys-Multi-tenant:jingquan into jingquan
Reviewed-on: #14
2025-09-30 16:06:10 +08:00
13db094336 feat(device): 新增设备类型查询与设备添加功能
- 在AppDeviceXinghanController中新增查询所有设备类型接口
- 实现新增设备功能,包含设备MAC和IMEI唯一性校验- 添加设备类型权限验证逻辑
- 完善设备绑定状态和主题设置
- 在DeviceXinghanBizService中实现设备分配记录保存
- 优化文件批量插入逻辑,支持是否批量删除历史数据- 增加文件删除功能,支持根据ID列表删除业务文件
- 缩短MQTT消息去重时间窗口至3秒
- 在设备维修记录查询条件中增加维修人员模糊查询- 调整设备消息发送数据顺序,单位名称移至首位
2025-09-30 15:59:24 +08:00
a1d6604520 app产品参数修改 2025-09-30 15:42:59 +08:00
b0e51e73c7 岗位信息1 2025-09-30 13:21:43 +08:00
dba537b83f 发送信息和告警故障5 2025-09-30 09:57:45 +08:00
d9b69ddc4e 不同的一级目录下的分组应当可以重名 2025-09-29 17:53:26 +08:00
6cec86bafe Merge remote-tracking branch 'upstream/6170' into jingquan 2025-09-29 13:57:29 +08:00
608aa8449a Merge branch 'main' into dyf-device 2025-09-29 09:26:49 +08:00
994e63bcde Merge branch '6170' of http://47.107.152.87:3000/dyf/fys-Multi-tenant into 6170 2025-09-29 09:24:52 +08:00
d6520d777a 发送信息和告警故障4 2025-09-29 09:17:20 +08:00
1898fe5db9 Merge branch '6170' into dyf-device 2025-09-28 18:19:21 +08:00
7d56e2e80e 发送信息和告警故障3 2025-09-28 17:18:47 +08:00
233e0e32b0 发送信息和告警故障2 2025-09-28 16:19:28 +08:00
5230a95865 自动报警 2025-09-28 15:28:19 +08:00
461fd9364c Merge remote-tracking branch 'upstream/6170' into 6170 2025-09-27 15:40:39 +08:00
ad81647939 发送信息和告警故障 2025-09-27 15:40:31 +08:00
42 changed files with 632 additions and 169 deletions

View File

@ -3,6 +3,7 @@ package com.fuyuanshen;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
* 启动程序
@ -10,6 +11,7 @@ import org.springframework.boot.context.metrics.buffering.BufferingApplicationSt
* @author Lion Li
*/
@SpringBootApplication
@EnableScheduling
public class DromaraApplication {
public static void main(String[] args) {

View File

@ -5,6 +5,7 @@ import cn.dev33.satoken.exception.NotLoginException;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSON;
import com.fuyuanshen.app.model.AppRegisterBody;
import com.fuyuanshen.app.model.AppSmsLoginBody;
import com.fuyuanshen.app.service.AppLoginService;
@ -32,6 +33,7 @@ import com.fuyuanshen.system.service.ISysClientService;
import com.fuyuanshen.system.service.ISysConfigService;
import com.fuyuanshen.system.service.ISysDictTypeService;
import com.fuyuanshen.system.service.ISysTenantService;
import com.fuyuanshen.web.domain.Dto.SystemVersionDto;
import com.fuyuanshen.web.domain.vo.LoginTenantVo;
import com.fuyuanshen.web.domain.vo.LoginVo;
import com.fuyuanshen.web.domain.vo.TenantListVo;
@ -54,6 +56,7 @@ import java.util.ArrayList;
import java.util.List;
import static com.fuyuanshen.common.core.constant.GlobalConstants.DEVICE_SHARE_CODES_KEY;
import static com.fuyuanshen.common.core.constant.GlobalConstants.GLOBAL_REDIS_KEY;
/**
* APP认证
@ -261,23 +264,14 @@ public class AppAuthController {
* @return
*/
@GetMapping("/version")
public R<List<SysDictDataVo>> getAppVersion() {
List<SysDictDataVo> list = dictTypeService.selectDictDataByType("app_version");
list.forEach(d -> {
// 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; // 跳过
public R<List<SystemVersionDto>> getAppVersion() {
String versionKey = GLOBAL_REDIS_KEY + "System:Version:Xinghan";
// 缓存告警消息到Redis
String versionJson = RedisUtils.getCacheObject(versionKey);
if (StringUtils.isBlank(versionJson)) { // hutool 工具,可用 StringUtils.isBlank 代替
return R.fail(versionJson);
}
// 2. 正常赋值
d.setDictValue(arr[0].trim()); // 版本号
d.setRemark(arr[1].trim()); // 下载地址
});
List<SystemVersionDto> list = JSON.parseArray(versionJson, SystemVersionDto.class);
return R.ok(list);
}

View File

@ -6,6 +6,7 @@ import com.fuyuanshen.app.domain.vo.APPDeviceTypeVo;
import com.fuyuanshen.common.core.domain.R;
import com.fuyuanshen.common.mybatis.core.page.PageQuery;
import com.fuyuanshen.common.mybatis.core.page.TableDataInfo;
import com.fuyuanshen.common.satoken.utils.AppLoginHelper;
import com.fuyuanshen.common.web.core.BaseController;
import com.fuyuanshen.equipment.domain.Device;
import com.fuyuanshen.equipment.domain.dto.AppDeviceBo;
@ -53,7 +54,8 @@ public class AppDeviceController extends BaseController {
*/
@DeleteMapping("/unBind")
public R<Void> unBind(Long id) {
return toAjax(appDeviceService.unBindDevice(id));
Long userId = AppLoginHelper.getUserId();
return toAjax(appDeviceService.unBindDevice(id,userId,0));
}
/**

View File

@ -5,6 +5,7 @@ import com.fuyuanshen.app.domain.dto.AppDeviceLogoUploadDto;
import com.fuyuanshen.app.domain.dto.DeviceInstructDto;
import com.fuyuanshen.app.domain.vo.AppDeviceDetailVo;
import com.fuyuanshen.common.core.domain.R;
import com.fuyuanshen.common.core.exception.ServiceException;
import com.fuyuanshen.common.core.validate.AddGroup;
import com.fuyuanshen.common.ratelimiter.annotation.FunctionAccessAnnotation;
import com.fuyuanshen.common.ratelimiter.annotation.FunctionAccessBatcAnnotation;
@ -17,6 +18,8 @@ import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
/**
* BJQ6170设备控制类
*/
@ -82,6 +85,8 @@ public class AppDeviceBJQController extends BaseController {
return R.ok();
}
/**
* 灯光模式
* 0关灯1强光模式2弱光模式, 3爆闪模式, 4泛光模式

View File

@ -9,15 +9,24 @@ import com.fuyuanshen.common.log.annotation.Log;
import com.fuyuanshen.common.ratelimiter.annotation.FunctionAccessAnnotation;
import com.fuyuanshen.common.ratelimiter.annotation.FunctionAccessBatcAnnotation;
import com.fuyuanshen.common.web.core.BaseController;
import com.fuyuanshen.customer.mapper.CustomerMapper;
import com.fuyuanshen.equipment.domain.DeviceType;
import com.fuyuanshen.equipment.domain.dto.AppDeviceSendMsgBo;
import com.fuyuanshen.equipment.domain.form.DeviceForm;
import com.fuyuanshen.equipment.mapper.DeviceMapper;
import com.fuyuanshen.equipment.service.DeviceService;
import com.fuyuanshen.equipment.service.DeviceTypeService;
import com.fuyuanshen.web.domain.Dto.DeviceXinghanInstructDto;
import com.fuyuanshen.web.service.device.DeviceBJQBizService;
import com.fuyuanshen.web.service.device.DeviceXinghanBizService;
import io.swagger.v3.oas.annotations.Operation;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
/**
* HBY670设备控制类
*/
@ -28,6 +37,7 @@ import org.springframework.web.multipart.MultipartFile;
public class AppDeviceXinghanController extends BaseController {
private final DeviceXinghanBizService appDeviceService;
private final DeviceService deviceService;
/**
* 人员信息登记
*/
@ -113,4 +123,23 @@ public class AppDeviceXinghanController extends BaseController {
return R.ok();
}
@GetMapping(value = "/typeAll")
@Operation(summary = "查询所有设备类型")
public R<List<DeviceType>> queryDeviceTypes() {
List<DeviceType> deviceTypes = appDeviceService.queryDeviceTypes();
return R.ok(deviceTypes);
}
// @Log("新增设备")
@Operation(summary = "新增设备")
@PostMapping(value = "/add")
public R<Void> addDevice(@RequestBody DeviceForm deviceForm) {
try {
appDeviceService.addDevice(deviceForm);
} catch (Exception e) {
return R.fail(e.getMessage());
}
return R.ok();
}
}

View File

@ -3,6 +3,8 @@ package com.fuyuanshen.app.domain.dto;
import lombok.Data;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
@Data
public class AppDeviceLogoUploadDto {
@ -14,5 +16,13 @@ public class AppDeviceLogoUploadDto {
*/
private MultipartFile file;
// /**
// * 文件
// */
// private List<MultipartFile> files;
private List<Long> deviceIds;
private Integer chunkSize;
}

View File

@ -73,9 +73,10 @@ public class AppDeviceShareService {
private static void buildDeviceStatus(AppDeviceShareVo item) {
// 设备在线状态
String onlineStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX + item.getDeviceImei() + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX);
if (StringUtils.isNotBlank(onlineStatus)) {
if("1".equals(onlineStatus)){
item.setOnlineStatus(1);
}else if("2".equals(onlineStatus)){
item.setOnlineStatus(2);
}else{
item.setOnlineStatus(0);
}

View File

@ -32,7 +32,7 @@ public class AppFileService {
private final IAppBusinessFileService appBusinessFileService;
public List<AppFileVo> list(AppBusinessFileBo bo) {
bo.setCreateBy(AppLoginHelper.getUserId());
// bo.setCreateBy(AppLoginHelper.getUserId());
return appBusinessFileService.queryAppFileList(bo);
}
@ -68,7 +68,7 @@ public class AppFileService {
public Boolean delete(Long[] ids) {
AppBusinessFileBo bo = new AppBusinessFileBo();
bo.setCreateBy(AppLoginHelper.getUserId());
// bo.setCreateBy(AppLoginHelper.getUserId());
bo.setIds(List.of(ids));
List<AppBusinessFileVo> appBusinessFileVos = appBusinessFileService.queryList(bo);
List<Long> fileIds = appBusinessFileVos.stream().map(AppBusinessFileVo::getFileId).toList();

View File

@ -65,13 +65,22 @@ public class RedisKeyExpirationListener implements MessageListener {
if (lockInfo != null) {
try {
String deviceOnlineStatusRedisKey = GlobalConstants.GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX+ message + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX ;
String deviceOnlineStatusRedis = RedisUtils.getCacheObject(deviceOnlineStatusRedisKey);
if(StringUtils.isBlank(deviceOnlineStatusRedis)){
// String deviceOnlineStatusRedis = RedisUtils.getCacheObject(deviceOnlineStatusRedisKey);
// if(StringUtils.isBlank(deviceOnlineStatusRedis)){
// UpdateWrapper<Device> deviceUpdateWrapper = new UpdateWrapper<>();
// deviceUpdateWrapper.eq("device_imei", element);
// deviceUpdateWrapper.set("online_status", 0);
// deviceMapper.update(deviceUpdateWrapper);
// }else{
// RedisUtils.deleteObject(deviceOnlineStatusRedisKey);
// }
UpdateWrapper<Device> deviceUpdateWrapper = new UpdateWrapper<>();
deviceUpdateWrapper.eq("device_imei", element);
deviceUpdateWrapper.set("online_status", 0);
deviceMapper.update(deviceUpdateWrapper);
}else{
String deviceOnlineStatusRedis = RedisUtils.getCacheObject(deviceOnlineStatusRedisKey);
if(StringUtils.isNotBlank(deviceOnlineStatusRedis)){
RedisUtils.deleteObject(deviceOnlineStatusRedisKey);
}
} finally {

View File

@ -48,17 +48,18 @@ public class ReceiverMessageHandler implements MessageHandler {
}
String[] subStr = receivedTopic.split("/");
String deviceImei = subStr[1];
String state = payloadDict.getStr("state");
Object[] convertArr = ImageToCArrayConverter.convertByteStringToMixedObjectArray(state);
if(StringUtils.isNotBlank(deviceImei)){
String queueKey = MqttMessageQueueConstants.MQTT_MESSAGE_QUEUE_KEY;
String dedupKey = MqttMessageQueueConstants.MQTT_MESSAGE_DEDUP_KEY;
RedisUtils.offerDeduplicated(queueKey,dedupKey,deviceImei, Duration.ofHours(24));
//在线状态
String deviceOnlineStatusRedisKey = GlobalConstants.GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX+ deviceImei + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX ;
RedisUtils.setCacheObject(deviceOnlineStatusRedisKey, "1", Duration.ofSeconds(120));
RedisUtils.setCacheObject(deviceOnlineStatusRedisKey, "1", Duration.ofSeconds(360));
}
String state = payloadDict.getStr("state");
Object[] convertArr = ImageToCArrayConverter.convertByteStringToMixedObjectArray(state);
if (convertArr.length > 0) {
Byte val1 = (Byte) convertArr[0];

View File

@ -57,12 +57,17 @@ public class BjqAlarmRule implements MqttMessageRule {
if (StringUtils.isNotBlank(convertValue)) {
// 将设备状态信息存储到Redis中
String deviceRedisKey = GlobalConstants.GLOBAL_REDIS_KEY + DeviceRedisKeyConstants.DEVICE_KEY_PREFIX + context.getDeviceImei() + DEVICE_ALARM_KEY_PREFIX;
String sendMessageIng = GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + context.getDeviceImei() + ":messageSending";
if ("1".equals(convertValue)) {
RedisUtils.setCacheObject(sendMessageIng, "1", Duration.ofDays(1));
// 存储到Redis
RedisUtils.setCacheObject(deviceRedisKey, convertValue);
RedisUtils.setCacheObject(deviceRedisKey, "1");
}else if ("0".equals(convertValue)){
RedisUtils.deleteObject(sendMessageIng);
RedisUtils.deleteObject(deviceRedisKey);
}
}
RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.OK.getCode(), Duration.ofSeconds(20));
// 保存告警信息
String deviceImei = context.getDeviceImei();
// 设备告警状态 0:解除告警 1:报警产生

View File

@ -56,7 +56,11 @@ public class BjqLocationDataRule implements MqttMessageRule {
// Latitude, longitude
String latitude = convertArr[1].toString();
String longitude = convertArr[2].toString();
// 判断 latitude 和 longitude 是否都为 0
if ("0".equals(latitude) && "0".equals(longitude)) {
log.info("位置信息为0不存储到Redis: device={}, lat={}, lon={}", context.getDeviceImei(), latitude, longitude);
return;
}
// 异步发送经纬度到Redis
asyncSendLocationToRedisWithFuture(context.getDeviceImei(), latitude, longitude);
// 异步保存数据
@ -122,6 +126,7 @@ public class BjqLocationDataRule implements MqttMessageRule {
if (StringUtils.isBlank(latitude) || StringUtils.isBlank(longitude)) {
return;
}
// String[] latArr = latitude.split("\\.");
// String[] lonArr = longitude.split("\\.");
// // 将位置信息存储到Redis中

View File

@ -1,8 +1,16 @@
package com.fuyuanshen.global.mqtt.rule.bjq;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
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.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.service.DeviceService;
import com.fuyuanshen.equipment.service.IDeviceAlarmService;
import com.fuyuanshen.global.mqtt.base.MqttMessageRule;
import com.fuyuanshen.global.mqtt.base.MqttRuleContext;
import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants;
@ -13,9 +21,11 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.util.Date;
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.*;
/**
@ -26,6 +36,10 @@ import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.*;
@Slf4j
public class BjqModeRule implements MqttMessageRule {
private final DeviceService deviceService;
private final IDeviceAlarmService deviceAlarmService;
@Override
public String getCommandType() {
return LightingCommandTypeConstants.LIGHT_MODE;
@ -40,9 +54,31 @@ public class BjqModeRule implements MqttMessageRule {
String mainLightMode = convertArr[1].toString();
String batteryRemainingTime = convertArr[2].toString();
if (StringUtils.isNotBlank(mainLightMode)) {
log.info("设备离线mainLightMode{}", mainLightMode);
if ("0".equals(mainLightMode)) {
// 设备离线
String deviceOnlineStatusRedisKey = GlobalConstants.GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX + context.getDeviceImei() + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX;
RedisUtils.setCacheObject(deviceOnlineStatusRedisKey, "0", Duration.ofSeconds(60*15));
RedisUtils.setCacheObject(deviceOnlineStatusRedisKey, "0");
String sendMessageIng = GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX + context.getDeviceImei() + ":messageSending";
String messageSendingValue = RedisUtils.getCacheObject(sendMessageIng);
if ("1".equals(messageSendingValue)) {
// 设置为故障状态
RedisUtils.setCacheObject(deviceOnlineStatusRedisKey, "2");
UpdateWrapper<Device> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("device_imei", context.getDeviceImei());
updateWrapper.set("online_status", 2);
deviceService.update(updateWrapper);
RedisUtils.deleteObject(sendMessageIng);
// 解除告警
String deviceRedisKey = GlobalConstants.GLOBAL_REDIS_KEY + DeviceRedisKeyConstants.DEVICE_KEY_PREFIX + context.getDeviceImei() + DEVICE_ALARM_KEY_PREFIX;
if (RedisUtils.getCacheObject(deviceRedisKey) != null) {
RedisUtils.deleteObject(deviceRedisKey);
}
cancelAlarm(context.getDeviceImei());
}
}
// 发送设备状态和位置信息到Redis
syncSendDeviceDataToRedisWithFuture(context.getDeviceImei(), mainLightMode);
@ -80,5 +116,29 @@ public class BjqModeRule implements MqttMessageRule {
}
/**
* 解除告警
*
* @param deviceImei 设备IMEI
*/
public void cancelAlarm(String deviceImei) {
DeviceAlarmVo deviceAlarmVo = deviceAlarmService.queryLatestByDeviceImei(deviceImei);
DeviceAlarmBo deviceAlarmBo = new DeviceAlarmBo();
if (deviceAlarmVo != null) {
if (deviceAlarmVo.getFinishTime() == null) {
BeanUtil.copyProperties(deviceAlarmVo, deviceAlarmBo);
deviceAlarmBo.setFinishTime(new Date());
String durationBetween = DurationUtils.getDurationBetween(deviceAlarmVo.getStartTime(), deviceAlarmBo.getFinishTime());
deviceAlarmBo.setDurationTime(durationBetween);
// 0已处理1未处理
deviceAlarmBo.setTreatmentState(0);
// 告警状态0 解除告警, 1 告警中
deviceAlarmBo.setAlarmState(0);
deviceAlarmService.updateByBo(deviceAlarmBo);
}
}
}
}

View File

@ -43,12 +43,23 @@ public class BjqSendMessageRule implements MqttMessageRule {
public void execute(MqttRuleContext context) {
String functionAccess = FUNCTION_ACCESS_KEY + context.getDeviceImei();
try {
Byte val2 = (Byte) context.getConvertArr()[1];
// Byte val2 = (Byte) context.getConvertArr()[1];
String val2Str = context.getConvertArr()[1].toString();
int val2 = Integer.parseInt(val2Str);
System.out.println("收到设备信息命令:"+val2);
if (val2 == 100) {
RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.OK.getCode(), Duration.ofSeconds(20));
return;
}
if(val2==200){
String sendMessageIng = GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + context.getDeviceImei() + ":messageSending";
RedisUtils.deleteObject(sendMessageIng);
return;
}
String data = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + context.getDeviceImei() + ":app_send_message_data");
if (StringUtils.isEmpty(data)) {
return;
@ -61,7 +72,7 @@ public class BjqSendMessageRule implements MqttMessageRule {
ArrayList<Integer> intData = new ArrayList<>();
intData.add(6);
intData.add((int) val2);
intData.add(val2);
ImageToCArrayConverter.buildArr(convertHexToDecimal(specificChunk), intData);
intData.add(0);
intData.add(0);

View File

@ -64,7 +64,7 @@ public class XinghanBootLogoRule implements MqttMessageRule {
// --- 去重 START ---
String dedupKey = "xd:MSG:LOGO:" + ctx.getDeviceImei() + ":" + respText;
boolean first = RedisUtils.setObjectIfAbsent(dedupKey, "1", Duration.ofSeconds(10));
boolean first = RedisUtils.setObjectIfAbsent(dedupKey, "1", Duration.ofSeconds(3));
if (!first) {
log.warn("重复消息丢弃 {}", dedupKey);
return;

View File

@ -57,7 +57,7 @@ public class XinghanSendAlarmMessageRule implements MqttMessageRule {
log.info("设备上报紧急通知握手: {} ", respText);
// --- 去重 START ---
String dedupKey = "xd:ALARM:dedup:" + ctx.getDeviceImei() + ":" + respText;
boolean first = RedisUtils.setObjectIfAbsent(dedupKey, "1", Duration.ofSeconds(10));
boolean first = RedisUtils.setObjectIfAbsent(dedupKey, "1", Duration.ofSeconds(3));
if (!first) {
log.warn("重复消息丢弃 {}", dedupKey);
return;

View File

@ -17,10 +17,7 @@ import org.springframework.stereotype.Component;
import java.nio.charset.Charset;
import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_KEY;
import static com.fuyuanshen.common.core.constant.GlobalConstants.GLOBAL_REDIS_KEY;
@ -42,6 +39,12 @@ public class XinghanSendMsgRule implements MqttMessageRule {
@Override
public String getCommandType() {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
// 三种等价的写法
Integer[] array1 = list.toArray(new Integer[0]);
Integer[] array2 = list.toArray(size -> new Integer[size]);
return XingHanCommandTypeConstants.XingHan_ESEND_MSG;
}
@ -57,7 +60,7 @@ public class XinghanSendMsgRule implements MqttMessageRule {
// --- 去重 START ---
String dedupKey = "xd:MSG:dedup:" + ctx.getDeviceImei() + ":" + respText;
boolean first = RedisUtils.setObjectIfAbsent(dedupKey, "1", Duration.ofSeconds(10));
boolean first = RedisUtils.setObjectIfAbsent(dedupKey, "1", Duration.ofSeconds(3));
if (!first) {
log.warn("重复消息丢弃 {}", dedupKey);
return;

View File

@ -106,16 +106,22 @@ public class MqttMessageConsumer {
// .set("online_status", 1);
// deviceMapper.update(updateWrapper);
// }
QueryWrapper<Device> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("device_imei", message);
queryWrapper.eq("online_status", 1);
Long count = deviceMapper.selectCount(queryWrapper);
if(count == 0){
// QueryWrapper<Device> queryWrapper = new QueryWrapper<>();
// queryWrapper.eq("device_imei", message);
// queryWrapper.eq("online_status", 1);
// Long count = deviceMapper.selectCount(queryWrapper);
// if(count == 0){
// UpdateWrapper<Device> updateWrapper = new UpdateWrapper<>();
// updateWrapper.eq("device_imei", message)
// .eq("online_status", 0)
// .set("online_status", 1);
// deviceMapper.update(updateWrapper);
// }
UpdateWrapper<Device> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("device_imei", message)
.in("online_status", 0,2)
.set("online_status", 1);
deviceMapper.update(updateWrapper);
}
int update = deviceMapper.update(updateWrapper);
// 模拟业务处理耗时
// Thread.sleep(200);

View File

@ -0,0 +1,43 @@
package com.fuyuanshen.global.queue;// ScheduledTasks.java
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.fuyuanshen.common.core.utils.StringUtils;
import com.fuyuanshen.common.redis.utils.RedisUtils;
import com.fuyuanshen.equipment.domain.Device;
import com.fuyuanshen.equipment.mapper.DeviceMapper;
import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.List;
import static com.fuyuanshen.common.core.constant.GlobalConstants.GLOBAL_REDIS_KEY;
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX;
@Component
public class OnlineStatusTask {
@Autowired
private DeviceMapper deviceMapper;
// 使用cron表达式每分钟的第0秒执行
@Scheduled(cron = "0 */3 * * * ?")
public void cronTask() {
QueryWrapper<Device> queryWrapper = new QueryWrapper<>();
List<Device> devices = deviceMapper.selectList(queryWrapper);
devices.forEach(item -> {
String onlineStatusKey = GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX + item.getDeviceImei() + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX;
String status = RedisUtils.getCacheObject(onlineStatusKey);
String onlineStatus = item.getOnlineStatus()==null?"0" : item.getOnlineStatus().toString();
if("1".equals(onlineStatus) || "2".equals(onlineStatus)){
if(StringUtils.isBlank(status) || "0".equals(status)){
UpdateWrapper<Device> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("id", item.getId());
updateWrapper.set("online_status", 0);
deviceMapper.update(updateWrapper);
}
}
});
}
}

View File

@ -82,6 +82,22 @@ public class DeviceBJQController extends BaseController {
return R.ok();
}
/**
* 批量上传设备logo图片
*/
@PostMapping("/batchUploadLogo")
@FunctionAccessAnnotation("batchUploadLogo")
public R<Void> batchUploadLogo(@Validated @ModelAttribute AppDeviceLogoUploadDto bo) {
MultipartFile file = bo.getFile();
if(file.getSize()>1024*1024*2){
return R.warn("图片不能大于2M");
}
appDeviceService.batchUploadLogo(bo);
return R.ok();
}
/**
* 灯光模式
* 0关灯1强光模式2弱光模式, 3爆闪模式, 4泛光模式

View File

@ -61,7 +61,7 @@ public class DeviceDebugController extends BaseController {
@Log(title = "批量上传文件")
@PostMapping("/addFile")
public R<Void> uploadFile(@Validated @ModelAttribute AppFileDto bo) throws IOException {
return toAjax(deviceDebugService.addFileHash(bo));
return toAjax(deviceDebugService.addFileHash(bo,true));
}
/**
@ -113,7 +113,7 @@ public class DeviceDebugController extends BaseController {
appFileDto.setDeviceIds(new Long[]{ bo.getDeviceId() });
appFileDto.setFileType(1L);
appFileDto.setFiles(bo.getExplanationFiles());
deviceDebugService.addFileHash(appFileDto);
deviceDebugService.addFileHash(appFileDto,false);
}
// 修改上传设备参数
if (bo.getParameterFiles() != null) {
@ -121,8 +121,9 @@ public class DeviceDebugController extends BaseController {
appFileDto.setDeviceIds(new Long[]{ bo.getDeviceId() });
appFileDto.setFileType(2L);
appFileDto.setFiles(bo.getParameterFiles());
deviceDebugService.addFileHash(appFileDto);
deviceDebugService.addFileHash(appFileDto,false);
}
deviceDebugService.delFile(bo.getFileIds());
// 修改操作视频
if (bo.getVideoUrl().isEmpty()) {
AppOperationVideoBo appOperationVideoBo = new AppOperationVideoBo();

View File

@ -1,16 +1,21 @@
package com.fuyuanshen.web.controller.device;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.alibaba.fastjson.JSON;
import com.fuyuanshen.app.domain.bo.AppPersonnelInfoBo;
import com.fuyuanshen.app.domain.dto.AppDeviceLogoUploadDto;
import com.fuyuanshen.app.domain.dto.DeviceInstructDto;
import com.fuyuanshen.app.domain.vo.AppDeviceDetailVo;
import com.fuyuanshen.common.core.domain.R;
import com.fuyuanshen.common.core.utils.StringUtils;
import com.fuyuanshen.common.core.validate.AddGroup;
import com.fuyuanshen.common.ratelimiter.annotation.FunctionAccessAnnotation;
import com.fuyuanshen.common.ratelimiter.annotation.FunctionAccessBatcAnnotation;
import com.fuyuanshen.common.redis.utils.RedisUtils;
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.Dto.SystemVersionDto;
import com.fuyuanshen.web.domain.vo.DeviceXinghanDetailVo;
import com.fuyuanshen.web.service.device.DeviceXinghanBizService;
import jakarta.validation.constraints.NotNull;
@ -19,6 +24,13 @@ import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.time.Duration;
import java.util.List;
import static com.fuyuanshen.common.core.constant.GlobalConstants.GLOBAL_REDIS_KEY;
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_ALARM_MESSAGE_KEY_PREFIX;
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX;
/**
* 设备控制类 HBY670
*/
@ -130,4 +142,22 @@ public class DeviceXinghanController extends BaseController {
deviceXinghanBizService.upShakeBitSettings(params);
return R.ok();
}
/**
* 版本更新
*/
@SaCheckPermission("system:appVersion:up")
@PostMapping("/UpVersion")
public R<Void> VersionSettings(@RequestBody List<SystemVersionDto> params) {
// params 已经是 List<SystemVersionDto>
// 2. 转 JSON 并写 Rediskey 自己定,带个过期时间
String versionKey = GLOBAL_REDIS_KEY + "System:Version:Xinghan";
String json = JSON.toJSONString(params); // fastjson
if (StringUtils.isBlank(json)) { // hutool 工具,可用 StringUtils.isBlank 代替
return R.fail("信息无效");
}
// 缓存告警消息到Redis
RedisUtils.setCacheObject(versionKey, json, Duration.ofDays(30));
return R.ok();
}
}

View File

@ -31,4 +31,6 @@ public class DeviceDebugEditDto {
* 视频链接
*/
private String videoUrl;
private List<Long> fileIds;
}

View File

@ -0,0 +1,10 @@
package com.fuyuanshen.web.domain.Dto;
import lombok.Data;
@Data
public class SystemVersionDto {
private String dictValue;
private String dictLabel;
private String remark;
}

View File

@ -15,6 +15,7 @@ import com.fuyuanshen.app.domain.vo.AppPersonnelInfoVo;
import com.fuyuanshen.app.mapper.AppPersonnelInfoMapper;
import com.fuyuanshen.app.mapper.AppPersonnelInfoRecordsMapper;
import com.fuyuanshen.common.core.constant.GlobalConstants;
import com.fuyuanshen.common.core.domain.R;
import com.fuyuanshen.common.core.exception.ServiceException;
import com.fuyuanshen.common.core.utils.*;
import com.fuyuanshen.common.redis.utils.RedisUtils;
@ -108,7 +109,8 @@ public class DeviceBJQBizService {
log.info("发送信息设备发送信息失败:{}" ,deviceId);
throw new ServiceException("发送指令失败");
}
//发送消息
messageSending(device.getDeviceImei());
}
@ -171,8 +173,11 @@ public class DeviceBJQBizService {
}
//设备在线状态
String onlineStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + device.getDeviceImei()+ DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX);
if(StringUtils.isNotBlank(onlineStatus)){
//设备在线状态
if("1".equals(onlineStatus)){
vo.setOnlineStatus(1);
}else if("2".equals(onlineStatus)){
vo.setOnlineStatus(2);
}else{
vo.setOnlineStatus(0);
}
@ -250,8 +255,8 @@ public class DeviceBJQBizService {
ArrayList<Integer> intData = new ArrayList<>();
intData.add(2);
buildArr(convertHexToDecimal(unitName), intData);
buildArr(convertHexToDecimal(position), intData);
buildArr(convertHexToDecimal(name), intData);
buildArr(convertHexToDecimal(position), intData);
buildArr(convertHexToDecimal(id), intData);
intData.add(0);
intData.add(0);
@ -333,7 +338,10 @@ public class DeviceBJQBizService {
throw new ServiceException("设备不存在");
}
if(getDeviceStatus(device.getDeviceImei())){
throw new ServiceException(device.getDeviceName()+",设备已断开连接");
// throw new ServiceException(device.getDeviceName()+",设备已断开连接");
log.info(device.getDeviceName()+",设备已断开连接");
recordDeviceLog(device.getId(), device.getDeviceName(), "上传开机画面", device.getDeviceName()+",设备已断开连接", AppLoginHelper.getUserId());
return;
}
MultipartFile file = bo.getFile();
@ -518,16 +526,20 @@ public class DeviceBJQBizService {
UpdateWrapper<Device> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("id", deviceId)
.eq("binding_user_id", AppLoginHelper.getUserId())
.set("send_msg", bo.getSendMsg());
deviceMapper.update(updateWrapper);
if("0".equals(bo.getInstructValue())){
recordDeviceLog(device.getId(), device.getDeviceName(), "解除告警信息", "关闭设备", AppLoginHelper.getUserId());
}else{
recordDeviceLog(device.getId(), device.getDeviceName(), "发送告警信息", bo.getSendMsg(), AppLoginHelper.getUserId());
}
} catch (Exception e) {
log.info("设备发送告警信息信息失败:{}" ,deviceId);
throw new ServiceException("设备发送告警信息信息失败");
}
messageSending(device.getDeviceImei());
}
} catch (Exception e){
e.printStackTrace();
throw new ServiceException("发送告警信息指令失败");
@ -535,8 +547,28 @@ public class DeviceBJQBizService {
return 1;
}
private void messageSending(String deviceImei){
String sendMessageIng = GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + deviceImei + ":messageSending";
RedisUtils.setCacheObject(sendMessageIng, "1", Duration.ofDays(1));
}
private boolean getDeviceStatus(String deviceImei) {
String deviceOnlineStatusRedisKey = GlobalConstants.GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX+ deviceImei + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX ;
return RedisUtils.getCacheObject(deviceOnlineStatusRedisKey) == null;
}
public void batchUploadLogo(AppDeviceLogoUploadDto bo) {
List<Long> deviceIds = bo.getDeviceIds();
MultipartFile file = bo.getFile();
if (deviceIds == null || deviceIds.isEmpty()) {
throw new ServiceException("请选择设备");
}
for (Long deviceId : deviceIds) {
AppDeviceLogoUploadDto dto = new AppDeviceLogoUploadDto();
dto.setDeviceId(deviceId);
dto.setFile(file);
uploadDeviceLogo(dto);
}
}
}

View File

@ -90,9 +90,10 @@ public class DeviceBizService {
//设备在线状态
String onlineStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX+ item.getDeviceImei() + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX);
if(StringUtils.isNotBlank(onlineStatus)){
if("1".equals(onlineStatus)){
item.setOnlineStatus(1);
}else if("2".equals(onlineStatus)){
item.setOnlineStatus(2);
}else{
item.setOnlineStatus(0);
}
@ -130,13 +131,14 @@ public class DeviceBizService {
if(item.getCommunicationMode()!=null && Ints.asList(0, 2).contains(item.getCommunicationMode())){
//设备在线状态
String onlineStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX+ item.getDeviceImei() + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX);
if(StringUtils.isNotBlank(onlineStatus)){
item.setOnlineStatus(1);
}else{
item.setOnlineStatus(0);
}
// String onlineStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX+ item.getDeviceImei() + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX);
// if("1".equals(onlineStatus)){
// item.setOnlineStatus(1);
// }else if("2".equals(onlineStatus)){
// item.setOnlineStatus(2);
// }else{
// item.setOnlineStatus(0);
// }
String deviceStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX+ item.getDeviceImei() + DEVICE_STATUS_KEY_PREFIX);
// 获取电量
if(StringUtils.isNotBlank(deviceStatus)){
@ -263,35 +265,53 @@ public class DeviceBizService {
if (device == null) {
throw new RuntimeException("请先将设备入库!!!");
}
// type: 0 app,1 web
if (type == 0) {
QueryWrapper<AppDeviceBindRecord> bindRecordQueryWrapper = new QueryWrapper<>();
bindRecordQueryWrapper.eq("device_id", device.getId());
bindRecordQueryWrapper.eq("binding_user_id", userId);
Long count = appDeviceBindRecordMapper.selectCount(bindRecordQueryWrapper);
if (count == 0) {
throw new RuntimeException("请先绑定设备!!!");
}
if(count<2){
UpdateWrapper<Device> deviceUpdateWrapper = new UpdateWrapper<>();
deviceUpdateWrapper.eq("id", device.getId())
.set("binding_user_id", null)
.set("binding_status", BindingStatusEnum.UNBOUND.getCode())
.set("binding_time", null);
deviceMapper.update(null, deviceUpdateWrapper);
if (userId == null) {
userId = AppLoginHelper.getUserId();
}
}else{
QueryWrapper<AppDeviceBindRecord> bindRecordQueryWrapper = new QueryWrapper<>();
bindRecordQueryWrapper.eq("device_id", device.getId());
// 设备端解绑 0:设备端解绑 1:web端解绑
if (type == 1) {
bindRecordQueryWrapper.eq("binding_user_id", userId);
Long count = appDeviceBindRecordMapper.selectCount(bindRecordQueryWrapper);
if (count == 0) {
throw new RuntimeException("请先绑定设备!!!");
}
UpdateWrapper<Device> deviceUpdateWrapper = new UpdateWrapper<>();
deviceUpdateWrapper.eq("id", device.getId())
.set("binding_user_id", null)
.set("binding_status", BindingStatusEnum.UNBOUND.getCode())
.set("binding_time", null);
deviceMapper.update(null, deviceUpdateWrapper);
}
// AppDeviceBindRecord appDeviceBindRecord = appDeviceBindRecordMapper.selectOne(bindRecordQueryWrapper);
// if (appDeviceBindRecord != null) {
// return appDeviceBindRecordMapper.deleteById(appDeviceBindRecord.getId());
// }
QueryWrapper<AppDeviceBindRecord> brqWrapper = new QueryWrapper<>();
brqWrapper.eq("device_id", device.getId());
if(userId != null){
brqWrapper.eq("binding_user_id", userId);
}
List<AppDeviceBindRecord> appDeviceBindRecordList = appDeviceBindRecordMapper.selectList(bindRecordQueryWrapper);
List<AppDeviceBindRecord> appDeviceBindRecordList = appDeviceBindRecordMapper.selectList(brqWrapper);
if (CollectionUtil.isNotEmpty(appDeviceBindRecordList)) {
appDeviceBindRecordList.forEach(appDeviceBindRecord ->
appDeviceBindRecordMapper.deleteById(appDeviceBindRecord.getId()));
}
AppUserVo appUserVo = appUserMapper.selectVoById(userId);
if(appUserVo != null){
QueryWrapper<AppDeviceShare> appDeviceShareQueryWrapper = new QueryWrapper<>();
appDeviceShareQueryWrapper.eq("device_id", device.getId());
appDeviceShareQueryWrapper.eq("phonenumber", appUserVo.getPhonenumber());
@ -300,6 +320,8 @@ public class DeviceBizService {
appDeviceShareList.forEach(appDeviceShare ->
appDeviceShareMapper.deleteById(appDeviceShare.getId()));
}
}
return 1;
}
@ -373,6 +395,10 @@ public class DeviceBizService {
JSONObject jsonObject = JSONObject.parseObject(obj);
Long timestamp = jsonObject.getLong("timestamp");
LocalDate date = LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.systemDefault()).toLocalDate();
String address = jsonObject.getString("address");
if(StringUtils.isBlank( address) || "[]".equals(address)){
continue;
}
if (map.containsKey(date.toString())) {
map.get(date.toString()).add(jsonObject);
} else {

View File

@ -47,7 +47,7 @@ public class DeviceDebugService {
* @throws IOException
*/
@Transactional(rollbackFor = Exception.class)
public Boolean addFileHash(AppFileDto bo) throws IOException {
public Boolean addFileHash(AppFileDto bo,Boolean isBatch) throws IOException {
MultipartFile[] files = bo.getFiles();
if (files == null || files.length == 0) {
throw new ServiceException("请选择要上传的文件");
@ -100,7 +100,7 @@ public class DeviceDebugService {
if (CollUtil.isEmpty(bizList)) { // 空集合直接返回
throw new ServiceException("请选择要上传的文件");
}
return appBusinessFileService.insertBatch(bizList);
return appBusinessFileService.insertBatch(bizList,isBatch);
}
public Boolean addVideoList(AppOperationVideoBo bo){
@ -124,6 +124,13 @@ public class DeviceDebugService {
}
return appOperationVideoService.insertBatch(bizList);
}
// 删除文件
public void delFile(List<Long> ids) {
// 3. 删除旧图片
if(ids != null){
appBusinessFileService.deleteWithValidByIds(ids, true);
}
}
/**
* 设备详情

View File

@ -1,6 +1,8 @@
package com.fuyuanshen.web.service.device;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
@ -19,6 +21,10 @@ import com.fuyuanshen.app.domain.vo.AppPersonnelInfoVo;
import com.fuyuanshen.app.mapper.AppPersonnelInfoMapper;
import com.fuyuanshen.app.mapper.AppPersonnelInfoRecordsMapper;
import com.fuyuanshen.common.core.constant.GlobalConstants;
import com.fuyuanshen.common.core.domain.R;
import com.fuyuanshen.common.core.domain.model.AppLoginUser;
import com.fuyuanshen.common.core.domain.model.LoginUser;
import com.fuyuanshen.common.core.exception.BadRequestException;
import com.fuyuanshen.common.core.exception.ServiceException;
import com.fuyuanshen.common.core.utils.ImageToCArrayConverter;
import com.fuyuanshen.common.core.utils.MapstructUtils;
@ -27,20 +33,26 @@ import com.fuyuanshen.common.core.utils.StringUtils;
import com.fuyuanshen.common.json.utils.JsonUtils;
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.DeviceLog;
import com.fuyuanshen.equipment.domain.DeviceType;
import com.fuyuanshen.common.satoken.utils.LoginHelper;
import com.fuyuanshen.equipment.domain.*;
import com.fuyuanshen.equipment.domain.bo.DeviceAlarmBo;
import com.fuyuanshen.equipment.domain.dto.AppDeviceSendMsgBo;
import com.fuyuanshen.equipment.domain.form.DeviceForm;
import com.fuyuanshen.equipment.domain.query.DeviceTypeQueryCriteria;
import com.fuyuanshen.equipment.enums.DeviceActiveStatusEnum;
import com.fuyuanshen.equipment.enums.LightModeEnum;
import com.fuyuanshen.equipment.mapper.DeviceLogMapper;
import com.fuyuanshen.equipment.mapper.DeviceMapper;
import com.fuyuanshen.equipment.mapper.DeviceTypeGrantsMapper;
import com.fuyuanshen.equipment.mapper.DeviceTypeMapper;
import com.fuyuanshen.equipment.service.DeviceAssignmentsService;
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.system.domain.vo.SysOssVo;
import com.fuyuanshen.system.domain.vo.SysRoleVo;
import com.fuyuanshen.web.domain.Dto.DeviceDebugLogoUploadDto;
import com.fuyuanshen.web.domain.Dto.DeviceXinghanInstructDto;
import com.fuyuanshen.web.domain.vo.DeviceXinghanDetailVo;
@ -50,17 +62,19 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import static com.fuyuanshen.common.core.constant.GlobalConstants.GLOBAL_REDIS_KEY;
import static com.fuyuanshen.common.core.utils.Bitmap80x12Generator.buildArr;
import static com.fuyuanshen.common.core.utils.Bitmap80x12Generator.generateFixedBitmapData;
import static com.fuyuanshen.common.core.utils.ImageToCArrayConverter.convertHexToDecimal;
import static com.fuyuanshen.equipment.service.impl.DeviceServiceImpl.USER_ID_SEPARATOR;
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.*;
@Slf4j
@ -75,6 +89,8 @@ public class DeviceXinghanBizService {
private final DeviceLogMapper deviceLogMapper;
private final AppPersonnelInfoRecordsMapper appPersonnelInfoRecordsMapper;
private final IDeviceAlarmService deviceAlarmService;
private final DeviceTypeGrantsMapper deviceTypeGrantsMapper;
private final DeviceAssignmentsService deviceAssignmentsService;
@Autowired
private ObjectMapper objectMapper;
@ -324,12 +340,14 @@ public class DeviceXinghanBizService {
}
List<String> list = new ArrayList<>();
list.add(bo.getUnitName());
list.add(bo.getName());
list.add(bo.getPosition());
list.add(bo.getUnitName());
list.add(bo.getCode());
RedisUtils.setCacheList(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + deviceObj.getDeviceImei() + ":app_send_message_data", list);
RedisUtils.expire(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + deviceObj.getDeviceImei() + ":app_send_message_data", Duration.ofSeconds(5 * 60L));
String key = GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX + deviceObj.getDeviceImei() + ":app_send_message_data";
RedisUtils.deleteObject(key); // 先整体删掉
RedisUtils.setCacheList(key, list); // 再重新写入
RedisUtils.expire(key, Duration.ofSeconds(5*60));
Map<String, Object> payload = Map.of("ins_TexTrans",
Collections.singletonList(0));
@ -615,7 +633,7 @@ public class DeviceXinghanBizService {
if (!"ins_SOSGrade".equals(payloadKey) || value == 0) {
return;
}
AlarmTypeEnum type = value == 1 ? AlarmTypeEnum.SOS : AlarmTypeEnum.SHAKE;
AlarmTypeEnum type = AlarmTypeEnum.SOS;
String redisKey = buildAlarmRedisKey(deviceImei, type);
Long alarmId = RedisUtils.getCacheObject(redisKey);
// 已存在未结束报警 -> 什么都不做(同一条报警)
@ -677,4 +695,76 @@ public class DeviceXinghanBizService {
return RedisUtils.getCacheObject(deviceOnlineStatusRedisKey)==null;
}
public List<DeviceType> queryDeviceTypes() {
DeviceTypeQueryCriteria criteria = new DeviceTypeQueryCriteria();
return deviceTypeMapper.findAll(criteria);
}
// @Log("新增设备")
public void addDevice(DeviceForm deviceForm) {
if (deviceForm.getDeviceMac() != null && deviceForm.getBluetoothName() == null) {
throw new BadRequestException("请填写蓝牙名称!!!");
}
Device device1 = deviceMapper.selectOne(new QueryWrapper<Device>().eq("device_mac", deviceForm.getDeviceMac()));
if (device1 != null) {
throw new BadRequestException("设备MAC已存在");
}
Device device2 = deviceMapper.selectOne(new QueryWrapper<Device>().eq("device_imei", deviceForm.getDeviceImei()));
if (device2 != null) {
throw new BadRequestException("设备IMEI已存在");
}
DeviceTypeQueryCriteria queryCriteria = new DeviceTypeQueryCriteria();
queryCriteria.setDeviceTypeId(deviceForm.getDeviceType());
queryCriteria.setCustomerId(LoginHelper.getUserId());
DeviceTypeGrants typeGrants = deviceTypeGrantsMapper.selectById(queryCriteria.getDeviceTypeId());
if (typeGrants == null) {
throw new ServiceException("设备类型不存在!!!");
}
DeviceType deviceTypes = deviceTypeMapper.selectById(typeGrants.getDeviceTypeId());
if (deviceTypes == null) {
throw new ServiceException("设备类型不存在!!!");
}
// 转换对象并插入数据库
Device device = new Device();
BeanUtil.copyProperties(deviceForm, device, true);
device.setDeviceNo(createDeviceNo());
AppLoginUser loginUser = AppLoginHelper.getLoginUser();
device.setCurrentOwnerId(loginUser.getUserId());
device.setOriginalOwnerId(loginUser.getUserId());
device.setCreateByName(loginUser.getNickname());
device.setTypeName(deviceTypes.getTypeName());
device.setDeviceType(deviceTypes.getId());
if (device.getDeviceImei() != null) {
device.setPubTopic("A/" + device.getDeviceImei());
device.setSubTopic("B/" + device.getDeviceImei());
}
// 0 未绑定
device.setBindingStatus(0);
deviceMapper.insert(device);
// 新增设备类型记录
DeviceAssignments assignments = new DeviceAssignments();
assignments.setDeviceId(device.getId());
assignments.setAssignedAt(LocalDateTime.now());
// 分配者
assignments.setAssignerId(loginUser.getUserId());
assignments.setAssignerName(loginUser.getUsername());
// 接收者
assignments.setAssigneeId(loginUser.getUserId());
assignments.setActive(DeviceActiveStatusEnum.ACTIVE.getCode());
String lever = USER_ID_SEPARATOR + loginUser.getUserId();
assignments.setLever(lever);
deviceAssignmentsService.save(assignments);
}
private String createDeviceNo() {
String uuidStr = UUID.fastUUID().toString(); // 获取带 - 的标准格式字符串
return uuidStr.replaceAll("-", "");
}
}

View File

@ -301,7 +301,7 @@ file:
mqtt:
username: admin
password: #YtvpSfCNG
url: tcp://47.120.79.150:2883
url: tcp://www.cnxhyc.com:2883
subClientId: fys_subClient
subTopic: A/#
pubTopic: B/#

View File

@ -57,7 +57,7 @@ public interface IAppBusinessFileService {
* @param bo 批量新增app业务文件
* @return 是否新增成功
*/
Boolean insertBatch(Collection<AppBusinessFile> bo);
Boolean insertBatch(Collection<AppBusinessFile> bo,Boolean isBatch);
/**
* 修改app业务文件

View File

@ -102,8 +102,9 @@ public class AppBusinessFileServiceImpl implements IAppBusinessFileService {
}
@Override
public Boolean insertBatch(Collection<AppBusinessFile> bo) {
public Boolean insertBatch(Collection<AppBusinessFile> bo,Boolean isBatch) {
// 1. 去重后的 businessId 集合
if(isBatch){
List<Long> businessIds = bo.stream()
.map(AppBusinessFile::getBusinessId)
.distinct()
@ -112,6 +113,7 @@ public class AppBusinessFileServiceImpl implements IAppBusinessFileService {
queryWrapper.in("business_id", businessIds);
queryWrapper.eq("file_type", bo.stream().findFirst().orElseThrow().getFileType());
baseMapper.delete(queryWrapper);
}
return baseMapper.insertBatch(bo);
}

View File

@ -163,6 +163,8 @@ public class Device extends TenantEntity {
/**
* 在线状态(0离线1在线,2异常)
* online_status
*/
private Integer onlineStatus;
}

View File

@ -43,6 +43,7 @@ public class DeviceGroupBo extends BaseEntity {
/**
* 父分组ID
* parent_id
*/
@Schema(title = "父分组ID")
private Long parentId;

View File

@ -105,6 +105,7 @@ public class DeviceQueryCriteria extends BaseEntity {
/**
* 设备所属分组
* group_id
*/
private Long groupId;
@ -117,7 +118,8 @@ public class DeviceQueryCriteria extends BaseEntity {
/**
* 设备在线状态
* 0:离线;1:在线
* 0:离线;1:在线2:故障
* online_status
*/
private Integer onlineStatus;

View File

@ -4,6 +4,7 @@ import lombok.Data;
/**
* 报警信息
* 0-强制报警1-撞击闯入2-自动报警3-电子围栏告警
*
* @author: 默苍璃
* @date: 2025-09-0114:24
@ -21,7 +22,6 @@ public class AlarmInformationVo {
*/
private Integer processingAlarm = 0;
/**
* 今日报警总数
*/
@ -32,7 +32,6 @@ public class AlarmInformationVo {
*/
private Integer processingAlarmToday = 0;
/**
* 强制报警
*/
@ -44,12 +43,12 @@ public class AlarmInformationVo {
private Integer intrusionImpact = 0;
/**
* 动报警
* 动报警
*/
private Integer alarmManual = 0;
private Integer alarmAuto = 0;
/**
*
* 电子围栏告警
*/
private Integer fenceElectronic = 0;

View File

@ -43,10 +43,10 @@ public class AlarmStatisticsVo implements Serializable {
private Integer intrusionImpactAlarms = 0;
/**
* 动报警数量
* 动报警数量
* device_action = 2
*/
private Integer manualAlarms = 0;
private Integer autoAlarms = 0;
/**
* 电子围栏告警数量

View File

@ -58,6 +58,13 @@ public class WebDeviceVo implements Serializable {
*/
private Integer deviceStatus;
/**绑定状态
* 0 未绑定
* 1 已绑定
*/
private Integer bindingStatus;
/**
* 绑定时间
*/

View File

@ -120,7 +120,14 @@ public class DeviceGroupServiceImpl implements IDeviceGroupService {
public Boolean insertByBo(DeviceGroupBo bo) {
// 验证分组名称唯一性
DeviceGroup deviceGroup = baseMapper.selectOne(new QueryWrapper<DeviceGroup>().eq("group_name", bo.getGroupName()));
QueryWrapper<DeviceGroup> queryWrapper = new QueryWrapper<DeviceGroup>().eq("group_name", bo.getGroupName());
if (bo.getParentId() != null) {
queryWrapper.eq("parent_id", bo.getParentId());
} else {
queryWrapper.isNull("parent_id");
}
DeviceGroup deviceGroup = baseMapper.selectOne(queryWrapper);
if (deviceGroup != null) {
throw new RuntimeException("分组名称已存在,请勿重复添加!!!");
}
@ -159,9 +166,23 @@ public class DeviceGroupServiceImpl implements IDeviceGroupService {
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(DeviceGroup entity) {
private void validEntityBeforeSave(DeviceGroup bo) {
// TODO 做一些数据校验,如唯一约束
// 验证分组名称唯一性
QueryWrapper<DeviceGroup> queryWrapper = new QueryWrapper<DeviceGroup>().eq("group_name", bo.getGroupName());
if (bo.getParentId() != null) {
queryWrapper.eq("parent_id", bo.getParentId());
} else {
queryWrapper.isNull("parent_id");
}
DeviceGroup deviceGroup = baseMapper.selectOne(queryWrapper);
if (deviceGroup != null && deviceGroup.getId() != bo.getId()) {
throw new RuntimeException("分组名称已存在,请勿重复添加!!!");
}
}
/**
* 校验并批量删除设备分组信息

View File

@ -342,6 +342,9 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> impleme
for (Long id : ids) {
DeviceAssignments deviceAssignment = deviceAssignmentsMapper.selectById(id);
if (deviceAssignment == null) {
continue;
}
Device device = deviceMapper.selectById(deviceAssignment.getDeviceId());
if (StringUtils.isNotEmpty(deviceAssignment.getAssigneeName())) {
@ -834,5 +837,4 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> impleme
}
}

View File

@ -76,7 +76,7 @@
(SELECT COUNT(1) FROM device_alarm WHERE treatment_state = 0) AS processedAlarms,
(SELECT COUNT(1) FROM device_alarm WHERE device_action = 0) AS forcedAlarms,
(SELECT COUNT(1) FROM device_alarm WHERE device_action = 1) AS intrusionImpactAlarms,
(SELECT COUNT(1) FROM device_alarm WHERE device_action = 2) AS manualAlarms,
(SELECT COUNT(1) FROM device_alarm WHERE device_action = 2) AS autoAlarms,
(SELECT COUNT(1) FROM device_alarm WHERE device_action = 3) AS geoFenceAlarms
</select>

View File

@ -42,7 +42,7 @@
FROM (
SELECT
da.id AS id, d.id AS deviceId, d.device_name, d.bluetooth_name, d.group_id,
d.pub_topic, d.sub_topic, d.device_pic,
d.pub_topic, d.sub_topic, d.device_pic,d.online_status,
d.device_mac, d.device_sn, d.update_by,
d.device_imei, d.update_time, dg.id AS device_type,
d.remark, d.binding_status, t.type_name AS typeName,
@ -59,11 +59,11 @@
<if test="criteria.deviceName != null and criteria.deviceName.trim() != ''">
and d.device_name like concat('%', TRIM(#{criteria.deviceName}), '%')
</if>
<if test="criteria.deviceMac != null">
and d.device_mac = #{criteria.deviceMac}
<if test="criteria.deviceMac != null and criteria.deviceMac.trim() != ''">
and d.device_mac like concat('%', TRIM(#{criteria.deviceMac}), '%')
</if>
<if test="criteria.deviceImei != null">
and d.device_imei = #{criteria.deviceImei}
<if test="criteria.deviceImei != null and criteria.deviceImei.trim() != ''">
and d.device_imei like concat('%', TRIM(#{criteria.deviceImei}), '%')
</if>
<if test="criteria.deviceType != null">
and d.device_type = #{criteria.deviceType}
@ -71,6 +71,9 @@
<if test="criteria.deviceStatus != null">
and da.active = #{criteria.deviceStatus}
</if>
<if test="criteria.onlineStatus != null">
and d.online_status = #{criteria.onlineStatus}
</if>
<if test="criteria.groupId != null">
and d.group_id = #{criteria.groupId}
</if>
@ -125,10 +128,10 @@
<if test="criteria.deviceName != null and criteria.deviceName.trim() != ''">
and d.device_name like concat('%', TRIM(#{criteria.deviceName}), '%')
</if>
<if test="criteria.deviceMac != null">
<if test="criteria.deviceMac != null and criteria.deviceMac.trim() != ''">
and d.device_mac = #{criteria.deviceMac}
</if>
<if test="criteria.deviceImei != null">
<if test="criteria.deviceImei != null and criteria.deviceImei.trim() != ''">
and d.device_imei = #{criteria.deviceImei}
</if>
<if test="criteria.deviceType != null">
@ -137,6 +140,9 @@
<if test="criteria.deviceStatus != null">
and d.device_status = #{criteria.deviceStatus}
</if>
<if test="criteria.groupId != null">
and d.group_id = #{criteria.groupId}
</if>
<if test="criteria.currentOwnerId != null">
and d.current_owner_id = #{criteria.currentOwnerId}
</if>
@ -241,23 +247,27 @@
<select id="queryWebDeviceList" resultType="com.fuyuanshen.equipment.domain.vo.WebDeviceVo">
select * from (select d.id, d.device_name,
d.device_mac,
d.device_type,
d.device_sn,
d.device_imei,
d.device_pic,
d.group_id,
dt.type_name,
dt.communication_mode,
d.bluetooth_name,
dt.pc_model_dictionary detailPageUrl,
ap.name personnelBy,
d.device_status,
d.binding_status,
d.online_status,
c.binding_time,
ROW_NUMBER() OVER (PARTITION BY d.id ORDER BY c.binding_time) AS row_num
d.create_time,
ROW_NUMBER() OVER (PARTITION BY d.id ORDER BY d.id) AS row_num
from device d
inner join device_type dt on d.device_type = dt.id
inner join app_device_bind_record c on d.id = c.device_id
left join app_device_bind_record c on d.id = c.device_id
left join app_personnel_info ap on ap.device_id = d.id
where dt.communication_mode in (0, 2) ) a where a.row_num = 1
where dt.communication_mode in (0, 2) ORDER BY (case when d.online_status = 1 then 2 else 1 end) DESC) a where a.row_num = 1
<if test="criteria.deviceType != null">
and a.device_type = #{criteria.deviceType}
</if>
@ -265,10 +275,13 @@
and a.device_name like concat('%', #{criteria.deviceName}, '%')
</if>
<if test="criteria.deviceImei != null and criteria.deviceImei != ''">
and a.device_imei = #{criteria.deviceImei}
and a.device_imei like concat('%',#{criteria.deviceImei}, '%')
</if>
<if test="criteria.deviceMac != null and criteria.deviceMac != ''">
and a.device_mac like concat('%',#{criteria.deviceMac}, '%')
</if>
<if test="criteria.content != null and criteria.content != ''">
AND (a.device_imei = #{criteria.content} or a.device_mac = #{criteria.content})
AND (a.device_imei like concat('%',#{criteria.content}, '%') or a.device_mac like concat('%',#{criteria.content}, '%') )
</if>
<if test="criteria.deviceStatus != null">
and a.device_status = #{criteria.deviceStatus}
@ -285,7 +298,7 @@
<if test="criteria.onlineStatus != null">
and a.online_status = #{criteria.onlineStatus}
</if>
ORDER BY a.binding_time DESC
</select>
<select id="getLocationHistory" resultType="com.fuyuanshen.equipment.domain.vo.LocationHistoryVo">
select a.id,a.device_name,a.device_type,b.type_name deviceTypeName,a.device_imei,a.device_mac from device a
@ -301,10 +314,10 @@
AND a.device_mac = #{bo.deviceMac}
</if>
<if test="bo.deviceImei != null and bo.deviceImei != ''">
AND a.device_imei = #{bo.deviceImei}
and a.device_imei like concat('%',#{bo.deviceImei}, '%')
</if>
<if test="bo.content != null and bo.content != ''">
AND a.device_imei = #{bo.content} or a.device_mac = #{bo.content}
AND (a.device_imei like concat('%',#{bo.content}, '%') or a.device_mac like concat('%',#{bo.content}, '%') )
</if>
<if test="bo.startTime != null and bo.startTime != ''">
AND a.create_time <![CDATA[>=]]> #{bo.startTime}
@ -404,7 +417,7 @@
SELECT COUNT (1)
FROM device_alarm
WHERE device_action = 2
) AS alarmManual
) AS alarmAuto
, (
SELECT COUNT (1)
FROM device_alarm
@ -450,7 +463,7 @@
</select>
<!-- 获取设备使用频次统计 -->
<select id="getDeviceUsageFrequency" resultType="com.fuyuanshen.equipment.domain.vo.DeviceUsageFrequencyVo">
<select id="getDeviceUsageFrequency1" resultType="com.fuyuanshen.equipment.domain.vo.DeviceUsageFrequencyVo">
SELECT
device_name AS deviceName,
COUNT(*) AS frequency
@ -459,6 +472,17 @@
GROUP BY device_name
ORDER BY frequency DESC
</select>
<!-- 获取设备使用频次统计 (设备类型)-->
<select id="getDeviceUsageFrequency" resultType="com.fuyuanshen.equipment.domain.vo.DeviceUsageFrequencyVo">
SELECT
d.type_name AS deviceName,
COUNT(*) AS frequency
FROM device_log dl
INNER JOIN device d ON dl.device_id = d.id
WHERE dl.create_time >= DATE_SUB(NOW(), INTERVAL #{days} DAY)
GROUP BY d.device_type, d.type_name
ORDER BY frequency DESC
</select>
<!-- 根据设备IMEI查询设备 -->
<select id="selectDeviceByImei" resultType="com.fuyuanshen.equipment.domain.Device">

View File

@ -32,6 +32,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="criteria.repairPart != null">
and dr.repair_part like concat('%', TRIM(#{criteria.repairPart}), '%')
</if>
<if test="criteria.repairPerson != null">
and dr.repair_person like concat('%', TRIM(#{criteria.repairPerson}), '%')
</if>
<!-- 时间段条件 -->
<if test="criteria.repairBeginTime != null">
AND dr.repair_time <![CDATA[ >= ]]> #{criteria.repairBeginTime}