diff --git a/src/main/java/com/rj/controller/AudioTextAnalysisFurnitureController.java b/src/main/java/com/rj/controller/AudioTextAnalysisFurnitureController.java index bbe1d20..e88c783 100644 --- a/src/main/java/com/rj/controller/AudioTextAnalysisFurnitureController.java +++ b/src/main/java/com/rj/controller/AudioTextAnalysisFurnitureController.java @@ -1,8 +1,20 @@ package com.rj.controller; +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.common.Message; +import com.alibaba.dashscope.common.Role; +import com.alibaba.dashscope.exception.InputRequiredException; +import com.alibaba.dashscope.exception.NoApiKeyException; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.rj.entity.AudioManagement; import com.rj.entity.AudioTextAnalysisFurniture; +import com.rj.service.IAudioManagementService; import com.rj.service.IAudioTextAnalysisFurnitureService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -14,6 +26,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.time.LocalDateTime; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -31,6 +44,12 @@ public class AudioTextAnalysisFurnitureController { @Autowired private IAudioTextAnalysisFurnitureService furnitureService; + @Autowired + private IAudioManagementService audioManagementService; + + @Autowired + private ObjectMapper objectMapper; + @PostMapping("/add") @Operation(summary = "新增记录", description = "新增一条家具意向分析记录") public ResponseEntity> add(@RequestBody AudioTextAnalysisFurniture furniture) { @@ -179,5 +198,152 @@ public class AudioTextAnalysisFurnitureController { return ResponseEntity.internalServerError().body(result); } } + + @PostMapping("/generateSummary/{id}") + @Operation(summary = "生成总结", description = "根据ID获取recordingText,调用大模型生成总结并保存") + public ResponseEntity> generateSummary( + @Parameter(description = "家具意向分析记录ID", required = true) + @PathVariable String id) { + Map result = new HashMap<>(); + try { + // 1. 根据ID查询家具意向分析记录 + AudioManagement audioManagement = audioManagementService.getById(id); + if (audioManagement == null) { + result.put("success", false); + result.put("message", "记录不存在"); + return ResponseEntity.badRequest().body(result); + } + // 3. 获取录音文本 + String recordingText = audioManagement.getRecordingText(); + if (recordingText == null || recordingText.trim().isEmpty()) { + result.put("success", false); + result.put("message", "录音文本为空"); + return ResponseEntity.badRequest().body(result); + } + AudioTextAnalysisFurniture furniture = new AudioTextAnalysisFurniture(); + furniture.setRecordingText(recordingText); + // 4. 调用大模型生成总结 + generateSummaryByLLM(furniture); + + + audioManagement = new AudioManagement(); + audioManagement.setSummary(furniture.getSummarySentence()); + audioManagement.setId(id); + audioManagementService.updateById(audioManagement); + + + // 5. 保存总结到数据库 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(AudioTextAnalysisFurniture::getParentId, id); + AudioTextAnalysisFurniture one = furnitureService.getOne(wrapper); + if (one != null) { + furniture.setId(one.getId()); + } + + furniture.setUpdatedAt(LocalDateTime.now()); + furniture.setParentId( id); + furniture.setRecordingText(null); + boolean updateSuccess = furnitureService.saveOrUpdate(furniture); + + if (updateSuccess) { + result.put("success", true); + result.put("message", "生成总结成功"); + result.put("data", furniture); + return ResponseEntity.ok(result); + } else { + result.put("success", false); + result.put("message", "保存总结失败"); + return ResponseEntity.badRequest().body(result); + } + + } catch (Exception e) { + log.error("生成总结失败,ID: {}", id, e); + result.put("success", false); + result.put("message", "生成总结异常:" + e.getMessage()); + return ResponseEntity.internalServerError().body(result); + } + } + + /** + * 调用大模型生成总结 + * + * @param + * @return 生成的总结 + */ + private void generateSummaryByLLM(AudioTextAnalysisFurniture furniture) { + Generation gen = new Generation(); + Message systemMsg = Message.builder() + .role(Role.SYSTEM.getValue()) + .content("你是一个专业的家居销售顾问助手,需要从录音文本中抽取结构化信息并输出JSON。必须严格按照要求输出JSON,不能包含额外文字。") + .build(); + Message userMsg = Message.builder() + .role(Role.USER.getValue()) + .content(buildPrompt(furniture.getRecordingText())) + .build(); + + GenerationParam param = GenerationParam.builder() + .apiKey(System.getenv("DASHSCOPE_API_KEY")) + .model("qwen-plus") + .messages(Arrays.asList(systemMsg, userMsg)) + .resultFormat(GenerationParam.ResultFormat.MESSAGE) + .build(); + + try { + GenerationResult call = gen.call(param); + String rawContent = call.getOutput().getChoices().get(0).getMessage().getContent(); + log.info("大模型生成总结完成,结果长度: {},内容是:{}", rawContent.length(), rawContent); + applyStructuredResult(furniture, rawContent); + } catch (NoApiKeyException e) { + log.error("API密钥未配置", e); + throw new RuntimeException("API密钥未配置: " + e.getMessage(), e); + } catch (InputRequiredException e) { + log.error("输入参数错误", e); + throw new RuntimeException("输入参数错误: " + e.getMessage(), e); + } catch (Exception e) { + log.error("调用大模型生成总结失败", e); + throw new RuntimeException("调用大模型失败: " + e.getMessage(), e); + } + } + + private String buildPrompt(String recordingText) { + return "请阅读以下录音文本,从中提取客户信息并生成 JSON。字段要求:\n" + + "1. customer_type:可选值仅限「新房装修」「二次翻修」「补充家居」,从谈话中判断最匹配的一个;\n" + + "2. family_structure:描述家庭人数、孩子数量、老人情况,必须突出家庭成员组成;\n" + + "3. intention_products:只列出客户提到的家居类产品(如床、柜子、沙发等),多个产品用顿号或逗号分隔;\n" + + "4. summary:一句话总结客户需求,必须提到预算(若未提及则默认为3万元),还需涵盖客户类型、装修风格偏好及主要意向产品。\n" + + "严格输出 JSON,如:{\"customer_type\":\"二次翻修\",\"family_structure\":\"三口之家,一个小男孩\",\"intention_products\":\"床、沙发、酒柜、厨房柜子\",\"summary\":\"...\"}\n" + + "不要添加任何额外文字。\n\n录音文本:\n" + recordingText; + } + + private void applyStructuredResult(AudioTextAnalysisFurniture furniture, String rawContent) { + String normalized = normalizeJson(rawContent); + try { + JsonNode root = objectMapper.readTree(normalized); + furniture.setCustomerType(textValue(root, "customer_type")); + furniture.setFamilyStructure(textValue(root, "family_structure")); + furniture.setIntentionProducts(textValue(root, "intention_products")); + furniture.setSummarySentence(textValue(root, "summary")); + } catch (JsonProcessingException e) { + log.warn("解析大模型返回JSON失败,使用原始内容作为总结: {}", e.getMessage()); + furniture.setSummarySentence(rawContent); + } + } + + private String normalizeJson(String content) { + String trimmed = content.trim(); + if (trimmed.startsWith("```")) { + int firstLineBreak = trimmed.indexOf('\n'); + int lastFence = trimmed.lastIndexOf("```"); + if (firstLineBreak >= 0 && lastFence > firstLineBreak) { + trimmed = trimmed.substring(firstLineBreak + 1, lastFence).trim(); + } + } + return trimmed; + } + + private String textValue(JsonNode root, String field) { + JsonNode node = root.get(field); + return node == null || node.isNull() ? null : node.asText(); + } } diff --git a/src/main/java/com/rj/entity/AudioManagement.java b/src/main/java/com/rj/entity/AudioManagement.java index 507b1f2..00da749 100644 --- a/src/main/java/com/rj/entity/AudioManagement.java +++ b/src/main/java/com/rj/entity/AudioManagement.java @@ -167,6 +167,10 @@ public class AudioManagement implements Serializable { @TableField("recording_text") private String recordingText; + @Schema(description = "概要总结") + @TableField("summary") + private String summary; + /** * 前端上传的录音文件 * 此字段不保存在数据库中,仅用于接收前端上传的文件 diff --git a/src/main/java/com/rj/entity/AudioTextAnalysisFurniture.java b/src/main/java/com/rj/entity/AudioTextAnalysisFurniture.java index 5daa9e3..aef7660 100644 --- a/src/main/java/com/rj/entity/AudioTextAnalysisFurniture.java +++ b/src/main/java/com/rj/entity/AudioTextAnalysisFurniture.java @@ -1,5 +1,6 @@ package com.rj.entity; +import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; @@ -24,7 +25,7 @@ public class AudioTextAnalysisFurniture implements Serializable { private static final long serialVersionUID = 1L; @Schema(description = "记录唯一标识,存放UUID字符") - @TableId("id") + @TableId(type = IdType.ASSIGN_UUID) private String id; @Schema(description = "音频管理ID/父级ID") @@ -111,6 +112,10 @@ public class AudioTextAnalysisFurniture implements Serializable { @TableField("customer_id") private String customerId; + @Schema(description = "录音文本") + @TableField(exist = false) + private String recordingText; + @Schema(description = "创建时间") @TableField("created_at") private LocalDateTime createdAt; diff --git a/src/main/resources/mapper/AudioTextAnalysisFurnitureMapper.xml b/src/main/resources/mapper/AudioTextAnalysisFurnitureMapper.xml new file mode 100644 index 0000000..b3b5662 --- /dev/null +++ b/src/main/resources/mapper/AudioTextAnalysisFurnitureMapper.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + id, parent_id, customer_type, family_structure, intention_products, + decoration_style, summary_sentence, sofa, tea_table, dining_table, + study_desk, tv_cabinet, cabinet, wine_cabinet, master_bedroom_cabinet, + secondary_bedroom_cabinet, shoe_cabinet, bed_and_mattress, + owner_phone, owner_id, customer_phone, customer_id, created_at, updated_at + + + diff --git a/src/main/sql/audio_segment.sql b/src/main/sql/audio_segment.sql new file mode 100644 index 0000000..51fc2f6 --- /dev/null +++ b/src/main/sql/audio_segment.sql @@ -0,0 +1,16 @@ +CREATE TABLE audio_segment ( + id CHAR(36) NOT NULL COMMENT '主键,存放UUID字符', + parent_id CHAR(36) NULL COMMENT '音频管理ID/父级ID', + segment_url VARCHAR(500) NULL COMMENT '音频分段访问URL', + segment_text TEXT NULL COMMENT '分段对应文本内容', + owner_phone VARCHAR(30) NULL COMMENT '所属人电话', + owner_id CHAR(36) NULL COMMENT '所属人ID', + duration int NULL COMMENT '时长', + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (id), + KEY idx_audio_segment_parent_id (parent_id) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = '音频分段信息表'; + + + diff --git a/src/main/sql/audio_text_analysis_furniture.sql b/src/main/sql/audio_text_analysis_furniture.sql new file mode 100644 index 0000000..3a382f4 --- /dev/null +++ b/src/main/sql/audio_text_analysis_furniture.sql @@ -0,0 +1,30 @@ +CREATE TABLE audio_text_analysis_furniture ( + id CHAR(36) NOT NULL COMMENT '记录唯一标识,存放UUID字符', + parent_id CHAR(36) NULL COMMENT '对应的音频管理或父级记录ID,便于树状关联', + customer_type VARCHAR(50) NULL COMMENT '客户类型(如个人、企业、设计师等)', + family_structure VARCHAR(100) NULL COMMENT '家庭结构描述(如二人家庭、四口之家)', + intention_products TEXT NULL COMMENT '意向产品清单,可存放多件物品的信息', + decoration_style VARCHAR(100) NULL COMMENT '偏好的装修/家居风格', + summary_sentence VARCHAR(255) NULL COMMENT '一句话总结客户需求或关键信息', + sofa VARCHAR(255) NULL COMMENT '沙发需求或配置说明', + tea_table VARCHAR(255) NULL COMMENT '茶几/茶桌需求或配置', + dining_table VARCHAR(255) NULL COMMENT '餐桌椅需求或配置', + study_desk VARCHAR(255) NULL COMMENT '学习桌/书桌需求', + tv_cabinet VARCHAR(255) NULL COMMENT '电视柜需求说明', + cabinet VARCHAR(255) NULL COMMENT '橱柜或厨房柜体说明', + wine_cabinet VARCHAR(255) NULL COMMENT '酒柜需求说明', + master_bedroom_cabinet VARCHAR(255) NULL COMMENT '主卧衣柜/储物柜配置', + secondary_bedroom_cabinet VARCHAR(255) NULL COMMENT '次卧衣柜/储物柜配置', + shoe_cabinet VARCHAR(255) NULL COMMENT '鞋柜需求或配置', + bed_and_mattress VARCHAR(255) NULL COMMENT '床及床垫配置', + owner_phone VARCHAR(30) NULL COMMENT '所属人或负责人的联系方式', + owner_id CHAR(36) NULL COMMENT '所属人或负责人的唯一标识', + customer_phone VARCHAR(30) NULL COMMENT '客户的联系方式', + customer_id CHAR(36) NULL COMMENT '客户的唯一标识', + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间', + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录最近修改时间', + PRIMARY KEY (id), + KEY idx_audio_text_analysis_furniture_parent_id (parent_id) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = '音频文本家居意向分析表'; + +