diff --git a/src/main/java/com/rj/common/DataEnrichmentUtil.java b/src/main/java/com/rj/common/DataEnrichmentUtil.java index 34bcfa1..2666574 100644 --- a/src/main/java/com/rj/common/DataEnrichmentUtil.java +++ b/src/main/java/com/rj/common/DataEnrichmentUtil.java @@ -299,6 +299,26 @@ public class DataEnrichmentUtil { return saleNameMap; } + /** + * 批量获取销售名称映射(使用ServiceManager) + */ + public Map getSalesIdPhoneMap(Collection salesIds) { + + // 批量获取 销售信息 + Map salePhoneMap; + if (!salesIds.isEmpty()) { + LambdaQueryWrapper salesQueryWrapper = new LambdaQueryWrapper<>(); + salesQueryWrapper.in(SalesManagement::getId, salesIds); + List sales = serviceManager.getSalesManagementService().list(salesQueryWrapper); + salePhoneMap = sales.stream() + .collect(Collectors.toMap(SalesManagement::getId, SalesManagement::getLoginAccount)); + } else { + salePhoneMap = new HashMap<>(); + } + + + return salePhoneMap; + } /** * 批量获取客户名称映射(使用ServiceManager) */ diff --git a/src/main/java/com/rj/common/PasswordUtil.java b/src/main/java/com/rj/common/PasswordUtil.java index 18b2f62..83400ff 100644 --- a/src/main/java/com/rj/common/PasswordUtil.java +++ b/src/main/java/com/rj/common/PasswordUtil.java @@ -182,6 +182,13 @@ public class PasswordUtil { + + + + + + + diff --git a/src/main/java/com/rj/common/Result.java b/src/main/java/com/rj/common/Result.java index 22e6079..9967238 100644 --- a/src/main/java/com/rj/common/Result.java +++ b/src/main/java/com/rj/common/Result.java @@ -4,7 +4,7 @@ package com.rj.common; /** * 返回值的标准格式 */ -public class Result { +public class Result { /** * 状态码 */ @@ -22,29 +22,29 @@ public class Result { /** * 成功 不返回数据 */ - public static Result ok(){ - return new Result(); + public static Result ok(){ + return new Result(); } /** * 失败返回自定义消息 */ - public static Result error(String msg){ - return new Result(-1,msg); + public static Result error(String msg){ + return new Result(-1,msg); } /** * 失败返回系统异常消息 */ - public static Result error(){ - return new Result(-1,"系统异常"); + public static Result error(){ + return new Result(-1,"系统异常"); } /** * 成功返回数据 */ - public static Result ok(Object data){ - return new Result(data); + public static Result ok(Object data){ + return new Result(data); } public Result(int code, String msg) { diff --git a/src/main/java/com/rj/common/ServiceManager.java b/src/main/java/com/rj/common/ServiceManager.java index 7c941d9..f7dabbd 100644 --- a/src/main/java/com/rj/common/ServiceManager.java +++ b/src/main/java/com/rj/common/ServiceManager.java @@ -166,6 +166,13 @@ public class ServiceManager { + + + + + + + diff --git a/src/main/java/com/rj/controller/AudioManagementController.java b/src/main/java/com/rj/controller/AudioManagementController.java index 82d1150..5002a77 100644 --- a/src/main/java/com/rj/controller/AudioManagementController.java +++ b/src/main/java/com/rj/controller/AudioManagementController.java @@ -4,10 +4,12 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.rj.common.DataEnrichmentUtil; import com.rj.entity.AudioManagement; -import com.rj.entity.CustomerManagement; -import com.rj.entity.SalesManagement; import com.rj.service.IAudioManagementService; import com.rj.service.IFileUploadService; +import com.rj.service.MinIOService; +import com.rj.service.ITtsRequestLogService; +import com.rj.dto.AsrRequest; +import com.rj.dto.AsrResponse; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; @@ -15,9 +17,12 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import java.io.InputStream; import java.math.BigDecimal; import java.time.LocalDateTime; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -44,28 +49,140 @@ public class AudioManagementController { @Autowired private IFileUploadService fileUploadService; + @Autowired + private MinIOService minIOService; + + @Autowired + private ITtsRequestLogService ttsRequestLogService; + /** * 新增录音 */ - @PostMapping("/add") + @PostMapping("/uploadAndSubmit") @Operation(summary = "新增录音", description = "添加新的录音信息") public ResponseEntity> addAudio( - @Parameter(description = "录音信息", required = true) - @RequestBody AudioManagement audioManagement) { + @Parameter(description = "录音文件", required = true) + @RequestParam("file") MultipartFile file, + @Parameter(description = "录音名称", required = false) + @RequestParam(value = "recordingName", required = false) String recordingName, + @Parameter(description = "录音时间", required = false) + @RequestParam(value = "recordingTime", required = false) String recordingTime, + @Parameter(description = "所属销售ID", required = false) + @RequestParam(value = "salesId", required = false) String salesId, + @Parameter(description = "所属销售名称", required = false) + @RequestParam(value = "salesName", required = false) String salesName, + @Parameter(description = "销售人员电话", required = false) + @RequestParam(value = "salesPhone", required = false) String salesPhone, + @Parameter(description = "录音时长(分钟)", required = false) + @RequestParam(value = "duration", required = false) BigDecimal duration, + @Parameter(description = "客户ID", required = false) + @RequestParam(value = "customerId", required = false) String customerId, + @Parameter(description = "客户姓名", required = false) + @RequestParam(value = "customerName", required = false) String customerName, + @Parameter(description = "客户手机号", required = false) + @RequestParam(value = "customerPhone", required = false) String customerPhone, + @Parameter(description = "意向级别", required = false) + @RequestParam(value = "intentionLevel", required = false) String intentionLevel, + @Parameter(description = "所属门店ID", required = false) + @RequestParam(value = "dealershipId", required = false) String dealershipId, + @Parameter(description = "所属门店名称", required = false) + @RequestParam(value = "dealershipName", required = false) String dealershipName, + @Parameter(description = "所属项目ID", required = false) + @RequestParam(value = "projectId", required = false) String projectId, + @Parameter(description = "所属项目名称", required = false) + @RequestParam(value = "projectName", required = false) String projectName, + @Parameter(description = "话术模型", required = false) + @RequestParam(value = "scriptModel", required = false) String scriptModel, + @Parameter(description = "录音备注", required = false) + @RequestParam(value = "remarks", required = false) String remarks, + @Parameter(description = "录音文本内容", required = false) + @RequestParam(value = "recordingText", required = false) String recordingText) { Map result = new HashMap<>(); try { + // 验证文件 + if (file == null || file.isEmpty()) { + result.put("success", false); + result.put("message", "录音文件不能为空"); + return ResponseEntity.badRequest().body(result); + } + + // 验证文件类型 + String contentType = file.getContentType(); + if (contentType == null || (!contentType.startsWith("audio/"))) { + result.put("success", false); + result.put("message", "只支持音频文件格式"); + return ResponseEntity.badRequest().body(result); + } + + // 验证文件大小(限制为100MB) + if (file.getSize() > 100 * 1024 * 1024) { + result.put("success", false); + result.put("message", "文件大小不能超过100MB"); + return ResponseEntity.badRequest().body(result); + } + // 生成录音ID - if (audioManagement.getId() == null || audioManagement.getId().trim().isEmpty()) { - audioManagement.setId(UUID.randomUUID().toString()); + String audioId = UUID.randomUUID().toString(); + + // 先将音频文件上传到MinIO + String minioUrl; + try { + minioUrl = minIOService.uploadFile(file); + log.info("音频文件上传到MinIO成功,URL: {}", minioUrl); + } catch (Exception e) { + log.error("音频文件上传到MinIO失败: {}", e.getMessage(), e); + result.put("success", false); + result.put("message", "音频文件上传失败:" + e.getMessage()); + return ResponseEntity.internalServerError().body(result); } + // 创建录音管理对象 + AudioManagement audioManagement = new AudioManagement(); + + // 设置录音ID + audioManagement.setId(audioId); + + // 设置基本信息 + audioManagement.setRecordingName(recordingName); + if (recordingTime != null && !recordingTime.trim().isEmpty()) { + audioManagement.setRecordingTime(LocalDateTime.parse(recordingTime)); + } else { + audioManagement.setRecordingTime(LocalDateTime.now()); + } + audioManagement.setSalesId(salesId); + audioManagement.setSalesName(salesName); + audioManagement.setSalesPhone(salesPhone); + audioManagement.setDuration(duration); + audioManagement.setCustomerId(customerId); + audioManagement.setCustomerName(customerName); + audioManagement.setCustomerPhone(customerPhone); + audioManagement.setIntentionLevel(intentionLevel); + audioManagement.setDealershipId(dealershipId); + audioManagement.setDealershipName(dealershipName); + audioManagement.setProjectId(projectId); + audioManagement.setProjectName(projectName); + audioManagement.setScriptModel(scriptModel); + audioManagement.setRemarks(remarks); + audioManagement.setRecordingText(recordingText); + + // 设置文件信息(MinIO返回的URL和文件信息) + audioManagement.setAudioFileOriginalName(file.getOriginalFilename()); + audioManagement.setAudioFileSize(file.getSize()); + audioManagement.setAudioFileExtension(getFileExtension(file.getOriginalFilename())); + audioManagement.setAudioFileUrl(minioUrl); // 保存MinIO返回的URL + + // 设置时间 audioManagement.setCreateTime(LocalDateTime.now()); audioManagement.setUpdateTime(LocalDateTime.now()); + audioManagement.setUploadTime(LocalDateTime.now()); + + // 保存录音记录到数据库 boolean success = audioManagementService.save(audioManagement); if (success) { result.put("success", true); result.put("message", "录音添加成功"); result.put("data", audioManagement); + result.put("minioUrl", minioUrl); return ResponseEntity.ok(result); } else { result.put("success", false); @@ -79,6 +196,20 @@ public class AudioManagementController { } } + /** + * 获取文件扩展名 + */ + private String getFileExtension(String filename) { + if (filename == null || filename.isEmpty()) { + return ""; + } + int lastDotIndex = filename.lastIndexOf('.'); + if (lastDotIndex == -1 || lastDotIndex == filename.length() - 1) { + return ""; + } + return filename.substring(lastDotIndex + 1).toLowerCase(); + } + @PostMapping("/difyCallbackPLForCustomerProfile") @Operation(summary = "dify回调,为了客户画像完善", description = "dify回调,为了客户画像完善") public Map difyCallbackPLForCustomerProfile(@RequestBody String message){ @@ -214,18 +345,21 @@ public class AudioManagementController { // 使用工具类批量获取关联数据 Map dealershipNameMap = dataEnrichmentUtil.getDealershipIdNameMap(dealershipIds); Map saleNameMap = dataEnrichmentUtil.getSalesIdNameMap(saleIds); + Map salePhoneMap = dataEnrichmentUtil.getSalesIdPhoneMap(saleIds); Map projectNameMap = dataEnrichmentUtil.getProjectIdNameMap(projectIds); // Map customerNameMap = dataEnrichmentUtil.getCustomerNameMap(customerIds); audioPage.getRecords().forEach(audio -> { audio.setDealershipName(dealershipNameMap.getOrDefault(audio.getDealershipId(), "")); audio.setSalesName(saleNameMap.getOrDefault(audio.getSalesId(), "")); + audio.setSalesPhone(salePhoneMap.getOrDefault(audio.getSalesId(), "")); audio.setProjectName(projectNameMap.getOrDefault(audio.getProjectId(), "")); // audio.setCustomerName(customerNameMap.getOrDefault(audio.getCustomerId(), "")); - - // 检查音频文件是否存在 - boolean fileExists = fileUploadService.audioFileExists(audio.getId()); - // 注意:这里不需要设置,因为实体类中已经有这些字段了 + //音频的url不要暴露对前端。 + if (audio.getAudioFileUrl() != null) { + audio.setAudioFileUrl( audio.getId()); + } + // 注意:这里不需要检查文件存在性,因为实体类中已经有这些字段了 }); result.put("success", true); @@ -366,6 +500,381 @@ public class AudioManagementController { } } + /** + * 获取音频文件的预签名URL + * 短期播放: 1小时(3600秒) + 正常使用: 7天(604800秒) + 长期访问: 30天(2592000秒) + + */ + @PostMapping("/presigned-url") + @Operation(summary = "获取音频文件预签名URL", description = "根据录音ID和URL获取音频文件的预签名访问URL") + public ResponseEntity> getAudioPresignedUrl( + @Parameter(description = "录音ID", required = false) + @RequestParam(required = false) String audioId, + @Parameter(description = "录音文件URL", required = false) + @RequestParam(required = false) String audioFileUrl, + @Parameter(description = "过期时间(秒),默认1小时") + @RequestParam(defaultValue = "3600") int expires){ + Map result = new HashMap<>(); + try { + String finalAudioFileUrl = null; + String finalAudioId = null; + + + // 如果录音文件URL为空,则通过录音ID查询数据库 + if (audioId != null && !audioId.trim().isEmpty()) { + AudioManagement audio = audioManagementService.getById(audioId); + if (audio == null) { + result.put("success", false); + result.put("message", "录音不存在"); + return ResponseEntity.notFound().build(); + } + + finalAudioFileUrl = audio.getAudioFileUrl(); + finalAudioId = audioId; + log.info("从数据库查询录音文件URL: {}", finalAudioFileUrl); + } + // 如果两个参数都为空,返回错误 + else { + result.put("success", false); + result.put("message", "录音ID和录音文件URL不能同时为空"); + return ResponseEntity.badRequest().body(result); + } + + // 检查最终的文件URL是否有效 + if (finalAudioFileUrl == null || finalAudioFileUrl.trim().isEmpty()) { + result.put("success", false); + result.put("message", "音频文件URL为空"); + return ResponseEntity.badRequest().body(result); + } + + // 提取文件名 + String fileName = finalAudioFileUrl.substring(finalAudioFileUrl.lastIndexOf("/") + 1); + + // 生成新的预签名URL + String presignedUrl = minIOService.getPresignedUrl(fileName, expires); + + if (presignedUrl != null) { + result.put("success", true); + result.put("message", "预签名URL生成成功"); + result.put("audioId", finalAudioId); + result.put("fileName", fileName); + result.put("presignedUrl", presignedUrl); + result.put("expires", expires); + result.put("expiresInHours", expires / 3600); + return ResponseEntity.ok(result); + } else { + result.put("success", false); + result.put("message", "预签名URL生成失败"); + return ResponseEntity.internalServerError().body(result); + } + } catch (Exception e) { + log.error("获取音频文件预签名URL异常:{}", e.getMessage(), e); + result.put("success", false); + result.put("message", "获取预签名URL异常:" + e.getMessage()); + return ResponseEntity.internalServerError().body(result); + } + } + + /** + * 批量分配录音到项目 + */ + @PostMapping("/allocateProject") + @Operation(summary = "批量分配录音到项目", description = "将多个录音批量分配到指定项目") + public ResponseEntity> allocateProject( + @Parameter(description = "录音ID列表", required = true) + @RequestParam List audioIds, + @Parameter(description = "项目ID", required = true) + @RequestParam String projectId, + @Parameter(description = "项目名称", required = true) + @RequestParam String projectName) { + Map result = new HashMap<>(); + try { + // 验证参数 + if (audioIds == null || audioIds.isEmpty()) { + result.put("success", false); + result.put("message", "录音ID列表不能为空"); + return ResponseEntity.badRequest().body(result); + } + + if (projectId == null || projectId.trim().isEmpty()) { + result.put("success", false); + result.put("message", "项目ID不能为空"); + return ResponseEntity.badRequest().body(result); + } + + if (projectName == null || projectName.trim().isEmpty()) { + result.put("success", false); + result.put("message", "项目名称不能为空"); + return ResponseEntity.badRequest().body(result); + } + + // 批量更新录音记录 + int successCount = 0; + int failureCount = 0; + List failedIds = new java.util.ArrayList<>(); + + for (String audioId : audioIds) { + try { + AudioManagement audio = audioManagementService.getById(audioId); + if (audio != null) { + audio.setProjectId(projectId); + audio.setProjectName(projectName); + audio.setUpdateTime(LocalDateTime.now()); + + boolean updated = audioManagementService.updateById(audio); + if (updated) { + successCount++; + log.info("录音分配项目成功:audioId={}, projectId={}, projectName={}", + audioId, projectId, projectName); + } else { + failureCount++; + failedIds.add(audioId); + log.warn("录音分配项目失败:audioId={}", audioId); + } + } else { + failureCount++; + failedIds.add(audioId); + log.warn("录音不存在:audioId={}", audioId); + } + } catch (Exception e) { + failureCount++; + failedIds.add(audioId); + log.error("录音分配项目异常:audioId={}, error={}", audioId, e.getMessage()); + } + } + + // 构建返回结果 + result.put("success", true); + result.put("message", "批量分配完成"); + result.put("totalCount", audioIds.size()); + result.put("successCount", successCount); + result.put("failureCount", failureCount); + result.put("projectId", projectId); + result.put("projectName", projectName); + + if (failureCount > 0) { + result.put("failedIds", failedIds); + result.put("message", String.format("批量分配完成,成功:%d,失败:%d", successCount, failureCount)); + } + + return ResponseEntity.ok(result); + + } catch (Exception e) { + log.error("批量分配录音到项目异常:{}", e.getMessage(), e); + result.put("success", false); + result.put("message", "批量分配异常:" + e.getMessage()); + return ResponseEntity.internalServerError().body(result); + } + } + + /** + * 批量分配录音到销售人员 + */ + @PostMapping("/allocateSales") + @Operation(summary = "批量分配录音到销售人员", description = "将多个录音批量分配到指定销售人员") + public ResponseEntity> allocateSales( + @Parameter(description = "录音ID列表", required = true) + @RequestParam List audioIds, + @Parameter(description = "销售人员ID", required = true) + @RequestParam String salesId, + @Parameter(description = "销售人员姓名", required = true) + @RequestParam String salesName) { + Map result = new HashMap<>(); + try { + // 验证参数 + if (audioIds == null || audioIds.isEmpty()) { + result.put("success", false); + result.put("message", "录音ID列表不能为空"); + return ResponseEntity.badRequest().body(result); + } + + if (salesId == null || salesId.trim().isEmpty()) { + result.put("success", false); + result.put("message", "销售人员ID不能为空"); + return ResponseEntity.badRequest().body(result); + } + + if (salesName == null || salesName.trim().isEmpty()) { + result.put("success", false); + result.put("message", "销售人员姓名不能为空"); + return ResponseEntity.badRequest().body(result); + } + + // 批量更新录音记录 + int successCount = 0; + int failureCount = 0; + List failedIds = new java.util.ArrayList<>(); + + for (String audioId : audioIds) { + try { + AudioManagement audio = audioManagementService.getById(audioId); + if (audio != null) { + audio.setSalesId(salesId); + audio.setSalesName(salesName); + audio.setUpdateTime(LocalDateTime.now()); + + boolean updated = audioManagementService.updateById(audio); + if (updated) { + successCount++; + log.info("录音分配销售人员成功:audioId={}, salesId={}, salesName={}", + audioId, salesId, salesName); + } else { + failureCount++; + failedIds.add(audioId); + log.warn("录音分配销售人员失败:audioId={}", audioId); + } + } else { + failureCount++; + failedIds.add(audioId); + log.warn("录音不存在:audioId={}", audioId); + } + } catch (Exception e) { + failureCount++; + failedIds.add(audioId); + log.error("录音分配销售人员异常:audioId={}, error={}", audioId, e.getMessage()); + } + } + + // 构建返回结果 + result.put("success", true); + result.put("message", "批量分配完成"); + result.put("totalCount", audioIds.size()); + result.put("successCount", successCount); + result.put("failureCount", failureCount); + result.put("salesId", salesId); + result.put("salesName", salesName); + + if (failureCount > 0) { + result.put("failedIds", failedIds); + result.put("message", String.format("批量分配完成,成功:%d,失败:%d", successCount, failureCount)); + } + + return ResponseEntity.ok(result); + + } catch (Exception e) { + log.error("批量分配录音到销售人员异常:{}", e.getMessage(), e); + result.put("success", false); + result.put("message", "批量分配异常:" + e.getMessage()); + return ResponseEntity.internalServerError().body(result); + } + } + + /** + * 录音转文本 + */ + @PostMapping("/convertAudioToText") + @Operation(summary = "录音转文本", description = "将录音文件转换为文本内容") + public ResponseEntity> convertAudioToText( + @Parameter(description = "录音ID", required = true) + @RequestParam String audioId, + @Parameter(description = "录音名称", required = true) + @RequestParam String audioName) { + Map result = new HashMap<>(); + try { + // 验证参数 + if (audioId == null || audioId.trim().isEmpty()) { + result.put("success", false); + result.put("message", "录音ID不能为空"); + return ResponseEntity.badRequest().body(result); + } + + if (audioName == null || audioName.trim().isEmpty()) { + result.put("success", false); + result.put("message", "录音名称不能为空"); + return ResponseEntity.badRequest().body(result); + } + + // 检查录音是否存在 + AudioManagement audio = audioManagementService.getById(audioId); + if (audio == null) { + result.put("success", false); + result.put("message", "录音不存在"); + return ResponseEntity.notFound().build(); + } + + // 检查录音文件URL是否存在 + String audioFileUrl = audio.getAudioFileUrl(); + if (audioFileUrl == null || audioFileUrl.trim().isEmpty()) { + result.put("success", false); + result.put("message", "录音文件不存在"); + return ResponseEntity.badRequest().body(result); + } + + // 提取文件名 + String fileName = audioFileUrl.substring(audioFileUrl.lastIndexOf("/") + 1); + + // 生成音频文件的预签名URL用于ASR服务 + String presignedUrl = minIOService.getPresignedUrl(fileName, 3600); // 1小时有效期 + + if (presignedUrl == null) { + result.put("success", false); + result.put("message", "生成音频文件访问URL失败"); + return ResponseEntity.internalServerError().body(result); + } + + // 创建ASR请求对象 + AsrRequest asrRequest = new AsrRequest(); + asrRequest.setAudioUrl(presignedUrl); + asrRequest.setAudioName(audioName); + asrRequest.setModel("paraformer-v2"); // 使用默认模型 + asrRequest.setFormat("wav"); // 默认格式 + asrRequest.setSampleRate(16000); // 默认采样率 + asrRequest.setEnablePunctuation(true); // 启用标点符号 + asrRequest.setEnableNumberConversion(true); // 启用数字转换 + asrRequest.setEnableSpeakerDiarization(false); // 不启用说话人分离 + + // 调用ASR服务进行语音识别 + AsrResponse asrResponse = ttsRequestLogService.speechToText(asrRequest); + + if (!asrResponse.isSuccess()) { + result.put("success", false); + result.put("message", "语音识别失败:" + asrResponse.getMessage()); + return ResponseEntity.internalServerError().body(result); + } + + String recognizedText = asrResponse.getText(); + + // 更新录音记录中的文本内容 + audio.setRecordingText(recognizedText); + audio.setUpdateTime(LocalDateTime.now()); + + boolean updated = audioManagementService.updateById(audio); + + if (updated) { + result.put("success", true); + result.put("message", "录音转文本成功"); + result.put("audioId", audioId); + result.put("audioName", audioName); + result.put("recognizedText", recognizedText); + result.put("textLength", recognizedText.length()); + result.put("taskId", asrResponse.getTaskId()); + result.put("requestId", asrResponse.getRequestId()); + result.put("duration", asrResponse.getDuration()); + result.put("processingTimeMs", asrResponse.getProcessingTimeMs()); + + log.info("录音转文本成功:audioId={}, audioName={}, textLength={}, taskId={}", + audioId, audioName, recognizedText.length(), asrResponse.getTaskId()); + + return ResponseEntity.ok(result); + } else { + result.put("success", false); + result.put("message", "录音转文本成功但更新数据库失败"); + result.put("recognizedText", recognizedText); + result.put("taskId", asrResponse.getTaskId()); + return ResponseEntity.internalServerError().body(result); + } + + } catch (Exception e) { + log.error("录音转文本异常:{}", e.getMessage(), e); + result.put("success", false); + result.put("message", "录音转文本异常:" + e.getMessage()); + return ResponseEntity.internalServerError().body(result); + } + } + + /** * 获取录音的音频文件信息 */ diff --git a/src/main/java/com/rj/controller/AudioStatisticsController.java b/src/main/java/com/rj/controller/AudioStatisticsController.java index 084ca65..7c50c08 100644 --- a/src/main/java/com/rj/controller/AudioStatisticsController.java +++ b/src/main/java/com/rj/controller/AudioStatisticsController.java @@ -36,12 +36,12 @@ public class AudioStatisticsController { */ @Operation(summary = "手动生成指定日期的统计数据", description = "手动触发指定日期的门店录音统计") @PostMapping("/generate") - public Result generateStatistics( + public Result generateStatistics( @Parameter(description = "统计日期,格式:yyyy-MM-dd") @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date) { try { int count = audioStatisticsScheduler.manualGenerateStatistics(date); - Result result = Result.ok(count); + Result result = Result.ok(count); result.setMsg("统计生成成功"); return result; } catch (Exception e) { @@ -54,14 +54,14 @@ public class AudioStatisticsController { */ @Operation(summary = "查询指定日期范围的统计数据", description = "查询指定日期范围内的门店录音统计数据") @GetMapping("/range") - public Result getStatisticsByDateRange( + public Result getStatisticsByDateRange( @Parameter(description = "开始日期,格式:yyyy-MM-dd") @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startDate, @Parameter(description = "结束日期,格式:yyyy-MM-dd") @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate endDate) { try { List statistics = audioStatisticsService.getStatisticsByDateRange(startDate, endDate); - Result result = Result.ok(statistics); + Result result = Result.ok(statistics); result.setMsg("查询成功"); return result; } catch (Exception e) { @@ -74,7 +74,7 @@ public class AudioStatisticsController { */ @Operation(summary = "查询指定门店的统计数据", description = "查询指定门店在指定日期范围内的录音统计数据") @GetMapping("/dealership/{dealershipId}") - public Result getStatisticsByDealership( + public Result getStatisticsByDealership( @Parameter(description = "门店ID") @PathVariable Long dealershipId, @Parameter(description = "开始日期,格式:yyyy-MM-dd") @@ -83,7 +83,7 @@ public class AudioStatisticsController { @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate endDate) { try { List statistics = audioStatisticsService.getStatisticsByDealership(dealershipId, startDate, endDate); - Result result = Result.ok(statistics); + Result result = Result.ok(statistics); result.setMsg("查询成功"); return result; } catch (Exception e) { @@ -96,7 +96,7 @@ public class AudioStatisticsController { */ @Operation(summary = "查询指定销售人员的统计数据", description = "查询指定销售人员在指定日期范围内的录音统计数据") @GetMapping("/sales/{salesId}") - public Result getStatisticsBySales( + public Result getStatisticsBySales( @Parameter(description = "销售人员ID") @PathVariable String salesId, @Parameter(description = "开始日期,格式:yyyy-MM-dd") @@ -105,7 +105,7 @@ public class AudioStatisticsController { @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate endDate) { try { List statistics = audioStatisticsService.getStatisticsBySales(salesId, startDate, endDate); - Result result = Result.ok(statistics); + Result result = Result.ok(statistics); result.setMsg("查询成功"); return result; } catch (Exception e) { @@ -118,7 +118,7 @@ public class AudioStatisticsController { */ @Operation(summary = "查询指定项目的统计数据", description = "查询指定项目在指定日期范围内的录音统计数据") @GetMapping("/project/{projectId}") - public Result getStatisticsByProject( + public Result getStatisticsByProject( @Parameter(description = "项目ID") @PathVariable String projectId, @Parameter(description = "开始日期,格式:yyyy-MM-dd") @@ -127,7 +127,7 @@ public class AudioStatisticsController { @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate endDate) { try { List statistics = audioStatisticsService.getStatisticsByProject(projectId, startDate, endDate); - Result result = Result.ok(statistics); + Result result = Result.ok(statistics); result.setMsg("查询成功"); return result; } catch (Exception e) { @@ -140,12 +140,12 @@ public class AudioStatisticsController { */ @Operation(summary = "查询指定日期的统计数据", description = "查询指定日期的所有门店录音统计数据") @GetMapping("/date/{date}") - public Result getStatisticsByDate( + public Result getStatisticsByDate( @Parameter(description = "统计日期,格式:yyyy-MM-dd") @PathVariable @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date) { try { List statistics = audioStatisticsService.getStatisticsByDateRange(date, date); - Result result = Result.ok(statistics); + Result result = Result.ok(statistics); result.setMsg("查询成功"); return result; } catch (Exception e) { @@ -158,16 +158,16 @@ public class AudioStatisticsController { */ @Operation(summary = "获取统计概览", description = "获取最近7天的门店录音统计概览") @GetMapping("/overview") - public Result getStatisticsOverview() { + public Result getStatisticsOverview() { try { LocalDate endDate = LocalDate.now().minusDays(1); // 昨天 LocalDate startDate = endDate.minusDays(6); // 7天前 List statistics = audioStatisticsService.getStatisticsByDateRange(startDate, endDate); - Result result = Result.ok(statistics); + Result result = Result.ok(statistics); result.setMsg("查询成功"); return result; } catch (Exception e) { return Result.error("查询失败:" + e.getMessage()); } } -} +} \ No newline at end of file diff --git a/src/main/java/com/rj/controller/CommunicationRecordController.java b/src/main/java/com/rj/controller/CommunicationRecordController.java new file mode 100644 index 0000000..109f755 --- /dev/null +++ b/src/main/java/com/rj/controller/CommunicationRecordController.java @@ -0,0 +1,281 @@ +package com.rj.controller; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.rj.entity.CommunicationRecord; +import com.rj.service.ICommunicationRecordService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import jakarta.validation.Valid; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + *

+ * 沟通记录表 前端控制器 + *

+ * + * @author 系统生成 + * @since 2025-01-30 + */ +@RestController +@RequestMapping("/api/communication-record") +@Tag(name = "沟通记录管理", description = "沟通记录管理相关接口") +@Slf4j +public class CommunicationRecordController { + + @Autowired + private ICommunicationRecordService communicationRecordService; + + /** + * 分页查询沟通记录 + */ + + @GetMapping("/page") + @Operation(summary = "分页查询沟通记录", description = "根据条件分页查询沟通记录列表") + public ResponseEntity> getPage( + @Parameter(description = "页码") @RequestParam(defaultValue = "1") Integer current, + @Parameter(description = "每页大小") @RequestParam(defaultValue = "10") Integer size, + @Parameter(description = "客户姓名") @RequestParam(required = false) String customerName, + @Parameter(description = "客户电话") @RequestParam(required = false) String customerPhone, + @Parameter(description = "沟通类型") @RequestParam(required = false) String communicationType, + @Parameter(description = "所属人姓名") @RequestParam(required = false) String ownerName) { + + try { + Page page = new Page<>(current, size); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + + // 构建查询条件 + queryWrapper.like(customerName != null, CommunicationRecord::getCustomerName, customerName) + .like(customerPhone != null, CommunicationRecord::getCustomerPhone, customerPhone) + .like(communicationType != null, CommunicationRecord::getCommunicationType, communicationType) + .like(ownerName != null, CommunicationRecord::getOwnerName, ownerName) + .eq(CommunicationRecord::getStatus, 1) + .orderByDesc(CommunicationRecord::getCreateTime); + + Page result = communicationRecordService.page(page, queryWrapper); + log.info("分页查询沟通记录成功,结果:{}", result.toString()); + Map response = new HashMap<>(); + response.put("code", 200); + response.put("message", "查询成功"); + response.put("data", result.getRecords()); + response.put("total", result.getTotal()); + response.put("current", result.getCurrent()); + response.put("size", result.getSize()); + response.put("pages", result.getPages()); + return ResponseEntity.ok(response); + } catch (Exception e) { + log.error("分页查询沟通记录失败", e); + Map response = new HashMap<>(); + response.put("code", 500); + response.put("message", "查询失败:" + e.getMessage()); + response.put("data", null); + return ResponseEntity.ok(response); + } + } + + /** + * 根据ID查询沟通记录 + */ + @GetMapping("/{id}") + @Operation(summary = "根据ID查询沟通记录", description = "根据ID查询沟通记录详情") + public ResponseEntity> getById( + @Parameter(description = "沟通记录ID") @PathVariable String id) { + try { + CommunicationRecord record = communicationRecordService.getById(id); + if (record == null) { + Map response = new HashMap<>(); + response.put("code", 404); + response.put("message", "沟通记录不存在"); + response.put("data", null); + return ResponseEntity.ok(response); + } + Map response = new HashMap<>(); + response.put("code", 200); + response.put("message", "查询成功"); + response.put("data", record); + return ResponseEntity.ok(response); + } catch (Exception e) { + log.error("查询沟通记录失败", e); + Map response = new HashMap<>(); + response.put("code", 500); + response.put("message", "查询失败:" + e.getMessage()); + response.put("data", null); + return ResponseEntity.ok(response); + } + } + + /** + * 新增沟通记录 + */ + @PostMapping + @Operation(summary = "新增沟通记录", description = "新增沟通记录") + public ResponseEntity> save(@Valid @RequestBody CommunicationRecord communicationRecord) { + try { + // 设置创建时间 + communicationRecord.setCreateTime(LocalDateTime.now()); + communicationRecord.setStatus(1); + + boolean success = communicationRecordService.save(communicationRecord); + Map response = new HashMap<>(); + if (success) { + response.put("code", 200); + response.put("message", "新增成功"); + response.put("data", null); + } else { + response.put("code", 500); + response.put("message", "新增失败"); + response.put("data", null); + } + return ResponseEntity.ok(response); + } catch (Exception e) { + log.error("新增沟通记录失败", e); + Map response = new HashMap<>(); + response.put("code", 500); + response.put("message", "新增失败:" + e.getMessage()); + response.put("data", null); + return ResponseEntity.ok(response); + } + } + + /** + * 修改沟通记录 + */ + @PutMapping + @Operation(summary = "修改沟通记录", description = "修改沟通记录") + public ResponseEntity> update(@Valid @RequestBody CommunicationRecord communicationRecord) { + try { + // 设置更新时间 + communicationRecord.setUpdateTime(LocalDateTime.now()); + + boolean success = communicationRecordService.updateById(communicationRecord); + Map response = new HashMap<>(); + if (success) { + response.put("code", 200); + response.put("message", "修改成功"); + response.put("data", null); + } else { + response.put("code", 500); + response.put("message", "修改失败"); + response.put("data", null); + } + return ResponseEntity.ok(response); + } catch (Exception e) { + log.error("修改沟通记录失败", e); + Map response = new HashMap<>(); + response.put("code", 500); + response.put("message", "修改失败:" + e.getMessage()); + response.put("data", null); + return ResponseEntity.ok(response); + } + } + + /** + * 删除沟通记录(软删除) + */ + @DeleteMapping("/{id}") + @Operation(summary = "删除沟通记录", description = "删除沟通记录(软删除)") + public ResponseEntity> delete(@Parameter(description = "沟通记录ID") @PathVariable String id) { + try { + CommunicationRecord record = communicationRecordService.getById(id); + if (record == null) { + Map response = new HashMap<>(); + response.put("code", 404); + response.put("message", "沟通记录不存在"); + response.put("data", null); + return ResponseEntity.ok(response); + } + + // 软删除 + record.setStatus(0); + record.setUpdateTime(LocalDateTime.now()); + boolean success = communicationRecordService.updateById(record); + + Map response = new HashMap<>(); + if (success) { + response.put("code", 200); + response.put("message", "删除成功"); + response.put("data", null); + } else { + response.put("code", 500); + response.put("message", "删除失败"); + response.put("data", null); + } + return ResponseEntity.ok(response); + } catch (Exception e) { + log.error("删除沟通记录失败", e); + Map response = new HashMap<>(); + response.put("code", 500); + response.put("message", "删除失败:" + e.getMessage()); + response.put("data", null); + return ResponseEntity.ok(response); + } + } + + /** + * 根据客户ID查询沟通记录 + */ + @GetMapping("/customer/{customerId}") + @Operation(summary = "根据客户ID查询沟通记录", description = "根据客户ID查询该客户的所有沟通记录") + public ResponseEntity> getByCustomerId( + @Parameter(description = "客户ID") @PathVariable String customerId) { + try { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(CommunicationRecord::getCustomerId, customerId) + .eq(CommunicationRecord::getStatus, 1) + .orderByDesc(CommunicationRecord::getCommunicationTime); + + List records = communicationRecordService.list(queryWrapper); + Map response = new HashMap<>(); + response.put("code", 200); + response.put("message", "查询成功"); + response.put("data", records); + return ResponseEntity.ok(response); + } catch (Exception e) { + log.error("根据客户ID查询沟通记录失败", e); + Map response = new HashMap<>(); + response.put("code", 500); + response.put("message", "查询失败:" + e.getMessage()); + response.put("data", null); + return ResponseEntity.ok(response); + } + } + + /** + * 根据所属人查询沟通记录 + */ + @GetMapping("/owner/{ownerName}") + @Operation(summary = "根据所属人查询沟通记录", description = "根据所属人姓名查询沟通记录") + public ResponseEntity> getByOwnerName( + @Parameter(description = "所属人姓名") @PathVariable String ownerName) { + try { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(CommunicationRecord::getOwnerName, ownerName) + .eq(CommunicationRecord::getStatus, 1) + .orderByDesc(CommunicationRecord::getCreateTime); + + List records = communicationRecordService.list(queryWrapper); + Map response = new HashMap<>(); + response.put("code", 200); + response.put("message", "查询成功"); + response.put("data", records); + return ResponseEntity.ok(response); + } catch (Exception e) { + log.error("根据所属人查询沟通记录失败", e); + Map response = new HashMap<>(); + response.put("code", 500); + response.put("message", "查询失败:" + e.getMessage()); + response.put("data", null); + return ResponseEntity.ok(response); + } + } + +} \ No newline at end of file diff --git a/src/main/java/com/rj/controller/sys/MenuController.java b/src/main/java/com/rj/controller/sys/MenuController.java index 985f1ec..e72dd29 100644 --- a/src/main/java/com/rj/controller/sys/MenuController.java +++ b/src/main/java/com/rj/controller/sys/MenuController.java @@ -404,6 +404,13 @@ public class MenuController { + + + + + + + diff --git a/src/main/java/com/rj/controller/sys/RoleController.java b/src/main/java/com/rj/controller/sys/RoleController.java index b4a0def..583e048 100644 --- a/src/main/java/com/rj/controller/sys/RoleController.java +++ b/src/main/java/com/rj/controller/sys/RoleController.java @@ -374,6 +374,13 @@ public class RoleController { + + + + + + + diff --git a/src/main/java/com/rj/controller/sys/UserRoleController.java b/src/main/java/com/rj/controller/sys/UserRoleController.java index 0fdd3d0..011b37b 100644 --- a/src/main/java/com/rj/controller/sys/UserRoleController.java +++ b/src/main/java/com/rj/controller/sys/UserRoleController.java @@ -380,6 +380,13 @@ public class UserRoleController { + + + + + + + diff --git a/src/main/java/com/rj/dto/DifyWorkflowResponseDto.java b/src/main/java/com/rj/dto/DifyWorkflowResponseDto.java index 3fb2a87..59cc3bd 100644 --- a/src/main/java/com/rj/dto/DifyWorkflowResponseDto.java +++ b/src/main/java/com/rj/dto/DifyWorkflowResponseDto.java @@ -146,6 +146,13 @@ public class DifyWorkflowResponseDto { + + + + + + + diff --git a/src/main/java/com/rj/entity/AudioManagement.java b/src/main/java/com/rj/entity/AudioManagement.java index c29f421..507b1f2 100644 --- a/src/main/java/com/rj/entity/AudioManagement.java +++ b/src/main/java/com/rj/entity/AudioManagement.java @@ -7,6 +7,9 @@ import java.time.LocalDateTime; import com.baomidou.mybatisplus.annotation.TableField; import java.io.Serializable; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import org.springframework.web.multipart.MultipartFile; + import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.EqualsAndHashCode; @@ -19,6 +22,7 @@ import lombok.EqualsAndHashCode; * @author 李中华 ,spllzh * @since 2025-08-07 */ +@JsonIgnoreProperties(ignoreUnknown = true) @Data @EqualsAndHashCode(callSuper = false) @TableName("audio_management") @@ -163,4 +167,12 @@ public class AudioManagement implements Serializable { @TableField("recording_text") private String recordingText; + /** + * 前端上传的录音文件 + * 此字段不保存在数据库中,仅用于接收前端上传的文件 + */ + @Schema(description = "前端上传的录音文件") + @TableField(exist = false) + private MultipartFile audioFile; + } diff --git a/src/main/java/com/rj/entity/CommunicationRecord.java b/src/main/java/com/rj/entity/CommunicationRecord.java new file mode 100644 index 0000000..eaa871c --- /dev/null +++ b/src/main/java/com/rj/entity/CommunicationRecord.java @@ -0,0 +1,84 @@ +package com.rj.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableField; +import java.time.LocalDateTime; +import java.io.Serializable; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + *

+ * 沟通记录表 + *

+ * + * @author 系统生成 + * @since 2025-01-30 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("communication_record") +@Schema(description="沟通记录表") +public class CommunicationRecord implements Serializable { + + private static final long serialVersionUID = 1L; + + @Schema(description = "主键ID(UUID)") + @TableId("id") + private String id; + + @Schema(description = "客户姓名") + @TableField("customer_name") + private String customerName; + + @Schema(description = "客户电话") + @TableField("customer_phone") + private String customerPhone; + + @Schema(description = "客户ID") + @TableField("customer_id") + private String customerId; + + @Schema(description = "沟通类型") + @TableField("communication_type") + private String communicationType; + + @Schema(description = "沟通时间") + @TableField("communication_time") + private LocalDateTime communicationTime; + + @Schema(description = "沟通内容") + @TableField("communication_content") + private String communicationContent; + + @Schema(description = "沟通结果") + @TableField("communication_result") + private String communicationResult; + + @Schema(description = "所属人姓名") + @TableField("owner_name") + private String ownerName; + + @Schema(description = "所属人电话") + @TableField("owner_phone") + private String ownerPhone; + + @Schema(description = "待办建议") + @TableField("todo_suggestion") + private String todoSuggestion; + + @Schema(description = "创建时间") + @TableField("create_time") + private LocalDateTime createTime; + + @Schema(description = "更新时间") + @TableField("update_time") + private LocalDateTime updateTime; + + @Schema(description = "状态:1-有效,0-删除") + @TableField("status") + private Integer status; + +} diff --git a/src/main/java/com/rj/mapper/CommunicationRecordMapper.java b/src/main/java/com/rj/mapper/CommunicationRecordMapper.java new file mode 100644 index 0000000..a9d3eae --- /dev/null +++ b/src/main/java/com/rj/mapper/CommunicationRecordMapper.java @@ -0,0 +1,18 @@ +package com.rj.mapper; + +import com.rj.entity.CommunicationRecord; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * 沟通记录表 Mapper 接口 + *

+ * + * @author 系统生成 + * @since 2025-01-30 + */ +@Mapper +public interface CommunicationRecordMapper extends BaseMapper { + +} diff --git a/src/main/java/com/rj/mapper/CustomerProfileAnalysisMapper.java b/src/main/java/com/rj/mapper/CustomerProfileAnalysisMapper.java index 8fb68ac..a93dab3 100644 --- a/src/main/java/com/rj/mapper/CustomerProfileAnalysisMapper.java +++ b/src/main/java/com/rj/mapper/CustomerProfileAnalysisMapper.java @@ -89,6 +89,13 @@ public interface CustomerProfileAnalysisMapper extends BaseMapper> resp = audioManagementController.addAudio(audio); - if (resp != null && resp.getStatusCode().is2xxSuccessful()) { - success++; - } else { - log.warn("第{}次插入失败,返回:{}", i + 1, resp == null ? "null" : resp.getStatusCode()); - } +// ResponseEntity> resp = audioManagementController.addAudio(audio); +// if (resp != null && resp.getStatusCode().is2xxSuccessful()) { +// success++; +// } else { +// log.warn("第{}次插入失败,返回:{}", i + 1, resp == null ? "null" : resp.getStatusCode()); +// } } log.info("音频管理模拟数据任务完成,共尝试100次,成功 {} 次", success); } catch (Exception e) { diff --git a/src/main/java/com/rj/service/ICommunicationRecordService.java b/src/main/java/com/rj/service/ICommunicationRecordService.java new file mode 100644 index 0000000..91e3cfe --- /dev/null +++ b/src/main/java/com/rj/service/ICommunicationRecordService.java @@ -0,0 +1,16 @@ +package com.rj.service; + +import com.rj.entity.CommunicationRecord; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + *

+ * 沟通记录表 服务类 + *

+ * + * @author 系统生成 + * @since 2025-01-30 + */ +public interface ICommunicationRecordService extends IService { + +} diff --git a/src/main/java/com/rj/service/IFileUploadService.java b/src/main/java/com/rj/service/IFileUploadService.java index fc50b5a..c979446 100644 --- a/src/main/java/com/rj/service/IFileUploadService.java +++ b/src/main/java/com/rj/service/IFileUploadService.java @@ -49,4 +49,12 @@ public interface IFileUploadService { * @return 是否存在 */ boolean audioFileExists(String audioId); + + /** + * 使用MinIO上传音频文件 + * @param file 音频文件 + * @param audioId 录音ID + * @return 上传结果 + */ + Map uploadAudioFileToMinIO(MultipartFile file, String audioId) throws IOException; } diff --git a/src/main/java/com/rj/service/biz/ICustomerProfileAnalysisService.java b/src/main/java/com/rj/service/biz/ICustomerProfileAnalysisService.java index 28fec40..f1731e7 100644 --- a/src/main/java/com/rj/service/biz/ICustomerProfileAnalysisService.java +++ b/src/main/java/com/rj/service/biz/ICustomerProfileAnalysisService.java @@ -110,6 +110,13 @@ public interface ICustomerProfileAnalysisService extends IService + * 沟通记录表 服务实现类 + *

+ * + * @author 系统生成 + * @since 2025-01-30 + */ +@Service +public class CommunicationRecordServiceImpl extends ServiceImpl implements ICommunicationRecordService { + +} diff --git a/src/main/java/com/rj/service/impl/FileUploadServiceImpl.java b/src/main/java/com/rj/service/impl/FileUploadServiceImpl.java index db86f53..5f7a4e7 100644 --- a/src/main/java/com/rj/service/impl/FileUploadServiceImpl.java +++ b/src/main/java/com/rj/service/impl/FileUploadServiceImpl.java @@ -3,6 +3,7 @@ package com.rj.service.impl; import com.rj.entity.AudioManagement; import com.rj.service.IFileUploadService; import com.rj.service.IAudioManagementService; +import com.rj.service.MinIOService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -32,6 +33,9 @@ public class FileUploadServiceImpl implements IFileUploadService { @Autowired private IAudioManagementService audioManagementService; + @Autowired + private MinIOService minIOService; + @Value("${app.audio.upload.path:uploads/audio}") private String uploadPath; @@ -74,31 +78,39 @@ public class FileUploadServiceImpl implements IFileUploadService { return result; } - // 创建上传目录 - Path uploadDir = Paths.get(uploadPath); - if (!Files.exists(uploadDir)) { - Files.createDirectories(uploadDir); + // 直接上传到MinIO,不保存到本地 + log.info("开始上传文件到MinIO..."); + String fileUrl = minIOService.uploadFile(file); + log.info("文件上传成功,URL: {}", fileUrl); + + // 获取录音记录并更新 + AudioManagement audio = audioManagementService.getById(audioId); + if (audio != null) { + // 更新录音记录的文件信息 + String fileExtension = getFileExtension(originalFilename); + + audio.setAudioFileUrl(fileUrl); + audio.setAudioFileSize(file.getSize()); + audio.setAudioFileOriginalName(originalFilename); + audio.setAudioFileExtension(fileExtension); + audio.setUploadTime(java.time.LocalDateTime.now()); + audio.setUpdateTime(java.time.LocalDateTime.now()); + + // 保存更新 + audioManagementService.updateById(audio); + + log.info("录音记录更新成功:audioId={}", audioId); + } else { + log.warn("未找到对应的录音记录:audioId={}", audioId); } - // 生成文件名 - String fileExtension = getFileExtension(originalFilename); - String fileName = audioId+"_"+UUID.randomUUID() + "." + fileExtension; - Path filePath = uploadDir.resolve(fileName); - - // 保存文件 - Files.copy(file.getInputStream(), filePath); - - // 生成访问URL - String fileUrl = accessUrl + fileName; - result.put("success", true); result.put("message", "文件上传成功"); - result.put("fileName", fileName); result.put("fileUrl", fileUrl); result.put("fileSize", file.getSize()); result.put("originalName", originalFilename); - log.info("音频文件上传成功:audioId={}, fileName={}, size={}", audioId, fileName, file.getSize()); + log.info("音频文件上传成功:audioId={}, minioUrl={}, size={}", audioId, fileUrl, file.getSize()); } catch (Exception e) { log.error("音频文件上传失败:audioId={}, error={}", audioId, e.getMessage(), e); @@ -240,6 +252,55 @@ public class FileUploadServiceImpl implements IFileUploadService { return files != null && files.length > 0; } + @Override + public Map uploadAudioFileToMinIO(MultipartFile file, String audioId) throws IOException { + Map result = new HashMap<>(); + + try { + // 验证文件 + if (file == null || file.isEmpty()) { + result.put("success", false); + result.put("message", "文件不能为空"); + return result; + } + + // 验证文件大小 + long maxSize = parseFileSize(maxFileSize); + if (file.getSize() > maxSize) { + result.put("success", false); + result.put("message", "文件大小超过限制,最大允许 " + maxFileSize); + return result; + } + + // 验证文件格式 + String originalFilename = file.getOriginalFilename(); + if (!isValidAudioFormat(originalFilename)) { + result.put("success", false); + result.put("message", "不支持的音频格式,支持的格式:" + String.join(", ", SUPPORTED_AUDIO_FORMATS)); + return result; + } + + // 使用MinIO上传文件 + String minioUrl = minIOService.uploadFile(file); + + result.put("success", true); + result.put("message", "文件上传到MinIO成功"); + result.put("fileUrl", minioUrl); + result.put("fileSize", file.getSize()); + result.put("originalName", originalFilename); + result.put("audioId", audioId); + + log.info("音频文件上传到MinIO成功:audioId={}, minioUrl={}, size={}", audioId, minioUrl, file.getSize()); + + } catch (Exception e) { + log.error("音频文件上传到MinIO失败:audioId={}, error={}", audioId, e.getMessage(), e); + result.put("success", false); + result.put("message", "文件上传到MinIO失败:" + e.getMessage()); + } + + return result; + } + /** * 验证音频文件格式 */ diff --git a/src/main/java/com/rj/service/sys/impl/MenuServiceImpl.java b/src/main/java/com/rj/service/sys/impl/MenuServiceImpl.java index 3ed0ada..880c28e 100644 --- a/src/main/java/com/rj/service/sys/impl/MenuServiceImpl.java +++ b/src/main/java/com/rj/service/sys/impl/MenuServiceImpl.java @@ -117,6 +117,13 @@ public class MenuServiceImpl extends ServiceImpl implements IM + + + + + + + diff --git a/src/main/java/com/rj/service/sys/impl/RoleServiceImpl.java b/src/main/java/com/rj/service/sys/impl/RoleServiceImpl.java index 63087ce..9071bd8 100644 --- a/src/main/java/com/rj/service/sys/impl/RoleServiceImpl.java +++ b/src/main/java/com/rj/service/sys/impl/RoleServiceImpl.java @@ -117,6 +117,13 @@ public class RoleServiceImpl extends ServiceImpl implements IR + + + + + + + diff --git a/src/main/java/com/rj/service/sys/impl/UserRoleServiceImpl.java b/src/main/java/com/rj/service/sys/impl/UserRoleServiceImpl.java index 3bf125e..6695022 100644 --- a/src/main/java/com/rj/service/sys/impl/UserRoleServiceImpl.java +++ b/src/main/java/com/rj/service/sys/impl/UserRoleServiceImpl.java @@ -117,6 +117,13 @@ public class UserRoleServiceImpl extends ServiceImpl i + + + + + + + diff --git a/src/main/resources/application-audio.yml b/src/main/resources/application-audio.yml index 560393f..38086e1 100644 --- a/src/main/resources/application-audio.yml +++ b/src/main/resources/application-audio.yml @@ -126,6 +126,13 @@ spring: + + + + + + + diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index a932d98..4060fed 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -103,6 +103,10 @@ spring: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0 + jackson: + deserialization: + # 忽略不能解析的字段。当前端传过来的字段,后端不能解析时直接抛弃。 + fail-on-unknown-properties: false knife4j: diff --git a/src/main/resources/mapper/AudioManagementStatisticsMapper.xml b/src/main/resources/mapper/AudioManagementStatisticsMapper.xml index ed02d8a..a3bb163 100644 --- a/src/main/resources/mapper/AudioManagementStatisticsMapper.xml +++ b/src/main/resources/mapper/AudioManagementStatisticsMapper.xml @@ -103,6 +103,13 @@ + + + + + + + diff --git a/src/main/resources/mapper/CommunicationRecordMapper.xml b/src/main/resources/mapper/CommunicationRecordMapper.xml new file mode 100644 index 0000000..38073f8 --- /dev/null +++ b/src/main/resources/mapper/CommunicationRecordMapper.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + id, customer_name, customer_phone, customer_id, communication_type, + communication_time, communication_content, communication_result, + owner_name, owner_phone, todo_suggestion, create_time, update_time, status + + + diff --git a/src/main/resources/static/tts-demo.html b/src/main/resources/static/tts-demo.html index 8660294..efb1294 100644 --- a/src/main/resources/static/tts-demo.html +++ b/src/main/resources/static/tts-demo.html @@ -302,6 +302,13 @@ + + + + + + + diff --git a/src/test/java/com/rj/service/FaceDetectImageCountTest.java b/src/test/java/com/rj/service/FaceDetectImageCountTest.java index a198473..64a51b9 100644 --- a/src/test/java/com/rj/service/FaceDetectImageCountTest.java +++ b/src/test/java/com/rj/service/FaceDetectImageCountTest.java @@ -240,6 +240,13 @@ public class FaceDetectImageCountTest { + + + + + + + diff --git a/src/test/java/com/rj/service/TtsRequestLogShortUrlTest.java b/src/test/java/com/rj/service/TtsRequestLogShortUrlTest.java index 9c778a7..c67b9d6 100644 --- a/src/test/java/com/rj/service/TtsRequestLogShortUrlTest.java +++ b/src/test/java/com/rj/service/TtsRequestLogShortUrlTest.java @@ -181,6 +181,13 @@ public class TtsRequestLogShortUrlTest { + + + + + + + diff --git a/src/test/java/com/rj/service/VideoSynthesisTempUrlTest.java b/src/test/java/com/rj/service/VideoSynthesisTempUrlTest.java index 6f5f8b0..da5d05d 100644 --- a/src/test/java/com/rj/service/VideoSynthesisTempUrlTest.java +++ b/src/test/java/com/rj/service/VideoSynthesisTempUrlTest.java @@ -159,6 +159,13 @@ public class VideoSynthesisTempUrlTest { + + + + + + + diff --git a/src/test/java/com/rj/service/VideoSynthesisVideoNameTest.java b/src/test/java/com/rj/service/VideoSynthesisVideoNameTest.java index 896f251..4e42413 100644 --- a/src/test/java/com/rj/service/VideoSynthesisVideoNameTest.java +++ b/src/test/java/com/rj/service/VideoSynthesisVideoNameTest.java @@ -134,6 +134,13 @@ public class VideoSynthesisVideoNameTest { + + + + + + +