diff --git a/src/main/java/com/rj/common/PasswordUtil.java b/src/main/java/com/rj/common/PasswordUtil.java index 7288785..7cc975f 100644 --- a/src/main/java/com/rj/common/PasswordUtil.java +++ b/src/main/java/com/rj/common/PasswordUtil.java @@ -159,5 +159,6 @@ public class PasswordUtil { + diff --git a/src/main/java/com/rj/common/ServiceManager.java b/src/main/java/com/rj/common/ServiceManager.java index 65c9dcf..192cd4b 100644 --- a/src/main/java/com/rj/common/ServiceManager.java +++ b/src/main/java/com/rj/common/ServiceManager.java @@ -143,5 +143,6 @@ public class ServiceManager { + diff --git a/src/main/java/com/rj/config/AliyunConfig.java b/src/main/java/com/rj/config/AliyunConfig.java index 362eaa0..ffe7b33 100644 --- a/src/main/java/com/rj/config/AliyunConfig.java +++ b/src/main/java/com/rj/config/AliyunConfig.java @@ -120,5 +120,6 @@ public class AliyunConfig { + diff --git a/src/main/java/com/rj/controller/TextModelController.java b/src/main/java/com/rj/controller/TextModelController.java new file mode 100644 index 0000000..f5d4080 --- /dev/null +++ b/src/main/java/com/rj/controller/TextModelController.java @@ -0,0 +1,291 @@ +package com.rj.controller; + +import com.alibaba.dashscope.exception.InputRequiredException; +import com.alibaba.dashscope.exception.NoApiKeyException; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; + + +import java.lang.System; +import java.util.Collections; +import com.alibaba.dashscope.aigc.generation.Generation; +import com.alibaba.dashscope.aigc.generation.GenerationParam; +import com.alibaba.dashscope.aigc.generation.GenerationResult; +import com.alibaba.dashscope.aigc.generation.TranslationOptions; +import com.alibaba.dashscope.common.Message; +import com.alibaba.dashscope.common.Role; + + +/** + * 翻译、法律、意图理解控制器 + * + * @author 李中华 + * @date 2025/1/3 + */ +@Slf4j +@RestController +@RequestMapping("/api/translation") +@Tag(name = "文本模型AI服务", description = "翻译、法律咨询、意图理解API接口") +public class TextModelController { + + /** + * 翻译请求DTO + */ + @Data + public static class TranslationRequest { + @NotBlank(message = "原语言文字不能为空") + private String sourceText; + + @NotBlank(message = "目标语言不能为空") + private String targetLanguage; + + @NotBlank(message = "原语言不能为空") + private String sourceLanguage; + @NotBlank(message = "大模型名称") + private String model; + } + + /** + * 法律咨询请求DTO + */ + @Data + public static class LegalRequest { + @NotBlank(message = "法律问题不能为空") + private String question; + + private String category; + private String jurisdiction; + @NotBlank(message = "大模型名称") + private String model; + } + + /** + * 意图理解请求DTO + */ + @Data + public static class IntentRequest { + @NotBlank(message = "输入文本不能为空") + private String text; + + private String context; + private String domain; + @NotBlank(message = "大模型名称") + private String model; + } + + /** + * 翻译接口 + */ + @PostMapping("/translate") + @Operation(summary = "文本翻译", description = "将文本从源语言翻译到目标语言") + public ResponseEntity> translate( + @Parameter(description = "翻译请求参数") @Valid @RequestBody TranslationRequest request) { + + Map result = new HashMap<>(); + + try { + log.info("开始翻译,模型: {}, 目标语言: {}, 文本长度: {}", + request.getModel(), request.getTargetLanguage(), request.getSourceText().length()); + + // 模拟翻译处理(实际项目中应该调用翻译服务) + String translatedText = performTranslation( + request.getSourceText(), + request.getModel(), + request.getTargetLanguage() + ); + + result.put("success", true); + result.put("message", "翻译成功"); + result.put("sourceText", request.getSourceText()); + result.put("translatedText", translatedText); + result.put("sourceLanguage", request.getSourceLanguage()); + result.put("targetLanguage", request.getTargetLanguage()); + result.put("translationConfidence", 0.95); + result.put("processingTimeMs", 1200); + result.put("requestId", generateRequestId()); + result.put("timestamp", LocalDateTime.now()); + + log.info("翻译完成,目标文本长度: {}", translatedText.length()); + + return ResponseEntity.ok(result); + + } catch (Exception e) { + log.error("翻译失败: {}", e.getMessage(), e); + result.put("success", false); + result.put("message", "翻译失败: " + e.getMessage()); + result.put("error", e.getClass().getSimpleName()); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result); + } + } + + /** + * 法律咨询接口 + */ + @PostMapping("/legal") + @Operation(summary = "法律咨询", description = "提供法律问题咨询和建议") + public ResponseEntity> legalConsultation( + @Parameter(description = "法律咨询请求参数") @Valid @RequestBody LegalRequest request) { + + Map result = new HashMap<>(); + + try { + log.info("开始法律咨询,问题类别: {}, 管辖区域: {}, 问题长度: {}", + request.getCategory(), request.getJurisdiction(), request.getQuestion().length()); + + // 模拟法律咨询处理(实际项目中应该调用AI法律服务) + Map legalResponse = performLegalConsultation(request); + + result.put("success", true); + result.put("message", "法律咨询完成"); + result.put("question", request.getQuestion()); + result.put("answer", legalResponse.get("answer")); + result.put("category", legalResponse.get("category")); + result.put("jurisdiction", legalResponse.get("jurisdiction")); + result.put("confidence", legalResponse.get("confidence")); + result.put("relatedLaws", legalResponse.get("relatedLaws")); + result.put("suggestions", legalResponse.get("suggestions")); + result.put("processingTimeMs", 2500); + result.put("requestId", generateRequestId()); + result.put("timestamp", LocalDateTime.now()); + + log.info("法律咨询完成,回答长度: {}", legalResponse.get("answer").toString().length()); + + return ResponseEntity.ok(result); + + } catch (Exception e) { + log.error("法律咨询失败: {}", e.getMessage(), e); + result.put("success", false); + result.put("message", "法律咨询失败: " + e.getMessage()); + result.put("error", e.getClass().getSimpleName()); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result); + } + } + + /** + * 意图理解接口 + */ + @PostMapping("/intent") + @Operation(summary = "意图理解", description = "分析文本的意图和情感") + public ResponseEntity> intentAnalysis( + @Parameter(description = "意图理解请求参数") @Valid @RequestBody IntentRequest request) { + + Map result = new HashMap<>(); + + try { + log.info("开始意图分析,领域: {}, 上下文: {}, 文本长度: {}", + request.getDomain(), request.getContext(), request.getText().length()); + + // 模拟意图理解处理(实际项目中应该调用NLP服务) + Map intentResponse = performIntentAnalysis(request); + + result.put("success", true); + result.put("message", "意图分析完成"); + result.put("text", request.getText()); + result.put("intent", intentResponse.get("intent")); + result.put("confidence", intentResponse.get("confidence")); + result.put("entities", intentResponse.get("entities")); + result.put("sentiment", intentResponse.get("sentiment")); + result.put("emotion", intentResponse.get("emotion")); + result.put("domain", intentResponse.get("domain")); + result.put("processingTimeMs", 800); + result.put("requestId", generateRequestId()); + result.put("timestamp", LocalDateTime.now()); + + log.info("意图分析完成,识别意图: {}", intentResponse.get("intent")); + + return ResponseEntity.ok(result); + + } catch (Exception e) { + log.error("意图分析失败: {}", e.getMessage(), e); + result.put("success", false); + result.put("message", "意图分析失败: " + e.getMessage()); + result.put("error", e.getClass().getSimpleName()); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result); + } + } + + /** + * 执行翻译的模拟方法 + */ + private String performTranslation(String sourceText, String model, String targetLanguage) { + + Generation gen = new Generation(); + Message userMsg = Message.builder() + .role(Role.USER.getValue()) + .content(sourceText) + .build(); + TranslationOptions options = TranslationOptions.builder() + .sourceLang("auto") +// .targetLang("English") + .targetLang(targetLanguage) + .build(); + GenerationParam param = GenerationParam.builder() + // 若没有配置环境变量,请用阿里云百炼API Key将下行替换为:.apiKey("sk-xxx") + .apiKey(System.getenv("DASHSCOPE_API_KEY")) +// .model("qwen-mt-plus") + .model(model) + .messages(Collections.singletonList(userMsg)) + .resultFormat(GenerationParam.ResultFormat.MESSAGE) + .translationOptions(options) + .build(); + try { + GenerationResult call = gen.call(param); + String content = call.getOutput().getChoices().get(0).getMessage().getContent(); + log.info("翻译完成,结果长度: {} ,内容是:{} ", content.length(),content); + return content; + } catch (NoApiKeyException e) { + throw new RuntimeException(e); + } catch (InputRequiredException e) { + throw new RuntimeException(e); + } + + } + + /** + * 执行法律咨询的模拟方法 + */ + private Map performLegalConsultation(LegalRequest request) { + Map response = new HashMap<>(); + response.put("answer", "根据您的问题,我建议您咨询专业律师。这是一个复杂的法律问题,需要详细分析具体情况。"); + response.put("category", request.getCategory() != null ? request.getCategory() : "一般法律咨询"); + response.put("jurisdiction", request.getJurisdiction() != null ? request.getJurisdiction() : "中国大陆"); + response.put("confidence", 0.85); + response.put("relatedLaws", new String[]{"民法典", "合同法", "劳动法"}); + response.put("suggestions", new String[]{"收集相关证据", "咨询专业律师", "了解相关法律条文"}); + return response; + } + + /** + * 执行意图分析的模拟方法 + */ + private Map performIntentAnalysis(IntentRequest request) { + Map response = new HashMap<>(); + response.put("intent", "查询信息"); + response.put("confidence", 0.92); + response.put("entities", new String[]{"时间", "地点", "人物"}); + response.put("sentiment", "中性"); + response.put("emotion", "平静"); + response.put("domain", request.getDomain() != null ? request.getDomain() : "通用"); + return response; + } + + /** + * 生成请求ID + */ + private String generateRequestId() { + return "REQ_" + System.currentTimeMillis() + "_" + (int)(Math.random() * 1000); + } +} diff --git a/src/main/java/com/rj/controller/VideoImageAnalysisController.java b/src/main/java/com/rj/controller/VideoImageAnalysisController.java index 4df3466..1e8e9cc 100644 --- a/src/main/java/com/rj/controller/VideoImageAnalysisController.java +++ b/src/main/java/com/rj/controller/VideoImageAnalysisController.java @@ -1,5 +1,12 @@ package com.rj.controller; +import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversation; +import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversationParam; +import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversationResult; +import com.alibaba.dashscope.common.MultiModalMessage; +import com.alibaba.dashscope.common.Role; +import com.alibaba.dashscope.exception.NoApiKeyException; +import com.alibaba.dashscope.exception.UploadFileException; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.rj.entity.VideoImageAnalysis; @@ -607,7 +614,13 @@ public class VideoImageAnalysisController { analysis.setUpdatedAt(now); // 调用阿里云AI服务进行实际分析 - Map aiResult = performAiAnalysisWithDetails(model, type, userQuestion, analysis.getVideoImageTempUrl()); + Map aiResult=null; + if ("video".equals(type) ){ + aiResult = performAiAnalysisWithDetailsForVideo(model, type, userQuestion, analysis.getVideoImageTempUrl()); + }else { + aiResult = performAiAnalysisWithDetails(model, type, userQuestion, analysis.getVideoImageTempUrl()); + } + String aiResponse = (String) aiResult.get("content"); analysis.setResponseContent(aiResponse); @@ -619,10 +632,15 @@ public class VideoImageAnalysisController { // 记录AI分析结果和token使用情况 analysis.setFinishReason("stop"); - analysis.setUsagePromptTokens((Integer) aiResult.getOrDefault("promptTokens", 0)); - analysis.setUsageCompletionTokens((Integer) aiResult.getOrDefault("completionTokens", 0)); - analysis.setUsageTotalTokens((Integer) aiResult.getOrDefault("totalTokens", 0)); - + try { + analysis.setUsagePromptTokens((Integer) aiResult.getOrDefault("promptTokens", Integer.valueOf(0))); + analysis.setUsageCompletionTokens((Integer) aiResult.getOrDefault("completionTokens", Integer.valueOf(0))); + analysis.setUsageTotalTokens((Integer) aiResult.getOrDefault("totalTokens", Integer.valueOf(0))); + + } catch (Exception e) { + log.error("获取token使用情况失败:{}" ,e ); + } + // 保存分析记录 boolean success = videoImageAnalysisService.saveVideoImageAnalysis(analysis); @@ -706,7 +724,46 @@ public class VideoImageAnalysisController { throw new RuntimeException("提取对象名失败: " + e.getMessage()); } } + private Map performAiAnalysisWithDetailsForVideo(String model, String type, String userQuestion, String fileUrl) { + MultiModalConversation conv = new MultiModalConversation(); + Map result = new HashMap<>(); + + // fps 可参数控制视频抽帧频率,表示每隔 1/fps 秒抽取一帧,完整用法请参见:https://help.aliyun.com/zh/model-studio/use-qwen-by-calling-api?#2ed5ee7377fum + Map params = new HashMap<>(); +// params.put("video", "https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20241115/cqqkru/1.mp4"); + params.put("video",fileUrl); + + params.put("fps", 2); + MultiModalMessage userMessage = MultiModalMessage.builder().role(Role.USER.getValue()) + .content(Arrays.asList( + params, + Collections.singletonMap("text", userQuestion))).build(); + + + MultiModalConversationParam param = MultiModalConversationParam.builder() + .apiKey(System.getenv("DASHSCOPE_API_KEY")) + .model(model) + .messages(Arrays.asList(userMessage)) + .build(); + result.put("requestData", userMessage.toString()); + MultiModalConversationResult callResult = null; + try { + callResult = conv.call(param); + } catch (NoApiKeyException e) { + throw new RuntimeException(e); + } catch (UploadFileException e) { + throw new RuntimeException(e); + } + result.put("responseData", callResult.toString()); + System.out.println(callResult.getOutput().getChoices().get(0).getMessage().getContent().get(0).get("text")); + + result.put("content",callResult.getOutput().getChoices().get(0).getMessage().getContent().get(0).get("text") ); + result.put("promptTokens",callResult.getUsage().getInputTokens() ); + result.put("completionTokens",callResult.getUsage().getOutputTokensDetails() ); + result.put("totalTokens",callResult.getUsage().getTotalTokens() ); + return result; + } /** * 执行AI分析 - 调用阿里云通义千问API(返回详细信息) */ @@ -737,27 +794,19 @@ public class VideoImageAnalysisController { imageContent.put("type", type); if (type.equals("video")) { - /** - * [{ - * "role": "user", - * "content": [ - * { - * "type": "video", - * "video": [ - * "https://img.alicdn.com/imgextra/i3/O1CN01K3SgGo1eqmlUgeE9b_!!6000000003923-0-tps-3840-2160.jpg", - * "https://img.alicdn.com/imgextra/i4/O1CN01BjZvwg1Y23CF5qIRB_!!6000000003000-0-tps-3840-2160.jpg", - * "https://img.alicdn.com/imgextra/i4/O1CN01Ib0clU27vTgBdbVLQ_!!6000000007859-0-tps-3840-2160.jpg", - * "https://img.alicdn.com/imgextra/i1/O1CN01aygPLW1s3EXCdSN4X_!!6000000005710-0-tps-3840-2160.jpg"] - * }, - * { - * "type": "text", - * "text": "描述这个视频的具体过程" - * }]}] - */ + // 检查是否提供了图片序列 + if (fileUrl == null || fileUrl.trim().isEmpty()) { + throw new RuntimeException("视频分析需要提供图片序列,当前未提供任何文件"); + } + + // 检查是否为图片序列(通过URL数量判断) + // 这里假设如果fileUrl包含多个URL(用逗号分隔),则为图片序列 + // 满足要求的图片序列 + Map videoContent = new HashMap<>(); + videoContent.put("video", Arrays.asList(fileUrl)); + content.add(videoContent); - imageContent.put("video", Arrays.asList(fileUrl)); - content.add(imageContent); - }else if (type.equals("image_url")) { + } else if (type.equals("image_url")) { /** * [{"role": "user","content": [ * {"type": "image_url", @@ -789,11 +838,12 @@ public class VideoImageAnalysisController { // 创建HTTP实体 HttpEntity> entity = new HttpEntity<>(requestBody, headers); - + // 保存请求数据到结果中 String requestDataJson = objectMapper.writeValueAsString(requestBody); - log.info("调用阿里云AI分析API,URL:{}, 请求体:{}", apiUrl, requestDataJson); - + log.info("调用阿里云AI分析API, 请求体:{}", requestDataJson); + Map result = new HashMap<>(); + result.put("requestData", requestDataJson); // 发送请求 ResponseEntity response = restTemplate.exchange( apiUrl, @@ -801,13 +851,12 @@ public class VideoImageAnalysisController { entity, String.class ); - - log.info("阿里云AI分析API响应状态:{}", response.getStatusCode()); + log.info("阿里云AI分析API响应内容:{}", response.getBody()); - // 保存响应数据 String responseDataJson = response.getBody(); - + result.put("responseData", responseDataJson); + if (response.getStatusCode().is2xxSuccessful()) { // 解析响应 JsonNode responseJson = objectMapper.readTree(response.getBody()); @@ -819,7 +868,7 @@ public class VideoImageAnalysisController { // 记录token使用情况 JsonNode usage = responseJson.path("usage"); - Map result = new HashMap<>(); + result.put("content", contentText); if (!usage.isMissingNode()) { @@ -839,9 +888,7 @@ public class VideoImageAnalysisController { result.put("totalTokens", 0); } - // 添加请求和响应数据 - result.put("requestData", requestDataJson); - result.put("responseData", responseDataJson); + return result; } else { diff --git a/src/main/java/com/rj/controller/sys/MenuController.java b/src/main/java/com/rj/controller/sys/MenuController.java index 2e7439c..dd58681 100644 --- a/src/main/java/com/rj/controller/sys/MenuController.java +++ b/src/main/java/com/rj/controller/sys/MenuController.java @@ -381,5 +381,6 @@ 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 b0de730..5754072 100644 --- a/src/main/java/com/rj/controller/sys/RoleController.java +++ b/src/main/java/com/rj/controller/sys/RoleController.java @@ -351,5 +351,6 @@ 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 0679ac1..7b569f2 100644 --- a/src/main/java/com/rj/controller/sys/UserRoleController.java +++ b/src/main/java/com/rj/controller/sys/UserRoleController.java @@ -357,5 +357,6 @@ public class UserRoleController { + diff --git a/src/main/java/com/rj/dto/DifyWorkflowResponseDto.java b/src/main/java/com/rj/dto/DifyWorkflowResponseDto.java index 64a9d45..b9dcfee 100644 --- a/src/main/java/com/rj/dto/DifyWorkflowResponseDto.java +++ b/src/main/java/com/rj/dto/DifyWorkflowResponseDto.java @@ -123,5 +123,6 @@ public class DifyWorkflowResponseDto { + diff --git a/src/main/java/com/rj/mapper/CustomerProfileAnalysisMapper.java b/src/main/java/com/rj/mapper/CustomerProfileAnalysisMapper.java index 7e917ec..c022970 100644 --- a/src/main/java/com/rj/mapper/CustomerProfileAnalysisMapper.java +++ b/src/main/java/com/rj/mapper/CustomerProfileAnalysisMapper.java @@ -66,5 +66,6 @@ public interface CustomerProfileAnalysisMapper extends BaseMapper 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 015d335..f6c7ccf 100644 --- a/src/main/java/com/rj/service/sys/impl/RoleServiceImpl.java +++ b/src/main/java/com/rj/service/sys/impl/RoleServiceImpl.java @@ -94,5 +94,6 @@ 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 7e8b7fa..0d60e69 100644 --- a/src/main/java/com/rj/service/sys/impl/UserRoleServiceImpl.java +++ b/src/main/java/com/rj/service/sys/impl/UserRoleServiceImpl.java @@ -94,5 +94,6 @@ public class UserRoleServiceImpl extends ServiceImpl i + diff --git a/src/main/resources/application-audio.yml b/src/main/resources/application-audio.yml index 650365c..0014f78 100644 --- a/src/main/resources/application-audio.yml +++ b/src/main/resources/application-audio.yml @@ -81,6 +81,18 @@ spring: + + + + + + + + + + + + diff --git a/src/main/resources/mapper/AudioManagementStatisticsMapper.xml b/src/main/resources/mapper/AudioManagementStatisticsMapper.xml index c6fecb9..9198522 100644 --- a/src/main/resources/mapper/AudioManagementStatisticsMapper.xml +++ b/src/main/resources/mapper/AudioManagementStatisticsMapper.xml @@ -58,6 +58,18 @@ + + + + + + + + + + + + diff --git a/src/main/resources/static/tts-demo.html b/src/main/resources/static/tts-demo.html index d4e6d97..f78098d 100644 --- a/src/main/resources/static/tts-demo.html +++ b/src/main/resources/static/tts-demo.html @@ -262,6 +262,18 @@ + + + + + + + + + + + + diff --git a/src/main/sql/add_audio_management_sales_phone.sql b/src/main/sql/add_audio_management_sales_phone.sql deleted file mode 100644 index 0876b20..0000000 --- a/src/main/sql/add_audio_management_sales_phone.sql +++ /dev/null @@ -1,32 +0,0 @@ --- 为 audio_management 表添加销售人员电话字段 --- 执行日期: 2025-01-27 - --- 添加销售人员电话字段 -ALTER TABLE `audio_management` -ADD COLUMN `sales_phone` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '销售人员电话' -AFTER `sales_name`; - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/sql/add_audio_name_to_tts_request_log.sql b/src/main/sql/add_audio_name_to_tts_request_log.sql deleted file mode 100644 index ecdd76c..0000000 --- a/src/main/sql/add_audio_name_to_tts_request_log.sql +++ /dev/null @@ -1,21 +0,0 @@ --- 为 tts_request_log 表添加音频名称字段 -ALTER TABLE `tts_request_log` -ADD COLUMN `audio_name` varchar(255) DEFAULT NULL COMMENT '音频文件名' AFTER `minio_url`; - --- 添加索引以提高查询性能 -ALTER TABLE `tts_request_log` -ADD INDEX `idx_audio_name` (`audio_name`); - - - - - - - - - - - - - - diff --git a/src/main/sql/add_topsales_score_result_fields.sql b/src/main/sql/add_topsales_score_result_fields.sql deleted file mode 100644 index b911355..0000000 --- a/src/main/sql/add_topsales_score_result_fields.sql +++ /dev/null @@ -1,22 +0,0 @@ --- 为 topsales_score_result 表添加销售人员和经销商信息字段 --- 执行日期: 2025-01-27 - --- 添加销售人员姓名字段 -ALTER TABLE `topsales_score_result` -ADD COLUMN `salesperson_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '销售人员姓名' -AFTER `negotiation_deal_score`; - --- 添加销售人员电话字段 -ALTER TABLE `topsales_score_result` -ADD COLUMN `salesperson_phone` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '销售人员电话' -AFTER `salesperson_name`; - --- 添加经销商名称字段 -ALTER TABLE `topsales_score_result` -ADD COLUMN `dealer_name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '经销商名称' -AFTER `salesperson_phone`; - --- 添加经销商电话字段 -ALTER TABLE `topsales_score_result` -ADD COLUMN `dealer_phone` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '经销商电话' -AFTER `dealer_name`; diff --git a/src/main/sql/customer_profile_analysis_table_fields_classification.md b/src/main/sql/customer_profile_analysis_table_fields_classification.md deleted file mode 100644 index 77fd685..0000000 --- a/src/main/sql/customer_profile_analysis_table_fields_classification.md +++ /dev/null @@ -1,192 +0,0 @@ -# Customer Profile Analysis 表字段分类 - -## 1. 主键和基础标识字段 (Primary Key & Basic Identity Fields) -| 字段名 | 类型 | 是否必填 | 说明 | -|--------|------|----------|------| -| `id` | VARCHAR(36) | NOT NULL | 主键UUID | -| `profile_analysis_record_id` | VARCHAR(255) | NOT NULL | AI分析记录ID | -| `analysis_scene_type` | TINYINT UNSIGNED | NOT NULL | 分析类型:1-企微会话 2-AI通话录音 3-AI铭牌(客流) 4-AI铭牌(试驾) | -| `related_business_id` | VARCHAR(255) | NOT NULL | 业务ID:根据分析不同传不同的值 1-unionId 2-通话ID 3-客流ID 4-试驾ID | - -## 2. 时间相关字段 (Time Related Fields) -| 字段名 | 类型 | 是否必填 | 说明 | -|--------|------|----------|------| -| `interaction_date` | DATETIME | NOT NULL | 沟通时间:AI通话录音-通话开始时间 企微会话-语料消息截至时间 面谈-语料消息截至时间 | -| `created_at` | DATETIME | NOT NULL | 创建时间 | -| `updated_at` | DATETIME | NOT NULL | 更新时间 | - -## 3. 经销商和组织信息字段 (Dealer & Organization Fields) -| 字段名 | 类型 | 是否必填 | 说明 | -|--------|------|----------|------| -| `dealer_code` | VARCHAR(36) | NULL | 经销商编码 | -| `dealer_name` | VARCHAR(100) | NULL | 经销商门店名称 | -| `big_area` | VARCHAR(50) | NULL | 大区 | -| `project_id` | VARCHAR(36) | NULL | 项目ID | -| `project_name` | VARCHAR(100) | NULL | 项目名称 | - -## 4. 销售团队信息字段 (Sales Team Fields) -| 字段名 | 类型 | 是否必填 | 说明 | -|--------|------|----------|------| -| `sales_person_id` | VARCHAR(36) | NULL | 销售顾问ID | -| `sales_person_name` | VARCHAR(50) | NULL | 销售顾问姓名 | - -## 5. 商机和客户信息字段 (Opportunity & Customer Fields) -| 字段名 | 类型 | 是否必填 | 说明 | -|--------|------|----------|------| -| `opportunity_id` | VARCHAR(36) | NULL | 商机ID | -| `client_id` | VARCHAR(36) | NULL | 客户ID | -| `client_name` | VARCHAR(100) | NULL | 客户姓名 | -| `client_phone` | VARCHAR(20) | NULL | 客户电话 | - -## 6. 购车意向信息字段 (Purchase Intention Fields) -| 字段名 | 类型 | 是否必填 | 说明 | -|--------|------|----------|------| -| `purchase_date` | VARCHAR(50) | NULL | 预购日期(示例值:天数) | -| `purchase_type` | VARCHAR(20) | NULL | 购买类型(示例值:新购/置换/增购) | -| `payment_way` | VARCHAR(20) | NULL | 付款方式(示例值:贷款/全款/分期) | -| `competitor_brand` | VARCHAR(50) | NULL | 竞品品牌(示例值:奔驰/宝马/奥迪) | -| `vehicle_usage` | VARCHAR(20) | NULL | 购车用途(示例值:家用/商用/代步) | -| `primary_driver` | VARCHAR(20) | NULL | 实际驾驶人(示例值:本人/家人/其他) | -| `budget_range` | VARCHAR(50) | NULL | 购车预算(示例值:具体预算范围) | -| `purchase_qualification` | VARCHAR(10) | NULL | 购车资质(示例值:是否) | - -## 7. 客户个人背景信息字段 (Customer Background Fields) -| 字段名 | 类型 | 是否必填 | 说明 | -|--------|------|----------|------| -| `education_level` | VARCHAR(20) | NULL | 教育水平(示例值:本科/硕士/高中) | -| `family_composition` | VARCHAR(50) | NULL | 家庭结构(示例值:已婚有子女/单身) | -| `monthly_income` | VARCHAR(50) | NULL | 家庭月收入(示例值:具体收入区间) | -| `job_title` | VARCHAR(50) | NULL | 职业(示例值:教师/工程师/医生) | -| `company_type` | VARCHAR(50) | NULL | 企业性质(示例值:民营企业/国企/外企) | -| `industry_sector` | VARCHAR(50) | NULL | 所属行业(示例值:教育/互联网/金融) | -| `home_address` | VARCHAR(200) | NULL | 详细地址(非枚举,示例值) | -| `family_size` | VARCHAR(10) | NULL | 家庭人数(非枚举,示例值) | -| `hobby` | VARCHAR(200) | NULL | 个人爱好(非枚举,示例值) | - -## 8. 客户关注点和偏好字段 (Customer Focus & Preference Fields) -| 字段名 | 类型 | 是否必填 | 说明 | -|--------|------|----------|------| -| `interest_focus` | VARCHAR(200) | NULL | 兴趣点(示例值:智能驾驶/油耗/空间) | -| `concern_point` | VARCHAR(200) | NULL | 顾虑点(示例值:保养成本/保值率) | -| `current_brand` | VARCHAR(50) | NULL | 保有品牌(非枚举,示例值) | - -## 9. 备注和扩展信息字段 (Notes & Extended Information Fields) -| 字段名 | 类型 | 是否必填 | 说明 | -|--------|------|----------|------| -| `notes` | VARCHAR(500) | NULL | 备注(非枚举,保留名称) | - -## 10. 实体情感分析字段 (Entity Sentiment Analysis Fields) - -### 10.1 品牌和设计相关情感 -| 字段名 | 类型 | 是否必填 | 说明 | -|--------|------|----------|------| -| `entity_brand` | VARCHAR(10) | NULL | 品牌情感倾向:积极/中性/消极 | -| `entity_brand_reason` | VARCHAR(500) | NULL | 品牌情感倾向原因 | -| `entity_design` | VARCHAR(10) | NULL | 外观设计情感倾向:积极/中性/消极 | -| `entity_design_reason` | VARCHAR(500) | NULL | 外观设计情感倾向原因 | -| `entity_color` | VARCHAR(10) | NULL | 颜色情感倾向:积极/中性/消极 | -| `entity_color_reason` | VARCHAR(500) | NULL | 颜色情感倾向原因 | - -### 10.2 车辆尺寸和空间相关情感 -| 字段名 | 类型 | 是否必填 | 说明 | -|--------|------|----------|------| -| `entity_size` | VARCHAR(10) | NULL | 尺寸情感倾向:积极/中性/消极 | -| `entity_size_reason` | VARCHAR(500) | NULL | 尺寸情感倾向原因 | -| `entity_space` | VARCHAR(10) | NULL | 空间情感倾向:积极/中性/消极 | -| `entity_space_reason` | VARCHAR(500) | NULL | 空间情感倾向原因 | - -### 10.3 内饰和配置相关情感 -| 字段名 | 类型 | 是否必填 | 说明 | -|--------|------|----------|------| -| `entity_interior` | VARCHAR(10) | NULL | 内饰情感倾向:积极/中性/消极 | -| `entity_interior_reason` | VARCHAR(500) | NULL | 内饰情感倾向原因 | -| `entity_audio` | VARCHAR(10) | NULL | 音响情感倾向:积极/中性/消极 | -| `entity_audio_reason` | VARCHAR(500) | NULL | 音响情感倾向原因 | -| `entity_ivinfo` | VARCHAR(10) | NULL | 车机情感倾向:积极/中性/消极 | -| `entity_ivinfo_reason` | VARCHAR(500) | NULL | 车机情感倾向原因 | - -### 10.4 智能驾驶和续航相关情感 -| 字段名 | 类型 | 是否必填 | 说明 | -|--------|------|----------|------| -| `entity_fsd` | VARCHAR(10) | NULL | 智驾情感倾向:积极/中性/消极 | -| `entity_fsd_reason` | VARCHAR(500) | NULL | 智驾情感倾向原因 | -| `entity_mileage` | VARCHAR(10) | NULL | 续航情感倾向:积极/中性/消极 | -| `entity_mileage_reason` | VARCHAR(500) | NULL | 续航情感倾向原因 | - -### 10.5 安全和环保相关情感 -| 字段名 | 类型 | 是否必填 | 说明 | -|--------|------|----------|------| -| `entity_safety` | VARCHAR(10) | NULL | 安全情感倾向:积极/中性/消极 | -| `entity_safety_reason` | VARCHAR(500) | NULL | 安全情感倾向原因 | -| `entity_eco` | VARCHAR(10) | NULL | 环保情感倾向:积极/中性/消极 | -| `entity_eco_reason` | VARCHAR(500) | NULL | 环保情感倾向原因 | -| `entity_liscense` | VARCHAR(10) | NULL | 绿牌/路权情感倾向:积极/中性/消极 | -| `entity_liscense_reason` | VARCHAR(500) | NULL | 绿牌/路权情感倾向原因 | - -### 10.6 价格和成本相关情感 -| 字段名 | 类型 | 是否必填 | 说明 | -|--------|------|----------|------| -| `entity_price` | VARCHAR(10) | NULL | 价格情感倾向:积极/中性/消极 | -| `entity_price_reason` | VARCHAR(500) | NULL | 价格情感倾向原因 | -| `entity_benefit` | VARCHAR(10) | NULL | 购车权益情感倾向:积极/中性/消极 | -| `entity_benefit_reason` | VARCHAR(500) | NULL | 购车权益情感倾向原因 | -| `entity_cost` | VARCHAR(10) | NULL | 用车成本情感倾向:积极/中性/消极 | -| `entity_cost_reason` | VARCHAR(500) | NULL | 用车成本情感倾向原因 | -| `entity_value` | VARCHAR(10) | NULL | 性价比情感倾向:积极/中性/消极 | -| `entity_value_reason` | VARCHAR(500) | NULL | 性价比情感倾向原因 | - -### 10.7 动力和操控相关情感 -| 字段名 | 类型 | 是否必填 | 说明 | -|--------|------|----------|------| -| `entity_power` | VARCHAR(10) | NULL | 动力情感倾向:积极/中性/消极 | -| `entity_power_reason` | VARCHAR(500) | NULL | 动力情感倾向原因 | -| `entity_control` | VARCHAR(10) | NULL | 操控情感倾向:积极/中性/消极 | -| `entity_control_reason` | VARCHAR(500) | NULL | 操控情感倾向原因 | - -### 10.8 服务和政策相关情感 -| 字段名 | 类型 | 是否必填 | 说明 | -|--------|------|----------|------| -| `entity_tradein` | VARCHAR(10) | NULL | 置换政策情感倾向:积极/中性/消极 | -| `entity_tradein_reason` | VARCHAR(500) | NULL | 置换政策情感倾向原因 | -| `entity_finance` | VARCHAR(10) | NULL | 金融方案情感倾向:积极/中性/消极 | -| `entity_finance_reason` | VARCHAR(500) | NULL | 金融方案情感倾向原因 | -| `entity_warranty` | VARCHAR(10) | NULL | 质保情感倾向:积极/中性/消极 | -| `entity_warranty_reason` | VARCHAR(500) | NULL | 质保情感倾向原因 | -| `entity_delivery` | VARCHAR(10) | NULL | 交付时间情感倾向:积极/中性/消极 | -| `entity_delivery_reason` | VARCHAR(500) | NULL | 交付时间情感倾向原因 | - -## 11. 系统管理字段 (System Management Fields) -| 字段名 | 类型 | 是否必填 | 说明 | -|--------|------|----------|------| -| `created_by` | VARCHAR(36) | NULL | 创建人id | -| `updated_by` | VARCHAR(36) | NULL | 更新人id | -| `create_sqlby` | VARCHAR(8) | NULL | 创建sql人 | -| `update_sqlby` | VARCHAR(8) | NULL | 更新人sql人 | -| `record_version` | TINYINT UNSIGNED | DEFAULT 0 | 版本号(乐观锁) | -| `is_deleted` | TINYINT UNSIGNED | NOT NULL DEFAULT 0 | 是否删除 | - -## 字段分类总结 - -### 按功能分类: -1. **基础标识字段** (4个):主键、分析记录ID、分析类型、业务ID -2. **时间字段** (3个):交互时间、创建时间、更新时间 -3. **组织架构字段** (5个):经销商、大区、项目信息 -4. **人员信息字段** (2个):销售顾问信息 -5. **客户信息字段** (4个):商机、客户基本信息 -6. **购车意向字段** (8个):购车相关的意向和偏好 -7. **客户背景字段** (8个):个人背景和社会属性 -8. **关注偏好字段** (3个):兴趣点和顾虑点 -9. **备注字段** (1个):扩展备注信息 -10. **情感分析字段** (46个):各种实体的情感倾向和原因 -11. **系统管理字段** (6个):审计和版本控制 - -### 按数据来源分类: -- **系统生成字段**:id、created_at、updated_at、record_version、is_deleted -- **业务输入字段**:大部分业务相关字段 -- **AI分析字段**:所有entity_*字段 -- **用户输入字段**:客户基本信息、购车意向等 - -### 按更新频率分类: -- **静态字段**:客户背景信息、组织架构信息 -- **动态字段**:情感分析结果、购车意向 -- **系统字段**:时间戳、版本号等 diff --git a/src/test/java/com/rj/ai/VideoImageToText.java b/src/test/java/com/rj/ai/VideoImageToText.java new file mode 100644 index 0000000..b5dc84d --- /dev/null +++ b/src/test/java/com/rj/ai/VideoImageToText.java @@ -0,0 +1,97 @@ +package com.rj.ai; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversation; +import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversationParam; +import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversationResult; +import com.alibaba.dashscope.common.MultiModalMessage; +import com.alibaba.dashscope.common.Role; +import com.alibaba.dashscope.exception.ApiException; +import com.alibaba.dashscope.exception.NoApiKeyException; +import com.alibaba.dashscope.exception.UploadFileException; + + +import java.lang.System; +import java.util.Collections; +import com.alibaba.dashscope.aigc.generation.Generation; +import com.alibaba.dashscope.aigc.generation.GenerationParam; +import com.alibaba.dashscope.aigc.generation.GenerationResult; +import com.alibaba.dashscope.aigc.generation.TranslationOptions; +import com.alibaba.dashscope.common.Message; +import com.alibaba.dashscope.common.Role; +import com.alibaba.dashscope.exception.ApiException; +import com.alibaba.dashscope.exception.InputRequiredException; + + + +public class VideoImageToText { + + // 若使用新加坡地域的模型,请取消下列注释 + // static {Constants.baseHttpApiUrl="https://dashscope-intl.aliyuncs.com/api/v1";} + public static void simpleMultiModalConversationCall() + throws ApiException, NoApiKeyException, UploadFileException { + MultiModalConversation conv = new MultiModalConversation(); + // fps 可参数控制视频抽帧频率,表示每隔 1/fps 秒抽取一帧,完整用法请参见:https://help.aliyun.com/zh/model-studio/use-qwen-by-calling-api?#2ed5ee7377fum + Map params = new HashMap<>(); +// params.put("video", "https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20241115/cqqkru/1.mp4"); + params.put("video", + "http://101.35.52.237:19005/car/20251012_094805_c50e423f61a5429ba2984500e43acc1f.mp4?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=minioadmin%2F20251012%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20251012T014810Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Signature=eaa996f9992c2e48acf22f3148328265a9b98630be5bb9dcbe85404a35f923da"); + + params.put("fps", 2); + MultiModalMessage userMessage = MultiModalMessage.builder().role(Role.USER.getValue()) + .content(Arrays.asList( + params, + Collections.singletonMap("text", "这段视频的内容是什么?"))).build(); + MultiModalConversationParam param = MultiModalConversationParam.builder() + // 若没有配置环境变量,请用百炼API Key将下行替换为:.apiKey("sk-xxx") + // 新加坡和北京地域的API Key不同。获取API Key:https://help.aliyun.com/zh/model-studio/get-api-key + .apiKey(System.getenv("DASHSCOPE_API_KEY")) + .model("qwen3-vl-plus") + .messages(Arrays.asList(userMessage)) + .build(); + MultiModalConversationResult result = conv.call(param); + System.out.println(result.getOutput().getChoices().get(0).getMessage().getContent().get(0).get("text")); + Map resultoo = new HashMap<>(); + resultoo.put("promptTokens",result.getUsage().getInputTokens() ); + resultoo.put("completionTokens",result.getUsage().getOutputTokensDetails() ); + resultoo.put("totalTokens",result.getUsage().getTotalTokens() ); + + } + + public static GenerationResult callWithMessage() throws ApiException, NoApiKeyException, InputRequiredException { + Generation gen = new Generation(); + Message userMsg = Message.builder() + .role(Role.USER.getValue()) + .content("我看到这个视频后没有笑") + .build(); + TranslationOptions options = TranslationOptions.builder() + .sourceLang("auto") + .targetLang("English") + .build(); + GenerationParam param = GenerationParam.builder() + // 若没有配置环境变量,请用阿里云百炼API Key将下行替换为:.apiKey("sk-xxx") + .apiKey(System.getenv("DASHSCOPE_API_KEY")) + .model("qwen-mt-plus") + .messages(Collections.singletonList(userMsg)) + .resultFormat(GenerationParam.ResultFormat.MESSAGE) + .translationOptions(options) + .build(); + return gen.call(param); + } + + public static void main(String[] args) { + try { +// simpleMultiModalConversationCall(); + GenerationResult generationResult = callWithMessage(); + System.out.println(generationResult.getOutput()); + System.out.println(generationResult.getUsage()); + } catch (Exception e) { + System.out.println(e.getMessage()); + } + System.exit(0); + } +} \ No newline at end of file diff --git a/src/test/java/com/rj/service/TtsRequestLogShortUrlTest.java b/src/test/java/com/rj/service/TtsRequestLogShortUrlTest.java index 9d00f01..9ef8ff7 100644 --- a/src/test/java/com/rj/service/TtsRequestLogShortUrlTest.java +++ b/src/test/java/com/rj/service/TtsRequestLogShortUrlTest.java @@ -145,6 +145,18 @@ public class TtsRequestLogShortUrlTest { + + + + + + + + + + + +