Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

449 строки
21 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.mp.manager.WxMpManager;
  9. import com.iformall.service.*;
  10. import com.iformall.service.wechat.FmOpenService;
  11. import io.swagger.annotations.Api;
  12. import io.swagger.annotations.ApiOperation;
  13. import me.chanjar.weixin.common.error.WxErrorException;
  14. import me.chanjar.weixin.mp.api.WxMpService;
  15. import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage;
  16. import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
  17. import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
  18. import me.chanjar.weixin.open.bean.message.WxOpenXmlMessage;
  19. import me.chanjar.weixin.open.bean.result.WxOpenAuthorizerInfoResult;
  20. import me.chanjar.weixin.open.bean.result.WxOpenPlatformResult;
  21. import me.chanjar.weixin.open.bean.result.WxOpenQueryAuthResult;
  22. import me.chanjar.weixin.open.bean.result.WxOpenResult;
  23. import org.apache.commons.lang3.StringUtils;
  24. import org.slf4j.Logger;
  25. import org.slf4j.LoggerFactory;
  26. import org.springframework.beans.factory.annotation.Autowired;
  27. import org.springframework.stereotype.Controller;
  28. import org.springframework.web.bind.annotation.*;
  29. import javax.servlet.http.HttpServletRequest;
  30. import javax.servlet.http.HttpServletResponse;
  31. import java.io.ByteArrayOutputStream;
  32. import java.io.IOException;
  33. import java.io.InputStream;
  34. import java.io.PrintWriter;
  35. import java.nio.charset.Charset;
  36. import java.text.SimpleDateFormat;
  37. import java.util.Date;
  38. @Controller
  39. @RequestMapping("/wxOpen")
  40. @Api(description = "微信第三方开放平台回调相关接口")
  41. public class WechatCalllbackController extends BaseController {
  42. private final Logger logger = LoggerFactory.getLogger(this.getClass());
  43. @Autowired
  44. protected FmOpenService openService;
  45. @Autowired
  46. private WxComponentVerifyTicketService componentVerifyTicketService;
  47. @Autowired
  48. private WxAuthorizerInfoService authorizerInfoService;
  49. @Autowired
  50. private WxWeappAuditStatusService weappAuditStatusService;
  51. @Autowired
  52. private WxAppinfoService appinfoService;
  53. @Autowired
  54. private MailService mailService;
  55. @Autowired
  56. private WxMpManager mpManager;
  57. @PostMapping(value = "/notify")
  58. @ApiOperation("接收ticket回调")
  59. @ResponseBody
  60. public String receiveTicket(HttpServletRequest request) throws IOException {
  61. logger.info("[" + getIpAddr() + "]接收微信回调 /wxOpen/notify");
  62. String timestamp = request.getParameter("timestamp");
  63. String nonce = request.getParameter("nonce");
  64. String signature = request.getParameter("signature");
  65. String encType = request.getParameter("encrypt_type");
  66. String msgSignature = request.getParameter("msg_signature");
  67. if (!StringUtils.equalsIgnoreCase("aes", encType)
  68. || !openService.getWxOpenComponentService().checkSignature(timestamp, nonce, signature)) {
  69. throw new IllegalArgumentException("非法请求,可能属于伪造的请求!");
  70. }
  71. InputStream inStream = request.getInputStream();
  72. ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
  73. byte[] buffer = new byte[1024];
  74. int len = 0;
  75. while ((len = inStream.read(buffer)) != -1) {
  76. outSteam.write(buffer, 0, len);
  77. }
  78. String resultxml = new String(outSteam.toByteArray(), Charset.forName("UTF-8"));
  79. logger.info(resultxml);
  80. outSteam.close();
  81. inStream.close();
  82. logger.info("\n接收微信回调:[signature=[{}], encType=[{}], msgSignature=[{}],"
  83. + " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ",
  84. signature, encType, msgSignature, timestamp, nonce, resultxml);
  85. // aes加密的消息
  86. WxOpenXmlMessage inMessage = WxOpenXmlMessage.fromEncryptedXml(resultxml,
  87. openService.getWxOpenConfigStorage(), timestamp, nonce, msgSignature);
  88. this.logger.info("接收微信回调,消息解密后内容为:{} ", inMessage.toString());
  89. if (inMessage.getInfoType().equals(EnumWxAuthorizationInfoType.COMPONENT_VERIFY_TICKET.getMessage())) {
  90. // 保存component_verify_ticket
  91. componentVerifyTicketService.saveOrUpdate(new WxComponentVerifyTicket() {
  92. {
  93. setComponentVerifyTicket(inMessage.getComponentVerifyTicket());
  94. setCreateTime(new Date(Long.valueOf(inMessage.getCreateTime() * 1000)));
  95. setComponentAppid(inMessage.getAppId());
  96. setDeadline(new Date(Long.valueOf(System.currentTimeMillis() + (60 * 60 * 1000))));
  97. }
  98. });
  99. } else if (inMessage.getInfoType().equals(EnumWxAuthorizationInfoType.AUTHORIZED.getMessage()) ||
  100. inMessage.getInfoType().equals(EnumWxAuthorizationInfoType.UPDATEAUTHORIZED.getMessage())) {
  101. // 审核通过
  102. try {
  103. WxOpenQueryAuthResult queryAuthResult = openService.getWxOpenComponentService().getQueryAuth(inMessage.getAuthorizationCode());
  104. logger.info("getQueryAuth", queryAuthResult);
  105. WxOpenAuthorizerInfoResult openAuthorizerInfoResult = openService.getWxOpenComponentService().getAuthorizerInfo(
  106. queryAuthResult.getAuthorizationInfo().getAuthorizerAppid());
  107. logger.info(openAuthorizerInfoResult.toString());
  108. // save auth info
  109. authorizerInfoService.saveOrUpdate(new WxAuthorizerInfo() {
  110. {
  111. setAuthorizerAppid(openAuthorizerInfoResult.getAuthorizationInfo().getAuthorizerAppid());
  112. setAlias(openAuthorizerInfoResult.getAuthorizerInfo().getAlias());
  113. setHeadImg(openAuthorizerInfoResult.getAuthorizerInfo().getHeadImg());
  114. setQrcodeUrl(openAuthorizerInfoResult.getAuthorizerInfo().getQrcodeUrl());
  115. setRefreshToken(openAuthorizerInfoResult.getAuthorizationInfo().getAuthorizerRefreshToken());
  116. setAccessToken(openAuthorizerInfoResult.getAuthorizationInfo().getAuthorizerAccessToken());
  117. setAccessTokenExpire(new Date(Long.valueOf(System.currentTimeMillis() + openAuthorizerInfoResult.getAuthorizationInfo().getExpiresIn()*1000)));
  118. setAuthorizationStatus(EnumWxAuthorizationStatus.AUTHORIZED.getCode());
  119. setAuthTime(new Date());
  120. }
  121. });
  122. } catch (WxErrorException e) {
  123. logger.error(e.getMessage());
  124. }
  125. } else if (inMessage.getInfoType().equals(EnumWxAuthorizationInfoType.UNAUTHORIZED.getMessage())) {
  126. // 授权取消
  127. authorizerInfoService.saveOrUpdate(new WxAuthorizerInfo() {
  128. {
  129. setAuthorizerAppid(inMessage.getAuthorizerAppid());
  130. setAuthorizationStatus(EnumWxAuthorizationStatus.UNAUTHORIZED.getCode());
  131. setAuthTime(new Date());
  132. }
  133. });
  134. }
  135. try {
  136. String out = openService.getWxOpenComponentService().route(inMessage);
  137. this.logger.info("组装回复信息:{}", out);
  138. } catch (WxErrorException e) {
  139. this.logger.error("notify", e);
  140. }
  141. return "success";
  142. }
  143. @RequestMapping(value = "/{appId}/callback")
  144. @ApiOperation("消息回调")
  145. public void callback(@PathVariable("appId") String appId, HttpServletRequest request, HttpServletResponse response) throws IOException {
  146. logger.info("[" + getIpAddr() + "]接收微信APP回调 /{appId}/callback");
  147. String timestamp = request.getParameter("timestamp");
  148. String nonce = request.getParameter("nonce");
  149. String signature = request.getParameter("signature");
  150. String encType = request.getParameter("encrypt_type");
  151. String openid = request.getParameter("openid");
  152. String msgSignature = request.getParameter("msg_signature");
  153. // 第三方平台一定接收的是加密的消息
  154. if (!StringUtils.equalsIgnoreCase("aes", encType)
  155. || !openService.getWxOpenComponentService().checkSignature(timestamp, nonce, signature)) {
  156. throw new IllegalArgumentException("非法请求,可能属于伪造的请求!");
  157. }
  158. InputStream inStream = request.getInputStream();
  159. ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
  160. byte[] buffer = new byte[1024];
  161. int len = 0;
  162. while ((len = inStream.read(buffer)) != -1) {
  163. outSteam.write(buffer, 0, len);
  164. }
  165. String resultxml = new String(outSteam.toByteArray(), Charset.forName("UTF-8"));
  166. logger.info(resultxml);
  167. outSteam.close();
  168. inStream.close();
  169. logger.info("接收微信APP回调:[appId=[{}], openid=[{}], signature=[{}], encType=[{}], msgSignature=[{}],"
  170. + " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ",
  171. appId, openid, signature, encType, msgSignature, timestamp, nonce, resultxml);
  172. String out = "";
  173. // aes加密的消息
  174. WxMpXmlMessage inMessage = WxOpenXmlMessage.fromEncryptedMpXml(resultxml,
  175. openService.getWxOpenConfigStorage(), timestamp, nonce, msgSignature);
  176. logger.info("接收微信APP回调,消息解密后内容为:\n{}", inMessage.toString());
  177. // 全网发布测试用例
  178. if (StringUtils.equalsAnyIgnoreCase(appId, "wx570bc396a51b8ff8", "wxd101a85aa106f53e")) {
  179. try {
  180. if (StringUtils.equals(inMessage.getMsgType(), "text")) {
  181. if (StringUtils.equals(inMessage.getContent(), "TESTCOMPONENT_MSG_TYPE_TEXT")) {
  182. out = WxOpenXmlMessage.wxMpOutXmlMessageToEncryptedXml(
  183. WxMpXmlOutMessage.TEXT().content("TESTCOMPONENT_MSG_TYPE_TEXT_callback")
  184. .fromUser(inMessage.getToUser())
  185. .toUser(inMessage.getFromUser())
  186. .build(),
  187. openService.getWxOpenConfigStorage()
  188. );
  189. } else if (StringUtils.startsWith(inMessage.getContent(), "QUERY_AUTH_CODE:")) {
  190. String msg = inMessage.getContent().replace("QUERY_AUTH_CODE:", "") + "_from_api";
  191. WxMpKefuMessage kefuMessage = WxMpKefuMessage.TEXT().content(msg).toUser(inMessage.getFromUser()).build();
  192. openService.getWxOpenComponentService().getWxMpServiceByAppid(appId).getKefuService().sendKefuMessage(kefuMessage);
  193. }
  194. } else if (StringUtils.equals(inMessage.getMsgType(), "event")) {
  195. WxMpKefuMessage kefuMessage = WxMpKefuMessage.TEXT().content(inMessage.getEvent() + "from_callback").toUser(inMessage.getFromUser()).build();
  196. openService.getWxOpenComponentService().getWxMpServiceByAppid(appId).getKefuService().sendKefuMessage(kefuMessage);
  197. }
  198. } catch (WxErrorException e) {
  199. logger.error("callback", e);
  200. }
  201. PrintWriter printWriter = response.getWriter();
  202. printWriter.print("success");
  203. printWriter.flush();
  204. printWriter.close();
  205. return;
  206. }
  207. WxAuthorizerInfo authorizerInfo = authorizerInfoService.getByAppId(appId);
  208. PrintWriter printWriter = response.getWriter();
  209. if(authorizerInfo != null &&
  210. (authorizerInfo.getType().equals(EnumAppType.B.getCode()) ||
  211. authorizerInfo.getType().equals(EnumAppType.C.getCode()))) {
  212. // 小程序 审核 消息
  213. /**判断消息类型,调用对应的方法*/
  214. switch (inMessage.getMsgType()) {
  215. case "event": {
  216. switch (inMessage.getEvent()) {
  217. case "weapp_audit_success": { // 审核成功
  218. updateAuditStatus(appId, true, "");
  219. break;
  220. }
  221. case "weapp_audit_fail": { // 审核失败
  222. updateAuditStatus(appId, false, inMessage.getReason());
  223. break;
  224. }
  225. default:
  226. break;
  227. }
  228. printWriter.print("success");
  229. printWriter.flush();
  230. printWriter.close();
  231. return;
  232. }
  233. case "text":
  234. WxMpXmlOutMessage outMessage = openService.getWxOpenMessageRouter().route(inMessage, appId);
  235. if (outMessage == null) {
  236. printWriter.print("success");
  237. printWriter.flush();
  238. printWriter.close();
  239. return;
  240. }
  241. // out = WxOpenXmlMessage.wxMpOutXmlMessageToEncryptedXml(outMessage, openService.getWxOpenConfigStorage());
  242. // logger.info(out);
  243. break;
  244. default:
  245. printWriter.print("success");
  246. printWriter.flush();
  247. printWriter.close();
  248. return;
  249. }
  250. } else {
  251. // 公众号
  252. WxMpService mpService = openService.getWxOpenComponentService().getWxMpServiceByAppid(appId);
  253. try {
  254. WxMpXmlOutMessage outMessage = mpManager.getRouter(mpService).route(inMessage);
  255. if (outMessage == null) {
  256. this.logger.info("2 return: success");
  257. printWriter.print("success");
  258. printWriter.flush();
  259. printWriter.close();
  260. return;
  261. }
  262. // logger.info("组装回复" + outMessage.toXml());
  263. // out = outMessage.toEncryptedXml(mpService.getWxMpConfigStorage());
  264. // logger.info("组装回复消息-->"+out);
  265. } catch (Exception e) {
  266. logger.error("路由消息时出现异常!", e);
  267. }
  268. }
  269. printWriter.print("success");
  270. printWriter.flush();
  271. printWriter.close();
  272. }
  273. private void updateAuditStatus(String appId, boolean success, String reason) {
  274. WxWeappAuditStatus auditStatus = weappAuditStatusService.getByAppId(appId);
  275. if (auditStatus != null) {
  276. if (success) {
  277. auditStatus.setAuditStatus(EnumWeappAuditStatus.SUCCESS.getCode());
  278. auditStatus.setAuditErrCode("");
  279. } else {
  280. auditStatus.setAuditStatus(EnumWeappAuditStatus.FAIL.getCode());
  281. auditStatus.setAuditErrCode(reason);
  282. }
  283. auditStatus.setAuditTime(new Date());
  284. weappAuditStatusService.updateStatus(auditStatus);
  285. }
  286. // 发邮件
  287. WxAppinfo appinfo = appinfoService.getByAppId(appId);
  288. if (appinfo != null) {
  289. SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式
  290. StringBuilder titleSB = new StringBuilder();
  291. if (appinfo.getType().equals(EnumAppType.C.getCode())) {
  292. titleSB.append("C端小程序");
  293. } else if (appinfo.getType().equals(EnumAppType.B.getCode())) {
  294. titleSB.append("B端小程序");
  295. } else if (appinfo.getType().equals(EnumAppType.MP_S.getCode())) {
  296. titleSB.append("公众号-服务号");
  297. } else if (appinfo.getType().equals(EnumAppType.MP_P.getCode())) {
  298. titleSB.append("公众号-订阅号");
  299. }
  300. titleSB.append(appinfo.getName());
  301. if (success) {
  302. titleSB.append("审核通过");
  303. } else {
  304. titleSB.append("审核未通过");
  305. }
  306. StringBuilder sb = new StringBuilder();
  307. sb.append(df.format(new Date())).append("\n");
  308. if (success) {
  309. sb.append("审核通过\n");
  310. } else {
  311. sb.append("审核未通过\n").append("原因").append(reason);
  312. }
  313. String[] receivers = new String[]{"wangmei@iformall.com", "wuguoqiang@iformall.com", "huanghui@iformall.com", "sunmingming@iformall.com"};
  314. //发送邮件
  315. mailService.sendSimpleMail(receivers, titleSB.toString(), sb.toString());
  316. }
  317. }
  318. @GetMapping(value = "/createOpenPlatform")
  319. @ApiOperation("创建微信开放平台")
  320. public ResultData createOpenPlatform(@RequestParam(value = "appId") String appId) {
  321. Gson gson = new GsonBuilder().create();
  322. try {
  323. String responseStr = openService.getWxOpenComponentService().getWxMpServiceByAppid(appId).createOpenPlatform(appId);
  324. logger.info(responseStr);
  325. WxOpenPlatformResult openPlatformResult = gson.fromJson(responseStr, WxOpenPlatformResult.class);
  326. if (openPlatformResult.isSuccess()) {
  327. WxAuthorizerInfo authorizerInfo = authorizerInfoService.getByAppId(appId);
  328. if (authorizerInfo != null) {
  329. authorizerInfo.setId(authorizerInfo.getId());
  330. authorizerInfo.setOpenAppid(openPlatformResult.getOpenAppid());
  331. authorizerInfoService.updateOpenAppid(authorizerInfo);
  332. }
  333. }
  334. return new ResultData(responseStr);
  335. } catch (WxErrorException e) {
  336. logger.error(e.getMessage());
  337. return new ResultData(Result.ERROR, e.getMessage());
  338. }
  339. }
  340. @GetMapping(value = "/bindOpenPlatform")
  341. @ApiOperation("绑定微信开放平台")
  342. public ResultData bindOpenPlatform(@RequestParam(value = "appId") String appId, @RequestParam(value = "openAppId") String openAppId) {
  343. Gson gson = new GsonBuilder().create();
  344. try {
  345. String responseStr = openService.getWxOpenComponentService().getWxMpServiceByAppid(appId).bindOpenPlatform(appId, openAppId);
  346. logger.info(responseStr);
  347. WxOpenResult openResult = gson.fromJson(responseStr, WxOpenResult.class);
  348. if (openResult.isSuccess()) {
  349. return new ResultData();
  350. }
  351. return new ResultData(Result.ERROR, responseStr);
  352. } catch (WxErrorException e) {
  353. logger.error(e.getMessage());
  354. return new ResultData(Result.ERROR, e.getMessage());
  355. }
  356. }
  357. @GetMapping(value = "/unbindOpenPlatform")
  358. @ApiOperation("解绑微信开放平台")
  359. public ResultData unbindOpenPlatform(@RequestParam(value = "appId") String appId, @RequestParam(value = "openAppId") String openAppId) {
  360. Gson gson = new GsonBuilder().create();
  361. try {
  362. String responseStr = openService.getWxOpenComponentService().getWxMpServiceByAppid(appId).unbindOpenPlatform(appId, openAppId);
  363. logger.info(responseStr);
  364. WxOpenResult openResult = gson.fromJson(responseStr, WxOpenResult.class);
  365. if (openResult.isSuccess()) {
  366. return new ResultData();
  367. }
  368. return new ResultData(Result.ERROR, responseStr);
  369. } catch (WxErrorException e) {
  370. logger.error(e.getMessage());
  371. return new ResultData(Result.ERROR, e.getMessage());
  372. }
  373. }
  374. @GetMapping(value = "/getOpenPlatform")
  375. @ApiOperation("获取微信开放平台appId")
  376. public ResultData getOpenPlatform(@RequestParam(value = "appId") String appId) {
  377. Gson gson = new GsonBuilder().create();
  378. try {
  379. String responseStr = openService.getWxOpenComponentService().getWxMpServiceByAppid(appId).getBindOpenPlatform(appId);
  380. logger.info(responseStr);
  381. WxOpenPlatformResult openPlatformResult = gson.fromJson(responseStr, WxOpenPlatformResult.class);
  382. if (openPlatformResult.isSuccess()) {
  383. return new ResultData(openPlatformResult.getOpenAppid());
  384. }
  385. return new ResultData(Result.ERROR, responseStr);
  386. } catch (WxErrorException e) {
  387. logger.error(e.getMessage());
  388. return new ResultData(Result.ERROR, e.getMessage());
  389. }
  390. }
  391. @GetMapping(value = "/clearQuota")
  392. @ApiOperation("代公众号清零quota,每个公众号每个月有10次清零机会,包括在微信公众平台上的清零以及调用API进行清零")
  393. public ResultData clearQuota(@RequestParam(value = "appId") String appId) {
  394. try {
  395. openService.getWxOpenComponentService().getWxMpServiceByAppid(appId).clearQuota(appId);
  396. return new ResultData();
  397. } catch (WxErrorException e) {
  398. logger.error(e.getMessage());
  399. return new ResultData(Result.ERROR, e.getMessage());
  400. }
  401. }
  402. }