98 lines
3.7 KiB
Java
Raw Normal View History

2026-06-01 19:09:40 +08:00
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.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.HexFormat;
import java.util.List;
@RestController
@RequestMapping("/v1/integrations/wecom")
public class WecomCallbackController {
private final FoundationProperties properties;
private final ChatService chatService;
private final WecomClient wecomClient;
private final XmlMapper xmlMapper = new XmlMapper();
public WecomCallbackController(FoundationProperties properties, ChatService chatService, WecomClient wecomClient) {
this.properties = properties;
this.chatService = chatService;
this.wecomClient = wecomClient;
}
@GetMapping("/callback")
public String verify(@RequestParam("msg_signature") String signature,
@RequestParam("timestamp") String timestamp,
@RequestParam("nonce") String nonce,
@RequestParam("echostr") String echostr) {
if (!validSignature(signature, timestamp, nonce)) {
throw new IllegalArgumentException("Invalid WeCom signature");
}
return 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 {
if (!validSignature(signature, timestamp, nonce)) {
throw new IllegalArgumentException("Invalid WeCom signature");
}
WecomIncomingMessage msg = xmlMapper.readValue(body, WecomIncomingMessage.class);
if (msg == null || msg.getMsgType() == null || !"text".equalsIgnoreCase(msg.getMsgType())) {
return "success";
}
String userText = msg.getContent() == null ? "" : msg.getContent().trim();
if (userText.isBlank()) {
return "success";
}
ChatRequest req = new ChatRequest(
null,
List.of(
new ChatRequest.Message("system", "你是企业CRM智能助手。请给出简洁、可执行的客户跟进建议。"),
new ChatRequest.Message("user", userText)
),
0.3
);
ChatResponse chat = chatService.chat(new AgentContext(properties.getWecom().getCrmAgentId()), req);
String reply = (chat.content() == null || chat.content().isBlank()) ? "我已收到你的请求,但暂时无法生成建议。" : chat.content();
wecomClient.sendText(msg.getFromUserName(), reply);
return "success";
}
private boolean validSignature(String signature, String timestamp, String nonce) {
String token = properties.getWecom().getToken();
if (token == null || token.isBlank()) {
return false;
}
String[] arr = new String[]{token, timestamp, nonce};
Arrays.sort(arr);
String raw = String.join("", arr);
try {
MessageDigest digest = MessageDigest.getInstance("SHA-1");
byte[] sha1 = digest.digest(raw.getBytes(StandardCharsets.UTF_8));
String localSignature = HexFormat.of().formatHex(sha1);
return signature.equalsIgnoreCase(localSignature);
} catch (Exception e) {
return false;
}
}
}