Compare commits

2 Commits

Author SHA1 Message Date
3d1c2f4e56 Merge branch 'dyf-device' into prod 2025-12-19 14:08:03 +08:00
c480bda112 围栏进出记录 2025-12-19 14:06:01 +08:00
13 changed files with 180 additions and 50 deletions

View 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();
}
}

View File

@ -64,4 +64,5 @@ public class AppVideoController extends BaseController {
public R<String> extract(@RequestParam("file") MultipartFile file) throws Exception { public R<String> extract(@RequestParam("file") MultipartFile file) throws Exception {
return R.ok("Success",audioProcessService.extract(file)); return R.ok("Success",audioProcessService.extract(file));
} }
} }

View File

@ -10,17 +10,38 @@ import org.springframework.integration.mqtt.core.MqttPahoClientFactory;
@Configuration @Configuration
public class MqttConfiguration { public class MqttConfiguration {
@Autowired @Autowired
private MqttPropertiesConfig mqttPropertiesConfig; private MqttPropertiesConfig mqttPropertiesConfig;
/** 创建连接工厂 **/
/**
* 创建连接工厂
**/
@Bean @Bean
public MqttPahoClientFactory mqttPahoClientFactory(){ public MqttPahoClientFactory mqttPahoClientFactory() {
DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory(); DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
MqttConnectOptions options = new MqttConnectOptions(); MqttConnectOptions options = new MqttConnectOptions();
options.setCleanSession(true); //设置新会话 options.setCleanSession(true); // 设置新会话
options.setUserName(mqttPropertiesConfig.getUsername());
options.setPassword(mqttPropertiesConfig.getPassword().toCharArray()); // 修复用户名为null时的空指针异常
options.setServerURIs(new String[]{mqttPropertiesConfig.getUrl()}); 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.setAutomaticReconnect(true); // 启用自动重连
options.setConnectionTimeout(10); // 设置连接超时时间 options.setConnectionTimeout(10); // 设置连接超时时间
options.setKeepAliveInterval(60); // 设置心跳间隔 options.setKeepAliveInterval(60); // 设置心跳间隔

View File

@ -39,8 +39,14 @@ public class MqttInboundConfiguration {
public MessageProducer messageProducer(){ public MessageProducer messageProducer(){
// 生成一个不重复的随机数 // 生成一个不重复的随机数
String clientId = mqttPropertiesConfig.getSubClientId() + "_" + UUID.fastUUID(); String clientId = mqttPropertiesConfig.getSubClientId() + "_" + UUID.fastUUID();
// 修复URL为null时的空指针异常
String url = mqttPropertiesConfig.getUrl();
if (url == null) {
throw new IllegalStateException("MQTT服务器URL未配置");
}
MqttPahoMessageDrivenChannelAdapter mqttPahoMessageDrivenChannelAdapter = new MqttPahoMessageDrivenChannelAdapter( MqttPahoMessageDrivenChannelAdapter mqttPahoMessageDrivenChannelAdapter = new MqttPahoMessageDrivenChannelAdapter(
mqttPropertiesConfig.getUrl(), url,
clientId, clientId,
mqttPahoClientFactory, mqttPahoClientFactory,
mqttPropertiesConfig.getSubTopic().split(",") mqttPropertiesConfig.getSubTopic().split(",")

View File

@ -32,8 +32,14 @@ public class MqttOutboundConfiguration {
@ServiceActivator(inputChannel = "mqttOutboundChannel") // 指定处理器针对哪个通道的消息进行处理 @ServiceActivator(inputChannel = "mqttOutboundChannel") // 指定处理器针对哪个通道的消息进行处理
public MessageHandler mqttOutboundMessageHandler(){ public MessageHandler mqttOutboundMessageHandler(){
String clientId = mqttPropertiesConfig.getPubClientId() + "_" + UUID.fastUUID(); String clientId = mqttPropertiesConfig.getPubClientId() + "_" + UUID.fastUUID();
// 修复URL为null时的空指针异常
String url = mqttPropertiesConfig.getUrl();
if (url == null) {
throw new IllegalStateException("MQTT服务器URL未配置");
}
MqttPahoMessageHandler mqttPahoMessageHandler = new MqttPahoMessageHandler( MqttPahoMessageHandler mqttPahoMessageHandler = new MqttPahoMessageHandler(
mqttPropertiesConfig.getUrl(), url,
clientId, clientId,
mqttPahoClientFactory mqttPahoClientFactory
); );

View File

@ -59,6 +59,7 @@ public class DeviceFenceAccessRecordController extends BaseController {
ExcelUtil.exportExcel(list, "围栏进出记录", DeviceFenceAccessRecordVo.class, response); ExcelUtil.exportExcel(list, "围栏进出记录", DeviceFenceAccessRecordVo.class, response);
} }
/** /**
* 获取围栏进出记录详细信息 * 获取围栏进出记录详细信息
* *

View File

@ -103,6 +103,7 @@ public class DeviceGeoFenceController extends BaseController {
return toAjax(deviceGeoFenceService.updateByBo(bo)); return toAjax(deviceGeoFenceService.updateByBo(bo));
} }
/** /**
* 删除电子围栏 * 删除电子围栏
* *
@ -130,6 +131,7 @@ public class DeviceGeoFenceController extends BaseController {
return ResponseEntity.ok(response); return ResponseEntity.ok(response);
} }
/** /**
* 添加电子围栏终端 * 添加电子围栏终端
* *

View File

@ -39,8 +39,8 @@ public class EncryptUtilsTest {
loginBody.setClientId("e5cd7e4891bf95d1d19206ce24a7b32e"); loginBody.setClientId("e5cd7e4891bf95d1d19206ce24a7b32e");
loginBody.setGrantType("password"); loginBody.setGrantType("password");
loginBody.setTenantId("894078"); loginBody.setTenantId("894078");
loginBody.setCode("0"); loginBody.setCode("15");
loginBody.setUuid("1d6615668c7f410da77c4e002c601073"); loginBody.setUuid("28ecf3d396ce4e6db8eb414992235fad");
// loginBody.setUsername("admin"); // loginBody.setUsername("admin");
// loginBody.setPassword("admin123"); // loginBody.setPassword("admin123");
loginBody.setUsername("dyf"); loginBody.setUsername("dyf");

View File

@ -2,6 +2,7 @@ package com.fuyuanshen.equipment.domain.vo;
import java.util.Date; import java.util.Date;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated; import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty; import cn.idev.excel.annotation.ExcelProperty;
@ -60,6 +61,13 @@ public class DeviceFenceAccessRecordVo implements Serializable {
@ExcelProperty(value = "设备名称") @ExcelProperty(value = "设备名称")
private String deviceName; private String deviceName;
/**
* 事件时间
*/
@ExcelProperty(value = "事件时间")
@ColumnWidth(120)
private String eventTime;
/** /**
* 用户ID * 用户ID
*/ */
@ -76,27 +84,21 @@ public class DeviceFenceAccessRecordVo implements Serializable {
/** /**
* 纬度 * 纬度
*/ */
@ExcelProperty(value = "纬度") // @ExcelProperty(value = "纬度")
private Double latitude; private Double latitude;
/** /**
* 经度 * 经度
*/ */
@ExcelProperty(value = "经度") // @ExcelProperty(value = "经度")
private Double longitude; private Double longitude;
/** /**
* 定位精度 * 定位精度
*/ */
@ExcelProperty(value = "定位精度") // @ExcelProperty(value = "定位精度")
private Long accuracy; private Long accuracy;
/**
* 事件时间
*/
@ExcelProperty(value = "事件时间")
private Date eventTime;
/** /**
* 事件地址 * 事件地址
*/ */
@ -109,5 +111,4 @@ public class DeviceFenceAccessRecordVo implements Serializable {
@ExcelProperty(value = "记录创建时间") @ExcelProperty(value = "记录创建时间")
private Date createTime; private Date createTime;
} }

View File

@ -28,17 +28,19 @@ public interface DeviceFenceAccessRecordMapper extends BaseMapperPlus<DeviceFenc
*/ */
Page<DeviceFenceAccessRecordVo> selectVoPageWithFenceAndDeviceName(Page<DeviceFenceAccessRecord> page, @Param(Constants.WRAPPER) Wrapper<DeviceFenceAccessRecord> wrapper); Page<DeviceFenceAccessRecordVo> selectVoPageWithFenceAndDeviceName(Page<DeviceFenceAccessRecord> page, @Param(Constants.WRAPPER) Wrapper<DeviceFenceAccessRecord> wrapper);
List<DeviceFenceAccessRecordVo> selectVoPageWithFenceAndDeviceName(@Param(Constants.WRAPPER) Wrapper<DeviceFenceAccessRecord> wrapper,@Param("fenceName") String fenceName); List<DeviceFenceAccessRecordVo> selectVoPageWithFenceAndDeviceName(@Param(Constants.WRAPPER) Wrapper<DeviceFenceAccessRecord> wrapper, @Param("fenceName") String fenceName);
/** /**
* 分页查询围栏进出记录列表纯XML形式 * 分页查询围栏进出记录列表纯XML形式
* *
* @param page 分页参数 * @param page 分页参数
* @param bo 查询条件 * @param bo 查询条件
* @return 围栏进出记录分页列表 * @return 围栏进出记录分页列表
*/ */
Page<DeviceFenceAccessRecordVo> selectVoPageByXml(Page<DeviceFenceAccessRecord> page, @Param("bo") DeviceFenceAccessRecordBo bo); Page<DeviceFenceAccessRecordVo> selectVoPageByXml(Page<DeviceFenceAccessRecord> page, @Param("bo") DeviceFenceAccessRecordBo bo);
List<DeviceFenceAccessRecordVo> selectVoPageByXml(@Param("bo") DeviceFenceAccessRecordBo bo);
/** /**
* 查询设备最新的围栏记录 * 查询设备最新的围栏记录

View File

@ -33,6 +33,7 @@ public class DeviceFenceAccessRecordServiceImpl implements IDeviceFenceAccessRec
private final DeviceFenceAccessRecordMapper baseMapper; private final DeviceFenceAccessRecordMapper baseMapper;
/** /**
* 查询围栏进出记录 * 查询围栏进出记录
* *
@ -68,8 +69,8 @@ public class DeviceFenceAccessRecordServiceImpl implements IDeviceFenceAccessRec
*/ */
@Override @Override
public List<DeviceFenceAccessRecordVo> queryList(DeviceFenceAccessRecordBo bo) { public List<DeviceFenceAccessRecordVo> queryList(DeviceFenceAccessRecordBo bo) {
LambdaQueryWrapper<DeviceFenceAccessRecord> lqw = buildQueryWrapper(bo); // LambdaQueryWrapper<DeviceFenceAccessRecord> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoPageWithFenceAndDeviceName(lqw, bo.getFenceName()); return baseMapper.selectVoPageByXml(bo);
} }

View File

@ -275,6 +275,7 @@ public class DeviceGeoFenceServiceImpl extends ServiceImpl<DeviceGeoFenceMapper
return response; return response;
} }
@Override @Override
public Boolean addFenceTerminal(FenceTerminalBo bo) { public Boolean addFenceTerminal(FenceTerminalBo bo) {
// 新增围栏与终端关联信息 // 新增围栏与终端关联信息

View File

@ -30,27 +30,45 @@ import static cn.dev33.satoken.SaManager.log;
@Component @Component
public class AlibabaTTSUtil { public class AlibabaTTSUtil {
// ========== 常量配置 ========== // ========== 常量配置 ==========
/** 阿里云TTS服务基础URL */ /**
* 阿里云TTS服务基础URL
*/
private static final String BASE_URL = "https://nls-gateway-cn-shanghai.aliyuncs.com/stream/v1/tts"; private static final String BASE_URL = "https://nls-gateway-cn-shanghai.aliyuncs.com/stream/v1/tts";
/** 音频内容类型标识 */ /**
* 音频内容类型标识
*/
private static final String CONTENT_TYPE_AUDIO = "audio/mpeg"; private static final String CONTENT_TYPE_AUDIO = "audio/mpeg";
// ========== 默认参数值 ========== // ========== 默认参数值 ==========
/** 默认发音人 - 小云 */ /**
* 默认发音人 - 小云
*/
private static final String DEFAULT_VOICE = "xiaoyun"; private static final String DEFAULT_VOICE = "xiaoyun";
/** 默认音量 50% */ /**
* 默认音量 50%
*/
private static final int DEFAULT_VOLUME = 50; private static final int DEFAULT_VOLUME = 50;
/** 默认语速 0正常 */ /**
* 默认语速 0正常
*/
private static final int DEFAULT_SPEECH_RATE = 1; private static final int DEFAULT_SPEECH_RATE = 1;
/** 默认语调 0正常 */ /**
* 默认语调 0正常
*/
private static final int DEFAULT_PITCH_RATE = 0; private static final int DEFAULT_PITCH_RATE = 0;
/** 默认音频格式 pcm */ /**
* 默认音频格式 pcm
*/
private static final String DEFAULT_FORMAT = "pcm"; private static final String DEFAULT_FORMAT = "pcm";
/** 默认采样率 16000Hz */ /**
* 默认采样率 16000Hz
*/
private static final int DEFAULT_SAMPLE_RATE = 16000; private static final int DEFAULT_SAMPLE_RATE = 16000;
// ========== Token管理配置 ========== // ========== Token管理配置 ==========
/** Token刷新缓冲时间提前5分钟刷新单位毫秒 */ /**
* Token刷新缓冲时间提前5分钟刷新单位毫秒
*/
private static final long TOKEN_REFRESH_BUFFER = 5 * 60 * 1000L; private static final long TOKEN_REFRESH_BUFFER = 5 * 60 * 1000L;
// ========== 配置参数(从配置文件读取) ========== // ========== 配置参数(从配置文件读取) ==========
@ -70,7 +88,8 @@ public class AlibabaTTSUtil {
/** /**
* 生成语音文件 - 简化版(使用默认参数) * 生成语音文件 - 简化版(使用默认参数)
* @param text 要转换的文本内容 *
* @param text 要转换的文本内容
* @param audioSaveFile 音频文件保存路径 * @param audioSaveFile 音频文件保存路径
* @return true-成功 false-失败 * @return true-成功 false-失败
*/ */
@ -80,11 +99,12 @@ public class AlibabaTTSUtil {
/** /**
* 生成语音文件 - 标准版 * 生成语音文件 - 标准版
* @param text 要转换的文本内容 *
* @param text 要转换的文本内容
* @param audioSaveFile 音频文件保存路径 * @param audioSaveFile 音频文件保存路径
* @param format 音频格式mp3, wav等 * @param format 音频格式mp3, wav等
* @param sampleRate 采样率16000, 22050, 44100等 * @param sampleRate 采样率16000, 22050, 44100等
* @param voice 发音人xiaoyun, xiaoqian等 * @param voice 发音人xiaoyun, xiaoqian等
* @return true-成功 false-失败 * @return true-成功 false-失败
*/ */
public boolean generateSpeech(String text, String audioSaveFile, String format, public boolean generateSpeech(String text, String audioSaveFile, String format,
@ -95,14 +115,15 @@ public class AlibabaTTSUtil {
/** /**
* 生成语音文件 - 完整版(支持所有参数调节) * 生成语音文件 - 完整版(支持所有参数调节)
* @param text 要转换的文本内容 *
* @param text 要转换的文本内容
* @param audioSaveFile 音频文件保存路径 * @param audioSaveFile 音频文件保存路径
* @param format 音频格式 * @param format 音频格式
* @param sampleRate 采样率 * @param sampleRate 采样率
* @param voice 发音人 * @param voice 发音人
* @param volume 音量0-100 * @param volume 音量0-100
* @param speechRate 语速(-500~500 * @param speechRate 语速(-500~500
* @param pitchRate 语调(-500~500 * @param pitchRate 语调(-500~500
* @return true-成功 false-失败 * @return true-成功 false-失败
*/ */
public boolean generateSpeech(String text, String audioSaveFile, String format, public boolean generateSpeech(String text, String audioSaveFile, String format,
@ -158,6 +179,7 @@ public class AlibabaTTSUtil {
/** /**
* 获取有效的访问令牌(优先从缓存获取,缓存不存在则重新生成) * 获取有效的访问令牌(优先从缓存获取,缓存不存在则重新生成)
*
* @return 访问令牌获取失败返回null * @return 访问令牌获取失败返回null
*/ */
private String getValidAccessToken() { private String getValidAccessToken() {
@ -181,6 +203,7 @@ public class AlibabaTTSUtil {
/** /**
* 刷新访问令牌调用阿里云API获取新令牌并缓存 * 刷新访问令牌调用阿里云API获取新令牌并缓存
*
* @return 新的访问令牌获取失败返回null * @return 新的访问令牌获取失败返回null
*/ */
private String refreshAccessToken() { private String refreshAccessToken() {
@ -202,6 +225,7 @@ public class AlibabaTTSUtil {
/** /**
* 参数验证 * 参数验证
*
* @throws IllegalArgumentException 参数不合法时抛出异常 * @throws IllegalArgumentException 参数不合法时抛出异常
*/ */
private void validateParameters(String text, String audioSaveFile, String format, int sampleRate) { private void validateParameters(String text, String audioSaveFile, String format, int sampleRate) {
@ -248,10 +272,10 @@ public class AlibabaTTSUtil {
"&text=" + encodedText + "&text=" + encodedText +
"&format=" + format + "&format=" + format +
"&sample_rate=" + sampleRate; "&sample_rate=" + sampleRate;
//"&voice=" + actualVoice + //"&voice=" + actualVoice +
//"&volume=" + Math.max(0, Math.min(100, volume)) + // 音量范围限制 //"&volume=" + Math.max(0, Math.min(100, volume)) + // 音量范围限制
//"&speech_rate=" + Math.max(-500, Math.min(500, speechRate)) + // 语速范围限制 //"&speech_rate=" + Math.max(-500, Math.min(500, speechRate)) + // 语速范围限制
//"&pitch_rate=" + Math.max(-500, Math.min(500, pitchRate)); // 语调范围限制 //"&pitch_rate=" + Math.max(-500, Math.min(500, pitchRate)); // 语调范围限制
} catch (UnsupportedEncodingException e) { } catch (UnsupportedEncodingException e) {
throw new RuntimeException("UTF-8编码不支持", e); throw new RuntimeException("UTF-8编码不支持", e);