Browse Source

Merge pull request #30 from wechat-group/develop

merge Develop
master
Binary Wang 8 years ago
committed by GitHub
parent
commit
a370309abf
100 changed files with 2114 additions and 1669 deletions
  1. +5
    -0
      .gitignore
  2. +25
    -19
      README.md
  3. +34
    -0
      build.gradle
  4. +8
    -2
      pom.xml
  5. +8
    -0
      settings.gradle
  6. +12
    -0
      weixin-java-common/build.gradle
  7. +1
    -1
      weixin-java-common/pom.xml
  8. +56
    -36
      weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java
  9. +5
    -4
      weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxMessageDuplicateChecker.java
  10. +2
    -1
      weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxMessageInMemoryDuplicateChecker.java
  11. +5
    -5
      weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/WxAccessToken.java
  12. +3
    -3
      weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/WxJsapiSignature.java
  13. +0
    -208
      weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/WxMenu.java
  14. +69
    -0
      weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/menu/WxMenu.java
  15. +72
    -0
      weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/menu/WxMenuButton.java
  16. +75
    -0
      weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/menu/WxMenuRule.java
  17. +22
    -19
      weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/result/WxError.java
  18. +6
    -6
      weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/result/WxMediaUploadResult.java
  19. +2
    -2
      weixin-java-common/src/main/java/me/chanjar/weixin/common/exception/WxErrorException.java
  20. +1
    -1
      weixin-java-common/src/main/java/me/chanjar/weixin/common/session/Constants.java
  21. +5
    -5
      weixin-java-common/src/main/java/me/chanjar/weixin/common/session/InternalSession.java
  22. +14
    -11
      weixin-java-common/src/main/java/me/chanjar/weixin/common/session/InternalSessionManager.java
  23. +0
    -1
      weixin-java-common/src/main/java/me/chanjar/weixin/common/session/LocalStrings.properties
  24. +69
    -83
      weixin-java-common/src/main/java/me/chanjar/weixin/common/session/StandardSession.java
  25. +65
    -75
      weixin-java-common/src/main/java/me/chanjar/weixin/common/session/StandardSessionManager.java
  26. +28
    -31
      weixin-java-common/src/main/java/me/chanjar/weixin/common/session/TooManyActiveSessionsException.java
  27. +10
    -12
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/StringUtils.java
  28. +16
    -16
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/ByteGroup.java
  29. +46
    -46
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/PKCS7Encoder.java
  30. +3
    -3
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/SHA1.java
  31. +53
    -49
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/WxCryptUtil.java
  32. +9
    -7
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/fs/FileUtils.java
  33. +6
    -0
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/ApacheHttpClientBuilder.java
  34. +59
    -55
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/DefaultApacheHttpClientBuilder.java
  35. +4
    -4
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/InputStreamResponseHandler.java
  36. +33
    -33
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/JoddGetRequestExecutor.java
  37. +27
    -27
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/JoddPostRequestExecutor.java
  38. +6
    -7
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaDownloadRequestExecutor.java
  39. +6
    -7
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaUploadRequestExecutor.java
  40. +5
    -7
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/RequestExecutor.java
  41. +2
    -3
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/SimpleGetRequestExecutor.java
  42. +2
    -3
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/SimplePostRequestExecutor.java
  43. +4
    -4
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/Utf8ResponseHandler.java
  44. +97
    -97
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/GsonHelper.java
  45. +1
    -3
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxAccessTokenAdapter.java
  46. +1
    -3
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxErrorAdapter.java
  47. +3
    -2
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxGsonBuilder.java
  48. +1
    -3
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxMediaUploadResultAdapter.java
  49. +25
    -21
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxMenuGsonAdapter.java
  50. +178
    -186
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/res/StringManager.java
  51. +3
    -2
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/xml/XStreamInitializer.java
  52. +1
    -1
      weixin-java-common/src/test/java/me/chanjar/weixin/common/bean/WxAccessTokenTest.java
  53. +3
    -3
      weixin-java-common/src/test/java/me/chanjar/weixin/common/bean/WxErrorTest.java
  54. +80
    -77
      weixin-java-common/src/test/java/me/chanjar/weixin/common/bean/WxMenuTest.java
  55. +2
    -2
      weixin-java-common/src/test/java/me/chanjar/weixin/common/session/SessionTest.java
  56. +1
    -1
      weixin-java-common/src/test/java/me/chanjar/weixin/common/util/WxMessageInMemoryDuplicateCheckerTest.java
  57. +12
    -4
      weixin-java-common/src/test/java/me/chanjar/weixin/common/util/crypto/WxCryptUtilTest.java
  58. +2
    -1
      weixin-java-common/src/test/resources/logback-test.xml
  59. +12
    -0
      weixin-java-cp/build.gradle
  60. +1
    -1
      weixin-java-cp/pom.xml
  61. +5
    -3
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpConfigStorage.java
  62. +35
    -35
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpInMemoryConfigStorage.java
  63. +267
    -0
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpJedisConfigStorage.java
  64. +0
    -1
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageHandler.java
  65. +23
    -19
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageRouter.java
  66. +22
    -24
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageRouterRule.java
  67. +52
    -22
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java
  68. +73
    -61
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpServiceImpl.java
  69. +9
    -9
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpDepart.java
  70. +76
    -51
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMessage.java
  71. +4
    -4
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTag.java
  72. +9
    -9
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpUser.java
  73. +98
    -100
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlMessage.java
  74. +4
    -4
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutImageMessage.java
  75. +39
    -39
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutMessage.java
  76. +13
    -14
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutNewsMessage.java
  77. +4
    -4
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutTextMessage.java
  78. +6
    -6
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutVideoMessage.java
  79. +4
    -4
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutVoiceMessage.java
  80. +1
    -1
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/BaseBuilder.java
  81. +1
    -1
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/FileBuilder.java
  82. +1
    -1
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/ImageBuilder.java
  83. +2
    -2
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/NewsBuilder.java
  84. +1
    -1
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/TextBuilder.java
  85. +1
    -1
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/VideoBuilder.java
  86. +1
    -1
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/VoiceBuilder.java
  87. +6
    -6
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/outxmlbuilder/BaseBuilder.java
  88. +2
    -1
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/outxmlbuilder/ImageBuilder.java
  89. +5
    -4
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/outxmlbuilder/NewsBuilder.java
  90. +1
    -1
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/outxmlbuilder/TextBuilder.java
  91. +5
    -3
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/outxmlbuilder/VideoBuilder.java
  92. +3
    -2
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/outxmlbuilder/VoiceBuilder.java
  93. +4
    -0
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/crypto/WxCpCryptUtil.java
  94. +1
    -1
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpDepartGsonAdapter.java
  95. +1
    -3
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpMessageGsonAdapter.java
  96. +1
    -1
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpTagGsonAdapter.java
  97. +4
    -4
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpUserGsonAdapter.java
  98. +5
    -5
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/xml/XStreamTransformer.java
  99. +18
    -17
      weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/ApiTestModule.java
  100. +1
    -1
      weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpBaseAPITest.java

+ 5
- 0
.gitignore View File

@@ -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

+ 25
- 19
README.md View File

@@ -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 [![Join QQ Group](http://pub.idqqimg.com/wpa/images/group.png)](http://shang.qq.com/wpa/qunwpa?idkey=078f7a153d243853e24cf2b542e7a6ccbf2a592bc138080f84d11297f736ec46) * QQ群:343954419 [![Join QQ Group](http://pub.idqqimg.com/wpa/images/group.png)](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的同学,本项目可以采用两种方式接受代码贡献:


+ 34
- 0
build.gradle View File

@@ -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'
}
}

+ 8
- 2
pom.xml View File

@@ -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>


+ 8
- 0
settings.gradle View File

@@ -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

+ 12
- 0
weixin-java-common/build.gradle View 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()

+ 1
- 1
weixin-java-common/pom.xml View File

@@ -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>


+ 56
- 36
weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java View File

@@ -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_涉嫌其他, "涉嫌其他");
}
} }

+ 5
- 4
weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxMessageDuplicateChecker.java View File

@@ -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
*/ */


+ 2
- 1
weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxMessageInMemoryDuplicateChecker.java View File

@@ -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) {


+ 5
- 5
weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/WxAccessToken.java View File

@@ -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);
}
} }

+ 3
- 3
weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/WxJsapiSignature.java View File

@@ -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;
} }


} }

+ 0
- 208
weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/WxMenu.java View File

@@ -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 + '\'' +
"}";
}
}
}

+ 69
- 0
weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/menu/WxMenu.java View File

@@ -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 +
'}';
}

}

+ 72
- 0
weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/menu/WxMenuButton.java View File

@@ -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;
}
}

+ 75
- 0
weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/menu/WxMenuRule.java View File

@@ -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);
}
}

+ 22
- 19
weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/result/WxError.java View File

@@ -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
- 6
weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/result/WxMediaUploadResult.java View File

@@ -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 + "]";
} }


} }

+ 2
- 2
weixin-java-common/src/main/java/me/chanjar/weixin/common/exception/WxErrorException.java View File

@@ -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;
} }


} }

+ 1
- 1
weixin-java-common/src/main/java/me/chanjar/weixin/common/session/Constants.java View File

@@ -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";


} }

+ 5
- 5
weixin-java-common/src/main/java/me/chanjar/weixin/common/session/InternalSession.java View File

@@ -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.
*/ */


+ 14
- 11
weixin-java-common/src/main/java/me/chanjar/weixin/common/session/InternalSessionManager.java View File

@@ -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);


+ 0
- 1
weixin-java-common/src/main/java/me/chanjar/weixin/common/session/LocalStrings.properties View File

@@ -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}


+ 69
- 83
weixin-java-common/src/main/java/me/chanjar/weixin/common/session/StandardSession.java View File

@@ -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);


+ 65
- 75
weixin-java-common/src/main/java/me/chanjar/weixin/common/session/StandardSessionManager.java View File

@@ -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);


} }




+ 28
- 31
weixin-java-common/src/main/java/me/chanjar/weixin/common/session/TooManyActiveSessionsException.java View File

@@ -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;
}
} }

+ 10
- 12
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/StringUtils.java View File

@@ -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)
*/ */


+ 16
- 16
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/ByteGroup.java View File

@@ -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();
}
} }

+ 46
- 46
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/PKCS7Encoder.java View File

@@ -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;
}
} }

+ 3
- 3
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/SHA1.java View File

@@ -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());
} }
}
}

+ 53
- 49
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/WxCryptUtil.java View File

@@ -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);
}
}
} }

+ 9
- 7
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/fs/FileUtils.java View File

@@ -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);
} }
} }

+ 6
- 0
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/ApacheHttpClientBuilder.java View File

@@ -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);


weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/DefaultApacheHttpHttpClientBuilder.java → weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/DefaultApacheHttpClientBuilder.java View File

@@ -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();
} }

+ 4
- 4
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/InputStreamResponseHandler.java View File

@@ -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();


+ 33
- 33
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/JoddGetRequestExecutor.java View File

@@ -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;
}

} }

+ 27
- 27
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/JoddPostRequestExecutor.java View File

@@ -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
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaDownloadRequestExecutor.java View File

@@ -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;
} }
} }

+ 6
- 7
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaUploadRequestExecutor.java View File

@@ -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();
} }
} }


+ 5
- 7
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/RequestExecutor.java View File

@@ -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


+ 2
- 3
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/SimpleGetRequestExecutor.java View File

@@ -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();
} }
} }


+ 2
- 3
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/SimplePostRequestExecutor.java View File

@@ -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();
} }
} }


+ 4
- 4
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/Utf8ResponseHandler.java View File

@@ -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();


+ 97
- 97
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/GsonHelper.java View File

@@ -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;
}
} }

+ 1
- 3
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxAccessTokenAdapter.java View File

@@ -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;
} }
} }

+ 1
- 3
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxErrorAdapter.java View File

@@ -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;
} }
} }

+ 3
- 2
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxGsonBuilder.java View File

@@ -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 {


+ 1
- 3
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxMediaUploadResultAdapter.java View File

@@ -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;
} }
} }

+ 25
- 21
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxMenuGsonAdapter.java View File

@@ -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;
} }




+ 178
- 186
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/res/StringManager.java View File

@@ -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;
}
} }

+ 3
- 2
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/xml/XStreamInitializer.java View File

@@ -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)) {


+ 1
- 1
weixin-java-common/src/test/java/me/chanjar/weixin/common/bean/WxAccessTokenTest.java View File

@@ -14,5 +14,5 @@ public class WxAccessTokenTest {
Assert.assertTrue(wxError.getExpiresIn() == 7200); Assert.assertTrue(wxError.getExpiresIn() == 7200);


} }
} }

+ 3
- 3
weixin-java-common/src/test/java/me/chanjar/weixin/common/bean/WxErrorTest.java View File

@@ -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);


} }
} }

+ 80
- 77
weixin-java-common/src/test/java/me/chanjar/weixin/common/bean/WxMenuTest.java View File

@@ -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}
}; };
} }
} }

+ 2
- 2
weixin-java-common/src/test/java/me/chanjar/weixin/common/session/SessionTest.java View File

@@ -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()}
}; };


} }


+ 1
- 1
weixin-java-common/src/test/java/me/chanjar/weixin/common/util/WxMessageInMemoryDuplicateCheckerTest.java View File

@@ -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);


// 第一次检查 // 第一次检查


+ 12
- 4
weixin-java-common/src/test/java/me/chanjar/weixin/common/util/crypto/WxCryptUtilTest.java View File

@@ -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);


+ 2
- 1
weixin-java-common/src/test/resources/logback-test.xml View File

@@ -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 -->


+ 12
- 0
weixin-java-cp/build.gradle View File

@@ -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()

+ 1
- 1
weixin-java-cp/pom.xml View File

@@ -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>


+ 5
- 3
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpConfigStorage.java View File

@@ -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();


+ 35
- 35
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpInMemoryConfigStorage.java View File

@@ -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() {


+ 267
- 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpJedisConfigStorage.java View File

@@ -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;
}
}

+ 0
- 1
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageHandler.java View File

@@ -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;


+ 23
- 19
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageRouter.java View File

@@ -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();
} }


+ 22
- 24
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageRouterRule.java View File

@@ -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 {




+ 52
- 22
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java View File

@@ -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
*/ */


+ 73
- 61
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpServiceImpl.java View File

@@ -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() {


+ 9
- 9
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpDepart.java View File

@@ -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 +
'}';
} }
} }

+ 76
- 51
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMessage.java View File

@@ -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();
}

} }

+ 4
- 4
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTag.java View File

@@ -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);
} }


+ 9
- 9
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpUser.java View File

@@ -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;


+ 98
- 100
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlMessage.java View File

@@ -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() {


+ 4
- 4
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutImageMessage.java View File

@@ -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;
} }
} }

+ 39
- 39
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutMessage.java View File

@@ -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();
}
} }

+ 13
- 14
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutNewsMessage.java View File

@@ -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;
} }


+ 4
- 4
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutTextMessage.java View File

@@ -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;
} }


} }

+ 6
- 6
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutVideoMessage.java View File

@@ -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;
} }
} }


} }

+ 4
- 4
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutVoiceMessage.java View File

@@ -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;
} }
} }

+ 1
- 1
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/BaseBuilder.java View File

@@ -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;
} }




+ 1
- 1
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/FileBuilder.java View File

@@ -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;


+ 1
- 1
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/ImageBuilder.java View File

@@ -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;


+ 2
- 2
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/NewsBuilder.java View File

@@ -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;
} }


+ 1
- 1
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/TextBuilder.java View File

@@ -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;


+ 1
- 1
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/VideoBuilder.java View File

@@ -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;


+ 1
- 1
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/VoiceBuilder.java View File

@@ -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;


+ 6
- 6
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/outxmlbuilder/BaseBuilder.java View File

@@ -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);
} }
} }

+ 2
- 1
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/outxmlbuilder/ImageBuilder.java View File

@@ -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;
} }
} }

+ 5
- 4
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/outxmlbuilder/NewsBuilder.java View File

@@ -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;
} }
} }

+ 1
- 1
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/outxmlbuilder/TextBuilder.java View File

@@ -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;


+ 5
- 3
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/outxmlbuilder/VideoBuilder.java View File

@@ -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;
} }
} }

+ 3
- 2
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/outxmlbuilder/VoiceBuilder.java View File

@@ -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;
} }
} }

+ 4
- 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/crypto/WxCpCryptUtil.java View File

@@ -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
*/ */
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------


+ 1
- 1
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpDepartGsonAdapter.java View File

@@ -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()) {


+ 1
- 3
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpMessageGsonAdapter.java View File

@@ -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;
} }




+ 1
- 1
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpTagGsonAdapter.java View File

@@ -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"));
} }


+ 4
- 4
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpUserGsonAdapter.java View File

@@ -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);
} }


+ 5
- 5
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/xml/XStreamTransformer.java View File

@@ -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);


+ 18
- 17
weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/ApiTestModule.java View File

@@ -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 + '\'' +
'}';
} }
} }
} }

+ 1
- 1
weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpBaseAPITest.java View File

@@ -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)


Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save