0
0

Merge remote-tracking branch 'upstream/dyf-device' into main-dyf

This commit is contained in:
2025-08-25 14:32:19 +08:00
36 changed files with 1673 additions and 293 deletions

View File

@ -10,6 +10,7 @@ import com.fuyuanshen.equipment.domain.form.DeviceForm;
import com.fuyuanshen.equipment.domain.query.DeviceQueryCriteria;
import com.fuyuanshen.equipment.domain.vo.AppDeviceVo;
import com.fuyuanshen.equipment.domain.vo.CustomerVo;
import com.fuyuanshen.equipment.domain.vo.WebDeviceVo;
import java.io.IOException;
import java.util.List;
@ -29,4 +30,12 @@ public interface WEBDeviceService extends IService<Device> {
*/
int webUnBindDevice(Long id, Long userId);
/**
* WEB端设备详情
*
* @param id
* @return
*/
WebDeviceVo getDevice(Long id);
}

View File

@ -1,16 +1,19 @@
package com.fuyuanshen.web.service.device;
import cn.hutool.core.bean.BeanUtil;
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.fuyuanshen.app.domain.AppPersonnelInfo;
import com.fuyuanshen.app.domain.AppPersonnelInfoRecords;
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.domain.vo.AppPersonnelInfoVo;
import com.fuyuanshen.app.mapper.AppPersonnelInfoMapper;
import com.fuyuanshen.app.mapper.AppPersonnelInfoRecordsMapper;
import com.fuyuanshen.common.core.constant.GlobalConstants;
import com.fuyuanshen.common.core.exception.ServiceException;
import com.fuyuanshen.common.core.utils.*;
@ -50,6 +53,7 @@ public class DeviceBJQBizService {
private final DeviceMapper deviceMapper;
private final AppPersonnelInfoMapper appPersonnelInfoMapper;
private final AppPersonnelInfoRecordsMapper appPersonnelInfoRecordsMapper;
private final DeviceTypeMapper deviceTypeMapper;
private final MqttGateway mqttGateway;
private final DeviceLogMapper deviceLogMapper;
@ -242,11 +246,18 @@ public class DeviceBJQBizService {
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);
recordDeviceLog(deviceId, deviceObj.getDeviceName(), "人员信息登记", JSON.toJSONString(bo), AppLoginHelper.getUserId());
String logContent = "单位:"+bo.getUnitName()+",职位:"+bo.getPosition()+",姓名:"+bo.getName()+",ID"+bo.getCode();
recordDeviceLog(deviceId, deviceObj.getDeviceName(), "人员信息登记", logContent, AppLoginHelper.getUserId());
if (ObjectUtils.length(appPersonnelInfoVos) == 0) {
AppPersonnelInfo appPersonnelInfo = MapstructUtils.convert(bo, AppPersonnelInfo.class);
return appPersonnelInfoMapper.insertOrUpdate(appPersonnelInfo);
appPersonnelInfoMapper.insertOrUpdate(appPersonnelInfo);
AppPersonnelInfoRecords appPersonnelInfoRecords = new AppPersonnelInfoRecords();
BeanUtil.copyProperties(appPersonnelInfo, appPersonnelInfoRecords);
appPersonnelInfoRecords.setId(null);
appPersonnelInfoRecords.setPersonnelId(appPersonnelInfo.getId());
appPersonnelInfoRecordsMapper.insert(appPersonnelInfoRecords);
} else {
UpdateWrapper<AppPersonnelInfo> uw = new UpdateWrapper<>();
uw.eq("device_id", deviceId)
@ -254,10 +265,52 @@ public class DeviceBJQBizService {
.set("position", bo.getPosition())
.set("unit_name", bo.getUnitName())
.set("code", bo.getCode());
return appPersonnelInfoMapper.update(null, uw) > 0;
appPersonnelInfoMapper.update(null, uw);
AppPersonnelInfoVo personnelInfoVo = appPersonnelInfoVos.get(0);
AppPersonnelInfo appPersonnelInfo = MapstructUtils.convert(bo, AppPersonnelInfo.class);
AppPersonnelInfoRecords appPersonnelInfoRecords = new AppPersonnelInfoRecords();
BeanUtil.copyProperties(appPersonnelInfo, appPersonnelInfoRecords);
appPersonnelInfoRecords.setId(null);
appPersonnelInfoRecords.setPersonnelId(personnelInfoVo.getId());
appPersonnelInfoRecordsMapper.insert(appPersonnelInfoRecords);
}
return true;
}
public void uploadDeviceLogo2(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);
// 在获取 largeData 后,将其与前缀合并
byte[] prefix = new byte[]{0x50, 0x49, 0x43, 0x54, 0x55, 0x52, 0x45}; // "PICTURE"
byte[] combinedData = new byte[prefix.length + largeData.length];
System.arraycopy(prefix, 0, combinedData, 0, prefix.length);
System.arraycopy(largeData, 0, combinedData, prefix.length, largeData.length);
// 将 combinedData 转换为十六进制表示
String[] hexArray = new String[combinedData.length];
for (int i = 0; i < combinedData.length; i++) {
hexArray[i] = String.format("0x%02X", combinedData[i]);
}
// Map<String, Object> map = new HashMap<>();
// map.put("instruct", combinedData);
String[] specificChunk = ImageToCArrayConverter.getChunk2(hexArray, 0, bo.getChunkSize());
mqttGateway.sendMsgToMqtt(MqttConstants.GLOBAL_PUB_KEY+device.getDeviceImei(), 1 , Arrays.toString(specificChunk));
log.info("发送点阵数据到设备消息=>topic:{},payload:{}", MqttConstants.GLOBAL_PUB_KEY+device.getDeviceImei(),Arrays.toString(specificChunk));
recordDeviceLog(device.getId(), device.getDeviceName(), "上传开机画面", "上传开机画面", AppLoginHelper.getUserId());
} catch (Exception e){
e.printStackTrace();
throw new ServiceException("发送指令失败");
}
}
public void uploadDeviceLogo(AppDeviceLogoUploadDto bo) {
try {
Device device = deviceMapper.selectById(bo.getDeviceId());

View File

@ -28,6 +28,7 @@ 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.domain.vo.WebDeviceVo;
import com.fuyuanshen.equipment.enums.BindingStatusEnum;
import com.fuyuanshen.equipment.enums.CommunicationModeEnum;
import com.fuyuanshen.equipment.mapper.DeviceLogMapper;
@ -123,6 +124,47 @@ public class DeviceBizService {
return TableDataInfo.build(result);
}
public TableDataInfo<WebDeviceVo> queryWebDeviceList(DeviceQueryCriteria bo, PageQuery pageQuery) {
Page<WebDeviceVo> result = deviceMapper.queryWebDeviceList(pageQuery.build(), bo);
List<WebDeviceVo> 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();

View File

@ -0,0 +1,269 @@
package com.fuyuanshen.web.service.device;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.fuyuanshen.app.domain.AppPersonnelInfo;
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.AppPersonnelInfoVo;
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.ImageToCArrayConverter;
import com.fuyuanshen.common.core.utils.MapstructUtils;
import com.fuyuanshen.common.core.utils.ObjectUtils;
import com.fuyuanshen.common.core.utils.StringUtils;
import com.fuyuanshen.common.json.utils.JsonUtils;
import com.fuyuanshen.common.redis.utils.RedisUtils;
import com.fuyuanshen.common.satoken.utils.AppLoginHelper;
import com.fuyuanshen.equipment.domain.Device;
import com.fuyuanshen.equipment.domain.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;
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.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
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.DEVICE_BOOT_LOGO_KEY_PREFIX;
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX;
@Slf4j
@Service
@RequiredArgsConstructor
public class DeviceXinghanBizService {
private final DeviceMapper deviceMapper;
private final AppPersonnelInfoMapper appPersonnelInfoMapper;
private final DeviceTypeMapper deviceTypeMapper;
private final MqttGateway mqttGateway;
private final DeviceLogMapper deviceLogMapper;
/**
* 所有档位的描述表
* key : 指令类型,如 "ins_DetectGrade"、"ins_LightGrade" ……
* value : Map<Integer,String> 值 -> 描述
*/
private static final Map<String, Map<Integer, String>> GRADE_DESC = Map.of(
"ins_DetectGrade", Map.of(1, "低档", 2, "中档", 3, "高档"),
"ins_LightGrade", Map.of(1, "强光", 2, "弱光"),
"ins_SOSGrade", Map.of(1, "爆闪模式", 2, "红蓝模式"),
"ins_ShakeBit", Map.of(0, "未静止报警", 1, "正在静止报警")
// 再加 4、5、6…… 档,直接往 Map 里塞即可
);
/**
* 根据指令类型和值,返回中文描述
*/
private static String resolveGradeDesc(String type, int value) {
return GRADE_DESC.getOrDefault(type, Map.of())
.getOrDefault(value, "关闭");
}
/**
* 设置静电预警档位
*/
public void upDetectGradeSettings(DeviceInstructDto dto) {
sendCommand(dto, "ins_DetectGrade","静电预警档位");
}
/**
* 设置照明档位
*/
public void upLightGradeSettings(DeviceInstructDto dto) {
sendCommand(dto, "ins_LightGrade","照明档位");
}
/**
* 设置SOS档位
*/
public void upSOSGradeSettings(DeviceInstructDto dto) {
sendCommand(dto, "ins_SOSGrade","SOS档位");
}
/**
* 设置强制报警
*/
public void upShakeBitSettings(DeviceInstructDto dto) {
sendCommand(dto, "ins_ShakeBit","强制报警");
}
/**
* 上传设备logo
*/
public void uploadDeviceLogo(AppDeviceLogoUploadDto bo) {
try {
Device device = deviceMapper.selectById(bo.getDeviceId());
if (device == null) {
throw new ServiceException("设备不存在");
}
if (isDeviceOffline(device.getDeviceImei())) {
throw new ServiceException("设备已断开连接:" + device.getDeviceName());
}
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(5 * 60L));
Map<String, Object> payload = Map.of("ins_PicTrans",
Collections.singletonList(0));
String topic = MqttConstants.GLOBAL_PUB_KEY + device.getDeviceImei();
String json = JsonUtils.toJsonString(payload);
try {
mqttGateway.sendMsgToMqtt(topic, 1, json);
} catch (Exception e) {
log.error("上传开机画面失败, topic={}, payload={}", topic, json, e);
throw new ServiceException("上传LOGO失败" + e.getMessage());
}
log.info("发送上传开机画面到设备消息=>topic:{},payload:{}", MqttConstants.GLOBAL_PUB_KEY+device.getDeviceImei(),json);
recordDeviceLog(device.getId(), device.getDeviceName(), "上传开机画面", "上传开机画面", AppLoginHelper.getUserId());
} catch (Exception e){
throw new ServiceException("发送指令失败");
}
}
/**
* 人员登记
* @param bo
*/
public boolean registerPersonInfo(AppPersonnelInfoBo bo) {
Long deviceId = bo.getDeviceId();
Device deviceObj = deviceMapper.selectById(deviceId);
if (deviceObj == null) {
throw new RuntimeException("请先将设备入库!!!");
}
if (isDeviceOffline(deviceObj.getDeviceImei())) {
throw new ServiceException("设备已断开连接:" + deviceObj.getDeviceName());
}
QueryWrapper<AppPersonnelInfo> qw = new QueryWrapper<AppPersonnelInfo>()
.eq("device_id", deviceId);
List<AppPersonnelInfoVo> appPersonnelInfoVos = appPersonnelInfoMapper.selectVoList(qw);
List<String> list = new ArrayList<>();
list.add(bo.getUnitName());
list.add(bo.getName());
list.add(bo.getPosition());
list.add(bo.getCode());
RedisUtils.setCacheList(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + deviceObj.getDeviceImei() + ":app_send_message_data", list);
Map<String, Object> payload = Map.of("ins_TexTrans",
Collections.singletonList(0));
String topic = MqttConstants.GLOBAL_PUB_KEY + deviceObj.getDeviceImei();
String json = JsonUtils.toJsonString(payload);
try {
mqttGateway.sendMsgToMqtt(topic, 1, json);
} catch (Exception e) {
log.error("人员信息登记失败, topic={}, payload={}", topic, json, e);
throw new ServiceException("人员信息登记失败:" + e.getMessage());
}
log.info("发送人员信息登记到设备消息=>topic:{},payload:{}", MqttConstants.GLOBAL_PUB_KEY + 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);
} 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;
}
}
/* ---------------------------------- 私有通用方法 ---------------------------------- */
private void sendCommand(DeviceInstructDto dto,
String payloadKey,String deviceAction) {
long deviceId = dto.getDeviceId();
Device device = deviceMapper.selectById(deviceId);
if (device == null) {
throw new ServiceException("设备不存在");
}
if (isDeviceOffline(device.getDeviceImei())) {
throw new ServiceException("设备已断开连接:" + device.getDeviceName());
}
Integer value = Integer.parseInt(dto.getInstructValue());
Map<String, Object> payload = Map.of(payloadKey,
Collections.singletonList(value));
String topic = MqttConstants.GLOBAL_PUB_KEY + device.getDeviceImei();
String json = JsonUtils.toJsonString(payload);
try {
mqttGateway.sendMsgToMqtt(topic, 1, json);
} catch (Exception e) {
log.error("发送指令失败, topic={}, payload={}", topic, json, e);
throw new ServiceException("发送指令失败:" + e.getMessage());
}
log.info("发送指令成功 => topic:{}, payload:{}", topic, json);
String content = resolveGradeDesc("ins_DetectGrade", value);
recordDeviceLog(device.getId(),
device.getDeviceName(),
deviceAction,
content,
AppLoginHelper.getUserId());
}
private boolean isDeviceOffline(String imei) {
// 原方法名语义相反,这里取反,使含义更清晰
return getDeviceStatus(imei);
}
/**
* 记录设备操作日志
* @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);
}
}
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

@ -1,12 +1,17 @@
package com.fuyuanshen.web.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fuyuanshen.app.domain.AppDeviceBindRecord;
import com.fuyuanshen.app.domain.AppDeviceShare;
import com.fuyuanshen.app.domain.vo.AppDeviceShareVo;
import com.fuyuanshen.app.mapper.AppDeviceBindRecordMapper;
import com.fuyuanshen.app.mapper.AppDeviceShareMapper;
import com.fuyuanshen.equipment.domain.Device;
import com.fuyuanshen.equipment.domain.DeviceAssignments;
import com.fuyuanshen.equipment.domain.vo.WebDeviceVo;
import com.fuyuanshen.equipment.enums.BindingStatusEnum;
import com.fuyuanshen.equipment.mapper.DeviceAssignmentsMapper;
import com.fuyuanshen.equipment.mapper.DeviceMapper;
@ -27,13 +32,12 @@ import org.springframework.transaction.annotation.Transactional;
@RequiredArgsConstructor
public class WEBDeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> implements WEBDeviceService {
private final DeviceBizService appDeviceService;
private final DeviceAssignmentsMapper deviceAssignmentsMapper;
private final AppDeviceBindRecordMapper appDeviceBindRecordMapper;
private final DeviceMapper deviceMapper;
private final AppDeviceShareMapper appDeviceShareMapper;
/**
@ -70,4 +74,25 @@ public class WEBDeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> impl
}
/**
* WEB端设备详情
*
* @param id
* @return
*/
@Override
public WebDeviceVo getDevice(Long id) {
Device device = deviceMapper.selectById(id);
if (device != null) {
WebDeviceVo webDeviceVo = new WebDeviceVo();
BeanUtil.copyProperties(device, webDeviceVo);
// 查询分享用户数
Long count = appDeviceShareMapper.selectCount(new QueryWrapper<AppDeviceShare>().eq("device_id", id));
webDeviceVo.setShareUsersNumber(Math.toIntExact(count));
return webDeviceVo;
}
return null;
}
}