package com.iformall.controller; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.iformall.common.Result; import com.iformall.common.ResultData; import com.iformall.controller.base.BaseController; import com.iformall.domain.po.*; import com.iformall.enums.*; import com.iformall.service.*; import com.iformall.service.wechat.FmOpenService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; import me.chanjar.weixin.open.bean.WxOpenCreateResult; import me.chanjar.weixin.open.bean.WxOpenGetResult; import me.chanjar.weixin.open.bean.message.WxOpenXmlMessage; import me.chanjar.weixin.open.bean.result.*; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.nio.charset.Charset; import java.util.Date; import java.util.Map; @Controller @RequestMapping("/wxOpen") @Api(description = "微信第三方开放平台回调相关接口") public class WechatCalllbackController extends BaseController { private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired protected FmOpenService openService; @Autowired private WxComponentVerifyTicketService componentVerifyTicketService; @Autowired private WxAuthorizerInfoService authorizerInfoService; @PostMapping(value = "/notify") @ApiOperation("接收ticket回调") @ResponseBody public String receiveTicket(HttpServletRequest request) throws IOException { logger.info("[" + getIpAddr() + "]接收微信回调 /wxOpen/notify"); String timestamp = request.getParameter("timestamp"); String nonce = request.getParameter("nonce"); String signature = request.getParameter("signature"); String encType = request.getParameter("encrypt_type"); String msgSignature = request.getParameter("msg_signature"); if (!StringUtils.equalsIgnoreCase("aes", encType) || !openService.getWxOpenComponentService().checkSignature(timestamp, nonce, signature)) { throw new IllegalArgumentException("非法请求,可能属于伪造的请求!"); } String resultxml = getRequestXML(request); logger.info("\n接收微信回调:[signature=[{}], encType=[{}], msgSignature=[{}]," + " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ", signature, encType, msgSignature, timestamp, nonce, resultxml); // aes加密的消息, 解密 WxOpenXmlMessage inMessage = WxOpenXmlMessage.fromEncryptedXml(resultxml, openService.getWxOpenConfigStorage(), timestamp, nonce, msgSignature); this.logger.info("接收微信回调,消息解密后内容为:{} ", inMessage.toString()); // 处理 handleTicket(inMessage); return "success"; } /** * 处理ticket消息 * @param inMessage * @return */ private void handleTicket(WxOpenXmlMessage inMessage) { try { String out = openService.getWxOpenComponentService().route(inMessage); this.logger.info("组装回复信息:{}", out); } catch (WxErrorException e) { this.logger.error("notify", e); } if (inMessage.getInfoType().equalsIgnoreCase(EnumWxAuthorizationInfoType.COMPONENT_VERIFY_TICKET.getMessage())) { // 保存component_verify_ticket componentVerifyTicketService.saveOrUpdate(new WxComponentVerifyTicket() { { setComponentVerifyTicket(inMessage.getComponentVerifyTicket()); setCreateTime(new Date(Long.valueOf(inMessage.getCreateTime() * 1000))); setComponentAppid(inMessage.getAppId()); setDeadline(new Date(Long.valueOf(System.currentTimeMillis() + (60 * 60 * 1000)))); } }); } else if (inMessage.getInfoType().equalsIgnoreCase(EnumWxAuthorizationInfoType.AUTHORIZED.getMessage()) || inMessage.getInfoType().equalsIgnoreCase(EnumWxAuthorizationInfoType.UPDATEAUTHORIZED.getMessage())) { // 审核通过 handleWeappAuditedMessage(inMessage); } else if (inMessage.getInfoType().equals(EnumWxAuthorizationInfoType.UNAUTHORIZED.getMessage())) { // 授权取消 authorizerInfoService.saveOrUpdate(new WxAuthorizerInfo() { { setAuthorizerAppid(inMessage.getAuthorizerAppid()); setAuthorizationStatus(EnumWxAuthorizationStatus.UNAUTHORIZED.getCode()); setAuthTime(new Date()); } }); } } /** * 处理小程序审核消息 * @param inMessage * @return */ private boolean handleWeappAuditedMessage(WxOpenXmlMessage inMessage) { // 审核通过 try { WxOpenQueryAuthResult queryAuthResult = openService.getWxOpenComponentService().getQueryAuth(inMessage.getAuthorizationCode()); logger.info("getQueryAuth", queryAuthResult); WxOpenAuthorizerInfoResult openAuthorizerInfoResult = openService.getWxOpenComponentService().getAuthorizerInfo( queryAuthResult.getAuthorizationInfo().getAuthorizerAppid()); logger.info(openAuthorizerInfoResult.toString()); // save auth info authorizerInfoService.saveOrUpdate(new WxAuthorizerInfo() { { setAuthorizerAppid(openAuthorizerInfoResult.getAuthorizationInfo().getAuthorizerAppid()); setAlias(openAuthorizerInfoResult.getAuthorizerInfo().getAlias()); setHeadImg(openAuthorizerInfoResult.getAuthorizerInfo().getHeadImg()); setQrcodeUrl(openAuthorizerInfoResult.getAuthorizerInfo().getQrcodeUrl()); setRefreshToken(openAuthorizerInfoResult.getAuthorizationInfo().getAuthorizerRefreshToken()); setAccessToken(openAuthorizerInfoResult.getAuthorizationInfo().getAuthorizerAccessToken()); setAccessTokenExpire(new Date(Long.valueOf(System.currentTimeMillis() + openAuthorizerInfoResult.getAuthorizationInfo().getExpiresIn()*1000))); setAuthorizationStatus(EnumWxAuthorizationStatus.AUTHORIZED.getCode()); setAuthTime(new Date()); } }); return true; } catch (WxErrorException e) { logger.error(e.getMessage()); } return false; } @RequestMapping(value = "/{appId}/callback") @ApiOperation("消息回调") public void callback(@PathVariable("appId") String appId, HttpServletRequest request, HttpServletResponse response) throws IOException { logger.info("[" + getIpAddr() + "]接收微信APP回调 /{appId}/callback"); String timestamp = request.getParameter("timestamp"); String nonce = request.getParameter("nonce"); String signature = request.getParameter("signature"); String encType = request.getParameter("encrypt_type"); String openid = request.getParameter("openid"); String msgSignature = request.getParameter("msg_signature"); // 第三方平台一定接收的是加密的消息 if (!StringUtils.equalsIgnoreCase("aes", encType) || !openService.getWxOpenComponentService().checkSignature(timestamp, nonce, signature)) { throw new IllegalArgumentException("非法请求,可能属于伪造的请求!"); } String resultxml = getRequestXML(request); logger.info("接收微信APP回调:[appId=[{}], openid=[{}], signature=[{}], encType=[{}], msgSignature=[{}]," + " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ", appId, openid, signature, encType, msgSignature, timestamp, nonce, resultxml); String out = ""; // aes加密的消息, 解密 WxMpXmlMessage inMessage = WxOpenXmlMessage.fromEncryptedMpXml(resultxml, openService.getWxOpenConfigStorage(), timestamp, nonce, msgSignature); logger.info("接收微信APP回调,消息解密后内容为:\n{}", inMessage.toString()); // 全网发布测试用例 if (StringUtils.equalsAnyIgnoreCase(appId, "wx570bc396a51b8ff8", "wxd101a85aa106f53e")) { handleWechatTest(appId, response, inMessage); PrintWriter printWriter = response.getWriter(); wechatWriteSuccess(printWriter); return; } // 实际 消息 处理 //handleCallbackMessage(appId, response, inMessage); WxMpXmlOutMessage outMessage = openService.getWxOpenMessageRouter().route(inMessage, appId); if(outMessage == null) { PrintWriter printWriter = response.getWriter(); wechatWriteSuccess(printWriter); } PrintWriter printWriter = response.getWriter(); wechatWriteSuccess(printWriter); } private void wechatWriteSuccess(PrintWriter printWriter) { printWriter.print("success"); printWriter.flush(); printWriter.close(); } /** * 测试账号特殊处理 * @param appId * @param response * @param inMessage * @throws IOException */ private void handleWechatTest(@PathVariable("appId") String appId, HttpServletResponse response, WxMpXmlMessage inMessage) throws IOException { String out; try { if (StringUtils.equals(inMessage.getMsgType(), "text")) { if (StringUtils.equals(inMessage.getContent(), "TESTCOMPONENT_MSG_TYPE_TEXT")) { out = WxOpenXmlMessage.wxMpOutXmlMessageToEncryptedXml( WxMpXmlOutMessage.TEXT().content("TESTCOMPONENT_MSG_TYPE_TEXT_callback") .fromUser(inMessage.getToUser()) .toUser(inMessage.getFromUser()) .build(), openService.getWxOpenConfigStorage() ); } else if (StringUtils.startsWith(inMessage.getContent(), "QUERY_AUTH_CODE:")) { String msg = inMessage.getContent().replace("QUERY_AUTH_CODE:", "") + "_from_api"; WxMpKefuMessage kefuMessage = WxMpKefuMessage.TEXT().content(msg).toUser(inMessage.getFromUser()).build(); openService.getWxOpenComponentService().getWxMpServiceByAppid(appId).getKefuService().sendKefuMessage(kefuMessage); } } else if (StringUtils.equals(inMessage.getMsgType(), "event")) { WxMpKefuMessage kefuMessage = WxMpKefuMessage.TEXT().content(inMessage.getEvent() + "from_callback").toUser(inMessage.getFromUser()).build(); openService.getWxOpenComponentService().getWxMpServiceByAppid(appId).getKefuService().sendKefuMessage(kefuMessage); } } catch (WxErrorException e) { logger.error("callback", e); } } private String getRequestXML(HttpServletRequest request) throws IOException { InputStream inStream = request.getInputStream(); ByteArrayOutputStream outSteam = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len = 0; while ((len = inStream.read(buffer)) != -1) { outSteam.write(buffer, 0, len); } String resultxml = new String(outSteam.toByteArray(), Charset.forName("UTF-8")); logger.info(resultxml); outSteam.close(); inStream.close(); return resultxml; } private void getThreadCount( int i) { ThreadGroup group = Thread.currentThread().getThreadGroup(); ThreadGroup topGroup = group; // 遍历线程组树,获取根线程组 while (group != null) { topGroup = group; group = group.getParent(); } // 激活的线程数加倍 int activeCount = topGroup.activeCount(); // copy into a list that is the exact size logger.info("Thread list size == " + i + " : " + activeCount); } @GetMapping(value = "/createOpenPlatform") @ApiOperation("创建微信开放平台") public ResultData createOpenPlatform(@RequestParam(value = "appId") String appId) { Gson gson = new GsonBuilder().create(); try { WxOpenCreateResult ret = openService.getWxOpenComponentService().createOpenAccount(appId); logger.info(ret.toString()); if (ret.isSuccess()) { WxAuthorizerInfo authorizerInfo = authorizerInfoService.getByAppId(appId); if (authorizerInfo != null) { authorizerInfo.setId(authorizerInfo.getId()); authorizerInfo.setOpenAppid(ret.getOpenAppid()); authorizerInfoService.updateOpenAppid(authorizerInfo); } } return new ResultData(ret.toString()); } catch (WxErrorException e) { logger.error(e.getMessage()); return new ResultData(Result.ERROR, e.getMessage()); } } @GetMapping(value = "/bindOpenPlatform") @ApiOperation("绑定微信开放平台") public ResultData bindOpenPlatform(@RequestParam(value = "appId") String appId, @RequestParam(value = "openAppId") String openAppId) { Gson gson = new GsonBuilder().create(); try { Boolean ret = openService.getWxOpenComponentService().bindOpenAccount(appId, openAppId); if (ret) { WxAuthorizerInfo authorizerInfo = authorizerInfoService.getByAppId(appId); if (authorizerInfo != null) { authorizerInfo.setId(authorizerInfo.getId()); authorizerInfo.setOpenAppid(openAppId); authorizerInfoService.updateOpenAppid(authorizerInfo); } return new ResultData(); } return new ResultData(Result.ERROR, ret.toString()); } catch (WxErrorException e) { logger.error(e.getMessage()); return new ResultData(Result.ERROR, e.getMessage()); } } @GetMapping(value = "/unbindOpenPlatform") @ApiOperation("解绑微信开放平台") public ResultData unbindOpenPlatform(@RequestParam(value = "appId") String appId, @RequestParam(value = "openAppId") String openAppId) { Gson gson = new GsonBuilder().create(); try { Boolean ret = openService.getWxOpenComponentService().unbindOpenAccount(appId, openAppId); if (ret) { WxAuthorizerInfo authorizerInfo = new WxAuthorizerInfo(); authorizerInfo.setAuthorizerAppid(appId); authorizerInfoService.removeOpenAppid(authorizerInfo); return new ResultData(); } return new ResultData(Result.ERROR, ret.toString()); } catch (WxErrorException e) { logger.error(e.getMessage()); return new ResultData(Result.ERROR, e.getMessage()); } } @GetMapping(value = "/getOpenPlatform") @ApiOperation("获取微信开放平台appId") public ResultData getOpenPlatform(@RequestParam(value = "appId") String appId) { Gson gson = new GsonBuilder().create(); try { WxOpenGetResult ret = openService.getWxOpenComponentService().getOpenAccount(appId); logger.info(ret.toString()); if (ret.isSuccess()) { WxAuthorizerInfo authorizerInfo = authorizerInfoService.getByAppId(appId); if (authorizerInfo != null) { if(StringUtils.isBlank(authorizerInfo.getOpenAppid()) && StringUtils.isNotBlank(ret.getOpenAppid())) { authorizerInfo.setId(authorizerInfo.getId()); authorizerInfo.setOpenAppid(ret.getOpenAppid()); authorizerInfoService.updateOpenAppid(authorizerInfo); } } return new ResultData(ret.getOpenAppid()); } return new ResultData(Result.ERROR, ret.toString()); } catch (WxErrorException e) { logger.error(e.getMessage()); return new ResultData(Result.ERROR, e.getMessage()); } } @GetMapping(value = "/clearQuota") @ApiOperation("代公众号清零quota,每个公众号每个月有10次清零机会,包括在微信公众平台上的清零以及调用API进行清零") public ResultData clearQuota(@RequestParam(value = "appId") String appId) { try { openService.getWxOpenComponentService().getWxMpServiceByAppid(appId).clearQuota(appId); return new ResultData(); } catch (WxErrorException e) { logger.error(e.getMessage()); return new ResultData(Result.ERROR, e.getMessage()); } } @GetMapping(value = "/getAuthorizerList") @ApiOperation("拉取当前所有已授权的帐号基本信息") public ResultData getAuthorizerList() { try { WxOpenAuthorizerListResult authorizerListResult = openService.getWxOpenComponentService().getAuthorizerList(0, 100); logger.info("getAuthorizerList: " + authorizerListResult); if(authorizerListResult.getTotalCount() > 0) { for (Map data : authorizerListResult.getList()) { String appId = data.get("authorizer_appid"); String refreshToken = data.get("refresh_token"); Long authTime = Long.valueOf(data.get("auth_time")); Date authDate = new Date(authTime*1000); if(StringUtils.isBlank(refreshToken)) { continue; } WxAuthorizerInfo authorizerInfo = new WxAuthorizerInfo(); authorizerInfo.setAuthorizerAppid(appId); authorizerInfo.setRefreshToken(refreshToken); authorizerInfo.setAuthTime(authDate); authorizerInfoService.updateAuthAppidInfo(authorizerInfo); openService.getWxOpenComponentService().getWxOpenConfigStorage().setAuthorizerRefreshToken(appId, refreshToken); } } return new ResultData(authorizerListResult.toString()); } catch (WxErrorException e) { logger.error(e.getMessage()); return new ResultData(Result.ERROR, e.getMessage()); } } }