共计 6690 个字符,预计需要花费 17 分钟才能阅读完成。
微信商城开源版二次开发(二)
最近想理解如何 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<Future<?>> 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<String, Object> context, WxMpService wxMpService,
WxSessionManager sessionManager) {
// 组装回复音讯
if (!wxMessage.getMsgType().equals(XmlMsgType.EVENT)) {
WxMpXmlOutMessage rs;
//TODO 能够抉择将音讯保留到本地
WxUser wxUser = wxUserMapper.selectOne(Wrappers.<WxUser>lambdaQuery()
.eq(WxUser::getOpenId,wxMessage.getFromUser()));
if(WxConsts.KefuMsgType.TEXT.equals(wxMessage.getMsgType())){//1、先解决是否有文本关键字回复
// 先全匹配 网页中设置的主动全匹配服务, 能够关上网页本人操作试试 http://81.69.254.72/wxmp/wxautoreply
List<WxAutoReply> listWxAutoReply = wxAutoReplyService.list(Wrappers
.<WxAutoReply>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){return rs;}
}
// 再半匹配
listWxAutoReply = wxAutoReplyService.list(Wrappers
.<WxAutoReply>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<WxAutoReply> listWxAutoReply = wxAutoReplyService.list(Wrappers
.<WxAutoReply>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<String, String> 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();
}
}