Compare commits
35 Commits
c8f9cc4f31
...
dyf-device
| Author | SHA1 | Date | |
|---|---|---|---|
| 04cb699081 | |||
| 0f0dd4c6b1 | |||
| 2db37a75d2 | |||
| 696bbb4aa4 | |||
| ee445dc0d2 | |||
| 897561ba89 | |||
| 2e575f78b1 | |||
| 6fd7f8ca94 | |||
| 9936a5ad13 | |||
| c267fe0c14 | |||
| e80830e76c | |||
| 346792d5c1 | |||
| cb87871982 | |||
| 437ab110b7 | |||
| b280038502 | |||
| 3e119b1dd8 | |||
| cab0884d7f | |||
| 88650c3d9f | |||
| ca11ef0e35 | |||
| ef2dc6a6f6 | |||
| dff37b245a | |||
| d7c4d22de3 | |||
| 6a058318f2 | |||
| f2d74b8f17 | |||
| af42a2199c | |||
| aaf142ca67 | |||
| c0dfe36b59 | |||
| c480bda112 | |||
| 8c636d0484 | |||
| 0c474ae1f3 | |||
| b85664900e | |||
| 035b24fedd | |||
| e920cfb860 | |||
| b33ee00dbd | |||
| ec28ab1092 |
@ -133,6 +133,25 @@
|
||||
<version>1.5.7</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<!-- Java音频转换库 -->
|
||||
<dependency>
|
||||
<groupId>ws.schild</groupId>
|
||||
<artifactId>jave-core</artifactId>
|
||||
<version>3.3.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- <dependency>
|
||||
<groupId>ws.schild</groupId>
|
||||
<artifactId>jave-nativebin-all</artifactId>
|
||||
<version>3.3.1</version>
|
||||
</dependency>-->
|
||||
|
||||
<dependency>
|
||||
<groupId>ws.schild</groupId>
|
||||
<artifactId>jave-all-deps</artifactId>
|
||||
<version>3.1.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- skywalking 整合 logback -->
|
||||
<!-- <dependency>-->
|
||||
|
||||
81
fys-admin/src/main/java/com/fuyuanshen/SimpleQR.java
Normal file
81
fys-admin/src/main/java/com/fuyuanshen/SimpleQR.java
Normal file
@ -0,0 +1,81 @@
|
||||
package com.fuyuanshen;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
|
||||
public class SimpleQR {
|
||||
public static void main(String[] args) throws Exception {
|
||||
String text = "HELLO";
|
||||
int size = 21; // 版本1的大小
|
||||
boolean[][] qr = new boolean[size][size];
|
||||
|
||||
// 1. 三个定位标记
|
||||
for (int i = 0; i < 7; i++)
|
||||
for (int j = 0; j < 7; j++) {
|
||||
boolean black = i == 0 || i == 6 || j == 0 || j == 6 || (i > 1 && i < 5 && j > 1 && j < 5);
|
||||
qr[i][j] = qr[i][size - 1 - j] = qr[size - 1 - i][j] = black;
|
||||
}
|
||||
|
||||
// 2. 定时图案(简化)
|
||||
for (int i = 8; i < 13; i++) qr[i][6] = qr[6][i] = (i % 2 == 0);
|
||||
|
||||
// 3. 编码数据(极简编码)
|
||||
byte[] data = text.getBytes();
|
||||
int x = 20, y = 20, up = 1; // 从右下角开始
|
||||
for (byte b : data)
|
||||
for (int bit = 7; bit >= 0; bit--) {
|
||||
while (qr[x][y] || (x == 6 && y < 9) || (y == 6 && x < 9) || (x < 9 && y < 9) ||
|
||||
(x < 9 && y > size - 10) || (y < 9 && x > size - 10)) {
|
||||
if (up > 0) {
|
||||
if (--y < 0) {
|
||||
y = 0;
|
||||
x -= 2;
|
||||
up = -1;
|
||||
}
|
||||
} else {
|
||||
if (++y >= size) {
|
||||
y = size - 1;
|
||||
x -= 2;
|
||||
up = 1;
|
||||
}
|
||||
}
|
||||
if (x == 6) x--;
|
||||
}
|
||||
qr[x][y] = ((b >> bit) & 1) == 1;
|
||||
if (up > 0) {
|
||||
if (--y < 0) {
|
||||
y = 0;
|
||||
x -= 2;
|
||||
up = -1;
|
||||
}
|
||||
} else {
|
||||
if (++y >= size) {
|
||||
y = size - 1;
|
||||
x -= 2;
|
||||
up = 1;
|
||||
}
|
||||
}
|
||||
if (x == 6) x--;
|
||||
}
|
||||
|
||||
// 4. 保存图像
|
||||
int scale = 10, margin = 4;
|
||||
BufferedImage img = new BufferedImage(
|
||||
size * scale + margin * 2, size * scale + margin * 2, BufferedImage.TYPE_INT_RGB);
|
||||
for (int i = 0; i < img.getWidth(); i++)
|
||||
for (int j = 0; j < img.getHeight(); j++)
|
||||
img.setRGB(i, j, 0xFFFFFF); // 白色背景
|
||||
|
||||
for (int i = 0; i < size; i++)
|
||||
for (int j = 0; j < size; j++)
|
||||
if (qr[i][j])
|
||||
for (int di = 0; di < scale; di++)
|
||||
for (int dj = 0; dj < scale; dj++)
|
||||
img.setRGB(margin + i * scale + di, margin + j * scale + dj, 0x000000);
|
||||
|
||||
ImageIO.write(img, "PNG", new File("qr.png"));
|
||||
System.out.println("二维码已生成");
|
||||
}
|
||||
|
||||
}
|
||||
64
fys-admin/src/main/java/com/fuyuanshen/Text.java
Normal file
64
fys-admin/src/main/java/com/fuyuanshen/Text.java
Normal file
@ -0,0 +1,64 @@
|
||||
package com.fuyuanshen;
|
||||
|
||||
import com.fuyuanshen.equipment.utils.AlibabaTTSUtil;
|
||||
import com.fuyuanshen.equipment.utils.AudioProcessUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author: 默苍璃
|
||||
* @date: 2025-12-1518:51
|
||||
*/
|
||||
public class Text {
|
||||
public static void main(String[] args) throws IOException {
|
||||
String text = "简述人生观的主要内容。\n" +
|
||||
"人生观的主要内容包括以下三个方面:\n" +
|
||||
"1.人生目的:回答“人为什么活着”的根本问题。它规定了人生的方向,是人生观的核心。\n" +
|
||||
"2.人生态度:回答“人应该怎样活着”的问题。它是指人们通过生活实践形成的对人生问题的一种稳定的心理倾向和基本意图。\n" +
|
||||
"3.人生价值:回答“什么样的人生才有意义”的问题。它是指人的生命及其实践活动对于社会和个人所具有的作用和意义。\n" +
|
||||
"人生目的、人生态度和人生价值相互联系、相辅相成,共同构成一个有机整体。\n" +
|
||||
"人生目的是人生观的核心,它决定人生态度和人生价值的方向;人生态度影响人生目的的实现和人生价值的创造;人生价值是衡量人生观正确与否的尺度。";
|
||||
|
||||
AlibabaTTSUtil alibabaTTSUtil = new AlibabaTTSUtil();
|
||||
AudioProcessUtil audioProcessUtil = new AudioProcessUtil();
|
||||
|
||||
byte[] rawPcmData = alibabaTTSUtil.generateStandardPcmData(text);
|
||||
|
||||
// 使用AudioProcessUtil转换成带头44字节 PCM
|
||||
byte[] pcmData = audioProcessUtil.rawPcmToStandardWav(rawPcmData);
|
||||
|
||||
// String savedPath = audioProcessUtil.saveWavToFile(pcmData, "test_output.wav");
|
||||
// if (savedPath != null) {
|
||||
// log.info("测试文件已保存: {}", savedPath);
|
||||
// }
|
||||
|
||||
// 保存WAV文件到本地
|
||||
String savedPath = saveByteArrayToFile(pcmData, "人生观.wav");
|
||||
if (savedPath != null) {
|
||||
System.out.println("WAV文件已保存: " + savedPath);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static String saveByteArrayToFile(byte[] data, String filename) throws IOException {
|
||||
// 确定保存路径(可以是临时目录或指定目录)
|
||||
String directory = System.getProperty("java.io.tmpdir"); // 使用系统临时目录
|
||||
File dir = new File(directory);
|
||||
if (!dir.exists()) {
|
||||
dir.mkdirs();
|
||||
}
|
||||
|
||||
// 创建完整文件路径
|
||||
File file = new File(dir, filename);
|
||||
|
||||
// 写入文件
|
||||
try (FileOutputStream fos = new FileOutputStream(file)) {
|
||||
fos.write(data);
|
||||
}
|
||||
|
||||
return file.getAbsolutePath();
|
||||
}
|
||||
|
||||
}
|
||||
@ -11,6 +11,7 @@ import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@ -39,7 +40,7 @@ public class AppFileController extends BaseController {
|
||||
* 上传文件
|
||||
*/
|
||||
@PostMapping("/upload")
|
||||
public R<Void> upload(@Validated @ModelAttribute AppFileDto bo) {
|
||||
public R<Void> upload(@Validated @ModelAttribute AppFileDto bo) throws IOException {
|
||||
return toAjax(appFileService.add(bo));
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,12 @@
|
||||
package com.fuyuanshen.app.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaIgnore;
|
||||
import com.fuyuanshen.app.domain.bo.AppBusinessFileBo;
|
||||
import com.fuyuanshen.app.domain.dto.AppAudioFileDto;
|
||||
import com.fuyuanshen.app.domain.dto.AppFileDto;
|
||||
import com.fuyuanshen.app.domain.dto.AppFileRenameDto;
|
||||
import com.fuyuanshen.app.domain.dto.TextToSpeechRequest;
|
||||
import com.fuyuanshen.app.domain.vo.AppFileVo;
|
||||
import com.fuyuanshen.app.service.AudioProcessService;
|
||||
import com.fuyuanshen.app.service.VideoProcessService;
|
||||
import com.fuyuanshen.common.core.domain.R;
|
||||
@ -8,6 +14,7 @@ import com.fuyuanshen.common.idempotent.annotation.RepeatSubmit;
|
||||
import com.fuyuanshen.common.web.core.BaseController;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
@ -56,6 +63,7 @@ public class AppVideoController extends BaseController {
|
||||
return R.ok(audioProcessService.generateStandardPcmData(text));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 提取文本内容(只支持txt/docx)
|
||||
*/
|
||||
@ -64,4 +72,69 @@ public class AppVideoController extends BaseController {
|
||||
public R<String> extract(@RequestParam("file") MultipartFile file) throws Exception {
|
||||
return R.ok("Success",audioProcessService.extract(file));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 上传音频文件进行处理
|
||||
* 支持MP3、WAV、PCM格式
|
||||
*/
|
||||
@PostMapping("/uploadAudioToOss")
|
||||
public R<String> uploadAudioToOss(@ModelAttribute AppAudioFileDto bo) {
|
||||
try {
|
||||
String result = audioProcessService.uploadAudioToOss(bo);
|
||||
return R.ok(result);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return R.fail("文件格式错误: " + e.getMessage());
|
||||
} catch (Exception e) {
|
||||
|
||||
return R.fail("上传处理失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 文本转语音
|
||||
* 支持MP3、WAV、PCM格式
|
||||
*/
|
||||
@PostMapping("/ttsToOss")
|
||||
public R<String> textToSpeech(@RequestBody TextToSpeechRequest request) {
|
||||
try {
|
||||
if (request.getDeviceId() == null) {
|
||||
return R.fail("设备ID不能为空");
|
||||
}
|
||||
|
||||
String result = audioProcessService.textToSpeech(
|
||||
request.getDeviceId(),
|
||||
request.getText(),
|
||||
request.getFileSuffix()
|
||||
);
|
||||
return R.ok(result);
|
||||
} catch (Exception e) {
|
||||
return R.fail("文本转语音失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 查询语音文件列表
|
||||
*/
|
||||
@GetMapping("/queryAudioFileList")
|
||||
public R<List<AppFileVo>> queryAudioFileList(Long deviceId) {
|
||||
return R.ok(audioProcessService.queryAudioFileList(deviceId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除语音文件
|
||||
*/
|
||||
@GetMapping("/deleteAudioFile")
|
||||
public R<Void> deleteAudioFile(Long fileId,Long deviceId) {
|
||||
return audioProcessService.deleteAudioFile(fileId,deviceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件重命名
|
||||
*/
|
||||
@PostMapping("/renameAudioFile")
|
||||
public R<Void> renameAudioFile(@RequestBody AppFileRenameDto bo) {
|
||||
return audioProcessService.renameAudioFile(bo);
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,6 +26,7 @@ import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* HBY670设备控制类
|
||||
@ -133,7 +134,7 @@ public class AppDeviceXinghanController extends BaseController {
|
||||
}
|
||||
|
||||
// @Log("新增设备")
|
||||
@Operation(summary = "新增设备")
|
||||
@Log(title = "新增设备")
|
||||
@PostMapping(value = "/add")
|
||||
public R<Void> addDevice(@RequestBody DeviceForm deviceForm) {
|
||||
try {
|
||||
@ -144,4 +145,26 @@ public class AppDeviceXinghanController extends BaseController {
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@PostMapping(value = "/GetDeviceByName")
|
||||
@Operation(summary = "通过蓝牙名/设备名称查询设备")
|
||||
public R<Object> GetDeviceByName(@RequestBody DeviceForm deviceForm) {
|
||||
Object device = appDeviceService.GetDeviceByName(deviceForm);
|
||||
return R.ok(device);
|
||||
}
|
||||
|
||||
|
||||
@PostMapping(value = "/getEquipCountByType")
|
||||
@Operation(summary = "查询某个类型下的设备总数量")
|
||||
public R<Object> getEquipCountByType(@RequestBody DeviceForm deviceForm) {
|
||||
Object device = appDeviceService.getEquipCountByType(deviceForm);
|
||||
return R.ok(device);
|
||||
}
|
||||
|
||||
@PostMapping(value = "/getEquipAllByType")
|
||||
@Operation(summary = "查询某个类型下的设备")
|
||||
public R<List<Map<String,Object>>> getEquipAllByType(@RequestBody DeviceForm deviceForm){
|
||||
List<Map<String,Object>> list=appDeviceService.getEquipAllByType(deviceForm);
|
||||
return R.ok(list);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,191 @@
|
||||
package com.fuyuanshen.app.controller.device.bjq;
|
||||
|
||||
import com.fuyuanshen.app.domain.vo.AppDeviceHBY100JDetailVo;
|
||||
import com.fuyuanshen.common.core.domain.R;
|
||||
import com.fuyuanshen.common.web.core.BaseController;
|
||||
import com.fuyuanshen.web.service.device.DeviceHBY100JBizService;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* HBY100J设备控制类
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/app/hby100j/device")
|
||||
public class AppDeviceHBY100JController extends BaseController {
|
||||
|
||||
private final DeviceHBY100JBizService deviceHBY100JBizService;
|
||||
|
||||
|
||||
/**
|
||||
* 获取设备详细信息
|
||||
*
|
||||
* @param id 主键
|
||||
*/
|
||||
@GetMapping("/{id}")
|
||||
public R<AppDeviceHBY100JDetailVo> getInfo(@NotNull(message = "主键不能为空")
|
||||
@PathVariable Long id) {
|
||||
return R.ok(deviceHBY100JBizService.getInfo(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新语音
|
||||
*/
|
||||
@PostMapping("/updateVoice")
|
||||
public R<Void> updateVoice(@RequestBody HBY100JUpdateVoiceDto dto) {
|
||||
deviceHBY100JBizService.updateVoice(dto);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 强制报警
|
||||
*
|
||||
*/
|
||||
|
||||
@PostMapping("/forceAlarmActivation")
|
||||
public R<Void> forceAlarmActivation(@RequestBody HBY100JForceAlarmActivationDto bo) {
|
||||
deviceHBY100JBizService.forceAlarmActivation(bo);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 爆闪模式
|
||||
*/
|
||||
@PostMapping("/strobeMode")
|
||||
public R<Void> strobeMode(@RequestBody HBY100JStrobeModeDto params) {
|
||||
deviceHBY100JBizService.strobeMode(params);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 灯光调节
|
||||
*/
|
||||
@PostMapping("/lightAdjustment")
|
||||
public R<Void> lightAdjustment(@RequestBody HBY100JLightAdjustmentDto params) {
|
||||
deviceHBY100JBizService.lightAdjustment(params);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 爆闪频率
|
||||
*/
|
||||
@PostMapping("/strobeFrequency")
|
||||
public R<Void> strobeFrequency(@RequestBody HBY100JStrobeFrequencyDto params) {
|
||||
deviceHBY100JBizService.strobeFrequency(params);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改音量
|
||||
*/
|
||||
@PostMapping("/updateVolume")
|
||||
public R<Void> updateVolume(@RequestBody HBY100JUpdateVolumeDto params) {
|
||||
deviceHBY100JBizService.updateVolume(params);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class HBY100JUpdateVoiceDto {
|
||||
|
||||
private Long id;
|
||||
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class HBY100JForceAlarmActivationDto {
|
||||
|
||||
/**
|
||||
* 设备ID
|
||||
*/
|
||||
List<Long> deviceIds;
|
||||
/**
|
||||
* 0 关闭, 1开启
|
||||
*/
|
||||
private Integer voiceStrobeAlarm;
|
||||
/**
|
||||
* 0 公安,1消防,2应急,3交警,4 市政,5 铁路,6 医疗,7部队,8 水利,9 语音
|
||||
*/
|
||||
private Integer mode;
|
||||
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class HBY100JUpdateVolumeDto{
|
||||
/**
|
||||
* 设备ID
|
||||
*/
|
||||
private Long deviceId;
|
||||
/**
|
||||
* "volume": 1-100(app端可根据需求把40作为低音量, 70作为中音量,100作为高音量)
|
||||
*/
|
||||
private Integer volume;
|
||||
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class HBY100JStrobeFrequencyDto{
|
||||
/**
|
||||
* 设备ID
|
||||
*/
|
||||
private Long deviceId;
|
||||
/**
|
||||
* "frequency": 1-12
|
||||
*/
|
||||
private Integer frequency;
|
||||
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class HBY100JLightAdjustmentDto{
|
||||
/**
|
||||
* 设备ID
|
||||
*/
|
||||
private Long deviceId;
|
||||
|
||||
/**
|
||||
* 亮度值0-100
|
||||
*/
|
||||
private Integer brightness;
|
||||
// /**
|
||||
// * 红色LED亮度值0-100
|
||||
// */
|
||||
// private Integer red;
|
||||
//
|
||||
// /**
|
||||
// * 蓝色LED亮度值0-100
|
||||
// */
|
||||
// private Integer blue;
|
||||
//
|
||||
// /**
|
||||
// * 黄色LED亮度值0-100
|
||||
// */
|
||||
// private Integer yellow;
|
||||
|
||||
}
|
||||
@Data
|
||||
public static class HBY100JStrobeModeDto{
|
||||
|
||||
/**
|
||||
* 设备ID
|
||||
*/
|
||||
private Long deviceId;
|
||||
/**
|
||||
* 0 关闭 1 开启
|
||||
*/
|
||||
private Integer enable;
|
||||
|
||||
/**
|
||||
* 0 红色爆闪,1 蓝色爆闪,2 黄色爆闪,3,红色顺时针旋转爆闪,4黄色顺时针旋转爆闪,5,红蓝顺时针旋转爆闪,6 红蓝交替爆闪
|
||||
*/
|
||||
private Integer mode;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
package com.fuyuanshen.app.domain.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@Data
|
||||
public class AppAudioFileDto {
|
||||
|
||||
private Long deviceId;
|
||||
|
||||
/**
|
||||
* 文件
|
||||
*/
|
||||
private MultipartFile file;
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
package com.fuyuanshen.app.domain.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@Data
|
||||
public class AppFileRenameDto {
|
||||
|
||||
/**
|
||||
* 文件id
|
||||
*/
|
||||
private Long fileId;
|
||||
|
||||
/**
|
||||
* 设备id
|
||||
*/
|
||||
private Long deviceId;
|
||||
|
||||
/**
|
||||
* 文件名称
|
||||
*/
|
||||
private String fileName;
|
||||
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
package com.fuyuanshen.app.domain.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* PCM生成请求实体
|
||||
*/
|
||||
@Data
|
||||
public class PcmGenerationRequest {
|
||||
private String text;
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package com.fuyuanshen.app.domain.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 文本转语音请求实体
|
||||
*/
|
||||
@Data
|
||||
public class TextToSpeechRequest {
|
||||
|
||||
private Long deviceId;
|
||||
|
||||
private String text;
|
||||
|
||||
/**
|
||||
* 文件后缀格式
|
||||
*/
|
||||
private String fileSuffix;
|
||||
}
|
||||
@ -0,0 +1,130 @@
|
||||
package com.fuyuanshen.app.http;
|
||||
|
||||
import com.alibaba.nls.client.AccessToken;
|
||||
import com.fuyuanshen.common.redis.utils.RedisUtils;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.time.Duration;
|
||||
import java.util.Base64;
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import static cn.dev33.satoken.SaManager.log;
|
||||
|
||||
public class HttpTtsClient {
|
||||
|
||||
private String accessKeyId;
|
||||
private String accessKeySecret;
|
||||
private String appKey;
|
||||
/**
|
||||
* 阿里云TTS服务基础URL
|
||||
*/
|
||||
private static final String BASE_URL = "https://nls-gateway-cn-shanghai.aliyuncs.com/stream/v1/tts";
|
||||
|
||||
public HttpTtsClient(String accessKeyId, String accessKeySecret, String appKey) {
|
||||
this.accessKeyId = accessKeyId;
|
||||
this.accessKeySecret = accessKeySecret;
|
||||
this.appKey = appKey;
|
||||
}
|
||||
private String refreshAccessToken() {
|
||||
try {
|
||||
// 调用阿里云API获取访问令牌
|
||||
AccessToken accessToken = new AccessToken(accessKeyId, accessKeySecret);
|
||||
accessToken.apply();
|
||||
String token = accessToken.getToken();
|
||||
log.info("访问令牌刷新成功");
|
||||
|
||||
return token;
|
||||
} catch (Exception e) {
|
||||
log.error("刷新访问令牌失败: {}", e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 使用HTTP POST方式调用阿里云TTS服务生成MP3格式语音
|
||||
*/
|
||||
public byte[] synthesizeTextToMp3(String text) throws IOException {
|
||||
String endpoint = "https://nls-gateway-cn-shanghai.aliyuncs.com/stream/v1/tts";
|
||||
|
||||
// 构建请求体
|
||||
// String requestBody = String.format(
|
||||
// "{\"appkey\":\"%s\",\"text\":\"%s\",\"voice\":\"zhifeng\",\"format\":\"MP3\",\"sample_rate\":24000,\"volume\":50,\"speech_rate\":0,\"pitch_rate\":0}",
|
||||
// appKey,
|
||||
// text.replace("\"", "\\\"")
|
||||
// );
|
||||
|
||||
String token = refreshAccessToken();
|
||||
String requestBody = " {\n" +
|
||||
" \"appkey\":\""+appKey+"\",\n" +
|
||||
" \"text\":\""+text+"\",\n" +
|
||||
" \"token\":\""+token+"\",\n" +
|
||||
" \"format\":\"mp3\"\n" +
|
||||
" }";
|
||||
|
||||
URL url = new URL(endpoint);
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
|
||||
// 设置请求方法和头部
|
||||
conn.setRequestMethod("POST");
|
||||
conn.setRequestProperty("Content-Type", "application/json");
|
||||
conn.setRequestProperty("Authorization", buildAuthorization());
|
||||
conn.setRequestProperty("Content-Length", String.valueOf(requestBody.getBytes().length));
|
||||
conn.setDoOutput(true);
|
||||
|
||||
// 发送请求体
|
||||
try (OutputStream os = conn.getOutputStream()) {
|
||||
os.write(requestBody.getBytes("UTF-8"));
|
||||
}
|
||||
|
||||
// 读取响应
|
||||
int responseCode = conn.getResponseCode();
|
||||
if (responseCode == HttpURLConnection.HTTP_OK) {
|
||||
// 读取音频数据
|
||||
try (InputStream is = conn.getInputStream()) {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
int nRead;
|
||||
byte[] data = new byte[1024];
|
||||
while ((nRead = is.read(data, 0, data.length)) != -1) {
|
||||
buffer.write(data, 0, nRead);
|
||||
}
|
||||
return buffer.toByteArray();
|
||||
}
|
||||
} else {
|
||||
throw new IOException("HTTP请求失败,状态码: " + responseCode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建授权头部
|
||||
*/
|
||||
private String buildAuthorization() {
|
||||
// 实际实现需要根据阿里云API规范构建签名
|
||||
// 这里是简化示例
|
||||
return "Bearer " + generateAccessToken();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成访问令牌
|
||||
*/
|
||||
private String generateAccessToken() {
|
||||
// 实际实现需要调用阿里云获取token的API
|
||||
return "your-access-token";
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存MP3到文件
|
||||
*/
|
||||
public String saveMp3ToFile(String text, String outputPath) throws IOException {
|
||||
byte[] mp3Data = synthesizeTextToMp3(text);
|
||||
|
||||
try (FileOutputStream fos = new FileOutputStream(outputPath)) {
|
||||
fos.write(mp3Data);
|
||||
}
|
||||
|
||||
return outputPath;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
package com.fuyuanshen.app.http;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class HttpTtsExample {
|
||||
|
||||
/**
|
||||
* appKey: lbGuq5K5bEH4uxmT
|
||||
* akId: LTAI5t66moCkhNC32TDJ5ReP
|
||||
* akSecret: 2F3sdoBJ08bYvJcuDgSkLnJwGXsvYH
|
||||
* @param args
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
String accessKeyId = "LTAI5t66moCkhNC32TDJ5ReP";
|
||||
String accessKeySecret = "2F3sdoBJ08bYvJcuDgSkLnJwGXsvYH";
|
||||
String appKey = "lbGuq5K5bEH4uxmT";
|
||||
|
||||
try {
|
||||
// 使用HTTP方式调用
|
||||
HttpTtsClient httpClient = new HttpTtsClient(accessKeyId, accessKeySecret, appKey);
|
||||
|
||||
String text = "大江东去,浪淘尽,千古风流人物。故垒西边,人道是,三国周郎赤壁。乱石穿空,惊涛拍岸,卷起千堆雪。";
|
||||
String outputPath = "D:\\http_output9.mp3";
|
||||
|
||||
String resultFile = httpClient.saveMp3ToFile(text, outputPath);
|
||||
System.out.println("MP3音频已保存至: " + resultFile);
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
package com.fuyuanshen.app.http;
|
||||
|
||||
import okhttp3.*;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class OkHttpTtsClient {
|
||||
|
||||
private OkHttpClient client = new OkHttpClient();
|
||||
private String accessKeyId;
|
||||
private String accessKeySecret;
|
||||
private String appKey;
|
||||
|
||||
public OkHttpTtsClient(String accessKeyId, String accessKeySecret, String appKey) {
|
||||
this.accessKeyId = accessKeyId;
|
||||
this.accessKeySecret = accessKeySecret;
|
||||
this.appKey = appKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用OkHttp调用TTS服务
|
||||
*/
|
||||
public byte[] synthesizeTextToMp3(String text) throws IOException {
|
||||
MediaType JSON = MediaType.parse("application/json; charset=utf-8");
|
||||
|
||||
String jsonPayload = String.format(
|
||||
"{\"appkey\":\"%s\",\"text\":\"%s\",\"voice\":\"zhifeng\",\"format\":\"MP3\",\"sample_rate\":24000,\"volume\":50,\"speech_rate\":0,\"pitch_rate\":0}",
|
||||
appKey,
|
||||
text.replace("\"", "\\\"")
|
||||
);
|
||||
|
||||
RequestBody body = RequestBody.create(JSON,jsonPayload);
|
||||
Request request = new Request.Builder()
|
||||
.url("https://nls-gateway.cn-shanghai.aliyuncs.com/v1/tts")
|
||||
.post(body)
|
||||
.addHeader("Content-Type", "application/json")
|
||||
.addHeader("Authorization", "Bearer " + getAccessToken())
|
||||
.build();
|
||||
|
||||
try (Response response = client.newCall(request).execute()) {
|
||||
if (!response.isSuccessful()) {
|
||||
throw new IOException("HTTP请求失败: " + response.code());
|
||||
}
|
||||
|
||||
ResponseBody responseBody = response.body();
|
||||
if (responseBody != null) {
|
||||
return responseBody.bytes();
|
||||
} else {
|
||||
throw new IOException("响应体为空");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getAccessToken() {
|
||||
// 实现获取访问令牌的逻辑
|
||||
return "your-access-token";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
package com.fuyuanshen.app.http;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* 获取阿里云TTS服务访问令牌
|
||||
*/
|
||||
public class TokenClient {
|
||||
|
||||
public static String getAccessToken(String accessKeyId, String accessKeySecret) throws IOException {
|
||||
String endpoint = "https://nls-meta.cn-shanghai.aliyuncs.com/v1/token";
|
||||
|
||||
URL url = new URL(endpoint);
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
|
||||
conn.setRequestMethod("POST");
|
||||
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
|
||||
conn.setDoOutput(true);
|
||||
|
||||
// 发送认证信息
|
||||
String params = String.format("grant_type=client_credentials&client_id=%s&client_secret=%s",
|
||||
accessKeyId, accessKeySecret);
|
||||
|
||||
try (OutputStream os = conn.getOutputStream()) {
|
||||
os.write(params.getBytes("UTF-8"));
|
||||
}
|
||||
|
||||
int responseCode = conn.getResponseCode();
|
||||
if (responseCode == HttpURLConnection.HTTP_OK) {
|
||||
try (InputStream is = conn.getInputStream();
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(is))) {
|
||||
|
||||
StringBuilder response = new StringBuilder();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
response.append(line);
|
||||
}
|
||||
|
||||
// 解析响应获取token(实际需要使用JSON解析库)
|
||||
return response.toString().replaceAll(".*\"access_token\":\"([^\"]+)\".*", "$1");
|
||||
}
|
||||
} else {
|
||||
throw new IOException("获取令牌失败,状态码: " + responseCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -227,7 +227,7 @@ public class AppDeviceShareService {
|
||||
appDeviceShare.setPermission(bo.getPermission());
|
||||
appDeviceShare.setCreateBy(userId);
|
||||
return appDeviceShareMapper.insert(appDeviceShare);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int remove(Long[] ids) {
|
||||
|
||||
@ -8,6 +8,7 @@ import com.fuyuanshen.common.core.exception.ServiceException;
|
||||
import com.fuyuanshen.common.oss.core.OssClient;
|
||||
import com.fuyuanshen.common.oss.factory.OssFactory;
|
||||
import com.fuyuanshen.common.satoken.utils.AppLoginHelper;
|
||||
import com.fuyuanshen.equipment.utils.FileHashUtil;
|
||||
import com.fuyuanshen.system.domain.vo.SysOssVo;
|
||||
import com.fuyuanshen.system.service.ISysOssService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@ -15,6 +16,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@ -31,24 +33,38 @@ public class AppFileService {
|
||||
|
||||
private final IAppBusinessFileService appBusinessFileService;
|
||||
|
||||
private final FileHashUtil fileHashUtil;
|
||||
private final ISysOssService ossService;
|
||||
|
||||
|
||||
public List<AppFileVo> list(AppBusinessFileBo bo) {
|
||||
// bo.setCreateBy(AppLoginHelper.getUserId());
|
||||
return appBusinessFileService.queryAppFileList(bo);
|
||||
}
|
||||
|
||||
public Boolean add(AppFileDto bo) {
|
||||
|
||||
/**
|
||||
* APP文件上传
|
||||
*/
|
||||
public Boolean add(AppFileDto bo) throws IOException {
|
||||
|
||||
MultipartFile[] files = bo.getFiles();
|
||||
if(files == null || files.length == 0){
|
||||
if (files == null || files.length == 0) {
|
||||
throw new ServiceException("请选择要上传的文件");
|
||||
}
|
||||
if(files.length > 5){
|
||||
if (files.length > 5) {
|
||||
throw new ServiceException("最多只能上传5个文件");
|
||||
}
|
||||
for (int i = 0; i < files.length; i++) {
|
||||
MultipartFile file = files[i];
|
||||
// 上传文件
|
||||
SysOssVo upload = sysOssService.upload(file);
|
||||
// SysOssVo upload = sysOssService.upload(file);
|
||||
String fileHash = fileHashUtil.hash(file);
|
||||
SysOssVo upload = ossService.updateHash(file, fileHash);
|
||||
// 强制将HTTP替换为HTTPS
|
||||
if (upload.getUrl() != null && upload.getUrl().startsWith("http://")) {
|
||||
upload.setUrl(upload.getUrl().replaceFirst("^http://", "https://"));
|
||||
}
|
||||
|
||||
if (upload == null) {
|
||||
return false;
|
||||
@ -66,6 +82,7 @@ public class AppFileService {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public Boolean delete(Long[] ids) {
|
||||
AppBusinessFileBo bo = new AppBusinessFileBo();
|
||||
// bo.setCreateBy(AppLoginHelper.getUserId());
|
||||
|
||||
@ -1,7 +1,21 @@
|
||||
package com.fuyuanshen.app.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||
import com.fuyuanshen.app.domain.AppBusinessFile;
|
||||
import com.fuyuanshen.app.domain.bo.AppBusinessFileBo;
|
||||
import com.fuyuanshen.app.domain.dto.AppAudioFileDto;
|
||||
import com.fuyuanshen.app.domain.dto.AppFileRenameDto;
|
||||
import com.fuyuanshen.app.domain.vo.AppFileVo;
|
||||
import com.fuyuanshen.app.http.HttpTtsClient;
|
||||
import com.fuyuanshen.app.mapper.AppBusinessFileMapper;
|
||||
import com.fuyuanshen.common.core.domain.R;
|
||||
import com.fuyuanshen.common.satoken.utils.AppLoginHelper;
|
||||
import com.fuyuanshen.equipment.utils.AlibabaTTSUtil;
|
||||
import com.fuyuanshen.equipment.utils.AudioProcessUtil;
|
||||
import com.fuyuanshen.equipment.utils.FileHashUtil;
|
||||
import com.fuyuanshen.equipment.utils.Mp3Duration;
|
||||
import com.fuyuanshen.system.domain.vo.SysOssVo;
|
||||
import com.fuyuanshen.system.service.ISysOssService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
@ -17,8 +31,12 @@ import javax.xml.stream.XMLStreamReader;
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
@ -39,6 +57,11 @@ public class AudioProcessService {
|
||||
private final AudioProcessUtil audioProcessUtil;
|
||||
private final AlibabaTTSUtil alibabaTTSUtil;
|
||||
|
||||
private final FileHashUtil fileHashUtil;
|
||||
private final ISysOssService ossService;
|
||||
private final IAppBusinessFileService appBusinessFileService;
|
||||
private final AppBusinessFileMapper appBusinessFileMapper;
|
||||
|
||||
/**
|
||||
* 处理上传的音频文件
|
||||
*/
|
||||
@ -175,6 +198,28 @@ public class AudioProcessService {
|
||||
return file.getAbsolutePath();
|
||||
}
|
||||
|
||||
private String saveByteArrayToFile(InputStream inputStream, String filename) throws IOException {
|
||||
// 确定保存路径(可以是临时目录或指定目录)
|
||||
String directory = System.getProperty("java.io.tmpdir"); // 使用系统临时目录
|
||||
File dir = new File(directory);
|
||||
if (!dir.exists()) {
|
||||
dir.mkdirs();
|
||||
}
|
||||
|
||||
// 创建完整文件路径
|
||||
File file = new File(dir, filename);
|
||||
|
||||
// 从输入流读取数据并写入文件
|
||||
try (FileOutputStream fos = new FileOutputStream(file);
|
||||
InputStream is = inputStream) {
|
||||
byte[] buffer = new byte[8192]; // 8KB缓冲区
|
||||
int bytesRead;
|
||||
while ((bytesRead = is.read(buffer)) != -1) {
|
||||
fos.write(buffer, 0, bytesRead);
|
||||
}
|
||||
}
|
||||
return file.getAbsolutePath();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@ -305,4 +350,191 @@ public class AudioProcessService {
|
||||
}
|
||||
|
||||
|
||||
public String uploadAudioToOss(AppAudioFileDto bo) {
|
||||
MultipartFile file = bo.getFile();
|
||||
// 校验文件格式和大小
|
||||
validateAudioFileForRestful(file);
|
||||
// 上传文件
|
||||
// SysOssVo upload = sysOssService.upload(file);
|
||||
String filename = file.getOriginalFilename();
|
||||
String savedPath = null;
|
||||
try {
|
||||
String fileHash = fileHashUtil.hash(file);
|
||||
SysOssVo upload = ossService.updateHash(file, fileHash);
|
||||
// 强制将HTTP替换为HTTPS
|
||||
if (upload.getUrl() != null && upload.getUrl().startsWith("http://")) {
|
||||
upload.setUrl(upload.getUrl().replaceFirst("^http://", "https://"));
|
||||
}
|
||||
String fileSuffix = filename.substring(filename.lastIndexOf('.')).toLowerCase();
|
||||
AppBusinessFileBo appBusinessFileBo = new AppBusinessFileBo();
|
||||
appBusinessFileBo.setFileId(upload.getOssId());
|
||||
appBusinessFileBo.setBusinessId(bo.getDeviceId());
|
||||
appBusinessFileBo.setFileType(3L);
|
||||
appBusinessFileBo.setCreateBy(AppLoginHelper.getUserId());
|
||||
savedPath = saveByteArrayToFile(file.getInputStream(), generateRandomFileName(fileSuffix));
|
||||
if (savedPath != null) {
|
||||
log.info("MP3文件已保存: {}", savedPath);
|
||||
Integer mp3Duration = Mp3Duration.getMp3Duration(savedPath);
|
||||
log.info("MP3文件时长: {} 秒", mp3Duration);
|
||||
appBusinessFileBo.setDuration(mp3Duration);
|
||||
}
|
||||
appBusinessFileService.insertByBo(appBusinessFileBo);
|
||||
if (upload != null) {
|
||||
return upload.getUrl();
|
||||
}
|
||||
} catch (Exception e){
|
||||
log.error("上传音频文件失败", e);
|
||||
}finally {
|
||||
log.info("删除临时文件: {}", savedPath);
|
||||
if(savedPath != null){
|
||||
deleteTempFile(new File(savedPath));
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验音频文件格式
|
||||
*/
|
||||
private void validateAudioFileForRestful(MultipartFile file) {
|
||||
if (file == null || file.isEmpty()) {
|
||||
throw new IllegalArgumentException("上传文件不能为空");
|
||||
}
|
||||
|
||||
String originalFilename = file.getOriginalFilename();
|
||||
if (originalFilename == null) {
|
||||
throw new IllegalArgumentException("文件名不能为空");
|
||||
}
|
||||
List<String> SUPPORTED_FORMATS = Arrays.asList(
|
||||
".wav", ".mp3", ".pcm"
|
||||
);
|
||||
|
||||
String ext = originalFilename.substring(originalFilename.lastIndexOf('.')).toLowerCase();
|
||||
// 检查文件扩展名
|
||||
if (!SUPPORTED_FORMATS.contains(ext)) {
|
||||
throw new IllegalArgumentException("只允许上传MP3、WAV、PCM格式的音频文件");
|
||||
}
|
||||
|
||||
// 检查文件大小
|
||||
if (file.getSize() > MAX_AUDIO_SIZE) {
|
||||
throw new IllegalArgumentException("音频大小不能超过5MB");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断文件是否为支持的音频格式
|
||||
*/
|
||||
private boolean isSupportedFormat(String filename) {
|
||||
if (filename == null || filename.lastIndexOf('.') == -1) {
|
||||
return false;
|
||||
}
|
||||
String ext = filename.substring(filename.lastIndexOf('.')).toLowerCase();
|
||||
return SUPPORTED_FORMATS.contains(ext);
|
||||
}
|
||||
|
||||
public String textToSpeech(Long deviceId,String text, String fileSuffix) {
|
||||
//支持PCM/WAV/MP3格式
|
||||
if (fileSuffix == null || fileSuffix.isEmpty()) {
|
||||
fileSuffix = "mp3";
|
||||
}
|
||||
fileSuffix = fileSuffix.toLowerCase();
|
||||
List<String> SUPPORTED_FORMATS = Arrays.asList(
|
||||
"wav", "mp3", "pcm"
|
||||
);
|
||||
boolean contains = SUPPORTED_FORMATS.contains(fileSuffix);
|
||||
if (!contains) {
|
||||
throw new IllegalArgumentException("不支持的音频格式");
|
||||
}
|
||||
String accessKeyId = "LTAI5t66moCkhNC32TDJ5ReP";
|
||||
String accessKeySecret = "2F3sdoBJ08bYvJcuDgSkLnJwGXsvYH";
|
||||
String appKey = "lbGuq5K5bEH4uxmT";
|
||||
String savedPath = null;
|
||||
try {
|
||||
// 使用HTTP方式调用
|
||||
HttpTtsClient httpClient = new HttpTtsClient(accessKeyId, accessKeySecret, appKey);
|
||||
//
|
||||
byte[] mp3Data = httpClient.synthesizeTextToMp3(text);
|
||||
// byte[] mp3Data = alibabaTTSUtil.synthesizeTextToMp3(text);
|
||||
|
||||
SysOssVo upload = ossService.upload(mp3Data, generateRandomFileName(fileSuffix));
|
||||
|
||||
// 强制将HTTP替换为HTTPS
|
||||
if (upload.getUrl() != null && upload.getUrl().startsWith("http://")) {
|
||||
upload.setUrl(upload.getUrl().replaceFirst("^http://", "https://"));
|
||||
}
|
||||
|
||||
|
||||
AppBusinessFileBo appBusinessFileBo = new AppBusinessFileBo();
|
||||
appBusinessFileBo.setFileId(upload.getOssId());
|
||||
appBusinessFileBo.setBusinessId(deviceId);
|
||||
appBusinessFileBo.setFileType(3L);
|
||||
appBusinessFileBo.setCreateBy(AppLoginHelper.getUserId());
|
||||
savedPath = saveByteArrayToFile(mp3Data, generateRandomFileName(fileSuffix));
|
||||
if (savedPath != null) {
|
||||
log.info("MP3文件已保存: {}", savedPath);
|
||||
Integer mp3Duration = Mp3Duration.getMp3Duration(savedPath);
|
||||
log.info("MP3文件时长: {} 秒", mp3Duration);
|
||||
appBusinessFileBo.setDuration(mp3Duration);
|
||||
}
|
||||
appBusinessFileService.insertByBo(appBusinessFileBo);
|
||||
if (upload != null) {
|
||||
return upload.getUrl();
|
||||
}
|
||||
} catch (Exception e){
|
||||
log.error("上传音频文件失败", e);
|
||||
} finally {
|
||||
log.info("删除临时文件: {}", savedPath);
|
||||
if(savedPath != null){
|
||||
deleteTempFile(new File(savedPath));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static final Random random = new Random();
|
||||
private static final DateTimeFormatter formatter =
|
||||
DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
|
||||
/**
|
||||
* 生成纯随机文件名(无原文件名依赖)
|
||||
*/
|
||||
public static String generateRandomFileName(String extension) {
|
||||
String timestamp = LocalDateTime.now().format(formatter);
|
||||
int randomNum = random.nextInt(10000);
|
||||
String uuidPart = UUID.randomUUID().toString().substring(0, 8);
|
||||
|
||||
if (!extension.startsWith(".")) {
|
||||
extension = "." + extension;
|
||||
}
|
||||
|
||||
return timestamp + "_" + String.format("%04d", randomNum) + "_" + uuidPart + extension;
|
||||
}
|
||||
|
||||
public List<AppFileVo> queryAudioFileList(Long deviceId) {
|
||||
if(deviceId == null){
|
||||
return null;
|
||||
}
|
||||
AppBusinessFileBo bo = new AppBusinessFileBo();
|
||||
bo.setBusinessId(deviceId);
|
||||
bo.setFileType(3L);
|
||||
List<AppFileVo> appFileVos = appBusinessFileService.queryAppFileList(bo);
|
||||
return appFileVos;
|
||||
}
|
||||
|
||||
public R<Void> deleteAudioFile(Long fileId,Long deviceId) {
|
||||
UpdateWrapper<AppBusinessFile> updateWrapper = new UpdateWrapper<>();
|
||||
updateWrapper.eq("file_id",fileId);
|
||||
updateWrapper.eq("business_id",deviceId);
|
||||
appBusinessFileMapper.delete(updateWrapper);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
public R<Void> renameAudioFile(AppFileRenameDto bo) {
|
||||
UpdateWrapper<AppBusinessFile> updateWrapper = new UpdateWrapper<>();
|
||||
updateWrapper.eq("file_id",bo.getFileId());
|
||||
updateWrapper.eq("business_id",bo.getDeviceId());
|
||||
updateWrapper.set("re_name",bo.getFileName());
|
||||
appBusinessFileMapper.update(updateWrapper);
|
||||
return R.ok();
|
||||
}
|
||||
}
|
||||
@ -1,9 +1,5 @@
|
||||
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;
|
||||
@ -16,9 +12,9 @@ import java.util.List;
|
||||
@Component
|
||||
public class MqttRuleEngine {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("threadPoolTaskExecutor")
|
||||
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
|
||||
// @Autowired
|
||||
// @Qualifier("threadPoolTaskExecutor")
|
||||
// private ThreadPoolTaskExecutor threadPoolTaskExecutor;
|
||||
|
||||
private final LinkedHashMap<String, MqttMessageRule> rulesMap = new LinkedHashMap<>();
|
||||
|
||||
@ -41,7 +37,8 @@ public class MqttRuleEngine {
|
||||
int commandType = context.getCommandType();
|
||||
MqttMessageRule mqttMessageRule = rulesMap.get("Light_" + commandType);
|
||||
if (mqttMessageRule != null) {
|
||||
threadPoolTaskExecutor.execute(() -> mqttMessageRule.execute(context));
|
||||
// threadPoolTaskExecutor.execute(() -> mqttMessageRule.execute(context));
|
||||
mqttMessageRule.execute(context);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,26 @@
|
||||
package com.fuyuanshen.global.mqtt.base;
|
||||
|
||||
/**
|
||||
* MQTT消息处理接口
|
||||
*/
|
||||
public interface NewMqttMessageRule {
|
||||
|
||||
/**
|
||||
* 获取命令类型
|
||||
* @return 命令类型
|
||||
*/
|
||||
String getCommandType();
|
||||
/**
|
||||
* 执行处理
|
||||
* @param context 处理上下文
|
||||
*/
|
||||
void execute(NewMqttRuleContext context);
|
||||
|
||||
/**
|
||||
* 获取优先级,数值越小优先级越高
|
||||
* @return 优先级
|
||||
*/
|
||||
default int getPriority() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
package com.fuyuanshen.global.mqtt.base;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* MQTT消息处理上下文
|
||||
*/
|
||||
@Data
|
||||
public class NewMqttRuleContext {
|
||||
/**
|
||||
* 命令类型
|
||||
*/
|
||||
private String commandType;
|
||||
/**
|
||||
* 设备IMEI
|
||||
*/
|
||||
private String deviceImei;
|
||||
|
||||
/**
|
||||
* MQTT消息负载字典
|
||||
*/
|
||||
private Map<String, Object> payloadDict;
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
package com.fuyuanshen.global.mqtt.base;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* MQTT消息引擎
|
||||
*/
|
||||
@Component
|
||||
public class NewMqttRuleEngine {
|
||||
|
||||
// @Autowired
|
||||
// @Qualifier("threadPoolTaskExecutor")
|
||||
// private ThreadPoolTaskExecutor threadPoolTaskExecutor;
|
||||
|
||||
private final LinkedHashMap<String, NewMqttMessageRule> rulesMap = new LinkedHashMap<>();
|
||||
public NewMqttRuleEngine(List<NewMqttMessageRule> rules) {
|
||||
// 按优先级排序
|
||||
rules.sort(Comparator.comparing(NewMqttMessageRule::getPriority));
|
||||
rules.forEach(rule -> rulesMap.put(rule.getCommandType(), rule)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行匹配
|
||||
* @param context 处理上下文
|
||||
* @return
|
||||
*/
|
||||
public boolean executeRule(NewMqttRuleContext context) {
|
||||
String commandType = context.getCommandType();
|
||||
// String funcType = context.getFuncType();
|
||||
NewMqttMessageRule mqttMessageRule = rulesMap.get(commandType);
|
||||
if (mqttMessageRule != null) {
|
||||
// threadPoolTaskExecutor.execute(() -> mqttMessageRule.execute(context));
|
||||
mqttMessageRule.execute(context);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -10,22 +10,43 @@ import org.springframework.integration.mqtt.core.MqttPahoClientFactory;
|
||||
|
||||
@Configuration
|
||||
public class MqttConfiguration {
|
||||
|
||||
@Autowired
|
||||
private MqttPropertiesConfig mqttPropertiesConfig;
|
||||
/** 创建连接工厂 **/
|
||||
|
||||
|
||||
/**
|
||||
* 创建连接工厂
|
||||
**/
|
||||
@Bean
|
||||
public MqttPahoClientFactory mqttPahoClientFactory(){
|
||||
public MqttPahoClientFactory mqttPahoClientFactory() {
|
||||
DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
|
||||
MqttConnectOptions options = new MqttConnectOptions();
|
||||
options.setCleanSession(true); //设置新会话
|
||||
options.setUserName(mqttPropertiesConfig.getUsername());
|
||||
options.setPassword(mqttPropertiesConfig.getPassword().toCharArray());
|
||||
options.setServerURIs(new String[]{mqttPropertiesConfig.getUrl()});
|
||||
options.setCleanSession(true); // 设置新会话
|
||||
|
||||
// 修复用户名为null时的空指针异常
|
||||
String username = mqttPropertiesConfig.getUsername();
|
||||
if (username != null) {
|
||||
options.setUserName(username);
|
||||
}
|
||||
|
||||
// 修复密码为null时的空指针异常
|
||||
String password = mqttPropertiesConfig.getPassword();
|
||||
if (password != null) {
|
||||
options.setPassword(password.toCharArray());
|
||||
}
|
||||
|
||||
// 修复URL为null时的空指针异常
|
||||
String url = mqttPropertiesConfig.getUrl();
|
||||
if (url != null) {
|
||||
options.setServerURIs(new String[]{url});
|
||||
}
|
||||
|
||||
options.setAutomaticReconnect(true); // 启用自动重连
|
||||
options.setConnectionTimeout(10); // 设置连接超时时间
|
||||
options.setKeepAliveInterval(60); // 设置心跳间隔
|
||||
factory.setConnectionOptions(options);
|
||||
return factory;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -39,11 +39,17 @@ public class MqttInboundConfiguration {
|
||||
public MessageProducer messageProducer(){
|
||||
// 生成一个不重复的随机数
|
||||
String clientId = mqttPropertiesConfig.getSubClientId() + "_" + UUID.fastUUID();
|
||||
// 修复URL为null时的空指针异常
|
||||
String url = mqttPropertiesConfig.getUrl();
|
||||
if (url == null) {
|
||||
throw new IllegalStateException("MQTT服务器URL未配置");
|
||||
}
|
||||
String subTopic = mqttPropertiesConfig.getSubTopic();
|
||||
MqttPahoMessageDrivenChannelAdapter mqttPahoMessageDrivenChannelAdapter = new MqttPahoMessageDrivenChannelAdapter(
|
||||
mqttPropertiesConfig.getUrl(),
|
||||
url,
|
||||
clientId,
|
||||
mqttPahoClientFactory,
|
||||
mqttPropertiesConfig.getSubTopic().split(",")
|
||||
subTopic.split(",")
|
||||
);
|
||||
mqttPahoMessageDrivenChannelAdapter.setQos(1);
|
||||
mqttPahoMessageDrivenChannelAdapter.setConverter(new DefaultPahoMessageConverter());
|
||||
|
||||
@ -15,11 +15,14 @@ import org.springframework.messaging.MessageHandler;
|
||||
@Configuration
|
||||
@Slf4j
|
||||
public class MqttOutboundConfiguration {
|
||||
|
||||
@Autowired
|
||||
private MqttPropertiesConfig mqttPropertiesConfig;
|
||||
|
||||
@Autowired
|
||||
private MqttPahoClientFactory mqttPahoClientFactory;
|
||||
|
||||
|
||||
|
||||
// 消息通道
|
||||
@Bean
|
||||
public MessageChannel mqttOutboundChannel(){
|
||||
@ -32,13 +35,19 @@ public class MqttOutboundConfiguration {
|
||||
@ServiceActivator(inputChannel = "mqttOutboundChannel") // 指定处理器针对哪个通道的消息进行处理
|
||||
public MessageHandler mqttOutboundMessageHandler(){
|
||||
String clientId = mqttPropertiesConfig.getPubClientId() + "_" + UUID.fastUUID();
|
||||
// 修复URL为null时的空指针异常
|
||||
String url = mqttPropertiesConfig.getUrl();
|
||||
if (url == null) {
|
||||
throw new IllegalStateException("MQTT服务器URL未配置");
|
||||
}
|
||||
|
||||
MqttPahoMessageHandler mqttPahoMessageHandler = new MqttPahoMessageHandler(
|
||||
mqttPropertiesConfig.getUrl(),
|
||||
url,
|
||||
clientId,
|
||||
mqttPahoClientFactory
|
||||
);
|
||||
mqttPahoMessageHandler.setDefaultQos(1);
|
||||
mqttPahoMessageHandler.setDefaultTopic("B/#");
|
||||
mqttPahoMessageHandler.setDefaultTopic(mqttPropertiesConfig.getPubTopic());
|
||||
mqttPahoMessageHandler.setAsync(true);
|
||||
return mqttPahoMessageHandler;
|
||||
}
|
||||
|
||||
@ -14,6 +14,8 @@ public class MqttPropertiesConfig {
|
||||
private String url;
|
||||
private String subClientId;
|
||||
private String subTopic;
|
||||
private String subTopic2;
|
||||
private String pubClientId;
|
||||
private String pubTopic;
|
||||
private String pubTopic2;
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
package com.fuyuanshen.global.mqtt.config;
|
||||
|
||||
|
||||
import cn.hutool.core.lang.UUID;
|
||||
import com.fuyuanshen.global.mqtt.receiver.NewReceiverMessageHandler;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
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;
|
||||
|
||||
|
||||
@Configuration
|
||||
@Slf4j
|
||||
public class NewMqttInboundConfiguration {
|
||||
@Autowired
|
||||
private MqttPropertiesConfig mqttPropertiesConfig;
|
||||
@Autowired
|
||||
private MqttPahoClientFactory mqttPahoClientFactory;
|
||||
@Autowired
|
||||
private NewReceiverMessageHandler receiverMessageHandler2;
|
||||
//消息通道
|
||||
@Bean
|
||||
public MessageChannel messageInboundChannel2(){
|
||||
return new DirectChannel();
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置入站适配器
|
||||
* 作用: 设置订阅主题,以及指定消息的通道 等相关属性
|
||||
* */
|
||||
@Bean
|
||||
public MessageProducer messageProducer2(){
|
||||
// 生成一个不重复的随机数
|
||||
String clientId = mqttPropertiesConfig.getSubClientId() + "_" + UUID.fastUUID();
|
||||
String subTopic = mqttPropertiesConfig.getSubTopic2();
|
||||
log.info("订阅主题:{}", subTopic);
|
||||
MqttPahoMessageDrivenChannelAdapter mqttPahoMessageDrivenChannelAdapter = new MqttPahoMessageDrivenChannelAdapter(
|
||||
mqttPropertiesConfig.getUrl(),
|
||||
clientId,
|
||||
mqttPahoClientFactory,
|
||||
subTopic.split(",")
|
||||
);
|
||||
mqttPahoMessageDrivenChannelAdapter.setQos(1);
|
||||
mqttPahoMessageDrivenChannelAdapter.setConverter(new DefaultPahoMessageConverter());
|
||||
mqttPahoMessageDrivenChannelAdapter.setOutputChannel(messageInboundChannel2());
|
||||
return mqttPahoMessageDrivenChannelAdapter;
|
||||
}
|
||||
/** 指定处理消息来自哪个通道 */
|
||||
@Bean
|
||||
@ServiceActivator(inputChannel = "messageInboundChannel2")
|
||||
public MessageHandler messageHandler2(){
|
||||
return receiverMessageHandler2;
|
||||
}
|
||||
|
||||
// @Bean
|
||||
// @ServiceActivator(inputChannel = "messageInboundChannel") // 确保通道名称正确
|
||||
// public MessageHandler deviceAlarmMessageHandler() {
|
||||
// return new DeviceAlrmMessageHandler();
|
||||
// }
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
package com.fuyuanshen.global.mqtt.config;
|
||||
|
||||
import cn.hutool.core.lang.UUID;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
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.mqtt.core.MqttPahoClientFactory;
|
||||
import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
|
||||
@Configuration
|
||||
@Slf4j
|
||||
public class NewMqttOutboundConfiguration {
|
||||
@Autowired
|
||||
private MqttPropertiesConfig mqttPropertiesConfig;
|
||||
@Autowired
|
||||
private MqttPahoClientFactory mqttPahoClientFactory;
|
||||
|
||||
// 消息通道
|
||||
@Bean
|
||||
public MessageChannel mqttOutboundChannel2(){
|
||||
return new DirectChannel();
|
||||
}
|
||||
|
||||
|
||||
/** 配置出站消息处理器 */
|
||||
@Bean
|
||||
@ServiceActivator(inputChannel = "mqttOutboundChannel2") // 指定处理器针对哪个通道的消息进行处理
|
||||
public MessageHandler mqttOutboundMessageHandler2(){
|
||||
String clientId = mqttPropertiesConfig.getPubClientId() + "_" + UUID.fastUUID();
|
||||
MqttPahoMessageHandler mqttPahoMessageHandler = new MqttPahoMessageHandler(
|
||||
mqttPropertiesConfig.getUrl(),
|
||||
clientId,
|
||||
mqttPahoClientFactory
|
||||
);
|
||||
mqttPahoMessageHandler.setDefaultQos(1);
|
||||
mqttPahoMessageHandler.setDefaultTopic(mqttPropertiesConfig.getPubTopic2());
|
||||
mqttPahoMessageHandler.setAsync(true);
|
||||
return mqttPahoMessageHandler;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
package com.fuyuanshen.global.mqtt.constants;
|
||||
|
||||
public class DeviceRedisKeyConstants {
|
||||
|
||||
public static final String DEVICE_KEY_PREFIX = "device:";
|
||||
// 设备上报状态
|
||||
public static final String DEVICE_STATUS_KEY_PREFIX = ":status";
|
||||
@ -52,4 +53,5 @@ public class DeviceRedisKeyConstants {
|
||||
* 告警信息
|
||||
*/
|
||||
public static final String DEVICE_ALARM_MESSAGE_KEY_PREFIX = ":alarmMessage";
|
||||
|
||||
}
|
||||
|
||||
@ -36,7 +36,6 @@ public class LightingCommandTypeConstants {
|
||||
*/
|
||||
public static final String SEND_MESSAGE = "Light_6";
|
||||
|
||||
|
||||
/**
|
||||
* 报警模式
|
||||
*/
|
||||
|
||||
@ -13,4 +13,5 @@ public interface MqttConstants {
|
||||
*/
|
||||
String GLOBAL_SUB_KEY = "A/";
|
||||
|
||||
String GLOBAL_PUB_KEY2 = "command/";
|
||||
}
|
||||
|
||||
@ -1,20 +1,22 @@
|
||||
package com.fuyuanshen.global.mqtt.publish;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaIgnore;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/")
|
||||
@RequestMapping("/api")
|
||||
@SaIgnore
|
||||
@Slf4j
|
||||
public class DeviceDataController {
|
||||
|
||||
@Autowired
|
||||
private MqttClientTest mqttClientTest;
|
||||
|
||||
// @PostMapping("/{deviceId}/command")
|
||||
@GetMapping("/command")
|
||||
public ResponseEntity<String> sendCommand() {
|
||||
|
||||
mqttClientTest.sendMsg();
|
||||
|
||||
@ -13,10 +13,10 @@ public class MqttClientTest {
|
||||
private MqttGateway mqttGateway;
|
||||
|
||||
public void sendMsg() {
|
||||
mqttGateway.sendMsgToMqtt("worker/location/1", "hello mqtt spring boot");
|
||||
mqttGateway.sendMsgToMqtt("command/894078/HBY670/864865082081523", "hello mqtt spring boot");
|
||||
log.info("message is send");
|
||||
|
||||
mqttGateway.sendMsgToMqtt("worker/alert/2", "hello mqtt spring boot2");
|
||||
mqttGateway.sendMsgToMqtt("report/894078/HBY670/864865082081523", "hello mqtt spring boot2");
|
||||
log.info("message is send2");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,125 @@
|
||||
package com.fuyuanshen.global.mqtt.receiver;
|
||||
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import com.baomidou.lock.LockTemplate;
|
||||
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.global.mqtt.base.NewMqttRuleContext;
|
||||
import com.fuyuanshen.global.mqtt.base.NewMqttRuleEngine;
|
||||
import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants;
|
||||
import com.fuyuanshen.global.queue.MqttMessageQueueConstants;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.redisson.api.RLock;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class NewReceiverMessageHandler implements MessageHandler {
|
||||
|
||||
@Autowired
|
||||
private NewMqttRuleEngine newRuleEngine;
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message<?> message) throws MessagingException {
|
||||
|
||||
Object payload = message.getPayload();
|
||||
MessageHeaders headers = message.getHeaders();
|
||||
String receivedTopic = Objects.requireNonNull(headers.get("mqtt_receivedTopic")).toString();
|
||||
String receivedQos = Objects.requireNonNull(headers.get("mqtt_receivedQos")).toString();
|
||||
String timestamp = Objects.requireNonNull(headers.get("timestamp")).toString();
|
||||
|
||||
log.info("MQTT2 payload= {} \n receivedTopic = {} \n receivedQos = {} \n timestamp = {}",
|
||||
payload, receivedTopic, receivedQos, timestamp);
|
||||
|
||||
Dict payloadDict = JsonUtils.parseMap(payload.toString());
|
||||
if (receivedTopic == null || payloadDict == null) {
|
||||
return;
|
||||
}
|
||||
String imei = payloadDict.getStr("imei");
|
||||
String funcType = payloadDict.getStr("funcType");
|
||||
// 执行业务逻辑
|
||||
if(StringUtils.isNotBlank(imei)){
|
||||
String queueKey = MqttMessageQueueConstants.MQTT_MESSAGE_QUEUE_KEY;
|
||||
String dedupKey = MqttMessageQueueConstants.MQTT_MESSAGE_DEDUP_KEY;
|
||||
RedisUtils.offerDeduplicated(queueKey,dedupKey,imei, Duration.ofSeconds(900));
|
||||
//在线状态
|
||||
String deviceOnlineStatusRedisKey = GlobalConstants.GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX+ imei + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX ;
|
||||
RedisUtils.setCacheObject(deviceOnlineStatusRedisKey, "1", Duration.ofSeconds(360));
|
||||
}
|
||||
|
||||
String[] topicArr = receivedTopic.split("/");
|
||||
|
||||
NewMqttRuleContext context = new NewMqttRuleContext();
|
||||
context.setCommandType(topicArr[2]+"_"+funcType);
|
||||
context.setDeviceImei(imei);
|
||||
context.setPayloadDict(payloadDict);
|
||||
|
||||
boolean ruleExecuted = newRuleEngine.executeRule(context);
|
||||
|
||||
if (!ruleExecuted) {
|
||||
log.warn("未找到匹配的规则来处理命令类型: {}", topicArr[2] + " : " +funcType);
|
||||
}
|
||||
// final LockInfo lockInfo = lockTemplate.lock(GlobalConstants.GLOBAL_REDIS_KEY + lockKey + imei, 100L, 3000L, RedissonLockExecutor.class);
|
||||
// if (null == lockInfo) {
|
||||
// log.info("MQTT3业务处理中,请稍后再试:funcType=>{},imei=>{}",funcType,imei);
|
||||
// return;
|
||||
// }
|
||||
//// 获取锁成功,处理业务
|
||||
// try {
|
||||
// if(StringUtils.isNotBlank(imei)){
|
||||
// String queueKey = MqttMessageQueueConstants.MQTT_MESSAGE_QUEUE_KEY;
|
||||
// String dedupKey = MqttMessageQueueConstants.MQTT_MESSAGE_DEDUP_KEY;
|
||||
// RedisUtils.offerDeduplicated(queueKey,dedupKey,imei, Duration.ofSeconds(900));
|
||||
// //在线状态
|
||||
// String deviceOnlineStatusRedisKey = GlobalConstants.GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX+ imei + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX ;
|
||||
// RedisUtils.setCacheObject(deviceOnlineStatusRedisKey, "1", Duration.ofSeconds(360));
|
||||
// }
|
||||
//
|
||||
// String[] topicArr = receivedTopic.split("/");
|
||||
//
|
||||
// NewMqttRuleContext context = new NewMqttRuleContext();
|
||||
// context.setCommandType(topicArr[2]+"_"+funcType);
|
||||
// context.setDeviceImei(imei);
|
||||
// context.setPayloadDict(payloadDict);
|
||||
//
|
||||
// boolean ruleExecuted = newRuleEngine.executeRule(context);
|
||||
//
|
||||
// if (!ruleExecuted) {
|
||||
// log.warn("未找到匹配的规则来处理命令类型: {}", topicArr[2] + " : " +funcType);
|
||||
// }
|
||||
// } finally {
|
||||
// //释放锁
|
||||
// lockTemplate.releaseLock(lockInfo);
|
||||
// }
|
||||
|
||||
|
||||
/* ===== 追加:根据报文内容识别格式并统一解析 ===== */
|
||||
// int intType = MqttXinghanCommandType.computeVirtualCommandType(payloadDict);
|
||||
// if (intType > 0) {
|
||||
// MqttRuleContext newCtx = new MqttRuleContext();
|
||||
// String commandType = "Light_"+intType;
|
||||
// newCtx.setCommandType(commandType);
|
||||
// newCtx.setDeviceImei(imei);
|
||||
// newCtx.setPayloadDict(payloadDict);
|
||||
//
|
||||
// boolean ok = ruleEngine.executeRule(newCtx);
|
||||
// if (!ok) {
|
||||
// log.warn("新规则引擎未命中, imei={}", imei);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
@ -9,9 +9,12 @@ import com.fuyuanshen.common.redis.utils.RedisUtils;
|
||||
import com.fuyuanshen.global.mqtt.base.MqttRuleContext;
|
||||
import com.fuyuanshen.global.mqtt.base.MqttRuleEngine;
|
||||
import com.fuyuanshen.global.mqtt.base.MqttXinghanCommandType;
|
||||
import com.fuyuanshen.global.mqtt.base.NewMqttRuleContext;
|
||||
import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants;
|
||||
import com.fuyuanshen.global.queue.MqttMessageQueueConstants;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.redisson.api.RLock;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
@ -21,6 +24,7 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX;
|
||||
|
||||
@ -38,55 +42,77 @@ public class ReceiverMessageHandler implements MessageHandler {
|
||||
String receivedTopic = Objects.requireNonNull(headers.get("mqtt_receivedTopic")).toString();
|
||||
String receivedQos = Objects.requireNonNull(headers.get("mqtt_receivedQos")).toString();
|
||||
String timestamp = Objects.requireNonNull(headers.get("timestamp")).toString();
|
||||
|
||||
|
||||
log.info("MQTT payload= {} \n receivedTopic = {} \n receivedQos = {} \n timestamp = {}",
|
||||
payload, receivedTopic, receivedQos, timestamp);
|
||||
|
||||
|
||||
Dict payloadDict = JsonUtils.parseMap(payload.toString());
|
||||
if (receivedTopic == null || payloadDict == null) {
|
||||
return;
|
||||
}
|
||||
String[] subStr = receivedTopic.split("/");
|
||||
String deviceImei = subStr[1];
|
||||
String state = payloadDict.getStr("state");
|
||||
Object[] convertArr = ImageToCArrayConverter.convertByteStringToMixedObjectArray(state);
|
||||
if(StringUtils.isNotBlank(deviceImei)){
|
||||
String queueKey = MqttMessageQueueConstants.MQTT_MESSAGE_QUEUE_KEY;
|
||||
String dedupKey = MqttMessageQueueConstants.MQTT_MESSAGE_DEDUP_KEY;
|
||||
RedisUtils.offerDeduplicated(queueKey,dedupKey,deviceImei, Duration.ofSeconds(900));
|
||||
//在线状态
|
||||
String deviceOnlineStatusRedisKey = GlobalConstants.GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX+ deviceImei + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX ;
|
||||
RedisUtils.setCacheObject(deviceOnlineStatusRedisKey, "1", Duration.ofSeconds(360));
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (convertArr.length > 0) {
|
||||
Byte val1 = (Byte) convertArr[0];
|
||||
MqttRuleContext context = new MqttRuleContext();
|
||||
context.setCommandType(val1);
|
||||
context.setConvertArr(convertArr);
|
||||
context.setDeviceImei(deviceImei);
|
||||
context.setPayloadDict(payloadDict);
|
||||
RedissonClient client = RedisUtils.getClient();
|
||||
String lockKey = "mqtt:consumer:lock:";
|
||||
String KEY = GlobalConstants.GLOBAL_REDIS_KEY + lockKey + deviceImei;
|
||||
RLock lock = client.getLock(KEY);
|
||||
|
||||
boolean ruleExecuted = ruleEngine.executeRule(context);
|
||||
|
||||
if (!ruleExecuted) {
|
||||
log.warn("未找到匹配的规则来处理命令类型: {}", val1);
|
||||
try {
|
||||
// 尝试获取锁,
|
||||
boolean isLocked = lock.tryLock(60, 3000, TimeUnit.MILLISECONDS);
|
||||
if (isLocked) {
|
||||
String state = payloadDict.getStr("state");
|
||||
Object[] convertArr = ImageToCArrayConverter.convertByteStringToMixedObjectArray(state);
|
||||
if(StringUtils.isNotBlank(deviceImei)){
|
||||
String queueKey = MqttMessageQueueConstants.MQTT_MESSAGE_QUEUE_KEY;
|
||||
String dedupKey = MqttMessageQueueConstants.MQTT_MESSAGE_DEDUP_KEY;
|
||||
RedisUtils.offerDeduplicated(queueKey,dedupKey,deviceImei, Duration.ofSeconds(900));
|
||||
//在线状态
|
||||
String deviceOnlineStatusRedisKey = GlobalConstants.GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX+ deviceImei + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX ;
|
||||
RedisUtils.setCacheObject(deviceOnlineStatusRedisKey, "1", Duration.ofSeconds(360));
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (convertArr.length > 0) {
|
||||
Byte val1 = (Byte) convertArr[0];
|
||||
MqttRuleContext context = new MqttRuleContext();
|
||||
context.setCommandType(val1);
|
||||
context.setConvertArr(convertArr);
|
||||
context.setDeviceImei(deviceImei);
|
||||
context.setPayloadDict(payloadDict);
|
||||
|
||||
boolean ruleExecuted = ruleEngine.executeRule(context);
|
||||
|
||||
if (!ruleExecuted) {
|
||||
log.warn("未找到匹配的规则来处理命令类型: {}", val1);
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== 追加:根据报文内容识别格式并统一解析 ===== */
|
||||
int intType = MqttXinghanCommandType.computeVirtualCommandType(payloadDict);
|
||||
if (intType > 0) {
|
||||
MqttRuleContext newCtx = new MqttRuleContext();
|
||||
newCtx.setCommandType((byte) intType);
|
||||
newCtx.setDeviceImei(deviceImei);
|
||||
newCtx.setPayloadDict(payloadDict);
|
||||
|
||||
boolean ok = ruleEngine.executeRule(newCtx);
|
||||
if (!ok) {
|
||||
log.warn("新规则引擎未命中, imei={}", deviceImei);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
log.warn("MQTT获取锁失败,请稍后再试");
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== 追加:根据报文内容识别格式并统一解析 ===== */
|
||||
int intType = MqttXinghanCommandType.computeVirtualCommandType(payloadDict);
|
||||
if (intType > 0) {
|
||||
MqttRuleContext newCtx = new MqttRuleContext();
|
||||
newCtx.setCommandType((byte) intType);
|
||||
newCtx.setDeviceImei(deviceImei);
|
||||
newCtx.setPayloadDict(payloadDict);
|
||||
|
||||
boolean ok = ruleEngine.executeRule(newCtx);
|
||||
if (!ok) {
|
||||
log.warn("新规则引擎未命中, imei={}", deviceImei);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
} finally {
|
||||
// 释放锁
|
||||
if (lock.isHeldByCurrentThread()) {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,18 +38,18 @@ public class BjqActiveReportingDeviceDataRule implements MqttMessageRule {
|
||||
@Override
|
||||
public void execute(MqttRuleContext context) {
|
||||
try {
|
||||
Object[] convertArr = context.getConvertArr();
|
||||
// Latitude, longitude
|
||||
//主灯档位,激光灯档位,电量百分比,充电状态,电池剩余续航时间
|
||||
String mainLightMode = convertArr[1].toString();
|
||||
String laserLightMode = convertArr[2].toString();
|
||||
String batteryPercentage = convertArr[3].toString();
|
||||
String chargeState = convertArr[4].toString();
|
||||
String batteryRemainingTime = convertArr[5].toString();
|
||||
|
||||
// 发送设备状态和位置信息到Redis
|
||||
asyncSendDeviceDataToRedisWithFuture(context.getDeviceImei(), mainLightMode, laserLightMode,
|
||||
batteryPercentage, chargeState, batteryRemainingTime);
|
||||
// Object[] convertArr = context.getConvertArr();
|
||||
// // Latitude, longitude
|
||||
// //主灯档位,激光灯档位,电量百分比,充电状态,电池剩余续航时间
|
||||
// String mainLightMode = convertArr[1].toString();
|
||||
// String laserLightMode = convertArr[2].toString();
|
||||
// String batteryPercentage = convertArr[3].toString();
|
||||
// String chargeState = convertArr[4].toString();
|
||||
// String batteryRemainingTime = convertArr[5].toString();
|
||||
//
|
||||
// // 发送设备状态和位置信息到Redis
|
||||
// asyncSendDeviceDataToRedisWithFuture(context.getDeviceImei(), mainLightMode, laserLightMode,
|
||||
// batteryPercentage, chargeState, batteryRemainingTime);
|
||||
} catch (Exception e) {
|
||||
log.error("处理上报数据命令时出错", e);
|
||||
}
|
||||
@ -67,31 +67,29 @@ public class BjqActiveReportingDeviceDataRule implements MqttMessageRule {
|
||||
*/
|
||||
public void asyncSendDeviceDataToRedisWithFuture(String deviceImei, String mainLightMode, String laserLightMode,
|
||||
String batteryPercentage, String chargeState, String batteryRemainingTime) {
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
// 构造设备状态信息对象
|
||||
Map<String, Object> deviceInfo = new LinkedHashMap<>();
|
||||
deviceInfo.put("deviceImei", deviceImei);
|
||||
deviceInfo.put("mainLightMode", mainLightMode);
|
||||
deviceInfo.put("laserLightMode", laserLightMode);
|
||||
deviceInfo.put("batteryPercentage", batteryPercentage);
|
||||
deviceInfo.put("chargeState", chargeState);
|
||||
deviceInfo.put("batteryRemainingTime", batteryRemainingTime);
|
||||
deviceInfo.put("timestamp", System.currentTimeMillis());
|
||||
try {
|
||||
// 构造设备状态信息对象
|
||||
Map<String, Object> deviceInfo = new LinkedHashMap<>();
|
||||
deviceInfo.put("deviceImei", deviceImei);
|
||||
deviceInfo.put("mainLightMode", mainLightMode);
|
||||
deviceInfo.put("laserLightMode", laserLightMode);
|
||||
deviceInfo.put("batteryPercentage", batteryPercentage);
|
||||
deviceInfo.put("chargeState", chargeState);
|
||||
deviceInfo.put("batteryRemainingTime", batteryRemainingTime);
|
||||
deviceInfo.put("timestamp", System.currentTimeMillis());
|
||||
|
||||
// 将设备状态信息存储到Redis中
|
||||
String deviceRedisKey = GlobalConstants.GLOBAL_REDIS_KEY+DeviceRedisKeyConstants.DEVICE_KEY_PREFIX + deviceImei + DEVICE_STATUS_KEY_PREFIX;
|
||||
String deviceInfoJson = JsonUtils.toJsonString(deviceInfo);
|
||||
// 将设备状态信息存储到Redis中
|
||||
String deviceRedisKey = GlobalConstants.GLOBAL_REDIS_KEY+DeviceRedisKeyConstants.DEVICE_KEY_PREFIX + deviceImei + DEVICE_STATUS_KEY_PREFIX;
|
||||
String deviceInfoJson = JsonUtils.toJsonString(deviceInfo);
|
||||
|
||||
// 存储到Redis
|
||||
RedisUtils.setCacheObject(deviceRedisKey, deviceInfoJson);
|
||||
// 存储到Redis
|
||||
RedisUtils.setCacheObject(deviceRedisKey, deviceInfoJson);
|
||||
|
||||
log.info("设备状态信息已异步发送到Redis: device={}, mainLightMode={}, laserLightMode={}, batteryPercentage={}",
|
||||
deviceImei, mainLightMode, laserLightMode, batteryPercentage);
|
||||
} catch (Exception e) {
|
||||
log.error("异步发送设备信息到Redis时出错: device={}, error={}", deviceImei, e.getMessage(), e);
|
||||
}
|
||||
});
|
||||
log.info("设备状态信息已异步发送到Redis: device={}, mainLightMode={}, laserLightMode={}, batteryPercentage={}",
|
||||
deviceImei, mainLightMode, laserLightMode, batteryPercentage);
|
||||
} catch (Exception e) {
|
||||
log.error("异步发送设备信息到Redis时出错: device={}, error={}", deviceImei, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -57,12 +57,12 @@ public class BjqAlarmRule implements MqttMessageRule {
|
||||
if (StringUtils.isNotBlank(convertValue)) {
|
||||
// 将设备状态信息存储到Redis中
|
||||
String deviceRedisKey = GlobalConstants.GLOBAL_REDIS_KEY + DeviceRedisKeyConstants.DEVICE_KEY_PREFIX + context.getDeviceImei() + DEVICE_ALARM_KEY_PREFIX;
|
||||
String sendMessageIng = GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + context.getDeviceImei() + ":messageSending";
|
||||
String sendMessageIng = GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX + context.getDeviceImei() + ":messageSending";
|
||||
if ("1".equals(convertValue)) {
|
||||
RedisUtils.setCacheObject(sendMessageIng, "1", Duration.ofDays(1));
|
||||
// 存储到Redis
|
||||
RedisUtils.setCacheObject(deviceRedisKey, "1");
|
||||
}else if ("0".equals(convertValue)){
|
||||
} else if ("0".equals(convertValue)) {
|
||||
RedisUtils.deleteObject(sendMessageIng);
|
||||
RedisUtils.deleteObject(deviceRedisKey);
|
||||
}
|
||||
|
||||
@ -44,37 +44,37 @@ public class BjqBootLogoRule implements MqttMessageRule {
|
||||
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;
|
||||
}
|
||||
|
||||
String data = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + context.getDeviceImei() +DEVICE_BOOT_LOGO_KEY_PREFIX);
|
||||
if (StringUtils.isEmpty(data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] arr = ImageToCArrayConverter.convertStringToByteArray(data);
|
||||
byte[] specificChunk = ImageToCArrayConverter.getChunk(arr, (val2 - 1), 512);
|
||||
log.info("第{}块数据大小: {} 字节", val2, specificChunk.length);
|
||||
|
||||
ArrayList<Integer> intData = new ArrayList<>();
|
||||
intData.add(3);
|
||||
intData.add((int) val2);
|
||||
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 + context.getDeviceImei(), 1, JsonUtils.toJsonString(map));
|
||||
log.info("发送开机LOGO点阵数据到设备消息=>topic:{},payload:{}",
|
||||
MqttConstants.GLOBAL_PUB_KEY + context.getDeviceImei(),
|
||||
JsonUtils.toJsonString(map));
|
||||
// Byte val2 = (Byte) context.getConvertArr()[1];
|
||||
// if (val2 == 100) {
|
||||
// RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.OK.getCode(), Duration.ofSeconds(20));
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// String data = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + context.getDeviceImei() +DEVICE_BOOT_LOGO_KEY_PREFIX);
|
||||
// if (StringUtils.isEmpty(data)) {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// byte[] arr = ImageToCArrayConverter.convertStringToByteArray(data);
|
||||
// byte[] specificChunk = ImageToCArrayConverter.getChunk(arr, (val2 - 1), 512);
|
||||
// log.info("第{}块数据大小: {} 字节", val2, specificChunk.length);
|
||||
//
|
||||
// ArrayList<Integer> intData = new ArrayList<>();
|
||||
// intData.add(3);
|
||||
// intData.add((int) val2);
|
||||
// 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 + context.getDeviceImei(), 1, JsonUtils.toJsonString(map));
|
||||
// log.info("发送开机LOGO点阵数据到设备消息=>topic:{},payload:{}",
|
||||
// MqttConstants.GLOBAL_PUB_KEY + context.getDeviceImei(),
|
||||
// JsonUtils.toJsonString(map));
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("处理开机LOGO时出错", e);
|
||||
|
||||
@ -34,15 +34,15 @@ public class BjqLaserModeSettingsRule implements MqttMessageRule {
|
||||
public void execute(MqttRuleContext context) {
|
||||
String functionAccess = FUNCTION_ACCESS_KEY + context.getDeviceImei();
|
||||
try {
|
||||
Object[] convertArr = context.getConvertArr();
|
||||
|
||||
String mode = convertArr[1].toString();
|
||||
if(StringUtils.isNotBlank(mode)){
|
||||
// 发送设备状态和位置信息到Redis
|
||||
syncSendDeviceDataToRedisWithFuture(context.getDeviceImei(),mode);
|
||||
}
|
||||
|
||||
RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.OK.getCode(), Duration.ofSeconds(30));
|
||||
// Object[] convertArr = context.getConvertArr();
|
||||
//
|
||||
// String mode = convertArr[1].toString();
|
||||
// if(StringUtils.isNotBlank(mode)){
|
||||
// // 发送设备状态和位置信息到Redis
|
||||
// syncSendDeviceDataToRedisWithFuture(context.getDeviceImei(),mode);
|
||||
// }
|
||||
//
|
||||
// RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.OK.getCode(), Duration.ofSeconds(30));
|
||||
} catch (Exception e) {
|
||||
log.error("处理激光模式命令时出错", e);
|
||||
RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.FAILED.getCode(), Duration.ofSeconds(30));
|
||||
@ -71,5 +71,4 @@ public class BjqLaserModeSettingsRule implements MqttMessageRule {
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -34,15 +34,15 @@ public class BjqLightBrightnessRule implements MqttMessageRule {
|
||||
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));
|
||||
// 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));
|
||||
|
||||
@ -52,26 +52,26 @@ public class BjqLocationDataRule implements MqttMessageRule {
|
||||
public void execute(MqttRuleContext context) {
|
||||
String functionAccess = FUNCTION_ACCESS_KEY + context.getDeviceImei();
|
||||
try {
|
||||
Object[] convertArr = context.getConvertArr();
|
||||
// Latitude, longitude
|
||||
String latitude = convertArr[1].toString();
|
||||
String longitude = convertArr[2].toString();
|
||||
// 判断 latitude 和 longitude 是否都为 0
|
||||
if ("0".equals(latitude) && "0".equals(longitude)) {
|
||||
log.info("位置信息为0,不存储到Redis: device={}, lat={}, lon={}", context.getDeviceImei(), latitude, longitude);
|
||||
return;
|
||||
}
|
||||
// 异步发送经纬度到Redis
|
||||
asyncSendLocationToRedisWithFuture(context.getDeviceImei(), latitude, longitude);
|
||||
// 异步保存数据
|
||||
asyncSaveLocationToMySQLWithFuture(context.getDeviceImei(), latitude, longitude);
|
||||
|
||||
Map<String, Object> map = buildLocationDataMap(latitude, longitude);
|
||||
mqttGateway.sendMsgToMqtt(MqttConstants.GLOBAL_PUB_KEY + context.getDeviceImei(), 1, JsonUtils.toJsonString(map));
|
||||
log.info("发送定位数据到设备=>topic:{},payload:{}",
|
||||
MqttConstants.GLOBAL_PUB_KEY + context.getDeviceImei(),
|
||||
JsonUtils.toJsonString(map));
|
||||
RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.OK.getCode(), Duration.ofSeconds(20));
|
||||
// Object[] convertArr = context.getConvertArr();
|
||||
// // Latitude, longitude
|
||||
// String latitude = convertArr[1].toString();
|
||||
// String longitude = convertArr[2].toString();
|
||||
// // 判断 latitude 和 longitude 是否都为 0
|
||||
// if ("0".equals(latitude) && "0".equals(longitude)) {
|
||||
// log.info("位置信息为0,不存储到Redis: device={}, lat={}, lon={}", context.getDeviceImei(), latitude, longitude);
|
||||
// return;
|
||||
// }
|
||||
// // 异步发送经纬度到Redis
|
||||
// asyncSendLocationToRedisWithFuture(context.getDeviceImei(), latitude, longitude);
|
||||
// // 异步保存数据
|
||||
// asyncSaveLocationToMySQLWithFuture(context.getDeviceImei(), latitude, longitude);
|
||||
//
|
||||
// Map<String, Object> map = buildLocationDataMap(latitude, longitude);
|
||||
// mqttGateway.sendMsgToMqtt(MqttConstants.GLOBAL_PUB_KEY + context.getDeviceImei(), 1, JsonUtils.toJsonString(map));
|
||||
// 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));
|
||||
@ -121,16 +121,15 @@ public class BjqLocationDataRule implements MqttMessageRule {
|
||||
* @param longitude 经度
|
||||
*/
|
||||
public void asyncSendLocationToRedisWithFuture(String deviceImei, String latitude, String longitude) {
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
if (StringUtils.isBlank(latitude) || StringUtils.isBlank(longitude)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (StringUtils.isBlank(latitude) || StringUtils.isBlank(longitude)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// String[] latArr = latitude.split("\\.");
|
||||
// String[] lonArr = longitude.split("\\.");
|
||||
// // 将位置信息存储到Redis中
|
||||
String redisKey = GlobalConstants.GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX + deviceImei + DEVICE_LOCATION_KEY_PREFIX;
|
||||
String redisKey = GlobalConstants.GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX + deviceImei + DEVICE_LOCATION_KEY_PREFIX;
|
||||
// String redisObj = RedisUtils.getCacheObject(redisKey);
|
||||
// JSONObject jsonOBj = JSONObject.parseObject(redisObj);
|
||||
// if(jsonOBj != null){
|
||||
@ -150,34 +149,33 @@ public class BjqLocationDataRule implements MqttMessageRule {
|
||||
// }
|
||||
// }
|
||||
|
||||
// 构造位置信息对象
|
||||
Map<String, Object> locationInfo = new LinkedHashMap<>();
|
||||
double[] doubles = LngLonUtil.gps84_To_Gcj02(Double.parseDouble(latitude), Double.parseDouble(longitude));
|
||||
locationInfo.put("deviceImei", deviceImei);
|
||||
locationInfo.put("latitude", doubles[0]);
|
||||
locationInfo.put("longitude", doubles[1]);
|
||||
locationInfo.put("wgs84_latitude", latitude);
|
||||
locationInfo.put("wgs84_longitude", longitude);
|
||||
String address = GetAddressFromLatUtil.getAdd(String.valueOf(doubles[1]), String.valueOf(doubles[0]));
|
||||
locationInfo.put("address", address);
|
||||
locationInfo.put("timestamp", System.currentTimeMillis());
|
||||
// 构造位置信息对象
|
||||
Map<String, Object> locationInfo = new LinkedHashMap<>();
|
||||
double[] doubles = LngLonUtil.gps84_To_Gcj02(Double.parseDouble(latitude), Double.parseDouble(longitude));
|
||||
locationInfo.put("deviceImei", deviceImei);
|
||||
locationInfo.put("latitude", doubles[0]);
|
||||
locationInfo.put("longitude", doubles[1]);
|
||||
locationInfo.put("wgs84_latitude", latitude);
|
||||
locationInfo.put("wgs84_longitude", longitude);
|
||||
String address = GetAddressFromLatUtil.getAdd(String.valueOf(doubles[1]), String.valueOf(doubles[0]));
|
||||
locationInfo.put("address", address);
|
||||
locationInfo.put("timestamp", System.currentTimeMillis());
|
||||
|
||||
|
||||
String locationJson = JsonUtils.toJsonString(locationInfo);
|
||||
String locationJson = JsonUtils.toJsonString(locationInfo);
|
||||
|
||||
// 存储到Redis
|
||||
RedisUtils.setCacheObject(redisKey, locationJson);
|
||||
// 存储到Redis
|
||||
RedisUtils.setCacheObject(redisKey, locationJson);
|
||||
|
||||
// 存储到一个列表中,保留历史位置信息
|
||||
// 存储到一个列表中,保留历史位置信息
|
||||
// String locationHistoryKey = GlobalConstants.GLOBAL_REDIS_KEY+DeviceRedisKeyConstants.DEVICE_LOCATION_HISTORY_KEY_PREFIX + deviceImei;
|
||||
// RedisUtils.addCacheList(locationHistoryKey, locationJson);
|
||||
// RedisUtils.expire(locationHistoryKey, Duration.ofDays(90));
|
||||
storeDeviceTrajectoryWithSortedSet(deviceImei, locationJson);
|
||||
log.info("位置信息已异步发送到Redis: device={}, lat={}, lon={}", deviceImei, latitude, longitude);
|
||||
} catch (Exception e) {
|
||||
log.error("异步发送位置信息到Redis时出错: device={}, error={}", deviceImei, e.getMessage(), e);
|
||||
}
|
||||
});
|
||||
storeDeviceTrajectoryWithSortedSet(deviceImei, locationJson);
|
||||
log.info("位置信息已异步发送到Redis: device={}, lat={}, lon={}", deviceImei, latitude, longitude);
|
||||
} catch (Exception e) {
|
||||
log.error("异步发送位置信息到Redis时出错: device={}, error={}", deviceImei, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -189,20 +187,18 @@ public class BjqLocationDataRule implements MqttMessageRule {
|
||||
* @param longitude 经度
|
||||
*/
|
||||
public void asyncSaveLocationToMySQLWithFuture(String deviceImei, String latitude, String longitude) {
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
if (StringUtils.isBlank(latitude) || StringUtils.isBlank(longitude)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 调用服务层方法更新设备位置信息
|
||||
deviceService.updateDeviceLocationByImei(deviceImei, longitude, latitude);
|
||||
|
||||
log.info("位置信息已异步保存到MySQL: device={}, lat={}, lon={}", deviceImei, latitude, longitude);
|
||||
} catch (Exception e) {
|
||||
log.error("异步保存位置信息到MySQL时出错: device={}, error={}", deviceImei, e.getMessage(), e);
|
||||
try {
|
||||
if (StringUtils.isBlank(latitude) || StringUtils.isBlank(longitude)) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
// 调用服务层方法更新设备位置信息
|
||||
deviceService.updateDeviceLocationByImei(deviceImei, longitude, latitude);
|
||||
|
||||
log.info("位置信息已异步保存到MySQL: device={}, lat={}, lon={}", deviceImei, latitude, longitude);
|
||||
} catch (Exception e) {
|
||||
log.error("异步保存位置信息到MySQL时出错: device={}, error={}", deviceImei, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -45,49 +45,52 @@ public class BjqModeRule implements MqttMessageRule {
|
||||
return LightingCommandTypeConstants.LIGHT_MODE;
|
||||
}
|
||||
|
||||
|
||||
@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)) {
|
||||
log.info("设备离线mainLightMode:{}", mainLightMode);
|
||||
if ("0".equals(mainLightMode)) {
|
||||
|
||||
// 设备离线
|
||||
String deviceOnlineStatusRedisKey = GlobalConstants.GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX + context.getDeviceImei() + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX;
|
||||
RedisUtils.setCacheObject(deviceOnlineStatusRedisKey, "0");
|
||||
|
||||
String sendMessageIng = GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX + context.getDeviceImei() + ":messageSending";
|
||||
String messageSendingValue = RedisUtils.getCacheObject(sendMessageIng);
|
||||
if ("1".equals(messageSendingValue)) {
|
||||
// 设置为故障状态
|
||||
RedisUtils.setCacheObject(deviceOnlineStatusRedisKey, "2");
|
||||
UpdateWrapper<Device> updateWrapper = new UpdateWrapper<>();
|
||||
updateWrapper.eq("device_imei", context.getDeviceImei());
|
||||
updateWrapper.set("online_status", 2);
|
||||
deviceService.update(updateWrapper);
|
||||
RedisUtils.deleteObject(sendMessageIng);
|
||||
|
||||
// 解除告警
|
||||
String deviceRedisKey = GlobalConstants.GLOBAL_REDIS_KEY + DeviceRedisKeyConstants.DEVICE_KEY_PREFIX + context.getDeviceImei() + DEVICE_ALARM_KEY_PREFIX;
|
||||
if (RedisUtils.getCacheObject(deviceRedisKey) != null) {
|
||||
RedisUtils.deleteObject(deviceRedisKey);
|
||||
}
|
||||
cancelAlarm(context.getDeviceImei());
|
||||
}
|
||||
}
|
||||
// 发送设备状态和位置信息到Redis
|
||||
syncSendDeviceDataToRedisWithFuture(context.getDeviceImei(), mainLightMode);
|
||||
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));
|
||||
log.info("处理灯光模式命令");
|
||||
log.info("MQTT消息负载字典:{}", context.getPayloadDict());
|
||||
// Object[] convertArr = context.getConvertArr();
|
||||
//
|
||||
// String mainLightMode = convertArr[1].toString();
|
||||
// String batteryRemainingTime = convertArr[2].toString();
|
||||
// if (StringUtils.isNotBlank(mainLightMode)) {
|
||||
// log.info("设备离线mainLightMode:{}", mainLightMode);
|
||||
// if ("0".equals(mainLightMode)) {
|
||||
//
|
||||
// // 设备离线
|
||||
// String deviceOnlineStatusRedisKey = GlobalConstants.GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX + context.getDeviceImei() + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX;
|
||||
// RedisUtils.setCacheObject(deviceOnlineStatusRedisKey, "0");
|
||||
//
|
||||
// String sendMessageIng = GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX + context.getDeviceImei() + ":messageSending";
|
||||
// String messageSendingValue = RedisUtils.getCacheObject(sendMessageIng);
|
||||
// if ("1".equals(messageSendingValue)) {
|
||||
// // 设置为故障状态
|
||||
// RedisUtils.setCacheObject(deviceOnlineStatusRedisKey, "2");
|
||||
// UpdateWrapper<Device> updateWrapper = new UpdateWrapper<>();
|
||||
// updateWrapper.eq("device_imei", context.getDeviceImei());
|
||||
// updateWrapper.set("online_status", 2);
|
||||
// deviceService.update(updateWrapper);
|
||||
// RedisUtils.deleteObject(sendMessageIng);
|
||||
//
|
||||
// // 解除告警
|
||||
// String deviceRedisKey = GlobalConstants.GLOBAL_REDIS_KEY + DeviceRedisKeyConstants.DEVICE_KEY_PREFIX + context.getDeviceImei() + DEVICE_ALARM_KEY_PREFIX;
|
||||
// if (RedisUtils.getCacheObject(deviceRedisKey) != null) {
|
||||
// RedisUtils.deleteObject(deviceRedisKey);
|
||||
// }
|
||||
// cancelAlarm(context.getDeviceImei());
|
||||
// }
|
||||
// }
|
||||
// // 发送设备状态和位置信息到Redis
|
||||
// syncSendDeviceDataToRedisWithFuture(context.getDeviceImei(), mainLightMode);
|
||||
// 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));
|
||||
|
||||
@ -1,32 +1,18 @@
|
||||
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.*;
|
||||
|
||||
/**
|
||||
* 定位数据命令处理
|
||||
@ -48,11 +34,12 @@ public class BjqPersonnelInfoDataRule implements MqttMessageRule {
|
||||
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));
|
||||
// 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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -45,47 +45,47 @@ public class BjqSendMessageRule implements MqttMessageRule {
|
||||
try {
|
||||
|
||||
// Byte val2 = (Byte) context.getConvertArr()[1];
|
||||
String val2Str = context.getConvertArr()[1].toString();
|
||||
int val2 = Integer.parseInt(val2Str);
|
||||
System.out.println("收到设备信息命令:"+val2);
|
||||
if (val2 == 100) {
|
||||
RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.OK.getCode(), Duration.ofSeconds(20));
|
||||
return;
|
||||
}
|
||||
|
||||
if(val2==200){
|
||||
String sendMessageIng = GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + context.getDeviceImei() + ":messageSending";
|
||||
RedisUtils.deleteObject(sendMessageIng);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
String data = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + context.getDeviceImei() + ":app_send_message_data");
|
||||
if (StringUtils.isEmpty(data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
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(6);
|
||||
intData.add(val2);
|
||||
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 + context.getDeviceImei(), 1, JsonUtils.toJsonString(map));
|
||||
log.info("发送设备信息数据到设备消息=>topic:{},payload:{}",
|
||||
MqttConstants.GLOBAL_PUB_KEY + context.getDeviceImei(),
|
||||
JsonUtils.toJsonString(map));
|
||||
// String val2Str = context.getConvertArr()[1].toString();
|
||||
// int val2 = Integer.parseInt(val2Str);
|
||||
// System.out.println("收到设备信息命令:"+val2);
|
||||
// if (val2 == 100) {
|
||||
// RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.OK.getCode(), Duration.ofSeconds(20));
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// if(val2==200){
|
||||
// String sendMessageIng = GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + context.getDeviceImei() + ":messageSending";
|
||||
// RedisUtils.deleteObject(sendMessageIng);
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// String data = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + context.getDeviceImei() + ":app_send_message_data");
|
||||
// if (StringUtils.isEmpty(data)) {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// 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(6);
|
||||
// intData.add(val2);
|
||||
// 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 + context.getDeviceImei(), 1, JsonUtils.toJsonString(map));
|
||||
// log.info("发送设备信息数据到设备消息=>topic:{},payload:{}",
|
||||
// MqttConstants.GLOBAL_PUB_KEY + context.getDeviceImei(),
|
||||
// JsonUtils.toJsonString(map));
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("处理发送设备信息时出错", e);
|
||||
|
||||
@ -0,0 +1,43 @@
|
||||
package com.fuyuanshen.global.mqtt.rule.hby100j;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.fuyuanshen.common.core.constant.GlobalConstants;
|
||||
import com.fuyuanshen.common.redis.utils.RedisUtils;
|
||||
import com.fuyuanshen.global.mqtt.base.NewMqttMessageRule;
|
||||
import com.fuyuanshen.global.mqtt.base.NewMqttRuleContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX;
|
||||
|
||||
/**
|
||||
* 爆闪模式开启/关闭
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class FuncType10StrobeMode implements NewMqttMessageRule {
|
||||
|
||||
@Override
|
||||
public String getCommandType() {
|
||||
return "HBY100_10";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(NewMqttRuleContext context) {
|
||||
log.info("HBY100J爆闪模式开启/关闭,消息负载:{}", context.getPayloadDict());
|
||||
|
||||
try {
|
||||
// String redisKey = GlobalConstants.GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX +
|
||||
// context.getDeviceImei() + ":strobeMode";
|
||||
//
|
||||
// Map<String, Object> payloadDict = context.getPayloadDict();
|
||||
// if (payloadDict != null) {
|
||||
// RedisUtils.setCacheObject(redisKey, JSONObject.toJSONString(payloadDict));
|
||||
// }
|
||||
} catch (Exception e) {
|
||||
log.error("HBY100J爆闪模式开启/关闭失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
package com.fuyuanshen.global.mqtt.rule.hby100j;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.fuyuanshen.common.core.constant.GlobalConstants;
|
||||
import com.fuyuanshen.common.redis.utils.RedisUtils;
|
||||
import com.fuyuanshen.global.mqtt.base.NewMqttMessageRule;
|
||||
import com.fuyuanshen.global.mqtt.base.NewMqttRuleContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX;
|
||||
|
||||
/**
|
||||
* 修改警示灯爆闪频率
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class FuncType11Frequency implements NewMqttMessageRule {
|
||||
|
||||
@Override
|
||||
public String getCommandType() {
|
||||
return "HBY100_11";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(NewMqttRuleContext context) {
|
||||
log.info("HBY100J修改警示灯爆闪频率,消息负载:{}", context.getPayloadDict());
|
||||
|
||||
try {
|
||||
// String redisKey = GlobalConstants.GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX +
|
||||
// context.getDeviceImei() + ":frequency";
|
||||
//
|
||||
// Map<String, Object> payloadDict = context.getPayloadDict();
|
||||
// if (payloadDict != null) {
|
||||
// RedisUtils.setCacheObject(redisKey, JSONObject.toJSONString(payloadDict));
|
||||
// }
|
||||
} catch (Exception e) {
|
||||
log.error("HBY100J修改警示灯爆闪频率失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
package com.fuyuanshen.global.mqtt.rule.hby100j;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.fuyuanshen.common.core.constant.GlobalConstants;
|
||||
import com.fuyuanshen.common.redis.utils.RedisUtils;
|
||||
import com.fuyuanshen.global.mqtt.base.NewMqttMessageRule;
|
||||
import com.fuyuanshen.global.mqtt.base.NewMqttRuleContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX;
|
||||
|
||||
/**
|
||||
* 强制声光报警开启/关闭
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class FuncType12ForceAudio implements NewMqttMessageRule {
|
||||
|
||||
@Override
|
||||
public String getCommandType() {
|
||||
return "HBY100_12";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(NewMqttRuleContext context) {
|
||||
log.info("HBY100J强制声光报警开启/关闭,消息负载:{}", context.getPayloadDict());
|
||||
|
||||
try {
|
||||
// 构建强制声光报警开关的Redis键
|
||||
// String redisKey = GlobalConstants.GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX +
|
||||
// context.getDeviceImei() + ":forceAudio";
|
||||
//
|
||||
// Map<String, Object> payloadDict = context.getPayloadDict();
|
||||
// if (payloadDict != null) {
|
||||
// // 存储强制声光报警开关状态到Redis
|
||||
// RedisUtils.setCacheObject(redisKey, JSONObject.toJSONString(payloadDict));
|
||||
// }
|
||||
} catch (Exception e) {
|
||||
log.error("HBY100J强制声光报警开启/关闭失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
package com.fuyuanshen.global.mqtt.rule.hby100j;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.fuyuanshen.common.core.constant.GlobalConstants;
|
||||
import com.fuyuanshen.common.redis.utils.RedisUtils;
|
||||
import com.fuyuanshen.global.mqtt.base.NewMqttMessageRule;
|
||||
import com.fuyuanshen.global.mqtt.base.NewMqttRuleContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX;
|
||||
|
||||
/**
|
||||
* 警示灯LED亮度调节
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class FuncType13Brightness implements NewMqttMessageRule {
|
||||
|
||||
@Override
|
||||
public String getCommandType() {
|
||||
return "HBY100_13";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(NewMqttRuleContext context) {
|
||||
log.info("HBY100J警示灯LED亮度调节,消息负载:{}", context.getPayloadDict());
|
||||
|
||||
try {
|
||||
// String redisKey = GlobalConstants.GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX +
|
||||
// context.getDeviceImei() + ":brightness";
|
||||
//
|
||||
// Map<String, Object> payloadDict = context.getPayloadDict();
|
||||
// if (payloadDict != null) {
|
||||
// RedisUtils.setCacheObject(redisKey, JSONObject.toJSONString(payloadDict));
|
||||
// }
|
||||
} catch (Exception e) {
|
||||
log.error("HBY100J警示灯LED亮度调节失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
package com.fuyuanshen.global.mqtt.rule.hby100j;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.fuyuanshen.common.core.constant.GlobalConstants;
|
||||
import com.fuyuanshen.common.redis.utils.RedisUtils;
|
||||
import com.fuyuanshen.global.mqtt.base.NewMqttMessageRule;
|
||||
import com.fuyuanshen.global.mqtt.base.NewMqttRuleContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX;
|
||||
|
||||
/**
|
||||
* 获取警示灯的当前工作方式(设备下发返回响应数据、设备定时主动上报)
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class FuncType14Report implements NewMqttMessageRule {
|
||||
|
||||
@Override
|
||||
public String getCommandType() {
|
||||
return "HBY100_14";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(NewMqttRuleContext context) {
|
||||
log.info("HBY100J设备定时主动上报,消息负载:{}", context.getPayloadDict());
|
||||
|
||||
try {
|
||||
String redisKey = GlobalConstants.GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX +
|
||||
context.getDeviceImei() + ":report";
|
||||
|
||||
Map<String, Object> payloadDict = context.getPayloadDict();
|
||||
if (payloadDict != null) {
|
||||
RedisUtils.setCacheObject(redisKey, JSONObject.toJSONString(payloadDict));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("HBY100J设备定时主动上报失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
//package com.fuyuanshen.global.mqtt.rule.hby100j;
|
||||
//
|
||||
//import com.alibaba.fastjson2.JSONObject;
|
||||
//import com.fuyuanshen.common.core.constant.GlobalConstants;
|
||||
//import com.fuyuanshen.common.redis.utils.RedisUtils;
|
||||
//import com.fuyuanshen.global.mqtt.base.NewMqttMessageRule;
|
||||
//import com.fuyuanshen.global.mqtt.base.NewMqttRuleContext;
|
||||
//import lombok.extern.slf4j.Slf4j;
|
||||
//import org.springframework.stereotype.Component;
|
||||
//
|
||||
//import java.util.Map;
|
||||
//
|
||||
//import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX;
|
||||
//
|
||||
///**
|
||||
// * 设备复位
|
||||
// */
|
||||
//@Slf4j
|
||||
//@Component
|
||||
//public class FuncType1Rest implements NewMqttMessageRule {
|
||||
//
|
||||
// @Override
|
||||
// public String getCommandType() {
|
||||
// return "HBY100_1";
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void execute(NewMqttRuleContext context) {
|
||||
// log.info("开始处理设备复位,消息负载:{}", context.getPayloadDict());
|
||||
//
|
||||
// try {
|
||||
// // 构建强制声光报警开关的Redis键
|
||||
//// String redisKey = GlobalConstants.GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX +
|
||||
//// context.getDeviceImei() + ":force_audio_visual_alarm_switch";
|
||||
////
|
||||
//// Map<String, Object> payloadDict = context.getPayloadDict();
|
||||
//// if (payloadDict != null) {
|
||||
//// // 存储强制声光报警开关状态到Redis
|
||||
//// RedisUtils.setCacheObject(redisKey, JSONObject.toJSONString(payloadDict));
|
||||
//// }
|
||||
// log.info("设备复位,设备ID:{}", context.getDeviceImei());
|
||||
// } catch (Exception e) {
|
||||
// log.error("处理设备复位失败", e);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
@ -0,0 +1,46 @@
|
||||
//package com.fuyuanshen.global.mqtt.rule.hby100j;
|
||||
//
|
||||
//import com.alibaba.fastjson2.JSONObject;
|
||||
//import com.fuyuanshen.common.core.constant.GlobalConstants;
|
||||
//import com.fuyuanshen.common.redis.utils.RedisUtils;
|
||||
//import com.fuyuanshen.global.mqtt.base.NewMqttMessageRule;
|
||||
//import com.fuyuanshen.global.mqtt.base.NewMqttRuleContext;
|
||||
//import lombok.extern.slf4j.Slf4j;
|
||||
//import org.springframework.stereotype.Component;
|
||||
//
|
||||
//import java.util.Map;
|
||||
//
|
||||
//import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX;
|
||||
//
|
||||
///**
|
||||
// * 获取设备基础信息
|
||||
// */
|
||||
//@Slf4j
|
||||
//@Component
|
||||
//public class FuncType2BaseInfo implements NewMqttMessageRule {
|
||||
//
|
||||
// @Override
|
||||
// public String getCommandType() {
|
||||
// return "HBY100_2";
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void execute(NewMqttRuleContext context) {
|
||||
// log.info("开始处理强制声光报警开关,消息负载:{}", context.getPayloadDict());
|
||||
//
|
||||
// try {
|
||||
// // 构建强制声光报警开关的Redis键
|
||||
// String redisKey = GlobalConstants.GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX +
|
||||
// context.getDeviceImei() + ":force_audio_visual_alarm_switch";
|
||||
//
|
||||
// Map<String, Object> payloadDict = context.getPayloadDict();
|
||||
// if (payloadDict != null) {
|
||||
// // 存储强制声光报警开关状态到Redis
|
||||
// RedisUtils.setCacheObject(redisKey, JSONObject.toJSONString(payloadDict));
|
||||
// }
|
||||
// log.info("强制声光报警开关处理完成,设备ID:{}", context.getDeviceImei());
|
||||
// } catch (Exception e) {
|
||||
// log.error("处理强制声光报警开关失败", e);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
@ -0,0 +1,43 @@
|
||||
package com.fuyuanshen.global.mqtt.rule.hby100j;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.fuyuanshen.common.core.constant.GlobalConstants;
|
||||
import com.fuyuanshen.common.redis.utils.RedisUtils;
|
||||
import com.fuyuanshen.global.mqtt.base.NewMqttMessageRule;
|
||||
import com.fuyuanshen.global.mqtt.base.NewMqttRuleContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX;
|
||||
|
||||
/**
|
||||
* 获取设备位置信息(设备下发返回响应数据、设备定时主动上报)
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class FuncType3Location implements NewMqttMessageRule {
|
||||
|
||||
@Override
|
||||
public String getCommandType() {
|
||||
return "HBY100_3";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(NewMqttRuleContext context) {
|
||||
log.info("HBY100J获取设备位置信息,消息负载:{}", context.getPayloadDict());
|
||||
|
||||
try {
|
||||
String redisKey = GlobalConstants.GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX +
|
||||
context.getDeviceImei() + ":location";
|
||||
|
||||
Map<String, Object> payloadDict = context.getPayloadDict();
|
||||
if (payloadDict != null) {
|
||||
RedisUtils.setCacheObject(redisKey, JSONObject.toJSONString(payloadDict));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("HBY100J获取设备位置信息失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
package com.fuyuanshen.global.mqtt.rule.hby100j;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.fuyuanshen.common.core.constant.GlobalConstants;
|
||||
import com.fuyuanshen.common.redis.utils.RedisUtils;
|
||||
import com.fuyuanshen.global.mqtt.base.NewMqttMessageRule;
|
||||
import com.fuyuanshen.global.mqtt.base.NewMqttRuleContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX;
|
||||
|
||||
/**
|
||||
* 获取设备电源状态(设备下发返回响应数据、设备定时主动上报)
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class FuncType4PowerStatus implements NewMqttMessageRule {
|
||||
|
||||
@Override
|
||||
public String getCommandType() {
|
||||
return "HBY100_4";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(NewMqttRuleContext context) {
|
||||
log.info("HBY100J获取设备电源状态,消息负载:{}", context.getPayloadDict());
|
||||
|
||||
try {
|
||||
String redisKey = GlobalConstants.GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX +
|
||||
context.getDeviceImei() + ":powerStatus";
|
||||
|
||||
Map<String, Object> payloadDict = context.getPayloadDict();
|
||||
if (payloadDict != null) {
|
||||
RedisUtils.setCacheObject(redisKey, JSONObject.toJSONString(payloadDict));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("HBY100J获取设备电源状态失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
package com.fuyuanshen.global.mqtt.rule.hby100j;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.fuyuanshen.common.core.constant.GlobalConstants;
|
||||
import com.fuyuanshen.common.redis.utils.RedisUtils;
|
||||
import com.fuyuanshen.global.mqtt.base.NewMqttMessageRule;
|
||||
import com.fuyuanshen.global.mqtt.base.NewMqttRuleContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX;
|
||||
|
||||
/**
|
||||
* 更新语音
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class FuncType5UpdateVoice implements NewMqttMessageRule {
|
||||
|
||||
@Override
|
||||
public String getCommandType() {
|
||||
return "HBY100_5";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(NewMqttRuleContext context) {
|
||||
log.info("HBY100J更新语音,消息负载:{}", context.getPayloadDict());
|
||||
|
||||
try {
|
||||
// String redisKey = GlobalConstants.GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX +
|
||||
// context.getDeviceImei() + ":updateVoice";
|
||||
//
|
||||
// Map<String, Object> payloadDict = context.getPayloadDict();
|
||||
// if (payloadDict != null) {
|
||||
// RedisUtils.setCacheObject(redisKey, JSONObject.toJSONString(payloadDict));
|
||||
// }
|
||||
} catch (Exception e) {
|
||||
log.error("HBY100J更新语音失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
package com.fuyuanshen.global.mqtt.rule.hby100j;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.fuyuanshen.common.core.constant.GlobalConstants;
|
||||
import com.fuyuanshen.common.redis.utils.RedisUtils;
|
||||
import com.fuyuanshen.global.mqtt.base.NewMqttMessageRule;
|
||||
import com.fuyuanshen.global.mqtt.base.NewMqttRuleContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX;
|
||||
|
||||
/**
|
||||
* 语音播报开启/关闭
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class FuncType6VoicePlay implements NewMqttMessageRule {
|
||||
|
||||
@Override
|
||||
public String getCommandType() {
|
||||
return "HBY100_9";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(NewMqttRuleContext context) {
|
||||
log.info("HBY100J语音播报开启/关闭,消息负载:{}", context.getPayloadDict());
|
||||
|
||||
try {
|
||||
// String redisKey = GlobalConstants.GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX +
|
||||
// context.getDeviceImei() + ":VoicePlay";
|
||||
//
|
||||
// Map<String, Object> payloadDict = context.getPayloadDict();
|
||||
// if (payloadDict != null) {
|
||||
// RedisUtils.setCacheObject(redisKey, JSONObject.toJSONString(payloadDict));
|
||||
// }
|
||||
} catch (Exception e) {
|
||||
log.error("HBY100J语音播报开启/关闭失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
package com.fuyuanshen.global.mqtt.rule.hby100j;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.fuyuanshen.common.core.constant.GlobalConstants;
|
||||
import com.fuyuanshen.common.redis.utils.RedisUtils;
|
||||
import com.fuyuanshen.global.mqtt.base.NewMqttMessageRule;
|
||||
import com.fuyuanshen.global.mqtt.base.NewMqttRuleContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX;
|
||||
|
||||
/**
|
||||
* 修改音量
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class FuncType9UpdateVolume implements NewMqttMessageRule {
|
||||
|
||||
@Override
|
||||
public String getCommandType() {
|
||||
return "HBY100_9";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(NewMqttRuleContext context) {
|
||||
log.info("HBY100J修改音量,消息负载:{}", context.getPayloadDict());
|
||||
|
||||
try {
|
||||
// String redisKey = GlobalConstants.GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX +
|
||||
// context.getDeviceImei() + ":updateVolume";
|
||||
//
|
||||
// Map<String, Object> payloadDict = context.getPayloadDict();
|
||||
// if (payloadDict != null) {
|
||||
// RedisUtils.setCacheObject(redisKey, JSONObject.toJSONString(payloadDict));
|
||||
// }
|
||||
} catch (Exception e) {
|
||||
log.error("HBY100J修改音量失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
package com.fuyuanshen.global.mqtt.rule.hby100j.domin;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 爆闪模式开启/关闭
|
||||
* 对应 funcType="10"
|
||||
* mode:
|
||||
* 0 红色爆闪,1 蓝色爆闪,2 黄色爆闪,3,红色顺时针旋转爆闪,4黄色顺时针旋转爆闪,5,红蓝顺时针旋转爆闪,6 红蓝交替爆闪
|
||||
*/
|
||||
@Data
|
||||
public class FuncType10StrobeModeRequest {
|
||||
|
||||
/**
|
||||
* 请求唯一标识符(用于链路追踪或幂等处理)
|
||||
*/
|
||||
@JsonProperty("requestId")
|
||||
private String requestId;
|
||||
|
||||
/**
|
||||
* 设备IMEI号(国际移动设备识别码)
|
||||
*/
|
||||
private String imei;
|
||||
|
||||
/**
|
||||
* 功能类型编码
|
||||
*/
|
||||
private String funcType;
|
||||
|
||||
/**
|
||||
* 时间戳(毫秒级Unix时间戳)
|
||||
*/
|
||||
private Long timestamp;
|
||||
|
||||
/**
|
||||
* 语音配置数据
|
||||
*/
|
||||
private Data data;
|
||||
|
||||
/**
|
||||
* 语音配置详情内部类
|
||||
*/
|
||||
@lombok.Data
|
||||
public static class Data {
|
||||
|
||||
/**
|
||||
* 爆闪模式是否启用
|
||||
* 0 - 禁用
|
||||
* 1 - 启用
|
||||
*/
|
||||
private Integer enable;
|
||||
/**
|
||||
* 爆闪模式类型 0 红色爆闪,1 蓝色爆闪,2 黄色爆闪,3,红色顺时针旋转爆闪,4黄色顺时针旋转爆闪,5,红蓝顺时针旋转爆闪,6 红蓝交替爆闪
|
||||
**/
|
||||
private Integer mode;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
package com.fuyuanshen.global.mqtt.rule.hby100j.domin;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 修改警示灯爆闪频率
|
||||
* 对应 funcType="11"
|
||||
*/
|
||||
@Data
|
||||
public class FuncType11FrequencyRequest {
|
||||
|
||||
/**
|
||||
* 请求唯一标识符(用于链路追踪或幂等处理)
|
||||
*/
|
||||
@JsonProperty("requestId")
|
||||
private String requestId;
|
||||
|
||||
/**
|
||||
* 设备IMEI号(国际移动设备识别码)
|
||||
*/
|
||||
private String imei;
|
||||
|
||||
/**
|
||||
* 功能类型编码
|
||||
*/
|
||||
private String funcType;
|
||||
|
||||
/**
|
||||
* 时间戳(毫秒级Unix时间戳)
|
||||
*/
|
||||
private Long timestamp;
|
||||
|
||||
/**
|
||||
* 语音配置数据
|
||||
*/
|
||||
private Data data;
|
||||
|
||||
/**
|
||||
* 语音配置详情内部类
|
||||
*/
|
||||
@lombok.Data
|
||||
public static class Data {
|
||||
//"frequency": 1-12
|
||||
private Integer frequency;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
package com.fuyuanshen.global.mqtt.rule.hby100j.domin;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 强制声光报警开启/关闭
|
||||
* 对应 funcType="12"
|
||||
*/
|
||||
@Data
|
||||
public class FuncType12ForceAudioRequest {
|
||||
|
||||
/**
|
||||
* 请求唯一标识符(用于链路追踪或幂等处理)
|
||||
*/
|
||||
@JsonProperty("requestId")
|
||||
private String requestId;
|
||||
|
||||
/**
|
||||
* 设备IMEI号(国际移动设备识别码)
|
||||
*/
|
||||
private String imei;
|
||||
|
||||
/**
|
||||
* 功能类型编码
|
||||
*/
|
||||
private String funcType;
|
||||
|
||||
/**
|
||||
* 时间戳(毫秒级Unix时间戳)
|
||||
*/
|
||||
private Long timestamp;
|
||||
|
||||
/**
|
||||
* 语音配置数据
|
||||
*/
|
||||
private Data data;
|
||||
|
||||
/**
|
||||
* 语音配置详情内部类
|
||||
*/
|
||||
@lombok.Data
|
||||
public static class Data {
|
||||
/**
|
||||
* 语音报警0关闭,1开启
|
||||
*/
|
||||
private Integer voiceStrobeAlarm;
|
||||
//语音模式0公安,1消防,2应急,3交警,4市政,5铁路,6医疗,7部队,8水利
|
||||
private Integer mode;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,59 @@
|
||||
package com.fuyuanshen.global.mqtt.rule.hby100j.domin;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 警示灯LED亮度调节
|
||||
* 对应 funcType="13"
|
||||
*/
|
||||
@Data
|
||||
public class FuncType13BrightnessRequest {
|
||||
|
||||
/**
|
||||
* 请求唯一标识符(用于链路追踪或幂等处理)
|
||||
*/
|
||||
@JsonProperty("requestId")
|
||||
private String requestId;
|
||||
|
||||
/**
|
||||
* 设备IMEI号(国际移动设备识别码)
|
||||
*/
|
||||
private String imei;
|
||||
|
||||
/**
|
||||
* 功能类型编码
|
||||
*/
|
||||
private String funcType;
|
||||
|
||||
/**
|
||||
* 时间戳(毫秒级Unix时间戳)
|
||||
*/
|
||||
private Long timestamp;
|
||||
|
||||
/**
|
||||
* 语音配置数据
|
||||
*/
|
||||
private Data data;
|
||||
|
||||
/**
|
||||
* 语音配置详情内部类
|
||||
*/
|
||||
@lombok.Data
|
||||
public static class Data {
|
||||
/**
|
||||
* 红色LED亮度值0-100
|
||||
*/
|
||||
private Integer red;
|
||||
|
||||
/**
|
||||
* 蓝色LED亮度值0-100
|
||||
*/
|
||||
private Integer blue;
|
||||
|
||||
/**
|
||||
* 黄色LED亮度值0-100
|
||||
*/
|
||||
private Integer yellow;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,143 @@
|
||||
package com.fuyuanshen.global.mqtt.rule.hby100j.domin;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 设备状态上报数据DTO
|
||||
* 对应MQTT消息中设备上报的完整状态数据结构
|
||||
*/
|
||||
@Data
|
||||
public class FuncType14StatusReport {
|
||||
|
||||
/**
|
||||
* 设备IMEI号(国际移动设备识别码)
|
||||
*/
|
||||
private String imei;
|
||||
|
||||
/**
|
||||
* 功能类型编码(如"14"表示特定功能指令)
|
||||
*/
|
||||
private String funcType;
|
||||
|
||||
/**
|
||||
* 响应状态码(如"200"表示成功)
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 时间戳(毫秒级Unix时间戳)
|
||||
*/
|
||||
private Long timestamp;
|
||||
|
||||
/**
|
||||
* 设备详细状态数据
|
||||
*/
|
||||
private Data data;
|
||||
|
||||
/**
|
||||
* 设备详细状态数据内部类
|
||||
*/
|
||||
@lombok.Data
|
||||
public static class Data {
|
||||
|
||||
/**
|
||||
* 语音播报开关:0-关闭,1-开启
|
||||
*/
|
||||
@JsonProperty("voice_broadcast")
|
||||
private Integer voiceBroadcast;
|
||||
|
||||
/**
|
||||
* 警报器(警笛)设置
|
||||
*/
|
||||
@JsonProperty("siren_alarm")
|
||||
private SirenAlarm sirenAlarm;
|
||||
|
||||
/**
|
||||
* LED爆闪设置
|
||||
*/
|
||||
@JsonProperty("led_strobe")
|
||||
private LedStrobe ledStrobe;
|
||||
|
||||
/**
|
||||
* 音量设置(0-100范围)
|
||||
*/
|
||||
@JsonProperty("volume")
|
||||
private Integer volume;
|
||||
|
||||
/**
|
||||
* 亮度设置(RGB三色亮度值)
|
||||
*/
|
||||
@JsonProperty("brightness")
|
||||
private Brightness brightness;
|
||||
}
|
||||
|
||||
/**
|
||||
* 警报器(警笛)设置
|
||||
*/
|
||||
@lombok.Data
|
||||
public static class SirenAlarm {
|
||||
|
||||
/**
|
||||
* 是否启用:0-禁用,1-启用
|
||||
*/
|
||||
@JsonProperty("enable")
|
||||
private Integer enable;
|
||||
|
||||
/**
|
||||
* 工作模式:0-默认模式,其他值表示不同报警模式
|
||||
*/
|
||||
@JsonProperty("mode")
|
||||
private Integer mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* LED爆闪设置
|
||||
*/
|
||||
@lombok.Data
|
||||
public static class LedStrobe {
|
||||
|
||||
/**
|
||||
* 是否启用:0-禁用,1-启用
|
||||
*/
|
||||
@JsonProperty("enable")
|
||||
private Integer enable;
|
||||
|
||||
/**
|
||||
* 工作模式:0-默认模式,其他值表示不同爆闪模式
|
||||
*/
|
||||
@JsonProperty("mode")
|
||||
private Integer mode;
|
||||
|
||||
/**
|
||||
* 爆闪频率(单位:Hz或自定义单位)
|
||||
*/
|
||||
@JsonProperty("frequency")
|
||||
private Integer frequency;
|
||||
}
|
||||
|
||||
/**
|
||||
* 亮度设置(RGB三色)
|
||||
*/
|
||||
@lombok.Data
|
||||
public static class Brightness {
|
||||
|
||||
/**
|
||||
* 红色通道亮度值(0-255或百分比)
|
||||
*/
|
||||
@JsonProperty("red")
|
||||
private Integer red;
|
||||
|
||||
/**
|
||||
* 绿色通道亮度值(0-255或百分比)
|
||||
*/
|
||||
@JsonProperty("green")
|
||||
private Integer green;
|
||||
|
||||
/**
|
||||
* 蓝色通道亮度值(0-255或百分比)
|
||||
*/
|
||||
@JsonProperty("blue")
|
||||
private Integer blue;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,59 @@
|
||||
package com.fuyuanshen.global.mqtt.rule.hby100j.domin;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 更新设备语音
|
||||
*/
|
||||
@Data
|
||||
public class FuncType5UpdateVoiceReport {
|
||||
|
||||
/**
|
||||
* 设备IMEI号(国际移动设备识别码)
|
||||
*/
|
||||
private String imei;
|
||||
|
||||
/**
|
||||
* 功能类型编码("5" 表示操作状态上报)
|
||||
*/
|
||||
private String funcType;
|
||||
|
||||
/**
|
||||
* 响应状态码("200" 表示请求成功)
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 时间戳(毫秒级Unix时间戳)
|
||||
*/
|
||||
private Long timestamp;
|
||||
|
||||
/**
|
||||
* 操作详细状态数据
|
||||
*/
|
||||
private Data data;
|
||||
|
||||
/**
|
||||
* 操作状态数据内部类
|
||||
*/
|
||||
@lombok.Data
|
||||
public static class Data {
|
||||
|
||||
/**
|
||||
* 操作执行状态:
|
||||
* 0 - 未开始 / 失败
|
||||
* 1 - 执行中
|
||||
* 2 - 成功完成
|
||||
* (具体含义需结合业务协议定义)
|
||||
*/
|
||||
@JsonProperty("status")
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 操作进度百分比(0-100),仅在 status=1(执行中)时有效
|
||||
*/
|
||||
@JsonProperty("progress")
|
||||
private Integer progress;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,60 @@
|
||||
package com.fuyuanshen.global.mqtt.rule.hby100j.domin;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 语音资源配置请求DTO
|
||||
* 对应 funcType="5" 的语音资源下发指令(如语音播报文件配置)
|
||||
*/
|
||||
@Data
|
||||
public class FuncType5UpdateVoiceRequest {
|
||||
|
||||
/**
|
||||
* 请求唯一标识符(用于链路追踪或幂等处理)
|
||||
*/
|
||||
@JsonProperty("requestId")
|
||||
private String requestId;
|
||||
|
||||
/**
|
||||
* 设备IMEI号(国际移动设备识别码)
|
||||
*/
|
||||
private String imei;
|
||||
|
||||
/**
|
||||
* 功能类型编码("5" 表示语音资源配置)
|
||||
*/
|
||||
private String funcType;
|
||||
|
||||
/**
|
||||
* 时间戳(毫秒级Unix时间戳)
|
||||
*/
|
||||
private Long timestamp;
|
||||
|
||||
/**
|
||||
* 语音配置数据
|
||||
*/
|
||||
private Data data;
|
||||
|
||||
/**
|
||||
* 语音配置详情内部类
|
||||
*/
|
||||
@lombok.Data
|
||||
public static class Data {
|
||||
|
||||
/**
|
||||
* 语音类型:
|
||||
* 0 - 默认语音(如标准提示音)
|
||||
* 1 - 自定义语音
|
||||
* 2 - 紧急语音
|
||||
* (具体含义需依据设备协议定义)
|
||||
*/
|
||||
private Integer voiceType;
|
||||
|
||||
/**
|
||||
* 语音资源URL(MP3等音频文件地址)
|
||||
* 示例:http://8.129.5.250:10001/voice/01.mp3
|
||||
*/
|
||||
private String voiceResource;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
package com.fuyuanshen.global.mqtt.rule.hby100j.domin;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 语音播报开启/关闭
|
||||
* 对应 funcType="6"
|
||||
*/
|
||||
@Data
|
||||
public class FuncType6VoicePlayRequest {
|
||||
|
||||
/**
|
||||
* 请求唯一标识符(用于链路追踪或幂等处理)
|
||||
*/
|
||||
@JsonProperty("requestId")
|
||||
private String requestId;
|
||||
|
||||
/**
|
||||
* 设备IMEI号(国际移动设备识别码)
|
||||
*/
|
||||
private String imei;
|
||||
|
||||
/**
|
||||
* 功能类型编码
|
||||
*/
|
||||
private String funcType;
|
||||
|
||||
/**
|
||||
* 时间戳(毫秒级Unix时间戳)
|
||||
*/
|
||||
private Long timestamp;
|
||||
|
||||
/**
|
||||
* 语音配置数据
|
||||
*/
|
||||
private Data data;
|
||||
|
||||
/**
|
||||
* 语音配置详情内部类
|
||||
*/
|
||||
@lombok.Data
|
||||
public static class Data {
|
||||
/**
|
||||
* 语音报警0关闭,1开启
|
||||
*/
|
||||
private Integer voiceBroadcast;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
package com.fuyuanshen.global.mqtt.rule.hby100j.domin;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 修改音量
|
||||
* 对应 funcType="9"
|
||||
*/
|
||||
@Data
|
||||
public class FuncType9UpdateVolumeRequest {
|
||||
|
||||
/**
|
||||
* 请求唯一标识符(用于链路追踪或幂等处理)
|
||||
*/
|
||||
@JsonProperty("requestId")
|
||||
private String requestId;
|
||||
|
||||
/**
|
||||
* 设备IMEI号(国际移动设备识别码)
|
||||
*/
|
||||
private String imei;
|
||||
|
||||
/**
|
||||
* 功能类型编码
|
||||
*/
|
||||
private String funcType;
|
||||
|
||||
/**
|
||||
* 时间戳(毫秒级Unix时间戳)
|
||||
*/
|
||||
private Long timestamp;
|
||||
|
||||
/**
|
||||
* 语音配置数据
|
||||
*/
|
||||
private Data data;
|
||||
|
||||
/**
|
||||
* 语音配置详情内部类
|
||||
*/
|
||||
@lombok.Data
|
||||
public static class Data {
|
||||
//"volume": 1-100(app端可根据需求把40作为低音量, 70作为中音量,100作为高音量)
|
||||
private Integer volume;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
package com.fuyuanshen.global.mqtt.rule.hby100j.domin;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 设备位置上报DTO
|
||||
* 对应MQTT消息中设备上报的地理位置数据
|
||||
*/
|
||||
@Data
|
||||
public class FunctionType3LocationReport {
|
||||
|
||||
/**
|
||||
* 设备IMEI号(国际移动设备识别码)
|
||||
*/
|
||||
private String imei;
|
||||
|
||||
/**
|
||||
* 功能类型编码(如"3"表示位置上报)
|
||||
*/
|
||||
private String funcType;
|
||||
|
||||
/**
|
||||
* 响应状态码(如"200"表示成功)
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 时间戳(毫秒级Unix时间戳)
|
||||
*/
|
||||
private Long timestamp;
|
||||
|
||||
/**
|
||||
* 位置详细数据
|
||||
*/
|
||||
private Data data;
|
||||
|
||||
/**
|
||||
* 位置详细数据内部类
|
||||
*/
|
||||
@lombok.Data
|
||||
public static class Data {
|
||||
|
||||
/**
|
||||
* 经度(WGS-84坐标系,如22.543100)
|
||||
*/
|
||||
@JsonProperty("longitude")
|
||||
private Double longitude;
|
||||
|
||||
/**
|
||||
* 纬度(WGS-84坐标系,如114.057900)
|
||||
*/
|
||||
@JsonProperty("latitude")
|
||||
private Double latitude;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,74 @@
|
||||
package com.fuyuanshen.global.mqtt.rule.hby100j.domin;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 设备电源状态上报DTO
|
||||
* 对应MQTT消息中设备上报的电源相关状态数据
|
||||
*/
|
||||
@Data
|
||||
public class FunctionType4PowerStatusReport {
|
||||
|
||||
/**
|
||||
* 设备IMEI号(国际移动设备识别码)
|
||||
*/
|
||||
private String imei;
|
||||
|
||||
/**
|
||||
* 功能类型编码(如"4"表示电源状态上报)
|
||||
*/
|
||||
private String funcType;
|
||||
|
||||
/**
|
||||
* 响应状态码(如"200"表示成功)
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 时间戳(毫秒级Unix时间戳)
|
||||
*/
|
||||
private Long timestamp;
|
||||
|
||||
/**
|
||||
* 电源详细状态数据
|
||||
*/
|
||||
private Data data;
|
||||
|
||||
/**
|
||||
* 电源详细状态数据内部类
|
||||
*/
|
||||
@lombok.Data
|
||||
public static class Data {
|
||||
|
||||
/**
|
||||
* 电池容量(如"5000mAh")
|
||||
*/
|
||||
@JsonProperty("capacity")
|
||||
private String capacity;
|
||||
|
||||
/**
|
||||
* 电压值(单位:V,0表示未检测或无电)
|
||||
*/
|
||||
@JsonProperty("voltage")
|
||||
private Integer voltage;
|
||||
|
||||
/**
|
||||
* 电量百分比(0-100,0表示低电量或未检测)
|
||||
*/
|
||||
@JsonProperty("level")
|
||||
private Integer level;
|
||||
|
||||
/**
|
||||
* 充电状态:0-未充电,1-正在充电
|
||||
*/
|
||||
@JsonProperty("charge")
|
||||
private Integer charge;
|
||||
|
||||
/**
|
||||
* 12V电源状态:0-关闭/无输出,1-开启/有输出
|
||||
*/
|
||||
@JsonProperty("12v_power")
|
||||
private Integer twelveVPower;
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,13 @@
|
||||
package com.fuyuanshen.global.mqtt.rule.xinghan;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import cn.hutool.json.JSONArray;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import com.alibaba.fastjson2.JSONWriter;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fuyuanshen.common.core.constant.GlobalConstants;
|
||||
import com.fuyuanshen.common.core.utils.StringUtils;
|
||||
@ -10,11 +15,20 @@ import com.fuyuanshen.common.core.utils.date.DurationUtils;
|
||||
import com.fuyuanshen.common.json.utils.JsonUtils;
|
||||
import com.fuyuanshen.common.redis.utils.RedisUtils;
|
||||
import com.fuyuanshen.equipment.domain.Device;
|
||||
import com.fuyuanshen.equipment.domain.DeviceFenceAccessRecord;
|
||||
import com.fuyuanshen.equipment.domain.DeviceGeoFence;
|
||||
import com.fuyuanshen.equipment.domain.bo.DeviceAlarmBo;
|
||||
import com.fuyuanshen.equipment.domain.bo.DeviceFenceAccessRecordBo;
|
||||
import com.fuyuanshen.equipment.domain.vo.DeviceAlarmVo;
|
||||
import com.fuyuanshen.equipment.domain.vo.DeviceGeoFenceVo;
|
||||
import com.fuyuanshen.equipment.mapper.DeviceAlarmMapper;
|
||||
import com.fuyuanshen.equipment.mapper.DeviceFenceAccessRecordMapper;
|
||||
import com.fuyuanshen.equipment.mapper.DeviceGeoFenceMapper;
|
||||
import com.fuyuanshen.equipment.service.DeviceService;
|
||||
import com.fuyuanshen.equipment.service.IDeviceAlarmService;
|
||||
import com.fuyuanshen.equipment.service.IDeviceFenceAccessRecordService;
|
||||
import com.fuyuanshen.equipment.service.IDeviceGeoFenceService;
|
||||
import com.fuyuanshen.equipment.utils.map.AmapTrackUtil;
|
||||
import com.fuyuanshen.equipment.utils.map.GetAddressFromLatUtil;
|
||||
import com.fuyuanshen.equipment.utils.map.LngLonUtil;
|
||||
import com.fuyuanshen.global.mqtt.base.MqttMessageRule;
|
||||
@ -30,13 +44,16 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.redisson.api.RLock;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.time.Duration;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_KEY;
|
||||
import static com.fuyuanshen.common.core.constant.GlobalConstants.GLOBAL_REDIS_KEY;
|
||||
@ -72,6 +89,12 @@ public class XinghanDeviceDataRule implements MqttMessageRule {
|
||||
private final IDeviceAlarmService deviceAlarmService;
|
||||
private final DeviceService deviceService;
|
||||
private final DeviceAlarmMapper deviceAlarmMapper;
|
||||
private final AmapTrackUtil amapTrackUtil;
|
||||
private final IDeviceFenceAccessRecordService deviceFenceAccessRecordService;
|
||||
private final DeviceFenceAccessRecordMapper deviceFenceAccessRecordMapper;
|
||||
private final DeviceGeoFenceMapper deviceGeoFenceMapper;
|
||||
/** 位置未发生明显变化的距离阈值(米),可通过配置中心动态调整 */
|
||||
private final double MOVEMENT_THRESHOLD_METER = 10.0;
|
||||
|
||||
@Override
|
||||
public void execute(MqttRuleContext context) {
|
||||
@ -127,16 +150,20 @@ public class XinghanDeviceDataRule implements MqttMessageRule {
|
||||
* 入口:保存报警(SOS 与 Shake 完全独立)
|
||||
*/
|
||||
public void saveAlarm(String deviceImei, MqttXinghanJson status) {
|
||||
int sos = Optional.ofNullable(status.getStaSOSGrade()).orElse(0);
|
||||
// 1. 处理 SOS 报警
|
||||
handleSingleAlarm(deviceImei,
|
||||
sos > 0,
|
||||
AlarmTypeEnum.SOS);
|
||||
int shake = Optional.ofNullable(status.getStaShakeBit()).orElse(0);
|
||||
// 2. 处理 Shake 报警
|
||||
handleSingleAlarm(deviceImei,
|
||||
shake > 0,
|
||||
AlarmTypeEnum.SHAKE);
|
||||
try {
|
||||
int sos = Optional.ofNullable(status.getStaSOSGrade()).orElse(0);
|
||||
// 1. 处理 SOS 报警
|
||||
handleSingleAlarm(deviceImei,
|
||||
sos > 0,
|
||||
AlarmTypeEnum.SOS);
|
||||
int shake = Optional.ofNullable(status.getStaShakeBit()).orElse(0);
|
||||
// 2. 处理 Shake 报警
|
||||
handleSingleAlarm(deviceImei,
|
||||
shake > 0,
|
||||
AlarmTypeEnum.SHAKE);
|
||||
} catch (Exception e) {
|
||||
log.error("异步保存报警(SOS 与 Shake 完全独立)报错: device={}, error={}", deviceImei, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -236,7 +263,7 @@ public class XinghanDeviceDataRule implements MqttMessageRule {
|
||||
String location = RedisUtils.getCacheObject(
|
||||
GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX + deviceImei + DEVICE_LOCATION_KEY_PREFIX);
|
||||
if (StrUtil.isNotBlank(location)) {
|
||||
bo.setLocation(JSONObject.parseObject(location).getString("address"));
|
||||
bo.setLocation(com.alibaba.fastjson2.JSONObject.parseObject(location).getString("address"));
|
||||
}
|
||||
return bo;
|
||||
}
|
||||
@ -259,63 +286,312 @@ public class XinghanDeviceDataRule implements MqttMessageRule {
|
||||
public void asyncSendLocationToRedisWithFuture(String deviceImei, String latitude, String longitude) {
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
if(StringUtils.isBlank(latitude) || StringUtils.isBlank(longitude)){
|
||||
if (StringUtils.isAnyBlank(deviceImei, latitude, longitude)) {
|
||||
log.warn("位置上报参数为空,deviceImei={}", deviceImei);
|
||||
return;
|
||||
}
|
||||
//log.info("位置上报,deviceImei={}, lat={}, lon={}", deviceImei, latitude, longitude);
|
||||
// 1. 解析当前上报的经纬度
|
||||
Double curLat = parseDoubleSafe(latitude.trim());
|
||||
Double curLon = parseDoubleSafe(longitude.trim());
|
||||
if (curLat == null || curLon == null) {
|
||||
log.warn("经纬度格式错误,直接更新,deviceImei={}, lat={}, lon={}", deviceImei, latitude, longitude);
|
||||
doSaveLocation(deviceImei, latitude, longitude);
|
||||
return;
|
||||
}
|
||||
String[] latArr = latitude.split("\\.");
|
||||
String[] lonArr = longitude.split("\\.");
|
||||
// 将位置信息存储到Redis中
|
||||
String redisKey = GlobalConstants.GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX+ deviceImei + DEVICE_LOCATION_KEY_PREFIX;
|
||||
String redisObj = RedisUtils.getCacheObject(redisKey);
|
||||
JSONObject jsonOBj = JSONObject.parseObject(redisObj);
|
||||
if(jsonOBj != null){
|
||||
String str1 = latArr[0] +"."+ latArr[1].substring(0,4);
|
||||
String str2 = lonArr[0] +"."+ lonArr[1].substring(0,4);
|
||||
|
||||
String cacheLatitude = jsonOBj.getString("wgs84_latitude");
|
||||
String cacheLongitude = jsonOBj.getString("wgs84_longitude");
|
||||
String[] latArr1 = cacheLatitude.split("\\.");
|
||||
String[] lonArr1 = cacheLongitude.split("\\.");
|
||||
// 2. 读取 Redis 中缓存的上一次位置
|
||||
String redisKey = GlobalConstants.GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX + deviceImei + DEVICE_LOCATION_KEY_PREFIX;
|
||||
String cachedJson = RedisUtils.getCacheObject(redisKey);
|
||||
|
||||
String cacheStr1 = latArr1[0] +"."+ latArr1[1].substring(0,4);
|
||||
String cacheStr2 = lonArr1[0] +"."+ lonArr1[1].substring(0,4);
|
||||
if(str1.equals(cacheStr1) && str2.equals(cacheStr2)){
|
||||
log.info("位置信息未发生变化: device={}, lat={}, lon={}", deviceImei, latitude, longitude);
|
||||
return;
|
||||
if (StringUtils.isNotBlank(cachedJson)) {
|
||||
com.alibaba.fastjson2.JSONObject cachedObj = com.alibaba.fastjson2.JSONObject.parseObject(cachedJson);
|
||||
String cachedWgs84Lat = cachedObj.getString("wgs84_latitude");
|
||||
String cachedWgs84Lon = cachedObj.getString("wgs84_longitude");
|
||||
|
||||
Double oldLat = parseDoubleSafe(cachedWgs84Lat);
|
||||
Double oldLon = parseDoubleSafe(cachedWgs84Lon);
|
||||
|
||||
if (oldLat != null && oldLon != null) {
|
||||
double distance = haversine(oldLat, oldLon, curLat, curLon);
|
||||
if (distance <= MOVEMENT_THRESHOLD_METER) {
|
||||
log.info("位置未发生明显变化({}米 <= {}米),不更新 Redis,deviceImei={}, lat={}, lon={}",
|
||||
distance, MOVEMENT_THRESHOLD_METER, deviceImei, latitude, longitude);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 构造位置信息对象
|
||||
Map<String, Object> locationInfo = new LinkedHashMap<>();
|
||||
double[] doubles = LngLonUtil.gps84_To_Gcj02(Double.parseDouble(latitude), Double.parseDouble(longitude));
|
||||
locationInfo.put("deviceImei", deviceImei);
|
||||
locationInfo.put("latitude", doubles[0]);
|
||||
locationInfo.put("longitude", doubles[1]);
|
||||
locationInfo.put("wgs84_latitude", latitude);
|
||||
locationInfo.put("wgs84_longitude", longitude);
|
||||
String address = GetAddressFromLatUtil.getAdd(String.valueOf(doubles[1]), String.valueOf(doubles[0]));
|
||||
locationInfo.put("address", address);
|
||||
locationInfo.put("timestamp", System.currentTimeMillis());
|
||||
// 3. 位置有明显变化,执行保存
|
||||
doSaveLocation(deviceImei, latitude, longitude);
|
||||
|
||||
|
||||
|
||||
String locationJson = JsonUtils.toJsonString(locationInfo);
|
||||
|
||||
// 存储到Redis
|
||||
RedisUtils.setCacheObject(redisKey, locationJson);
|
||||
|
||||
// 存储到一个列表中,保留历史位置信息
|
||||
// String locationHistoryKey = GlobalConstants.GLOBAL_REDIS_KEY+DeviceRedisKeyConstants.DEVICE_LOCATION_HISTORY_KEY_PREFIX + deviceImei;
|
||||
// RedisUtils.addCacheList(locationHistoryKey, locationJson);
|
||||
// RedisUtils.expire(locationHistoryKey, Duration.ofDays(90));
|
||||
storeDeviceTrajectoryWithSortedSet(deviceImei, locationJson);
|
||||
log.info("位置信息已异步发送到Redis: device={}, lat={}, lon={}", deviceImei, latitude, longitude);
|
||||
} catch (Exception e) {
|
||||
log.error("异步发送位置信息到Redis时出错: device={}, error={}", deviceImei, e.getMessage(), e);
|
||||
log.error("异步发送位置信息到 Redis 失败,deviceImei={}", deviceImei, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** 真正执行保存逻辑(抽取出来便于测试和阅读) */
|
||||
private void doSaveLocation(String deviceImei, String wgs84Lat, String wgs84Lon) {
|
||||
|
||||
// Map<String, Object> locationInfo = new LinkedHashMap<>();
|
||||
// locationInfo.put("deviceImei", deviceImei);
|
||||
// locationInfo.put("latitude", gcj02[0]); // GCJ02 纬度
|
||||
// locationInfo.put("longitude", gcj02[1]); // GCJ02 经度
|
||||
// locationInfo.put("wgs84_latitude", wgs84Lat);
|
||||
// locationInfo.put("wgs84_longitude", wgs84Lon);
|
||||
//
|
||||
//
|
||||
// locationInfo.put("address", StringUtils.defaultIfBlank(address, "未知"));
|
||||
// locationInfo.put("timestamp", System.currentTimeMillis());
|
||||
//
|
||||
// String locationJson = JsonUtils.toJsonString(locationInfo);
|
||||
// 使用 fastjson2 零 GC 序列化
|
||||
// WGS84 → GCJ02(火星坐标)
|
||||
double[] gcj02 = LngLonUtil.gps84_To_Gcj02(
|
||||
Double.parseDouble(wgs84Lat),
|
||||
Double.parseDouble(wgs84Lon)
|
||||
);
|
||||
|
||||
String gcj02Lat = String.format("%.6f", gcj02[0]);
|
||||
String gcj02Lon = String.format("%.6f", gcj02[1]);
|
||||
|
||||
// 逆地理编码(可自行决定是否异步)
|
||||
String address = GetAddressFromLatUtil.getAdd(gcj02Lon, gcj02Lat);
|
||||
|
||||
String locationJson = buildLocationJsonFastJSON2(deviceImei, wgs84Lat, wgs84Lon, gcj02Lon, gcj02Lat,address);
|
||||
|
||||
// 主位置信息(最新一条)
|
||||
String redisKey = GlobalConstants.GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX + deviceImei + DEVICE_LOCATION_KEY_PREFIX;
|
||||
RedisUtils.setCacheObject(redisKey, locationJson);
|
||||
|
||||
// 轨迹存储(SortedSet,按时间戳排序)
|
||||
storeDeviceTrajectoryWithSortedSet(deviceImei, locationJson);
|
||||
// 轨迹上传 查询检测对象与围栏关系 记录设备进出围栏事件
|
||||
uploadTrackPointAsync(deviceImei, gcj02Lat, gcj02Lon, address);
|
||||
|
||||
log.info("位置信息已更新 Redis,deviceImei={}, wgs84=({},{})", deviceImei, wgs84Lat, wgs84Lon);
|
||||
}
|
||||
|
||||
private String buildLocationJsonFastJSON2(
|
||||
String deviceImei,
|
||||
String wgs84Lat, String wgs84Lon,
|
||||
String gcj02Lon, String gcj02Lat,String address) {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
|
||||
|
||||
// 直接用默认 writer,零配置,自动零 GC
|
||||
try (JSONWriter w = JSONWriter.of()) {
|
||||
w.startObject();
|
||||
w.writeString("deviceImei"); w.writeColon(); w.writeString(deviceImei); w.writeComma();
|
||||
w.writeString("latitude"); w.writeColon(); w.writeString(gcj02Lat); w.writeComma();
|
||||
w.writeString("longitude"); w.writeColon(); w.writeString(gcj02Lon); w.writeComma();
|
||||
w.writeString("wgs84_latitude"); w.writeColon(); w.writeString(wgs84Lat); w.writeComma();
|
||||
w.writeString("wgs84_longitude"); w.writeColon(); w.writeString(wgs84Lon); w.writeComma();
|
||||
w.writeString("address"); w.writeColon(); w.writeString(StringUtils.defaultIfBlank(address, "未知")); w.writeComma();
|
||||
w.writeString("timestamp"); w.writeColon(); w.writeInt64(timestamp);
|
||||
w.endObject();
|
||||
return w.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传轨迹点并处理电子围栏出入事件(高德猎鹰服务)
|
||||
* 优化点:
|
||||
* 1. 避免重复查询数据库(围栏信息批量缓存)
|
||||
* 2. 防御性编程:全面空指针防护
|
||||
* 3. Redis 操作原子性 + 合理 TTL
|
||||
* 4. 减少不必要的对象创建和流操作
|
||||
* 5. 精确的事件时间使用 locationTime
|
||||
* 6. 失败不阻塞主流程,记录关键错误
|
||||
*/
|
||||
private void uploadTrackPointAsync(String deviceImei,
|
||||
String gcj02Lat,
|
||||
String gcj02Lon,String address) {
|
||||
|
||||
if (StrUtil.hasBlank(deviceImei, gcj02Lat, gcj02Lon)) {
|
||||
log.warn("上传轨迹点参数非法,deviceImei={}, lat={}, lon={}", deviceImei, gcj02Lat, gcj02Lon);
|
||||
return;
|
||||
}
|
||||
long locationTime = System.currentTimeMillis();
|
||||
String fenceStatusKey = GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX + deviceImei + ":terminal:status";
|
||||
|
||||
try {
|
||||
// 1. 查询设备信息(建议加缓存,热点设备可大幅降低DB压力)
|
||||
Device device = deviceService.selectDeviceByImei(deviceImei);
|
||||
if (device == null || device.getSid() == null || device.getTid() == null) {
|
||||
log.warn("设备不存在或未完成高德终端创建,imei={}", deviceImei);
|
||||
return;
|
||||
}
|
||||
|
||||
Long trid = ObjectUtil.defaultIfNull(device.getTrid(), 0L); // trid 可能为空
|
||||
|
||||
// 2. 上传轨迹点
|
||||
JSONObject point = new JSONObject();
|
||||
point.set("location", String.format("%s,%s", gcj02Lon, gcj02Lat));
|
||||
point.set("locatetime", locationTime);
|
||||
|
||||
JSONArray pointsArray = new JSONArray();
|
||||
pointsArray.add(point);
|
||||
log.info("上传轨迹点开始,deviceImei={}, point={}", deviceImei, point);
|
||||
JSONObject uploadResult = amapTrackUtil.uploadPoints(device.getSid(), device.getTid(), trid, pointsArray);
|
||||
if (uploadResult == null || uploadResult.getInt("errcode", -1) != 10000) {
|
||||
return;
|
||||
}
|
||||
log.info("上传轨迹点成功,deviceImei={}, result={}", deviceImei, uploadResult);
|
||||
// 3. 查询当前围栏状态
|
||||
JSONObject fenceResult = amapTrackUtil.queryTerminalFenceStatus(device.getSid(), null, device.getTid());
|
||||
if (fenceResult == null || fenceResult.getInt("errcode", -1) != 10000) {
|
||||
log.warn("查询设备围栏状态失败,imei={}, result={}", deviceImei, fenceResult);
|
||||
return;
|
||||
}
|
||||
log.info("查询设备围栏状态成功,imei={}, result={}", deviceImei, fenceResult);
|
||||
JSONArray results = fenceResult.getByPath("data.results", JSONArray.class);
|
||||
if (results == null || results.isEmpty()) {
|
||||
// 没有任何围栏关系,清空旧状态
|
||||
RedisUtils.deleteObject(fenceStatusKey);
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. 当前在围栏内的 gfid 集合
|
||||
Set<Long> newInFenceGfids = new HashSet<>();
|
||||
List<JSONObject> currentInList = new ArrayList<>();
|
||||
|
||||
for (Object obj : results) {
|
||||
JSONObject item = (JSONObject) obj;
|
||||
if (item.getInt("in", 0) == 1) {
|
||||
Long gfid = item.getLong("gfid");
|
||||
if (gfid != null) {
|
||||
newInFenceGfids.add(gfid);
|
||||
currentInList.add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 获取上一次的围栏状态
|
||||
List<JSONObject> oldInList = RedisUtils.getCacheObject(fenceStatusKey);
|
||||
Set<Long> oldInFenceGfids = (oldInList == null || oldInList.isEmpty())
|
||||
? Collections.emptySet()
|
||||
: oldInList.stream()
|
||||
.map(o -> o.getLong("gfid"))
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// 6. 计算出入事件(使用高效的 Set 操作)
|
||||
Set<Long> enteredGfids = new HashSet<>(newInFenceGfids);
|
||||
enteredGfids.removeAll(oldInFenceGfids);// 进入:这次有,上次没有
|
||||
|
||||
Set<Long> exitedGfids = new HashSet<>(oldInFenceGfids);
|
||||
exitedGfids.removeAll(newInFenceGfids);// 离开:上次有,这次没有
|
||||
|
||||
Date eventTime = new Date(locationTime);
|
||||
Double latitude = Double.valueOf(gcj02Lat);
|
||||
Double longitude = Double.valueOf(gcj02Lon);
|
||||
|
||||
// 批量查询围栏信息(关键优化:避免 N+1 查询)
|
||||
Set<Long> allChangedGfids = new HashSet<>();
|
||||
allChangedGfids.addAll(enteredGfids);
|
||||
allChangedGfids.addAll(exitedGfids);
|
||||
|
||||
Map<Long, DeviceGeoFenceVo> fenceMap = new HashMap<>();
|
||||
if (CollUtil.isNotEmpty(allChangedGfids)) {
|
||||
List<DeviceGeoFenceVo> fenceList = deviceGeoFenceMapper.selectVoList(
|
||||
new LambdaQueryWrapper<DeviceGeoFence>().in(DeviceGeoFence::getGfid, allChangedGfids));
|
||||
fenceMap = fenceList.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toMap(DeviceGeoFenceVo::getGfid, v -> v, (a, b) -> a));
|
||||
}
|
||||
|
||||
// 7. 记录进入事件
|
||||
if (CollUtil.isNotEmpty(enteredGfids)) {
|
||||
List<DeviceFenceAccessRecord> enterRecords = new ArrayList<>();
|
||||
for (Long gfid : enteredGfids) {
|
||||
DeviceFenceAccessRecord record = buildFenceRecord(device, fenceMap.get(gfid), 1L, latitude, longitude, eventTime, address);
|
||||
if (record != null) {
|
||||
enterRecords.add(record);
|
||||
}
|
||||
}
|
||||
deviceFenceAccessRecordMapper.insertBatch(enterRecords);
|
||||
log.info("设备进入围栏,imei={}, gfids={}", deviceImei, enteredGfids);
|
||||
}
|
||||
|
||||
// 8. 记录离开事件
|
||||
if (CollUtil.isNotEmpty(exitedGfids)) {
|
||||
List<DeviceFenceAccessRecord> exitRecords = new ArrayList<>();
|
||||
for (Long gfid : exitedGfids) {
|
||||
DeviceFenceAccessRecord record = buildFenceRecord(device, fenceMap.get(gfid), 2L, latitude, longitude, eventTime, address);
|
||||
if (record != null) {
|
||||
exitRecords.add(record);
|
||||
}
|
||||
}
|
||||
deviceFenceAccessRecordMapper.insertBatch(exitRecords);
|
||||
log.info("设备离开围栏,imei={}, gfids={}", deviceImei, exitedGfids);
|
||||
}
|
||||
|
||||
// 9. 更新 Redis 状态(TTL 5分钟,防止频繁查询)
|
||||
if (CollUtil.isNotEmpty(currentInList)) {
|
||||
RedisUtils.setCacheObject(fenceStatusKey, currentInList, Duration.ofMinutes(5));
|
||||
} else {
|
||||
RedisUtils.deleteObject(fenceStatusKey);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("上传轨迹点并处理围栏事件异常,imei={}, lat={}, lon={}, time={}",
|
||||
deviceImei, gcj02Lat, gcj02Lon, locationTime, e);
|
||||
// 可落库待重试或发告警,此处不抛异常影响定位主流程
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建围栏出入记录(提取公共逻辑,避免重复代码)
|
||||
*/
|
||||
private DeviceFenceAccessRecord buildFenceRecord(Device device,
|
||||
DeviceGeoFenceVo fence,
|
||||
Long eventType,
|
||||
Double latitude,
|
||||
Double longitude,
|
||||
Date eventTime,String address) {
|
||||
if (fence == null || fence.getId() == null) {
|
||||
log.warn("围栏信息不存在,gfid 可能已被删除或未绑定");
|
||||
return null;
|
||||
}
|
||||
DeviceFenceAccessRecord bo = new DeviceFenceAccessRecord();
|
||||
bo.setDeviceId(device.getId().toString());
|
||||
bo.setFenceId(fence.getId());
|
||||
bo.setEventType(eventType); // 1=进入, 2=离开
|
||||
bo.setAccuracy(20L);
|
||||
bo.setLatitude(latitude);
|
||||
bo.setLongitude(longitude);
|
||||
bo.setEventTime(eventTime);
|
||||
bo.setTenantId(device.getTenantId());
|
||||
bo.setCreateBy(device.getCreateBy());
|
||||
bo.setCreateDept(device.getCreateDept());
|
||||
bo.setEventAddress(address);
|
||||
return bo;
|
||||
}
|
||||
|
||||
/** 安全解析 double,解析失败返回 null */
|
||||
private Double parseDoubleSafe(String str) {
|
||||
if (StringUtils.isBlank(str)) return null;
|
||||
try {
|
||||
return Double.parseDouble(str.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Haversine 公式计算两点球面距离(米) */
|
||||
private double haversine(double lat1, double lon1, double lat2, double lon2) {
|
||||
double dLat = Math.toRadians(lat2 - lat1);
|
||||
double dLon = Math.toRadians(lon2 - lon1);
|
||||
double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
|
||||
Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) *
|
||||
Math.sin(dLon / 2) * Math.sin(dLon / 2);
|
||||
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||||
/** 地球平均半径(米) */
|
||||
double EARTH_RADIUS = 6371_393.0;
|
||||
return EARTH_RADIUS * c;
|
||||
}
|
||||
|
||||
/**
|
||||
* 存储设备30天历史轨迹到Redis (使用Sorted Set)
|
||||
*/
|
||||
@ -358,4 +634,6 @@ public class XinghanDeviceDataRule implements MqttMessageRule {
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -110,7 +110,7 @@ public class XinghanSendAlarmMessageRule implements MqttMessageRule {
|
||||
dto.setMessage(String.format("%s设备已收到通知!", latestLog.getDeviceName()));
|
||||
dto.setUserIds(List.of(latestLog.getCreateBy()));
|
||||
SseMessageUtils.publishMessage(dto);
|
||||
}, 5, TimeUnit.SECONDS);
|
||||
}, 2, TimeUnit.SECONDS);
|
||||
return;
|
||||
}
|
||||
// 1. cover! —— 成功标记
|
||||
|
||||
@ -0,0 +1,24 @@
|
||||
package com.fuyuanshen.global.mqtt.utils;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
|
||||
public class GenerateIdUtil {
|
||||
/**
|
||||
* 生成数字唯一ID - 基于UUID
|
||||
*/
|
||||
public static String generateNumericId() {
|
||||
UUID uuid = UUID.randomUUID();
|
||||
return Math.abs(uuid.toString().hashCode()) + "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成数字唯一ID - 基于时间戳+随机数
|
||||
*/
|
||||
public static long generateTimestampBasedId() {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
int random = new Random().nextInt(1000); // 0-999随机数
|
||||
return timestamp * 1000 + random;
|
||||
}
|
||||
|
||||
}
|
||||
@ -7,14 +7,18 @@ import com.fuyuanshen.common.core.utils.StringUtils;
|
||||
import com.fuyuanshen.common.redis.utils.RedisUtils;
|
||||
import com.fuyuanshen.equipment.domain.Device;
|
||||
import com.fuyuanshen.equipment.mapper.DeviceMapper;
|
||||
import com.fuyuanshen.global.mqtt.base.NewMqttRuleContext;
|
||||
import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.annotation.PreDestroy;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.redisson.api.RLock;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@ -27,11 +31,12 @@ public class MqttMessageConsumer {
|
||||
|
||||
@Autowired
|
||||
private DeviceMapper deviceMapper;
|
||||
|
||||
|
||||
// 创建两个线程池:一个用于消息获取,一个用于业务处理
|
||||
private ExecutorService messageConsumerPool = Executors.newFixedThreadPool(3);
|
||||
private ExecutorService messageProcessorPool = Executors.newFixedThreadPool(10);
|
||||
|
||||
|
||||
// 初始化方法,启动消息监听
|
||||
@PostConstruct
|
||||
public void start() {
|
||||
@ -94,40 +99,19 @@ public class MqttMessageConsumer {
|
||||
}
|
||||
|
||||
// 处理具体业务逻辑的方法
|
||||
private void processMessage(String message) {
|
||||
private void processMessage(String imei) {
|
||||
String threadName = Thread.currentThread().getName();
|
||||
try {
|
||||
log.info("业务处理线程 {} 开始处理消息: {}", threadName, message);
|
||||
// String deviceOnlineStatusRedisKey = GlobalConstants.GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX+ message + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX ;
|
||||
// String deviceOnlineStatusRedis = RedisUtils.getCacheObject(deviceOnlineStatusRedisKey);
|
||||
// if(StringUtils.isBlank(deviceOnlineStatusRedis)){
|
||||
// UpdateWrapper<Device> updateWrapper = new UpdateWrapper<>();
|
||||
// updateWrapper.eq("device_imei", message)
|
||||
// .set("online_status", 1);
|
||||
// deviceMapper.update(updateWrapper);
|
||||
// }
|
||||
// QueryWrapper<Device> queryWrapper = new QueryWrapper<>();
|
||||
// queryWrapper.eq("device_imei", message);
|
||||
// queryWrapper.eq("online_status", 1);
|
||||
// Long count = deviceMapper.selectCount(queryWrapper);
|
||||
// if(count == 0){
|
||||
// UpdateWrapper<Device> updateWrapper = new UpdateWrapper<>();
|
||||
// updateWrapper.eq("device_imei", message)
|
||||
// .eq("online_status", 0)
|
||||
// .set("online_status", 1);
|
||||
// deviceMapper.update(updateWrapper);
|
||||
// }
|
||||
// 执行业务逻辑
|
||||
UpdateWrapper<Device> updateWrapper = new UpdateWrapper<>();
|
||||
updateWrapper.eq("device_imei", message)
|
||||
updateWrapper.eq("device_imei", imei)
|
||||
.in("online_status", 0,2)
|
||||
.set("online_status", 1);
|
||||
int update = deviceMapper.update(updateWrapper);
|
||||
// 模拟业务处理耗时
|
||||
// Thread.sleep(200);
|
||||
|
||||
log.info("业务处理线程 {} 完成消息处理: {}", threadName, message);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("业务处理线程 {} 处理消息时发生错误: {}", threadName, message, e);
|
||||
log.error("业务处理线程 {} 处理消息时发生错误: {}", threadName, imei, e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -22,6 +22,8 @@ public class OnlineStatusTask {
|
||||
|
||||
@Autowired
|
||||
private DeviceMapper deviceMapper;
|
||||
|
||||
|
||||
// 使用cron表达式,每分钟的第0秒执行
|
||||
@Scheduled(cron = "0 */3 * * * ?")
|
||||
public void cronTask() {
|
||||
@ -37,4 +39,5 @@ public class OnlineStatusTask {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -157,4 +157,4 @@ public class CaptchaController {
|
||||
return captchaVo;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -98,6 +98,7 @@ public class DeviceControlCenterController extends BaseController {
|
||||
return R.ok(appDeviceService.getDeviceInfo(deviceMac));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 指令下发记录
|
||||
*/
|
||||
@ -106,6 +107,7 @@ public class DeviceControlCenterController extends BaseController {
|
||||
return appDeviceService.getInstructionRecord(dto, pageQuery);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 导出
|
||||
*/
|
||||
|
||||
@ -59,6 +59,7 @@ public class DeviceFenceAccessRecordController extends BaseController {
|
||||
ExcelUtil.exportExcel(list, "围栏进出记录", DeviceFenceAccessRecordVo.class, response);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取围栏进出记录详细信息
|
||||
*
|
||||
|
||||
@ -3,6 +3,7 @@ package com.fuyuanshen.web.controller.device.fence;
|
||||
import java.util.List;
|
||||
|
||||
import com.fuyuanshen.equipment.domain.bo.DeviceGeoFenceBo;
|
||||
import com.fuyuanshen.equipment.domain.bo.FenceTerminalBo;
|
||||
import com.fuyuanshen.equipment.domain.dto.FenceCheckResponse;
|
||||
import com.fuyuanshen.equipment.domain.query.FenceCheckRequest;
|
||||
import com.fuyuanshen.equipment.domain.vo.DeviceGeoFenceVo;
|
||||
@ -102,6 +103,7 @@ public class DeviceGeoFenceController extends BaseController {
|
||||
return toAjax(deviceGeoFenceService.updateByBo(bo));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 删除电子围栏
|
||||
*
|
||||
@ -129,4 +131,26 @@ public class DeviceGeoFenceController extends BaseController {
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 添加电子围栏终端
|
||||
*
|
||||
* @param bo
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/addTerminal")
|
||||
public R<Void> addFenceTerminal(@RequestBody FenceTerminalBo bo) {
|
||||
return toAjax(deviceGeoFenceService.addFenceTerminal(bo));
|
||||
}
|
||||
/**
|
||||
* 删除电子围栏终端
|
||||
*
|
||||
* @param bo
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/delTerminal")
|
||||
public R<Void> delFenceTerminal(@RequestBody FenceTerminalBo bo) {
|
||||
return toAjax(deviceGeoFenceService.delFenceTerminal(bo));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,340 @@
|
||||
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.controller.device.bjq.AppDeviceHBY100JController;
|
||||
import com.fuyuanshen.app.domain.AppBusinessFile;
|
||||
import com.fuyuanshen.app.domain.AppPersonnelInfo;
|
||||
import com.fuyuanshen.app.domain.AppPersonnelInfoRecords;
|
||||
import com.fuyuanshen.app.domain.bo.AppBusinessFileBo;
|
||||
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.AppBusinessFileVo;
|
||||
import com.fuyuanshen.app.domain.vo.AppDeviceDetailVo;
|
||||
import com.fuyuanshen.app.domain.vo.AppDeviceHBY100JDetailVo;
|
||||
import com.fuyuanshen.app.domain.vo.AppPersonnelInfoVo;
|
||||
import com.fuyuanshen.app.mapper.AppBusinessFileMapper;
|
||||
import com.fuyuanshen.app.mapper.AppPersonnelInfoMapper;
|
||||
import com.fuyuanshen.app.mapper.AppPersonnelInfoRecordsMapper;
|
||||
import com.fuyuanshen.app.service.IAppBusinessFileService;
|
||||
import com.fuyuanshen.common.core.constant.GlobalConstants;
|
||||
import com.fuyuanshen.common.core.domain.model.LoginUser;
|
||||
import com.fuyuanshen.common.core.exception.ServiceException;
|
||||
import com.fuyuanshen.common.core.utils.*;
|
||||
import com.fuyuanshen.common.redis.utils.RedisUtils;
|
||||
import com.fuyuanshen.common.satoken.utils.AppLoginHelper;
|
||||
import com.fuyuanshen.common.satoken.utils.LoginHelper;
|
||||
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;
|
||||
import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants;
|
||||
import com.fuyuanshen.global.mqtt.constants.MqttConstants;
|
||||
import com.fuyuanshen.global.mqtt.rule.hby100j.domin.*;
|
||||
import com.fuyuanshen.global.mqtt.utils.GenerateIdUtil;
|
||||
import com.fuyuanshen.system.domain.SysOss;
|
||||
import com.fuyuanshen.system.domain.vo.SysOssVo;
|
||||
import com.fuyuanshen.system.mapper.SysOssMapper;
|
||||
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.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 DeviceHBY100JBizService {
|
||||
|
||||
private final DeviceMapper deviceMapper;
|
||||
private final DeviceTypeMapper deviceTypeMapper;
|
||||
private final MqttGateway mqttGateway;
|
||||
private final DeviceLogMapper deviceLogMapper;
|
||||
private final IAppBusinessFileService appBusinessFileService;
|
||||
private final AppBusinessFileMapper appBusinessFileMapper;
|
||||
private final SysOssMapper sysOssMapper;
|
||||
|
||||
|
||||
private static final String DEVICE_TYPE = "HBY100/";
|
||||
/**
|
||||
* 记录设备操作日志
|
||||
*
|
||||
* @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 AppDeviceHBY100JDetailVo getInfo(Long id) {
|
||||
Device device = deviceMapper.selectById(id);
|
||||
if (device == null) {
|
||||
throw new RuntimeException("请先将设备入库!!!");
|
||||
}
|
||||
|
||||
AppDeviceHBY100JDetailVo vo = new AppDeviceHBY100JDetailVo();
|
||||
vo.setDeviceId(device.getId());
|
||||
vo.setDeviceName(device.getDeviceName());
|
||||
vo.setDevicePic(device.getDevicePic());
|
||||
vo.setDeviceImei(device.getDeviceImei());
|
||||
vo.setDeviceMac(device.getDeviceMac());
|
||||
DeviceType deviceType = deviceTypeMapper.selectById(device.getDeviceType());
|
||||
if (deviceType != null) {
|
||||
vo.setCommunicationMode(Integer.valueOf(deviceType.getCommunicationMode()));
|
||||
vo.setTypeName(deviceType.getTypeName());
|
||||
}
|
||||
vo.setBluetoothName(device.getBluetoothName());
|
||||
|
||||
// 设备在线状态
|
||||
String onlineStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX + device.getDeviceImei() + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX);
|
||||
// 设备在线状态
|
||||
if ("1".equals(onlineStatus)) {
|
||||
vo.setOnlineStatus(1);
|
||||
} else if ("2".equals(onlineStatus)) {
|
||||
vo.setOnlineStatus(2);
|
||||
} else {
|
||||
vo.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);
|
||||
vo.setBatteryPercentage(jsonObject.getString("batteryPercentage"));
|
||||
vo.setChargeState(jsonObject.getString("chargeState"));
|
||||
} else {
|
||||
vo.setBatteryPercentage("0");
|
||||
}
|
||||
|
||||
String lightModeStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX + device.getDeviceImei() + DeviceRedisKeyConstants.DEVICE_LIGHT_MODE_KEY_PREFIX);
|
||||
|
||||
|
||||
String lightBrightnessStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX + device.getDeviceImei() + DeviceRedisKeyConstants.DEVICE_LIGHT_BRIGHTNESS_KEY_PREFIX);
|
||||
if (StringUtils.isNotBlank(lightBrightnessStatus)) {
|
||||
vo.setLightBrightness(lightBrightnessStatus);
|
||||
}
|
||||
|
||||
// 获取经度纬度
|
||||
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);
|
||||
// vo.setLongitude(jsonObject.get("longitude").toString());
|
||||
// vo.setLatitude(jsonObject.get("latitude").toString());
|
||||
// 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.setVoiceStrobeAlarm(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;
|
||||
}
|
||||
|
||||
|
||||
private boolean getDeviceStatus(String deviceImei) {
|
||||
String deviceOnlineStatusRedisKey = GlobalConstants.GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX + deviceImei + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX;
|
||||
return RedisUtils.getCacheObject(deviceOnlineStatusRedisKey) == null;
|
||||
}
|
||||
|
||||
|
||||
public void forceAlarmActivation(AppDeviceHBY100JController.HBY100JForceAlarmActivationDto bo) {
|
||||
List<Long> deviceIds = bo.getDeviceIds();
|
||||
if (deviceIds == null || deviceIds.isEmpty()) {
|
||||
throw new ServiceException("请选择设备");
|
||||
}
|
||||
LoginUser loginUser = LoginHelper.getLoginUser();
|
||||
bo.getDeviceIds().forEach(deviceId -> {
|
||||
Device deviceObj = deviceMapper.selectById(deviceId);
|
||||
// if (getDeviceStatus(deviceObj.getDeviceImei())) {
|
||||
// throw new ServiceException(deviceObj.getDeviceName() + ",设备已断开连接");
|
||||
// }
|
||||
|
||||
// 语音播报
|
||||
if(bo.getMode() == 9){
|
||||
FuncType6VoicePlayRequest request = new FuncType6VoicePlayRequest();
|
||||
request.setRequestId(GenerateIdUtil.generateNumericId());
|
||||
request.setImei(deviceObj.getDeviceImei());
|
||||
request.setFuncType("6");
|
||||
request.setTimestamp(System.currentTimeMillis());
|
||||
FuncType6VoicePlayRequest.Data data = new FuncType6VoicePlayRequest.Data();
|
||||
data.setVoiceBroadcast(bo.getVoiceStrobeAlarm());
|
||||
request.setData(data);
|
||||
log.info("HBY100J更新语音,参数:{}", request);
|
||||
mqttGateway.sendMsgToMqtt(buildMqttTopic(deviceObj.getDeviceImei()), 1, JSON.toJSONString(request));
|
||||
}else{
|
||||
FuncType12ForceAudioRequest request = new FuncType12ForceAudioRequest();
|
||||
request.setRequestId(GenerateIdUtil.generateNumericId());
|
||||
request.setImei(deviceObj.getDeviceImei());
|
||||
request.setFuncType("12");
|
||||
request.setTimestamp(System.currentTimeMillis());
|
||||
FuncType12ForceAudioRequest.Data data = new FuncType12ForceAudioRequest.Data();
|
||||
data.setVoiceStrobeAlarm(bo.getVoiceStrobeAlarm());
|
||||
data.setMode(bo.getMode());
|
||||
request.setData(data);
|
||||
log.info("HBY100J更新语音,参数:{}", request);
|
||||
mqttGateway.sendMsgToMqtt(buildMqttTopic(deviceObj.getDeviceImei()), 1, JSON.toJSONString(request));
|
||||
}
|
||||
|
||||
|
||||
// recordDeviceLog(deviceId, deviceObj.getDeviceName(), "强制报警激活", "强制报警激活", loginUser.getUserId());
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public void updateVoice(AppDeviceHBY100JController.HBY100JUpdateVoiceDto dto) {
|
||||
AppBusinessFileVo appBusinessFileVo = appBusinessFileMapper.selectVoById(dto.getId());
|
||||
if(appBusinessFileVo == null){
|
||||
throw new ServiceException("文件不存在");
|
||||
}
|
||||
Device deviceObj = deviceMapper.selectById(appBusinessFileVo.getBusinessId());
|
||||
// if (getDeviceStatus(deviceObj.getDeviceImei())) {
|
||||
// throw new ServiceException(deviceObj.getDeviceName() + ",设备已断开连接");
|
||||
// }
|
||||
|
||||
LoginUser loginUser = LoginHelper.getLoginUser();
|
||||
SysOssVo sysOssVo = sysOssMapper.selectVoById(appBusinessFileVo.getFileId());
|
||||
FuncType5UpdateVoiceRequest request = new FuncType5UpdateVoiceRequest();
|
||||
request.setRequestId(GenerateIdUtil.generateNumericId());
|
||||
request.setImei(deviceObj.getDeviceImei());
|
||||
request.setFuncType("5");
|
||||
request.setTimestamp(System.currentTimeMillis());
|
||||
FuncType5UpdateVoiceRequest.Data data = new FuncType5UpdateVoiceRequest.Data();
|
||||
data.setVoiceResource(sysOssVo.getUrl());
|
||||
data.setVoiceType(0);
|
||||
request.setData(data);
|
||||
log.info("HBY100J更新语音,参数:{}", request);
|
||||
|
||||
mqttGateway.sendMsgToMqtt(buildMqttTopic(deviceObj.getDeviceImei()), 1, JSON.toJSONString(request));
|
||||
|
||||
UpdateWrapper<AppBusinessFile> updateWrapper = new UpdateWrapper<>();
|
||||
updateWrapper.eq("business_id", appBusinessFileVo.getBusinessId());
|
||||
updateWrapper.set("use_status", 0);
|
||||
appBusinessFileMapper.update(updateWrapper);
|
||||
|
||||
UpdateWrapper<AppBusinessFile> updateWrapper2 = new UpdateWrapper<>();
|
||||
updateWrapper2.eq("id", appBusinessFileVo.getId());
|
||||
updateWrapper2.set("use_status", 1);
|
||||
appBusinessFileMapper.update(updateWrapper2);
|
||||
}
|
||||
|
||||
private String buildMqttTopic(String deviceImei) {
|
||||
String tenantId = LoginHelper.getTenantId();
|
||||
return MqttConstants.GLOBAL_PUB_KEY2 +tenantId + "/" + DEVICE_TYPE + deviceImei;
|
||||
}
|
||||
|
||||
public void strobeMode(AppDeviceHBY100JController.HBY100JStrobeModeDto params) {
|
||||
log.info("HBY100J爆闪模式开启/关闭,请求参数:{}", params);
|
||||
Device deviceObj = deviceMapper.selectById(params.getDeviceId());
|
||||
// if (getDeviceStatus(deviceObj.getDeviceImei())) {
|
||||
// throw new ServiceException(deviceObj.getDeviceName() + ",设备已断开连接");
|
||||
// }
|
||||
FuncType10StrobeModeRequest request = new FuncType10StrobeModeRequest();
|
||||
request.setRequestId(GenerateIdUtil.generateNumericId());
|
||||
request.setImei(deviceObj.getDeviceImei());
|
||||
request.setFuncType("10");
|
||||
request.setTimestamp(System.currentTimeMillis());
|
||||
FuncType10StrobeModeRequest.Data data = new FuncType10StrobeModeRequest.Data();
|
||||
data.setMode(params.getMode());
|
||||
data.setEnable(params.getEnable());
|
||||
request.setData(data);
|
||||
log.info("HBY100J爆闪模式开启/关闭,下发设备参数:{}", request);
|
||||
mqttGateway.sendMsgToMqtt(buildMqttTopic(deviceObj.getDeviceImei()), 1, JSON.toJSONString(request));
|
||||
}
|
||||
|
||||
public void lightAdjustment(AppDeviceHBY100JController.HBY100JLightAdjustmentDto params) {
|
||||
log.info("HBY100J灯光调节,请求参数:{}", params);
|
||||
Device deviceObj = deviceMapper.selectById(params.getDeviceId());
|
||||
// if (getDeviceStatus(deviceObj.getDeviceImei())) {
|
||||
// throw new ServiceException(deviceObj.getDeviceName() + ",设备已断开连接");
|
||||
// }
|
||||
FuncType13BrightnessRequest request = new FuncType13BrightnessRequest();
|
||||
request.setRequestId(GenerateIdUtil.generateNumericId());
|
||||
request.setImei(deviceObj.getDeviceImei());
|
||||
request.setFuncType("13");
|
||||
request.setTimestamp(System.currentTimeMillis());
|
||||
FuncType13BrightnessRequest.Data data = new FuncType13BrightnessRequest.Data();
|
||||
data.setRed(params.getBrightness());
|
||||
data.setBlue(params.getBrightness());
|
||||
data.setYellow(params.getBrightness());
|
||||
request.setData(data);
|
||||
log.info("HBY100J灯光调节,下发设备参数:{}", request);
|
||||
mqttGateway.sendMsgToMqtt(buildMqttTopic(deviceObj.getDeviceImei()), 1, JSON.toJSONString(request));
|
||||
}
|
||||
|
||||
public void strobeFrequency(AppDeviceHBY100JController.HBY100JStrobeFrequencyDto params) {
|
||||
log.info("HBY100J爆闪频率,请求参数:{}", params);
|
||||
Device deviceObj = deviceMapper.selectById(params.getDeviceId());
|
||||
// if (getDeviceStatus(deviceObj.getDeviceImei())) {
|
||||
// throw new ServiceException(deviceObj.getDeviceName() + ",设备已断开连接");
|
||||
// }
|
||||
FuncType11FrequencyRequest request = new FuncType11FrequencyRequest();
|
||||
request.setRequestId(GenerateIdUtil.generateNumericId());
|
||||
request.setImei(deviceObj.getDeviceImei());
|
||||
request.setFuncType("11");
|
||||
request.setTimestamp(System.currentTimeMillis());
|
||||
FuncType11FrequencyRequest.Data data = new FuncType11FrequencyRequest.Data();
|
||||
data.setFrequency(params.getFrequency());
|
||||
request.setData(data);
|
||||
log.info("HBY100J灯光调节,下发设备参数:{}", request);
|
||||
mqttGateway.sendMsgToMqtt(buildMqttTopic(deviceObj.getDeviceImei()), 1, JSON.toJSONString(request));
|
||||
}
|
||||
|
||||
public void updateVolume(AppDeviceHBY100JController.HBY100JUpdateVolumeDto params) {
|
||||
log.info("HBY100J更新音量,请求参数:{}", params);
|
||||
Device deviceObj = deviceMapper.selectById(params.getDeviceId());
|
||||
// if (getDeviceStatus(deviceObj.getDeviceImei())) {
|
||||
// throw new ServiceException(deviceObj.getDeviceName() + ",设备已断开连接");
|
||||
// }
|
||||
FuncType9UpdateVolumeRequest request = new FuncType9UpdateVolumeRequest();
|
||||
request.setRequestId(GenerateIdUtil.generateNumericId());
|
||||
request.setImei(deviceObj.getDeviceImei());
|
||||
request.setFuncType("9");
|
||||
request.setTimestamp(System.currentTimeMillis());
|
||||
FuncType9UpdateVolumeRequest.Data data = new FuncType9UpdateVolumeRequest.Data();
|
||||
data.setVolume(params.getVolume());
|
||||
request.setData(data);
|
||||
log.info("HBY100J更新音量,下发设备参数:{}", JSON.toJSONString(request));
|
||||
mqttGateway.sendMsgToMqtt(buildMqttTopic(deviceObj.getDeviceImei()), 1, JSON.toJSONString(request));
|
||||
}
|
||||
}
|
||||
@ -57,9 +57,11 @@ import com.fuyuanshen.web.domain.Dto.DeviceDebugLogoUploadDto;
|
||||
import com.fuyuanshen.web.domain.Dto.DeviceXinghanInstructDto;
|
||||
import com.fuyuanshen.web.domain.vo.DeviceXinghanDetailVo;
|
||||
import com.fuyuanshen.web.enums.AlarmTypeEnum;
|
||||
import com.fuyuanshen.web.util.AliyunVoiceUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
@ -93,6 +95,7 @@ public class DeviceXinghanBizService {
|
||||
private final DeviceAssignmentsService deviceAssignmentsService;
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
private final AliyunVoiceUtil voiceUtil;
|
||||
|
||||
/**
|
||||
* 所有档位的描述表
|
||||
@ -135,7 +138,6 @@ public class DeviceXinghanBizService {
|
||||
public void upSOSGradeSettings(DeviceXinghanInstructDto dto) {
|
||||
if(dto.getIsBluetooth()){
|
||||
long deviceId = dto.getDeviceId();
|
||||
|
||||
// 1. 使用Optional简化空值检查,使代码更简洁
|
||||
Device device = Optional.ofNullable(deviceMapper.selectById(deviceId))
|
||||
.orElseThrow(() -> new ServiceException("设备不存在"));
|
||||
@ -147,6 +149,24 @@ public class DeviceXinghanBizService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发异步报警
|
||||
* Spring 会自动调用 AsyncConfig.getAsyncExecutor() 来执行此方法
|
||||
*/
|
||||
@Async
|
||||
public void executeSosCall(String phone) {
|
||||
log.info("[SOS业务] 准备发起语音拨号 -> 目标: {}", phone);
|
||||
Map<String, String> params = Map.of("device", "670");
|
||||
String callId = voiceUtil.sendTtsSync(phone, "TTS_328730104", params);
|
||||
|
||||
if (callId != null) {
|
||||
log.info("[SOS业务] 拨号指令下发成功, callId: {}", callId);
|
||||
// 这里可以记录拨打日志到数据库
|
||||
} else {
|
||||
log.error("[SOS业务] 拨号指令下发失败,请检查配置或余额");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置强制报警
|
||||
*/
|
||||
@ -739,6 +759,7 @@ public class DeviceXinghanBizService {
|
||||
device.setCreateByName(loginUser.getNickname());
|
||||
device.setTypeName(deviceTypes.getTypeName());
|
||||
device.setDeviceType(deviceTypes.getId());
|
||||
device.setDevicePic(deviceTypes.getDevicePic());
|
||||
if (device.getDeviceImei() != null) {
|
||||
device.setPubTopic("A/" + device.getDeviceImei());
|
||||
device.setSubTopic("B/" + device.getDeviceImei());
|
||||
@ -767,4 +788,23 @@ public class DeviceXinghanBizService {
|
||||
return uuidStr.replaceAll("-", "");
|
||||
}
|
||||
|
||||
public Map<String, Object> GetDeviceByName(DeviceForm deviceForm){
|
||||
List<Map<String, Object>> list= deviceMapper.GetDeviceByName(deviceForm);
|
||||
Map<String, Object> device=null;
|
||||
if(list!=null && list.size()>0){
|
||||
device=list.get(0);
|
||||
}
|
||||
return device;
|
||||
}
|
||||
|
||||
public int getEquipCountByType(DeviceForm form){
|
||||
var res=deviceMapper.getEquipCountByType(form);
|
||||
return res;
|
||||
}
|
||||
|
||||
public List<Map<String,Object>> getEquipAllByType(DeviceForm deviceForm){
|
||||
List<Map<String, Object>> list= deviceMapper.getEquipAllByType(deviceForm);
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,87 @@
|
||||
package com.fuyuanshen.web.util;
|
||||
|
||||
import com.aliyun.dyvmsapi20170525.Client;
|
||||
import com.aliyun.dyvmsapi20170525.models.SingleCallByTtsRequest;
|
||||
import com.aliyun.dyvmsapi20170525.models.SingleCallByTtsResponse;
|
||||
import com.aliyun.teaopenapi.models.Config;
|
||||
import com.aliyun.teautil.models.RuntimeOptions;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class AliyunVoiceUtil {
|
||||
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
@Value("${alibaba.tts.akId}")
|
||||
private String akId;
|
||||
@Value("${alibaba.tts.akSecret}")
|
||||
private String akSecret;
|
||||
// @Value("${alibaba.tts.calledShowNumber:}")
|
||||
private String calledShowNumber;
|
||||
|
||||
// ========== 核心:单例客户端(类似 OkHttpClient) ==========
|
||||
private volatile Client client;
|
||||
|
||||
/**
|
||||
* 获取客户端(双重检查锁实现单例)
|
||||
* 只有在第一次调用时才会根据配置实例化,后续直接返回复用
|
||||
*/
|
||||
private Client getClient() throws Exception {
|
||||
if (client == null) {
|
||||
synchronized (this) {
|
||||
if (client == null) {
|
||||
log.info("[AliyunVoice] 正在初始化阿里云语音客户端...");
|
||||
Config config = new Config()
|
||||
.setAccessKeyId(akId)
|
||||
.setAccessKeySecret(akSecret)
|
||||
.setEndpoint("dyvmsapi.aliyuncs.com");
|
||||
this.client = new Client(config);
|
||||
}
|
||||
}
|
||||
}
|
||||
return client;
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步发送方法:由异步架构调用
|
||||
*/
|
||||
public String sendTtsSync(String phone, String templateCode, Map<String, String> params) {
|
||||
|
||||
try {
|
||||
// 1. 获取(或初始化)单例客户端
|
||||
Client voiceClient = getClient();
|
||||
|
||||
SingleCallByTtsRequest request = new SingleCallByTtsRequest()
|
||||
.setCalledNumber(phone)
|
||||
.setTtsCode(templateCode)
|
||||
.setTtsParam(objectMapper.writeValueAsString(params));
|
||||
|
||||
if (StringUtils.hasText(calledShowNumber)) {
|
||||
request.setCalledShowNumber(calledShowNumber);
|
||||
}
|
||||
|
||||
// 生产级超时配置
|
||||
RuntimeOptions runtime = new RuntimeOptions();
|
||||
runtime.setConnectTimeout(5000);
|
||||
runtime.setReadTimeout(10000);
|
||||
|
||||
SingleCallByTtsResponse response = voiceClient.singleCallByTtsWithOptions(request, runtime);
|
||||
|
||||
if ("OK".equalsIgnoreCase(response.getBody().getCode())) {
|
||||
return response.getBody().getCallId();
|
||||
} else {
|
||||
log.error("[AliyunVoice] 拨号失败: {}", response.getBody().getMessage());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("[AliyunVoice] 接口异常", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -279,11 +279,13 @@ justauth:
|
||||
# MQTT配置
|
||||
mqtt:
|
||||
username: admin
|
||||
password: fys123456
|
||||
url: tcp://47.107.152.87:1883
|
||||
password: #YtvpSfCNG
|
||||
url: tcp://47.120.79.150:2883
|
||||
subClientId: fys_subClient
|
||||
subTopic: A/#,status/tenantCode/#,report/tenantCode/#
|
||||
pubTopic: B/#,command/tenantCode/#
|
||||
subTopic: A/#
|
||||
pubTopic: B/#
|
||||
subTopic2: command/894078/#
|
||||
pubTopic2: status/894078/#
|
||||
pubClientId: fys_pubClient
|
||||
|
||||
|
||||
|
||||
@ -283,14 +283,16 @@ mqtt:
|
||||
password: #YtvpSfCNG
|
||||
url: tcp://47.120.79.150:3883
|
||||
subClientId: fys_subClient
|
||||
subTopic: A/#,status/tenantCode/#,report/tenantCode/#
|
||||
pubTopic: B/#,command/tenantCode/#
|
||||
subTopic: A/#
|
||||
pubTopic: B/#
|
||||
subTopic2: status/894078/#
|
||||
pubTopic2: command/894078/#
|
||||
pubClientId: fys_pubClient
|
||||
|
||||
# TTS语音交互配置
|
||||
alibaba:
|
||||
tts:
|
||||
appKey: KTwSUKMrf2olFfjC
|
||||
akId: LTAI5t6RsfCvQh57qojzbEoe
|
||||
akSecret: MTqvK2mXYeCRkl1jVPndiNumyaad0R
|
||||
appKey: lbGuq5K5bEH4uxmT
|
||||
akId: LTAI5t66moCkhNC32TDJ5ReP
|
||||
akSecret: 2F3sdoBJ08bYvJcuDgSkLnJwGXsvYH
|
||||
|
||||
|
||||
@ -132,6 +132,8 @@ tenant:
|
||||
- app_menu
|
||||
- app_user_role
|
||||
- app_role_menu
|
||||
- track_service
|
||||
- device_fence_terminal
|
||||
|
||||
# MyBatisPlus配置
|
||||
# https://baomidou.com/config/
|
||||
|
||||
@ -77,4 +77,9 @@ public interface SystemConstants {
|
||||
*/
|
||||
String ROOT_DEPT_ANCESTORS = "0";
|
||||
|
||||
/**
|
||||
* 菜单ID
|
||||
*/
|
||||
public static final Long RESTRICTED_MENU_ID = 102L;
|
||||
|
||||
}
|
||||
|
||||
@ -39,8 +39,8 @@ public class EncryptUtilsTest {
|
||||
loginBody.setClientId("e5cd7e4891bf95d1d19206ce24a7b32e");
|
||||
loginBody.setGrantType("password");
|
||||
loginBody.setTenantId("894078");
|
||||
loginBody.setCode("0");
|
||||
loginBody.setUuid("1d6615668c7f410da77c4e002c601073");
|
||||
loginBody.setCode("6");
|
||||
loginBody.setUuid("399d45194f3d4c7e9cbfb3294b198a9b");
|
||||
// loginBody.setUsername("admin");
|
||||
// loginBody.setPassword("admin123");
|
||||
loginBody.setUsername("dyf");
|
||||
|
||||
@ -74,6 +74,7 @@ public class ExcelUtil {
|
||||
return listener.getExcelResult();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 导出excel
|
||||
*
|
||||
@ -92,6 +93,7 @@ public class ExcelUtil {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 导出excel
|
||||
*
|
||||
@ -174,6 +176,7 @@ public class ExcelUtil {
|
||||
public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, OutputStream os, List<DropDownOptions> options) {
|
||||
exportExcel(list, sheetName, clazz, false, os, options);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 导出excel
|
||||
@ -187,13 +190,13 @@ public class ExcelUtil {
|
||||
public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, boolean merge,
|
||||
OutputStream os, List<DropDownOptions> options) {
|
||||
ExcelWriterSheetBuilder builder = FastExcel.write(os, clazz)
|
||||
.autoCloseStream(false)
|
||||
// 自动适配
|
||||
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
|
||||
// 大数值自动转换 防止失真
|
||||
.registerConverter(new ExcelBigNumberConvert())
|
||||
.registerWriteHandler(new DataWriteHandler(clazz))
|
||||
.sheet(sheetName);
|
||||
.autoCloseStream(false)
|
||||
// 自动适配
|
||||
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
|
||||
// 大数值自动转换 防止失真
|
||||
.registerConverter(new ExcelBigNumberConvert())
|
||||
.registerWriteHandler(new DataWriteHandler(clazz))
|
||||
.sheet(sheetName);
|
||||
if (merge) {
|
||||
// 合并处理器
|
||||
builder.registerWriteHandler(new CellMergeStrategy(list, true));
|
||||
@ -203,6 +206,7 @@ public class ExcelUtil {
|
||||
builder.doWrite(list);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 单表多数据模板导出 模板格式为 {.属性}
|
||||
*
|
||||
@ -238,12 +242,12 @@ public class ExcelUtil {
|
||||
public static <T> void exportTemplate(List<T> data, String templatePath, OutputStream os) {
|
||||
ClassPathResource templateResource = new ClassPathResource(templatePath);
|
||||
ExcelWriter excelWriter = FastExcel.write(os)
|
||||
.withTemplate(templateResource.getStream())
|
||||
.autoCloseStream(false)
|
||||
// 大数值自动转换 防止失真
|
||||
.registerConverter(new ExcelBigNumberConvert())
|
||||
.registerWriteHandler(new DataWriteHandler(data.get(0).getClass()))
|
||||
.build();
|
||||
.withTemplate(templateResource.getStream())
|
||||
.autoCloseStream(false)
|
||||
// 大数值自动转换 防止失真
|
||||
.registerConverter(new ExcelBigNumberConvert())
|
||||
.registerWriteHandler(new DataWriteHandler(data.get(0).getClass()))
|
||||
.build();
|
||||
WriteSheet writeSheet = FastExcel.writerSheet().build();
|
||||
FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
|
||||
// 单表多数据导出 模板格式为 {.属性}
|
||||
@ -311,11 +315,11 @@ public class ExcelUtil {
|
||||
public static void exportTemplateMultiList(Map<String, Object> data, String templatePath, OutputStream os) {
|
||||
ClassPathResource templateResource = new ClassPathResource(templatePath);
|
||||
ExcelWriter excelWriter = FastExcel.write(os)
|
||||
.withTemplate(templateResource.getStream())
|
||||
.autoCloseStream(false)
|
||||
// 大数值自动转换 防止失真
|
||||
.registerConverter(new ExcelBigNumberConvert())
|
||||
.build();
|
||||
.withTemplate(templateResource.getStream())
|
||||
.autoCloseStream(false)
|
||||
// 大数值自动转换 防止失真
|
||||
.registerConverter(new ExcelBigNumberConvert())
|
||||
.build();
|
||||
WriteSheet writeSheet = FastExcel.writerSheet().build();
|
||||
for (Map.Entry<String, Object> map : data.entrySet()) {
|
||||
// 设置列表后续还有数据
|
||||
@ -342,11 +346,11 @@ public class ExcelUtil {
|
||||
public static void exportTemplateMultiSheet(List<Map<String, Object>> data, String templatePath, OutputStream os) {
|
||||
ClassPathResource templateResource = new ClassPathResource(templatePath);
|
||||
ExcelWriter excelWriter = FastExcel.write(os)
|
||||
.withTemplate(templateResource.getStream())
|
||||
.autoCloseStream(false)
|
||||
// 大数值自动转换 防止失真
|
||||
.registerConverter(new ExcelBigNumberConvert())
|
||||
.build();
|
||||
.withTemplate(templateResource.getStream())
|
||||
.autoCloseStream(false)
|
||||
// 大数值自动转换 防止失真
|
||||
.registerConverter(new ExcelBigNumberConvert())
|
||||
.build();
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
WriteSheet writeSheet = FastExcel.writerSheet(i).build();
|
||||
for (Map.Entry<String, Object> map : data.get(i).entrySet()) {
|
||||
|
||||
@ -47,5 +47,18 @@ public class AppBusinessFile extends TenantEntity {
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 文件时长
|
||||
*/
|
||||
private Integer duration;
|
||||
|
||||
/**
|
||||
* 重命名
|
||||
*/
|
||||
private String reName;
|
||||
|
||||
/**
|
||||
* 是否使用语音播报(0-否,1-是)
|
||||
*/
|
||||
private Integer useStatus;
|
||||
}
|
||||
|
||||
@ -38,7 +38,7 @@ public class AppBusinessFileBo extends BaseEntity {
|
||||
private Long businessId;
|
||||
|
||||
/**
|
||||
* 文件类型(1:操作说明,2:产品参数)
|
||||
* 文件类型(1:操作说明,2:产品参数,3:语音管理)
|
||||
*/
|
||||
private Long fileType;
|
||||
|
||||
@ -49,4 +49,11 @@ public class AppBusinessFileBo extends BaseEntity {
|
||||
|
||||
|
||||
private List<Long> ids;
|
||||
|
||||
private Integer duration;
|
||||
|
||||
/**
|
||||
* 是否使用语音播报(0-否,1-是)
|
||||
*/
|
||||
private Integer useStatus;
|
||||
}
|
||||
|
||||
@ -0,0 +1,104 @@
|
||||
package com.fuyuanshen.app.domain.vo;
|
||||
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
public class AppDeviceHBY100JDetailVo implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 设备ID
|
||||
*/
|
||||
@ExcelProperty(value = "设备ID")
|
||||
private Long deviceId;
|
||||
|
||||
|
||||
/**
|
||||
* 设备名称
|
||||
*/
|
||||
private String deviceName;
|
||||
|
||||
/**
|
||||
* 设备IMEI
|
||||
*/
|
||||
private String deviceImei;
|
||||
|
||||
/**
|
||||
* 设备MAC
|
||||
*/
|
||||
private String deviceMac;
|
||||
|
||||
/**
|
||||
* 通讯方式 0:4G;1:蓝牙,2 4G&蓝牙
|
||||
*/
|
||||
private Integer communicationMode;
|
||||
|
||||
/**
|
||||
* 设备图片
|
||||
*/
|
||||
private String devicePic;
|
||||
|
||||
/**
|
||||
* 设备类型
|
||||
*/
|
||||
private String typeName;
|
||||
|
||||
/**
|
||||
* 蓝牙名称
|
||||
*/
|
||||
private String bluetoothName;
|
||||
|
||||
//电量百分比
|
||||
private String batteryPercentage;
|
||||
|
||||
//充电状态(0没有充电,1正在充电,2为已充满)
|
||||
private String chargeState;
|
||||
|
||||
/**
|
||||
* 在线状态(0离线,1在线)
|
||||
*/
|
||||
private Integer onlineStatus;
|
||||
|
||||
// 经度
|
||||
private String longitude;
|
||||
|
||||
// 纬度
|
||||
private String latitude;
|
||||
|
||||
// 逆解析地址
|
||||
private String address;
|
||||
|
||||
// 亮度
|
||||
private String lightBrightness;
|
||||
|
||||
// 音量
|
||||
private Integer volume;
|
||||
|
||||
|
||||
/**
|
||||
* 语音资源URL(MP3等音频文件地址)
|
||||
*/
|
||||
private String voiceResource;
|
||||
|
||||
// 报警模式 0公安,1 消防,2应急,3交警,4 市政,5 铁路,6 医疗,7 部队,8 水利,9 语音
|
||||
private String alarmMode;
|
||||
|
||||
// 强制报警开关: 0 关闭, 1开启
|
||||
private String voiceStrobeAlarm;
|
||||
|
||||
/**
|
||||
* 0 红色爆闪,1 蓝色爆闪,2 黄色爆闪,3,红色顺时针旋转爆闪,4黄色顺时针旋转爆闪,5,红蓝顺时针旋转爆闪,6 红蓝交替爆闪
|
||||
*/
|
||||
private Integer strobeMode;
|
||||
|
||||
/**
|
||||
* 爆闪频率
|
||||
*/
|
||||
private Integer strobeFrequency;
|
||||
|
||||
}
|
||||
@ -51,6 +51,11 @@ public class AppDeviceShareVo implements Serializable {
|
||||
@ExcelProperty(value = "设备名称")
|
||||
private String deviceName;
|
||||
|
||||
/**
|
||||
* 设备mac地址
|
||||
*/
|
||||
private String deviceMac;
|
||||
|
||||
/**
|
||||
* 手机号
|
||||
*/
|
||||
|
||||
@ -27,4 +27,16 @@ public class AppFileVo {
|
||||
* 文件url
|
||||
*/
|
||||
private String fileUrl;
|
||||
|
||||
private Integer duration;
|
||||
|
||||
/**
|
||||
* 扩展文件名称
|
||||
*/
|
||||
private String fileNameExt;
|
||||
|
||||
/***
|
||||
* 是否使用语音播报(0-否,1-是)
|
||||
*/
|
||||
private Integer useStatus;
|
||||
}
|
||||
|
||||
@ -78,4 +78,5 @@ public interface IAppBusinessFileService {
|
||||
|
||||
|
||||
List<AppFileVo> queryAppFileList(AppBusinessFileBo bo);
|
||||
|
||||
}
|
||||
|
||||
@ -5,7 +5,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<mapper namespace="com.fuyuanshen.app.mapper.AppBusinessFileMapper">
|
||||
|
||||
<select id="queryAppFileList" resultType="com.fuyuanshen.app.domain.vo.AppFileVo">
|
||||
select a.id,a.business_id,a.file_id,a.file_type,b.file_name,b.url fileUrl from app_business_file a left join sys_oss b on a.file_id = b.oss_id
|
||||
select a.id,a.business_id,a.file_id,a.file_type,b.file_name,b.url fileUrl,a.duration,
|
||||
CASE
|
||||
WHEN a.re_name IS NULL THEN
|
||||
SUBSTRING_INDEX(COALESCE(a.re_name, b.original_name), '.', 1)
|
||||
ELSE
|
||||
a.re_name
|
||||
END AS fileNameExt,a.use_status
|
||||
from app_business_file a left join sys_oss b on a.file_id = b.oss_id
|
||||
where 1=1
|
||||
<if test="businessId != null">
|
||||
and a.business_id = #{businessId}
|
||||
|
||||
@ -16,10 +16,8 @@ import com.fuyuanshen.customer.domain.vo.ConsumerVo;
|
||||
import com.fuyuanshen.customer.mapper.CustomerMapper;
|
||||
import com.fuyuanshen.customer.service.CustomerService;
|
||||
import com.fuyuanshen.system.domain.SysUserRole;
|
||||
import com.fuyuanshen.system.domain.bo.SysUserBo;
|
||||
import com.fuyuanshen.system.mapper.SysRoleMapper;
|
||||
import com.fuyuanshen.system.mapper.SysUserRoleMapper;
|
||||
import com.fuyuanshen.system.service.ISysUserService;
|
||||
import com.fuyuanshen.system.service.impl.SysUserServiceImpl;
|
||||
import io.undertow.util.BadRequestException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@ -18,6 +18,12 @@
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org</groupId>
|
||||
<artifactId>jaudiotagger</artifactId>
|
||||
<version>2.0.3</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<!-- <dependency> -->
|
||||
<!-- <groupId>com.fuyuanshen</groupId> -->
|
||||
<!-- <artifactId>fys-app</artifactId> -->
|
||||
@ -140,6 +146,18 @@
|
||||
<version>3.3.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 电话语音通知 -->
|
||||
<dependency>
|
||||
<groupId>com.aliyun</groupId>
|
||||
<artifactId>dyvmsapi20170525</artifactId>
|
||||
<version>4.2.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.aliyun</groupId>
|
||||
<artifactId>tea-openapi</artifactId>
|
||||
<version>0.3.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- fastjson2 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.fastjson2</groupId>
|
||||
|
||||
@ -72,6 +72,12 @@ public class DeviceController extends BaseController {
|
||||
return deviceService.queryAll(criteria, page);
|
||||
}
|
||||
|
||||
@GetMapping(value = "/pageTerminal")
|
||||
public TableDataInfo<Device> deviceFenecSelect(DeviceQueryCriteria criteria) throws IOException {
|
||||
Page<Device> page = new Page<>(criteria.getPageNum(), criteria.getPageSize());
|
||||
return deviceService.queryAllTerminal(criteria, page);
|
||||
}
|
||||
|
||||
|
||||
// @Log("新增设备")
|
||||
@Operation(summary = "新增设备")
|
||||
|
||||
@ -41,6 +41,7 @@ public class DeviceRepairRecordsController extends BaseController {
|
||||
|
||||
private final IDeviceRepairRecordsService deviceRepairRecordsService;
|
||||
|
||||
|
||||
/**
|
||||
* 查询设备维修记录列表
|
||||
*/
|
||||
@ -52,6 +53,7 @@ public class DeviceRepairRecordsController extends BaseController {
|
||||
return deviceRepairRecordsService.queryPageList(criteria, page);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 导出设备维修记录列表
|
||||
*/
|
||||
@ -63,6 +65,7 @@ public class DeviceRepairRecordsController extends BaseController {
|
||||
ExcelUtil.exportExcel(list, "设备维修记录", DeviceRepairRecordsVo.class, response);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取设备维修记录详细信息
|
||||
*
|
||||
@ -75,6 +78,7 @@ public class DeviceRepairRecordsController extends BaseController {
|
||||
return R.ok(deviceRepairRecordsService.queryById(recordId));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 新增设备维修记录
|
||||
*/
|
||||
@ -86,6 +90,7 @@ public class DeviceRepairRecordsController extends BaseController {
|
||||
return toAjax(deviceRepairRecordsService.insertByBo(bo));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 修改设备维修记录
|
||||
*/
|
||||
@ -109,4 +114,5 @@ public class DeviceRepairRecordsController extends BaseController {
|
||||
@PathVariable Long[] recordIds) {
|
||||
return toAjax(deviceRepairRecordsService.deleteWithValidByIds(List.of(recordIds), true));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -15,6 +15,7 @@ import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@ -50,7 +51,7 @@ public class DeviceTypeController {
|
||||
// @Log("新增设备类型")
|
||||
@Operation(summary = "新增设备类型")
|
||||
@PostMapping(value = "/add")
|
||||
public R<Void> createDeviceType(@Validated @RequestBody DeviceType resources) {
|
||||
public R<Void> createDeviceType(@Validated @ModelAttribute DeviceTypeForm resources) throws IOException {
|
||||
deviceTypeService.create(resources);
|
||||
return R.ok();
|
||||
}
|
||||
@ -59,7 +60,7 @@ public class DeviceTypeController {
|
||||
// @Log("修改设备类型")
|
||||
@Operation(summary = "修改设备类型")
|
||||
@PutMapping(value = "/update")
|
||||
public R<Void> updateDeviceType(@Validated @RequestBody DeviceTypeForm resources) {
|
||||
public R<Void> updateDeviceType(@Validated @ModelAttribute DeviceTypeForm resources) throws IOException {
|
||||
deviceTypeService.update(resources);
|
||||
return R.ok();
|
||||
}
|
||||
@ -79,7 +80,6 @@ public class DeviceTypeController {
|
||||
public R<DeviceType> getCommunicationMode(@Parameter(name = "设备类型ID", required = true) Long id) {
|
||||
DeviceType communicationMode = deviceTypeService.getCommunicationMode(id);
|
||||
return R.ok(communicationMode);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,143 @@
|
||||
package com.fuyuanshen.equipment.controller;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.fuyuanshen.equipment.domain.Device;
|
||||
import com.fuyuanshen.equipment.domain.bo.TerminalDelBo;
|
||||
import com.fuyuanshen.equipment.domain.bo.TerminalDeviceBo;
|
||||
import com.fuyuanshen.equipment.domain.bo.TerminalQueryBo;
|
||||
import com.fuyuanshen.equipment.domain.bo.TrackServiceBo;
|
||||
import com.fuyuanshen.equipment.domain.dto.TerminalDeviceDto;
|
||||
import com.fuyuanshen.equipment.domain.vo.TrackServiceVo;
|
||||
import com.fuyuanshen.equipment.service.ITrackServiceService;
|
||||
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.common.mybatis.core.page.TableDataInfo;
|
||||
|
||||
/**
|
||||
* 轨迹服务
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2025-11-24
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/api/trackService")
|
||||
public class TrackServiceController extends BaseController {
|
||||
|
||||
private final ITrackServiceService trackServiceService;
|
||||
|
||||
/**
|
||||
* 查询轨迹服务列表
|
||||
*/
|
||||
// @SaCheckPermission("equipment:trackService:list")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<TrackServiceVo> list(TrackServiceBo bo, PageQuery pageQuery) {
|
||||
return trackServiceService.queryPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 轨迹服务列表
|
||||
*/
|
||||
// @SaCheckPermission("equipment:trackService:export")
|
||||
@Log(title = "轨迹服务", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(TrackServiceBo bo, HttpServletResponse response) {
|
||||
List<TrackServiceVo> list = trackServiceService.queryList(bo);
|
||||
ExcelUtil.exportExcel(list, "轨迹服务", TrackServiceVo.class, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取轨迹服务详细信息
|
||||
*
|
||||
* @param id 主键
|
||||
*/
|
||||
@GetMapping("/{id}")
|
||||
public R<TrackServiceVo> getInfo(@NotNull(message = "主键不能为空")
|
||||
@PathVariable Long id) {
|
||||
return R.ok(trackServiceService.queryById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增轨迹服务
|
||||
*/
|
||||
// @SaCheckPermission("equipment:trackService:add")
|
||||
@Log(title = "轨迹服务", businessType = BusinessType.INSERT)
|
||||
@PostMapping(value = "/add")
|
||||
public R<Void> add(@Validated(AddGroup.class) @RequestBody TrackServiceBo bo) {
|
||||
return toAjax(trackServiceService.insertByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改轨迹服务
|
||||
*/
|
||||
// @SaCheckPermission("equipment:trackService:edit")
|
||||
@Log(title = "轨迹服务", businessType = BusinessType.UPDATE)
|
||||
@PostMapping(value = "/update")
|
||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody TrackServiceBo bo) {
|
||||
return toAjax(trackServiceService.updateByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除轨迹服务
|
||||
*
|
||||
* @param ids 主键串
|
||||
*/
|
||||
// @SaCheckPermission("equipment:trackService:remove")
|
||||
@Log(title = "轨迹服务", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping(value = "/delete")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空")
|
||||
@PathVariable Long[] ids) {
|
||||
return toAjax(trackServiceService.deleteWithValidByIds(List.of(ids), true));
|
||||
}
|
||||
|
||||
@Log(title = "高德添加终端设备")
|
||||
@PostMapping(value = "/terminal")
|
||||
public R<Void> terminalDevice(@RequestBody TerminalDeviceBo model) {
|
||||
trackServiceService.terminal(model);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@Log(title = "高德移除终端设备")
|
||||
@DeleteMapping(value = "/delTerminal/{ids}")
|
||||
public R<Void> delTerminalDevice(@NotEmpty(message = "主键不能为空")
|
||||
@PathVariable Long[] ids) {
|
||||
return toAjax(trackServiceService.delTerminalDevice(List.of(ids), true));
|
||||
}
|
||||
|
||||
@Log(title = "高德移除终端设备")
|
||||
@DeleteMapping(value = "/del/Terminal")
|
||||
public R<Void> delTerminalDevice(TerminalDelBo bo) {
|
||||
return toAjax(trackServiceService.delTerminalDevice(bo, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* 高德终端设备列表
|
||||
*/
|
||||
@GetMapping("/listTerminal")
|
||||
public TableDataInfo<TerminalDeviceDto> listTerminal(TerminalQueryBo bo) {
|
||||
return trackServiceService.listTerminal(bo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 高德终端设备列表(关键字查询)
|
||||
*/
|
||||
@GetMapping("/searchTerminal")
|
||||
public TableDataInfo<TerminalDeviceDto> searchTerminal(TerminalQueryBo bo) {
|
||||
return trackServiceService.searchTerminal(bo);
|
||||
}
|
||||
}
|
||||
@ -167,4 +167,20 @@ public class Device extends TenantEntity {
|
||||
*/
|
||||
private Integer onlineStatus;
|
||||
|
||||
/**
|
||||
* 高德服务ID
|
||||
*/
|
||||
@Schema(title = "服务ID(高德)")
|
||||
private Long sid;
|
||||
/**
|
||||
* 高德终端ID
|
||||
*/
|
||||
@Schema(title = "终端ID(高德)")
|
||||
private Long tid;
|
||||
/**
|
||||
* 高德轨迹ID
|
||||
*/
|
||||
@Schema(title = "轨迹ID(高德)")
|
||||
private Long trid;
|
||||
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ package com.fuyuanshen.equipment.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.fuyuanshen.common.mybatis.core.domain.BaseEntity;
|
||||
import com.fuyuanshen.common.tenant.core.TenantEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import java.util.Date;
|
||||
@ -18,7 +19,7 @@ import java.io.Serial;
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("device_fence_access_record")
|
||||
public class DeviceFenceAccessRecord extends BaseEntity {
|
||||
public class DeviceFenceAccessRecord extends TenantEntity {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user