From f24fcdf2e20eccf6b155e83cb11136503138d5f7 Mon Sep 17 00:00:00 2001
From: qfrjava <13402782+qfrjava@user.noreply.gitee.com>
Date: 星期四, 06 三月 2025 14:35:48 +0800
Subject: [PATCH] refactor(smtaiserver):重构微信控制类

---
 JAVA/SMTAIServer/src/main/java/com/smtaiserver/smtaiserver/control/SMTAIWeixinControl.java |  374 +++++++++++++++--------------------------------------
 1 files changed, 105 insertions(+), 269 deletions(-)

diff --git a/JAVA/SMTAIServer/src/main/java/com/smtaiserver/smtaiserver/control/SMTAIWeixinControl.java b/JAVA/SMTAIServer/src/main/java/com/smtaiserver/smtaiserver/control/SMTAIWeixinControl.java
index 2c2b918..d02c495 100644
--- a/JAVA/SMTAIServer/src/main/java/com/smtaiserver/smtaiserver/control/SMTAIWeixinControl.java
+++ b/JAVA/SMTAIServer/src/main/java/com/smtaiserver/smtaiserver/control/SMTAIWeixinControl.java
@@ -7,11 +7,13 @@
 import com.smtaiserver.smtaiserver.database.SMTDatabase.DBRecords;
 import com.smtaiserver.smtaiserver.javaai.ast.ASTDBMap;
 import com.smtaiserver.smtaiserver.javaai.llm.core.SMTLLMConnect;
+import com.smtaiserver.smtaiserver.util.SMTWXSStatic.WeixinuUtil;
 import com.smtservlet.core.SMTRequest;
 import com.smtservlet.util.Json;
 import com.smtservlet.util.SMTJsonWriter;
 import com.smtservlet.util.SMTStatic;
 
+import okhttp3.OkHttpClient;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 import org.dom4j.Document;
@@ -23,20 +25,18 @@
 
 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.sql.Timestamp;
 import java.util.*;
 import java.util.concurrent.CompletableFuture;
 
+import static com.smtaiserver.smtaiserver.util.SMTWXSStatic.WeixinuUtil.*;
 import static java.util.Arrays.sort;
 
 public class SMTAIWeixinControl {
@@ -46,27 +46,29 @@
   private static final String CONTENT = "Content";
   private static Logger _logger = LogManager.getLogger(SMTAIServerControl.class);
 
-  private Object _lockToken = new Object();
+  private final Object _lockToken = new Object();
   private String _tokenValue = null;
   private long _tokenTicket = 0;
+  private OkHttpClient _web;
 
   /** 寰俊楠岃瘉 */
   public ModelAndView weChatNotify(SMTAIServerRequest tranReq) throws Exception {
     String method = tranReq.getRequest().getMethod();
-    if (method.equals("GET")) return getModelAndView(tranReq);
+    if (method.equals("GET")) return WeixinuUtil.getModelAndView(tranReq);
     return reply(tranReq);
   }
 
   /** 琚姩鍥炲 */
-  private ModelAndView reply(SMTAIServerRequest 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);
+    String fromUserName = requestMap.get(FROM_USER_NAME);
+    String toUserName = requestMap.get(TO_USER_NAME);
     if (requestMap.isEmpty()) {
       return null;
     }
-    //    wechatMessage.setFromUserName(requestMap.get(TO_USER_NAME));
-    //    wechatMessage.setToUserName(requestMap.get(FROM_USER_NAME));
-    //    wechatMessage.setCreateTime(System.currentTimeMillis() / 1000);
     String xmltemp =
         "<xml>\n"
             + "  <ToUserName><![CDATA[{{{toUser}}}]]></ToUserName>\n"
@@ -75,42 +77,13 @@
             + "  <MsgType><![CDATA[text]]></MsgType>\n"
             + "  <Content><![CDATA[鎴戞鍦ㄦ�濊�冨摝~璇风◢绛夆�︹�]></Content>\n"
             + "</xml>";
-    long l = System.currentTimeMillis() / 1000;
-    String createTimeStr = String.valueOf(l); // 灏� long 杞崲涓� String
     // 鏇挎崲鍗犱綅绗�
     String result =
         xmltemp
-            .replace("{{{toUser}}}", requestMap.get(TO_USER_NAME))
-            .replace("{{{fromUser}}}", requestMap.get(FROM_USER_NAME))
+            .replace("{{{toUser}}}", fromUserName)
+            .replace("{{{fromUser}}}", toUserName)
             .replace("{{{CreateTime}}}", createTimeStr);
     String reqContent = requestMap.get(CONTENT);
-    // 璁剧疆娑堟伅绫诲瀷
-    //    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;
-    //    }
-
-    //    MediaVo media = new MediaVo();
-    //    wechatMessage.setContent("鎴戞鍦ㄦ�濊�冨摝~璇风◢绛夆�︹��");
     // 寮傛璋冪敤 aiReplyToTheUserASecondTime
     CompletableFuture.runAsync(
         () -> {
@@ -121,57 +94,10 @@
             _logger.error("aiReplyToTheUserASecondTime error", e);
           }
         });
-
-    // 璁剧疆娑堟伅鍐呭
-    //    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("寰俊娑堟伅杩斿弬锛�" + xmltemp);
+      _logger.info("寰俊娑堟伅杩斿弬锛�" + result);
       // 杩斿洖 XML 瀛楃涓�
-      return tranReq.returnText(xmltemp.toString());
+      return tranReq.returnText(result);
     } catch (Exception e) {
       throw new RuntimeException(e);
     }
@@ -203,41 +129,45 @@
     SMTLLMConnect llm = SMTAIServerApp.getApp().allocLLMConnect(null);
     String answer = llm.callWithMessage(new String[] {prompt}, question, tranReq);
     tranReq.traceLLMDebug(answer);
-    List<Json> jsonASTList = SMTStatic.convLLMAnswerToJson(answer, true).asJsonList();
-    if (jsonASTList.size() > 0) {
-      Json jsonAST = jsonASTList.get(0);
-      if ("query_water_fee".equals(jsonAST.safeGetStr("call", null))) {
-        jsonAST = jsonAST.getJson("args");
-        try (ASTDBMap dbMap = new ASTDBMap()) {
-          SMTDatabase db = dbMap.getDatabase("DS_74_CHENGTOU");
-          DBRecords recs =
-              db.querySQL(
-                  " SELECT ROUND(SUM("
-                      + jsonAST.getJson("value_name").asString()
-                      + ")::NUMERIC(10, 2), 2) AS TOTAL"
-                      + " FROM chengtou_data.bill_data WHERE billing_date BETWEEN ? AND ?",
-                  new Object[] {
-                    SMTStatic.toDate(jsonAST.getJson("start_time").asString()),
-                    SMTStatic.toDate(jsonAST.getJson("end_time").asString())
-                  });
+    Json ojsonASTList = SMTStatic.convLLMAnswerToJson(answer, true);
+    if (ojsonASTList != null && ojsonASTList.isArray()) {
+      List<Json> jsonASTList = ojsonASTList.asJsonList();
+      if (jsonASTList.size() > 0) {
+        Json jsonAST = jsonASTList.get(0);
+        if ("query_water_fee".equals(jsonAST.safeGetStr("call", null))) {
+          jsonAST = jsonAST.getJson("args");
+          try (ASTDBMap dbMap = new ASTDBMap()) {
+            SMTDatabase db = dbMap.getDatabase("DS_74_CHENGTOU");
+            DBRecords recs =
+                db.querySQL(
+                    " SELECT ROUND(SUM("
+                        + jsonAST.getJson("value_name").asString()
+                        + ")::NUMERIC(10, 2), 2) AS TOTAL"
+                        + " FROM chengtou_data.bill_data WHERE billing_date BETWEEN ? AND ?",
+                    new Object[] {
+                      SMTStatic.toDate(jsonAST.getJson("start_time").asString()),
+                      SMTStatic.toDate(jsonAST.getJson("end_time").asString())
+                    });
 
-          if (recs.getRowCount() == 0)
+            if (recs.getRecord(0).getString(0) == null)
+              return "浠�"
+                  + jsonAST.getJson("start_time").asString()
+                  + "鍒�"
+                  + jsonAST.getJson("end_time").asString()
+                  + "鐨�"
+                  + jsonAST.getJson("value_title").asString()
+                  + "鏈煡鍒颁换浣曟暟鎹�";
+
             return "浠�"
                 + jsonAST.getJson("start_time").asString()
                 + "鍒�"
                 + jsonAST.getJson("end_time").asString()
                 + "鐨�"
                 + jsonAST.getJson("value_title").asString()
-                + "鏈煡鍒颁换浣曟暟鎹�";
-
-          return "浠�"
-              + jsonAST.getJson("start_time").asString()
-              + "鍒�"
-              + jsonAST.getJson("end_time").asString()
-              + "鐨�"
-              + jsonAST.getJson("value_title").asString()
-              + "鎬昏"
-              + recs.getRecord(0).getString(0);
+                + "鎬昏"
+                + recs.getRecord(0).getString(0)
+                + ("volume".equals(jsonAST.getJson("value_name").asString()) ? "鍚�" : "鍏�");
+          }
         }
       }
     }
@@ -250,7 +180,6 @@
   public ModelAndView aiReplyToTheUserASecondTime(String answer, String fromUserName)
       throws Exception {
     String accessToken = getAccessToken();
-
     SMTJsonWriter jsonWr = new SMTJsonWriter(false);
     jsonWr.addKeyValue("touser", fromUserName);
     jsonWr.addKeyValue("msgtype", "text");
@@ -259,174 +188,81 @@
       jsonWr.addKeyValue("content", answer);
     }
     jsonWr.endMap();
-    //    JSONObject jsonObject = new JSONObject();
-    //    jsonObject.put("touser", fromUserName);
-    //    jsonObject.put("msgtype", "text");
-    //    JSONObject jsonObject1 = new JSONObject();
-    //    jsonObject1.put("content", answer);
-    //    jsonObject.put("text", jsonObject1);
-    //    _logger.info("jsonObject: {}", jsonObject);
     String url =
         String.format(
             "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=%s", accessToken);
-    // Map<String, Object> stringObjectMap = jsonObjectToMap(jsonWr.getRootJson());
     String s = sendPost(url, jsonWr.getRootJson());
     _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(
-        "寮�濮嬫牎楠屾娆℃秷鎭槸鍚︽潵鑷井淇℃湇鍔″櫒锛宲aram->signature:{},\ntimestamp:{},\nnonce:{},\nechostr:{}",
-        signature,
-        timestamp,
-        nonce,
-        echostr);
-    // 闇�瑕侀獙璇佺殑鏃跺�欏氨鍚敤
-    if (checkSignature(signature, timestamp, nonce)) {
-      return tranReq.returnText(echostr);
-    }
-    return tranReq.returnText("");
-  }
-
   public String getAccessToken() throws Exception {
     synchronized (this._lockToken) {
-      if (_tokenValue == null || (System.currentTimeMillis() - _tokenTicket) > 3600 * 1000) {
+      SMTDatabase db = SMTAIServerApp.getApp().allocDatabase();
+      try {
         HashMap<String, String> weixinParam = getWeixinParam();
-        String url =
-            "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="
-                + weixinParam.get("appId")
-                + "&secret="
-                + weixinParam.get("secret");
-        String response = HttpUtil.get(url);
-        JSONObject jsonObject = new JSONObject(response);
-        if (jsonObject.has("access_token")) {
-          String accessToken = jsonObject.getString("access_token");
+        long ONE_HOUR_IN_MILLIS = 3600 * 1000;
+        long expiresTime = System.currentTimeMillis() + ONE_HOUR_IN_MILLIS;
+        Timestamp expiresTimestamp = new Timestamp(expiresTime);
+        String appId = weixinParam.get("appId");
 
-          _tokenValue = accessToken;
-        } else {
-          throw new Exception("can't get weixin token");
+        // 鏌ヨ鏈繃鏈熺殑 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, expiresTimestamp});
+
+        // 鏁版嵁搴撴棤璁板綍锛屼粠寰俊鏈嶅姟鍣ㄨ幏鍙� access_token
+        List<SMTDatabase.DBRecord> records = dbRecord.getRecords();
+        boolean res = false;
+        if (dbRecord.getRowCount() != 0) {
+          String expires_time = records.get(0).getString("expires_time");
+          long dbExpiresTime = Timestamp.valueOf(expires_time).getTime();
+          res = System.currentTimeMillis() <= dbExpiresTime;
         }
-        _tokenTicket = System.currentTimeMillis();
+        // 寰俊鍙栵紝杩斿洖token骞朵笖淇濆瓨鎴栬鐩栨暟鎹�
+        if (dbRecord.getRowCount() == 0 || !res) {
+          String accessToken = fetchAccessTokenFromWeixinServer(); // 浠庡井淇℃湇鍔″櫒鑾峰彇 access_token
+          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, expiresTimestamp});
+          return accessToken;
+        } else { // 鐩存帴鎷挎暟鎹簱accesstoken
+          return records.get(0).getString("access_token");
+        }
+      } catch (Exception e) {
+        throw new Exception("Failed to get access token: " + e);
+      } finally {
+        if (db != null) {
+          db.close();
+        }
       }
-      return _tokenValue;
     }
   }
 
-  /** 楠岃瘉绛惧悕util */
-  public static boolean checkSignature(String signature, String timestamp, String nonce)
-      throws Exception {
+  private String fetchAccessTokenFromWeixinServer() throws Exception {
     HashMap<String, String> weixinParam = getWeixinParam();
-    String[] arr = new String[] {weixinParam.get("token"), timestamp, nonce};
-    // 灏唗oken銆乼imestamp銆乶once涓変釜鍙傛暟杩涜瀛楀吀搴忔帓搴�
-    // 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;
-    // 灏唖ha1鍔犲瘑鍚庣殑瀛楃涓插彲涓巗ignature瀵规瘮锛屾爣璇嗚璇锋眰鏉ユ簮浜庡井淇�
-
-    return tmpStr != null && tmpStr.equals(signature.toUpperCase());
-  }
-
-  private static HashMap<String, String> getWeixinParam() throws Exception {
-    Object weixinParam = SMTAIServerApp.getApp().getGlobalConfig("weixin_core_param", "false");
-    _logger.info("寰俊鍙傛暟锛歿}", weixinParam);
-    JSONObject weixinJson = new JSONObject(weixinParam.toString());
-    String appId = (String) weixinJson.get("appId");
-    String secret = (String) weixinJson.get("secret");
-    String token = (String) weixinJson.get("token");
-    HashMap<String, String> paramMap = new HashMap<>();
-    paramMap.put("appId", appId);
-    paramMap.put("secret", secret);
-    paramMap.put("token", token);
-    return paramMap;
-  }
-
-  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());
-      }
-    } 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, Json jsonParam) throws Exception {
-    // 灏嗗弬鏁拌浆鎹负 JSON 鏍煎紡瀛楃涓�
-    // JSONObject jsonParams = new JSONObject(params);
-    String payload = jsonParam.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() : "";
+    String url =
+        "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="
+            + weixinParam.get("appId")
+            + "&secret="
+            + weixinParam.get("secret");
+    String response = HttpUtil.get(url);
+    Json json = Json.read(response);
+    String accessToken = json.safeGetStr("access_token", null);
+    if (accessToken != null) {
+      return accessToken;
+    } else {
+      throw new Exception("can't get weixin token : " + json);
     }
   }
 }

--
Gitblit v1.9.3