136 lines
6.3 KiB
Java
136 lines
6.3 KiB
Java
package com.company.agentfoundation.integration.wecom;
|
|
|
|
import com.company.agentfoundation.config.FoundationProperties;
|
|
import com.company.agentfoundation.domain.ChatRequest;
|
|
import com.company.agentfoundation.domain.ChatResponse;
|
|
import com.company.agentfoundation.security.AgentContext;
|
|
import com.company.agentfoundation.service.ChatService;
|
|
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
import org.springframework.http.MediaType;
|
|
import org.springframework.web.bind.annotation.GetMapping;
|
|
import org.springframework.web.bind.annotation.PostMapping;
|
|
import org.springframework.web.bind.annotation.RequestBody;
|
|
import org.springframework.web.bind.annotation.RequestMapping;
|
|
import org.springframework.web.bind.annotation.RequestParam;
|
|
import org.springframework.web.bind.annotation.RestController;
|
|
|
|
import java.util.List;
|
|
|
|
@RestController
|
|
@RequestMapping("/v1/integrations/wecom")
|
|
public class WecomCallbackController {
|
|
private static final Logger log = LoggerFactory.getLogger(WecomCallbackController.class);
|
|
private final FoundationProperties properties;
|
|
private final ChatService chatService;
|
|
private final WecomClient wecomClient;
|
|
private final WecomCryptoService wecomCryptoService;
|
|
private final XmlMapper xmlMapper = new XmlMapper();
|
|
|
|
public WecomCallbackController(
|
|
FoundationProperties properties,
|
|
ChatService chatService,
|
|
WecomClient wecomClient,
|
|
WecomCryptoService wecomCryptoService
|
|
) {
|
|
this.properties = properties;
|
|
this.chatService = chatService;
|
|
this.wecomClient = wecomClient;
|
|
this.wecomCryptoService = wecomCryptoService;
|
|
}
|
|
|
|
@GetMapping("/callback")
|
|
public String verify(
|
|
@RequestParam("msg_signature") String signature,
|
|
@RequestParam("timestamp") String timestamp,
|
|
@RequestParam("nonce") String nonce,
|
|
@RequestParam("echostr") String echostr
|
|
) {
|
|
log.info("wecom callback verify received safeMode={} timestamp={} nonce={}", wecomCryptoService.isSafeModeEnabled(), timestamp, nonce);
|
|
String payload = wecomCryptoService.isSafeModeEnabled() ? echostr : null;
|
|
if (!wecomCryptoService.validateSignature(signature, timestamp, nonce, payload)) {
|
|
log.warn("wecom callback verify signature invalid timestamp={} nonce={}", timestamp, nonce);
|
|
throw new IllegalArgumentException("Invalid WeCom signature");
|
|
}
|
|
log.info("wecom callback verify success safeMode={}", wecomCryptoService.isSafeModeEnabled());
|
|
return wecomCryptoService.isSafeModeEnabled() ? wecomCryptoService.decrypt(echostr) : echostr;
|
|
}
|
|
|
|
@PostMapping(value = "/callback", consumes = {MediaType.TEXT_XML_VALUE, MediaType.APPLICATION_XML_VALUE})
|
|
public String receive(
|
|
@RequestParam("msg_signature") String signature,
|
|
@RequestParam("timestamp") String timestamp,
|
|
@RequestParam("nonce") String nonce,
|
|
@RequestBody String body
|
|
) 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);
|
|
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())) {
|
|
log.info("wecom callback ignored non-text message");
|
|
return "success";
|
|
}
|
|
|
|
String userText = msg.getContent() == null ? "" : msg.getContent().trim();
|
|
if (userText.isBlank()) {
|
|
log.info("wecom callback ignored blank text");
|
|
return "success";
|
|
}
|
|
|
|
ChatRequest req = new ChatRequest(
|
|
null,
|
|
List.of(
|
|
new ChatRequest.Message("system", "You are an enterprise CRM assistant. Provide concise, actionable follow-up advice."),
|
|
new ChatRequest.Message("user", userText)
|
|
),
|
|
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);
|
|
String reply = (chat.content() == null || chat.content().isBlank())
|
|
? "Request received, but I could not generate advice right now."
|
|
: chat.content();
|
|
log.info("wecom callback agent response traceId={} replyPreview={}", chat.traceId(), preview(reply));
|
|
wecomClient.sendText(msg.getFromUserName(), reply);
|
|
log.info("wecom callback reply sent toUser={}", msg.getFromUserName());
|
|
return "success";
|
|
}
|
|
|
|
private WecomIncomingMessage parseIncomingMessage(String signature, String timestamp, String nonce, String body) throws Exception {
|
|
if (wecomCryptoService.isSafeModeEnabled()) {
|
|
WecomEncryptedEnvelope envelope = xmlMapper.readValue(body, WecomEncryptedEnvelope.class);
|
|
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");
|
|
}
|
|
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");
|
|
}
|
|
String plainXml = wecomCryptoService.decrypt(envelope.getEncrypt());
|
|
log.info("wecom callback decrypt success plainXmlLength={}", plainXml.length());
|
|
return xmlMapper.readValue(plainXml, WecomIncomingMessage.class);
|
|
}
|
|
|
|
if (!wecomCryptoService.validateSignature(signature, timestamp, nonce, null)) {
|
|
log.warn("wecom callback plain signature invalid timestamp={} nonce={}", timestamp, nonce);
|
|
throw new IllegalArgumentException("Invalid WeCom signature");
|
|
}
|
|
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) + "...";
|
|
}
|
|
}
|