diff --git a/mlWechatOpen/src/main/java/com/iformall/controller/WechatCalllbackController.java b/mlWechatOpen/src/main/java/com/iformall/controller/WechatCalllbackController.java index 5f542e6..42b55fc 100644 --- a/mlWechatOpen/src/main/java/com/iformall/controller/WechatCalllbackController.java +++ b/mlWechatOpen/src/main/java/com/iformall/controller/WechatCalllbackController.java @@ -6,11 +6,15 @@ import com.iformall.common.Result; import com.iformall.common.ResultData; import com.iformall.domain.po.*; import com.iformall.enums.*; +import com.iformall.mp.service.WxMpManager; +import com.iformall.mp.utils.MpUtil; import com.iformall.service.*; import com.iformall.service.wechat.WxOpenService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.api.WxMpMessageRouter; +import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; @@ -59,10 +63,13 @@ public class WechatCalllbackController extends BaseController { @Autowired private MailService mailService; + @Autowired + private WxMpManager mpManager; + @RequestMapping(value = "/notify", method = RequestMethod.POST) @ApiOperation("接收ticket回调") public Object receiveTicket(HttpServletRequest request) throws IOException { - logger.info("[" +getIpAddr() + "]接收微信回调 /wxOpen/notify"); + logger.info("[" + getIpAddr() + "]接收微信回调 /wxOpen/notify"); InputStream inStream = request.getInputStream(); ByteArrayOutputStream outSteam = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; @@ -77,10 +84,10 @@ public class WechatCalllbackController extends BaseController { inStream.close(); String timestamp = request.getParameter("timestamp"); - String nonce = request.getParameter("nonce"); + String nonce = request.getParameter("nonce"); String signature = request.getParameter("signature"); String encType = request.getParameter("encrypt_type"); - String msgSignature= request.getParameter("msg_signature"); + String msgSignature = request.getParameter("msg_signature"); /* // 测试数据 @@ -108,19 +115,19 @@ public class WechatCalllbackController extends BaseController { WxOpenXmlMessage inMessage = WxOpenXmlMessage.fromEncryptedXml(resultxml, openService.getWxOpenConfigStorage(), timestamp, nonce, msgSignature); this.logger.info("接收微信回调,消息解密后内容为:{} ", inMessage.toString()); - if(inMessage.getInfoType().equals(EnumWxAuthorizationInfoType.COMPONENT_VERIFY_TICKET.getMessage())) { + if (inMessage.getInfoType().equals(EnumWxAuthorizationInfoType.COMPONENT_VERIFY_TICKET.getMessage())) { // 保存component_verify_ticket - componentVerifyTicketService.saveOrUpdate(new WxComponentVerifyTicket(){ + 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)))); + setDeadline(new Date(Long.valueOf(System.currentTimeMillis() + (60 * 60 * 1000)))); } }); } else if (inMessage.getInfoType().equals(EnumWxAuthorizationInfoType.AUTHORIZED.getMessage()) || inMessage.getInfoType().equals(EnumWxAuthorizationInfoType.UPDATEAUTHORIZED.getMessage()) - ) { + ) { try { WxOpenQueryAuthResult queryAuthResult = openService.getWxOpenComponentService().getQueryAuth(inMessage.getAuthorizationCode()); logger.info("getQueryAuth", queryAuthResult); @@ -129,7 +136,7 @@ public class WechatCalllbackController extends BaseController { queryAuthResult.getAuthorizationInfo().getAuthorizerAppid()); logger.info(openAuthorizerInfoResult.toString()); // save auth info - authorizerInfoService.saveOrUpdate(new WxAuthorizerInfo(){ + authorizerInfoService.saveOrUpdate(new WxAuthorizerInfo() { { setAuthorizerAppid(openAuthorizerInfoResult.getAuthorizationInfo().getAuthorizerAppid()); setAlias(openAuthorizerInfoResult.getAuthorizerInfo().getAlias()); @@ -143,7 +150,7 @@ public class WechatCalllbackController extends BaseController { logger.error(e.getMessage()); } } else if (inMessage.getInfoType().equals(EnumWxAuthorizationInfoType.UNAUTHORIZED.getMessage())) { - authorizerInfoService.saveOrUpdate(new WxAuthorizerInfo(){ + authorizerInfoService.saveOrUpdate(new WxAuthorizerInfo() { { setAuthorizerAppid(inMessage.getAuthorizerAppid()); setAuthorizationStatus(EnumWxAuthorizationStatus.UNAUTHORIZED.getCode()); @@ -163,8 +170,16 @@ public class WechatCalllbackController extends BaseController { @RequestMapping(value = "/{appId}/callback") @ApiOperation("消息回调") - public Object callback(@PathVariable("appId") String appId, HttpServletRequest request) throws IOException { - logger.info("[" +getIpAddr() + "]接收微信APP回调 /{appId}/callback"); + @ResponseBody + public String callback(@PathVariable("appId") String appId, HttpServletRequest request) 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"); + InputStream inStream = request.getInputStream(); ByteArrayOutputStream outSteam = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; @@ -178,27 +193,24 @@ public class WechatCalllbackController extends BaseController { outSteam.close(); inStream.close(); - 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"); - logger.info("接收微信APP回调:[appId=[{}], openid=[{}], signature=[{}], encType=[{}], msgSignature=[{}]," + " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ", appId, openid, signature, encType, msgSignature, timestamp, nonce, resultxml); + // 第三方平台一定接收的是加密的消息 if (!StringUtils.equalsIgnoreCase("aes", encType) || !openService.getWxOpenComponentService().checkSignature(timestamp, nonce, signature)) { throw new IllegalArgumentException("非法请求,可能属于伪造的请求!"); } + WxAppinfo appinfo = appinfoService.getByAppId(appId); + 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")) { try { if (StringUtils.equals(inMessage.getMsgType(), "text")) { @@ -222,9 +234,12 @@ public class WechatCalllbackController extends BaseController { } catch (WxErrorException e) { logger.error("callback", e); } - } else { + } + */ + if(appinfo.getType().equals(EnumAppType.B.getCode()) || appinfo.getType().equals(EnumAppType.C.getCode())) { + // 小程序 /**判断消息类型,调用对应的方法*/ - switch (inMessage.getMsgType()){ + switch (inMessage.getMsgType()) { case "event": { switch (inMessage.getEvent()) { case "weapp_audit_success": { // 审核成功 @@ -246,18 +261,6 @@ public class WechatCalllbackController extends BaseController { out = WxOpenXmlMessage.wxMpOutXmlMessageToEncryptedXml(outMessage, openService.getWxOpenConfigStorage()); logger.info(out); } - /* - else { - - WxMpXmlOutMessage outMsg = new WxMpXmlOutTextMessage(); - outMsg.setCreateTime(System.currentTimeMillis()); - outMsg.setToUserName(inMessage.getFromUser()); - outMsg.setFromUserName(inMessage.getToUser()); - outMsg.setMsgType("text"); - ((WxMpXmlOutTextMessage) outMsg).setContent(inMessage.getContent()); - out = WxOpenXmlMessage.wxMpOutXmlMessageToEncryptedXml(outMsg, wxOpenService.getWxOpenConfigStorage()); - } - */ else { out = "success"; } @@ -265,9 +268,21 @@ public class WechatCalllbackController extends BaseController { default: return "success"; } + } else { + // 公众号 + WxMpService mpService = MpUtil.getMpService(appinfo); + try { + WxMpXmlOutMessage outMessage = mpManager.getRouter(mpService).route(inMessage); + if (outMessage == null) { + this.logger.info("2 return: success"); + return "success"; + } + out = outMessage.toEncryptedXml(mpService.getWxMpConfigStorage()); + } catch (Exception e) { + logger.error("路由消息时出现异常!", e); + } } - return out; } @@ -275,8 +290,8 @@ public class WechatCalllbackController extends BaseController { WxWeappAuditStatus auditStatus = weappAuditStatusService.getByAppId(appId); - if(auditStatus != null) { - if(success) { + if (auditStatus != null) { + if (success) { auditStatus.setAuditStatus(EnumWeappAuditStatus.SUCCESS.getCode()); auditStatus.setAuditErrCode(""); } else { @@ -289,21 +304,21 @@ public class WechatCalllbackController extends BaseController { // 发邮件 WxAppinfo appinfo = appinfoService.getByAppId(appId); - if(appinfo != null) { + if (appinfo != null) { SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式 StringBuilder titleSB = new StringBuilder(); - if(appinfo.getType().equals(EnumAppType.C.getCode())) { + if (appinfo.getType().equals(EnumAppType.C.getCode())) { titleSB.append("C端小程序"); - } else if(appinfo.getType().equals(EnumAppType.B.getCode())) { + } else if (appinfo.getType().equals(EnumAppType.B.getCode())) { titleSB.append("B端小程序"); - } else if(appinfo.getType().equals(EnumAppType.MP_S.getCode())) { + } else if (appinfo.getType().equals(EnumAppType.MP_S.getCode())) { titleSB.append("公众号-服务号"); - } else if(appinfo.getType().equals(EnumAppType.MP_P.getCode())) { + } else if (appinfo.getType().equals(EnumAppType.MP_P.getCode())) { titleSB.append("公众号-订阅号"); } titleSB.append(appinfo.getName()); - if(success) { + if (success) { titleSB.append("审核通过"); } else { titleSB.append("审核未通过"); @@ -311,13 +326,13 @@ public class WechatCalllbackController extends BaseController { StringBuilder sb = new StringBuilder(); sb.append(df.format(new Date())).append("\n"); - if(success) { + if (success) { sb.append("审核通过\n"); } else { sb.append("审核未通过\n").append("原因").append(reason); } - String[] receivers = new String[] {"wangmei@iformall.com", "wuguoqiang@iformall.com", "huanghui@iformall.com", "sunmingming@iformall.com"}; + String[] receivers = new String[]{"wangmei@iformall.com", "wuguoqiang@iformall.com", "huanghui@iformall.com", "sunmingming@iformall.com"}; //发送邮件 mailService.sendSimpleMail(receivers, titleSB.toString(), sb.toString()); @@ -326,15 +341,15 @@ public class WechatCalllbackController extends BaseController { @GetMapping(value = "/createOpenPlatform") @ApiOperation("创建微信开放平台") - public ResultData createOpenPlatform(@RequestParam(value="appId") String appId) { + public ResultData createOpenPlatform(@RequestParam(value = "appId") String appId) { Gson gson = new GsonBuilder().create(); try { String responseStr = openService.getWxOpenComponentService().getWxMpServiceByAppid(appId).createOpenPlatform(appId); logger.info(responseStr); WxOpenPlatformResult openPlatformResult = gson.fromJson(responseStr, WxOpenPlatformResult.class); - if(openPlatformResult.isSuccess()) { + if (openPlatformResult.isSuccess()) { WxAuthorizerInfo authorizerInfo = authorizerInfoService.getByAppId(appId); - if(authorizerInfo != null) { + if (authorizerInfo != null) { authorizerInfo.setId(authorizerInfo.getId()); authorizerInfo.setOpenAppid(openPlatformResult.getOpenAppid()); authorizerInfoService.updateOpenAppid(authorizerInfo); @@ -355,7 +370,7 @@ public class WechatCalllbackController extends BaseController { String responseStr = openService.getWxOpenComponentService().getWxMpServiceByAppid(appId).bindOpenPlatform(appId, openAppId); logger.info(responseStr); WxOpenResult openResult = gson.fromJson(responseStr, WxOpenResult.class); - if(openResult.isSuccess()) { + if (openResult.isSuccess()) { return new ResultData(); } return new ResultData(Result.ERROR, responseStr); @@ -373,7 +388,7 @@ public class WechatCalllbackController extends BaseController { String responseStr = openService.getWxOpenComponentService().getWxMpServiceByAppid(appId).unbindOpenPlatform(appId, openAppId); logger.info(responseStr); WxOpenResult openResult = gson.fromJson(responseStr, WxOpenResult.class); - if(openResult.isSuccess()) { + if (openResult.isSuccess()) { return new ResultData(); } return new ResultData(Result.ERROR, responseStr); @@ -391,7 +406,7 @@ public class WechatCalllbackController extends BaseController { String responseStr = openService.getWxOpenComponentService().getWxMpServiceByAppid(appId).getBindOpenPlatform(appId); logger.info(responseStr); WxOpenPlatformResult openPlatformResult = gson.fromJson(responseStr, WxOpenPlatformResult.class); - if(openPlatformResult.isSuccess()) { + if (openPlatformResult.isSuccess()) { return new ResultData(openPlatformResult.getOpenAppid()); } return new ResultData(Result.ERROR, responseStr); diff --git a/mlWechatOpen/src/main/java/com/iformall/mp/builder/AbstractBuilder.java b/mlWechatOpen/src/main/java/com/iformall/mp/builder/AbstractBuilder.java new file mode 100644 index 0000000..7dabed7 --- /dev/null +++ b/mlWechatOpen/src/main/java/com/iformall/mp/builder/AbstractBuilder.java @@ -0,0 +1,17 @@ +package com.iformall.mp.builder; + +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; +import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Stormeye + */ +public abstract class AbstractBuilder { + protected final Logger logger = LoggerFactory.getLogger(getClass()); + + public abstract WxMpXmlOutMessage build(String content, + WxMpXmlMessage wxMessage, WxMpService service); +} diff --git a/mlWechatOpen/src/main/java/com/iformall/mp/builder/ImageBuilder.java b/mlWechatOpen/src/main/java/com/iformall/mp/builder/ImageBuilder.java new file mode 100644 index 0000000..54e00de --- /dev/null +++ b/mlWechatOpen/src/main/java/com/iformall/mp/builder/ImageBuilder.java @@ -0,0 +1,24 @@ +package com.iformall.mp.builder; + +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; +import me.chanjar.weixin.mp.bean.message.WxMpXmlOutImageMessage; +import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; + +/** + * @author Binary Wang(https://github.com/binarywang) + */ +public class ImageBuilder extends AbstractBuilder { + + @Override + public WxMpXmlOutMessage build(String content, WxMpXmlMessage wxMessage, + WxMpService service) { + + WxMpXmlOutImageMessage m = WxMpXmlOutMessage.IMAGE().mediaId(content) + .fromUser(wxMessage.getToUser()).toUser(wxMessage.getFromUser()) + .build(); + + return m; + } + +} diff --git a/mlWechatOpen/src/main/java/com/iformall/mp/builder/TextBuilder.java b/mlWechatOpen/src/main/java/com/iformall/mp/builder/TextBuilder.java new file mode 100644 index 0000000..faaef86 --- /dev/null +++ b/mlWechatOpen/src/main/java/com/iformall/mp/builder/TextBuilder.java @@ -0,0 +1,22 @@ +package com.iformall.mp.builder; + +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; +import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; +import me.chanjar.weixin.mp.bean.message.WxMpXmlOutTextMessage; + +/** + * @author Binary Wang(https://github.com/binarywang) + */ +public class TextBuilder extends AbstractBuilder { + + @Override + public WxMpXmlOutMessage build(String content, WxMpXmlMessage wxMessage, + WxMpService service) { + WxMpXmlOutTextMessage m = WxMpXmlOutMessage.TEXT().content(content) + .fromUser(wxMessage.getToUser()).toUser(wxMessage.getFromUser()) + .build(); + return m; + } + +} diff --git a/mlWechatOpen/src/main/java/com/iformall/mp/controller/WxMemController.java b/mlWechatOpen/src/main/java/com/iformall/mp/controller/WxMemController.java new file mode 100644 index 0000000..c2b170d --- /dev/null +++ b/mlWechatOpen/src/main/java/com/iformall/mp/controller/WxMemController.java @@ -0,0 +1,49 @@ +package com.iformall.mp.controller; + +import com.iformall.common.ResultData; +import com.iformall.mp.service.WxMpManager; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.bean.result.WxMpUser; +import me.chanjar.weixin.mp.bean.result.WxMpUserList; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * @author Stormeye + */ +@RestController +@RequestMapping("/wx/{account}/mem") +public class WxMemController { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + @Autowired + private WxMpManager wxMpManager; + + @GetMapping("/syncAccountFansList") + public ResultData syncAccountFansList(@PathVariable String account) throws WxErrorException { + final WxMpService wxService = wxMpManager.getMpService(account); + + WxMpUserList mpUserList = wxService.getUserService().userList(null); + logger.info(mpUserList.toString()); + List userList = wxService.getUserService().userInfoList(mpUserList.getOpenids()); + logger.info(userList.toString()); + while(StringUtils.isNotBlank(mpUserList.getNextOpenid())) { + mpUserList = wxService.getUserService().userList(mpUserList.getNextOpenid()); + logger.info(mpUserList.toString()); + if(mpUserList.getOpenids().size() > 0) { + userList = wxService.getUserService().userInfoList(mpUserList.getOpenids()); + logger.info(userList.toString()); + } + } + return new ResultData(); + } +} diff --git a/mlWechatOpen/src/main/java/com/iformall/mp/controller/WxMenuController.java b/mlWechatOpen/src/main/java/com/iformall/mp/controller/WxMenuController.java new file mode 100644 index 0000000..4d332b2 --- /dev/null +++ b/mlWechatOpen/src/main/java/com/iformall/mp/controller/WxMenuController.java @@ -0,0 +1,190 @@ +package com.iformall.mp.controller; + +import com.iformall.mp.service.WxMpManager; +import me.chanjar.weixin.common.api.WxConsts; +import me.chanjar.weixin.common.bean.menu.WxMenu; +import me.chanjar.weixin.common.bean.menu.WxMenuButton; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.bean.menu.WxMpGetSelfMenuInfoResult; +import me.chanjar.weixin.mp.bean.menu.WxMpMenu; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; +import java.net.MalformedURLException; +import java.net.URL; + +import static me.chanjar.weixin.common.api.WxConsts.MenuButtonType; + +/** + * @author Stormeye + */ +@RestController +@RequestMapping("/wx/menu/{account}") +public class WxMenuController { + + @Autowired + private WxMpManager wxMpManager; + + /** + *
+     * 自定义菜单创建接口
+     * 详情请见:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141013&token=&lang=zh_CN
+     * 如果要创建个性化菜单,请设置matchrule属性
+     * 详情请见:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1455782296&token=&lang=zh_CN
+     * 
+ * + * @return 如果是个性化菜单,则返回menuid,否则返回null + */ + @PostMapping("/create") + public String menuCreate(@PathVariable String account, @RequestBody WxMenu menu) throws WxErrorException { + return wxMpManager.getMpService(account).getMenuService().menuCreate(menu); + } + + @GetMapping("/create") + public String menuCreateSample(@PathVariable String account) throws WxErrorException, MalformedURLException { + WxMenu menu = new WxMenu(); + WxMenuButton button1 = new WxMenuButton(); + button1.setType(MenuButtonType.CLICK); + button1.setName("今日歌曲"); + button1.setKey("V1001_TODAY_MUSIC"); + +// WxMenuButton button2 = new WxMenuButton(); +// button2.setType(WxConsts.BUTTON_MINIPROGRAM); +// button2.setName("小程序"); +// button2.setAppId("wx286b93c14bbf93aa"); +// button2.setPagePath("pages/lunar/index.html"); +// button2.setUrl("http://mp.weixin.qq.com"); + + WxMenuButton button3 = new WxMenuButton(); + button3.setName("菜单"); + + menu.getButtons().add(button1); +// menu.getButtons().add(button2); + menu.getButtons().add(button3); + + WxMenuButton button31 = new WxMenuButton(); + button31.setType(MenuButtonType.VIEW); + button31.setName("搜索"); + button31.setUrl("http://www.soso.com/"); + + WxMenuButton button32 = new WxMenuButton(); + button32.setType(MenuButtonType.VIEW); + button32.setName("视频"); + button32.setUrl("http://v.qq.com/"); + + WxMenuButton button33 = new WxMenuButton(); + button33.setType(MenuButtonType.CLICK); + button33.setName("赞一下我们"); + button33.setKey("V1001_GOOD"); + + WxMenuButton button34 = new WxMenuButton(); + button34.setType(MenuButtonType.VIEW); + button34.setName("获取用户信息"); + + ServletRequestAttributes servletRequestAttributes = + (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + if (servletRequestAttributes != null) { + HttpServletRequest request = servletRequestAttributes.getRequest(); + URL requestURL = new URL(request.getRequestURL().toString()); + String url = wxMpManager.getMpService(account) + .oauth2buildAuthorizationUrl( + String.format("%s://%s/wx/redirect/%s/greet", requestURL.getProtocol(), requestURL.getHost(), account), + WxConsts.OAuth2Scope.SNSAPI_USERINFO, null); + button34.setUrl(url); + } + + button3.getSubButtons().add(button31); + button3.getSubButtons().add(button32); + button3.getSubButtons().add(button33); + button3.getSubButtons().add(button34); + + return wxMpManager.getMpService(account).getMenuService().menuCreate(menu); + } + + /** + *
+     * 自定义菜单创建接口
+     * 详情请见: https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141013&token=&lang=zh_CN
+     * 如果要创建个性化菜单,请设置matchrule属性
+     * 详情请见:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1455782296&token=&lang=zh_CN
+     * 
+ * + * @param json + * @return 如果是个性化菜单,则返回menuid,否则返回null + */ + @PostMapping("/createByJson") + public String menuCreate(@PathVariable String account, @RequestBody String json) throws WxErrorException { + return wxMpManager.getMpService(account).getMenuService().menuCreate(json); + } + + /** + *
+     * 自定义菜单删除接口
+     * 详情请见: https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141015&token=&lang=zh_CN
+     * 
+ */ + @GetMapping("/delete") + public void menuDelete(@PathVariable String account) throws WxErrorException { + wxMpManager.getMpService(account).getMenuService().menuDelete(); + } + + /** + *
+     * 删除个性化菜单接口
+     * 详情请见: https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1455782296&token=&lang=zh_CN
+     * 
+ * + * @param menuId 个性化菜单的menuid + */ + @GetMapping("/delete/{menuId}") + public void menuDelete(@PathVariable String account, @PathVariable String menuId) throws WxErrorException { + wxMpManager.getMpService(account).getMenuService().menuDelete(menuId); + } + + /** + *
+     * 自定义菜单查询接口
+     * 详情请见: https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141014&token=&lang=zh_CN
+     * 
+ */ + @GetMapping("/get") + public WxMpMenu menuGet(@PathVariable String account) throws WxErrorException { + return wxMpManager.getMpService(account).getMenuService().menuGet(); + } + + /** + *
+     * 测试个性化菜单匹配结果
+     * 详情请见: http://mp.weixin.qq.com/wiki/0/c48ccd12b69ae023159b4bfaa7c39c20.html
+     * 
+ * + * @param userid 可以是粉丝的OpenID,也可以是粉丝的微信号。 + */ + @GetMapping("/menuTryMatch/{userid}") + public WxMenu menuTryMatch(@PathVariable String account, @PathVariable String userid) throws WxErrorException { + return wxMpManager.getMpService(account).getMenuService().menuTryMatch(userid); + } + + /** + *
+     * 获取自定义菜单配置接口
+     * 本接口将会提供公众号当前使用的自定义菜单的配置,如果公众号是通过API调用设置的菜单,则返回菜单的开发配置,而如果公众号是在公众平台官网通过网站功能发布菜单,则本接口返回运营者设置的菜单配置。
+     * 请注意:
+     * 1、第三方平台开发者可以通过本接口,在旗下公众号将业务授权给你后,立即通过本接口检测公众号的自定义菜单配置,并通过接口再次给公众号设置好自动回复规则,以提升公众号运营者的业务体验。
+     * 2、本接口与自定义菜单查询接口的不同之处在于,本接口无论公众号的接口是如何设置的,都能查询到接口,而自定义菜单查询接口则仅能查询到使用API设置的菜单配置。
+     * 3、认证/未认证的服务号/订阅号,以及接口测试号,均拥有该接口权限。
+     * 4、从第三方平台的公众号登录授权机制上来说,该接口从属于消息与菜单权限集。
+     * 5、本接口中返回的图片/语音/视频为临时素材(临时素材每次获取都不同,3天内有效,通过素材管理-获取临时素材接口来获取这些素材),本接口返回的图文消息为永久素材素材(通过素材管理-获取永久素材接口来获取这些素材)。
+     *  接口调用请求说明:
+     * http请求方式: GET(请使用https协议)
+     * https://api.weixin.qq.com/cgi-bin/get_current_selfmenu_info?access_token=ACCESS_TOKEN
+     * 
+ */ + @GetMapping("/getSelfMenuInfo") + public WxMpGetSelfMenuInfoResult getSelfMenuInfo(@PathVariable String account) throws WxErrorException { + return wxMpManager.getMpService(account).getMenuService().getSelfMenuInfo(); + } +} diff --git a/mlWechatOpen/src/main/java/com/iformall/mp/controller/WxMpCallBackController.java b/mlWechatOpen/src/main/java/com/iformall/mp/controller/WxMpCallBackController.java new file mode 100644 index 0000000..cad7f63 --- /dev/null +++ b/mlWechatOpen/src/main/java/com/iformall/mp/controller/WxMpCallBackController.java @@ -0,0 +1,150 @@ +package com.iformall.mp.controller; + +import com.iformall.mp.service.WxMpManager; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; +import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; +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; + +/** + * @author Stormeye + */ +@Controller +@RequestMapping("/wx") +public class WxMpCallBackController { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + @Autowired + private WxMpManager wxMpManager; + + /** + * GET请求:进行URL、Tocken 认证; + * 1. 将token、timestamp、nonce三个参数进行字典序排序 + * 2. 将三个参数字符串拼接成一个字符串进行sha1加密 + * 3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信 + */ + @GetMapping(value = "/{account}/message") + @ResponseBody + public void authGet(@PathVariable String account, HttpServletRequest request, HttpServletResponse response) throws IOException { + String signature = request.getParameter("signature");// 微信加密签名 + String timestamp = request.getParameter("timestamp");// 时间戳 + String nonce = request.getParameter("nonce");// 随机数 + String echostr = request.getParameter("echostr");// 随机字符串 + + if (StringUtils.isAnyBlank(signature, timestamp, nonce, echostr)) { + throw new IllegalArgumentException("请求参数非法,请核实!"); + } + + this.logger.info("接收到来自微信服务器的认证消息:[{}, {}, {}, {}]", + signature, timestamp, nonce, echostr); + + // 数据库 -> + final WxMpService wxService = wxMpManager.getMpService(account); + if (wxService == null) { + throw new IllegalArgumentException(String.format("未找到对应account=[%s]的配置,请核实!", account)); + } + + if (wxService.checkSignature(timestamp, nonce, signature)) { + logger.info("微信服务验证成功"); + PrintWriter out = response.getWriter(); + out.print(echostr); + out.close(); + } + } + + /** + * POST 请求:进行消息处理; + */ + @PostMapping(value = "/{account}/message") + @ResponseBody + public String post(@PathVariable String account, HttpServletRequest request) throws IOException { + // 处理用户和微信公众账号交互消息 + String signature = request.getParameter("signature");// 微信加密签名 + String timestamp = request.getParameter("timestamp");// 时间戳 + String nonce = request.getParameter("nonce");// 随机数 + String openid = request.getParameter("openid"); + String encType = request.getParameter("encrypt_type"); + String msgSignature = request.getParameter("msg_signature"); + + if (StringUtils.isAnyBlank(signature, timestamp, nonce)) { + throw new IllegalArgumentException("请求参数非法,请核实!"); + } + + final WxMpService wxService = wxMpManager.getMpService(account); + + if (!wxService.checkSignature(timestamp, nonce, signature)) { + throw new IllegalArgumentException("非法请求,可能属于伪造的请求!"); + } + + 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 requestBody = new String(outSteam.toByteArray(), Charset.forName("UTF-8")); + logger.info(requestBody); + + outSteam.close(); + inStream.close(); + + this.logger.info("\n接收微信请求:[openid=[{}], [signature=[{}], encType=[{}], msgSignature=[{}]," + + " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ", + openid, signature, encType, msgSignature, timestamp, nonce, requestBody); + + String out = null; + if (encType == null) { + // 明文传输的消息 + WxMpXmlMessage inMessage = WxMpXmlMessage.fromXml(requestBody); + + WxMpXmlOutMessage outMessage = this.route(inMessage, wxService); + if (outMessage == null) { + logger.info("1 return: success"); + return "success"; + } + + out = outMessage.toXml(); + logger.info(out); + } else if ("aes".equalsIgnoreCase(encType)) { + // aes加密的消息 + WxMpXmlMessage inMessage = WxMpXmlMessage.fromEncryptedXml(requestBody, wxService.getWxMpConfigStorage(), + timestamp, nonce, msgSignature); + this.logger.debug("\n消息解密后内容为:\n{} ", inMessage.toString()); + WxMpXmlOutMessage outMessage = this.route(inMessage, wxService); + if (outMessage == null) { + this.logger.info("2 return: success"); + return "success"; + } + logger.info(outMessage.toXml()); + + out = outMessage.toEncryptedXml(wxService.getWxMpConfigStorage()); + } + this.logger.info("\n组装回复信息:{}", out); + return out; + } + + private WxMpXmlOutMessage route(WxMpXmlMessage message, WxMpService wxService) { + try { + return wxMpManager.getRouter(wxService).route(message); + } catch (Exception e) { + this.logger.error("路由消息时出现异常!", e); + } + + return null; + } + +} diff --git a/mlWechatOpen/src/main/java/com/iformall/mp/controller/WxRedirectController.java b/mlWechatOpen/src/main/java/com/iformall/mp/controller/WxRedirectController.java new file mode 100644 index 0000000..7e6a1ae --- /dev/null +++ b/mlWechatOpen/src/main/java/com/iformall/mp/controller/WxRedirectController.java @@ -0,0 +1,44 @@ +package com.iformall.mp.controller; + +import com.iformall.mp.service.WxMpManager; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken; +import me.chanjar.weixin.mp.bean.result.WxMpUser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +/** + * @author Stormeye + */ +@Controller +@RequestMapping("/wx/redirect/{account}") +public class WxRedirectController { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + @Autowired + private WxMpManager wxMpManager; + + + @RequestMapping("/greet") + public String greetUser(@PathVariable String account, @RequestParam String code, ModelMap map) { + + WxMpService mpService = wxMpManager.getMpService(account); + + try { + WxMpOAuth2AccessToken accessToken = mpService.oauth2getAccessToken(code); + WxMpUser user = mpService.oauth2getUserInfo(accessToken, null); + map.put("user", user); + } catch (WxErrorException e) { + e.printStackTrace(); + } + + return "greet_user"; + } +} diff --git a/mlWechatOpen/src/main/java/com/iformall/mp/error/ErrorController.java b/mlWechatOpen/src/main/java/com/iformall/mp/error/ErrorController.java new file mode 100644 index 0000000..4fd9060 --- /dev/null +++ b/mlWechatOpen/src/main/java/com/iformall/mp/error/ErrorController.java @@ -0,0 +1,29 @@ +package com.iformall.mp.error; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +/** + *
+ * 出错页面控制器
+ * Created by Binary Wang on 2018/8/25.
+ * 
+ * + * @author Binary Wang + */ +@Controller +@RequestMapping("/error") +public class ErrorController { + + @GetMapping(value = "/404") + public String error404() { + return "error"; + } + + @GetMapping(value = "/500") + public String error500() { + return "error"; + } + +} diff --git a/mlWechatOpen/src/main/java/com/iformall/mp/error/ErrorPageConfiguration.java b/mlWechatOpen/src/main/java/com/iformall/mp/error/ErrorPageConfiguration.java new file mode 100644 index 0000000..3a8807d --- /dev/null +++ b/mlWechatOpen/src/main/java/com/iformall/mp/error/ErrorPageConfiguration.java @@ -0,0 +1,27 @@ +package com.iformall.mp.error; + +import org.springframework.boot.web.server.ErrorPage; +import org.springframework.boot.web.server.ErrorPageRegistrar; +import org.springframework.boot.web.server.ErrorPageRegistry; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Component; + +/** + *
+ * 配置错误状态与对应访问路径
+ * Created by Binary Wang on 2018/8/25.
+ * 
+ * + * @author Binary Wang + */ +@Component +public class ErrorPageConfiguration implements ErrorPageRegistrar { + @Override + public void registerErrorPages(ErrorPageRegistry errorPageRegistry) { + errorPageRegistry.addErrorPages( + new ErrorPage(HttpStatus.NOT_FOUND, "/error/404"), + new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error/500") + ); + } + +} diff --git a/mlWechatOpen/src/main/java/com/iformall/mp/handler/AbstractHandler.java b/mlWechatOpen/src/main/java/com/iformall/mp/handler/AbstractHandler.java new file mode 100644 index 0000000..ee1f64b --- /dev/null +++ b/mlWechatOpen/src/main/java/com/iformall/mp/handler/AbstractHandler.java @@ -0,0 +1,12 @@ +package com.iformall.mp.handler; + +import me.chanjar.weixin.mp.api.WxMpMessageHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Binary Wang(https://github.com/binarywang) + */ +public abstract class AbstractHandler implements WxMpMessageHandler { + protected Logger logger = LoggerFactory.getLogger(getClass()); +} diff --git a/mlWechatOpen/src/main/java/com/iformall/mp/handler/KfSessionHandler.java b/mlWechatOpen/src/main/java/com/iformall/mp/handler/KfSessionHandler.java new file mode 100644 index 0000000..df34455 --- /dev/null +++ b/mlWechatOpen/src/main/java/com/iformall/mp/handler/KfSessionHandler.java @@ -0,0 +1,25 @@ +package com.iformall.mp.handler; + +import me.chanjar.weixin.common.session.WxSessionManager; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; +import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; +import org.springframework.stereotype.Component; + +import java.util.Map; + +/** + * @author Binary Wang(https://github.com/binarywang) + */ +@Component +public class KfSessionHandler extends AbstractHandler { + + @Override + public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, + Map context, WxMpService wxMpService, + WxSessionManager sessionManager) { + //TODO 对会话做处理 + return null; + } + +} diff --git a/mlWechatOpen/src/main/java/com/iformall/mp/handler/LocationHandler.java b/mlWechatOpen/src/main/java/com/iformall/mp/handler/LocationHandler.java new file mode 100644 index 0000000..2b96ade --- /dev/null +++ b/mlWechatOpen/src/main/java/com/iformall/mp/handler/LocationHandler.java @@ -0,0 +1,44 @@ +package com.iformall.mp.handler; + +import com.iformall.mp.builder.TextBuilder; +import me.chanjar.weixin.common.session.WxSessionManager; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; +import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; +import org.springframework.stereotype.Component; + +import java.util.Map; + +import static me.chanjar.weixin.common.api.WxConsts.XmlMsgType; + +/** + * @author Binary Wang(https://github.com/binarywang) + */ +@Component +public class LocationHandler extends AbstractHandler { + + @Override + public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, + Map context, WxMpService wxMpService, + WxSessionManager sessionManager) { + if (wxMessage.getMsgType().equals(XmlMsgType.LOCATION)) { + //TODO 接收处理用户发送的地理位置消息 + try { + String content = "感谢反馈,您的的地理位置已收到!"; + return new TextBuilder().build(content, wxMessage, null); + } catch (Exception e) { + this.logger.error("位置消息接收处理失败", e); + return null; + } + } + + //上报地理位置事件 + this.logger.info("上报地理位置,纬度 : {},经度 : {},精度 : {}", + wxMessage.getLatitude(), wxMessage.getLongitude(), String.valueOf(wxMessage.getPrecision())); + + //TODO 可以将用户地理位置信息保存到本地数据库,以便以后使用 + + return null; + } + +} diff --git a/mlWechatOpen/src/main/java/com/iformall/mp/handler/LogHandler.java b/mlWechatOpen/src/main/java/com/iformall/mp/handler/LogHandler.java new file mode 100644 index 0000000..794e6f9 --- /dev/null +++ b/mlWechatOpen/src/main/java/com/iformall/mp/handler/LogHandler.java @@ -0,0 +1,25 @@ +package com.iformall.mp.handler; + +import com.iformall.mp.utils.JsonUtils; +import me.chanjar.weixin.common.session.WxSessionManager; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; +import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; +import org.springframework.stereotype.Component; + +import java.util.Map; + +/** + * @author Binary Wang(https://github.com/binarywang) + */ +@Component +public class LogHandler extends AbstractHandler { + @Override + public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, + Map context, WxMpService wxMpService, + WxSessionManager sessionManager) { + this.logger.info("\n接收到请求消息,内容:{}", JsonUtils.toJson(wxMessage)); + return null; + } + +} diff --git a/mlWechatOpen/src/main/java/com/iformall/mp/handler/MenuHandler.java b/mlWechatOpen/src/main/java/com/iformall/mp/handler/MenuHandler.java new file mode 100644 index 0000000..880c94e --- /dev/null +++ b/mlWechatOpen/src/main/java/com/iformall/mp/handler/MenuHandler.java @@ -0,0 +1,36 @@ +package com.iformall.mp.handler; + +import me.chanjar.weixin.common.session.WxSessionManager; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; +import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; +import org.springframework.stereotype.Component; + +import java.util.Map; + +import static me.chanjar.weixin.common.api.WxConsts.MenuButtonType; + +/** + * @author Binary Wang(https://github.com/binarywang) + */ +@Component +public class MenuHandler extends AbstractHandler { + + @Override + public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, + Map context, WxMpService weixinService, + WxSessionManager sessionManager) { + + String msg = String.format("type:%s, event:%s, key:%s", + wxMessage.getMsgType(), wxMessage.getEvent(), + wxMessage.getEventKey()); + if (MenuButtonType.VIEW.equals(wxMessage.getEvent())) { + return null; + } + + return WxMpXmlOutMessage.TEXT().content(msg) + .fromUser(wxMessage.getToUser()).toUser(wxMessage.getFromUser()) + .build(); + } + +} diff --git a/mlWechatOpen/src/main/java/com/iformall/mp/handler/MsgHandler.java b/mlWechatOpen/src/main/java/com/iformall/mp/handler/MsgHandler.java new file mode 100644 index 0000000..e775e1d --- /dev/null +++ b/mlWechatOpen/src/main/java/com/iformall/mp/handler/MsgHandler.java @@ -0,0 +1,51 @@ +package com.iformall.mp.handler; + +import com.iformall.mp.builder.TextBuilder; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.session.WxSessionManager; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; +import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +import java.util.Map; + +import static me.chanjar.weixin.common.api.WxConsts.XmlMsgType; + +/** + * @author Binary Wang(https://github.com/binarywang) + */ +@Component +public class MsgHandler extends AbstractHandler { + + @Override + public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, + Map context, WxMpService weixinService, + WxSessionManager sessionManager) { + + if (!wxMessage.getMsgType().equals(XmlMsgType.EVENT)) { + //TODO 可以选择将消息保存到本地 + } + + //当用户输入关键词如“你好”,“客服”等,并且有客服在线时,把消息转发给在线客服 + try { + if (StringUtils.startsWithAny(wxMessage.getContent(), "你好", "客服") + && weixinService.getKefuService().kfOnlineList() + .getKfOnlineList().size() > 0) { + return WxMpXmlOutMessage.TRANSFER_CUSTOMER_SERVICE() + .fromUser(wxMessage.getToUser()) + .toUser(wxMessage.getFromUser()).build(); + } + } catch (WxErrorException e) { + e.printStackTrace(); + } + + //TODO 组装回复消息 + String content = "您好,有什么可以帮您";// "收到信息内容:" + JsonUtils.toJson(wxMessage); + + return new TextBuilder().build(content, wxMessage, weixinService); + + } + +} diff --git a/mlWechatOpen/src/main/java/com/iformall/mp/handler/NullHandler.java b/mlWechatOpen/src/main/java/com/iformall/mp/handler/NullHandler.java new file mode 100644 index 0000000..ea8d029 --- /dev/null +++ b/mlWechatOpen/src/main/java/com/iformall/mp/handler/NullHandler.java @@ -0,0 +1,24 @@ +package com.iformall.mp.handler; + +import me.chanjar.weixin.common.session.WxSessionManager; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; +import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; +import org.springframework.stereotype.Component; + +import java.util.Map; + +/** + * @author Binary Wang(https://github.com/binarywang) + */ +@Component +public class NullHandler extends AbstractHandler { + + @Override + public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, + Map context, WxMpService wxMpService, + WxSessionManager sessionManager) { + return null; + } + +} diff --git a/mlWechatOpen/src/main/java/com/iformall/mp/handler/ScanHandler.java b/mlWechatOpen/src/main/java/com/iformall/mp/handler/ScanHandler.java new file mode 100644 index 0000000..251c47b --- /dev/null +++ b/mlWechatOpen/src/main/java/com/iformall/mp/handler/ScanHandler.java @@ -0,0 +1,8 @@ +package com.iformall.mp.handler; + +/** + * @author Binary Wang(https://github.com/binarywang) + */ +public abstract class ScanHandler extends AbstractHandler { + +} diff --git a/mlWechatOpen/src/main/java/com/iformall/mp/handler/StoreCheckNotifyHandler.java b/mlWechatOpen/src/main/java/com/iformall/mp/handler/StoreCheckNotifyHandler.java new file mode 100644 index 0000000..4bee501 --- /dev/null +++ b/mlWechatOpen/src/main/java/com/iformall/mp/handler/StoreCheckNotifyHandler.java @@ -0,0 +1,27 @@ +package com.iformall.mp.handler; + +import me.chanjar.weixin.common.session.WxSessionManager; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; +import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; +import org.springframework.stereotype.Component; + +import java.util.Map; + +/** + * 门店审核事件处理 + * + * @author Binary Wang(https://github.com/binarywang) + */ +@Component +public class StoreCheckNotifyHandler extends AbstractHandler { + + @Override + public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, + Map context, WxMpService wxMpService, + WxSessionManager sessionManager) { + // TODO 处理门店审核事件 + return null; + } + +} diff --git a/mlWechatOpen/src/main/java/com/iformall/mp/handler/SubscribeHandler.java b/mlWechatOpen/src/main/java/com/iformall/mp/handler/SubscribeHandler.java new file mode 100644 index 0000000..21c3b2a --- /dev/null +++ b/mlWechatOpen/src/main/java/com/iformall/mp/handler/SubscribeHandler.java @@ -0,0 +1,70 @@ +package com.iformall.mp.handler; + +import com.iformall.mp.builder.TextBuilder; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.session.WxSessionManager; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; +import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; +import me.chanjar.weixin.mp.bean.result.WxMpUser; +import org.springframework.stereotype.Component; + +import java.util.Map; + +/** + * @author Binary Wang(https://github.com/binarywang) + */ +@Component +public class SubscribeHandler extends AbstractHandler { + + @Override + public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, + Map context, WxMpService weixinService, + WxSessionManager sessionManager) throws WxErrorException { + + this.logger.info("新关注用户 OPENID: " + wxMessage.getFromUser()); + + // 获取微信用户基本信息 + try { + WxMpUser userWxInfo = weixinService.getUserService() + .userInfo(wxMessage.getFromUser(), null); + if (userWxInfo != null) { + // TODO 可以添加关注用户到本地数据库 + } + } catch (WxErrorException e) { + if (e.getError().getErrorCode() == 48001) { + this.logger.info("该公众号没有获取用户信息权限!"); + } + } + + + WxMpXmlOutMessage responseResult = null; + try { + responseResult = this.handleSpecial(wxMessage); + } catch (Exception e) { + this.logger.error(e.getMessage(), e); + } + + if (responseResult != null) { + return responseResult; + } + + try { + return new TextBuilder().build("感谢关注", wxMessage, weixinService); + } catch (Exception e) { + this.logger.error(e.getMessage(), e); + } + + return null; + } + + /** + * 处理特殊请求,比如如果是扫码进来的,可以做相应处理 + */ + private WxMpXmlOutMessage handleSpecial(WxMpXmlMessage wxMessage) + throws Exception { + //TODO + return null; + } + +} diff --git a/mlWechatOpen/src/main/java/com/iformall/mp/handler/UnsubscribeHandler.java b/mlWechatOpen/src/main/java/com/iformall/mp/handler/UnsubscribeHandler.java new file mode 100644 index 0000000..9fa0f70 --- /dev/null +++ b/mlWechatOpen/src/main/java/com/iformall/mp/handler/UnsubscribeHandler.java @@ -0,0 +1,27 @@ +package com.iformall.mp.handler; + +import me.chanjar.weixin.common.session.WxSessionManager; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; +import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; +import org.springframework.stereotype.Component; + +import java.util.Map; + +/** + * @author Binary Wang(https://github.com/binarywang) + */ +@Component +public class UnsubscribeHandler extends AbstractHandler { + + @Override + public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, + Map context, WxMpService wxMpService, + WxSessionManager sessionManager) { + String openId = wxMessage.getFromUser(); + this.logger.info("取消关注用户 OPENID: " + openId); + // TODO 可以更新本地数据库为取消关注状态 + return null; + } + +} diff --git a/mlWechatOpen/src/main/java/com/iformall/mp/manager/WxMpManager.java b/mlWechatOpen/src/main/java/com/iformall/mp/manager/WxMpManager.java new file mode 100644 index 0000000..5340951 --- /dev/null +++ b/mlWechatOpen/src/main/java/com/iformall/mp/manager/WxMpManager.java @@ -0,0 +1,116 @@ +package com.iformall.mp.manager; + +import com.iformall.domain.po.WxAppinfo; +import com.iformall.mp.handler.*; +import com.iformall.mp.utils.MpUtil; +import com.iformall.service.WxAppinfoService; +import me.chanjar.weixin.mp.api.WxMpMessageRouter; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.constant.WxMpEventConstants; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import static me.chanjar.weixin.common.api.WxConsts.*; + +/** + * wechat mp configuration + * + * @author Binary Wang(https://github.com/binarywang) + */ +@Service +public class WxMpManager { + + @Autowired + private WxAppinfoService appinfoService; + @Autowired + private LogHandler logHandler; + @Autowired + private NullHandler nullHandler; + @Autowired + private KfSessionHandler kfSessionHandler; + @Autowired + private StoreCheckNotifyHandler storeCheckNotifyHandler; + @Autowired + private LocationHandler locationHandler; + @Autowired + private MenuHandler menuHandler; + @Autowired + private MsgHandler msgHandler; + @Autowired + private UnsubscribeHandler unsubscribeHandler; + @Autowired + private SubscribeHandler subscribeHandler; + + public WxMpMessageRouter getRouter(WxAppinfo appinfo) { + WxMpService mpService = MpUtil.getMpService(appinfo); + if(mpService != null) { + return newRouter(mpService); + } + return null; + } + + public WxMpMessageRouter getRouter(WxMpService mpService) { + return newRouter(mpService); + } + + private WxMpMessageRouter newRouter(WxMpService wxMpService) { + final WxMpMessageRouter newRouter = new WxMpMessageRouter(wxMpService); + + // 记录所有事件的日志 (异步执行) + newRouter.rule().handler(this.logHandler).next(); + + // 接收客服会话管理事件 + newRouter.rule().async(false).msgType(XmlMsgType.EVENT) + .event(WxMpEventConstants.CustomerService.KF_CREATE_SESSION) + .handler(this.kfSessionHandler).end(); + newRouter.rule().async(false).msgType(XmlMsgType.EVENT) + .event(WxMpEventConstants.CustomerService.KF_CLOSE_SESSION) + .handler(this.kfSessionHandler) + .end(); + newRouter.rule().async(false).msgType(XmlMsgType.EVENT) + .event(WxMpEventConstants.CustomerService.KF_SWITCH_SESSION) + .handler(this.kfSessionHandler).end(); + + // 门店审核事件 + newRouter.rule().async(false).msgType(XmlMsgType.EVENT) + .event(WxMpEventConstants.POI_CHECK_NOTIFY) + .handler(this.storeCheckNotifyHandler).end(); + + // 自定义菜单事件 + newRouter.rule().async(false).msgType(XmlMsgType.EVENT) + .event(MenuButtonType.CLICK).handler(this.menuHandler).end(); + + // 点击菜单连接事件 + newRouter.rule().async(false).msgType(XmlMsgType.EVENT) + .event(MenuButtonType.VIEW).handler(this.nullHandler).end(); + + // 关注事件 + newRouter.rule().async(false).msgType(XmlMsgType.EVENT) + .event(EventType.SUBSCRIBE).handler(this.subscribeHandler) + .end(); + + // 取消关注事件 + newRouter.rule().async(false).msgType(XmlMsgType.EVENT) + .event(EventType.UNSUBSCRIBE) + .handler(this.unsubscribeHandler).end(); + + // 上报地理位置事件 + newRouter.rule().async(false).msgType(XmlMsgType.EVENT) + .event(EventType.LOCATION).handler(this.locationHandler) + .end(); + + // 接收地理位置消息 + newRouter.rule().async(false).msgType(XmlMsgType.LOCATION) + .handler(this.locationHandler).end(); + + // 扫码事件 + newRouter.rule().async(false).msgType(XmlMsgType.EVENT) + .event(EventType.SCAN).handler(this.nullHandler).end(); + + // 默认 + newRouter.rule().async(false).handler(this.msgHandler).end(); + + return newRouter; + } + +} diff --git a/mlWechatOpen/src/main/java/com/iformall/mp/service/WxMpManager.java b/mlWechatOpen/src/main/java/com/iformall/mp/service/WxMpManager.java new file mode 100644 index 0000000..35e13da --- /dev/null +++ b/mlWechatOpen/src/main/java/com/iformall/mp/service/WxMpManager.java @@ -0,0 +1,123 @@ +package com.iformall.mp.service; + +import com.iformall.domain.po.WxAppinfo; +import com.iformall.mp.handler.*; +import com.iformall.mp.utils.MpUtil; +import com.iformall.service.WxAppinfoService; +import me.chanjar.weixin.mp.api.WxMpMessageRouter; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.constant.WxMpEventConstants; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import static me.chanjar.weixin.common.api.WxConsts.*; + +/** + * wechat mp configuration + * + * @author Binary Wang(https://github.com/binarywang) + */ +@Service +public class WxMpManager { + + @Autowired + private WxAppinfoService appinfoService; + @Autowired + private LogHandler logHandler; + @Autowired + private NullHandler nullHandler; + @Autowired + private KfSessionHandler kfSessionHandler; + @Autowired + private StoreCheckNotifyHandler storeCheckNotifyHandler; + @Autowired + private LocationHandler locationHandler; + @Autowired + private MenuHandler menuHandler; + @Autowired + private MsgHandler msgHandler; + @Autowired + private UnsubscribeHandler unsubscribeHandler; + @Autowired + private SubscribeHandler subscribeHandler; + + public WxMpMessageRouter getRouter(String appId) { + WxMpService mpService = getMpService(appId); + if(mpService != null) { + return newRouter(mpService); + } + return null; + } + + public WxMpMessageRouter getRouter(WxMpService mpService) { + return newRouter(mpService); + } + + public WxMpService getMpService(String appId) { + WxAppinfo appinfo = appinfoService.getByAppId(appId); + if(appinfo == null) + return null; + return MpUtil.getMpService(appinfo); + } + + private WxMpMessageRouter newRouter(WxMpService wxMpService) { + final WxMpMessageRouter newRouter = new WxMpMessageRouter(wxMpService); + + // 记录所有事件的日志 (异步执行) + newRouter.rule().handler(this.logHandler).next(); + + // 接收客服会话管理事件 + newRouter.rule().async(false).msgType(XmlMsgType.EVENT) + .event(WxMpEventConstants.CustomerService.KF_CREATE_SESSION) + .handler(this.kfSessionHandler).end(); + newRouter.rule().async(false).msgType(XmlMsgType.EVENT) + .event(WxMpEventConstants.CustomerService.KF_CLOSE_SESSION) + .handler(this.kfSessionHandler) + .end(); + newRouter.rule().async(false).msgType(XmlMsgType.EVENT) + .event(WxMpEventConstants.CustomerService.KF_SWITCH_SESSION) + .handler(this.kfSessionHandler).end(); + + // 门店审核事件 + newRouter.rule().async(false).msgType(XmlMsgType.EVENT) + .event(WxMpEventConstants.POI_CHECK_NOTIFY) + .handler(this.storeCheckNotifyHandler).end(); + + // 自定义菜单事件 + newRouter.rule().async(false).msgType(XmlMsgType.EVENT) + .event(MenuButtonType.CLICK).handler(this.menuHandler).end(); + + // 点击菜单连接事件 + newRouter.rule().async(false).msgType(XmlMsgType.EVENT) + .event(MenuButtonType.VIEW).handler(this.nullHandler).end(); + + // 关注事件 + newRouter.rule().async(false).msgType(XmlMsgType.EVENT) + .event(EventType.SUBSCRIBE).handler(this.subscribeHandler) + .end(); + + // 取消关注事件 + newRouter.rule().async(false).msgType(XmlMsgType.EVENT) + .event(EventType.UNSUBSCRIBE) + .handler(this.unsubscribeHandler).end(); + + // 上报地理位置事件 + newRouter.rule().async(false).msgType(XmlMsgType.EVENT) + .event(EventType.LOCATION).handler(this.locationHandler) + .end(); + + // 接收地理位置消息 + newRouter.rule().async(false).msgType(XmlMsgType.LOCATION) + .handler(this.locationHandler).end(); + + // 扫码事件 + newRouter.rule().async(false).msgType(XmlMsgType.EVENT) + .event(EventType.SCAN).handler(this.nullHandler).end(); + + // 默认 + newRouter.rule().async(false).handler(this.msgHandler).end(); + + return newRouter; + } + +} diff --git a/mlWechatOpen/src/main/java/com/iformall/mp/utils/JsonUtils.java b/mlWechatOpen/src/main/java/com/iformall/mp/utils/JsonUtils.java new file mode 100644 index 0000000..11918ee --- /dev/null +++ b/mlWechatOpen/src/main/java/com/iformall/mp/utils/JsonUtils.java @@ -0,0 +1,16 @@ +package com.iformall.mp.utils; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +/** + * @author Binary Wang(https://github.com/binarywang) + */ +public class JsonUtils { + public static String toJson(Object obj) { + Gson gson = new GsonBuilder() + .setPrettyPrinting() + .create(); + return gson.toJson(obj); + } +} diff --git a/mlWechatOpen/src/main/java/com/iformall/mp/utils/MpUtil.java b/mlWechatOpen/src/main/java/com/iformall/mp/utils/MpUtil.java new file mode 100644 index 0000000..dfcf8d2 --- /dev/null +++ b/mlWechatOpen/src/main/java/com/iformall/mp/utils/MpUtil.java @@ -0,0 +1,22 @@ +package com.iformall.mp.utils; + +import com.iformall.domain.po.WxAppinfo; +import me.chanjar.weixin.mp.api.WxMpInMemoryConfigStorage; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl; + +/** + * @author Stormeye + */ +public class MpUtil { + static public WxMpService getMpService(WxAppinfo appinfo) { + WxMpInMemoryConfigStorage configStorage = new WxMpInMemoryConfigStorage(); + configStorage.setAppId(appinfo.getAppId()); + configStorage.setSecret(appinfo.getSecret()); + configStorage.setToken(appinfo.getToken()); + configStorage.setAesKey(appinfo.getAesKey()); + WxMpService service = new WxMpServiceImpl(); + service.setWxMpConfigStorage(configStorage); + return service; + } +}