@@ -13,6 +13,7 @@ test-output | |||||
hs_err_pid* | hs_err_pid* | ||||
target | target | ||||
bin | |||||
.project | .project | ||||
.classpath | .classpath | ||||
.settings | .settings | ||||
@@ -21,3 +22,7 @@ sw-pom.xml | |||||
*.iml | *.iml | ||||
test-config.xml | test-config.xml | ||||
.idea | .idea | ||||
/.gradle/ | |||||
/gradle/ | |||||
*.bat | |||||
/gradlew |
@@ -8,22 +8,7 @@ | |||||
#### 由于本次更新涉及接口调整比较大,主要是公众号的调整,企业号无过多调整,主要是为了解决主接口类过于庞大不方便管理的问题,将接口实现代码按模块进行拆分。因此版本号直接从1.X.X直接升级到2.0.0,所以如果习惯于1.X.X版本的同学不想做过多更改的话,请慎重考虑升级到最新版本。 | #### 由于本次更新涉及接口调整比较大,主要是公众号的调整,企业号无过多调整,主要是为了解决主接口类过于庞大不方便管理的问题,将接口实现代码按模块进行拆分。因此版本号直接从1.X.X直接升级到2.0.0,所以如果习惯于1.X.X版本的同学不想做过多更改的话,请慎重考虑升级到最新版本。 | ||||
--- | --- | ||||
#### 本项目主要存放在github上,地址为 : | |||||
* https://github.com/wechat-group/weixin-java-tools | |||||
* ===========但同时会在其他几个网站同步更新,地址分别是: | |||||
* https://bitbucket.org/binarywang/weixin-java-tools | |||||
* http://git.oschina.net/binary/weixin-java-tools | |||||
* https://git.coding.net/binarywang/weixin-java-tools.git | |||||
### 详细开发文档请看 [wiki](https://github.com/chanjarster/weixin-java-tools/wiki)。 | ### 详细开发文档请看 [wiki](https://github.com/chanjarster/weixin-java-tools/wiki)。 | ||||
## 目前可参考的Demo项目: | |||||
* https://github.com/wechat-group/weixin-java-tools-springmvc | |||||
* https://github.com/wechat-group/weixin-mp-demo | |||||
* ===========以下为备份仓库,会保持跟主仓库同步 | |||||
* http://git.oschina.net/binary/weixin-mp-demo | |||||
* https://bitbucket.org/binarywang/weixin-mp-demo | |||||
=========== | =========== | ||||
## 开发交流工具: | ## 开发交流工具: | ||||
* QQ群:343954419 [](http://shang.qq.com/wpa/qunwpa?idkey=078f7a153d243853e24cf2b542e7a6ccbf2a592bc138080f84d11297f736ec46) | * QQ群:343954419 [](http://shang.qq.com/wpa/qunwpa?idkey=078f7a153d243853e24cf2b542e7a6ccbf2a592bc138080f84d11297f736ec46) | ||||
@@ -40,10 +25,9 @@ | |||||
- [【企业号】](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.github.binarywang%22%20AND%20a%3A%22weixin-java-cp%22) | - [【企业号】](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.github.binarywang%22%20AND%20a%3A%22weixin-java-cp%22) | ||||
## Quick Start | |||||
* 如果要开发公众号(订阅号、服务号)应用,在你的maven项目中添加: | |||||
## Maven & Gradle | |||||
* 公众号(订阅号、服务号): | |||||
```xml | ```xml | ||||
<dependency> | <dependency> | ||||
<groupId>com.github.binarywang</groupId> | <groupId>com.github.binarywang</groupId> | ||||
@@ -52,8 +36,11 @@ | |||||
</dependency> | </dependency> | ||||
``` | ``` | ||||
* 如果要开发企业号应用,在你的maven项目中添加: | |||||
```groovy | |||||
compile 'com.github.binarywang:weixin-java-mp:2.0.0' | |||||
``` | |||||
* 企业号: | |||||
```xml | ```xml | ||||
<dependency> | <dependency> | ||||
<groupId>com.github.binarywang</groupId> | <groupId>com.github.binarywang</groupId> | ||||
@@ -62,6 +49,25 @@ | |||||
</dependency> | </dependency> | ||||
``` | ``` | ||||
```groovy | |||||
compile 'com.github.binarywang:weixin-java-cp:2.0.0' | |||||
``` | |||||
#### 本项目主要存放在github上,地址为 : | |||||
* https://github.com/wechat-group/weixin-java-tools | |||||
* ===========但同时会在其他几个网站同步更新,地址分别是: | |||||
* https://bitbucket.org/binarywang/weixin-java-tools | |||||
* http://git.oschina.net/binary/weixin-java-tools | |||||
* https://git.coding.net/binarywang/weixin-java-tools.git | |||||
## 目前可参考的Demo项目: | |||||
* https://github.com/wechat-group/weixin-java-tools-springmvc | |||||
* https://github.com/wechat-group/weixin-mp-demo | |||||
* ===========以下为备份仓库,会保持跟主仓库同步 | |||||
* http://git.oschina.net/binary/weixin-mp-demo | |||||
* https://bitbucket.org/binarywang/weixin-mp-demo | |||||
## 关于代码贡献 | ## 关于代码贡献 | ||||
* 非常欢迎和感谢对本项目发起Pull Request的同学,本项目可以采用两种方式接受代码贡献: | * 非常欢迎和感谢对本项目发起Pull Request的同学,本项目可以采用两种方式接受代码贡献: | ||||
@@ -0,0 +1,34 @@ | |||||
allprojects { | |||||
apply plugin: 'maven' | |||||
group = 'com.github.binarywang' | |||||
version = '2.1.0-SNAPSHOT' | |||||
} | |||||
subprojects { | |||||
apply plugin: 'java' | |||||
sourceCompatibility = 1.7 | |||||
targetCompatibility = 1.7 | |||||
repositories { | |||||
mavenLocal() | |||||
maven { url "http://maven.aliyun.com/nexus/content/groups/public" } | |||||
} | |||||
dependencies { | |||||
compile group: 'org.slf4j', name: 'slf4j-api', version:'1.7.10' | |||||
compile group: 'org.apache.httpcomponents', name: 'fluent-hc', version:'4.5' | |||||
compile group: 'org.apache.httpcomponents', name: 'httpmime', version:'4.5' | |||||
compile group: 'org.jodd', name: 'jodd-http', version:'3.6.7' | |||||
compile group: 'com.google.code.gson', name: 'gson', version:'2.7' | |||||
compile group: 'commons-codec', name: 'commons-codec', version:'1.10' | |||||
compile group: 'commons-io', name: 'commons-io', version:'2.5' | |||||
compile group: 'org.apache.commons', name: 'commons-lang3', version:'3.4' | |||||
compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version:'2.8.0' | |||||
compile group: 'redis.clients', name: 'jedis', version:'2.9.0' | |||||
testCompile group: 'ch.qos.logback', name: 'logback-classic', version:'1.1.2' | |||||
} | |||||
} |
@@ -5,7 +5,7 @@ | |||||
<modelVersion>4.0.0</modelVersion> | <modelVersion>4.0.0</modelVersion> | ||||
<groupId>com.github.binarywang</groupId> | <groupId>com.github.binarywang</groupId> | ||||
<artifactId>weixin-java-parent</artifactId> | <artifactId>weixin-java-parent</artifactId> | ||||
<version>2.0.0</version> | |||||
<version>2.1.0</version> | |||||
<packaging>pom</packaging> | <packaging>pom</packaging> | ||||
<name>WeiXin Java Tools - Parent</name> | <name>WeiXin Java Tools - Parent</name> | ||||
<description>微信公众号、企业号上级POM</description> | <description>微信公众号、企业号上级POM</description> | ||||
@@ -50,6 +50,7 @@ | |||||
<logback.version>1.1.2</logback.version> | <logback.version>1.1.2</logback.version> | ||||
<jodd-http.version>3.6.7</jodd-http.version> | <jodd-http.version>3.6.7</jodd-http.version> | ||||
<jackson.version>2.8.0</jackson.version> | <jackson.version>2.8.0</jackson.version> | ||||
<jedis.version>2.9.0</jedis.version> | |||||
<gson.version>2.7</gson.version> | <gson.version>2.7</gson.version> | ||||
<commons-lang3.version>3.4</commons-lang3.version> | <commons-lang3.version>3.4</commons-lang3.version> | ||||
<commons-io.version>2.5</commons-io.version> | <commons-io.version>2.5</commons-io.version> | ||||
@@ -110,8 +111,13 @@ | |||||
<artifactId>jackson-databind</artifactId> | <artifactId>jackson-databind</artifactId> | ||||
<version>${jackson.version}</version> | <version>${jackson.version}</version> | ||||
</dependency> | </dependency> | ||||
<dependency> | |||||
<groupId>redis.clients</groupId> | |||||
<artifactId>jedis</artifactId> | |||||
<version>${jedis.version}</version> | |||||
</dependency> | |||||
</dependencies> | </dependencies> | ||||
<dependencyManagement> | <dependencyManagement> | ||||
<dependencies> | <dependencies> | ||||
<dependency> | <dependency> | ||||
@@ -0,0 +1,8 @@ | |||||
rootProject.name = 'weixin-java-parent' | |||||
include ':weixin-java-common' | |||||
include ':weixin-java-cp' | |||||
include ':weixin-java-mp' | |||||
project(':weixin-java-common').projectDir = "$rootDir/weixin-java-common" as File | |||||
project(':weixin-java-cp').projectDir = "$rootDir/weixin-java-cp" as File | |||||
project(':weixin-java-mp').projectDir = "$rootDir/weixin-java-mp" as File |
@@ -0,0 +1,12 @@ | |||||
description = 'WeiXin Java Tools - Common' | |||||
dependencies { | |||||
compile group: 'com.thoughtworks.xstream', name: 'xstream', version:'1.4.7' | |||||
testCompile group: 'junit', name: 'junit', version:'4.11' | |||||
testCompile group: 'org.testng', name: 'testng', version:'6.8.7' | |||||
testCompile group: 'org.mockito', name: 'mockito-all', version:'1.9.5' | |||||
testCompile group: 'com.google.inject', name: 'guice', version:'3.0' | |||||
testCompile group: 'org.eclipse.jetty', name: 'jetty-server', version:'9.3.0.M0' | |||||
testCompile group: 'org.eclipse.jetty', name: 'jetty-servlet', version:'9.3.0.M0' | |||||
} | |||||
test.useTestNG() |
@@ -6,7 +6,7 @@ | |||||
<parent> | <parent> | ||||
<groupId>com.github.binarywang</groupId> | <groupId>com.github.binarywang</groupId> | ||||
<artifactId>weixin-java-parent</artifactId> | <artifactId>weixin-java-parent</artifactId> | ||||
<version>2.0.0</version> | |||||
<version>2.1.0</version> | |||||
</parent> | </parent> | ||||
<artifactId>weixin-java-common</artifactId> | <artifactId>weixin-java-common</artifactId> | ||||
@@ -19,7 +19,7 @@ public class WxConsts { | |||||
public static final String XML_MSG_LINK = "link"; | public static final String XML_MSG_LINK = "link"; | ||||
public static final String XML_MSG_EVENT = "event"; | public static final String XML_MSG_EVENT = "event"; | ||||
public static final String XML_TRANSFER_CUSTOMER_SERVICE = "transfer_customer_service"; | public static final String XML_TRANSFER_CUSTOMER_SERVICE = "transfer_customer_service"; | ||||
/////////////////////// | /////////////////////// | ||||
// 主动发送消息(即客服消息)的消息类型 | // 主动发送消息(即客服消息)的消息类型 | ||||
/////////////////////// | /////////////////////// | ||||
@@ -33,7 +33,7 @@ public class WxConsts { | |||||
public static final String CUSTOM_MSG_TRANSFER_CUSTOMER_SERVICE = "transfer_customer_service"; | public static final String CUSTOM_MSG_TRANSFER_CUSTOMER_SERVICE = "transfer_customer_service"; | ||||
public static final String CUSTOM_MSG_SAFE_NO = "0"; | public static final String CUSTOM_MSG_SAFE_NO = "0"; | ||||
public static final String CUSTOM_MSG_SAFE_YES = "1"; | public static final String CUSTOM_MSG_SAFE_YES = "1"; | ||||
/////////////////////// | /////////////////////// | ||||
// 群发消息的消息类型 | // 群发消息的消息类型 | ||||
/////////////////////// | /////////////////////// | ||||
@@ -42,7 +42,7 @@ public class WxConsts { | |||||
public static final String MASS_MSG_VOICE = "voice"; | public static final String MASS_MSG_VOICE = "voice"; | ||||
public static final String MASS_MSG_IMAGE = "image"; | public static final String MASS_MSG_IMAGE = "image"; | ||||
public static final String MASS_MSG_VIDEO = "mpvideo"; | public static final String MASS_MSG_VIDEO = "mpvideo"; | ||||
/////////////////////// | /////////////////////// | ||||
// 群发消息后微信端推送给服务器的反馈消息 | // 群发消息后微信端推送给服务器的反馈消息 | ||||
/////////////////////// | /////////////////////// | ||||
@@ -57,25 +57,11 @@ public class WxConsts { | |||||
public static final String MASS_ST_涉嫌版权 = "err(20013)"; | public static final String MASS_ST_涉嫌版权 = "err(20013)"; | ||||
public static final String MASS_ST_涉嫌互推_互相宣传 = "err(22000)"; | public static final String MASS_ST_涉嫌互推_互相宣传 = "err(22000)"; | ||||
public static final String MASS_ST_涉嫌其他 = "err(21000)"; | public static final String MASS_ST_涉嫌其他 = "err(21000)"; | ||||
/** | /** | ||||
* 群发反馈消息代码所对应的文字描述 | * 群发反馈消息代码所对应的文字描述 | ||||
*/ | */ | ||||
public static final Map<String, String> MASS_ST_2_DESC = new HashMap<String, String>(); | public static final Map<String, String> MASS_ST_2_DESC = new HashMap<String, String>(); | ||||
static { | |||||
MASS_ST_2_DESC.put(MASS_ST_SUCCESS, "发送成功"); | |||||
MASS_ST_2_DESC.put(MASS_ST_FAIL, "发送失败"); | |||||
MASS_ST_2_DESC.put(MASS_ST_涉嫌广告, "涉嫌广告"); | |||||
MASS_ST_2_DESC.put(MASS_ST_涉嫌政治, "涉嫌政治"); | |||||
MASS_ST_2_DESC.put(MASS_ST_涉嫌社会, "涉嫌社会"); | |||||
MASS_ST_2_DESC.put(MASS_ST_涉嫌色情, "涉嫌色情"); | |||||
MASS_ST_2_DESC.put(MASS_ST_涉嫌违法犯罪, "涉嫌违法犯罪"); | |||||
MASS_ST_2_DESC.put(MASS_ST_涉嫌欺诈, "涉嫌欺诈"); | |||||
MASS_ST_2_DESC.put(MASS_ST_涉嫌版权, "涉嫌版权"); | |||||
MASS_ST_2_DESC.put(MASS_ST_涉嫌互推_互相宣传, "涉嫌互推_互相宣传"); | |||||
MASS_ST_2_DESC.put(MASS_ST_涉嫌其他, "涉嫌其他"); | |||||
} | |||||
/////////////////////// | /////////////////////// | ||||
// 微信端推送过来的事件类型 | // 微信端推送过来的事件类型 | ||||
/////////////////////// | /////////////////////// | ||||
@@ -103,11 +89,9 @@ public class WxConsts { | |||||
public static final String EVT_USER_VIEW_CARD = "user_view_card"; | public static final String EVT_USER_VIEW_CARD = "user_view_card"; | ||||
public static final String EVT_USER_ENTER_SESSION_FROM_CARD = "user_enter_session_from_card"; | public static final String EVT_USER_ENTER_SESSION_FROM_CARD = "user_enter_session_from_card"; | ||||
public static final String EVT_CARD_SKU_REMIND = "card_sku_remind"; // 库存报警 | public static final String EVT_CARD_SKU_REMIND = "card_sku_remind"; // 库存报警 | ||||
public static final String EVT_KF_CREATE_SESSION = "kf_create_session"; // 客服接入会话 | public static final String EVT_KF_CREATE_SESSION = "kf_create_session"; // 客服接入会话 | ||||
public static final String EVT_KF_CLOSE_SESSION = "kf_close_session"; // 客服关闭会话 | public static final String EVT_KF_CLOSE_SESSION = "kf_close_session"; // 客服关闭会话 | ||||
public static final String EVT_KF_SWITCH_SESSION = "kf_switch_session"; // 客服转接会话 | public static final String EVT_KF_SWITCH_SESSION = "kf_switch_session"; // 客服转接会话 | ||||
/////////////////////// | /////////////////////// | ||||
// 上传多媒体文件的类型 | // 上传多媒体文件的类型 | ||||
/////////////////////// | /////////////////////// | ||||
@@ -116,7 +100,6 @@ public class WxConsts { | |||||
public static final String MEDIA_VIDEO = "video"; | public static final String MEDIA_VIDEO = "video"; | ||||
public static final String MEDIA_THUMB = "thumb"; | public static final String MEDIA_THUMB = "thumb"; | ||||
public static final String MEDIA_FILE = "file"; | public static final String MEDIA_FILE = "file"; | ||||
/////////////////////// | /////////////////////// | ||||
// 文件类型 | // 文件类型 | ||||
/////////////////////// | /////////////////////// | ||||
@@ -124,40 +107,63 @@ public class WxConsts { | |||||
public static final String FILE_MP3 = "mp3"; | public static final String FILE_MP3 = "mp3"; | ||||
public static final String FILE_AMR = "amr"; | public static final String FILE_AMR = "amr"; | ||||
public static final String FILE_MP4 = "mp4"; | public static final String FILE_MP4 = "mp4"; | ||||
/** | |||||
* 点击推事件 | |||||
*/ | |||||
public static final String BUTTON_CLICK = "click"; | |||||
/////////////////////// | /////////////////////// | ||||
// 自定义菜单的按钮类型 | // 自定义菜单的按钮类型 | ||||
/////////////////////// | /////////////////////// | ||||
/** 点击推事件 */ | |||||
public static final String BUTTON_CLICK = "click"; | |||||
/** 跳转URL */ | |||||
/** | |||||
* 跳转URL | |||||
*/ | |||||
public static final String BUTTON_VIEW = "view"; | public static final String BUTTON_VIEW = "view"; | ||||
/** 扫码推事件 */ | |||||
/** | |||||
* 扫码推事件 | |||||
*/ | |||||
public static final String BUTTON_SCANCODE_PUSH = "scancode_push"; | public static final String BUTTON_SCANCODE_PUSH = "scancode_push"; | ||||
/** 扫码推事件且弹出“消息接收中”提示框 */ | |||||
/** | |||||
* 扫码推事件且弹出“消息接收中”提示框 | |||||
*/ | |||||
public static final String BUTTON_SCANCODE_WAITMSG = "scancode_waitmsg"; | public static final String BUTTON_SCANCODE_WAITMSG = "scancode_waitmsg"; | ||||
/** 弹出系统拍照发图 */ | |||||
/** | |||||
* 弹出系统拍照发图 | |||||
*/ | |||||
public static final String BUTTON_PIC_SYSPHOTO = "pic_sysphoto"; | public static final String BUTTON_PIC_SYSPHOTO = "pic_sysphoto"; | ||||
/** 弹出拍照或者相册发图 */ | |||||
/** | |||||
* 弹出拍照或者相册发图 | |||||
*/ | |||||
public static final String BUTTON_PIC_PHOTO_OR_ALBUM = "pic_photo_or_album"; | public static final String BUTTON_PIC_PHOTO_OR_ALBUM = "pic_photo_or_album"; | ||||
/** 弹出微信相册发图器 */ | |||||
/** | |||||
* 弹出微信相册发图器 | |||||
*/ | |||||
public static final String BUTTON_PIC_WEIXIN = "pic_weixin"; | public static final String BUTTON_PIC_WEIXIN = "pic_weixin"; | ||||
/** 弹出地理位置选择器 */ | |||||
/** | |||||
* 弹出地理位置选择器 | |||||
*/ | |||||
public static final String BUTTON_LOCATION_SELECT = "location_select"; | public static final String BUTTON_LOCATION_SELECT = "location_select"; | ||||
/** 下发消息(除文本消息) */ | |||||
/** | |||||
* 下发消息(除文本消息) | |||||
*/ | |||||
public static final String BUTTON_MEDIA_ID = "media_id"; | public static final String BUTTON_MEDIA_ID = "media_id"; | ||||
/** 跳转图文消息URL */ | |||||
/** | |||||
* 跳转图文消息URL | |||||
*/ | |||||
public static final String BUTTON_VIEW_LIMITED = "view_limited"; | public static final String BUTTON_VIEW_LIMITED = "view_limited"; | ||||
/** | |||||
* 不弹出授权页面,直接跳转,只能获取用户openid | |||||
*/ | |||||
public static final String OAUTH2_SCOPE_BASE = "snsapi_base"; | |||||
/////////////////////// | /////////////////////// | ||||
// oauth2网页授权的scope | // oauth2网页授权的scope | ||||
/////////////////////// | /////////////////////// | ||||
/** 不弹出授权页面,直接跳转,只能获取用户openid */ | |||||
public static final String OAUTH2_SCOPE_BASE = "snsapi_base"; | |||||
/** 弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息 */ | |||||
/** | |||||
* 弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息 | |||||
*/ | |||||
public static final String OAUTH2_SCOPE_USER_INFO = "snsapi_userinfo"; | public static final String OAUTH2_SCOPE_USER_INFO = "snsapi_userinfo"; | ||||
/////////////////////// | /////////////////////// | ||||
// 永久素材类型 | // 永久素材类型 | ||||
/////////////////////// | /////////////////////// | ||||
@@ -165,4 +171,18 @@ public class WxConsts { | |||||
public static final String MATERIAL_VOICE = "voice"; | public static final String MATERIAL_VOICE = "voice"; | ||||
public static final String MATERIAL_IMAGE = "image"; | public static final String MATERIAL_IMAGE = "image"; | ||||
public static final String MATERIAL_VIDEO = "video"; | public static final String MATERIAL_VIDEO = "video"; | ||||
static { | |||||
MASS_ST_2_DESC.put(MASS_ST_SUCCESS, "发送成功"); | |||||
MASS_ST_2_DESC.put(MASS_ST_FAIL, "发送失败"); | |||||
MASS_ST_2_DESC.put(MASS_ST_涉嫌广告, "涉嫌广告"); | |||||
MASS_ST_2_DESC.put(MASS_ST_涉嫌政治, "涉嫌政治"); | |||||
MASS_ST_2_DESC.put(MASS_ST_涉嫌社会, "涉嫌社会"); | |||||
MASS_ST_2_DESC.put(MASS_ST_涉嫌色情, "涉嫌色情"); | |||||
MASS_ST_2_DESC.put(MASS_ST_涉嫌违法犯罪, "涉嫌违法犯罪"); | |||||
MASS_ST_2_DESC.put(MASS_ST_涉嫌欺诈, "涉嫌欺诈"); | |||||
MASS_ST_2_DESC.put(MASS_ST_涉嫌版权, "涉嫌版权"); | |||||
MASS_ST_2_DESC.put(MASS_ST_涉嫌互推_互相宣传, "涉嫌互推_互相宣传"); | |||||
MASS_ST_2_DESC.put(MASS_ST_涉嫌其他, "涉嫌其他"); | |||||
} | |||||
} | } |
@@ -10,15 +10,16 @@ public interface WxMessageDuplicateChecker { | |||||
/** | /** | ||||
* <h2>公众号的排重方式</h2> | * <h2>公众号的排重方式</h2> | ||||
* | |||||
* <p> | |||||
* <p>普通消息:关于重试的消息排重,推荐使用msgid排重。<a href="http://mp.weixin.qq.com/wiki/10/79502792eef98d6e0c6e1739da387346.html">文档参考</a>。</p> | * <p>普通消息:关于重试的消息排重,推荐使用msgid排重。<a href="http://mp.weixin.qq.com/wiki/10/79502792eef98d6e0c6e1739da387346.html">文档参考</a>。</p> | ||||
* <p>事件消息:关于重试的消息排重,推荐使用FromUserName + CreateTime 排重。<a href="http://mp.weixin.qq.com/wiki/2/5baf56ce4947d35003b86a9805634b1e.html">文档参考</a></p> | * <p>事件消息:关于重试的消息排重,推荐使用FromUserName + CreateTime 排重。<a href="http://mp.weixin.qq.com/wiki/2/5baf56ce4947d35003b86a9805634b1e.html">文档参考</a></p> | ||||
* | |||||
* <p> | |||||
* <h2>企业号的排重方式</h2> | * <h2>企业号的排重方式</h2> | ||||
* | |||||
* <p> | |||||
* 官方文档完全没有写,参照公众号的方式排重。 | * 官方文档完全没有写,参照公众号的方式排重。 | ||||
* | |||||
* <p> | |||||
* <p>或者可以采取更简单的方式,如果有MsgId就用MsgId排重,如果没有就用FromUserName+CreateTime排重</p> | * <p>或者可以采取更简单的方式,如果有MsgId就用MsgId排重,如果没有就用FromUserName+CreateTime排重</p> | ||||
* | |||||
* @param messageId messageId需要根据上面讲的方式构造 | * @param messageId messageId需要根据上面讲的方式构造 | ||||
* @return 如果是重复消息,返回true,否则返回false | * @return 如果是重复消息,返回true,否则返回false | ||||
*/ | */ | ||||
@@ -46,7 +46,8 @@ public class WxMessageInMemoryDuplicateChecker implements WxMessageDuplicateChec | |||||
/** | /** | ||||
* WxMsgIdInMemoryDuplicateChecker构造函数 | * WxMsgIdInMemoryDuplicateChecker构造函数 | ||||
* @param timeToLive 一个消息ID在内存的过期时间:毫秒 | |||||
* | |||||
* @param timeToLive 一个消息ID在内存的过期时间:毫秒 | |||||
* @param clearPeriod 每隔多少周期检查消息ID是否过期:毫秒 | * @param clearPeriod 每隔多少周期检查消息ID是否过期:毫秒 | ||||
*/ | */ | ||||
public WxMessageInMemoryDuplicateChecker(Long timeToLive, Long clearPeriod) { | public WxMessageInMemoryDuplicateChecker(Long timeToLive, Long clearPeriod) { | ||||
@@ -8,9 +8,13 @@ public class WxAccessToken implements Serializable { | |||||
private static final long serialVersionUID = 8709719312922168909L; | private static final long serialVersionUID = 8709719312922168909L; | ||||
private String accessToken; | private String accessToken; | ||||
private int expiresIn = -1; | private int expiresIn = -1; | ||||
public static WxAccessToken fromJson(String json) { | |||||
return WxGsonBuilder.create().fromJson(json, WxAccessToken.class); | |||||
} | |||||
public String getAccessToken() { | public String getAccessToken() { | ||||
return accessToken; | return accessToken; | ||||
} | } | ||||
@@ -27,8 +31,4 @@ public class WxAccessToken implements Serializable { | |||||
this.expiresIn = expiresIn; | this.expiresIn = expiresIn; | ||||
} | } | ||||
public static WxAccessToken fromJson(String json) { | |||||
return WxGsonBuilder.create().fromJson(json, WxAccessToken.class); | |||||
} | |||||
} | } |
@@ -9,7 +9,7 @@ public class WxJsapiSignature implements Serializable { | |||||
private static final long serialVersionUID = -1116808193154384804L; | private static final long serialVersionUID = -1116808193154384804L; | ||||
private String appid; | private String appid; | ||||
private String noncestr; | private String noncestr; | ||||
private long timestamp; | private long timestamp; | ||||
@@ -51,11 +51,11 @@ public class WxJsapiSignature implements Serializable { | |||||
} | } | ||||
public String getAppid() { | public String getAppid() { | ||||
return appid; | |||||
return appid; | |||||
} | } | ||||
public void setAppid(String appid) { | public void setAppid(String appid) { | ||||
this.appid = appid; | |||||
this.appid = appid; | |||||
} | } | ||||
} | } |
@@ -1,208 +0,0 @@ | |||||
package me.chanjar.weixin.common.bean; | |||||
import java.io.InputStream; | |||||
import java.io.InputStreamReader; | |||||
import java.io.Serializable; | |||||
import java.nio.charset.StandardCharsets; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import me.chanjar.weixin.common.util.json.WxGsonBuilder; | |||||
/** | |||||
* 企业号菜单 | |||||
* @author Daniel Qian | |||||
* | |||||
*/ | |||||
public class WxMenu implements Serializable { | |||||
private static final long serialVersionUID = -7083914585539687746L; | |||||
private List<WxMenuButton> buttons = new ArrayList<WxMenuButton>(); | |||||
private WxMenuRule matchRule; | |||||
public List<WxMenuButton> getButtons() { | |||||
return buttons; | |||||
} | |||||
public void setButtons(List<WxMenuButton> buttons) { | |||||
this.buttons = buttons; | |||||
} | |||||
public WxMenuRule getMatchRule() { | |||||
return matchRule; | |||||
} | |||||
public void setMatchRule(WxMenuRule matchRule) { | |||||
this.matchRule = matchRule; | |||||
} | |||||
public String toJson() { | |||||
return WxGsonBuilder.create().toJson(this); | |||||
} | |||||
/** | |||||
* 要用 http://mp.weixin.qq.com/wiki/16/ff9b7b85220e1396ffa16794a9d95adc.html 格式来反序列化 | |||||
* 相比 http://mp.weixin.qq.com/wiki/13/43de8269be54a0a6f64413e4dfa94f39.html 的格式,外层多套了一个menu | |||||
*/ | |||||
public static WxMenu fromJson(String json) { | |||||
return WxGsonBuilder.create().fromJson(json, WxMenu.class); | |||||
} | |||||
/** | |||||
* 要用 http://mp.weixin.qq.com/wiki/16/ff9b7b85220e1396ffa16794a9d95adc.html 格式来反序列化 | |||||
* 相比 http://mp.weixin.qq.com/wiki/13/43de8269be54a0a6f64413e4dfa94f39.html 的格式,外层多套了一个menu | |||||
*/ | |||||
public static WxMenu fromJson(InputStream is) { | |||||
return WxGsonBuilder.create().fromJson(new InputStreamReader(is, StandardCharsets.UTF_8), WxMenu.class); | |||||
} | |||||
@Override | |||||
public String toString() { | |||||
return "WxMenu{" + | |||||
"buttons=" + buttons + | |||||
'}'; | |||||
} | |||||
public static class WxMenuButton { | |||||
private String type; | |||||
private String name; | |||||
private String key; | |||||
private String url; | |||||
private List<WxMenuButton> subButtons = new ArrayList<WxMenuButton>(); | |||||
public String getType() { | |||||
return type; | |||||
} | |||||
public void setType(String type) { | |||||
this.type = type; | |||||
} | |||||
public String getName() { | |||||
return name; | |||||
} | |||||
public void setName(String name) { | |||||
this.name = name; | |||||
} | |||||
public String getKey() { | |||||
return key; | |||||
} | |||||
public void setKey(String key) { | |||||
this.key = key; | |||||
} | |||||
public String getUrl() { | |||||
return url; | |||||
} | |||||
public void setUrl(String url) { | |||||
this.url = url; | |||||
} | |||||
public List<WxMenuButton> getSubButtons() { | |||||
return subButtons; | |||||
} | |||||
public void setSubButtons(List<WxMenuButton> subButtons) { | |||||
this.subButtons = subButtons; | |||||
} | |||||
@Override | |||||
public String toString() { | |||||
return "WxMenuButton{" + | |||||
"type='" + type + '\'' + | |||||
", name='" + name + '\'' + | |||||
", key='" + key + '\'' + | |||||
", url='" + url + '\'' + | |||||
", subButtons=" + subButtons + | |||||
'}'; | |||||
} | |||||
} | |||||
public static class WxMenuRule { | |||||
private String tagId; | |||||
private String sex; | |||||
private String country; | |||||
private String province; | |||||
private String city; | |||||
private String clientPlatformType; | |||||
private String language; | |||||
public String getTagId() { | |||||
return tagId; | |||||
} | |||||
public void setTagId(String tagId) { | |||||
this.tagId = tagId; | |||||
} | |||||
public String getSex() { | |||||
return sex; | |||||
} | |||||
public void setSex(String sex) { | |||||
this.sex = sex; | |||||
} | |||||
public String getCountry() { | |||||
return country; | |||||
} | |||||
public void setCountry(String country) { | |||||
this.country = country; | |||||
} | |||||
public String getProvince() { | |||||
return province; | |||||
} | |||||
public void setProvince(String province) { | |||||
this.province = province; | |||||
} | |||||
public String getCity() { | |||||
return city; | |||||
} | |||||
public void setCity(String city) { | |||||
this.city = city; | |||||
} | |||||
public String getClientPlatformType() { | |||||
return clientPlatformType; | |||||
} | |||||
public void setClientPlatformType(String clientPlatformType) { | |||||
this.clientPlatformType = clientPlatformType; | |||||
} | |||||
public String getLanguage() { | |||||
return language; | |||||
} | |||||
public void setLanguage(String language) { | |||||
this.language = language; | |||||
} | |||||
@Override | |||||
public String toString() { | |||||
return "matchrule:{" + | |||||
"tag_id='" + tagId + '\'' + | |||||
", sex='" + sex + '\'' + | |||||
", country" + country + '\'' + | |||||
", province" + province + '\'' + | |||||
", city" + city + '\'' + | |||||
", client_platform_type" + clientPlatformType + '\'' + | |||||
", language" + language + '\'' + | |||||
"}"; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,69 @@ | |||||
package me.chanjar.weixin.common.bean.menu; | |||||
import java.io.InputStream; | |||||
import java.io.InputStreamReader; | |||||
import java.io.Serializable; | |||||
import java.nio.charset.StandardCharsets; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import me.chanjar.weixin.common.bean.menu.WxMenuButton; | |||||
import me.chanjar.weixin.common.util.json.WxGsonBuilder; | |||||
/** | |||||
* 企业号菜单 | |||||
* | |||||
* @author Daniel Qian | |||||
*/ | |||||
public class WxMenu implements Serializable { | |||||
private static final long serialVersionUID = -7083914585539687746L; | |||||
private List<WxMenuButton> buttons = new ArrayList<WxMenuButton>(); | |||||
private WxMenuRule matchRule; | |||||
/** | |||||
* 要用 http://mp.weixin.qq.com/wiki/16/ff9b7b85220e1396ffa16794a9d95adc.html 格式来反序列化 | |||||
* 相比 http://mp.weixin.qq.com/wiki/13/43de8269be54a0a6f64413e4dfa94f39.html 的格式,外层多套了一个menu | |||||
*/ | |||||
public static WxMenu fromJson(String json) { | |||||
return WxGsonBuilder.create().fromJson(json, WxMenu.class); | |||||
} | |||||
/** | |||||
* 要用 http://mp.weixin.qq.com/wiki/16/ff9b7b85220e1396ffa16794a9d95adc.html 格式来反序列化 | |||||
* 相比 http://mp.weixin.qq.com/wiki/13/43de8269be54a0a6f64413e4dfa94f39.html 的格式,外层多套了一个menu | |||||
*/ | |||||
public static WxMenu fromJson(InputStream is) { | |||||
return WxGsonBuilder.create().fromJson(new InputStreamReader(is, StandardCharsets.UTF_8), WxMenu.class); | |||||
} | |||||
public List<WxMenuButton> getButtons() { | |||||
return buttons; | |||||
} | |||||
public void setButtons(List<WxMenuButton> buttons) { | |||||
this.buttons = buttons; | |||||
} | |||||
public WxMenuRule getMatchRule() { | |||||
return matchRule; | |||||
} | |||||
public void setMatchRule(WxMenuRule matchRule) { | |||||
this.matchRule = matchRule; | |||||
} | |||||
public String toJson() { | |||||
return WxGsonBuilder.create().toJson(this); | |||||
} | |||||
@Override | |||||
public String toString() { | |||||
return "WxMenu{" + | |||||
"buttons=" + buttons + | |||||
'}'; | |||||
} | |||||
} |
@@ -0,0 +1,72 @@ | |||||
package me.chanjar.weixin.common.bean.menu; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import org.apache.commons.lang3.builder.ToStringBuilder; | |||||
import org.apache.commons.lang3.builder.ToStringStyle; | |||||
public class WxMenuButton { | |||||
private String type; | |||||
private String name; | |||||
private String key; | |||||
private String url; | |||||
private String mediaId; | |||||
private List<WxMenuButton> subButtons = new ArrayList<WxMenuButton>(); | |||||
@Override | |||||
public String toString() { | |||||
return ToStringBuilder.reflectionToString(this, | |||||
ToStringStyle.JSON_STYLE); | |||||
} | |||||
public String getType() { | |||||
return type; | |||||
} | |||||
public void setType(String type) { | |||||
this.type = type; | |||||
} | |||||
public String getName() { | |||||
return name; | |||||
} | |||||
public void setName(String name) { | |||||
this.name = name; | |||||
} | |||||
public String getKey() { | |||||
return key; | |||||
} | |||||
public void setKey(String key) { | |||||
this.key = key; | |||||
} | |||||
public String getUrl() { | |||||
return url; | |||||
} | |||||
public void setUrl(String url) { | |||||
this.url = url; | |||||
} | |||||
public List<WxMenuButton> getSubButtons() { | |||||
return subButtons; | |||||
} | |||||
public void setSubButtons(List<WxMenuButton> subButtons) { | |||||
this.subButtons = subButtons; | |||||
} | |||||
public String getMediaId() { | |||||
return mediaId; | |||||
} | |||||
public void setMediaId(String mediaId) { | |||||
this.mediaId = mediaId; | |||||
} | |||||
} |
@@ -0,0 +1,75 @@ | |||||
package me.chanjar.weixin.common.bean.menu; | |||||
import org.apache.commons.lang3.builder.ToStringBuilder; | |||||
import org.apache.commons.lang3.builder.ToStringStyle; | |||||
public class WxMenuRule { | |||||
private String tagId; | |||||
private String sex; | |||||
private String country; | |||||
private String province; | |||||
private String city; | |||||
private String clientPlatformType; | |||||
private String language; | |||||
public String getTagId() { | |||||
return tagId; | |||||
} | |||||
public void setTagId(String tagId) { | |||||
this.tagId = tagId; | |||||
} | |||||
public String getSex() { | |||||
return sex; | |||||
} | |||||
public void setSex(String sex) { | |||||
this.sex = sex; | |||||
} | |||||
public String getCountry() { | |||||
return country; | |||||
} | |||||
public void setCountry(String country) { | |||||
this.country = country; | |||||
} | |||||
public String getProvince() { | |||||
return province; | |||||
} | |||||
public void setProvince(String province) { | |||||
this.province = province; | |||||
} | |||||
public String getCity() { | |||||
return city; | |||||
} | |||||
public void setCity(String city) { | |||||
this.city = city; | |||||
} | |||||
public String getClientPlatformType() { | |||||
return clientPlatformType; | |||||
} | |||||
public void setClientPlatformType(String clientPlatformType) { | |||||
this.clientPlatformType = clientPlatformType; | |||||
} | |||||
public String getLanguage() { | |||||
return language; | |||||
} | |||||
public void setLanguage(String language) { | |||||
this.language = language; | |||||
} | |||||
@Override | |||||
public String toString() { | |||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); | |||||
} | |||||
} |
@@ -6,19 +6,28 @@ import java.io.Serializable; | |||||
/** | /** | ||||
* 微信错误码说明,请阅读: <a href="http://mp.weixin.qq.com/wiki/10/6380dc743053a91c544ffd2b7c959166.html">全局返回码说明</a> | * 微信错误码说明,请阅读: <a href="http://mp.weixin.qq.com/wiki/10/6380dc743053a91c544ffd2b7c959166.html">全局返回码说明</a> | ||||
* @author Daniel Qian | |||||
* | * | ||||
* @author Daniel Qian | |||||
*/ | */ | ||||
public class WxError implements Serializable { | public class WxError implements Serializable { | ||||
private static final long serialVersionUID = 7869786563361406291L; | private static final long serialVersionUID = 7869786563361406291L; | ||||
private int errorCode; | private int errorCode; | ||||
private String errorMsg; | private String errorMsg; | ||||
private String json; | private String json; | ||||
public static WxError fromJson(String json) { | |||||
WxError error = WxGsonBuilder.create().fromJson(json, WxError.class); | |||||
return error; | |||||
} | |||||
public static Builder newBuilder() { | |||||
return new Builder(); | |||||
} | |||||
public int getErrorCode() { | public int getErrorCode() { | ||||
return errorCode; | return errorCode; | ||||
} | } | ||||
@@ -43,40 +52,34 @@ public class WxError implements Serializable { | |||||
this.json = json; | this.json = json; | ||||
} | } | ||||
public static WxError fromJson(String json) { | |||||
WxError error = WxGsonBuilder.create().fromJson(json, WxError.class); | |||||
return error; | |||||
} | |||||
@Override | @Override | ||||
public String toString() { | public String toString() { | ||||
return "微信错误: errcode=" + errorCode + ", errmsg=" + errorMsg + "\njson:" + json; | |||||
if (json != null) { | |||||
return json; | |||||
} | |||||
return "错误: Code=" + errorCode + ", Msg=" + errorMsg; | |||||
} | } | ||||
public static Builder newBuilder(){ | |||||
return new Builder(); | |||||
} | |||||
public static class Builder{ | |||||
public static class Builder { | |||||
private int errorCode; | private int errorCode; | ||||
private String errorMsg; | private String errorMsg; | ||||
public Builder setErrorCode(int errorCode) { | public Builder setErrorCode(int errorCode) { | ||||
this.errorCode = errorCode; | this.errorCode = errorCode; | ||||
return this; | return this; | ||||
} | } | ||||
public Builder setErrorMsg(String errorMsg) { | public Builder setErrorMsg(String errorMsg) { | ||||
this.errorMsg = errorMsg; | this.errorMsg = errorMsg; | ||||
return this; | return this; | ||||
} | } | ||||
public WxError build(){ | |||||
public WxError build() { | |||||
WxError wxError = new WxError(); | WxError wxError = new WxError(); | ||||
wxError.setErrorCode(this.errorCode); | wxError.setErrorCode(this.errorCode); | ||||
wxError.setErrorMsg(this.errorMsg); | wxError.setErrorMsg(this.errorMsg); | ||||
return wxError; | return wxError; | ||||
} | } | ||||
} | } | ||||
} | } |
@@ -6,12 +6,16 @@ import java.io.Serializable; | |||||
public class WxMediaUploadResult implements Serializable { | public class WxMediaUploadResult implements Serializable { | ||||
private static final long serialVersionUID = 330834334738622341L; | private static final long serialVersionUID = 330834334738622341L; | ||||
private String type; | private String type; | ||||
private String mediaId; | private String mediaId; | ||||
private String thumbMediaId; | private String thumbMediaId; | ||||
private long createdAt; | private long createdAt; | ||||
public static WxMediaUploadResult fromJson(String json) { | |||||
return WxGsonBuilder.create().fromJson(json, WxMediaUploadResult.class); | |||||
} | |||||
public String getType() { | public String getType() { | ||||
return type; | return type; | ||||
} | } | ||||
@@ -44,14 +48,10 @@ public class WxMediaUploadResult implements Serializable { | |||||
this.thumbMediaId = thumbMediaId; | this.thumbMediaId = thumbMediaId; | ||||
} | } | ||||
public static WxMediaUploadResult fromJson(String json) { | |||||
return WxGsonBuilder.create().fromJson(json, WxMediaUploadResult.class); | |||||
} | |||||
@Override | @Override | ||||
public String toString() { | public String toString() { | ||||
return "WxUploadResult [type=" + type + ", media_id=" + mediaId + ", thumb_media_id=" + thumbMediaId | return "WxUploadResult [type=" + type + ", media_id=" + mediaId + ", thumb_media_id=" + thumbMediaId | ||||
+ ", created_at=" + createdAt + "]"; | |||||
+ ", created_at=" + createdAt + "]"; | |||||
} | } | ||||
} | } |
@@ -5,7 +5,7 @@ import me.chanjar.weixin.common.bean.result.WxError; | |||||
public class WxErrorException extends Exception { | public class WxErrorException extends Exception { | ||||
private static final long serialVersionUID = -6357149550353160810L; | private static final long serialVersionUID = -6357149550353160810L; | ||||
private WxError error; | private WxError error; | ||||
public WxErrorException(WxError error) { | public WxErrorException(WxError error) { | ||||
@@ -17,5 +17,5 @@ public class WxErrorException extends Exception { | |||||
return error; | return error; | ||||
} | } | ||||
} | } |
@@ -26,6 +26,6 @@ package me.chanjar.weixin.common.session; | |||||
public class Constants { | public class Constants { | ||||
public static final String Package = "me.chanjar.weixin.common.session"; | |||||
public static final String Package = "me.chanjar.weixin.common.session"; | |||||
} | } |
@@ -8,6 +8,11 @@ public interface InternalSession { | |||||
*/ | */ | ||||
WxSession getSession(); | WxSession getSession(); | ||||
/** | |||||
* Return the <code>isValid</code> flag for this session. | |||||
*/ | |||||
boolean isValid(); | |||||
/** | /** | ||||
* Set the <code>isValid</code> flag for this session. | * Set the <code>isValid</code> flag for this session. | ||||
* | * | ||||
@@ -15,11 +20,6 @@ public interface InternalSession { | |||||
*/ | */ | ||||
void setValid(boolean isValid); | void setValid(boolean isValid); | ||||
/** | |||||
* Return the <code>isValid</code> flag for this session. | |||||
*/ | |||||
boolean isValid(); | |||||
/** | /** | ||||
* Return the session identifier for this session. | * Return the session identifier for this session. | ||||
*/ | */ | ||||
@@ -7,11 +7,10 @@ public interface InternalSessionManager { | |||||
* specified session id (if any); otherwise return <code>null</code>. | * specified session id (if any); otherwise return <code>null</code>. | ||||
* | * | ||||
* @param id The session id for the session to be returned | * @param id The session id for the session to be returned | ||||
* | |||||
* @exception IllegalStateException if a new session cannot be | |||||
* instantiated for any reason | |||||
* @exception java.io.IOException if an input/output error occurs while | |||||
* processing this request | |||||
* @throws IllegalStateException if a new session cannot be | |||||
* instantiated for any reason | |||||
* @throws java.io.IOException if an input/output error occurs while | |||||
* processing this request | |||||
*/ | */ | ||||
InternalSession findSession(String id); | InternalSession findSession(String id); | ||||
@@ -23,10 +22,10 @@ public interface InternalSessionManager { | |||||
* <code>null</code>. | * <code>null</code>. | ||||
* | * | ||||
* @param sessionId The session id which should be used to create the | * @param sessionId The session id which should be used to create the | ||||
* new session; if <code>null</code>, a new session id will be | |||||
* generated | |||||
* @exception IllegalStateException if a new session cannot be | |||||
* instantiated for any reason | |||||
* new session; if <code>null</code>, a new session id will be | |||||
* generated | |||||
* @throws IllegalStateException if a new session cannot be | |||||
* instantiated for any reason | |||||
*/ | */ | ||||
InternalSession createSession(String sessionId); | InternalSession createSession(String sessionId); | ||||
@@ -40,8 +39,8 @@ public interface InternalSessionManager { | |||||
/** | /** | ||||
* Remove this Session from the active Sessions for this Manager. | * Remove this Session from the active Sessions for this Manager. | ||||
* | * | ||||
* @param session Session to be removed | |||||
* @param update Should the expiration statistics be updated | |||||
* @param session Session to be removed | |||||
* @param update Should the expiration statistics be updated | |||||
*/ | */ | ||||
void remove(InternalSession session, boolean update); | void remove(InternalSession session, boolean update); | ||||
@@ -59,6 +58,7 @@ public interface InternalSessionManager { | |||||
* @return number of sessions active | * @return number of sessions active | ||||
*/ | */ | ||||
int getActiveSessions(); | int getActiveSessions(); | ||||
/** | /** | ||||
* Get a session from the recycled ones or create a new empty one. | * Get a session from the recycled ones or create a new empty one. | ||||
* The PersistentManager manager does not need to create session data | * The PersistentManager manager does not need to create session data | ||||
@@ -88,6 +88,7 @@ public interface InternalSessionManager { | |||||
* 要和{@link #setBackgroundProcessorDelay(int)}联合起来看 | * 要和{@link #setBackgroundProcessorDelay(int)}联合起来看 | ||||
* 如果把这个数字设置为6(默认),那么就是说manager要等待 6 * backgroundProcessorDay的时间才会清理过期session | * 如果把这个数字设置为6(默认),那么就是说manager要等待 6 * backgroundProcessorDay的时间才会清理过期session | ||||
* </pre> | * </pre> | ||||
* | |||||
* @param processExpiresFrequency the new manager checks frequency | * @param processExpiresFrequency the new manager checks frequency | ||||
*/ | */ | ||||
void setProcessExpiresFrequency(int processExpiresFrequency); | void setProcessExpiresFrequency(int processExpiresFrequency); | ||||
@@ -97,6 +98,7 @@ public interface InternalSessionManager { | |||||
* Set the manager background processor delay | * Set the manager background processor delay | ||||
* 设置manager sleep几秒,尝试执行一次background操作(清理过期session) | * 设置manager sleep几秒,尝试执行一次background操作(清理过期session) | ||||
* </pre> | * </pre> | ||||
* | |||||
* @param backgroundProcessorDelay | * @param backgroundProcessorDelay | ||||
*/ | */ | ||||
void setBackgroundProcessorDelay(int backgroundProcessorDelay); | void setBackgroundProcessorDelay(int backgroundProcessorDelay); | ||||
@@ -106,6 +108,7 @@ public interface InternalSessionManager { | |||||
* Set the maximum number of active Sessions allowed, or -1 for | * Set the maximum number of active Sessions allowed, or -1 for | ||||
* no limit. | * no limit. | ||||
* 设置最大活跃session数,默认无限 | * 设置最大活跃session数,默认无限 | ||||
* | |||||
* @param max The new maximum number of sessions | * @param max The new maximum number of sessions | ||||
*/ | */ | ||||
void setMaxActiveSessions(int max); | void setMaxActiveSessions(int max); | ||||
@@ -12,7 +12,6 @@ | |||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
# See the License for the specific language governing permissions and | # See the License for the specific language governing permissions and | ||||
# limitations under the License. | # limitations under the License. | ||||
applicationSession.session.ise=invalid session state | applicationSession.session.ise=invalid session state | ||||
applicationSession.value.iae=null value | applicationSession.value.iae=null value | ||||
fileStore.saving=Saving Session {0} to file {1} | fileStore.saving=Saving Session {0} to file {1} | ||||
@@ -12,17 +12,68 @@ public class StandardSession implements WxSession, InternalSession { | |||||
* The string manager for this package. | * The string manager for this package. | ||||
*/ | */ | ||||
protected static final StringManager sm = | protected static final StringManager sm = | ||||
StringManager.getManager(Constants.Package); | |||||
StringManager.getManager(Constants.Package); | |||||
/** | |||||
* Type array. | |||||
*/ | |||||
protected static final String EMPTY_ARRAY[] = new String[0]; | |||||
// ------------------------------ WxSession | // ------------------------------ WxSession | ||||
protected Map<String, Object> attributes = new ConcurrentHashMap<String, Object>(); | protected Map<String, Object> attributes = new ConcurrentHashMap<String, Object>(); | ||||
/** | |||||
* The session identifier of this Session. | |||||
*/ | |||||
protected String id = null; | |||||
/** | |||||
* Flag indicating whether this session is valid or not. | |||||
*/ | |||||
protected volatile boolean isValid = false; | |||||
/** | |||||
* We are currently processing a session expiration, so bypass | |||||
* certain IllegalStateException tests. NOTE: This value is not | |||||
* included in the serialized version of this object. | |||||
*/ | |||||
protected transient volatile boolean expiring = false; | |||||
/** | |||||
* The Manager with which this Session is associated. | |||||
*/ | |||||
protected transient InternalSessionManager manager = null; | |||||
// ------------------------------ InternalSession | |||||
/** | |||||
* The time this session was created, in milliseconds since midnight, | |||||
* January 1, 1970 GMT. | |||||
*/ | |||||
protected long creationTime = 0L; | |||||
/** | |||||
* The current accessed time for this session. | |||||
*/ | |||||
protected volatile long thisAccessedTime = creationTime; | |||||
/** | |||||
* The default maximum inactive interval for Sessions created by | |||||
* this Manager. | |||||
*/ | |||||
protected int maxInactiveInterval = 30 * 60; | |||||
/** | |||||
* The facade associated with this session. NOTE: This value is not | |||||
* included in the serialized version of this object. | |||||
*/ | |||||
protected transient StandardSessionFacade facade = null; | |||||
/** | |||||
* The access count for this session. | |||||
*/ | |||||
protected transient AtomicInteger accessCount = null; | |||||
public StandardSession(InternalSessionManager manager) { | |||||
this.manager = manager; | |||||
this.accessCount = new AtomicInteger(); | |||||
} | |||||
@Override | @Override | ||||
public Object getAttribute(String name) { | public Object getAttribute(String name) { | ||||
if (!isValidInternal()) | if (!isValidInternal()) | ||||
throw new IllegalStateException | throw new IllegalStateException | ||||
(sm.getString("sessionImpl.getAttribute.ise")); | |||||
(sm.getString("sessionImpl.getAttribute.ise")); | |||||
if (name == null) return null; | if (name == null) return null; | ||||
@@ -33,7 +84,7 @@ public class StandardSession implements WxSession, InternalSession { | |||||
public Enumeration<String> getAttributeNames() { | public Enumeration<String> getAttributeNames() { | ||||
if (!isValidInternal()) | if (!isValidInternal()) | ||||
throw new IllegalStateException | throw new IllegalStateException | ||||
(sm.getString("sessionImpl.getAttributeNames.ise")); | |||||
(sm.getString("sessionImpl.getAttributeNames.ise")); | |||||
Set<String> names = new HashSet<String>(); | Set<String> names = new HashSet<String>(); | ||||
names.addAll(attributes.keySet()); | names.addAll(attributes.keySet()); | ||||
@@ -45,7 +96,7 @@ public class StandardSession implements WxSession, InternalSession { | |||||
// Name cannot be null | // Name cannot be null | ||||
if (name == null) | if (name == null) | ||||
throw new IllegalArgumentException | throw new IllegalArgumentException | ||||
(sm.getString("sessionImpl.setAttribute.namenull")); | |||||
(sm.getString("sessionImpl.setAttribute.namenull")); | |||||
// Null value is the same as removeAttribute() | // Null value is the same as removeAttribute() | ||||
if (value == null) { | if (value == null) { | ||||
@@ -56,97 +107,32 @@ public class StandardSession implements WxSession, InternalSession { | |||||
// Validate our current state | // Validate our current state | ||||
if (!isValidInternal()) | if (!isValidInternal()) | ||||
throw new IllegalStateException(sm.getString( | throw new IllegalStateException(sm.getString( | ||||
"sessionImpl.setAttribute.ise", getIdInternal())); | |||||
"sessionImpl.setAttribute.ise", getIdInternal())); | |||||
attributes.put(name, value); | attributes.put(name, value); | ||||
} | } | ||||
@Override | @Override | ||||
public void removeAttribute(String name) { | public void removeAttribute(String name) { | ||||
removeAttributeInternal(name); | removeAttributeInternal(name); | ||||
} | } | ||||
@Override | @Override | ||||
public void invalidate() { | public void invalidate() { | ||||
if (!isValidInternal()) | if (!isValidInternal()) | ||||
throw new IllegalStateException | throw new IllegalStateException | ||||
(sm.getString("sessionImpl.invalidate.ise")); | |||||
(sm.getString("sessionImpl.invalidate.ise")); | |||||
// Cause this session to expire | // Cause this session to expire | ||||
expire(); | expire(); | ||||
} | } | ||||
// ------------------------------ InternalSession | |||||
/** | |||||
* The session identifier of this Session. | |||||
*/ | |||||
protected String id = null; | |||||
/** | |||||
* Flag indicating whether this session is valid or not. | |||||
*/ | |||||
protected volatile boolean isValid = false; | |||||
/** | |||||
* We are currently processing a session expiration, so bypass | |||||
* certain IllegalStateException tests. NOTE: This value is not | |||||
* included in the serialized version of this object. | |||||
*/ | |||||
protected transient volatile boolean expiring = false; | |||||
/** | |||||
* The Manager with which this Session is associated. | |||||
*/ | |||||
protected transient InternalSessionManager manager = null; | |||||
/** | |||||
* Type array. | |||||
*/ | |||||
protected static final String EMPTY_ARRAY[] = new String[0]; | |||||
/** | |||||
* The time this session was created, in milliseconds since midnight, | |||||
* January 1, 1970 GMT. | |||||
*/ | |||||
protected long creationTime = 0L; | |||||
/** | |||||
* The current accessed time for this session. | |||||
*/ | |||||
protected volatile long thisAccessedTime = creationTime; | |||||
/** | |||||
* The default maximum inactive interval for Sessions created by | |||||
* this Manager. | |||||
*/ | |||||
protected int maxInactiveInterval = 30 * 60; | |||||
/** | |||||
* The facade associated with this session. NOTE: This value is not | |||||
* included in the serialized version of this object. | |||||
*/ | |||||
protected transient StandardSessionFacade facade = null; | |||||
/** | |||||
* The access count for this session. | |||||
*/ | |||||
protected transient AtomicInteger accessCount = null; | |||||
public StandardSession(InternalSessionManager manager) { | |||||
this.manager = manager; | |||||
this.accessCount = new AtomicInteger(); | |||||
} | |||||
@Override | @Override | ||||
public WxSession getSession() { | public WxSession getSession() { | ||||
if (facade == null){ | |||||
if (facade == null) { | |||||
facade = new StandardSessionFacade(this); | facade = new StandardSessionFacade(this); | ||||
} | } | ||||
return (facade); | return (facade); | ||||
@@ -161,16 +147,6 @@ public class StandardSession implements WxSession, InternalSession { | |||||
return this.isValid; | return this.isValid; | ||||
} | } | ||||
/** | |||||
* Set the <code>isValid</code> flag for this session. | |||||
* | |||||
* @param isValid The new value for the <code>isValid</code> flag | |||||
*/ | |||||
@Override | |||||
public void setValid(boolean isValid) { | |||||
this.isValid = isValid; | |||||
} | |||||
@Override | @Override | ||||
public boolean isValid() { | public boolean isValid() { | ||||
if (!this.isValid) { | if (!this.isValid) { | ||||
@@ -197,6 +173,16 @@ public class StandardSession implements WxSession, InternalSession { | |||||
return this.isValid; | return this.isValid; | ||||
} | } | ||||
/** | |||||
* Set the <code>isValid</code> flag for this session. | |||||
* | |||||
* @param isValid The new value for the <code>isValid</code> flag | |||||
*/ | |||||
@Override | |||||
public void setValid(boolean isValid) { | |||||
this.isValid = isValid; | |||||
} | |||||
@Override | @Override | ||||
public String getIdInternal() { | public String getIdInternal() { | ||||
return (this.id); | return (this.id); | ||||
@@ -13,60 +13,26 @@ import java.util.concurrent.atomic.AtomicBoolean; | |||||
*/ | */ | ||||
public class StandardSessionManager implements WxSessionManager, InternalSessionManager { | public class StandardSessionManager implements WxSessionManager, InternalSessionManager { | ||||
protected final Logger log = LoggerFactory.getLogger(StandardSessionManager.class); | |||||
protected static final StringManager sm = | protected static final StringManager sm = | ||||
StringManager.getManager(Constants.Package); | |||||
StringManager.getManager(Constants.Package); | |||||
/** | /** | ||||
* The set of currently active Sessions for this Manager, keyed by | |||||
* session identifier. | |||||
* The descriptive name of this Manager implementation (for logging). | |||||
*/ | */ | ||||
protected Map<String, InternalSession> sessions = new ConcurrentHashMap<String, InternalSession>(); | |||||
@Override | |||||
public WxSession getSession(String sessionId) { | |||||
return getSession(sessionId, true); | |||||
} | |||||
@Override | |||||
public WxSession getSession(String sessionId, boolean create) { | |||||
if (sessionId == null) { | |||||
throw new IllegalStateException | |||||
(sm.getString("sessionManagerImpl.getSession.ise")); | |||||
} | |||||
InternalSession session = findSession(sessionId); | |||||
if ((session != null) && !session.isValid()) { | |||||
session = null; | |||||
} | |||||
if (session != null) { | |||||
session.access(); | |||||
return session.getSession(); | |||||
} | |||||
// Create a new session if requested and the response is not committed | |||||
if (!create) { | |||||
return (null); | |||||
} | |||||
session = createSession(sessionId); | |||||
if (session == null) { | |||||
return null; | |||||
} | |||||
session.access(); | |||||
return session.getSession(); | |||||
} | |||||
private static final String name = "SessionManagerImpl"; | |||||
protected final Logger log = LoggerFactory.getLogger(StandardSessionManager.class); | |||||
private final Object maxActiveUpdateLock = new Object(); | |||||
/** | |||||
* 后台清理线程是否已经开启 | |||||
*/ | |||||
private final AtomicBoolean backgroundProcessStarted = new AtomicBoolean(false); | |||||
// -------------------------------------- InternalSessionManager | // -------------------------------------- InternalSessionManager | ||||
/** | /** | ||||
* The descriptive name of this Manager implementation (for logging). | |||||
* The set of currently active Sessions for this Manager, keyed by | |||||
* session identifier. | |||||
*/ | */ | ||||
private static final String name = "SessionManagerImpl"; | |||||
protected Map<String, InternalSession> sessions = new ConcurrentHashMap<String, InternalSession>(); | |||||
/** | /** | ||||
* The maximum number of active Sessions allowed, or -1 for no limit. | * The maximum number of active Sessions allowed, or -1 for no limit. | ||||
*/ | */ | ||||
@@ -84,22 +50,13 @@ public class StandardSessionManager implements WxSessionManager, InternalSession | |||||
protected int maxInactiveInterval = 30 * 60; | protected int maxInactiveInterval = 30 * 60; | ||||
// Number of sessions created by this manager | // Number of sessions created by this manager | ||||
protected long sessionCounter=0; | |||||
protected volatile int maxActive=0; | |||||
private final Object maxActiveUpdateLock = new Object(); | |||||
protected long sessionCounter = 0; | |||||
protected volatile int maxActive = 0; | |||||
/** | /** | ||||
* Processing time during session expiration. | * Processing time during session expiration. | ||||
*/ | */ | ||||
protected long processingTime = 0; | protected long processingTime = 0; | ||||
/** | |||||
* Iteration count for background processing. | |||||
*/ | |||||
private int count = 0; | |||||
/** | /** | ||||
* Frequency of the session expiration, and related manager operations. | * Frequency of the session expiration, and related manager operations. | ||||
* Manager operations will be done once for the specified amount of | * Manager operations will be done once for the specified amount of | ||||
@@ -107,16 +64,50 @@ public class StandardSessionManager implements WxSessionManager, InternalSession | |||||
* checks will occur). | * checks will occur). | ||||
*/ | */ | ||||
protected int processExpiresFrequency = 6; | protected int processExpiresFrequency = 6; | ||||
/** | /** | ||||
* background processor delay in seconds | * background processor delay in seconds | ||||
*/ | */ | ||||
protected int backgroundProcessorDelay = 10; | protected int backgroundProcessorDelay = 10; | ||||
/** | /** | ||||
* 后台清理线程是否已经开启 | |||||
* Iteration count for background processing. | |||||
*/ | */ | ||||
private final AtomicBoolean backgroundProcessStarted = new AtomicBoolean(false); | |||||
private int count = 0; | |||||
@Override | |||||
public WxSession getSession(String sessionId) { | |||||
return getSession(sessionId, true); | |||||
} | |||||
@Override | |||||
public WxSession getSession(String sessionId, boolean create) { | |||||
if (sessionId == null) { | |||||
throw new IllegalStateException | |||||
(sm.getString("sessionManagerImpl.getSession.ise")); | |||||
} | |||||
InternalSession session = findSession(sessionId); | |||||
if ((session != null) && !session.isValid()) { | |||||
session = null; | |||||
} | |||||
if (session != null) { | |||||
session.access(); | |||||
return session.getSession(); | |||||
} | |||||
// Create a new session if requested and the response is not committed | |||||
if (!create) { | |||||
return (null); | |||||
} | |||||
session = createSession(sessionId); | |||||
if (session == null) { | |||||
return null; | |||||
} | |||||
session.access(); | |||||
return session.getSession(); | |||||
} | |||||
@Override | @Override | ||||
public void remove(InternalSession session) { | public void remove(InternalSession session) { | ||||
@@ -131,7 +122,6 @@ public class StandardSessionManager implements WxSessionManager, InternalSession | |||||
} | } | ||||
@Override | @Override | ||||
public InternalSession findSession(String id) { | public InternalSession findSession(String id) { | ||||
@@ -145,15 +135,15 @@ public class StandardSessionManager implements WxSessionManager, InternalSession | |||||
public InternalSession createSession(String sessionId) { | public InternalSession createSession(String sessionId) { | ||||
if (sessionId == null) { | if (sessionId == null) { | ||||
throw new IllegalStateException | throw new IllegalStateException | ||||
(sm.getString("sessionManagerImpl.createSession.ise")); | |||||
(sm.getString("sessionManagerImpl.createSession.ise")); | |||||
} | } | ||||
if ((maxActiveSessions >= 0) && | if ((maxActiveSessions >= 0) && | ||||
(getActiveSessions() >= maxActiveSessions)) { | |||||
(getActiveSessions() >= maxActiveSessions)) { | |||||
rejectedSessions++; | rejectedSessions++; | ||||
throw new TooManyActiveSessionsException( | throw new TooManyActiveSessionsException( | ||||
sm.getString("sessionManagerImpl.createSession.tmase"), | |||||
maxActiveSessions); | |||||
sm.getString("sessionManagerImpl.createSession.tmase"), | |||||
maxActiveSessions); | |||||
} | } | ||||
// Recycle or create a Session instance | // Recycle or create a Session instance | ||||
@@ -216,14 +206,14 @@ public class StandardSessionManager implements WxSessionManager, InternalSession | |||||
sessions.put(session.getIdInternal(), session); | sessions.put(session.getIdInternal(), session); | ||||
int size = getActiveSessions(); | int size = getActiveSessions(); | ||||
if( size > maxActive ) { | |||||
synchronized(maxActiveUpdateLock) { | |||||
if( size > maxActive ) { | |||||
if (size > maxActive) { | |||||
synchronized (maxActiveUpdateLock) { | |||||
if (size > maxActive) { | |||||
maxActive = size; | maxActive = size; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
@@ -251,19 +241,19 @@ public class StandardSessionManager implements WxSessionManager, InternalSession | |||||
long timeNow = System.currentTimeMillis(); | long timeNow = System.currentTimeMillis(); | ||||
InternalSession sessions[] = findSessions(); | InternalSession sessions[] = findSessions(); | ||||
int expireHere = 0 ; | |||||
int expireHere = 0; | |||||
if(log.isDebugEnabled()) | |||||
if (log.isDebugEnabled()) | |||||
log.debug("Start expire sessions {} at {} sessioncount {}", getName(), timeNow, sessions.length); | log.debug("Start expire sessions {} at {} sessioncount {}", getName(), timeNow, sessions.length); | ||||
for (int i = 0; i < sessions.length; i++) { | for (int i = 0; i < sessions.length; i++) { | ||||
if (sessions[i]!=null && !sessions[i].isValid()) { | |||||
if (sessions[i] != null && !sessions[i].isValid()) { | |||||
expireHere++; | expireHere++; | ||||
} | } | ||||
} | } | ||||
long timeEnd = System.currentTimeMillis(); | long timeEnd = System.currentTimeMillis(); | ||||
if(log.isDebugEnabled()) | |||||
if (log.isDebugEnabled()) | |||||
log.debug("End expire sessions {} processingTime {} expired sessions: {}", getName(), timeEnd - timeNow, expireHere); | log.debug("End expire sessions {} processingTime {} expired sessions: {}", getName(), timeEnd - timeNow, expireHere); | ||||
processingTime += ( timeEnd - timeNow ); | |||||
processingTime += (timeEnd - timeNow); | |||||
} | } | ||||
@@ -21,37 +21,34 @@ package me.chanjar.weixin.common.session; | |||||
* reached and the server is refusing to create any new sessions. | * reached and the server is refusing to create any new sessions. | ||||
*/ | */ | ||||
public class TooManyActiveSessionsException | public class TooManyActiveSessionsException | ||||
extends IllegalStateException | |||||
{ | |||||
private static final long serialVersionUID = 1L; | |||||
extends IllegalStateException { | |||||
private static final long serialVersionUID = 1L; | |||||
/** | |||||
* The maximum number of active sessions the server will tolerate. | |||||
*/ | |||||
private final int maxActiveSessions; | |||||
/** | |||||
* The maximum number of active sessions the server will tolerate. | |||||
*/ | |||||
private final int maxActiveSessions; | |||||
/** | |||||
* Creates a new TooManyActiveSessionsException. | |||||
* | |||||
* @param message A description for the exception. | |||||
* @param maxActive The maximum number of active sessions allowed by the | |||||
* session manager. | |||||
*/ | |||||
public TooManyActiveSessionsException(String message, | |||||
int maxActive) | |||||
{ | |||||
super(message); | |||||
maxActiveSessions = maxActive; | |||||
} | |||||
/** | |||||
* Gets the maximum number of sessions allowed by the session manager. | |||||
* | |||||
* @return The maximum number of sessions allowed by the session manager. | |||||
*/ | |||||
public int getMaxActiveSessions() | |||||
{ | |||||
return maxActiveSessions; | |||||
} | |||||
/** | |||||
* Creates a new TooManyActiveSessionsException. | |||||
* | |||||
* @param message A description for the exception. | |||||
* @param maxActive The maximum number of active sessions allowed by the | |||||
* session manager. | |||||
*/ | |||||
public TooManyActiveSessionsException(String message, | |||||
int maxActive) { | |||||
super(message); | |||||
maxActiveSessions = maxActive; | |||||
} | |||||
/** | |||||
* Gets the maximum number of sessions allowed by the session manager. | |||||
* | |||||
* @return The maximum number of sessions allowed by the session manager. | |||||
*/ | |||||
public int getMaxActiveSessions() { | |||||
return maxActiveSessions; | |||||
} | |||||
} | } |
@@ -7,7 +7,7 @@ public class StringUtils { | |||||
/** | /** | ||||
* <p>Checks if a CharSequence is whitespace, empty ("") or null.</p> | * <p>Checks if a CharSequence is whitespace, empty ("") or null.</p> | ||||
* | |||||
* <p> | |||||
* <pre> | * <pre> | ||||
* StringUtils.isBlank(null) = true | * StringUtils.isBlank(null) = true | ||||
* StringUtils.isBlank("") = true | * StringUtils.isBlank("") = true | ||||
@@ -16,9 +16,8 @@ public class StringUtils { | |||||
* StringUtils.isBlank(" bob ") = false | * StringUtils.isBlank(" bob ") = false | ||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param cs the CharSequence to check, may be null | |||||
* @param cs the CharSequence to check, may be null | |||||
* @return {@code true} if the CharSequence is null, empty or whitespace | * @return {@code true} if the CharSequence is null, empty or whitespace | ||||
* @since 2.0 | |||||
* @since 3.0 Changed signature from isBlank(String) to isBlank(CharSequence) | * @since 3.0 Changed signature from isBlank(String) to isBlank(CharSequence) | ||||
*/ | */ | ||||
public static boolean isBlank(CharSequence cs) { | public static boolean isBlank(CharSequence cs) { | ||||
@@ -36,7 +35,7 @@ public class StringUtils { | |||||
/** | /** | ||||
* <p>Checks if a CharSequence is not empty (""), not null and not whitespace only.</p> | * <p>Checks if a CharSequence is not empty (""), not null and not whitespace only.</p> | ||||
* | |||||
* <p> | |||||
* <pre> | * <pre> | ||||
* StringUtils.isNotBlank(null) = false | * StringUtils.isNotBlank(null) = false | ||||
* StringUtils.isNotBlank("") = false | * StringUtils.isNotBlank("") = false | ||||
@@ -45,10 +44,9 @@ public class StringUtils { | |||||
* StringUtils.isNotBlank(" bob ") = true | * StringUtils.isNotBlank(" bob ") = true | ||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param cs the CharSequence to check, may be null | |||||
* @param cs the CharSequence to check, may be null | |||||
* @return {@code true} if the CharSequence is | * @return {@code true} if the CharSequence is | ||||
* not empty and not null and not whitespace | |||||
* @since 2.0 | |||||
* not empty and not null and not whitespace | |||||
* @since 3.0 Changed signature from isNotBlank(String) to isNotBlank(CharSequence) | * @since 3.0 Changed signature from isNotBlank(String) to isNotBlank(CharSequence) | ||||
*/ | */ | ||||
public static boolean isNotBlank(CharSequence cs) { | public static boolean isNotBlank(CharSequence cs) { | ||||
@@ -57,7 +55,7 @@ public class StringUtils { | |||||
/** | /** | ||||
* <p>Checks if a CharSequence is empty ("") or null.</p> | * <p>Checks if a CharSequence is empty ("") or null.</p> | ||||
* | |||||
* <p> | |||||
* <pre> | * <pre> | ||||
* StringUtils.isEmpty(null) = true | * StringUtils.isEmpty(null) = true | ||||
* StringUtils.isEmpty("") = true | * StringUtils.isEmpty("") = true | ||||
@@ -65,12 +63,12 @@ public class StringUtils { | |||||
* StringUtils.isEmpty("bob") = false | * StringUtils.isEmpty("bob") = false | ||||
* StringUtils.isEmpty(" bob ") = false | * StringUtils.isEmpty(" bob ") = false | ||||
* </pre> | * </pre> | ||||
* | |||||
* <p> | |||||
* <p>NOTE: This method changed in Lang version 2.0. | * <p>NOTE: This method changed in Lang version 2.0. | ||||
* It no longer trims the CharSequence. | * It no longer trims the CharSequence. | ||||
* That functionality is available in isBlank().</p> | * That functionality is available in isBlank().</p> | ||||
* | * | ||||
* @param cs the CharSequence to check, may be null | |||||
* @param cs the CharSequence to check, may be null | |||||
* @return {@code true} if the CharSequence is empty or null | * @return {@code true} if the CharSequence is empty or null | ||||
* @since 3.0 Changed signature from isEmpty(String) to isEmpty(CharSequence) | * @since 3.0 Changed signature from isEmpty(String) to isEmpty(CharSequence) | ||||
*/ | */ | ||||
@@ -80,7 +78,7 @@ public class StringUtils { | |||||
/** | /** | ||||
* <p>Checks if a CharSequence is not empty ("") and not null.</p> | * <p>Checks if a CharSequence is not empty ("") and not null.</p> | ||||
* | |||||
* <p> | |||||
* <pre> | * <pre> | ||||
* StringUtils.isNotEmpty(null) = false | * StringUtils.isNotEmpty(null) = false | ||||
* StringUtils.isNotEmpty("") = false | * StringUtils.isNotEmpty("") = false | ||||
@@ -89,7 +87,7 @@ public class StringUtils { | |||||
* StringUtils.isNotEmpty(" bob ") = true | * StringUtils.isNotEmpty(" bob ") = true | ||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param cs the CharSequence to check, may be null | |||||
* @param cs the CharSequence to check, may be null | |||||
* @return {@code true} if the CharSequence is not empty and not null | * @return {@code true} if the CharSequence is not empty and not null | ||||
* @since 3.0 Changed signature from isNotEmpty(String) to isNotEmpty(CharSequence) | * @since 3.0 Changed signature from isNotEmpty(String) to isNotEmpty(CharSequence) | ||||
*/ | */ | ||||
@@ -3,24 +3,24 @@ package me.chanjar.weixin.common.util.crypto; | |||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
public class ByteGroup { | public class ByteGroup { | ||||
ArrayList<Byte> byteContainer = new ArrayList<Byte>(); | |||||
ArrayList<Byte> byteContainer = new ArrayList<Byte>(); | |||||
public byte[] toBytes() { | public byte[] toBytes() { | ||||
byte[] bytes = new byte[byteContainer.size()]; | |||||
for (int i = 0; i < byteContainer.size(); i++) { | |||||
bytes[i] = byteContainer.get(i); | |||||
} | |||||
return bytes; | |||||
} | |||||
byte[] bytes = new byte[byteContainer.size()]; | |||||
for (int i = 0; i < byteContainer.size(); i++) { | |||||
bytes[i] = byteContainer.get(i); | |||||
} | |||||
return bytes; | |||||
} | |||||
public ByteGroup addBytes(byte[] bytes) { | |||||
for (byte b : bytes) { | |||||
byteContainer.add(b); | |||||
} | |||||
return this; | |||||
} | |||||
public ByteGroup addBytes(byte[] bytes) { | |||||
for (byte b : bytes) { | |||||
byteContainer.add(b); | |||||
} | |||||
return this; | |||||
} | |||||
public int size() { | |||||
return byteContainer.size(); | |||||
} | |||||
public int size() { | |||||
return byteContainer.size(); | |||||
} | |||||
} | } |
@@ -1,6 +1,6 @@ | |||||
/** | /** | ||||
* 对公众平台发送给公众账号的消息加解密示例代码. | * 对公众平台发送给公众账号的消息加解密示例代码. | ||||
* | |||||
* | |||||
* @copyright Copyright (c) 1998-2014 Tencent Inc. | * @copyright Copyright (c) 1998-2014 Tencent Inc. | ||||
*/ | */ | ||||
@@ -16,53 +16,53 @@ import java.util.Arrays; | |||||
*/ | */ | ||||
public class PKCS7Encoder { | public class PKCS7Encoder { | ||||
private static final Charset CHARSET = Charset.forName("utf-8"); | |||||
private static final int BLOCK_SIZE = 32; | |||||
private static final Charset CHARSET = Charset.forName("utf-8"); | |||||
private static final int BLOCK_SIZE = 32; | |||||
/** | |||||
* 获得对明文进行补位填充的字节. | |||||
* | |||||
* @param count 需要进行填充补位操作的明文字节个数 | |||||
* @return 补齐用的字节数组 | |||||
*/ | |||||
public static byte[] encode(int count) { | |||||
// 计算需要填充的位数 | |||||
int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE); | |||||
if (amountToPad == 0) { | |||||
amountToPad = BLOCK_SIZE; | |||||
} | |||||
// 获得补位所用的字符 | |||||
char padChr = chr(amountToPad); | |||||
String tmp = new String(); | |||||
for (int index = 0; index < amountToPad; index++) { | |||||
tmp += padChr; | |||||
} | |||||
return tmp.getBytes(CHARSET); | |||||
} | |||||
/** | |||||
* 获得对明文进行补位填充的字节. | |||||
* | |||||
* @param count 需要进行填充补位操作的明文字节个数 | |||||
* @return 补齐用的字节数组 | |||||
*/ | |||||
public static byte[] encode(int count) { | |||||
// 计算需要填充的位数 | |||||
int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE); | |||||
if (amountToPad == 0) { | |||||
amountToPad = BLOCK_SIZE; | |||||
} | |||||
// 获得补位所用的字符 | |||||
char padChr = chr(amountToPad); | |||||
String tmp = new String(); | |||||
for (int index = 0; index < amountToPad; index++) { | |||||
tmp += padChr; | |||||
} | |||||
return tmp.getBytes(CHARSET); | |||||
} | |||||
/** | |||||
* 删除解密后明文的补位字符 | |||||
* | |||||
* @param decrypted 解密后的明文 | |||||
* @return 删除补位字符后的明文 | |||||
*/ | |||||
public static byte[] decode(byte[] decrypted) { | |||||
int pad = (int) decrypted[decrypted.length - 1]; | |||||
if (pad < 1 || pad > 32) { | |||||
pad = 0; | |||||
} | |||||
return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad); | |||||
} | |||||
/** | |||||
* 删除解密后明文的补位字符 | |||||
* | |||||
* @param decrypted 解密后的明文 | |||||
* @return 删除补位字符后的明文 | |||||
*/ | |||||
public static byte[] decode(byte[] decrypted) { | |||||
int pad = (int) decrypted[decrypted.length - 1]; | |||||
if (pad < 1 || pad > 32) { | |||||
pad = 0; | |||||
} | |||||
return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad); | |||||
} | |||||
/** | |||||
* 将数字转化成ASCII码对应的字符,用于对明文进行补码 | |||||
* | |||||
* @param a 需要转化的数字 | |||||
* @return 转化得到的字符 | |||||
*/ | |||||
public static char chr(int a) { | |||||
byte target = (byte) (a & 0xFF); | |||||
return (char) target; | |||||
} | |||||
/** | |||||
* 将数字转化成ASCII码对应的字符,用于对明文进行补码 | |||||
* | |||||
* @param a 需要转化的数字 | |||||
* @return 转化得到的字符 | |||||
*/ | |||||
public static char chr(int a) { | |||||
byte target = (byte) (a & 0xFF); | |||||
return (char) target; | |||||
} | |||||
} | } |
@@ -1,10 +1,10 @@ | |||||
package me.chanjar.weixin.common.util.crypto; | package me.chanjar.weixin.common.util.crypto; | ||||
import org.apache.commons.codec.digest.DigestUtils; | |||||
import java.security.NoSuchAlgorithmException; | import java.security.NoSuchAlgorithmException; | ||||
import java.util.Arrays; | import java.util.Arrays; | ||||
import org.apache.commons.codec.digest.DigestUtils; | |||||
/** | /** | ||||
* Created by Daniel Qian on 14/10/19. | * Created by Daniel Qian on 14/10/19. | ||||
*/ | */ | ||||
@@ -37,4 +37,4 @@ public class SHA1 { | |||||
} | } | ||||
return DigestUtils.sha1Hex(sb.toString()); | return DigestUtils.sha1Hex(sb.toString()); | ||||
} | } | ||||
} | |||||
} |
@@ -2,6 +2,10 @@ | |||||
* 对公众平台发送给公众账号的消息加解密示例代码. | * 对公众平台发送给公众账号的消息加解密示例代码. | ||||
* | * | ||||
* @copyright Copyright (c) 1998-2014 Tencent Inc. | * @copyright Copyright (c) 1998-2014 Tencent Inc. | ||||
* <p> | |||||
* 针对org.apache.commons.codec.binary.Base64, | |||||
* 需要导入架包commons-codec-1.9(或commons-codec-1.8等其他版本) | |||||
* 官方下载地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi | |||||
*/ | */ | ||||
// ------------------------------------------------------------------------ | // ------------------------------------------------------------------------ | ||||
@@ -62,12 +66,51 @@ public class WxCryptUtil { | |||||
* @param appidOrCorpid 公众平台appid/corpid | * @param appidOrCorpid 公众平台appid/corpid | ||||
*/ | */ | ||||
public WxCryptUtil(String token, String encodingAesKey, | public WxCryptUtil(String token, String encodingAesKey, | ||||
String appidOrCorpid) { | |||||
String appidOrCorpid) { | |||||
this.token = token; | this.token = token; | ||||
this.appidOrCorpid = appidOrCorpid; | this.appidOrCorpid = appidOrCorpid; | ||||
this.aesKey = Base64.decodeBase64(encodingAesKey + "="); | this.aesKey = Base64.decodeBase64(encodingAesKey + "="); | ||||
} | } | ||||
/** | |||||
* 微信公众号支付签名算法(详见:http://pay.weixin.qq.com/wiki/doc/api/index.php?chapter=4_3) | |||||
* @param packageParams 原始参数 | |||||
* @param signKey 加密Key(即 商户Key) | |||||
* @return 签名字符串 | |||||
*/ | |||||
public static String createSign(Map<String, String> packageParams, | |||||
String signKey) { | |||||
SortedMap<String, String> sortedMap = new TreeMap<String, String>(); | |||||
sortedMap.putAll(packageParams); | |||||
List<String> keys = new ArrayList<String>(packageParams.keySet()); | |||||
Collections.sort(keys); | |||||
StringBuffer toSign = new StringBuffer(); | |||||
for (String key : keys) { | |||||
String value = packageParams.get(key); | |||||
if (null != value && !"".equals(value) && !"sign".equals(key) | |||||
&& !"key".equals(key)) { | |||||
toSign.append(key + "=" + value + "&"); | |||||
} | |||||
} | |||||
toSign.append("key=" + signKey); | |||||
String sign = DigestUtils.md5Hex(toSign.toString()).toUpperCase(); | |||||
return sign; | |||||
} | |||||
static String extractEncryptPart(String xml) { | |||||
try { | |||||
DocumentBuilder db = builderLocal.get(); | |||||
Document document = db.parse(new InputSource(new StringReader(xml))); | |||||
Element root = document.getDocumentElement(); | |||||
return root.getElementsByTagName("Encrypt").item(0).getTextContent(); | |||||
} catch (Exception e) { | |||||
throw new RuntimeException(e); | |||||
} | |||||
} | |||||
/** | /** | ||||
* 将公众平台回复用户的消息加密打包. | * 将公众平台回复用户的消息加密打包. | ||||
* <ol> | * <ol> | ||||
@@ -107,7 +150,7 @@ public class WxCryptUtil { | |||||
byte[] randomStringBytes = randomStr.getBytes(CHARSET); | byte[] randomStringBytes = randomStr.getBytes(CHARSET); | ||||
byte[] plainTextBytes = plainText.getBytes(CHARSET); | byte[] plainTextBytes = plainText.getBytes(CHARSET); | ||||
byte[] bytesOfSizeInNetworkOrder = number2BytesInNetworkOrder( | byte[] bytesOfSizeInNetworkOrder = number2BytesInNetworkOrder( | ||||
plainTextBytes.length); | |||||
plainTextBytes.length); | |||||
byte[] appIdBytes = appidOrCorpid.getBytes(CHARSET); | byte[] appIdBytes = appidOrCorpid.getBytes(CHARSET); | ||||
// randomStr + networkBytesOrder + text + appid | // randomStr + networkBytesOrder + text + appid | ||||
@@ -157,7 +200,7 @@ public class WxCryptUtil { | |||||
* @return 解密后的原文 | * @return 解密后的原文 | ||||
*/ | */ | ||||
public String decrypt(String msgSignature, String timeStamp, String nonce, | public String decrypt(String msgSignature, String timeStamp, String nonce, | ||||
String encryptedXml) { | |||||
String encryptedXml) { | |||||
// 密钥,公众账号的app corpSecret | // 密钥,公众账号的app corpSecret | ||||
// 提取密文 | // 提取密文 | ||||
String cipherText = extractEncryptPart(encryptedXml); | String cipherText = extractEncryptPart(encryptedXml); | ||||
@@ -190,7 +233,7 @@ public class WxCryptUtil { | |||||
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); | Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); | ||||
SecretKeySpec key_spec = new SecretKeySpec(aesKey, "AES"); | SecretKeySpec key_spec = new SecretKeySpec(aesKey, "AES"); | ||||
IvParameterSpec iv = new IvParameterSpec( | IvParameterSpec iv = new IvParameterSpec( | ||||
Arrays.copyOfRange(aesKey, 0, 16)); | |||||
Arrays.copyOfRange(aesKey, 0, 16)); | |||||
cipher.init(Cipher.DECRYPT_MODE, key_spec, iv); | cipher.init(Cipher.DECRYPT_MODE, key_spec, iv); | ||||
// 使用BASE64对密文进行解码 | // 使用BASE64对密文进行解码 | ||||
@@ -213,9 +256,9 @@ public class WxCryptUtil { | |||||
int xmlLength = bytesNetworkOrder2Number(networkOrder); | int xmlLength = bytesNetworkOrder2Number(networkOrder); | ||||
xmlContent = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength), | xmlContent = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength), | ||||
CHARSET); | |||||
CHARSET); | |||||
from_appid = new String( | from_appid = new String( | ||||
Arrays.copyOfRange(bytes, 20 + xmlLength, bytes.length), CHARSET); | |||||
Arrays.copyOfRange(bytes, 20 + xmlLength, bytes.length), CHARSET); | |||||
} catch (Exception e) { | } catch (Exception e) { | ||||
throw new RuntimeException(e); | throw new RuntimeException(e); | ||||
} | } | ||||
@@ -229,33 +272,6 @@ public class WxCryptUtil { | |||||
} | } | ||||
/** | |||||
* 微信公众号支付签名算法(详见:http://pay.weixin.qq.com/wiki/doc/api/index.php?chapter=4_3) | |||||
* @param packageParams 原始参数 | |||||
* @param signKey 加密Key(即 商户Key) | |||||
* @return 签名字符串 | |||||
*/ | |||||
public static String createSign(Map<String, String> packageParams, | |||||
String signKey) { | |||||
SortedMap<String, String> sortedMap = new TreeMap<String, String>(); | |||||
sortedMap.putAll(packageParams); | |||||
List<String> keys = new ArrayList<String>(packageParams.keySet()); | |||||
Collections.sort(keys); | |||||
StringBuffer toSign = new StringBuffer(); | |||||
for (String key : keys) { | |||||
String value = packageParams.get(key); | |||||
if (null != value && !"".equals(value) && !"sign".equals(key) | |||||
&& !"key".equals(key)) { | |||||
toSign.append(key + "=" + value + "&"); | |||||
} | |||||
} | |||||
toSign.append("key=" + signKey); | |||||
String sign = DigestUtils.md5Hex(toSign.toString()).toUpperCase(); | |||||
return sign; | |||||
} | |||||
/** | /** | ||||
* 将一个数字转换成生成4个字节的网络字节序bytes数组 | * 将一个数字转换成生成4个字节的网络字节序bytes数组 | ||||
* | * | ||||
@@ -308,24 +324,12 @@ public class WxCryptUtil { | |||||
* @return 生成的xml字符串 | * @return 生成的xml字符串 | ||||
*/ | */ | ||||
private String generateXml(String encrypt, String signature, String timestamp, | private String generateXml(String encrypt, String signature, String timestamp, | ||||
String nonce) { | |||||
String nonce) { | |||||
String format = "<xml>\n" + "<Encrypt><![CDATA[%1$s]]></Encrypt>\n" | String format = "<xml>\n" + "<Encrypt><![CDATA[%1$s]]></Encrypt>\n" | ||||
+ "<MsgSignature><![CDATA[%2$s]]></MsgSignature>\n" | |||||
+ "<TimeStamp>%3$s</TimeStamp>\n" + "<Nonce><![CDATA[%4$s]]></Nonce>\n" | |||||
+ "</xml>"; | |||||
+ "<MsgSignature><![CDATA[%2$s]]></MsgSignature>\n" | |||||
+ "<TimeStamp>%3$s</TimeStamp>\n" + "<Nonce><![CDATA[%4$s]]></Nonce>\n" | |||||
+ "</xml>"; | |||||
return String.format(format, encrypt, signature, timestamp, nonce); | return String.format(format, encrypt, signature, timestamp, nonce); | ||||
} | } | ||||
static String extractEncryptPart(String xml) { | |||||
try { | |||||
DocumentBuilder db = builderLocal.get(); | |||||
Document document = db.parse(new InputSource(new StringReader(xml))); | |||||
Element root = document.getDocumentElement(); | |||||
return root.getElementsByTagName("Encrypt").item(0).getTextContent(); | |||||
} catch (Exception e) { | |||||
throw new RuntimeException(e); | |||||
} | |||||
} | |||||
} | } |
@@ -10,17 +10,18 @@ public class FileUtils { | |||||
/** | /** | ||||
* 创建临时文件 | * 创建临时文件 | ||||
* | |||||
* @param inputStream | * @param inputStream | ||||
* @param name 文件名 | |||||
* @param ext 扩展名 | |||||
* @param tmpDirFile 临时文件夹目录 | |||||
* @param name 文件名 | |||||
* @param ext 扩展名 | |||||
* @param tmpDirFile 临时文件夹目录 | |||||
*/ | */ | ||||
public static File createTmpFile(InputStream inputStream, String name, String ext, File tmpDirFile) throws IOException { | public static File createTmpFile(InputStream inputStream, String name, String ext, File tmpDirFile) throws IOException { | ||||
FileOutputStream fos = null; | FileOutputStream fos = null; | ||||
try { | try { | ||||
File tmpFile; | File tmpFile; | ||||
if (tmpDirFile == null) { | if (tmpDirFile == null) { | ||||
tmpFile = File.createTempFile(name, '.' + ext); | |||||
tmpFile = File.createTempFile(name, '.' + ext); | |||||
} else { | } else { | ||||
tmpFile = File.createTempFile(name, '.' + ext, tmpDirFile); | tmpFile = File.createTempFile(name, '.' + ext, tmpDirFile); | ||||
} | } | ||||
@@ -51,12 +52,13 @@ public class FileUtils { | |||||
/** | /** | ||||
* 创建临时文件 | * 创建临时文件 | ||||
* | |||||
* @param inputStream | * @param inputStream | ||||
* @param name 文件名 | |||||
* @param ext 扩展名 | |||||
* @param name 文件名 | |||||
* @param ext 扩展名 | |||||
*/ | */ | ||||
public static File createTmpFile(InputStream inputStream, String name, String ext) throws IOException { | public static File createTmpFile(InputStream inputStream, String name, String ext) throws IOException { | ||||
return createTmpFile(inputStream, name, ext, null); | return createTmpFile(inputStream, name, ext, null); | ||||
} | } | ||||
} | } |
@@ -10,36 +10,42 @@ public interface ApacheHttpClientBuilder { | |||||
/** | /** | ||||
* 构建httpclient实例 | * 构建httpclient实例 | ||||
* | |||||
* @return new instance of CloseableHttpClient | * @return new instance of CloseableHttpClient | ||||
*/ | */ | ||||
CloseableHttpClient build(); | CloseableHttpClient build(); | ||||
/** | /** | ||||
* 代理服务器地址 | * 代理服务器地址 | ||||
* | |||||
* @param httpProxyHost | * @param httpProxyHost | ||||
*/ | */ | ||||
ApacheHttpClientBuilder httpProxyHost(String httpProxyHost); | ApacheHttpClientBuilder httpProxyHost(String httpProxyHost); | ||||
/** | /** | ||||
* 代理服务器端口 | * 代理服务器端口 | ||||
* | |||||
* @param httpProxyPort | * @param httpProxyPort | ||||
*/ | */ | ||||
ApacheHttpClientBuilder httpProxyPort(int httpProxyPort); | ApacheHttpClientBuilder httpProxyPort(int httpProxyPort); | ||||
/** | /** | ||||
* 代理服务器用户名 | * 代理服务器用户名 | ||||
* | |||||
* @param httpProxyUsername | * @param httpProxyUsername | ||||
*/ | */ | ||||
ApacheHttpClientBuilder httpProxyUsername(String httpProxyUsername); | ApacheHttpClientBuilder httpProxyUsername(String httpProxyUsername); | ||||
/** | /** | ||||
* 代理服务器密码 | * 代理服务器密码 | ||||
* | |||||
* @param httpProxyPassword | * @param httpProxyPassword | ||||
*/ | */ | ||||
ApacheHttpClientBuilder httpProxyPassword(String httpProxyPassword); | ApacheHttpClientBuilder httpProxyPassword(String httpProxyPassword); | ||||
/** | /** | ||||
* ssl连接socket工厂 | * ssl连接socket工厂 | ||||
* | |||||
* @param sslConnectionSocketFactory | * @param sslConnectionSocketFactory | ||||
*/ | */ | ||||
ApacheHttpClientBuilder sslConnectionSocketFactory(SSLConnectionSocketFactory sslConnectionSocketFactory); | ApacheHttpClientBuilder sslConnectionSocketFactory(SSLConnectionSocketFactory sslConnectionSocketFactory); | ||||
@@ -28,12 +28,12 @@ import java.util.concurrent.TimeUnit; | |||||
* httpclient 连接管理器 | * httpclient 连接管理器 | ||||
*/ | */ | ||||
@NotThreadSafe | @NotThreadSafe | ||||
public class DefaultApacheHttpHttpClientBuilder implements ApacheHttpClientBuilder { | |||||
public class DefaultApacheHttpClientBuilder implements ApacheHttpClientBuilder { | |||||
private int connectionRequestTimeout = 3000; | private int connectionRequestTimeout = 3000; | ||||
private int connectionTimeout = 5000; | private int connectionTimeout = 5000; | ||||
private int soTimeout = 5000; | private int soTimeout = 5000; | ||||
private int idleConnTimeout = 60000; | private int idleConnTimeout = 60000; | ||||
private int checkWaitTime = 5000; | |||||
private int checkWaitTime = 60000; | |||||
private int maxConnPerHost = 10; | private int maxConnPerHost = 10; | ||||
private int maxTotalConn = 50; | private int maxTotalConn = 50; | ||||
private String userAgent; | private String userAgent; | ||||
@@ -51,109 +51,112 @@ public class DefaultApacheHttpHttpClientBuilder implements ApacheHttpClientBuild | |||||
private String httpProxyUsername; | private String httpProxyUsername; | ||||
private String httpProxyPassword; | private String httpProxyPassword; | ||||
/** | |||||
* 连接管理器 | |||||
*/ | |||||
private PoolingHttpClientConnectionManager connectionManager; | |||||
/** | /** | ||||
* 闲置连接监控线程 | * 闲置连接监控线程 | ||||
*/ | */ | ||||
private IdleConnectionMonitorThread idleConnectionMonitorThread; | private IdleConnectionMonitorThread idleConnectionMonitorThread; | ||||
/** | |||||
* httpClientBuilder | |||||
*/ | |||||
private HttpClientBuilder httpClientBuilder; | private HttpClientBuilder httpClientBuilder; | ||||
private boolean prepared = false; | private boolean prepared = false; | ||||
private DefaultApacheHttpHttpClientBuilder() { | |||||
private DefaultApacheHttpClientBuilder() { | |||||
} | } | ||||
public static DefaultApacheHttpHttpClientBuilder get() { | |||||
return new DefaultApacheHttpHttpClientBuilder(); | |||||
public static DefaultApacheHttpClientBuilder get() { | |||||
return new DefaultApacheHttpClientBuilder(); | |||||
} | } | ||||
@Override | |||||
public ApacheHttpClientBuilder httpProxyHost(String httpProxyHost) { | public ApacheHttpClientBuilder httpProxyHost(String httpProxyHost) { | ||||
this.httpProxyHost = httpProxyHost; | this.httpProxyHost = httpProxyHost; | ||||
return this; | return this; | ||||
} | } | ||||
@Override | |||||
public ApacheHttpClientBuilder httpProxyPort(int httpProxyPort) { | public ApacheHttpClientBuilder httpProxyPort(int httpProxyPort) { | ||||
this.httpProxyPort = httpProxyPort; | this.httpProxyPort = httpProxyPort; | ||||
return this; | return this; | ||||
} | } | ||||
@Override | |||||
public ApacheHttpClientBuilder httpProxyUsername(String httpProxyUsername) { | public ApacheHttpClientBuilder httpProxyUsername(String httpProxyUsername) { | ||||
this.httpProxyUsername = httpProxyUsername; | this.httpProxyUsername = httpProxyUsername; | ||||
return this; | return this; | ||||
} | } | ||||
@Override | |||||
public ApacheHttpClientBuilder httpProxyPassword(String httpProxyPassword) { | public ApacheHttpClientBuilder httpProxyPassword(String httpProxyPassword) { | ||||
this.httpProxyPassword = httpProxyPassword; | this.httpProxyPassword = httpProxyPassword; | ||||
return this; | return this; | ||||
} | } | ||||
public ApacheHttpClientBuilder sslConnectionSocketFactory(SSLConnectionSocketFactory sslConnectionSocketFactory){ | |||||
@Override | |||||
public ApacheHttpClientBuilder sslConnectionSocketFactory(SSLConnectionSocketFactory sslConnectionSocketFactory) { | |||||
this.sslConnectionSocketFactory = sslConnectionSocketFactory; | this.sslConnectionSocketFactory = sslConnectionSocketFactory; | ||||
return this; | return this; | ||||
} | } | ||||
public IdleConnectionMonitorThread getIdleConnectionMonitorThread() { | public IdleConnectionMonitorThread getIdleConnectionMonitorThread() { | ||||
return idleConnectionMonitorThread; | |||||
return this.idleConnectionMonitorThread; | |||||
} | } | ||||
private void prepare(){ | |||||
private void prepare() { | |||||
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create() | Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create() | ||||
.register("http", plainConnectionSocketFactory) | |||||
.register("https", sslConnectionSocketFactory) | |||||
.build(); | |||||
connectionManager = new PoolingHttpClientConnectionManager(registry); | |||||
connectionManager.setMaxTotal(maxTotalConn); | |||||
connectionManager.setDefaultMaxPerRoute(maxConnPerHost); | |||||
.register("http", this.plainConnectionSocketFactory) | |||||
.register("https", this.sslConnectionSocketFactory) | |||||
.build(); | |||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry); | |||||
connectionManager.setMaxTotal(this.maxTotalConn); | |||||
connectionManager.setDefaultMaxPerRoute(this.maxConnPerHost); | |||||
connectionManager.setDefaultSocketConfig( | connectionManager.setDefaultSocketConfig( | ||||
SocketConfig.copy(SocketConfig.DEFAULT) | |||||
.setSoTimeout(soTimeout) | |||||
.build() | |||||
SocketConfig.copy(SocketConfig.DEFAULT) | |||||
.setSoTimeout(this.soTimeout) | |||||
.build() | |||||
); | ); | ||||
idleConnectionMonitorThread = new IdleConnectionMonitorThread(connectionManager, idleConnTimeout, checkWaitTime); | |||||
idleConnectionMonitorThread.setDaemon(true); | |||||
idleConnectionMonitorThread.start(); | |||||
httpClientBuilder = HttpClients.custom() | |||||
.setConnectionManager(connectionManager) | |||||
.setDefaultRequestConfig( | |||||
RequestConfig.custom() | |||||
.setSocketTimeout(soTimeout) | |||||
.setConnectTimeout(connectionTimeout) | |||||
.setConnectionRequestTimeout(connectionRequestTimeout) | |||||
.build() | |||||
) | |||||
.setRetryHandler(httpRequestRetryHandler); | |||||
if (StringUtils.isNotBlank(httpProxyHost) && StringUtils.isNotBlank(httpProxyUsername)) { | |||||
this.idleConnectionMonitorThread = new IdleConnectionMonitorThread( | |||||
connectionManager, this.idleConnTimeout, this.checkWaitTime); | |||||
this.idleConnectionMonitorThread.setDaemon(true); | |||||
this.idleConnectionMonitorThread.start(); | |||||
this.httpClientBuilder = HttpClients.custom() | |||||
.setConnectionManager(connectionManager) | |||||
.setDefaultRequestConfig( | |||||
RequestConfig.custom() | |||||
.setSocketTimeout(this.soTimeout) | |||||
.setConnectTimeout(this.connectionTimeout) | |||||
.setConnectionRequestTimeout(this.connectionRequestTimeout) | |||||
.build() | |||||
) | |||||
.setRetryHandler(this.httpRequestRetryHandler); | |||||
if (StringUtils.isNotBlank(this.httpProxyHost) | |||||
&& StringUtils.isNotBlank(this.httpProxyUsername)) { | |||||
// 使用代理服务器 需要用户认证的代理服务器 | // 使用代理服务器 需要用户认证的代理服务器 | ||||
CredentialsProvider credsProvider = new BasicCredentialsProvider(); | |||||
credsProvider.setCredentials( | |||||
new AuthScope(httpProxyHost, httpProxyPort), | |||||
new UsernamePasswordCredentials(httpProxyUsername, httpProxyPassword)); | |||||
httpClientBuilder.setDefaultCredentialsProvider(credsProvider); | |||||
CredentialsProvider provider = new BasicCredentialsProvider(); | |||||
provider.setCredentials( | |||||
new AuthScope(this.httpProxyHost, this.httpProxyPort), | |||||
new UsernamePasswordCredentials(this.httpProxyUsername, | |||||
this.httpProxyPassword)); | |||||
this.httpClientBuilder.setDefaultCredentialsProvider(provider); | |||||
} | } | ||||
if (StringUtils.isNotBlank(userAgent)) { | |||||
httpClientBuilder.setUserAgent(userAgent); | |||||
if (StringUtils.isNotBlank(this.userAgent)) { | |||||
this.httpClientBuilder.setUserAgent(this.userAgent); | |||||
} | } | ||||
} | } | ||||
@Override | |||||
public CloseableHttpClient build() { | public CloseableHttpClient build() { | ||||
if(!prepared){ | |||||
if (!this.prepared) { | |||||
prepare(); | prepare(); | ||||
prepared = true; | |||||
this.prepared = true; | |||||
} | } | ||||
return httpClientBuilder.build(); | |||||
return this.httpClientBuilder.build(); | |||||
} | } | ||||
public static class IdleConnectionMonitorThread extends Thread { | public static class IdleConnectionMonitorThread extends Thread { | ||||
@@ -172,11 +175,12 @@ public class DefaultApacheHttpHttpClientBuilder implements ApacheHttpClientBuild | |||||
@Override | @Override | ||||
public void run() { | public void run() { | ||||
try { | try { | ||||
while (!shutdown) { | |||||
while (!this.shutdown) { | |||||
synchronized (this) { | synchronized (this) { | ||||
wait(checkWaitTime); | |||||
connMgr.closeExpiredConnections(); | |||||
connMgr.closeIdleConnections(idleConnTimeout, TimeUnit.MILLISECONDS); | |||||
wait(this.checkWaitTime); | |||||
this.connMgr.closeExpiredConnections(); | |||||
this.connMgr.closeIdleConnections(this.idleConnTimeout, | |||||
TimeUnit.MILLISECONDS); | |||||
} | } | ||||
} | } | ||||
} catch (InterruptedException ignore) { | } catch (InterruptedException ignore) { | ||||
@@ -190,7 +194,7 @@ public class DefaultApacheHttpHttpClientBuilder implements ApacheHttpClientBuild | |||||
} | } | ||||
public void shutdown() { | public void shutdown() { | ||||
shutdown = true; | |||||
this.shutdown = true; | |||||
synchronized (this) { | synchronized (this) { | ||||
notifyAll(); | notifyAll(); | ||||
} | } |
@@ -1,8 +1,5 @@ | |||||
package me.chanjar.weixin.common.util.http; | package me.chanjar.weixin.common.util.http; | ||||
import java.io.IOException; | |||||
import java.io.InputStream; | |||||
import org.apache.http.HttpEntity; | import org.apache.http.HttpEntity; | ||||
import org.apache.http.HttpResponse; | import org.apache.http.HttpResponse; | ||||
import org.apache.http.StatusLine; | import org.apache.http.StatusLine; | ||||
@@ -10,10 +7,13 @@ import org.apache.http.client.HttpResponseException; | |||||
import org.apache.http.client.ResponseHandler; | import org.apache.http.client.ResponseHandler; | ||||
import org.apache.http.util.EntityUtils; | import org.apache.http.util.EntityUtils; | ||||
import java.io.IOException; | |||||
import java.io.InputStream; | |||||
public class InputStreamResponseHandler implements ResponseHandler<InputStream> { | public class InputStreamResponseHandler implements ResponseHandler<InputStream> { | ||||
public static final ResponseHandler<InputStream> INSTANCE = new InputStreamResponseHandler(); | public static final ResponseHandler<InputStream> INSTANCE = new InputStreamResponseHandler(); | ||||
public InputStream handleResponse(final HttpResponse response) throws IOException { | public InputStream handleResponse(final HttpResponse response) throws IOException { | ||||
final StatusLine statusLine = response.getStatusLine(); | final StatusLine statusLine = response.getStatusLine(); | ||||
final HttpEntity entity = response.getEntity(); | final HttpEntity entity = response.getEntity(); | ||||
@@ -18,39 +18,39 @@ import java.io.IOException; | |||||
*/ | */ | ||||
public class JoddGetRequestExecutor implements RequestExecutor<String, String> { | public class JoddGetRequestExecutor implements RequestExecutor<String, String> { | ||||
@Override | |||||
public String execute(CloseableHttpClient httpclient, HttpHost httpProxy, String uri, | |||||
String queryParam) throws WxErrorException, IOException { | |||||
if (queryParam != null) { | |||||
if (uri.indexOf('?') == -1) { | |||||
uri += '?'; | |||||
} | |||||
uri += uri.endsWith("?") ? queryParam : '&' + queryParam; | |||||
} | |||||
SocketHttpConnectionProvider provider = new SocketHttpConnectionProvider(); | |||||
if (httpProxy != null) { | |||||
ProxyInfo proxyInfoObj = new ProxyInfo( | |||||
ProxyInfo.ProxyType.HTTP, | |||||
httpProxy.getHostName(), | |||||
httpProxy.getPort(), "", ""); | |||||
provider.useProxy(proxyInfoObj); | |||||
} | |||||
HttpRequest request = HttpRequest.get(uri); | |||||
request.method("GET"); | |||||
request.charset("UTF-8"); | |||||
HttpResponse response = request.open(provider).send(); | |||||
response.charset("UTF-8"); | |||||
String result = response.bodyText(); | |||||
WxError error = WxError.fromJson(result); | |||||
if (error.getErrorCode() != 0) { | |||||
throw new WxErrorException(error); | |||||
} | |||||
return result; | |||||
@Override | |||||
public String execute(CloseableHttpClient httpclient, HttpHost httpProxy, String uri, | |||||
String queryParam) throws WxErrorException, IOException { | |||||
if (queryParam != null) { | |||||
if (uri.indexOf('?') == -1) { | |||||
uri += '?'; | |||||
} | |||||
uri += uri.endsWith("?") ? queryParam : '&' + queryParam; | |||||
} | } | ||||
SocketHttpConnectionProvider provider = new SocketHttpConnectionProvider(); | |||||
if (httpProxy != null) { | |||||
ProxyInfo proxyInfoObj = new ProxyInfo( | |||||
ProxyInfo.ProxyType.HTTP, | |||||
httpProxy.getHostName(), | |||||
httpProxy.getPort(), "", ""); | |||||
provider.useProxy(proxyInfoObj); | |||||
} | |||||
HttpRequest request = HttpRequest.get(uri); | |||||
request.method("GET"); | |||||
request.charset("UTF-8"); | |||||
HttpResponse response = request.open(provider).send(); | |||||
response.charset("UTF-8"); | |||||
String result = response.bodyText(); | |||||
WxError error = WxError.fromJson(result); | |||||
if (error.getErrorCode() != 0) { | |||||
throw new WxErrorException(error); | |||||
} | |||||
return result; | |||||
} | |||||
} | } |
@@ -18,33 +18,33 @@ import java.io.IOException; | |||||
*/ | */ | ||||
public class JoddPostRequestExecutor implements RequestExecutor<String, String> { | public class JoddPostRequestExecutor implements RequestExecutor<String, String> { | ||||
@Override | |||||
public String execute(CloseableHttpClient httpclient, HttpHost httpProxy, String uri, | |||||
String postEntity) throws WxErrorException, IOException { | |||||
SocketHttpConnectionProvider provider = new SocketHttpConnectionProvider(); | |||||
if (httpProxy != null) { | |||||
ProxyInfo proxyInfoObj = new ProxyInfo( | |||||
ProxyInfo.ProxyType.HTTP, | |||||
httpProxy.getAddress().getHostAddress(), | |||||
httpProxy.getPort(), "", ""); | |||||
provider.useProxy(proxyInfoObj); | |||||
} | |||||
HttpRequest request = HttpRequest.get(uri); | |||||
request.method("POST"); | |||||
request.charset("UTF-8"); | |||||
request.bodyText(postEntity); | |||||
HttpResponse response = request.open(provider).send(); | |||||
response.charset("UTF-8"); | |||||
String result = response.bodyText(); | |||||
WxError error = WxError.fromJson(result); | |||||
if (error.getErrorCode() != 0) { | |||||
throw new WxErrorException(error); | |||||
} | |||||
return result; | |||||
@Override | |||||
public String execute(CloseableHttpClient httpclient, HttpHost httpProxy, String uri, | |||||
String postEntity) throws WxErrorException, IOException { | |||||
SocketHttpConnectionProvider provider = new SocketHttpConnectionProvider(); | |||||
if (httpProxy != null) { | |||||
ProxyInfo proxyInfoObj = new ProxyInfo( | |||||
ProxyInfo.ProxyType.HTTP, | |||||
httpProxy.getAddress().getHostAddress(), | |||||
httpProxy.getPort(), "", ""); | |||||
provider.useProxy(proxyInfoObj); | |||||
} | } | ||||
HttpRequest request = HttpRequest.get(uri); | |||||
request.method("POST"); | |||||
request.charset("UTF-8"); | |||||
request.bodyText(postEntity); | |||||
HttpResponse response = request.open(provider).send(); | |||||
response.charset("UTF-8"); | |||||
String result = response.bodyText(); | |||||
WxError error = WxError.fromJson(result); | |||||
if (error.getErrorCode() != 0) { | |||||
throw new WxErrorException(error); | |||||
} | |||||
return result; | |||||
} | |||||
} | } |
@@ -6,7 +6,6 @@ import me.chanjar.weixin.common.util.StringUtils; | |||||
import me.chanjar.weixin.common.util.fs.FileUtils; | import me.chanjar.weixin.common.util.fs.FileUtils; | ||||
import org.apache.http.Header; | import org.apache.http.Header; | ||||
import org.apache.http.HttpHost; | import org.apache.http.HttpHost; | ||||
import org.apache.http.client.ClientProtocolException; | |||||
import org.apache.http.client.config.RequestConfig; | import org.apache.http.client.config.RequestConfig; | ||||
import org.apache.http.client.methods.CloseableHttpResponse; | import org.apache.http.client.methods.CloseableHttpResponse; | ||||
import org.apache.http.client.methods.HttpGet; | import org.apache.http.client.methods.HttpGet; | ||||
@@ -21,11 +20,11 @@ import java.util.regex.Pattern; | |||||
/** | /** | ||||
* 下载媒体文件请求执行器,请求的参数是String, 返回的结果是File | * 下载媒体文件请求执行器,请求的参数是String, 返回的结果是File | ||||
* @author Daniel Qian | |||||
* | * | ||||
* @author Daniel Qian | |||||
*/ | */ | ||||
public class MediaDownloadRequestExecutor implements RequestExecutor<File, String> { | public class MediaDownloadRequestExecutor implements RequestExecutor<File, String> { | ||||
private File tmpDirFile; | private File tmpDirFile; | ||||
public MediaDownloadRequestExecutor() { | public MediaDownloadRequestExecutor() { | ||||
@@ -36,7 +35,7 @@ public class MediaDownloadRequestExecutor implements RequestExecutor<File, Strin | |||||
super(); | super(); | ||||
this.tmpDirFile = tmpDirFile; | this.tmpDirFile = tmpDirFile; | ||||
} | } | ||||
@Override | @Override | ||||
public File execute(CloseableHttpClient httpclient, HttpHost httpProxy, String uri, String queryParam) throws WxErrorException, IOException { | public File execute(CloseableHttpClient httpclient, HttpHost httpProxy, String uri, String queryParam) throws WxErrorException, IOException { | ||||
@@ -46,7 +45,7 @@ public class MediaDownloadRequestExecutor implements RequestExecutor<File, Strin | |||||
} | } | ||||
uri += uri.endsWith("?") ? queryParam : '&' + queryParam; | uri += uri.endsWith("?") ? queryParam : '&' + queryParam; | ||||
} | } | ||||
HttpGet httpGet = new HttpGet(uri); | HttpGet httpGet = new HttpGet(uri); | ||||
if (httpProxy != null) { | if (httpProxy != null) { | ||||
RequestConfig config = RequestConfig.custom().setProxy(httpProxy).build(); | RequestConfig config = RequestConfig.custom().setProxy(httpProxy).build(); | ||||
@@ -74,7 +73,7 @@ public class MediaDownloadRequestExecutor implements RequestExecutor<File, Strin | |||||
File localFile = FileUtils.createTmpFile(inputStream, name_ext[0], name_ext[1], tmpDirFile); | File localFile = FileUtils.createTmpFile(inputStream, name_ext[0], name_ext[1], tmpDirFile); | ||||
return localFile; | return localFile; | ||||
}finally { | |||||
} finally { | |||||
httpGet.releaseConnection(); | httpGet.releaseConnection(); | ||||
} | } | ||||
@@ -88,5 +87,5 @@ public class MediaDownloadRequestExecutor implements RequestExecutor<File, Strin | |||||
String fileName = m.group(1); | String fileName = m.group(1); | ||||
return fileName; | return fileName; | ||||
} | } | ||||
} | } |
@@ -5,7 +5,6 @@ import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | |||||
import me.chanjar.weixin.common.exception.WxErrorException; | import me.chanjar.weixin.common.exception.WxErrorException; | ||||
import org.apache.http.HttpEntity; | import org.apache.http.HttpEntity; | ||||
import org.apache.http.HttpHost; | import org.apache.http.HttpHost; | ||||
import org.apache.http.client.ClientProtocolException; | |||||
import org.apache.http.client.config.RequestConfig; | import org.apache.http.client.config.RequestConfig; | ||||
import org.apache.http.client.methods.CloseableHttpResponse; | import org.apache.http.client.methods.CloseableHttpResponse; | ||||
import org.apache.http.client.methods.HttpPost; | import org.apache.http.client.methods.HttpPost; | ||||
@@ -19,8 +18,8 @@ import java.io.IOException; | |||||
/** | /** | ||||
* 上传媒体文件请求执行器,请求的参数是File, 返回的结果是String | * 上传媒体文件请求执行器,请求的参数是File, 返回的结果是String | ||||
* @author Daniel Qian | |||||
* | * | ||||
* @author Daniel Qian | |||||
*/ | */ | ||||
public class MediaUploadRequestExecutor implements RequestExecutor<WxMediaUploadResult, File> { | public class MediaUploadRequestExecutor implements RequestExecutor<WxMediaUploadResult, File> { | ||||
@@ -33,10 +32,10 @@ public class MediaUploadRequestExecutor implements RequestExecutor<WxMediaUpload | |||||
} | } | ||||
if (file != null) { | if (file != null) { | ||||
HttpEntity entity = MultipartEntityBuilder | HttpEntity entity = MultipartEntityBuilder | ||||
.create() | |||||
.addBinaryBody("media", file) | |||||
.setMode(HttpMultipartMode.RFC6532) | |||||
.build(); | |||||
.create() | |||||
.addBinaryBody("media", file) | |||||
.setMode(HttpMultipartMode.RFC6532) | |||||
.build(); | |||||
httpPost.setEntity(entity); | httpPost.setEntity(entity); | ||||
httpPost.setHeader("Content-Type", ContentType.MULTIPART_FORM_DATA.toString()); | httpPost.setHeader("Content-Type", ContentType.MULTIPART_FORM_DATA.toString()); | ||||
} | } | ||||
@@ -47,7 +46,7 @@ public class MediaUploadRequestExecutor implements RequestExecutor<WxMediaUpload | |||||
throw new WxErrorException(error); | throw new WxErrorException(error); | ||||
} | } | ||||
return WxMediaUploadResult.fromJson(responseContent); | return WxMediaUploadResult.fromJson(responseContent); | ||||
}finally { | |||||
} finally { | |||||
httpPost.releaseConnection(); | httpPost.releaseConnection(); | ||||
} | } | ||||
} | } | ||||
@@ -1,12 +1,11 @@ | |||||
package me.chanjar.weixin.common.util.http; | package me.chanjar.weixin.common.util.http; | ||||
import java.io.IOException; | |||||
import me.chanjar.weixin.common.exception.WxErrorException; | |||||
import org.apache.http.HttpHost; | import org.apache.http.HttpHost; | ||||
import org.apache.http.client.ClientProtocolException; | import org.apache.http.client.ClientProtocolException; | ||||
import org.apache.http.impl.client.CloseableHttpClient; | import org.apache.http.impl.client.CloseableHttpClient; | ||||
import me.chanjar.weixin.common.exception.WxErrorException; | |||||
import java.io.IOException; | |||||
/** | /** | ||||
* http请求执行器 | * http请求执行器 | ||||
@@ -17,11 +16,10 @@ import me.chanjar.weixin.common.exception.WxErrorException; | |||||
public interface RequestExecutor<T, E> { | public interface RequestExecutor<T, E> { | ||||
/** | /** | ||||
* | |||||
* @param httpclient 传入的httpClient | * @param httpclient 传入的httpClient | ||||
* @param httpProxy http代理对象,如果没有配置代理则为空 | |||||
* @param uri uri | |||||
* @param data 数据 | |||||
* @param httpProxy http代理对象,如果没有配置代理则为空 | |||||
* @param uri uri | |||||
* @param data 数据 | |||||
* @throws WxErrorException | * @throws WxErrorException | ||||
* @throws ClientProtocolException | * @throws ClientProtocolException | ||||
* @throws IOException | * @throws IOException | ||||
@@ -3,7 +3,6 @@ package me.chanjar.weixin.common.util.http; | |||||
import me.chanjar.weixin.common.bean.result.WxError; | import me.chanjar.weixin.common.bean.result.WxError; | ||||
import me.chanjar.weixin.common.exception.WxErrorException; | import me.chanjar.weixin.common.exception.WxErrorException; | ||||
import org.apache.http.HttpHost; | import org.apache.http.HttpHost; | ||||
import org.apache.http.client.ClientProtocolException; | |||||
import org.apache.http.client.config.RequestConfig; | import org.apache.http.client.config.RequestConfig; | ||||
import org.apache.http.client.methods.CloseableHttpResponse; | import org.apache.http.client.methods.CloseableHttpResponse; | ||||
import org.apache.http.client.methods.HttpGet; | import org.apache.http.client.methods.HttpGet; | ||||
@@ -13,8 +12,8 @@ import java.io.IOException; | |||||
/** | /** | ||||
* 简单的GET请求执行器,请求的参数是String, 返回的结果也是String | * 简单的GET请求执行器,请求的参数是String, 返回的结果也是String | ||||
* @author Daniel Qian | |||||
* | * | ||||
* @author Daniel Qian | |||||
*/ | */ | ||||
public class SimpleGetRequestExecutor implements RequestExecutor<String, String> { | public class SimpleGetRequestExecutor implements RequestExecutor<String, String> { | ||||
@@ -39,7 +38,7 @@ public class SimpleGetRequestExecutor implements RequestExecutor<String, String> | |||||
throw new WxErrorException(error); | throw new WxErrorException(error); | ||||
} | } | ||||
return responseContent; | return responseContent; | ||||
}finally { | |||||
} finally { | |||||
httpGet.releaseConnection(); | httpGet.releaseConnection(); | ||||
} | } | ||||
} | } | ||||
@@ -4,7 +4,6 @@ import me.chanjar.weixin.common.bean.result.WxError; | |||||
import me.chanjar.weixin.common.exception.WxErrorException; | import me.chanjar.weixin.common.exception.WxErrorException; | ||||
import org.apache.http.Consts; | import org.apache.http.Consts; | ||||
import org.apache.http.HttpHost; | import org.apache.http.HttpHost; | ||||
import org.apache.http.client.ClientProtocolException; | |||||
import org.apache.http.client.config.RequestConfig; | import org.apache.http.client.config.RequestConfig; | ||||
import org.apache.http.client.methods.CloseableHttpResponse; | import org.apache.http.client.methods.CloseableHttpResponse; | ||||
import org.apache.http.client.methods.HttpPost; | import org.apache.http.client.methods.HttpPost; | ||||
@@ -15,8 +14,8 @@ import java.io.IOException; | |||||
/** | /** | ||||
* 简单的POST请求执行器,请求的参数是String, 返回的结果也是String | * 简单的POST请求执行器,请求的参数是String, 返回的结果也是String | ||||
* @author Daniel Qian | |||||
* | * | ||||
* @author Daniel Qian | |||||
*/ | */ | ||||
public class SimplePostRequestExecutor implements RequestExecutor<String, String> { | public class SimplePostRequestExecutor implements RequestExecutor<String, String> { | ||||
@@ -40,7 +39,7 @@ public class SimplePostRequestExecutor implements RequestExecutor<String, String | |||||
throw new WxErrorException(error); | throw new WxErrorException(error); | ||||
} | } | ||||
return responseContent; | return responseContent; | ||||
}finally { | |||||
} finally { | |||||
httpPost.releaseConnection(); | httpPost.releaseConnection(); | ||||
} | } | ||||
} | } | ||||
@@ -1,7 +1,5 @@ | |||||
package me.chanjar.weixin.common.util.http; | package me.chanjar.weixin.common.util.http; | ||||
import java.io.IOException; | |||||
import org.apache.http.Consts; | import org.apache.http.Consts; | ||||
import org.apache.http.HttpEntity; | import org.apache.http.HttpEntity; | ||||
import org.apache.http.HttpResponse; | import org.apache.http.HttpResponse; | ||||
@@ -10,15 +8,17 @@ import org.apache.http.client.HttpResponseException; | |||||
import org.apache.http.client.ResponseHandler; | import org.apache.http.client.ResponseHandler; | ||||
import org.apache.http.util.EntityUtils; | import org.apache.http.util.EntityUtils; | ||||
import java.io.IOException; | |||||
/** | /** | ||||
* copy from {@link org.apache.http.impl.client.BasicResponseHandler} | * copy from {@link org.apache.http.impl.client.BasicResponseHandler} | ||||
* @author Daniel Qian | |||||
* | * | ||||
* @author Daniel Qian | |||||
*/ | */ | ||||
public class Utf8ResponseHandler implements ResponseHandler<String> { | public class Utf8ResponseHandler implements ResponseHandler<String> { | ||||
public static final ResponseHandler<String> INSTANCE = new Utf8ResponseHandler(); | public static final ResponseHandler<String> INSTANCE = new Utf8ResponseHandler(); | ||||
public String handleResponse(final HttpResponse response) throws IOException { | public String handleResponse(final HttpResponse response) throws IOException { | ||||
final StatusLine statusLine = response.getStatusLine(); | final StatusLine statusLine = response.getStatusLine(); | ||||
final HttpEntity entity = response.getEntity(); | final HttpEntity entity = response.getEntity(); | ||||
@@ -15,101 +15,101 @@ import com.google.gson.JsonObject; | |||||
public class GsonHelper { | public class GsonHelper { | ||||
public static boolean isNull(JsonElement element) { | |||||
return element == null || element.isJsonNull(); | |||||
} | |||||
public static boolean isNotNull(JsonElement element) { | |||||
return !isNull(element); | |||||
} | |||||
public static Long getLong(JsonObject json, String property) { | |||||
return getAsLong(json.get(property)); | |||||
} | |||||
public static long getPrimitiveLong(JsonObject json, String property) { | |||||
return getAsPrimitiveLong(json.get(property)); | |||||
} | |||||
public static Integer getInteger(JsonObject json, String property) { | |||||
return getAsInteger(json.get(property)); | |||||
} | |||||
public static int getPrimitiveInteger(JsonObject json, String property) { | |||||
return getAsPrimitiveInt(json.get(property)); | |||||
} | |||||
public static Double getDouble(JsonObject json, String property) { | |||||
return getAsDouble(json.get(property)); | |||||
} | |||||
public static double getPrimitiveDouble(JsonObject json, String property) { | |||||
return getAsPrimitiveDouble(json.get(property)); | |||||
} | |||||
public static Float getFloat(JsonObject json, String property) { | |||||
return getAsFloat(json.get(property)); | |||||
} | |||||
public static float getPrimitiveFloat(JsonObject json, String property) { | |||||
return getAsPrimitiveFloat(json.get(property)); | |||||
} | |||||
public static Boolean getBoolean(JsonObject json, String property) { | |||||
return getAsBoolean(json.get(property)); | |||||
} | |||||
public static String getString(JsonObject json, String property) { | |||||
return getAsString(json.get(property)); | |||||
} | |||||
public static String getAsString(JsonElement element) { | |||||
return isNull(element) ? null : element.getAsString(); | |||||
} | |||||
public static Long getAsLong(JsonElement element) { | |||||
return isNull(element) ? null : element.getAsLong(); | |||||
} | |||||
public static long getAsPrimitiveLong(JsonElement element) { | |||||
Long r = getAsLong(element); | |||||
return r == null ? 0l : r; | |||||
} | |||||
public static Integer getAsInteger(JsonElement element) { | |||||
return isNull(element) ? null : element.getAsInt(); | |||||
} | |||||
public static int getAsPrimitiveInt(JsonElement element) { | |||||
Integer r = getAsInteger(element); | |||||
return r == null ? 0 : r; | |||||
} | |||||
public static Boolean getAsBoolean(JsonElement element) { | |||||
return isNull(element) ? null : element.getAsBoolean(); | |||||
} | |||||
public static boolean getAsPrimitiveBool(JsonElement element) { | |||||
Boolean r = getAsBoolean(element); | |||||
return r != null && r.booleanValue(); | |||||
} | |||||
public static Double getAsDouble(JsonElement element) { | |||||
return isNull(element) ? null : element.getAsDouble(); | |||||
} | |||||
public static double getAsPrimitiveDouble(JsonElement element) { | |||||
Double r = getAsDouble(element); | |||||
return r == null ? 0d : r; | |||||
} | |||||
public static Float getAsFloat(JsonElement element) { | |||||
return isNull(element) ? null : element.getAsFloat(); | |||||
} | |||||
public static float getAsPrimitiveFloat(JsonElement element) { | |||||
Float r = getAsFloat(element); | |||||
return r == null ? 0f : r; | |||||
} | |||||
public static boolean isNull(JsonElement element) { | |||||
return element == null || element.isJsonNull(); | |||||
} | |||||
public static boolean isNotNull(JsonElement element) { | |||||
return !isNull(element); | |||||
} | |||||
public static Long getLong(JsonObject json, String property) { | |||||
return getAsLong(json.get(property)); | |||||
} | |||||
public static long getPrimitiveLong(JsonObject json, String property) { | |||||
return getAsPrimitiveLong(json.get(property)); | |||||
} | |||||
public static Integer getInteger(JsonObject json, String property) { | |||||
return getAsInteger(json.get(property)); | |||||
} | |||||
public static int getPrimitiveInteger(JsonObject json, String property) { | |||||
return getAsPrimitiveInt(json.get(property)); | |||||
} | |||||
public static Double getDouble(JsonObject json, String property) { | |||||
return getAsDouble(json.get(property)); | |||||
} | |||||
public static double getPrimitiveDouble(JsonObject json, String property) { | |||||
return getAsPrimitiveDouble(json.get(property)); | |||||
} | |||||
public static Float getFloat(JsonObject json, String property) { | |||||
return getAsFloat(json.get(property)); | |||||
} | |||||
public static float getPrimitiveFloat(JsonObject json, String property) { | |||||
return getAsPrimitiveFloat(json.get(property)); | |||||
} | |||||
public static Boolean getBoolean(JsonObject json, String property) { | |||||
return getAsBoolean(json.get(property)); | |||||
} | |||||
public static String getString(JsonObject json, String property) { | |||||
return getAsString(json.get(property)); | |||||
} | |||||
public static String getAsString(JsonElement element) { | |||||
return isNull(element) ? null : element.getAsString(); | |||||
} | |||||
public static Long getAsLong(JsonElement element) { | |||||
return isNull(element) ? null : element.getAsLong(); | |||||
} | |||||
public static long getAsPrimitiveLong(JsonElement element) { | |||||
Long r = getAsLong(element); | |||||
return r == null ? 0l : r; | |||||
} | |||||
public static Integer getAsInteger(JsonElement element) { | |||||
return isNull(element) ? null : element.getAsInt(); | |||||
} | |||||
public static int getAsPrimitiveInt(JsonElement element) { | |||||
Integer r = getAsInteger(element); | |||||
return r == null ? 0 : r; | |||||
} | |||||
public static Boolean getAsBoolean(JsonElement element) { | |||||
return isNull(element) ? null : element.getAsBoolean(); | |||||
} | |||||
public static boolean getAsPrimitiveBool(JsonElement element) { | |||||
Boolean r = getAsBoolean(element); | |||||
return r != null && r.booleanValue(); | |||||
} | |||||
public static Double getAsDouble(JsonElement element) { | |||||
return isNull(element) ? null : element.getAsDouble(); | |||||
} | |||||
public static double getAsPrimitiveDouble(JsonElement element) { | |||||
Double r = getAsDouble(element); | |||||
return r == null ? 0d : r; | |||||
} | |||||
public static Float getAsFloat(JsonElement element) { | |||||
return isNull(element) ? null : element.getAsFloat(); | |||||
} | |||||
public static float getAsPrimitiveFloat(JsonElement element) { | |||||
Float r = getAsFloat(element); | |||||
return r == null ? 0f : r; | |||||
} | |||||
} | } |
@@ -14,9 +14,7 @@ import me.chanjar.weixin.common.bean.WxAccessToken; | |||||
import java.lang.reflect.Type; | import java.lang.reflect.Type; | ||||
/** | /** | ||||
* | |||||
* @author Daniel Qian | * @author Daniel Qian | ||||
* | |||||
*/ | */ | ||||
public class WxAccessTokenAdapter implements JsonDeserializer<WxAccessToken> { | public class WxAccessTokenAdapter implements JsonDeserializer<WxAccessToken> { | ||||
@@ -32,5 +30,5 @@ public class WxAccessTokenAdapter implements JsonDeserializer<WxAccessToken> { | |||||
} | } | ||||
return accessToken; | return accessToken; | ||||
} | } | ||||
} | } |
@@ -14,9 +14,7 @@ import me.chanjar.weixin.common.bean.result.WxError; | |||||
import java.lang.reflect.Type; | import java.lang.reflect.Type; | ||||
/** | /** | ||||
* | |||||
* @author Daniel Qian | * @author Daniel Qian | ||||
* | |||||
*/ | */ | ||||
public class WxErrorAdapter implements JsonDeserializer<WxError> { | public class WxErrorAdapter implements JsonDeserializer<WxError> { | ||||
@@ -33,5 +31,5 @@ public class WxErrorAdapter implements JsonDeserializer<WxError> { | |||||
wxError.setJson(json.toString()); | wxError.setJson(json.toString()); | ||||
return wxError; | return wxError; | ||||
} | } | ||||
} | } |
@@ -2,9 +2,10 @@ package me.chanjar.weixin.common.util.json; | |||||
import com.google.gson.Gson; | import com.google.gson.Gson; | ||||
import com.google.gson.GsonBuilder; | import com.google.gson.GsonBuilder; | ||||
import me.chanjar.weixin.common.bean.WxMenu; | |||||
import me.chanjar.weixin.common.bean.result.WxError; | |||||
import me.chanjar.weixin.common.bean.WxAccessToken; | import me.chanjar.weixin.common.bean.WxAccessToken; | ||||
import me.chanjar.weixin.common.bean.menu.WxMenu; | |||||
import me.chanjar.weixin.common.bean.result.WxError; | |||||
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | ||||
public class WxGsonBuilder { | public class WxGsonBuilder { | ||||
@@ -14,9 +14,7 @@ import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | |||||
import java.lang.reflect.Type; | import java.lang.reflect.Type; | ||||
/** | /** | ||||
* | |||||
* @author Daniel Qian | * @author Daniel Qian | ||||
* | |||||
*/ | */ | ||||
public class WxMediaUploadResultAdapter implements JsonDeserializer<WxMediaUploadResult> { | public class WxMediaUploadResultAdapter implements JsonDeserializer<WxMediaUploadResult> { | ||||
@@ -38,5 +36,5 @@ public class WxMediaUploadResultAdapter implements JsonDeserializer<WxMediaUploa | |||||
} | } | ||||
return uploadResult; | return uploadResult; | ||||
} | } | ||||
} | } |
@@ -8,6 +8,8 @@ | |||||
*/ | */ | ||||
package me.chanjar.weixin.common.util.json; | package me.chanjar.weixin.common.util.json; | ||||
import java.lang.reflect.Type; | |||||
import com.google.gson.JsonArray; | import com.google.gson.JsonArray; | ||||
import com.google.gson.JsonDeserializationContext; | import com.google.gson.JsonDeserializationContext; | ||||
import com.google.gson.JsonDeserializer; | import com.google.gson.JsonDeserializer; | ||||
@@ -16,14 +18,14 @@ import com.google.gson.JsonObject; | |||||
import com.google.gson.JsonParseException; | import com.google.gson.JsonParseException; | ||||
import com.google.gson.JsonSerializationContext; | import com.google.gson.JsonSerializationContext; | ||||
import com.google.gson.JsonSerializer; | import com.google.gson.JsonSerializer; | ||||
import me.chanjar.weixin.common.bean.WxMenu; | |||||
import java.lang.reflect.Type; | |||||
import me.chanjar.weixin.common.bean.menu.WxMenu; | |||||
import me.chanjar.weixin.common.bean.menu.WxMenuButton; | |||||
import me.chanjar.weixin.common.bean.menu.WxMenuRule; | |||||
/** | /** | ||||
* | |||||
* @author Daniel Qian | * @author Daniel Qian | ||||
* | |||||
*/ | */ | ||||
public class WxMenuGsonAdapter implements JsonSerializer<WxMenu>, JsonDeserializer<WxMenu> { | public class WxMenuGsonAdapter implements JsonSerializer<WxMenu>, JsonDeserializer<WxMenu> { | ||||
@@ -31,28 +33,29 @@ public class WxMenuGsonAdapter implements JsonSerializer<WxMenu>, JsonDeserializ | |||||
JsonObject json = new JsonObject(); | JsonObject json = new JsonObject(); | ||||
JsonArray buttonArray = new JsonArray(); | JsonArray buttonArray = new JsonArray(); | ||||
for (WxMenu.WxMenuButton button : menu.getButtons()) { | |||||
for (WxMenuButton button : menu.getButtons()) { | |||||
JsonObject buttonJson = convertToJson(button); | JsonObject buttonJson = convertToJson(button); | ||||
buttonArray.add(buttonJson); | buttonArray.add(buttonJson); | ||||
} | } | ||||
json.add("button", buttonArray); | json.add("button", buttonArray); | ||||
if (menu.getMatchRule() != null) { | if (menu.getMatchRule() != null) { | ||||
json.add("matchrule", convertToJson(menu.getMatchRule())); | json.add("matchrule", convertToJson(menu.getMatchRule())); | ||||
} | } | ||||
return json; | return json; | ||||
} | } | ||||
protected JsonObject convertToJson(WxMenu.WxMenuButton button) { | |||||
protected JsonObject convertToJson(WxMenuButton button) { | |||||
JsonObject buttonJson = new JsonObject(); | JsonObject buttonJson = new JsonObject(); | ||||
buttonJson.addProperty("type", button.getType()); | buttonJson.addProperty("type", button.getType()); | ||||
buttonJson.addProperty("name", button.getName()); | buttonJson.addProperty("name", button.getName()); | ||||
buttonJson.addProperty("key", button.getKey()); | buttonJson.addProperty("key", button.getKey()); | ||||
buttonJson.addProperty("url", button.getUrl()); | buttonJson.addProperty("url", button.getUrl()); | ||||
buttonJson.addProperty("media_id", button.getMediaId()); | |||||
if (button.getSubButtons() != null && button.getSubButtons().size() > 0) { | if (button.getSubButtons() != null && button.getSubButtons().size() > 0) { | ||||
JsonArray buttonArray = new JsonArray(); | JsonArray buttonArray = new JsonArray(); | ||||
for (WxMenu.WxMenuButton sub_button : button.getSubButtons()) { | |||||
for (WxMenuButton sub_button : button.getSubButtons()) { | |||||
buttonArray.add(convertToJson(sub_button)); | buttonArray.add(convertToJson(sub_button)); | ||||
} | } | ||||
buttonJson.add("sub_button", buttonArray); | buttonJson.add("sub_button", buttonArray); | ||||
@@ -60,15 +63,15 @@ public class WxMenuGsonAdapter implements JsonSerializer<WxMenu>, JsonDeserializ | |||||
return buttonJson; | return buttonJson; | ||||
} | } | ||||
protected JsonObject convertToJson(WxMenu.WxMenuRule menuRule){ | |||||
protected JsonObject convertToJson(WxMenuRule menuRule) { | |||||
JsonObject matchRule = new JsonObject(); | JsonObject matchRule = new JsonObject(); | ||||
matchRule.addProperty("tag_id",menuRule.getTagId()); | |||||
matchRule.addProperty("sex",menuRule.getSex()); | |||||
matchRule.addProperty("country",menuRule.getCountry()); | |||||
matchRule.addProperty("province",menuRule.getProvince()); | |||||
matchRule.addProperty("city",menuRule.getCity()); | |||||
matchRule.addProperty("client_platform_type",menuRule.getClientPlatformType()); | |||||
matchRule.addProperty("language",menuRule.getLanguage()); | |||||
matchRule.addProperty("tag_id", menuRule.getTagId()); | |||||
matchRule.addProperty("sex", menuRule.getSex()); | |||||
matchRule.addProperty("country", menuRule.getCountry()); | |||||
matchRule.addProperty("province", menuRule.getProvince()); | |||||
matchRule.addProperty("city", menuRule.getCity()); | |||||
matchRule.addProperty("client_platform_type", menuRule.getClientPlatformType()); | |||||
matchRule.addProperty("language", menuRule.getLanguage()); | |||||
return matchRule; | return matchRule; | ||||
} | } | ||||
@@ -83,7 +86,7 @@ public class WxMenuGsonAdapter implements JsonSerializer<WxMenu>, JsonDeserializ | |||||
JsonArray buttonsJson = menuJson.get("button").getAsJsonArray(); | JsonArray buttonsJson = menuJson.get("button").getAsJsonArray(); | ||||
for (int i = 0; i < buttonsJson.size(); i++) { | for (int i = 0; i < buttonsJson.size(); i++) { | ||||
JsonObject buttonJson = buttonsJson.get(i).getAsJsonObject(); | JsonObject buttonJson = buttonsJson.get(i).getAsJsonObject(); | ||||
WxMenu.WxMenuButton button = convertFromJson(buttonJson); | |||||
WxMenuButton button = convertFromJson(buttonJson); | |||||
menu.getButtons().add(button); | menu.getButtons().add(button); | ||||
if (buttonJson.get("sub_button") == null || buttonJson.get("sub_button").isJsonNull()) { | if (buttonJson.get("sub_button") == null || buttonJson.get("sub_button").isJsonNull()) { | ||||
continue; | continue; | ||||
@@ -96,13 +99,14 @@ public class WxMenuGsonAdapter implements JsonSerializer<WxMenu>, JsonDeserializ | |||||
} | } | ||||
return menu; | return menu; | ||||
} | } | ||||
protected WxMenu.WxMenuButton convertFromJson(JsonObject json) { | |||||
WxMenu.WxMenuButton button = new WxMenu.WxMenuButton(); | |||||
protected WxMenuButton convertFromJson(JsonObject json) { | |||||
WxMenuButton button = new WxMenuButton(); | |||||
button.setName(GsonHelper.getString(json, "name")); | button.setName(GsonHelper.getString(json, "name")); | ||||
button.setKey(GsonHelper.getString(json, "key")); | button.setKey(GsonHelper.getString(json, "key")); | ||||
button.setUrl(GsonHelper.getString(json, "url")); | button.setUrl(GsonHelper.getString(json, "url")); | ||||
button.setType(GsonHelper.getString(json, "type")); | button.setType(GsonHelper.getString(json, "type")); | ||||
button.setMediaId(GsonHelper.getString(json, "media_id")); | |||||
return button; | return button; | ||||
} | } | ||||
@@ -18,30 +18,24 @@ | |||||
package me.chanjar.weixin.common.util.res; | package me.chanjar.weixin.common.util.res; | ||||
import java.text.MessageFormat; | import java.text.MessageFormat; | ||||
import java.util.Enumeration; | |||||
import java.util.Hashtable; | |||||
import java.util.LinkedHashMap; | |||||
import java.util.Locale; | |||||
import java.util.Map; | |||||
import java.util.MissingResourceException; | |||||
import java.util.ResourceBundle; | |||||
import java.util.*; | |||||
/** | /** | ||||
* An internationalization / localization helper class which reduces | * An internationalization / localization helper class which reduces | ||||
* the bother of handling ResourceBundles and takes care of the | * the bother of handling ResourceBundles and takes care of the | ||||
* common cases of message formating which otherwise require the | * common cases of message formating which otherwise require the | ||||
* creation of Object arrays and such. | * creation of Object arrays and such. | ||||
* | |||||
* <p> | |||||
* <p>The StringManager operates on a package basis. One StringManager | * <p>The StringManager operates on a package basis. One StringManager | ||||
* per package can be created and accessed via the getManager method | * per package can be created and accessed via the getManager method | ||||
* call. | * call. | ||||
* | |||||
* <p> | |||||
* <p>The StringManager will look for a ResourceBundle named by | * <p>The StringManager will look for a ResourceBundle named by | ||||
* the package name given plus the suffix of "LocalStrings". In | * the package name given plus the suffix of "LocalStrings". In | ||||
* practice, this means that the localized information will be contained | * practice, this means that the localized information will be contained | ||||
* in a LocalStrings.properties file located in the package | * in a LocalStrings.properties file located in the package | ||||
* directory of the classpath. | * directory of the classpath. | ||||
* | |||||
* <p> | |||||
* <p>Please see the documentation for java.util.ResourceBundle for | * <p>Please see the documentation for java.util.ResourceBundle for | ||||
* more information. | * more information. | ||||
* | * | ||||
@@ -52,152 +46,80 @@ import java.util.ResourceBundle; | |||||
*/ | */ | ||||
public class StringManager { | public class StringManager { | ||||
private static int LOCALE_CACHE_SIZE = 10; | |||||
/** | |||||
* The ResourceBundle for this StringManager. | |||||
*/ | |||||
private final ResourceBundle bundle; | |||||
private final Locale locale; | |||||
/** | |||||
* Creates a new StringManager for a given package. This is a | |||||
* private method and all access to it is arbitrated by the | |||||
* static getManager method call so that only one StringManager | |||||
* per package will be created. | |||||
* | |||||
* @param packageName Name of package to create StringManager for. | |||||
*/ | |||||
private StringManager(String packageName, Locale locale) { | |||||
String bundleName = packageName + ".LocalStrings"; | |||||
ResourceBundle bnd = null; | |||||
private static final Map<String, Map<Locale, StringManager>> managers = | |||||
new Hashtable<String, Map<Locale, StringManager>>(); | |||||
private static int LOCALE_CACHE_SIZE = 10; | |||||
/** | |||||
* The ResourceBundle for this StringManager. | |||||
*/ | |||||
private final ResourceBundle bundle; | |||||
private final Locale locale; | |||||
/** | |||||
* Creates a new StringManager for a given package. This is a | |||||
* private method and all access to it is arbitrated by the | |||||
* static getManager method call so that only one StringManager | |||||
* per package will be created. | |||||
* | |||||
* @param packageName Name of package to create StringManager for. | |||||
*/ | |||||
private StringManager(String packageName, Locale locale) { | |||||
String bundleName = packageName + ".LocalStrings"; | |||||
ResourceBundle bnd = null; | |||||
try { | |||||
bnd = ResourceBundle.getBundle(bundleName, locale); | |||||
} catch (MissingResourceException ex) { | |||||
// Try from the current loader (that's the case for trusted apps) | |||||
// Should only be required if using a TC5 style classloader structure | |||||
// where common != shared != server | |||||
ClassLoader cl = Thread.currentThread().getContextClassLoader(); | |||||
if (cl != null) { | |||||
try { | try { | ||||
bnd = ResourceBundle.getBundle(bundleName, locale); | |||||
} catch( MissingResourceException ex ) { | |||||
// Try from the current loader (that's the case for trusted apps) | |||||
// Should only be required if using a TC5 style classloader structure | |||||
// where common != shared != server | |||||
ClassLoader cl = Thread.currentThread().getContextClassLoader(); | |||||
if( cl != null ) { | |||||
try { | |||||
bnd = ResourceBundle.getBundle(bundleName, locale, cl); | |||||
} catch(MissingResourceException ex2) { | |||||
// Ignore | |||||
} | |||||
} | |||||
} | |||||
bundle = bnd; | |||||
// Get the actual locale, which may be different from the requested one | |||||
if (bundle != null) { | |||||
Locale bundleLocale = bundle.getLocale(); | |||||
if (bundleLocale.equals(Locale.ROOT)) { | |||||
this.locale = Locale.ENGLISH; | |||||
} else { | |||||
this.locale = bundleLocale; | |||||
} | |||||
} else { | |||||
this.locale = null; | |||||
bnd = ResourceBundle.getBundle(bundleName, locale, cl); | |||||
} catch (MissingResourceException ex2) { | |||||
// Ignore | |||||
} | } | ||||
} | |||||
} | } | ||||
/** | |||||
Get a string from the underlying resource bundle or return | |||||
null if the String is not found. | |||||
@param key to desired resource String | |||||
@return resource String matching <i>key</i> from underlying | |||||
bundle or null if not found. | |||||
@throws IllegalArgumentException if <i>key</i> is null. | |||||
*/ | |||||
public String getString(String key) { | |||||
if(key == null){ | |||||
String msg = "key may not have a null value"; | |||||
throw new IllegalArgumentException(msg); | |||||
} | |||||
String str = null; | |||||
try { | |||||
// Avoid NPE if bundle is null and treat it like an MRE | |||||
if (bundle != null) { | |||||
str = bundle.getString(key); | |||||
} | |||||
} catch(MissingResourceException mre) { | |||||
//bad: shouldn't mask an exception the following way: | |||||
// str = "[cannot find message associated with key '" + key + | |||||
// "' due to " + mre + "]"; | |||||
// because it hides the fact that the String was missing | |||||
// from the calling code. | |||||
//good: could just throw the exception (or wrap it in another) | |||||
// but that would probably cause much havoc on existing | |||||
// code. | |||||
//better: consistent with container pattern to | |||||
// simply return null. Calling code can then do | |||||
// a null check. | |||||
str = null; | |||||
} | |||||
return str; | |||||
bundle = bnd; | |||||
// Get the actual locale, which may be different from the requested one | |||||
if (bundle != null) { | |||||
Locale bundleLocale = bundle.getLocale(); | |||||
if (bundleLocale.equals(Locale.ROOT)) { | |||||
this.locale = Locale.ENGLISH; | |||||
} else { | |||||
this.locale = bundleLocale; | |||||
} | |||||
} else { | |||||
this.locale = null; | |||||
} | } | ||||
/** | |||||
* Get a string from the underlying resource bundle and format | |||||
* it with the given set of arguments. | |||||
* | |||||
* @param key | |||||
* @param args | |||||
*/ | |||||
public String getString(final String key, final Object... args) { | |||||
String value = getString(key); | |||||
if (value == null) { | |||||
value = key; | |||||
} | |||||
MessageFormat mf = new MessageFormat(value); | |||||
mf.setLocale(locale); | |||||
return mf.format(args, new StringBuffer(), null).toString(); | |||||
} | |||||
/** | |||||
* Identify the Locale this StringManager is associated with | |||||
*/ | |||||
public Locale getLocale() { | |||||
return locale; | |||||
} | |||||
// -------------------------------------------------------------- | |||||
// STATIC SUPPORT METHODS | |||||
// -------------------------------------------------------------- | |||||
private static final Map<String, Map<Locale,StringManager>> managers = | |||||
new Hashtable<String, Map<Locale,StringManager>>(); | |||||
/** | |||||
* Get the StringManager for a particular package. If a manager for | |||||
* a package already exists, it will be reused, else a new | |||||
* StringManager will be created and returned. | |||||
* | |||||
* @param packageName The package name | |||||
*/ | |||||
public static final synchronized StringManager getManager( | |||||
String packageName) { | |||||
return getManager(packageName, Locale.getDefault()); | |||||
} | |||||
/** | |||||
* Get the StringManager for a particular package and Locale. If a manager | |||||
* for a package/Locale combination already exists, it will be reused, else | |||||
* a new StringManager will be created and returned. | |||||
* | |||||
* @param packageName The package name | |||||
* @param locale The Locale | |||||
*/ | |||||
public static final synchronized StringManager getManager( | |||||
String packageName, Locale locale) { | |||||
Map<Locale,StringManager> map = managers.get(packageName); | |||||
if (map == null) { | |||||
} | |||||
/** | |||||
* Get the StringManager for a particular package. If a manager for | |||||
* a package already exists, it will be reused, else a new | |||||
* StringManager will be created and returned. | |||||
* | |||||
* @param packageName The package name | |||||
*/ | |||||
public static final synchronized StringManager getManager( | |||||
String packageName) { | |||||
return getManager(packageName, Locale.getDefault()); | |||||
} | |||||
/** | |||||
* Get the StringManager for a particular package and Locale. If a manager | |||||
* for a package/Locale combination already exists, it will be reused, else | |||||
* a new StringManager will be created and returned. | |||||
* | |||||
* @param packageName The package name | |||||
* @param locale The Locale | |||||
*/ | |||||
public static final synchronized StringManager getManager( | |||||
String packageName, Locale locale) { | |||||
Map<Locale, StringManager> map = managers.get(packageName); | |||||
if (map == null) { | |||||
/* | /* | ||||
* Don't want the HashMap to be expanded beyond LOCALE_CACHE_SIZE. | * Don't want the HashMap to be expanded beyond LOCALE_CACHE_SIZE. | ||||
* Expansion occurs when size() exceeds capacity. Therefore keep | * Expansion occurs when size() exceeds capacity. Therefore keep | ||||
@@ -206,43 +128,113 @@ public class StringManager { | |||||
* for removal needs to use one less than the maximum desired size | * for removal needs to use one less than the maximum desired size | ||||
* | * | ||||
*/ | */ | ||||
map = new LinkedHashMap<Locale,StringManager>(LOCALE_CACHE_SIZE, 1, true) { | |||||
private static final long serialVersionUID = 1L; | |||||
@Override | |||||
protected boolean removeEldestEntry( | |||||
Map.Entry<Locale,StringManager> eldest) { | |||||
return size() > (LOCALE_CACHE_SIZE - 1); | |||||
} | |||||
}; | |||||
managers.put(packageName, map); | |||||
} | |||||
map = new LinkedHashMap<Locale, StringManager>(LOCALE_CACHE_SIZE, 1, true) { | |||||
private static final long serialVersionUID = 1L; | |||||
StringManager mgr = map.get(locale); | |||||
if (mgr == null) { | |||||
mgr = new StringManager(packageName, locale); | |||||
map.put(locale, mgr); | |||||
@Override | |||||
protected boolean removeEldestEntry( | |||||
Map.Entry<Locale, StringManager> eldest) { | |||||
return size() > (LOCALE_CACHE_SIZE - 1); | |||||
} | } | ||||
return mgr; | |||||
}; | |||||
managers.put(packageName, map); | |||||
} | } | ||||
/** | |||||
* Retrieve the StringManager for a list of Locales. The first StringManager | |||||
* found will be returned. | |||||
* | |||||
* @param requestedLocales the list of Locales | |||||
* | |||||
* @return the found StringManager or the default StringManager | |||||
*/ | |||||
public static StringManager getManager(String packageName, | |||||
Enumeration<Locale> requestedLocales) { | |||||
while (requestedLocales.hasMoreElements()) { | |||||
Locale locale = requestedLocales.nextElement(); | |||||
StringManager result = getManager(packageName, locale); | |||||
if (result.getLocale().equals(locale)) { | |||||
return result; | |||||
} | |||||
} | |||||
// Return the default | |||||
return getManager(packageName); | |||||
StringManager mgr = map.get(locale); | |||||
if (mgr == null) { | |||||
mgr = new StringManager(packageName, locale); | |||||
map.put(locale, mgr); | |||||
} | } | ||||
return mgr; | |||||
} | |||||
// -------------------------------------------------------------- | |||||
// STATIC SUPPORT METHODS | |||||
// -------------------------------------------------------------- | |||||
/** | |||||
* Retrieve the StringManager for a list of Locales. The first StringManager | |||||
* found will be returned. | |||||
* | |||||
* @param requestedLocales the list of Locales | |||||
* @return the found StringManager or the default StringManager | |||||
*/ | |||||
public static StringManager getManager(String packageName, | |||||
Enumeration<Locale> requestedLocales) { | |||||
while (requestedLocales.hasMoreElements()) { | |||||
Locale locale = requestedLocales.nextElement(); | |||||
StringManager result = getManager(packageName, locale); | |||||
if (result.getLocale().equals(locale)) { | |||||
return result; | |||||
} | |||||
} | |||||
// Return the default | |||||
return getManager(packageName); | |||||
} | |||||
/** | |||||
* Get a string from the underlying resource bundle or return | |||||
* null if the String is not found. | |||||
* | |||||
* @param key to desired resource String | |||||
* @return resource String matching <i>key</i> from underlying | |||||
* bundle or null if not found. | |||||
* @throws IllegalArgumentException if <i>key</i> is null. | |||||
*/ | |||||
public String getString(String key) { | |||||
if (key == null) { | |||||
String msg = "key may not have a null value"; | |||||
throw new IllegalArgumentException(msg); | |||||
} | |||||
String str = null; | |||||
try { | |||||
// Avoid NPE if bundle is null and treat it like an MRE | |||||
if (bundle != null) { | |||||
str = bundle.getString(key); | |||||
} | |||||
} catch (MissingResourceException mre) { | |||||
//bad: shouldn't mask an exception the following way: | |||||
// str = "[cannot find message associated with key '" + key + | |||||
// "' due to " + mre + "]"; | |||||
// because it hides the fact that the String was missing | |||||
// from the calling code. | |||||
//good: could just throw the exception (or wrap it in another) | |||||
// but that would probably cause much havoc on existing | |||||
// code. | |||||
//better: consistent with container pattern to | |||||
// simply return null. Calling code can then do | |||||
// a null check. | |||||
str = null; | |||||
} | |||||
return str; | |||||
} | |||||
/** | |||||
* Get a string from the underlying resource bundle and format | |||||
* it with the given set of arguments. | |||||
* | |||||
* @param key | |||||
* @param args | |||||
*/ | |||||
public String getString(final String key, final Object... args) { | |||||
String value = getString(key); | |||||
if (value == null) { | |||||
value = key; | |||||
} | |||||
MessageFormat mf = new MessageFormat(value); | |||||
mf.setLocale(locale); | |||||
return mf.format(args, new StringBuffer(), null).toString(); | |||||
} | |||||
/** | |||||
* Identify the Locale this StringManager is associated with | |||||
*/ | |||||
public Locale getLocale() { | |||||
return locale; | |||||
} | |||||
} | } |
@@ -1,7 +1,5 @@ | |||||
package me.chanjar.weixin.common.util.xml; | package me.chanjar.weixin.common.util.xml; | ||||
import java.io.Writer; | |||||
import com.thoughtworks.xstream.XStream; | import com.thoughtworks.xstream.XStream; | ||||
import com.thoughtworks.xstream.core.util.QuickWriter; | import com.thoughtworks.xstream.core.util.QuickWriter; | ||||
import com.thoughtworks.xstream.io.HierarchicalStreamWriter; | import com.thoughtworks.xstream.io.HierarchicalStreamWriter; | ||||
@@ -10,6 +8,8 @@ import com.thoughtworks.xstream.io.xml.XppDriver; | |||||
import com.thoughtworks.xstream.security.NullPermission; | import com.thoughtworks.xstream.security.NullPermission; | ||||
import com.thoughtworks.xstream.security.PrimitiveTypePermission; | import com.thoughtworks.xstream.security.PrimitiveTypePermission; | ||||
import java.io.Writer; | |||||
public class XStreamInitializer { | public class XStreamInitializer { | ||||
public static XStream getInstance() { | public static XStream getInstance() { | ||||
@@ -22,6 +22,7 @@ public class XStreamInitializer { | |||||
protected String SUFFIX_CDATA = "]]>"; | protected String SUFFIX_CDATA = "]]>"; | ||||
protected String PREFIX_MEDIA_ID = "<MediaId>"; | protected String PREFIX_MEDIA_ID = "<MediaId>"; | ||||
protected String SUFFIX_MEDIA_ID = "</MediaId>"; | protected String SUFFIX_MEDIA_ID = "</MediaId>"; | ||||
@Override | @Override | ||||
protected void writeText(QuickWriter writer, String text) { | protected void writeText(QuickWriter writer, String text) { | ||||
if (text.startsWith(PREFIX_CDATA) && text.endsWith(SUFFIX_CDATA)) { | if (text.startsWith(PREFIX_CDATA) && text.endsWith(SUFFIX_CDATA)) { | ||||
@@ -14,5 +14,5 @@ public class WxAccessTokenTest { | |||||
Assert.assertTrue(wxError.getExpiresIn() == 7200); | Assert.assertTrue(wxError.getExpiresIn() == 7200); | ||||
} | } | ||||
} | } |
@@ -15,7 +15,7 @@ public class WxErrorTest { | |||||
Assert.assertEquals(wxError.getErrorMsg(), "invalid openid"); | Assert.assertEquals(wxError.getErrorMsg(), "invalid openid"); | ||||
} | } | ||||
public void testFromBadJson1() { | public void testFromBadJson1() { | ||||
String json = "{ \"errcode\": 40003, \"errmsg\": \"invalid openid\", \"media_id\": \"12323423dsfafsf232f\" }"; | String json = "{ \"errcode\": 40003, \"errmsg\": \"invalid openid\", \"media_id\": \"12323423dsfafsf232f\" }"; | ||||
@@ -24,7 +24,7 @@ public class WxErrorTest { | |||||
Assert.assertEquals(wxError.getErrorMsg(), "invalid openid"); | Assert.assertEquals(wxError.getErrorMsg(), "invalid openid"); | ||||
} | } | ||||
public void testFromBadJson2() { | public void testFromBadJson2() { | ||||
String json = "{\"access_token\":\"ACCESS_TOKEN\",\"expires_in\":7200}"; | String json = "{\"access_token\":\"ACCESS_TOKEN\",\"expires_in\":7200}"; | ||||
@@ -33,5 +33,5 @@ public class WxErrorTest { | |||||
Assert.assertEquals(wxError.getErrorMsg(), null); | Assert.assertEquals(wxError.getErrorMsg(), null); | ||||
} | } | ||||
} | } |
@@ -1,58 +1,61 @@ | |||||
package me.chanjar.weixin.common.bean; | package me.chanjar.weixin.common.bean; | ||||
import me.chanjar.weixin.common.bean.WxMenu.WxMenuButton; | |||||
import org.testng.Assert; | import org.testng.Assert; | ||||
import org.testng.annotations.DataProvider; | import org.testng.annotations.DataProvider; | ||||
import org.testng.annotations.Test; | import org.testng.annotations.Test; | ||||
import me.chanjar.weixin.common.bean.menu.WxMenu; | |||||
import me.chanjar.weixin.common.bean.menu.WxMenuButton; | |||||
import me.chanjar.weixin.common.bean.menu.WxMenuRule; | |||||
@Test | @Test | ||||
public class WxMenuTest { | public class WxMenuTest { | ||||
@Test(dataProvider="wxReturnMenu") | |||||
@Test(dataProvider = "wxReturnMenu") | |||||
public void testFromJson(String json) { | public void testFromJson(String json) { | ||||
WxMenu menu = WxMenu.fromJson(json); | WxMenu menu = WxMenu.fromJson(json); | ||||
Assert.assertEquals(menu.getButtons().size(), 3); | Assert.assertEquals(menu.getButtons().size(), 3); | ||||
} | } | ||||
@Test(dataProvider="wxPushMenu") | |||||
@Test(dataProvider = "wxPushMenu") | |||||
public void testToJson(String json) { | public void testToJson(String json) { | ||||
WxMenu menu = new WxMenu(); | WxMenu menu = new WxMenu(); | ||||
WxMenuButton button1 = new WxMenuButton(); | WxMenuButton button1 = new WxMenuButton(); | ||||
button1.setType("click"); | button1.setType("click"); | ||||
button1.setName("今日歌曲"); | button1.setName("今日歌曲"); | ||||
button1.setKey("V1001_TODAY_MUSIC"); | button1.setKey("V1001_TODAY_MUSIC"); | ||||
WxMenuButton button2 = new WxMenuButton(); | WxMenuButton button2 = new WxMenuButton(); | ||||
button2.setType("click"); | button2.setType("click"); | ||||
button2.setName("歌手简介"); | button2.setName("歌手简介"); | ||||
button2.setKey("V1001_TODAY_SINGER"); | button2.setKey("V1001_TODAY_SINGER"); | ||||
WxMenuButton button3 = new WxMenuButton(); | WxMenuButton button3 = new WxMenuButton(); | ||||
button3.setName("菜单"); | button3.setName("菜单"); | ||||
menu.getButtons().add(button1); | menu.getButtons().add(button1); | ||||
menu.getButtons().add(button2); | menu.getButtons().add(button2); | ||||
menu.getButtons().add(button3); | menu.getButtons().add(button3); | ||||
WxMenuButton button31 = new WxMenuButton(); | WxMenuButton button31 = new WxMenuButton(); | ||||
button31.setType("view"); | button31.setType("view"); | ||||
button31.setName("搜索"); | button31.setName("搜索"); | ||||
button31.setUrl("http://www.soso.com/"); | button31.setUrl("http://www.soso.com/"); | ||||
WxMenuButton button32 = new WxMenuButton(); | WxMenuButton button32 = new WxMenuButton(); | ||||
button32.setType("view"); | button32.setType("view"); | ||||
button32.setName("视频"); | button32.setName("视频"); | ||||
button32.setUrl("http://v.qq.com/"); | button32.setUrl("http://v.qq.com/"); | ||||
WxMenuButton button33 = new WxMenuButton(); | WxMenuButton button33 = new WxMenuButton(); | ||||
button33.setType("click"); | button33.setType("click"); | ||||
button33.setName("赞一下我们"); | button33.setName("赞一下我们"); | ||||
button33.setKey("V1001_GOOD"); | button33.setKey("V1001_GOOD"); | ||||
button3.getSubButtons().add(button31); | button3.getSubButtons().add(button31); | ||||
button3.getSubButtons().add(button32); | button3.getSubButtons().add(button32); | ||||
button3.getSubButtons().add(button33); | button3.getSubButtons().add(button33); | ||||
Assert.assertEquals(menu.toJson(), json); | Assert.assertEquals(menu.toJson(), json); | ||||
} | } | ||||
@@ -66,7 +69,7 @@ public class WxMenuTest { | |||||
menu.getButtons().add(button1); | menu.getButtons().add(button1); | ||||
WxMenu.WxMenuRule wxMenuRule = new WxMenu.WxMenuRule(); | |||||
WxMenuRule wxMenuRule = new WxMenuRule(); | |||||
wxMenuRule.setTagId("2"); | wxMenuRule.setTagId("2"); | ||||
wxMenuRule.setSex("1"); | wxMenuRule.setSex("1"); | ||||
wxMenuRule.setCountry("中国"); | wxMenuRule.setCountry("中国"); | ||||
@@ -78,82 +81,82 @@ public class WxMenuTest { | |||||
Assert.assertEquals(menu.toJson(), json); | Assert.assertEquals(menu.toJson(), json); | ||||
} | } | ||||
@DataProvider | @DataProvider | ||||
public Object[][] wxReturnMenu() { | public Object[][] wxReturnMenu() { | ||||
Object[][] res = menuJson(); | |||||
Object[][] res = menuJson(); | |||||
String json = "{ \"menu\" : " + res[0][0] + " }"; | String json = "{ \"menu\" : " + res[0][0] + " }"; | ||||
return new Object[][] { | |||||
new Object[] { json } | |||||
return new Object[][]{ | |||||
new Object[]{json} | |||||
}; | }; | ||||
} | } | ||||
@DataProvider(name="wxPushMenu") | |||||
@DataProvider(name = "wxPushMenu") | |||||
public Object[][] menuJson() { | public Object[][] menuJson() { | ||||
String json = | |||||
"{" | |||||
+"\"button\":[" | |||||
+"{" | |||||
+"\"type\":\"click\"," | |||||
+"\"name\":\"今日歌曲\"," | |||||
+"\"key\":\"V1001_TODAY_MUSIC\"" | |||||
+"}," | |||||
+"{" | |||||
+"\"type\":\"click\"," | |||||
+"\"name\":\"歌手简介\"," | |||||
+"\"key\":\"V1001_TODAY_SINGER\"" | |||||
+"}," | |||||
+"{" | |||||
+"\"name\":\"菜单\"," | |||||
+"\"sub_button\":[" | |||||
+"{" | |||||
+"\"type\":\"view\"," | |||||
+"\"name\":\"搜索\"," | |||||
+"\"url\":\"http://www.soso.com/\"" | |||||
+"}," | |||||
+"{" | |||||
+"\"type\":\"view\"," | |||||
+"\"name\":\"视频\"," | |||||
+"\"url\":\"http://v.qq.com/\"" | |||||
+"}," | |||||
+"{" | |||||
+"\"type\":\"click\"," | |||||
+"\"name\":\"赞一下我们\"," | |||||
+"\"key\":\"V1001_GOOD\"" | |||||
+"}" | |||||
+"]" | |||||
+"}" | |||||
+"]" | |||||
+"}"; | |||||
return new Object[][] { | |||||
new Object[] { json } | |||||
String json = | |||||
"{" | |||||
+ "\"button\":[" | |||||
+ "{" | |||||
+ "\"type\":\"click\"," | |||||
+ "\"name\":\"今日歌曲\"," | |||||
+ "\"key\":\"V1001_TODAY_MUSIC\"" | |||||
+ "}," | |||||
+ "{" | |||||
+ "\"type\":\"click\"," | |||||
+ "\"name\":\"歌手简介\"," | |||||
+ "\"key\":\"V1001_TODAY_SINGER\"" | |||||
+ "}," | |||||
+ "{" | |||||
+ "\"name\":\"菜单\"," | |||||
+ "\"sub_button\":[" | |||||
+ "{" | |||||
+ "\"type\":\"view\"," | |||||
+ "\"name\":\"搜索\"," | |||||
+ "\"url\":\"http://www.soso.com/\"" | |||||
+ "}," | |||||
+ "{" | |||||
+ "\"type\":\"view\"," | |||||
+ "\"name\":\"视频\"," | |||||
+ "\"url\":\"http://v.qq.com/\"" | |||||
+ "}," | |||||
+ "{" | |||||
+ "\"type\":\"click\"," | |||||
+ "\"name\":\"赞一下我们\"," | |||||
+ "\"key\":\"V1001_GOOD\"" | |||||
+ "}" | |||||
+ "]" | |||||
+ "}" | |||||
+ "]" | |||||
+ "}"; | |||||
return new Object[][]{ | |||||
new Object[]{json} | |||||
}; | }; | ||||
} | } | ||||
@DataProvider(name = "wxAddConditionalMenu") | @DataProvider(name = "wxAddConditionalMenu") | ||||
public Object[][] addConditionalMenuJson(){ | |||||
public Object[][] addConditionalMenuJson() { | |||||
String json = | String json = | ||||
"{" | |||||
+"\"button\":[" | |||||
+"{" | |||||
+"\"type\":\"click\"," | |||||
+"\"name\":\"今日歌曲\"," | |||||
+"\"key\":\"V1001_TODAY_MUSIC\"" | |||||
+"}" | |||||
+"]," | |||||
+"\"matchrule\":{" | |||||
+"\"group_id\":\"2\"," | |||||
+"\"sex\":\"1\"," | |||||
+"\"country\":\"中国\"," | |||||
+"\"province\":\"广东\"," | |||||
+"\"city\":\"广州\"," | |||||
+"\"client_platform_type\":\"2\"," | |||||
+"\"language\":\"zh_CN\"" | |||||
+"}" | |||||
+"}"; | |||||
"{" | |||||
+ "\"button\":[" | |||||
+ "{" | |||||
+ "\"type\":\"click\"," | |||||
+ "\"name\":\"今日歌曲\"," | |||||
+ "\"key\":\"V1001_TODAY_MUSIC\"" | |||||
+ "}" | |||||
+ "]," | |||||
+ "\"matchrule\":{" | |||||
+ "\"group_id\":\"2\"," | |||||
+ "\"sex\":\"1\"," | |||||
+ "\"country\":\"中国\"," | |||||
+ "\"province\":\"广东\"," | |||||
+ "\"city\":\"广州\"," | |||||
+ "\"client_platform_type\":\"2\"," | |||||
+ "\"language\":\"zh_CN\"" | |||||
+ "}" | |||||
+ "}"; | |||||
return new Object[][]{ | return new Object[][]{ | ||||
new Object[]{json} | |||||
new Object[]{json} | |||||
}; | }; | ||||
} | } | ||||
} | } |
@@ -10,8 +10,8 @@ public class SessionTest { | |||||
@DataProvider | @DataProvider | ||||
public Object[][] getSessionManager() { | public Object[][] getSessionManager() { | ||||
return new Object[][] { | |||||
new Object[] { new StandardSessionManager() } | |||||
return new Object[][]{ | |||||
new Object[]{new StandardSessionManager()} | |||||
}; | }; | ||||
} | } | ||||
@@ -8,7 +8,7 @@ import org.testng.annotations.Test; | |||||
public class WxMessageInMemoryDuplicateCheckerTest { | public class WxMessageInMemoryDuplicateCheckerTest { | ||||
public void test() throws InterruptedException { | public void test() throws InterruptedException { | ||||
Long[] msgIds = new Long[] { 1l, 2l, 3l, 4l, 5l, 6l, 7l, 8l }; | |||||
Long[] msgIds = new Long[]{1l, 2l, 3l, 4l, 5l, 6l, 7l, 8l}; | |||||
WxMessageInMemoryDuplicateChecker checker = new WxMessageInMemoryDuplicateChecker(2000l, 1000l); | WxMessageInMemoryDuplicateChecker checker = new WxMessageInMemoryDuplicateChecker(2000l, 1000l); | ||||
// 第一次检查 | // 第一次检查 | ||||
@@ -45,16 +45,24 @@ public class WxCryptUtilTest { | |||||
Element root = document.getDocumentElement(); | Element root = document.getDocumentElement(); | ||||
String cipherText = root.getElementsByTagName("Encrypt").item(0).getTextContent(); | String cipherText = root.getElementsByTagName("Encrypt").item(0).getTextContent(); | ||||
System.out.println(cipherText); | |||||
String msgSignature = root.getElementsByTagName("MsgSignature").item(0).getTextContent(); | String msgSignature = root.getElementsByTagName("MsgSignature").item(0).getTextContent(); | ||||
System.out.println(msgSignature); | |||||
String timestamp = root.getElementsByTagName("TimeStamp").item(0).getTextContent(); | String timestamp = root.getElementsByTagName("TimeStamp").item(0).getTextContent(); | ||||
System.out.println(timestamp); | |||||
String nonce = root.getElementsByTagName("Nonce").item(0).getTextContent(); | String nonce = root.getElementsByTagName("Nonce").item(0).getTextContent(); | ||||
System.out.println(nonce); | |||||
String messageText = String.format(xmlFormat, cipherText); | String messageText = String.format(xmlFormat, cipherText); | ||||
System.out.println(messageText); | |||||
// 第三方收到企业号平台发送的消息 | // 第三方收到企业号平台发送的消息 | ||||
String plainMessage = pc.decrypt(cipherText); | String plainMessage = pc.decrypt(cipherText); | ||||
System.out.println(plainMessage); | System.out.println(plainMessage); | ||||
assertEquals(plainMessage, replyMsg); | assertEquals(plainMessage, replyMsg); | ||||
} | } | ||||
@@ -69,7 +77,7 @@ public class WxCryptUtilTest { | |||||
} | } | ||||
public void testValidateSignatureError() throws ParserConfigurationException, SAXException, | public void testValidateSignatureError() throws ParserConfigurationException, SAXException, | ||||
IOException { | |||||
IOException { | |||||
try { | try { | ||||
WxCryptUtil pc = new WxCryptUtil(token, encodingAesKey, appId); | WxCryptUtil pc = new WxCryptUtil(token, encodingAesKey, appId); | ||||
String afterEncrpt = pc.encrypt(replyMsg); | String afterEncrpt = pc.encrypt(replyMsg); | ||||
@@ -1,5 +1,6 @@ | |||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?> | |||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/2002/xmlspec/dtd/2.10/xmlspec.dtd"> | |||||
<configuration> | <configuration> | ||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> | <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> | ||||
<!-- encoders are assigned the type | <!-- encoders are assigned the type | ||||
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default --> | ch.qos.logback.classic.encoder.PatternLayoutEncoder by default --> | ||||
@@ -0,0 +1,12 @@ | |||||
description = 'WeiXin Java Tools - CP' | |||||
dependencies { | |||||
compile project(':weixin-java-common') | |||||
testCompile group: 'junit', name: 'junit', version:'4.11' | |||||
testCompile group: 'org.testng', name: 'testng', version:'6.8.7' | |||||
testCompile group: 'org.mockito', name: 'mockito-all', version:'1.9.5' | |||||
testCompile group: 'com.google.inject', name: 'guice', version:'3.0' | |||||
testCompile group: 'org.eclipse.jetty', name: 'jetty-server', version:'9.3.0.M0' | |||||
testCompile group: 'org.eclipse.jetty', name: 'jetty-servlet', version:'9.3.0.M0' | |||||
} | |||||
test.useTestNG() |
@@ -6,7 +6,7 @@ | |||||
<parent> | <parent> | ||||
<groupId>com.github.binarywang</groupId> | <groupId>com.github.binarywang</groupId> | ||||
<artifactId>weixin-java-parent</artifactId> | <artifactId>weixin-java-parent</artifactId> | ||||
<version>2.0.0</version> | |||||
<version>2.1.0</version> | |||||
</parent> | </parent> | ||||
<artifactId>weixin-java-cp</artifactId> | <artifactId>weixin-java-cp</artifactId> | ||||
@@ -7,8 +7,8 @@ import java.io.File; | |||||
/** | /** | ||||
* 微信客户端配置存储 | * 微信客户端配置存储 | ||||
* @author Daniel Qian | |||||
* | * | ||||
* @author Daniel Qian | |||||
*/ | */ | ||||
public interface WxCpConfigStorage { | public interface WxCpConfigStorage { | ||||
@@ -36,12 +36,13 @@ public interface WxCpConfigStorage { | |||||
/** | /** | ||||
* 应该是线程安全的 | * 应该是线程安全的 | ||||
* | |||||
* @param jsapiTicket | * @param jsapiTicket | ||||
*/ | */ | ||||
void updateJsapiTicket(String jsapiTicket, int expiresInSeconds); | void updateJsapiTicket(String jsapiTicket, int expiresInSeconds); | ||||
String getCorpId(); | String getCorpId(); | ||||
String getCorpSecret(); | String getCorpSecret(); | ||||
String getAgentId(); | String getAgentId(); | ||||
@@ -61,11 +62,12 @@ public interface WxCpConfigStorage { | |||||
String getHttp_proxy_username(); | String getHttp_proxy_username(); | ||||
String getHttp_proxy_password(); | String getHttp_proxy_password(); | ||||
File getTmpDirFile(); | File getTmpDirFile(); | ||||
/** | /** | ||||
* http client builder | * http client builder | ||||
* | |||||
* @return ApacheHttpClientBuilder | * @return ApacheHttpClientBuilder | ||||
*/ | */ | ||||
ApacheHttpClientBuilder getApacheHttpClientBuilder(); | ApacheHttpClientBuilder getApacheHttpClientBuilder(); | ||||
@@ -7,8 +7,8 @@ import java.io.File; | |||||
/** | /** | ||||
* 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化 | * 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化 | ||||
* @author Daniel Qian | |||||
* | * | ||||
* @author Daniel Qian | |||||
*/ | */ | ||||
public class WxCpInMemoryConfigStorage implements WxCpConfigStorage { | public class WxCpInMemoryConfigStorage implements WxCpConfigStorage { | ||||
@@ -39,6 +39,10 @@ public class WxCpInMemoryConfigStorage implements WxCpConfigStorage { | |||||
return this.accessToken; | return this.accessToken; | ||||
} | } | ||||
public void setAccessToken(String accessToken) { | |||||
this.accessToken = accessToken; | |||||
} | |||||
public boolean isAccessTokenExpired() { | public boolean isAccessTokenExpired() { | ||||
return System.currentTimeMillis() > this.expiresTime; | return System.currentTimeMillis() > this.expiresTime; | ||||
} | } | ||||
@@ -50,7 +54,7 @@ public class WxCpInMemoryConfigStorage implements WxCpConfigStorage { | |||||
public synchronized void updateAccessToken(WxAccessToken accessToken) { | public synchronized void updateAccessToken(WxAccessToken accessToken) { | ||||
updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); | updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); | ||||
} | } | ||||
public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) { | public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) { | ||||
this.accessToken = accessToken; | this.accessToken = accessToken; | ||||
this.expiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000l; | this.expiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000l; | ||||
@@ -91,28 +95,32 @@ public class WxCpInMemoryConfigStorage implements WxCpConfigStorage { | |||||
return this.corpId; | return this.corpId; | ||||
} | } | ||||
public void setCorpId(String corpId) { | |||||
this.corpId = corpId; | |||||
} | |||||
public String getCorpSecret() { | public String getCorpSecret() { | ||||
return this.corpSecret; | return this.corpSecret; | ||||
} | } | ||||
public String getToken() { | |||||
return this.token; | |||||
public void setCorpSecret(String corpSecret) { | |||||
this.corpSecret = corpSecret; | |||||
} | } | ||||
public long getExpiresTime() { | |||||
return this.expiresTime; | |||||
public String getToken() { | |||||
return this.token; | |||||
} | } | ||||
public void setCorpId(String corpId) { | |||||
this.corpId = corpId; | |||||
public void setToken(String token) { | |||||
this.token = token; | |||||
} | } | ||||
public void setCorpSecret(String corpSecret) { | |||||
this.corpSecret = corpSecret; | |||||
public long getExpiresTime() { | |||||
return this.expiresTime; | |||||
} | } | ||||
public void setToken(String token) { | |||||
this.token = token; | |||||
public void setExpiresTime(long expiresTime) { | |||||
this.expiresTime = expiresTime; | |||||
} | } | ||||
public String getAesKey() { | public String getAesKey() { | ||||
@@ -123,14 +131,6 @@ public class WxCpInMemoryConfigStorage implements WxCpConfigStorage { | |||||
this.aesKey = aesKey; | this.aesKey = aesKey; | ||||
} | } | ||||
public void setAccessToken(String accessToken) { | |||||
this.accessToken = accessToken; | |||||
} | |||||
public void setExpiresTime(long expiresTime) { | |||||
this.expiresTime = expiresTime; | |||||
} | |||||
public String getAgentId() { | public String getAgentId() { | ||||
return agentId; | return agentId; | ||||
} | } | ||||
@@ -183,21 +183,21 @@ public class WxCpInMemoryConfigStorage implements WxCpConfigStorage { | |||||
@Override | @Override | ||||
public String toString() { | public String toString() { | ||||
return "WxCpInMemoryConfigStorage{" + | return "WxCpInMemoryConfigStorage{" + | ||||
"corpId='" + corpId + '\'' + | |||||
", corpSecret='" + corpSecret + '\'' + | |||||
", token='" + token + '\'' + | |||||
", accessToken='" + accessToken + '\'' + | |||||
", aesKey='" + aesKey + '\'' + | |||||
", agentId='" + agentId + '\'' + | |||||
", expiresTime=" + expiresTime + | |||||
", http_proxy_host='" + http_proxy_host + '\'' + | |||||
", http_proxy_port=" + http_proxy_port + | |||||
", http_proxy_username='" + http_proxy_username + '\'' + | |||||
", http_proxy_password='" + http_proxy_password + '\'' + | |||||
", jsapiTicket='" + jsapiTicket + '\'' + | |||||
", jsapiTicketExpiresTime='" + jsapiTicketExpiresTime + '\'' + | |||||
", tmpDirFile='" + tmpDirFile + '\'' + | |||||
'}'; | |||||
"corpId='" + corpId + '\'' + | |||||
", corpSecret='" + corpSecret + '\'' + | |||||
", token='" + token + '\'' + | |||||
", accessToken='" + accessToken + '\'' + | |||||
", aesKey='" + aesKey + '\'' + | |||||
", agentId='" + agentId + '\'' + | |||||
", expiresTime=" + expiresTime + | |||||
", http_proxy_host='" + http_proxy_host + '\'' + | |||||
", http_proxy_port=" + http_proxy_port + | |||||
", http_proxy_username='" + http_proxy_username + '\'' + | |||||
", http_proxy_password='" + http_proxy_password + '\'' + | |||||
", jsapiTicket='" + jsapiTicket + '\'' + | |||||
", jsapiTicketExpiresTime='" + jsapiTicketExpiresTime + '\'' + | |||||
", tmpDirFile='" + tmpDirFile + '\'' + | |||||
'}'; | |||||
} | } | ||||
public File getTmpDirFile() { | public File getTmpDirFile() { | ||||
@@ -0,0 +1,267 @@ | |||||
package me.chanjar.weixin.cp.api; | |||||
import java.io.File; | |||||
import me.chanjar.weixin.common.bean.WxAccessToken; | |||||
import me.chanjar.weixin.common.util.http.ApacheHttpClientBuilder; | |||||
import redis.clients.jedis.Jedis; | |||||
import redis.clients.jedis.JedisPool; | |||||
/** | |||||
* Jedis client implementor for wechat config storage | |||||
* | |||||
* @author gaigeshen | |||||
*/ | |||||
public class WxCpJedisConfigStorage implements WxCpConfigStorage { | |||||
/* Redis keys here */ | |||||
private static final String ACCESS_TOKEN_KEY = "WX_CP_ACCESS_TOKEN"; | |||||
private static final String ACCESS_TOKEN_EXPIRES_TIME_KEY = "WX_CP_ACCESS_TOKEN_EXPIRES_TIME"; | |||||
private static final String JS_API_TICKET_KEY = "WX_CP_JS_API_TICKET"; | |||||
private static final String JS_API_TICKET_EXPIRES_TIME_KEY = "WX_CP_JS_API_TICKET_EXPIRES_TIME"; | |||||
private volatile String corpId; | |||||
private volatile String corpSecret; | |||||
private volatile String token; | |||||
private volatile String aesKey; | |||||
private volatile String agentId; | |||||
private volatile String oauth2redirectUri; | |||||
private volatile String http_proxy_host; | |||||
private volatile int http_proxy_port; | |||||
private volatile String http_proxy_username; | |||||
private volatile String http_proxy_password; | |||||
private volatile File tmpDirFile; | |||||
private volatile ApacheHttpClientBuilder apacheHttpClientBuilder; | |||||
/* Redis clients pool */ | |||||
private final JedisPool jedisPool; | |||||
public WxCpJedisConfigStorage(String host, int port) { | |||||
this.jedisPool = new JedisPool(host, port); | |||||
} | |||||
/** | |||||
* | |||||
* This method will be destroy jedis pool | |||||
*/ | |||||
public void destroy() { | |||||
this.jedisPool.destroy(); | |||||
} | |||||
@Override | |||||
public String getAccessToken() { | |||||
try (Jedis jedis = this.jedisPool.getResource()) { | |||||
return jedis.get(ACCESS_TOKEN_KEY); | |||||
} | |||||
} | |||||
@Override | |||||
public boolean isAccessTokenExpired() { | |||||
try (Jedis jedis = this.jedisPool.getResource()) { | |||||
String expiresTimeStr = jedis.get(ACCESS_TOKEN_EXPIRES_TIME_KEY); | |||||
if (expiresTimeStr != null) { | |||||
Long expiresTime = Long.parseLong(expiresTimeStr); | |||||
return System.currentTimeMillis() > expiresTime; | |||||
} | |||||
return true; | |||||
} | |||||
} | |||||
@Override | |||||
public void expireAccessToken() { | |||||
try (Jedis jedis = this.jedisPool.getResource()) { | |||||
jedis.set(ACCESS_TOKEN_EXPIRES_TIME_KEY, "0"); | |||||
} | |||||
} | |||||
@Override | |||||
public synchronized void updateAccessToken(WxAccessToken accessToken) { | |||||
this.updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); | |||||
} | |||||
@Override | |||||
public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) { | |||||
try (Jedis jedis = this.jedisPool.getResource()) { | |||||
jedis.set(ACCESS_TOKEN_KEY, accessToken); | |||||
jedis.set(ACCESS_TOKEN_EXPIRES_TIME_KEY, | |||||
(System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L) + ""); | |||||
} | |||||
} | |||||
@Override | |||||
public String getJsapiTicket() { | |||||
try (Jedis jedis = this.jedisPool.getResource()) { | |||||
return jedis.get(JS_API_TICKET_KEY); | |||||
} | |||||
} | |||||
@Override | |||||
public boolean isJsapiTicketExpired() { | |||||
try (Jedis jedis = this.jedisPool.getResource()) { | |||||
String expiresTimeStr = jedis.get(JS_API_TICKET_EXPIRES_TIME_KEY); | |||||
if (expiresTimeStr != null) { | |||||
Long expiresTime = Long.parseLong(expiresTimeStr); | |||||
return System.currentTimeMillis() > expiresTime; | |||||
} | |||||
return true; | |||||
} | |||||
} | |||||
@Override | |||||
public void expireJsapiTicket() { | |||||
try (Jedis jedis = this.jedisPool.getResource()) { | |||||
jedis.set(JS_API_TICKET_EXPIRES_TIME_KEY, "0"); | |||||
} | |||||
} | |||||
@Override | |||||
public synchronized void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) { | |||||
try (Jedis jedis = this.jedisPool.getResource()) { | |||||
jedis.set(JS_API_TICKET_KEY, jsapiTicket); | |||||
jedis.set(JS_API_TICKET_EXPIRES_TIME_KEY, | |||||
(System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L + "")); | |||||
} | |||||
} | |||||
@Override | |||||
public String getCorpId() { | |||||
return this.corpId; | |||||
} | |||||
@Override | |||||
public String getCorpSecret() { | |||||
return this.corpSecret; | |||||
} | |||||
@Override | |||||
public String getAgentId() { | |||||
return this.agentId; | |||||
} | |||||
@Override | |||||
public String getToken() { | |||||
return this.token; | |||||
} | |||||
@Override | |||||
public String getAesKey() { | |||||
return this.aesKey; | |||||
} | |||||
@Override | |||||
public long getExpiresTime() { | |||||
try (Jedis jedis = this.jedisPool.getResource()) { | |||||
String expiresTimeStr = jedis.get(ACCESS_TOKEN_EXPIRES_TIME_KEY); | |||||
if (expiresTimeStr != null) { | |||||
Long expiresTime = Long.parseLong(expiresTimeStr); | |||||
return expiresTime; | |||||
} | |||||
return 0L; | |||||
} | |||||
} | |||||
@Override | |||||
public String getOauth2redirectUri() { | |||||
return this.oauth2redirectUri; | |||||
} | |||||
@Override | |||||
public String getHttp_proxy_host() { | |||||
return this.http_proxy_host; | |||||
} | |||||
@Override | |||||
public int getHttp_proxy_port() { | |||||
return this.http_proxy_port; | |||||
} | |||||
@Override | |||||
public String getHttp_proxy_username() { | |||||
return this.http_proxy_username; | |||||
} | |||||
@Override | |||||
public String getHttp_proxy_password() { | |||||
return this.http_proxy_password; | |||||
} | |||||
@Override | |||||
public File getTmpDirFile() { | |||||
return this.tmpDirFile; | |||||
} | |||||
@Override | |||||
public ApacheHttpClientBuilder getApacheHttpClientBuilder() { | |||||
return this.apacheHttpClientBuilder; | |||||
} | |||||
public void setCorpId(String corpId) { | |||||
this.corpId = corpId; | |||||
} | |||||
public void setCorpSecret(String corpSecret) { | |||||
this.corpSecret = corpSecret; | |||||
} | |||||
public void setToken(String token) { | |||||
this.token = token; | |||||
} | |||||
public void setAesKey(String aesKey) { | |||||
this.aesKey = aesKey; | |||||
} | |||||
public void setAgentId(String agentId) { | |||||
this.agentId = agentId; | |||||
} | |||||
// ============================ Setters below | |||||
public void setOauth2redirectUri(String oauth2redirectUri) { | |||||
this.oauth2redirectUri = oauth2redirectUri; | |||||
} | |||||
public void setHttp_proxy_host(String http_proxy_host) { | |||||
this.http_proxy_host = http_proxy_host; | |||||
} | |||||
public void setHttp_proxy_port(int http_proxy_port) { | |||||
this.http_proxy_port = http_proxy_port; | |||||
} | |||||
public void setHttp_proxy_username(String http_proxy_username) { | |||||
this.http_proxy_username = http_proxy_username; | |||||
} | |||||
public void setHttp_proxy_password(String http_proxy_password) { | |||||
this.http_proxy_password = http_proxy_password; | |||||
} | |||||
public void setTmpDirFile(File tmpDirFile) { | |||||
this.tmpDirFile = tmpDirFile; | |||||
} | |||||
public void setApacheHttpClientBuilder(ApacheHttpClientBuilder apacheHttpClientBuilder) { | |||||
this.apacheHttpClientBuilder = apacheHttpClientBuilder; | |||||
} | |||||
} |
@@ -1,7 +1,6 @@ | |||||
package me.chanjar.weixin.cp.api; | package me.chanjar.weixin.cp.api; | ||||
import me.chanjar.weixin.common.exception.WxErrorException; | import me.chanjar.weixin.common.exception.WxErrorException; | ||||
import me.chanjar.weixin.common.session.WxSession; | |||||
import me.chanjar.weixin.common.session.WxSessionManager; | import me.chanjar.weixin.common.session.WxSessionManager; | ||||
import me.chanjar.weixin.cp.bean.WxCpXmlMessage; | import me.chanjar.weixin.cp.bean.WxCpXmlMessage; | ||||
import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage; | import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage; | ||||
@@ -1,13 +1,13 @@ | |||||
package me.chanjar.weixin.cp.api; | package me.chanjar.weixin.cp.api; | ||||
import me.chanjar.weixin.common.api.WxErrorExceptionHandler; | |||||
import me.chanjar.weixin.common.api.WxMessageDuplicateChecker; | |||||
import me.chanjar.weixin.common.api.WxMessageInMemoryDuplicateChecker; | |||||
import me.chanjar.weixin.common.session.InternalSession; | import me.chanjar.weixin.common.session.InternalSession; | ||||
import me.chanjar.weixin.common.session.InternalSessionManager; | import me.chanjar.weixin.common.session.InternalSessionManager; | ||||
import me.chanjar.weixin.common.session.StandardSessionManager; | import me.chanjar.weixin.common.session.StandardSessionManager; | ||||
import me.chanjar.weixin.common.session.WxSessionManager; | import me.chanjar.weixin.common.session.WxSessionManager; | ||||
import me.chanjar.weixin.common.util.LogExceptionHandler; | import me.chanjar.weixin.common.util.LogExceptionHandler; | ||||
import me.chanjar.weixin.common.api.WxErrorExceptionHandler; | |||||
import me.chanjar.weixin.common.api.WxMessageDuplicateChecker; | |||||
import me.chanjar.weixin.common.api.WxMessageInMemoryDuplicateChecker; | |||||
import me.chanjar.weixin.cp.bean.WxCpXmlMessage; | import me.chanjar.weixin.cp.bean.WxCpXmlMessage; | ||||
import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage; | import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage; | ||||
import org.slf4j.Logger; | import org.slf4j.Logger; | ||||
@@ -45,15 +45,13 @@ import java.util.concurrent.Future; | |||||
* router.route(message); | * router.route(message); | ||||
* | * | ||||
* </pre> | * </pre> | ||||
* @author Daniel Qian | |||||
* | * | ||||
* @author Daniel Qian | |||||
*/ | */ | ||||
public class WxCpMessageRouter { | public class WxCpMessageRouter { | ||||
protected final Logger log = LoggerFactory.getLogger(WxCpMessageRouter.class); | |||||
private static final int DEFAULT_THREAD_POOL_SIZE = 100; | private static final int DEFAULT_THREAD_POOL_SIZE = 100; | ||||
protected final Logger log = LoggerFactory.getLogger(WxCpMessageRouter.class); | |||||
private final List<WxCpMessageRouterRule> rules = new ArrayList<WxCpMessageRouterRule>(); | private final List<WxCpMessageRouterRule> rules = new ArrayList<WxCpMessageRouterRule>(); | ||||
private final WxCpService wxCpService; | private final WxCpService wxCpService; | ||||
@@ -79,6 +77,7 @@ public class WxCpMessageRouter { | |||||
* 设置自定义的 {@link ExecutorService} | * 设置自定义的 {@link ExecutorService} | ||||
* 如果不调用该方法,默认使用 Executors.newFixedThreadPool(100) | * 如果不调用该方法,默认使用 Executors.newFixedThreadPool(100) | ||||
* </pre> | * </pre> | ||||
* | |||||
* @param executorService | * @param executorService | ||||
*/ | */ | ||||
public void setExecutorService(ExecutorService executorService) { | public void setExecutorService(ExecutorService executorService) { | ||||
@@ -90,6 +89,7 @@ public class WxCpMessageRouter { | |||||
* 设置自定义的 {@link me.chanjar.weixin.common.api.WxMessageDuplicateChecker} | * 设置自定义的 {@link me.chanjar.weixin.common.api.WxMessageDuplicateChecker} | ||||
* 如果不调用该方法,默认使用 {@link me.chanjar.weixin.common.api.WxMessageInMemoryDuplicateChecker} | * 如果不调用该方法,默认使用 {@link me.chanjar.weixin.common.api.WxMessageInMemoryDuplicateChecker} | ||||
* </pre> | * </pre> | ||||
* | |||||
* @param messageDuplicateChecker | * @param messageDuplicateChecker | ||||
*/ | */ | ||||
public void setMessageDuplicateChecker(WxMessageDuplicateChecker messageDuplicateChecker) { | public void setMessageDuplicateChecker(WxMessageDuplicateChecker messageDuplicateChecker) { | ||||
@@ -101,6 +101,7 @@ public class WxCpMessageRouter { | |||||
* 设置自定义的{@link me.chanjar.weixin.common.session.WxSessionManager} | * 设置自定义的{@link me.chanjar.weixin.common.session.WxSessionManager} | ||||
* 如果不调用该方法,默认使用 {@link me.chanjar.weixin.common.session.StandardSessionManager} | * 如果不调用该方法,默认使用 {@link me.chanjar.weixin.common.session.StandardSessionManager} | ||||
* </pre> | * </pre> | ||||
* | |||||
* @param sessionManager | * @param sessionManager | ||||
*/ | */ | ||||
public void setSessionManager(WxSessionManager sessionManager) { | public void setSessionManager(WxSessionManager sessionManager) { | ||||
@@ -112,6 +113,7 @@ public class WxCpMessageRouter { | |||||
* 设置自定义的{@link me.chanjar.weixin.common.api.WxErrorExceptionHandler} | * 设置自定义的{@link me.chanjar.weixin.common.api.WxErrorExceptionHandler} | ||||
* 如果不调用该方法,默认使用 {@link me.chanjar.weixin.common.util.LogExceptionHandler} | * 如果不调用该方法,默认使用 {@link me.chanjar.weixin.common.util.LogExceptionHandler} | ||||
* </pre> | * </pre> | ||||
* | |||||
* @param exceptionHandler | * @param exceptionHandler | ||||
*/ | */ | ||||
public void setExceptionHandler(WxErrorExceptionHandler exceptionHandler) { | public void setExceptionHandler(WxErrorExceptionHandler exceptionHandler) { | ||||
@@ -131,6 +133,7 @@ public class WxCpMessageRouter { | |||||
/** | /** | ||||
* 处理微信消息 | * 处理微信消息 | ||||
* | |||||
* @param wxMessage | * @param wxMessage | ||||
*/ | */ | ||||
public WxCpXmlOutMessage route(final WxCpXmlMessage wxMessage) { | public WxCpXmlOutMessage route(final WxCpXmlMessage wxMessage) { | ||||
@@ -144,7 +147,7 @@ public class WxCpMessageRouter { | |||||
for (final WxCpMessageRouterRule rule : rules) { | for (final WxCpMessageRouterRule rule : rules) { | ||||
if (rule.test(wxMessage)) { | if (rule.test(wxMessage)) { | ||||
matchRules.add(rule); | matchRules.add(rule); | ||||
if(!rule.isReEnter()) { | |||||
if (!rule.isReEnter()) { | |||||
break; | break; | ||||
} | } | ||||
} | } | ||||
@@ -158,13 +161,13 @@ public class WxCpMessageRouter { | |||||
final List<Future> futures = new ArrayList<Future>(); | final List<Future> futures = new ArrayList<Future>(); | ||||
for (final WxCpMessageRouterRule rule : matchRules) { | for (final WxCpMessageRouterRule rule : matchRules) { | ||||
// 返回最后一个非异步的rule的执行结果 | // 返回最后一个非异步的rule的执行结果 | ||||
if(rule.isAsync()) { | |||||
if (rule.isAsync()) { | |||||
futures.add( | futures.add( | ||||
executorService.submit(new Runnable() { | |||||
public void run() { | |||||
rule.service(wxMessage, wxCpService, sessionManager, exceptionHandler); | |||||
} | |||||
}) | |||||
executorService.submit(new Runnable() { | |||||
public void run() { | |||||
rule.service(wxMessage, wxCpService, sessionManager, exceptionHandler); | |||||
} | |||||
}) | |||||
); | ); | ||||
} else { | } else { | ||||
res = rule.service(wxMessage, wxCpService, sessionManager, exceptionHandler); | res = rule.service(wxMessage, wxCpService, sessionManager, exceptionHandler); | ||||
@@ -201,10 +204,10 @@ public class WxCpMessageRouter { | |||||
String messageId = ""; | String messageId = ""; | ||||
if (wxMessage.getMsgId() == null) { | if (wxMessage.getMsgId() == null) { | ||||
messageId = String.valueOf(wxMessage.getCreateTime()) | messageId = String.valueOf(wxMessage.getCreateTime()) | ||||
+ "-" +String.valueOf(wxMessage.getAgentId() == null ? "" : wxMessage.getAgentId()) | |||||
+ "-" + wxMessage.getFromUserName() | |||||
+ "-" + String.valueOf(wxMessage.getEventKey() == null ? "" : wxMessage.getEventKey()) | |||||
+ "-" + String.valueOf(wxMessage.getEvent() == null ? "" : wxMessage.getEvent()) | |||||
+ "-" + String.valueOf(wxMessage.getAgentId() == null ? "" : wxMessage.getAgentId()) | |||||
+ "-" + wxMessage.getFromUserName() | |||||
+ "-" + String.valueOf(wxMessage.getEventKey() == null ? "" : wxMessage.getEventKey()) | |||||
+ "-" + String.valueOf(wxMessage.getEvent() == null ? "" : wxMessage.getEvent()) | |||||
; | ; | ||||
} else { | } else { | ||||
messageId = String.valueOf(wxMessage.getMsgId()); | messageId = String.valueOf(wxMessage.getMsgId()); | ||||
@@ -216,11 +219,12 @@ public class WxCpMessageRouter { | |||||
/** | /** | ||||
* 对session的访问结束 | * 对session的访问结束 | ||||
* | |||||
* @param wxMessage | * @param wxMessage | ||||
*/ | */ | ||||
protected void sessionEndAccess(WxCpXmlMessage wxMessage) { | protected void sessionEndAccess(WxCpXmlMessage wxMessage) { | ||||
InternalSession session = ((InternalSessionManager)sessionManager).findSession(wxMessage.getFromUserName()); | |||||
InternalSession session = ((InternalSessionManager) sessionManager).findSession(wxMessage.getFromUserName()); | |||||
if (session != null) { | if (session != null) { | ||||
session.endAccess(); | session.endAccess(); | ||||
} | } | ||||
@@ -1,8 +1,8 @@ | |||||
package me.chanjar.weixin.cp.api; | package me.chanjar.weixin.cp.api; | ||||
import me.chanjar.weixin.common.api.WxErrorExceptionHandler; | |||||
import me.chanjar.weixin.common.exception.WxErrorException; | import me.chanjar.weixin.common.exception.WxErrorException; | ||||
import me.chanjar.weixin.common.session.WxSessionManager; | import me.chanjar.weixin.common.session.WxSessionManager; | ||||
import me.chanjar.weixin.common.api.WxErrorExceptionHandler; | |||||
import me.chanjar.weixin.cp.bean.WxCpXmlMessage; | import me.chanjar.weixin.cp.bean.WxCpXmlMessage; | ||||
import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage; | import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage; | ||||
@@ -186,7 +186,6 @@ public class WxCpMessageRouterRule { | |||||
/** | /** | ||||
* 规则结束,代表如果一个消息匹配该规则,那么它将不再会进入其他规则 | * 规则结束,代表如果一个消息匹配该规则,那么它将不再会进入其他规则 | ||||
* | |||||
*/ | */ | ||||
public WxCpMessageRouter end() { | public WxCpMessageRouter end() { | ||||
this.routerBuilder.getRules().add(this); | this.routerBuilder.getRules().add(this); | ||||
@@ -195,7 +194,6 @@ public class WxCpMessageRouterRule { | |||||
/** | /** | ||||
* 规则结束,但是消息还会进入其他规则 | * 规则结束,但是消息还会进入其他规则 | ||||
* | |||||
*/ | */ | ||||
public WxCpMessageRouter next() { | public WxCpMessageRouter next() { | ||||
this.reEnter = true; | this.reEnter = true; | ||||
@@ -204,24 +202,24 @@ public class WxCpMessageRouterRule { | |||||
protected boolean test(WxCpXmlMessage wxMessage) { | protected boolean test(WxCpXmlMessage wxMessage) { | ||||
return | return | ||||
(this.fromUser == null || this.fromUser.equals(wxMessage.getFromUserName())) | |||||
&& | |||||
(this.agentId == null || this.agentId.equals(wxMessage.getAgentId())) | |||||
&& | |||||
(this.msgType == null || this.msgType.equals(wxMessage.getMsgType())) | |||||
&& | |||||
(this.event == null || this.event.equals(wxMessage.getEvent())) | |||||
&& | |||||
(this.eventKey == null || this.eventKey.equals(wxMessage.getEventKey())) | |||||
&& | |||||
(this.content == null || this.content | |||||
.equals(wxMessage.getContent() == null ? null : wxMessage.getContent().trim())) | |||||
&& | |||||
(this.rContent == null || Pattern | |||||
.matches(this.rContent, wxMessage.getContent() == null ? "" : wxMessage.getContent().trim())) | |||||
&& | |||||
(this.matcher == null || this.matcher.match(wxMessage)) | |||||
; | |||||
(this.fromUser == null || this.fromUser.equals(wxMessage.getFromUserName())) | |||||
&& | |||||
(this.agentId == null || this.agentId.equals(wxMessage.getAgentId())) | |||||
&& | |||||
(this.msgType == null || this.msgType.equals(wxMessage.getMsgType())) | |||||
&& | |||||
(this.event == null || this.event.equals(wxMessage.getEvent())) | |||||
&& | |||||
(this.eventKey == null || this.eventKey.equals(wxMessage.getEventKey())) | |||||
&& | |||||
(this.content == null || this.content | |||||
.equals(wxMessage.getContent() == null ? null : wxMessage.getContent().trim())) | |||||
&& | |||||
(this.rContent == null || Pattern | |||||
.matches(this.rContent, wxMessage.getContent() == null ? "" : wxMessage.getContent().trim())) | |||||
&& | |||||
(this.matcher == null || this.matcher.match(wxMessage)) | |||||
; | |||||
} | } | ||||
/** | /** | ||||
@@ -231,9 +229,9 @@ public class WxCpMessageRouterRule { | |||||
* @return true 代表继续执行别的router,false 代表停止执行别的router | * @return true 代表继续执行别的router,false 代表停止执行别的router | ||||
*/ | */ | ||||
protected WxCpXmlOutMessage service(WxCpXmlMessage wxMessage, | protected WxCpXmlOutMessage service(WxCpXmlMessage wxMessage, | ||||
WxCpService wxCpService, | |||||
WxSessionManager sessionManager, | |||||
WxErrorExceptionHandler exceptionHandler) { | |||||
WxCpService wxCpService, | |||||
WxSessionManager sessionManager, | |||||
WxErrorExceptionHandler exceptionHandler) { | |||||
try { | try { | ||||
@@ -1,7 +1,7 @@ | |||||
package me.chanjar.weixin.cp.api; | package me.chanjar.weixin.cp.api; | ||||
import me.chanjar.weixin.common.bean.WxJsapiSignature; | import me.chanjar.weixin.common.bean.WxJsapiSignature; | ||||
import me.chanjar.weixin.common.bean.WxMenu; | |||||
import me.chanjar.weixin.common.bean.menu.WxMenu; | |||||
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | ||||
import me.chanjar.weixin.common.exception.WxErrorException; | import me.chanjar.weixin.common.exception.WxErrorException; | ||||
import me.chanjar.weixin.common.session.WxSession; | import me.chanjar.weixin.common.session.WxSession; | ||||
@@ -47,8 +47,9 @@ public interface WxCpService { | |||||
/** | /** | ||||
* 获取access_token, 不强制刷新access_token | * 获取access_token, 不强制刷新access_token | ||||
* @see #getAccessToken(boolean) | |||||
* | |||||
* @throws WxErrorException | * @throws WxErrorException | ||||
* @see #getAccessToken(boolean) | |||||
*/ | */ | ||||
String getAccessToken() throws WxErrorException; | String getAccessToken() throws WxErrorException; | ||||
@@ -60,6 +61,7 @@ public interface WxCpService { | |||||
* 程序员在非必要情况下尽量不要主动调用此方法 | * 程序员在非必要情况下尽量不要主动调用此方法 | ||||
* 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=获取access_token | * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=获取access_token | ||||
* </pre> | * </pre> | ||||
* | |||||
* @param forceRefresh 强制刷新 | * @param forceRefresh 强制刷新 | ||||
* @throws me.chanjar.weixin.common.exception.WxErrorException | * @throws me.chanjar.weixin.common.exception.WxErrorException | ||||
*/ | */ | ||||
@@ -67,8 +69,9 @@ public interface WxCpService { | |||||
/** | /** | ||||
* 获得jsapi_ticket,不强制刷新jsapi_ticket | * 获得jsapi_ticket,不强制刷新jsapi_ticket | ||||
* @see #getJsapiTicket(boolean) | |||||
* | |||||
* @throws WxErrorException | * @throws WxErrorException | ||||
* @see #getJsapiTicket(boolean) | |||||
*/ | */ | ||||
String getJsapiTicket() throws WxErrorException; | String getJsapiTicket() throws WxErrorException; | ||||
@@ -79,6 +82,7 @@ public interface WxCpService { | |||||
* | * | ||||
* 详情请见:http://qydev.weixin.qq.com/wiki/index.php?title=微信JS接口#.E9.99.84.E5.BD.951-JS-SDK.E4.BD.BF.E7.94.A8.E6.9D.83.E9.99.90.E7.AD.BE.E5.90.8D.E7.AE.97.E6.B3.95 | * 详情请见:http://qydev.weixin.qq.com/wiki/index.php?title=微信JS接口#.E9.99.84.E5.BD.951-JS-SDK.E4.BD.BF.E7.94.A8.E6.9D.83.E9.99.90.E7.AD.BE.E5.90.8D.E7.AE.97.E6.B3.95 | ||||
* </pre> | * </pre> | ||||
* | |||||
* @param forceRefresh 强制刷新 | * @param forceRefresh 强制刷新 | ||||
* @throws WxErrorException | * @throws WxErrorException | ||||
*/ | */ | ||||
@@ -90,7 +94,8 @@ public interface WxCpService { | |||||
* | * | ||||
* 详情请见:http://qydev.weixin.qq.com/wiki/index.php?title=微信JS接口#.E9.99.84.E5.BD.951-JS-SDK.E4.BD.BF.E7.94.A8.E6.9D.83.E9.99.90.E7.AD.BE.E5.90.8D.E7.AE.97.E6.B3.95 | * 详情请见:http://qydev.weixin.qq.com/wiki/index.php?title=微信JS接口#.E9.99.84.E5.BD.951-JS-SDK.E4.BD.BF.E7.94.A8.E6.9D.83.E9.99.90.E7.AD.BE.E5.90.8D.E7.AE.97.E6.B3.95 | ||||
* </pre> | * </pre> | ||||
* @param url url | |||||
* | |||||
* @param url url | |||||
*/ | */ | ||||
WxJsapiSignature createJsapiSignature(String url) throws WxErrorException; | WxJsapiSignature createJsapiSignature(String url) throws WxErrorException; | ||||
@@ -111,7 +116,7 @@ public interface WxCpService { | |||||
* @throws WxErrorException | * @throws WxErrorException | ||||
*/ | */ | ||||
WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputStream inputStream) | WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputStream inputStream) | ||||
throws WxErrorException, IOException; | |||||
throws WxErrorException, IOException; | |||||
/** | /** | ||||
* @param mediaType | * @param mediaType | ||||
@@ -128,9 +133,9 @@ public interface WxCpService { | |||||
* 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=上传下载多媒体文件 | * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=上传下载多媒体文件 | ||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param media_id | |||||
* @return 保存到本地的临时文件 | * @return 保存到本地的临时文件 | ||||
* @throws WxErrorException | * @throws WxErrorException | ||||
* @param media_id | |||||
*/ | */ | ||||
File mediaDownload(String media_id) throws WxErrorException; | File mediaDownload(String media_id) throws WxErrorException; | ||||
@@ -152,10 +157,10 @@ public interface WxCpService { | |||||
* | * | ||||
* 注意: 这个方法使用WxCpConfigStorage里的agentId | * 注意: 这个方法使用WxCpConfigStorage里的agentId | ||||
* </pre> | * </pre> | ||||
* @see #menuCreate(String, me.chanjar.weixin.common.bean.WxMenu) | |||||
* | * | ||||
* @param menu | * @param menu | ||||
* @throws WxErrorException | * @throws WxErrorException | ||||
* @see #menuCreate(String, me.chanjar.weixin.common.bean.menu.WxMenu) | |||||
*/ | */ | ||||
void menuCreate(WxMenu menu) throws WxErrorException; | void menuCreate(WxMenu menu) throws WxErrorException; | ||||
@@ -166,11 +171,11 @@ public interface WxCpService { | |||||
* | * | ||||
* 注意: 这个方法不使用WxCpConfigStorage里的agentId,需要开发人员自己给出 | * 注意: 这个方法不使用WxCpConfigStorage里的agentId,需要开发人员自己给出 | ||||
* </pre> | * </pre> | ||||
* @see #menuCreate(me.chanjar.weixin.common.bean.WxMenu) | |||||
* | * | ||||
* @param agentId 企业号应用的id | * @param agentId 企业号应用的id | ||||
* @param menu | * @param menu | ||||
* @throws WxErrorException | * @throws WxErrorException | ||||
* @see #menuCreate(me.chanjar.weixin.common.bean.menu.WxMenu) | |||||
*/ | */ | ||||
void menuCreate(String agentId, WxMenu menu) throws WxErrorException; | void menuCreate(String agentId, WxMenu menu) throws WxErrorException; | ||||
@@ -181,9 +186,9 @@ public interface WxCpService { | |||||
* | * | ||||
* 注意: 这个方法使用WxCpConfigStorage里的agentId | * 注意: 这个方法使用WxCpConfigStorage里的agentId | ||||
* </pre> | * </pre> | ||||
* @see #menuDelete(String) | |||||
* | * | ||||
* @throws WxErrorException | * @throws WxErrorException | ||||
* @see #menuDelete(String) | |||||
*/ | */ | ||||
void menuDelete() throws WxErrorException; | void menuDelete() throws WxErrorException; | ||||
@@ -194,10 +199,10 @@ public interface WxCpService { | |||||
* | * | ||||
* 注意: 这个方法不使用WxCpConfigStorage里的agentId,需要开发人员自己给出 | * 注意: 这个方法不使用WxCpConfigStorage里的agentId,需要开发人员自己给出 | ||||
* </pre> | * </pre> | ||||
* @see #menuDelete() | |||||
* | * | ||||
* @param agentId 企业号应用的id | * @param agentId 企业号应用的id | ||||
* @throws WxErrorException | * @throws WxErrorException | ||||
* @see #menuDelete() | |||||
*/ | */ | ||||
void menuDelete(String agentId) throws WxErrorException; | void menuDelete(String agentId) throws WxErrorException; | ||||
@@ -208,9 +213,9 @@ public interface WxCpService { | |||||
* | * | ||||
* 注意: 这个方法使用WxCpConfigStorage里的agentId | * 注意: 这个方法使用WxCpConfigStorage里的agentId | ||||
* </pre> | * </pre> | ||||
* @see #menuGet(String) | |||||
* | * | ||||
* @throws WxErrorException | * @throws WxErrorException | ||||
* @see #menuGet(String) | |||||
*/ | */ | ||||
WxMenu menuGet() throws WxErrorException; | WxMenu menuGet() throws WxErrorException; | ||||
@@ -221,10 +226,10 @@ public interface WxCpService { | |||||
* | * | ||||
* 注意: 这个方法不使用WxCpConfigStorage里的agentId,需要开发人员自己给出 | * 注意: 这个方法不使用WxCpConfigStorage里的agentId,需要开发人员自己给出 | ||||
* </pre> | * </pre> | ||||
* @see #menuGet() | |||||
* | * | ||||
* @param agentId 企业号应用的id | * @param agentId 企业号应用的id | ||||
* @throws WxErrorException | * @throws WxErrorException | ||||
* @see #menuGet() | |||||
*/ | */ | ||||
WxMenu menuGet(String agentId) throws WxErrorException; | WxMenu menuGet(String agentId) throws WxErrorException; | ||||
@@ -279,9 +284,10 @@ public interface WxCpService { | |||||
* | * | ||||
* http://qydev.weixin.qq.com/wiki/index.php?title=管理成员#.E8.8E.B7.E5.8F.96.E9.83.A8.E9.97.A8.E6.88.90.E5.91.98.28.E8.AF.A6.E6.83.85.29 | * http://qydev.weixin.qq.com/wiki/index.php?title=管理成员#.E8.8E.B7.E5.8F.96.E9.83.A8.E9.97.A8.E6.88.90.E5.91.98.28.E8.AF.A6.E6.83.85.29 | ||||
* </pre> | * </pre> | ||||
* @param departId 必填。部门id | |||||
* @param fetchChild 非必填。1/0:是否递归获取子部门下面的成员 | |||||
* @param status 非必填。0获取全部员工,1获取已关注成员列表,2获取禁用成员列表,4获取未关注成员列表。status可叠加 | |||||
* | |||||
* @param departId 必填。部门id | |||||
* @param fetchChild 非必填。1/0:是否递归获取子部门下面的成员 | |||||
* @param status 非必填。0获取全部员工,1获取已关注成员列表,2获取禁用成员列表,4获取未关注成员列表。status可叠加 | |||||
* @throws WxErrorException | * @throws WxErrorException | ||||
*/ | */ | ||||
List<WxCpUser> userList(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException; | List<WxCpUser> userList(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException; | ||||
@@ -330,7 +336,8 @@ public interface WxCpService { | |||||
* | * | ||||
* http://qydev.weixin.qq.com/wiki/index.php?title=管理成员#.E6.89.B9.E9.87.8F.E5.88.A0.E9.99.A4.E6.88.90.E5.91.98 | * http://qydev.weixin.qq.com/wiki/index.php?title=管理成员#.E6.89.B9.E9.87.8F.E5.88.A0.E9.99.A4.E6.88.90.E5.91.98 | ||||
* </pre> | * </pre> | ||||
* @param userids 员工UserID列表。对应管理端的帐号 | |||||
* | |||||
* @param userids 员工UserID列表。对应管理端的帐号 | |||||
* @throws WxErrorException | * @throws WxErrorException | ||||
*/ | */ | ||||
void userDelete(String[] userids) throws WxErrorException; | void userDelete(String[] userids) throws WxErrorException; | ||||
@@ -367,7 +374,6 @@ public interface WxCpService { | |||||
/** | /** | ||||
* 获得标签列表 | * 获得标签列表 | ||||
* | |||||
*/ | */ | ||||
List<WxCpTag> tagGet() throws WxErrorException; | List<WxCpTag> tagGet() throws WxErrorException; | ||||
@@ -386,11 +392,22 @@ public interface WxCpService { | |||||
*/ | */ | ||||
void tagAddUsers(String tagId, List<String> userIds, List<String> partyIds) throws WxErrorException; | void tagAddUsers(String tagId, List<String> userIds, List<String> partyIds) throws WxErrorException; | ||||
/** | |||||
* <pre> | |||||
* 构造oauth2授权的url连接 | |||||
* </pre> | |||||
* | |||||
* @param state | |||||
* @return url | |||||
*/ | |||||
String oauth2buildAuthorizationUrl(String state); | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 构造oauth2授权的url连接 | * 构造oauth2授权的url连接 | ||||
* 详情请见: http://qydev.weixin.qq.com/wiki/index.php?title=企业获取code | * 详情请见: http://qydev.weixin.qq.com/wiki/index.php?title=企业获取code | ||||
* </pre> | * </pre> | ||||
* | |||||
* @param redirectUri | * @param redirectUri | ||||
* @param state | * @param state | ||||
* @return url | * @return url | ||||
@@ -405,10 +422,10 @@ public interface WxCpService { | |||||
* | * | ||||
* 注意: 这个方法使用WxCpConfigStorage里的agentId | * 注意: 这个方法使用WxCpConfigStorage里的agentId | ||||
* </pre> | * </pre> | ||||
* @see #oauth2getUserInfo(String, String) | |||||
* | * | ||||
* @param code | * @param code | ||||
* @return [userid, deviceid] | * @return [userid, deviceid] | ||||
* @see #oauth2getUserInfo(String, String) | |||||
*/ | */ | ||||
String[] oauth2getUserInfo(String code) throws WxErrorException; | String[] oauth2getUserInfo(String code) throws WxErrorException; | ||||
@@ -420,11 +437,11 @@ public interface WxCpService { | |||||
* | * | ||||
* 注意: 这个方法不使用WxCpConfigStorage里的agentId,需要开发人员自己给出 | * 注意: 这个方法不使用WxCpConfigStorage里的agentId,需要开发人员自己给出 | ||||
* </pre> | * </pre> | ||||
* @see #oauth2getUserInfo(String) | |||||
* | * | ||||
* @param agentId 企业号应用的id | * @param agentId 企业号应用的id | ||||
* @param code | * @param code | ||||
* @return [userid, deviceid] | * @return [userid, deviceid] | ||||
* @see #oauth2getUserInfo(String) | |||||
*/ | */ | ||||
String[] oauth2getUserInfo(String agentId, String code) throws WxErrorException; | String[] oauth2getUserInfo(String agentId, String code) throws WxErrorException; | ||||
@@ -442,8 +459,9 @@ public interface WxCpService { | |||||
* 邀请成员关注 | * 邀请成员关注 | ||||
* http://qydev.weixin.qq.com/wiki/index.php?title=管理成员#.E9.82.80.E8.AF.B7.E6.88.90.E5.91.98.E5.85.B3.E6.B3.A8 | * http://qydev.weixin.qq.com/wiki/index.php?title=管理成员#.E9.82.80.E8.AF.B7.E6.88.90.E5.91.98.E5.85.B3.E6.B3.A8 | ||||
* </pre> | * </pre> | ||||
* @param userId 用户的userid | |||||
* @param inviteTips 推送到微信上的提示语(只有认证号可以使用)。当使用微信推送时,该字段默认为“请关注XXX企业号”,邮件邀请时,该字段无效。 | |||||
* | |||||
* @param userId 用户的userid | |||||
* @param inviteTips 推送到微信上的提示语(只有认证号可以使用)。当使用微信推送时,该字段默认为“请关注XXX企业号”,邮件邀请时,该字段无效。 | |||||
* @return 1:微信邀请 2.邮件邀请 | * @return 1:微信邀请 2.邮件邀请 | ||||
* @throws WxErrorException | * @throws WxErrorException | ||||
*/ | */ | ||||
@@ -454,6 +472,7 @@ public interface WxCpService { | |||||
* 获取微信服务器的ip段 | * 获取微信服务器的ip段 | ||||
* http://qydev.weixin.qq.com/wiki/index.php?title=回调模式#.E8.8E.B7.E5.8F.96.E5.BE.AE.E4.BF.A1.E6.9C.8D.E5.8A.A1.E5.99.A8.E7.9A.84ip.E6.AE.B5 | * http://qydev.weixin.qq.com/wiki/index.php?title=回调模式#.E8.8E.B7.E5.8F.96.E5.BE.AE.E4.BF.A1.E6.9C.8D.E5.8A.A1.E5.99.A8.E7.9A.84ip.E6.AE.B5 | ||||
* </pre> | * </pre> | ||||
* | |||||
* @return { "ip_list": ["101.226.103.*", "101.226.62.*"] } | * @return { "ip_list": ["101.226.103.*", "101.226.62.*"] } | ||||
* @throws WxErrorException | * @throws WxErrorException | ||||
*/ | */ | ||||
@@ -461,6 +480,7 @@ public interface WxCpService { | |||||
/** | /** | ||||
* 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求 | * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求 | ||||
* | |||||
* @param url | * @param url | ||||
* @param queryParam | * @param queryParam | ||||
* @throws WxErrorException | * @throws WxErrorException | ||||
@@ -469,6 +489,7 @@ public interface WxCpService { | |||||
/** | /** | ||||
* 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的POST请求 | * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的POST请求 | ||||
* | |||||
* @param url | * @param url | ||||
* @param postData | * @param postData | ||||
* @throws WxErrorException | * @throws WxErrorException | ||||
@@ -481,6 +502,7 @@ public interface WxCpService { | |||||
* 比{@link #get}和{@link #post}方法更灵活,可以自己构造RequestExecutor用来处理不同的参数和不同的返回类型。 | * 比{@link #get}和{@link #post}方法更灵活,可以自己构造RequestExecutor用来处理不同的参数和不同的返回类型。 | ||||
* 可以参考,{@link me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor}的实现方法 | * 可以参考,{@link me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor}的实现方法 | ||||
* </pre> | * </pre> | ||||
* | |||||
* @param executor | * @param executor | ||||
* @param uri | * @param uri | ||||
* @param data | * @param data | ||||
@@ -502,6 +524,7 @@ public interface WxCpService { | |||||
* 设置当微信系统响应系统繁忙时,要等待多少 retrySleepMillis(ms) * 2^(重试次数 - 1) 再发起重试 | * 设置当微信系统响应系统繁忙时,要等待多少 retrySleepMillis(ms) * 2^(重试次数 - 1) 再发起重试 | ||||
* 默认:1000ms | * 默认:1000ms | ||||
* </pre> | * </pre> | ||||
* | |||||
* @param retrySleepMillis | * @param retrySleepMillis | ||||
*/ | */ | ||||
void setRetrySleepMillis(int retrySleepMillis); | void setRetrySleepMillis(int retrySleepMillis); | ||||
@@ -511,19 +534,22 @@ public interface WxCpService { | |||||
* 设置当微信系统响应系统繁忙时,最大重试次数 | * 设置当微信系统响应系统繁忙时,最大重试次数 | ||||
* 默认:5次 | * 默认:5次 | ||||
* </pre> | * </pre> | ||||
* | |||||
* @param maxRetryTimes | * @param maxRetryTimes | ||||
*/ | */ | ||||
void setMaxRetryTimes(int maxRetryTimes); | void setMaxRetryTimes(int maxRetryTimes); | ||||
/** | /** | ||||
* 获取某个sessionId对应的session,如果sessionId没有对应的session,则新建一个并返回。 | * 获取某个sessionId对应的session,如果sessionId没有对应的session,则新建一个并返回。 | ||||
* | |||||
* @param id id可以为任意字符串,建议使用FromUserName作为id | * @param id id可以为任意字符串,建议使用FromUserName作为id | ||||
*/ | */ | ||||
WxSession getSession(String id); | WxSession getSession(String id); | ||||
/** | /** | ||||
* 获取某个sessionId对应的session,如果sessionId没有对应的session,若create为true则新建一个,否则返回null。 | * 获取某个sessionId对应的session,如果sessionId没有对应的session,若create为true则新建一个,否则返回null。 | ||||
* @param id id可以为任意字符串,建议使用FromUserName作为id | |||||
* | |||||
* @param id id可以为任意字符串,建议使用FromUserName作为id | |||||
* @param create | * @param create | ||||
*/ | */ | ||||
WxSession getSession(String id, boolean create); | WxSession getSession(String id, boolean create); | ||||
@@ -533,12 +559,14 @@ public interface WxCpService { | |||||
* 设置WxSessionManager,只有当需要使用个性化的WxSessionManager的时候才需要调用此方法, | * 设置WxSessionManager,只有当需要使用个性化的WxSessionManager的时候才需要调用此方法, | ||||
* WxCpService默认使用的是{@link me.chanjar.weixin.common.session.StandardSessionManager} | * WxCpService默认使用的是{@link me.chanjar.weixin.common.session.StandardSessionManager} | ||||
* </pre> | * </pre> | ||||
* | |||||
* @param sessionManager | * @param sessionManager | ||||
*/ | */ | ||||
void setSessionManager(WxSessionManager sessionManager); | void setSessionManager(WxSessionManager sessionManager); | ||||
/** | /** | ||||
* 上传部门列表覆盖企业号上的部门信息 | * 上传部门列表覆盖企业号上的部门信息 | ||||
* | |||||
* @param mediaId | * @param mediaId | ||||
* @throws WxErrorException | * @throws WxErrorException | ||||
*/ | */ | ||||
@@ -546,6 +574,7 @@ public interface WxCpService { | |||||
/** | /** | ||||
* 上传用户列表覆盖企业号上的用户信息 | * 上传用户列表覆盖企业号上的用户信息 | ||||
* | |||||
* @param mediaId | * @param mediaId | ||||
* @throws WxErrorException | * @throws WxErrorException | ||||
*/ | */ | ||||
@@ -553,6 +582,7 @@ public interface WxCpService { | |||||
/** | /** | ||||
* 获取异步任务结果 | * 获取异步任务结果 | ||||
* | |||||
* @param joinId | * @param joinId | ||||
* @throws WxErrorException | * @throws WxErrorException | ||||
*/ | */ | ||||
@@ -9,7 +9,7 @@ import com.google.gson.reflect.TypeToken; | |||||
import com.google.gson.stream.JsonReader; | import com.google.gson.stream.JsonReader; | ||||
import me.chanjar.weixin.common.bean.WxAccessToken; | import me.chanjar.weixin.common.bean.WxAccessToken; | ||||
import me.chanjar.weixin.common.bean.WxJsapiSignature; | import me.chanjar.weixin.common.bean.WxJsapiSignature; | ||||
import me.chanjar.weixin.common.bean.WxMenu; | |||||
import me.chanjar.weixin.common.bean.menu.WxMenu; | |||||
import me.chanjar.weixin.common.bean.result.WxError; | import me.chanjar.weixin.common.bean.result.WxError; | ||||
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | ||||
import me.chanjar.weixin.common.exception.WxErrorException; | import me.chanjar.weixin.common.exception.WxErrorException; | ||||
@@ -64,17 +64,13 @@ public class WxCpServiceImpl implements WxCpService { | |||||
protected CloseableHttpClient httpClient; | protected CloseableHttpClient httpClient; | ||||
protected HttpHost httpProxy; | protected HttpHost httpProxy; | ||||
private int retrySleepMillis = 1000; | |||||
private int maxRetryTimes = 5; | |||||
protected WxSessionManager sessionManager = new StandardSessionManager(); | protected WxSessionManager sessionManager = new StandardSessionManager(); | ||||
/** | /** | ||||
* 临时文件目录 | * 临时文件目录 | ||||
*/ | */ | ||||
protected File tmpDirFile; | protected File tmpDirFile; | ||||
private int retrySleepMillis = 1000; | |||||
private int maxRetryTimes = 5; | |||||
public boolean checkSignature(String msgSignature, String timestamp, String nonce, String data) { | public boolean checkSignature(String msgSignature, String timestamp, String nonce, String data) { | ||||
try { | try { | ||||
@@ -101,8 +97,8 @@ public class WxCpServiceImpl implements WxCpService { | |||||
synchronized (globalAccessTokenRefreshLock) { | synchronized (globalAccessTokenRefreshLock) { | ||||
if (wxCpConfigStorage.isAccessTokenExpired()) { | if (wxCpConfigStorage.isAccessTokenExpired()) { | ||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?" | String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?" | ||||
+ "&corpid=" + wxCpConfigStorage.getCorpId() | |||||
+ "&corpsecret=" + wxCpConfigStorage.getCorpSecret(); | |||||
+ "&corpid=" + wxCpConfigStorage.getCorpId() | |||||
+ "&corpsecret=" + wxCpConfigStorage.getCorpSecret(); | |||||
try { | try { | ||||
HttpGet httpGet = new HttpGet(url); | HttpGet httpGet = new HttpGet(url); | ||||
if (httpProxy != null) { | if (httpProxy != null) { | ||||
@@ -113,7 +109,7 @@ public class WxCpServiceImpl implements WxCpService { | |||||
String resultContent = null; | String resultContent = null; | ||||
try (CloseableHttpResponse response = httpclient.execute(httpGet)) { | try (CloseableHttpResponse response = httpclient.execute(httpGet)) { | ||||
resultContent = new BasicResponseHandler().handleResponse(response); | resultContent = new BasicResponseHandler().handleResponse(response); | ||||
}finally { | |||||
} finally { | |||||
httpGet.releaseConnection(); | httpGet.releaseConnection(); | ||||
} | } | ||||
WxError error = WxError.fromJson(resultContent); | WxError error = WxError.fromJson(resultContent); | ||||
@@ -163,16 +159,20 @@ public class WxCpServiceImpl implements WxCpService { | |||||
String jsapiTicket = getJsapiTicket(false); | String jsapiTicket = getJsapiTicket(false); | ||||
try { | try { | ||||
String signature = SHA1.genWithAmple( | String signature = SHA1.genWithAmple( | ||||
"jsapi_ticket=" + jsapiTicket, | |||||
"noncestr=" + noncestr, | |||||
"timestamp=" + timestamp, | |||||
"url=" + url | |||||
"jsapi_ticket=" + jsapiTicket, | |||||
"noncestr=" + noncestr, | |||||
"timestamp=" + timestamp, | |||||
"url=" + url | |||||
); | ); | ||||
WxJsapiSignature jsapiSignature = new WxJsapiSignature(); | WxJsapiSignature jsapiSignature = new WxJsapiSignature(); | ||||
jsapiSignature.setTimestamp(timestamp); | jsapiSignature.setTimestamp(timestamp); | ||||
jsapiSignature.setNoncestr(noncestr); | jsapiSignature.setNoncestr(noncestr); | ||||
jsapiSignature.setUrl(url); | jsapiSignature.setUrl(url); | ||||
jsapiSignature.setSignature(signature); | jsapiSignature.setSignature(signature); | ||||
// Fixed bug | |||||
jsapiSignature.setAppid(this.wxCpConfigStorage.getCorpId()); | |||||
return jsapiSignature; | return jsapiSignature; | ||||
} catch (NoSuchAlgorithmException e) { | } catch (NoSuchAlgorithmException e) { | ||||
throw new RuntimeException(e); | throw new RuntimeException(e); | ||||
@@ -227,7 +227,7 @@ public class WxCpServiceImpl implements WxCpService { | |||||
} | } | ||||
public WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputStream inputStream) | public WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputStream inputStream) | ||||
throws WxErrorException, IOException { | |||||
throws WxErrorException, IOException { | |||||
return mediaUpload(mediaType, FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), fileType)); | return mediaUpload(mediaType, FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), fileType)); | ||||
} | } | ||||
@@ -245,9 +245,9 @@ public class WxCpServiceImpl implements WxCpService { | |||||
public Integer departCreate(WxCpDepart depart) throws WxErrorException { | public Integer departCreate(WxCpDepart depart) throws WxErrorException { | ||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/department/create"; | String url = "https://qyapi.weixin.qq.com/cgi-bin/department/create"; | ||||
String responseContent = execute( | String responseContent = execute( | ||||
new SimplePostRequestExecutor(), | |||||
url, | |||||
depart.toJson()); | |||||
new SimplePostRequestExecutor(), | |||||
url, | |||||
depart.toJson()); | |||||
JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); | JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); | ||||
return GsonHelper.getAsInteger(tmpJsonElement.getAsJsonObject().get("id")); | return GsonHelper.getAsInteger(tmpJsonElement.getAsJsonObject().get("id")); | ||||
} | } | ||||
@@ -271,11 +271,11 @@ public class WxCpServiceImpl implements WxCpService { | |||||
*/ | */ | ||||
JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); | JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); | ||||
return WxCpGsonBuilder.INSTANCE.create() | return WxCpGsonBuilder.INSTANCE.create() | ||||
.fromJson( | |||||
tmpJsonElement.getAsJsonObject().get("department"), | |||||
new TypeToken<List<WxCpDepart>>() { | |||||
}.getType() | |||||
); | |||||
.fromJson( | |||||
tmpJsonElement.getAsJsonObject().get("department"), | |||||
new TypeToken<List<WxCpDepart>>() { | |||||
}.getType() | |||||
); | |||||
} | } | ||||
@Override | @Override | ||||
@@ -331,10 +331,11 @@ public class WxCpServiceImpl implements WxCpService { | |||||
String responseContent = get(url, params); | String responseContent = get(url, params); | ||||
JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); | JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); | ||||
return WxCpGsonBuilder.INSTANCE.create() | return WxCpGsonBuilder.INSTANCE.create() | ||||
.fromJson( | |||||
tmpJsonElement.getAsJsonObject().get("userlist"), | |||||
new TypeToken<List<WxCpUser>>() { }.getType() | |||||
); | |||||
.fromJson( | |||||
tmpJsonElement.getAsJsonObject().get("userlist"), | |||||
new TypeToken<List<WxCpUser>>() { | |||||
}.getType() | |||||
); | |||||
} | } | ||||
@Override | @Override | ||||
@@ -353,10 +354,11 @@ public class WxCpServiceImpl implements WxCpService { | |||||
String responseContent = get(url, params); | String responseContent = get(url, params); | ||||
JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); | JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); | ||||
return WxCpGsonBuilder.INSTANCE.create() | return WxCpGsonBuilder.INSTANCE.create() | ||||
.fromJson( | |||||
tmpJsonElement.getAsJsonObject().get("userlist"), | |||||
new TypeToken<List<WxCpUser>>() { }.getType() | |||||
); | |||||
.fromJson( | |||||
tmpJsonElement.getAsJsonObject().get("userlist"), | |||||
new TypeToken<List<WxCpUser>>() { | |||||
}.getType() | |||||
); | |||||
} | } | ||||
@Override | @Override | ||||
@@ -390,11 +392,11 @@ public class WxCpServiceImpl implements WxCpService { | |||||
String responseContent = get(url, null); | String responseContent = get(url, null); | ||||
JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); | JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); | ||||
return WxCpGsonBuilder.INSTANCE.create() | return WxCpGsonBuilder.INSTANCE.create() | ||||
.fromJson( | |||||
tmpJsonElement.getAsJsonObject().get("taglist"), | |||||
new TypeToken<List<WxCpTag>>() { | |||||
}.getType() | |||||
); | |||||
.fromJson( | |||||
tmpJsonElement.getAsJsonObject().get("taglist"), | |||||
new TypeToken<List<WxCpTag>>() { | |||||
}.getType() | |||||
); | |||||
} | } | ||||
@Override | @Override | ||||
@@ -403,10 +405,11 @@ public class WxCpServiceImpl implements WxCpService { | |||||
String responseContent = get(url, null); | String responseContent = get(url, null); | ||||
JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); | JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); | ||||
return WxCpGsonBuilder.INSTANCE.create() | return WxCpGsonBuilder.INSTANCE.create() | ||||
.fromJson( | |||||
tmpJsonElement.getAsJsonObject().get("userlist"), | |||||
new TypeToken<List<WxCpUser>>() { }.getType() | |||||
); | |||||
.fromJson( | |||||
tmpJsonElement.getAsJsonObject().get("userlist"), | |||||
new TypeToken<List<WxCpUser>>() { | |||||
}.getType() | |||||
); | |||||
} | } | ||||
@Override | @Override | ||||
@@ -445,8 +448,16 @@ public class WxCpServiceImpl implements WxCpService { | |||||
} | } | ||||
@Override | @Override | ||||
public String oauth2buildAuthorizationUrl(String state) { | |||||
return this.oauth2buildAuthorizationUrl( | |||||
this.wxCpConfigStorage.getOauth2redirectUri(), | |||||
state | |||||
); | |||||
} | |||||
@Override | |||||
public String oauth2buildAuthorizationUrl(String redirectUri, String state) { | public String oauth2buildAuthorizationUrl(String redirectUri, String state) { | ||||
String url = "https://open.weixin.qq.com/connect/oauth2/authorize?" ; | |||||
String url = "https://open.weixin.qq.com/connect/oauth2/authorize?"; | |||||
url += "appid=" + wxCpConfigStorage.getCorpId(); | url += "appid=" + wxCpConfigStorage.getCorpId(); | ||||
url += "&redirect_uri=" + URIUtil.encodeURIComponent(redirectUri); | url += "&redirect_uri=" + URIUtil.encodeURIComponent(redirectUri); | ||||
url += "&response_type=code"; | url += "&response_type=code"; | ||||
@@ -466,12 +477,12 @@ public class WxCpServiceImpl implements WxCpService { | |||||
@Override | @Override | ||||
public String[] oauth2getUserInfo(String agentId, String code) throws WxErrorException { | public String[] oauth2getUserInfo(String agentId, String code) throws WxErrorException { | ||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?" | String url = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?" | ||||
+ "code=" + code | |||||
+ "&agendid=" + agentId; | |||||
+ "code=" + code | |||||
+ "&agendid=" + agentId; | |||||
String responseText = get(url, null); | String responseText = get(url, null); | ||||
JsonElement je = Streams.parse(new JsonReader(new StringReader(responseText))); | JsonElement je = Streams.parse(new JsonReader(new StringReader(responseText))); | ||||
JsonObject jo = je.getAsJsonObject(); | JsonObject jo = je.getAsJsonObject(); | ||||
return new String[] {GsonHelper.getString(jo, "UserId"), GsonHelper.getString(jo, "DeviceId")}; | |||||
return new String[]{GsonHelper.getString(jo, "UserId"), GsonHelper.getString(jo, "DeviceId")}; | |||||
} | } | ||||
@Override | @Override | ||||
@@ -494,7 +505,7 @@ public class WxCpServiceImpl implements WxCpService { | |||||
JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); | JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); | ||||
JsonArray jsonArray = tmpJsonElement.getAsJsonObject().get("ip_list").getAsJsonArray(); | JsonArray jsonArray = tmpJsonElement.getAsJsonObject().get("ip_list").getAsJsonArray(); | ||||
String[] ips = new String[jsonArray.size()]; | String[] ips = new String[jsonArray.size()]; | ||||
for(int i = 0; i < jsonArray.size(); i++) { | |||||
for (int i = 0; i < jsonArray.size(); i++) { | |||||
ips[i] = jsonArray.get(i).getAsString(); | ips[i] = jsonArray.get(i).getAsString(); | ||||
} | } | ||||
return ips; | return ips; | ||||
@@ -533,7 +544,7 @@ public class WxCpServiceImpl implements WxCpService { | |||||
throw e; | throw e; | ||||
} | } | ||||
} | } | ||||
} while(++retryTimes < maxRetryTimes); | |||||
} while (++retryTimes < maxRetryTimes); | |||||
throw new RuntimeException("微信服务端异常,超出重试次数"); | throw new RuntimeException("微信服务端异常,超出重试次数"); | ||||
} | } | ||||
@@ -571,6 +582,7 @@ public class WxCpServiceImpl implements WxCpService { | |||||
throw new RuntimeException(e); | throw new RuntimeException(e); | ||||
} | } | ||||
} | } | ||||
protected CloseableHttpClient getHttpclient() { | protected CloseableHttpClient getHttpclient() { | ||||
return httpClient; | return httpClient; | ||||
} | } | ||||
@@ -579,12 +591,12 @@ public class WxCpServiceImpl implements WxCpService { | |||||
this.wxCpConfigStorage = wxConfigProvider; | this.wxCpConfigStorage = wxConfigProvider; | ||||
ApacheHttpClientBuilder apacheHttpClientBuilder = wxCpConfigStorage.getApacheHttpClientBuilder(); | ApacheHttpClientBuilder apacheHttpClientBuilder = wxCpConfigStorage.getApacheHttpClientBuilder(); | ||||
if (null == apacheHttpClientBuilder) { | if (null == apacheHttpClientBuilder) { | ||||
apacheHttpClientBuilder = DefaultApacheHttpHttpClientBuilder.get(); | |||||
apacheHttpClientBuilder = DefaultApacheHttpClientBuilder.get(); | |||||
} | } | ||||
apacheHttpClientBuilder.httpProxyHost(wxCpConfigStorage.getHttp_proxy_host()) | apacheHttpClientBuilder.httpProxyHost(wxCpConfigStorage.getHttp_proxy_host()) | ||||
.httpProxyPort(wxCpConfigStorage.getHttp_proxy_port()) | |||||
.httpProxyUsername(wxCpConfigStorage.getHttp_proxy_username()) | |||||
.httpProxyPassword(wxCpConfigStorage.getHttp_proxy_password()); | |||||
.httpProxyPort(wxCpConfigStorage.getHttp_proxy_port()) | |||||
.httpProxyUsername(wxCpConfigStorage.getHttp_proxy_username()) | |||||
.httpProxyPassword(wxCpConfigStorage.getHttp_proxy_password()); | |||||
httpClient = apacheHttpClientBuilder.build(); | httpClient = apacheHttpClientBuilder.build(); | ||||
} | } | ||||
@@ -621,27 +633,27 @@ public class WxCpServiceImpl implements WxCpService { | |||||
public void setSessionManager(WxSessionManager sessionManager) { | public void setSessionManager(WxSessionManager sessionManager) { | ||||
this.sessionManager = sessionManager; | this.sessionManager = sessionManager; | ||||
} | } | ||||
@Override | @Override | ||||
public String replaceParty(String mediaId) throws WxErrorException { | public String replaceParty(String mediaId) throws WxErrorException { | ||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/batch/replaceparty"; | |||||
JsonObject jsonObject = new JsonObject(); | |||||
jsonObject.addProperty("media_id", mediaId); | |||||
return post(url, jsonObject.toString()); | |||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/batch/replaceparty"; | |||||
JsonObject jsonObject = new JsonObject(); | |||||
jsonObject.addProperty("media_id", mediaId); | |||||
return post(url, jsonObject.toString()); | |||||
} | } | ||||
@Override | @Override | ||||
public String replaceUser(String mediaId) throws WxErrorException { | public String replaceUser(String mediaId) throws WxErrorException { | ||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/batch/replaceuser"; | |||||
JsonObject jsonObject = new JsonObject(); | |||||
jsonObject.addProperty("media_id", mediaId); | |||||
return post(url, jsonObject.toString()); | |||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/batch/replaceuser"; | |||||
JsonObject jsonObject = new JsonObject(); | |||||
jsonObject.addProperty("media_id", mediaId); | |||||
return post(url, jsonObject.toString()); | |||||
} | } | ||||
@Override | @Override | ||||
public String getTaskResult(String joinId) throws WxErrorException { | public String getTaskResult(String joinId) throws WxErrorException { | ||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/batch/getresult?jobid="+joinId; | |||||
return get(url, null); | |||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/batch/getresult?jobid=" + joinId; | |||||
return get(url, null); | |||||
} | } | ||||
public File getTmpDirFile() { | public File getTmpDirFile() { | ||||
@@ -16,6 +16,10 @@ public class WxCpDepart implements Serializable { | |||||
private Integer parentId; | private Integer parentId; | ||||
private Integer order; | private Integer order; | ||||
public static WxCpDepart fromJson(String json) { | |||||
return WxCpGsonBuilder.create().fromJson(json, WxCpDepart.class); | |||||
} | |||||
public Integer getId() { | public Integer getId() { | ||||
return id; | return id; | ||||
} | } | ||||
@@ -48,10 +52,6 @@ public class WxCpDepart implements Serializable { | |||||
this.order = order; | this.order = order; | ||||
} | } | ||||
public static WxCpDepart fromJson(String json) { | |||||
return WxCpGsonBuilder.create().fromJson(json, WxCpDepart.class); | |||||
} | |||||
public String toJson() { | public String toJson() { | ||||
return WxCpGsonBuilder.create().toJson(this); | return WxCpGsonBuilder.create().toJson(this); | ||||
} | } | ||||
@@ -59,10 +59,10 @@ public class WxCpDepart implements Serializable { | |||||
@Override | @Override | ||||
public String toString() { | public String toString() { | ||||
return "WxCpDepart{" + | return "WxCpDepart{" + | ||||
"id=" + id + | |||||
", name='" + name + '\'' + | |||||
", parentId=" + parentId + | |||||
", order=" + order + | |||||
'}'; | |||||
"id=" + id + | |||||
", name='" + name + '\'' + | |||||
", parentId=" + parentId + | |||||
", order=" + order + | |||||
'}'; | |||||
} | } | ||||
} | } |
@@ -9,8 +9,8 @@ import java.util.List; | |||||
/** | /** | ||||
* 消息 | * 消息 | ||||
* @author Daniel Qian | |||||
* | * | ||||
* @author Daniel Qian | |||||
*/ | */ | ||||
public class WxCpMessage implements Serializable { | public class WxCpMessage implements Serializable { | ||||
@@ -29,9 +29,52 @@ public class WxCpMessage implements Serializable { | |||||
private String safe; | private String safe; | ||||
private List<WxArticle> articles = new ArrayList<WxArticle>(); | private List<WxArticle> articles = new ArrayList<WxArticle>(); | ||||
/** | |||||
* 获得文本消息builder | |||||
*/ | |||||
public static TextBuilder TEXT() { | |||||
return new TextBuilder(); | |||||
} | |||||
/** | |||||
* 获得图片消息builder | |||||
*/ | |||||
public static ImageBuilder IMAGE() { | |||||
return new ImageBuilder(); | |||||
} | |||||
/** | |||||
* 获得语音消息builder | |||||
*/ | |||||
public static VoiceBuilder VOICE() { | |||||
return new VoiceBuilder(); | |||||
} | |||||
/** | |||||
* 获得视频消息builder | |||||
*/ | |||||
public static VideoBuilder VIDEO() { | |||||
return new VideoBuilder(); | |||||
} | |||||
/** | |||||
* 获得图文消息builder | |||||
*/ | |||||
public static NewsBuilder NEWS() { | |||||
return new NewsBuilder(); | |||||
} | |||||
/** | |||||
* 获得文件消息builder | |||||
*/ | |||||
public static FileBuilder FILE() { | |||||
return new FileBuilder(); | |||||
} | |||||
public String getToUser() { | public String getToUser() { | ||||
return toUser; | return toUser; | ||||
} | } | ||||
public void setToUser(String toUser) { | public void setToUser(String toUser) { | ||||
this.toUser = toUser; | this.toUser = toUser; | ||||
} | } | ||||
@@ -64,14 +107,6 @@ public class WxCpMessage implements Serializable { | |||||
return msgType; | return msgType; | ||||
} | } | ||||
public String getSafe() { | |||||
return safe; | |||||
} | |||||
public void setSafe(String safe) { | |||||
this.safe = safe; | |||||
} | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 请使用 | * 请使用 | ||||
@@ -82,56 +117,81 @@ public class WxCpMessage implements Serializable { | |||||
* {@link me.chanjar.weixin.common.api.WxConsts#CUSTOM_MSG_VIDEO} | * {@link me.chanjar.weixin.common.api.WxConsts#CUSTOM_MSG_VIDEO} | ||||
* {@link me.chanjar.weixin.common.api.WxConsts#CUSTOM_MSG_NEWS} | * {@link me.chanjar.weixin.common.api.WxConsts#CUSTOM_MSG_NEWS} | ||||
* </pre> | * </pre> | ||||
* | |||||
* @param msgType | * @param msgType | ||||
*/ | */ | ||||
public void setMsgType(String msgType) { | public void setMsgType(String msgType) { | ||||
this.msgType = msgType; | this.msgType = msgType; | ||||
} | } | ||||
public String getSafe() { | |||||
return safe; | |||||
} | |||||
public void setSafe(String safe) { | |||||
this.safe = safe; | |||||
} | |||||
public String getContent() { | public String getContent() { | ||||
return content; | return content; | ||||
} | } | ||||
public void setContent(String content) { | public void setContent(String content) { | ||||
this.content = content; | this.content = content; | ||||
} | } | ||||
public String getMediaId() { | public String getMediaId() { | ||||
return mediaId; | return mediaId; | ||||
} | } | ||||
public void setMediaId(String mediaId) { | public void setMediaId(String mediaId) { | ||||
this.mediaId = mediaId; | this.mediaId = mediaId; | ||||
} | } | ||||
public String getThumbMediaId() { | public String getThumbMediaId() { | ||||
return thumbMediaId; | return thumbMediaId; | ||||
} | } | ||||
public void setThumbMediaId(String thumbMediaId) { | public void setThumbMediaId(String thumbMediaId) { | ||||
this.thumbMediaId = thumbMediaId; | this.thumbMediaId = thumbMediaId; | ||||
} | } | ||||
public String getTitle() { | public String getTitle() { | ||||
return title; | return title; | ||||
} | } | ||||
public void setTitle(String title) { | public void setTitle(String title) { | ||||
this.title = title; | this.title = title; | ||||
} | } | ||||
public String getDescription() { | public String getDescription() { | ||||
return description; | return description; | ||||
} | } | ||||
public void setDescription(String description) { | public void setDescription(String description) { | ||||
this.description = description; | this.description = description; | ||||
} | } | ||||
public String getMusicUrl() { | public String getMusicUrl() { | ||||
return musicUrl; | return musicUrl; | ||||
} | } | ||||
public void setMusicUrl(String musicUrl) { | public void setMusicUrl(String musicUrl) { | ||||
this.musicUrl = musicUrl; | this.musicUrl = musicUrl; | ||||
} | } | ||||
public String getHqMusicUrl() { | public String getHqMusicUrl() { | ||||
return hqMusicUrl; | return hqMusicUrl; | ||||
} | } | ||||
public void setHqMusicUrl(String hqMusicUrl) { | public void setHqMusicUrl(String hqMusicUrl) { | ||||
this.hqMusicUrl = hqMusicUrl; | this.hqMusicUrl = hqMusicUrl; | ||||
} | } | ||||
public List<WxArticle> getArticles() { | public List<WxArticle> getArticles() { | ||||
return articles; | return articles; | ||||
} | } | ||||
public void setArticles(List<WxArticle> articles) { | public void setArticles(List<WxArticle> articles) { | ||||
this.articles = articles; | this.articles = articles; | ||||
} | } | ||||
@@ -150,70 +210,35 @@ public class WxCpMessage implements Serializable { | |||||
public String getTitle() { | public String getTitle() { | ||||
return title; | return title; | ||||
} | } | ||||
public void setTitle(String title) { | public void setTitle(String title) { | ||||
this.title = title; | this.title = title; | ||||
} | } | ||||
public String getDescription() { | public String getDescription() { | ||||
return description; | return description; | ||||
} | } | ||||
public void setDescription(String description) { | public void setDescription(String description) { | ||||
this.description = description; | this.description = description; | ||||
} | } | ||||
public String getUrl() { | public String getUrl() { | ||||
return url; | return url; | ||||
} | } | ||||
public void setUrl(String url) { | public void setUrl(String url) { | ||||
this.url = url; | this.url = url; | ||||
} | } | ||||
public String getPicUrl() { | public String getPicUrl() { | ||||
return picUrl; | return picUrl; | ||||
} | } | ||||
public void setPicUrl(String picUrl) { | public void setPicUrl(String picUrl) { | ||||
this.picUrl = picUrl; | this.picUrl = picUrl; | ||||
} | } | ||||
} | } | ||||
/** | |||||
* 获得文本消息builder | |||||
*/ | |||||
public static TextBuilder TEXT() { | |||||
return new TextBuilder(); | |||||
} | |||||
/** | |||||
* 获得图片消息builder | |||||
*/ | |||||
public static ImageBuilder IMAGE() { | |||||
return new ImageBuilder(); | |||||
} | |||||
/** | |||||
* 获得语音消息builder | |||||
*/ | |||||
public static VoiceBuilder VOICE() { | |||||
return new VoiceBuilder(); | |||||
} | |||||
/** | |||||
* 获得视频消息builder | |||||
*/ | |||||
public static VideoBuilder VIDEO() { | |||||
return new VideoBuilder(); | |||||
} | |||||
/** | |||||
* 获得图文消息builder | |||||
*/ | |||||
public static NewsBuilder NEWS() { | |||||
return new NewsBuilder(); | |||||
} | |||||
/** | |||||
* 获得文件消息builder | |||||
*/ | |||||
public static FileBuilder FILE() { | |||||
return new FileBuilder(); | |||||
} | |||||
} | } |
@@ -23,6 +23,10 @@ public class WxCpTag implements Serializable { | |||||
this.name = name; | this.name = name; | ||||
} | } | ||||
public static WxCpTag fromJson(String json) { | |||||
return WxCpGsonBuilder.create().fromJson(json, WxCpTag.class); | |||||
} | |||||
public String getName() { | public String getName() { | ||||
return name; | return name; | ||||
} | } | ||||
@@ -39,10 +43,6 @@ public class WxCpTag implements Serializable { | |||||
this.id = id; | this.id = id; | ||||
} | } | ||||
public static WxCpTag fromJson(String json) { | |||||
return WxCpGsonBuilder.create().fromJson(json, WxCpTag.class); | |||||
} | |||||
public String toJson() { | public String toJson() { | ||||
return WxCpGsonBuilder.create().toJson(this); | return WxCpGsonBuilder.create().toJson(this); | ||||
} | } | ||||
@@ -13,6 +13,7 @@ import java.util.List; | |||||
*/ | */ | ||||
public class WxCpUser implements Serializable { | public class WxCpUser implements Serializable { | ||||
private final List<Attr> extAttrs = new ArrayList<Attr>(); | |||||
private String userId; | private String userId; | ||||
private String name; | private String name; | ||||
private Integer[] departIds; | private Integer[] departIds; | ||||
@@ -25,7 +26,10 @@ public class WxCpUser implements Serializable { | |||||
private String avatar; | private String avatar; | ||||
private Integer status; | private Integer status; | ||||
private Integer enable; | private Integer enable; | ||||
private final List<Attr> extAttrs = new ArrayList<Attr>(); | |||||
public static WxCpUser fromJson(String json) { | |||||
return WxCpGsonBuilder.INSTANCE.create().fromJson(json, WxCpUser.class); | |||||
} | |||||
public String getUserId() { | public String getUserId() { | ||||
return userId; | return userId; | ||||
@@ -116,13 +120,13 @@ public class WxCpUser implements Serializable { | |||||
} | } | ||||
public Integer getEnable() { | public Integer getEnable() { | ||||
return enable; | |||||
return enable; | |||||
} | } | ||||
public void setEnable(Integer enable) { | public void setEnable(Integer enable) { | ||||
this.enable = enable; | |||||
this.enable = enable; | |||||
} | } | ||||
public void addExtAttr(String name, String value) { | public void addExtAttr(String name, String value) { | ||||
this.extAttrs.add(new Attr(name, value)); | this.extAttrs.add(new Attr(name, value)); | ||||
} | } | ||||
@@ -135,10 +139,6 @@ public class WxCpUser implements Serializable { | |||||
return WxCpGsonBuilder.INSTANCE.create().toJson(this); | return WxCpGsonBuilder.INSTANCE.create().toJson(this); | ||||
} | } | ||||
public static WxCpUser fromJson(String json) { | |||||
return WxCpGsonBuilder.INSTANCE.create().fromJson(json, WxCpUser.class); | |||||
} | |||||
public static class Attr { | public static class Attr { | ||||
private String name; | private String name; | ||||
@@ -36,41 +36,41 @@ public class WxCpXmlMessage implements Serializable { | |||||
private Integer agentId; | private Integer agentId; | ||||
@XStreamAlias("ToUserName") | @XStreamAlias("ToUserName") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String toUserName; | private String toUserName; | ||||
@XStreamAlias("FromUserName") | @XStreamAlias("FromUserName") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String fromUserName; | private String fromUserName; | ||||
@XStreamAlias("CreateTime") | @XStreamAlias("CreateTime") | ||||
private Long createTime; | private Long createTime; | ||||
@XStreamAlias("MsgType") | @XStreamAlias("MsgType") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String msgType; | private String msgType; | ||||
@XStreamAlias("Content") | @XStreamAlias("Content") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String content; | private String content; | ||||
@XStreamAlias("MsgId") | @XStreamAlias("MsgId") | ||||
private Long msgId; | private Long msgId; | ||||
@XStreamAlias("PicUrl") | @XStreamAlias("PicUrl") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String picUrl; | private String picUrl; | ||||
@XStreamAlias("MediaId") | @XStreamAlias("MediaId") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String mediaId; | private String mediaId; | ||||
@XStreamAlias("Format") | @XStreamAlias("Format") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String format; | private String format; | ||||
@XStreamAlias("ThumbMediaId") | @XStreamAlias("ThumbMediaId") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String thumbMediaId; | private String thumbMediaId; | ||||
@XStreamAlias("Location_X") | @XStreamAlias("Location_X") | ||||
@@ -83,31 +83,31 @@ public class WxCpXmlMessage implements Serializable { | |||||
private Double scale; | private Double scale; | ||||
@XStreamAlias("Label") | @XStreamAlias("Label") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String label; | private String label; | ||||
@XStreamAlias("Title") | @XStreamAlias("Title") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String title; | private String title; | ||||
@XStreamAlias("Description") | @XStreamAlias("Description") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String description; | private String description; | ||||
@XStreamAlias("Url") | @XStreamAlias("Url") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String url; | private String url; | ||||
@XStreamAlias("Event") | @XStreamAlias("Event") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String event; | private String event; | ||||
@XStreamAlias("EventKey") | @XStreamAlias("EventKey") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String eventKey; | private String eventKey; | ||||
@XStreamAlias("Ticket") | @XStreamAlias("Ticket") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String ticket; | private String ticket; | ||||
@XStreamAlias("Latitude") | @XStreamAlias("Latitude") | ||||
@@ -120,7 +120,7 @@ public class WxCpXmlMessage implements Serializable { | |||||
private Double precision; | private Double precision; | ||||
@XStreamAlias("Recognition") | @XStreamAlias("Recognition") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String recognition; | private String recognition; | ||||
/////////////////////////////////////// | /////////////////////////////////////// | ||||
@@ -130,7 +130,7 @@ public class WxCpXmlMessage implements Serializable { | |||||
* 群发的结果 | * 群发的结果 | ||||
*/ | */ | ||||
@XStreamAlias("Status") | @XStreamAlias("Status") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String status; | private String status; | ||||
/** | /** | ||||
* group_id下粉丝数;或者openid_list中的粉丝数 | * group_id下粉丝数;或者openid_list中的粉丝数 | ||||
@@ -162,6 +162,43 @@ public class WxCpXmlMessage implements Serializable { | |||||
@XStreamAlias("SendLocationInfo") | @XStreamAlias("SendLocationInfo") | ||||
private SendLocationInfo sendLocationInfo = new SendLocationInfo(); | private SendLocationInfo sendLocationInfo = new SendLocationInfo(); | ||||
protected static WxCpXmlMessage fromXml(String xml) { | |||||
return XStreamTransformer.fromXml(WxCpXmlMessage.class, xml); | |||||
} | |||||
protected static WxCpXmlMessage fromXml(InputStream is) { | |||||
return XStreamTransformer.fromXml(WxCpXmlMessage.class, is); | |||||
} | |||||
/** | |||||
* 从加密字符串转换 | |||||
* | |||||
* @param encryptedXml | |||||
* @param wxCpConfigStorage | |||||
* @param timestamp | |||||
* @param nonce | |||||
* @param msgSignature | |||||
*/ | |||||
public static WxCpXmlMessage fromEncryptedXml( | |||||
String encryptedXml, | |||||
WxCpConfigStorage wxCpConfigStorage, | |||||
String timestamp, String nonce, String msgSignature) { | |||||
WxCpCryptUtil cryptUtil = new WxCpCryptUtil(wxCpConfigStorage); | |||||
String plainText = cryptUtil.decrypt(msgSignature, timestamp, nonce, encryptedXml); | |||||
return fromXml(plainText); | |||||
} | |||||
public static WxCpXmlMessage fromEncryptedXml( | |||||
InputStream is, | |||||
WxCpConfigStorage wxCpConfigStorage, | |||||
String timestamp, String nonce, String msgSignature) { | |||||
try { | |||||
return fromEncryptedXml(IOUtils.toString(is, "UTF-8"), wxCpConfigStorage, timestamp, nonce, msgSignature); | |||||
} catch (IOException e) { | |||||
throw new RuntimeException(e); | |||||
} | |||||
} | |||||
public Integer getAgentId() { | public Integer getAgentId() { | ||||
return agentId; | return agentId; | ||||
} | } | ||||
@@ -197,7 +234,6 @@ public class WxCpXmlMessage implements Serializable { | |||||
* {@link me.chanjar.weixin.common.api.WxConsts#XML_MSG_LINK} | * {@link me.chanjar.weixin.common.api.WxConsts#XML_MSG_LINK} | ||||
* {@link me.chanjar.weixin.common.api.WxConsts#XML_MSG_EVENT} | * {@link me.chanjar.weixin.common.api.WxConsts#XML_MSG_EVENT} | ||||
* </pre> | * </pre> | ||||
* | |||||
*/ | */ | ||||
public String getMsgType() { | public String getMsgType() { | ||||
return msgType; | return msgType; | ||||
@@ -387,43 +423,6 @@ public class WxCpXmlMessage implements Serializable { | |||||
this.fromUserName = fromUserName; | this.fromUserName = fromUserName; | ||||
} | } | ||||
protected static WxCpXmlMessage fromXml(String xml) { | |||||
return XStreamTransformer.fromXml(WxCpXmlMessage.class, xml); | |||||
} | |||||
protected static WxCpXmlMessage fromXml(InputStream is) { | |||||
return XStreamTransformer.fromXml(WxCpXmlMessage.class, is); | |||||
} | |||||
/** | |||||
* 从加密字符串转换 | |||||
* | |||||
* @param encryptedXml | |||||
* @param wxCpConfigStorage | |||||
* @param timestamp | |||||
* @param nonce | |||||
* @param msgSignature | |||||
*/ | |||||
public static WxCpXmlMessage fromEncryptedXml( | |||||
String encryptedXml, | |||||
WxCpConfigStorage wxCpConfigStorage, | |||||
String timestamp, String nonce, String msgSignature) { | |||||
WxCpCryptUtil cryptUtil = new WxCpCryptUtil(wxCpConfigStorage); | |||||
String plainText = cryptUtil.decrypt(msgSignature, timestamp, nonce, encryptedXml); | |||||
return fromXml(plainText); | |||||
} | |||||
public static WxCpXmlMessage fromEncryptedXml( | |||||
InputStream is, | |||||
WxCpConfigStorage wxCpConfigStorage, | |||||
String timestamp, String nonce, String msgSignature) { | |||||
try { | |||||
return fromEncryptedXml(IOUtils.toString(is, "UTF-8"), wxCpConfigStorage, timestamp, nonce, msgSignature); | |||||
} catch (IOException e) { | |||||
throw new RuntimeException(e); | |||||
} | |||||
} | |||||
public String getStatus() { | public String getStatus() { | ||||
return status; | return status; | ||||
} | } | ||||
@@ -491,51 +490,51 @@ public class WxCpXmlMessage implements Serializable { | |||||
@Override | @Override | ||||
public String toString() { | public String toString() { | ||||
return "WxCpXmlMessage{" + | return "WxCpXmlMessage{" + | ||||
"agentId=" + agentId + | |||||
", toUserName='" + toUserName + '\'' + | |||||
", fromUserName='" + fromUserName + '\'' + | |||||
", createTime=" + createTime + | |||||
", msgType='" + msgType + '\'' + | |||||
", content='" + content + '\'' + | |||||
", msgId=" + msgId + | |||||
", picUrl='" + picUrl + '\'' + | |||||
", mediaId='" + mediaId + '\'' + | |||||
", format='" + format + '\'' + | |||||
", thumbMediaId='" + thumbMediaId + '\'' + | |||||
", locationX=" + locationX + | |||||
", locationY=" + locationY + | |||||
", scale=" + scale + | |||||
", label='" + label + '\'' + | |||||
", title='" + title + '\'' + | |||||
", description='" + description + '\'' + | |||||
", url='" + url + '\'' + | |||||
", event='" + event + '\'' + | |||||
", eventKey='" + eventKey + '\'' + | |||||
", ticket='" + ticket + '\'' + | |||||
", latitude=" + latitude + | |||||
", longitude=" + longitude + | |||||
", precision=" + precision + | |||||
", recognition='" + recognition + '\'' + | |||||
", status='" + status + '\'' + | |||||
", totalCount=" + totalCount + | |||||
", filterCount=" + filterCount + | |||||
", sentCount=" + sentCount + | |||||
", errorCount=" + errorCount + | |||||
", scanCodeInfo=" + scanCodeInfo + | |||||
", sendPicsInfo=" + sendPicsInfo + | |||||
", sendLocationInfo=" + sendLocationInfo + | |||||
'}'; | |||||
"agentId=" + agentId + | |||||
", toUserName='" + toUserName + '\'' + | |||||
", fromUserName='" + fromUserName + '\'' + | |||||
", createTime=" + createTime + | |||||
", msgType='" + msgType + '\'' + | |||||
", content='" + content + '\'' + | |||||
", msgId=" + msgId + | |||||
", picUrl='" + picUrl + '\'' + | |||||
", mediaId='" + mediaId + '\'' + | |||||
", format='" + format + '\'' + | |||||
", thumbMediaId='" + thumbMediaId + '\'' + | |||||
", locationX=" + locationX + | |||||
", locationY=" + locationY + | |||||
", scale=" + scale + | |||||
", label='" + label + '\'' + | |||||
", title='" + title + '\'' + | |||||
", description='" + description + '\'' + | |||||
", url='" + url + '\'' + | |||||
", event='" + event + '\'' + | |||||
", eventKey='" + eventKey + '\'' + | |||||
", ticket='" + ticket + '\'' + | |||||
", latitude=" + latitude + | |||||
", longitude=" + longitude + | |||||
", precision=" + precision + | |||||
", recognition='" + recognition + '\'' + | |||||
", status='" + status + '\'' + | |||||
", totalCount=" + totalCount + | |||||
", filterCount=" + filterCount + | |||||
", sentCount=" + sentCount + | |||||
", errorCount=" + errorCount + | |||||
", scanCodeInfo=" + scanCodeInfo + | |||||
", sendPicsInfo=" + sendPicsInfo + | |||||
", sendLocationInfo=" + sendLocationInfo + | |||||
'}'; | |||||
} | } | ||||
@XStreamAlias("ScanCodeInfo") | @XStreamAlias("ScanCodeInfo") | ||||
public static class ScanCodeInfo { | public static class ScanCodeInfo { | ||||
@XStreamAlias("ScanType") | @XStreamAlias("ScanType") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String scanType; | private String scanType; | ||||
@XStreamAlias("ScanResult") | @XStreamAlias("ScanResult") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String scanResult; | private String scanResult; | ||||
/** | /** | ||||
@@ -566,11 +565,10 @@ public class WxCpXmlMessage implements Serializable { | |||||
@XStreamAlias("SendPicsInfo") | @XStreamAlias("SendPicsInfo") | ||||
public static class SendPicsInfo { | public static class SendPicsInfo { | ||||
@XStreamAlias("Count") | |||||
private Long count; | |||||
@XStreamAlias("PicList") | @XStreamAlias("PicList") | ||||
protected final List<Item> picList = new ArrayList<Item>(); | protected final List<Item> picList = new ArrayList<Item>(); | ||||
@XStreamAlias("Count") | |||||
private Long count; | |||||
public Long getCount() { | public Long getCount() { | ||||
return count; | return count; | ||||
@@ -588,7 +586,7 @@ public class WxCpXmlMessage implements Serializable { | |||||
public static class Item { | public static class Item { | ||||
@XStreamAlias("PicMd5Sum") | @XStreamAlias("PicMd5Sum") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String PicMd5Sum; | private String PicMd5Sum; | ||||
public String getPicMd5Sum() { | public String getPicMd5Sum() { | ||||
@@ -605,23 +603,23 @@ public class WxCpXmlMessage implements Serializable { | |||||
public static class SendLocationInfo { | public static class SendLocationInfo { | ||||
@XStreamAlias("Location_X") | @XStreamAlias("Location_X") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String locationX; | private String locationX; | ||||
@XStreamAlias("Location_Y") | @XStreamAlias("Location_Y") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String locationY; | private String locationY; | ||||
@XStreamAlias("Scale") | @XStreamAlias("Scale") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String scale; | private String scale; | ||||
@XStreamAlias("Label") | @XStreamAlias("Label") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String label; | private String label; | ||||
@XStreamAlias("Poiname") | @XStreamAlias("Poiname") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String poiname; | private String poiname; | ||||
public String getLocationX() { | public String getLocationX() { | ||||
@@ -7,15 +7,15 @@ import me.chanjar.weixin.common.util.xml.XStreamMediaIdConverter; | |||||
@XStreamAlias("xml") | @XStreamAlias("xml") | ||||
public class WxCpXmlOutImageMessage extends WxCpXmlOutMessage { | public class WxCpXmlOutImageMessage extends WxCpXmlOutMessage { | ||||
@XStreamAlias("Image") | @XStreamAlias("Image") | ||||
@XStreamConverter(value=XStreamMediaIdConverter.class) | |||||
@XStreamConverter(value = XStreamMediaIdConverter.class) | |||||
private String mediaId; | private String mediaId; | ||||
public WxCpXmlOutImageMessage() { | public WxCpXmlOutImageMessage() { | ||||
this.msgType = WxConsts.XML_MSG_IMAGE; | this.msgType = WxConsts.XML_MSG_IMAGE; | ||||
} | } | ||||
public String getMediaId() { | public String getMediaId() { | ||||
return mediaId; | return mediaId; | ||||
} | } | ||||
@@ -23,5 +23,5 @@ public class WxCpXmlOutImageMessage extends WxCpXmlOutMessage { | |||||
public void setMediaId(String mediaId) { | public void setMediaId(String mediaId) { | ||||
this.mediaId = mediaId; | this.mediaId = mediaId; | ||||
} | } | ||||
} | } |
@@ -12,20 +12,55 @@ import me.chanjar.weixin.cp.util.xml.XStreamTransformer; | |||||
public abstract class WxCpXmlOutMessage { | public abstract class WxCpXmlOutMessage { | ||||
@XStreamAlias("ToUserName") | @XStreamAlias("ToUserName") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
protected String toUserName; | protected String toUserName; | ||||
@XStreamAlias("FromUserName") | @XStreamAlias("FromUserName") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
protected String fromUserName; | protected String fromUserName; | ||||
@XStreamAlias("CreateTime") | @XStreamAlias("CreateTime") | ||||
protected Long createTime; | protected Long createTime; | ||||
@XStreamAlias("MsgType") | @XStreamAlias("MsgType") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
protected String msgType; | protected String msgType; | ||||
/** | |||||
* 获得文本消息builder | |||||
*/ | |||||
public static TextBuilder TEXT() { | |||||
return new TextBuilder(); | |||||
} | |||||
/** | |||||
* 获得图片消息builder | |||||
*/ | |||||
public static ImageBuilder IMAGE() { | |||||
return new ImageBuilder(); | |||||
} | |||||
/** | |||||
* 获得语音消息builder | |||||
*/ | |||||
public static VoiceBuilder VOICE() { | |||||
return new VoiceBuilder(); | |||||
} | |||||
/** | |||||
* 获得视频消息builder | |||||
*/ | |||||
public static VideoBuilder VIDEO() { | |||||
return new VideoBuilder(); | |||||
} | |||||
/** | |||||
* 获得图文消息builder | |||||
*/ | |||||
public static NewsBuilder NEWS() { | |||||
return new NewsBuilder(); | |||||
} | |||||
public String getToUserName() { | public String getToUserName() { | ||||
return toUserName; | return toUserName; | ||||
} | } | ||||
@@ -59,7 +94,7 @@ public abstract class WxCpXmlOutMessage { | |||||
} | } | ||||
protected String toXml() { | protected String toXml() { | ||||
return XStreamTransformer.toXml((Class)this.getClass(), this); | |||||
return XStreamTransformer.toXml((Class) this.getClass(), this); | |||||
} | } | ||||
/** | /** | ||||
@@ -70,39 +105,4 @@ public abstract class WxCpXmlOutMessage { | |||||
WxCpCryptUtil pc = new WxCpCryptUtil(wxCpConfigStorage); | WxCpCryptUtil pc = new WxCpCryptUtil(wxCpConfigStorage); | ||||
return pc.encrypt(plainXml); | return pc.encrypt(plainXml); | ||||
} | } | ||||
/** | |||||
* 获得文本消息builder | |||||
*/ | |||||
public static TextBuilder TEXT() { | |||||
return new TextBuilder(); | |||||
} | |||||
/** | |||||
* 获得图片消息builder | |||||
*/ | |||||
public static ImageBuilder IMAGE() { | |||||
return new ImageBuilder(); | |||||
} | |||||
/** | |||||
* 获得语音消息builder | |||||
*/ | |||||
public static VoiceBuilder VOICE() { | |||||
return new VoiceBuilder(); | |||||
} | |||||
/** | |||||
* 获得视频消息builder | |||||
*/ | |||||
public static VideoBuilder VIDEO() { | |||||
return new VideoBuilder(); | |||||
} | |||||
/** | |||||
* 获得图文消息builder | |||||
*/ | |||||
public static NewsBuilder NEWS() { | |||||
return new NewsBuilder(); | |||||
} | |||||
} | } |
@@ -11,12 +11,11 @@ import java.util.List; | |||||
@XStreamAlias("xml") | @XStreamAlias("xml") | ||||
public class WxCpXmlOutNewsMessage extends WxCpXmlOutMessage { | public class WxCpXmlOutNewsMessage extends WxCpXmlOutMessage { | ||||
@XStreamAlias("ArticleCount") | |||||
protected int articleCount; | |||||
@XStreamAlias("Articles") | @XStreamAlias("Articles") | ||||
protected final List<Item> articles = new ArrayList<Item>(); | protected final List<Item> articles = new ArrayList<Item>(); | ||||
@XStreamAlias("ArticleCount") | |||||
protected int articleCount; | |||||
public WxCpXmlOutNewsMessage() { | public WxCpXmlOutNewsMessage() { | ||||
this.msgType = WxConsts.XML_MSG_NEWS; | this.msgType = WxConsts.XML_MSG_NEWS; | ||||
} | } | ||||
@@ -29,31 +28,31 @@ public class WxCpXmlOutNewsMessage extends WxCpXmlOutMessage { | |||||
this.articles.add(item); | this.articles.add(item); | ||||
this.articleCount = this.articles.size(); | this.articleCount = this.articles.size(); | ||||
} | } | ||||
public List<Item> getArticles() { | public List<Item> getArticles() { | ||||
return articles; | return articles; | ||||
} | } | ||||
@XStreamAlias("item") | @XStreamAlias("item") | ||||
public static class Item { | public static class Item { | ||||
@XStreamAlias("Title") | @XStreamAlias("Title") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String Title; | private String Title; | ||||
@XStreamAlias("Description") | @XStreamAlias("Description") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String Description; | private String Description; | ||||
@XStreamAlias("PicUrl") | @XStreamAlias("PicUrl") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String PicUrl; | private String PicUrl; | ||||
@XStreamAlias("Url") | @XStreamAlias("Url") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String Url; | private String Url; | ||||
public String getTitle() { | public String getTitle() { | ||||
return Title; | return Title; | ||||
} | } | ||||
@@ -7,15 +7,15 @@ import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; | |||||
@XStreamAlias("xml") | @XStreamAlias("xml") | ||||
public class WxCpXmlOutTextMessage extends WxCpXmlOutMessage { | public class WxCpXmlOutTextMessage extends WxCpXmlOutMessage { | ||||
@XStreamAlias("Content") | @XStreamAlias("Content") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String content; | private String content; | ||||
public WxCpXmlOutTextMessage() { | public WxCpXmlOutTextMessage() { | ||||
this.msgType = WxConsts.XML_MSG_TEXT; | this.msgType = WxConsts.XML_MSG_TEXT; | ||||
} | } | ||||
public String getContent() { | public String getContent() { | ||||
return content; | return content; | ||||
} | } | ||||
@@ -24,5 +24,5 @@ public class WxCpXmlOutTextMessage extends WxCpXmlOutMessage { | |||||
this.content = content; | this.content = content; | ||||
} | } | ||||
} | } |
@@ -38,21 +38,21 @@ public class WxCpXmlOutVideoMessage extends WxCpXmlOutMessage { | |||||
public void setDescription(String description) { | public void setDescription(String description) { | ||||
video.setDescription(description); | video.setDescription(description); | ||||
} | } | ||||
@XStreamAlias("Video") | @XStreamAlias("Video") | ||||
public static class Video { | public static class Video { | ||||
@XStreamAlias("MediaId") | @XStreamAlias("MediaId") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String mediaId; | private String mediaId; | ||||
@XStreamAlias("Title") | @XStreamAlias("Title") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String title; | private String title; | ||||
@XStreamAlias("Description") | @XStreamAlias("Description") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String description; | private String description; | ||||
public String getMediaId() { | public String getMediaId() { | ||||
@@ -78,7 +78,7 @@ public class WxCpXmlOutVideoMessage extends WxCpXmlOutMessage { | |||||
public void setDescription(String description) { | public void setDescription(String description) { | ||||
this.description = description; | this.description = description; | ||||
} | } | ||||
} | } | ||||
} | } |
@@ -7,15 +7,15 @@ import me.chanjar.weixin.common.util.xml.XStreamMediaIdConverter; | |||||
@XStreamAlias("xml") | @XStreamAlias("xml") | ||||
public class WxCpXmlOutVoiceMessage extends WxCpXmlOutMessage { | public class WxCpXmlOutVoiceMessage extends WxCpXmlOutMessage { | ||||
@XStreamAlias("Voice") | @XStreamAlias("Voice") | ||||
@XStreamConverter(value=XStreamMediaIdConverter.class) | |||||
@XStreamConverter(value = XStreamMediaIdConverter.class) | |||||
private String mediaId; | private String mediaId; | ||||
public WxCpXmlOutVoiceMessage() { | public WxCpXmlOutVoiceMessage() { | ||||
this.msgType = WxConsts.XML_MSG_VOICE; | this.msgType = WxConsts.XML_MSG_VOICE; | ||||
} | } | ||||
public String getMediaId() { | public String getMediaId() { | ||||
return mediaId; | return mediaId; | ||||
} | } | ||||
@@ -23,5 +23,5 @@ public class WxCpXmlOutVoiceMessage extends WxCpXmlOutMessage { | |||||
public void setMediaId(String mediaId) { | public void setMediaId(String mediaId) { | ||||
this.mediaId = mediaId; | this.mediaId = mediaId; | ||||
} | } | ||||
} | } |
@@ -44,7 +44,7 @@ public class BaseBuilder<T> { | |||||
m.setToParty(this.toParty); | m.setToParty(this.toParty); | ||||
m.setToTag(this.toTag); | m.setToTag(this.toTag); | ||||
m.setSafe( | m.setSafe( | ||||
(this.safe == null || "".equals(this.safe))? WxConsts.CUSTOM_MSG_SAFE_NO: this.safe); | |||||
(this.safe == null || "".equals(this.safe)) ? WxConsts.CUSTOM_MSG_SAFE_NO : this.safe); | |||||
return m; | return m; | ||||
} | } | ||||
@@ -8,8 +8,8 @@ import me.chanjar.weixin.cp.bean.WxCpMessage; | |||||
* <pre> | * <pre> | ||||
* 用法: WxCustomMessage m = WxCustomMessage.FILE().mediaId(...).toUser(...).build(); | * 用法: WxCustomMessage m = WxCustomMessage.FILE().mediaId(...).toUser(...).build(); | ||||
* </pre> | * </pre> | ||||
* @author Daniel Qian | |||||
* | * | ||||
* @author Daniel Qian | |||||
*/ | */ | ||||
public final class FileBuilder extends BaseBuilder<FileBuilder> { | public final class FileBuilder extends BaseBuilder<FileBuilder> { | ||||
private String mediaId; | private String mediaId; | ||||
@@ -8,8 +8,8 @@ import me.chanjar.weixin.cp.bean.WxCpMessage; | |||||
* <pre> | * <pre> | ||||
* 用法: WxCustomMessage m = WxCustomMessage.IMAGE().mediaId(...).toUser(...).build(); | * 用法: WxCustomMessage m = WxCustomMessage.IMAGE().mediaId(...).toUser(...).build(); | ||||
* </pre> | * </pre> | ||||
* @author Daniel Qian | |||||
* | * | ||||
* @author Daniel Qian | |||||
*/ | */ | ||||
public final class ImageBuilder extends BaseBuilder<ImageBuilder> { | public final class ImageBuilder extends BaseBuilder<ImageBuilder> { | ||||
private String mediaId; | private String mediaId; | ||||
@@ -12,13 +12,13 @@ import java.util.List; | |||||
* 用法: | * 用法: | ||||
* WxCustomMessage m = WxCustomMessage.NEWS().addArticle(article).toUser(...).build(); | * WxCustomMessage m = WxCustomMessage.NEWS().addArticle(article).toUser(...).build(); | ||||
* </pre> | * </pre> | ||||
* @author Daniel Qian | |||||
* | * | ||||
* @author Daniel Qian | |||||
*/ | */ | ||||
public final class NewsBuilder extends BaseBuilder<NewsBuilder> { | public final class NewsBuilder extends BaseBuilder<NewsBuilder> { | ||||
private List<WxCpMessage.WxArticle> articles = new ArrayList<WxCpMessage.WxArticle>(); | private List<WxCpMessage.WxArticle> articles = new ArrayList<WxCpMessage.WxArticle>(); | ||||
public NewsBuilder() { | public NewsBuilder() { | ||||
this.msgType = WxConsts.CUSTOM_MSG_NEWS; | this.msgType = WxConsts.CUSTOM_MSG_NEWS; | ||||
} | } | ||||
@@ -8,8 +8,8 @@ import me.chanjar.weixin.cp.bean.WxCpMessage; | |||||
* <pre> | * <pre> | ||||
* 用法: WxCustomMessage m = WxCustomMessage.TEXT().content(...).toUser(...).build(); | * 用法: WxCustomMessage m = WxCustomMessage.TEXT().content(...).toUser(...).build(); | ||||
* </pre> | * </pre> | ||||
* @author Daniel Qian | |||||
* | * | ||||
* @author Daniel Qian | |||||
*/ | */ | ||||
public final class TextBuilder extends BaseBuilder<TextBuilder> { | public final class TextBuilder extends BaseBuilder<TextBuilder> { | ||||
private String content; | private String content; | ||||
@@ -14,8 +14,8 @@ import me.chanjar.weixin.cp.bean.WxCpMessage; | |||||
* .toUser(...) | * .toUser(...) | ||||
* .build(); | * .build(); | ||||
* </pre> | * </pre> | ||||
* @author Daniel Qian | |||||
* | * | ||||
* @author Daniel Qian | |||||
*/ | */ | ||||
public final class VideoBuilder extends BaseBuilder<VideoBuilder> { | public final class VideoBuilder extends BaseBuilder<VideoBuilder> { | ||||
private String mediaId; | private String mediaId; | ||||
@@ -8,8 +8,8 @@ import me.chanjar.weixin.cp.bean.WxCpMessage; | |||||
* <pre> | * <pre> | ||||
* 用法: WxCustomMessage m = WxCustomMessage.VOICE().mediaId(...).toUser(...).build(); | * 用法: WxCustomMessage m = WxCustomMessage.VOICE().mediaId(...).toUser(...).build(); | ||||
* </pre> | * </pre> | ||||
* @author Daniel Qian | |||||
* | * | ||||
* @author Daniel Qian | |||||
*/ | */ | ||||
public final class VoiceBuilder extends BaseBuilder<VoiceBuilder> { | public final class VoiceBuilder extends BaseBuilder<VoiceBuilder> { | ||||
private String mediaId; | private String mediaId; | ||||
@@ -3,27 +3,27 @@ package me.chanjar.weixin.cp.bean.outxmlbuilder; | |||||
import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage; | import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage; | ||||
public abstract class BaseBuilder<BuilderType, ValueType> { | public abstract class BaseBuilder<BuilderType, ValueType> { | ||||
protected String toUserName; | protected String toUserName; | ||||
protected String fromUserName; | protected String fromUserName; | ||||
public BuilderType toUser(String touser) { | public BuilderType toUser(String touser) { | ||||
this.toUserName = touser; | this.toUserName = touser; | ||||
return (BuilderType) this; | return (BuilderType) this; | ||||
} | } | ||||
public BuilderType fromUser(String fromusername) { | public BuilderType fromUser(String fromusername) { | ||||
this.fromUserName = fromusername; | this.fromUserName = fromusername; | ||||
return (BuilderType) this; | return (BuilderType) this; | ||||
} | } | ||||
public abstract ValueType build(); | public abstract ValueType build(); | ||||
public void setCommon(WxCpXmlOutMessage m) { | public void setCommon(WxCpXmlOutMessage m) { | ||||
m.setToUserName(this.toUserName); | m.setToUserName(this.toUserName); | ||||
m.setFromUserName(this.fromUserName); | m.setFromUserName(this.fromUserName); | ||||
m.setCreateTime(System.currentTimeMillis() / 1000l); | m.setCreateTime(System.currentTimeMillis() / 1000l); | ||||
} | } | ||||
} | } |
@@ -4,6 +4,7 @@ import me.chanjar.weixin.cp.bean.WxCpXmlOutImageMessage; | |||||
/** | /** | ||||
* 图片消息builder | * 图片消息builder | ||||
* | |||||
* @author Daniel Qian | * @author Daniel Qian | ||||
*/ | */ | ||||
public final class ImageBuilder extends BaseBuilder<ImageBuilder, WxCpXmlOutImageMessage> { | public final class ImageBuilder extends BaseBuilder<ImageBuilder, WxCpXmlOutImageMessage> { | ||||
@@ -21,5 +22,5 @@ public final class ImageBuilder extends BaseBuilder<ImageBuilder, WxCpXmlOutImag | |||||
m.setMediaId(this.mediaId); | m.setMediaId(this.mediaId); | ||||
return m; | return m; | ||||
} | } | ||||
} | } |
@@ -8,24 +8,25 @@ import java.util.List; | |||||
/** | /** | ||||
* 图文消息builder | * 图文消息builder | ||||
* | |||||
* @author Daniel Qian | * @author Daniel Qian | ||||
*/ | */ | ||||
public final class NewsBuilder extends BaseBuilder<NewsBuilder, WxCpXmlOutNewsMessage> { | public final class NewsBuilder extends BaseBuilder<NewsBuilder, WxCpXmlOutNewsMessage> { | ||||
protected final List<Item> articles = new ArrayList<Item>(); | protected final List<Item> articles = new ArrayList<Item>(); | ||||
public NewsBuilder addArticle(Item item) { | public NewsBuilder addArticle(Item item) { | ||||
this.articles.add(item); | this.articles.add(item); | ||||
return this; | return this; | ||||
} | } | ||||
public WxCpXmlOutNewsMessage build() { | public WxCpXmlOutNewsMessage build() { | ||||
WxCpXmlOutNewsMessage m = new WxCpXmlOutNewsMessage(); | WxCpXmlOutNewsMessage m = new WxCpXmlOutNewsMessage(); | ||||
for(Item item : articles) { | |||||
for (Item item : articles) { | |||||
m.addArticle(item); | m.addArticle(item); | ||||
} | } | ||||
setCommon(m); | setCommon(m); | ||||
return m; | return m; | ||||
} | } | ||||
} | } |
@@ -4,8 +4,8 @@ import me.chanjar.weixin.cp.bean.WxCpXmlOutTextMessage; | |||||
/** | /** | ||||
* 文本消息builder | * 文本消息builder | ||||
* @author Daniel Qian | |||||
* | * | ||||
* @author Daniel Qian | |||||
*/ | */ | ||||
public final class TextBuilder extends BaseBuilder<TextBuilder, WxCpXmlOutTextMessage> { | public final class TextBuilder extends BaseBuilder<TextBuilder, WxCpXmlOutTextMessage> { | ||||
private String content; | private String content; | ||||
@@ -4,8 +4,8 @@ import me.chanjar.weixin.cp.bean.WxCpXmlOutVideoMessage; | |||||
/** | /** | ||||
* 视频消息builder | * 视频消息builder | ||||
* @author Daniel Qian | |||||
* | * | ||||
* @author Daniel Qian | |||||
*/ | */ | ||||
public final class VideoBuilder extends BaseBuilder<VideoBuilder, WxCpXmlOutVideoMessage> { | public final class VideoBuilder extends BaseBuilder<VideoBuilder, WxCpXmlOutVideoMessage> { | ||||
@@ -17,15 +17,17 @@ public final class VideoBuilder extends BaseBuilder<VideoBuilder, WxCpXmlOutVide | |||||
this.title = title; | this.title = title; | ||||
return this; | return this; | ||||
} | } | ||||
public VideoBuilder description(String description) { | public VideoBuilder description(String description) { | ||||
this.description = description; | this.description = description; | ||||
return this; | return this; | ||||
} | } | ||||
public VideoBuilder mediaId(String mediaId) { | public VideoBuilder mediaId(String mediaId) { | ||||
this.mediaId = mediaId; | this.mediaId = mediaId; | ||||
return this; | return this; | ||||
} | } | ||||
public WxCpXmlOutVideoMessage build() { | public WxCpXmlOutVideoMessage build() { | ||||
WxCpXmlOutVideoMessage m = new WxCpXmlOutVideoMessage(); | WxCpXmlOutVideoMessage m = new WxCpXmlOutVideoMessage(); | ||||
setCommon(m); | setCommon(m); | ||||
@@ -34,5 +36,5 @@ public final class VideoBuilder extends BaseBuilder<VideoBuilder, WxCpXmlOutVide | |||||
m.setMediaId(mediaId); | m.setMediaId(mediaId); | ||||
return m; | return m; | ||||
} | } | ||||
} | } |
@@ -4,6 +4,7 @@ import me.chanjar.weixin.cp.bean.WxCpXmlOutVoiceMessage; | |||||
/** | /** | ||||
* 语音消息builder | * 语音消息builder | ||||
* | |||||
* @author Daniel Qian | * @author Daniel Qian | ||||
*/ | */ | ||||
public final class VoiceBuilder extends BaseBuilder<VoiceBuilder, WxCpXmlOutVoiceMessage> { | public final class VoiceBuilder extends BaseBuilder<VoiceBuilder, WxCpXmlOutVoiceMessage> { | ||||
@@ -14,12 +15,12 @@ public final class VoiceBuilder extends BaseBuilder<VoiceBuilder, WxCpXmlOutVoic | |||||
this.mediaId = mediaId; | this.mediaId = mediaId; | ||||
return this; | return this; | ||||
} | } | ||||
public WxCpXmlOutVoiceMessage build() { | public WxCpXmlOutVoiceMessage build() { | ||||
WxCpXmlOutVoiceMessage m = new WxCpXmlOutVoiceMessage(); | WxCpXmlOutVoiceMessage m = new WxCpXmlOutVoiceMessage(); | ||||
setCommon(m); | setCommon(m); | ||||
m.setMediaId(mediaId); | m.setMediaId(mediaId); | ||||
return m; | return m; | ||||
} | } | ||||
} | } |
@@ -2,6 +2,10 @@ | |||||
* 对公众平台发送给公众账号的消息加解密示例代码. | * 对公众平台发送给公众账号的消息加解密示例代码. | ||||
* | * | ||||
* @copyright Copyright (c) 1998-2014 Tencent Inc. | * @copyright Copyright (c) 1998-2014 Tencent Inc. | ||||
* <p> | |||||
* 针对org.apache.commons.codec.binary.Base64, | |||||
* 需要导入架包commons-codec-1.9(或commons-codec-1.8等其他版本) | |||||
* 官方下载地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi | |||||
*/ | */ | ||||
// ------------------------------------------------------------------------ | // ------------------------------------------------------------------------ | ||||
@@ -37,7 +37,7 @@ public class WxCpDepartGsonAdapter implements JsonSerializer<WxCpDepart>, JsonDe | |||||
} | } | ||||
public WxCpDepart deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) | public WxCpDepart deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) | ||||
throws JsonParseException { | |||||
throws JsonParseException { | |||||
WxCpDepart depart = new WxCpDepart(); | WxCpDepart depart = new WxCpDepart(); | ||||
JsonObject departJson = json.getAsJsonObject(); | JsonObject departJson = json.getAsJsonObject(); | ||||
if (departJson.get("id") != null && !departJson.get("id").isJsonNull()) { | if (departJson.get("id") != null && !departJson.get("id").isJsonNull()) { | ||||
@@ -16,9 +16,7 @@ import me.chanjar.weixin.cp.bean.WxCpMessage; | |||||
import java.lang.reflect.Type; | import java.lang.reflect.Type; | ||||
/** | /** | ||||
* | |||||
* @author Daniel Qian | * @author Daniel Qian | ||||
* | |||||
*/ | */ | ||||
public class WxCpMessageGsonAdapter implements JsonSerializer<WxCpMessage> { | public class WxCpMessageGsonAdapter implements JsonSerializer<WxCpMessage> { | ||||
@@ -83,7 +81,7 @@ public class WxCpMessageGsonAdapter implements JsonSerializer<WxCpMessage> { | |||||
newsJsonObject.add("articles", articleJsonArray); | newsJsonObject.add("articles", articleJsonArray); | ||||
messageJson.add("news", newsJsonObject); | messageJson.add("news", newsJsonObject); | ||||
} | } | ||||
return messageJson; | return messageJson; | ||||
} | } | ||||
@@ -27,7 +27,7 @@ public class WxCpTagGsonAdapter implements JsonSerializer<WxCpTag>, JsonDeserial | |||||
} | } | ||||
public WxCpTag deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) | public WxCpTag deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) | ||||
throws JsonParseException { | |||||
throws JsonParseException { | |||||
JsonObject jsonObject = json.getAsJsonObject(); | JsonObject jsonObject = json.getAsJsonObject(); | ||||
return new WxCpTag(GsonHelper.getString(jsonObject, "tagid"), GsonHelper.getString(jsonObject, "tagname")); | return new WxCpTag(GsonHelper.getString(jsonObject, "tagid"), GsonHelper.getString(jsonObject, "tagname")); | ||||
} | } | ||||
@@ -20,13 +20,13 @@ import java.lang.reflect.Type; | |||||
public class WxCpUserGsonAdapter implements JsonDeserializer<WxCpUser>, JsonSerializer<WxCpUser> { | public class WxCpUserGsonAdapter implements JsonDeserializer<WxCpUser>, JsonSerializer<WxCpUser> { | ||||
public WxCpUser deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) | public WxCpUser deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) | ||||
throws JsonParseException { | |||||
throws JsonParseException { | |||||
JsonObject o = json.getAsJsonObject(); | JsonObject o = json.getAsJsonObject(); | ||||
WxCpUser user = new WxCpUser(); | WxCpUser user = new WxCpUser(); | ||||
user.setUserId(GsonHelper.getString(o, "userid")); | user.setUserId(GsonHelper.getString(o, "userid")); | ||||
user.setName(GsonHelper.getString(o, "name")); | user.setName(GsonHelper.getString(o, "name")); | ||||
if(o.get("department") != null) { | |||||
if (o.get("department") != null) { | |||||
JsonArray departJsonArray = o.get("department").getAsJsonArray(); | JsonArray departJsonArray = o.get("department").getAsJsonArray(); | ||||
Integer[] departIds = new Integer[departJsonArray.size()]; | Integer[] departIds = new Integer[departJsonArray.size()]; | ||||
int i = 0; | int i = 0; | ||||
@@ -49,8 +49,8 @@ public class WxCpUserGsonAdapter implements JsonDeserializer<WxCpUser>, JsonSeri | |||||
JsonArray attrJsonElements = o.get("extattr").getAsJsonObject().get("attrs").getAsJsonArray(); | JsonArray attrJsonElements = o.get("extattr").getAsJsonObject().get("attrs").getAsJsonArray(); | ||||
for (JsonElement attrJsonElement : attrJsonElements) { | for (JsonElement attrJsonElement : attrJsonElements) { | ||||
WxCpUser.Attr attr = new WxCpUser.Attr( | WxCpUser.Attr attr = new WxCpUser.Attr( | ||||
GsonHelper.getString(attrJsonElement.getAsJsonObject(), "name"), | |||||
GsonHelper.getString(attrJsonElement.getAsJsonObject(), "value") | |||||
GsonHelper.getString(attrJsonElement.getAsJsonObject(), "name"), | |||||
GsonHelper.getString(attrJsonElement.getAsJsonObject(), "value") | |||||
); | ); | ||||
user.getExtAttrs().add(attr); | user.getExtAttrs().add(attr); | ||||
} | } | ||||
@@ -29,16 +29,16 @@ public class XStreamTransformer { | |||||
/** | /** | ||||
* 注册扩展消息的解析器 | * 注册扩展消息的解析器 | ||||
* @param clz 类型 | |||||
* | |||||
* @param clz 类型 | |||||
* @param xStream xml解析器 | * @param xStream xml解析器 | ||||
*/ | |||||
public static void register(Class clz,XStream xStream){ | |||||
CLASS_2_XSTREAM_INSTANCE.put(clz,xStream); | |||||
*/ | |||||
public static void register(Class clz, XStream xStream) { | |||||
CLASS_2_XSTREAM_INSTANCE.put(clz, xStream); | |||||
} | } | ||||
/** | /** | ||||
* pojo -> xml | * pojo -> xml | ||||
* | |||||
*/ | */ | ||||
public static <T> String toXml(Class<T> clazz, T object) { | public static <T> String toXml(Class<T> clazz, T object) { | ||||
return CLASS_2_XSTREAM_INSTANCE.get(clazz).toXML(object); | return CLASS_2_XSTREAM_INSTANCE.get(clazz).toXML(object); | ||||
@@ -10,17 +10,6 @@ import java.io.InputStream; | |||||
public class ApiTestModule implements Module { | public class ApiTestModule implements Module { | ||||
@Override | |||||
public void configure(Binder binder) { | |||||
InputStream is1 = ClassLoader.getSystemResourceAsStream("test-config.xml"); | |||||
WxXmlCpInMemoryConfigStorage config = fromXml(WxXmlCpInMemoryConfigStorage.class, is1); | |||||
WxCpServiceImpl wxService = new WxCpServiceImpl(); | |||||
wxService.setWxCpConfigStorage(config); | |||||
binder.bind(WxCpServiceImpl.class).toInstance(wxService); | |||||
binder.bind(WxCpConfigStorage.class).toInstance(config); | |||||
} | |||||
public static <T> T fromXml(Class<T> clazz, InputStream is) { | public static <T> T fromXml(Class<T> clazz, InputStream is) { | ||||
XStream xstream = XStreamInitializer.getInstance(); | XStream xstream = XStreamInitializer.getInstance(); | ||||
xstream.alias("xml", clazz); | xstream.alias("xml", clazz); | ||||
@@ -28,9 +17,20 @@ public class ApiTestModule implements Module { | |||||
return (T) xstream.fromXML(is); | return (T) xstream.fromXML(is); | ||||
} | } | ||||
@Override | |||||
public void configure(Binder binder) { | |||||
InputStream is1 = ClassLoader.getSystemResourceAsStream("test-config.xml"); | |||||
WxXmlCpInMemoryConfigStorage config = fromXml(WxXmlCpInMemoryConfigStorage.class, is1); | |||||
WxCpServiceImpl wxService = new WxCpServiceImpl(); | |||||
wxService.setWxCpConfigStorage(config); | |||||
binder.bind(WxCpServiceImpl.class).toInstance(wxService); | |||||
binder.bind(WxCpConfigStorage.class).toInstance(config); | |||||
} | |||||
@XStreamAlias("xml") | @XStreamAlias("xml") | ||||
public static class WxXmlCpInMemoryConfigStorage extends WxCpInMemoryConfigStorage { | public static class WxXmlCpInMemoryConfigStorage extends WxCpInMemoryConfigStorage { | ||||
protected String userId; | protected String userId; | ||||
protected String departmentId; | protected String departmentId; | ||||
@@ -40,6 +40,7 @@ public class ApiTestModule implements Module { | |||||
public String getUserId() { | public String getUserId() { | ||||
return userId; | return userId; | ||||
} | } | ||||
public void setUserId(String userId) { | public void setUserId(String userId) { | ||||
this.userId = userId; | this.userId = userId; | ||||
} | } | ||||
@@ -63,11 +64,11 @@ public class ApiTestModule implements Module { | |||||
@Override | @Override | ||||
public String toString() { | public String toString() { | ||||
return super.toString() + " > WxXmlCpConfigStorage{" + | return super.toString() + " > WxXmlCpConfigStorage{" + | ||||
"userId='" + userId + '\'' + | |||||
", departmentId='" + departmentId + '\'' + | |||||
", tagId='" + tagId + '\'' + | |||||
'}'; | |||||
"userId='" + userId + '\'' + | |||||
", departmentId='" + departmentId + '\'' + | |||||
", tagId='" + tagId + '\'' + | |||||
'}'; | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -9,8 +9,8 @@ import org.testng.annotations.Test; | |||||
/** | /** | ||||
* 基础API测试 | * 基础API测试 | ||||
* @author Daniel Qian | |||||
* | * | ||||
* @author Daniel Qian | |||||
*/ | */ | ||||
@Test(groups = "baseAPI") | @Test(groups = "baseAPI") | ||||
@Guice(modules = ApiTestModule.class) | @Guice(modules = ApiTestModule.class) | ||||