package com.wecom.robot.service.impl; import com.wecom.robot.config.AiServiceConfig; import com.wecom.robot.dto.ai.ChatRequest; import com.wecom.robot.dto.ai.ChatResponse; import com.wecom.robot.service.AiServiceClient; import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker; import io.github.resilience4j.timelimiter.annotation.TimeLimiter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import java.util.concurrent.CompletableFuture; @Slf4j @Service @RequiredArgsConstructor public class AiServiceClientImpl implements AiServiceClient { private static final String CHAT_ENDPOINT = "/ai/chat"; private static final String HEALTH_ENDPOINT = "/ai/health"; private final AiServiceConfig aiServiceConfig; private final RestTemplate restTemplate; @Override @CircuitBreaker(name = "aiService", fallbackMethod = "generateReplyFallback") @TimeLimiter(name = "aiService") public CompletableFuture generateReply(ChatRequest request) { log.info("[AC-MCA-04] 调用 AI 服务: sessionId={}", request.getSessionId()); String url = aiServiceConfig.getUrl() + CHAT_ENDPOINT; HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); HttpEntity entity = new HttpEntity<>(request, headers); ResponseEntity response = restTemplate.postForEntity( url, entity, ChatResponse.class); log.info("[AC-MCA-05] AI 服务响应: sessionId={}, shouldTransfer={}", request.getSessionId(), response.getBody() != null ? response.getBody().getShouldTransfer() : null); return CompletableFuture.completedFuture(response.getBody()); } public CompletableFuture generateReplyFallback(ChatRequest request, Throwable cause) { log.warn("[AC-MCA-06][AC-MCA-07] AI 服务降级: sessionId={}, cause={}", request.getSessionId(), cause.getMessage()); ChatResponse fallbackResponse = ChatResponse.fallbackWithTransfer( "抱歉,我暂时无法回答您的问题,正在为您转接人工客服...", cause.getMessage() ); return CompletableFuture.completedFuture(fallbackResponse); } @Override public boolean healthCheck() { try { String url = aiServiceConfig.getUrl() + HEALTH_ENDPOINT; ResponseEntity response = restTemplate.getForEntity(url, String.class); return response.getStatusCode().is2xxSuccessful(); } catch (Exception e) { log.error("[AC-MCA-04] AI 服务健康检查失败: {}", e.getMessage()); return false; } } }