0
0

Merge remote-tracking branch 'upstream/6170'

# Conflicts:
#	fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/utils/EncryptUtilsTest.java
This commit is contained in:
2025-08-19 14:11:02 +08:00
81 changed files with 4295 additions and 1409 deletions

119
aa.conf Normal file
View File

@ -0,0 +1,119 @@
#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
server {
listen 80;
server_name cnxhyc.com;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root html;
index index.html index.htm;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000;
# listen somename:8080;
# server_name somename alias another.alias;
# location / {
# root html;
# index index.html index.htm;
# }
#}
# HTTPS server
server {
listen 443 ssl http2;
server_name cnxhyc.com;
ssl_certificate /cert/cnxhyc.com.pem;
ssl_certificate_key /cert/cnxhyc.com.key;
# 使用更现代的 SSL 配置
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers TLS_AES_256_GCM_SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
location / {
root html;
index index.html index.htm;
}
}
}

View File

@ -0,0 +1,96 @@
package com.fuyuanshen;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class CorrectVCardGenerator {
public static void main(String[] args) {
// // 定义江西上饶的134号段共22个
// String[] prefixes = {
// "1340703", "1340793", "1342650", "1342651", "1342663",
// "1342664", "1342665", "1343703", "1343793", "1347901",
// "1347902", "1347903", "1347930", "1347931", "1347932",
// "1347933", "1347934", "1347935", "1347936", "1347937",
// "1347938", "1347939"
// }; 1340700 号段(移动)
// 1340708 号段(移动)
// 1340709 号段(移动)
// 1340791 号段(移动)
// 1342668 号段(移动)
// 1343700 号段(移动)
// 1343708 号段(移动)
// 1343709 号段(移动)
// 1343790 号段(移动)
// 1343791 号段(移动)
// 1347910 号段(移动)
// 1347911 号段(移动)
// 1347912 号段(移动)
// 1347913 号段(移动)
// 1347914 号段(移动)
// 1347915 号段(移动)
// 1347916 号段(移动)
// 1347917 号段(移动)
// 1347918 号段(移动)
// 1347919 号段(移动)
// 定义江西南昌的134号段共22个
String[] prefixes = {
"1340700", "1340708", "1340709", "1340791", "1342668",
"1343700", "1343708", "1343709", "1343790", "1343791",
"1347910", "1347911", "1347912", "1347913", "1347914",
"1347915", "1347916", "1347917", "1347918", "1347919"
};
// 创建.vcf文件
String filename = "南昌联系人.vcf";
try (BufferedWriter writer = new BufferedWriter(new FileWriter(filename))) {
// 生成所有联系人
int count = 0;
for (String prefix : prefixes) {
for (int i = 0; i < 100; i++) {
String middle = String.format("%02d", i);
String phoneNumber = prefix + middle + "51";
// 每个联系人以BEGIN:VCARD开始
writer.write("BEGIN:VCARD");
writer.newLine();
writer.write("VERSION:3.0");
writer.newLine();
// 中文姓名(用户+序号)
writer.write("FN:用户" + (count + 1));
writer.newLine();
// 手机号码
writer.write("TEL;TYPE=CELL;TYPE=VOICE:" + phoneNumber);
writer.newLine();
// 添加唯一标识符
writer.write("UID:" + phoneNumber + "@shangrao");
writer.newLine();
// 添加备注信息
writer.write("NOTE:生成于" + new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
writer.newLine();
// 每个联系人以END:VCARD结束
writer.write("END:VCARD");
writer.newLine();
writer.newLine(); // 联系人之间空一行
count++;
}
}
System.out.println("成功生成 " + count + " 个联系人");
System.out.println("文件已保存为: " + filename);
} catch (IOException e) {
System.err.println("文件写入错误: " + e.getMessage());
}
}
}

View File

@ -1,20 +1,23 @@
package com.fuyuanshen.app.controller;
import com.fuyuanshen.app.domain.dto.APPReNameDTO;
import com.fuyuanshen.app.domain.dto.AppRealTimeStatusDto;
import com.fuyuanshen.app.domain.vo.APPDeviceTypeVo;
import com.fuyuanshen.app.service.AppDeviceBizService;
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.web.core.BaseController;
import com.fuyuanshen.equipment.domain.Device;
import com.fuyuanshen.equipment.domain.dto.AppDeviceBo;
import com.fuyuanshen.equipment.domain.query.DeviceQueryCriteria;
import com.fuyuanshen.equipment.domain.vo.AppDeviceVo;
import com.fuyuanshen.web.service.device.DeviceBizService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
/**
* APP设备信息管理
@ -25,7 +28,7 @@ import java.util.List;
@RequestMapping("/app/device")
public class AppDeviceController extends BaseController {
private final AppDeviceBizService appDeviceService;
private final DeviceBizService appDeviceService;
/**
@ -72,4 +75,19 @@ public class AppDeviceController extends BaseController {
appDeviceService.reName(reNameDTO);
return R.ok("重命名成功!!!");
}
@GetMapping("/realTimeStatus")
public R<Map<String, Object>> getRealTimeStatus(AppRealTimeStatusDto statusDto) {
Map<String, Object> status = appDeviceService.getRealTimeStatus(statusDto);
return R.ok(status);
}
/**
* 根据mac查询设备信息
*/
@GetMapping("/getDeviceInfoByDeviceMac")
public R<AppDeviceVo> getDeviceInfo(String deviceMac) {
return R.ok(appDeviceService.getDeviceInfo(deviceMac));
}
}

View File

@ -5,7 +5,6 @@ import cn.hutool.core.util.RandomUtil;
import com.fuyuanshen.app.domain.bo.AppDeviceShareBo;
import com.fuyuanshen.app.domain.vo.AppDeviceShareDetailVo;
import com.fuyuanshen.app.domain.vo.AppDeviceShareVo;
import com.fuyuanshen.app.service.AppDeviceShareService;
import com.fuyuanshen.app.service.IAppDeviceShareService;
import com.fuyuanshen.common.core.constant.Constants;
import com.fuyuanshen.common.core.domain.R;
@ -16,6 +15,7 @@ import com.fuyuanshen.common.mybatis.core.page.TableDataInfo;
import com.fuyuanshen.common.ratelimiter.annotation.RateLimiter;
import com.fuyuanshen.common.redis.utils.RedisUtils;
import com.fuyuanshen.common.web.core.BaseController;
import com.fuyuanshen.web.service.DeviceShareService;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
@ -45,14 +45,14 @@ public class AppDeviceShareController extends BaseController {
private final IAppDeviceShareService deviceShareService;
private final AppDeviceShareService appDeviceShareService;
private final DeviceShareService appDeviceShareService;
/**
* 分享管理列表
*/
@GetMapping("/deviceShareList")
public TableDataInfo<AppDeviceShareVo> list(AppDeviceShareBo bo, PageQuery pageQuery) {
return deviceShareService.queryPageList(bo, pageQuery);
return appDeviceShareService.queryPageList(bo, pageQuery);
}
/**
@ -60,7 +60,7 @@ public class AppDeviceShareController extends BaseController {
*/
@GetMapping("/otherDeviceShareList")
public TableDataInfo<AppDeviceShareVo> otherDeviceShareList(AppDeviceShareBo bo, PageQuery pageQuery) {
return deviceShareService.otherDeviceShareList(bo, pageQuery);
return appDeviceShareService.otherDeviceShareList(bo, pageQuery);
}
/**

View File

@ -4,11 +4,13 @@ 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.app.service.device.AppDeviceBJQBizService;
import com.fuyuanshen.common.core.domain.R;
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.web.core.BaseController;
import com.fuyuanshen.equipment.domain.dto.AppDeviceSendMsgBo;
import com.fuyuanshen.web.service.device.DeviceBJQBizService;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
@ -24,7 +26,7 @@ import org.springframework.web.multipart.MultipartFile;
@RequestMapping("/app/bjq/device")
public class AppDeviceBJQController extends BaseController {
private final AppDeviceBJQBizService appDeviceService;
private final DeviceBJQBizService appDeviceService;
/**
* 获取设备详细信息
@ -41,6 +43,7 @@ public class AppDeviceBJQController extends BaseController {
* 人员信息登记
*/
@PostMapping(value = "/registerPersonInfo")
// @FunctionAccessAnnotation("registerPersonInfo")
public R<Void> registerPersonInfo(@Validated(AddGroup.class) @RequestBody AppPersonnelInfoBo bo) {
return toAjax(appDeviceService.registerPersonInfo(bo));
}
@ -49,6 +52,7 @@ public class AppDeviceBJQController extends BaseController {
* 发送信息
*/
@PostMapping(value = "/sendMessage")
@FunctionAccessBatcAnnotation(value = "sendMessage", timeOut = 30, batchMaxTimeOut = 40)
public R<Void> sendMessage(@RequestBody AppDeviceSendMsgBo bo) {
return toAjax(appDeviceService.sendMessage(bo));
}
@ -57,6 +61,7 @@ public class AppDeviceBJQController extends BaseController {
* 发送报警信息
*/
@PostMapping(value = "/sendAlarmMessage")
@FunctionAccessBatcAnnotation(value = "sendAlarmMessage", timeOut = 5, batchMaxTimeOut = 10)
public R<Void> sendAlarmMessage(@RequestBody AppDeviceSendMsgBo bo) {
return toAjax(appDeviceService.sendAlarmMessage(bo));
}
@ -65,6 +70,7 @@ public class AppDeviceBJQController extends BaseController {
* 上传设备logo图片
*/
@PostMapping("/uploadLogo")
@FunctionAccessAnnotation("uploadLogo")
public R<Void> upload(@Validated @ModelAttribute AppDeviceLogoUploadDto bo) {
MultipartFile file = bo.getFile();
@ -80,6 +86,7 @@ public class AppDeviceBJQController extends BaseController {
* 灯光模式
* 0关灯1强光模式2弱光模式, 3爆闪模式, 4泛光模式
*/
// @FunctionAccessAnnotation("lightModeSettings")
@PostMapping("/lightModeSettings")
public R<Void> lightModeSettings(@RequestBody DeviceInstructDto params) {
// params 转 JSONObject
@ -91,6 +98,7 @@ public class AppDeviceBJQController extends BaseController {
* 灯光亮度设置
*
*/
// @FunctionAccessAnnotation("lightBrightnessSettings")
@PostMapping("/lightBrightnessSettings")
public R<Void> lightBrightnessSettings(@RequestBody DeviceInstructDto params) {
appDeviceService.lightBrightnessSettings(params);
@ -102,18 +110,10 @@ public class AppDeviceBJQController extends BaseController {
*
*/
@PostMapping("/laserModeSettings")
// @FunctionAccessAnnotation("laserModeSettings")
public R<Void> laserModeSettings(@RequestBody DeviceInstructDto params) {
appDeviceService.laserModeSettings(params);
return R.ok();
}
/**
* 地图逆解析
*
*/
@PostMapping("/mapReverseGeocoding")
public R<Void> mapReverseGeocoding(@RequestBody DeviceInstructDto params) {
String mapJson = appDeviceService.mapReverseGeocoding(params);
return R.ok(mapJson);
}
}

View File

@ -8,6 +8,7 @@ public class AppDeviceLogoUploadDto {
private Long deviceId;
private String deviceImei;
/**
* 文件
*/

View File

@ -0,0 +1,30 @@
package com.fuyuanshen.app.domain.dto;
import lombok.Data;
/**
* 设备实时状态
*/
@Data
public class AppRealTimeStatusDto {
/**
* 设备IMEI
*/
private String deviceImei;
/**
* 设备类型
*/
private String typeName;
/**
* 功能类型
*/
private String functionMode;
/**
* 批次号
*/
private String batchId;
}

View File

@ -1,605 +0,0 @@
package com.fuyuanshen.app.service;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fuyuanshen.app.domain.AppDeviceBindRecord;
import com.fuyuanshen.app.domain.AppPersonnelInfo;
import com.fuyuanshen.app.domain.bo.AppPersonnelInfoBo;
import com.fuyuanshen.app.domain.dto.APPReNameDTO;
import com.fuyuanshen.app.domain.dto.AppDeviceLogoUploadDto;
import com.fuyuanshen.app.domain.dto.DeviceInstructDto;
import com.fuyuanshen.app.domain.vo.APPDeviceTypeVo;
import com.fuyuanshen.app.domain.vo.AppDeviceDetailVo;
import com.fuyuanshen.app.domain.vo.AppPersonnelInfoVo;
import com.fuyuanshen.app.mapper.AppDeviceBindRecordMapper;
import com.fuyuanshen.app.mapper.AppPersonnelInfoMapper;
import com.fuyuanshen.app.mapper.equipment.APPDeviceMapper;
import com.fuyuanshen.common.core.constant.GlobalConstants;
import com.fuyuanshen.common.core.exception.ServiceException;
import com.fuyuanshen.common.core.utils.*;
import com.fuyuanshen.common.mybatis.core.page.PageQuery;
import com.fuyuanshen.common.mybatis.core.page.TableDataInfo;
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.dto.AppDeviceBo;
import com.fuyuanshen.equipment.domain.dto.AppDeviceSendMsgBo;
import com.fuyuanshen.equipment.domain.query.DeviceQueryCriteria;
import com.fuyuanshen.equipment.domain.vo.AppDeviceVo;
import com.fuyuanshen.equipment.enums.BindingStatusEnum;
import com.fuyuanshen.equipment.enums.CommunicationModeEnum;
import com.fuyuanshen.equipment.mapper.DeviceMapper;
import com.fuyuanshen.equipment.mapper.DeviceTypeMapper;
import com.fuyuanshen.equipment.utils.c.ReliableTextToBitmap;
import com.fuyuanshen.global.mqtt.config.MqttGateway;
import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants;
import com.fuyuanshen.global.mqtt.constants.MqttConstants;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.time.Duration;
import java.util.*;
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.global.mqtt.constants.DeviceRedisKeyConstants.*;
@Slf4j
@Service
@RequiredArgsConstructor
public class AppDeviceBizService {
private final APPDeviceMapper appDeviceMapper;
private final DeviceMapper deviceMapper;
private final AppPersonnelInfoMapper appPersonnelInfoMapper;
private final DeviceTypeMapper deviceTypeMapper;
private final MqttGateway mqttGateway;
private final AppDeviceBindRecordMapper appDeviceBindRecordMapper;
public List<APPDeviceTypeVo> getTypeList() {
Long userId = AppLoginHelper.getUserId();
return appDeviceMapper.getTypeList(userId);
}
public void reName(APPReNameDTO reNameDTO) {
UpdateWrapper<Device> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("id", reNameDTO.getId())
.eq("binding_user_id", AppLoginHelper.getUserId())
.set("device_name", reNameDTO.getDeviceName());
deviceMapper.update(updateWrapper);
}
public int sendMessage(AppDeviceSendMsgBo bo) {
List<Long> deviceIds = bo.getDeviceIds();
if (deviceIds == null || deviceIds.isEmpty()) {
throw new ServiceException("请选择设备");
}
for (Long deviceId : deviceIds) {
Device device = deviceMapper.selectById(deviceId);
if (device == null) {
throw new ServiceException("设备不存在" + deviceId);
}
try {
ClassPathResource resource = new ClassPathResource("image/background.png");
InputStream inputStream = resource.getInputStream();
// String backgroundImagePath = "D:\\background.png"; // 替换为实际背景图片路径
byte[] largeData = ImageWithTextGenerate.generate160x80ImageWithText2(bo.getSendMsg(), inputStream, 25600);
int[] ints = convertHexToDecimal(largeData);
RedisUtils.setCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + device.getDeviceImei() + DEVICE_SEND_MESSAGE_KEY_PREFIX , Arrays.toString(ints), Duration.ofSeconds(30 * 60L));
String data = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + device.getDeviceImei() + DEVICE_SEND_MESSAGE_KEY_PREFIX);
byte[] arr = ImageToCArrayConverter.convertStringToByteArray(data);
byte[] specificChunk = ImageToCArrayConverter.getChunk(arr, 0, 512);
log.info("发送信息第0块数据大小: {} 字节",specificChunk.length);
// log.info("第0块数据: {}", Arrays.toString(specificChunk));
ArrayList<Integer> intData = new ArrayList<>();
intData.add(6);
intData.add(1);
ImageToCArrayConverter.buildArr(convertHexToDecimal(specificChunk),intData);
intData.add(0);
intData.add(0);
intData.add(0);
intData.add(0);
Map<String, Object> map = new HashMap<>();
map.put("instruct", intData);
mqttGateway.sendMsgToMqtt(MqttConstants.GLOBAL_PUB_KEY+device.getDeviceImei(), 1 , JSON.toJSONString(map));
log.info("发送信息点阵数据到设备消息=>topic:{},payload:{}", MqttConstants.GLOBAL_PUB_KEY+device.getDeviceImei(),JSON.toJSONString(map));
UpdateWrapper<Device> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("id", deviceId)
.eq("binding_user_id", AppLoginHelper.getUserId())
.set("send_msg", bo.getSendMsg());
deviceMapper.update(updateWrapper);
} catch (Exception e) {
log.info("发送信息设备发送信息失败:{}" ,deviceId);
}
}
return 1;
}
public TableDataInfo<AppDeviceVo> queryAppDeviceList(DeviceQueryCriteria bo, PageQuery pageQuery) {
if (bo.getBindingUserId() == null) {
Long userId = AppLoginHelper.getUserId();
bo.setBindingUserId(userId);
}
Page<AppDeviceVo> result = deviceMapper.queryAppBindDeviceList(pageQuery.build(), bo);
List<AppDeviceVo> records = result.getRecords();
if(records != null && !records.isEmpty()){
records.forEach(item -> {
if(item.getCommunicationMode()!=null && item.getCommunicationMode() == 0){
//设备在线状态
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 deviceStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX+ item.getDeviceImei() + DEVICE_STATUS_KEY_PREFIX);
// 获取电量
if(StringUtils.isNotBlank(deviceStatus)){
JSONObject jsonObject = JSONObject.parseObject(deviceStatus);
item.setBattery(jsonObject.getString("batteryPercentage"));
}else{
item.setBattery("0");
}
String location = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY +DEVICE_KEY_PREFIX+ item.getDeviceImei()+ DEVICE_LOCATION_KEY_PREFIX);
if(StringUtils.isNotBlank(location)){
JSONObject jsonObject = JSONObject.parseObject(location);
item.setLatitude(jsonObject.getString("latitude"));
item.setLongitude(jsonObject.getString("longitude"));
}
String alarmStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY +DEVICE_KEY_PREFIX+ item.getDeviceImei()+ DEVICE_ALARM_KEY_PREFIX);
if(StringUtils.isNotBlank(alarmStatus)){
item.setAlarmStatus(alarmStatus);
}
}
});
}
return TableDataInfo.build(result);
}
public int bindDevice(AppDeviceBo bo) {
Integer mode = bo.getCommunicationMode();
Long userId = AppLoginHelper.getUserId();
if (mode == CommunicationModeEnum.FOUR_G.getValue()) {
String deviceImei = bo.getDeviceImei();
QueryWrapper<Device> qw = new QueryWrapper<Device>()
.eq("device_imei", deviceImei);
List<Device> devices = deviceMapper.selectList(qw);
if (devices.isEmpty()) {
throw new RuntimeException("请先将设备入库!!!");
}
Device device = devices.get(0);
if (device.getBindingStatus() != null && device.getBindingStatus() == BindingStatusEnum.BOUND.getCode()) {
throw new RuntimeException("设备已绑定");
}
QueryWrapper<AppDeviceBindRecord> bindRecordQueryWrapper = new QueryWrapper<>();
bindRecordQueryWrapper.eq("device_id", device.getId());
AppDeviceBindRecord appDeviceBindRecord = appDeviceBindRecordMapper.selectOne(bindRecordQueryWrapper);
if (appDeviceBindRecord != null) {
UpdateWrapper<AppDeviceBindRecord> deviceUpdateWrapper = new UpdateWrapper<>();
deviceUpdateWrapper.eq("device_id", device.getId())
.set("binding_status", BindingStatusEnum.BOUND.getCode())
.set("binding_user_id", userId)
.set("update_time", new Date())
.set("binding_time", new Date());
return appDeviceBindRecordMapper.update(null, deviceUpdateWrapper);
} else {
AppDeviceBindRecord bindRecord = new AppDeviceBindRecord();
bindRecord.setDeviceId(device.getId());
bindRecord.setBindingUserId(userId);
bindRecord.setBindingTime(new Date());
bindRecord.setCreateBy(userId);
appDeviceBindRecordMapper.insert(bindRecord);
}
UpdateWrapper<Device> deviceUpdateWrapper = new UpdateWrapper<>();
deviceUpdateWrapper.eq("id", device.getId())
.set("binding_status", BindingStatusEnum.BOUND.getCode())
.set("binding_user_id", userId)
.set("binding_time", new Date());
return deviceMapper.update(null, deviceUpdateWrapper);
} else if (mode == CommunicationModeEnum.BLUETOOTH.getValue()) {
String deviceMac = bo.getDeviceMac();
QueryWrapper<Device> qw = new QueryWrapper<Device>()
.eq("device_mac", deviceMac);
List<Device> devices = deviceMapper.selectList(qw);
if (devices.isEmpty()) {
throw new RuntimeException("请先将设备入库!!!");
}
Device device = devices.get(0);
QueryWrapper<AppDeviceBindRecord> bindRecordQueryWrapper = new QueryWrapper<>();
bindRecordQueryWrapper.eq("device_id", device.getId());
bindRecordQueryWrapper.eq("binding_user_id", userId);
AppDeviceBindRecord appDeviceBindRecord = appDeviceBindRecordMapper.selectOne(bindRecordQueryWrapper);
if (appDeviceBindRecord != null) {
UpdateWrapper<AppDeviceBindRecord> deviceUpdateWrapper = new UpdateWrapper<>();
deviceUpdateWrapper.eq("device_id", device.getId())
.eq("binding_user_id", userId)
.set("binding_user_id", userId)
.set("binding_time", new Date());
return appDeviceBindRecordMapper.update(null, deviceUpdateWrapper);
} else {
AppDeviceBindRecord bindRecord = new AppDeviceBindRecord();
bindRecord.setDeviceId(device.getId());
bindRecord.setBindingUserId(userId);
bindRecord.setBindingTime(new Date());
bindRecord.setCreateBy(userId);
appDeviceBindRecordMapper.insert(bindRecord);
}
UpdateWrapper<Device> deviceUpdateWrapper = new UpdateWrapper<>();
deviceUpdateWrapper.eq("id", device.getId())
.set("binding_status", BindingStatusEnum.BOUND.getCode())
.set("binding_user_id", userId)
.set("binding_time", new Date());
return deviceMapper.update(null, deviceUpdateWrapper);
} else {
throw new RuntimeException("通讯方式错误");
}
}
public int unBindDevice(Long id) {
return unBindDevice(id, null, 1);
}
public int unBindDevice(Long id, Long userId, int type) {
Device device = deviceMapper.selectById(id);
if (device == null) {
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);
if (userId == null) {
userId = AppLoginHelper.getUserId();
}
QueryWrapper<AppDeviceBindRecord> bindRecordQueryWrapper = new QueryWrapper<>();
bindRecordQueryWrapper.eq("device_id", device.getId());
// 设备端解绑 0:设备端解绑 1:web端解绑
if (type == 1) {
bindRecordQueryWrapper.eq("binding_user_id", userId);
}
// AppDeviceBindRecord appDeviceBindRecord = appDeviceBindRecordMapper.selectOne(bindRecordQueryWrapper);
// if (appDeviceBindRecord != null) {
// return appDeviceBindRecordMapper.deleteById(appDeviceBindRecord.getId());
// }
List<AppDeviceBindRecord> appDeviceBindRecordList = appDeviceBindRecordMapper.selectList(bindRecordQueryWrapper);
if (CollectionUtil.isNotEmpty(appDeviceBindRecordList)) {
appDeviceBindRecordList.forEach(appDeviceBindRecord ->
appDeviceBindRecordMapper.deleteById(appDeviceBindRecord.getId()));
}
return 1;
}
public AppDeviceDetailVo getInfo(Long id) {
Device device = deviceMapper.selectById(id);
if (device == null) {
throw new RuntimeException("请先将设备入库!!!");
}
AppDeviceDetailVo vo = new AppDeviceDetailVo();
vo.setDeviceId(device.getId());
vo.setDeviceName(device.getDeviceName());
vo.setDevicePic(device.getDevicePic());
vo.setDeviceImei(device.getDeviceImei());
vo.setDeviceMac(device.getDeviceMac());
vo.setDeviceStatus(device.getDeviceStatus());
DeviceType deviceType = deviceTypeMapper.selectById(device.getDeviceType());
if (deviceType != null) {
vo.setCommunicationMode(Integer.valueOf(deviceType.getCommunicationMode()));
vo.setTypeName(deviceType.getTypeName());
}
vo.setBluetoothName(device.getBluetoothName());
vo.setSendMsg(device.getSendMsg());
QueryWrapper<AppPersonnelInfo> qw = new QueryWrapper<AppPersonnelInfo>()
.eq("device_id", device.getId());
AppPersonnelInfo appPersonnelInfo = appPersonnelInfoMapper.selectOne(qw);
if (appPersonnelInfo != null) {
AppPersonnelInfoVo personnelInfoVo = MapstructUtils.convert(appPersonnelInfo, AppPersonnelInfoVo.class);
vo.setPersonnelInfo(personnelInfoVo);
}
//设备在线状态
String onlineStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + device.getDeviceImei() + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX);
if(StringUtils.isNotBlank(onlineStatus)){
vo.setOnlineStatus(1);
}else{
vo.setOnlineStatus(0);
}
String deviceStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + device.getDeviceImei() + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX);
// 获取电量
if(StringUtils.isNotBlank(deviceStatus)){
JSONObject jsonObject = JSONObject.parseObject(deviceStatus);
vo.setMainLightMode(jsonObject.getString("mainLightMode"));
vo.setLaserLightMode(jsonObject.getString("laserLightMode"));
vo.setBatteryPercentage(jsonObject.getString("batteryPercentage"));
vo.setChargeState(jsonObject.getString("chargeState"));
vo.setBatteryRemainingTime(jsonObject.getString("batteryRemainingTime"));
}else{
vo.setBatteryPercentage("0");
}
// 获取经度纬度
String locationKey = GlobalConstants.GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + device.getDeviceImei() + DEVICE_LOCATION_KEY_PREFIX;
String locationInfo = RedisUtils.getCacheObject(locationKey);
if(StringUtils.isNotBlank(locationInfo)){
JSONObject jsonObject = JSONObject.parseObject(locationInfo);
vo.setLongitude(jsonObject.get("longitude").toString());
vo.setLatitude(jsonObject.get("latitude").toString());
vo.setAddress((String)jsonObject.get("address"));
}
return vo;
}
public boolean registerPersonInfo(AppPersonnelInfoBo bo) {
Long deviceId = bo.getDeviceId();
Device deviceObj = deviceMapper.selectById(deviceId);
if (deviceObj == null) {
throw new RuntimeException("请先将设备入库!!!");
}
QueryWrapper<AppPersonnelInfo> qw = new QueryWrapper<AppPersonnelInfo>()
.eq("device_id", deviceId);
List<AppPersonnelInfoVo> appPersonnelInfoVos = appPersonnelInfoMapper.selectVoList(qw);
// unitName,position,name,id
byte[] unitName = generateFixedBitmapData(bo.getUnitName(), 120);
byte[] position = generateFixedBitmapData(bo.getPosition(), 120);
byte[] name = generateFixedBitmapData(bo.getName(), 120);
byte[] id = generateFixedBitmapData(bo.getCode(), 120);
ArrayList<Integer> intData = new ArrayList<>();
intData.add(2);
buildArr(convertHexToDecimal(unitName), intData);
buildArr(convertHexToDecimal(position), intData);
buildArr(convertHexToDecimal(name), intData);
buildArr(convertHexToDecimal(id), intData);
intData.add(0);
intData.add(0);
intData.add(0);
intData.add(0);
Map<String, Object> map = new HashMap<>();
map.put("instruct", intData);
mqttGateway.sendMsgToMqtt(MqttConstants.GLOBAL_PUB_KEY + deviceObj.getDeviceImei(), 1, JSON.toJSONString(map));
log.info("发送点阵数据到设备消息=>topic:{},payload:{}", MqttConstants.GLOBAL_PUB_KEY + deviceObj.getDeviceImei(), bo);
if (ObjectUtils.length(appPersonnelInfoVos) == 0) {
AppPersonnelInfo appPersonnelInfo = MapstructUtils.convert(bo, AppPersonnelInfo.class);
return appPersonnelInfoMapper.insertOrUpdate(appPersonnelInfo);
} else {
UpdateWrapper<AppPersonnelInfo> uw = new UpdateWrapper<>();
uw.eq("device_id", deviceId)
.set("name", bo.getName())
.set("position", bo.getPosition())
.set("unit_name", bo.getUnitName())
.set("code", bo.getCode());
return appPersonnelInfoMapper.update(null, uw) > 0;
}
}
public static void main(String[] args) throws IOException {
byte[] largeData = ImageToCArrayConverter.convertImageToCArray("E:\\workspace\\demo.png", 160, 80, 25600);
System.out.println("长度:" + largeData.length);
System.out.println("原始数据大小: " + largeData.length + " 字节");
int[] ints = convertHexToDecimal(largeData);
System.out.println("转换后的数据: " + Arrays.toString(ints));
}
public void uploadDeviceLogo(AppDeviceLogoUploadDto bo) {
try {
Device device = deviceMapper.selectById(bo.getDeviceId());
if (device == null) {
throw new ServiceException("设备不存在");
}
MultipartFile file = bo.getFile();
byte[] largeData = ImageToCArrayConverter.convertImageToCArray(file.getInputStream(), 160, 80, 25600);
log.info("长度:" + largeData.length);
log.info("原始数据大小: {} 字节", largeData.length);
int[] ints = convertHexToDecimal(largeData);
RedisUtils.setCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + device.getDeviceImei() + DEVICE_BOOT_LOGO_KEY_PREFIX, Arrays.toString(ints), Duration.ofSeconds(30 * 60L));
String data = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + device.getDeviceImei() + DEVICE_BOOT_LOGO_KEY_PREFIX);
byte[] arr = ImageToCArrayConverter.convertStringToByteArray(data);
byte[] specificChunk = ImageToCArrayConverter.getChunk(arr, 0, 512);
log.info("第0块数据大小: {} 字节", specificChunk.length);
// log.info("第0块数据: {}", Arrays.toString(specificChunk));
ArrayList<Integer> intData = new ArrayList<>();
intData.add(3);
intData.add(1);
ImageToCArrayConverter.buildArr(convertHexToDecimal(specificChunk),intData);
intData.add(0);
intData.add(0);
intData.add(0);
intData.add(0);
Map<String, Object> map = new HashMap<>();
map.put("instruct", intData);
mqttGateway.sendMsgToMqtt(MqttConstants.GLOBAL_PUB_KEY+device.getDeviceImei(), 1 , JSON.toJSONString(map));
log.info("发送点阵数据到设备消息=>topic:{},payload:{}", MqttConstants.GLOBAL_PUB_KEY+device.getDeviceImei(),JSON.toJSONString(map));
} catch (Exception e){
e.printStackTrace();
}
}
/**
* 灯光模式
* 0关灯1强光模式2弱光模式, 3爆闪模式, 4泛光模式
*/
public void lightModeSettings(DeviceInstructDto params) {
try {
Long deviceId = params.getDeviceId();
Device device = deviceMapper.selectById(deviceId);
if(device == null){
throw new ServiceException("设备不存在");
}
Integer instructValue = Integer.parseInt(params.getInstructValue());
ArrayList<Integer> intData = new ArrayList<>();
intData.add(1);
intData.add(instructValue);
intData.add(0);
intData.add(0);
intData.add(0);
Map<String, Object> map = new HashMap<>();
map.put("instruct", intData);
mqttGateway.sendMsgToMqtt(MqttConstants.GLOBAL_PUB_KEY+device.getDeviceImei(), 1 , JSON.toJSONString(map));
log.info("发送点阵数据到设备消息=>topic:{},payload:{}", MqttConstants.GLOBAL_PUB_KEY+device.getDeviceImei(),JSON.toJSONString(map));
} catch (Exception e){
e.printStackTrace();
}
}
//灯光亮度设置
public void lightBrightnessSettings(DeviceInstructDto params) {
try {
Long deviceId = params.getDeviceId();
Device device = deviceMapper.selectById(deviceId);
if(device == null){
throw new ServiceException("设备不存在");
}
String instructValue = params.getInstructValue();
ArrayList<Integer> intData = new ArrayList<>();
intData.add(5);
String[] values = instructValue.split("\\.");
String value1 = values[0];
String value2 = values[1];
if(StringUtils.isNoneBlank(value1)){
intData.add(Integer.parseInt(value1));
}
if(StringUtils.isNoneBlank(value2)){
intData.add(Integer.parseInt(value2));
}
intData.add(0);
intData.add(0);
intData.add(0);
Map<String, Object> map = new HashMap<>();
map.put("instruct", intData);
mqttGateway.sendMsgToMqtt(MqttConstants.GLOBAL_PUB_KEY+device.getDeviceImei(), 1 , JSON.toJSONString(map));
log.info("发送点阵数据到设备消息=>topic:{},payload:{}", MqttConstants.GLOBAL_PUB_KEY+device.getDeviceImei(),JSON.toJSONString(map));
} catch (Exception e){
e.printStackTrace();
}
}
//激光模式设置
public void laserModeSettings(DeviceInstructDto params) {
try {
Long deviceId = params.getDeviceId();
Device device = deviceMapper.selectById(deviceId);
if(device == null){
throw new ServiceException("设备不存在");
}
Integer instructValue = Integer.parseInt(params.getInstructValue());
ArrayList<Integer> intData = new ArrayList<>();
intData.add(4);
intData.add(instructValue);
intData.add(0);
intData.add(0);
intData.add(0);
Map<String, Object> map = new HashMap<>();
map.put("instruct", intData);
mqttGateway.sendMsgToMqtt(MqttConstants.GLOBAL_PUB_KEY+device.getDeviceImei(), 1 , JSON.toJSONString(map));
log.info("发送点阵数据到设备消息=>topic:{},payload:{}", MqttConstants.GLOBAL_PUB_KEY+device.getDeviceImei(),JSON.toJSONString(map));
} catch (Exception e){
e.printStackTrace();
}
}
public String mapReverseGeocoding(DeviceInstructDto params) {
// Long deviceId = params.getDeviceId();
// Device device = deviceMapper.selectById(deviceId);
QueryWrapper<Device> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("device_imei", params.getDeviceImei());
List<Device> devices = deviceMapper.selectList(queryWrapper);
if(ObjectUtils.length( devices) ==0){
throw new ServiceException("设备不存在");
}
return RedisUtils.getCacheObject("device:location:" + devices.get(0).getDeviceImei());
}
public int sendAlarmMessage(AppDeviceSendMsgBo bo) {
try {
List<Long> deviceIds = bo.getDeviceIds();
if (deviceIds == null || deviceIds.isEmpty()) {
throw new ServiceException("请选择设备");
}
for (Long deviceId : deviceIds) {
Device device = deviceMapper.selectById(deviceId);
if (device == null) {
throw new ServiceException("设备不存在" + deviceId);
}
try {
ArrayList<Integer> intData = new ArrayList<>();
intData.add(7);
intData.add(Integer.parseInt(bo.getInstructValue()));
intData.add(0);
intData.add(0);
intData.add(0);
intData.add(0);
Map<String, Object> map = new HashMap<>();
map.put("instruct", intData);
mqttGateway.sendMsgToMqtt(MqttConstants.GLOBAL_PUB_KEY+device.getDeviceImei(), 1 , JSON.toJSONString(map));
log.info("发送点阵数据到设备消息=>topic:{},payload:{}", MqttConstants.GLOBAL_PUB_KEY+device.getDeviceImei(),JSON.toJSONString(map));
UpdateWrapper<Device> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("id", deviceId)
.eq("binding_user_id", AppLoginHelper.getUserId())
.set("send_msg", bo.getSendMsg());
deviceMapper.update(updateWrapper);
} catch (Exception e) {
log.info("设备发送信息失败:{}" ,deviceId);
}
}
} catch (Exception e){
e.printStackTrace();
}
return 1;
}
}

View File

@ -1,135 +0,0 @@
package com.fuyuanshen.app.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.fuyuanshen.app.domain.AppDeviceShare;
import com.fuyuanshen.app.domain.AppPersonnelInfo;
import com.fuyuanshen.app.domain.bo.AppDeviceShareBo;
import com.fuyuanshen.app.domain.vo.AppDeviceShareDetailVo;
import com.fuyuanshen.app.domain.vo.AppDeviceShareVo;
import com.fuyuanshen.app.domain.vo.AppPersonnelInfoVo;
import com.fuyuanshen.app.mapper.AppDeviceShareMapper;
import com.fuyuanshen.app.mapper.AppPersonnelInfoMapper;
import com.fuyuanshen.common.core.constant.Constants;
import com.fuyuanshen.common.core.constant.GlobalConstants;
import com.fuyuanshen.common.core.exception.ServiceException;
import com.fuyuanshen.common.core.exception.user.CaptchaExpireException;
import com.fuyuanshen.common.core.utils.MessageUtils;
import com.fuyuanshen.common.core.utils.StringUtils;
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.mapper.DeviceMapper;
import com.fuyuanshen.equipment.mapper.DeviceTypeMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
@RequiredArgsConstructor
@Slf4j
@Service
public class AppDeviceShareService {
private final AppDeviceShareMapper appDeviceShareMapper;
private final DeviceMapper deviceMapper;
private final DeviceTypeMapper deviceTypeMapper;
private final AppPersonnelInfoMapper appPersonnelInfoMapper;
public AppDeviceShareDetailVo getInfo(Long id) {
LambdaQueryWrapper<AppDeviceShare> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(AppDeviceShare::getDeviceId, id);
List<AppDeviceShareVo> appDeviceShareVos = appDeviceShareMapper.selectVoList(queryWrapper);
if(appDeviceShareVos==null || appDeviceShareVos.isEmpty()){
return null;
}
AppDeviceShareVo shareVo = appDeviceShareVos.get(0);
AppDeviceShareDetailVo shareDetailVo = new AppDeviceShareDetailVo();
shareDetailVo.setId(shareVo.getId());
shareDetailVo.setDeviceId(shareVo.getDeviceId());
shareDetailVo.setPhonenumber(shareVo.getPhonenumber());
shareDetailVo.setPermission(shareVo.getPermission());
Device device = deviceMapper.selectById(shareVo.getDeviceId());
shareDetailVo.setDeviceName(device.getDeviceName());
shareDetailVo.setDeviceImei(device.getDeviceImei());
shareDetailVo.setDeviceMac(device.getDeviceMac());
DeviceType deviceType = deviceTypeMapper.selectById(device.getDeviceType());
if(deviceType!=null){
shareDetailVo.setCommunicationMode(Integer.valueOf(deviceType.getCommunicationMode()));
}
shareDetailVo.setDevicePic(device.getDevicePic());
shareDetailVo.setTypeName(deviceType.getTypeName());
shareDetailVo.setBluetoothName(device.getBluetoothName());
shareDetailVo.setDeviceStatus(device.getDeviceStatus());
shareDetailVo.setSendMsg(device.getSendMsg());
LambdaQueryWrapper<AppPersonnelInfo> qw = new LambdaQueryWrapper<>();
qw.eq(AppPersonnelInfo::getDeviceId, device.getId());
List<AppPersonnelInfoVo> appPersonnelInfoVos = appPersonnelInfoMapper.selectVoList(qw);
if(appPersonnelInfoVos!=null && !appPersonnelInfoVos.isEmpty()){
shareDetailVo.setPersonnelInfo(appPersonnelInfoVos.get(0));
}
return shareDetailVo;
}
/**
* 校验短信验证码
*/
private boolean validateSmsCode(String tenantId, String phonenumber, String smsCode) {
String code = RedisUtils.getCacheObject(GlobalConstants.DEVICE_SHARE_CODES_KEY + phonenumber);
if (StringUtils.isBlank(code)) {
throw new ServiceException("验证码失效");
}
return code.equals(smsCode);
}
public int deviceShare(AppDeviceShareBo bo) {
boolean flag = validateSmsCode(AppLoginHelper.getTenantId(), bo.getPhonenumber(), bo.getSmsCode());
if(!flag){
throw new ServiceException("验证码错误");
}
Device device = deviceMapper.selectById(bo.getDeviceId());
if(device==null){
throw new ServiceException("设备不存在");
}
Long userId = AppLoginHelper.getUserId();
LambdaQueryWrapper<AppDeviceShare> lqw = new LambdaQueryWrapper<>();
lqw.eq(AppDeviceShare::getDeviceId, bo.getDeviceId());
lqw.eq(AppDeviceShare::getPhonenumber, bo.getPhonenumber());
Long count = appDeviceShareMapper.selectCount(lqw);
if(count>0){
UpdateWrapper<AppDeviceShare> uw = new UpdateWrapper<>();
uw.eq("device_id", bo.getDeviceId());
uw.eq("phonenumber", bo.getPhonenumber());
uw.set("permission", bo.getPermission());
uw.set("update_by", userId);
uw.set("update_time", new Date());
return appDeviceShareMapper.update(uw);
}else {
AppDeviceShare appDeviceShare = new AppDeviceShare();
appDeviceShare.setDeviceId(bo.getDeviceId());
appDeviceShare.setPhonenumber(bo.getPhonenumber());
appDeviceShare.setPermission(bo.getPermission());
appDeviceShare.setCreateBy(userId);
return appDeviceShareMapper.insert(appDeviceShare);
}
}
public int remove(Long[] ids) {
return appDeviceShareMapper.deleteByIds(Arrays.asList(ids));
}
}

View File

@ -1,5 +1,9 @@
package com.fuyuanshen.global.mqtt.base;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import java.util.Comparator;
@ -11,7 +15,10 @@ import java.util.List;
*/
@Component
public class MqttRuleEngine {
@Autowired
@Qualifier("threadPoolTaskExecutor")
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
private final LinkedHashMap<String, MqttMessageRule> rulesMap = new LinkedHashMap<>();
public MqttRuleEngine(List<MqttMessageRule> rules) {
@ -30,7 +37,7 @@ public class MqttRuleEngine {
int commandType = context.getCommandType();
MqttMessageRule mqttMessageRule = rulesMap.get("Light_"+commandType);
if (mqttMessageRule != null) {
mqttMessageRule.execute(context);
threadPoolTaskExecutor.execute(() -> mqttMessageRule.execute(context));
return true;
}

View File

@ -35,6 +35,12 @@ public class LightingCommandTypeConstants {
* 设备发送消息
*/
public static final String SEND_MESSAGE = "Light_6";
/**
* 报警模式
*/
public static final String ALARM_MESSAGE = "Light_7";
/**
* 定位数据 (Location Data)

View File

@ -0,0 +1,38 @@
package com.fuyuanshen.global.mqtt.listener;
import com.fuyuanshen.common.redis.utils.RedisUtils;
import com.fuyuanshen.global.mqtt.listener.domain.FunctionAccessStatus;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_KEY;
import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_TIMEOUT_KEY;
@Component
@Slf4j
public class RedisKeyExpirationListener implements MessageListener {
@Override
public void onMessage(Message message, byte[] pattern) {
String expiredKey = new String(message.getBody());
if (expiredKey.startsWith(FUNCTION_ACCESS_KEY)) {
String element = expiredKey.substring(FUNCTION_ACCESS_KEY.length());
handleFunctionAccessExpired(element);
}
}
/**
* 访问key过期事件
* @param element 批次ID
*/
private void handleFunctionAccessExpired(String element) {
RedisUtils.setCacheObject(FUNCTION_ACCESS_TIMEOUT_KEY + element, FunctionAccessStatus.TIMEOUT.getCode(), Duration.ofSeconds(30L));
}
}

View File

@ -0,0 +1,25 @@
package com.fuyuanshen.global.mqtt.listener.config;
import com.fuyuanshen.global.mqtt.listener.RedisKeyExpirationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_KEY;
@Configuration
public class RedisListenerConfig {
@Bean
RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory connectionFactory,
RedisKeyExpirationListener redisKeyExpirationListener) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
// 监听过期事件
container.addMessageListener(redisKeyExpirationListener, new PatternTopic("__keyevent@*__:expired"));
return container;
}
}

View File

@ -0,0 +1,96 @@
package com.fuyuanshen.global.mqtt.listener.domain;
import lombok.Data;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
/**
* 功能访问状态对象
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class FunctionAccessResult {
/**
* 状态码
*/
private String status;
/**
* 消息描述
*/
private String message;
/**
* 时间戳
*/
private Long timestamp;
/**
* 设备IMEI可选
*/
private String deviceImei;
/**
* 批次ID可选
*/
private String batchId;
public FunctionAccessResult(String status, String message) {
this.status = status;
this.message = message;
this.timestamp = System.currentTimeMillis();
}
/**
* 创建失败状态对象
* @param message 消息
* @return FunctionAccessResult
*/
public static FunctionAccessResult failed(String message) {
return new FunctionAccessResult("FAILED", message);
}
/**
* 创建成功状态对象
* @param message 消息
* @return FunctionAccessResult
*/
public static FunctionAccessResult ok(String message) {
return new FunctionAccessResult("OK", message);
}
/**
* 创建超时状态对象
* @param message 消息
* @return FunctionAccessResult
*/
public static FunctionAccessResult timeout(String message) {
return new FunctionAccessResult("TIMEOUT", message);
}
/**
* 判断是否为失败状态
* @return boolean
*/
public boolean isFailed() {
return "FAILED".equals(this.status);
}
/**
* 判断是否为成功状态
* @return boolean
*/
public boolean isOk() {
return "OK".equals(this.status);
}
/**
* 判断是否为超时状态
* @return boolean
*/
public boolean isTimeout() {
return "TIMEOUT".equals(this.status);
}
}

View File

@ -0,0 +1,62 @@
package com.fuyuanshen.global.mqtt.listener.domain;
/**
* 功能访问状态枚举
*/
public enum FunctionAccessStatus {
/**
* 失败状态
*/
FAILED("FAILED", "失败"),
/**
* 成功状态
*/
OK("OK", "成功"),
/**
* 激活中状态
*/
ACTIVE("ACTIVE", "处理中"),
/**
* 超时状态
*/
TIMEOUT("TIMEOUT", "超时");
private final String code;
private final String description;
FunctionAccessStatus(String code, String description) {
this.code = code;
this.description = description;
}
public String getCode() {
return code;
}
public String getDescription() {
return description;
}
/**
* 根据代码获取状态枚举
* @param code 状态代码
* @return 对应的状态枚举
*/
public static FunctionAccessStatus fromCode(String code) {
for (FunctionAccessStatus status : FunctionAccessStatus.values()) {
if (status.getCode().equals(code)) {
return status;
}
}
throw new IllegalArgumentException("未知的状态代码: " + code);
}
@Override
public String toString() {
return code;
}
}

View File

@ -49,7 +49,7 @@ public class ReceiverMessageHandler implements MessageHandler {
if(StringUtils.isNotBlank(deviceImei)){
//在线状态
String deviceOnlineStatusRedisKey = GlobalConstants.GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX+ deviceImei + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX ;
RedisUtils.setCacheObject(deviceOnlineStatusRedisKey, "1", Duration.ofSeconds(60*15));
RedisUtils.setCacheObject(deviceOnlineStatusRedisKey, "1", Duration.ofSeconds(62));
}
String state = payloadDict.getStr("state");

View File

@ -7,14 +7,17 @@ import com.fuyuanshen.global.mqtt.base.MqttMessageRule;
import com.fuyuanshen.global.mqtt.base.MqttRuleContext;
import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants;
import com.fuyuanshen.global.mqtt.constants.LightingCommandTypeConstants;
import com.fuyuanshen.global.mqtt.listener.domain.FunctionAccessStatus;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_KEY;
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_STATUS_KEY_PREFIX;
/**
@ -44,16 +47,16 @@ public class BjqActiveReportingDeviceDataRule implements MqttMessageRule {
String chargeState = convertArr[4].toString();
String batteryRemainingTime = convertArr[5].toString();
// 异步发送设备状态和位置信息到Redis
// 发送设备状态和位置信息到Redis
asyncSendDeviceDataToRedisWithFuture(context.getDeviceImei(), mainLightMode, laserLightMode,
batteryPercentage, chargeState, batteryRemainingTime);
} catch (Exception e) {
log.error("处理定位数据命令时出错", e);
log.error("处理上报数据命令时出错", e);
}
}
/**
* 异步发送设备状态信息和位置信息到Redis使用CompletableFuture
* 发送设备状态信息和位置信息到Redis
*
* @param deviceImei 设备IMEI
* @param mainLightMode 主灯档位

View File

@ -7,12 +7,15 @@ import com.fuyuanshen.global.mqtt.base.MqttMessageRule;
import com.fuyuanshen.global.mqtt.base.MqttRuleContext;
import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants;
import com.fuyuanshen.global.mqtt.constants.LightingCommandTypeConstants;
import com.fuyuanshen.global.mqtt.listener.domain.FunctionAccessStatus;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.util.concurrent.CompletableFuture;
import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_KEY;
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.*;
/**
@ -25,45 +28,29 @@ public class BjqAlarmRule implements MqttMessageRule {
@Override
public String getCommandType() {
return LightingCommandTypeConstants.LIGHT_MODE;
return LightingCommandTypeConstants.ALARM_MESSAGE;
}
@Override
public void execute(MqttRuleContext context) {
String functionAccess = FUNCTION_ACCESS_KEY + context.getDeviceImei();
try {
Object[] convertArr = context.getConvertArr();
String mainLightMode = convertArr[1].toString();
if(StringUtils.isNotBlank(mainLightMode)){
// 异步发送设备状态和位置信息到Redis
asyncSendDeviceDataToRedisWithFuture(context.getDeviceImei(),mainLightMode);
}
String convertValue = convertArr[1].toString();
if(StringUtils.isNotBlank(convertValue)){
// 将设备状态信息存储到Redis
String deviceRedisKey = GlobalConstants.GLOBAL_REDIS_KEY+DeviceRedisKeyConstants.DEVICE_KEY_PREFIX + context.getDeviceImei() + DEVICE_ALARM_KEY_PREFIX;
// 存储到Redis
RedisUtils.setCacheObject(deviceRedisKey, convertValue);
}
RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.OK.getCode(), Duration.ofSeconds(20));
} catch (Exception e) {
log.error("处理灯光模式命令时出错", e);
log.error("处理告警命令时出错", e);
RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.FAILED.getCode(), Duration.ofSeconds(20));
}
}
/**
* 异步发送设备状态信息和位置信息到Redis使用CompletableFuture
*
* @param deviceImei 设备IMEI
*/
public void asyncSendDeviceDataToRedisWithFuture(String deviceImei,Object convertValue) {
CompletableFuture.runAsync(() -> {
try {
// 将设备状态信息存储到Redis中
String deviceRedisKey = GlobalConstants.GLOBAL_REDIS_KEY+DeviceRedisKeyConstants.DEVICE_KEY_PREFIX + deviceImei + DEVICE_ALARM_KEY_PREFIX;
// 存储到Redis
RedisUtils.setCacheObject(deviceRedisKey, convertValue.toString());
} catch (Exception e) {
log.error("异步发送设备信息到Redis时出错: device={}, error={}", deviceImei, e.getMessage(), e);
}
});
}
}

View File

@ -9,21 +9,24 @@ import com.fuyuanshen.global.mqtt.base.MqttRuleContext;
import com.fuyuanshen.global.mqtt.config.MqttGateway;
import com.fuyuanshen.global.mqtt.constants.LightingCommandTypeConstants;
import com.fuyuanshen.global.mqtt.constants.MqttConstants;
import com.fuyuanshen.global.mqtt.listener.domain.FunctionAccessStatus;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
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.common.core.utils.ImageToCArrayConverter.convertHexToDecimal;
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_BOOT_LOGO_KEY_PREFIX;
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX;
/**
* 人员信息命令处理
* 上传开机图片命令处理
*/
@Component
@RequiredArgsConstructor
@ -39,9 +42,11 @@ public class BjqBootLogoRule implements MqttMessageRule {
@Override
public void execute(MqttRuleContext context) {
String functionAccess = FUNCTION_ACCESS_KEY + context.getDeviceImei();
try {
Byte val2 = (Byte) context.getConvertArr()[1];
if (val2 == 100) {
RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.OK.getCode(), Duration.ofSeconds(20));
return;
}
@ -53,7 +58,6 @@ public class BjqBootLogoRule implements MqttMessageRule {
byte[] arr = ImageToCArrayConverter.convertStringToByteArray(data);
byte[] specificChunk = ImageToCArrayConverter.getChunk(arr, (val2 - 1), 512);
log.info("第{}块数据大小: {} 字节", val2, specificChunk.length);
// System.out.println("第" + val2 + "块数据: " + Arrays.toString(specificChunk));
ArrayList<Integer> intData = new ArrayList<>();
intData.add(3);
@ -71,8 +75,10 @@ public class BjqBootLogoRule implements MqttMessageRule {
log.info("发送开机LOGO点阵数据到设备消息=>topic:{},payload:{}",
MqttConstants.GLOBAL_PUB_KEY + context.getDeviceImei(),
JsonUtils.toJsonString(map));
} catch (Exception e) {
log.error("处理开机LOGO时出错", e);
RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.FAILED.getCode(), Duration.ofSeconds(20));
}
}
}

View File

@ -0,0 +1,80 @@
package com.fuyuanshen.global.mqtt.rule.bjq;
import com.fuyuanshen.common.core.constant.GlobalConstants;
import com.fuyuanshen.common.core.utils.StringUtils;
import com.fuyuanshen.common.redis.utils.RedisUtils;
import com.fuyuanshen.global.mqtt.base.MqttMessageRule;
import com.fuyuanshen.global.mqtt.base.MqttRuleContext;
import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants;
import com.fuyuanshen.global.mqtt.constants.LightingCommandTypeConstants;
import com.fuyuanshen.global.mqtt.listener.domain.FunctionAccessStatus;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.time.Duration;
import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_KEY;
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX;
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_LIGHT_MODE_KEY_PREFIX;
/**
* 灯光模式订阅设备回传消息
*/
@Component
@RequiredArgsConstructor
@Slf4j
public class BjqLaserModeSettingsRule implements MqttMessageRule {
@Override
public String getCommandType() {
return LightingCommandTypeConstants.LASER_LIGHT;
}
@Override
public void execute(MqttRuleContext context) {
String functionAccess = FUNCTION_ACCESS_KEY + context.getDeviceImei();
try {
Object[] convertArr = context.getConvertArr();
String mainLightMode = convertArr[1].toString();
if(StringUtils.isNotBlank(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));
}
// 发送设备状态和位置信息到Redis
syncSendDeviceDataToRedisWithFuture(context.getDeviceImei(),mainLightMode);
}
RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.OK.getCode(), Duration.ofSeconds(30));
} catch (Exception e) {
log.error("处理激光模式命令时出错", e);
RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.FAILED.getCode(), Duration.ofSeconds(30));
}
}
/**
* 发送设备状态信息和位置信息到Redis
*
* @param deviceImei 设备IMEI
*/
public void syncSendDeviceDataToRedisWithFuture(String deviceImei,Object convertValue) {
// CompletableFuture.runAsync(() -> {
//
// });
try {
// 将设备状态信息存储到Redis中
String deviceRedisKey = GlobalConstants.GLOBAL_REDIS_KEY+DeviceRedisKeyConstants.DEVICE_KEY_PREFIX + deviceImei + DEVICE_LIGHT_MODE_KEY_PREFIX;
// 存储到Redis
RedisUtils.setCacheObject(deviceRedisKey, convertValue.toString());
} catch (Exception e) {
log.error("异步发送设备信息到Redis时出错: device={}, error={}", deviceImei, e.getMessage(), e);
}
}
}

View File

@ -0,0 +1,51 @@
package com.fuyuanshen.global.mqtt.rule.bjq;
import com.fuyuanshen.common.core.constant.GlobalConstants;
import com.fuyuanshen.common.core.utils.StringUtils;
import com.fuyuanshen.common.redis.utils.RedisUtils;
import com.fuyuanshen.global.mqtt.base.MqttMessageRule;
import com.fuyuanshen.global.mqtt.base.MqttRuleContext;
import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants;
import com.fuyuanshen.global.mqtt.constants.LightingCommandTypeConstants;
import com.fuyuanshen.global.mqtt.listener.domain.FunctionAccessStatus;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.time.Duration;
import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_KEY;
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.*;
/**
* 灯光模式订阅设备回传消息
*/
@Component
@RequiredArgsConstructor
@Slf4j
public class BjqLightBrightnessRule implements MqttMessageRule {
@Override
public String getCommandType() {
return LightingCommandTypeConstants.MAIN_LIGHT_BRIGHTNESS;
}
@Override
public void execute(MqttRuleContext context) {
String functionAccess = FUNCTION_ACCESS_KEY + context.getDeviceImei();
try {
Object[] convertArr = context.getConvertArr();
String convertValue = convertArr[1].toString();
// 将设备状态信息存储到Redis中
String deviceRedisKey = GlobalConstants.GLOBAL_REDIS_KEY+DeviceRedisKeyConstants.DEVICE_KEY_PREFIX + context.getDeviceImei() + DEVICE_LIGHT_BRIGHTNESS_KEY_PREFIX;
// 存储到Redis
RedisUtils.setCacheObject(deviceRedisKey, convertValue);
RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.OK.getCode(), Duration.ofSeconds(20));
} catch (Exception e) {
log.error("处理灯光亮度命令时出错", e);
RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.FAILED.getCode(), Duration.ofSeconds(20));
}
}
}

View File

@ -13,16 +13,19 @@ import com.fuyuanshen.global.mqtt.config.MqttGateway;
import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants;
import com.fuyuanshen.global.mqtt.constants.LightingCommandTypeConstants;
import com.fuyuanshen.global.mqtt.constants.MqttConstants;
import com.fuyuanshen.global.mqtt.listener.domain.FunctionAccessStatus;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_KEY;
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX;
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_LOCATION_KEY_PREFIX;
@ -44,6 +47,7 @@ public class BjqLocationDataRule implements MqttMessageRule {
@Override
public void execute(MqttRuleContext context) {
String functionAccess = FUNCTION_ACCESS_KEY + context.getDeviceImei();
try {
Object[] convertArr = context.getConvertArr();
// Latitude, longitude
@ -58,8 +62,10 @@ public class BjqLocationDataRule implements MqttMessageRule {
log.info("发送定位数据到设备=>topic:{},payload:{}",
MqttConstants.GLOBAL_PUB_KEY + context.getDeviceImei(),
JsonUtils.toJsonString(map));
RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.OK.getCode(), Duration.ofSeconds(20));
} catch (Exception e) {
log.error("处理定位数据命令时出错", e);
RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.FAILED.getCode(), Duration.ofSeconds(20));
}
}

View File

@ -7,6 +7,7 @@ import com.fuyuanshen.global.mqtt.base.MqttMessageRule;
import com.fuyuanshen.global.mqtt.base.MqttRuleContext;
import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants;
import com.fuyuanshen.global.mqtt.constants.LightingCommandTypeConstants;
import com.fuyuanshen.global.mqtt.listener.domain.FunctionAccessStatus;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@ -14,8 +15,8 @@ import org.springframework.stereotype.Component;
import java.time.Duration;
import java.util.concurrent.CompletableFuture;
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX;
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_LIGHT_MODE_KEY_PREFIX;
import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_KEY;
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.*;
/**
* 灯光模式订阅设备回传消息
@ -32,42 +33,50 @@ public class BjqModeRule implements MqttMessageRule {
@Override
public void execute(MqttRuleContext context) {
String functionAccess = FUNCTION_ACCESS_KEY + context.getDeviceImei();
try {
Object[] convertArr = context.getConvertArr();
String mainLightMode = convertArr[1].toString();
String batteryRemainingTime = convertArr[2].toString();
if(StringUtils.isNotBlank(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));
}
// 异步发送设备状态和位置信息到Redis
asyncSendDeviceDataToRedisWithFuture(context.getDeviceImei(),mainLightMode);
}
// 发送设备状态和位置信息到Redis
syncSendDeviceDataToRedisWithFuture(context.getDeviceImei(),mainLightMode);
String deviceRedisKey = GlobalConstants.GLOBAL_REDIS_KEY+DeviceRedisKeyConstants.DEVICE_KEY_PREFIX + context.getDeviceImei() + DEVICE_LIGHT_BRIGHTNESS_KEY_PREFIX;
// 存储到Redis
RedisUtils.setCacheObject(deviceRedisKey, batteryRemainingTime);
}
RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.OK.getCode(), Duration.ofSeconds(20));
} catch (Exception e) {
log.error("处理灯光模式命令时出错", e);
RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.FAILED.getCode(), Duration.ofSeconds(20));
}
}
/**
* 异步发送设备状态信息和位置信息到Redis使用CompletableFuture
* 发送设备状态信息和位置信息到Redis
*
* @param deviceImei 设备IMEI
*/
public void asyncSendDeviceDataToRedisWithFuture(String deviceImei,Object convertValue) {
CompletableFuture.runAsync(() -> {
try {
// 将设备状态信息存储到Redis中
String deviceRedisKey = GlobalConstants.GLOBAL_REDIS_KEY+DeviceRedisKeyConstants.DEVICE_KEY_PREFIX + deviceImei + DEVICE_LIGHT_MODE_KEY_PREFIX;
public void syncSendDeviceDataToRedisWithFuture(String deviceImei,Object convertValue) {
// CompletableFuture.runAsync(() -> {
//
// });
try {
// 将设备状态信息存储到Redis中
String deviceRedisKey = GlobalConstants.GLOBAL_REDIS_KEY+DeviceRedisKeyConstants.DEVICE_KEY_PREFIX + deviceImei + DEVICE_LIGHT_MODE_KEY_PREFIX;
// 存储到Redis
RedisUtils.setCacheObject(deviceRedisKey, convertValue.toString());
// 存储到Redis
RedisUtils.setCacheObject(deviceRedisKey, convertValue.toString());
} catch (Exception e) {
log.error("异步发送设备信息到Redis时出错: device={}, error={}", deviceImei, e.getMessage(), e);
}
});
} catch (Exception e) {
log.error("异步发送设备信息到Redis时出错: device={}, error={}", deviceImei, e.getMessage(), e);
}
}

View File

@ -0,0 +1,58 @@
package com.fuyuanshen.global.mqtt.rule.bjq;
import com.alibaba.fastjson2.JSONObject;
import com.fuyuanshen.common.core.constant.GlobalConstants;
import com.fuyuanshen.common.core.utils.StringUtils;
import com.fuyuanshen.common.json.utils.JsonUtils;
import com.fuyuanshen.common.redis.utils.RedisUtils;
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.config.MqttGateway;
import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants;
import com.fuyuanshen.global.mqtt.constants.LightingCommandTypeConstants;
import com.fuyuanshen.global.mqtt.constants.MqttConstants;
import com.fuyuanshen.global.mqtt.listener.domain.FunctionAccessStatus;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_KEY;
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.*;
/**
* 定位数据命令处理
*/
@Component
@RequiredArgsConstructor
@Slf4j
public class BjqPersonnelInfoDataRule implements MqttMessageRule {
private final MqttGateway mqttGateway;
@Override
public String getCommandType() {
return LightingCommandTypeConstants.PERSONNEL_INFO;
}
@Override
public void execute(MqttRuleContext context) {
String functionAccess = FUNCTION_ACCESS_KEY + context.getDeviceImei();
try {
Object[] convertArr = context.getConvertArr();
RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.OK.getCode(), Duration.ofSeconds(30));
} catch (Exception e) {
log.error("处理定位数据命令时出错", e);
RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.FAILED.getCode(), Duration.ofSeconds(30));
}
}
}

View File

@ -9,14 +9,17 @@ import com.fuyuanshen.global.mqtt.base.MqttRuleContext;
import com.fuyuanshen.global.mqtt.config.MqttGateway;
import com.fuyuanshen.global.mqtt.constants.LightingCommandTypeConstants;
import com.fuyuanshen.global.mqtt.constants.MqttConstants;
import com.fuyuanshen.global.mqtt.listener.domain.FunctionAccessStatus;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
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.common.core.utils.ImageToCArrayConverter.convertHexToDecimal;
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX;
@ -38,9 +41,11 @@ public class BjqSendMessageRule implements MqttMessageRule {
@Override
public void execute(MqttRuleContext context) {
String functionAccess = FUNCTION_ACCESS_KEY + context.getDeviceImei();
try {
Byte val2 = (Byte) context.getConvertArr()[1];
if (val2 == 100) {
RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.OK.getCode(), Duration.ofSeconds(20));
return;
}
@ -70,8 +75,10 @@ public class BjqSendMessageRule implements MqttMessageRule {
log.info("发送设备信息数据到设备消息=>topic:{},payload:{}",
MqttConstants.GLOBAL_PUB_KEY + context.getDeviceImei(),
JsonUtils.toJsonString(map));
} catch (Exception e) {
log.error("处理发送设备信息时出错", e);
RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.FAILED.getCode(), Duration.ofSeconds(20));
}
}
}

View File

@ -0,0 +1,64 @@
package com.fuyuanshen.web;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class ChineseVCardGenerator {
public static void main(String[] args) {
// 定义江西上饶的134号段共22个
String[] prefixes = {
"1340703", "1340793", "1342650", "1342651", "1342663",
"1342664", "1342665", "1343703", "1343793", "1347901",
"1347902", "1347903", "1347930", "1347931", "1347932",
"1347933", "1347934", "1347935", "1347936", "1347937",
"1347938", "1347939"
};
// 创建.vcf文件
String filename = "上饶联系人.vcf";
try (BufferedWriter writer = new BufferedWriter(new FileWriter(filename, true))) {
// 添加文件头信息(包含中文)
writer.write("BEGIN:VCARD");
writer.newLine();
writer.write("VERSION:3.0");
writer.newLine();
writer.write("X-GENERATOR:Java VCard Generator");
writer.newLine();
writer.write("PRODID:-//Apple Inc.//iPhone OS 15.6//EN");
writer.newLine();
writer.newLine();
// 生成所有联系人
int count = 0;
for (String prefix : prefixes) {
for (int i = 0; i < 100; i++) {
String middle = String.format("%02d", i);
String phoneNumber = prefix + middle + "51";
// 写入单个联系人(使用中文)
writer.write("FN;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:用户" + (count + 1)); // 中文姓名
writer.newLine();
writer.write("TEL;TYPE=CELL;CHARSET=UTF-8:" + phoneNumber); // 手机号
writer.newLine();
writer.write("NOTE;CHARSET=UTF-8:生成时间 " + new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
writer.newLine();
writer.write("END:VCARD");
writer.newLine();
writer.newLine();
count++;
}
}
System.out.println("成功生成 " + count + " 个中文联系人");
System.out.println("文件已保存为: " + filename);
} catch (IOException e) {
System.err.println("文件写入错误: " + e.getMessage());
}
}
}

View File

@ -0,0 +1,41 @@
package com.fuyuanshen.web;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class PhoneNumberGenerator {
public static void main(String[] args) {
// 定义江西上饶的134号段共22个
String[] prefixes = {
"1340703", "1340793", "1342650", "1342651", "1342663",
"1342664", "1342665", "1343703", "1343793", "1347901",
"1347902", "1347903", "1347930", "1347931", "1347932",
"1347933", "1347934", "1347935", "1347936", "1347937",
"1347938", "1347939"
};
// 输出到控制台
System.out.println("江西上饶134号段完整手机号码共2200个");
for (String prefix : prefixes) {
for (int i = 0; i < 100; i++) {
String middle = String.format("%02d", i); // 生成00-99的中间数字
System.out.println(prefix + middle + "51");
}
}
// 同时输出到文件(可选)
try (BufferedWriter writer = new BufferedWriter(new FileWriter("shangrao_phones.txt"))) {
for (String prefix : prefixes) {
for (int i = 0; i < 100; i++) {
String middle = String.format("%02d", i);
writer.write(prefix + middle + "51");
writer.newLine();
}
}
System.out.println("\n同时已保存到文件shangrao_phones.txt");
} catch (IOException e) {
System.err.println("文件写入错误:" + e.getMessage());
}
}
}

View File

@ -0,0 +1,59 @@
package com.fuyuanshen.web;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class VCardGenerator {
public static void main(String[] args) {
// 定义江西上饶的134号段共22个
String[] prefixes = {
"1340703", "1340793", "1342650", "1342651", "1342663",
"1342664", "1342665", "1343703", "1343793", "1347901",
"1347902", "1347903", "1347930", "1347931", "1347932",
"1347933", "1347934", "1347935", "1347936", "1347937",
"1347938", "1347939"
};
// 创建.vcf文件
String filename = "shangrao_contacts.vcf";
try (BufferedWriter writer = new BufferedWriter(new FileWriter(filename))) {
// 写入文件头
writer.write("BEGIN:VCARD");
writer.newLine();
writer.write("VERSION:3.0");
writer.newLine();
writer.newLine();
// 生成所有联系人
int count = 0;
for (String prefix : prefixes) {
for (int i = 0; i < 100; i++) {
String middle = String.format("%02d", i);
String phoneNumber = prefix + middle + "51";
// 写入单个联系人
writer.write("FN:" + phoneNumber); // 姓名字段使用手机号
writer.newLine();
writer.write("TEL;TYPE=CELL:" + phoneNumber); // 电话字段使用手机号
writer.newLine();
writer.write("END:VCARD");
writer.newLine();
writer.newLine();
count++;
}
}
// 写入文件尾
writer.write("END:VCARD");
System.out.println("成功生成 " + count + " 个联系人");
System.out.println("文件已保存为: " + filename);
} catch (IOException e) {
System.err.println("文件写入错误: " + e.getMessage());
}
}
}

View File

@ -1,61 +1,61 @@
package com.fuyuanshen.web.config;
import cn.hutool.core.lang.UUID;
import com.fuyuanshen.global.mqtt.config.MqttPropertiesConfig;
import com.fuyuanshen.web.handler.mqtt.DeviceReceiverMessageHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.core.MessageProducer;
import org.springframework.integration.mqtt.core.MqttPahoClientFactory;
import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter;
import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;
/**
* @author: 默苍璃
* @date: 2025-08-0110:46
*/
@Configuration
public class CustomMqttInboundConfiguration {
@Autowired
private MqttPropertiesConfig mqttPropertiesConfig;
@Autowired
private MqttPahoClientFactory mqttPahoClientFactory;
@Autowired
private DeviceReceiverMessageHandler deviceReceiverMessageHandler;
@Bean
public MessageChannel customMqttChannel(){
return new DirectChannel();
}
@Bean
public MessageProducer customMessageProducer(){
String clientId = "custom_client_" + UUID.fastUUID();
MqttPahoMessageDrivenChannelAdapter adapter = new MqttPahoMessageDrivenChannelAdapter(
mqttPropertiesConfig.getUrl(),
clientId,
mqttPahoClientFactory,
"A/#", "B/#" // 直接指定这两个主题
);
adapter.setQos(1);
adapter.setConverter(new DefaultPahoMessageConverter());
adapter.setOutputChannel(customMqttChannel());
return adapter;
}
@Bean
@ServiceActivator(inputChannel = "customMqttChannel")
public MessageHandler customMessageHandler(){
return deviceReceiverMessageHandler;
}
}
//package com.fuyuanshen.web.config;
//
//import cn.hutool.core.lang.UUID;
//import com.fuyuanshen.global.mqtt.config.MqttPropertiesConfig;
//import com.fuyuanshen.web.handler.mqtt.DeviceReceiverMessageHandler;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.context.annotation.Bean;
//import org.springframework.context.annotation.Configuration;
//import org.springframework.integration.annotation.ServiceActivator;
//import org.springframework.integration.channel.DirectChannel;
//import org.springframework.integration.core.MessageProducer;
//import org.springframework.integration.mqtt.core.MqttPahoClientFactory;
//import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter;
//import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter;
//import org.springframework.messaging.MessageChannel;
//import org.springframework.messaging.MessageHandler;
//
///**
// * @author: 默苍璃
// * @date: 2025-08-0110:46
// */
//@Configuration
//public class CustomMqttInboundConfiguration {
//
// @Autowired
// private MqttPropertiesConfig mqttPropertiesConfig;
// @Autowired
// private MqttPahoClientFactory mqttPahoClientFactory;
// @Autowired
// private DeviceReceiverMessageHandler deviceReceiverMessageHandler;
//
//
// @Bean
// public MessageChannel customMqttChannel(){
// return new DirectChannel();
// }
//
//
// @Bean
// public MessageProducer customMessageProducer(){
// String clientId = "custom_client_" + UUID.fastUUID();
// MqttPahoMessageDrivenChannelAdapter adapter = new MqttPahoMessageDrivenChannelAdapter(
// mqttPropertiesConfig.getUrl(),
// clientId,
// mqttPahoClientFactory,
// "A/#", "B/#" // 直接指定这两个主题
// );
// adapter.setQos(1);
// adapter.setConverter(new DefaultPahoMessageConverter());
// adapter.setOutputChannel(customMqttChannel());
// return adapter;
// }
//
//
// @Bean
// @ServiceActivator(inputChannel = "customMqttChannel")
// public MessageHandler customMessageHandler(){
// return deviceReceiverMessageHandler;
// }
//
//}

View File

@ -1,14 +1,16 @@
package com.fuyuanshen.app.controller.device;
package com.fuyuanshen.web.controller.device;
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.app.service.AppDeviceBizService;
import com.fuyuanshen.common.core.domain.R;
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.web.core.BaseController;
import com.fuyuanshen.equipment.domain.dto.AppDeviceSendMsgBo;
import com.fuyuanshen.web.service.device.DeviceBJQBizService;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
@ -16,16 +18,15 @@ import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
/**
* HBY210设备控制类
* BJQ6170设备控制类
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/app/hby/device")
public class AppDeviceHBYController extends BaseController {
private final AppDeviceBizService appDeviceService;
@RequestMapping("/api/bjq/device")
public class DeviceBJQController extends BaseController {
private final DeviceBJQBizService appDeviceService;
/**
* 获取设备详细信息
@ -42,6 +43,7 @@ public class AppDeviceHBYController extends BaseController {
* 人员信息登记
*/
@PostMapping(value = "/registerPersonInfo")
// @FunctionAccessAnnotation("registerPersonInfo")
public R<Void> registerPersonInfo(@Validated(AddGroup.class) @RequestBody AppPersonnelInfoBo bo) {
return toAjax(appDeviceService.registerPersonInfo(bo));
}
@ -50,14 +52,25 @@ public class AppDeviceHBYController extends BaseController {
* 发送信息
*/
@PostMapping(value = "/sendMessage")
@FunctionAccessBatcAnnotation(value = "sendMessage", timeOut = 30, batchMaxTimeOut = 40)
public R<Void> sendMessage(@RequestBody AppDeviceSendMsgBo bo) {
return toAjax(appDeviceService.sendMessage(bo));
}
/**
* 发送报警信息
*/
@PostMapping(value = "/sendAlarmMessage")
@FunctionAccessBatcAnnotation(value = "sendAlarmMessage", timeOut = 5, batchMaxTimeOut = 10)
public R<Void> sendAlarmMessage(@RequestBody AppDeviceSendMsgBo bo) {
return toAjax(appDeviceService.sendAlarmMessage(bo));
}
/**
* 上传设备logo图片
*/
@PostMapping("/uploadLogo")
@FunctionAccessAnnotation("uploadLogo")
public R<Void> upload(@Validated @ModelAttribute AppDeviceLogoUploadDto bo) {
MultipartFile file = bo.getFile();
@ -73,6 +86,7 @@ public class AppDeviceHBYController extends BaseController {
* 灯光模式
* 0关灯1强光模式2弱光模式, 3爆闪模式, 4泛光模式
*/
// @FunctionAccessAnnotation("lightModeSettings")
@PostMapping("/lightModeSettings")
public R<Void> lightModeSettings(@RequestBody DeviceInstructDto params) {
// params JSONObject
@ -84,6 +98,7 @@ public class AppDeviceHBYController extends BaseController {
* 灯光亮度设置
*
*/
// @FunctionAccessAnnotation("lightBrightnessSettings")
@PostMapping("/lightBrightnessSettings")
public R<Void> lightBrightnessSettings(@RequestBody DeviceInstructDto params) {
appDeviceService.lightBrightnessSettings(params);
@ -95,18 +110,10 @@ public class AppDeviceHBYController extends BaseController {
*
*/
@PostMapping("/laserModeSettings")
// @FunctionAccessAnnotation("laserModeSettings")
public R<Void> laserModeSettings(@RequestBody DeviceInstructDto params) {
appDeviceService.laserModeSettings(params);
return R.ok();
}
/**
* 地图逆解析
*
*/
@PostMapping("/mapReverseGeocoding")
public R<Void> mapReverseGeocoding(@RequestBody DeviceInstructDto params) {
String mapJson = appDeviceService.mapReverseGeocoding(params);
return R.ok(mapJson);
}
}

View File

@ -0,0 +1,210 @@
package com.fuyuanshen.web.controller.device;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author: 默苍璃
* @date: 2025-08-0810:40
*/
@Slf4j
@Tag(name = "web后台:设备控制中心", description = "web后台:设备控制中心")
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/device/controlCenter")
public class DeviceControlCenterController {
/**
* 获取设备基本信息
* @param deviceId 设备ID
* @return 设备基本信息
*/
@GetMapping("/info/{deviceId}")
public ResponseEntity<Map<String, Object>> getDeviceInfo(@PathVariable String deviceId) {
// 实际应用中这里会从数据库查询设备信息
Map<String, Object> deviceInfo = new HashMap<>();
deviceInfo.put("deviceName", "6170零零一");
deviceInfo.put("deviceModel", "BJQ6170");
deviceInfo.put("deviceId", deviceId);
deviceInfo.put("status", "在线");
deviceInfo.put("batteryLevel", 85);
return ResponseEntity.ok(deviceInfo);
}
/**
* 设置灯光模式
* @param lightModeRequest 灯光模式请求
* @return 操作结果
*/
@PostMapping("/light-mode")
public ResponseEntity<Map<String, Object>> setLightMode(@RequestBody LightModeRequest lightModeRequest) {
// 实际应用中这里会控制设备灯光
Map<String, Object> response = new HashMap<>();
response.put("code", 200);
// response.put("message", "灯光模式已设置为: " + lightModeRequest.getMode());
// response.put("deviceId", lightModeRequest.getDeviceId());
// response.put("mode", lightModeRequest.getMode());
return ResponseEntity.ok(response);
}
/**
* 更新人员信息
* @param personInfo 人员信息
* @return 操作结果
*/
@PostMapping("/person-info")
public ResponseEntity<Map<String, Object>> updatePersonInfo(@RequestBody PersonInfo personInfo) {
// 实际应用中这里会更新数据库
Map<String, Object> response = new HashMap<>();
response.put("code", 200);
response.put("message", "人员信息已更新");
// response.put("unit", personInfo.getUnit());
// response.put("position", personInfo.getPosition());
return ResponseEntity.ok(response);
}
/**
* 管理开机画面内容
* @param bootScreenRequest 开机画面请求
* @return 操作结果
*/
@PostMapping("/boot-screen")
public ResponseEntity<Map<String, Object>> manageBootScreen(@RequestBody BootScreenRequest bootScreenRequest) {
// 实际应用中这里会更新设备开机画面
Map<String, Object> response = new HashMap<>();
response.put("code", 200);
response.put("message", "开机画面内容已更新");
// response.put("deviceId", bootScreenRequest.getDeviceId());
// response.put("screens", bootScreenRequest.getScreens());
return ResponseEntity.ok(response);
}
/**
* 设置灯光亮度
* @param brightnessRequest 亮度请求
* @return 操作结果
*/
@PostMapping("/brightness")
public ResponseEntity<Map<String, Object>> setBrightness(@RequestBody BrightnessRequest brightnessRequest) {
// 实际应用中这里会控制设备亮度
Map<String, Object> response = new HashMap<>();
response.put("code", 200);
// response.put("message", "灯光亮度已设置为: " + brightnessRequest.getBrightness() + "%");
// response.put("deviceId", brightnessRequest.getDeviceId());
// response.put("brightness", brightnessRequest.getBrightness());
// response.put("forceAlarm", brightnessRequest.isForceAlarm());
return ResponseEntity.ok(response);
}
/**
* 获取设备位置信息
* @param deviceId 设备ID
* @return 位置信息
*/
@GetMapping("/location/{deviceId}")
public ResponseEntity<Map<String, Object>> getLocation(@PathVariable String deviceId) {
// 实际应用中这里会从设备获取实时位置
Map<String, Object> locationInfo = new HashMap<>();
locationInfo.put("deviceId", deviceId);
locationInfo.put("longitude", "114°7'E");
locationInfo.put("latitude", "30'28'N");
locationInfo.put("address", "湖北省武汉市洪山区光谷大道国际企业中心");
locationInfo.put("timestamp", new Date());
return ResponseEntity.ok(locationInfo);
}
/**
* 发送紧急消息
* @param messageRequest 消息请求
* @return 操作结果
*/
@PostMapping("/send-message")
public ResponseEntity<Map<String, Object>> sendMessage(@RequestBody MessageRequest messageRequest) {
// 实际应用中这里会向设备发送消息
Map<String, Object> response = new HashMap<>();
response.put("code", 200);
response.put("message", "消息已发送");
// response.put("deviceId", messageRequest.getDeviceId());
// response.put("content", messageRequest.getContent());
response.put("timestamp", new Date());
return ResponseEntity.ok(response);
}
/**
* 管理操作视频
* @param videoRequest 视频请求
* @return 操作结果
*/
@PostMapping("/operation-video")
public ResponseEntity<Map<String, Object>> manageOperationVideo(@RequestBody VideoRequest videoRequest) {
// 实际应用中这里会更新设备操作视频
Map<String, Object> response = new HashMap<>();
response.put("code", 200);
response.put("message", "操作视频已更新");
// response.put("deviceId", videoRequest.getDeviceId());
// response.put("videoUrl", videoRequest.getVideoUrl());
return ResponseEntity.ok(response);
}
// 请求对象类定义
public static class LightModeRequest {
private String deviceId;
private String mode; // 强光、弱光、爆闪、泛光、激光
// Getters and Setters
}
public static class PersonInfo {
private String deviceId;
private String unit; // 单位
private String position; // 职位
// Getters and Setters
}
public static class BootScreenRequest {
private String deviceId;
private List<String> screens; // 产品参数、操作说明等
// Getters and Setters
}
public static class BrightnessRequest {
private String deviceId;
private int brightness; // 0-100
private boolean forceAlarm; // 强制报警
// Getters and Setters
}
public static class MessageRequest {
private String deviceId;
private String content; // 消息内容
// Getters and Setters
}
public static class VideoRequest {
private String deviceId;
private String videoUrl; // 视频链接
// Getters and Setters
}
}

View File

@ -0,0 +1,109 @@
package com.fuyuanshen.web.controller.device;
import java.util.List;
import com.fuyuanshen.common.mybatis.core.page.TableDataInfo;
import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import com.fuyuanshen.common.idempotent.annotation.RepeatSubmit;
import com.fuyuanshen.common.log.annotation.Log;
import com.fuyuanshen.common.web.core.BaseController;
import com.fuyuanshen.common.core.domain.R;
import com.fuyuanshen.common.core.validate.AddGroup;
import com.fuyuanshen.common.core.validate.EditGroup;
import com.fuyuanshen.common.log.enums.BusinessType;
import com.fuyuanshen.common.excel.utils.ExcelUtil;
import com.fuyuanshen.equipment.domain.vo.DeviceGroupVo;
import com.fuyuanshen.equipment.domain.bo.DeviceGroupBo;
import com.fuyuanshen.equipment.service.IDeviceGroupService;
/**
* 设备分组
*
* @author Lion Li
* @date 2025-08-08
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/api/device/group")
public class DeviceGroupController extends BaseController {
private final IDeviceGroupService deviceGroupService;
/**
* 查询设备分组列表
*/
@SaCheckPermission("fys-equipment:group:list")
@GetMapping("/list")
public R<List<DeviceGroupVo>> list(DeviceGroupBo bo) {
List<DeviceGroupVo> list = deviceGroupService.queryList(bo);
return R.ok(list);
}
/**
* 导出设备分组列表
*/
@SaCheckPermission("fys-equipment:group:export")
@Log(title = "设备分组", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(DeviceGroupBo bo, HttpServletResponse response) {
List<DeviceGroupVo> list = deviceGroupService.queryList(bo);
ExcelUtil.exportExcel(list, "设备分组", DeviceGroupVo.class, response);
}
/**
* 获取设备分组详细信息
*
* @param id 主键
*/
@SaCheckPermission("fys-equipment:group:query")
@GetMapping("/{id}")
public R<DeviceGroupVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) {
return R.ok(deviceGroupService.queryById(id));
}
/**
* 新增设备分组
*/
@SaCheckPermission("fys-equipment:group:add")
@Log(title = "设备分组", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody DeviceGroupBo bo) {
return toAjax(deviceGroupService.insertByBo(bo));
}
/**
* 修改设备分组
*/
@SaCheckPermission("fys-equipment:group:edit")
@Log(title = "设备分组", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody DeviceGroupBo bo) {
return toAjax(deviceGroupService.updateByBo(bo));
}
/**
* 删除设备分组
*
* @param ids 主键串
*/
@SaCheckPermission("fys-equipment:group:remove")
@Log(title = "设备分组", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) {
return toAjax(deviceGroupService.deleteWithValidByIds(List.of(ids), true));
}
}

View File

@ -1,16 +1,27 @@
package com.fuyuanshen.web.controller.device;
import com.fuyuanshen.app.domain.dto.APPReNameDTO;
import com.fuyuanshen.app.domain.dto.AppRealTimeStatusDto;
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.web.core.BaseController;
import com.fuyuanshen.equipment.domain.dto.AppDeviceBo;
import com.fuyuanshen.equipment.domain.query.DeviceQueryCriteria;
import com.fuyuanshen.equipment.domain.vo.AppDeviceVo;
import com.fuyuanshen.web.service.WEBDeviceService;
import com.fuyuanshen.web.service.device.DeviceBizService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
/**
* @Description:
@ -25,7 +36,7 @@ import org.springframework.web.bind.annotation.RestController;
public class WEBDeviceController extends BaseController {
private final WEBDeviceService deviceService;
private final DeviceBizService appDeviceService;
/**
* @param id
@ -41,6 +52,67 @@ public class WEBDeviceController extends BaseController {
}
/**
* 查询设备列表
*/
@GetMapping("/list")
public TableDataInfo<AppDeviceVo> list(DeviceQueryCriteria bo, PageQuery pageQuery) {
return appDeviceService.queryAppDeviceList(bo,pageQuery);
}
/**
* 绑定设备
*/
@PostMapping("/bind")
public R<Void> bind(@RequestBody AppDeviceBo bo) {
return toAjax(appDeviceService.bindDevice(bo));
}
/**
* 解绑设备
*/
@DeleteMapping("/unBind")
public R<Void> unBind(Long id) {
return toAjax(appDeviceService.unBindDevice(id));
}
/**
* 查询设备类型列表
*/
@GetMapping(value = "/typeList")
public R<List<APPDeviceTypeVo>> getTypeList() {
List<APPDeviceTypeVo> typeList = appDeviceService.getTypeList();
return R.ok(typeList);
}
/**
* 重命名设备
* @param reNameDTO
* @return
*/
@PostMapping(value = "/reName")
public R<String> reName(@Validated @RequestBody APPReNameDTO reNameDTO) {
appDeviceService.reName(reNameDTO);
return R.ok("重命名成功!!!");
}
@GetMapping("/realTimeStatus")
public R<Map<String, Object>> getRealTimeStatus(AppRealTimeStatusDto statusDto) {
Map<String, Object> status = appDeviceService.getRealTimeStatus(statusDto);
return R.ok(status);
}
/**
* 根据mac查询设备信息
*/
@GetMapping("/getDeviceInfoByDeviceMac")
public R<AppDeviceVo> getDeviceInfo(String deviceMac) {
return R.ok(appDeviceService.getDeviceInfo(deviceMac));
}
}

View File

@ -1,318 +1,318 @@
package com.fuyuanshen.web.handler.mqtt;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fuyuanshen.common.satoken.utils.LoginHelper;
import com.fuyuanshen.equipment.domain.Device;
import com.fuyuanshen.equipment.domain.DeviceLog;
import com.fuyuanshen.equipment.mapper.DeviceLogMapper;
import com.fuyuanshen.equipment.mapper.DeviceMapper;
import com.fuyuanshen.equipment.utils.map.GetAddressFromLatUtil;
import com.fuyuanshen.equipment.utils.map.LngLonUtil;
import com.fuyuanshen.global.mqtt.constants.TenantsConstant;
import com.fuyuanshen.web.enums.InstructType6170;
import com.fuyuanshen.web.enums.LightModeEnum6170;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.MessagingException;
import org.springframework.stereotype.Component;
/**
* 定义监听主题消息的处理器
*
* @author: 默苍璃
* @date: 2025-08-0110:19
*/
@Component
@Data
@AllArgsConstructor
@Slf4j
public class DeviceReceiverMessageHandler implements MessageHandler {
private final DeviceMapper deviceMapper;
private final DeviceLogMapper deviceLogMapper;
// 使用Jackson解析JSON
private static final ObjectMapper objectMapper = new ObjectMapper();
/**
* 处理接收的消息
*
* @param message
* @throws MessagingException
*/
@Override
public void handleMessage(Message<?> message) throws MessagingException {
// System.out.println("接收到的消息:" + message.getPayload());
MessageHeaders headers = message.getHeaders();
String receivedTopicName = (String) headers.get("mqtt_receivedTopic");
System.out.println("消息来自主题:" + receivedTopicName);
// String tenantId = LoginHelper.getTenantId();
String tenantId = TenantsConstant.FU_YUAN_SHENG;
String payload = message.getPayload().toString();
if (receivedTopicName != null) {
// 1. 提取设备ID (从主题中获取)
String deviceImei = extractDeviceId(receivedTopicName);
Device device = deviceMapper.selectOne(new QueryWrapper<Device>()
.eq("tenant_id", tenantId)
.eq("device_imei", deviceImei));
if (device == null) {
log.info("不存在的设备IMEI: {}", deviceImei);
} else {
try {
JsonNode root = objectMapper.readTree(payload);
DeviceLog record = new DeviceLog();
// 手动设置租户ID
record.setTenantId(device.getTenantId()); // 从设备信息中获取租户ID
// 设备ID
record.setDeviceId(device.getId());
// 设备名称
record.setDeviceName(device.getDeviceName());
// 2. 处理instruct消息
if (root.has("instruct")) {
JsonNode instructNode = root.get("instruct");
if (instructNode.isArray()) {
boolean b = receivedTopicName.startsWith("B/");
record = parseInstruct(device, instructNode, b);
// 根据不同主题进行不同处理
if (receivedTopicName.startsWith("A/")) {
// 处理A主题的消息设备上传
record.setDataSource("设备上报");
} else if (receivedTopicName.startsWith("B/")) {
// 处理B主题的消息 (手动上传)
record.setDataSource("客户端操作");
}
}
// 确保在插入前设置tenantId和deviceId
record.setTenantId(device.getTenantId());
record.setDeviceId(device.getId());
deviceLogMapper.insert(record);
}
// 2. 处理 state 消息
if (root.has("state")) {
JsonNode instructNode = root.get("state");
if (instructNode.isArray()) {
boolean b = receivedTopicName.startsWith("B/");
record = parseState(device, instructNode, b);
// 根据不同主题进行不同处理
if (receivedTopicName.startsWith("A/")) {
// 处理A主题的消息设备上传
record.setDataSource("设备上报");
} else if (receivedTopicName.startsWith("B/")) {
// 处理B主题的消息 (手动上传)
record.setDataSource("客户端操作");
}
}
// 确保在插入前设置tenantId和deviceId
record.setTenantId(device.getTenantId());
record.setDeviceId(device.getId());
deviceLogMapper.insert(record);
}
if (root.has("imei")) {
// 设备行为
record.setDeviceAction(InstructType6170.fromCode(0).getDescription());
record.setDataSource("设备上报");
record.setContent("设备启动");
// 确保在插入前设置tenantId和deviceId
record.setTenantId(device.getTenantId());
record.setDeviceId(device.getId());
deviceLogMapper.insert(record);
}
// 3. 处理state消息
// else if (root.has("state")) {
// JsonNode stateNode = root.get("state");
// if (stateNode.isArray()) {
// StateRecord record = parseState(device, stateNode);
// stateRepo.save(record);
// }
// }
} catch (Exception e) {
log.error("消息解析失败: {}", payload, e);
}
}
}
}
/**
* 从主题中提取设备ID(IMEI)
*
* @param topic
* @return
*/
private String extractDeviceId(String topic) {
// 处理 A/# 或 B/# 格式的主题,例如 B/861556078765285 或 A/861556078765285
String[] segments = topic.split("/");
if (segments.length >= 2) {
// 返回第二个段,即 / 后面的部分
return segments[1];
}
// 如果格式不符合预期,返回原主题
return topic;
}
/**
* 解析instruct消息
*
* @param device
* @param array
* @param b
* @return
*/
private DeviceLog parseInstruct(Device device, JsonNode array, boolean b) {
DeviceLog record = new DeviceLog();
record.setDeviceName(device.getDeviceName());
// 设备行为
record.setDeviceAction(InstructType6170.fromCode(array.get(0).asInt()).getDescription());
switch (array.get(0).asInt()) {
case 1: // 灯光模式
LightModeEnum6170 lightModeEnum6170 = LightModeEnum6170.fromCode(array.get(1).asInt());
record.setContent(lightModeEnum6170.getDescription());
break;
case 2: // 单位/姓名/职位
byte[] unitBytes = new byte[480];
for (int i = 1; i <= 480; i++) {
unitBytes[i - 1] = (byte) array.get(i).asInt();
}
// record.setUnitData(unitBytes);
break;
case 3: // 开机图片
// record.setImagePage(array.get(1).asInt());
byte[] imageBytes = new byte[512];
for (int i = 2; i <= 513; i++) {
imageBytes[i - 2] = (byte) array.get(i).asInt();
}
// record.setImageData(imageBytes);
break;
case 4: // 激光灯
int anInt = array.get(1).asInt();
if (anInt == 0) {
record.setContent("关闭激光灯");
} else if (anInt == 1) {
record.setContent("开启激光灯");
} else {
record.setContent("未知操作");
}
break;
case 5: // 亮度调节
record.setContent(+array.get(1).asInt() + "%");
break;
case 11: // 定位数据
if (b) {
break;
}
int i1 = array.get(1).asInt();
int i2 = array.get(2).asInt();
int i3 = array.get(3).asInt();
int i4 = array.get(4).asInt();
// 优雅的转换方式 Longitude and latitude
double latitude = i1 + i2 / 10.0;
double Longitude = i3 + i4 / 10.0;
// 84 ==》 高德
double[] doubles = LngLonUtil.gps84_To_Gcj02(latitude, Longitude);
String address = GetAddressFromLatUtil.getAdd(String.valueOf(doubles[1]), String.valueOf(doubles[0]));
record.setContent(address);
break;
}
return record;
}
/**
* 解析 state 消息
*
* @param device
* @param array
* @return
*/
private DeviceLog parseState(Device device, JsonNode array, boolean b) {
DeviceLog record = new DeviceLog();
record.setDeviceName(device.getDeviceName());
// 设备行为
record.setDeviceAction(InstructType6170.fromCode(array.get(0).asInt()).getDescription());
switch (array.get(0).asInt()) {
case 1: // 灯光模式
LightModeEnum6170 lightModeEnum6170 = LightModeEnum6170.fromCode(array.get(1).asInt());
record.setContent(lightModeEnum6170.getDescription());
break;
case 2: // 单位/姓名/职位
byte[] unitBytes = new byte[480];
for (int i = 1; i <= 480; i++) {
unitBytes[i - 1] = (byte) array.get(i).asInt();
}
// record.setUnitData(unitBytes);
break;
case 3: // 开机图片
// record.setImagePage(array.get(1).asInt());
byte[] imageBytes = new byte[512];
for (int i = 2; i <= 513; i++) {
imageBytes[i - 2] = (byte) array.get(i).asInt();
}
// record.setImageData(imageBytes);
break;
case 4: // 激光灯
int anInt = array.get(1).asInt();
if (anInt == 0) {
record.setContent("关闭激光灯");
} else if (anInt == 1) {
record.setContent("开启激光灯");
} else {
record.setContent("未知操作");
}
break;
case 5: // 亮度调节
record.setContent(+array.get(1).asInt() + "%");
break;
case 11: // 定位数据
if (b) {
break;
}
int i1 = array.get(1).asInt();
int i2 = array.get(2).asInt();
int i3 = array.get(3).asInt();
int i4 = array.get(4).asInt();
// 优雅的转换方式 Longitude and latitude
double latitude = i1 + i2 / 10.0;
double Longitude = i3 + i4 / 10.0;
// 84 ==》 高德
double[] doubles = LngLonUtil.gps84_To_Gcj02(latitude, Longitude);
String address = GetAddressFromLatUtil.getAdd(String.valueOf(doubles[1]), String.valueOf(doubles[0]));
record.setContent(address);
break;
}
return record;
}
}
//package com.fuyuanshen.web.handler.mqtt;
//
//import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
//import com.fasterxml.jackson.databind.JsonNode;
//import com.fasterxml.jackson.databind.ObjectMapper;
//import com.fuyuanshen.common.satoken.utils.LoginHelper;
//import com.fuyuanshen.equipment.domain.Device;
//import com.fuyuanshen.equipment.domain.DeviceLog;
//import com.fuyuanshen.equipment.mapper.DeviceLogMapper;
//import com.fuyuanshen.equipment.mapper.DeviceMapper;
//import com.fuyuanshen.equipment.utils.map.GetAddressFromLatUtil;
//import com.fuyuanshen.equipment.utils.map.LngLonUtil;
//import com.fuyuanshen.global.mqtt.constants.TenantsConstant;
//import com.fuyuanshen.web.enums.InstructType6170;
//import com.fuyuanshen.web.enums.LightModeEnum6170;
//import lombok.AllArgsConstructor;
//import lombok.Data;
//import lombok.extern.slf4j.Slf4j;
//import org.springframework.messaging.Message;
//import org.springframework.messaging.MessageHandler;
//import org.springframework.messaging.MessageHeaders;
//import org.springframework.messaging.MessagingException;
//import org.springframework.stereotype.Component;
//
///**
// * 定义监听主题消息的处理器
// *
// * @author: 默苍璃
// * @date: 2025-08-0110:19
// */
//@Component
//@Data
//@AllArgsConstructor
//@Slf4j
//public class DeviceReceiverMessageHandler implements MessageHandler {
//
// private final DeviceMapper deviceMapper;
// private final DeviceLogMapper deviceLogMapper;
//
// // 使用Jackson解析JSON
// private static final ObjectMapper objectMapper = new ObjectMapper();
//
//
// /**
// * 处理接收的消息
// *
// * @param message
// * @throws MessagingException
// */
// @Override
// public void handleMessage(Message<?> message) throws MessagingException {
// // System.out.println("接收到的消息:" + message.getPayload());
// MessageHeaders headers = message.getHeaders();
// String receivedTopicName = (String) headers.get("mqtt_receivedTopic");
// System.out.println("消息来自主题:" + receivedTopicName);
//
// // String tenantId = LoginHelper.getTenantId();
// String tenantId = TenantsConstant.FU_YUAN_SHENG;
// String payload = message.getPayload().toString();
//
// if (receivedTopicName != null) {
// // 1. 提取设备ID (从主题中获取)
// String deviceImei = extractDeviceId(receivedTopicName);
// Device device = deviceMapper.selectOne(new QueryWrapper<Device>()
// .eq("tenant_id", tenantId)
// .eq("device_imei", deviceImei));
// if (device == null) {
// log.info("不存在的设备IMEI: {}", deviceImei);
// } else {
//
// try {
// JsonNode root = objectMapper.readTree(payload);
//
// DeviceLog record = new DeviceLog();
// // 手动设置租户ID
// record.setTenantId(device.getTenantId()); // 从设备信息中获取租户ID
// // 设备ID
// record.setDeviceId(device.getId());
// // 设备名称
// record.setDeviceName(device.getDeviceName());
//
// // 2. 处理instruct消息
// if (root.has("instruct")) {
// JsonNode instructNode = root.get("instruct");
// if (instructNode.isArray()) {
// boolean b = receivedTopicName.startsWith("B/");
// record = parseInstruct(device, instructNode, b);
// // 根据不同主题进行不同处理
// if (receivedTopicName.startsWith("A/")) {
// // 处理A主题的消息设备上传
// record.setDataSource("设备上报");
// } else if (receivedTopicName.startsWith("B/")) {
// // 处理B主题的消息 (手动上传)
// record.setDataSource("客户端操作");
// }
// }
// // 确保在插入前设置tenantId和deviceId
// record.setTenantId(device.getTenantId());
// record.setDeviceId(device.getId());
// deviceLogMapper.insert(record);
// }
//
// // 2. 处理 state 消息
// if (root.has("state")) {
// JsonNode instructNode = root.get("state");
// if (instructNode.isArray()) {
// boolean b = receivedTopicName.startsWith("B/");
// record = parseState(device, instructNode, b);
// // 根据不同主题进行不同处理
// if (receivedTopicName.startsWith("A/")) {
// // 处理A主题的消息设备上传
// record.setDataSource("设备上报");
// } else if (receivedTopicName.startsWith("B/")) {
// // 处理B主题的消息 (手动上传)
// record.setDataSource("客户端操作");
// }
// }
// // 确保在插入前设置tenantId和deviceId
// record.setTenantId(device.getTenantId());
// record.setDeviceId(device.getId());
// deviceLogMapper.insert(record);
// }
//
// if (root.has("imei")) {
// // 设备行为
// record.setDeviceAction(InstructType6170.fromCode(0).getDescription());
// record.setDataSource("设备上报");
// record.setContent("设备启动");
// // 确保在插入前设置tenantId和deviceId
// record.setTenantId(device.getTenantId());
// record.setDeviceId(device.getId());
// deviceLogMapper.insert(record);
// }
//
//
// // 3. 处理state消息
// // else if (root.has("state")) {
// // JsonNode stateNode = root.get("state");
// // if (stateNode.isArray()) {
// // StateRecord record = parseState(device, stateNode);
// // stateRepo.save(record);
// // }
// // }
// } catch (Exception e) {
// log.error("消息解析失败: {}", payload, e);
// }
//
// }
//
// }
// }
//
//
// /**
// * 从主题中提取设备ID(IMEI)
// *
// * @param topic
// * @return
// */
// private String extractDeviceId(String topic) {
// // 处理 A/# 或 B/# 格式的主题,例如 B/861556078765285 或 A/861556078765285
// String[] segments = topic.split("/");
// if (segments.length >= 2) {
// // 返回第二个段,即 / 后面的部分
// return segments[1];
// }
// // 如果格式不符合预期,返回原主题
// return topic;
// }
//
//
// /**
// * 解析instruct消息
// *
// * @param device
// * @param array
// * @param b
// * @return
// */
// private DeviceLog parseInstruct(Device device, JsonNode array, boolean b) {
// DeviceLog record = new DeviceLog();
// record.setDeviceName(device.getDeviceName());
// // 设备行为
// record.setDeviceAction(InstructType6170.fromCode(array.get(0).asInt()).getDescription());
//
// switch (array.get(0).asInt()) {
// case 1: // 灯光模式
// LightModeEnum6170 lightModeEnum6170 = LightModeEnum6170.fromCode(array.get(1).asInt());
// record.setContent(lightModeEnum6170.getDescription());
// break;
//
// case 2: // 单位/姓名/职位
// byte[] unitBytes = new byte[480];
// for (int i = 1; i <= 480; i++) {
// unitBytes[i - 1] = (byte) array.get(i).asInt();
// }
// // record.setUnitData(unitBytes);
// break;
//
// case 3: // 开机图片
// // record.setImagePage(array.get(1).asInt());
// byte[] imageBytes = new byte[512];
// for (int i = 2; i <= 513; i++) {
// imageBytes[i - 2] = (byte) array.get(i).asInt();
// }
// // record.setImageData(imageBytes);
// break;
//
// case 4: // 激光灯
// int anInt = array.get(1).asInt();
// if (anInt == 0) {
// record.setContent("关闭激光灯");
// } else if (anInt == 1) {
// record.setContent("开启激光灯");
// } else {
// record.setContent("未知操作");
// }
// break;
//
// case 5: // 亮度调节
// record.setContent(+array.get(1).asInt() + "%");
// break;
//
// case 11: // 定位数据
// if (b) {
// break;
// }
// int i1 = array.get(1).asInt();
// int i2 = array.get(2).asInt();
// int i3 = array.get(3).asInt();
// int i4 = array.get(4).asInt();
//
// // 优雅的转换方式 Longitude and latitude
// double latitude = i1 + i2 / 10.0;
// double Longitude = i3 + i4 / 10.0;
// // 84 ==》 高德
// double[] doubles = LngLonUtil.gps84_To_Gcj02(latitude, Longitude);
// String address = GetAddressFromLatUtil.getAdd(String.valueOf(doubles[1]), String.valueOf(doubles[0]));
// record.setContent(address);
// break;
// }
// return record;
// }
//
//
// /**
// * 解析 state 消息
// *
// * @param device
// * @param array
// * @return
// */
// private DeviceLog parseState(Device device, JsonNode array, boolean b) {
// DeviceLog record = new DeviceLog();
// record.setDeviceName(device.getDeviceName());
// // 设备行为
// record.setDeviceAction(InstructType6170.fromCode(array.get(0).asInt()).getDescription());
//
// switch (array.get(0).asInt()) {
// case 1: // 灯光模式
// LightModeEnum6170 lightModeEnum6170 = LightModeEnum6170.fromCode(array.get(1).asInt());
// record.setContent(lightModeEnum6170.getDescription());
// break;
//
// case 2: // 单位/姓名/职位
// byte[] unitBytes = new byte[480];
// for (int i = 1; i <= 480; i++) {
// unitBytes[i - 1] = (byte) array.get(i).asInt();
// }
// // record.setUnitData(unitBytes);
// break;
//
// case 3: // 开机图片
// // record.setImagePage(array.get(1).asInt());
// byte[] imageBytes = new byte[512];
// for (int i = 2; i <= 513; i++) {
// imageBytes[i - 2] = (byte) array.get(i).asInt();
// }
// // record.setImageData(imageBytes);
// break;
//
// case 4: // 激光灯
// int anInt = array.get(1).asInt();
// if (anInt == 0) {
// record.setContent("关闭激光灯");
// } else if (anInt == 1) {
// record.setContent("开启激光灯");
// } else {
// record.setContent("未知操作");
// }
// break;
//
// case 5: // 亮度调节
// record.setContent(+array.get(1).asInt() + "%");
// break;
//
// case 11: // 定位数据
// if (b) {
// break;
// }
// int i1 = array.get(1).asInt();
// int i2 = array.get(2).asInt();
// int i3 = array.get(3).asInt();
// int i4 = array.get(4).asInt();
//
// // 优雅的转换方式 Longitude and latitude
// double latitude = i1 + i2 / 10.0;
// double Longitude = i3 + i4 / 10.0;
// // 84 ==》 高德
// double[] doubles = LngLonUtil.gps84_To_Gcj02(latitude, Longitude);
// String address = GetAddressFromLatUtil.getAdd(String.valueOf(doubles[1]), String.valueOf(doubles[0]));
// record.setContent(address);
// break;
// }
// return record;
// }
//
//}

View File

@ -1,163 +1,163 @@
package com.fuyuanshen.web.listener;
import cn.dev33.satoken.listener.SaTokenListener;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.stp.parameter.SaLoginParameter;
import cn.hutool.core.convert.Convert;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import com.fuyuanshen.common.core.constant.CacheConstants;
import com.fuyuanshen.common.core.constant.Constants;
import com.fuyuanshen.common.core.domain.dto.UserOnlineDTO;
import com.fuyuanshen.common.core.utils.MessageUtils;
import com.fuyuanshen.common.core.utils.ServletUtils;
import com.fuyuanshen.common.core.utils.SpringUtils;
import com.fuyuanshen.common.core.utils.ip.AddressUtils;
import com.fuyuanshen.common.log.event.LogininforEvent;
import com.fuyuanshen.common.redis.utils.RedisUtils;
import com.fuyuanshen.common.satoken.utils.LoginHelper;
import com.fuyuanshen.common.tenant.helper.TenantHelper;
import com.fuyuanshen.web.service.SysLoginService;
import org.springframework.stereotype.Component;
import java.time.Duration;
/**
* 用户行为 侦听器的实现
*
* @author Lion Li
*/
@RequiredArgsConstructor
@Component
@Slf4j
public class UserActionListener implements SaTokenListener {
private final SysLoginService loginService;
/**
* 每次登录时触发
*/
@Override
public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginParameter loginParameter) {
UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent"));
String ip = ServletUtils.getClientIP();
UserOnlineDTO dto = new UserOnlineDTO();
dto.setIpaddr(ip);
dto.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
dto.setBrowser(userAgent.getBrowser().getName());
dto.setOs(userAgent.getOs().getName());
dto.setLoginTime(System.currentTimeMillis());
dto.setTokenId(tokenValue);
String username = (String) loginParameter.getExtra(LoginHelper.USER_NAME_KEY);
String tenantId = (String) loginParameter.getExtra(LoginHelper.TENANT_KEY);
dto.setUserName(username);
dto.setClientKey((String) loginParameter.getExtra(LoginHelper.CLIENT_KEY));
dto.setDeviceType(loginParameter.getDeviceType());
dto.setDeptName((String) loginParameter.getExtra(LoginHelper.DEPT_NAME_KEY));
TenantHelper.dynamic(tenantId, () -> {
if(loginParameter.getTimeout() == -1) {
RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto);
} else {
RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(loginParameter.getTimeout()));
}
});
// 记录登录日志
LogininforEvent logininforEvent = new LogininforEvent();
logininforEvent.setTenantId(tenantId);
logininforEvent.setUsername(username);
logininforEvent.setStatus(Constants.LOGIN_SUCCESS);
logininforEvent.setMessage(MessageUtils.message("user.login.success"));
logininforEvent.setRequest(ServletUtils.getRequest());
SpringUtils.context().publishEvent(logininforEvent);
// 更新登录信息
loginService.recordLoginInfo((Long) loginParameter.getExtra(LoginHelper.USER_KEY), ip);
log.info("user doLogin, userId:{}, token:{}", loginId, tokenValue);
}
/**
* 每次注销时触发
*/
@Override
public void doLogout(String loginType, Object loginId, String tokenValue) {
String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY));
TenantHelper.dynamic(tenantId, () -> {
RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
});
log.info("user doLogout, userId:{}, token:{}", loginId, tokenValue);
}
/**
* 每次被踢下线时触发
*/
@Override
public void doKickout(String loginType, Object loginId, String tokenValue) {
String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY));
TenantHelper.dynamic(tenantId, () -> {
RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
});
log.info("user doKickout, userId:{}, token:{}", loginId, tokenValue);
}
/**
* 每次被顶下线时触发
*/
@Override
public void doReplaced(String loginType, Object loginId, String tokenValue) {
String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY));
TenantHelper.dynamic(tenantId, () -> {
RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
});
log.info("user doReplaced, userId:{}, token:{}", loginId, tokenValue);
}
/**
* 每次被封禁时触发
*/
@Override
public void doDisable(String loginType, Object loginId, String service, int level, long disableTime) {
}
/**
* 每次被解封时触发
*/
@Override
public void doUntieDisable(String loginType, Object loginId, String service) {
}
/**
* 每次打开二级认证时触发
*/
@Override
public void doOpenSafe(String loginType, String tokenValue, String service, long safeTime) {
}
/**
* 每次创建Session时触发
*/
@Override
public void doCloseSafe(String loginType, String tokenValue, String service) {
}
/**
* 每次创建Session时触发
*/
@Override
public void doCreateSession(String id) {
}
/**
* 每次注销Session时触发
*/
@Override
public void doLogoutSession(String id) {
}
/**
* 每次Token续期时触发
*/
@Override
public void doRenewTimeout(String tokenValue, Object loginId, long timeout) {
}
}
//package com.fuyuanshen.web.listener;
//
//import cn.dev33.satoken.listener.SaTokenListener;
//import cn.dev33.satoken.stp.StpUtil;
//import cn.dev33.satoken.stp.parameter.SaLoginParameter;
//import cn.hutool.core.convert.Convert;
//import cn.hutool.http.useragent.UserAgent;
//import cn.hutool.http.useragent.UserAgentUtil;
//import lombok.RequiredArgsConstructor;
//import lombok.extern.slf4j.Slf4j;
//import com.fuyuanshen.common.core.constant.CacheConstants;
//import com.fuyuanshen.common.core.constant.Constants;
//import com.fuyuanshen.common.core.domain.dto.UserOnlineDTO;
//import com.fuyuanshen.common.core.utils.MessageUtils;
//import com.fuyuanshen.common.core.utils.ServletUtils;
//import com.fuyuanshen.common.core.utils.SpringUtils;
//import com.fuyuanshen.common.core.utils.ip.AddressUtils;
//import com.fuyuanshen.common.log.event.LogininforEvent;
//import com.fuyuanshen.common.redis.utils.RedisUtils;
//import com.fuyuanshen.common.satoken.utils.LoginHelper;
//import com.fuyuanshen.common.tenant.helper.TenantHelper;
//import com.fuyuanshen.web.service.SysLoginService;
//import org.springframework.stereotype.Component;
//
//import java.time.Duration;
//
///**
// * 用户行为 侦听器的实现
// *
// * @author Lion Li
// */
//@RequiredArgsConstructor
//@Component
//@Slf4j
//public class UserActionListener implements SaTokenListener {
//
// private final SysLoginService loginService;
//
// /**
// * 每次登录时触发
// */
// @Override
// public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginParameter loginParameter) {
// UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent"));
// String ip = ServletUtils.getClientIP();
// UserOnlineDTO dto = new UserOnlineDTO();
// dto.setIpaddr(ip);
// dto.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
// dto.setBrowser(userAgent.getBrowser().getName());
// dto.setOs(userAgent.getOs().getName());
// dto.setLoginTime(System.currentTimeMillis());
// dto.setTokenId(tokenValue);
// String username = (String) loginParameter.getExtra(LoginHelper.USER_NAME_KEY);
// String tenantId = (String) loginParameter.getExtra(LoginHelper.TENANT_KEY);
// dto.setUserName(username);
// dto.setClientKey((String) loginParameter.getExtra(LoginHelper.CLIENT_KEY));
// dto.setDeviceType(loginParameter.getDeviceType());
// dto.setDeptName((String) loginParameter.getExtra(LoginHelper.DEPT_NAME_KEY));
// TenantHelper.dynamic(tenantId, () -> {
// if(loginParameter.getTimeout() == -1) {
// RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto);
// } else {
// RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(loginParameter.getTimeout()));
// }
// });
// // 记录登录日志
// LogininforEvent logininforEvent = new LogininforEvent();
// logininforEvent.setTenantId(tenantId);
// logininforEvent.setUsername(username);
// logininforEvent.setStatus(Constants.LOGIN_SUCCESS);
// logininforEvent.setMessage(MessageUtils.message("user.login.success"));
// logininforEvent.setRequest(ServletUtils.getRequest());
// SpringUtils.context().publishEvent(logininforEvent);
// // 更新登录信息
// loginService.recordLoginInfo((Long) loginParameter.getExtra(LoginHelper.USER_KEY), ip);
// log.info("user doLogin, userId:{}, token:{}", loginId, tokenValue);
// }
//
// /**
// * 每次注销时触发
// */
// @Override
// public void doLogout(String loginType, Object loginId, String tokenValue) {
// String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY));
// TenantHelper.dynamic(tenantId, () -> {
// RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
// });
// log.info("user doLogout, userId:{}, token:{}", loginId, tokenValue);
// }
//
// /**
// * 每次被踢下线时触发
// */
// @Override
// public void doKickout(String loginType, Object loginId, String tokenValue) {
// String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY));
// TenantHelper.dynamic(tenantId, () -> {
// RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
// });
// log.info("user doKickout, userId:{}, token:{}", loginId, tokenValue);
// }
//
// /**
// * 每次被顶下线时触发
// */
// @Override
// public void doReplaced(String loginType, Object loginId, String tokenValue) {
// String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY));
// TenantHelper.dynamic(tenantId, () -> {
// RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
// });
// log.info("user doReplaced, userId:{}, token:{}", loginId, tokenValue);
// }
//
// /**
// * 每次被封禁时触发
// */
// @Override
// public void doDisable(String loginType, Object loginId, String service, int level, long disableTime) {
// }
//
// /**
// * 每次被解封时触发
// */
// @Override
// public void doUntieDisable(String loginType, Object loginId, String service) {
// }
//
// /**
// * 每次打开二级认证时触发
// */
// @Override
// public void doOpenSafe(String loginType, String tokenValue, String service, long safeTime) {
// }
//
// /**
// * 每次创建Session时触发
// */
// @Override
// public void doCloseSafe(String loginType, String tokenValue, String service) {
// }
//
// /**
// * 每次创建Session时触发
// */
// @Override
// public void doCreateSession(String id) {
// }
//
// /**
// * 每次注销Session时触发
// */
// @Override
// public void doLogoutSession(String id) {
// }
//
// /**
// * 每次Token续期时触发
// */
// @Override
// public void doRenewTimeout(String tokenValue, Object loginId, long timeout) {
// }
//}

View File

@ -0,0 +1,235 @@
package com.fuyuanshen.web.service;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fuyuanshen.app.domain.AppDeviceShare;
import com.fuyuanshen.app.domain.AppPersonnelInfo;
import com.fuyuanshen.app.domain.bo.AppDeviceShareBo;
import com.fuyuanshen.app.domain.vo.AppDeviceShareDetailVo;
import com.fuyuanshen.app.domain.vo.AppDeviceShareVo;
import com.fuyuanshen.app.domain.vo.AppPersonnelInfoVo;
import com.fuyuanshen.app.mapper.AppDeviceShareMapper;
import com.fuyuanshen.app.mapper.AppPersonnelInfoMapper;
import com.fuyuanshen.common.core.constant.GlobalConstants;
import com.fuyuanshen.common.core.exception.ServiceException;
import com.fuyuanshen.common.core.utils.StringUtils;
import com.fuyuanshen.common.mybatis.core.page.PageQuery;
import com.fuyuanshen.common.mybatis.core.page.TableDataInfo;
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.mapper.DeviceMapper;
import com.fuyuanshen.equipment.mapper.DeviceTypeMapper;
import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import static com.fuyuanshen.common.core.constant.GlobalConstants.GLOBAL_REDIS_KEY;
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.*;
@RequiredArgsConstructor
@Slf4j
@Service
public class DeviceShareService {
private final AppDeviceShareMapper appDeviceShareMapper;
private final DeviceMapper deviceMapper;
private final DeviceTypeMapper deviceTypeMapper;
private final AppPersonnelInfoMapper appPersonnelInfoMapper;
public TableDataInfo<AppDeviceShareVo> queryPageList(AppDeviceShareBo bo, PageQuery pageQuery) {
Long userId = AppLoginHelper.getUserId();
bo.setCreateBy(userId);
Page<AppDeviceShareVo> page = new Page<>(pageQuery.getPageNum(), pageQuery.getPageSize());
Page<AppDeviceShareVo> result = appDeviceShareMapper.selectAppDeviceShareList(bo, page);
List<AppDeviceShareVo> records = result.getRecords();
records.forEach(DeviceShareService::buildDeviceStatus);
return TableDataInfo.build(result);
}
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)){
item.setOnlineStatus(1);
}else{
item.setOnlineStatus(0);
}
String deviceStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX+ item.getDeviceImei() + DEVICE_STATUS_KEY_PREFIX);
// 获取电量
if(StringUtils.isNotBlank(deviceStatus)){
JSONObject jsonObject = JSONObject.parseObject(deviceStatus);
item.setBattery(jsonObject.getString("batteryPercentage"));
}else{
item.setBattery("0");
}
String location = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY +DEVICE_KEY_PREFIX+ item.getDeviceImei()+ DEVICE_LOCATION_KEY_PREFIX);
if(StringUtils.isNotBlank(location)){
JSONObject jsonObject = JSONObject.parseObject(location);
item.setLatitude(jsonObject.getString("latitude"));
item.setLongitude(jsonObject.getString("longitude"));
}
String alarmStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY +DEVICE_KEY_PREFIX+ item.getDeviceImei()+ DEVICE_ALARM_KEY_PREFIX);
if(StringUtils.isNotBlank(alarmStatus)){
item.setAlarmStatus(alarmStatus);
}
}
public AppDeviceShareDetailVo getInfo(Long id) {
LambdaQueryWrapper<AppDeviceShare> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(AppDeviceShare::getId, id);
List<AppDeviceShareVo> appDeviceShareVos = appDeviceShareMapper.selectVoList(queryWrapper);
if(appDeviceShareVos==null || appDeviceShareVos.isEmpty()){
return null;
}
AppDeviceShareVo shareVo = appDeviceShareVos.get(0);
AppDeviceShareDetailVo shareDetailVo = new AppDeviceShareDetailVo();
shareDetailVo.setId(shareVo.getId());
shareDetailVo.setDeviceId(shareVo.getDeviceId());
shareDetailVo.setPhonenumber(shareVo.getPhonenumber());
shareDetailVo.setPermission(shareVo.getPermission());
Device device = deviceMapper.selectById(shareVo.getDeviceId());
shareDetailVo.setDeviceName(device.getDeviceName());
shareDetailVo.setDeviceImei(device.getDeviceImei());
shareDetailVo.setDeviceMac(device.getDeviceMac());
DeviceType deviceType = deviceTypeMapper.selectById(device.getDeviceType());
if(deviceType!=null){
shareDetailVo.setCommunicationMode(Integer.valueOf(deviceType.getCommunicationMode()));
}
shareDetailVo.setDevicePic(device.getDevicePic());
shareDetailVo.setTypeName(deviceType.getTypeName());
shareDetailVo.setBluetoothName(device.getBluetoothName());
shareDetailVo.setDeviceStatus(device.getDeviceStatus());
shareDetailVo.setSendMsg(device.getSendMsg());
LambdaQueryWrapper<AppPersonnelInfo> qw = new LambdaQueryWrapper<>();
qw.eq(AppPersonnelInfo::getDeviceId, device.getId());
List<AppPersonnelInfoVo> appPersonnelInfoVos = appPersonnelInfoMapper.selectVoList(qw);
if(appPersonnelInfoVos!=null && !appPersonnelInfoVos.isEmpty()){
shareDetailVo.setPersonnelInfo(appPersonnelInfoVos.get(0));
}
//设备在线状态
String onlineStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + device.getDeviceImei()+ DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX);
if(StringUtils.isNotBlank(onlineStatus)){
shareDetailVo.setOnlineStatus(1);
}else{
shareDetailVo.setOnlineStatus(0);
}
String deviceStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + device.getDeviceImei() + DeviceRedisKeyConstants.DEVICE_STATUS_KEY_PREFIX);
// 获取电量
if(StringUtils.isNotBlank(deviceStatus)){
JSONObject jsonObject = JSONObject.parseObject(deviceStatus);
shareDetailVo.setMainLightMode(jsonObject.getString("mainLightMode"));
shareDetailVo.setLaserLightMode(jsonObject.getString("laserLightMode"));
shareDetailVo.setBatteryPercentage(jsonObject.getString("batteryPercentage"));
shareDetailVo.setChargeState(jsonObject.getString("chargeState"));
shareDetailVo.setBatteryRemainingTime(jsonObject.getString("batteryRemainingTime"));
}else{
shareDetailVo.setBatteryPercentage("0");
}
// 获取经度纬度
String locationKey = GlobalConstants.GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + device.getDeviceImei() + DeviceRedisKeyConstants.DEVICE_LOCATION_KEY_PREFIX;
String locationInfo = RedisUtils.getCacheObject(locationKey);
if(StringUtils.isNotBlank(locationInfo)){
JSONObject jsonObject = JSONObject.parseObject(locationInfo);
shareDetailVo.setLongitude(jsonObject.get("longitude").toString());
shareDetailVo.setLatitude(jsonObject.get("latitude").toString());
shareDetailVo.setAddress((String)jsonObject.get("address"));
}
String alarmStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY +DEVICE_KEY_PREFIX+ device.getDeviceImei()+ DEVICE_ALARM_KEY_PREFIX);
if(StringUtils.isNotBlank(alarmStatus)){
shareDetailVo.setAlarmStatus(alarmStatus);
}
String lightBrightness = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY +DEVICE_KEY_PREFIX+ device.getDeviceImei()+ DEVICE_LIGHT_BRIGHTNESS_KEY_PREFIX);
if(StringUtils.isNotBlank(lightBrightness)){
shareDetailVo.setLightBrightness(lightBrightness);
}
return shareDetailVo;
}
/**
* 校验短信验证码
*/
private boolean validateSmsCode(String tenantId, String phonenumber, String smsCode) {
String code = RedisUtils.getCacheObject(GlobalConstants.DEVICE_SHARE_CODES_KEY + phonenumber);
if (StringUtils.isBlank(code)) {
throw new ServiceException("验证码失效");
}
return code.equals(smsCode);
}
public int deviceShare(AppDeviceShareBo bo) {
boolean flag = validateSmsCode(AppLoginHelper.getTenantId(), bo.getPhonenumber(), bo.getSmsCode());
if(!flag){
throw new ServiceException("验证码错误");
}
Device device = deviceMapper.selectById(bo.getDeviceId());
if(device==null){
throw new ServiceException("设备不存在");
}
Long userId = AppLoginHelper.getUserId();
LambdaQueryWrapper<AppDeviceShare> lqw = new LambdaQueryWrapper<>();
lqw.eq(AppDeviceShare::getDeviceId, bo.getDeviceId());
lqw.eq(AppDeviceShare::getPhonenumber, bo.getPhonenumber());
Long count = appDeviceShareMapper.selectCount(lqw);
if(count>0){
UpdateWrapper<AppDeviceShare> uw = new UpdateWrapper<>();
uw.eq("device_id", bo.getDeviceId());
uw.eq("phonenumber", bo.getPhonenumber());
uw.set("permission", bo.getPermission());
uw.set("update_by", userId);
uw.set("update_time", new Date());
return appDeviceShareMapper.update(uw);
}else {
AppDeviceShare appDeviceShare = new AppDeviceShare();
appDeviceShare.setDeviceId(bo.getDeviceId());
appDeviceShare.setPhonenumber(bo.getPhonenumber());
appDeviceShare.setPermission(bo.getPermission());
appDeviceShare.setCreateBy(userId);
return appDeviceShareMapper.insert(appDeviceShare);
}
}
public int remove(Long[] ids) {
return appDeviceShareMapper.deleteByIds(Arrays.asList(ids));
}
public TableDataInfo<AppDeviceShareVo> otherDeviceShareList(AppDeviceShareBo bo, PageQuery pageQuery) {
String username = AppLoginHelper.getUsername();
bo.setPhonenumber(username);
Page<AppDeviceShareVo> page = new Page<>(pageQuery.getPageNum(), pageQuery.getPageSize());
IPage<AppDeviceShareVo> result = appDeviceShareMapper.otherDeviceShareList(bo, page);
List<AppDeviceShareVo> records = result.getRecords();
records.forEach(DeviceShareService::buildDeviceStatus);
return TableDataInfo.build(result);
}
}

View File

@ -1,4 +1,4 @@
package com.fuyuanshen.app.service.device;
package com.fuyuanshen.web.service.device;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
@ -19,6 +19,8 @@ import com.fuyuanshen.common.satoken.utils.AppLoginHelper;
import com.fuyuanshen.equipment.domain.Device;
import com.fuyuanshen.equipment.domain.DeviceType;
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.global.mqtt.config.MqttGateway;
@ -38,19 +40,19 @@ import static com.fuyuanshen.common.core.constant.GlobalConstants.GLOBAL_REDIS_K
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.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_BOOT_LOGO_KEY_PREFIX;
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX;
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.*;
@Slf4j
@Service
@RequiredArgsConstructor
public class AppDeviceBJQBizService {
public class DeviceBJQBizService {
private final DeviceMapper deviceMapper;
private final AppPersonnelInfoMapper appPersonnelInfoMapper;
private final DeviceTypeMapper deviceTypeMapper;
private final MqttGateway mqttGateway;
private final DeviceLogMapper deviceLogMapper;
public int sendMessage(AppDeviceSendMsgBo bo) {
List<Long> deviceIds = bo.getDeviceIds();
@ -62,22 +64,22 @@ public class AppDeviceBJQBizService {
if (device == null) {
throw new ServiceException("设备不存在" + deviceId);
}
if(getDeviceStatus(device.getDeviceImei())){
throw new ServiceException(device.getDeviceName()+",设备已断开连接");
}
try {
ClassPathResource resource = new ClassPathResource("image/background.png");
InputStream inputStream = resource.getInputStream();
// String backgroundImagePath = "D:\\background.png"; // 替换为实际背景图片路径
byte[] largeData = ImageWithTextGenerate.generate160x80ImageWithText2(bo.getSendMsg(), inputStream, 25600);
int[] ints = convertHexToDecimal(largeData);
RedisUtils.setCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + device.getDeviceImei() + ":app_send_message_data" , Arrays.toString(ints), Duration.ofSeconds(30 * 60L));
RedisUtils.setCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + device.getDeviceImei() + ":app_send_message_data" , Arrays.toString(ints), Duration.ofSeconds(5 * 60L));
String data = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + device.getDeviceImei() + ":app_send_message_data");
byte[] arr = ImageToCArrayConverter.convertStringToByteArray(data);
byte[] specificChunk = ImageToCArrayConverter.getChunk(arr, 0, 512);
log.info("发送信息第0块数据大小: {} 字节",specificChunk.length);
// log.info("第0块数据: {}", Arrays.toString(specificChunk));
ArrayList<Integer> intData = new ArrayList<>();
intData.add(6);
@ -97,19 +99,50 @@ public class AppDeviceBJQBizService {
.eq("binding_user_id", AppLoginHelper.getUserId())
.set("send_msg", bo.getSendMsg());
deviceMapper.update(updateWrapper);
recordDeviceLog(deviceId, device.getDeviceName(), "发送信息", bo.getSendMsg(), AppLoginHelper.getUserId());
} catch (Exception e) {
log.info("发送信息设备发送信息失败:{}" ,deviceId);
throw new ServiceException("发送指令失败");
}
}
return 1;
}
/**
* 记录设备操作日志
* @param deviceId 设备ID
* @param content 日志内容
* @param operator 操作人
*/
private void recordDeviceLog(Long deviceId,String deviceName, String deviceAction, String content, Long operator) {
try {
// 创建设备日志实体
com.fuyuanshen.equipment.domain.DeviceLog deviceLog = new com.fuyuanshen.equipment.domain.DeviceLog();
deviceLog.setDeviceId(deviceId);
deviceLog.setDeviceAction(deviceAction);
deviceLog.setContent(content);
deviceLog.setCreateBy(operator);
deviceLog.setDeviceName(deviceName);
deviceLog.setCreateTime(new Date());
// 插入日志记录
deviceLogMapper.insert(deviceLog);
} catch (Exception e) {
log.error("记录设备操作日志失败: {}", e.getMessage(), e);
}
}
public AppDeviceDetailVo getInfo(Long id) {
Device device = deviceMapper.selectById(id);
if (device == null) {
throw new RuntimeException("请先将设备入库!!!");
}
AppDeviceDetailVo vo = new AppDeviceDetailVo();
vo.setDeviceId(device.getId());
vo.setDeviceName(device.getDeviceName());
@ -134,13 +167,13 @@ public class AppDeviceBJQBizService {
vo.setPersonnelInfo(personnelInfoVo);
}
//设备在线状态
String onlineStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX + device.getDeviceImei());
String onlineStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + device.getDeviceImei()+ DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX);
if(StringUtils.isNotBlank(onlineStatus)){
vo.setOnlineStatus(1);
}else{
vo.setOnlineStatus(0);
}
String deviceStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+DeviceRedisKeyConstants.DEVICE_STATUS_KEY_PREFIX + device.getDeviceImei());
String deviceStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + device.getDeviceImei() + DeviceRedisKeyConstants.DEVICE_STATUS_KEY_PREFIX);
// 获取电量
if(StringUtils.isNotBlank(deviceStatus)){
JSONObject jsonObject = JSONObject.parseObject(deviceStatus);
@ -154,7 +187,7 @@ public class AppDeviceBJQBizService {
}
// 获取经度纬度
String locationKey = GlobalConstants.GLOBAL_REDIS_KEY+DeviceRedisKeyConstants.DEVICE_LOCATION_KEY_PREFIX + device.getDeviceImei();
String locationKey = GlobalConstants.GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + device.getDeviceImei() + DeviceRedisKeyConstants.DEVICE_LOCATION_KEY_PREFIX;
String locationInfo = RedisUtils.getCacheObject(locationKey);
if(StringUtils.isNotBlank(locationInfo)){
JSONObject jsonObject = JSONObject.parseObject(locationInfo);
@ -163,16 +196,30 @@ public class AppDeviceBJQBizService {
vo.setAddress((String)jsonObject.get("address"));
}
String alarmStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY +DEVICE_KEY_PREFIX+ device.getDeviceImei()+ DEVICE_ALARM_KEY_PREFIX);
if(StringUtils.isNotBlank(alarmStatus)){
vo.setAlarmStatus(alarmStatus);
}
String lightBrightness = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY +DEVICE_KEY_PREFIX+ device.getDeviceImei()+ DEVICE_LIGHT_BRIGHTNESS_KEY_PREFIX);
if(StringUtils.isNotBlank(lightBrightness)){
vo.setLightBrightness(lightBrightness);
}
return vo;
}
public boolean registerPersonInfo(AppPersonnelInfoBo bo) {
Long deviceId = bo.getDeviceId();
Device deviceObj = deviceMapper.selectById(deviceId);
if (deviceObj == null) {
throw new RuntimeException("请先将设备入库!!!");
}
if(getDeviceStatus(deviceObj.getDeviceImei())){
throw new ServiceException(deviceObj.getDeviceName()+",设备已断开连接");
}
QueryWrapper<AppPersonnelInfo> qw = new QueryWrapper<AppPersonnelInfo>()
.eq("device_id", deviceId);
List<AppPersonnelInfoVo> appPersonnelInfoVos = appPersonnelInfoMapper.selectVoList(qw);
@ -196,6 +243,7 @@ public class AppDeviceBJQBizService {
mqttGateway.sendMsgToMqtt(MqttConstants.GLOBAL_PUB_KEY + deviceObj.getDeviceImei(), 1, JSON.toJSONString(map));
log.info("发送点阵数据到设备消息=>topic:{},payload:{}", MqttConstants.GLOBAL_PUB_KEY + deviceObj.getDeviceImei(), bo);
recordDeviceLog(deviceId, deviceObj.getDeviceName(), "人员信息登记", JSON.toJSONString(bo), AppLoginHelper.getUserId());
if (ObjectUtils.length(appPersonnelInfoVos) == 0) {
AppPersonnelInfo appPersonnelInfo = MapstructUtils.convert(bo, AppPersonnelInfo.class);
return appPersonnelInfoMapper.insertOrUpdate(appPersonnelInfo);
@ -208,8 +256,6 @@ public class AppDeviceBJQBizService {
.set("code", bo.getCode());
return appPersonnelInfoMapper.update(null, uw) > 0;
}
}
public void uploadDeviceLogo(AppDeviceLogoUploadDto bo) {
@ -218,6 +264,9 @@ public class AppDeviceBJQBizService {
if (device == null) {
throw new ServiceException("设备不存在");
}
if(getDeviceStatus(device.getDeviceImei())){
throw new ServiceException(device.getDeviceName()+",设备已断开连接");
}
MultipartFile file = bo.getFile();
byte[] largeData = ImageToCArrayConverter.convertImageToCArray(file.getInputStream(), 160, 80, 25600);
@ -226,7 +275,7 @@ public class AppDeviceBJQBizService {
log.info("原始数据大小: {} 字节", largeData.length);
int[] ints = convertHexToDecimal(largeData);
RedisUtils.setCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + device.getDeviceImei() +DEVICE_BOOT_LOGO_KEY_PREFIX, Arrays.toString(ints), Duration.ofSeconds(30 * 60L));
RedisUtils.setCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + device.getDeviceImei() +DEVICE_BOOT_LOGO_KEY_PREFIX, Arrays.toString(ints), Duration.ofSeconds(5 * 60L));
String data = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + device.getDeviceImei() + DEVICE_BOOT_LOGO_KEY_PREFIX);
@ -247,8 +296,11 @@ public class AppDeviceBJQBizService {
map.put("instruct", intData);
mqttGateway.sendMsgToMqtt(MqttConstants.GLOBAL_PUB_KEY+device.getDeviceImei(), 1 , JSON.toJSONString(map));
log.info("发送点阵数据到设备消息=>topic:{},payload:{}", MqttConstants.GLOBAL_PUB_KEY+device.getDeviceImei(),JSON.toJSONString(map));
recordDeviceLog(device.getId(), device.getDeviceName(), "上传开机画面", "上传开机画面", AppLoginHelper.getUserId());
} catch (Exception e){
e.printStackTrace();
throw new ServiceException("发送指令失败");
}
}
@ -256,6 +308,7 @@ public class AppDeviceBJQBizService {
* 灯光模式
* 0关灯1强光模式2弱光模式, 3爆闪模式, 4泛光模式
*/
public void lightModeSettings(DeviceInstructDto params) {
try {
Long deviceId = params.getDeviceId();
@ -263,6 +316,9 @@ public class AppDeviceBJQBizService {
if(device == null){
throw new ServiceException("设备不存在");
}
if(getDeviceStatus(device.getDeviceImei())){
throw new ServiceException(device.getDeviceName()+",设备已断开连接");
}
Integer instructValue = Integer.parseInt(params.getInstructValue());
ArrayList<Integer> intData = new ArrayList<>();
intData.add(1);
@ -274,8 +330,11 @@ public class AppDeviceBJQBizService {
map.put("instruct", intData);
mqttGateway.sendMsgToMqtt(MqttConstants.GLOBAL_PUB_KEY+device.getDeviceImei(), 1 , JSON.toJSONString(map));
log.info("发送点阵数据到设备消息=>topic:{},payload:{}", MqttConstants.GLOBAL_PUB_KEY+device.getDeviceImei(),JSON.toJSONString(map));
LightModeEnum modeEnum = LightModeEnum.getByCode(instructValue);
recordDeviceLog(device.getId(), device.getDeviceName(), "灯光模式", modeEnum!=null?modeEnum.getName():null, AppLoginHelper.getUserId());
} catch (Exception e){
e.printStackTrace();
throw new ServiceException("发送指令失败");
}
}
@ -287,6 +346,9 @@ public class AppDeviceBJQBizService {
if(device == null){
throw new ServiceException("设备不存在");
}
if(getDeviceStatus(device.getDeviceImei())){
throw new ServiceException(device.getDeviceName()+",设备已断开连接");
}
String instructValue = params.getInstructValue();
ArrayList<Integer> intData = new ArrayList<>();
intData.add(5);
@ -306,8 +368,10 @@ public class AppDeviceBJQBizService {
map.put("instruct", intData);
mqttGateway.sendMsgToMqtt(MqttConstants.GLOBAL_PUB_KEY+device.getDeviceImei(), 1 , JSON.toJSONString(map));
log.info("发送点阵数据到设备消息=>topic:{},payload:{}", MqttConstants.GLOBAL_PUB_KEY+device.getDeviceImei(),JSON.toJSONString(map));
} catch (Exception e){
e.printStackTrace();
throw new ServiceException("发送指令失败");
}
}
@ -319,6 +383,9 @@ public class AppDeviceBJQBizService {
if(device == null){
throw new ServiceException("设备不存在");
}
if(getDeviceStatus(device.getDeviceImei())){
throw new ServiceException(device.getDeviceName()+",设备已断开连接");
}
Integer instructValue = Integer.parseInt(params.getInstructValue());
ArrayList<Integer> intData = new ArrayList<>();
intData.add(4);
@ -330,8 +397,15 @@ public class AppDeviceBJQBizService {
map.put("instruct", intData);
mqttGateway.sendMsgToMqtt(MqttConstants.GLOBAL_PUB_KEY+device.getDeviceImei(), 1 , JSON.toJSONString(map));
log.info("发送点阵数据到设备消息=>topic:{},payload:{}", MqttConstants.GLOBAL_PUB_KEY+device.getDeviceImei(),JSON.toJSONString(map));
// 1代表开启激光灯此时主灯关闭主灯控件为关机状态为0代表关闭激光灯
if("1".equals(params.getInstructValue())){
recordDeviceLog(device.getId(), device.getDeviceName(), "激光模式设置", "开启激光灯", AppLoginHelper.getUserId());
}else{
recordDeviceLog(device.getId(), device.getDeviceName(), "激光模式设置", "关闭激光灯", AppLoginHelper.getUserId());
}
} catch (Exception e){
e.printStackTrace();
throw new ServiceException("发送指令失败");
}
}
@ -351,13 +425,17 @@ public class AppDeviceBJQBizService {
if (deviceIds == null || deviceIds.isEmpty()) {
throw new ServiceException("请选择设备");
}
for (Long deviceId : deviceIds) {
Device device = deviceMapper.selectById(deviceId);
if (device == null) {
throw new ServiceException("设备不存在" + deviceId);
}
if(getDeviceStatus(device.getDeviceImei())){
throw new ServiceException(device.getDeviceName()+",设备已断开连接");
}
try {
ArrayList<Integer> intData = new ArrayList<>();
intData.add(7);
intData.add(Integer.parseInt(bo.getInstructValue()));
@ -375,14 +453,22 @@ public class AppDeviceBJQBizService {
.eq("binding_user_id", AppLoginHelper.getUserId())
.set("send_msg", bo.getSendMsg());
deviceMapper.update(updateWrapper);
recordDeviceLog(device.getId(), device.getDeviceName(), "发送告警信息", bo.getSendMsg(), AppLoginHelper.getUserId());
} catch (Exception e) {
log.info("设备发送信息失败:{}" ,deviceId);
log.info("设备发送告警信息信息失败:{}" ,deviceId);
throw new ServiceException("设备发送告警信息信息失败");
}
}
} catch (Exception e){
e.printStackTrace();
throw new ServiceException("发送告警信息指令失败");
}
return 1;
}
private boolean getDeviceStatus(String deviceImei) {
String deviceOnlineStatusRedisKey = GlobalConstants.GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX+ deviceImei + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX ;
return StringUtils.isBlank(deviceOnlineStatusRedisKey);
}
}

View File

@ -0,0 +1,293 @@
package com.fuyuanshen.web.service.device;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fuyuanshen.app.domain.AppDeviceBindRecord;
import com.fuyuanshen.app.domain.AppDeviceShare;
import com.fuyuanshen.app.domain.dto.APPReNameDTO;
import com.fuyuanshen.app.domain.dto.AppRealTimeStatusDto;
import com.fuyuanshen.app.domain.dto.DeviceInstructDto;
import com.fuyuanshen.app.domain.vo.APPDeviceTypeVo;
import com.fuyuanshen.app.domain.vo.AppUserVo;
import com.fuyuanshen.app.mapper.AppDeviceBindRecordMapper;
import com.fuyuanshen.app.mapper.AppDeviceShareMapper;
import com.fuyuanshen.app.mapper.AppPersonnelInfoMapper;
import com.fuyuanshen.app.mapper.AppUserMapper;
import com.fuyuanshen.app.mapper.equipment.APPDeviceMapper;
import com.fuyuanshen.common.core.exception.ServiceException;
import com.fuyuanshen.common.core.utils.ObjectUtils;
import com.fuyuanshen.common.core.utils.StringUtils;
import com.fuyuanshen.common.mybatis.core.page.PageQuery;
import com.fuyuanshen.common.mybatis.core.page.TableDataInfo;
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.dto.AppDeviceBo;
import com.fuyuanshen.equipment.domain.query.DeviceQueryCriteria;
import com.fuyuanshen.equipment.domain.vo.AppDeviceVo;
import com.fuyuanshen.equipment.enums.BindingStatusEnum;
import com.fuyuanshen.equipment.enums.CommunicationModeEnum;
import com.fuyuanshen.equipment.mapper.DeviceLogMapper;
import com.fuyuanshen.equipment.mapper.DeviceMapper;
import com.fuyuanshen.equipment.mapper.DeviceTypeMapper;
import com.fuyuanshen.global.mqtt.config.MqttGateway;
import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants;
import com.fuyuanshen.web.service.device.status.base.DeviceStatusRule;
import com.fuyuanshen.web.service.device.status.base.RealTimeStatusEngine;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.List;
import java.util.Map;
import static com.fuyuanshen.common.core.constant.GlobalConstants.GLOBAL_REDIS_KEY;
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.*;
@Slf4j
@Service
@RequiredArgsConstructor
public class DeviceBizService {
private final APPDeviceMapper appDeviceMapper;
private final DeviceMapper deviceMapper;
private final AppDeviceBindRecordMapper appDeviceBindRecordMapper;
private final RealTimeStatusEngine realTimeStatusEngine;
private final DeviceLogMapper deviceLogMapper;
private final AppDeviceShareMapper appDeviceShareMapper;
private final AppUserMapper appUserMapper;;
public List<APPDeviceTypeVo> getTypeList() {
Long userId = AppLoginHelper.getUserId();
return appDeviceMapper.getTypeList(userId);
}
public void reName(APPReNameDTO reNameDTO) {
UpdateWrapper<Device> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("id", reNameDTO.getId())
.eq("binding_user_id", AppLoginHelper.getUserId())
.set("device_name", reNameDTO.getDeviceName());
deviceMapper.update(updateWrapper);
}
public TableDataInfo<AppDeviceVo> queryAppDeviceList(DeviceQueryCriteria bo, PageQuery pageQuery) {
if (bo.getBindingUserId() == null) {
Long userId = AppLoginHelper.getUserId();
bo.setBindingUserId(userId);
}
Page<AppDeviceVo> result = deviceMapper.queryAppBindDeviceList(pageQuery.build(), bo);
List<AppDeviceVo> records = result.getRecords();
if(records != null && !records.isEmpty()){
records.forEach(item -> {
if(item.getCommunicationMode()!=null && item.getCommunicationMode() == 0){
//设备在线状态
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 deviceStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX+ item.getDeviceImei() + DEVICE_STATUS_KEY_PREFIX);
// 获取电量
if(StringUtils.isNotBlank(deviceStatus)){
JSONObject jsonObject = JSONObject.parseObject(deviceStatus);
item.setBattery(jsonObject.getString("batteryPercentage"));
}else{
item.setBattery("0");
}
String location = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY +DEVICE_KEY_PREFIX+ item.getDeviceImei()+ DEVICE_LOCATION_KEY_PREFIX);
if(StringUtils.isNotBlank(location)){
JSONObject jsonObject = JSONObject.parseObject(location);
item.setLatitude(jsonObject.getString("latitude"));
item.setLongitude(jsonObject.getString("longitude"));
}
String alarmStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY +DEVICE_KEY_PREFIX+ item.getDeviceImei()+ DEVICE_ALARM_KEY_PREFIX);
if(StringUtils.isNotBlank(alarmStatus)){
item.setAlarmStatus(alarmStatus);
}
}
});
}
return TableDataInfo.build(result);
}
public int bindDevice(AppDeviceBo bo) {
Integer mode = bo.getCommunicationMode();
Long userId = AppLoginHelper.getUserId();
if (mode == CommunicationModeEnum.FOUR_G.getValue()) {
String deviceImei = bo.getDeviceImei();
QueryWrapper<Device> qw = new QueryWrapper<Device>()
.eq("device_imei", deviceImei);
List<Device> devices = deviceMapper.selectList(qw);
if (devices.isEmpty()) {
throw new RuntimeException("请先将设备入库!!!");
}
Device device = devices.get(0);
if (device.getBindingStatus() != null && device.getBindingStatus() == BindingStatusEnum.BOUND.getCode()) {
throw new RuntimeException("设备已绑定");
}
QueryWrapper<AppDeviceBindRecord> bindRecordQueryWrapper = new QueryWrapper<>();
bindRecordQueryWrapper.eq("device_id", device.getId());
AppDeviceBindRecord appDeviceBindRecord = appDeviceBindRecordMapper.selectOne(bindRecordQueryWrapper);
if (appDeviceBindRecord != null) {
UpdateWrapper<AppDeviceBindRecord> deviceUpdateWrapper = new UpdateWrapper<>();
deviceUpdateWrapper.eq("device_id", device.getId())
.set("binding_status", BindingStatusEnum.BOUND.getCode())
.set("binding_user_id", userId)
.set("update_time", new Date())
.set("binding_time", new Date());
return appDeviceBindRecordMapper.update(null, deviceUpdateWrapper);
} else {
AppDeviceBindRecord bindRecord = new AppDeviceBindRecord();
bindRecord.setDeviceId(device.getId());
bindRecord.setBindingUserId(userId);
bindRecord.setBindingTime(new Date());
bindRecord.setCreateBy(userId);
appDeviceBindRecordMapper.insert(bindRecord);
}
UpdateWrapper<Device> deviceUpdateWrapper = new UpdateWrapper<>();
deviceUpdateWrapper.eq("id", device.getId())
.set("binding_status", BindingStatusEnum.BOUND.getCode())
.set("binding_user_id", userId)
.set("binding_time", new Date());
return deviceMapper.update(null, deviceUpdateWrapper);
} else if (mode == CommunicationModeEnum.BLUETOOTH.getValue()) {
String deviceMac = bo.getDeviceMac();
QueryWrapper<Device> qw = new QueryWrapper<Device>()
.eq("device_mac", deviceMac);
List<Device> devices = deviceMapper.selectList(qw);
if (devices.isEmpty()) {
throw new RuntimeException("请先将设备入库!!!");
}
Device device = devices.get(0);
QueryWrapper<AppDeviceBindRecord> bindRecordQueryWrapper = new QueryWrapper<>();
bindRecordQueryWrapper.eq("device_id", device.getId());
bindRecordQueryWrapper.eq("binding_user_id", userId);
AppDeviceBindRecord appDeviceBindRecord = appDeviceBindRecordMapper.selectOne(bindRecordQueryWrapper);
if (appDeviceBindRecord != null) {
UpdateWrapper<AppDeviceBindRecord> deviceUpdateWrapper = new UpdateWrapper<>();
deviceUpdateWrapper.eq("device_id", device.getId())
.eq("binding_user_id", userId)
.set("binding_user_id", userId)
.set("binding_time", new Date());
return appDeviceBindRecordMapper.update(null, deviceUpdateWrapper);
} else {
AppDeviceBindRecord bindRecord = new AppDeviceBindRecord();
bindRecord.setDeviceId(device.getId());
bindRecord.setBindingUserId(userId);
bindRecord.setBindingTime(new Date());
bindRecord.setCreateBy(userId);
appDeviceBindRecordMapper.insert(bindRecord);
}
UpdateWrapper<Device> deviceUpdateWrapper = new UpdateWrapper<>();
deviceUpdateWrapper.eq("id", device.getId())
.set("binding_status", BindingStatusEnum.BOUND.getCode())
.set("binding_user_id", userId)
.set("binding_time", new Date());
return deviceMapper.update(null, deviceUpdateWrapper);
} else {
throw new RuntimeException("通讯方式错误");
}
}
public int unBindDevice(Long id) {
return unBindDevice(id, null, 1);
}
public int unBindDevice(Long id, Long userId, int type) {
Device device = deviceMapper.selectById(id);
if (device == null) {
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);
if (userId == null) {
userId = AppLoginHelper.getUserId();
}
QueryWrapper<AppDeviceBindRecord> bindRecordQueryWrapper = new QueryWrapper<>();
bindRecordQueryWrapper.eq("device_id", device.getId());
// 设备端解绑 0:设备端解绑 1:web端解绑
if (type == 1) {
bindRecordQueryWrapper.eq("binding_user_id", userId);
}
// AppDeviceBindRecord appDeviceBindRecord = appDeviceBindRecordMapper.selectOne(bindRecordQueryWrapper);
// if (appDeviceBindRecord != null) {
// return appDeviceBindRecordMapper.deleteById(appDeviceBindRecord.getId());
// }
List<AppDeviceBindRecord> appDeviceBindRecordList = appDeviceBindRecordMapper.selectList(bindRecordQueryWrapper);
if (CollectionUtil.isNotEmpty(appDeviceBindRecordList)) {
appDeviceBindRecordList.forEach(appDeviceBindRecord ->
appDeviceBindRecordMapper.deleteById(appDeviceBindRecord.getId()));
}
AppUserVo appUserVo = appUserMapper.selectVoById(userId);
QueryWrapper<AppDeviceShare> appDeviceShareQueryWrapper = new QueryWrapper<>();
appDeviceShareQueryWrapper.eq("device_id", device.getId());
appDeviceShareQueryWrapper.eq("phonenumber", appUserVo.getPhonenumber());
List<AppDeviceShare> appDeviceShareList = appDeviceShareMapper.selectList(appDeviceShareQueryWrapper);
if (CollectionUtil.isNotEmpty(appDeviceShareList)) {
appDeviceShareList.forEach(appDeviceShare ->
appDeviceShareMapper.deleteById(appDeviceShare.getId()));
}
return 1;
}
public String mapReverseGeocoding(DeviceInstructDto params) {
// Long deviceId = params.getDeviceId();
// Device device = deviceMapper.selectById(deviceId);
QueryWrapper<Device> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("device_imei", params.getDeviceImei());
List<Device> devices = deviceMapper.selectList(queryWrapper);
if(ObjectUtils.length( devices) ==0){
throw new ServiceException("设备不存在");
}
return RedisUtils.getCacheObject("device:location:" + devices.get(0).getDeviceImei());
}
public Map<String, Object> getRealTimeStatus(AppRealTimeStatusDto statusDto) {
try {
String commandType = statusDto.getTypeName()+"_" + statusDto.getFunctionMode();
DeviceStatusRule rule = realTimeStatusEngine.getDeviceStatusRule(commandType);
if(rule == null){
throw new ServiceException("未匹配到处理命令");
}
return rule.getStatus(statusDto);
} catch (Exception e){
e.printStackTrace();
}
return null;
}
public AppDeviceVo getDeviceInfo(String deviceMac) {
// QueryWrapper<Device> queryWrapper = new QueryWrapper<>();
// queryWrapper.eq("device_mac", deviceMac);
// List<Device> devices = deviceMapper.selectList(queryWrapper);
return deviceMapper.getDeviceInfo(deviceMac);
}
}

View File

@ -0,0 +1,75 @@
package com.fuyuanshen.web.service.device.status;
import cn.hutool.json.JSONUtil;
import com.fuyuanshen.app.domain.dto.AppRealTimeStatusDto;
import com.fuyuanshen.common.core.utils.StringUtils;
import com.fuyuanshen.common.redis.utils.RedisUtils;
import com.fuyuanshen.global.mqtt.listener.domain.FunctionAccessStatus;
import com.fuyuanshen.web.service.device.status.base.DeviceStatusRule;
import com.fuyuanshen.web.service.device.status.constants.DeviceTypeConstants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_KEY;
@Slf4j
@Component
public class FunctionAccessBatchStatusRule implements DeviceStatusRule {
@Override
public String getCommandType() {
return DeviceTypeConstants.TYPE_BJQ6170+"_2";
}
@Override
public boolean supports(String deviceType) {
return true; // 适用于所有设备类型
}
@Override
public Map<String, Object> getStatus(AppRealTimeStatusDto dto) {
Map<String, Object> status = new HashMap<>();
String functionAccess = RedisUtils.getCacheObject(
FUNCTION_ACCESS_KEY + dto.getBatchId());
log.info("FunctionAccessBatchStatusRule:{}",functionAccess);
if(StringUtils.isBlank(functionAccess)){
status.put("functionAccess", FunctionAccessStatus.OK.getCode());
return status;
}
List<String> cachedDeviceImeiList = JSONUtil.toList(functionAccess, String.class);
if(cachedDeviceImeiList.isEmpty()){
status.put("functionAccess", FunctionAccessStatus.OK.getCode());
return status;
}
for (String key : cachedDeviceImeiList) {
String item = RedisUtils.getCacheObject(FUNCTION_ACCESS_KEY + key);
if("ACTIVE".equals(item)){
status.put("functionAccess", FunctionAccessStatus.ACTIVE.getCode());
break;
}else {
status.put("functionAccess", FunctionAccessStatus.OK.getCode());
}
// if (StringUtils.isBlank(item)) {
// String timeOut = RedisUtils.getCacheObject(FUNCTION_ACCESS_TIMEOUT_KEY + dto.getDeviceImei());
// if ("TIMEOUT".equals(timeOut)) {
// status.put("functionAccess", FunctionAccessStatus.TIMEOUT.getCode());
// break;
// } else {
// status.put("functionAccess", FunctionAccessStatus.OK.getCode());
// }
// } else {
// if (!FunctionAccessStatus.OK.getCode().equals(item)) {
// status.put("functionAccess", FunctionAccessStatus.FAILED.getCode());
// break;
// } else {
// status.put("functionAccess", FunctionAccessStatus.OK.getCode());
// }
// }
}
return status;
}
}

View File

@ -0,0 +1,51 @@
package com.fuyuanshen.web.service.device.status;
import com.fuyuanshen.app.domain.dto.AppRealTimeStatusDto;
import com.fuyuanshen.common.core.utils.StringUtils;
import com.fuyuanshen.common.redis.utils.RedisUtils;
import com.fuyuanshen.global.mqtt.listener.domain.FunctionAccessStatus;
import com.fuyuanshen.web.service.device.status.base.DeviceStatusRule;
import com.fuyuanshen.web.service.device.status.constants.DeviceTypeConstants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_KEY;
import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_TIMEOUT_KEY;
// 上传开机图片
@Slf4j
@Component
public class FunctionAccessStatusRule implements DeviceStatusRule {
@Override
public String getCommandType() {
return DeviceTypeConstants.TYPE_BJQ6170+"_1";
}
@Override
public boolean supports(String deviceType) {
return true; // 适用于所有设备类型
}
@Override
public Map<String, Object> getStatus(AppRealTimeStatusDto dto) {
Map<String, Object> status = new HashMap<>();
String functionAccess = RedisUtils.getCacheObject(
FUNCTION_ACCESS_KEY + dto.getDeviceImei());
log.info("FunctionAccessStatusRule:{}",functionAccess);
if(StringUtils.isBlank(functionAccess)){
String timeOut = RedisUtils.getCacheObject(FUNCTION_ACCESS_TIMEOUT_KEY + dto.getDeviceImei());
if("TIMEOUT".equals(timeOut)){
status.put("functionAccess", FunctionAccessStatus.TIMEOUT.getCode());
RedisUtils.deleteObject(FUNCTION_ACCESS_TIMEOUT_KEY + dto.getDeviceImei());
}else{
status.put("functionAccess", FunctionAccessStatus.OK.getCode());
}
}else{
status.put("functionAccess", functionAccess);
}
return status;
}
}

View File

@ -0,0 +1,19 @@
package com.fuyuanshen.web.service.device.status.base;
import com.fuyuanshen.app.domain.dto.AppRealTimeStatusDto;
import java.util.Map;
// 规则接口
public interface DeviceStatusRule {
/**
* 获取命令类型
* @return 命令类型
*/
String getCommandType();
boolean supports(String deviceType);
Map<String, Object> getStatus(AppRealTimeStatusDto statusDto);
}

View File

@ -0,0 +1,23 @@
package com.fuyuanshen.web.service.device.status.base;
import org.springframework.stereotype.Component;
import java.util.LinkedHashMap;
import java.util.List;
@Component
public class RealTimeStatusEngine {
private final LinkedHashMap<String, DeviceStatusRule> rulesMap = new LinkedHashMap<>();
public RealTimeStatusEngine(List<DeviceStatusRule> rules) {
rules.forEach(rule -> rulesMap.put(rule.getCommandType(), rule)
);
}
public DeviceStatusRule getDeviceStatusRule(String commandType) {
return rulesMap.get(commandType);
}
}

View File

@ -0,0 +1,6 @@
package com.fuyuanshen.web.service.device.status.constants;
public class DeviceTypeConstants {
public static final String TYPE_BJQ6170 = "BJQ6170";
}

View File

@ -1,12 +1,12 @@
package com.fuyuanshen.web.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fuyuanshen.app.service.AppDeviceBizService;
import com.fuyuanshen.equipment.domain.Device;
import com.fuyuanshen.equipment.domain.DeviceAssignments;
import com.fuyuanshen.equipment.mapper.DeviceAssignmentsMapper;
import com.fuyuanshen.equipment.mapper.DeviceMapper;
import com.fuyuanshen.web.service.WEBDeviceService;
import com.fuyuanshen.web.service.device.DeviceBizService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@ -21,7 +21,7 @@ import org.springframework.stereotype.Service;
@RequiredArgsConstructor
public class WEBDeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> implements WEBDeviceService {
private final AppDeviceBizService appDeviceService;
private final DeviceBizService appDeviceService;
private final DeviceAssignmentsMapper deviceAssignmentsMapper;

View File

@ -303,6 +303,6 @@ mqtt:
password: fys123456
url: tcp://47.107.152.87:1883
subClientId: fys_subClient
subTopic: A/#,B/#,worker/location/#
subTopic: worker/location/#
pubTopic: B/#
pubClientId: fys_pubClient

View File

@ -4,7 +4,7 @@ spring.servlet.multipart.location: /fys/server/temp
--- # 监控中心配置
spring.boot.admin.client:
# 增加客户端开关
enabled: true
enabled: false
url: http://localhost:9090/admin
instance:
service-host-type: IP
@ -16,7 +16,7 @@ spring.boot.admin.client:
--- # snail-job 配置
snail-job:
enabled: true
enabled: false
# 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务
group: "fys_group"
# SnailJob 接入验证令牌 详见 script/sql/ry_job.sql `sj_group_config`表
@ -52,9 +52,9 @@ spring:
driverClassName: com.mysql.cj.jdbc.Driver
# jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
# rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
url: jdbc:mysql://localhost:3306/fys_vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
url: jdbc:mysql://47.120.79.150:3306/fys-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
username: root
password: Jz_5623_cl1
password: Jq_123456#
# # 从库数据源
# slave:
# lazy: true
@ -101,7 +101,7 @@ spring:
spring.data:
redis:
# 地址
host: localhost
host: 47.120.79.150
# 端口默认为6379
port: 6379
# 数据库索引
@ -177,11 +177,14 @@ sms:
# 框架定义的厂商名称标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
supplier: alibaba
# 有些称为accessKey有些称之为apiKey也有称为sdkKey或者appId。
access-key-id: 您的accessKey
access-key-id: LTAI5tJdDNpZootsPQ5hdELx
# 称为accessSecret有些称之为apiSecret
access-key-secret: 您的accessKeySecret
signature: 您的短信签名
sdk-app-id: 您的sdkAppId
access-key-secret: mU4WtffcCXpHPz5tLwQpaGtLsJXONt
#模板ID 非必须配置如果使用sendMessage的快速发送需此配置
template-id: SMS_322180518
#模板变量 上述模板的变量
templateName: code
signature: 湖北星汉研创科技
config2:
# 厂商标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
supplier: tencent

View File

@ -232,7 +232,7 @@ xss:
# 如使用JDK21请直接使用虚拟线程 不要开启此配置
thread-pool:
# 是否开启线程池
enabled: false
enabled: true
# 队列最大长度
queueCapacity: 128
# 线程池维护线程所允许的空闲时间

Binary file not shown.

Before

Width:  |  Height:  |  Size: 397 B

After

Width:  |  Height:  |  Size: 263 B

View File

@ -41,4 +41,9 @@ public interface GlobalConstants {
* 三方认证 redis key
*/
String SOCIAL_AUTH_CODE_KEY = GLOBAL_REDIS_KEY + "social_auth_codes:";
String FUNCTION_ACCESS_KEY = GLOBAL_REDIS_KEY + "device:function_access:";
String FUNCTION_ACCESS_TIMEOUT_KEY = GLOBAL_REDIS_KEY + "device:function_access_timeout:";
}

View File

@ -0,0 +1,12 @@
package com.fuyuanshen.common.ratelimiter.annotation;// DeviceRedisKeyAnnotation.java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FunctionAccessAnnotation {
String value() default "";
long timeOut() default 30;
}

View File

@ -0,0 +1,13 @@
package com.fuyuanshen.common.ratelimiter.annotation;// DeviceRedisKeyAnnotation.java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FunctionAccessBatcAnnotation {
String value() default "";
long timeOut() default 30;
long batchMaxTimeOut() default 40;
}

View File

@ -0,0 +1,104 @@
package com.fuyuanshen.common.ratelimiter.aspectj;// DeviceRedisKeyAspect.java
import com.fuyuanshen.common.core.exception.ServiceException;
import com.fuyuanshen.common.core.utils.StringUtils;
import com.fuyuanshen.common.ratelimiter.annotation.FunctionAccessAnnotation;
import com.fuyuanshen.common.redis.utils.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import java.time.Duration;
import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_KEY;
import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_TIMEOUT_KEY;
@Slf4j
@Aspect
@Component
public class FunctionAccessAspect {
// 定义切点拦截带有DeviceRedisKeyAnnotation注解的方法
@Around("@annotation(functionAccessAnnotation)")
public Object addDeviceRedisKey(ProceedingJoinPoint joinPoint, FunctionAccessAnnotation functionAccessAnnotation) throws Throwable {
Object result;
String deviceImei = null;
// 获取方法参数查找设备ID
Object[] args = joinPoint.getArgs();
deviceImei = extractDeviceImei(args);
long timeout = functionAccessAnnotation.timeOut();
if (StringUtils.isNotBlank(deviceImei)) {
// 生成全局Redis key
String redisKey = generateDeviceRedisKey(deviceImei);
String cacheKey = RedisUtils.getCacheObject(redisKey);
if(StringUtils.isNotBlank(cacheKey) && "ACTIVE".equals(cacheKey)){
throw new ServiceException("设备已存在访问限制,请稍后再试", 500);
}
//
RedisUtils.setCacheObject(redisKey, "ACTIVE", Duration.ofSeconds(timeout));
}
// 执行原方法
result = joinPoint.proceed();
return result;
}
/**
* 从方法参数中提取设备ID
*/
private String extractDeviceImei(Object[] args) {
if (args == null || args.length == 0) {
return null;
}
for (Object arg : args) {
if (arg == null) continue;
// 如果参数本身就是设备ID (Long类型)
if (arg instanceof Long) {
return arg.toString();
}
// 如果参数是对象尝试获取deviceId字段
try {
// 使用反射获取deviceId字段
java.lang.reflect.Field[] fields = arg.getClass().getDeclaredFields();
for (java.lang.reflect.Field field : fields) {
if ("deviceImei".equalsIgnoreCase(field.getName()) ||
"device_imei".equalsIgnoreCase(field.getName())) {
field.setAccessible(true);
Object value = field.get(arg);
if (value != null) {
return value.toString();
}
}
}
// 尝试获取getId方法
try {
java.lang.reflect.Method getIdMethod = arg.getClass().getMethod("getDeviceImei");
Object value = getIdMethod.invoke(arg);
if (value != null) {
return value.toString();
}
} catch (Exception ignored) {}
} catch (Exception e) {
log.debug("从参数中提取设备ID时出错: {}", e.getMessage());
}
}
return null;
}
/**
* 生成设备的全局Redis key
*/
private String generateDeviceRedisKey(String deviceImei) {
return FUNCTION_ACCESS_KEY + deviceImei;
}
}

View File

@ -0,0 +1,183 @@
package com.fuyuanshen.common.ratelimiter.aspectj;// DeviceRedisKeyAspect.java
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import com.fuyuanshen.common.core.exception.ServiceException;
import com.fuyuanshen.common.core.utils.StringUtils;
import com.fuyuanshen.common.ratelimiter.annotation.FunctionAccessBatcAnnotation;
import com.fuyuanshen.common.redis.utils.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.util.List;
import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_KEY;
@Slf4j
@Aspect
@Component
public class FunctionAccessBatchAspect {
// 定义切点拦截带有DeviceRedisKeyAnnotation注解的方法
@Around("@annotation(functionAccessBatchAspect)")
public Object addDeviceRedisKey(ProceedingJoinPoint joinPoint, FunctionAccessBatcAnnotation functionAccessBatchAspect) throws Throwable {
Object result;
String batchId = null;
List<String> deviceImeiList = null;
// 获取方法参数查找设备ID
Object[] args = joinPoint.getArgs();
batchId = extractDeviceBatchId(args);
deviceImeiList = extractDeviceImeiList(args);
if (StringUtils.isNotBlank(batchId)) {
// 生成全局Redis key
String redisKey = generateDeviceRedisKey(batchId);
String cacheKey = RedisUtils.getCacheObject(redisKey);
if(StringUtils.isNotBlank(cacheKey) && "ACTIVE".equals(cacheKey)){
throw new ServiceException("设备已存在访问限制,请稍后再试", 500);
}
deviceImeiList.forEach(item->{
RedisUtils.setCacheObject(FUNCTION_ACCESS_KEY + item, "ACTIVE", Duration.ofSeconds(functionAccessBatchAspect.timeOut()));
});
String deviceImeiListStr = JSONUtil.toJsonStr(deviceImeiList);
RedisUtils.setCacheObject(redisKey, deviceImeiListStr , Duration.ofSeconds(functionAccessBatchAspect.batchMaxTimeOut()));
}
// 执行原方法
result = joinPoint.proceed();
return result;
}
/**
* 从方法参数中提取设备IMEI列表
*/
private List<String> extractDeviceImeiList(Object[] args) {
if (args == null || args.length == 0) {
return null;
}
for (Object arg : args) {
if (arg == null) continue;
// 如果参数本身就是List<String>类型
if (arg instanceof List) {
List<?> list = (List<?>) arg;
if (!list.isEmpty() && list.get(0) instanceof String) {
// 检查是否为deviceImeiList
return (List<String>) list;
}
}
// 如果参数是对象尝试获取deviceImeiList字段
try {
// 使用反射获取deviceImeiList字段
java.lang.reflect.Field[] fields = arg.getClass().getDeclaredFields();
for (java.lang.reflect.Field field : fields) {
if ("deviceImeiList".equalsIgnoreCase(field.getName()) ||
"device_imei_list".equalsIgnoreCase(field.getName()) ||
"deviceImeis".equalsIgnoreCase(field.getName()) ||
"device_imeis".equalsIgnoreCase(field.getName())) {
field.setAccessible(true);
Object value = field.get(arg);
if (value instanceof List) {
List<?> list = (List<?>) value;
if (!list.isEmpty() && list.get(0) instanceof String) {
return (List<String>) list;
}
}
}
}
// 尝试获取getDeviceImeiList方法
try {
java.lang.reflect.Method getDeviceImeiListMethod = arg.getClass().getMethod("getDeviceImeiList");
Object value = getDeviceImeiListMethod.invoke(arg);
if (value instanceof List) {
List<?> list = (List<?>) value;
if (!list.isEmpty() && list.get(0) instanceof String) {
return (List<String>) list;
}
}
} catch (Exception ignored) {}
// 尝试获取getDeviceImeis方法
try {
java.lang.reflect.Method getDeviceImeisMethod = arg.getClass().getMethod("getDeviceImeis");
Object value = getDeviceImeisMethod.invoke(arg);
if (value instanceof List) {
List<?> list = (List<?>) value;
if (!list.isEmpty() && list.get(0) instanceof String) {
return (List<String>) list;
}
}
} catch (Exception ignored) {}
} catch (Exception e) {
log.debug("从参数中提取设备IMEI列表时出错: {}", e.getMessage());
}
}
return null;
}
/**
* 从方法参数中提取设备ID
*/
private String extractDeviceBatchId(Object[] args) {
if (args == null || args.length == 0) {
return null;
}
for (Object arg : args) {
if (arg == null) continue;
// 如果参数本身就是设备ID (Long类型)
if (arg instanceof Long) {
return arg.toString();
}
// 如果参数是对象尝试获取deviceId字段
try {
// 使用反射获取deviceId字段
java.lang.reflect.Field[] fields = arg.getClass().getDeclaredFields();
for (java.lang.reflect.Field field : fields) {
if ("batchId".equalsIgnoreCase(field.getName()) ||
"batch_id".equalsIgnoreCase(field.getName())) {
field.setAccessible(true);
Object value = field.get(arg);
if (value != null) {
return value.toString();
}
}
}
// 尝试获取getId方法
try {
java.lang.reflect.Method getIdMethod = arg.getClass().getMethod("batchId");
Object value = getIdMethod.invoke(arg);
if (value != null) {
return value.toString();
}
} catch (Exception ignored) {}
} catch (Exception e) {
log.debug("从参数中提取批次号时出错: {}", e.getMessage());
}
}
return null;
}
/**
* 生成设备的全局Redis key
*/
private String generateDeviceRedisKey(String batchId) {
return FUNCTION_ACCESS_KEY + batchId;
}
}

View File

@ -25,6 +25,11 @@ public class AppPersonnelInfoBo extends BaseEntity {
*/
private Long id;
/**
* 设备IMEI
*/
private String deviceImei;
/**
* 设备id
*/

View File

@ -104,4 +104,7 @@ public class AppDeviceDetailVo {
* 告警状态(0解除告警1告警)
*/
private String alarmStatus;
// 灯光亮度
private String lightBrightness;
}

View File

@ -104,4 +104,41 @@ public class AppDeviceShareDetailVo implements Serializable {
* 发送信息
*/
private String sendMsg;
//设备主灯档位
private String mainLightMode;
//激光灯档位
private String laserLightMode;
//电量百分比
private String batteryPercentage;
//充电状态0没有充电1正在充电2为已充满
private String chargeState;
//电池剩余续航时间200分钟
private String batteryRemainingTime;
/**
* 在线状态(0离线1在线)
*/
private Integer onlineStatus;
// 经度
private String longitude;
// 纬度
private String latitude;
// 逆解析地址
private String address;
/**
* 告警状态(0解除告警1告警)
*/
private String alarmStatus;
// 灯光亮度
private String lightBrightness;
}

View File

@ -57,6 +57,12 @@ public class AppDeviceShareVo implements Serializable {
@ExcelProperty(value = "手机号")
private String phonenumber;
/**
* 他人分享手机号
*/
@ExcelProperty(value = "手机号")
private String otherPhonenumber;
/**
* 功能权限1灯光模式2激光模式3开机画面4人员信息登记5发送信息6产品信息
以逗号分隔
@ -65,6 +71,11 @@ public class AppDeviceShareVo implements Serializable {
@ExcelDictFormat(readConverterExp = "1=灯光模式2激光模式3开机画面4人员信息登记5发送信息6产品信息")
private String permission;
/**
* 设备类型
*/
private String typeName;
/**
* 备注
*/
@ -73,4 +84,29 @@ public class AppDeviceShareVo implements Serializable {
// 设备图片
private String devicePic;
/**
* 在线状态(0离线1在线)
*/
private Integer onlineStatus;
/**
* 电量 百分比
*/
private String battery;
/**
* 纬度
*/
private String latitude;
/**
* 经度
*/
private String longitude;
/**
* 告警状态(0解除告警1告警)
*/
private String alarmStatus;
}

View File

@ -1,5 +1,6 @@
package com.fuyuanshen.app.mapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fuyuanshen.app.domain.AppDeviceShare;
@ -16,4 +17,6 @@ import org.apache.ibatis.annotations.Param;
*/
public interface AppDeviceShareMapper extends BaseMapperPlus<AppDeviceShare, AppDeviceShareVo> {
IPage<AppDeviceShareVo> otherDeviceShareList(@Param("bo") AppDeviceShareBo bo, Page<AppDeviceShareVo> page);
Page<AppDeviceShareVo> selectAppDeviceShareList(@Param("bo") AppDeviceShareBo bo,Page<AppDeviceShareVo> page);
}

View File

@ -5,6 +5,46 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<mapper namespace="com.fuyuanshen.app.mapper.AppDeviceShareMapper">
<select id="otherDeviceShareList" resultType="com.fuyuanshen.app.domain.vo.AppDeviceShareVo">
SELECT * FROM app_device_share a where a.phonenumber = #{bo.phonenumber}
select d.device_name,
d.device_mac,
d.device_sn,
d.device_imei,
d.device_pic,
dt.type_name,
dt.communication_mode,
dt.model_dictionary detailPageUrl,
d.bluetooth_name,
c.binding_time,
ad.*,u.user_name otherPhonenumber
from
app_device_share ad
left join device d on ad.device_id = d.id
left join app_user u on ad.create_by = u.user_id
inner join device_type dt on d.device_type = dt.id
inner join app_device_bind_record c on d.id = c.device_id
where ad.phonenumber = #{bo.phonenumber}
</select>
<select id="selectAppDeviceShareList" resultType="com.fuyuanshen.app.domain.vo.AppDeviceShareVo">
select d.device_name,
d.device_mac,
d.device_sn,
d.device_imei,
d.device_pic,
dt.type_name,
dt.communication_mode,
d.bluetooth_name,
dt.model_dictionary detailPageUrl,
c.binding_time,
ad.*,u.user_name otherPhonenumber
from
app_device_share ad
left join device d on ad.device_id = d.id
left join app_user u on ad.create_by = u.user_id
inner join device_type dt on d.device_type = dt.id
inner join app_device_bind_record c on d.id = c.device_id
where ad.create_by = #{bo.createBy}
<if test="bo.deviceId != null">
and ad.device_id = #{bo.deviceId}
</if>
</select>
</mapper>

View File

@ -0,0 +1,105 @@
package com.fuyuanshen.equipment.controller;
import java.util.List;
import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import com.fuyuanshen.common.idempotent.annotation.RepeatSubmit;
import com.fuyuanshen.common.log.annotation.Log;
import com.fuyuanshen.common.web.core.BaseController;
import com.fuyuanshen.common.mybatis.core.page.PageQuery;
import com.fuyuanshen.common.core.domain.R;
import com.fuyuanshen.common.core.validate.AddGroup;
import com.fuyuanshen.common.core.validate.EditGroup;
import com.fuyuanshen.common.log.enums.BusinessType;
import com.fuyuanshen.common.excel.utils.ExcelUtil;
import com.fuyuanshen.equipment.domain.vo.DeviceRepairRecordsVo;
import com.fuyuanshen.equipment.domain.bo.DeviceRepairRecordsBo;
import com.fuyuanshen.equipment.service.IDeviceRepairRecordsService;
import com.fuyuanshen.common.mybatis.core.page.TableDataInfo;
/**
* 设备维修记录
*
* @author Lion Li
* @date 2025-08-08
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/equipment/repairRecords")
public class DeviceRepairRecordsController extends BaseController {
private final IDeviceRepairRecordsService deviceRepairRecordsService;
/**
* 查询设备维修记录列表
*/
@SaCheckPermission("equipment:repairRecords:list")
@GetMapping("/list")
public TableDataInfo<DeviceRepairRecordsVo> list(DeviceRepairRecordsBo bo, PageQuery pageQuery) {
return deviceRepairRecordsService.queryPageList(bo, pageQuery);
}
/**
* 导出设备维修记录列表
*/
@SaCheckPermission("equipment:repairRecords:export")
@Log(title = "设备维修记录", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(DeviceRepairRecordsBo bo, HttpServletResponse response) {
List<DeviceRepairRecordsVo> list = deviceRepairRecordsService.queryList(bo);
ExcelUtil.exportExcel(list, "设备维修记录", DeviceRepairRecordsVo.class, response);
}
/**
* 获取设备维修记录详细信息
*
* @param recordId 主键
*/
@SaCheckPermission("equipment:repairRecords:query")
@GetMapping("/{recordId}")
public R<DeviceRepairRecordsVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long recordId) {
return R.ok(deviceRepairRecordsService.queryById(recordId));
}
/**
* 新增设备维修记录
*/
@SaCheckPermission("equipment:repairRecords:add")
@Log(title = "设备维修记录", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody DeviceRepairRecordsBo bo) {
return toAjax(deviceRepairRecordsService.insertByBo(bo));
}
/**
* 修改设备维修记录
*/
@SaCheckPermission("equipment:repairRecords:edit")
@Log(title = "设备维修记录", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody DeviceRepairRecordsBo bo) {
return toAjax(deviceRepairRecordsService.updateByBo(bo));
}
/**
* 删除设备维修记录
*
* @param recordIds 主键串
*/
@SaCheckPermission("equipment:repairRecords:remove")
@Log(title = "设备维修记录", businessType = BusinessType.DELETE)
@DeleteMapping("/{recordIds}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] recordIds) {
return toAjax(deviceRepairRecordsService.deleteWithValidByIds(List.of(recordIds), true));
}
}

View File

@ -0,0 +1,56 @@
package com.fuyuanshen.equipment.domain;
import com.fuyuanshen.common.tenant.core.TenantEntity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
/**
* 设备分组对象 device_group
*
* @author Lion Li
* @date 2025-08-08
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("device_group")
public class DeviceGroup extends TenantEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 分组名称
*/
private String groupName;
/**
* 状态0-禁用1-正常
*/
private Long status;
/**
* 父分组ID
*/
private Long parentId;
/**
* 完整分组路径
*/
private String fullPath;
/**
* 删除标记0-未删除1-已删除
*/
private Long isDeleted;
}

View File

@ -0,0 +1,58 @@
package com.fuyuanshen.equipment.domain;
import com.fuyuanshen.common.tenant.core.TenantEntity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serial;
/**
* 设备维修记录对象 device_repair_records
*
* @author Lion Li
* @date 2025-08-08
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("device_repair_records")
public class DeviceRepairRecords extends TenantEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 维修记录ID
*/
@TableId(value = "record_id")
private Long recordId;
/**
* 设备ID
*/
private String deviceId;
/**
* 维修时间
*/
private Date repairTime;
/**
* 维修部位
*/
private String repairPart;
/**
* 维修原因
*/
private String repairReason;
/**
* 维修人员
*/
private String repairPerson;
}

View File

@ -65,4 +65,11 @@ public class DeviceType extends TenantEntity {
BeanUtil.copyProperties(source, this, CopyOptions.create().setIgnoreNullValue(true));
}
/**
* 型号字典用于APP页面跳转
*/
@Schema(name = "型号字典用于APP页面跳转")
private String modelDictionary;
}

View File

@ -0,0 +1,65 @@
package com.fuyuanshen.equipment.domain.bo;
import com.fuyuanshen.common.core.validate.AddGroup;
import com.fuyuanshen.common.core.validate.EditGroup;
import com.fuyuanshen.equipment.domain.DeviceGroup;
import com.fuyuanshen.common.mybatis.core.domain.BaseEntity;
import io.github.linpeilie.annotations.AutoMapper;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
/**
* 设备分组业务对象 device_group
*
* @author Lion Li
* @date 2025-08-08
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = DeviceGroup.class, reverseConvertGenerate = false)
public class DeviceGroupBo extends BaseEntity {
/**
* 主键ID
*/
// @NotNull(message = "主键ID不能为空", groups = { EditGroup.class })
private Long id;
/**
* 分组名称
*/
@NotBlank(message = "分组名称不能为空", groups = { AddGroup.class, EditGroup.class })
private String groupName;
/**
* 状态0-禁用1-正常
*/
// @NotNull(message = "状态0-禁用1-正常不能为空", groups = { AddGroup.class, EditGroup.class })
private Long status;
/**
* 父分组ID
*/
private Long parentId;
/**
* 完整分组路径
*/
private String fullPath;
/**
* 删除标记0-未删除1-已删除
*/
// @NotNull(message = "删除标记0-未删除1-已删除不能为空", groups = { AddGroup.class, EditGroup.class })
private Long isDeleted;
@Schema(name = "页码", example = "1")
private Integer pageNum = 1;
@Schema(name = "每页数据量", example = "10")
private Integer pageSize = 10;
}

View File

@ -0,0 +1,62 @@
package com.fuyuanshen.equipment.domain.bo;
import com.fuyuanshen.common.core.validate.AddGroup;
import com.fuyuanshen.common.core.validate.EditGroup;
import com.fuyuanshen.equipment.domain.DeviceRepairRecords;
import com.fuyuanshen.common.mybatis.core.domain.BaseEntity;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
/**
* 设备维修记录业务对象 device_repair_records
*
* @author Lion Li
* @date 2025-08-08
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = DeviceRepairRecords.class, reverseConvertGenerate = false)
public class DeviceRepairRecordsBo extends BaseEntity {
/**
* 维修记录ID
*/
@NotNull(message = "维修记录ID不能为空", groups = { EditGroup.class })
private Long recordId;
/**
* 设备ID
*/
@NotBlank(message = "设备ID不能为空", groups = { AddGroup.class, EditGroup.class })
private String deviceId;
/**
* 维修时间
*/
@NotNull(message = "维修时间不能为空", groups = { AddGroup.class, EditGroup.class })
private Date repairTime;
/**
* 维修部位
*/
@NotBlank(message = "维修部位不能为空", groups = { AddGroup.class, EditGroup.class })
private String repairPart;
/**
* 维修原因
*/
@NotBlank(message = "维修原因不能为空", groups = { AddGroup.class, EditGroup.class })
private String repairReason;
/**
* 维修人员
*/
@NotBlank(message = "维修人员不能为空", groups = { AddGroup.class, EditGroup.class })
private String repairPerson;
}

View File

@ -14,9 +14,16 @@ public class AppDeviceSendMsgBo {
private List<Long> deviceIds;
private List<String> deviceImeiList;
/**
* 下发指令
*/
private String instructValue;
/**
* 批次号
*/
private String batchId;
}

View File

@ -1,13 +1,6 @@
package com.fuyuanshen.equipment.domain.form;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fuyuanshen.common.tenant.core.TenantEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
/**
@ -36,4 +29,10 @@ public class DeviceTypeForm {
@Schema(name = "通讯方式", example = "0:4G;1:蓝牙")
private String communicationMode;
/**
* 型号字典用于APP页面跳转
*/
@Schema(name = "型号字典用于APP页面跳转")
private String modelDictionary;
}

View File

@ -81,4 +81,9 @@ public class AppDeviceVo implements Serializable {
* 告警状态(0解除告警1告警)
*/
private String alarmStatus;
/**
* 设备详情页面
*/
private String detailPageUrl;
}

View File

@ -0,0 +1,79 @@
package com.fuyuanshen.equipment.domain.vo;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.fuyuanshen.equipment.domain.DeviceGroup;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import com.fuyuanshen.common.excel.annotation.ExcelDictFormat;
import com.fuyuanshen.common.excel.convert.ExcelDictConvert;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
* 设备分组视图对象 device_group
*
* @author Lion Li
* @date 2025-08-08
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = DeviceGroup.class)
public class DeviceGroupVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@ExcelProperty(value = "主键ID")
private Long id;
/**
* 分组名称
*/
@ExcelProperty(value = "分组名称")
private String groupName;
/**
* 状态0-禁用1-正常
*/
@ExcelProperty(value = "状态0-禁用1-正常")
private String status;
/**
* 父分组ID
*/
@ExcelProperty(value = "父分组ID")
private Long parentId;
/**
* 完整分组路径
*/
@ExcelProperty(value = "完整分组路径")
private String fullPath;
/**
* 删除标记0-未删除1-已删除
*/
@ExcelProperty(value = "删除标记0-未删除1-已删除")
private Long isDeleted;
/**
* 嵌套子分组
*/
private List<DeviceGroupVo> children;
/**
* 创建时间
*/
private String createTime;
}

View File

@ -0,0 +1,70 @@
package com.fuyuanshen.equipment.domain.vo;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fuyuanshen.equipment.domain.DeviceRepairRecords;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import com.fuyuanshen.common.excel.annotation.ExcelDictFormat;
import com.fuyuanshen.common.excel.convert.ExcelDictConvert;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 设备维修记录视图对象 device_repair_records
*
* @author Lion Li
* @date 2025-08-08
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = DeviceRepairRecords.class)
public class DeviceRepairRecordsVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 维修记录ID
*/
@ExcelProperty(value = "维修记录ID")
private Long recordId;
/**
* 设备ID
*/
@ExcelProperty(value = "设备ID")
private String deviceId;
/**
* 维修时间
*/
@ExcelProperty(value = "维修时间")
private Date repairTime;
/**
* 维修部位
*/
@ExcelProperty(value = "维修部位")
private String repairPart;
/**
* 维修原因
*/
@ExcelProperty(value = "维修原因")
private String repairReason;
/**
* 维修人员
*/
@ExcelProperty(value = "维修人员")
private String repairPerson;
}

View File

@ -0,0 +1,72 @@
package com.fuyuanshen.equipment.enums;
/**
* 灯光模式枚举
*/
public enum LightModeEnum {
/**
* 关灯模式
*/
OFF(0, "关灯"),
/**
* 强光模式
*/
HIGH_BEAM(1, "开启强光模式"),
/**
* 弱光模式
*/
LOW_BEAM(2, "开启弱光模式"),
/**
* 爆闪模式
*/
STROBE(3, "开启爆闪模式"),
/**
* 泛光模式
*/
FLOOD(4, "开启泛光模式");
private final Integer code;
private final String name;
LightModeEnum(Integer code, String name) {
this.code = code;
this.name = name;
}
public Integer getCode() {
return code;
}
public String getName() {
return name;
}
/**
* 根据编号获取枚举
* @param code 编号
* @return 对应的枚举值
*/
public static LightModeEnum getByCode(Integer code) {
for (LightModeEnum mode : LightModeEnum.values()) {
if (mode.getCode().equals(code)) {
return mode;
}
}
return null;
}
/**
* 根据编号获取名称
* @param code 编号
* @return 对应的名称
*/
public static String getNameByCode(Integer code) {
LightModeEnum mode = getByCode(code);
return mode != null ? mode.getName() : null;
}
}

View File

@ -0,0 +1,38 @@
package com.fuyuanshen.equipment.mapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fuyuanshen.equipment.domain.Device;
import com.fuyuanshen.equipment.domain.DeviceGroup;
import com.fuyuanshen.equipment.domain.bo.DeviceGroupBo;
import com.fuyuanshen.equipment.domain.vo.DeviceGroupVo;
import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 设备分组Mapper接口
*
* @author Lion Li
* @date 2025-08-08
*/
public interface DeviceGroupMapper extends BaseMapperPlus<DeviceGroup, DeviceGroupVo> {
/**
* 查询设备分组列表
*
* @param bo 设备分组
* @return 设备分组
*/
IPage<DeviceGroup> selectRootGroups(@Param("bo") DeviceGroupBo bo, Page<Device> page);
/**
* 查询子分组
*
* @param id
* @return
*/
List<DeviceGroup> selectByParentId(Long id);
}

View File

@ -66,4 +66,6 @@ public interface DeviceMapper extends BaseMapper<Device> {
* @return
*/
List<Device> findByOriginalDeviceId(Long originalDeviceId);
AppDeviceVo getDeviceInfo(@Param("deviceMac") String deviceMac);
}

View File

@ -0,0 +1,15 @@
package com.fuyuanshen.equipment.mapper;
import com.fuyuanshen.equipment.domain.DeviceRepairRecords;
import com.fuyuanshen.equipment.domain.vo.DeviceRepairRecordsVo;
import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus;
/**
* 设备维修记录Mapper接口
*
* @author Lion Li
* @date 2025-08-08
*/
public interface DeviceRepairRecordsMapper extends BaseMapperPlus<DeviceRepairRecords, DeviceRepairRecordsVo> {
}

View File

@ -0,0 +1,59 @@
package com.fuyuanshen.equipment.service;
import com.fuyuanshen.common.mybatis.core.page.TableDataInfo;
import com.fuyuanshen.equipment.domain.vo.DeviceGroupVo;
import com.fuyuanshen.equipment.domain.bo.DeviceGroupBo;
import java.util.Collection;
import java.util.List;
/**
* 设备分组Service接口
*
* @author Lion Li
* @date 2025-08-08
*/
public interface IDeviceGroupService {
/**
* 查询设备分组
*
* @param id 主键
* @return 设备分组
*/
DeviceGroupVo queryById(Long id);
/**
* 查询符合条件的设备分组列表
*
* @param bo 查询条件
* @return 设备分组列表
*/
List<DeviceGroupVo> queryList(DeviceGroupBo bo);
/**
* 新增设备分组
*
* @param bo 设备分组
* @return 是否新增成功
*/
Boolean insertByBo(DeviceGroupBo bo);
/**
* 修改设备分组
*
* @param bo 设备分组
* @return 是否修改成功
*/
Boolean updateByBo(DeviceGroupBo bo);
/**
* 校验并批量删除设备分组信息
*
* @param ids 待删除的主键集合
* @param isValid 是否进行有效性校验
* @return 是否删除成功
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
}

View File

@ -0,0 +1,68 @@
package com.fuyuanshen.equipment.service;
import com.fuyuanshen.equipment.domain.vo.DeviceRepairRecordsVo;
import com.fuyuanshen.equipment.domain.bo.DeviceRepairRecordsBo;
import com.fuyuanshen.common.mybatis.core.page.TableDataInfo;
import com.fuyuanshen.common.mybatis.core.page.PageQuery;
import java.util.Collection;
import java.util.List;
/**
* 设备维修记录Service接口
*
* @author Lion Li
* @date 2025-08-08
*/
public interface IDeviceRepairRecordsService {
/**
* 查询设备维修记录
*
* @param recordId 主键
* @return 设备维修记录
*/
DeviceRepairRecordsVo queryById(Long recordId);
/**
* 分页查询设备维修记录列表
*
* @param bo 查询条件
* @param pageQuery 分页参数
* @return 设备维修记录分页列表
*/
TableDataInfo<DeviceRepairRecordsVo> queryPageList(DeviceRepairRecordsBo bo, PageQuery pageQuery);
/**
* 查询符合条件的设备维修记录列表
*
* @param bo 查询条件
* @return 设备维修记录列表
*/
List<DeviceRepairRecordsVo> queryList(DeviceRepairRecordsBo bo);
/**
* 新增设备维修记录
*
* @param bo 设备维修记录
* @return 是否新增成功
*/
Boolean insertByBo(DeviceRepairRecordsBo bo);
/**
* 修改设备维修记录
*
* @param bo 设备维修记录
* @return 是否修改成功
*/
Boolean updateByBo(DeviceRepairRecordsBo bo);
/**
* 校验并批量删除设备维修记录信息
*
* @param ids 待删除的主键集合
* @param isValid 是否进行有效性校验
* @return 是否删除成功
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
}

View File

@ -0,0 +1,172 @@
package com.fuyuanshen.equipment.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fuyuanshen.common.core.domain.R;
import com.fuyuanshen.common.core.utils.MapstructUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.fuyuanshen.common.mybatis.core.page.TableDataInfo;
import com.fuyuanshen.common.satoken.utils.LoginHelper;
import com.fuyuanshen.equipment.domain.Device;
import com.fuyuanshen.equipment.domain.DeviceTypeGrants;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import com.fuyuanshen.equipment.domain.bo.DeviceGroupBo;
import com.fuyuanshen.equipment.domain.vo.DeviceGroupVo;
import com.fuyuanshen.equipment.domain.DeviceGroup;
import com.fuyuanshen.equipment.mapper.DeviceGroupMapper;
import com.fuyuanshen.equipment.service.IDeviceGroupService;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Map;
import java.util.Collection;
import java.util.stream.Collectors;
/**
* 设备分组Service业务层处理
*
* @author Lion Li
* @date 2025-08-08
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class DeviceGroupServiceImpl implements IDeviceGroupService {
private final DeviceGroupMapper baseMapper;
/**
* 查询设备分组
*
* @param id 主键
* @return 设备分组
*/
@Override
public DeviceGroupVo queryById(Long id) {
return baseMapper.selectVoById(id);
}
/**
* 查询符合条件的设备分组列表
*
* @param bo 查询条件
* @return 设备分组列表
*/
@Override
public List<DeviceGroupVo> queryList(DeviceGroupBo bo) {
Page<Device> page = new Page<>(bo.getPageNum(), bo.getPageSize());
// 1. 查询顶级分组parent_id为null
IPage<DeviceGroup> rootGroups = baseMapper.selectRootGroups(bo, page);
List<DeviceGroup> records = rootGroups.getRecords();
// 2. 递归构建树形结构
return records.stream()
.map(this::buildGroupTree)
.collect(Collectors.toList());
}
private DeviceGroupVo buildGroupTree(DeviceGroup group) {
DeviceGroupVo vo = convertToVO(group);
// 递归查询子分组
List<DeviceGroup> children = baseMapper.selectByParentId(group.getId());
vo.setChildren(children.stream()
.map(this::buildGroupTree)
.collect(Collectors.toList()));
return vo;
}
private DeviceGroupVo convertToVO(DeviceGroup group) {
DeviceGroupVo vo = new DeviceGroupVo();
vo.setId(group.getId());
vo.setGroupName(group.getGroupName());
vo.setStatus(group.getStatus() == 1 ? "正常" : "禁用");
vo.setCreateTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(group.getCreateTime()));
return vo;
}
private LambdaQueryWrapper<DeviceGroup> buildQueryWrapper(DeviceGroupBo bo) {
Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<DeviceGroup> lqw = Wrappers.lambdaQuery();
lqw.orderByAsc(DeviceGroup::getId);
lqw.like(StringUtils.isNotBlank(bo.getGroupName()), DeviceGroup::getGroupName, bo.getGroupName());
lqw.eq(bo.getStatus() != null, DeviceGroup::getStatus, bo.getStatus());
lqw.eq(bo.getParentId() != null, DeviceGroup::getParentId, bo.getParentId());
lqw.eq(StringUtils.isNotBlank(bo.getFullPath()), DeviceGroup::getFullPath, bo.getFullPath());
lqw.eq(bo.getIsDeleted() != null, DeviceGroup::getIsDeleted, bo.getIsDeleted());
return lqw;
}
/**
* 新增设备分组
*
* @param bo 设备分组
* @return 是否新增成功
*/
@Override
public Boolean insertByBo(DeviceGroupBo bo) {
// 验证分组名称唯一性
DeviceGroup deviceGroup = baseMapper.selectOne(new QueryWrapper<DeviceGroup>().eq("group_name", bo.getGroupName()));
if (deviceGroup != null) {
throw new RuntimeException("分组名称已存在,请勿重复添加!!!");
}
// 验证父分组是否存在如果提供了parentId
DeviceGroup pDeviceGroup = baseMapper.selectById(bo.getParentId());
if (bo.getParentId() != null && pDeviceGroup == null) {
throw new RuntimeException("父分组不存在!!!");
}
DeviceGroup add = MapstructUtils.convert(bo, DeviceGroup.class);
// validEntityBeforeSave(add);
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setId(add.getId());
}
return flag;
}
/**
* 修改设备分组
*
* @param bo 设备分组
* @return 是否修改成功
*/
@Override
public Boolean updateByBo(DeviceGroupBo bo) {
DeviceGroup update = MapstructUtils.convert(bo, DeviceGroup.class);
validEntityBeforeSave(update);
return baseMapper.updateById(update) > 0;
}
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(DeviceGroup entity) {
// TODO 做一些数据校验,如唯一约束
}
/**
* 校验并批量删除设备分组信息
*
* @param ids 待删除的主键集合
* @param isValid 是否进行有效性校验
* @return 是否删除成功
*/
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid) {
// TODO 做一些业务上的校验,判断是否需要校验
}
return baseMapper.deleteByIds(ids) > 0;
}
}

View File

@ -0,0 +1,136 @@
package com.fuyuanshen.equipment.service.impl;
import com.fuyuanshen.common.core.utils.MapstructUtils;
import com.fuyuanshen.common.mybatis.core.page.TableDataInfo;
import com.fuyuanshen.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import com.fuyuanshen.equipment.domain.bo.DeviceRepairRecordsBo;
import com.fuyuanshen.equipment.domain.vo.DeviceRepairRecordsVo;
import com.fuyuanshen.equipment.domain.DeviceRepairRecords;
import com.fuyuanshen.equipment.mapper.DeviceRepairRecordsMapper;
import com.fuyuanshen.equipment.service.IDeviceRepairRecordsService;
import java.util.List;
import java.util.Map;
import java.util.Collection;
/**
* 设备维修记录Service业务层处理
*
* @author Lion Li
* @date 2025-08-08
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class DeviceRepairRecordsServiceImpl implements IDeviceRepairRecordsService {
private final DeviceRepairRecordsMapper baseMapper;
/**
* 查询设备维修记录
*
* @param recordId 主键
* @return 设备维修记录
*/
@Override
public DeviceRepairRecordsVo queryById(Long recordId){
return baseMapper.selectVoById(recordId);
}
/**
* 分页查询设备维修记录列表
*
* @param bo 查询条件
* @param pageQuery 分页参数
* @return 设备维修记录分页列表
*/
@Override
public TableDataInfo<DeviceRepairRecordsVo> queryPageList(DeviceRepairRecordsBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<DeviceRepairRecords> lqw = buildQueryWrapper(bo);
Page<DeviceRepairRecordsVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 查询符合条件的设备维修记录列表
*
* @param bo 查询条件
* @return 设备维修记录列表
*/
@Override
public List<DeviceRepairRecordsVo> queryList(DeviceRepairRecordsBo bo) {
LambdaQueryWrapper<DeviceRepairRecords> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<DeviceRepairRecords> buildQueryWrapper(DeviceRepairRecordsBo bo) {
Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<DeviceRepairRecords> lqw = Wrappers.lambdaQuery();
lqw.orderByAsc(DeviceRepairRecords::getRecordId);
lqw.eq(StringUtils.isNotBlank(bo.getDeviceId()), DeviceRepairRecords::getDeviceId, bo.getDeviceId());
lqw.eq(bo.getRepairTime() != null, DeviceRepairRecords::getRepairTime, bo.getRepairTime());
lqw.eq(StringUtils.isNotBlank(bo.getRepairPart()), DeviceRepairRecords::getRepairPart, bo.getRepairPart());
lqw.eq(StringUtils.isNotBlank(bo.getRepairReason()), DeviceRepairRecords::getRepairReason, bo.getRepairReason());
lqw.eq(StringUtils.isNotBlank(bo.getRepairPerson()), DeviceRepairRecords::getRepairPerson, bo.getRepairPerson());
return lqw;
}
/**
* 新增设备维修记录
*
* @param bo 设备维修记录
* @return 是否新增成功
*/
@Override
public Boolean insertByBo(DeviceRepairRecordsBo bo) {
DeviceRepairRecords add = MapstructUtils.convert(bo, DeviceRepairRecords.class);
validEntityBeforeSave(add);
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setRecordId(add.getRecordId());
}
return flag;
}
/**
* 修改设备维修记录
*
* @param bo 设备维修记录
* @return 是否修改成功
*/
@Override
public Boolean updateByBo(DeviceRepairRecordsBo bo) {
DeviceRepairRecords update = MapstructUtils.convert(bo, DeviceRepairRecords.class);
validEntityBeforeSave(update);
return baseMapper.updateById(update) > 0;
}
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(DeviceRepairRecords entity){
//TODO 做一些数据校验,如唯一约束
}
/**
* 校验并批量删除设备维修记录信息
*
* @param ids 待删除的主键集合
* @param isValid 是否进行有效性校验
* @return 是否删除成功
*/
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if(isValid){
//TODO 做一些业务上的校验,判断是否需要校验
}
return baseMapper.deleteByIds(ids) > 0;
}
}

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fuyuanshen.equipment.mapper.DeviceGroupMapper">
<!-- 查询顶级分组 -->
<select id="selectRootGroups" resultType="DeviceGroup">
SELECT * FROM device_group
WHERE parent_id IS NULL
<if test="bo.groupName != null and bo.groupName != ''">
AND group_name LIKE CONCAT('%', #{bo.groupName}, '%')
</if>
<if test="bo.status != null">
AND status = #{bo.status}
</if>
ORDER BY create_time DESC
</select>
<!-- 根据父ID查询子分组 -->
<select id="selectByParentId" resultType="DeviceGroup">
SELECT *
FROM device_group
WHERE parent_id = #{parentId}
ORDER BY create_time DESC
</select>
</mapper>

View File

@ -148,6 +148,7 @@
dt.type_name,
dt.communication_mode,
d.bluetooth_name,
dt.model_dictionary detailPageUrl,
c.binding_time
from device d
inner join device_type dt on d.device_type = dt.id
@ -177,6 +178,7 @@
d.device_pic,
dt.type_name,
dt.communication_mode,
dt.model_dictionary detailPageUrl,
d.bluetooth_name
from device d
inner join device_type dt on d.device_type = dt.id
@ -210,5 +212,20 @@
FROM device
WHERE original_device_id = #{originalDeviceId}
</select>
<select id="getDeviceInfo" resultType="com.fuyuanshen.equipment.domain.vo.AppDeviceVo">
select d.id, d.device_name, d.device_name,
d.device_name,
d.device_mac,
d.device_sn,
d.device_imei,
d.device_pic,
dt.type_name,
dt.communication_mode,
d.bluetooth_name,
dt.model_dictionary detailPageUrl
from device d
inner join device_type dt on d.device_type = dt.id
where d.device_mac = #{deviceMac}
</select>
</mapper>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fuyuanshen.equipment.mapper.DeviceRepairRecordsMapper">
</mapper>