package com.smtaiserver.smtaiserver.control; import com.smtaiserver.smtaiserver.core.SMTAIServerApp; 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.jetbrains.annotations.NotNull; import org.springframework.web.servlet.ModelAndView; public class SMTAIWeixinControl { private static final HashMap 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); 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(SMTAIServerRequest tranReq) { long l = System.currentTimeMillis() / 1000; String createTimeStr = String.valueOf(l); HttpServletRequest request = tranReq.getRequest(); Map 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); 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停止输出"; String baiduTextContent = "我是百度\n百度"; 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; } // 异步调用 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); } public ModelAndView weChatTest(SMTAIServerRequest tranReq) throws Exception { String question = "冰箱是什么"; tranReq.setAIQuestion(question); tranReq.setTextResultMode(); Set 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 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 { 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); 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("异步调用被取消"); } } /** 数据库获取 access_koen */ public String getAccessToken() throws Exception { synchronized (this._lockToken) { SMTDatabase db = SMTAIServerApp.getApp().allocDatabase(); // try (SMTDatabase db = SMTAIServerApp.getApp().allocDatabase()) { HashMap weixinParam = SMTWXSStatic.getWeixinParam(); Date curTime = new Date(); String appId = weixinParam.get("appId"); // 查询未过期的 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}); // 数据库无记录,从微信服务器获取 access_token List records = dbRecord.getRecords(); if (dbRecord.getRowCount() > 0) { return records.get(0).getString("access_token"); } // 微信取,返回token并且保存或覆盖数据 else { Object[] accessToken = fetchAccessTokenFromWeixinServer(); // 从微信服务器获取 access_token Date expiresTime = SMTStatic.calculateTime( curTime, SMTStatic.SMTCalcTime.ADD_SECOND, ((int) accessToken[1]) / 2); 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 (Exception e) { // throw new Exception("Failed to get access token", e); // // } } } /** 从微信服务器获取 access_token */ private Object[] fetchAccessTokenFromWeixinServer() throws Exception { HashMap 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); } } @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("\n") .append(" \n") .append(" \n") .append(" ").append(createTimeStr).append("\n") .append(" \n"); switch (msgType) { case "text": xmlBuilder.append(" \n"); break; case "image": xmlBuilder.append(" \n"); break; case "voice": xmlBuilder.append(" \n"); break; case "video": xmlBuilder.append(" \n"); break; case "music": xmlBuilder.append(" \n") .append(" <![CDATA[").append(title).append("]]>\n") .append(" \n") .append(" \n") .append(" \n") .append(" \n") .append(" \n"); break; case "news": // 图文消息 xmlBuilder.append(" 2\n") .append(" \n") .append(" \n") .append(" <![CDATA[").append(title).append("]]>\n") .append(" \n") .append(" \n") .append(" \n") .append(" \n") .append(" \n"); break; default: xmlBuilder.append(" \n"); } xmlBuilder.append(""); return xmlBuilder.toString(); } /** * 用于测试,正常可以不用 * * @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 = "\n" + " \n" + " \n" + " {{{CreateTime}}}\n" + " \n" + " 停止输出]]>\n" + " \n" + ""; // 替换占位符 return xmltemp .replace("{{{toUser}}}", fromUserName) .replace("{{{fromUser}}}", toUserName) .replace("{{{CreateTime}}}", createTimeStr); } }