package com.smtaiserver.smtaiserver.control; import com.smtaiserver.smtaiserver.core.SMTAIServerApp; import com.smtaiserver.smtaiserver.vo.MediaVo; import com.smtaiserver.smtaiserver.vo.WechatMessageVO; import com.smtservlet.core.SMTRequest; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import org.springframework.boot.configurationprocessor.json.JSONException; import org.springframework.boot.configurationprocessor.json.JSONObject; import org.springframework.http.HttpEntity; import org.springframework.web.servlet.ModelAndView; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import java.io.IOException; import java.io.OutputStream; import java.io.StringWriter; import java.net.HttpURLConnection; import java.net.URI; import java.net.URL; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; import static java.util.Arrays.sort; public class SMTAIWeixinControl { private static final String FROM_USER_NAME = "FromUserName"; private static final String TO_USER_NAME = "ToUserName"; private static final String CONTENT = "Content"; private final String accessToken = "89_pBAY9bgHt-7gBy2N8k_s31PMV3KFY1xgy_yCLw8eMHLqpugmHv2RXUgwf-2HC6bq5XPzVloiZp4ccnHDUfumjEOGxeFwFqIp8-gLU3OrPneiKgkv4l0mxd_orTAHNPgAJANTV"; private static Logger _logger = LogManager.getLogger(SMTAIServerControl.class); /** 微信验证 */ public ModelAndView weChatNotify(SMTRequest tranReq) throws Exception { String method = tranReq.getRequest().getMethod(); if (method.equals("GET")) return getModelAndView(tranReq); return reply(tranReq); } /** 被动回复 */ private ModelAndView reply(SMTRequest tranReq) throws Exception { HttpServletRequest request = tranReq.getRequest(); Map requestMap = getWechatReqMap(request); if (requestMap.isEmpty()) { return null; } String reqContent = requestMap.get(CONTENT); WechatMessageVO wechatMessage = new WechatMessageVO(); wechatMessage.setFromUserName(requestMap.get(TO_USER_NAME)); wechatMessage.setToUserName(requestMap.get(FROM_USER_NAME)); // 设置消息类型 switch (reqContent) { case "文字": wechatMessage.setMsgType("text"); break; case "图片": wechatMessage.setMsgType("image"); break; case "语音": wechatMessage.setMsgType("voice"); break; case "视频": wechatMessage.setMsgType("video"); break; case "音乐": wechatMessage.setMsgType("music"); break; case "图文": wechatMessage.setMsgType("news"); break; default: wechatMessage.setMsgType("text"); break; } wechatMessage.setCreateTime(System.currentTimeMillis() / 1000); MediaVo media = new MediaVo(); // 设置消息内容 switch (reqContent) { case "文字": wechatMessage.setContent("我正在思考哦~请稍等……"); // 异步调用 aiReplyToTheUserASecondTime CompletableFuture.runAsync( () -> { try { aiReplyToTheUserASecondTime(tranReq, requestMap.get(FROM_USER_NAME)); } catch (Exception e) { e.printStackTrace(); } }); break; case "图片": media.setMediaId("SI7HPwMI5PL1QV_I9M5AFw6K-1ZVMyTGE0-a5jQM4czTmffKTQpHa6zlYDmvIAPX"); wechatMessage.setImage(media); break; case "语音": media.setMediaId("c6hRH_X2HGwrOa1MiTQAcg35D7M42Xa4VMhyzSFMk8MA0pWFhly19W4K3W5NaH4b"); wechatMessage.setVoice(media); break; case "视频": media.setMediaId("c6hRH_X2HGwrOa1MiTQAcmo7zQjGAIV7uSP1U1S-tsnR0VJXUS0y10Z5FkaueU5Y"); media.setTitle("你好"); media.setDescription("你好啊"); wechatMessage.setVideo(media); break; case "音乐": System.out.println("进入音乐分支"); break; case "图文": System.out.println("进入图文分支"); break; default: System.out.println("进入默认分支"); wechatMessage.setContent("我不太认识你的输入哦"); break; } try { // 将 WechatMessageVO 对象转换为 XML JAXBContext jaxbContext = JAXBContext.newInstance(WechatMessageVO.class); Marshaller marshaller = jaxbContext.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); StringWriter sw = new StringWriter(); marshaller.marshal(wechatMessage, sw); _logger.info("微信消息返参:" + sw); // 返回 XML 字符串 return tranReq.returnText(sw.toString()); } catch (JAXBException e) { throw new RuntimeException(e); } } /** * 二次回复 * * @param tranReq * @return * @throws Exception */ public ModelAndView aiReplyToTheUserASecondTime(SMTRequest tranReq, String fromUserName) throws Exception { JSONObject jsonObject = new JSONObject(); jsonObject.put("touser", "oKAHz7LQhyL2IkcMpl8B7K4OotQk"); jsonObject.put("msgtype", "text"); JSONObject jsonObject1 = new JSONObject(); jsonObject1.put("content", "你好啊"); jsonObject.put("text", jsonObject1); _logger.info("jsonObject: {}", jsonObject); // 构建上传 URL String url = String.format( "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=%s", accessToken); // String body = HttpRequest.post(url).form(String.valueOf(jsonObject)).timeout(20000)//超时,毫秒 // .execute().body(); Map stringObjectMap = jsonObjectToMap(jsonObject); String s = sendPost(url, stringObjectMap); _logger.info("上传结果: {}", s); return null; } private static ModelAndView getModelAndView(SMTRequest tranReq) throws Exception { String signature = tranReq.convParamToString("signature", true); String timestamp = tranReq.convParamToString("timestamp", true); String nonce = tranReq.convParamToString("nonce", true); String echostr = tranReq.convParamToString("echostr", true); // 获取微信请求参数 _logger.info( "开始校验此次消息是否来自微信服务器,param->signature:{},\ntimestamp:{},\nnonce:{},\nechostr:{}", signature, timestamp, nonce, echostr); // 需要验证的时候就启用 if (checkSignature(signature, timestamp, nonce)) { return tranReq.returnText(echostr); } return tranReq.returnText(""); } public static Map jsonObjectToMap(JSONObject jsonObject) throws JSONException { Map map = new HashMap<>(); Iterator keys = jsonObject.keys(); while (keys.hasNext()) { String key = keys.next(); Object value = jsonObject.get(key); // 如果值是 JSONObject,递归转换为 Map if (value instanceof JSONObject) { value = jsonObjectToMap((JSONObject) value); } map.put(key, value); } return map; } /** * 验证签名 * * @param signature * @param timestamp * @param nonce * @return */ public static boolean checkSignature(String signature, String timestamp, String nonce) throws Exception { Object weixinParam = SMTAIServerApp.getApp().getGlobalConfig("weixin_core_param", "false"); _logger.info("微信参数:{}", weixinParam); JSONObject weixinJson = new JSONObject(weixinParam.toString()); Object appId = weixinJson.get("appId"); Object secret = weixinJson.get("secret"); String token = (String) weixinJson.get("token"); String[] arr = new String[] {token, timestamp, nonce}; // 将token、timestamp、nonce三个参数进行字典序排序 // Arrays.sort(arr); sort(arr); StringBuilder content = new StringBuilder(); for (String s : arr) { content.append(s); } MessageDigest md = null; String tmpStr = null; try { md = MessageDigest.getInstance("SHA-1"); // 将三个参数字符串拼接成一个字符串进行sha1加密 byte[] digest = md.digest(content.toString().getBytes()); tmpStr = byteToStr(digest); } catch (NoSuchAlgorithmException e) { _logger.error("签名异常", e); } content = null; // 将sha1加密后的字符串可与signature对比,标识该请求来源于微信 return tmpStr != null && tmpStr.equals(signature.toUpperCase()); } public static Map getWechatReqMap(HttpServletRequest request) { Map requestMap = new HashMap<>(); SAXReader reader = new SAXReader(); try (ServletInputStream inputStream = request.getInputStream()) { Document document = reader.read(inputStream); Element root = document.getRootElement(); List elementList = root.elements(); for (Element e : elementList) { requestMap.put(e.getName(), e.getText()); } } catch (IOException | DocumentException e) { throw new RuntimeException(e); } return requestMap; } /** * 将字节数组转换为十六进制字符串 * * @param byteArray * @return */ private static String byteToStr(byte[] byteArray) { StringBuilder strDigest = new StringBuilder(); for (byte b : byteArray) { strDigest.append(byteToHexStr(b)); } return strDigest.toString(); } /** * 将字节转换为十六进制字符串 * * @param mByte * @return */ private static String byteToHexStr(byte mByte) { char[] Digit = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; char[] tempArr = new char[2]; tempArr[0] = Digit[(mByte >>> 4) & 0X0F]; tempArr[1] = Digit[mByte & 0X0F]; String s = new String(tempArr); return s; } public static String sendPost(String urlString, Map params) throws Exception { // 将参数转换为 JSON 格式字符串 JSONObject jsonParams = new JSONObject(params); String payload = jsonParams.toString(); // 创建连接 URL url = new URL(urlString); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("POST"); // 设置请求头 connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8"); connection.setDoOutput(true); // 写入请求体 try (OutputStream os = connection.getOutputStream()) { byte[] input = payload.getBytes(StandardCharsets.UTF_8); os.write(input, 0, input.length); } // 读取响应 try (java.io.InputStream in = connection.getInputStream()) { java.util.Scanner scanner = new java.util.Scanner(in).useDelimiter("\\A"); return scanner.hasNext() ? scanner.next() : ""; } } }