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; } } }