实现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:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user