微信商城开源版二次开发(二)

微信商城开源版二次开发(二) 最近想了解如何Java对接微信平台,快速搭建完整项目开发,发现网上有很对开源的这类二开源码。https://gitee.com/luozijing12...就是其中一个,介绍可以打开网页自行查看,下面介绍了该项目如何对接微信公众号和部署。
【微信商城开源版二次开发(二)】下面是我自己在云服务器上利用docker快速部署的http://81.69.254.72/index。 体验账户: admin/admin123
SKD中接发微信公众号消息的路由模式 微信公众号会有许多不同类型的消息,有文本、图片、地理等等,为了更好的区分不同消息的处理,JAVA 微信开源包-- https://github.com/Wechat-Gro... 利用路由模式来处理。
首先业务代码包里注入路由器 和wxMpService(http通知处理类)

@Bean public WxMpMessageRouter messageRouter(WxMpService wxMpService) { final WxMpMessageRouter newRouter = new WxMpMessageRouter(wxMpService); // 记录所有事件的日志 (异步同步执行属性) newRouter.rule().handler(this.logHandler).next(); // 接收客服会话管理事件 handler可放进List多个处理 newRouter.rule().async(false).msgType(EVENT).event(KF_CREATE_SESSION) .handler(this.kfSessionHandler).end(); newRouter.rule().async(false).msgType(EVENT).event(KF_CLOSE_SESSION) .handler(this.kfSessionHandler).end(); newRouter.rule().async(false).msgType(EVENT).event(KF_SWITCH_SESSION) .handler(this.kfSessionHandler).end(); //...... }

异步和同步执行方式,满足不同业务属性,如有些数据需要异步同步给其他业务侧,就可用异步处理方式,这里的异步处理用来双线程,一个线程异步执行,一个线程异步等待,方便知悉结果,也提升了代码可靠性
WxMpXmlOutMessage res = null; final List> futures = new ArrayList(); Iterator var8 = matchRules.iterator(); while(var8.hasNext()) { final WxMpMessageRouterRule rule = (WxMpMessageRouterRule)var8.next(); if (rule.isAsync()) { // 线程池异步执行,futruesTask封装结果 futures.add(this.executorService.submit(new Runnable() { public void run() { rule.service(wxMessage, context, mpService, WxMpMessageRouter.this.sessionManager, WxMpMessageRouter.this.exceptionHandler); } })); } else { // 同步执行,进入下面的handler和切面处理 res = rule.service(wxMessage, context, mpService, this.sessionManager, this.exceptionHandler); this.log.debug("End session access: async=false, sessionId={}", wxMessage.getFromUser()); this.sessionEndAccess(wxMessage); } } // 有异步任务每,异步获取结果,判断异步是否完成 if (futures.size() > 0) { this.executorService.submit(new Runnable() { public void run() { Iterator var1 = futures.iterator(); while(var1.hasNext()) { Future future = (Future)var1.next(); try { future.get(); WxMpMessageRouter.this.log.debug("End session access: async=true, sessionId={}", wxMessage.getFromUser()); WxMpMessageRouter.this.sessionEndAccess(wxMessage); // 对异常的处理 } catch (InterruptedException var4) { WxMpMessageRouter.this.log.error("Error happened when wait task finish", var4); Thread.currentThread().interrupt(); } catch (ExecutionException var5) { WxMpMessageRouter.this.log.error("Error happened when wait task finish", var5); } }} }); }

举例子, wx文本消息进入对应后路由处理
try { Iterator var6 = this.interceptors.iterator(); // 路由拦截器数组 也是自定义,方便拓展功能,这里并未用到 WxMpMessageInterceptor interceptor; do { if (!var6.hasNext()) { WxMpXmlOutMessage res = null; Iterator var11 = this.handlers.iterator(); while(var11.hasNext()) { WxMpMessageHandler handler = (WxMpMessageHandler)var11.next(); if (handler != null) { // 上述定义的路由处理器数组 res = handler.handle(wxMessage, (Map)context, wxMpService, sessionManager); } }return res; }interceptor = (WxMpMessageInterceptor)var6.next(); // 先执行拦截器业务 } while(interceptor.intercept(wxMessage, (Map)context, wxMpService, sessionManager)); return null; } catch (WxErrorException var9) { exceptionHandler.handle(var9); return null; }

初始化中具体的handler业务,在网页配置页面配置好如何进行自动回复消息,有半匹配和全匹配两种,也有图片\语音\语音等,这些数据是存储在微信云端.具体可以扫描本系统绑定的公众号,在页面中自动配置http://81.69.254.72/wxmp/wxau...进行尝试.
@Override public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map context, WxMpService wxMpService, WxSessionManager sessionManager) { //组装回复消息 if (!wxMessage.getMsgType().equals(XmlMsgType.EVENT)) { WxMpXmlOutMessage rs; //TODO 可以选择将消息保存到本地 WxUser wxUser = wxUserMapper.selectOne(Wrappers.lambdaQuery() .eq(WxUser::getOpenId,wxMessage.getFromUser())); if(WxConsts.KefuMsgType.TEXT.equals(wxMessage.getMsgType())){//1、先处理是否有文本关键字回复 //先全匹配 网页中设置的自动全匹配服务,可以打开网页自己操作试试 http://81.69.254.72/wxmp/wxautoreply List listWxAutoReply = wxAutoReplyService.list(Wrappers .query().lambda() .eq(WxAutoReply::getType, ConfigConstant.WX_AUTO_REPLY_TYPE_3) .eq(WxAutoReply::getRepMate, ConfigConstant.WX_REP_MATE_1) .eq(WxAutoReply::getReqKey, wxMessage.getContent())); if(listWxAutoReply!=null && listWxAutoReply.size()>0){ rs = this.getWxMpXmlOutMessage(wxMessage,listWxAutoReply,wxUser,wxMsgService); if(rs != null){ returnrs; } } //再半匹配 listWxAutoReply = wxAutoReplyService.list(Wrappers .query().lambda() .eq(WxAutoReply::getType, ConfigConstant.WX_AUTO_REPLY_TYPE_3) .eq(WxAutoReply::getRepMate, ConfigConstant.WX_REP_MATE_2) .like(WxAutoReply::getReqKey, wxMessage.getContent())); if(listWxAutoReply!=null && listWxAutoReply.size()>0) { rs = this.getWxMpXmlOutMessage(wxMessage, listWxAutoReply, wxUser,wxMsgService); if (rs != null) { return rs; } } } //2、再处理消息回复 List listWxAutoReply = wxAutoReplyService.list(Wrappers .query().lambda() .eq(WxAutoReply::getType, ConfigConstant.WX_AUTO_REPLY_TYPE_2) .eq(WxAutoReply::getReqType, wxMessage.getMsgType())); rs = this.getWxMpXmlOutMessage(wxMessage,listWxAutoReply,wxUser,wxMsgService); return rs; } return null; }

微信商城开源版二次开发(二)
文章图片

微信公众号token校验 小程序交互时是通过token来进行验证用户是否登录小程序,因此每次交互需要校验用户登录有效性, 实际上通过微信获取登录的用户信息吗,获取后存入redis的过期来校验用户是否失效,这是通用性的解决方案,也有不足之处,就是session超时不好控制,影响体验.
//session过期后请求微信获取用户信息 然后存入redis 设置过期时间 public WxMaJscode2SessionResult jsCode2SessionInfo(String jsCode) throws WxErrorException { WxMaConfig config = this.getWxMaConfig(); Map params = new HashMap(8); params.put("appid", config.getAppid()); params.put("secret", config.getSecret()); params.put("js_code", jsCode); params.put("grant_type", "authorization_code"); String result = this.get("https://api.weixin.qq.com/sns/jscode2session", Joiner.on("&").withKeyValueSeparator("=").join(params)); return WxMaJscode2SessionResult.fromJson(result); }

spring security拦截器校验session有效性
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //获取header中的thirdSession String thirdSessionHeader = request.getHeader(ConfigConstant.HEADER_THIRDSESSION); if(StrUtil.isNotBlank(thirdSessionHeader)){ //获取缓存中的ThirdSession String key = WxMaConstants.THIRD_SESSION_BEGIN+ ":" + thirdSessionHeader; Object thirdSessionObj = redisTemplate.opsForValue().get(key); if(thirdSessionObj == null) {//session过期 AjaxResult r = AjaxResult.error(MyReturnCode.ERR_60001.getCode(), MyReturnCode.ERR_60001.getMsg()); this.writerPrint(response, r); return Boolean.FALSE; }else { String thirdSessionStr = String.valueOf(thirdSessionObj); ThirdSession thirdSession = JSONUtil.toBean(thirdSessionStr, ThirdSession.class); ThirdSessionHolder.setThirdSession(thirdSession); //设置thirdSession } }else{ AjaxResult r = AjaxResult.error(MyReturnCode.ERR_60002.getCode(), MyReturnCode.ERR_60002.getMsg()); this.writerPrint(response, r); return Boolean.FALSE; } return Boolean.TRUE; }private void writerPrint(HttpServletResponse response, AjaxResult r) throws IOException { //返回超时错误码,触发小程序重新登录 response.setCharacterEncoding(CommonConstants.UTF8); response.setContentType(MediaType.APPLICATION_JSON_VALUE); PrintWriter writer = response.getWriter(); writer.print(JSONUtil.parseObj(r)); if(writer != null){ writer.close(); } }

    推荐阅读