优化录音片段

This commit is contained in:
zhonghua1
2026-01-01 18:00:47 +08:00
parent fa6f4b0a47
commit 7db8035160
3 changed files with 235 additions and 75 deletions

View File

@@ -129,3 +129,6 @@ hikari:

View File

@@ -1,6 +1,7 @@
package com.rj.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.rj.common.AudioManagementConstants;
@@ -337,7 +338,7 @@ public class AudioFileController {
// 关联上传的文件到audio_management表
associateAudioManagement(requestBody);
// 处理Audio数据 验证文件、保存文件到本地、保存信息到数据库
// 处理Audio数据 验证文件、保存文件到本地、保存信息到数据库, 计算录音 总时长 ,标记为 服务结束
Map<String, Object> processResult = handleAudioDataType(requestBody, audioFile);
log.info("检测到数据类型Audio-----------处理结束-------------------");
@@ -1180,25 +1181,28 @@ public class AudioFileController {
// 8. 检查数据库中是否已存在相同的分段记录
if (isSegmentExists(segment)) {
log.info("音频分段记录已存在跳过保存。父录音ID: {}, chunkIndex: {}, deviceNo: {}, startTime: {}",
segment.getParentId(), segment.getChunkIndex(), segment.getDeviceNo(), segment.getStartTime());
log.info("音频分段记录已存在跳过保存。父录音ID: {}, chunkIndex: {}, deviceNo: {}, filename: {}",
segment.getParentId(), segment.getChunkIndex(), segment.getDeviceNo(), segment.getAudioFileOriginalName());
// 标记为已保存,避免后续重复检查
requestBody.put(segmentSavedKey, true);
return;
}
// 9. 从AudioManagement中获取录音名称如果存在父录音
// 9. 在保存之前就设置标记,防止并发保存
requestBody.put(segmentSavedKey, true);
// 10. 从AudioManagement中获取录音名称如果存在父录音
if (segment.getParentId() != null) {
populateSegmentFromAudioManagement(segment, segment.getParentId());
// populateSegmentFromAudioManagement(segment, segment.getParentId());
}
// 10. 设置通用字段
// 11. 设置通用字段
LocalDateTime now = TimeZoneUtils.now();
segment.setCreateTime(now);
segment.setUploadTime(now);
segment.setRecordingTime(now);
// 11. 设置默认状态
// 12. 设置默认状态
if (segment.getUploadStatus() == null) {
segment.setUploadStatus("已上传");
}
@@ -1209,29 +1213,68 @@ public class AudioFileController {
segment.setIsMerged(false);
}
// 12. 保存到数据库
// 13. 保存到数据库
audioManagementSegmentsService.save(segment);
log.info("音频分段记录保存成功ID: {}, 父录音ID: {}, 设备号: {}, 文件名: {}",
segment.getId(), segment.getParentId(), segment.getDeviceNo(), segment.getAudioFileOriginalName());
// 如果文件名包含结束标志,修改主记录为结束状态
if(segment.getAudioFileOriginalName().contains("-Z")){
LambdaQueryWrapper<AudioManagementSegments> audioQuery = new LambdaQueryWrapper<>();
audioQuery.eq(AudioManagementSegments::getParentId, deviceManagement.getId()) ; // 按创建时间倒序
List<AudioManagementSegments> segmentsList = audioManagementSegmentsService.list(audioQuery);
BigDecimal total = new BigDecimal(0);
segmentsList.forEach(segment2 ->{
total.add(segment2.getDuration());//分钟
});
// 14. 更新AudioManagement记录需要设置租户上下文
if (segment.getParentId() != null) {
// 保存原来的租户ID用于后续恢复
String originalTenantId = TenantContextHolder.getTenantId();
String tenantId = segment.getTenantId();
// 如果segment中没有tenantId尝试从DeviceManagement中获取
if (tenantId == null || tenantId.trim().isEmpty()) {
// deviceManagement 已在方法前面定义第1163行
if (deviceManagement != null && deviceManagement.getTenantId() != null) {
tenantId = deviceManagement.getTenantId();
}
}
try {
// 设置租户ID到上下文使后续查询和更新自动使用该租户ID
if (tenantId != null && !tenantId.trim().isEmpty()) {
TenantContextHolder.setTenantId(tenantId);
log.info("设置租户ID到上下文: {}", tenantId);
}
AudioManagement audioManagement = new AudioManagement();
audioManagement.setDuration(total);
audioManagement.setServiceStatus(AudioManagementConstants.SERVICE_STATUS_IN_SERVICE);
audioManagement.setUpdateTime(LocalDateTime.now());
serviceManager.getAudioManagementService().save(audioManagement);
audioManagement.setId(segment.getParentId());
// 如果文件名包含结束标志,修改主记录为结束状态
if(segment.getAudioFileOriginalName().contains("-Z")){
audioManagement.setServiceStatus(AudioManagementConstants.SERVICE_STATUS_SERVICE_FINISH);
}
// 13. 标记为已保存,避免重复保存
requestBody.put(segmentSavedKey, true);
AudioManagement byId = serviceManager.getAudioManagementService().getById(segment.getParentId());
if (byId != null) {
// 累加时长
BigDecimal currentDuration = byId.getDuration();
if (currentDuration == null) {
currentDuration = BigDecimal.ZERO;
}
BigDecimal segmentDuration = segment.getDuration();
if (segmentDuration == null) {
segmentDuration = BigDecimal.ZERO;
}
audioManagement.setDuration(currentDuration.add(segmentDuration));
audioManagement.setUpdateTime(LocalDateTime.now());
serviceManager.getAudioManagementService().updateById(audioManagement);
log.info("成功更新AudioManagement记录ID: {}, 累计时长: {}", segment.getParentId(), audioManagement.getDuration());
} else {
log.warn("未找到AudioManagement记录ID: {}", segment.getParentId());
}
} finally {
// 恢复原来的租户ID
if (originalTenantId != null) {
TenantContextHolder.setTenantId(originalTenantId);
} else {
TenantContextHolder.clear();
}
}
}
// 标记已在保存前设置第1190行这里不需要再次设置
} catch (Exception e) {
log.error("保存音频分段记录到audio_management_segments表失败", e);
@@ -1241,19 +1284,41 @@ public class AudioFileController {
/**
* 检查音频分段记录是否已存在
* 判断标准parent_id + chunk_index + deviceNo + start_time如果都存在
* 判断标准:优先使用 audio_file_original_name备用parent_id + chunk_index + deviceNo + start_time如果都存在
*
* @param segment 音频分段对象
* @return true表示已存在false表示不存在
*/
private boolean isSegmentExists(AudioManagementSegments segment) {
// 保存原来的租户ID用于后续恢复
String originalTenantId = TenantContextHolder.getTenantId();
String tenantId = segment.getTenantId();
try {
// 设置租户ID到上下文使后续查询自动使用该租户ID
if (tenantId != null && !tenantId.trim().isEmpty()) {
TenantContextHolder.setTenantId(tenantId);
log.debug("设置租户ID到上下文用于检查重复记录: {}", tenantId);
}
// 优先使用 audio_file_original_name 作为主要检查条件(最可靠)
if (segment.getAudioFileOriginalName() != null && !segment.getAudioFileOriginalName().isEmpty()) {
LambdaQueryWrapper<AudioManagementSegments> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(AudioManagementSegments::getAudioFileOriginalName, segment.getAudioFileOriginalName());
long count = audioManagementSegmentsService.count(queryWrapper);
if (count > 0) {
return true;
}
}
// 如果缺少关键字段无法判断返回false允许保存
if (segment.getParentId() == null || segment.getParentId().isEmpty()) {
return false;
}
// 构建查询条件
// 构建查询条件(备用检查:使用其他字段组合)
LambdaQueryWrapper<AudioManagementSegments> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(AudioManagementSegments::getParentId, segment.getParentId());
@@ -1288,6 +1353,13 @@ public class AudioFileController {
log.warn("检查音频分段记录是否存在时发生异常,将允许保存: {}", e.getMessage());
// 如果检查失败,允许保存(避免因为检查逻辑问题导致数据丢失)
return false;
} finally {
// 恢复原来的租户ID
if (originalTenantId != null) {
TenantContextHolder.setTenantId(originalTenantId);
} else {
TenantContextHolder.clear();
}
}
}
@@ -1487,47 +1559,6 @@ public class AudioFileController {
}
}
/**
* 从AudioManagement中获取录音名称等信息
*
* @param segment 音频分段对象
* @param parentId 父录音ID
*/
private void populateSegmentFromAudioManagement(AudioManagementSegments segment, String parentId) {
try {
AudioManagement audioManagement = serviceManager.getAudioManagementService().getById(parentId);
if (audioManagement != null) {
// 如果录音名称为空,使用父录音的名称
if (segment.getRecordingName() == null || segment.getRecordingName().isEmpty()) {
segment.setRecordingName(audioManagement.getRecordingName());
}
// 如果销售信息为空,使用父录音的销售信息
if (segment.getSalesPhone() == null || segment.getSalesPhone().isEmpty()) {
segment.setSalesPhone(audioManagement.getSalesPhone());
}
if (segment.getSalesName() == null || segment.getSalesName().isEmpty()) {
segment.setSalesName(audioManagement.getSalesName());
}
// 如果经销商信息为空,使用父录音的经销商信息
if (segment.getDealershipId() == null || segment.getDealershipId().isEmpty()) {
segment.setDealershipId(audioManagement.getDealershipId());
}
if (segment.getDealershipName() == null || segment.getDealershipName().isEmpty()) {
segment.setDealershipName(audioManagement.getDealershipName());
}
// 如果租户ID为空使用父录音的租户ID
if (segment.getTenantId() == null || segment.getTenantId().isEmpty()) {
segment.setTenantId(audioManagement.getTenantId());
}
}
} catch (Exception e) {
log.warn("从AudioManagement获取信息失败parentId: {}", parentId, e);
// 不影响主流程,只记录日志
}
}
/**
* 提取请求头信息
@@ -2064,13 +2095,139 @@ public class AudioFileController {
/**
* 处理AudioText数据类型语音转写
* 根据deviceNo和filename查询并更新audio_management_segments表的recording_text字段
* recording_text的值等于所有segments中text字段的累加
*/
private Map<String, Object> handleAudioTextDataType(Map<String, Object> requestBody) {
log.info("开始处理AudioText数据类型语音转写");
try {
// 1. 提取data字段
Map<String, Object> dataMap = extractDataField(requestBody);
if (dataMap == null || dataMap.isEmpty()) {
log.warn("AudioText数据中data字段为空或不存在");
return createErrorResult("AudioText数据中data字段为空或不存在");
}
// 2. 提取deviceNo和filename
String deviceNo = extractStringFieldWithFallback(requestBody, dataMap, "deviceNo");
String filename = extractStringField(dataMap, "filename");
if (deviceNo == null || deviceNo.trim().isEmpty()) {
log.warn("AudioText数据中deviceNo字段为空");
return createErrorResult("AudioText数据中deviceNo字段为空");
}
if (filename == null || filename.trim().isEmpty()) {
log.warn("AudioText数据中filename字段为空");
return createErrorResult("AudioText数据中filename字段为空");
}
log.info("处理语音转写数据deviceNo: {}, filename: {}", deviceNo, filename);
// 3. 提取segments数组
Object segmentsObj = dataMap.get("segments");
if (segmentsObj == null) {
log.warn("AudioText数据中segments字段为空");
return createErrorResult("AudioText数据中segments字段为空");
}
// 4. 解析segments并累加text字段
List<Map<String, Object>> segmentsList;
try {
segmentsList = objectMapper.convertValue(segmentsObj,
new TypeReference<List<Map<String, Object>>>() {});
} catch (Exception e) {
log.error("解析segments字段失败", e);
return createErrorResult("解析segments字段失败: " + e.getMessage());
}
if (segmentsList == null || segmentsList.isEmpty()) {
log.warn("AudioText数据中segments数组为空");
return createErrorResult("AudioText数据中segments数组为空");
}
// 5. 累加所有segments中的text字段
StringBuilder recordingTextBuilder = new StringBuilder();
for (Map<String, Object> segment : segmentsList) {
Object textObj = segment.get("text");
if (textObj != null) {
String text = textObj.toString().trim();
if (!text.isEmpty()) {
if (recordingTextBuilder.length() > 0) {
recordingTextBuilder.append(" ");
}
recordingTextBuilder.append(text);
}
}
}
String recordingText = recordingTextBuilder.toString();
log.info("累加的录音文本长度: {} 字符", recordingText.length());
// 6. 获取DeviceManagement和tenantId参考插入时的处理逻辑
DeviceManagement deviceManagement = (DeviceManagement) requestBody.get(deviceKey);
// 如果requestBody中没有DeviceManagement则根据deviceNo查询
if (deviceManagement == null) {
try {
deviceManagement = deviceManagementMapper.selectByDeviceCodeIgnoreTenant(deviceNo);
if (deviceManagement != null) {
requestBody.put(deviceKey, deviceManagement);
log.info("根据设备编号 {} 查询到DeviceManagement", deviceNo);
}
} catch (Exception e) {
log.warn("查询DeviceManagement失败设备编号: {}", deviceNo, e);
}
}
// 7. 设置租户ID到上下文使后续查询自动使用该租户ID参考associateAudioManagement的处理逻辑
String originalTenantId = TenantContextHolder.getTenantId();
String tenantId = null;
if (deviceManagement != null && deviceManagement.getTenantId() != null) {
tenantId = deviceManagement.getTenantId();
TenantContextHolder.setTenantId(tenantId);
log.info("设置租户ID到上下文: {}", tenantId);
} else {
log.warn("deviceManagement为空或tenantId为空无法设置租户上下文");
}
try {
// 8. 根据filename直接更新audio_management_segments表的recording_text字段
// 注意不手动添加tenant_id条件让多租户拦截器自动添加
AudioManagementSegments updateEntity = new AudioManagementSegments();
updateEntity.setRecordingText(recordingText);
LambdaUpdateWrapper<AudioManagementSegments> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(AudioManagementSegments::getAudioFileOriginalName, filename);
boolean updateResult = audioManagementSegmentsService.update(updateEntity, updateWrapper);
if (updateResult) {
log.info("成功更新录音文本deviceNo: {}, filename: {}", deviceNo, filename);
Map<String, Object> result = new HashMap<>();
log.info("语音转写处理功能待实现,数据: {}", requestBody);
result.put("success", true);
result.put("message", "语音转写接收成功(处理功能待实现)");
result.put("message", "语音转写数据更新成功");
result.put("recordingTextLength", recordingText.length());
return result;
} else {
log.warn("更新录音文本失败deviceNo: {}, filename: {}", deviceNo, filename);
return createErrorResult("更新录音文本失败,可能未找到匹配的记录");
}
} finally {
// 恢复原来的租户ID
if (originalTenantId != null) {
TenantContextHolder.setTenantId(originalTenantId);
} else {
TenantContextHolder.clear();
}
}
} catch (Exception e) {
log.error("处理AudioText数据类型异常", e);
return createErrorResult("处理AudioText数据类型异常: " + e.getMessage());
}
}
/**

View File

@@ -21,7 +21,7 @@ CREATE TABLE `yhy_datatype_log` (
`id` char(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '主键UUID',
`data_type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '数据类型固定值HeartbeatLog',
`device_no` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '设备号',
`contents` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '内容',
`contents` MEDIUMTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '内容',
`create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '系统创建时间',
`update_time` datetime NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '系统更新时间',
PRIMARY KEY (`id`) USING BTREE,