家居大模型联调,分析客户产品需求,家庭结构和情感倾向
This commit is contained in:
@@ -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<Map<String, Object>> 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<Map<String, Object>> generateSummary(
|
||||
@Parameter(description = "家具意向分析记录ID", required = true)
|
||||
@PathVariable String id) {
|
||||
Map<String, Object> 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<AudioTextAnalysisFurniture> 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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -167,6 +167,10 @@ public class AudioManagement implements Serializable {
|
||||
@TableField("recording_text")
|
||||
private String recordingText;
|
||||
|
||||
@Schema(description = "概要总结")
|
||||
@TableField("summary")
|
||||
private String summary;
|
||||
|
||||
/**
|
||||
* 前端上传的录音文件
|
||||
* 此字段不保存在数据库中,仅用于接收前端上传的文件
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.rj.mapper.AudioTextAnalysisFurnitureMapper">
|
||||
|
||||
<resultMap id="BaseResultMap" type="com.rj.entity.AudioTextAnalysisFurniture">
|
||||
<id column="id" property="id"/>
|
||||
<result column="parent_id" property="parentId"/>
|
||||
<result column="customer_type" property="customerType"/>
|
||||
<result column="family_structure" property="familyStructure"/>
|
||||
<result column="intention_products" property="intentionProducts"/>
|
||||
<result column="decoration_style" property="decorationStyle"/>
|
||||
<result column="summary_sentence" property="summarySentence"/>
|
||||
<result column="sofa" property="sofa"/>
|
||||
<result column="tea_table" property="teaTable"/>
|
||||
<result column="dining_table" property="diningTable"/>
|
||||
<result column="study_desk" property="studyDesk"/>
|
||||
<result column="tv_cabinet" property="tvCabinet"/>
|
||||
<result column="cabinet" property="cabinet"/>
|
||||
<result column="wine_cabinet" property="wineCabinet"/>
|
||||
<result column="master_bedroom_cabinet" property="masterBedroomCabinet"/>
|
||||
<result column="secondary_bedroom_cabinet" property="secondaryBedroomCabinet"/>
|
||||
<result column="shoe_cabinet" property="shoeCabinet"/>
|
||||
<result column="bed_and_mattress" property="bedAndMattress"/>
|
||||
<result column="owner_phone" property="ownerPhone"/>
|
||||
<result column="owner_id" property="ownerId"/>
|
||||
<result column="customer_phone" property="customerPhone"/>
|
||||
<result column="customer_id" property="customerId"/>
|
||||
<result column="created_at" property="createdAt"/>
|
||||
<result column="updated_at" property="updatedAt"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="Base_Column_List">
|
||||
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
|
||||
</sql>
|
||||
|
||||
</mapper>
|
||||
16
src/main/sql/audio_segment.sql
Normal file
16
src/main/sql/audio_segment.sql
Normal file
@@ -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 = '音频分段信息表';
|
||||
|
||||
|
||||
|
||||
30
src/main/sql/audio_text_analysis_furniture.sql
Normal file
30
src/main/sql/audio_text_analysis_furniture.sql
Normal file
@@ -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 = '音频文本家居意向分析表';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user