到ai智能工牌的录音后上传到minio并保存数据库的逻辑

This commit is contained in:
spllzh
2025-11-02 21:56:45 +08:00
parent b462cce2b0
commit a0c354bac0
24 changed files with 395 additions and 6 deletions

View File

@@ -197,6 +197,8 @@ public class PasswordUtil {

View File

@@ -181,6 +181,8 @@ public class ServiceManager {

View File

@@ -68,10 +68,19 @@ public class SoundRecordingUploadController {
log.info("收到录音上传请求 - deviceNo: {}, fileName: {}, chunkIndex: {}, startTime: {}, endTime: {}",
deviceNo, fileName, chunkIndex, startTime, endTime);
// 记录业务层处理开始时间
long startTimeMillis = System.currentTimeMillis();
// 调用服务层处理业务逻辑
SoundRecordingUploadServiceImpl.ResponseData serviceResponse = soundRecordingUploadService.uploadRecording(
deviceNo, deviceType, soundRecording, fileName, chunkIndex, startTime, endTime, usrNo);
// 记录业务层处理结束时间并计算耗时
long endTimeMillis = System.currentTimeMillis();
long elapsedTime = endTimeMillis - startTimeMillis;
double elapsedTimeSeconds = elapsedTime / 1000.0;
log.info("业务层处理完成 - deviceNo: {}, 耗时: {} 秒", deviceNo, String.format("%.2f", elapsedTimeSeconds));
// 转换服务层返回的结果为响应对象
SoundRecordingUploadResponse response = new SoundRecordingUploadResponse();
response.setCode(serviceResponse.code);

View File

@@ -419,6 +419,8 @@ public class MenuController {

View File

@@ -389,6 +389,8 @@ public class RoleController {

View File

@@ -395,6 +395,8 @@ public class UserRoleController {

View File

@@ -161,6 +161,8 @@ public class DifyWorkflowResponseDto {

View File

@@ -104,6 +104,8 @@ public interface CustomerProfileAnalysisMapper extends BaseMapper<CustomerProfil

View File

@@ -125,6 +125,8 @@ public interface ICustomerProfileAnalysisService extends IService<CustomerProfil

View File

@@ -160,6 +160,8 @@ public class CustomerProfileAnalysisServiceImpl extends ServiceImpl<CustomerProf

View File

@@ -0,0 +1,69 @@
package com.rj.service.impl;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@Service
public class CosyVoiceServiceImpl {
// Xinference集群地址和模型ID替换为你的实际信息
private static final String XINFERENCE_URL = "http://192.168.1.39:9997/v1/audio/speech";
private static final String MODEL_UID = "cosyvoice2-0.5b"; // 模型部署时的UID或名称
private final RestTemplate restTemplate;
// 注入RestTemplate
public CosyVoiceServiceImpl(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
/**
* 调用CosyVoice2生成语音
* @param text 待转换的文本
* @param outputPath 音频保存路径(如"output.wav"
* @return 是否成功
*/
public boolean generateSpeech(String text, String outputPath) {
try {
// 1. 构造请求体
Map<String, Object> requestBody = new HashMap<>();
requestBody.put("model", MODEL_UID); // 模型ID
requestBody.put("input", text); // 输入文本
// 可选参数:调整语音风格、语速等(根据模型支持的参数添加)
requestBody.put("voice", "default"); // 语音风格(默认值)
requestBody.put("speed", 1.0); // 语速1.0为正常)
// 2. 设置请求头JSON格式
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
// 3. 发送POST请求
HttpEntity<Map<String, Object>> request = new HttpEntity<>(requestBody, headers);
ResponseEntity<byte[]> response = restTemplate.postForEntity(
XINFERENCE_URL,
request,
byte[].class
);
// 4. 处理响应(保存音频文件)
if (response.getStatusCode() == HttpStatus.OK) {
byte[] audioData = response.getBody();
if (audioData != null) {
try (FileOutputStream fos = new FileOutputStream(new File(outputPath))) {
fos.write(audioData);
return true;
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
}

View File

@@ -1,11 +1,20 @@
package com.rj.service.impl;
import com.rj.entity.AudioManagement;
import com.rj.service.IAudioManagementService;
import com.rj.service.ISoundRecordingUploadService;
import com.rj.service.MinIOService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import java.math.BigDecimal;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.UUID;
import java.util.regex.Pattern;
/**
@@ -18,6 +27,12 @@ import java.util.regex.Pattern;
@Service
public class SoundRecordingUploadServiceImpl implements ISoundRecordingUploadService {
@Autowired
private MinIOService minIOService;
@Autowired
private IAudioManagementService audioManagementService;
/**
* 分片索引正则表达式
* A开头启动/运行段如A001, A005
@@ -50,13 +65,60 @@ public class SoundRecordingUploadServiceImpl implements ISoundRecordingUploadSer
log.info("分片信息解析 - prefix: {}, chunkNumber: {}, isLastChunk: {}, totalChunks: {}",
chunkInfo.prefix, chunkInfo.chunkNumber, chunkInfo.isLastChunk, chunkInfo.totalChunks);
// TODO: 这里可以添加文件保存逻辑
// 1. 保存文件到指定目录
// 2. 如果是分片上传,需要管理分片状态
// 3. 如果是最后一片,需要合并分片或标记完成
// 上传文件到 MinIO
String minioUrl;
try {
minioUrl = minIOService.uploadFile(soundRecording);
log.info("文件上传到MinIO成功 - deviceNo: {}, fileName: {}, minioUrl: {}", deviceNo, fileName, minioUrl);
} catch (Exception e) {
log.error("文件上传到MinIO失败 - deviceNo: {}, fileName: {}, error: {}", deviceNo, fileName, e.getMessage(), e);
return createErrorResponse(500, 1, "文件上传失败:" + e.getMessage());
}
log.info("录音文件上传成功 - deviceNo: {}, fileName: {}, fileSize: {} bytes",
deviceNo, fileName, soundRecording.getSize());
// 转换时间格式yyMMddHHmmss -> LocalDateTime
LocalDateTime recordingStartTime = parseTimeString(startTime);
LocalDateTime recordingEndTime = parseTimeString(endTime);
// 计算录音时长(分钟)
BigDecimal duration = calculateDuration(recordingStartTime, recordingEndTime);
// 获取文件扩展名
String fileExtension = getFileExtension(fileName);
// 创建录音管理记录
AudioManagement audioManagement = new AudioManagement();
audioManagement.setId(UUID.randomUUID().toString());
audioManagement.setRecordingName(fileName);
audioManagement.setRecordingTime(recordingStartTime);
audioManagement.setDuration(duration);
audioManagement.setAudioFileUrl(minioUrl);
audioManagement.setAudioFileSize(soundRecording.getSize());
audioManagement.setAudioFileOriginalName(fileName);
audioManagement.setAudioFileExtension(fileExtension);
audioManagement.setUploadTime(LocalDateTime.now());
audioManagement.setUploadStatus("已上传");
audioManagement.setSyncStatus("未同步");
audioManagement.setIsMerged(chunkInfo.isLastChunk && chunkInfo.totalChunks > 1);
// 如果有自定义编号,可以存储到备注中
if (StringUtils.hasText(usrNo)) {
audioManagement.setRemarks("设备编号: " + usrNo + ", 设备类型: " + (deviceType != null ? deviceType : "未知"));
}
// 设置创建和更新时间
LocalDateTime now = LocalDateTime.now();
audioManagement.setCreateTime(now);
audioManagement.setUpdateTime(now);
// 保存到数据库
boolean saved = audioManagementService.save(audioManagement);
if (!saved) {
log.error("保存录音记录到数据库失败 - deviceNo: {}, fileName: {}", deviceNo, fileName);
return createErrorResponse(500, 1, "保存录音记录失败");
}
log.info("录音文件上传成功并保存到数据库 - deviceNo: {}, fileName: {}, fileSize: {} bytes, audioId: {}",
deviceNo, fileName, soundRecording.getSize(), audioManagement.getId());
// 返回成功响应
return createSuccessResponse();
@@ -152,6 +214,62 @@ public class SoundRecordingUploadServiceImpl implements ISoundRecordingUploadSer
return response;
}
/**
* 解析时间字符串yyMMddHHmmss -> LocalDateTime
*/
private LocalDateTime parseTimeString(String timeStr) {
try {
// 将 yy 转换为 yyyy假设 yy < 50 表示 20xx否则表示 19xx
int year = Integer.parseInt(timeStr.substring(0, 2));
if (year < 50) {
year += 2000;
} else {
year += 1900;
}
// 构造完整的日期时间字符串
String fullTimeStr = String.format("%04d%s", year, timeStr.substring(2));
DateTimeFormatter fullFormatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
return LocalDateTime.parse(fullTimeStr, fullFormatter);
} catch (Exception e) {
log.error("时间格式解析失败 - timeStr: {}, error: {}", timeStr, e.getMessage());
return LocalDateTime.now();
}
}
/**
* 计算录音时长(分钟)
*/
private BigDecimal calculateDuration(LocalDateTime startTime, LocalDateTime endTime) {
if (startTime == null || endTime == null) {
return BigDecimal.ZERO;
}
long seconds = java.time.Duration.between(startTime, endTime).getSeconds();
if (seconds < 0) {
log.warn("录音结束时间早于开始时间返回0");
return BigDecimal.ZERO;
}
// 转换为分钟保留2位小数
double minutes = seconds / 60.0;
return BigDecimal.valueOf(minutes).setScale(2, java.math.RoundingMode.HALF_UP);
}
/**
* 获取文件扩展名
*/
private String getFileExtension(String filename) {
if (!StringUtils.hasText(filename)) {
return "";
}
int lastDotIndex = filename.lastIndexOf('.');
if (lastDotIndex > 0 && lastDotIndex < filename.length() - 1) {
return filename.substring(lastDotIndex + 1).toLowerCase();
}
return "";
}
/**
* 分片信息内部类
*/

View File

@@ -132,6 +132,8 @@ public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements IM

View File

@@ -132,6 +132,8 @@ public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements IR

View File

@@ -132,6 +132,8 @@ public class UserRoleServiceImpl extends ServiceImpl<UserRoleMapper, UserRole> i