feat(web): 新增设备联调中心功能
- 新增设备联调中心相关控制器、服务、DTO和VO - 实现设备列表查询、文件上传、操作视频添加、设备详情等功能 - 优化设备 logo 上传逻辑,支持批量上传 - 重构部分代码结构,提高可维护性
This commit is contained in:
@ -5,6 +5,7 @@ import com.fuyuanshen.app.domain.dto.AppDeviceLogoUploadDto;
|
||||
import com.fuyuanshen.app.domain.dto.DeviceInstructDto;
|
||||
import com.fuyuanshen.common.core.domain.R;
|
||||
import com.fuyuanshen.common.core.validate.AddGroup;
|
||||
import com.fuyuanshen.common.log.annotation.Log;
|
||||
import com.fuyuanshen.common.ratelimiter.annotation.FunctionAccessAnnotation;
|
||||
import com.fuyuanshen.common.ratelimiter.annotation.FunctionAccessBatcAnnotation;
|
||||
import com.fuyuanshen.common.web.core.BaseController;
|
||||
@ -29,6 +30,7 @@ public class AppDeviceXinghanController extends BaseController {
|
||||
/**
|
||||
* 人员信息登记
|
||||
*/
|
||||
@Log(title = "xinghan指令-人员信息登记")
|
||||
@PostMapping(value = "/registerPersonInfo")
|
||||
// @FunctionAccessAnnotation("registerPersonInfo")
|
||||
public R<Void> registerPersonInfo(@Validated(AddGroup.class) @RequestBody AppPersonnelInfoBo bo) {
|
||||
@ -38,6 +40,7 @@ public class AppDeviceXinghanController extends BaseController {
|
||||
/**
|
||||
* 发送紧急通知
|
||||
*/
|
||||
@Log(title = "xinghan指令-发送紧急通知")
|
||||
@PostMapping(value = "/sendAlarmMessage")
|
||||
@FunctionAccessBatcAnnotation(value = "sendAlarmMessage", timeOut = 5, batchMaxTimeOut = 10)
|
||||
public R<Void> sendAlarmMessage(@RequestBody AppDeviceSendMsgBo bo) {
|
||||
@ -47,6 +50,7 @@ public class AppDeviceXinghanController extends BaseController {
|
||||
/**
|
||||
* 上传设备logo图片
|
||||
*/
|
||||
@Log(title = "xinghan指令-上传设备logo图片")
|
||||
@PostMapping("/uploadLogo")
|
||||
@FunctionAccessAnnotation("uploadLogo")
|
||||
public R<Void> upload(@Validated @ModelAttribute AppDeviceLogoUploadDto bo) {
|
||||
@ -64,6 +68,7 @@ public class AppDeviceXinghanController extends BaseController {
|
||||
* 静电预警档位
|
||||
* 3,2,1,0,分别表示高档/中档/低挡/关闭
|
||||
*/
|
||||
@Log(title = "xinghan指令-静电预警档位")
|
||||
@PostMapping("/DetectGradeSettings")
|
||||
public R<Void> DetectGradeSettings(@RequestBody DeviceInstructDto params) {
|
||||
// params 转 JSONObject
|
||||
@ -75,6 +80,7 @@ public class AppDeviceXinghanController extends BaseController {
|
||||
* 照明档位
|
||||
* 照明档位,2,1,0,分别表示弱光/强光/关闭
|
||||
*/
|
||||
@Log(title = "xinghan指令-照明档位")
|
||||
@PostMapping("/LightGradeSettings")
|
||||
public R<Void> LightGradeSettings(@RequestBody DeviceInstructDto params) {
|
||||
// params 转 JSONObject
|
||||
@ -86,6 +92,7 @@ public class AppDeviceXinghanController extends BaseController {
|
||||
* SOS档位s
|
||||
* SOS档位,2,1,0, 分别表示红蓝模式/爆闪模式/关闭
|
||||
*/
|
||||
@Log(title = "xinghan指令-SOS档位s")
|
||||
@PostMapping("/SOSGradeSettings")
|
||||
public R<Void> SOSGradeSettings(@RequestBody DeviceInstructDto params) {
|
||||
// params 转 JSONObject
|
||||
@ -97,6 +104,7 @@ public class AppDeviceXinghanController extends BaseController {
|
||||
* 静止报警状态
|
||||
* 静止报警状态,0-未静止报警,1-正在静止报警。
|
||||
*/
|
||||
@Log(title = "xinghan指令-静止报警状态")
|
||||
@PostMapping("/ShakeBitSettings")
|
||||
public R<Void> ShakeBitSettings(@RequestBody DeviceInstructDto params) {
|
||||
// params 转 JSONObject
|
||||
|
@ -18,4 +18,9 @@ public class AppFileDto {
|
||||
*/
|
||||
private MultipartFile[] files;
|
||||
|
||||
/**
|
||||
* 多设备id
|
||||
*/
|
||||
private Long[] deviceIds;
|
||||
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ public class XinghanBootLogoRule implements MqttMessageRule {
|
||||
|
||||
@Override
|
||||
public void execute(MqttRuleContext ctx) {
|
||||
final String functionAccessKey = FUNCTION_ACCESS_KEY + ctx.getDeviceImei();
|
||||
final String functionAccessKey = FUNCTION_ACCESS_KEY + "LOGO:" + ctx.getDeviceImei();
|
||||
try {
|
||||
MqttXinghanLogoJson payload = objectMapper.convertValue(
|
||||
ctx.getPayloadDict(), MqttXinghanLogoJson.class);
|
||||
|
@ -60,7 +60,7 @@ public class XinghanDeviceDataRule implements MqttMessageRule {
|
||||
|
||||
@Override
|
||||
public void execute(MqttRuleContext context) {
|
||||
String functionAccess = FUNCTION_ACCESS_KEY + context.getDeviceImei();
|
||||
String functionAccess = FUNCTION_ACCESS_KEY + "DATA:" + context.getDeviceImei();
|
||||
try {
|
||||
// Latitude, longitude
|
||||
//主灯档位,激光灯档位,电量百分比,充电状态,电池剩余续航时间
|
||||
|
@ -48,7 +48,7 @@ public class XinghanSendAlarmMessageRule implements MqttMessageRule {
|
||||
|
||||
@Override
|
||||
public void execute(MqttRuleContext ctx) {
|
||||
String functionAccess = FUNCTION_ACCESS_KEY + ctx.getDeviceImei();
|
||||
String functionAccess = FUNCTION_ACCESS_KEY + "ALARM:" + ctx.getDeviceImei();
|
||||
try {
|
||||
XinghanSendAlarmMessageRule.MqttXinghanAlarmMsgJson payload = objectMapper.convertValue(
|
||||
ctx.getPayloadDict(), XinghanSendAlarmMessageRule.MqttXinghanAlarmMsgJson.class);
|
||||
|
@ -47,7 +47,7 @@ public class XinghanSendMsgRule implements MqttMessageRule {
|
||||
|
||||
@Override
|
||||
public void execute(MqttRuleContext ctx) {
|
||||
String functionAccess = FUNCTION_ACCESS_KEY + ctx.getDeviceImei();
|
||||
String functionAccess = FUNCTION_ACCESS_KEY + "MSG:" + ctx.getDeviceImei();
|
||||
try {
|
||||
XinghanSendMsgRule.MqttXinghanMsgJson payload = objectMapper.convertValue(
|
||||
ctx.getPayloadDict(), XinghanSendMsgRule.MqttXinghanMsgJson.class);
|
||||
|
@ -0,0 +1,160 @@
|
||||
package com.fuyuanshen.web.controller.device;
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
|
||||
import com.fuyuanshen.app.domain.bo.AppOperationVideoBo;
|
||||
import com.fuyuanshen.app.domain.dto.AppDeviceLogoUploadDto;
|
||||
import com.fuyuanshen.app.domain.dto.AppFileDto;
|
||||
import com.fuyuanshen.common.core.domain.R;
|
||||
import com.fuyuanshen.common.core.exception.ServiceException;
|
||||
import com.fuyuanshen.common.log.annotation.Log;
|
||||
import com.fuyuanshen.common.log.enums.BusinessType;
|
||||
import com.fuyuanshen.common.mybatis.core.page.PageQuery;
|
||||
import com.fuyuanshen.common.mybatis.core.page.TableDataInfo;
|
||||
import com.fuyuanshen.common.ratelimiter.annotation.FunctionAccessAnnotation;
|
||||
import com.fuyuanshen.common.web.core.BaseController;
|
||||
import com.fuyuanshen.equipment.domain.query.DeviceQueryCriteria;
|
||||
import com.fuyuanshen.equipment.domain.vo.AppDeviceVo;
|
||||
import com.fuyuanshen.equipment.domain.vo.WebDeviceVo;
|
||||
import com.fuyuanshen.web.domain.Dto.DeviceDebugEditDto;
|
||||
import com.fuyuanshen.web.domain.Dto.DeviceDebugLogoUploadDto;
|
||||
import com.fuyuanshen.web.domain.vo.DeviceInfoVo;
|
||||
import com.fuyuanshen.web.service.device.DeviceBizService;
|
||||
import com.fuyuanshen.web.service.device.DeviceDebugService;
|
||||
import com.fuyuanshen.web.service.device.DeviceXinghanBizService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 联调中心
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2025-08-28
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("api/device/debug")
|
||||
public class DeviceDebugController extends BaseController {
|
||||
|
||||
private final DeviceBizService appDeviceService;
|
||||
private final DeviceXinghanBizService deviceXinghanBizService;
|
||||
private final DeviceDebugService deviceDebugService;
|
||||
|
||||
/**
|
||||
* 查询设备列表
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<WebDeviceVo> list(DeviceQueryCriteria bo, PageQuery pageQuery) {
|
||||
return appDeviceService.queryWebDeviceList(bo, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
*/
|
||||
@Log(title = "批量上传文件")
|
||||
@PostMapping("/addFile")
|
||||
public R<Void> uploadFile(@Validated @ModelAttribute AppFileDto bo) throws IOException {
|
||||
return toAjax(deviceDebugService.addFileHash(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作视频添加
|
||||
*/
|
||||
@Log(title = "批量添加操作视频")
|
||||
@PostMapping("/addVideo")
|
||||
public R<Void> addOperationVideo(@RequestBody AppOperationVideoBo bo) {
|
||||
return toAjax(deviceDebugService.addVideoList(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传设备logo图片
|
||||
*/
|
||||
@Log(title = "批量上传设备logo图片")
|
||||
@PostMapping("/addLogo")
|
||||
@FunctionAccessAnnotation("uploadLogo")
|
||||
public R<Void> uploadLogo670(@Validated @ModelAttribute DeviceDebugLogoUploadDto bo) {
|
||||
|
||||
MultipartFile file = bo.getFile();
|
||||
if(file.getSize()>1024*1024*2){
|
||||
return R.warn("图片不能大于2M");
|
||||
}
|
||||
deviceXinghanBizService.uploadDeviceLogoBatch(bo);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设备详情
|
||||
*/
|
||||
@Operation(summary = "设备详情")
|
||||
@GetMapping(value = "/detail/{id}")
|
||||
public R<DeviceInfoVo> getDeviceInfo(@PathVariable Long id) {
|
||||
return R.ok(deviceDebugService.getDeviceInfo(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改设备联调信息
|
||||
*/
|
||||
@Log(title = "修改设备联调信息")
|
||||
@PostMapping("/editDebug")
|
||||
public R<Void> editDeviceDebug(@Validated @ModelAttribute DeviceDebugEditDto bo) throws Exception {
|
||||
// 1. 基础参数必填校验
|
||||
validateDeviceDebugEdit(bo);
|
||||
|
||||
// 修改上传设备说明
|
||||
if (bo.getExplanationFiles() != null) {
|
||||
AppFileDto appFileDto = new AppFileDto();
|
||||
appFileDto.setDeviceIds(new Long[]{ bo.getDeviceId() });
|
||||
appFileDto.setFileType(1L);
|
||||
appFileDto.setFiles(bo.getExplanationFiles());
|
||||
deviceDebugService.addFileHash(appFileDto);
|
||||
}
|
||||
// 修改上传设备参数
|
||||
if (bo.getParameterFiles() != null) {
|
||||
AppFileDto appFileDto = new AppFileDto();
|
||||
appFileDto.setDeviceIds(new Long[]{ bo.getDeviceId() });
|
||||
appFileDto.setFileType(2L);
|
||||
appFileDto.setFiles(bo.getParameterFiles());
|
||||
deviceDebugService.addFileHash(appFileDto);
|
||||
}
|
||||
// 修改操作视频
|
||||
if (bo.getVideoUrl().isEmpty()) {
|
||||
AppOperationVideoBo appOperationVideoBo = new AppOperationVideoBo();
|
||||
appOperationVideoBo.setDeviceIds(new Long[]{ bo.getDeviceId() });
|
||||
appOperationVideoBo.setVideoUrl(bo.getVideoUrl());
|
||||
deviceDebugService.addVideoList(appOperationVideoBo);
|
||||
}
|
||||
// 修改设备logo 每个型号设备走不同协议无法共用同一个上传
|
||||
// if(bo.getLogoFile() != null){
|
||||
// MultipartFile file = bo.getLogoFile();
|
||||
// if(file.getSize()>1024*1024*2){
|
||||
// return R.warn("图片不能大于2M");
|
||||
// }
|
||||
// AppDeviceLogoUploadDto bo1 = new AppDeviceLogoUploadDto();
|
||||
// bo1.setDeviceId(bo.getDeviceId());
|
||||
// bo1.setDeviceImei(bo.getDeviceImei());
|
||||
// bo1.setFile(file);
|
||||
// deviceXinghanBizService.uploadDeviceLogo(bo1);
|
||||
// }
|
||||
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/* ------------------ 私有复用 ------------------ */
|
||||
|
||||
private void validateDeviceDebugEdit(DeviceDebugEditDto bo) {
|
||||
if (bo.getDeviceId() == null || bo.getDeviceId() == 0L) {
|
||||
throw new ServiceException("请选择设备");
|
||||
}
|
||||
// if (bo.getDeviceImei().isEmpty()) {
|
||||
// throw new ServiceException("设备 IMEI 不能为空");
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package com.fuyuanshen.web.domain.Dto;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class DeviceDebugEditDto {
|
||||
/**
|
||||
* 设备主键列表
|
||||
*/
|
||||
private Long deviceId;
|
||||
/**
|
||||
* 设备 IMEI
|
||||
*/
|
||||
//private String deviceImei;
|
||||
/**
|
||||
* 上传 logo 图片
|
||||
*/
|
||||
//private MultipartFile LogoFile; // 同一张图
|
||||
/**
|
||||
* 参数文件
|
||||
*/
|
||||
private MultipartFile[] parameterFiles;
|
||||
/**
|
||||
* 说明文件
|
||||
*/
|
||||
private MultipartFile[] explanationFiles;
|
||||
/**
|
||||
* 视频链接
|
||||
*/
|
||||
private String videoUrl;
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package com.fuyuanshen.web.domain.Dto;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class DeviceDebugLogoUploadDto {
|
||||
/**
|
||||
* 设备主键列表
|
||||
*/
|
||||
private List<Long> deviceIds; // 设备主键列表
|
||||
/**
|
||||
* 上传 图片
|
||||
*/
|
||||
private MultipartFile file; // 同一张图
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package com.fuyuanshen.web.domain.vo;
|
||||
|
||||
import com.fuyuanshen.app.domain.vo.AppBusinessFileVo;
|
||||
import com.fuyuanshen.app.domain.vo.AppFileVo;
|
||||
import com.fuyuanshen.app.domain.vo.AppOperationVideoVo;
|
||||
import com.fuyuanshen.equipment.domain.Device;
|
||||
import com.fuyuanshen.equipment.domain.vo.AppDeviceVo;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 设备信息 视图对象
|
||||
*
|
||||
* @author Michelle.Chung
|
||||
*/
|
||||
@Data
|
||||
public class DeviceInfoVo {
|
||||
/**
|
||||
* 设备业务文件
|
||||
*/
|
||||
private List<AppFileVo> appBusinessFileVoList;
|
||||
/**
|
||||
* 设备操作视频
|
||||
*/
|
||||
private List<AppOperationVideoVo> appOperationVideoVoList;
|
||||
}
|
@ -0,0 +1,150 @@
|
||||
package com.fuyuanshen.web.service.device;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.fuyuanshen.app.domain.AppBusinessFile;
|
||||
import com.fuyuanshen.app.domain.AppOperationVideo;
|
||||
import com.fuyuanshen.app.domain.bo.AppBusinessFileBo;
|
||||
import com.fuyuanshen.app.domain.bo.AppOperationVideoBo;
|
||||
import com.fuyuanshen.app.domain.dto.AppFileDto;
|
||||
import com.fuyuanshen.app.service.IAppBusinessFileService;
|
||||
import com.fuyuanshen.app.service.IAppOperationVideoService;
|
||||
import com.fuyuanshen.common.core.exception.ServiceException;
|
||||
import com.fuyuanshen.common.satoken.utils.AppLoginHelper;
|
||||
import com.fuyuanshen.equipment.mapper.DeviceMapper;
|
||||
import com.fuyuanshen.equipment.service.DeviceService;
|
||||
import com.fuyuanshen.system.domain.vo.SysOssVo;
|
||||
import com.fuyuanshen.system.service.ISysOssService;
|
||||
import com.fuyuanshen.web.domain.vo.DeviceInfoVo;
|
||||
import com.fuyuanshen.web.util.FileHashUtil;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 设备调试服务
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class DeviceDebugService {
|
||||
private final ISysOssService sysOssService;
|
||||
|
||||
private final IAppBusinessFileService appBusinessFileService;
|
||||
private final IAppOperationVideoService appOperationVideoService;
|
||||
private final DeviceService deviceService;
|
||||
|
||||
/**
|
||||
* 文件上传并添加文件信息哈希去重
|
||||
* @param bo
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean addFileHash(AppFileDto bo) throws IOException {
|
||||
MultipartFile[] files = bo.getFiles();
|
||||
if (files == null || files.length == 0) {
|
||||
throw new ServiceException("请选择要上传的文件");
|
||||
}
|
||||
if (files.length > 5) {
|
||||
throw new ServiceException("最多只能上传5个文件");
|
||||
}
|
||||
if (bo.getDeviceIds() == null || bo.getDeviceIds().length == 0) {
|
||||
throw new ServiceException("请选择你要上传的设备");
|
||||
}
|
||||
|
||||
Map<String, Long> hash2OssId = new LinkedHashMap<>(files.length);
|
||||
for (MultipartFile file : files) {
|
||||
// 1. 计算文件哈希
|
||||
String hash = FileHashUtil.hash(file);
|
||||
|
||||
// 2. 先根据 hash 查库(秒传)
|
||||
SysOssVo exist = sysOssService.selectByHash(hash);
|
||||
Long ossId;
|
||||
if (exist != null) {
|
||||
// 2.1 已存在,直接复用
|
||||
ossId = exist.getOssId();
|
||||
hash2OssId.putIfAbsent(hash, ossId);
|
||||
} else {
|
||||
// 2.2 不存在,真正上传
|
||||
SysOssVo upload = sysOssService.upload(file);
|
||||
if (upload == null) {
|
||||
return false;
|
||||
}
|
||||
ossId = upload.getOssId();
|
||||
hash2OssId.putIfAbsent(hash, ossId);
|
||||
// 2.3 把 hash 写回记录(供下次去重)
|
||||
sysOssService.updateHashById(ossId, hash);
|
||||
}
|
||||
}
|
||||
// 4. 组装业务中间表
|
||||
List<AppBusinessFile> bizList = new ArrayList<>(bo.getDeviceIds().length * hash2OssId.size());
|
||||
Long userId = AppLoginHelper.getUserId();
|
||||
for (Long deviceId : bo.getDeviceIds()) {
|
||||
for (Long ossId : hash2OssId.values()) {
|
||||
// 3. 关联业务表
|
||||
AppBusinessFile appFile = new AppBusinessFile();
|
||||
appFile.setFileId(ossId);
|
||||
appFile.setBusinessId(deviceId);
|
||||
appFile.setFileType(bo.getFileType());
|
||||
appFile.setCreateBy(userId);
|
||||
bizList.add(appFile);
|
||||
}
|
||||
}
|
||||
if (CollUtil.isEmpty(bizList)) { // 空集合直接返回
|
||||
throw new ServiceException("请选择要上传的文件");
|
||||
}
|
||||
return appBusinessFileService.insertBatch(bizList);
|
||||
}
|
||||
|
||||
public Boolean addVideoList(AppOperationVideoBo bo){
|
||||
if (bo.getVideoUrl().isEmpty()) {
|
||||
throw new ServiceException("请输入视频地址");
|
||||
}
|
||||
if (bo.getDeviceIds() == null || bo.getDeviceIds().length == 0) {
|
||||
throw new ServiceException("请选择你要上传的设备");
|
||||
}
|
||||
List<AppOperationVideo> bizList = new ArrayList<>(bo.getDeviceIds().length);
|
||||
for (Long deviceId : bo.getDeviceIds()) {
|
||||
|
||||
AppOperationVideo appVideo = new AppOperationVideo();
|
||||
appVideo.setVideoName(bo.getVideoName());
|
||||
appVideo.setDeviceId(deviceId);
|
||||
appVideo.setVideoUrl(bo.getVideoUrl());
|
||||
bizList.add(appVideo);
|
||||
}
|
||||
if (CollUtil.isEmpty(bizList)) { // 空集合直接返回
|
||||
throw new ServiceException("请选择要上传的视频");
|
||||
}
|
||||
return appOperationVideoService.insertBatch(bizList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设备详情
|
||||
* @param deviceId
|
||||
* @return
|
||||
*/
|
||||
public DeviceInfoVo getDeviceInfo(Long deviceId) {
|
||||
if(deviceId == null || deviceId <= 0L) {
|
||||
throw new ServiceException("请选择设备");
|
||||
}
|
||||
DeviceInfoVo vo = new DeviceInfoVo();
|
||||
var device = deviceService.getById(deviceId);
|
||||
AppBusinessFileBo fileBo = new AppBusinessFileBo();
|
||||
fileBo.setBusinessId(deviceId);
|
||||
AppOperationVideoBo videoBo = new AppOperationVideoBo();
|
||||
videoBo.setDeviceId(deviceId);
|
||||
vo.setAppBusinessFileVoList(appBusinessFileService.queryAppFileList(fileBo));
|
||||
vo.setAppOperationVideoVoList(appOperationVideoService.queryList(videoBo));
|
||||
return vo;
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ 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.baomidou.mybatisplus.core.toolkit.CollectionUtils;
|
||||
import com.fuyuanshen.app.domain.AppPersonnelInfo;
|
||||
import com.fuyuanshen.app.domain.bo.AppPersonnelInfoBo;
|
||||
import com.fuyuanshen.app.domain.dto.AppDeviceLogoUploadDto;
|
||||
@ -27,13 +28,17 @@ 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 com.fuyuanshen.web.domain.Dto.DeviceDebugLogoUploadDto;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.fuyuanshen.common.core.constant.GlobalConstants.GLOBAL_REDIS_KEY;
|
||||
import static com.fuyuanshen.common.core.utils.Bitmap80x12Generator.buildArr;
|
||||
@ -142,6 +147,67 @@ public class DeviceXinghanBizService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量上传设备logo
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void uploadDeviceLogoBatch(DeviceDebugLogoUploadDto batchDto) {
|
||||
if (CollectionUtils.isEmpty(batchDto.getDeviceIds())) {
|
||||
throw new ServiceException("设备列表为空");
|
||||
}
|
||||
|
||||
// 1. 一次性把设备查出来(N -> 1)
|
||||
QueryWrapper<Device> query = new QueryWrapper<>();
|
||||
query.in("id", batchDto.getDeviceIds());
|
||||
List<Device> devices = deviceMapper.selectList(query);
|
||||
if (devices.size() != batchDto.getDeviceIds().size()) {
|
||||
throw new ServiceException("部分设备不存在");
|
||||
}
|
||||
|
||||
// 2. 图片只转换一次(160*80 固定尺寸)
|
||||
byte[] largeData;
|
||||
try {
|
||||
largeData = ImageToCArrayConverter.convertImageToCArray(
|
||||
batchDto.getFile().getInputStream(), 160, 80, 25600);
|
||||
} catch (IOException e) {
|
||||
throw new ServiceException("图片解析失败");
|
||||
}
|
||||
int[] picArray = convertHexToDecimal(largeData);
|
||||
|
||||
// 3. 过滤离线设备 & 组装指令
|
||||
List<Device> onlineDevices = devices.stream()
|
||||
.filter(d -> !isDeviceOffline(d.getDeviceImei()))
|
||||
.toList();
|
||||
onlineDevices.forEach(d -> {
|
||||
String redisKey = GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX + d.getDeviceImei() + DEVICE_BOOT_LOGO_KEY_PREFIX;
|
||||
|
||||
// 如果 Redis 里已存在,直接跳过
|
||||
if (RedisUtils.getCacheObject(redisKey) != null) {
|
||||
return; // 跳过本次循环
|
||||
}
|
||||
|
||||
RedisUtils.setCacheObject(
|
||||
redisKey,
|
||||
Arrays.toString(picArray),
|
||||
Duration.ofSeconds(5 * 60L));
|
||||
// 3.2 MQTT 下发
|
||||
Map<String, Object> payload =
|
||||
Collections.singletonMap("ins_PicTrans", Collections.singletonList(0));
|
||||
String topic = MqttConstants.GLOBAL_PUB_KEY + d.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());
|
||||
}
|
||||
|
||||
recordDeviceLog(d.getId(), d.getDeviceName(), "上传开机画面", "上传开机画面", AppLoginHelper.getUserId());
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 人员登记
|
||||
* @param bo
|
||||
|
@ -0,0 +1,28 @@
|
||||
package com.fuyuanshen.web.util;
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.HexFormat;
|
||||
|
||||
/**
|
||||
* 文件哈希工具类
|
||||
*/
|
||||
public class FileHashUtil {
|
||||
private static final String ALGORITHM = "SHA-256";
|
||||
|
||||
public static String hash(MultipartFile file) throws IOException {
|
||||
MessageDigest digest = DigestUtils.getDigest(ALGORITHM);
|
||||
try (InputStream in = file.getInputStream()) {
|
||||
byte[] buf = new byte[8192];
|
||||
int len;
|
||||
while ((len = in.read(buf)) != -1) {
|
||||
digest.update(buf, 0, len);
|
||||
}
|
||||
}
|
||||
return HexFormat.of().formatHex(digest.digest());
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user