提交 184ed687 编写于 作者: 如梦技术's avatar 如梦技术 🐛

v1.3.0

上级 75fa6d8b
......@@ -8,33 +8,26 @@ jfinal weixin 的 spring boot starter,这个starter是为了方便boot用户
<dependency>
<groupId>net.dreamlu</groupId>
<artifactId>spring-boot-starter-weixin</artifactId>
<version>1.2.0</version>
<version>1.3.0</version>
</dependency>
```
`说明`:依赖`spring-boot-starter-aop`
## 使用
### 启用微信
```java
@EnableDreamWeixin
```
### 消息
#### 公众号
1. 继承`MsgControllerAdapter`,实现需要重写的消息。
1. 继承`DreamMsgControllerAdapter`,实现需要重写的消息。
2. 添加注解`@WxMsgController`,注解value为你的消息地址,使用/weixin/wx
2. 类添加注解`@WxMsgController`,注解value为你的消息地址,使用/weixin/wx,已经组合[@RequestMapping和@Controller]
### 小程序
1. 继承`WxaMsgController`,实现需要重写的消息。
1. 继承`DreamWxaMsgController`,实现需要重写的消息。
2. 添加注解`@WxMsgController`,注解value为你的消息地址,使用/weixin/wxa
2. 添加注解`@WxMsgController`,注解value为你的消息地址,使用/weixin/wxa,已经组合[@RequestMapping和@Controller]
### Api
1. 使用传统的spring的控制器即可
2. 添加`@WxApi`注解
- 类添加`@WxApi`注解,已经组合[@RequestMapping和@Controller]
### 配置
| 配置项 | 默认值 | 说明 |
......@@ -42,7 +35,7 @@ jfinal weixin 的 spring boot starter,这个starter是为了方便boot用户
| dream.weixin.access-token-cache | dreamWeixinCache | 缓存名,需要开启spring cache |
| dream.weixin.app-id-key | appId | 多公众号参数名,如:/weixin/wx?appId=xxx |
| dream.weixin.dev-mode | false | 开发模式 |
| dream.weixin.url-patterns | /weixin/* | JFinal-weixin 过滤器url前缀 |
| dream.weixin.url-patterns | /weixin/* | weixin 消息处理spring拦截器url前缀 |
| dream.weixin.wx-configs | 公众号的配置 | 多公众号配置 |
| dream.weixin.wxa-config | 小程序配置 | 小程序配置 |
......@@ -66,6 +59,11 @@ dream:
- cache使用spring的cache,需要`@EnableCaching`开启。
- `access-token-cache`建议配置有效时间7100秒。
## 更新说明
>## 2018-05-03 v1.3.0
> 弃用`@EnableDreamWeixin`,导入jar包即可享用。
> 将消息路由改为spring接管。
## 捐助共勉
<img src="https://gitee.com/uploads/images/2018/0311/153544_5afb12b1_372.jpeg" width="250px"/>
<img src="https://gitee.com/uploads/images/2018/0311/153556_679db579_372.jpeg" width="250px"/>
......
VERSION=1.2.0
VERSION=1.3.0
GROUPID=net.dreamlu
userName=chunmeng
......
package net.dreamlu.weixin.annotation;
import net.dreamlu.weixin.config.DreamWeixinAutoConfiguration;
import net.dreamlu.weixin.properties.DreamWeixinProperties;
import org.springframework.context.annotation.Import;
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({
DreamWeixinProperties.class,
DreamWeixinAutoConfiguration.class
})
public @interface EnableDreamWeixin {}
package net.dreamlu.weixin.annotation;
import org.springframework.core.annotation.AliasFor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import java.lang.annotation.*;
/**
......@@ -10,12 +14,21 @@ import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping
@Controller
public @interface WxApi {
/**
* Alias for {@link RequestMapping#value}.
* @return {String[]}
*/
@AliasFor(annotation = RequestMapping.class)
String[] value() default {};
// /**
// * 目前不支持多小程序
// * @return {ApiType}
// */
// ApiType value() default ApiType.WX;
// ApiType type() default ApiType.WX;
}
......@@ -2,7 +2,12 @@ package net.dreamlu.weixin.annotation;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.core.annotation.AliasFor;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.lang.annotation.*;
......@@ -15,9 +20,16 @@ import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
@RequestMapping
@Controller
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public @interface WxMsgController {
String value() default "/";
/**
* Alias for {@link RequestMapping#value}.
* @return {String[]}
*/
@AliasFor(annotation = RequestMapping.class)
String[] value() default {};
}
package net.dreamlu.weixin.config;
import com.jfinal.core.JFinalFilter;
import net.dreamlu.weixin.aspect.WxApiAspect;
import net.dreamlu.weixin.cache.SpringAccessTokenCache;
import net.dreamlu.weixin.properties.DreamWeixinProperties;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import net.dreamlu.weixin.spring.MsgInterceptor;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
@AutoConfigureAfter(DreamWeixinProperties.class)
@EnableConfigurationProperties(DreamWeixinProperties.class)
public class DreamWeixinAutoConfiguration {
private final CacheManager cacheManager;
private final DreamWeixinProperties weixinProperties;
......@@ -23,19 +23,6 @@ public class DreamWeixinAutoConfiguration {
this.weixinProperties = weixinProperties;
}
@Bean
public FilterRegistrationBean jfinalFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
JFinalFilter jfinalFilter = new JFinalFilter();
registration.setFilter(jfinalFilter);
registration.addUrlPatterns(weixinProperties.getUrlPatterns());
// 添加JFinal configClass参数
registration.addInitParameter("configClass", DreamWeixinConfig.class.getName());
registration.setName("jfinalFilter");
registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
return registration;
}
@Bean
public WeixinAppConfig weixinAppConfig() {
return new WeixinAppConfig(weixinProperties);
......@@ -51,4 +38,21 @@ public class DreamWeixinAutoConfiguration {
public WxApiAspect wxApiAspect() {
return new WxApiAspect(weixinProperties);
}
@Configuration
public class MsgConfiguration extends WebMvcConfigurerAdapter {
private final DreamWeixinProperties properties;
public MsgConfiguration(DreamWeixinProperties properties) {
this.properties = properties;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
String urlPattern = properties.getUrlPatterns();
MsgInterceptor httpCacheInterceptor = new MsgInterceptor(properties);
registry.addInterceptor(httpCacheInterceptor)
.addPathPatterns(urlPattern);
}
}
}
package net.dreamlu.weixin.config;
import com.jfinal.config.*;
import com.jfinal.core.Controller;
import com.jfinal.core.JFinal;
import com.jfinal.json.JacksonFactory;
import com.jfinal.template.Engine;
import net.dreamlu.weixin.annotation.WxMsgController;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.context.support.WebApplicationContextUtils;
import java.util.Map;
public class DreamWeixinConfig extends JFinalConfig {
@Override
public void configConstant(Constants me) {
me.setJsonFactory(new JacksonFactory());
me.setControllerFactory(new SpringControllerFactory());
}
@Override
public void configRoute(Routes me) {
findAnnotatedBeans(me);
}
@Override
public void configEngine(Engine me) {}
@Override
public void configPlugin(Plugins me) {}
@Override
public void configInterceptor(Interceptors me) {}
@Override
public void configHandler(Handlers me) {}
/**
* Find the names of beans annotated with
* @param me Routes
*/
public static void findAnnotatedBeans(Routes me) {
ApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(JFinal.me().getServletContext());
Map<String, Controller> controllerMap = BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, Controller.class);
for (Controller controller : controllerMap.values()) {
Class<? extends Controller> controllerClass = controller.getClass();
WxMsgController wxController = AnnotationUtils.findAnnotation(controllerClass, WxMsgController.class);
if (wxController != null) {
me.add(wxController.value(), controllerClass);
}
}
}
}
package net.dreamlu.weixin.config;
import com.jfinal.core.Controller;
import com.jfinal.core.ControllerFactory;
import com.jfinal.core.JFinal;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
public class SpringControllerFactory extends ControllerFactory {
private final ApplicationContext applicationContext;
public SpringControllerFactory() {
this.applicationContext = WebApplicationContextUtils.getWebApplicationContext(JFinal.me().getServletContext());
}
@Override
public Controller getController(Class<? extends Controller> controllerClass) throws InstantiationException, IllegalAccessException {
return applicationContext.getBean(controllerClass);
}
}
/**
* Copyright (c) 2011-2014, James Zhan 詹波 (jfinal@126.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
*/
package net.dreamlu.weixin.spring;
import com.jfinal.weixin.iot.msg.InEquDataMsg;
import com.jfinal.weixin.iot.msg.InEqubindEvent;
import com.jfinal.weixin.sdk.msg.in.*;
import com.jfinal.weixin.sdk.msg.in.card.*;
import com.jfinal.weixin.sdk.msg.in.event.*;
import com.jfinal.weixin.sdk.msg.in.speech_recognition.InSpeechRecognitionResults;
/**
* MsgControllerAdapter 对 MsgController 部分方法提供了默认实现,
* 以便开发者不去关注 MsgController 中不需要处理的抽象方法,节省代码量
*/
public abstract class DreamMsgControllerAdapter extends MsgController {
// 关注/取消关注事件
@Override
protected abstract void processInFollowEvent(InFollowEvent inFollowEvent);
// 接收文本消息事件
@Override
protected abstract void processInTextMsg(InTextMsg inTextMsg);
// 自定义菜单事件
@Override
protected abstract void processInMenuEvent(InMenuEvent inMenuEvent);
// 接收图片消息事件
@Override
protected void processInImageMsg(InImageMsg inImageMsg) {
renderDefault();
}
// 接收语音消息事件
@Override
protected void processInVoiceMsg(InVoiceMsg inVoiceMsg) {
renderDefault();
}
// 接收视频消息事件
@Override
protected void processInVideoMsg(InVideoMsg inVideoMsg) {
renderDefault();
}
// 接收地理位置消息事件
@Override
protected void processInLocationMsg(InLocationMsg inLocationMsg) {
renderDefault();
}
// 接收链接消息事件
@Override
protected void processInLinkMsg(InLinkMsg inLinkMsg) {
renderDefault();
}
// 扫描带参数二维码事件
@Override
protected void processInQrCodeEvent(InQrCodeEvent inQrCodeEvent) {
renderDefault();
}
// 上报地理位置事件
@Override
protected void processInLocationEvent(InLocationEvent inLocationEvent) {
renderDefault();
}
// 接收语音识别结果,与 InVoiceMsg 唯一的不同是多了一个 Recognition 标记
@Override
protected void processInSpeechRecognitionResults(InSpeechRecognitionResults inSpeechRecognitionResults) {
renderDefault();
}
// 在模版消息发送任务完成后事件
@Override
protected void processInTemplateMsgEvent(InTemplateMsgEvent inTemplateMsgEvent) {
renderDefault();
}
// 群发完成事件
@Override
protected void processInMassEvent(InMassEvent inMassEvent) {
renderDefault();
}
// 接收小视频消息
@Override
protected void processInShortVideoMsg(InShortVideoMsg inShortVideoMsg) {
renderDefault();
}
// 接客服入会话事件
@Override
protected void processInCustomEvent(InCustomEvent inCustomEvent) {
renderDefault();
}
// 用户进入摇一摇界面,在“周边”页卡下摇一摇时事件
@Override
protected void processInShakearoundUserShakeEvent(InShakearoundUserShakeEvent inShakearoundUserShakeEvent) {
renderDefault();
}
// 资质认证事件
@Override
protected void processInVerifySuccessEvent(InVerifySuccessEvent inVerifySuccessEvent) {
renderDefault();
}
// 资质认证失败事件
@Override
protected void processInVerifyFailEvent(InVerifyFailEvent inVerifyFailEvent){
renderDefault();
}
// 门店在审核通过后下发消息事件
@Override
protected void processInPoiCheckNotifyEvent(InPoiCheckNotifyEvent inPoiCheckNotifyEvent) {
renderDefault();
}
// WIFI连网后下发消息 by unas at 2016-1-29
@Override
protected void processInWifiEvent(InWifiEvent inWifiEvent) {
renderDefault();
}
// 微信会员卡积分变更事件
@Override
protected void processInUpdateMemberCardEvent(InUpdateMemberCardEvent msg) {
renderDefault();
}
// 微信会员卡快速买单事件
@Override
protected void processInUserPayFromCardEvent(InUserPayFromCardEvent msg) {
renderDefault();
}
// 微信小店订单支付成功接口事件
@Override
protected void processInMerChantOrderEvent(InMerChantOrderEvent inMerChantOrderEvent) {
renderDefault();
}
// 没有找到对应的事件消息
@Override
protected void processIsNotDefinedEvent(InNotDefinedEvent inNotDefinedEvent) {
renderDefault();
}
// 没有找到对应的消息
@Override
protected void processIsNotDefinedMsg(InNotDefinedMsg inNotDefinedMsg) {
renderDefault();
}
@Override
protected void processInUserGiftingCardEvent(InUserGiftingCardEvent msg) {
renderDefault();
}
@Override
protected void processInUserGetCardEvent(InUserGetCardEvent msg) {
renderDefault();
}
@Override
protected void processInUserConsumeCardEvent(InUserConsumeCardEvent msg) {
renderDefault();
}
@Override
protected void processInCardSkuRemindEvent(InCardSkuRemindEvent msg) {
renderDefault();
}
@Override
protected void processInCardPayOrderEvent(InCardPayOrderEvent msg) {
renderDefault();
}
@Override
protected void processInCardPassCheckEvent(InCardPassCheckEvent msg) {
renderDefault();
}
@Override
protected void processInUserCardEvent(InUserCardEvent inUserCardEvent) {
renderDefault();
}
/**
* 处理微信硬件绑定和解绑事件
* @param msg 处理微信硬件绑定和解绑事件
*/
@Override
protected void processInEqubindEvent(InEqubindEvent msg) {
renderDefault();
}
/**
* 处理微信硬件发来数据
* @param msg 处理微信硬件发来数据
*/
@Override
protected void processInEquDataMsg(InEquDataMsg msg) {
renderDefault();
}
/**
* 方便没有使用的api返回“”避免出现,该公众号暂时不能提供服务
*
* 可重写该方法
*/
protected void renderDefault() {
WebUtils.renderText(response, "");
}
}
package net.dreamlu.weixin.spring;
import com.jfinal.kit.StrKit;
import com.jfinal.weixin.sdk.api.ApiConfigKit;
import com.jfinal.weixin.sdk.kit.MsgEncryptKit;
import com.jfinal.wxaapp.WxaConfigKit;
import com.jfinal.wxaapp.msg.IMsgParser;
import com.jfinal.wxaapp.msg.bean.WxaImageMsg;
import com.jfinal.wxaapp.msg.bean.WxaMsg;
import com.jfinal.wxaapp.msg.bean.WxaTextMsg;
import com.jfinal.wxaapp.msg.bean.WxaUserEnterSessionMsg;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 小程序消息控制器
* @author L.cm
*
*/
public abstract class DreamWxaMsgController {
private static final Log logger = LogFactory.getLog(DreamWxaMsgController.class);
private WxaMsg wxaMsg = null; // 本次请求 xml 解析后的 wxaMsg 对象
@Autowired
protected HttpServletRequest request;
@Autowired
protected HttpServletResponse response;
/**
* 小程序消息
* @param imXmlMsg imXmlMsg
*/
@RequestMapping("")
public void index(@RequestBody String imXmlMsg) {
// 开发模式输出微信服务发送过来的 xml、json 消息
if (WxaConfigKit.isDevMode()) {
System.out.println("接收消息:");
System.out.println(imXmlMsg);
}
if (StrKit.isBlank(imXmlMsg)) {
throw new RuntimeException("请不要在浏览器中请求该连接,调试请查看WIKI:http://git.oschina.net/jfinal/jfinal-weixin/wikis/JFinal-weixin-demo%E5%92%8C%E8%B0%83%E8%AF%95");
}
// 是否需要解密消息
if (WxaConfigKit.getWxaConfig().isMessageEncrypt()) {
imXmlMsg = MsgEncryptKit.decrypt(imXmlMsg,
request.getParameter("timestamp"),
request.getParameter("nonce"),
request.getParameter("msg_signature"));
}
IMsgParser msgParser = WxaConfigKit.getMsgParser();
wxaMsg = msgParser.parser(imXmlMsg);
if (wxaMsg instanceof WxaTextMsg) {
processTextMsg((WxaTextMsg) wxaMsg);
} else if (wxaMsg instanceof WxaImageMsg) {
processImageMsg((WxaImageMsg) wxaMsg);
} else if (wxaMsg instanceof WxaUserEnterSessionMsg) {
processUserEnterSessionMsg((WxaUserEnterSessionMsg) wxaMsg);
} else {
logger.error("未能识别的小程序消息类型。 消息内容为:\n" + imXmlMsg);
}
// 直接回复success(推荐方式)
WebUtils.renderText(response,"success");
}
/**
* 处理接收到的文本消息
* @param textMsg 处理接收到的文本消息
*/
protected abstract void processTextMsg(WxaTextMsg textMsg);
/**
* 处理接收到的图片消息
* @param imageMsg 处理接收到的图片消息
*/
protected abstract void processImageMsg(WxaImageMsg imageMsg);
/**
* 处理接收到的进入会话事件
* @param userEnterSessionMsg 处理接收到的进入会话事件
*/
protected abstract void processUserEnterSessionMsg(WxaUserEnterSessionMsg userEnterSessionMsg);
}
\ No newline at end of file
package net.dreamlu.weixin.spring;
import com.jfinal.kit.HttpKit;
import com.jfinal.kit.StrKit;
import com.jfinal.weixin.iot.msg.InEquDataMsg;
import com.jfinal.weixin.iot.msg.InEqubindEvent;
import com.jfinal.weixin.sdk.api.ApiConfigKit;
import com.jfinal.weixin.sdk.kit.MsgEncryptKit;
import com.jfinal.weixin.sdk.msg.InMsgParser;
import com.jfinal.weixin.sdk.msg.in.*;
import com.jfinal.weixin.sdk.msg.in.card.*;
import com.jfinal.weixin.sdk.msg.in.event.*;
import com.jfinal.weixin.sdk.msg.in.speech_recognition.InSpeechRecognitionResults;
import com.jfinal.weixin.sdk.msg.out.OutMsg;
import com.jfinal.weixin.sdk.msg.out.OutTextMsg;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public abstract class MsgController {
private static final Log logger = LogFactory.getLog(WebUtils.class);
// 本次请求 xml 解析后的 InMsg 对象
protected InMsg msg = null;
@Autowired
protected HttpServletRequest request;
@Autowired
protected HttpServletResponse response;
/**
* weixin 公众号服务器调用唯一入口,即在开发者中心输入的 URL 必须要指向此 action
* @param imXmlMsg imXmlMsg
*/
@RequestMapping("")
public void index(@RequestBody String imXmlMsg) {
// 开发模式输出微信服务发送过来的 xml 消息
if (ApiConfigKit.isDevMode()) {
System.out.println("接收消息:");
System.out.println(imXmlMsg);
}
if (StrKit.isBlank(imXmlMsg)) {
throw new RuntimeException("请不要在浏览器中请求该连接,调试请查看WIKI:http://git.oschina.net/jfinal/jfinal-weixin/wikis/JFinal-weixin-demo%E5%92%8C%E8%B0%83%E8%AF%95");
}
// 是否需要解密消息
if (ApiConfigKit.getApiConfig().isEncryptMessage()) {
imXmlMsg = MsgEncryptKit.decrypt(imXmlMsg,
request.getParameter("timestamp"),
request.getParameter("nonce"),
request.getParameter("msg_signature"));
}
// 解析消息并根据消息类型分发到相应的处理方法
msg = InMsgParser.parse(imXmlMsg);
if (msg instanceof InTextMsg)
processInTextMsg((InTextMsg) msg);
else if (msg instanceof InImageMsg)
processInImageMsg((InImageMsg) msg);
else if (msg instanceof InSpeechRecognitionResults) //update by unas at 2016-1-29, 由于继承InVoiceMsg,需要在InVoiceMsg前判断类型
processInSpeechRecognitionResults((InSpeechRecognitionResults) msg);
else if (msg instanceof InVoiceMsg)
processInVoiceMsg((InVoiceMsg) msg);
else if (msg instanceof InVideoMsg)
processInVideoMsg((InVideoMsg) msg);
else if (msg instanceof InShortVideoMsg) //支持小视频
processInShortVideoMsg((InShortVideoMsg) msg);
else if (msg instanceof InLocationMsg)
processInLocationMsg((InLocationMsg) msg);
else if (msg instanceof InLinkMsg)
processInLinkMsg((InLinkMsg) msg);
else if (msg instanceof InCustomEvent)
processInCustomEvent((InCustomEvent) msg);
else if (msg instanceof InFollowEvent)
processInFollowEvent((InFollowEvent) msg);
else if (msg instanceof InQrCodeEvent)
processInQrCodeEvent((InQrCodeEvent) msg);
else if (msg instanceof InLocationEvent)
processInLocationEvent((InLocationEvent) msg);
else if (msg instanceof InMassEvent)
processInMassEvent((InMassEvent) msg);
else if (msg instanceof InMenuEvent)
processInMenuEvent((InMenuEvent) msg);
else if (msg instanceof InTemplateMsgEvent)
processInTemplateMsgEvent((InTemplateMsgEvent) msg);
else if (msg instanceof InShakearoundUserShakeEvent)
processInShakearoundUserShakeEvent((InShakearoundUserShakeEvent) msg);
else if (msg instanceof InVerifySuccessEvent)
processInVerifySuccessEvent((InVerifySuccessEvent) msg);
else if (msg instanceof InVerifyFailEvent)
processInVerifyFailEvent((InVerifyFailEvent) msg);
else if (msg instanceof InPoiCheckNotifyEvent)
processInPoiCheckNotifyEvent((InPoiCheckNotifyEvent) msg);
else if (msg instanceof InWifiEvent)
processInWifiEvent((InWifiEvent) msg);
else if (msg instanceof InUserCardEvent)
processInUserCardEvent((InUserCardEvent) msg);
else if (msg instanceof InUpdateMemberCardEvent)
processInUpdateMemberCardEvent((InUpdateMemberCardEvent) msg);
else if (msg instanceof InUserPayFromCardEvent)
processInUserPayFromCardEvent((InUserPayFromCardEvent) msg);
else if (msg instanceof InMerChantOrderEvent)
processInMerChantOrderEvent((InMerChantOrderEvent) msg);
else if (msg instanceof InCardPassCheckEvent)
processInCardPassCheckEvent((InCardPassCheckEvent) msg);
else if (msg instanceof InCardPayOrderEvent)
processInCardPayOrderEvent((InCardPayOrderEvent) msg);
else if (msg instanceof InCardSkuRemindEvent)
processInCardSkuRemindEvent((InCardSkuRemindEvent) msg);
else if (msg instanceof InUserConsumeCardEvent)
processInUserConsumeCardEvent((InUserConsumeCardEvent) msg);
else if (msg instanceof InUserGetCardEvent)
processInUserGetCardEvent((InUserGetCardEvent) msg);
else if (msg instanceof InUserGiftingCardEvent)
processInUserGiftingCardEvent((InUserGiftingCardEvent) msg);
else if (msg instanceof InEqubindEvent)
processInEqubindEvent((InEqubindEvent) msg);
else if (msg instanceof InEquDataMsg)
processInEquDataMsg((InEquDataMsg) msg);
//===================微信智能硬件========================//
else if (msg instanceof InEqubindEvent)
processInEqubindEvent((InEqubindEvent) msg);
else if (msg instanceof InEquDataMsg)
processInEquDataMsg((InEquDataMsg) msg);
//===================微信智能硬件========================//
else if (msg instanceof InNotDefinedEvent) {
logger.error("未能识别的事件类型。 消息 xml 内容为:\n" + imXmlMsg);
processIsNotDefinedEvent((InNotDefinedEvent) msg);
} else if (msg instanceof InNotDefinedMsg) {
logger.error("未能识别的消息类型。 消息 xml 内容为:\n" + imXmlMsg);
processIsNotDefinedMsg((InNotDefinedMsg) msg);
}
}
/**
* 在接收到微信服务器的 InMsg 消息后后响应 OutMsg 消息
*
* @param outMsg 输出对象
*/
public void render(OutMsg outMsg) {
String outMsgXml = outMsg.toXml();
// 开发模式向控制台输出即将发送的 OutMsg 消息的 xml 内容
if (ApiConfigKit.isDevMode()) {
System.out.println("发送消息:");
System.out.println(outMsgXml);
System.out.println("--------------------------------------------------------------------------------\n");
}
// 是否需要加密消息
if (ApiConfigKit.getApiConfig().isEncryptMessage()) {
outMsgXml = MsgEncryptKit.encrypt(outMsgXml,
request.getParameter("timestamp"),
request.getParameter("nonce"));
}
WebUtils.renderText(response, outMsgXml);
}
/**
* 消息输出
* @param content 输出的消息
*/
public void renderOutTextMsg(String content) {
OutTextMsg outMsg = new OutTextMsg(msg);
outMsg.setContent(content);
render(outMsg);
}
public String getInMsgXml(HttpServletRequest request) {
String inMsgXml = HttpKit.readData(request);
// 是否需要解密消息
if (ApiConfigKit.getApiConfig().isEncryptMessage()) {
inMsgXml = MsgEncryptKit.decrypt(inMsgXml,
request.getParameter("timestamp"),
request.getParameter("nonce"),
request.getParameter("msg_signature"));
}
if (StrKit.isBlank(inMsgXml)) {
throw new RuntimeException("请不要在浏览器中请求该连接,调试请查看WIKI:http://git.oschina.net/jfinal/jfinal-weixin/wikis/JFinal-weixin-demo%E5%92%8C%E8%B0%83%E8%AF%95");
}
return inMsgXml;
}
/**
* 处理接收到的文本消息
* @param inTextMsg 处理接收到的文本消息
*/
protected abstract void processInTextMsg(InTextMsg inTextMsg);
/**
* 处理接收到的图片消息
* @param inImageMsg 处理接收到的图片消息
*/
protected abstract void processInImageMsg(InImageMsg inImageMsg);
/**
* 处理接收到的语音消息
* @param inVoiceMsg 处理接收到的语音消息
*/
protected abstract void processInVoiceMsg(InVoiceMsg inVoiceMsg);
/**
* 处理接收到的视频消息
* @param inVideoMsg 处理接收到的视频消息
*/
protected abstract void processInVideoMsg(InVideoMsg inVideoMsg);
/**
* 处理接收到的小视频消息
* @param inShortVideoMsg 处理接收到的小视频消息
*/
protected abstract void processInShortVideoMsg(InShortVideoMsg inShortVideoMsg);
/**
* 处理接收到的地址位置消息
* @param inLocationMsg 处理接收到的地址位置消息
*/
protected abstract void processInLocationMsg(InLocationMsg inLocationMsg);
/**
* 处理接收到的链接消息
* @param inLinkMsg 处理接收到的链接消息
*/
protected abstract void processInLinkMsg(InLinkMsg inLinkMsg);
/**
* 处理接收到的多客服管理事件
* @param inCustomEvent 处理接收到的多客服管理事件
*/
protected abstract void processInCustomEvent(InCustomEvent inCustomEvent);
/**
* 处理接收到的关注/取消关注事件
* @param inFollowEvent 处理接收到的关注/取消关注事件
*/
protected abstract void processInFollowEvent(InFollowEvent inFollowEvent);
/**
* 处理接收到的扫描带参数二维码事件
* @param inQrCodeEvent 处理接收到的扫描带参数二维码事件
*/
protected abstract void processInQrCodeEvent(InQrCodeEvent inQrCodeEvent);
/**
* 处理接收到的上报地理位置事件
* @param inLocationEvent 处理接收到的上报地理位置事件
*/
protected abstract void processInLocationEvent(InLocationEvent inLocationEvent);
/**
* 处理接收到的群发任务结束时通知事件
* @param inMassEvent 处理接收到的群发任务结束时通知事件
*/
protected abstract void processInMassEvent(InMassEvent inMassEvent);
/**
* 处理接收到的自定义菜单事件
* @param inMenuEvent 处理接收到的自定义菜单事件
*/
protected abstract void processInMenuEvent(InMenuEvent inMenuEvent);
/**
* 处理接收到的语音识别结果
* @param inSpeechRecognitionResults 处理接收到的语音识别结果
*/
protected abstract void processInSpeechRecognitionResults(InSpeechRecognitionResults inSpeechRecognitionResults);
/**
* 处理接收到的模板消息是否送达成功通知事件
* @param inTemplateMsgEvent 处理接收到的模板消息是否送达成功通知事件
*/
protected abstract void processInTemplateMsgEvent(InTemplateMsgEvent inTemplateMsgEvent);
/**
* 处理微信摇一摇事件
* @param inShakearoundUserShakeEvent 处理微信摇一摇事件
*/
protected abstract void processInShakearoundUserShakeEvent(InShakearoundUserShakeEvent inShakearoundUserShakeEvent);
/**
* 资质认证成功 || 名称认证成功 || 年审通知 || 认证过期失效通知
* @param inVerifySuccessEvent 资质认证成功 || 名称认证成功 || 年审通知 || 认证过期失效通知
*/
protected abstract void processInVerifySuccessEvent(InVerifySuccessEvent inVerifySuccessEvent);
/**
* 资质认证失败 || 名称认证失败
* @param inVerifyFailEvent 资质认证失败 || 名称认证失败
*/
protected abstract void processInVerifyFailEvent(InVerifyFailEvent inVerifyFailEvent);
/**
* 门店在审核事件消息
* @param inPoiCheckNotifyEvent 门店在审核事件消息
*/
protected abstract void processInPoiCheckNotifyEvent(InPoiCheckNotifyEvent inPoiCheckNotifyEvent);
/**
* WIFI连网后下发消息 by unas at 2016-1-29
* @param inWifiEvent WIFI连网后下发消息
*/
protected abstract void processInWifiEvent(InWifiEvent inWifiEvent);
/**
* 1. 微信会员卡二维码扫描领取接口
* 2. 微信会员卡激活接口
* 3. 卡券删除事件推送
* 4. 从卡券进入公众号会话事件推送
* @param inUserCardEvent InUserCardEvent
*/
protected abstract void processInUserCardEvent(InUserCardEvent inUserCardEvent);
/**
* 微信会员卡积分变更
* @param inUpdateMemberCardEvent 微信会员卡积分变更
*/
protected abstract void processInUpdateMemberCardEvent(InUpdateMemberCardEvent inUpdateMemberCardEvent);
/**
* 微信会员卡快速买单
* @param inUserPayFromCardEvent 微信会员卡快速买单
*/
protected abstract void processInUserPayFromCardEvent(InUserPayFromCardEvent inUserPayFromCardEvent);
/**
* 微信小店订单支付成功接口消息
* @param inMerChantOrderEvent 微信小店订单支付成功接口消息
*/
protected abstract void processInMerChantOrderEvent(InMerChantOrderEvent inMerChantOrderEvent);
//
/**
* 没有找到对应的事件消息
* @param inNotDefinedEvent 没有对应的事件消息
*/
protected abstract void processIsNotDefinedEvent(InNotDefinedEvent inNotDefinedEvent);
/**
* 没有找到对应的消息
* @param inNotDefinedMsg 没有对应消息
*/
protected abstract void processIsNotDefinedMsg(InNotDefinedMsg inNotDefinedMsg);
/**
* 卡券转赠事件推送
* @param msg 卡券转赠事件推送
*/
protected abstract void processInUserGiftingCardEvent(InUserGiftingCardEvent msg);
/**
* 卡券领取事件推送
* @param msg 卡券领取事件推送
*/
protected abstract void processInUserGetCardEvent(InUserGetCardEvent msg);
/**
* 卡券核销事件推送
* @param msg 卡券核销事件推送
*/
protected abstract void processInUserConsumeCardEvent(InUserConsumeCardEvent msg);
/**
* 卡券库存报警事件
* @param msg 卡券库存报警事件
*/
protected abstract void processInCardSkuRemindEvent(InCardSkuRemindEvent msg);
/**
* 券点流水详情事件
* @param msg 券点流水详情事件
*/
protected abstract void processInCardPayOrderEvent(InCardPayOrderEvent msg);
/**
* 审核事件推送
* @param msg 审核事件推送
*/
protected abstract void processInCardPassCheckEvent(InCardPassCheckEvent msg);
/**
* 处理微信硬件绑定和解绑事件
* @param msg 处理微信硬件绑定和解绑事件
*/
protected abstract void processInEqubindEvent(InEqubindEvent msg) ;
/**
* 处理微信硬件发来数据
* @param msg 处理微信硬件发来数据
*/
protected abstract void processInEquDataMsg(InEquDataMsg msg);
}
package net.dreamlu.weixin.spring;
import com.jfinal.kit.StrKit;
import com.jfinal.weixin.sdk.api.ApiConfigKit;
import com.jfinal.weixin.sdk.kit.SignatureCheckKit;
import com.jfinal.wxaapp.WxaConfigKit;
import net.dreamlu.weixin.properties.DreamWeixinProperties;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MsgInterceptor extends HandlerInterceptorAdapter {
private static final Log logger = LogFactory.getLog(MsgInterceptor.class);
private final DreamWeixinProperties weixinProperties;
public MsgInterceptor(DreamWeixinProperties weixinProperties) {
this.weixinProperties = weixinProperties;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 非控制器请求直接跳出
if (!(handler instanceof HandlerMethod)) {
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) handler;
Object bean = handlerMethod.getBean();
// 小程序直接跳出
if (bean instanceof DreamWxaMsgController) {
return true;
}
String appId = weixinProperties.getAppIdKey();
try {
// 将 appId 与当前线程绑定,以便在后续操作中方便获取ApiConfig对象: ApiConfigKit.getApiConfig();
ApiConfigKit.setThreadLocalAppId(appId);
// 如果是服务器配置请求,则配置服务器并返回
if (isConfigServerRequest(request)) {
configServer(request, response);
return false;
}
// 对开发测试更加友好
if (ApiConfigKit.isDevMode()) {
return true;
} else {
// 签名检测
if (checkSignature(request, response)) {
return true;
} else {
WebUtils.renderText(response, "签名验证失败,请确定是微信服务器在发送消息过来");
return false;
}
}
} finally {
ApiConfigKit.removeThreadLocalAppId();
}
}
/**
* 检测签名
*/
private boolean checkSignature(HttpServletRequest request, HttpServletResponse response) {
String signature = request.getParameter("signature");
String timestamp = request.getParameter("timestamp");
String nonce = request.getParameter("nonce");
if (StrKit.isBlank(signature) || StrKit.isBlank(timestamp) || StrKit.isBlank(nonce)) {
logger.error("check signature failure");
return false;
}
if (SignatureCheckKit.me.checkSignature(signature, timestamp, nonce)) {
return true;
} else {
logger.error("check signature failure: " +
" signature = " + signature +
" timestamp = " + timestamp +
" nonce = " + nonce);
return false;
}
}
/**
* 是否为开发者中心保存服务器配置的请求
*/
private boolean isConfigServerRequest(HttpServletRequest request) {
return StrKit.notBlank(request.getParameter("echostr"));
}
/**
* 配置开发者中心微信服务器所需的 url 与 token
*
* @param request HttpServletRequest
* @param response HttpServletResponse
*/
public void configServer(HttpServletRequest request, HttpServletResponse response) {
// 通过 echostr 判断请求是否为配置微信服务器回调所需的 url 与 token
String echostr = request.getParameter("echostr");
String signature = request.getParameter("signature");
String timestamp = request.getParameter("timestamp");
String nonce = request.getParameter("nonce");
boolean isOk = SignatureCheckKit.me.checkSignature(signature, timestamp, nonce);
if (isOk && !response.isCommitted()) {
WebUtils.renderText(response, echostr);
} else {
logger.error("验证失败:configServer");
}
}
}
package net.dreamlu.weixin.spring;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
class WebUtils {
private static final Log logger = LogFactory.getLog(WebUtils.class);
/**
* 返回json
*
* @param response HttpServletResponse
* @param text 文本
*/
public static void renderText(HttpServletResponse response, String text) {
response.setCharacterEncoding("UTF-8");
response.setContentType("text/plain;charset:utf-8;");
try (PrintWriter out = response.getWriter()) {
out.append(text);
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
}
}
restart.include.weixin=/spring-boot-starter-weixin-[\\w-]+\.jar
\ No newline at end of file
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
net.dreamlu.weixin.config.DreamWeixinAutoConfiguration
......@@ -15,7 +15,7 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.12.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
......@@ -36,7 +36,7 @@
<dependency>
<groupId>net.dreamlu</groupId>
<artifactId>spring-boot-starter-weixin</artifactId>
<version>1.2.0</version>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
......@@ -55,9 +55,11 @@
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork>
</configuration>
</plugin>
</plugins>
</build>
</project>
package com.example.demo;
import net.dreamlu.weixin.annotation.EnableDreamWeixin;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
@SpringBootApplication
@EnableDreamWeixin
@EnableCaching
public class DemoApplication {
......
......@@ -3,12 +3,10 @@ package com.example.demo;
import com.jfinal.weixin.sdk.api.ApiResult;
import com.jfinal.weixin.sdk.api.MenuApi;
import net.dreamlu.weixin.annotation.WxApi;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@WxApi
@WxApi("api")
public class WeixinApiController {
@GetMapping("menu")
......
......@@ -7,10 +7,11 @@ import com.jfinal.weixin.sdk.msg.in.event.InMenuEvent;
import com.jfinal.weixin.sdk.msg.out.OutTextMsg;
import net.dreamlu.weixin.annotation.WxMsgController;
import net.dreamlu.weixin.properties.DreamWeixinProperties;
import net.dreamlu.weixin.spring.DreamMsgControllerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
@WxMsgController("/weixin/wx")
public class WeixinController extends MsgControllerAdapter {
public class WeixinController extends DreamMsgControllerAdapter {
@Autowired
private DreamWeixinProperties weixinProperties;
......
package com.example.demo;
import com.jfinal.wxaapp.msg.bean.WxaImageMsg;
import com.jfinal.wxaapp.msg.bean.WxaTextMsg;
import com.jfinal.wxaapp.msg.bean.WxaUserEnterSessionMsg;
import net.dreamlu.weixin.annotation.WxMsgController;
import net.dreamlu.weixin.properties.DreamWeixinProperties;
import net.dreamlu.weixin.spring.DreamWxaMsgController;
import org.springframework.beans.factory.annotation.Autowired;
@WxMsgController("/weixin/wxa")
public class WxaController extends DreamWxaMsgController {
@Autowired
private DreamWeixinProperties weixinProperties;
@Override
protected void processTextMsg(WxaTextMsg wxaTextMsg) {
}
@Override
protected void processImageMsg(WxaImageMsg wxaImageMsg) {
}
@Override
protected void processUserEnterSessionMsg(WxaUserEnterSessionMsg wxaUserEnterSessionMsg) {
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册