From bf6894e1efeff7634e5e6e9a26f140691b12db0f Mon Sep 17 00:00:00 2001 From: DuLerWeil Date: Wed, 29 Jun 2016 20:56:56 +0800 Subject: [PATCH 1/6] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=A0=B7=E4=BE=8B?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E5=8F=8A=E5=BF=85=E8=A6=81=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../weixin/demo/WeixinApiController.java | 23 ------ .../com/jfinal/weixin/demo/WeixinConfig.java | 24 +++++- .../weixin/demo/WeixinMsgController.java | 40 +--------- .../jfinal/weixin/sdk/api/ApiConfigKit.java | 77 ++++++++++++++++--- .../weixin/sdk/jfinal/ApiController.java | 3 +- .../weixin/sdk/jfinal/ApiInterceptor.java | 20 +++-- .../jfinal/weixin/sdk/jfinal/AppIdParser.java | 47 +++++++++++ .../weixin/sdk/jfinal/MsgController.java | 67 ++++++++-------- .../sdk/jfinal/MsgControllerAdapter.java | 46 +++++------ .../weixin/sdk/jfinal/MsgInterceptor.java | 54 +++++++------ 10 files changed, 240 insertions(+), 161 deletions(-) create mode 100644 src/main/java/com/jfinal/weixin/sdk/jfinal/AppIdParser.java diff --git a/src/main/java/com/jfinal/weixin/demo/WeixinApiController.java b/src/main/java/com/jfinal/weixin/demo/WeixinApiController.java index 6391548..3b44975 100644 --- a/src/main/java/com/jfinal/weixin/demo/WeixinApiController.java +++ b/src/main/java/com/jfinal/weixin/demo/WeixinApiController.java @@ -1,32 +1,9 @@ package com.jfinal.weixin.demo; -import com.jfinal.kit.PropKit; import com.jfinal.weixin.sdk.api.*; import com.jfinal.weixin.sdk.jfinal.ApiController; public class WeixinApiController extends ApiController { - - /** - * 如果要支持多公众账号,只需要在此返回各个公众号对应的 ApiConfig 对象即可 - * 可以通过在请求 url 中挂参数来动态从数据库中获取 ApiConfig 属性值 - */ - public ApiConfig getApiConfig() { - ApiConfig ac = new ApiConfig(); - - // 配置微信 API 相关常量 - ac.setToken(PropKit.get("token")); - ac.setAppId(PropKit.get("appId")); - ac.setAppSecret(PropKit.get("appSecret")); - - /** - * 是否对消息进行加密,对应于微信平台的消息加解密方式: - * 1:true进行加密且必须配置 encodingAesKey - * 2:false采用明文模式,同时也支持混合模式 - */ - ac.setEncryptMessage(PropKit.getBoolean("encryptMessage", false)); - ac.setEncodingAesKey(PropKit.get("encodingAesKey", "setting it in config file")); - return ac; - } /** * 获取公众号菜单 diff --git a/src/main/java/com/jfinal/weixin/demo/WeixinConfig.java b/src/main/java/com/jfinal/weixin/demo/WeixinConfig.java index 9ea6006..dc9d6d7 100644 --- a/src/main/java/com/jfinal/weixin/demo/WeixinConfig.java +++ b/src/main/java/com/jfinal/weixin/demo/WeixinConfig.java @@ -14,6 +14,7 @@ import com.jfinal.config.Plugins; import com.jfinal.config.Routes; import com.jfinal.core.JFinal; import com.jfinal.kit.PropKit; +import com.jfinal.weixin.sdk.api.ApiConfig; import com.jfinal.weixin.sdk.api.ApiConfigKit; public class WeixinConfig extends JFinalConfig { @@ -58,7 +59,8 @@ public class WeixinConfig extends JFinalConfig { } public void configInterceptor(Interceptors me) { - + // ApiInterceptor.setAppIdParser(new AppIdParser.DefaultParameterAppIdParser("appId")); 默认无需设置 + // MsgInterceptor.setAppIdParser(new AppIdParser.DefaultParameterAppIdParser("appId")); 默认无需设置 } public void configHandler(Handlers me) { @@ -71,6 +73,26 @@ public class WeixinConfig extends JFinalConfig { // 1.6新增的2种初始化 // ApiConfigKit.setAccessTokenCache(new RedisAccessTokenCache(Redis.use("weixin"))); // ApiConfigKit.setAccessTokenCache(new RedisAccessTokenCache("weixin")); + + ApiConfig ac = new ApiConfig(); + // 配置微信 API 相关参数 + ac.setToken(PropKit.get("token")); + ac.setAppId(PropKit.get("appId")); + ac.setAppSecret(PropKit.get("appSecret")); + + /** + * 是否对消息进行加密,对应于微信平台的消息加解密方式: + * 1:true进行加密且必须配置 encodingAesKey + * 2:false采用明文模式,同时也支持混合模式 + */ + ac.setEncryptMessage(PropKit.getBoolean("encryptMessage", false)); + ac.setEncodingAesKey(PropKit.get("encodingAesKey", "setting it in config file")); + + /** + * 单个公众号时,和ApiConfigKit.putApiConfig(ac)等价。 + * 多个公众号时,重复调用ApiConfigKit.putApiConfig(ac)依次添加即可。 + */ + ApiConfigKit.setDefaultApiConfig(ac); } public static void main(String[] args) { diff --git a/src/main/java/com/jfinal/weixin/demo/WeixinMsgController.java b/src/main/java/com/jfinal/weixin/demo/WeixinMsgController.java index 98225d0..ba2e910 100644 --- a/src/main/java/com/jfinal/weixin/demo/WeixinMsgController.java +++ b/src/main/java/com/jfinal/weixin/demo/WeixinMsgController.java @@ -6,24 +6,10 @@ package com.jfinal.weixin.demo; -import com.jfinal.kit.PropKit; import com.jfinal.log.Log; -import com.jfinal.weixin.sdk.api.ApiConfig; import com.jfinal.weixin.sdk.jfinal.MsgControllerAdapter; -import com.jfinal.weixin.sdk.msg.in.InImageMsg; -import com.jfinal.weixin.sdk.msg.in.InLinkMsg; -import com.jfinal.weixin.sdk.msg.in.InLocationMsg; -import com.jfinal.weixin.sdk.msg.in.InShortVideoMsg; -import com.jfinal.weixin.sdk.msg.in.InTextMsg; -import com.jfinal.weixin.sdk.msg.in.InVideoMsg; -import com.jfinal.weixin.sdk.msg.in.InVoiceMsg; -import com.jfinal.weixin.sdk.msg.in.event.InCustomEvent; -import com.jfinal.weixin.sdk.msg.in.event.InFollowEvent; -import com.jfinal.weixin.sdk.msg.in.event.InLocationEvent; -import com.jfinal.weixin.sdk.msg.in.event.InMassEvent; -import com.jfinal.weixin.sdk.msg.in.event.InMenuEvent; -import com.jfinal.weixin.sdk.msg.in.event.InQrCodeEvent; -import com.jfinal.weixin.sdk.msg.in.event.InTemplateMsgEvent; +import com.jfinal.weixin.sdk.msg.in.*; +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.OutCustomMsg; import com.jfinal.weixin.sdk.msg.out.OutTextMsg; @@ -39,28 +25,6 @@ public class WeixinMsgController extends MsgControllerAdapter { static Log logger = Log.getLog(WeixinMsgController.class); private static final String helpStr = "\t发送 help 可获得帮助,发送\"视频\" 可获取视频教程,发送 \"美女\" 可看美女,发送 music 可听音乐 ,发送新闻可看JFinal新版本消息。公众号功能持续完善中"; - /** - * 如果要支持多公众账号,只需要在此返回各个公众号对应的 ApiConfig 对象即可 - * 可以通过在请求 url 中挂参数来动态从数据库中获取 ApiConfig 属性值 - */ - public ApiConfig getApiConfig() { - ApiConfig ac = new ApiConfig(); - - // 配置微信 API 相关常量 - ac.setToken(PropKit.get("token")); - ac.setAppId(PropKit.get("appId")); - ac.setAppSecret(PropKit.get("appSecret")); - - /** - * 是否对消息进行加密,对应于微信平台的消息加解密方式: - * 1:true进行加密且必须配置 encodingAesKey - * 2:false采用明文模式,同时也支持混合模式 - */ - ac.setEncryptMessage(PropKit.getBoolean("encryptMessage", false)); - ac.setEncodingAesKey(PropKit.get("encodingAesKey", "setting it in config file")); - return ac; - } - protected void processInTextMsg(InTextMsg inTextMsg) { //转发给多客服PC客户端 diff --git a/src/main/java/com/jfinal/weixin/sdk/api/ApiConfigKit.java b/src/main/java/com/jfinal/weixin/sdk/api/ApiConfigKit.java index 6799439..386f894 100644 --- a/src/main/java/com/jfinal/weixin/sdk/api/ApiConfigKit.java +++ b/src/main/java/com/jfinal/weixin/sdk/api/ApiConfigKit.java @@ -1,18 +1,27 @@ package com.jfinal.weixin.sdk.api; +import com.jfinal.kit.StrKit; +import com.jfinal.log.Log; import com.jfinal.weixin.sdk.cache.DefaultAccessTokenCache; import com.jfinal.weixin.sdk.cache.IAccessTokenCache; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + /** * 将 ApiConfig 绑定到 ThreadLocal 的工具类,以方便在当前线程的各个地方获取 ApiConfig 对象: * 1:如果控制器继承自 MsgController 该过程是自动的,详细可查看 MsgInterceptor 与之的配合 * 2:如果控制器继承自 ApiController 该过程是自动的,详细可查看 ApiInterceptor 与之的配合 * 3:如果控制器没有继承自 MsgController、ApiController,则需要先手动调用 - * ApiConfigKit.setThreadLocalApiConfig(ApiConfig) 来绑定 apiConfig 到线程之上 + * ApiConfigKit.setThreadLocalAppId(appId) 来绑定 appId 到线程之上 */ public class ApiConfigKit { + private static final Log log = Log.getLog(ApiConfigKit.class); - private static final ThreadLocal tl = new ThreadLocal(); + private static final ThreadLocal TL = new ThreadLocal(); + + private static final Map CFG_MAP = new ConcurrentHashMap(); + private static final String DEFAULT_CFG_KEY = "_default_cfg_key_"; // 开发模式将输出消息交互 xml 到控制台 private static boolean devMode = false; @@ -24,20 +33,68 @@ public class ApiConfigKit { public static boolean isDevMode() { return devMode; } + + /** + * 设置默认公众号接口配置 + * @param apiConfig 默认公众号配置 + * @return + */ + public static ApiConfig setDefaultApiConfig(ApiConfig apiConfig) { + return CFG_MAP.put(DEFAULT_CFG_KEY, apiConfig); + } + + /** + * 添加公众号配置,每个appId只需添加一次,相同appId将被覆盖。 + * 第一次添加的将作为默认公众号配置 + * @param apiConfig 公众号配置 + * @return + */ + public static ApiConfig putApiConfig(ApiConfig apiConfig) { + if (CFG_MAP.size() == 0) { + setDefaultApiConfig(apiConfig); + } + return CFG_MAP.put(apiConfig.getAppId(), apiConfig); + } + + public static ApiConfig removeApiConfig(ApiConfig apiConfig) { + return removeApiConfig(apiConfig.getAppId()); + } + + public static ApiConfig removeApiConfig(String appId) { + return CFG_MAP.remove(appId); + } - public static void setThreadLocalApiConfig(ApiConfig apiConfig) { - tl.set(apiConfig); + public static void setThreadLocalAppId(String appId) { + if (StrKit.isBlank(appId)) { + appId = DEFAULT_CFG_KEY; + } + TL.set(appId); } - public static void removeThreadLocalApiConfig() { - tl.remove(); + public static void removeThreadLocalAppId() { + TL.remove(); + } + + public static String getAppId() { + String appId = TL.get(); + if (StrKit.isBlank(appId)) { + appId = DEFAULT_CFG_KEY; + } + return appId; } public static ApiConfig getApiConfig() { - ApiConfig result = tl.get(); - if (result == null) - throw new IllegalStateException("需要事先使用 ApiConfigKit.setThreadLocalApiConfig(apiConfig) 将 ApiConfig对象存入,才可以调用 ApiConfigKit.getApiConfig() 方法"); - return result; + String appId = getAppId(); + return getApiConfig(appId); + } + + public static ApiConfig getApiConfig(String appId) { + log.debug("appId: " + appId); + ApiConfig cfg = CFG_MAP.get(appId); + if (cfg == null) + throw new IllegalStateException("需事先调用 ApiConfigKit.addApiConfig(apiConfig) 将 appId对应的 ApiConfig 对象存入," + + "如JFinalConfig.afterJFinalStart()中调用, 才可以使用 ApiConfigKit.getApiConfig() 系列方法"); + return cfg; } static IAccessTokenCache accessTokenCache = new DefaultAccessTokenCache(); diff --git a/src/main/java/com/jfinal/weixin/sdk/jfinal/ApiController.java b/src/main/java/com/jfinal/weixin/sdk/jfinal/ApiController.java index b4462b3..a163d63 100644 --- a/src/main/java/com/jfinal/weixin/sdk/jfinal/ApiController.java +++ b/src/main/java/com/jfinal/weixin/sdk/jfinal/ApiController.java @@ -2,12 +2,11 @@ package com.jfinal.weixin.sdk.jfinal; import com.jfinal.aop.Before; import com.jfinal.core.Controller; -import com.jfinal.weixin.sdk.api.ApiConfig; /** * 所有使用 Api 的 controller 需要继承此类 */ @Before(ApiInterceptor.class) public abstract class ApiController extends Controller { - public abstract ApiConfig getApiConfig(); + } diff --git a/src/main/java/com/jfinal/weixin/sdk/jfinal/ApiInterceptor.java b/src/main/java/com/jfinal/weixin/sdk/jfinal/ApiInterceptor.java index 6818871..2b6e1c1 100644 --- a/src/main/java/com/jfinal/weixin/sdk/jfinal/ApiInterceptor.java +++ b/src/main/java/com/jfinal/weixin/sdk/jfinal/ApiInterceptor.java @@ -10,18 +10,24 @@ import com.jfinal.weixin.sdk.api.ApiConfigKit; * 以便在后续的操作中可以使用 ApiConfigKit.getApiConfig() 获取到该对象 */ public class ApiInterceptor implements Interceptor { - + private static AppIdParser _parser = new AppIdParser.DefaultParameterAppIdParser(); + + public static void setAppIdParser(AppIdParser parser) { + _parser = parser; + } + public void intercept(Invocation inv) { Controller controller = inv.getController(); - if (controller instanceof ApiController == false) + if (!(controller instanceof ApiController)) throw new RuntimeException("控制器需要继承 ApiController"); - + try { - ApiConfigKit.setThreadLocalApiConfig(((ApiController)controller).getApiConfig()); + String appId = _parser.getAppId(controller); + // 将 appId 与当前线程绑定,以便在后续操作中方便获取ApiConfig对象: ApiConfigKit.getApiConfig(); + ApiConfigKit.setThreadLocalAppId(appId); inv.invoke(); - } - finally { - ApiConfigKit.removeThreadLocalApiConfig(); + } finally { + ApiConfigKit.removeThreadLocalAppId(); } } } diff --git a/src/main/java/com/jfinal/weixin/sdk/jfinal/AppIdParser.java b/src/main/java/com/jfinal/weixin/sdk/jfinal/AppIdParser.java new file mode 100644 index 0000000..53e366d --- /dev/null +++ b/src/main/java/com/jfinal/weixin/sdk/jfinal/AppIdParser.java @@ -0,0 +1,47 @@ +package com.jfinal.weixin.sdk.jfinal; + +import com.jfinal.aop.Invocation; +import com.jfinal.core.Controller; + +/** + * 从请求中解析 标识Key 并导出 appId。 + * 开发者可自行实现此接口,并在 JFinalConfig.configInterceptor 或在 JFinalConfig.afterJFinalStart等位置全局注入。 + * + * Created by DuLerWeil on 2016/6/29. + */ +public interface AppIdParser { + + public String getAppId(Invocation inv); + + public String getAppId(Controller ctl); + + + /** + * 默认appId解析器,根据设置的标识Key名称,从请求parameterMap中直接取appId值 + *

+ * 默认标识Key名称为"_appId_" + */ + public class DefaultParameterAppIdParser implements AppIdParser { + private static final String DEFAULT_APP_ID_KEY = "_appId_"; + + private String appIdKey = DEFAULT_APP_ID_KEY; + + public DefaultParameterAppIdParser() { + + } + + public DefaultParameterAppIdParser(String appIdKey) { + this.appIdKey = appIdKey; + } + + @Override + public String getAppId(Invocation inv) { + return getAppId(inv.getController()); + } + + @Override + public String getAppId(Controller ctl) { + return ctl.getPara(appIdKey); + } + } +} diff --git a/src/main/java/com/jfinal/weixin/sdk/jfinal/MsgController.java b/src/main/java/com/jfinal/weixin/sdk/jfinal/MsgController.java index 041d248..0645ee1 100644 --- a/src/main/java/com/jfinal/weixin/sdk/jfinal/MsgController.java +++ b/src/main/java/com/jfinal/weixin/sdk/jfinal/MsgController.java @@ -1,6 +1,6 @@ /** * Copyright (c) 2011-2014, James Zhan 詹波 (jfinal@126.com). - * + *

* Licensed under the Apache License, Version 2.0 (the "License"); */ @@ -12,7 +12,6 @@ import com.jfinal.ext.interceptor.NotAction; import com.jfinal.kit.HttpKit; import com.jfinal.kit.StrKit; import com.jfinal.log.Log; -import com.jfinal.weixin.sdk.api.ApiConfig; import com.jfinal.weixin.sdk.api.ApiConfigKit; import com.jfinal.weixin.sdk.kit.MsgEncryptKit; import com.jfinal.weixin.sdk.msg.InMsgParser; @@ -26,13 +25,11 @@ import com.jfinal.weixin.sdk.msg.out.OutTextMsg; * 接收微信服务器消息,自动解析成 InMsg 并分发到相应的处理方法 */ public abstract class MsgController extends Controller { - - private static final Log log = Log.getLog(MsgController.class); - private String inMsgXml = null; // 本次请求 xml数据 - private InMsg inMsg = null; // 本次请求 xml 解析后的 InMsg 对象 - - public abstract ApiConfig getApiConfig(); - + + private static final Log log = Log.getLog(MsgController.class); + private String inMsgXml = null; // 本次请求 xml数据 + private InMsg inMsg = null; // 本次请求 xml 解析后的 InMsg 对象 + /** * weixin 公众号服务器调用唯一入口,即在开发者中心输入的 URL 必须要指向此 action */ @@ -43,7 +40,7 @@ public abstract class MsgController extends Controller { System.out.println("接收消息:"); System.out.println(getInMsgXml()); } - + // 解析消息并根据消息类型分发到相应的处理方法 InMsg msg = getInMsg(); if (msg instanceof InTextMsg) @@ -107,6 +104,7 @@ public abstract class MsgController extends Controller { /** * 在接收到微信服务器的 InMsg 消息后后响应 OutMsg 消息 + * * @param outMsg 输出对象 */ public void render(OutMsg outMsg) { @@ -117,26 +115,26 @@ public abstract class MsgController extends Controller { System.out.println(outMsgXml); System.out.println("--------------------------------------------------------------------------------\n"); } - + // 是否需要加密消息 if (ApiConfigKit.getApiConfig().isEncryptMessage()) { outMsgXml = MsgEncryptKit.encrypt(outMsgXml, getPara("timestamp"), getPara("nonce")); } - + renderText(outMsgXml, "text/xml"); } - + public void renderOutTextMsg(String content) { - OutTextMsg outMsg= new OutTextMsg(getInMsg()); + OutTextMsg outMsg = new OutTextMsg(getInMsg()); outMsg.setContent(content); render(outMsg); } - + @Before(NotAction.class) public String getInMsgXml() { if (inMsgXml == null) { inMsgXml = HttpKit.readData(getRequest()); - + // 是否需要解密消息 if (ApiConfigKit.getApiConfig().isEncryptMessage()) { inMsgXml = MsgEncryptKit.decrypt(inMsgXml, getPara("timestamp"), getPara("nonce"), getPara("msg_signature")); @@ -147,56 +145,56 @@ public abstract class MsgController extends Controller { } return inMsgXml; } - + @Before(NotAction.class) public InMsg getInMsg() { if (inMsg == null) - inMsg = InMsgParser.parse(getInMsgXml()); + inMsg = InMsgParser.parse(getInMsgXml()); return inMsg; } - + // 处理接收到的文本消息 protected abstract void processInTextMsg(InTextMsg inTextMsg); - + // 处理接收到的图片消息 protected abstract void processInImageMsg(InImageMsg inImageMsg); - + // 处理接收到的语音消息 protected abstract void processInVoiceMsg(InVoiceMsg inVoiceMsg); - + // 处理接收到的视频消息 protected abstract void processInVideoMsg(InVideoMsg inVideoMsg); // 处理接收到的视频消息 protected abstract void processInShortVideoMsg(InShortVideoMsg inShortVideoMsg); - + // 处理接收到的地址位置消息 protected abstract void processInLocationMsg(InLocationMsg inLocationMsg); // 处理接收到的链接消息 protected abstract void processInLinkMsg(InLinkMsg inLinkMsg); - // 处理接收到的多客服管理事件 - protected abstract void processInCustomEvent(InCustomEvent inCustomEvent); + // 处理接收到的多客服管理事件 + protected abstract void processInCustomEvent(InCustomEvent inCustomEvent); // 处理接收到的关注/取消关注事件 protected abstract void processInFollowEvent(InFollowEvent inFollowEvent); - + // 处理接收到的扫描带参数二维码事件 protected abstract void processInQrCodeEvent(InQrCodeEvent inQrCodeEvent); - + // 处理接收到的上报地理位置事件 protected abstract void processInLocationEvent(InLocationEvent inLocationEvent); - // 处理接收到的群发任务结束时通知事件 - protected abstract void processInMassEvent(InMassEvent inMassEvent); + // 处理接收到的群发任务结束时通知事件 + protected abstract void processInMassEvent(InMassEvent inMassEvent); // 处理接收到的自定义菜单事件 protected abstract void processInMenuEvent(InMenuEvent inMenuEvent); - + // 处理接收到的语音识别结果 protected abstract void processInSpeechRecognitionResults(InSpeechRecognitionResults inSpeechRecognitionResults); - + // 处理接收到的模板消息是否送达成功通知事件 protected abstract void processInTemplateMsgEvent(InTemplateMsgEvent inTemplateMsgEvent); @@ -208,7 +206,7 @@ public abstract class MsgController extends Controller { // 资质认证失败 || 名称认证失败 protected abstract void processInVerifyFailEvent(InVerifyFailEvent inVerifyFailEvent); - + // 门店在审核事件消息 protected abstract void processInPoiCheckNotifyEvent(InPoiCheckNotifyEvent inPoiCheckNotifyEvent); @@ -217,17 +215,22 @@ public abstract class MsgController extends Controller { // 微信会员卡二维码扫描领取接口 protected abstract void processInUserViewCardEvent(InUserViewCardEvent inUserViewCardEvent); + // 微信会员卡激活接口 protected abstract void processInSubmitMemberCardEvent(InSubmitMemberCardEvent inSubmitMemberCardEvent); + // 微信会员卡积分变更 protected abstract void processInUpdateMemberCardEvent(InUpdateMemberCardEvent inUpdateMemberCardEvent); + // 微信会员卡快速买单 protected abstract void processInUserPayFromCardEvent(InUserPayFromCardEvent inUserPayFromCardEvent); + // 微信小店订单支付成功接口消息 protected abstract void processInMerChantOrderEvent(InMerChantOrderEvent inMerChantOrderEvent); // 没有找到对应的事件消息 protected abstract void processIsNotDefinedEvent(InNotDefinedEvent inNotDefinedEvent); + // 没有找到对应的消息 protected abstract void processIsNotDefinedMsg(InNotDefinedMsg inNotDefinedMsg); } diff --git a/src/main/java/com/jfinal/weixin/sdk/jfinal/MsgControllerAdapter.java b/src/main/java/com/jfinal/weixin/sdk/jfinal/MsgControllerAdapter.java index d264e7f..0f74840 100644 --- a/src/main/java/com/jfinal/weixin/sdk/jfinal/MsgControllerAdapter.java +++ b/src/main/java/com/jfinal/weixin/sdk/jfinal/MsgControllerAdapter.java @@ -1,6 +1,6 @@ /** * Copyright (c) 2011-2014, James Zhan 詹波 (jfinal@126.com). - * + *

* Licensed under the Apache License, Version 2.0 (the "License"); */ @@ -15,45 +15,45 @@ import com.jfinal.weixin.sdk.msg.in.speech_recognition.InSpeechRecognitionResult * 以便开发者不去关注 MsgController 中不需要处理的抽象方法,节省代码量 */ public abstract class MsgControllerAdapter extends MsgController { - + protected abstract void processInFollowEvent(InFollowEvent inFollowEvent); - + protected abstract void processInTextMsg(InTextMsg inTextMsg); - + protected abstract void processInMenuEvent(InMenuEvent inMenuEvent); - + protected void processInImageMsg(InImageMsg inImageMsg) { - + } - + protected void processInVoiceMsg(InVoiceMsg inVoiceMsg) { - + } - + protected void processInVideoMsg(InVideoMsg inVideoMsg) { - + } - + protected void processInLocationMsg(InLocationMsg inLocationMsg) { - + } - + protected void processInLinkMsg(InLinkMsg inLinkMsg) { - + } - + protected void processInQrCodeEvent(InQrCodeEvent inQrCodeEvent) { - + } - + protected void processInLocationEvent(InLocationEvent inLocationEvent) { - + } - + protected void processInSpeechRecognitionResults(InSpeechRecognitionResults inSpeechRecognitionResults) { - + } - + protected void processInTemplateMsgEvent(InTemplateMsgEvent inTemplateMsgEvent) { } @@ -78,12 +78,12 @@ public abstract class MsgControllerAdapter extends MsgController { } - protected void processInVerifyFailEvent(InVerifyFailEvent inVerifyFailEvent){ + protected void processInVerifyFailEvent(InVerifyFailEvent inVerifyFailEvent) { } protected void processInPoiCheckNotifyEvent(InPoiCheckNotifyEvent inPoiCheckNotifyEvent) { - + } protected void processInWifiEvent(InWifiEvent inWifiEvent) { diff --git a/src/main/java/com/jfinal/weixin/sdk/jfinal/MsgInterceptor.java b/src/main/java/com/jfinal/weixin/sdk/jfinal/MsgInterceptor.java index e972fa4..e6731aa 100644 --- a/src/main/java/com/jfinal/weixin/sdk/jfinal/MsgInterceptor.java +++ b/src/main/java/com/jfinal/weixin/sdk/jfinal/MsgInterceptor.java @@ -1,6 +1,6 @@ /** * Copyright (c) 2011-2014, James Zhan 詹波 (jfinal@126.com). - * + *

* Licensed under the Apache License, Version 2.0 (the "License"); */ @@ -20,27 +20,33 @@ import com.jfinal.weixin.sdk.kit.SignatureCheckKit; * 2:响应开发者中心服务器配置 URL 与 Token 请求 * 3:签名检测 * 注意: MsgController 的继承类如果覆盖了 index 方法,则需要对该 index 方法声明该拦截器 - * 因为子类覆盖父类方法会使父类方法配置的拦截器失效,从而失去本拦截器的功能 + * 因为子类覆盖父类方法会使父类方法配置的拦截器失效,从而失去本拦截器的功能 */ public class MsgInterceptor implements Interceptor { - - private static final Log log = Log.getLog(MsgInterceptor.class); - + private static final Log log = Log.getLog(MsgInterceptor.class); + + private static AppIdParser _parser = new AppIdParser.DefaultParameterAppIdParser(); + + public static void setAppIdParser(AppIdParser parser) { + _parser = parser; + } + public void intercept(Invocation inv) { Controller controller = inv.getController(); - if (controller instanceof MsgController == false) + if (!(controller instanceof MsgController)) throw new RuntimeException("控制器需要继承 MsgController"); - + try { - // 将 ApiConfig 对象与当前线程绑定,以便在后续操作中方便获取该对象: ApiConfigKit.getApiConfig(); - ApiConfigKit.setThreadLocalApiConfig(((MsgController)controller).getApiConfig()); - + String appId = _parser.getAppId(controller); + // 将 appId 与当前线程绑定,以便在后续操作中方便获取ApiConfig对象: ApiConfigKit.getApiConfig(); + ApiConfigKit.setThreadLocalAppId(appId); + // 如果是服务器配置请求,则配置服务器并返回 if (isConfigServerRequest(controller)) { configServer(controller); - return ; + return; } - + // 对开发测试更加友好 if (ApiConfigKit.isDevMode()) { inv.invoke(); @@ -48,18 +54,16 @@ public class MsgInterceptor implements Interceptor { // 签名检测 if (checkSignature(controller)) { inv.invoke(); - } - else { + } else { controller.renderText("签名验证失败,请确定是微信服务器在发送消息过来"); } } - - } - finally { - ApiConfigKit.removeThreadLocalApiConfig(); + + } finally { + ApiConfigKit.removeThreadLocalAppId(); } } - + /** * 检测签名 */ @@ -71,29 +75,29 @@ public class MsgInterceptor implements Interceptor { controller.renderText("check signature failure"); return false; } - + if (SignatureCheckKit.me.checkSignature(signature, timestamp, nonce)) { return true; - } - else { + } else { log.error("check signature failure: " + " signature = " + controller.getPara("signature") + " timestamp = " + controller.getPara("timestamp") + " nonce = " + controller.getPara("nonce")); - + return false; } } - + /** * 是否为开发者中心保存服务器配置的请求 */ private boolean isConfigServerRequest(Controller controller) { return StrKit.notBlank(controller.getPara("echostr")); } - + /** * 配置开发者中心微信服务器所需的 url 与 token + * * @param c 控制器 */ public void configServer(Controller c) { -- Gitee From 1cf21332ac0065b792e678be7dfd9b84d3a11423 Mon Sep 17 00:00:00 2001 From: DuLerWeil Date: Wed, 29 Jun 2016 21:35:12 +0800 Subject: [PATCH 2/6] =?UTF-8?q?=E6=B7=BB=E5=8A=A0editorconfig=E6=96=87?= =?UTF-8?q?=E4=BB=B6=EF=BC=8C=E4=B8=BB=E6=B5=81=E7=BC=96=E8=BE=91=E5=99=A8?= =?UTF-8?q?=E4=B8=8B=E9=83=BD=E6=94=AF=E6=8C=81=EF=BC=8C=E4=BE=BF=E4=BA=8E?= =?UTF-8?q?=E7=BB=9F=E4=B8=80=E5=BC=80=E5=8F=91=E8=80=85=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E9=A3=8E=E6=A0=BC=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .editorconfig | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..537f742 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,20 @@ +# http://editorconfig.org +root = true + +# 空格替代Tab缩进在各种编辑工具下效果一致,相对美观。多占有些字符倒是次要。 +[*] +indent_style = space +indent_size = 4 +charset = utf-8 +end_of_line = lf +trim_trailing_whitespace = true +insert_final_newline = true + +# 前端相关的代码通常含有开闭标签,会占用大量字符空间,缩进2空格更加美观 +[*.{html,xml,json,js,jsp}] +indent_style = space +indent_size = 2 +charset = utf-8 +end_of_line = lf +trim_trailing_whitespace = true +insert_final_newline = true -- Gitee From 5a68c4f961b82589d7cd913d59176e1e7b4f609d Mon Sep 17 00:00:00 2001 From: DuLerWeil Date: Wed, 29 Jun 2016 21:38:36 +0800 Subject: [PATCH 3/6] =?UTF-8?q?=E7=9B=B8=E5=BA=94=E8=B0=83=E6=95=B4unit=20?= =?UTF-8?q?test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/jfinal/weixin/sdk/api/AccessTokenApiTest.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/test/java/com/jfinal/weixin/sdk/api/AccessTokenApiTest.java b/src/test/java/com/jfinal/weixin/sdk/api/AccessTokenApiTest.java index 5c620e6..179d457 100644 --- a/src/test/java/com/jfinal/weixin/sdk/api/AccessTokenApiTest.java +++ b/src/test/java/com/jfinal/weixin/sdk/api/AccessTokenApiTest.java @@ -12,12 +12,13 @@ public class AccessTokenApiTest { public static String AppID = "wx9803d1188fa5fbda"; public static String AppSecret = "db859c968763c582794e7c3d003c3d87"; - + public static void init(){ ApiConfig ac = new ApiConfig(); ac.setAppId(AppID); ac.setAppSecret(AppSecret); - ApiConfigKit.setThreadLocalApiConfig(ac); + ApiConfigKit.putApiConfig(ac); + ApiConfigKit.setThreadLocalAppId(ac.getAppId()); } public static void main(String[] args) throws IOException { @@ -37,13 +38,13 @@ public class AccessTokenApiTest { System.out.println("access_token : " + at.getAccessToken()); else System.out.println(at.getErrorCode() + " : " + at.getErrorMsg()); - + at = AccessTokenApi.getAccessToken(); if (at.isAvailable()) System.out.println("access_token : " + at.getAccessToken()); else System.out.println(at.getErrorCode() + " : " + at.getErrorMsg()); - + at = AccessTokenApi.getAccessToken(); if (at.isAvailable()) System.out.println("access_token : " + at.getAccessToken()); -- Gitee From c6cec9d976dfa7e7d6e1615e3b48cce4feb79eda Mon Sep 17 00:00:00 2001 From: DuLerWeil Date: Thu, 30 Jun 2016 09:25:31 +0800 Subject: [PATCH 4/6] =?UTF-8?q?=E4=BF=AE=E6=AD=A3getAppId?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jfinal/weixin/sdk/api/ApiConfigKit.java | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/jfinal/weixin/sdk/api/ApiConfigKit.java b/src/main/java/com/jfinal/weixin/sdk/api/ApiConfigKit.java index 386f894..bad44eb 100644 --- a/src/main/java/com/jfinal/weixin/sdk/api/ApiConfigKit.java +++ b/src/main/java/com/jfinal/weixin/sdk/api/ApiConfigKit.java @@ -12,24 +12,24 @@ import java.util.concurrent.ConcurrentHashMap; * 将 ApiConfig 绑定到 ThreadLocal 的工具类,以方便在当前线程的各个地方获取 ApiConfig 对象: * 1:如果控制器继承自 MsgController 该过程是自动的,详细可查看 MsgInterceptor 与之的配合 * 2:如果控制器继承自 ApiController 该过程是自动的,详细可查看 ApiInterceptor 与之的配合 - * 3:如果控制器没有继承自 MsgController、ApiController,则需要先手动调用 + * 3:如果控制器没有继承自 MsgController、ApiController,则需要先手动调用 * ApiConfigKit.setThreadLocalAppId(appId) 来绑定 appId 到线程之上 */ public class ApiConfigKit { private static final Log log = Log.getLog(ApiConfigKit.class); - + private static final ThreadLocal TL = new ThreadLocal(); private static final Map CFG_MAP = new ConcurrentHashMap(); private static final String DEFAULT_CFG_KEY = "_default_cfg_key_"; - + // 开发模式将输出消息交互 xml 到控制台 private static boolean devMode = false; - + public static void setDevMode(boolean devMode) { ApiConfigKit.devMode = devMode; } - + public static boolean isDevMode() { return devMode; } @@ -63,14 +63,14 @@ public class ApiConfigKit { public static ApiConfig removeApiConfig(String appId) { return CFG_MAP.remove(appId); } - + public static void setThreadLocalAppId(String appId) { if (StrKit.isBlank(appId)) { appId = DEFAULT_CFG_KEY; } TL.set(appId); } - + public static void removeThreadLocalAppId() { TL.remove(); } @@ -78,11 +78,11 @@ public class ApiConfigKit { public static String getAppId() { String appId = TL.get(); if (StrKit.isBlank(appId)) { - appId = DEFAULT_CFG_KEY; + appId = CFG_MAP.get(DEFAULT_CFG_KEY).getAppId(); } return appId; } - + public static ApiConfig getApiConfig() { String appId = getAppId(); return getApiConfig(appId); @@ -96,15 +96,15 @@ public class ApiConfigKit { "如JFinalConfig.afterJFinalStart()中调用, 才可以使用 ApiConfigKit.getApiConfig() 系列方法"); return cfg; } - + static IAccessTokenCache accessTokenCache = new DefaultAccessTokenCache(); - + public static void setAccessTokenCache(IAccessTokenCache accessTokenCache) { ApiConfigKit.accessTokenCache = accessTokenCache; } - + public static IAccessTokenCache getAccessTokenCache() { return ApiConfigKit.accessTokenCache; } -} \ No newline at end of file +} -- Gitee From 427952e14044c03076bf292bfc40c65e9cf7d43d Mon Sep 17 00:00:00 2001 From: DuLerWeil Date: Thu, 30 Jun 2016 09:29:35 +0800 Subject: [PATCH 5/6] =?UTF-8?q?=E4=BF=AE=E6=AD=A3default=20appId=E8=8E=B7?= =?UTF-8?q?=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/jfinal/weixin/sdk/api/ApiConfigKit.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/jfinal/weixin/sdk/api/ApiConfigKit.java b/src/main/java/com/jfinal/weixin/sdk/api/ApiConfigKit.java index bad44eb..0f27be5 100644 --- a/src/main/java/com/jfinal/weixin/sdk/api/ApiConfigKit.java +++ b/src/main/java/com/jfinal/weixin/sdk/api/ApiConfigKit.java @@ -66,7 +66,7 @@ public class ApiConfigKit { public static void setThreadLocalAppId(String appId) { if (StrKit.isBlank(appId)) { - appId = DEFAULT_CFG_KEY; + appId = CFG_MAP.get(DEFAULT_CFG_KEY).getAppId(); } TL.set(appId); } -- Gitee From 0d61200914b13767c717053da11be113e0d61f88 Mon Sep 17 00:00:00 2001 From: DuLerWeil Date: Thu, 30 Jun 2016 09:43:23 +0800 Subject: [PATCH 6/6] =?UTF-8?q?=E5=8F=96=E6=B6=88setDefaultApiConfig?= =?UTF-8?q?=EF=BC=8C=E9=81=BF=E5=85=8D=E5=9B=B0=E6=83=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 336 +++++++++--------- .../com/jfinal/weixin/demo/WeixinConfig.java | 27 +- .../jfinal/weixin/sdk/api/ApiConfigKit.java | 15 +- 3 files changed, 184 insertions(+), 194 deletions(-) diff --git a/pom.xml b/pom.xml index 731f180..69584a6 100644 --- a/pom.xml +++ b/pom.xml @@ -1,168 +1,168 @@ - - 4.0.0 - com.jfinal - jfinal-weixin - 1.8-SNAPSHOT - jfinal-weixin - http://www.jfinal.com - - - UTF-8 - - - - org.sonatype.oss - oss-parent - 7 - - - - - - scm:git:git@git.oschina.net:jfinal/jfinal-weixin.git - scm:git:git@git.oschina.net:jfinal/jfinal-weixin.git - git@git.oschina.net:jfinal/jfinal-weixin.git - - - - - junit - junit - 4.8.2 - test - - - com.jfinal - jetty-server - 8.1.8 - provided - - - com.jfinal - jfinal - [2.2,) - - - com.fasterxml.jackson.core - jackson-databind - 2.4.3 - provided - - - redis.clients - jedis - 2.7.2 - provided - - - de.ruedigermoeller - fst - 2.29 - provided - - - - com.squareup.okhttp - okhttp - 2.7.5 - provided - - - - com.squareup.okhttp3 - okhttp - 3.2.0 - provided - - - - - jfinal-weixin - - - maven-war-plugin - 2.1.1 - - - maven-jar-plugin - 2.3.2 - - - default-jar - package - - jar - - - - - - a_little_config.txt - a_little_config_pro.txt - ehcache.xml - log4j.properties - com/jfinal/weixin/demo - com/jfinal/weixin/demo/* - - - - - org.apache.maven.plugins - maven-compiler-plugin - 2.5 - - 1.6 - 1.6 - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.10.3 - - UTF-8 - ${basedir} - ${basedir} - com.jfinal.weixin.demo - - - - - - - - - release - - - - - org.apache.maven.plugins - maven-gpg-plugin - 1.5 - - - verify - - sign - - - - - - - - - oss - https://oss.sonatype.org/content/repositories/snapshots/ - - - oss - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - - - - - + + 4.0.0 + com.jfinal + jfinal-weixin + 1.8-SNAPSHOT + jfinal-weixin + http://www.jfinal.com + + + UTF-8 + + + + org.sonatype.oss + oss-parent + 7 + + + + + + scm:git:git@git.oschina.net:jfinal/jfinal-weixin.git + scm:git:git@git.oschina.net:jfinal/jfinal-weixin.git + git@git.oschina.net:jfinal/jfinal-weixin.git + + + + + junit + junit + 4.8.2 + test + + + com.jfinal + jetty-server + 8.1.8 + provided + + + com.jfinal + jfinal + [2.2,) + + + com.fasterxml.jackson.core + jackson-databind + 2.4.3 + provided + + + redis.clients + jedis + 2.7.2 + provided + + + de.ruedigermoeller + fst + 2.29 + provided + + + + com.squareup.okhttp + okhttp + 2.7.5 + provided + + + + com.squareup.okhttp3 + okhttp + 3.2.0 + provided + + + + + jfinal-weixin + + + maven-war-plugin + 2.1.1 + + + maven-jar-plugin + 2.3.2 + + + default-jar + package + + jar + + + + + + a_little_config.txt + a_little_config_pro.txt + ehcache.xml + log4j.properties + com/jfinal/weixin/demo + com/jfinal/weixin/demo/* + + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.5 + + 1.6 + 1.6 + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.10.3 + + UTF-8 + ${basedir} + ${basedir} + com.jfinal.weixin.demo + + + + + + + + + release + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.5 + + + verify + + sign + + + + + + + + + oss + https://oss.sonatype.org/content/repositories/snapshots/ + + + oss + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + + + + + diff --git a/src/main/java/com/jfinal/weixin/demo/WeixinConfig.java b/src/main/java/com/jfinal/weixin/demo/WeixinConfig.java index dc9d6d7..11cfbe5 100644 --- a/src/main/java/com/jfinal/weixin/demo/WeixinConfig.java +++ b/src/main/java/com/jfinal/weixin/demo/WeixinConfig.java @@ -18,7 +18,7 @@ import com.jfinal.weixin.sdk.api.ApiConfig; import com.jfinal.weixin.sdk.api.ApiConfigKit; public class WeixinConfig extends JFinalConfig { - + /** * 如果生产环境配置文件存在,则优先加载该配置,否则加载开发环境配置文件 * @param pro 生产环境配置文件 @@ -32,41 +32,41 @@ public class WeixinConfig extends JFinalConfig { PropKit.use(dev); } } - + public void configConstant(Constants me) { loadProp("a_little_config_pro.txt", "a_little_config.txt"); me.setDevMode(PropKit.getBoolean("devMode", false)); - + // ApiConfigKit 设为开发模式可以在开发阶段输出请求交互的 xml 与 json 数据 ApiConfigKit.setDevMode(me.getDevMode()); } - + public void configRoute(Routes me) { me.add("/msg", WeixinMsgController.class); me.add("/api", WeixinApiController.class, "/api"); me.add("/pay", WeixinPayController.class); } - + public void configPlugin(Plugins me) { // C3p0Plugin c3p0Plugin = new C3p0Plugin(PropKit.get("jdbcUrl"), PropKit.get("user"), PropKit.get("password").trim()); // me.add(c3p0Plugin); - + // EhCachePlugin ecp = new EhCachePlugin(); // me.add(ecp); - + // RedisPlugin redisPlugin = new RedisPlugin("weixin", "127.0.0.1"); // me.add(redisPlugin); } - + public void configInterceptor(Interceptors me) { // ApiInterceptor.setAppIdParser(new AppIdParser.DefaultParameterAppIdParser("appId")); 默认无需设置 // MsgInterceptor.setAppIdParser(new AppIdParser.DefaultParameterAppIdParser("appId")); 默认无需设置 } - + public void configHandler(Handlers me) { - + } - + public void afterJFinalStart() { // 1.5 之后支持redis存储access_token、js_ticket,需要先启动RedisPlugin // ApiConfigKit.setAccessTokenCache(new RedisAccessTokenCache()); @@ -89,10 +89,9 @@ public class WeixinConfig extends JFinalConfig { ac.setEncodingAesKey(PropKit.get("encodingAesKey", "setting it in config file")); /** - * 单个公众号时,和ApiConfigKit.putApiConfig(ac)等价。 - * 多个公众号时,重复调用ApiConfigKit.putApiConfig(ac)依次添加即可。 + * 多个公众号时,重复调用ApiConfigKit.putApiConfig(ac)依次添加即可,第一个添加的是默认。 */ - ApiConfigKit.setDefaultApiConfig(ac); + ApiConfigKit.putApiConfig(ac); } public static void main(String[] args) { diff --git a/src/main/java/com/jfinal/weixin/sdk/api/ApiConfigKit.java b/src/main/java/com/jfinal/weixin/sdk/api/ApiConfigKit.java index 0f27be5..50d0e30 100644 --- a/src/main/java/com/jfinal/weixin/sdk/api/ApiConfigKit.java +++ b/src/main/java/com/jfinal/weixin/sdk/api/ApiConfigKit.java @@ -34,24 +34,15 @@ public class ApiConfigKit { return devMode; } - /** - * 设置默认公众号接口配置 - * @param apiConfig 默认公众号配置 - * @return - */ - public static ApiConfig setDefaultApiConfig(ApiConfig apiConfig) { - return CFG_MAP.put(DEFAULT_CFG_KEY, apiConfig); - } - /** * 添加公众号配置,每个appId只需添加一次,相同appId将被覆盖。 - * 第一次添加的将作为默认公众号配置 + * 第一个添加的将作为默认公众号配置 * @param apiConfig 公众号配置 * @return */ public static ApiConfig putApiConfig(ApiConfig apiConfig) { if (CFG_MAP.size() == 0) { - setDefaultApiConfig(apiConfig); + CFG_MAP.put(DEFAULT_CFG_KEY, apiConfig); } return CFG_MAP.put(apiConfig.getAppId(), apiConfig); } @@ -92,7 +83,7 @@ public class ApiConfigKit { log.debug("appId: " + appId); ApiConfig cfg = CFG_MAP.get(appId); if (cfg == null) - throw new IllegalStateException("需事先调用 ApiConfigKit.addApiConfig(apiConfig) 将 appId对应的 ApiConfig 对象存入," + + throw new IllegalStateException("需事先调用 ApiConfigKit.putApiConfig(apiConfig) 将 appId对应的 ApiConfig 对象存入," + "如JFinalConfig.afterJFinalStart()中调用, 才可以使用 ApiConfigKit.getApiConfig() 系列方法"); return cfg; } -- Gitee