日志
This commit is contained in:
parent
4588888604
commit
ac18a2cc5c
@ -6,6 +6,8 @@ import com.company.agentfoundation.domain.ChatResponse;
|
|||||||
import com.company.agentfoundation.security.AgentContext;
|
import com.company.agentfoundation.security.AgentContext;
|
||||||
import com.company.agentfoundation.service.ChatService;
|
import com.company.agentfoundation.service.ChatService;
|
||||||
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
|
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
@ -19,6 +21,7 @@ import java.util.List;
|
|||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/v1/integrations/wecom")
|
@RequestMapping("/v1/integrations/wecom")
|
||||||
public class WecomCallbackController {
|
public class WecomCallbackController {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(WecomCallbackController.class);
|
||||||
private final FoundationProperties properties;
|
private final FoundationProperties properties;
|
||||||
private final ChatService chatService;
|
private final ChatService chatService;
|
||||||
private final WecomClient wecomClient;
|
private final WecomClient wecomClient;
|
||||||
@ -44,10 +47,13 @@ public class WecomCallbackController {
|
|||||||
@RequestParam("nonce") String nonce,
|
@RequestParam("nonce") String nonce,
|
||||||
@RequestParam("echostr") String echostr
|
@RequestParam("echostr") String echostr
|
||||||
) {
|
) {
|
||||||
|
log.info("wecom callback verify received safeMode={} timestamp={} nonce={}", wecomCryptoService.isSafeModeEnabled(), timestamp, nonce);
|
||||||
String payload = wecomCryptoService.isSafeModeEnabled() ? echostr : null;
|
String payload = wecomCryptoService.isSafeModeEnabled() ? echostr : null;
|
||||||
if (!wecomCryptoService.validateSignature(signature, timestamp, nonce, payload)) {
|
if (!wecomCryptoService.validateSignature(signature, timestamp, nonce, payload)) {
|
||||||
|
log.warn("wecom callback verify signature invalid timestamp={} nonce={}", timestamp, nonce);
|
||||||
throw new IllegalArgumentException("Invalid WeCom signature");
|
throw new IllegalArgumentException("Invalid WeCom signature");
|
||||||
}
|
}
|
||||||
|
log.info("wecom callback verify success safeMode={}", wecomCryptoService.isSafeModeEnabled());
|
||||||
return wecomCryptoService.isSafeModeEnabled() ? wecomCryptoService.decrypt(echostr) : echostr;
|
return wecomCryptoService.isSafeModeEnabled() ? wecomCryptoService.decrypt(echostr) : echostr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,13 +64,22 @@ public class WecomCallbackController {
|
|||||||
@RequestParam("nonce") String nonce,
|
@RequestParam("nonce") String nonce,
|
||||||
@RequestBody String body
|
@RequestBody String body
|
||||||
) throws Exception {
|
) throws Exception {
|
||||||
|
log.info("wecom callback message received safeMode={} timestamp={} nonce={} bodyLength={}",
|
||||||
|
wecomCryptoService.isSafeModeEnabled(), timestamp, nonce, body == null ? 0 : body.length());
|
||||||
WecomIncomingMessage msg = parseIncomingMessage(signature, timestamp, nonce, body);
|
WecomIncomingMessage msg = parseIncomingMessage(signature, timestamp, nonce, body);
|
||||||
|
log.info("wecom callback parsed fromUser={} toUser={} msgType={} contentPreview={}",
|
||||||
|
msg == null ? null : msg.getFromUserName(),
|
||||||
|
msg == null ? null : msg.getToUserName(),
|
||||||
|
msg == null ? null : msg.getMsgType(),
|
||||||
|
preview(msg == null ? null : msg.getContent()));
|
||||||
if (msg == null || msg.getMsgType() == null || !"text".equalsIgnoreCase(msg.getMsgType())) {
|
if (msg == null || msg.getMsgType() == null || !"text".equalsIgnoreCase(msg.getMsgType())) {
|
||||||
|
log.info("wecom callback ignored non-text message");
|
||||||
return "success";
|
return "success";
|
||||||
}
|
}
|
||||||
|
|
||||||
String userText = msg.getContent() == null ? "" : msg.getContent().trim();
|
String userText = msg.getContent() == null ? "" : msg.getContent().trim();
|
||||||
if (userText.isBlank()) {
|
if (userText.isBlank()) {
|
||||||
|
log.info("wecom callback ignored blank text");
|
||||||
return "success";
|
return "success";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,11 +92,14 @@ public class WecomCallbackController {
|
|||||||
0.3
|
0.3
|
||||||
);
|
);
|
||||||
|
|
||||||
|
log.info("wecom callback invoking crm agent agentId={} fromUser={}", properties.getWecom().getCrmAgentId(), msg.getFromUserName());
|
||||||
ChatResponse chat = chatService.chat(new AgentContext(properties.getWecom().getCrmAgentId()), req);
|
ChatResponse chat = chatService.chat(new AgentContext(properties.getWecom().getCrmAgentId()), req);
|
||||||
String reply = (chat.content() == null || chat.content().isBlank())
|
String reply = (chat.content() == null || chat.content().isBlank())
|
||||||
? "Request received, but I could not generate advice right now."
|
? "Request received, but I could not generate advice right now."
|
||||||
: chat.content();
|
: chat.content();
|
||||||
|
log.info("wecom callback agent response traceId={} replyPreview={}", chat.traceId(), preview(reply));
|
||||||
wecomClient.sendText(msg.getFromUserName(), reply);
|
wecomClient.sendText(msg.getFromUserName(), reply);
|
||||||
|
log.info("wecom callback reply sent toUser={}", msg.getFromUserName());
|
||||||
return "success";
|
return "success";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,18 +107,29 @@ public class WecomCallbackController {
|
|||||||
if (wecomCryptoService.isSafeModeEnabled()) {
|
if (wecomCryptoService.isSafeModeEnabled()) {
|
||||||
WecomEncryptedEnvelope envelope = xmlMapper.readValue(body, WecomEncryptedEnvelope.class);
|
WecomEncryptedEnvelope envelope = xmlMapper.readValue(body, WecomEncryptedEnvelope.class);
|
||||||
if (envelope == null || envelope.getEncrypt() == null || envelope.getEncrypt().isBlank()) {
|
if (envelope == null || envelope.getEncrypt() == null || envelope.getEncrypt().isBlank()) {
|
||||||
|
log.warn("wecom callback encrypted envelope missing encrypt field");
|
||||||
throw new IllegalArgumentException("Missing WeCom encrypted payload");
|
throw new IllegalArgumentException("Missing WeCom encrypted payload");
|
||||||
}
|
}
|
||||||
if (!wecomCryptoService.validateSignature(signature, timestamp, nonce, envelope.getEncrypt())) {
|
if (!wecomCryptoService.validateSignature(signature, timestamp, nonce, envelope.getEncrypt())) {
|
||||||
|
log.warn("wecom callback encrypted signature invalid timestamp={} nonce={}", timestamp, nonce);
|
||||||
throw new IllegalArgumentException("Invalid WeCom signature");
|
throw new IllegalArgumentException("Invalid WeCom signature");
|
||||||
}
|
}
|
||||||
String plainXml = wecomCryptoService.decrypt(envelope.getEncrypt());
|
String plainXml = wecomCryptoService.decrypt(envelope.getEncrypt());
|
||||||
|
log.info("wecom callback decrypt success plainXmlLength={}", plainXml.length());
|
||||||
return xmlMapper.readValue(plainXml, WecomIncomingMessage.class);
|
return xmlMapper.readValue(plainXml, WecomIncomingMessage.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!wecomCryptoService.validateSignature(signature, timestamp, nonce, null)) {
|
if (!wecomCryptoService.validateSignature(signature, timestamp, nonce, null)) {
|
||||||
|
log.warn("wecom callback plain signature invalid timestamp={} nonce={}", timestamp, nonce);
|
||||||
throw new IllegalArgumentException("Invalid WeCom signature");
|
throw new IllegalArgumentException("Invalid WeCom signature");
|
||||||
}
|
}
|
||||||
return xmlMapper.readValue(body, WecomIncomingMessage.class);
|
return xmlMapper.readValue(body, WecomIncomingMessage.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String preview(String text) {
|
||||||
|
if (text == null || text.isBlank()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return text.length() <= 80 ? text : text.substring(0, 80) + "...";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
package com.company.agentfoundation.integration.wecom;
|
package com.company.agentfoundation.integration.wecom;
|
||||||
|
|
||||||
import com.company.agentfoundation.config.FoundationProperties;
|
import com.company.agentfoundation.config.FoundationProperties;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.web.reactive.function.client.WebClient;
|
import org.springframework.web.reactive.function.client.WebClient;
|
||||||
@ -9,6 +11,7 @@ import java.time.Instant;
|
|||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class WecomClient {
|
public class WecomClient {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(WecomClient.class);
|
||||||
private final FoundationProperties properties;
|
private final FoundationProperties properties;
|
||||||
private final WebClient webClient;
|
private final WebClient webClient;
|
||||||
|
|
||||||
@ -21,6 +24,7 @@ public class WecomClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void sendText(String toUser, String content) {
|
public void sendText(String toUser, String content) {
|
||||||
|
log.info("wecom sendText start toUser={} agentId={} contentPreview={}", toUser, properties.getWecom().getAgentId(), preview(content));
|
||||||
String token = getAccessToken();
|
String token = getAccessToken();
|
||||||
WecomSendMessageRequest req = new WecomSendMessageRequest();
|
WecomSendMessageRequest req = new WecomSendMessageRequest();
|
||||||
req.setToUser(toUser);
|
req.setToUser(toUser);
|
||||||
@ -38,12 +42,15 @@ public class WecomClient {
|
|||||||
.block();
|
.block();
|
||||||
|
|
||||||
if (response == null || !response.success()) {
|
if (response == null || !response.success()) {
|
||||||
|
log.error("wecom sendText failed toUser={} err={}", toUser, response == null ? "null" : response.errmsg());
|
||||||
throw new IllegalStateException("WeCom send failed: " + (response == null ? "null" : response.errmsg()));
|
throw new IllegalStateException("WeCom send failed: " + (response == null ? "null" : response.errmsg()));
|
||||||
}
|
}
|
||||||
|
log.info("wecom sendText success toUser={}", toUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getAccessToken() {
|
private String getAccessToken() {
|
||||||
if (cachedToken != null && Instant.now().isBefore(tokenExpireAt)) {
|
if (cachedToken != null && Instant.now().isBefore(tokenExpireAt)) {
|
||||||
|
log.info("wecom gettoken use cached token expireAt={}", tokenExpireAt);
|
||||||
return cachedToken;
|
return cachedToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,12 +64,21 @@ public class WecomClient {
|
|||||||
.block();
|
.block();
|
||||||
|
|
||||||
if (response == null || !response.success() || response.accessToken() == null) {
|
if (response == null || !response.success() || response.accessToken() == null) {
|
||||||
|
log.error("wecom gettoken failed err={}", response == null ? "null" : response.errmsg());
|
||||||
throw new IllegalStateException("WeCom gettoken failed: " + (response == null ? "null" : response.errmsg()));
|
throw new IllegalStateException("WeCom gettoken failed: " + (response == null ? "null" : response.errmsg()));
|
||||||
}
|
}
|
||||||
|
|
||||||
cachedToken = response.accessToken();
|
cachedToken = response.accessToken();
|
||||||
int expires = response.expiresIn() == null ? 7200 : response.expiresIn();
|
int expires = response.expiresIn() == null ? 7200 : response.expiresIn();
|
||||||
tokenExpireAt = Instant.now().plusSeconds(Math.max(300, expires - 120));
|
tokenExpireAt = Instant.now().plusSeconds(Math.max(300, expires - 120));
|
||||||
|
log.info("wecom gettoken success expireAt={}", tokenExpireAt);
|
||||||
return cachedToken;
|
return cachedToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String preview(String text) {
|
||||||
|
if (text == null || text.isBlank()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return text.length() <= 80 ? text : text.substring(0, 80) + "...";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,17 +5,21 @@ import com.company.agentfoundation.api.DeepseekClient;
|
|||||||
import com.company.agentfoundation.domain.ChatRequest;
|
import com.company.agentfoundation.domain.ChatRequest;
|
||||||
import com.company.agentfoundation.domain.ChatResponse;
|
import com.company.agentfoundation.domain.ChatResponse;
|
||||||
import com.company.agentfoundation.security.AgentContext;
|
import com.company.agentfoundation.security.AgentContext;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
@Service
|
@Service
|
||||||
public class ChatService {
|
public class ChatService {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(ChatService.class);
|
||||||
private final DeepseekClient deepseekClient;
|
private final DeepseekClient deepseekClient;
|
||||||
private final ModelRouter modelRouter;
|
private final ModelRouter modelRouter;
|
||||||
public ChatService(DeepseekClient deepseekClient, ModelRouter modelRouter) { this.deepseekClient = deepseekClient; this.modelRouter = modelRouter; }
|
public ChatService(DeepseekClient deepseekClient, ModelRouter modelRouter) { this.deepseekClient = deepseekClient; this.modelRouter = modelRouter; }
|
||||||
public ChatResponse chat(AgentContext context, ChatRequest request) {
|
public ChatResponse chat(AgentContext context, ChatRequest request) {
|
||||||
String traceId = UUID.randomUUID().toString();
|
String traceId = UUID.randomUUID().toString();
|
||||||
String model = modelRouter.routeModel(context.agentId(), request.model());
|
String model = modelRouter.routeModel(context.agentId(), request.model());
|
||||||
|
log.info("chat start traceId={} agentId={} model={} messageCount={}", traceId, context.agentId(), model, request.messages() == null ? 0 : request.messages().size());
|
||||||
DeepseekChatResponse resp = deepseekClient.chat(new DeepseekChatRequest(
|
DeepseekChatResponse resp = deepseekClient.chat(new DeepseekChatRequest(
|
||||||
model,
|
model,
|
||||||
request.messages().stream().map(m -> new DeepseekChatRequest.Message(m.role(), m.content())).toList(),
|
request.messages().stream().map(m -> new DeepseekChatRequest.Message(m.role(), m.content())).toList(),
|
||||||
@ -25,6 +29,14 @@ public class ChatService {
|
|||||||
if (resp != null && resp.choices() != null && !resp.choices().isEmpty() && resp.choices().get(0).message() != null) content = resp.choices().get(0).message().content();
|
if (resp != null && resp.choices() != null && !resp.choices().isEmpty() && resp.choices().get(0).message() != null) content = resp.choices().get(0).message().content();
|
||||||
ChatResponse.Usage usage = null;
|
ChatResponse.Usage usage = null;
|
||||||
if (resp != null && resp.usage() != null) usage = new ChatResponse.Usage(resp.usage().prompt_tokens(), resp.usage().completion_tokens(), resp.usage().total_tokens());
|
if (resp != null && resp.usage() != null) usage = new ChatResponse.Usage(resp.usage().prompt_tokens(), resp.usage().completion_tokens(), resp.usage().total_tokens());
|
||||||
|
log.info("chat success traceId={} totalTokens={} replyPreview={}", traceId, usage == null ? null : usage.totalTokens(), preview(content));
|
||||||
return new ChatResponse(traceId, context.agentId(), model, content, usage, Instant.now());
|
return new ChatResponse(traceId, context.agentId(), model, content, usage, Instant.now());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String preview(String text) {
|
||||||
|
if (text == null || text.isBlank()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return text.length() <= 80 ? text : text.substring(0, 80) + "...";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user