From 7db803516082717119132f11cc1bcab3235fc6b8 Mon Sep 17 00:00:00 2001 From: zhonghua1 Date: Thu, 1 Jan 2026 18:00:47 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=BD=95=E9=9F=B3=E7=89=87?= =?UTF-8?q?=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- HikariCP连接池问题分析.md | 3 + .../rj/controller/AudioFileController.java | 305 +++++++++++++----- src/main/sql/yhy_datatype_log.sql | 2 +- 3 files changed, 235 insertions(+), 75 deletions(-) diff --git a/HikariCP连接池问题分析.md b/HikariCP连接池问题分析.md index a01df0d..4e4c8dc 100644 --- a/HikariCP连接池问题分析.md +++ b/HikariCP连接池问题分析.md @@ -129,3 +129,6 @@ hikari: + + + diff --git a/src/main/java/com/rj/controller/AudioFileController.java b/src/main/java/com/rj/controller/AudioFileController.java index a19fb2f..1d880cf 100644 --- a/src/main/java/com/rj/controller/AudioFileController.java +++ b/src/main/java/com/rj/controller/AudioFileController.java @@ -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 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 audioQuery = new LambdaQueryWrapper<>(); - audioQuery.eq(AudioManagementSegments::getParentId, deviceManagement.getId()) ; // 按创建时间倒序 - List segmentsList = audioManagementSegmentsService.list(audioQuery); - BigDecimal total = new BigDecimal(0); - segmentsList.forEach(segment2 ->{ - total.add(segment2.getDuration());//分钟 - }); - - AudioManagement audioManagement = new AudioManagement(); - audioManagement.setDuration(total); - audioManagement.setServiceStatus(AudioManagementConstants.SERVICE_STATUS_IN_SERVICE); - audioManagement.setUpdateTime(LocalDateTime.now()); - serviceManager.getAudioManagementService().save(audioManagement); + + // 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.setId(segment.getParentId()); + + // 如果文件名包含结束标志,修改主记录为结束状态 + if(segment.getAudioFileOriginalName().contains("-Z")){ + audioManagement.setServiceStatus(AudioManagementConstants.SERVICE_STATUS_SERVICE_FINISH); + } + + 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(); + } + } } - // 13. 标记为已保存,避免重复保存 - requestBody.put(segmentSavedKey, true); + // 标记已在保存前设置(第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 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 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 handleAudioTextDataType(Map requestBody) { - Map result = new HashMap<>(); - log.info("语音转写处理功能待实现,数据: {}", requestBody); - result.put("success", true); - result.put("message", "语音转写接收成功(处理功能待实现)"); - return result; + log.info("开始处理AudioText数据类型(语音转写)"); + + try { + // 1. 提取data字段 + Map 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> segmentsList; + try { + segmentsList = objectMapper.convertValue(segmentsObj, + new TypeReference>>() {}); + } 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 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 updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(AudioManagementSegments::getAudioFileOriginalName, filename); + + boolean updateResult = audioManagementSegmentsService.update(updateEntity, updateWrapper); + + if (updateResult) { + log.info("成功更新录音文本,deviceNo: {}, filename: {}", deviceNo, filename); + Map result = new HashMap<>(); + result.put("success", true); + 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()); + } } /** diff --git a/src/main/sql/yhy_datatype_log.sql b/src/main/sql/yhy_datatype_log.sql index 9fbe790..19d0eaf 100644 --- a/src/main/sql/yhy_datatype_log.sql +++ b/src/main/sql/yhy_datatype_log.sql @@ -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,