feat: AI消息点赞/踩/取消点赞/取消踩/问题反馈信息保存功能完善

This commit is contained in:
郭诚奇
2025-12-04 14:25:39 +08:00
parent ee0a853b1b
commit b9fc27f4fb
4 changed files with 71 additions and 53 deletions

View File

@@ -89,6 +89,7 @@ public class CaseAiChatApi extends ApiBaseController {
@PostMapping("/likeMsg")
public JsonResponse<Boolean> likeMsg(@RequestBody CaseAiMsgLikeDto caseAiMsgLikeDto) {
try {
caseAiMsgLikeDto.setOperation(true);
if (caseAiChatService.msgFeedback(caseAiMsgLikeDto)) {
return success(true, "保存成功");
} else {
@@ -109,6 +110,7 @@ public class CaseAiChatApi extends ApiBaseController {
@PostMapping("/msgFeedback")
public JsonResponse<Boolean> msgFeedback(@RequestBody CaseAiMsgLikeDto caseAiMsgLikeDto) {
try {
caseAiMsgLikeDto.setOperation(false);
if (caseAiChatService.msgFeedback(caseAiMsgLikeDto)) {
return success(true, "保存成功");
} else {

View File

@@ -21,4 +21,12 @@ public class CaseAiMsgLikeDto {
* 反馈
*/
private String feedback;
/**
* 操作
* true: 点踩
* false: 反馈
* 为空:其他情况
*/
private Boolean operation;
}

View File

@@ -130,7 +130,7 @@ public class CaseAiChatServiceImpl implements ICaseAiChatService {
// 2. 查询历史
List<CaseAiMessageVo> historyMessages = elasticSearchIndexService.queryData(conversationId);
// 3. 构建请求参数
String userId = currentUser.getCode();
@@ -165,7 +165,7 @@ public class CaseAiChatServiceImpl implements ICaseAiChatService {
}
String chatParamStr = chatParam.toJSONString();
log.info("案例问答接口请求参数: [{}]", chatParamStr);
// 4. 设置请求头
String accessToken;
try {
@@ -195,7 +195,7 @@ public class CaseAiChatServiceImpl implements ICaseAiChatService {
RequestBody bodyRequestBody = RequestBody.create(chatParamStr, MediaType.parse("application/json"));
builder.post(bodyRequestBody);
Request request = builder.build();
// 7. 创建事件监听器
EventSourceListener listener = new EventSourceListener() {
@Override
@@ -257,14 +257,14 @@ public class CaseAiChatServiceImpl implements ICaseAiChatService {
@Override
public void onEvent(@NotNull EventSource eventSource, @Nullable String id, @Nullable String type, @NotNull String data) {
log.info("调用接口 [{}] 监听数据 id: [{}] type: [{}] data: [{}]", request.url(), id, type, data);
try {
// 解析返回的数据
JSONObject jsonData = JSONObject.parseObject(data);
if (jsonData.getBooleanValue("success") && jsonData.getIntValue("code") == 0) {
JSONObject responseData = jsonData.getJSONObject("data");
Integer status = responseData.getInteger("status");
if (status != null) {
CaseAiChatStatusEnum statusEnum = CaseAiChatStatusEnum.getByCode(status);
if (statusEnum == CaseAiChatStatusEnum.REFERS) { // 返回引用文件
@@ -350,7 +350,7 @@ public class CaseAiChatServiceImpl implements ICaseAiChatService {
saveConversationData(conversationData);
}
};
// 8. 执行HTTP请求
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(60, TimeUnit.SECONDS)
@@ -361,16 +361,16 @@ public class CaseAiChatServiceImpl implements ICaseAiChatService {
.build();
EventSource.Factory factory = EventSources.createFactory(client);
factory.newEventSource(request, listener);
return sseEmitter;
}
/**
* 获取或创建会话ID
*/
private String getOrCreateConversationId(CaseAiChatDto caseAiChatDto, CurrentUser currentUser) {
String conversationId = caseAiChatDto.getConversationId();
if (StringUtils.isEmpty(conversationId)) {
// 新会话,调用创建会话接口
String conversationName = "AI案例咨询-" + LocalDateTime.now().toString();
@@ -388,39 +388,39 @@ public class CaseAiChatServiceImpl implements ICaseAiChatService {
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
String url = caseAiProperties.getBaseUrl() + "/apigateway/knowledge/v1/conversation";
HttpPost httpPost = new HttpPost(url);
// 设置请求头
String accessToken = aiAccessTokenService.getAccessToken();
String apiCode = caseAiProperties.getChatApiCode();
httpPost.setHeader("access_token", accessToken);
httpPost.setHeader("X-AI-ApiCode", apiCode);
httpPost.setHeader("Content-Type", "application/json");
// 设置请求体
JSONObject requestBody = new JSONObject();
requestBody.put("userId", userId);
requestBody.put("name", conversationName);
StringEntity entity = new StringEntity(requestBody.toJSONString(), StandardCharsets.UTF_8);
httpPost.setEntity(entity);
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
int statusCode = response.getStatusLine().getStatusCode();
String responseBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
if (statusCode == 200) {
JSONObject result = JSONObject.parseObject(responseBody);
if (result.getIntValue("code") == 0 && result.getBooleanValue("success")) {
JSONObject data = result.getJSONObject("data");
String aiConversationId = data.getString("id");
String name = data.getString("name");
// 保存到数据库
CaseAiConversations conversation = new CaseAiConversations();
conversation.setAiConversationId(aiConversationId);
conversation.setConversationName(name);
conversation.setConversationUser(userId);
caseAiConversationsDao.save(conversation);
log.info("创建AI会话成功aiConversationId: {}, name: {}", aiConversationId, name);
return conversation;
} else {
@@ -503,44 +503,44 @@ public class CaseAiChatServiceImpl implements ICaseAiChatService {
CaseAiMessageVo messageVo = new CaseAiMessageVo();
messageVo.setQuery((String) sourceMap.get("query"));
messageVo.setAnswer((String) sourceMap.get("answer"));
// 解析 suggestions
Object suggestionsObj = sourceMap.get("suggestions");
if (suggestionsObj instanceof List) {
messageVo.setSuggestions((List<String>) suggestionsObj);
}
// 解析 caseRefer
Object caseReferObj = sourceMap.get("caseRefer");
if (caseReferObj instanceof List) {
List<CaseReferVo> caseReferList = new ArrayList<>();
List<Map<String, Object>> caseReferMaps = (List<Map<String, Object>>) caseReferObj;
for (Map<String, Object> caseReferMap : caseReferMaps) {
CaseReferVo caseRefer = new CaseReferVo();
caseRefer.setCaseId((String) caseReferMap.get("caseId"));
caseRefer.setTitle((String) caseReferMap.get("title"));
caseRefer.setAuthorName((String) caseReferMap.get("authorName"));
caseRefer.setContent((String) caseReferMap.get("content"));
// 解析 keywords
Object keywordsObj = caseReferMap.get("keywords");
if (keywordsObj instanceof List) {
caseRefer.setKeywords((List<String>) keywordsObj);
}
caseReferList.add(caseRefer);
}
messageVo.setCaseRefer(caseReferList);
}
return messageVo;
} catch (Exception e) {
log.error("解析ES消息数据异常", e);
return null;
}
}
/**
* 处理文件引用并构建返给前端的响应数据
*/
@@ -549,7 +549,7 @@ public class CaseAiChatServiceImpl implements ICaseAiChatService {
// 先处理文件引用收集CaseReferVo数据
List<CaseReferVo> currentCaseRefers = new ArrayList<>();
Set<String> docIds = new HashSet<>();
JSONObject fileRefer = responseData.getJSONObject("fileRefer");
if (fileRefer != null && fileRefer.containsKey("files")) {
JSONArray files = fileRefer.getJSONArray("files");
@@ -567,13 +567,13 @@ public class CaseAiChatServiceImpl implements ICaseAiChatService {
}
}
}
// 构建返给前端的数据结构
JSONObject data = new JSONObject();
data.put("status", 0);
data.put("conversationId", conversationData.getConversationId());
data.put("content", responseData.getString("content"));
// 添加处理后的案例引用数据
JSONArray caseReferArray = new JSONArray();
for (CaseReferVo caseRefer : currentCaseRefers) {
@@ -589,11 +589,11 @@ public class CaseAiChatServiceImpl implements ICaseAiChatService {
caseReferArray.add(caseReferObj);
}
// 构建新的fileRefer结构包含案例引用
JSONObject newFileRefer = new JSONObject();
newFileRefer.put("caseRefers", caseReferArray);
// 保留原始的docs和files信息如果需要
if (fileRefer != null) {
if (fileRefer.containsKey("docs")) {
@@ -603,19 +603,19 @@ public class CaseAiChatServiceImpl implements ICaseAiChatService {
newFileRefer.put("files", fileRefer.get("files"));
}
}
data.put("fileRefer", newFileRefer);
data.put("suggestions", responseData.get("suggestions"));
log.info("处理文件引用成功,返回 {} 个案例引用", currentCaseRefers.size());
return data;
} catch (Exception e) {
log.error("处理文件引用并构建响应数据异常", e);
return null;
}
}
/**
* 处理文件引用(原方法,保留用于数据收集)
*/
@@ -641,7 +641,7 @@ public class CaseAiChatServiceImpl implements ICaseAiChatService {
log.error("处理文件引用异常", e);
}
}
/**
* 处理建议
*/
@@ -660,7 +660,7 @@ public class CaseAiChatServiceImpl implements ICaseAiChatService {
log.error("处理建议异常", e);
}
}
/**
* 根据docId查询案例引用信息
*/
@@ -671,7 +671,7 @@ public class CaseAiChatServiceImpl implements ICaseAiChatService {
if (docLog == null) {
return null;
}
// 根据 case_id 查询案例详情
Cases caseEntity = casesDao.get(docLog.getCaseId());
if (caseEntity == null) {
@@ -688,7 +688,7 @@ public class CaseAiChatServiceImpl implements ICaseAiChatService {
caseRefer.setContent(caseEntity.getSummary());
caseRefer.setUploadTime(caseEntity.getSysCreateTime());
caseRefer.setOrgInfo(authorOrg.getName());
// 构建关键词列表
List<String> keywords = new ArrayList<>();
if (caseEntity.getKeyword1() != null) keywords.add(caseEntity.getKeyword1());
@@ -697,14 +697,14 @@ public class CaseAiChatServiceImpl implements ICaseAiChatService {
if (caseEntity.getKeyword4() != null) keywords.add(caseEntity.getKeyword4());
if (caseEntity.getKeyword5() != null) keywords.add(caseEntity.getKeyword5());
caseRefer.setKeywords(keywords);
return caseRefer;
} catch (Exception e) {
log.error("根据docId查询案例引用信息异常", e);
return null;
}
}
/**
* 当 SSE 失败时,作为普通 HTTP 请求处理
* 不再使用
@@ -717,12 +717,12 @@ public class CaseAiChatServiceImpl implements ICaseAiChatService {
.writeTimeout(60, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.build();
Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
String responseBody = response.body().string();
log.info("作为普通 HTTP 请求处理成功,将响应原封不动推送给前端");
// 将响应内容原封不动地推送到 SseEmitter
JSONObject responseData = JSONObject.parseObject(responseBody);
if (responseBody.contains("message")) {
@@ -810,9 +810,17 @@ public class CaseAiChatServiceImpl implements ICaseAiChatService {
log.error("操作失败docId为空");
return false;
}
if ((StringUtils.equalsAny(caseAiMsgLikeDto.getLikeStatus(), "0", "1", "-1")) || StringUtils.isBlank(caseAiMsgLikeDto.getFeedback())) {
log.error("操作失败,参数错误");
return false;
if (Boolean.TRUE.equals(caseAiMsgLikeDto.getOperation())) {
String likeStatus = caseAiMsgLikeDto.getLikeStatus();
if (!StringUtils.equals(likeStatus, "1") && !StringUtils.equals(likeStatus, "0") && !StringUtils.equals(likeStatus, "-1")) {
log.error("操作失败,参数错误");
return false;
}
} else if (Boolean.FALSE.equals(caseAiMsgLikeDto.getOperation())) {
if (StringUtils.isBlank(caseAiMsgLikeDto.getFeedback())) {
log.error("操作失败,参数错误");
return false;
}
}
aiChatConversationData.setLikeStatus(caseAiMsgLikeDto.getLikeStatus());
aiChatConversationData.setFeedback(caseAiMsgLikeDto.getFeedback());

View File

@@ -11,22 +11,22 @@ spring:
cloud:
nacos:
discovery:
server-addr: 192.168.0.253:8848
server-addr: 192.168.10.74:8848
config:
server-addr: 192.168.0.253:8848
server-addr: 192.168.10.74:8848
redis:
database: 1
host: 192.168.0.253
password: boe@123
port: 6379
database: 2
host: 39.104.123.58
password: Ebiz2020
port: 6378
jpa:
hibernate:
ddl-auto: none
datasource:
driverClassName: com.mysql.jdbc.Driver
url: jdbc:mysql://192.168.0.253:3306/boe_base?useSSL=false&useUnicode=true&characterEncoding=UTF8&zeroDateTimeBehavior=convertToNull
username: root
password: boe#1234A
url: jdbc:mysql://rm-hp3cpkk0u50q90eu9vo.mysql.huhehaote.rds.aliyuncs.com:3306/ebiz_doc_manage?characterEncoding=utf8&useUnicode=true&serverTimezone=Asia/Shanghai&useSSL=false&allowMultiQueries=true
username: ebiz_ai
password: ebiz_ai123
type: com.zaxxer.hikari.HikariDataSource
hikari:
auto-commit: true
@@ -36,7 +36,7 @@ spring:
max-lifetime: 1800000
maximum-pool-size: 20
activemq:
broker-url: tcp://192.168.0.253:61616
broker-url: tcp://192.168.10.74:61616
user: admin
password: admin
jms:
@@ -113,7 +113,7 @@ aop-log-record:
#不进行拦截的包或者类
excludeClassNames:
activemq:
broker-url: tcp://192.168.0.253:61616
broker-url: tcp://192.168.10.74:61616
user: admin
password: admin
elasticsearch: