秦芳睿
8 天以前 a7ae5db38611821cfa805556ed37065a256baefb
JAVA/SMTAIServer/src/main/java/com/smtaiserver/smtaiserver/control/SMTAIWeixinControl.java
@@ -1,343 +1,346 @@
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 com.smtaiserver.smtaiserver.core.SMTAIServerRequest;
import com.smtaiserver.smtaiserver.database.SMTDatabase;
import com.smtaiserver.smtaiserver.database.SMTDatabase.DBRecords;
import com.smtaiserver.smtaiserver.javaai.SMTJavaAIChat;
import com.smtaiserver.smtaiserver.util.SMTWXSStatic;
import com.smtservlet.util.Json;
import com.smtservlet.util.SMTJsonWriter;
import com.smtservlet.util.SMTStatic;
import java.util.*;
import javax.servlet.http.HttpServletRequest;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
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.jetbrains.annotations.NotNull;
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.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);
    private static final HashMap<String, String> asynchronousList = new HashMap<>();
    private static final String FROM_USER_NAME = "FromUserName";
    private static final String TO_USER_NAME = "ToUserName";
    private static final String CONTENT = "Content";
    private static final 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 final Object _lockToken = new Object();
    /**
     * 微信验证
     */
    public ModelAndView weChatNotify(SMTAIServerRequest tranReq) throws Exception {
        String method = tranReq.getRequest().getMethod();
        if (method.equals("GET")) return SMTWXSStatic.getModelAndView(tranReq);
        return reply(tranReq);
    }
  /** 被动回复 */
  private ModelAndView reply(SMTRequest tranReq) throws Exception {
  private ModelAndView reply(SMTAIServerRequest tranReq) {
    long l = System.currentTimeMillis() / 1000;
    String createTimeStr = String.valueOf(l);
    HttpServletRequest request = tranReq.getRequest();
    Map<String, String> requestMap = getWechatReqMap(request);
    Map<String, String> requestMap = SMTWXSStatic.getWechatReqMap(request);
    String fromUserName = requestMap.get(FROM_USER_NAME);
    String toUserName = requestMap.get(TO_USER_NAME);
    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;
    if (asynchronousList.get(fromUserName) != null && !reqContent.equals("停止输出")) {
      String dissuadeReturn = dissuadeReturn(fromUserName, toUserName, createTimeStr);
      return tranReq.returnText(dissuadeReturn);
    }
    UUID randomUuid = UUID.randomUUID();
    asynchronousList.put(fromUserName, randomUuid.toString());
    String reply;
    reply = getReply(reqContent);
    String textContent =
        "我正在思考哦~请稍等……\n<a href=\"weixin://bizmsgmenu?msgmenucontent=停止输出&msgmenuid=101\">停止输出</a>";
    String baiduTextContent = "我是百度\n<a href=\"www.baidu.com\">百度</a>";
    String result =
        getString(
            fromUserName,
            toUserName,
            createTimeStr,
            reply,
            null,
            "https://pics0.baidu.com/feed/d31b0ef41bd5ad6e9388c7f8d8eda0d4b6fd3c60.png@f_auto?token=3da8e06f44a46832a7d0f50fa9e92c34",
            "总书记引用的这些古语耐人寻味",
            "“没有规矩,不成方圆”、“己不正,焉能正人”、“尽小者大,慎微者著”……总书记引用的这些古语耐人寻味。",
            "https://www.baidu.com/s?&wd=%E6%80%BB%E4%B9%A6%E8%AE%B0%E5%BC%95%E7%94%A8%E7%9A%84%E8%BF%99%E4%BA%9B%E5%8F%A4%E8%AF%AD%E8%80%90%E4%BA%BA%E5%AF%BB%E5%91%B3",
            null);
    if (reqContent.equals("停止输出")) {
      asynchronousList.remove(fromUserName);
      _logger.info("用户停止输出");
      return null;
    }
    wechatMessage.setCreateTime(System.currentTimeMillis() / 1000);
    MediaVo media = new MediaVo();
  wechatMessage.setContent("我正在思考哦~请稍等……");
  // 异步调用 aiReplyToTheUserASecondTime
  CompletableFuture.runAsync(
      () -> {
        try {
          //aiReplyToTheUserASecondTime(tranReq, requestMap.get(FROM_USER_NAME));
           String answer = callAIForAnswerQuestion(reqContent);
        } catch (Exception e) {
          e.printStackTrace();
        }
      });
    // 设置消息内容
//    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);
        // 异步调用 aiReplyToTheUserASecondTime
//    CompletableFuture.runAsync(
//        () -> {
//          try {
//            SMTAIServerRequest threadTranReq = new SMTAIServerRequest();
//            threadTranReq.initInstance(null, "", null);
//            String answer = callAIForAnswerQuestion(reqContent, threadTranReq); // Ai调用 返回结果
//            aiReplyToTheUserASecondTime(
//                answer, requestMap.get(FROM_USER_NAME), randomUuid.toString());
//          } catch (Exception e) {
//            _logger.error("aiReplyToTheUserASecondTime error", e);
//            asynchronousList.remove(request.getParameter("FromUserName"));
//          }
//        });
        _logger.info("微信消息返参:" + result);
        // 返回 XML 字符串
        return tranReq.returnText(result);
    }
  }
  private String callAIForAnswerQuestion(String question)
  {
     return "ssss";
  }
  /**
   * 二次回复
   *
   * @param tranReq
   * @return
   * @throws Exception
   */
  public ModelAndView aiReplyToTheUserASecondTime(SMTRequest tranReq, String fromUserName)
//  public ModelAndView weChatTest(SMTAIServerRequest tranReq) throws Exception {
//    String question = "冰箱是什么";
//    tranReq.setAIQuestion(question);
//    tranReq.setTextResultMode();
//
//    Set<String> setAgentGroup = new HashSet<>();
//    String weixinGroupId =
//        (String) SMTAIServerApp.getApp().getGlobalConfig("weixin.group_id", false);
//    setAgentGroup.add(weixinGroupId);
//
//    SMTJavaAIChat.questionChat("aliyun", setAgentGroup, "业务场景", question, null, false, tranReq);
//    String text = tranReq.getTextResult();
//
//    return tranReq.returnText(text);
//  }
  /** ai回复 */
//  private String callAIForAnswerQuestion(String question, SMTAIServerRequest tranReq)
//      throws Exception {
//    tranReq.setAIQuestion(question);
//    tranReq.setTextResultMode();
//
//    Set<String> setAgentGroup = new HashSet<>();
//    String weixinGroupId =
//        (String) SMTAIServerApp.getApp().getGlobalConfig("weixin.group_id", false);
//    setAgentGroup.add(weixinGroupId);
//
//    SMTJavaAIChat.questionChat("aliyun", setAgentGroup, "业务场景", question, null, false, tranReq);
//    return tranReq.getTextResult();
//  }
  /** 二次回复 */
  public void aiReplyToTheUserASecondTime(String answer, String fromUserName, String abortID)
      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 accessToken = getAccessToken();
    if (answer.isEmpty()) answer = "抱歉,我暂时无法理解您的问题。";
    SMTJsonWriter jsonWr = new SMTJsonWriter(false);
    jsonWr.addKeyValue("touser", fromUserName);
    jsonWr.addKeyValue("msgtype", "text");
    jsonWr.beginMap("text");
    {
      jsonWr.addKeyValue("content", answer);
    }
    jsonWr.endMap();
    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<String, Object> 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);
    if (abortID.equals(asynchronousList.get(fromUserName))) {
      String s = SMTWXSStatic.sendPost(url, jsonWr.getRootJson());
      asynchronousList.remove(fromUserName);
      _logger.info("上传结果: : " + s);
    } else {
      asynchronousList.remove(fromUserName);
      _logger.info("异步调用被取消");
    }
    return tranReq.returnText("");
  }
  public static Map<String, Object> jsonObjectToMap(JSONObject jsonObject) throws JSONException {
    Map<String, Object> map = new HashMap<>();
    Iterator<String> keys = jsonObject.keys();
  /** 数据库获取 access_koen */
  public String getAccessToken() throws Exception {
    synchronized (this._lockToken) {
      SMTDatabase db = SMTAIServerApp.getApp().allocDatabase();
      //      try (SMTDatabase db = SMTAIServerApp.getApp().allocDatabase()) {
      HashMap<String, String> weixinParam = SMTWXSStatic.getWeixinParam();
      Date curTime = new Date();
      String appId = weixinParam.get("appId");
    while (keys.hasNext()) {
      String key = keys.next();
      Object value = jsonObject.get(key);
      // 查询未过期的 access_token
      DBRecords dbRecord =
          db.querySQL(
              "SELECT app_id, access_token, expires_time "
                  + "FROM ai_weixin_token "
                  + "WHERE app_id = ? "
                  + "  AND ? < expires_time",
              new Object[] {appId, curTime});
      // 如果值是 JSONObject,递归转换为 Map
      if (value instanceof JSONObject) {
        value = jsonObjectToMap((JSONObject) value);
      // 数据库无记录,从微信服务器获取 access_token
      List<SMTDatabase.DBRecord> records = dbRecord.getRecords();
      if (dbRecord.getRowCount() > 0) {
        return records.get(0).getString("access_token");
      }
      // 微信取,返回token并且保存或覆盖数据
      else {
      map.put(key, value);
    }
        Object[] accessToken = fetchAccessTokenFromWeixinServer(); // 从微信服务器获取 access_token
    return map;
  }
        Date expiresTime =
            SMTStatic.calculateTime(
                curTime, SMTStatic.SMTCalcTime.ADD_SECOND, ((int) accessToken[1]) / 2);
  /**
   * 验证签名
   *
   * @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<String, String> getWechatReqMap(HttpServletRequest request) {
    Map<String, String> requestMap = new HashMap<>();
    SAXReader reader = new SAXReader();
    try (ServletInputStream inputStream = request.getInputStream()) {
      Document document = reader.read(inputStream);
      Element root = document.getRootElement();
      List<Element> elementList = root.elements();
      for (Element e : elementList) {
        requestMap.put(e.getName(), e.getText());
        String sql =
            "INSERT INTO ai_weixin_token (app_id, access_token, expires_time) "
                + "VALUES (?, ?, ?) "
                + "ON CONFLICT (app_id) "
                + // 如果 app_id 冲突
                "DO UPDATE SET "
                + "    access_token = EXCLUDED.access_token, "
                + "    expires_time = EXCLUDED.expires_time;";
        db.executeSQL(sql, new Object[] {appId, accessToken[0], expiresTime});
        return (String) accessToken[0];
      }
    } catch (IOException | DocumentException e) {
      throw new RuntimeException(e);
      //      } catch (Exception e) {
      //        throw new Exception("Failed to get access token", 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;
  /** 从微信服务器获取 access_token */
  private Object[] fetchAccessTokenFromWeixinServer() throws Exception {
    HashMap<String, String> weixinParam = SMTWXSStatic.getWeixinParam();
    OkHttpClient okHttpClient = new OkHttpClient();
    // 创建请求
    Request request =
        new Request.Builder()
            .url(
                "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="
                    + weixinParam.get("appId")
                    + "&secret="
                    + weixinParam.get("secret")) // 请求URL
            .get() // 使用GET方法
            .build();
    Response response = okHttpClient.newCall(request).execute();
    if (!response.isSuccessful() || response.body() == null) {
      throw new Exception("can't get weixin token");
    }
    Json json = Json.read(response.body().string());
    String accessToken = json.safeGetStr("access_token", null);
    String expiresIn = json.safeGetStr("expires_in", null);
    if (accessToken != null) {
      return new Object[] {accessToken, SMTStatic.toInt(expiresIn)};
    } else {
      _logger.info("can't get weixin token : " + json);
      return null;
      //      throw new Exception("can't get weixin token : " + json);
    }
  }
  public static String sendPost(String urlString, Map<String, Object> 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);
    @NotNull
    private static String getString(String fromUserName, String toUserName, String createTimeStr, String msgType, String content, String mediaId, String title, String description, String musicUrl, String hqMusicUrl) {
        StringBuilder xmlBuilder = new StringBuilder();
        xmlBuilder.append("<xml>\n")
                .append("  <ToUserName><![CDATA[").append(fromUserName).append("]]></ToUserName>\n")
                .append("  <FromUserName><![CDATA[").append(toUserName).append("]]></FromUserName>\n")
                .append("  <CreateTime>").append(createTimeStr).append("</CreateTime>\n")
                .append("  <MsgType><![CDATA[").append(msgType).append("]]></MsgType>\n");
        switch (msgType) {
            case "text":
                xmlBuilder.append("  <Content><![CDATA[").append(content).append("]]></Content>\n");
                break;
            case "image":
                xmlBuilder.append("  <Image><MediaId><![CDATA[").append(mediaId).append("]]></MediaId></Image>\n");
                break;
            case "voice":
                xmlBuilder.append("  <Voice><MediaId><![CDATA[").append(mediaId).append("]]></MediaId></Voice>\n");
                break;
            case "video":
                xmlBuilder.append("  <Video>\n")
                        .append("    <MediaId><![CDATA[").append(mediaId).append("]]></MediaId>\n")
                        .append("    <Title><![CDATA[").append(title).append("]]></Title>\n")
                        .append("    <Description><![CDATA[").append(description).append("]]></Description>\n")
                        .append("  </Video>\n");
                break;
            case "music":
                xmlBuilder.append("  <Music>\n")
                        .append("    <Title><![CDATA[").append(title).append("]]></Title>\n")
                        .append("    <Description><![CDATA[").append(description).append("]]></Description>\n")
                        .append("    <MusicUrl><![CDATA[").append(musicUrl).append("]]></MusicUrl>\n")
                        .append("    <HQMusicUrl><![CDATA[").append(hqMusicUrl).append("]]></HQMusicUrl>\n")
                        .append("    <ThumbMediaId><![CDATA[").append(mediaId).append("]]></ThumbMediaId>\n")
                        .append("  </Music>\n");
                break;
            case "news": // 图文消息
                xmlBuilder.append("  <ArticleCount>2</ArticleCount>\n")
                        .append("  <Articles>\n")
                        .append("    <item>\n")
                        .append("      <Title><![CDATA[").append(title).append("]]></Title>\n")
                        .append("      <Description><![CDATA[").append(description).append("]]></Description>\n")
                        .append("      <PicUrl><![CDATA[").append(mediaId).append("]]></PicUrl>\n")
                        .append("      <Url><![CDATA[").append(musicUrl).append("]]></Url>\n")
                        .append("    </item>\n")
                        .append("  </Articles>\n");
                break;
            default:
                xmlBuilder.append("  <Content><![CDATA[未知的消息类型]]></Content>\n");
        }
        xmlBuilder.append("</xml>");
        return xmlBuilder.toString();
    }
    // 读取响应
    try (java.io.InputStream in = connection.getInputStream()) {
      java.util.Scanner scanner = new java.util.Scanner(in).useDelimiter("\\A");
      return scanner.hasNext() ? scanner.next() : "";
    /**
     * 用于测试,正常可以不用
     *
     * @param reqContent
     * @return
     */
    @NotNull
    private static String getReply(String reqContent) {
        String reply;
        switch (reqContent) {
            case "文字":
                reply = "text";
                break;
            case "图片":
                reply = "image";
                break;
            case "语音":
                reply = "voice";
                break;
            case "视频":
                reply = "video";
                break;
            case "音乐":
                reply = "music";
                break;
            case "图文":
                reply = "news";
                break;
            default:
                reply = "text";
                break;
        }
        return reply;
    }
  }
    private static String dissuadeReturn(
            String fromUserName, String toUserName, String createTimeStr) {
        String xmltemp =
                "<xml>\n"
                        + "  <ToUserName><![CDATA[{{{toUser}}}]]></ToUserName>\n"
                        + "  <FromUserName><![CDATA[{{{fromUser}}}]]></FromUserName>\n"
                        + "  <CreateTime>{{{CreateTime}}}</CreateTime>\n"
                        + "  <MsgType><![CDATA[text]]></MsgType>\n"
                        + "  <Content><![CDATA[上一条消息还在加载中哦,请稍等或点击下次停止上一轮回复\n<a href=\"weixin://bizmsgmenu?msgmenucontent=停止输出&msgmenuid=101\">停止输出</a>]]></Content>\n"
                        + "  <Content><![CDATA[]]></Content>\n"
                        + "</xml>";
        // 替换占位符
        return xmltemp
                .replace("{{{toUser}}}", fromUserName)
                .replace("{{{fromUser}}}", toUserName)
                .replace("{{{CreateTime}}}", createTimeStr);
    }
}