实现Coze API调用记录功能

- 在AiChatServiceImpl中添加完整的API调用记录功能
- 每次调用Coze API时自动记录请求和响应信息
- 支持聊天和总结两种类型的API调用记录
- 记录详细信息包括:
  * 请求信息:URL、请求体、请求头、用户消息
  * 响应信息:HTTP状态码、响应体、响应头
  * Coze信息:Bot ID、Workflow ID、Chat ID、Conversation ID
  * 用户信息:用户ID、客户端IP、User Agent、会话ID
  * 性能指标:开始时间、结束时间、耗时、轮询次数
  * 状态跟踪:调用状态、最终状态、错误信息
  * 追踪信息:唯一追踪ID
- 添加集成测试验证记录功能
- 支持错误处理和异常情况记录
This commit is contained in:
2025-07-25 00:39:51 +08:00
parent c5ca1651db
commit 3292a74698
2 changed files with 425 additions and 2 deletions
@@ -4,6 +4,7 @@ import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.emotion.entity.Message;
import com.emotion.entity.Conversation;
import com.emotion.entity.CozeApiCall;
import com.emotion.service.AIChatService;
import com.emotion.service.MessageService;
import com.emotion.service.ConversationService;
@@ -17,9 +18,15 @@ import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* AI聊天服务实现类
@@ -170,6 +177,9 @@ public class AiChatServiceImpl implements AIChatService {
public String sendMessage(String conversationId, String userMessage, String userId) {
log.info("发送消息到Coze AI: conversationId={}, userId={}", conversationId, userId);
// 创建API调用记录
CozeApiCall apiCall = createApiCallRecord(conversationId, userMessage, userId, "chat");
try {
// 构建请求头
HttpHeaders headers = new HttpHeaders();
@@ -179,6 +189,9 @@ public class AiChatServiceImpl implements AIChatService {
// 构建请求体 - 使用正确的Coze API格式
Map<String, Object> requestBody = buildCozeRequest(conversationId, userMessage, userId);
// 更新API调用记录的请求信息
updateApiCallRequest(apiCall, cozeBaseUrl + chatPath, requestBody, headers);
HttpEntity<Map<String, Object>> request = new HttpEntity<>(requestBody, headers);
// 构建完整的API URL
@@ -194,23 +207,35 @@ public class AiChatServiceImpl implements AIChatService {
log.info("收到Coze初始响应: {}", response.getBody());
// 更新API调用记录的响应信息
updateApiCallResponse(apiCall, response);
// 解析响应获取chat_id和conversation_id
JSONObject responseJson = JSON.parseObject(response.getBody());
String chatId = extractChatIdFromResponse(responseJson);
String cozeConversationId = extractConversationIdFromResponse(responseJson);
if (chatId != null && cozeConversationId != null) {
// 更新API调用记录的Coze ID信息
updateApiCallCozeIds(apiCall, chatId, cozeConversationId);
// 轮询聊天状态直到完成并获取回复内容
String aiReply = waitForChatCompletion(chatId, cozeConversationId);
String aiReply = waitForChatCompletionWithTracking(chatId, cozeConversationId, apiCall);
log.info("Coze AI响应成功: reply={}", aiReply);
// 更新API调用记录的最终结果
updateApiCallSuccess(apiCall, aiReply);
return aiReply;
} else {
log.error("无法从Coze响应中获取chat_id或conversation_id");
updateApiCallError(apiCall, "INVALID_RESPONSE", "无法从Coze响应中获取chat_id或conversation_id");
return "抱歉,AI服务响应异常,请稍后再试。";
}
} catch (Exception e) {
log.error("发送消息到Coze AI失败", e);
updateApiCallError(apiCall, "REQUEST_FAILED", e.getMessage());
return "抱歉,AI服务暂时不可用,请稍后再试。";
}
}
@@ -550,6 +575,9 @@ public class AiChatServiceImpl implements AIChatService {
private String sendSummaryMessage(String conversationId, String userMessage, String userId) {
log.info("发送总结消息到Coze AI: conversationId={}, userId={}", conversationId, userId);
// 创建API调用记录
CozeApiCall apiCall = createSummaryApiCallRecord(conversationId, userMessage, userId, "summary");
try {
// 构建请求头
HttpHeaders headers = new HttpHeaders();
@@ -559,6 +587,9 @@ public class AiChatServiceImpl implements AIChatService {
// 构建请求体 - 使用总结专用的bot和workflow
Map<String, Object> requestBody = buildSummaryRequest(conversationId, userMessage, userId);
// 更新API调用记录的请求信息
updateApiCallRequest(apiCall, cozeBaseUrl + chatPath, requestBody, headers);
HttpEntity<Map<String, Object>> request = new HttpEntity<>(requestBody, headers);
// 构建完整的API URL
@@ -574,23 +605,35 @@ public class AiChatServiceImpl implements AIChatService {
log.info("收到Coze总结初始响应: {}", response.getBody());
// 更新API调用记录的响应信息
updateApiCallResponse(apiCall, response);
// 解析响应获取chat_id和conversation_id
JSONObject responseJson = JSON.parseObject(response.getBody());
String chatId = extractChatIdFromResponse(responseJson);
String cozeConversationId = extractConversationIdFromResponse(responseJson);
if (chatId != null && cozeConversationId != null) {
// 更新API调用记录的Coze ID信息
updateApiCallCozeIds(apiCall, chatId, cozeConversationId);
// 轮询聊天状态直到完成并获取回复内容
String aiReply = waitForChatCompletion(chatId, cozeConversationId);
String aiReply = waitForChatCompletionWithTracking(chatId, cozeConversationId, apiCall);
log.info("Coze AI总结响应成功: reply={}", aiReply);
// 更新API调用记录的最终结果
updateApiCallSuccess(apiCall, aiReply);
return aiReply;
} else {
log.error("无法从Coze总结响应中获取chat_id或conversation_id");
updateApiCallError(apiCall, "INVALID_RESPONSE", "无法从Coze总结响应中获取chat_id或conversation_id");
return "抱歉,AI总结服务响应异常,请稍后再试。";
}
} catch (Exception e) {
log.error("发送总结消息到Coze AI失败", e);
updateApiCallError(apiCall, "REQUEST_FAILED", e.getMessage());
return "抱歉,AI总结服务暂时不可用,请稍后再试。";
}
}
@@ -644,4 +687,214 @@ public class AiChatServiceImpl implements AIChatService {
return "";
}
}
/**
* 创建API调用记录
*/
private CozeApiCall createApiCallRecord(String conversationId, String userMessage, String userId, String requestType) {
CozeApiCall apiCall = CozeApiCall.builder()
.conversationId(conversationId)
.userId(userId)
.requestType(requestType)
.userMessage(userMessage)
.userMessageType("text")
.botId(chatBotId)
.workflowId(chatWorkflowId)
.status("pending")
.startTime(LocalDateTime.now())
.traceId(UUID.randomUUID().toString())
.build();
// 获取客户端信息
try {
HttpServletRequest request = getCurrentRequest();
if (request != null) {
apiCall.setClientIp(getClientIp(request));
apiCall.setUserAgent(request.getHeader("User-Agent"));
apiCall.setSessionId(request.getSession().getId());
}
} catch (Exception e) {
log.warn("获取客户端信息失败: {}", e.getMessage());
}
// 保存API调用记录
cozeApiCallService.save(apiCall);
log.info("创建API调用记录: id={}, traceId={}", apiCall.getId(), apiCall.getTraceId());
return apiCall;
}
/**
* 创建总结API调用记录
*/
private CozeApiCall createSummaryApiCallRecord(String conversationId, String userMessage, String userId, String requestType) {
CozeApiCall apiCall = CozeApiCall.builder()
.conversationId(conversationId)
.userId(userId)
.requestType(requestType)
.userMessage(userMessage)
.userMessageType("text")
.botId(summaryBotId)
.workflowId(summaryWorkflowId)
.status("pending")
.startTime(LocalDateTime.now())
.traceId(UUID.randomUUID().toString())
.build();
// 获取客户端信息
try {
HttpServletRequest request = getCurrentRequest();
if (request != null) {
apiCall.setClientIp(getClientIp(request));
apiCall.setUserAgent(request.getHeader("User-Agent"));
apiCall.setSessionId(request.getSession().getId());
}
} catch (Exception e) {
log.warn("获取客户端信息失败: {}", e.getMessage());
}
// 保存API调用记录
cozeApiCallService.save(apiCall);
log.info("创建总结API调用记录: id={}, traceId={}", apiCall.getId(), apiCall.getTraceId());
return apiCall;
}
/**
* 更新API调用记录的请求信息
*/
private void updateApiCallRequest(CozeApiCall apiCall, String requestUrl, Map<String, Object> requestBody, HttpHeaders headers) {
try {
apiCall.setRequestUrl(requestUrl);
apiCall.setRequestBody(JSON.toJSONString(requestBody));
apiCall.setRequestHeaders(JSON.toJSONString(headers.toSingleValueMap()));
cozeApiCallService.updateById(apiCall);
} catch (Exception e) {
log.error("更新API调用记录请求信息失败: {}", e.getMessage(), e);
}
}
/**
* 更新API调用记录的响应信息
*/
private void updateApiCallResponse(CozeApiCall apiCall, ResponseEntity<String> response) {
try {
apiCall.setResponseStatus(response.getStatusCodeValue());
apiCall.setResponseBody(response.getBody());
apiCall.setResponseHeaders(JSON.toJSONString(response.getHeaders().toSingleValueMap()));
cozeApiCallService.updateById(apiCall);
} catch (Exception e) {
log.error("更新API调用记录响应信息失败: {}", e.getMessage(), e);
}
}
/**
* 更新API调用记录的Coze ID信息
*/
private void updateApiCallCozeIds(CozeApiCall apiCall, String cozeChatId, String cozeConversationId) {
try {
apiCall.setCozeChatId(cozeChatId);
apiCall.setCozeConversationId(cozeConversationId);
cozeApiCallService.updateById(apiCall);
} catch (Exception e) {
log.error("更新API调用记录Coze ID信息失败: {}", e.getMessage(), e);
}
}
/**
* 更新API调用记录的成功结果
*/
private void updateApiCallSuccess(CozeApiCall apiCall, String aiReply) {
try {
LocalDateTime endTime = LocalDateTime.now();
long durationMs = java.time.Duration.between(apiCall.getStartTime(), endTime).toMillis();
apiCall.setAiReply(aiReply);
apiCall.setAiReplyType("text");
apiCall.setStatus("success");
apiCall.setFinalStatus("completed");
apiCall.setEndTime(endTime);
apiCall.setDurationMs((int) durationMs);
cozeApiCallService.updateById(apiCall);
log.info("API调用成功: id={}, duration={}ms", apiCall.getId(), durationMs);
} catch (Exception e) {
log.error("更新API调用记录成功结果失败: {}", e.getMessage(), e);
}
}
/**
* 更新API调用记录的错误信息
*/
private void updateApiCallError(CozeApiCall apiCall, String errorCode, String errorMessage) {
try {
LocalDateTime endTime = LocalDateTime.now();
long durationMs = java.time.Duration.between(apiCall.getStartTime(), endTime).toMillis();
apiCall.setErrorCode(errorCode);
apiCall.setErrorMessage(errorMessage);
apiCall.setStatus("failed");
apiCall.setFinalStatus("failed");
apiCall.setEndTime(endTime);
apiCall.setDurationMs((int) durationMs);
cozeApiCallService.updateById(apiCall);
log.error("API调用失败: id={}, errorCode={}, errorMessage={}", apiCall.getId(), errorCode, errorMessage);
} catch (Exception e) {
log.error("更新API调用记录错误信息失败: {}", e.getMessage(), e);
}
}
/**
* 获取当前HTTP请求
*/
private HttpServletRequest getCurrentRequest() {
try {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
return attributes != null ? attributes.getRequest() : null;
} catch (Exception e) {
return null;
}
}
/**
* 获取客户端IP地址
*/
private String getClientIp(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
/**
* 带跟踪的轮询聊天完成状态
*/
private String waitForChatCompletionWithTracking(String chatId, String cozeConversationId, CozeApiCall apiCall) {
try {
// 更新轮询开始时间
apiCall.setPollStartTime(LocalDateTime.now());
apiCall.setPollCount(0);
cozeApiCallService.updateById(apiCall);
// 调用原有的轮询方法
String result = waitForChatCompletion(chatId, cozeConversationId);
// 更新轮询结束时间
apiCall.setPollEndTime(LocalDateTime.now());
cozeApiCallService.updateById(apiCall);
return result;
} catch (Exception e) {
log.error("轮询聊天完成状态失败", e);
throw e;
}
}
}