You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

371 lines
17 KiB

  1. package com.iformall.controller;
  2. import com.google.gson.Gson;
  3. import com.google.gson.GsonBuilder;
  4. import com.iformall.common.Result;
  5. import com.iformall.common.ResultData;
  6. import com.iformall.domain.po.*;
  7. import com.iformall.enums.*;
  8. import com.iformall.service.*;
  9. import com.iformall.service.wechat.FmOpenService;
  10. import io.swagger.annotations.Api;
  11. import io.swagger.annotations.ApiOperation;
  12. import me.chanjar.weixin.common.error.WxErrorException;
  13. import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage;
  14. import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
  15. import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
  16. import me.chanjar.weixin.open.bean.message.WxOpenXmlMessage;
  17. import me.chanjar.weixin.open.bean.result.WxOpenAuthorizerInfoResult;
  18. import me.chanjar.weixin.open.bean.result.WxOpenPlatformResult;
  19. import me.chanjar.weixin.open.bean.result.WxOpenQueryAuthResult;
  20. import me.chanjar.weixin.open.bean.result.WxOpenResult;
  21. import org.apache.commons.lang3.StringUtils;
  22. import org.slf4j.Logger;
  23. import org.slf4j.LoggerFactory;
  24. import org.springframework.beans.factory.annotation.Autowired;
  25. import org.springframework.stereotype.Controller;
  26. import org.springframework.web.bind.annotation.*;
  27. import javax.servlet.http.HttpServletRequest;
  28. import javax.servlet.http.HttpServletResponse;
  29. import java.io.ByteArrayOutputStream;
  30. import java.io.IOException;
  31. import java.io.InputStream;
  32. import java.io.PrintWriter;
  33. import java.nio.charset.Charset;
  34. import java.util.Date;
  35. @Controller
  36. @RequestMapping("/wxOpen")
  37. @Api(description = "微信第三方开放平台回调相关接口")
  38. public class WechatCalllbackController extends BaseController {
  39. private final Logger logger = LoggerFactory.getLogger(this.getClass());
  40. @Autowired
  41. protected FmOpenService openService;
  42. @Autowired
  43. private WxComponentVerifyTicketService componentVerifyTicketService;
  44. @Autowired
  45. private WxAuthorizerInfoService authorizerInfoService;
  46. @PostMapping(value = "/notify")
  47. @ApiOperation("接收ticket回调")
  48. @ResponseBody
  49. public String receiveTicket(HttpServletRequest request) throws IOException {
  50. logger.info("[" + getIpAddr() + "]接收微信回调 /wxOpen/notify");
  51. String timestamp = request.getParameter("timestamp");
  52. String nonce = request.getParameter("nonce");
  53. String signature = request.getParameter("signature");
  54. String encType = request.getParameter("encrypt_type");
  55. String msgSignature = request.getParameter("msg_signature");
  56. if (!StringUtils.equalsIgnoreCase("aes", encType)
  57. || !openService.getWxOpenComponentService().checkSignature(timestamp, nonce, signature)) {
  58. throw new IllegalArgumentException("非法请求,可能属于伪造的请求!");
  59. }
  60. String resultxml = getRequestXML(request);
  61. logger.info("\n接收微信回调:[signature=[{}], encType=[{}], msgSignature=[{}],"
  62. + " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ",
  63. signature, encType, msgSignature, timestamp, nonce, resultxml);
  64. // aes加密的消息, 解密
  65. WxOpenXmlMessage inMessage = WxOpenXmlMessage.fromEncryptedXml(resultxml,
  66. openService.getWxOpenConfigStorage(), timestamp, nonce, msgSignature);
  67. this.logger.info("接收微信回调,消息解密后内容为:{} ", inMessage.toString());
  68. // 处理
  69. return handleTicket(inMessage);
  70. }
  71. /**
  72. * 处理ticket消息
  73. * @param inMessage
  74. * @return
  75. */
  76. private String handleTicket(WxOpenXmlMessage inMessage) {
  77. if (inMessage.getInfoType().equals(EnumWxAuthorizationInfoType.COMPONENT_VERIFY_TICKET.getMessage())) {
  78. // 保存component_verify_ticket
  79. componentVerifyTicketService.saveOrUpdate(new WxComponentVerifyTicket() {
  80. {
  81. setComponentVerifyTicket(inMessage.getComponentVerifyTicket());
  82. setCreateTime(new Date(Long.valueOf(inMessage.getCreateTime() * 1000)));
  83. setComponentAppid(inMessage.getAppId());
  84. setDeadline(new Date(Long.valueOf(System.currentTimeMillis() + (60 * 60 * 1000))));
  85. }
  86. });
  87. return "success";
  88. } else if (inMessage.getInfoType().equals(EnumWxAuthorizationInfoType.AUTHORIZED.getMessage()) ||
  89. inMessage.getInfoType().equals(EnumWxAuthorizationInfoType.UPDATEAUTHORIZED.getMessage())) {
  90. // 审核通过
  91. if (handleWeappAuditedMessage(inMessage)) return "success";
  92. } else if (inMessage.getInfoType().equals(EnumWxAuthorizationInfoType.UNAUTHORIZED.getMessage())) {
  93. // 授权取消
  94. authorizerInfoService.saveOrUpdate(new WxAuthorizerInfo() {
  95. {
  96. setAuthorizerAppid(inMessage.getAuthorizerAppid());
  97. setAuthorizationStatus(EnumWxAuthorizationStatus.UNAUTHORIZED.getCode());
  98. setAuthTime(new Date());
  99. }
  100. });
  101. return "success";
  102. }
  103. try {
  104. String out = openService.getWxOpenComponentService().route(inMessage);
  105. this.logger.info("组装回复信息:{}", out);
  106. } catch (WxErrorException e) {
  107. this.logger.error("notify", e);
  108. }
  109. return "success";
  110. }
  111. /**
  112. * 处理小程序审核消息
  113. * @param inMessage
  114. * @return
  115. */
  116. private boolean handleWeappAuditedMessage(WxOpenXmlMessage inMessage) {
  117. // 审核通过
  118. try {
  119. WxOpenQueryAuthResult queryAuthResult = openService.getWxOpenComponentService().getQueryAuth(inMessage.getAuthorizationCode());
  120. logger.info("getQueryAuth", queryAuthResult);
  121. WxOpenAuthorizerInfoResult openAuthorizerInfoResult = openService.getWxOpenComponentService().getAuthorizerInfo(
  122. queryAuthResult.getAuthorizationInfo().getAuthorizerAppid());
  123. logger.info(openAuthorizerInfoResult.toString());
  124. // save auth info
  125. authorizerInfoService.saveOrUpdate(new WxAuthorizerInfo() {
  126. {
  127. setAuthorizerAppid(openAuthorizerInfoResult.getAuthorizationInfo().getAuthorizerAppid());
  128. setAlias(openAuthorizerInfoResult.getAuthorizerInfo().getAlias());
  129. setHeadImg(openAuthorizerInfoResult.getAuthorizerInfo().getHeadImg());
  130. setQrcodeUrl(openAuthorizerInfoResult.getAuthorizerInfo().getQrcodeUrl());
  131. setRefreshToken(openAuthorizerInfoResult.getAuthorizationInfo().getAuthorizerRefreshToken());
  132. setAccessToken(openAuthorizerInfoResult.getAuthorizationInfo().getAuthorizerAccessToken());
  133. setAccessTokenExpire(new Date(Long.valueOf(System.currentTimeMillis() + openAuthorizerInfoResult.getAuthorizationInfo().getExpiresIn()*1000)));
  134. setAuthorizationStatus(EnumWxAuthorizationStatus.AUTHORIZED.getCode());
  135. setAuthTime(new Date());
  136. }
  137. });
  138. return true;
  139. } catch (WxErrorException e) {
  140. logger.error(e.getMessage());
  141. }
  142. return false;
  143. }
  144. @RequestMapping(value = "/{appId}/callback")
  145. @ApiOperation("消息回调")
  146. public void callback(@PathVariable("appId") String appId, HttpServletRequest request, HttpServletResponse response) throws IOException {
  147. logger.info("[" + getIpAddr() + "]接收微信APP回调 /{appId}/callback");
  148. String timestamp = request.getParameter("timestamp");
  149. String nonce = request.getParameter("nonce");
  150. String signature = request.getParameter("signature");
  151. String encType = request.getParameter("encrypt_type");
  152. String openid = request.getParameter("openid");
  153. String msgSignature = request.getParameter("msg_signature");
  154. // 第三方平台一定接收的是加密的消息
  155. if (!StringUtils.equalsIgnoreCase("aes", encType)
  156. || !openService.getWxOpenComponentService().checkSignature(timestamp, nonce, signature)) {
  157. throw new IllegalArgumentException("非法请求,可能属于伪造的请求!");
  158. }
  159. String resultxml = getRequestXML(request);
  160. logger.info("接收微信APP回调:[appId=[{}], openid=[{}], signature=[{}], encType=[{}], msgSignature=[{}],"
  161. + " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ",
  162. appId, openid, signature, encType, msgSignature, timestamp, nonce, resultxml);
  163. String out = "";
  164. // aes加密的消息, 解密
  165. WxMpXmlMessage inMessage = WxOpenXmlMessage.fromEncryptedMpXml(resultxml,
  166. openService.getWxOpenConfigStorage(), timestamp, nonce, msgSignature);
  167. logger.info("接收微信APP回调,消息解密后内容为:\n{}", inMessage.toString());
  168. // 全网发布测试用例
  169. if (StringUtils.equalsAnyIgnoreCase(appId, "wx570bc396a51b8ff8", "wxd101a85aa106f53e")) {
  170. handleWechatTest(appId, response, inMessage);
  171. PrintWriter printWriter = response.getWriter();
  172. wechatWriteSuccess(printWriter);
  173. return;
  174. }
  175. // 实际 消息 处理
  176. //handleCallbackMessage(appId, response, inMessage);
  177. WxMpXmlOutMessage outMessage = openService.getWxOpenMessageRouter().route(inMessage, appId);
  178. if(outMessage == null) {
  179. PrintWriter printWriter = response.getWriter();
  180. wechatWriteSuccess(printWriter);
  181. }
  182. PrintWriter printWriter = response.getWriter();
  183. wechatWriteSuccess(printWriter);
  184. }
  185. private void wechatWriteSuccess(PrintWriter printWriter) {
  186. printWriter.print("success");
  187. printWriter.flush();
  188. printWriter.close();
  189. }
  190. /**
  191. * 测试账号特殊处理
  192. * @param appId
  193. * @param response
  194. * @param inMessage
  195. * @throws IOException
  196. */
  197. private void handleWechatTest(@PathVariable("appId") String appId, HttpServletResponse response, WxMpXmlMessage inMessage) throws IOException {
  198. String out;
  199. try {
  200. if (StringUtils.equals(inMessage.getMsgType(), "text")) {
  201. if (StringUtils.equals(inMessage.getContent(), "TESTCOMPONENT_MSG_TYPE_TEXT")) {
  202. out = WxOpenXmlMessage.wxMpOutXmlMessageToEncryptedXml(
  203. WxMpXmlOutMessage.TEXT().content("TESTCOMPONENT_MSG_TYPE_TEXT_callback")
  204. .fromUser(inMessage.getToUser())
  205. .toUser(inMessage.getFromUser())
  206. .build(),
  207. openService.getWxOpenConfigStorage()
  208. );
  209. } else if (StringUtils.startsWith(inMessage.getContent(), "QUERY_AUTH_CODE:")) {
  210. String msg = inMessage.getContent().replace("QUERY_AUTH_CODE:", "") + "_from_api";
  211. WxMpKefuMessage kefuMessage = WxMpKefuMessage.TEXT().content(msg).toUser(inMessage.getFromUser()).build();
  212. openService.getWxOpenComponentService().getWxMpServiceByAppid(appId).getKefuService().sendKefuMessage(kefuMessage);
  213. }
  214. } else if (StringUtils.equals(inMessage.getMsgType(), "event")) {
  215. WxMpKefuMessage kefuMessage = WxMpKefuMessage.TEXT().content(inMessage.getEvent() + "from_callback").toUser(inMessage.getFromUser()).build();
  216. openService.getWxOpenComponentService().getWxMpServiceByAppid(appId).getKefuService().sendKefuMessage(kefuMessage);
  217. }
  218. } catch (WxErrorException e) {
  219. logger.error("callback", e);
  220. }
  221. }
  222. private String getRequestXML(HttpServletRequest request) throws IOException {
  223. InputStream inStream = request.getInputStream();
  224. ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
  225. byte[] buffer = new byte[1024];
  226. int len = 0;
  227. while ((len = inStream.read(buffer)) != -1) {
  228. outSteam.write(buffer, 0, len);
  229. }
  230. String resultxml = new String(outSteam.toByteArray(), Charset.forName("UTF-8"));
  231. logger.info(resultxml);
  232. outSteam.close();
  233. inStream.close();
  234. return resultxml;
  235. }
  236. private void getThreadCount( int i) {
  237. ThreadGroup group = Thread.currentThread().getThreadGroup();
  238. ThreadGroup topGroup = group;
  239. // 遍历线程组树,获取根线程组
  240. while (group != null) {
  241. topGroup = group;
  242. group = group.getParent();
  243. }
  244. // 激活的线程数加倍
  245. int activeCount = topGroup.activeCount();
  246. // copy into a list that is the exact size
  247. logger.info("Thread list size == " + i + " : " + activeCount);
  248. }
  249. @GetMapping(value = "/createOpenPlatform")
  250. @ApiOperation("创建微信开放平台")
  251. public ResultData createOpenPlatform(@RequestParam(value = "appId") String appId) {
  252. Gson gson = new GsonBuilder().create();
  253. try {
  254. String responseStr = openService.getWxOpenComponentService().getWxMpServiceByAppid(appId).createOpenPlatform(appId);
  255. logger.info(responseStr);
  256. WxOpenPlatformResult openPlatformResult = gson.fromJson(responseStr, WxOpenPlatformResult.class);
  257. if (openPlatformResult.isSuccess()) {
  258. WxAuthorizerInfo authorizerInfo = authorizerInfoService.getByAppId(appId);
  259. if (authorizerInfo != null) {
  260. authorizerInfo.setId(authorizerInfo.getId());
  261. authorizerInfo.setOpenAppid(openPlatformResult.getOpenAppid());
  262. authorizerInfoService.updateOpenAppid(authorizerInfo);
  263. }
  264. }
  265. return new ResultData(responseStr);
  266. } catch (WxErrorException e) {
  267. logger.error(e.getMessage());
  268. return new ResultData(Result.ERROR, e.getMessage());
  269. }
  270. }
  271. @GetMapping(value = "/bindOpenPlatform")
  272. @ApiOperation("绑定微信开放平台")
  273. public ResultData bindOpenPlatform(@RequestParam(value = "appId") String appId, @RequestParam(value = "openAppId") String openAppId) {
  274. Gson gson = new GsonBuilder().create();
  275. try {
  276. String responseStr = openService.getWxOpenComponentService().getWxMpServiceByAppid(appId).bindOpenPlatform(appId, openAppId);
  277. logger.info(responseStr);
  278. WxOpenResult openResult = gson.fromJson(responseStr, WxOpenResult.class);
  279. if (openResult.isSuccess()) {
  280. return new ResultData();
  281. }
  282. return new ResultData(Result.ERROR, responseStr);
  283. } catch (WxErrorException e) {
  284. logger.error(e.getMessage());
  285. return new ResultData(Result.ERROR, e.getMessage());
  286. }
  287. }
  288. @GetMapping(value = "/unbindOpenPlatform")
  289. @ApiOperation("解绑微信开放平台")
  290. public ResultData unbindOpenPlatform(@RequestParam(value = "appId") String appId, @RequestParam(value = "openAppId") String openAppId) {
  291. Gson gson = new GsonBuilder().create();
  292. try {
  293. String responseStr = openService.getWxOpenComponentService().getWxMpServiceByAppid(appId).unbindOpenPlatform(appId, openAppId);
  294. logger.info(responseStr);
  295. WxOpenResult openResult = gson.fromJson(responseStr, WxOpenResult.class);
  296. if (openResult.isSuccess()) {
  297. return new ResultData();
  298. }
  299. return new ResultData(Result.ERROR, responseStr);
  300. } catch (WxErrorException e) {
  301. logger.error(e.getMessage());
  302. return new ResultData(Result.ERROR, e.getMessage());
  303. }
  304. }
  305. @GetMapping(value = "/getOpenPlatform")
  306. @ApiOperation("获取微信开放平台appId")
  307. public ResultData getOpenPlatform(@RequestParam(value = "appId") String appId) {
  308. Gson gson = new GsonBuilder().create();
  309. try {
  310. String responseStr = openService.getWxOpenComponentService().getWxMpServiceByAppid(appId).getBindOpenPlatform(appId);
  311. logger.info(responseStr);
  312. WxOpenPlatformResult openPlatformResult = gson.fromJson(responseStr, WxOpenPlatformResult.class);
  313. if (openPlatformResult.isSuccess()) {
  314. return new ResultData(openPlatformResult.getOpenAppid());
  315. }
  316. return new ResultData(Result.ERROR, responseStr);
  317. } catch (WxErrorException e) {
  318. logger.error(e.getMessage());
  319. return new ResultData(Result.ERROR, e.getMessage());
  320. }
  321. }
  322. @GetMapping(value = "/clearQuota")
  323. @ApiOperation("代公众号清零quota,每个公众号每个月有10次清零机会,包括在微信公众平台上的清零以及调用API进行清零")
  324. public ResultData clearQuota(@RequestParam(value = "appId") String appId) {
  325. try {
  326. openService.getWxOpenComponentService().getWxMpServiceByAppid(appId).clearQuota(appId);
  327. return new ResultData();
  328. } catch (WxErrorException e) {
  329. logger.error(e.getMessage());
  330. return new ResultData(Result.ERROR, e.getMessage());
  331. }
  332. }
  333. }