package com.emotion.service.impl; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; import com.emotion.entity.Message; import com.emotion.entity.Conversation; import com.emotion.service.AIChatService; import com.emotion.service.MessageService; import com.emotion.service.ConversationService; import com.emotion.service.CozeApiCallService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import java.time.LocalDateTime; import java.util.HashMap; import java.util.Map; import java.util.UUID; /** * AI聊天服务实现类 * * @author emotion-museum * @date 2025-07-24 */ @Slf4j @Service public class AiChatServiceImpl implements AIChatService { @Autowired private RestTemplate restTemplate; @Autowired private MessageService messageService; @Autowired private ConversationService conversationService; @Autowired private CozeApiCallService cozeApiCallService; @Value("${emotion.coze.api.token:}") private String cozeApiToken; @Value("${emotion.coze.api.base-url:https://api.coze.cn}") private String cozeBaseUrl; @Value("${emotion.coze.api.chat.talk.bot-id:}") private String chatBotId; @Value("${emotion.coze.api.chat.talk.workflow-id:}") private String chatWorkflowId; @Value("${emotion.coze.api.chat.summary.bot-id:}") private String summaryBotId; @Value("${emotion.coze.api.chat.summary.workflow-id:}") private String summaryWorkflowId; @Value("${emotion.coze.api.timeout:30000}") private int timeout; @Value("${emotion.coze.api.retry-count:3}") private int retryCount; @Value("${emotion.coze.api.retry-delay:1000}") private int retryDelay; private static final String DEFAULT_USER_ID = "emotion-museum-user"; @Override public String sendChatMessage(String conversationId, String message, String userId) { log.info("发送聊天消息: conversationId={}, userId={}, message={}", conversationId, userId, message); try { // 调用Coze API String aiReply = sendMessage(conversationId, message, userId); // 保存用户消息 Message userMessage = messageService.createMessage( conversationId, userId, message, "text", "user", userId); // 保存AI回复 Message aiMessage = messageService.createMessage( conversationId, "ai", aiReply, "text", "ai", "ai"); log.info("聊天消息处理完成: userMessageId={}, aiMessageId={}", userMessage.getId(), aiMessage.getId()); return aiReply; } catch (Exception e) { log.error("发送聊天消息失败", e); return "抱歉,我暂时无法回复,请稍后再试。"; } } @Override public String generateConversationSummary(String conversationId, String userId) { log.info("生成对话总结: conversationId={}, userId={}", conversationId, userId); try { // 获取对话历史 String conversationHistory = getConversationHistory(conversationId); // 构建总结请求 String summaryPrompt = "请为以下对话生成一个简洁的总结:\n\n" + conversationHistory; // 调用AI生成总结 - 使用专门的总结bot String summary = sendSummaryMessage(conversationId, summaryPrompt, userId); log.info("对话总结生成完成: conversationId={}", conversationId); return summary; } catch (Exception e) { log.error("生成对话总结失败", e); return "无法生成对话总结,请稍后再试。"; } } @Override public boolean isServiceAvailable() { try { // 简单的健康检查 return cozeApiToken != null && !cozeApiToken.isEmpty() && chatBotId != null && !chatBotId.isEmpty(); } catch (Exception e) { log.error("检查AI服务可用性失败", e); return false; } } @Override public String getServiceStatus() { if (isServiceAvailable()) { return "available"; } else { return "unavailable"; } } @Override public String sendMessage(String conversationId, String userMessage, String userId) { log.info("发送消息到Coze AI: conversationId={}, userId={}", conversationId, userId); try { // 构建请求头 HttpHeaders headers = new HttpHeaders(); headers.set("Authorization", "Bearer " + cozeApiToken); headers.set("Content-Type", "application/json"); // 构建请求体 - 参考backend-distributed的实现 Map requestBody = buildCozeRequest(conversationId, userMessage, userId); HttpEntity> request = new HttpEntity<>(requestBody, headers); // 构建完整的API URL String cozeApiUrl = cozeBaseUrl + "/api/message"; // 发送请求 ResponseEntity response = restTemplate.exchange( cozeApiUrl, HttpMethod.POST, request, String.class); // 解析响应 JSONObject responseJson = JSON.parseObject(response.getBody()); String aiReply = extractContentFromCozeResponse(responseJson); log.info("Coze AI响应成功: reply={}", aiReply); return aiReply; } catch (Exception e) { log.error("发送消息到Coze AI失败", e); return "抱歉,AI服务暂时不可用,请稍后再试。"; } } @Override public Map guestChat(String message, String clientIp) { log.info("访客聊天: message={}, clientIp={}", message, clientIp); Map result = new HashMap<>(); try { // 生成访客会话ID String guestConversationId = "guest_" + clientIp.replace(".", "_") + "_" + System.currentTimeMillis(); // 调用AI服务 String aiReply = sendMessage(guestConversationId, message, "guest"); // 保存访客消息 Message guestMessage = messageService.createMessage( guestConversationId, "guest", message, "text", "guest", clientIp); // 保存AI回复 Message aiMessage = messageService.createMessage( guestConversationId, "ai", aiReply, "text", "ai", "ai"); result.put("message", aiReply); result.put("messageId", aiMessage.getId()); result.put("timestamp", System.currentTimeMillis()); result.put("error", false); log.info("访客聊天处理完成: guestMessageId={}, aiMessageId={}", guestMessage.getId(), aiMessage.getId()); } catch (Exception e) { log.error("访客聊天失败", e); result.put("message", "抱歉,服务暂时不可用,请稍后再试。"); result.put("messageId", null); result.put("timestamp", System.currentTimeMillis()); result.put("error", true); } return result; } @Override public Map createConversation(String userId, String title) { log.info("创建对话: userId={}, title={}", userId, title); Map result = new HashMap<>(); try { // 创建数据库对话记录 String conversationId = UUID.randomUUID().toString(); // 调用数据库服务创建对话 Conversation conversation = conversationService.createConversation(userId, title, "user"); result.put("conversationId", conversation.getId()); result.put("title", title); result.put("userId", userId); result.put("createTime", System.currentTimeMillis()); result.put("success", true); log.info("对话创建成功: conversationId={}", conversation.getId()); } catch (Exception e) { log.error("创建对话失败", e); result.put("success", false); result.put("error", "创建对话失败"); } return result; } @Override public Map getGuestUserInfo(String clientIp) { log.info("获取访客用户信息: clientIp={}", clientIp); Map result = new HashMap<>(); try { // 生成访客用户信息 String guestId = "guest_" + clientIp.replace(".", "_"); String guestUsername = "访客_" + clientIp.substring(clientIp.lastIndexOf(".") + 1); result.put("id", guestId); result.put("username", guestUsername); result.put("nickname", guestUsername); result.put("type", "guest"); result.put("clientIp", clientIp); result.put("createTime", System.currentTimeMillis()); log.info("访客用户信息获取成功: guestId={}", guestId); } catch (Exception e) { log.error("获取访客用户信息失败", e); result.put("error", "获取用户信息失败"); } return result; } @Override public String streamChat(String conversationId, String message, String userId) { log.info("流式聊天: conversationId={}, userId={}", conversationId, userId); try { // 构建流式请求 Map requestBody = buildCozeRequest(conversationId, message, userId); requestBody.put("stream", true); // 这里应该实现流式处理,暂时降级到普通聊天 return sendMessage(conversationId, message, userId); } catch (Exception e) { log.error("流式聊天失败", e); return "抱歉,流式聊天暂时不可用,请稍后再试。"; } } @Override public boolean healthCheck() { try { // 调用Coze bot信息接口检查健康状态 HttpHeaders headers = new HttpHeaders(); headers.set("Authorization", "Bearer " + cozeApiToken); HttpEntity request = new HttpEntity<>(headers); ResponseEntity response = restTemplate.exchange( cozeBaseUrl + "/v1/bot/get_online_info?bot_id=" + chatBotId, HttpMethod.GET, request, String.class); JSONObject responseJson = JSON.parseObject(response.getBody()); return responseJson != null && responseJson.get("code") != null; } catch (Exception e) { log.error("健康检查失败: {}", e.getMessage()); return false; } } /** * 构建Coze API请求 - 参考backend-distributed的实现 */ private Map buildCozeRequest(String conversationId, String userMessage, String userId) { Map cozeRequest = new HashMap<>(); cozeRequest.put("bot_id", chatBotId); // 如果有workflow_id,则添加 if (chatWorkflowId != null && !chatWorkflowId.trim().isEmpty()) { cozeRequest.put("workflow_id", chatWorkflowId); } cozeRequest.put("user_id", userId != null ? userId : DEFAULT_USER_ID); cozeRequest.put("stream", false); // 构建消息内容 String message = userMessage; if (conversationId != null && !conversationId.trim().isEmpty()) { // 可以在这里添加上下文信息 message = "会话ID: " + conversationId + "\n\n用户消息: " + message; } // 添加聊天历史(简化版本) java.util.List> messages = new java.util.ArrayList<>(); // 添加当前消息 Map currentMsg = new HashMap<>(); currentMsg.put("role", "user"); currentMsg.put("content", message); currentMsg.put("content_type", "text"); currentMsg.put("type", "question"); messages.add(currentMsg); cozeRequest.put("additional_messages", messages); cozeRequest.put("parameters", new HashMap<>()); return cozeRequest; } /** * 从Coze响应中提取内容 */ private String extractContentFromCozeResponse(JSONObject responseJson) { try { if (responseJson != null && responseJson.get("data") != null) { JSONObject data = responseJson.getJSONObject("data"); // 根据Coze API响应格式解析内容 if (data.get("messages") != null) { java.util.List messages = data.getJSONArray("messages").toJavaList(JSONObject.class); for (JSONObject message : messages) { if ("assistant".equals(message.getString("role")) && "answer".equals(message.getString("type"))) { return message.getString("content"); } } } // 兼容旧格式 if (data.getString("reply") != null) { return data.getString("reply"); } } return "抱歉,我现在无法理解您的消息。"; } catch (Exception e) { log.error("解析Coze响应失败: {}", e.getMessage()); return "抱歉,响应解析出现问题。"; } } /** * 发送总结消息到Coze AI */ private String sendSummaryMessage(String conversationId, String userMessage, String userId) { log.info("发送总结消息到Coze AI: conversationId={}, userId={}", conversationId, userId); try { // 构建请求头 HttpHeaders headers = new HttpHeaders(); headers.set("Authorization", "Bearer " + cozeApiToken); headers.set("Content-Type", "application/json"); // 构建请求体 - 使用总结专用的bot和workflow Map requestBody = buildSummaryRequest(conversationId, userMessage, userId); HttpEntity> request = new HttpEntity<>(requestBody, headers); // 构建完整的API URL String cozeApiUrl = cozeBaseUrl + "/api/message"; // 发送请求 ResponseEntity response = restTemplate.exchange( cozeApiUrl, HttpMethod.POST, request, String.class); // 解析响应 JSONObject responseJson = JSON.parseObject(response.getBody()); String aiReply = extractContentFromCozeResponse(responseJson); log.info("Coze AI总结响应成功: reply={}", aiReply); return aiReply; } catch (Exception e) { log.error("发送总结消息到Coze AI失败", e); return "抱歉,AI总结服务暂时不可用,请稍后再试。"; } } /** * 构建总结请求 - 使用专门的总结bot和workflow */ private Map buildSummaryRequest(String conversationId, String userMessage, String userId) { Map cozeRequest = new HashMap<>(); cozeRequest.put("bot_id", summaryBotId); // 如果有总结workflow_id,则添加 if (summaryWorkflowId != null && !summaryWorkflowId.trim().isEmpty()) { cozeRequest.put("workflow_id", summaryWorkflowId); } cozeRequest.put("user_id", userId != null ? userId : DEFAULT_USER_ID); cozeRequest.put("stream", false); // 构建消息内容 String message = userMessage; if (conversationId != null && !conversationId.trim().isEmpty()) { // 可以在这里添加上下文信息 message = "会话ID: " + conversationId + "\n\n总结内容: " + message; } // 添加聊天历史(简化版本) java.util.List> messages = new java.util.ArrayList<>(); // 添加当前消息 Map currentMsg = new HashMap<>(); currentMsg.put("role", "user"); currentMsg.put("content", message); currentMsg.put("content_type", "text"); currentMsg.put("type", "question"); messages.add(currentMsg); cozeRequest.put("additional_messages", messages); cozeRequest.put("parameters", new HashMap<>()); return cozeRequest; } /** * 获取对话历史 */ private String getConversationHistory(String conversationId) { try { // 这里应该从数据库获取对话历史 // 暂时返回空字符串 return ""; } catch (Exception e) { log.error("获取对话历史失败", e); return ""; } } }