diff --git a/.codeclimate.yml b/.codeclimate.yml index 103ea86efb..fe226677d0 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -4,4 +4,4 @@ engines: ratings: paths: [] exclude_paths: -- "README.md" +- "readme.md" diff --git a/.travis.yml b/.travis.yml index 904ee9a47c..484475232b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,17 +1,17 @@ language: java -sudo: false -install: true -addons: - sonarqube: - token: - secure: "834110c7191f97ecb226970c46dcaff8e681da5a" +#sudo: false +#install: true +#addons: +# sonarcloud: +# token: +# secure: "834110c7191f97ecb226970c46dcaff8e681da5a" jdk: - oraclejdk8 -#script: "mvn clean package -Dmaven.test.skip=true" +script: "mvn clean package -Dmaven.test.skip=true" -script: - - mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent package sonar:sonar +#script: +# - mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent package sonar:sonar branches: only: @@ -20,7 +20,7 @@ branches: cache: directories: - '$HOME/.m2/repository' - - '$HOME/.sonar/cache' +# - '$HOME/.sonar/cache' notifications: email: diff --git a/alipay_qrcode.jpg b/alipay_qrcode.jpg new file mode 100644 index 0000000000..105f1d8753 Binary files /dev/null and b/alipay_qrcode.jpg differ diff --git a/build.gradle b/build.gradle deleted file mode 100644 index 6fa9a27ac2..0000000000 --- a/build.gradle +++ /dev/null @@ -1,33 +0,0 @@ -allprojects { - apply plugin: 'maven' - - group = 'com.github.binarywang' - version = '2.6.0' -} - -subprojects { - apply plugin: 'java' - sourceCompatibility = 1.7 - targetCompatibility = 1.7 - - - repositories { - mavenLocal() - maven { url "http://central.maven.org/maven2" } - //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: 'httpmime', version: '4.5' - compile group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5' - compile group: 'com.google.code.gson', name: 'gson', version: '2.7' - compile group: 'com.google.guava', name: 'guava', version: '19.0' - 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: 'redis.clients', name: 'jedis', version: '2.9.0' - testCompile group: 'ch.qos.logback', name: 'logback-classic', version: '1.1.2' - } -} diff --git a/contribution.md b/contribution.md index cbcbb7dbb3..093614d37c 100644 --- a/contribution.md +++ b/contribution.md @@ -1,9 +1,11 @@ # 代码贡献指南 -1. 非常欢迎和感谢对本项目发起Pull Request的同学,本项目代码风格为使用2个空格代表一个Tab,因此在提交代码时请注意一下,否则很容易在IDE格式化代码后与原代码产生大量diff,这样会给其他人阅读代码带来极大的困扰。为了便于设置,本项目引入editorconfig插件,请使用eclipse的同学在贡献代码前安装相关插件,IntelliJ IDEA新版本自带支持,如果没有可自行安装插件。 +1. 首先非常欢迎和感谢对本项目发起Pull Request的同学。 +1. 本项目代码风格为使用2个空格代表一个Tab,因此在提交代码时请注意一下,否则很容易在IDE格式化代码后与原代码产生大量diff,这样会给其他人阅读代码带来极大的困扰。 +1. 为了便于设置,本项目引入editorconfig支持,请使用Eclipse的同学在贡献代码前安装相关插件,而IntelliJ IDEA新版本自带支持,如果没有可自行安装插件。 +1. **提交代码前,请检查代码是否已经格式化,并且保证新增加或者修改的方法都有完整的参数说明,而public方法必须拥有相应的单元测试并通过测试。** 1. 本项目可以采用两种方式接受代码贡献: - * 第一种就是基于[Git Flow](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow)开发流程,因此在发起Pull Request的时候请选择develop分支,详细步骤参考后文。 - * 另外一种贡献代码的方式就是加入SDK Developers开发组,前提是对自己的代码足够自信就可以申请加入,加入之后可以随时直接提交代码,但要注意对所做的修改或新增的代码进行单元测试,保证提交代码没有明显问题,具体加入方式,请咨询QQ群管理员[![点击这里给我发消息](http://wpa.qq.com/pa?p=2:1211415707:51)](http://wpa.qq.com/msgrd?v=3&uin=1211415707&site=qq&menu=yes)。 - + - 第一种就是基于[Git Flow](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow)开发流程,因此在发起Pull Request的时候请选择develop分支,详细步骤参考后文,推荐使用此种方式贡献代码。 + - (***暂停此种方式,请使用第一种***)另外一种贡献代码的方式就是加入SDK Developers开发组,前提是对自己的代码足够自信就可以申请加入,加入之后可以随时直接提交代码,但要注意对所做的修改或新增的代码进行单元测试,保证提交代码没有明显问题。 ### PR方式贡献代码步骤 * 在 GitHub 上 `fork` 到自己的仓库,如 `my_user/weixin-java-tools`,然后 `clone` 到本地,并设置用户信息。 diff --git a/pom.xml b/pom.xml index 5fc6455948..fd6312842c 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ 4.0.0 com.github.binarywang weixin-java-parent - 2.6.1.BETA + 2.7.8.BETA pom WeiXin Java Tools - Parent 微信公众号、企业号上级POM @@ -93,6 +93,7 @@ weixin-java-cp weixin-java-mp weixin-java-pay + weixin-java-miniapp @@ -104,85 +105,90 @@ true true 4.5 - 1.7.10 - 1.1.2 - 2.7 - 19.0 - 3.5 - 2.5 - 1.10 9.3.0.RC0 - 2.9.0 - - 3.7 - - - org.jodd - jodd-http - ${jodd-http.version} - - - com.squareup.okhttp3 - okhttp - 3.7.0 - - - org.slf4j - slf4j-api - ${slf4j.version} - - - com.thoughtworks.xstream - xstream - 1.4.9 - - - ch.qos.logback - logback-classic - ${logback.version} - test - - - org.apache.httpcomponents - httpclient - ${httpclient.version} - - - org.apache.httpcomponents - httpmime - ${httpclient.version} - - - com.google.code.gson - gson - ${gson.version} - - - commons-codec - commons-codec - ${commons-codec.version} - - - commons-io - commons-io - ${commons-io.version} - - - org.apache.commons - commons-lang3 - ${commons-lang3.version} - - - com.google.guava - guava - ${guava.version} - - - + + com.github.binarywang + qrcode-utils + 1.1 + + + + org.jodd + jodd-http + 3.7.1 + provided + + + com.squareup.okhttp3 + okhttp + 3.7.0 + provided + + + + org.apache.httpcomponents + httpclient + ${httpclient.version} + + + org.apache.httpcomponents + httpmime + ${httpclient.version} + + + commons-codec + commons-codec + 1.10 + + + commons-io + commons-io + 2.5 + + + org.apache.commons + commons-lang3 + 3.5 + + + org.slf4j + slf4j-api + 1.7.24 + + + com.thoughtworks.xstream + xstream + 1.4.9 + + + + com.google.guava + guava + 20.0 + + + com.google.code.gson + gson + 2.8.0 + + + + + joda-time + joda-time + 2.9.7 + test + + + ch.qos.logback + logback-classic + 1.1.11 + test + com.google.inject guice @@ -192,7 +198,7 @@ org.testng testng - 6.8.7 + 6.10 test @@ -216,7 +222,8 @@ redis.clients jedis - ${jedis.version} + 2.9.0 + provided @@ -281,7 +288,7 @@ org.apache.maven.plugins maven-gpg-plugin - 1.5 + 1.7-SNAPSHOT sign-artifacts @@ -342,6 +349,26 @@ UTF-8 + + + org.apache.maven.plugins + maven-checkstyle-plugin + 2.17 + + quality-checks/google_checks.xml + true + true + true + + + + verify + + check + + + + diff --git a/quality-checks/google_checks.xml b/quality-checks/google_checks.xml new file mode 100644 index 0000000000..925172dab6 --- /dev/null +++ b/quality-checks/google_checks.xml @@ -0,0 +1,217 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/readme.md b/readme.md index b55333188b..7d4413d621 100644 --- a/readme.md +++ b/readme.md @@ -1,21 +1,33 @@ -微信支付、公众号&企业号开发Java SDK +微信支付、小程序、公众号&企业号开发Java SDK --------------------------------- [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.binarywang/weixin-java-parent/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.github.binarywang/weixin-java-parent) [![Build Status](https://travis-ci.org/Wechat-Group/weixin-java-tools.svg?branch=develop)](https://travis-ci.org/Wechat-Group/weixin-java-tools) -### 注意事项: -1. 声明: ***本项目Fork自chanjarster/weixin-java-tools,但由于原项目已停止维护,故单独维护和发布,且发布到maven上的groupId也会不同,详细信息见下文。*** -1. **新手请注意,本项目仅是一个开发工具包(即SDK),未提供Web实现,建议使用maven或gradle引用本项目即可使用本SDK提供的各种功能,详情可参考下文中提到的Demo项目或本项目中的部分单元测试代码;如果没有贡献代码的意愿,不建议下载项目的源码自行编译,因为如果想看源码使用maven也是可以下载源码的**; -1. 最新更新:**2017-4-13 发布[【2.6.0正式版】](https://github.com/Wechat-Group/weixin-java-tools/releases)**! +#### 声明: ***本项目Fork自chanjarster/weixin-java-tools,但由于原项目已停止维护,故单独维护和发布,且发布到maven上的groupId也会不同,详细信息见下文。*** + +***新人提示:本项目仅是一个开发工具包(即SDK),未提供Web实现,建议使用maven或gradle引用本项目即可使用本SDK提供的各种功能,详情可参考下文中提到的Demo项目或本项目中的部分单元测试代码;另外微信开发新手请务必阅读wiki首页的常见问题部分,可以少走很多弯路,节省不少时间。*** + +## Demo项目列表 +* https://github.com/wechat-group/weixin-java-miniapp-demo (微信小程序Demo) +* https://github.com/wechat-group/weixin-java-pay-demo (微信支付Demo) +* https://github.com/wechat-group/weixin-java-cp-demo (企业号Demo) +* https://github.com/wechat-group/weixin-java-mp-demo (公众号Demo,使用Spring MVC实现) +* https://github.com/wechat-group/weixin-java-mp-demo-springboot (公众号Demo,使用Spring Boot实现) +* https://github.com/wechat-group/weixin-java-tools-springmvc (公众号Demo,内含部分微信支付代码) +* https://github.com/wechat-group/weixin-java-mp-multi-demo (支持多公众号) + +--------------------------------- +### 其他信息: +1. 最新更新:**2017-6-25 发布[【2.7.0正式版】](https://github.com/Wechat-Group/weixin-java-tools/releases)**! 1. 开源中国网站的本项目介绍的首页链接地址:https://www.oschina.net/p/weixin-java-tools-new -1. 自2.0.0版本以来,公众号的接口调整比较大,主要是为了解决主接口类过于庞大不方便管理的问题,将接口实现代码按模块进行拆分。 1. 自2.6.0版本开始,微信支付相关功能抽出独立为一个模块,详细使用方式请参考相关demo; -1. SDK详细开发文档请查阅 [【Wiki】](https://github.com/wechat-group/weixin-java-tools/wiki),部分文档可能未能及时更新,如有发现,可以及时上报或者自行修改。***另外微信开发新手请务必阅读wiki首页的常见问题部分,可以少走很多弯路,节省不少时间。*** -1. 各个模块的Javadoc可以在线查看(有可能是最新的测试版本的,请注意观察版本号):[weixin-java-pay](https://binarywang.github.io/weixin-java-pay-javadoc/)、[weixin-java-mp](https://binarywang.github.io/weixin-java-mp-javadoc/)、[weixin-java-common](https://binarywang.github.io/weixin-java-common-javadoc/)、[weixin-java-cp](https://binarywang.github.io/weixin-java-cp-javadoc/) +1. 自2.7.0版本开始,支持微信小程序,具体可以参考相关demo; +1. SDK详细开发文档请查阅 [【Wiki】](https://github.com/wechat-group/weixin-java-tools/wiki),部分文档可能未能及时更新,如有发现,可以及时上报或者自行修改。 +1. 各个模块的Javadoc可以在线查看(有可能是最新的测试版本的,请注意观察版本号):[weixin-java-miniapp](https://binarywang.github.io/weixin-java-miniapp-javadoc/)、[weixin-java-pay](https://binarywang.github.io/weixin-java-pay-javadoc/)、[weixin-java-mp](https://binarywang.github.io/weixin-java-mp-javadoc/)、[weixin-java-common](https://binarywang.github.io/weixin-java-common-javadoc/)、[weixin-java-cp](https://binarywang.github.io/weixin-java-cp-javadoc/) 1. 本SDK要求的最低JDK版本是7,还在使用JDK6的用户请参考[【此项目】]( https://github.com/binarywang/weixin-java-tools-for-jdk6) ,而其他更早的JDK版本则需要自己改造实现。 1. 如有新功能需求,发现BUG,或者由于微信官方接口调整导致的代码问题,可以直接在[【Issues】](https://github.com/Wechat-Group/weixin-java-tools/issues)页提出issue,便于讨论追踪问题; 1. 如果想贡献代码,请阅读[【代码贡献指南】](contribution.md); -1. 捐助渠道已开通,如有意向请前往托管于码云的项目首页(具体地址见下文)的页面评论区上方,可以找到“捐助”按钮,非常感谢各位捐助的同学! +1. **捐助渠道已开通,如有意向请点击[【支付宝二维码】](alipay_qrcode.jpg)捐赠,或者直接前往[【托管于码云的项目首页】](http://git.oschina.net/binary/weixin-java-tools),在评论区上方可以找到“捐助”按钮。非常感谢各位捐助的同学!** --------------------------------- ## SDK使用交流方式说明: @@ -29,7 +41,7 @@ ## 版本说明 1. 本项目定为大约每两个月发布一次正式版,版本号格式为X.X.0(如2.1.0,2.2.0等),遇到重大问题需修复会及时提交新版本,欢迎大家随时提交Pull Request; 1. BUG修复和新特性一般会先发布成小版本作为临时测试版本(如2.4.5.BETA,2.4.6.BETA等,即尾号不为0,并添加BETA字样,以区别于正式版); -1. 目前最新版本号为 [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.binarywang/weixin-java-parent/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.github.binarywang/weixin-java-parent) ,也可以通过访问链接 [【微信支付】](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.github.binarywang%22%20AND%20a%3A%22weixin-java-pay%22) 、[【公众号】](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.github.binarywang%22%20AND%20a%3A%22weixin-java-mp%22) 、[【企业号】](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.github.binarywang%22%20AND%20a%3A%22weixin-java-cp%22) +1. 目前最新版本号为 [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.binarywang/weixin-java-parent/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.github.binarywang/weixin-java-parent) ,也可以通过访问链接 [【微信支付】](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.github.binarywang%22%20AND%20a%3A%22weixin-java-pay%22) 、[【微信小程序】](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.github.binarywang%22%20AND%20a%3A%22weixin-java-miniapp%22) 、[【公众号】](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.github.binarywang%22%20AND%20a%3A%22weixin-java-mp%22) 、[【企业号】](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.github.binarywang%22%20AND%20a%3A%22weixin-java-cp%22) 分别查看所有最新的版本。 --------------------------------- @@ -40,59 +52,44 @@ * Coding: https://git.coding.net/binarywang/weixin-java-tools.git --------------------------------- -## 可参考的Demo项目 -#### 欢迎提供更多的Demo供新手参考: -* https://github.com/wechat-group/weixin-java-mp-demo (公众号Demo,使用Spring MVC实现) -* https://github.com/wechat-group/weixin-java-mp-multi-demo (支持多公众号) -* https://github.com/wechat-group/weixin-java-tools-springmvc (公众号Demo,内含部分微信支付代码) -* https://github.com/wechat-group/weixin-java-mp-demo-springboot (公众号Demo,使用Spring Boot实现) -* https://github.com/wechat-group/weixin-java-pay-demo (微信支付demo) -* https://github.com/wechat-group/weixin-java-cp-demo (企业号demo,筹备中) +## Maven 最新正式版本 ---------------------------------- -## Maven & Gradle 最新正式版本 +* 微信小程序: + +```xml + + com.github.binarywang +  weixin-java-miniapp +  2.7.0 + +``` * 微信支付: -maven: ```xml com.github.binarywang weixin-java-pay - 2.6.0 + 2.7.0 ``` -gradle: -```groovy -compile 'com.github.binarywang:weixin-java-pay:2.6.0' -``` * 公众号(订阅号及服务号): -maven: ```xml com.github.binarywang weixin-java-mp - 2.6.0 + 2.7.0 ``` -gradle: -```groovy -compile 'com.github.binarywang:weixin-java-mp:2.6.0' -``` * 企业号: -maven: ```xml com.github.binarywang weixin-java-cp - 2.6.0 + 2.7.0 ``` -gradle: -```groovy -compile 'com.github.binarywang:weixin-java-cp:2.6.0' -``` diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index 0ff1d35b7c..0000000000 --- a/settings.gradle +++ /dev/null @@ -1,10 +0,0 @@ -rootProject.name = 'weixin-java-parent' -include ':weixin-java-common' -include ':weixin-java-cp' -include ':weixin-java-mp' -include ':weixin-java-pay' - -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 -project(':weixin-java-pay').projectDir = "$rootDir/weixin-java-pay" as File diff --git a/weixin-java-common/build.gradle b/weixin-java-common/build.gradle deleted file mode 100644 index f7f40ba01a..0000000000 --- a/weixin-java-common/build.gradle +++ /dev/null @@ -1,11 +0,0 @@ -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.RC0' - testCompile group: 'org.eclipse.jetty', name: 'jetty-servlet', version: '9.3.0.RC0' -} -test.useTestNG() diff --git a/weixin-java-common/pom.xml b/weixin-java-common/pom.xml index a0d5683f94..bd7bb51cee 100644 --- a/weixin-java-common/pom.xml +++ b/weixin-java-common/pom.xml @@ -1,12 +1,13 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0"> 4.0.0 com.github.binarywang weixin-java-parent - 2.6.1.BETA + 2.7.8.BETA weixin-java-common @@ -14,6 +15,71 @@ 微信公众号、企业号Java SDK Common + + org.jodd + jodd-http + provided + + + com.squareup.okhttp3 + okhttp + provided + + + + org.slf4j + slf4j-api + + + com.thoughtworks.xstream + xstream + + + org.apache.httpcomponents + httpclient + + + commons-logging + commons-logging + + + + + org.apache.httpcomponents + httpmime + + + org.slf4j + jcl-over-slf4j + 1.7.24 + + + + com.google.code.gson + gson + + + commons-codec + commons-codec + + + commons-io + commons-io + + + org.apache.commons + commons-lang3 + + + com.google.guava + guava + + + + ch.qos.logback + logback-classic + test + org.testng testng @@ -39,11 +105,6 @@ jetty-servlet test - - org.jodd - jodd-http - 3.7 - diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/annotation/Required.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/annotation/Required.java index 9370561fb7..e18be69e96 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/annotation/Required.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/annotation/Required.java @@ -6,9 +6,10 @@ import java.lang.annotation.Target; /** + *
  * 标识某个字段是否是必填的
- * 

* Created by Binary Wang on 2016/9/25. + *

* * @author binarywang (https://github.com/binarywang) */ diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java index e04dc45682..e416323e02 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java @@ -35,6 +35,7 @@ public class WxConsts { public static final String CUSTOM_MSG_NEWS = "news";//图文消息(点击跳转到外链) public static final String CUSTOM_MSG_MPNEWS = "mpnews";//图文消息(点击跳转到图文消息页面) public static final String CUSTOM_MSG_FILE = "file";//发送文件(CP专用) + public static final String CUSTOM_MSG_TEXTCARD = "textcard";//文本卡片消息(CP专用) public static final String CUSTOM_MSG_WXCARD = "wxcard";//卡券消息 public static final String CUSTOM_MSG_TRANSFER_CUSTOMER_SERVICE = "transfer_customer_service"; public static final String CUSTOM_MSG_SAFE_NO = "0"; diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/result/WxMediaUploadResult.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/result/WxMediaUploadResult.java index 8ea6a3149e..706de712c7 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/result/WxMediaUploadResult.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/result/WxMediaUploadResult.java @@ -1,6 +1,8 @@ package me.chanjar.weixin.common.bean.result; import me.chanjar.weixin.common.util.json.WxGsonBuilder; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; import java.io.Serializable; @@ -50,8 +52,7 @@ public void setThumbMediaId(String thumbMediaId) { @Override public String toString() { - return "WxUploadResult [type=" + this.type + ", media_id=" + this.mediaId + ", thumb_media_id=" + this.thumbMediaId - + ", created_at=" + this.createdAt + "]"; + return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); } } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/SHA1.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/SHA1.java index 36aa1e7e03..3cdc572387 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/SHA1.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/SHA1.java @@ -1,6 +1,7 @@ package me.chanjar.weixin.common.util.crypto; import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.lang3.StringUtils; import java.util.Arrays; @@ -13,6 +14,10 @@ public class SHA1 { * 串接arr参数,生成sha1 digest */ public static String gen(String... arr) { + if (StringUtils.isAnyEmpty(arr)) { + throw new IllegalArgumentException("非法请求参数,有部分参数为空 : " + Arrays.toString(arr)); + } + Arrays.sort(arr); StringBuilder sb = new StringBuilder(); for (String a : arr) { @@ -25,6 +30,10 @@ public static String gen(String... arr) { * 用&串接arr参数,生成sha1 digest */ public static String genWithAmple(String... arr) { + if (StringUtils.isAnyEmpty(arr)) { + throw new IllegalArgumentException("非法请求参数,有部分参数为空 : " + Arrays.toString(arr)); + } + Arrays.sort(arr); StringBuilder sb = new StringBuilder(); for (int i = 0; i < arr.length; i++) { diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/WxCryptUtil.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/WxCryptUtil.java index 68834f9e1d..e362e64b2f 100755 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/WxCryptUtil.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/WxCryptUtil.java @@ -1,24 +1,3 @@ -/** - * 对公众平台发送给公众账号的消息加解密示例代码. - * - * @copyright Copyright (c) 1998-2014 Tencent Inc. - *

- * 针对org.apache.commons.codec.binary.Base64, - * 需要导入架包commons-codec-1.9(或commons-codec-1.8等其他版本) - * 官方下载地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi - *

- * 针对org.apache.commons.codec.binary.Base64, - * 需要导入架包commons-codec-1.9(或commons-codec-1.8等其他版本) - * 官方下载地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi - */ - -// ------------------------------------------------------------------------ - -/** - * 针对org.apache.commons.codec.binary.Base64, - * 需要导入架包commons-codec-1.9(或commons-codec-1.8等其他版本) - * 官方下载地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi - */ package me.chanjar.weixin.common.util.crypto; import org.apache.commons.codec.binary.Base64; @@ -34,13 +13,23 @@ import javax.xml.parsers.ParserConfigurationException; import java.io.StringReader; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Random; +/** + *

+ * 对公众平台发送给公众账号的消息加解密示例代码.
+ * Copyright (c) 1998-2014 Tencent Inc.
+ * 针对org.apache.commons.codec.binary.Base64,
+ * 需要导入架包commons-codec-1.9(或commons-codec-1.8等其他版本)
+ * 官方下载地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi
+ * 
+ */ public class WxCryptUtil { private static final Base64 base64 = new Base64(); - private static final Charset CHARSET = Charset.forName("utf-8"); + private static final Charset CHARSET = StandardCharsets.UTF_8; private static final ThreadLocal builderLocal = new ThreadLocal() { @Override @@ -64,9 +53,9 @@ public WxCryptUtil() { /** * 构造函数 * - * @param token 公众平台上,开发者设置的token - * @param encodingAesKey 公众平台上,开发者设置的EncodingAESKey - * @param appidOrCorpid 公众平台appid/corpid + * @param token 公众平台上,开发者设置的token + * @param encodingAesKey 公众平台上,开发者设置的EncodingAESKey + * @param appidOrCorpid 公众平台appid/corpid */ public WxCryptUtil(String token, String encodingAesKey, String appidOrCorpid) { @@ -89,8 +78,6 @@ static String extractEncryptPart(String xml) { /** * 将一个数字转换成生成4个字节的网络字节序bytes数组 - * - * @param number */ private static byte[] number2BytesInNetworkOrder(int number) { byte[] orderBytes = new byte[4]; @@ -103,8 +90,6 @@ private static byte[] number2BytesInNetworkOrder(int number) { /** * 4个字节的网络字节序bytes数组还原成一个数字 - * - * @param bytesInNetworkOrder */ private static int bytesNetworkOrder2Number(byte[] bytesInNetworkOrder) { int sourceNumber = 0; @@ -231,8 +216,7 @@ protected String encrypt(String randomStr, String plainText) { * @param encryptedXml 密文,对应POST请求的数据 * @return 解密后的原文 */ - public String decrypt(String msgSignature, String timeStamp, String nonce, - String encryptedXml) { + public String decrypt(String msgSignature, String timeStamp, String nonce, String encryptedXml) { // 密钥,公众账号的app corpSecret // 提取密文 String cipherText = extractEncryptPart(encryptedXml); diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/HttpResponseProxy.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/HttpResponseProxy.java new file mode 100644 index 0000000000..a75aa963e0 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/HttpResponseProxy.java @@ -0,0 +1,88 @@ +package me.chanjar.weixin.common.util.http; + +import jodd.http.HttpResponse; +import me.chanjar.weixin.common.bean.result.WxError; +import me.chanjar.weixin.common.exception.WxErrorException; +import okhttp3.Response; +import org.apache.http.Header; +import org.apache.http.client.methods.CloseableHttpResponse; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + *
+ * 三种http框架的response代理类,方便提取公共方法
+ * Created by Binary Wang on 2017-8-3.
+ * 
+ * + * @author Binary Wang + */ +public class HttpResponseProxy { + private CloseableHttpResponse apacheHttpResponse; + private HttpResponse joddHttpResponse; + private Response okHttpResponse; + + public HttpResponseProxy(CloseableHttpResponse apacheHttpResponse) { + this.apacheHttpResponse = apacheHttpResponse; + } + + public HttpResponseProxy(HttpResponse joddHttpResponse) { + this.joddHttpResponse = joddHttpResponse; + } + + public HttpResponseProxy(Response okHttpResponse) { + this.okHttpResponse = okHttpResponse; + } + + public String getFileName() throws WxErrorException { + //由于对象只能由一个构造方法实现,因此三个response对象必定且只有一个不为空 + if (this.apacheHttpResponse != null) { + return this.getFileName(this.apacheHttpResponse); + } + + if (this.joddHttpResponse != null) { + return this.getFileName(this.joddHttpResponse); + } + + if (this.okHttpResponse != null) { + return this.getFileName(this.okHttpResponse); + } + + //cannot happen + return null; + } + + private String getFileName(CloseableHttpResponse response) throws WxErrorException { + Header[] contentDispositionHeader = response.getHeaders("Content-disposition"); + if (contentDispositionHeader == null || contentDispositionHeader.length == 0) { + throw new WxErrorException(WxError.newBuilder().setErrorMsg("无法获取到文件名").build()); + } + + return this.extractFileNameFromContentString(contentDispositionHeader[0].getValue()); + } + + private String getFileName(HttpResponse response) throws WxErrorException { + String content = response.header("Content-disposition"); + return this.extractFileNameFromContentString(content); + } + + private String getFileName(Response response) throws WxErrorException { + String content = response.header("Content-disposition"); + return this.extractFileNameFromContentString(content); + } + + private String extractFileNameFromContentString(String content) throws WxErrorException { + if (content == null || content.length() == 0) { + throw new WxErrorException(WxError.newBuilder().setErrorMsg("无法获取到文件名").build()); + } + + Pattern p = Pattern.compile(".*filename=\"(.*)\""); + Matcher m = p.matcher(content); + if (m.matches()) { + return m.group(1); + } + throw new WxErrorException(WxError.newBuilder().setErrorMsg("无法获取到文件名").build()); + } + +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/HttpType.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/HttpType.java index 5e78a6f1d0..c692eb9100 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/HttpType.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/HttpType.java @@ -4,5 +4,5 @@ * Created by ecoolper on 2017/4/28. */ public enum HttpType { - joddHttp, apacheHttp, okHttp; + JODD_HTTP, APACHE_HTTP, OK_HTTP; } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaDownloadRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaDownloadRequestExecutor.java index 72ceeb14b4..d948859748 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaDownloadRequestExecutor.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaDownloadRequestExecutor.java @@ -1,8 +1,8 @@ package me.chanjar.weixin.common.util.http; import me.chanjar.weixin.common.util.http.apache.ApacheMediaDownloadRequestExecutor; -import me.chanjar.weixin.common.util.http.jodd.JoddMediaDownloadRequestExecutor; -import me.chanjar.weixin.common.util.http.okhttp.OkMediaDownloadRequestExecutor; +import me.chanjar.weixin.common.util.http.jodd.JoddHttpMediaDownloadRequestExecutor; +import me.chanjar.weixin.common.util.http.okhttp.OkHttpMediaDownloadRequestExecutor; import java.io.File; @@ -17,17 +17,18 @@ public abstract class MediaDownloadRequestExecutor implements RequestExecu protected RequestHttp requestHttp; protected File tmpDirFile; public MediaDownloadRequestExecutor(RequestHttp requestHttp, File tmpDirFile) { + this.requestHttp = requestHttp; this.tmpDirFile = tmpDirFile; } public static RequestExecutor create(RequestHttp requestHttp, File tmpDirFile) { switch (requestHttp.getRequestType()) { - case apacheHttp: + case APACHE_HTTP: return new ApacheMediaDownloadRequestExecutor(requestHttp, tmpDirFile); - case joddHttp: - return new JoddMediaDownloadRequestExecutor(requestHttp, tmpDirFile); - case okHttp: - return new OkMediaDownloadRequestExecutor(requestHttp, tmpDirFile); + case JODD_HTTP: + return new JoddHttpMediaDownloadRequestExecutor(requestHttp, tmpDirFile); + case OK_HTTP: + return new OkHttpMediaDownloadRequestExecutor(requestHttp, tmpDirFile); default: return null; } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaUploadRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaUploadRequestExecutor.java index 1352954c2a..6b883ce7cd 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaUploadRequestExecutor.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaUploadRequestExecutor.java @@ -2,8 +2,8 @@ import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; import me.chanjar.weixin.common.util.http.apache.ApacheMediaUploadRequestExecutor; -import me.chanjar.weixin.common.util.http.jodd.JoddMediaUploadRequestExecutor; -import me.chanjar.weixin.common.util.http.okhttp.OkMediaUploadRequestExecutor; +import me.chanjar.weixin.common.util.http.jodd.JoddHttpMediaUploadRequestExecutor; +import me.chanjar.weixin.common.util.http.okhttp.OkHttpMediaUploadRequestExecutor; import java.io.File; @@ -21,12 +21,12 @@ public MediaUploadRequestExecutor(RequestHttp requestHttp) { public static RequestExecutor create(RequestHttp requestHttp) { switch (requestHttp.getRequestType()) { - case apacheHttp: + case APACHE_HTTP: return new ApacheMediaUploadRequestExecutor(requestHttp); - case joddHttp: - return new JoddMediaUploadRequestExecutor(requestHttp); - case okHttp: - return new OkMediaUploadRequestExecutor(requestHttp); + case JODD_HTTP: + return new JoddHttpMediaUploadRequestExecutor(requestHttp); + case OK_HTTP: + return new OkHttpMediaUploadRequestExecutor(requestHttp); default: return null; } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/RequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/RequestExecutor.java index a31796e668..583db4f5ae 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/RequestExecutor.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/RequestExecutor.java @@ -54,6 +54,6 @@ public interface RequestExecutor { * @throws WxErrorException * @throws IOException *//* - T executeOkhttp(ConnectionPool pool, final OkhttpProxyInfo proxyInfo, String uri, E data) throws WxErrorException, IOException; + T executeOkhttp(ConnectionPool pool, final OkHttpProxyInfo proxyInfo, String uri, E data) throws WxErrorException, IOException; */ } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/RequestHttp.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/RequestHttp.java index df27d44e92..efd9f99b9d 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/RequestHttp.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/RequestHttp.java @@ -8,20 +8,15 @@ public interface RequestHttp { /** * 返回httpClient * - * @return */ H getRequestHttpClient(); /** * 返回httpProxy * - * @return */ P getRequestHttpProxy(); - /** - * @return - */ HttpType getRequestType(); } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/SimpleGetRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/SimpleGetRequestExecutor.java index c7d782f50c..e72acc9084 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/SimpleGetRequestExecutor.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/SimpleGetRequestExecutor.java @@ -1,8 +1,8 @@ package me.chanjar.weixin.common.util.http; -import me.chanjar.weixin.common.util.http.apache.ApacheSimpleGetRequestExecutor; -import me.chanjar.weixin.common.util.http.jodd.JoddSimpleGetRequestExecutor; -import me.chanjar.weixin.common.util.http.okhttp.OkSimpleGetRequestExecutor; +import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientSimpleGetRequestExecutor; +import me.chanjar.weixin.common.util.http.jodd.JoddHttpSimpleGetRequestExecutor; +import me.chanjar.weixin.common.util.http.okhttp.OkHttpSimpleGetRequestExecutor; /** * 简单的GET请求执行器,请求的参数是String, 返回的结果也是String @@ -12,21 +12,20 @@ public abstract class SimpleGetRequestExecutor implements RequestExecutor { protected RequestHttp requestHttp; - public SimpleGetRequestExecutor(RequestHttp requestHttp) { + public SimpleGetRequestExecutor(RequestHttp requestHttp) { this.requestHttp = requestHttp; } - public static RequestExecutor create(RequestHttp requestHttp) { switch (requestHttp.getRequestType()) { - case apacheHttp: - return new ApacheSimpleGetRequestExecutor(requestHttp); - case joddHttp: - return new JoddSimpleGetRequestExecutor(requestHttp); - case okHttp: - return new OkSimpleGetRequestExecutor(requestHttp); + case APACHE_HTTP: + return new ApacheHttpClientSimpleGetRequestExecutor(requestHttp); + case JODD_HTTP: + return new JoddHttpSimpleGetRequestExecutor(requestHttp); + case OK_HTTP: + return new OkHttpSimpleGetRequestExecutor(requestHttp); default: - return null; + throw new IllegalArgumentException("非法请求参数"); } } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/SimplePostRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/SimplePostRequestExecutor.java index a8d4d7839d..2932f24728 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/SimplePostRequestExecutor.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/SimplePostRequestExecutor.java @@ -1,8 +1,8 @@ package me.chanjar.weixin.common.util.http; import me.chanjar.weixin.common.util.http.apache.ApacheSimplePostRequestExecutor; -import me.chanjar.weixin.common.util.http.jodd.JoddSimplePostRequestExecutor; -import me.chanjar.weixin.common.util.http.okhttp.OkSimplePostRequestExecutor; +import me.chanjar.weixin.common.util.http.jodd.JoddHttpSimplePostRequestExecutor; +import me.chanjar.weixin.common.util.http.okhttp.OkHttpSimplePostRequestExecutor; /** * 用装饰模式实现 @@ -19,12 +19,12 @@ public SimplePostRequestExecutor(RequestHttp requestHttp) { public static RequestExecutor create(RequestHttp requestHttp) { switch (requestHttp.getRequestType()) { - case apacheHttp: + case APACHE_HTTP: return new ApacheSimplePostRequestExecutor(requestHttp); - case joddHttp: - return new JoddSimplePostRequestExecutor(requestHttp); - case okHttp: - return new OkSimplePostRequestExecutor(requestHttp); + case JODD_HTTP: + return new JoddHttpSimplePostRequestExecutor(requestHttp); + case OK_HTTP: + return new OkHttpSimplePostRequestExecutor(requestHttp); default: return null; } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheSimpleGetRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheHttpClientSimpleGetRequestExecutor.java similarity index 88% rename from weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheSimpleGetRequestExecutor.java rename to weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheHttpClientSimpleGetRequestExecutor.java index ebc7050e3d..e47216e6cd 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheSimpleGetRequestExecutor.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheHttpClientSimpleGetRequestExecutor.java @@ -15,9 +15,9 @@ /** * Created by ecoolper on 2017/5/4. */ -public class ApacheSimpleGetRequestExecutor extends SimpleGetRequestExecutor { +public class ApacheHttpClientSimpleGetRequestExecutor extends SimpleGetRequestExecutor { - public ApacheSimpleGetRequestExecutor(RequestHttp requestHttp) { + public ApacheHttpClientSimpleGetRequestExecutor(RequestHttp requestHttp) { super(requestHttp); } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheMediaDownloadRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheMediaDownloadRequestExecutor.java index 6a928648e5..0a5a504873 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheMediaDownloadRequestExecutor.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheMediaDownloadRequestExecutor.java @@ -3,8 +3,10 @@ import me.chanjar.weixin.common.bean.result.WxError; import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.common.util.fs.FileUtils; +import me.chanjar.weixin.common.util.http.HttpResponseProxy; import me.chanjar.weixin.common.util.http.MediaDownloadRequestExecutor; import me.chanjar.weixin.common.util.http.RequestHttp; +import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.StringUtils; import org.apache.http.Header; import org.apache.http.HttpHost; @@ -17,15 +19,12 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.util.regex.Matcher; -import java.util.regex.Pattern; /** * Created by ecoolper on 2017/5/5. */ public class ApacheMediaDownloadRequestExecutor extends MediaDownloadRequestExecutor { - public ApacheMediaDownloadRequestExecutor(RequestHttp requestHttp, File tmpDirFile) { super(requestHttp, tmpDirFile); } @@ -57,31 +56,17 @@ public File execute(String uri, String queryParam) throws WxErrorException, IOEx } } - String fileName = getFileName(response); + String fileName = new HttpResponseProxy(response).getFileName(); if (StringUtils.isBlank(fileName)) { return null; } - String[] nameAndExt = fileName.split("\\."); - return FileUtils.createTmpFile(inputStream, nameAndExt[0], nameAndExt[1], super.tmpDirFile); + return FileUtils.createTmpFile(inputStream, FilenameUtils.getBaseName(fileName), FilenameUtils.getExtension(fileName), + super.tmpDirFile); } finally { httpGet.releaseConnection(); } } - private String getFileName(CloseableHttpResponse response) throws WxErrorException { - Header[] contentDispositionHeader = response.getHeaders("Content-disposition"); - if (contentDispositionHeader == null || contentDispositionHeader.length == 0) { - throw new WxErrorException(WxError.newBuilder().setErrorMsg("无法获取到文件名").build()); - } - - Pattern p = Pattern.compile(".*filename=\"(.*)\""); - Matcher m = p.matcher(contentDispositionHeader[0].getValue()); - if (m.matches()) { - return m.group(1); - } - throw new WxErrorException(WxError.newBuilder().setErrorMsg("无法获取到文件名").build()); - } - } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheMediaUploadRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheMediaUploadRequestExecutor.java index e250157bc8..bdddf0dfb9 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheMediaUploadRequestExecutor.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheMediaUploadRequestExecutor.java @@ -40,7 +40,6 @@ public WxMediaUploadResult execute(String uri, File file) throws WxErrorExceptio .setMode(HttpMultipartMode.RFC6532) .build(); httpPost.setEntity(entity); - httpPost.setHeader("Content-Type", ContentType.MULTIPART_FORM_DATA.toString()); } try (CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpPost)) { String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/DefaultApacheHttpClientBuilder.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/DefaultApacheHttpClientBuilder.java index 875f2fccb8..19c79bdcf2 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/DefaultApacheHttpClientBuilder.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/DefaultApacheHttpClientBuilder.java @@ -14,16 +14,24 @@ import org.apache.http.conn.socket.ConnectionSocketFactory; import org.apache.http.conn.socket.PlainConnectionSocketFactory; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.conn.ssl.TrustStrategy; import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.protocol.HttpContext; +import org.apache.http.ssl.SSLContexts; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.net.ssl.SSLContext; import java.io.IOException; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -60,13 +68,16 @@ public boolean retryRequest(IOException exception, int executionCount, HttpConte * 闲置连接监控线程 */ private IdleConnectionMonitorThread idleConnectionMonitorThread; - private HttpClientBuilder httpClientBuilder; + /** + * 持有client对象,仅初始化一次,避免多service实例的时候造成重复初始化的问题 + */ + private CloseableHttpClient closeableHttpClient; private DefaultApacheHttpClientBuilder() { } public static DefaultApacheHttpClientBuilder get() { - return new DefaultApacheHttpClientBuilder(); + return DefaultApacheHttpClientBuilder.SingletonHolder.INSTANCE; } @Override @@ -211,9 +222,10 @@ private synchronized void prepare() { this.idleConnectionMonitorThread.setDaemon(true); this.idleConnectionMonitorThread.start(); - this.httpClientBuilder = HttpClients.custom() + HttpClientBuilder httpClientBuilder = HttpClients.custom() .setConnectionManager(connectionManager) .setConnectionManagerShared(true) + .setSSLSocketFactory(this.buildSSLConnectionSocketFactory()) .setDefaultRequestConfig( RequestConfig.custom() .setSocketTimeout(this.soTimeout) @@ -231,21 +243,52 @@ private synchronized void prepare() { new AuthScope(this.httpProxyHost, this.httpProxyPort), new UsernamePasswordCredentials(this.httpProxyUsername, this.httpProxyPassword)); - this.httpClientBuilder.setDefaultCredentialsProvider(provider); + httpClientBuilder.setDefaultCredentialsProvider(provider); } if (StringUtils.isNotBlank(this.userAgent)) { - this.httpClientBuilder.setUserAgent(this.userAgent); + httpClientBuilder.setUserAgent(this.userAgent); } + this.closeableHttpClient = httpClientBuilder.build(); prepared.set(true); } + private SSLConnectionSocketFactory buildSSLConnectionSocketFactory() { + try { + SSLContext sslcontext = SSLContexts.custom() + //忽略掉对服务器端证书的校验 + .loadTrustMaterial(new TrustStrategy() { + @Override + public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { + return true; + } + }).build(); + + return new SSLConnectionSocketFactory( + sslcontext, + new String[]{"TLSv1"}, + null, + SSLConnectionSocketFactory.getDefaultHostnameVerifier()); + } catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) { + this.log.error(e.getMessage(), e); + } + + return null; + } + @Override public CloseableHttpClient build() { if (!prepared.get()) { prepare(); } - return this.httpClientBuilder.build(); + return this.closeableHttpClient; + } + + /** + * DefaultApacheHttpClientBuilder 改为单例模式,并持有唯一的CloseableHttpClient(仅首次调用创建) + */ + private static class SingletonHolder { + private static final DefaultApacheHttpClientBuilder INSTANCE = new DefaultApacheHttpClientBuilder(); } public static class IdleConnectionMonitorThread extends Thread { diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddMediaDownloadRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddHttpMediaDownloadRequestExecutor.java similarity index 63% rename from weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddMediaDownloadRequestExecutor.java rename to weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddHttpMediaDownloadRequestExecutor.java index 230b3ca69b..bbd8bec47c 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddMediaDownloadRequestExecutor.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddHttpMediaDownloadRequestExecutor.java @@ -4,27 +4,27 @@ import jodd.http.HttpRequest; import jodd.http.HttpResponse; import jodd.http.ProxyInfo; +import jodd.util.StringPool; import me.chanjar.weixin.common.bean.result.WxError; import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.common.util.fs.FileUtils; +import me.chanjar.weixin.common.util.http.HttpResponseProxy; import me.chanjar.weixin.common.util.http.MediaDownloadRequestExecutor; import me.chanjar.weixin.common.util.http.RequestHttp; +import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.StringUtils; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.util.regex.Matcher; -import java.util.regex.Pattern; /** * Created by ecoolper on 2017/5/5. */ -public class JoddMediaDownloadRequestExecutor extends MediaDownloadRequestExecutor { +public class JoddHttpMediaDownloadRequestExecutor extends MediaDownloadRequestExecutor { - - public JoddMediaDownloadRequestExecutor(RequestHttp requestHttp, File tmpDirFile) { + public JoddHttpMediaDownloadRequestExecutor(RequestHttp requestHttp, File tmpDirFile) { super(requestHttp, tmpDirFile); } @@ -44,34 +44,23 @@ public File execute(String uri, String queryParam) throws WxErrorException, IOEx request.withConnectionProvider(requestHttp.getRequestHttpClient()); HttpResponse response = request.send(); + response.charset(StringPool.UTF_8); + String contentType = response.header("Content-Type"); if (contentType != null && contentType.startsWith("application/json")) { // application/json; encoding=utf-8 下载媒体文件出错 throw new WxErrorException(WxError.fromJson(response.bodyText())); } - String fileName = getFileName(response); + String fileName = new HttpResponseProxy(response).getFileName(); if (StringUtils.isBlank(fileName)) { return null; } InputStream inputStream = new ByteArrayInputStream(response.bodyBytes()); - String[] nameAndExt = fileName.split("\\."); - return FileUtils.createTmpFile(inputStream, nameAndExt[0], nameAndExt[1], super.tmpDirFile); + return FileUtils.createTmpFile(inputStream, FilenameUtils.getBaseName(fileName), FilenameUtils.getExtension(fileName), + super.tmpDirFile); } - private String getFileName(HttpResponse response) throws WxErrorException { - String content = response.header("Content-disposition"); - if (content == null || content.length() == 0) { - throw new WxErrorException(WxError.newBuilder().setErrorMsg("无法获取到文件名").build()); - } - - Pattern p = Pattern.compile(".*filename=\"(.*)\""); - Matcher m = p.matcher(content); - if (m.matches()) { - return m.group(1); - } - throw new WxErrorException(WxError.newBuilder().setErrorMsg("无法获取到文件名").build()); - } } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddMediaUploadRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddHttpMediaUploadRequestExecutor.java similarity index 83% rename from weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddMediaUploadRequestExecutor.java rename to weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddHttpMediaUploadRequestExecutor.java index 43f17fbcf0..b4eef26a79 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddMediaUploadRequestExecutor.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddHttpMediaUploadRequestExecutor.java @@ -4,6 +4,8 @@ import jodd.http.HttpRequest; import jodd.http.HttpResponse; import jodd.http.ProxyInfo; +import jodd.util.StringPool; + import me.chanjar.weixin.common.bean.result.WxError; import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; import me.chanjar.weixin.common.exception.WxErrorException; @@ -16,8 +18,8 @@ /** * Created by ecoolper on 2017/5/5. */ -public class JoddMediaUploadRequestExecutor extends MediaUploadRequestExecutor { - public JoddMediaUploadRequestExecutor(RequestHttp requestHttp) { +public class JoddHttpMediaUploadRequestExecutor extends MediaUploadRequestExecutor { + public JoddHttpMediaUploadRequestExecutor(RequestHttp requestHttp) { super(requestHttp); } @@ -30,6 +32,8 @@ public WxMediaUploadResult execute(String uri, File file) throws WxErrorExceptio request.withConnectionProvider(requestHttp.getRequestHttpClient()); request.form("media", file); HttpResponse response = request.send(); + response.charset(StringPool.UTF_8); + String responseContent = response.bodyText(); WxError error = WxError.fromJson(responseContent); if (error.getErrorCode() != 0) { diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddSimpleGetRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddHttpSimpleGetRequestExecutor.java similarity index 79% rename from weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddSimpleGetRequestExecutor.java rename to weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddHttpSimpleGetRequestExecutor.java index a43fe5f206..63386e77a8 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddSimpleGetRequestExecutor.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddHttpSimpleGetRequestExecutor.java @@ -1,9 +1,8 @@ package me.chanjar.weixin.common.util.http.jodd; -import jodd.http.HttpConnectionProvider; -import jodd.http.HttpRequest; -import jodd.http.HttpResponse; -import jodd.http.ProxyInfo; +import jodd.http.*; +import jodd.util.StringPool; + import me.chanjar.weixin.common.bean.result.WxError; import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.common.util.http.RequestHttp; @@ -14,9 +13,9 @@ /** * Created by ecoolper on 2017/5/4. */ -public class JoddSimpleGetRequestExecutor extends SimpleGetRequestExecutor { +public class JoddHttpSimpleGetRequestExecutor extends SimpleGetRequestExecutor { - public JoddSimpleGetRequestExecutor(RequestHttp requestHttp) { + public JoddHttpSimpleGetRequestExecutor(RequestHttp requestHttp) { super(requestHttp); } @@ -35,7 +34,10 @@ public String execute(String uri, String queryParam) throws WxErrorException, IO } request.withConnectionProvider(requestHttp.getRequestHttpClient()); HttpResponse response = request.send(); + response.charset(StringPool.UTF_8); + String responseContent = response.bodyText(); + WxError error = WxError.fromJson(responseContent); if (error.getErrorCode() != 0) { throw new WxErrorException(error); diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddSimplePostRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddHttpSimplePostRequestExecutor.java similarity index 85% rename from weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddSimplePostRequestExecutor.java rename to weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddHttpSimplePostRequestExecutor.java index 1db0f2af30..8fa349c67a 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddSimplePostRequestExecutor.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddHttpSimplePostRequestExecutor.java @@ -4,6 +4,8 @@ import jodd.http.HttpRequest; import jodd.http.HttpResponse; import jodd.http.ProxyInfo; +import jodd.util.StringPool; + import me.chanjar.weixin.common.bean.result.WxError; import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.common.util.http.RequestHttp; @@ -14,9 +16,9 @@ /** * Created by ecoolper on 2017/5/4. */ -public class JoddSimplePostRequestExecutor extends SimplePostRequestExecutor { +public class JoddHttpSimplePostRequestExecutor extends SimplePostRequestExecutor { - public JoddSimplePostRequestExecutor(RequestHttp requestHttp) { + public JoddHttpSimplePostRequestExecutor(RequestHttp requestHttp) { super(requestHttp); } @@ -34,6 +36,7 @@ public String execute(String uri, String postEntity) throws WxErrorException, IO request.bodyText(postEntity); } HttpResponse response = request.send(); + response.charset(StringPool.UTF_8); String responseContent = response.bodyText(); if (responseContent.isEmpty()) { diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkHttpMediaDownloadRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkHttpMediaDownloadRequestExecutor.java new file mode 100644 index 0000000000..0923527b71 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkHttpMediaDownloadRequestExecutor.java @@ -0,0 +1,68 @@ +package me.chanjar.weixin.common.util.http.okhttp; + +import me.chanjar.weixin.common.bean.result.WxError; +import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.util.http.HttpResponseProxy; +import me.chanjar.weixin.common.util.http.MediaDownloadRequestExecutor; +import me.chanjar.weixin.common.util.http.RequestHttp; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okio.BufferedSink; +import okio.Okio; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; + +/** + * Created by ecoolper on 2017/5/5. + */ +public class OkHttpMediaDownloadRequestExecutor extends MediaDownloadRequestExecutor { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + public OkHttpMediaDownloadRequestExecutor(RequestHttp requestHttp, File tmpDirFile) { + super(requestHttp, tmpDirFile); + } + + @Override + public File execute(String uri, String queryParam) throws WxErrorException, IOException { + logger.debug("OkHttpMediaDownloadRequestExecutor is running"); + if (queryParam != null) { + if (uri.indexOf('?') == -1) { + uri += '?'; + } + uri += uri.endsWith("?") ? queryParam : '&' + queryParam; + } + + //得到httpClient + OkHttpClient client = requestHttp.getRequestHttpClient(); + + Request request = new Request.Builder().url(uri).get().build(); + + Response response = client.newCall(request).execute(); + + String contentType = response.header("Content-Type"); + if (contentType != null && contentType.startsWith("application/json")) { + // application/json; encoding=utf-8 下载媒体文件出错 + throw new WxErrorException(WxError.fromJson(response.body().string())); + } + + String fileName = new HttpResponseProxy(response).getFileName(); + if (StringUtils.isBlank(fileName)) { + return null; + } + + File file = File.createTempFile(FilenameUtils.getBaseName(fileName), FilenameUtils.getExtension(fileName), + super.tmpDirFile); + try (BufferedSink sink = Okio.buffer(Okio.sink(file))) { + sink.writeAll(response.body().source()); + } + file.deleteOnExit(); + return file; + } + +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkHttpMediaUploadRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkHttpMediaUploadRequestExecutor.java new file mode 100644 index 0000000000..9edeee89be --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkHttpMediaUploadRequestExecutor.java @@ -0,0 +1,48 @@ +package me.chanjar.weixin.common.util.http.okhttp; + +import me.chanjar.weixin.common.bean.result.WxError; +import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; +import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; +import me.chanjar.weixin.common.util.http.RequestHttp; +import okhttp3.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; + +/** + * Created by ecoolper on 2017/5/5. + */ +public class OkHttpMediaUploadRequestExecutor extends MediaUploadRequestExecutor { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + public OkHttpMediaUploadRequestExecutor(RequestHttp requestHttp) { + super(requestHttp); + } + + @Override + public WxMediaUploadResult execute(String uri, File file) throws WxErrorException, IOException { + logger.debug("OkHttpMediaUploadRequestExecutor is running"); + //得到httpClient + OkHttpClient client = requestHttp.getRequestHttpClient(); + + RequestBody body = new MultipartBody.Builder() + .setType(MediaType.parse("multipart/form-data")) + .addFormDataPart("media", + file.getName(), + RequestBody.create(MediaType.parse("application/octet-stream"), file)) + .build(); + Request request = new Request.Builder().url(uri).post(body).build(); + + Response response = client.newCall(request).execute(); + String responseContent = response.body().string(); + WxError error = WxError.fromJson(responseContent); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + return WxMediaUploadResult.fromJson(responseContent); + } + +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkhttpProxyInfo.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkHttpProxyInfo.java similarity index 81% rename from weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkhttpProxyInfo.java rename to weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkHttpProxyInfo.java index daaa92f2fb..eb9f7ac908 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkhttpProxyInfo.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkHttpProxyInfo.java @@ -7,13 +7,14 @@ * Created by ecoolper on 2017/4/26. * Proxy information. */ -public class OkhttpProxyInfo { +public class OkHttpProxyInfo { private final String proxyAddress; private final int proxyPort; private final String proxyUsername; private final String proxyPassword; private final ProxyType proxyType; - public OkhttpProxyInfo(ProxyType proxyType, String proxyHost, int proxyPort, String proxyUser, String proxyPassword) { + + public OkHttpProxyInfo(ProxyType proxyType, String proxyHost, int proxyPort, String proxyUser, String proxyPassword) { this.proxyType = proxyType; this.proxyAddress = proxyHost; this.proxyPort = proxyPort; @@ -24,8 +25,8 @@ public OkhttpProxyInfo(ProxyType proxyType, String proxyHost, int proxyPort, Str /** * Creates directProxy. */ - public static OkhttpProxyInfo directProxy() { - return new OkhttpProxyInfo(ProxyType.NONE, null, 0, null, null); + public static OkHttpProxyInfo directProxy() { + return new OkHttpProxyInfo(ProxyType.NONE, null, 0, null, null); } // ---------------------------------------------------------------- factory @@ -33,22 +34,22 @@ public static OkhttpProxyInfo directProxy() { /** * Creates SOCKS4 proxy. */ - public static OkhttpProxyInfo socks4Proxy(String proxyAddress, int proxyPort, String proxyUser) { - return new OkhttpProxyInfo(ProxyType.SOCKS4, proxyAddress, proxyPort, proxyUser, null); + public static OkHttpProxyInfo socks4Proxy(String proxyAddress, int proxyPort, String proxyUser) { + return new OkHttpProxyInfo(ProxyType.SOCKS4, proxyAddress, proxyPort, proxyUser, null); } /** * Creates SOCKS5 proxy. */ - public static OkhttpProxyInfo socks5Proxy(String proxyAddress, int proxyPort, String proxyUser, String proxyPassword) { - return new OkhttpProxyInfo(ProxyType.SOCKS5, proxyAddress, proxyPort, proxyUser, proxyPassword); + public static OkHttpProxyInfo socks5Proxy(String proxyAddress, int proxyPort, String proxyUser, String proxyPassword) { + return new OkHttpProxyInfo(ProxyType.SOCKS5, proxyAddress, proxyPort, proxyUser, proxyPassword); } /** * Creates HTTP proxy. */ - public static OkhttpProxyInfo httpProxy(String proxyAddress, int proxyPort, String proxyUser, String proxyPassword) { - return new OkhttpProxyInfo(ProxyType.HTTP, proxyAddress, proxyPort, proxyUser, proxyPassword); + public static OkHttpProxyInfo httpProxy(String proxyAddress, int proxyPort, String proxyUser, String proxyPassword) { + return new OkHttpProxyInfo(ProxyType.HTTP, proxyAddress, proxyPort, proxyUser, proxyPassword); } /** @@ -91,8 +92,6 @@ public String getProxyPassword() { /** * 返回 java.net.Proxy - * - * @return */ public Proxy getProxy() { Proxy proxy = null; diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkSimpleGetRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkHttpSimpleGetRequestExecutor.java similarity index 52% rename from weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkSimpleGetRequestExecutor.java rename to weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkHttpSimpleGetRequestExecutor.java index ba1ba999fd..e468c609c8 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkSimpleGetRequestExecutor.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkHttpSimpleGetRequestExecutor.java @@ -5,20 +5,24 @@ import me.chanjar.weixin.common.util.http.RequestHttp; import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; import okhttp3.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; /** * Created by ecoolper on 2017/5/4. */ -public class OkSimpleGetRequestExecutor extends SimpleGetRequestExecutor { +public class OkHttpSimpleGetRequestExecutor extends SimpleGetRequestExecutor { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); - public OkSimpleGetRequestExecutor(RequestHttp requestHttp) { + public OkHttpSimpleGetRequestExecutor(RequestHttp requestHttp) { super(requestHttp); } @Override public String execute(String uri, String queryParam) throws WxErrorException, IOException { + logger.debug("OkHttpSimpleGetRequestExecutor is running"); if (queryParam != null) { if (uri.indexOf('?') == -1) { uri += '?'; @@ -26,26 +30,9 @@ public String execute(String uri, String queryParam) throws WxErrorException, IO uri += uri.endsWith("?") ? queryParam : '&' + queryParam; } - OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder().connectionPool(requestHttp.getRequestHttpClient()); - //设置代理 - if (requestHttp.getRequestHttpProxy() != null) { - clientBuilder.proxy(requestHttp.getRequestHttpProxy().getProxy()); - } - //设置授权 - clientBuilder.authenticator(new Authenticator() { - @Override - public Request authenticate(Route route, Response response) throws IOException { - String credential = Credentials.basic(requestHttp.getRequestHttpProxy().getProxyUsername(), requestHttp.getRequestHttpProxy().getProxyPassword()); - return response.request().newBuilder() - .header("Authorization", credential) - .build(); - } - }); //得到httpClient - OkHttpClient client = clientBuilder.build(); - + OkHttpClient client = requestHttp.getRequestHttpClient(); Request request = new Request.Builder().url(uri).build(); - Response response = client.newCall(request).execute(); String responseContent = response.body().string(); WxError error = WxError.fromJson(responseContent); diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkSimplePostRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkHttpSimplePostRequestExecutor.java similarity index 51% rename from weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkSimplePostRequestExecutor.java rename to weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkHttpSimplePostRequestExecutor.java index eed6850172..bc54d7b4cd 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkSimplePostRequestExecutor.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkHttpSimplePostRequestExecutor.java @@ -5,41 +5,26 @@ import me.chanjar.weixin.common.util.http.RequestHttp; import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor; import okhttp3.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; /** * Created by ecoolper on 2017/5/4. */ -public class OkSimplePostRequestExecutor extends SimplePostRequestExecutor { +public class OkHttpSimplePostRequestExecutor extends SimplePostRequestExecutor { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); - public OkSimplePostRequestExecutor(RequestHttp requestHttp) { + public OkHttpSimplePostRequestExecutor(RequestHttp requestHttp) { super(requestHttp); } @Override public String execute(String uri, String postEntity) throws WxErrorException, IOException { - ConnectionPool pool = requestHttp.getRequestHttpClient(); - final OkhttpProxyInfo proxyInfo = requestHttp.getRequestHttpProxy(); - - OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder().connectionPool(pool); - //设置代理 - if (proxyInfo != null) { - clientBuilder.proxy(proxyInfo.getProxy()); - } - //设置授权 - clientBuilder.authenticator(new Authenticator() { - @Override - public Request authenticate(Route route, Response response) throws IOException { - String credential = Credentials.basic(proxyInfo.getProxyUsername(), proxyInfo.getProxyPassword()); - return response.request().newBuilder() - .header("Authorization", credential) - .build(); - } - }); + logger.debug("OkHttpSimplePostRequestExecutor running"); //得到httpClient - OkHttpClient client = clientBuilder.build(); - + OkHttpClient client = requestHttp.getRequestHttpClient(); MediaType mediaType = MediaType.parse("text/plain; charset=utf-8"); RequestBody body = RequestBody.create(mediaType, postEntity); diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkMediaDownloadRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkMediaDownloadRequestExecutor.java deleted file mode 100644 index ea9a95a9d6..0000000000 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkMediaDownloadRequestExecutor.java +++ /dev/null @@ -1,89 +0,0 @@ -package me.chanjar.weixin.common.util.http.okhttp; - -import me.chanjar.weixin.common.bean.result.WxError; -import me.chanjar.weixin.common.exception.WxErrorException; -import me.chanjar.weixin.common.util.fs.FileUtils; -import me.chanjar.weixin.common.util.http.MediaDownloadRequestExecutor; -import me.chanjar.weixin.common.util.http.RequestHttp; -import okhttp3.*; -import org.apache.commons.lang3.StringUtils; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Created by ecoolper on 2017/5/5. - */ -public class OkMediaDownloadRequestExecutor extends MediaDownloadRequestExecutor { - - - public OkMediaDownloadRequestExecutor(RequestHttp requestHttp, File tmpDirFile) { - super(requestHttp, tmpDirFile); - } - - @Override - public File execute(String uri, String queryParam) throws WxErrorException, IOException { - if (queryParam != null) { - if (uri.indexOf('?') == -1) { - uri += '?'; - } - uri += uri.endsWith("?") ? queryParam : '&' + queryParam; - } - - OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder().connectionPool(requestHttp.getRequestHttpClient()); - //设置代理 - if (requestHttp.getRequestHttpProxy() != null) { - clientBuilder.proxy(requestHttp.getRequestHttpProxy().getProxy()); - } - //设置授权 - clientBuilder.authenticator(new Authenticator() { - @Override - public Request authenticate(Route route, Response response) throws IOException { - String credential = Credentials.basic(requestHttp.getRequestHttpProxy().getProxyUsername(), requestHttp.getRequestHttpProxy().getProxyPassword()); - return response.request().newBuilder() - .header("Authorization", credential) - .build(); - } - }); - //得到httpClient - OkHttpClient client = clientBuilder.build(); - - Request request = new Request.Builder().url(uri).get().build(); - - Response response = client.newCall(request).execute(); - - String contentType = response.header("Content-Type"); - if (contentType != null && contentType.startsWith("application/json")) { - // application/json; encoding=utf-8 下载媒体文件出错 - throw new WxErrorException(WxError.fromJson(response.body().string())); - } - - String fileName = getFileName(response); - if (StringUtils.isBlank(fileName)) { - return null; - } - - InputStream inputStream = new ByteArrayInputStream(response.body().bytes()); - String[] nameAndExt = fileName.split("\\."); - return FileUtils.createTmpFile(inputStream, nameAndExt[0], nameAndExt[1], super.tmpDirFile); - } - - private String getFileName(Response response) throws WxErrorException { - String content = response.header("Content-disposition"); - if (content == null || content.length() == 0) { - throw new WxErrorException(WxError.newBuilder().setErrorMsg("无法获取到文件名").build()); - } - - Pattern p = Pattern.compile(".*filename=\"(.*)\""); - Matcher m = p.matcher(content); - if (m.matches()) { - return m.group(1); - } - throw new WxErrorException(WxError.newBuilder().setErrorMsg("无法获取到文件名").build()); - } - -} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkMediaUploadRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkMediaUploadRequestExecutor.java deleted file mode 100644 index 6dd1596d3f..0000000000 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkMediaUploadRequestExecutor.java +++ /dev/null @@ -1,55 +0,0 @@ -package me.chanjar.weixin.common.util.http.okhttp; - -import me.chanjar.weixin.common.bean.result.WxError; -import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; -import me.chanjar.weixin.common.exception.WxErrorException; -import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; -import me.chanjar.weixin.common.util.http.RequestHttp; -import okhttp3.*; - -import java.io.File; -import java.io.IOException; - -/** - * Created by ecoolper on 2017/5/5. - */ -public class OkMediaUploadRequestExecutor extends MediaUploadRequestExecutor { - - public OkMediaUploadRequestExecutor(RequestHttp requestHttp) { - super(requestHttp); - } - - @Override - public WxMediaUploadResult execute(String uri, File file) throws WxErrorException, IOException { - OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder().connectionPool(requestHttp.getRequestHttpClient()); - //设置代理 - if (requestHttp.getRequestHttpProxy() != null) { - clientBuilder.proxy(requestHttp.getRequestHttpProxy().getProxy()); - } - //设置授权 - clientBuilder.authenticator(new Authenticator() { - @Override - public Request authenticate(Route route, Response response) throws IOException { - String credential = Credentials.basic(requestHttp.getRequestHttpProxy().getProxyUsername(), requestHttp.getRequestHttpProxy().getProxyPassword()); - return response.request().newBuilder() - .header("Authorization", credential) - .build(); - } - }); - //得到httpClient - OkHttpClient client = clientBuilder.build(); - - RequestBody fileBody = RequestBody.create(MediaType.parse("multipart/form-data"), file); - RequestBody body = new MultipartBody.Builder().addFormDataPart("media", null, fileBody).build(); - Request request = new Request.Builder().url(uri).post(body).build(); - - Response response = client.newCall(request).execute(); - String responseContent = response.body().string(); - WxError error = WxError.fromJson(responseContent); - if (error.getErrorCode() != 0) { - throw new WxErrorException(error); - } - return WxMediaUploadResult.fromJson(responseContent); - } - -} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxBooleanTypeAdapter.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxBooleanTypeAdapter.java new file mode 100644 index 0000000000..3fbbef4a43 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxBooleanTypeAdapter.java @@ -0,0 +1,47 @@ +package me.chanjar.weixin.common.util.json; + +import com.google.gson.JsonParseException; +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import org.apache.commons.lang3.BooleanUtils; + +import java.io.IOException; + +/** + *
+ * Gson 布尔类型类型转换器
+ * Created by Binary Wang on 2017-7-8.
+ * 
+ * + * @author Binary Wang + */ +public class WxBooleanTypeAdapter extends TypeAdapter { + @Override + public void write(JsonWriter out, Boolean value) throws IOException { + if (value == null) { + out.nullValue(); + } else { + out.value(value); + } + } + + @Override + public Boolean read(JsonReader in) throws IOException { + JsonToken peek = in.peek(); + switch (peek) { + case BOOLEAN: + return in.nextBoolean(); + case NULL: + in.nextNull(); + return null; + case NUMBER: + return BooleanUtils.toBoolean(in.nextInt()); + case STRING: + return BooleanUtils.toBoolean(in.nextString()); + default: + throw new JsonParseException("Expected BOOLEAN or NUMBER but was " + peek); + } + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxDateTypeAdapter.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxDateTypeAdapter.java new file mode 100644 index 0000000000..fd54cf3f43 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxDateTypeAdapter.java @@ -0,0 +1,43 @@ +package me.chanjar.weixin.common.util.json; + +import com.google.gson.JsonParseException; +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; + +import java.io.IOException; +import java.util.Date; + +/** + *
+ * Gson 日期类型转换器
+ * Created by Binary Wang on 2017-7-8.
+ * 
+ * + * @author Binary Wang + */ +public class WxDateTypeAdapter extends TypeAdapter { + @Override + public void write(JsonWriter out, Date value) throws IOException { + if (value == null) { + out.nullValue(); + } else { + out.value(value.getTime() / 1000); + } + } + + @Override + public Date read(JsonReader in) throws IOException { + JsonToken peek = in.peek(); + switch (peek) { + case NULL: + in.nextNull(); + return null; + case NUMBER: + return new Date(in.nextInt() * 1000); + default: + throw new JsonParseException("Expected NUMBER but was " + peek); + } + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxMenuGsonAdapter.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxMenuGsonAdapter.java index 526b5f29c9..c506c1ed75 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxMenuGsonAdapter.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxMenuGsonAdapter.java @@ -90,9 +90,14 @@ public WxMenu deserialize(JsonElement json, Type typeOfT, JsonDeserializationCon * 操蛋的微信 * 创建菜单时是 { button : ... } * 查询菜单时是 { menu : { button : ... } } + * 现在企业号升级为企业微信后,没有此问题,因此需要单独处理 */ - WxMenu menu = new WxMenu(); JsonArray buttonsJson = json.getAsJsonObject().get("menu").getAsJsonObject().get("button").getAsJsonArray(); + return this.buildMenuFromJson(buttonsJson); + } + + protected WxMenu buildMenuFromJson(JsonArray buttonsJson) { + WxMenu menu = new WxMenu(); for (int i = 0; i < buttonsJson.size(); i++) { JsonObject buttonJson = buttonsJson.get(i).getAsJsonObject(); WxMenuButton button = convertFromJson(buttonJson); diff --git a/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/WxMessageInMemoryDuplicateCheckerTest.java b/weixin-java-common/src/test/java/me/chanjar/weixin/common/api/WxMessageInMemoryDuplicateCheckerTest.java similarity index 62% rename from weixin-java-common/src/test/java/me/chanjar/weixin/common/util/WxMessageInMemoryDuplicateCheckerTest.java rename to weixin-java-common/src/test/java/me/chanjar/weixin/common/api/WxMessageInMemoryDuplicateCheckerTest.java index c149584223..8599b29f89 100644 --- a/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/WxMessageInMemoryDuplicateCheckerTest.java +++ b/weixin-java-common/src/test/java/me/chanjar/weixin/common/api/WxMessageInMemoryDuplicateCheckerTest.java @@ -1,34 +1,36 @@ -package me.chanjar.weixin.common.util; +package me.chanjar.weixin.common.api; -import me.chanjar.weixin.common.api.WxMessageInMemoryDuplicateChecker; -import org.testng.*; -import org.testng.annotations.*; +import org.testng.Assert; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; @Test public class WxMessageInMemoryDuplicateCheckerTest { + private WxMessageInMemoryDuplicateChecker checker = new WxMessageInMemoryDuplicateChecker(2000l, 1000l); public void test() throws InterruptedException { Long[] msgIds = new Long[]{1l, 2l, 3l, 4l, 5l, 6l, 7l, 8l}; - WxMessageInMemoryDuplicateChecker checker = new WxMessageInMemoryDuplicateChecker(2000l, 1000l); // 第一次检查 for (Long msgId : msgIds) { boolean result = checker.isDuplicate(String.valueOf(msgId)); - Assert.assertFalse(result); + assertFalse(result); } // 过1秒再检查 Thread.sleep(1000l); for (Long msgId : msgIds) { boolean result = checker.isDuplicate(String.valueOf(msgId)); - Assert.assertTrue(result); + assertTrue(result); } // 过1.5秒再检查 Thread.sleep(1500l); for (Long msgId : msgIds) { boolean result = checker.isDuplicate(String.valueOf(msgId)); - Assert.assertFalse(result); + assertFalse(result); } } diff --git a/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/crypto/SHA1Test.java b/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/crypto/SHA1Test.java new file mode 100644 index 0000000000..323fc7060e --- /dev/null +++ b/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/crypto/SHA1Test.java @@ -0,0 +1,36 @@ +package me.chanjar.weixin.common.util.crypto; + +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; + +/** + *
+ *  Created by BinaryWang on 2017/6/10.
+ * 
+ * + * @author binarywang(Binary Wang) + */ +public class SHA1Test { + @Test + public void testGen() throws Exception { + final String result = SHA1.gen("123", "345"); + assertNotNull(result); + assertEquals(result,"9f537aeb751ec72605f57f94a2f6dc3e3958e1dd"); + } + + @Test(expectedExceptions = {IllegalArgumentException.class}) + public void testGen_illegalArguments() { + final String result = SHA1.gen(null, "", "345"); + assertNotNull(result); + } + + @Test + public void testGenWithAmple() throws Exception { + final String result = SHA1.genWithAmple("123", "345"); + assertNotNull(result); + assertEquals(result,"20b896ccbd5a72dde5dbe0878ff985e4069771c6"); + } + +} diff --git a/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/http/apache/DefaultApacheHttpClientBuilderTest.java b/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/http/apache/DefaultApacheHttpClientBuilderTest.java new file mode 100644 index 0000000000..24a45eea09 --- /dev/null +++ b/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/http/apache/DefaultApacheHttpClientBuilderTest.java @@ -0,0 +1,64 @@ +package me.chanjar.weixin.common.util.http.apache; + +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class DefaultApacheHttpClientBuilderTest { + @Test + public void testBuild() throws Exception { + DefaultApacheHttpClientBuilder builder1 = DefaultApacheHttpClientBuilder.get(); + DefaultApacheHttpClientBuilder builder2 = DefaultApacheHttpClientBuilder.get(); + Assert.assertSame(builder1, builder2, "DefaultApacheHttpClientBuilder为单例,获取到的对象应该相同"); + List threadList = new ArrayList<>(10); + for (int i = 0; i < 10; i++) { + TestThread thread = new TestThread(); + thread.start(); + threadList.add(thread); + } + for (TestThread testThread : threadList) { + testThread.join(); + Assert.assertNotEquals(-1,testThread.getRespState(),"请求响应code不应为-1"); + } + + for (int i = 1; i < threadList.size(); i++) { + TestThread thread1 = threadList.get(i - 1); + TestThread thread2 = threadList.get(i); + Assert.assertSame( + thread1.getClient(), + thread2.getClient(), + "DefaultApacheHttpClientBuilder为单例,并持有了相同的HttpClient" + ); + } + } + + + public static class TestThread extends Thread { + private CloseableHttpClient client; + private int respState = -1; + + @Override + public void run() { + client = DefaultApacheHttpClientBuilder.get().build(); + HttpGet httpGet = new HttpGet("http://www.sina.com.cn/"); + try (CloseableHttpResponse resp = client.execute(httpGet)){ + respState = resp.getStatusLine().getStatusCode(); + } catch (IOException ignored) { + } + } + + public CloseableHttpClient getClient() { + return client; + } + + public int getRespState() { + return respState; + } + } +} diff --git a/weixin-java-common/src/test/resources/logback-test.xml b/weixin-java-common/src/test/resources/logback-test.xml index e69034238e..9a6fe3eea1 100644 --- a/weixin-java-common/src/test/resources/logback-test.xml +++ b/weixin-java-common/src/test/resources/logback-test.xml @@ -1,17 +1,12 @@ - - - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + %d{HH:mm:ss.SSS} [%thread] %-5level %replace(%caller{1}){'Caller', ''} - %msg%n - + - diff --git a/weixin-java-common/src/test/resources/testng.xml b/weixin-java-common/src/test/resources/testng.xml index 793ba0a650..9eeba0df4c 100644 --- a/weixin-java-common/src/test/resources/testng.xml +++ b/weixin-java-common/src/test/resources/testng.xml @@ -7,7 +7,7 @@ - + diff --git a/weixin-java-cp/build.gradle b/weixin-java-cp/build.gradle deleted file mode 100644 index 3e0c09ad0c..0000000000 --- a/weixin-java-cp/build.gradle +++ /dev/null @@ -1,11 +0,0 @@ -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.RC0' - testCompile group: 'org.eclipse.jetty', name: 'jetty-servlet', version: '9.3.0.RC0' -} -test.useTestNG() diff --git a/weixin-java-cp/pom.xml b/weixin-java-cp/pom.xml index 68ad828313..330a34b098 100644 --- a/weixin-java-cp/pom.xml +++ b/weixin-java-cp/pom.xml @@ -7,7 +7,7 @@ com.github.binarywang weixin-java-parent - 2.6.1.BETA + 2.7.8.BETA weixin-java-cp @@ -20,10 +20,25 @@ weixin-java-common ${project.version} + + org.jodd + jodd-http + provided + + + com.squareup.okhttp3 + okhttp + provided + redis.clients jedis + + org.slf4j + slf4j-api + + org.testng testng @@ -49,6 +64,11 @@ jetty-servlet test + + ch.qos.logback + logback-classic + test + diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpDepartmentService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpDepartmentService.java new file mode 100644 index 0000000000..7ca1ade3e5 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpDepartmentService.java @@ -0,0 +1,57 @@ +package me.chanjar.weixin.cp.api; + +import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.cp.bean.WxCpDepart; + +import java.util.List; + +/** + *
+ *  部门管理接口
+ *  Created by BinaryWang on 2017/6/24.
+ * 
+ * + * @author Binary Wang + */ +public interface WxCpDepartmentService { + + /** + *
+   * 部门管理接口 - 创建部门
+   * 最多支持创建500个部门
+   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=部门管理接口
+   * 
+ * + * @param depart 部门 + * @return 部门id + */ + Integer create(WxCpDepart depart) throws WxErrorException; + + /** + *
+   * 部门管理接口 - 查询所有部门
+   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=部门管理接口
+   * 
+ */ + List listAll() throws WxErrorException; + + /** + *
+   * 部门管理接口 - 修改部门名
+   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=部门管理接口
+   * 如果id为0(未部门),1(黑名单),2(星标组),或者不存在的id,微信会返回系统繁忙的错误
+   * 
+ * + * @param group 要更新的group,group的id,name必须设置 + */ + void update(WxCpDepart group) throws WxErrorException; + + /** + *
+   * 部门管理接口 - 删除部门
+   * 
+ * + * @param departId 部门id + */ + void delete(Integer departId) throws WxErrorException; +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMediaService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMediaService.java new file mode 100644 index 0000000000..f813f1bf3f --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMediaService.java @@ -0,0 +1,57 @@ +package me.chanjar.weixin.cp.api; + +import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; +import me.chanjar.weixin.common.exception.WxErrorException; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +/** + *
+ *  媒体管理接口
+ *  Created by BinaryWang on 2017/6/24.
+ * 
+ * + * @author Binary Wang + */ +public interface WxCpMediaService { + + /** + *
+   * 上传多媒体文件
+   * 上传的多媒体文件有格式和大小限制,如下:
+   *   图片(image): 1M,支持JPG格式
+   *   语音(voice):2M,播放长度不超过60s,支持AMR\MP3格式
+   *   视频(video):10MB,支持MP4格式
+   *   缩略图(thumb):64KB,支持JPG格式
+   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=上传下载多媒体文件
+   * 
+ * + * @param mediaType 媒体类型, 请看{@link me.chanjar.weixin.common.api.WxConsts} + * @param fileType 文件类型,请看{@link me.chanjar.weixin.common.api.WxConsts} + * @param inputStream 输入流 + */ + WxMediaUploadResult upload(String mediaType, String fileType, InputStream inputStream) + throws WxErrorException, IOException; + + /** + * @param mediaType 媒体类型 + * @param file 文件对象 + * @see #upload(String, String, InputStream) + */ + WxMediaUploadResult upload(String mediaType, File file) throws WxErrorException; + + /** + *
+   * 下载多媒体文件
+   * 根据微信文档,视频文件下载不了,会返回null
+   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=上传下载多媒体文件
+   * 
+ * + * @param mediaId 媒体id + * @return 保存到本地的临时文件 + */ + File download(String mediaId) throws WxErrorException; + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMenuService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMenuService.java new file mode 100644 index 0000000000..012efefb7f --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMenuService.java @@ -0,0 +1,91 @@ +package me.chanjar.weixin.cp.api; + +import me.chanjar.weixin.common.bean.menu.WxMenu; +import me.chanjar.weixin.common.exception.WxErrorException; + +/** + *
+ *  菜单管理相关接口
+ *  Created by BinaryWang on 2017/6/24.
+ * 
+ * + * @author Binary Wang + */ +public interface WxCpMenuService { + /** + *
+   * 自定义菜单创建接口
+   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=自定义菜单创建接口
+   *
+   * 注意: 这个方法使用WxCpConfigStorage里的agentId
+   * 
+ * + * @param menu 菜单对象 + * @see #create(Integer, WxMenu) + */ + void create(WxMenu menu) throws WxErrorException; + + /** + *
+   * 自定义菜单创建接口
+   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=自定义菜单创建接口
+   *
+   * 注意: 这个方法不使用WxCpConfigStorage里的agentId,需要开发人员自己给出
+   * 
+ * + * @param agentId 企业号应用的id + * @param menu 菜单对象 + * @see #create(me.chanjar.weixin.common.bean.menu.WxMenu) + */ + void create(Integer agentId, WxMenu menu) throws WxErrorException; + + /** + *
+   * 自定义菜单删除接口
+   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=自定义菜单删除接口
+   *
+   * 注意: 这个方法使用WxCpConfigStorage里的agentId
+   * 
+ * + * @see #delete(Integer) + */ + void delete() throws WxErrorException; + + /** + *
+   * 自定义菜单删除接口
+   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=自定义菜单删除接口
+   *
+   * 注意: 这个方法不使用WxCpConfigStorage里的agentId,需要开发人员自己给出
+   * 
+ * + * @param agentId 企业号应用的id + * @see #delete() + */ + void delete(Integer agentId) throws WxErrorException; + + /** + *
+   * 自定义菜单查询接口
+   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=自定义菜单查询接口
+   *
+   * 注意: 这个方法使用WxCpConfigStorage里的agentId
+   * 
+ * + * @see #get(Integer) + */ + WxMenu get() throws WxErrorException; + + /** + *
+   * 自定义菜单查询接口
+   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=自定义菜单查询接口
+   *
+   * 注意: 这个方法不使用WxCpConfigStorage里的agentId,需要开发人员自己给出
+   * 
+ * + * @param agentId 企业号应用的id + * @see #get() + */ + WxMenu get(Integer agentId) throws WxErrorException; +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOAuth2Service.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOAuth2Service.java new file mode 100644 index 0000000000..3312489538 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOAuth2Service.java @@ -0,0 +1,67 @@ +package me.chanjar.weixin.cp.api; + +import me.chanjar.weixin.common.exception.WxErrorException; + +/** + *
+ * OAuth2相关管理接口
+ *  Created by BinaryWang on 2017/6/24.
+ * 
+ * + * @author Binary Wang + */ +public interface WxCpOAuth2Service { + /** + *
+   * 构造oauth2授权的url连接
+   * 
+ * + * @param state 状态码 + * @return url + */ + String buildAuthorizationUrl(String state); + + /** + *
+   * 构造oauth2授权的url连接
+   * 详情请见: http://qydev.weixin.qq.com/wiki/index.php?title=企业获取code
+   * 
+ * + * @param redirectUri 跳转链接地址 + * @param state 状态码 + * @return url + */ + String buildAuthorizationUrl(String redirectUri, String state); + + /** + *
+   * 用oauth2获取用户信息
+   * http://qydev.weixin.qq.com/wiki/index.php?title=根据code获取成员信息
+   * 因为企业号oauth2.0必须在应用设置里设置通过ICP备案的可信域名,所以无法测试,因此这个方法很可能是坏的。
+   *
+   * 注意: 这个方法使用WxCpConfigStorage里的agentId
+   * 
+ * + * @param code 微信oauth授权返回的代码 + * @return [userid, deviceid] + * @see #getUserInfo(Integer, String) + */ + String[] getUserInfo(String code) throws WxErrorException; + + /** + *
+   * 用oauth2获取用户信息
+   * http://qydev.weixin.qq.com/wiki/index.php?title=根据code获取成员信息
+   * 因为企业号oauth2.0必须在应用设置里设置通过ICP备案的可信域名,所以无法测试,因此这个方法很可能是坏的。
+   *
+   * 注意: 这个方法不使用WxCpConfigStorage里的agentId,需要开发人员自己给出
+   * 
+ * + * @param agentId 企业号应用的id + * @param code 微信oauth授权返回的代码 + * @return [userid, deviceid] + * @see #getUserInfo(String) + */ + String[] getUserInfo(Integer agentId, String code) throws WxErrorException; + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java index 6e30ce55e0..880a92b06e 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java @@ -8,10 +8,9 @@ import me.chanjar.weixin.common.session.WxSessionManager; import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; import me.chanjar.weixin.common.util.http.RequestExecutor; -import me.chanjar.weixin.cp.bean.WxCpDepart; -import me.chanjar.weixin.cp.bean.WxCpMessage; -import me.chanjar.weixin.cp.bean.WxCpTag; -import me.chanjar.weixin.cp.bean.WxCpUser; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.cp.bean.*; +import me.chanjar.weixin.cp.config.WxCpConfigStorage; import java.io.File; import java.io.IOException; @@ -36,16 +35,6 @@ public interface WxCpService { */ boolean checkSignature(String msgSignature, String timestamp, String nonce, String data); - /** - *
-   *   用在二次验证的时候
-   *   企业在员工验证成功后,调用本方法告诉企业号平台该员工关注成功。
-   * 
- * - * @param userId 用户id - */ - void userAuthenticated(String userId) throws WxErrorException; - /** * 获取access_token, 不强制刷新access_token * @@ -97,337 +86,208 @@ public interface WxCpService { WxJsapiSignature createJsapiSignature(String url) throws WxErrorException; /** - *
-   * 上传多媒体文件
-   * 上传的多媒体文件有格式和大小限制,如下:
-   *   图片(image): 1M,支持JPG格式
-   *   语音(voice):2M,播放长度不超过60s,支持AMR\MP3格式
-   *   视频(video):10MB,支持MP4格式
-   *   缩略图(thumb):64KB,支持JPG格式
-   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=上传下载多媒体文件
-   * 
- * - * @param mediaType 媒体类型, 请看{@link me.chanjar.weixin.common.api.WxConsts} - * @param fileType 文件类型,请看{@link me.chanjar.weixin.common.api.WxConsts} - * @param inputStream 输入流 - */ - WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputStream inputStream) - throws WxErrorException, IOException; - - /** - * @param mediaType 媒体类型 - * @param file 文件对象 - * @see #mediaUpload(String, String, InputStream) + * @deprecated 请使用 {@link WxCpMenuService#create(WxMenu)} */ - WxMediaUploadResult mediaUpload(String mediaType, File file) throws WxErrorException; + @Deprecated + void menuCreate(WxMenu menu) throws WxErrorException; /** - *
-   * 下载多媒体文件
-   * 根据微信文档,视频文件下载不了,会返回null
-   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=上传下载多媒体文件
-   * 
- * - * @param mediaId 媒体id - * @return 保存到本地的临时文件 + * @deprecated 请使用 {@link WxCpMenuService#create(Integer, WxMenu)} */ - File mediaDownload(String mediaId) throws WxErrorException; + @Deprecated + void menuCreate(Integer agentId, WxMenu menu) throws WxErrorException; /** - *
-   * 发送消息
-   * 详情请见: http://qydev.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E
-   * 
- * - * @param message 要发送的消息对象 + * @deprecated 请使用 {@link WxCpMenuService#delete()} } */ - void messageSend(WxCpMessage message) throws WxErrorException; + @Deprecated + void menuDelete() throws WxErrorException; /** - *
-   * 自定义菜单创建接口
-   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=自定义菜单创建接口
-   *
-   * 注意: 这个方法使用WxCpConfigStorage里的agentId
-   * 
- * - * @param menu 菜单对象 - * @see #menuCreate(Integer, WxMenu) + * @deprecated 请使用 {@link WxCpMenuService#delete(Integer)} */ - void menuCreate(WxMenu menu) throws WxErrorException; + @Deprecated + void menuDelete(Integer agentId) throws WxErrorException; /** - *
-   * 自定义菜单创建接口
-   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=自定义菜单创建接口
-   *
-   * 注意: 这个方法不使用WxCpConfigStorage里的agentId,需要开发人员自己给出
-   * 
- * - * @param agentId 企业号应用的id - * @param menu 菜单对象 - * @see #menuCreate(me.chanjar.weixin.common.bean.menu.WxMenu) + * @deprecated 请使用 {@link WxCpMenuService#get() } */ - void menuCreate(Integer agentId, WxMenu menu) throws WxErrorException; + @Deprecated + WxMenu menuGet() throws WxErrorException; /** - *
-   * 自定义菜单删除接口
-   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=自定义菜单删除接口
-   *
-   * 注意: 这个方法使用WxCpConfigStorage里的agentId
-   * 
- * - * @see #menuDelete(Integer) + * @deprecated 请使用 {@link WxCpMenuService#get(Integer)} */ - void menuDelete() throws WxErrorException; + @Deprecated + WxMenu menuGet(Integer agentId) throws WxErrorException; /** *
-   * 自定义菜单删除接口
-   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=自定义菜单删除接口
-   *
-   * 注意: 这个方法不使用WxCpConfigStorage里的agentId,需要开发人员自己给出
+   * 发送消息
+   * 详情请见: http://qydev.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E
    * 
* - * @param agentId 企业号应用的id - * @see #menuDelete() + * @param message 要发送的消息对象 */ - void menuDelete(Integer agentId) throws WxErrorException; + WxCpMessageSendResult messageSend(WxCpMessage message) throws WxErrorException; /** - *
-   * 自定义菜单查询接口
-   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=自定义菜单查询接口
-   *
-   * 注意: 这个方法使用WxCpConfigStorage里的agentId
-   * 
- * - * @see #menuGet(Integer) + * @deprecated 请使用 {@link WxCpDepartmentService#create(WxCpDepart)} */ - WxMenu menuGet() throws WxErrorException; + @Deprecated + Integer departCreate(WxCpDepart depart) throws WxErrorException; /** - *
-   * 自定义菜单查询接口
-   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=自定义菜单查询接口
-   *
-   * 注意: 这个方法不使用WxCpConfigStorage里的agentId,需要开发人员自己给出
-   * 
- * - * @param agentId 企业号应用的id - * @see #menuGet() + * @deprecated 请使用 {@link WxCpDepartmentService#update(WxCpDepart)} */ - WxMenu menuGet(Integer agentId) throws WxErrorException; + @Deprecated + void departUpdate(WxCpDepart group) throws WxErrorException; /** - *
-   * 部门管理接口 - 创建部门
-   * 最多支持创建500个部门
-   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=部门管理接口
-   * 
- * - * @param depart 部门 - * @return 部门id + * @deprecated 请使用 {@link WxCpDepartmentService#delete(Integer)} */ - Integer departCreate(WxCpDepart depart) throws WxErrorException; + @Deprecated + void departDelete(Integer departId) throws WxErrorException; /** - *
-   * 部门管理接口 - 查询所有部门
-   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=部门管理接口
-   * 
+ * @deprecated 请使用 {@link WxCpDepartmentService#listAll() } */ + @Deprecated List departGet() throws WxErrorException; /** - *
-   * 部门管理接口 - 修改部门名
-   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=部门管理接口
-   * 如果id为0(未部门),1(黑名单),2(星标组),或者不存在的id,微信会返回系统繁忙的错误
-   * 
- * - * @param group 要更新的group,group的id,name必须设置 + * @deprecated 请使用 {@link WxCpMediaService#upload(String, String, InputStream)} */ - void departUpdate(WxCpDepart group) throws WxErrorException; + @Deprecated + WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputStream inputStream) + throws WxErrorException, IOException; /** - *
-   * 部门管理接口 - 删除部门
-   * 
- * - * @param departId 部门id + * @deprecated 请使用 {@link WxCpMediaService#upload(String, File)} */ - void departDelete(Integer departId) throws WxErrorException; + @Deprecated + WxMediaUploadResult mediaUpload(String mediaType, File file) throws WxErrorException; /** - *
-   * 获取部门成员(详情)
-   *
-   * 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
-   * 
- * - * @param departId 必填。部门id - * @param fetchChild 非必填。1/0:是否递归获取子部门下面的成员 - * @param status 非必填。0获取全部员工,1获取已关注成员列表,2获取禁用成员列表,4获取未关注成员列表。status可叠加 + * @deprecated 请使用 {@link WxCpMediaService#download(String)} */ - List userList(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException; + @Deprecated + File mediaDownload(String mediaId) throws WxErrorException; /** - *
-   * 获取部门成员
-   *
-   * 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
-   * 
- * - * @param departId 必填。部门id - * @param fetchChild 非必填。1/0:是否递归获取子部门下面的成员 - * @param status 非必填。0获取全部员工,1获取已关注成员列表,2获取禁用成员列表,4获取未关注成员列表。status可叠加 + * @deprecated 请使用 {@link WxCpUserService#authenticate(String)} */ - List departGetUsers(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException; + @Deprecated + void userAuthenticated(String userId) throws WxErrorException; /** - * 新建用户 - * - * @param user 用户对象 + * @deprecated 请使用 {@link WxCpUserService#create(WxCpUser)} */ + @Deprecated void userCreate(WxCpUser user) throws WxErrorException; /** - * 更新用户 - * - * @param user 用户对象 + * @deprecated 请使用 {@link WxCpUserService#update(WxCpUser)} */ + @Deprecated void userUpdate(WxCpUser user) throws WxErrorException; /** - * 删除用户 - * - * @param userid 用户id + * @deprecated 请使用 {@link WxCpUserService#delete(String...)} */ + @Deprecated void userDelete(String userid) throws WxErrorException; /** - *
-   * 批量删除成员
-   * 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
-   * 
- * - * @param userids 员工UserID列表。对应管理端的帐号 + * @deprecated 请使用 {@link WxCpUserService#delete(String...)} */ + @Deprecated void userDelete(String[] userids) throws WxErrorException; /** - * 获取用户 - * - * @param userid 用户id + * @deprecated 请使用 {@link WxCpUserService#getById(String)} */ + @Deprecated WxCpUser userGet(String userid) throws WxErrorException; /** - * 创建标签 - * - * @param tagName 标签名 + * @deprecated 请使用 {@link WxCpUserService#listByDepartment(Integer, Boolean, Integer)} + */ + @Deprecated + List userList(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException; + + /** + * @deprecated 请使用 {@link WxCpUserService#listSimpleByDepartment(Integer, Boolean, Integer)} + */ + @Deprecated + List departGetUsers(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException; + + /** + * @deprecated 请使用 {@link WxCpTagService#create(String)} */ + @Deprecated String tagCreate(String tagName) throws WxErrorException; /** - * 更新标签 - * - * @param tagId 标签id - * @param tagName 标签名 + * @deprecated 请使用 {@link WxCpTagService#update(String, String)} */ + @Deprecated void tagUpdate(String tagId, String tagName) throws WxErrorException; /** - * 删除标签 - * - * @param tagId 标签id + * @deprecated 请使用 {@link WxCpTagService#delete(String)} */ + @Deprecated void tagDelete(String tagId) throws WxErrorException; /** - * 获得标签列表 + * @deprecated 请使用 {@link WxCpTagService#listAll()} */ + @Deprecated List tagGet() throws WxErrorException; /** - * 获取标签成员 - * - * @param tagId 标签ID + * @deprecated 请使用 {@link WxCpTagService#listUsersByTagId(String)} */ + @Deprecated List tagGetUsers(String tagId) throws WxErrorException; /** - * 增加标签成员 - * - * @param tagId 标签id - * @param userIds 用户ID 列表 + * @deprecated 请使用 {@link WxCpTagService#addUsers2Tag(String, List, List)} */ + @Deprecated void tagAddUsers(String tagId, List userIds, List partyIds) throws WxErrorException; /** - *
-   * 构造oauth2授权的url连接
-   * 
- * - * @param state 状态码 - * @return url + * @deprecated 请使用 {@link WxCpTagService#removeUsersFromTag(String, List)} + */ + @Deprecated + void tagRemoveUsers(String tagId, List userIds) throws WxErrorException; + + /** + * @deprecated 请使用 {@link WxCpOAuth2Service#buildAuthorizationUrl(String)} */ + @Deprecated String oauth2buildAuthorizationUrl(String state); /** - *
-   * 构造oauth2授权的url连接
-   * 详情请见: http://qydev.weixin.qq.com/wiki/index.php?title=企业获取code
-   * 
- * - * @param redirectUri 跳转链接地址 - * @param state 状态码 - * @return url + * @deprecated 请使用 {@link WxCpOAuth2Service#buildAuthorizationUrl(String, String)} */ + @Deprecated String oauth2buildAuthorizationUrl(String redirectUri, String state); /** - *
-   * 用oauth2获取用户信息
-   * http://qydev.weixin.qq.com/wiki/index.php?title=根据code获取成员信息
-   * 因为企业号oauth2.0必须在应用设置里设置通过ICP备案的可信域名,所以无法测试,因此这个方法很可能是坏的。
-   *
-   * 注意: 这个方法使用WxCpConfigStorage里的agentId
-   * 
- * - * @param code 微信oauth授权返回的代码 - * @return [userid, deviceid] - * @see #oauth2getUserInfo(Integer, String) + * @deprecated 请使用 {@link WxCpOAuth2Service#getUserInfo(String)} */ + @Deprecated String[] oauth2getUserInfo(String code) throws WxErrorException; /** - *
-   * 用oauth2获取用户信息
-   * http://qydev.weixin.qq.com/wiki/index.php?title=根据code获取成员信息
-   * 因为企业号oauth2.0必须在应用设置里设置通过ICP备案的可信域名,所以无法测试,因此这个方法很可能是坏的。
-   *
-   * 注意: 这个方法不使用WxCpConfigStorage里的agentId,需要开发人员自己给出
-   * 
- * - * @param agentId 企业号应用的id - * @param code 微信oauth授权返回的代码 - * @return [userid, deviceid] - * @see #oauth2getUserInfo(String) + * @deprecated 请使用 {@link WxCpOAuth2Service#getUserInfo(Integer, String)} */ + @Deprecated String[] oauth2getUserInfo(Integer agentId, String code) throws WxErrorException; - - /** - * 移除标签成员 - * - * @param tagId 标签id - * @param userIds 用户id列表 - */ - void tagRemoveUsers(String tagId, List userIds) throws WxErrorException; - /** *
    * 邀请成员关注
@@ -563,4 +423,39 @@ WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputStream i
    * @param wxConfigProvider 配置对象
    */
   void setWxCpConfigStorage(WxCpConfigStorage wxConfigProvider);
+
+  /**
+   * 获取部门相关接口的服务类对象
+   */
+  WxCpDepartmentService getDepartmentService();
+
+  /**
+   * 获取媒体相关接口的服务类对象
+   */
+  WxCpMediaService getMediaService();
+
+  /**
+   * 获取菜单相关接口的服务类对象
+   */
+  WxCpMenuService getMenuService();
+
+  /**
+   * 获取Oauth2相关接口的服务类对象
+   */
+  WxCpOAuth2Service getOauth2Service();
+
+  /**
+   * 获取标签相关接口的服务类对象
+   */
+  WxCpTagService getTagService();
+
+  /**
+   * 获取用户相关接口的服务类对象
+   */
+  WxCpUserService getUserService();
+
+  /**
+   * http请求对象
+   */
+  RequestHttp getRequestHttp();
 }
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpTagService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpTagService.java
new file mode 100644
index 0000000000..c96318b80b
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpTagService.java
@@ -0,0 +1,66 @@
+package me.chanjar.weixin.cp.api;
+
+import me.chanjar.weixin.common.exception.WxErrorException;
+import me.chanjar.weixin.cp.bean.WxCpTag;
+import me.chanjar.weixin.cp.bean.WxCpTagAddOrRemoveUsersResult;
+import me.chanjar.weixin.cp.bean.WxCpUser;
+
+import java.util.List;
+
+/**
+ * 
+ *  标签管理接口
+ *  Created by BinaryWang on 2017/6/24.
+ * 
+ * + * @author Binary Wang + */ +public interface WxCpTagService { + /** + * 创建标签 + * + * @param tagName 标签名 + */ + String create(String tagName) throws WxErrorException; + + /** + * 更新标签 + * + * @param tagId 标签id + * @param tagName 标签名 + */ + void update(String tagId, String tagName) throws WxErrorException; + + /** + * 删除标签 + * + * @param tagId 标签id + */ + void delete(String tagId) throws WxErrorException; + + /** + * 获得标签列表 + */ + List listAll() throws WxErrorException; + + /** + * 获取标签成员 + * + * @param tagId 标签ID + */ + List listUsersByTagId(String tagId) throws WxErrorException; + + /** + * 增加标签成员 + * @param tagId 标签id + * @param userIds 用户ID 列表 + */ + WxCpTagAddOrRemoveUsersResult addUsers2Tag(String tagId, List userIds, List partyIds) throws WxErrorException; + + /** + * 移除标签成员 + * @param tagId 标签id + * @param userIds 用户id列表 + */ + WxCpTagAddOrRemoveUsersResult removeUsersFromTag(String tagId, List userIds) throws WxErrorException; +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpUserService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpUserService.java new file mode 100644 index 0000000000..49ef9134f5 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpUserService.java @@ -0,0 +1,83 @@ +package me.chanjar.weixin.cp.api; + +import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.cp.bean.WxCpUser; + +import java.util.List; + +/** + *
+ * 用户管理接口
+ *  Created by BinaryWang on 2017/6/24.
+ * 
+ * + * @author Binary Wang + */ +public interface WxCpUserService { + /** + *
+   *   用在二次验证的时候
+   *   企业在员工验证成功后,调用本方法告诉企业号平台该员工关注成功。
+   * 
+ * + * @param userId 用户id + */ + void authenticate(String userId) throws WxErrorException; + + /** + *
+   * 获取部门成员(详情)
+   *
+   * 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
+   * 
+ * + * @param departId 必填。部门id + * @param fetchChild 非必填。1/0:是否递归获取子部门下面的成员 + * @param status 非必填。0获取全部员工,1获取已关注成员列表,2获取禁用成员列表,4获取未关注成员列表。status可叠加 + */ + List listByDepartment(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException; + + /** + *
+   * 获取部门成员
+   *
+   * 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
+   * 
+ * + * @param departId 必填。部门id + * @param fetchChild 非必填。1/0:是否递归获取子部门下面的成员 + * @param status 非必填。0获取全部员工,1获取已关注成员列表,2获取禁用成员列表,4获取未关注成员列表。status可叠加 + */ + List listSimpleByDepartment(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException; + + /** + * 新建用户 + * + * @param user 用户对象 + */ + void create(WxCpUser user) throws WxErrorException; + + /** + * 更新用户 + * + * @param user 用户对象 + */ + void update(WxCpUser user) throws WxErrorException; + + /** + *
+   * 删除用户/批量删除成员
+   * 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
+   * 
+ * + * @param userIds 员工UserID列表。对应管理端的帐号 + */ + void delete(String... userIds) throws WxErrorException; + + /** + * 获取用户 + * + * @param userid 用户id + */ + WxCpUser getById(String userid) throws WxErrorException; +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpDepartmentServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpDepartmentServiceImpl.java new file mode 100644 index 0000000000..82cd8c345c --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpDepartmentServiceImpl.java @@ -0,0 +1,65 @@ +package me.chanjar.weixin.cp.api.impl; + +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import com.google.gson.reflect.TypeToken; +import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.util.json.GsonHelper; +import me.chanjar.weixin.cp.api.WxCpDepartmentService; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.bean.WxCpDepart; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.util.List; + +/** + *
+ *  部门管理接口
+ *  Created by BinaryWang on 2017/6/24.
+ * 
+ * + * @author Binary Wang + */ +public class WxCpDepartmentServiceImpl implements WxCpDepartmentService { + private WxCpService mainService; + + public WxCpDepartmentServiceImpl(WxCpService mainService) { + this.mainService = mainService; + } + + @Override + public Integer create(WxCpDepart depart) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/department/create"; + String responseContent = this.mainService.post(url, depart.toJson()); + JsonElement tmpJsonElement = new JsonParser().parse(responseContent); + return GsonHelper.getAsInteger(tmpJsonElement.getAsJsonObject().get("id")); + } + + @Override + public void update(WxCpDepart group) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/department/update"; + this.mainService.post(url, group.toJson()); + } + + @Override + public void delete(Integer departId) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/department/delete?id=" + departId; + this.mainService.get(url, null); + } + + @Override + public List listAll() throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/department/list"; + String responseContent = this.mainService.get(url, null); + /* + * 操蛋的微信API,创建时返回的是 { group : { id : ..., name : ...} } + * 查询时返回的是 { groups : [ { id : ..., name : ..., count : ... }, ... ] } + */ + JsonElement tmpJsonElement = new JsonParser().parse(responseContent); + return WxCpGsonBuilder.INSTANCE.create() + .fromJson(tmpJsonElement.getAsJsonObject().get("department"), + new TypeToken>() { + }.getType() + ); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMediaServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMediaServiceImpl.java new file mode 100644 index 0000000000..1364550aac --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMediaServiceImpl.java @@ -0,0 +1,50 @@ +package me.chanjar.weixin.cp.api.impl; + +import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; +import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.util.fs.FileUtils; +import me.chanjar.weixin.common.util.http.MediaDownloadRequestExecutor; +import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; +import me.chanjar.weixin.cp.api.WxCpMediaService; +import me.chanjar.weixin.cp.api.WxCpService; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.UUID; + +/** + *
+ * 媒体管理接口
+ * Created by Binary Wang on 2017-6-25.
+ * @author Binary Wang
+ * 
+ */ +public class WxCpMediaServiceImpl implements WxCpMediaService { + private WxCpService mainService; + + public WxCpMediaServiceImpl(WxCpService mainService) { + this.mainService = mainService; + } + + @Override + public WxMediaUploadResult upload(String mediaType, String fileType, InputStream inputStream) + throws WxErrorException, IOException { + return this.upload(mediaType, FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), fileType)); + } + + @Override + public WxMediaUploadResult upload(String mediaType, File file) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/media/upload?type=" + mediaType; + return this.mainService.execute(MediaUploadRequestExecutor.create(this.mainService.getRequestHttp()), url, file); + } + + @Override + public File download(String mediaId) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/media/get"; + return this.mainService.execute( + MediaDownloadRequestExecutor.create(this.mainService.getRequestHttp(), + this.mainService.getWxCpConfigStorage().getTmpDirFile()), + url, "media_id=" + mediaId); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMenuServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMenuServiceImpl.java new file mode 100644 index 0000000000..7c3937d83e --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMenuServiceImpl.java @@ -0,0 +1,64 @@ +package me.chanjar.weixin.cp.api.impl; + +import me.chanjar.weixin.common.bean.menu.WxMenu; +import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.cp.api.WxCpMenuService; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +/** + *
+ * 菜单管理相关接口
+ * Created by Binary Wang on 2017-6-25.
+ * @author Binary Wang
+ * 
+ */ +public class WxCpMenuServiceImpl implements WxCpMenuService { + private WxCpService mainService; + + public WxCpMenuServiceImpl(WxCpService mainService) { + this.mainService = mainService; + } + + @Override + public void create(WxMenu menu) throws WxErrorException { + this.create(this.mainService.getWxCpConfigStorage().getAgentId(), menu); + } + + @Override + public void create(Integer agentId, WxMenu menu) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/menu/create?agentid=" + agentId; + this.mainService.post(url, menu.toJson()); + } + + @Override + public void delete() throws WxErrorException { + this.delete(this.mainService.getWxCpConfigStorage().getAgentId()); + } + + @Override + public void delete(Integer agentId) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/menu/delete?agentid=" + agentId; + this.mainService.get(url, null); + } + + @Override + public WxMenu get() throws WxErrorException { + return this.get(this.mainService.getWxCpConfigStorage().getAgentId()); + } + + @Override + public WxMenu get(Integer agentId) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/menu/get?agentid=" + agentId; + try { + String resultContent = this.mainService.get(url, null); + return WxCpGsonBuilder.create().fromJson(resultContent, WxMenu.class); + } catch (WxErrorException e) { + // 46003 不存在的菜单数据 + if (e.getError().getErrorCode() == 46003) { + return null; + } + throw e; + } + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpOAuth2ServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpOAuth2ServiceImpl.java new file mode 100644 index 0000000000..a317bfb4e6 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpOAuth2ServiceImpl.java @@ -0,0 +1,66 @@ +package me.chanjar.weixin.cp.api.impl; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.util.http.URIUtil; +import me.chanjar.weixin.common.util.json.GsonHelper; +import me.chanjar.weixin.cp.api.WxCpOAuth2Service; +import me.chanjar.weixin.cp.api.WxCpService; + +/** + *
+ *
+ * Created by Binary Wang on 2017-6-25.
+ * @author Binary Wang
+ * 
+ */ +public class WxCpOAuth2ServiceImpl implements WxCpOAuth2Service { + private WxCpService mainService; + + public WxCpOAuth2ServiceImpl(WxCpService mainService) { + this.mainService = mainService; + } + + @Override + public String buildAuthorizationUrl(String state) { + return this.buildAuthorizationUrl( + this.mainService.getWxCpConfigStorage().getOauth2redirectUri(), + state + ); + } + + @Override + public String buildAuthorizationUrl(String redirectUri, String state) { + String url = "https://open.weixin.qq.com/connect/oauth2/authorize?"; + url += "appid=" + this.mainService.getWxCpConfigStorage().getCorpId(); + url += "&redirect_uri=" + URIUtil.encodeURIComponent(redirectUri); + url += "&response_type=code"; + url += "&scope=snsapi_base"; + if (state != null) { + url += "&state=" + state; + } + url += "#wechat_redirect"; + return url; + } + + @Override + public String[] getUserInfo(String code) throws WxErrorException { + return getUserInfo(this.mainService.getWxCpConfigStorage().getAgentId(), code); + } + + @Override + public String[] getUserInfo(Integer agentId, String code) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?" + + "code=" + code + + "&agentid=" + agentId; + String responseText = this.mainService.get(url, null); + JsonElement je = new JsonParser().parse(responseText); + JsonObject jo = je.getAsJsonObject(); + return new String[]{GsonHelper.getString(jo, "UserId"), + GsonHelper.getString(jo, "DeviceId"), + GsonHelper.getString(jo, "OpenId")}; + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/AbstractWxCpServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceAbstractImpl.java similarity index 58% rename from weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/AbstractWxCpServiceImpl.java rename to weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceAbstractImpl.java index 85065a7138..43c3415ef1 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/AbstractWxCpServiceImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceAbstractImpl.java @@ -1,7 +1,9 @@ package me.chanjar.weixin.cp.api.impl; -import com.google.gson.*; -import com.google.gson.reflect.TypeToken; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import me.chanjar.weixin.common.bean.WxJsapiSignature; import me.chanjar.weixin.common.bean.menu.WxMenu; import me.chanjar.weixin.common.bean.result.WxError; @@ -12,16 +14,13 @@ import me.chanjar.weixin.common.session.WxSessionManager; import me.chanjar.weixin.common.util.RandomUtils; import me.chanjar.weixin.common.util.crypto.SHA1; -import me.chanjar.weixin.common.util.fs.FileUtils; -import me.chanjar.weixin.common.util.http.*; -import me.chanjar.weixin.common.util.json.GsonHelper; -import me.chanjar.weixin.cp.api.WxCpConfigStorage; -import me.chanjar.weixin.cp.api.WxCpService; -import me.chanjar.weixin.cp.bean.WxCpDepart; -import me.chanjar.weixin.cp.bean.WxCpMessage; -import me.chanjar.weixin.cp.bean.WxCpTag; -import me.chanjar.weixin.cp.bean.WxCpUser; -import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; +import me.chanjar.weixin.common.util.http.RequestExecutor; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; +import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor; +import me.chanjar.weixin.cp.api.*; +import me.chanjar.weixin.cp.bean.*; +import me.chanjar.weixin.cp.config.WxCpConfigStorage; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,11 +29,16 @@ import java.io.IOException; import java.io.InputStream; import java.util.List; -import java.util.UUID; -public abstract class AbstractWxCpServiceImpl implements WxCpService, RequestHttp { +public abstract class WxCpServiceAbstractImpl implements WxCpService, RequestHttp { + protected final Logger log = LoggerFactory.getLogger(this.getClass()); - protected final Logger log = LoggerFactory.getLogger(AbstractWxCpServiceImpl.class); + private WxCpUserService userService = new WxCpUserServiceImpl(this); + private WxCpDepartmentService departmentService = new WxCpDepartmentServiceImpl(this); + private WxCpMediaService mediaService = new WxCpMediaServiceImpl(this); + private WxCpMenuService menuService = new WxCpMenuServiceImpl(this); + private WxCpOAuth2Service oauth2Service = new WxCpOAuth2ServiceImpl(this); + private WxCpTagService tagService = new WxCpTagServiceImpl(this); /** * 全局的是否正在刷新access token的锁 @@ -63,16 +67,11 @@ public boolean checkSignature(String msgSignature, String timestamp, String nonc return SHA1.gen(this.configStorage.getToken(), timestamp, nonce, data) .equals(msgSignature); } catch (Exception e) { + this.log.error("Checking signature failed, and the reason is :" + e.getMessage()); return false; } } - @Override - public void userAuthenticated(String userId) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/user/authsucc?userid=" + userId; - get(url, null); - } - @Override public String getAccessToken() throws WxErrorException { return getAccessToken(false); @@ -130,321 +129,182 @@ public WxJsapiSignature createJsapiSignature(String url) throws WxErrorException } @Override - public void messageSend(WxCpMessage message) throws WxErrorException { + public WxCpMessageSendResult messageSend(WxCpMessage message) throws WxErrorException { String url = "https://qyapi.weixin.qq.com/cgi-bin/message/send"; - post(url, message.toJson()); + Integer agentId = message.getAgentId(); + if(null == agentId){ + message.setAgentId(this.getWxCpConfigStorage().getAgentId()); + } + return WxCpMessageSendResult.fromJson(this.post(url, message.toJson())); } @Override + @Deprecated public void menuCreate(WxMenu menu) throws WxErrorException { - menuCreate(this.configStorage.getAgentId(), menu); + this.getMenuService().create(menu); } @Override + @Deprecated public void menuCreate(Integer agentId, WxMenu menu) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/menu/create?agentid=" - + this.configStorage.getAgentId(); - post(url, menu.toJson()); + this.getMenuService().create(agentId, menu); } @Override + @Deprecated public void menuDelete() throws WxErrorException { - menuDelete(this.configStorage.getAgentId()); + this.getMenuService().delete(); } @Override + @Deprecated public void menuDelete(Integer agentId) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/menu/delete?agentid=" + agentId; - get(url, null); + this.getMenuService().delete(agentId); } @Override + @Deprecated public WxMenu menuGet() throws WxErrorException { - return menuGet(this.configStorage.getAgentId()); + return this.getMenuService().get(); } @Override + @Deprecated public WxMenu menuGet(Integer agentId) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/menu/get?agentid=" + agentId; - try { - String resultContent = get(url, null); - return WxMenu.fromJson(resultContent); - } catch (WxErrorException e) { - // 46003 不存在的菜单数据 - if (e.getError().getErrorCode() == 46003) { - return null; - } - throw e; - } + return this.getMenuService().get(agentId); } @Override + @Deprecated public WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputStream inputStream) throws WxErrorException, IOException { - return mediaUpload(mediaType, FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), fileType)); + return this.getMediaService().upload(mediaType, fileType, inputStream); } @Override + @Deprecated public WxMediaUploadResult mediaUpload(String mediaType, File file) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/media/upload?type=" + mediaType; - return execute(MediaUploadRequestExecutor.create(this), url, file); + return this.getMediaService().upload(mediaType, file); } @Override + @Deprecated public File mediaDownload(String mediaId) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/media/get"; - return execute( - MediaDownloadRequestExecutor.create(this, - this.configStorage.getTmpDirFile()), - url, "media_id=" + mediaId); - } - - - @Override - public Integer departCreate(WxCpDepart depart) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/department/create"; - String responseContent = execute( - SimplePostRequestExecutor.create(this), - url, - depart.toJson()); - JsonElement tmpJsonElement = new JsonParser().parse(responseContent); - return GsonHelper.getAsInteger(tmpJsonElement.getAsJsonObject().get("id")); - } - - @Override - public void departUpdate(WxCpDepart group) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/department/update"; - post(url, group.toJson()); - } - - @Override - public void departDelete(Integer departId) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/department/delete?id=" + departId; - get(url, null); + return this.getMediaService().download(mediaId); } @Override - public List departGet() throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/department/list"; - String responseContent = get(url, null); - /* - * 操蛋的微信API,创建时返回的是 { group : { id : ..., name : ...} } - * 查询时返回的是 { groups : [ { id : ..., name : ..., count : ... }, ... ] } - */ - JsonElement tmpJsonElement = new JsonParser().parse(responseContent); - return WxCpGsonBuilder.INSTANCE.create() - .fromJson( - tmpJsonElement.getAsJsonObject().get("department"), - new TypeToken>() { - }.getType() - ); + @Deprecated + public void userAuthenticated(String userId) throws WxErrorException { + this.getUserService().authenticate(userId); } @Override + @Deprecated public void userCreate(WxCpUser user) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/user/create"; - post(url, user.toJson()); + this.getUserService().create(user); } @Override + @Deprecated public void userUpdate(WxCpUser user) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/user/update"; - post(url, user.toJson()); + this.getUserService().update(user); } @Override + @Deprecated public void userDelete(String userid) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/user/delete?userid=" + userid; - get(url, null); + this.getUserService().delete(userid); } @Override + @Deprecated public void userDelete(String[] userids) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/user/batchdelete"; - JsonObject jsonObject = new JsonObject(); - JsonArray jsonArray = new JsonArray(); - for (String userid : userids) { - jsonArray.add(new JsonPrimitive(userid)); - } - jsonObject.add("useridlist", jsonArray); - post(url, jsonObject.toString()); + this.getUserService().delete(userids); } @Override + @Deprecated public WxCpUser userGet(String userid) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/user/get?userid=" + userid; - String responseContent = get(url, null); - return WxCpUser.fromJson(responseContent); + return this.getUserService().getById(userid); } @Override + @Deprecated public List userList(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/user/list?department_id=" + departId; - String params = ""; - if (fetchChild != null) { - params += "&fetch_child=" + (fetchChild ? "1" : "0"); - } - if (status != null) { - params += "&status=" + status; - } else { - params += "&status=0"; - } - - String responseContent = get(url, params); - JsonElement tmpJsonElement = new JsonParser().parse(responseContent); - return WxCpGsonBuilder.INSTANCE.create() - .fromJson( - tmpJsonElement.getAsJsonObject().get("userlist"), - new TypeToken>() { - }.getType() - ); + return this.getUserService().listByDepartment(departId, fetchChild, status); } @Override + @Deprecated public List departGetUsers(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/user/simplelist?department_id=" + departId; - String params = ""; - if (fetchChild != null) { - params += "&fetch_child=" + (fetchChild ? "1" : "0"); - } - if (status != null) { - params += "&status=" + status; - } else { - params += "&status=0"; - } - - String responseContent = get(url, params); - JsonElement tmpJsonElement = new JsonParser().parse(responseContent); - return WxCpGsonBuilder.INSTANCE.create() - .fromJson( - tmpJsonElement.getAsJsonObject().get("userlist"), - new TypeToken>() { - }.getType() - ); + return this.getUserService().listSimpleByDepartment(departId, fetchChild, status); } @Override + @Deprecated public String tagCreate(String tagName) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/create"; - JsonObject o = new JsonObject(); - o.addProperty("tagname", tagName); - String responseContent = post(url, o.toString()); - JsonElement tmpJsonElement = new JsonParser().parse(responseContent); - return tmpJsonElement.getAsJsonObject().get("tagid").getAsString(); + return this.getTagService().create(tagName); } @Override + @Deprecated public void tagUpdate(String tagId, String tagName) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/update"; - JsonObject o = new JsonObject(); - o.addProperty("tagid", tagId); - o.addProperty("tagname", tagName); - post(url, o.toString()); + this.getTagService().update(tagId, tagName); } @Override + @Deprecated public void tagDelete(String tagId) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/delete?tagid=" + tagId; - get(url, null); + this.getTagService().delete(tagId); } @Override + @Deprecated public List tagGet() throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/list"; - String responseContent = get(url, null); - JsonElement tmpJsonElement = new JsonParser().parse(responseContent); - return WxCpGsonBuilder.INSTANCE.create() - .fromJson( - tmpJsonElement.getAsJsonObject().get("taglist"), - new TypeToken>() { - }.getType() - ); + return this.getTagService().listAll(); } @Override + @Deprecated public List tagGetUsers(String tagId) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/get?tagid=" + tagId; - String responseContent = get(url, null); - JsonElement tmpJsonElement = new JsonParser().parse(responseContent); - return WxCpGsonBuilder.INSTANCE.create() - .fromJson( - tmpJsonElement.getAsJsonObject().get("userlist"), - new TypeToken>() { - }.getType() - ); + return this.getTagService().listUsersByTagId(tagId); } @Override + @Deprecated public void tagAddUsers(String tagId, List userIds, List partyIds) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/addtagusers"; - JsonObject jsonObject = new JsonObject(); - jsonObject.addProperty("tagid", tagId); - if (userIds != null) { - JsonArray jsonArray = new JsonArray(); - for (String userId : userIds) { - jsonArray.add(new JsonPrimitive(userId)); - } - jsonObject.add("userlist", jsonArray); - } - if (partyIds != null) { - JsonArray jsonArray = new JsonArray(); - for (String userId : partyIds) { - jsonArray.add(new JsonPrimitive(userId)); - } - jsonObject.add("partylist", jsonArray); - } - post(url, jsonObject.toString()); + this.getTagService().addUsers2Tag(tagId, userIds, partyIds); } @Override + @Deprecated public void tagRemoveUsers(String tagId, List userIds) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/deltagusers"; - JsonObject jsonObject = new JsonObject(); - jsonObject.addProperty("tagid", tagId); - JsonArray jsonArray = new JsonArray(); - for (String userId : userIds) { - jsonArray.add(new JsonPrimitive(userId)); - } - jsonObject.add("userlist", jsonArray); - post(url, jsonObject.toString()); + this.getTagService().removeUsersFromTag(tagId, userIds); } @Override + @Deprecated public String oauth2buildAuthorizationUrl(String state) { - return this.oauth2buildAuthorizationUrl( - this.configStorage.getOauth2redirectUri(), - state - ); + return this.getOauth2Service().buildAuthorizationUrl(state); } @Override + @Deprecated public String oauth2buildAuthorizationUrl(String redirectUri, String state) { - String url = "https://open.weixin.qq.com/connect/oauth2/authorize?"; - url += "appid=" + this.configStorage.getCorpId(); - url += "&redirect_uri=" + URIUtil.encodeURIComponent(redirectUri); - url += "&response_type=code"; - url += "&scope=snsapi_base"; - if (state != null) { - url += "&state=" + state; - } - url += "#wechat_redirect"; - return url; + return this.getOauth2Service().buildAuthorizationUrl(redirectUri, state); } @Override + @Deprecated public String[] oauth2getUserInfo(String code) throws WxErrorException { - return oauth2getUserInfo(this.configStorage.getAgentId(), code); + return this.getOauth2Service().getUserInfo(code); } @Override + @Deprecated public String[] oauth2getUserInfo(Integer agentId, String code) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?" - + "code=" + code - + "&agentid=" + agentId; - String responseText = get(url, null); - JsonElement je = new JsonParser().parse(responseText); - JsonObject jo = je.getAsJsonObject(); - return new String[]{GsonHelper.getString(jo, "UserId"), GsonHelper.getString(jo, "DeviceId"), GsonHelper.getString(jo, "OpenId")}; + return this.getOauth2Service().getUserInfo(agentId, code); } @Override @@ -531,7 +391,7 @@ protected synchronized T executeInternal(RequestExecutor executor, try { T result = executor.execute(uriWithAccessToken, data); - this.log.debug("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}", uriWithAccessToken, data, result); + this.log.debug("\n【请求地址】: {}\n【请求参数】:{}\n【响应数据】:{}", uriWithAccessToken, data, result); return result; } catch (WxErrorException e) { WxError error = e.getError(); @@ -548,12 +408,12 @@ protected synchronized T executeInternal(RequestExecutor executor, } if (error.getErrorCode() != 0) { - this.log.error("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}", uriWithAccessToken, data, error); - throw new WxErrorException(error); + this.log.error("\n【请求地址】: {}\n【请求参数】:{}\n【错误信息】:{}", uriWithAccessToken, data, error); + throw new WxErrorException(error, e); } return null; } catch (IOException e) { - this.log.error("\n[URL]: {}\n[PARAMS]: {}\n[EXCEPTION]: {}", uriWithAccessToken, data, e.getMessage()); + this.log.error("\n【请求地址】: {}\n【请求参数】:{}\n【异常信息】:{}", uriWithAccessToken, data, e.getMessage()); throw new RuntimeException(e); } } @@ -591,7 +451,6 @@ public WxSession getSession(String id, boolean create) { return this.sessionManager.getSession(id, create); } - @Override public void setSessionManager(WxSessionManager sessionManager) { this.sessionManager = sessionManager; @@ -627,4 +486,62 @@ public void setTmpDirFile(File tmpDirFile) { this.tmpDirFile = tmpDirFile; } + @Override + public WxCpDepartmentService getDepartmentService() { + return departmentService; + } + + @Override + public WxCpMediaService getMediaService() { + return mediaService; + } + + @Override + public WxCpMenuService getMenuService() { + return menuService; + } + + @Override + public WxCpOAuth2Service getOauth2Service() { + return oauth2Service; + } + + @Override + public WxCpTagService getTagService() { + return tagService; + } + + @Override + public WxCpUserService getUserService() { + return userService; + } + + @Override + @Deprecated + public Integer departCreate(WxCpDepart depart) throws WxErrorException { + return this.getDepartmentService().create(depart); + } + + @Override + @Deprecated + public void departUpdate(WxCpDepart depart) throws WxErrorException { + this.getDepartmentService().update(depart); + } + + @Override + @Deprecated + public void departDelete(Integer departId) throws WxErrorException { + this.getDepartmentService().delete(departId); + } + + @Override + @Deprecated + public List departGet() throws WxErrorException { + return this.getDepartmentService().listAll(); + } + + @Override + public RequestHttp getRequestHttp() { + return this; + } } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/apache/WxCpServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceApacheHttpClientImpl.java similarity index 92% rename from weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/apache/WxCpServiceImpl.java rename to weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceApacheHttpClientImpl.java index 006bb52ee1..fc6249662b 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/apache/WxCpServiceImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceApacheHttpClientImpl.java @@ -1,4 +1,4 @@ -package me.chanjar.weixin.cp.api.impl.apache; +package me.chanjar.weixin.cp.api.impl; import me.chanjar.weixin.common.bean.WxAccessToken; @@ -7,8 +7,7 @@ import me.chanjar.weixin.common.util.http.HttpType; import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder; -import me.chanjar.weixin.cp.api.WxCpConfigStorage; -import me.chanjar.weixin.cp.api.impl.AbstractWxCpServiceImpl; +import me.chanjar.weixin.cp.config.WxCpConfigStorage; import org.apache.http.HttpHost; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; @@ -18,7 +17,7 @@ import java.io.IOException; -public class WxCpServiceImpl extends AbstractWxCpServiceImpl { +public class WxCpServiceApacheHttpClientImpl extends WxCpServiceAbstractImpl { protected CloseableHttpClient httpClient; protected HttpHost httpProxy; @@ -34,7 +33,7 @@ public HttpHost getRequestHttpProxy() { @Override public HttpType getRequestType() { - return HttpType.apacheHttp; + return HttpType.APACHE_HTTP; } @Override @@ -102,4 +101,5 @@ public void initHttp() { public WxCpConfigStorage getWxCpConfigStorage() { return this.configStorage; } + } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceImpl.java new file mode 100644 index 0000000000..ff8e149c77 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceImpl.java @@ -0,0 +1,11 @@ +package me.chanjar.weixin.cp.api.impl; + +/** + *
+ *  默认接口实现类,使用apache httpclient实现
+ * Created by Binary Wang on 2017-5-27.
+ * @author binarywang(Binary Wang)
+ * 
+ */ +public class WxCpServiceImpl extends WxCpServiceApacheHttpClientImpl { +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/jodd/WxCpServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceJoddHttpImpl.java similarity index 89% rename from weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/jodd/WxCpServiceImpl.java rename to weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceJoddHttpImpl.java index 1dc2dd0efc..5a14eb27fe 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/jodd/WxCpServiceImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceJoddHttpImpl.java @@ -1,14 +1,13 @@ -package me.chanjar.weixin.cp.api.impl.jodd; +package me.chanjar.weixin.cp.api.impl; import jodd.http.*; import me.chanjar.weixin.common.bean.WxAccessToken; import me.chanjar.weixin.common.bean.result.WxError; import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.common.util.http.HttpType; -import me.chanjar.weixin.cp.api.WxCpConfigStorage; -import me.chanjar.weixin.cp.api.impl.AbstractWxCpServiceImpl; +import me.chanjar.weixin.cp.config.WxCpConfigStorage; -public class WxCpServiceImpl extends AbstractWxCpServiceImpl { +public class WxCpServiceJoddHttpImpl extends WxCpServiceAbstractImpl { protected HttpConnectionProvider httpClient; protected ProxyInfo httpProxy; @@ -25,7 +24,7 @@ public ProxyInfo getRequestHttpProxy() { @Override public HttpType getRequestType() { - return HttpType.joddHttp; + return HttpType.JODD_HTTP; } @Override diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/okhttp/WxCpServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceOkHttpImpl.java similarity index 54% rename from weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/okhttp/WxCpServiceImpl.java rename to weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceOkHttpImpl.java index 48f4b6707c..2ad4c72015 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/okhttp/WxCpServiceImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceOkHttpImpl.java @@ -1,38 +1,42 @@ -package me.chanjar.weixin.cp.api.impl.okhttp; +package me.chanjar.weixin.cp.api.impl; import me.chanjar.weixin.common.bean.WxAccessToken; import me.chanjar.weixin.common.bean.result.WxError; import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.common.util.http.HttpType; -import me.chanjar.weixin.common.util.http.okhttp.OkhttpProxyInfo; -import me.chanjar.weixin.cp.api.WxCpConfigStorage; -import me.chanjar.weixin.cp.api.impl.AbstractWxCpServiceImpl; +import me.chanjar.weixin.common.util.http.okhttp.OkHttpProxyInfo; +import me.chanjar.weixin.cp.config.WxCpConfigStorage; import okhttp3.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; -public class WxCpServiceImpl extends AbstractWxCpServiceImpl { - protected ConnectionPool httpClient; - protected OkhttpProxyInfo httpProxy; +public class WxCpServiceOkHttpImpl extends WxCpServiceAbstractImpl { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + protected OkHttpClient httpClient; + protected OkHttpProxyInfo httpProxy; @Override - public ConnectionPool getRequestHttpClient() { + public OkHttpClient getRequestHttpClient() { return httpClient; } @Override - public OkhttpProxyInfo getRequestHttpProxy() { + public OkHttpProxyInfo getRequestHttpProxy() { return httpProxy; } @Override public HttpType getRequestType() { - return HttpType.okHttp; + return HttpType.OK_HTTP; } @Override public String getAccessToken(boolean forceRefresh) throws WxErrorException { + logger.debug("WxCpServiceOkHttpImpl is running"); if (forceRefresh) { this.configStorage.expireAccessToken(); } @@ -42,24 +46,8 @@ public String getAccessToken(boolean forceRefresh) throws WxErrorException { String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?" + "&corpid=" + this.configStorage.getCorpId() + "&corpsecret=" + this.configStorage.getCorpSecret(); - - OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder().connectionPool(httpClient); - //设置代理 - if (httpProxy != null) { - clientBuilder.proxy(getRequestHttpProxy().getProxy()); - } - //设置授权 - clientBuilder.authenticator(new Authenticator() { - @Override - public Request authenticate(Route route, Response response) throws IOException { - String credential = Credentials.basic(httpProxy.getProxyUsername(), httpProxy.getProxyPassword()); - return response.request().newBuilder() - .header("Authorization", credential) - .build(); - } - }); //得到httpClient - OkHttpClient client = clientBuilder.build(); + OkHttpClient client = getRequestHttpClient(); //请求的request Request request = new Request.Builder().url(url).get().build(); Response response = null; @@ -89,13 +77,24 @@ public Request authenticate(Route route, Response response) throws IOException { @Override public void initHttp() { - WxCpConfigStorage configStorage = this.configStorage; + logger.debug("WxCpServiceOkHttpImpl initHttp"); + OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder(); + //设置代理 + if (httpProxy != null) { + clientBuilder.proxy(getRequestHttpProxy().getProxy()); - if (configStorage.getHttpProxyHost() != null && configStorage.getHttpProxyPort() > 0) { - httpProxy = new OkhttpProxyInfo(OkhttpProxyInfo.ProxyType.SOCKS5, configStorage.getHttpProxyHost(), configStorage.getHttpProxyPort(), configStorage.getHttpProxyUsername(), configStorage.getHttpProxyPassword()); + //设置授权 + clientBuilder.authenticator(new Authenticator() { + @Override + public Request authenticate(Route route, Response response) throws IOException { + String credential = Credentials.basic(httpProxy.getProxyUsername(), httpProxy.getProxyPassword()); + return response.request().newBuilder() + .header("Authorization", credential) + .build(); + } + }); } - - httpClient = new ConnectionPool(); + httpClient = clientBuilder.build(); } @Override diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpTagServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpTagServiceImpl.java new file mode 100644 index 0000000000..ff995d9724 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpTagServiceImpl.java @@ -0,0 +1,116 @@ +package me.chanjar.weixin.cp.api.impl; + +import com.google.gson.*; +import com.google.gson.reflect.TypeToken; +import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.api.WxCpTagService; +import me.chanjar.weixin.cp.bean.WxCpTag; +import me.chanjar.weixin.cp.bean.WxCpTagAddOrRemoveUsersResult; +import me.chanjar.weixin.cp.bean.WxCpUser; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.util.List; + +/** + *
+ *  标签管理接口
+ * Created by Binary Wang on 2017-6-25.
+ * @author Binary Wang
+ * 
+ */ +public class WxCpTagServiceImpl implements WxCpTagService { + private WxCpService mainService; + + public WxCpTagServiceImpl(WxCpService mainService) { + this.mainService = mainService; + } + + @Override + public String create(String tagName) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/create"; + JsonObject o = new JsonObject(); + o.addProperty("tagname", tagName); + String responseContent = this.mainService.post(url, o.toString()); + JsonElement tmpJsonElement = new JsonParser().parse(responseContent); + return tmpJsonElement.getAsJsonObject().get("tagid").getAsString(); + } + + @Override + public void update(String tagId, String tagName) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/update"; + JsonObject o = new JsonObject(); + o.addProperty("tagid", tagId); + o.addProperty("tagname", tagName); + this.mainService.post(url, o.toString()); + } + + @Override + public void delete(String tagId) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/delete?tagid=" + tagId; + this.mainService.get(url, null); + } + + @Override + public List listAll() throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/list"; + String responseContent = this.mainService.get(url, null); + JsonElement tmpJsonElement = new JsonParser().parse(responseContent); + return WxCpGsonBuilder.INSTANCE.create() + .fromJson( + tmpJsonElement.getAsJsonObject().get("taglist"), + new TypeToken>() { + }.getType() + ); + } + + @Override + public List listUsersByTagId(String tagId) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/get?tagid=" + tagId; + String responseContent = this.mainService.get(url, null); + JsonElement tmpJsonElement = new JsonParser().parse(responseContent); + return WxCpGsonBuilder.INSTANCE.create() + .fromJson( + tmpJsonElement.getAsJsonObject().get("userlist"), + new TypeToken>() { + }.getType() + ); + } + + @Override + public WxCpTagAddOrRemoveUsersResult addUsers2Tag(String tagId, List userIds, List partyIds) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/addtagusers"; + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("tagid", tagId); + if (userIds != null) { + JsonArray jsonArray = new JsonArray(); + for (String userId : userIds) { + jsonArray.add(new JsonPrimitive(userId)); + } + jsonObject.add("userlist", jsonArray); + } + if (partyIds != null) { + JsonArray jsonArray = new JsonArray(); + for (String userId : partyIds) { + jsonArray.add(new JsonPrimitive(userId)); + } + jsonObject.add("partylist", jsonArray); + } + + return WxCpTagAddOrRemoveUsersResult.fromJson(this.mainService.post(url, jsonObject.toString())); + } + + @Override + public WxCpTagAddOrRemoveUsersResult removeUsersFromTag(String tagId, List userIds) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/deltagusers"; + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("tagid", tagId); + JsonArray jsonArray = new JsonArray(); + for (String userId : userIds) { + jsonArray.add(new JsonPrimitive(userId)); + } + jsonObject.add("userlist", jsonArray); + + return WxCpTagAddOrRemoveUsersResult.fromJson(this.mainService.post(url, jsonObject.toString())); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpUserServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpUserServiceImpl.java new file mode 100644 index 0000000000..13009427c6 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpUserServiceImpl.java @@ -0,0 +1,115 @@ +package me.chanjar.weixin.cp.api.impl; + +import com.google.gson.*; +import com.google.gson.reflect.TypeToken; +import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.api.WxCpUserService; +import me.chanjar.weixin.cp.bean.WxCpUser; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.util.List; + +/** + *
+ *  Created by BinaryWang on 2017/6/24.
+ * 
+ * + * @author Binary Wang + */ +public class WxCpUserServiceImpl implements WxCpUserService { + private WxCpService mainService; + + public WxCpUserServiceImpl(WxCpService mainService) { + this.mainService = mainService; + } + + @Override + public void authenticate(String userId) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/user/authsucc?userid=" + userId; + this.mainService.get(url, null); + } + + @Override + public void create(WxCpUser user) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/user/create"; + this.mainService.post(url, user.toJson()); + } + + @Override + public void update(WxCpUser user) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/user/update"; + this.mainService.post(url, user.toJson()); + } + + @Override + public void delete(String... userIds) throws WxErrorException { + if (userIds.length == 1) { + String url = "https://qyapi.weixin.qq.com/cgi-bin/user/delete?userid=" + userIds[0]; + this.mainService.get(url, null); + return; + } + + String url = "https://qyapi.weixin.qq.com/cgi-bin/user/batchdelete"; + JsonObject jsonObject = new JsonObject(); + JsonArray jsonArray = new JsonArray(); + for (String userid : userIds) { + jsonArray.add(new JsonPrimitive(userid)); + } + jsonObject.add("useridlist", jsonArray); + this.mainService.post(url, jsonObject.toString()); + } + + @Override + public WxCpUser getById(String userid) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/user/get?userid=" + userid; + String responseContent = this.mainService.get(url, null); + return WxCpUser.fromJson(responseContent); + } + + @Override + public List listByDepartment(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/user/list?department_id=" + departId; + String params = ""; + if (fetchChild != null) { + params += "&fetch_child=" + (fetchChild ? "1" : "0"); + } + if (status != null) { + params += "&status=" + status; + } else { + params += "&status=0"; + } + + String responseContent = this.mainService.get(url, params); + JsonElement tmpJsonElement = new JsonParser().parse(responseContent); + return WxCpGsonBuilder.INSTANCE.create() + .fromJson(tmpJsonElement.getAsJsonObject().get("userlist"), + new TypeToken>() { + }.getType() + ); + } + + @Override + public List listSimpleByDepartment(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/user/simplelist?department_id=" + departId; + String params = ""; + if (fetchChild != null) { + params += "&fetch_child=" + (fetchChild ? "1" : "0"); + } + if (status != null) { + params += "&status=" + status; + } else { + params += "&status=0"; + } + + String responseContent = this.mainService.get(url, params); + JsonElement tmpJsonElement = new JsonParser().parse(responseContent); + return WxCpGsonBuilder.INSTANCE.create() + .fromJson( + tmpJsonElement.getAsJsonObject().get("userlist"), + new TypeToken>() { + }.getType() + ); + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMessage.java index 2d07fcf867..c59ca87e1f 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMessage.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMessage.java @@ -30,6 +30,7 @@ public class WxCpMessage implements Serializable { private String musicUrl; private String hqMusicUrl; private String safe; + private String url; private List articles = new ArrayList<>(); private List mpnewsArticles = new ArrayList<>(); @@ -40,6 +41,13 @@ public static TextBuilder TEXT() { return new TextBuilder(); } + /** + * 获得文本卡片消息builder + */ + public static TextCardBuilder TEXTCARD() { + return new TextCardBuilder(); + } + /** * 获得图片消息builder */ @@ -220,4 +228,11 @@ public String toJson() { return WxCpGsonBuilder.INSTANCE.create().toJson(this); } + public String getUrl() { + return this.url; + } + + public void setUrl(String url) { + this.url = url; + } } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMessageSendResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMessageSendResult.java new file mode 100644 index 0000000000..6989c4988f --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMessageSendResult.java @@ -0,0 +1,103 @@ +package me.chanjar.weixin.cp.bean; + +import com.google.common.base.Splitter; +import com.google.gson.annotations.SerializedName; +import me.chanjar.weixin.common.util.ToStringUtils; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; +import org.apache.commons.lang3.StringUtils; + +import java.util.Collections; +import java.util.List; + +/** + *
+ * 消息发送结果对象类
+ * Created by Binary Wang on 2017-6-22.
+ * @author Binary Wang
+ * 
+ */ +public class WxCpMessageSendResult { + @Override + public String toString() { + return ToStringUtils.toSimpleString(this); + } + + public static WxCpMessageSendResult fromJson(String json) { + return WxCpGsonBuilder.INSTANCE.create().fromJson(json, WxCpMessageSendResult.class); + } + + @SerializedName("errcode") + private Integer errCode; + + @SerializedName("errmsg") + private String errMsg; + + @SerializedName("invaliduser") + private String invalidUser; + + @SerializedName("invalidparty") + private String invalidParty; + + @SerializedName("invalidtag") + private String invalidTag; + + public Integer getErrCode() { + return this.errCode; + } + + public void setErrCode(Integer errCode) { + this.errCode = errCode; + } + + public String getErrMsg() { + return this.errMsg; + } + + public void setErrMsg(String errMsg) { + this.errMsg = errMsg; + } + + public String getInvalidUser() { + return this.invalidUser; + } + + public void setInvalidUser(String invalidUser) { + this.invalidUser = invalidUser; + } + + public String getInvalidParty() { + return this.invalidParty; + } + + public void setInvalidParty(String invalidParty) { + this.invalidParty = invalidParty; + } + + public String getInvalidTag() { + return this.invalidTag; + } + + public void setInvalidTag(String invalidTag) { + this.invalidTag = invalidTag; + } + + public List getInvalidUserList() { + return this.content2List(this.invalidUser); + } + + private List content2List(String content) { + if(StringUtils.isBlank(content)){ + return Collections.emptyList(); + } + + return Splitter.on("|").splitToList(content); + } + + public List getInvalidPartyList() { + return this.content2List(this.invalidParty); + } + + public List getInvalidTagList() { + return this.content2List(this.invalidTag); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTagAddOrRemoveUsersResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTagAddOrRemoveUsersResult.java new file mode 100644 index 0000000000..c216ae46a6 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTagAddOrRemoveUsersResult.java @@ -0,0 +1,85 @@ +package me.chanjar.weixin.cp.bean; + +import com.google.common.base.Splitter; +import com.google.gson.annotations.SerializedName; +import me.chanjar.weixin.common.util.ToStringUtils; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; +import org.apache.commons.lang3.StringUtils; + +import java.util.Collections; +import java.util.List; + +/** + *
+ * 为标签添加或移除用户结果对象类
+ * Created by Binary Wang on 2017-6-22.
+ * @author Binary Wang
+ * 
+ */ +public class WxCpTagAddOrRemoveUsersResult { + @Override + public String toString() { + return ToStringUtils.toSimpleString(this); + } + + public static WxCpTagAddOrRemoveUsersResult fromJson(String json) { + return WxCpGsonBuilder.INSTANCE.create().fromJson(json, WxCpTagAddOrRemoveUsersResult.class); + } + + @SerializedName("errcode") + private Integer errCode; + + @SerializedName("errmsg") + private String errMsg; + + @SerializedName("invalidlist") + private String invalidUsers; + + @SerializedName("invalidparty") + private String[] invalidParty; + + public Integer getErrCode() { + return this.errCode; + } + + public void setErrCode(Integer errCode) { + this.errCode = errCode; + } + + public String getErrMsg() { + return this.errMsg; + } + + public void setErrMsg(String errMsg) { + this.errMsg = errMsg; + } + + public String getInvalidUser() { + return this.invalidUsers; + } + + public void setInvalidUser(String invalidUser) { + this.invalidUsers = invalidUser; + } + + public String[] getInvalidParty() { + return this.invalidParty; + } + + public void setInvalidParty(String[] invalidParty) { + this.invalidParty = invalidParty; + } + + public List getInvalidUserList() { + return this.content2List(this.invalidUsers); + } + + private List content2List(String content) { + if(StringUtils.isBlank(content)){ + return Collections.emptyList(); + } + + return Splitter.on("|").splitToList(content); + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpUser.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpUser.java index a3adca9894..647a4e6ba2 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpUser.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpUser.java @@ -13,20 +13,54 @@ */ public class WxCpUser implements Serializable { + public enum Gender { + MALE("男", "1"), + FEMAIL("女", "2"); + + private String genderName; + private String code; + + Gender(String genderName, String code) { + this.genderName = genderName; + this.code = code; + } + + public String getGenderName() { + return this.genderName; + } + + public String getCode() { + return this.code; + } + + public static Gender fromCode(String code) { + if ("1".equals(code)) { + return Gender.MALE; + } + if ("2".equals(code)) { + return Gender.FEMAIL; + } + + return null; + } + } + private static final long serialVersionUID = -5696099236344075582L; - private final List extAttrs = new ArrayList<>(); private String userId; private String name; private Integer[] departIds; private String position; private String mobile; - private String gender; - private String tel; + private Gender gender; private String email; - private String weiXinId; private String avatar; private Integer status; private Integer enable; + private Integer isLeader; + private final List extAttrs = new ArrayList<>(); + private Integer hideMobile; + private String englishName; + private String telephone; public static WxCpUser fromJson(String json) { return WxCpGsonBuilder.INSTANCE.create().fromJson(json, WxCpUser.class); @@ -56,11 +90,11 @@ public void setDepartIds(Integer[] departIds) { this.departIds = departIds; } - public String getGender() { + public Gender getGender() { return this.gender; } - public void setGender(String gender) { + public void setGender(Gender gender) { this.gender = gender; } @@ -80,12 +114,12 @@ public void setMobile(String mobile) { this.mobile = mobile; } - public String getTel() { - return this.tel; + public String getTelephone() { + return this.telephone; } - public void setTel(String tel) { - this.tel = tel; + public void setTelephone(String telephone) { + this.telephone = telephone; } public String getEmail() { @@ -96,14 +130,6 @@ public void setEmail(String email) { this.email = email; } - public String getWeiXinId() { - return this.weiXinId; - } - - public void setWeiXinId(String weiXinId) { - this.weiXinId = weiXinId; - } - public String getAvatar() { return this.avatar; } @@ -136,12 +162,35 @@ public List getExtAttrs() { return this.extAttrs; } + public Integer getIsLeader() { + return isLeader; + } + + public void setIsLeader(Integer isLeader) { + this.isLeader = isLeader; + } + + public Integer getHideMobile() { + return hideMobile; + } + + public void setHideMobile(Integer hideMobile) { + this.hideMobile = hideMobile; + } + + public String getEnglishName() { + return englishName; + } + + public void setEnglishName(String englishName) { + this.englishName = englishName; + } + public String toJson() { return WxCpGsonBuilder.INSTANCE.create().toJson(this); } public static class Attr { - private String name; private String value; diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlMessage.java index 3c0f50b883..117cf4115b 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlMessage.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlMessage.java @@ -3,7 +3,7 @@ import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.annotations.XStreamConverter; import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; -import me.chanjar.weixin.cp.api.WxCpConfigStorage; +import me.chanjar.weixin.cp.config.WxCpConfigStorage; import me.chanjar.weixin.cp.util.crypto.WxCpCryptUtil; import me.chanjar.weixin.cp.util.xml.XStreamTransformer; import org.apache.commons.io.IOUtils; @@ -164,6 +164,8 @@ public class WxCpXmlMessage implements Serializable { private SendLocationInfo sendLocationInfo = new SendLocationInfo(); protected static WxCpXmlMessage fromXml(String xml) { + //修改微信变态的消息内容格式,方便解析 + xml = xml.replace("", ""); return XStreamTransformer.fromXml(WxCpXmlMessage.class, xml); } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutMessage.java index 0d90a012c7..d69703502f 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutMessage.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutMessage.java @@ -3,7 +3,7 @@ import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.annotations.XStreamConverter; import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; -import me.chanjar.weixin.cp.api.WxCpConfigStorage; +import me.chanjar.weixin.cp.config.WxCpConfigStorage; import me.chanjar.weixin.cp.bean.outxmlbuilder.*; import me.chanjar.weixin.cp.util.crypto.WxCpCryptUtil; import me.chanjar.weixin.cp.util.xml.XStreamTransformer; diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/article/MpnewsArticle.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/article/MpnewsArticle.java index 82c4e2aaa6..b3f4acf402 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/article/MpnewsArticle.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/article/MpnewsArticle.java @@ -1,5 +1,7 @@ package me.chanjar.weixin.cp.bean.article; +import java.io.Serializable; + /** *
  *  Created by BinaryWang on 2017/3/27.
@@ -7,7 +9,9 @@
  *
  * @author Binary Wang
  */
-public class MpnewsArticle {
+public class MpnewsArticle implements Serializable {
+  private static final long serialVersionUID = 6985871812170756481L;
+
   private String title;
   private String thumbMediaId;
   private String author;
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/article/NewArticle.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/article/NewArticle.java
index d4c056d5ad..02b0b1086f 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/article/NewArticle.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/article/NewArticle.java
@@ -1,5 +1,7 @@
 package me.chanjar.weixin.cp.bean.article;
 
+import java.io.Serializable;
+
 /**
  * 
  *  Created by BinaryWang on 2017/3/27.
@@ -7,7 +9,8 @@
  *
  * @author Binary Wang
  */
-public class NewArticle {
+public class NewArticle implements Serializable {
+  private static final long serialVersionUID = 4087852055781140659L;
 
   private String title;
   private String description;
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/BaseBuilder.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/BaseBuilder.java
index ea3e710c64..784de9a769 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/BaseBuilder.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/BaseBuilder.java
@@ -2,6 +2,7 @@
 
 import me.chanjar.weixin.common.api.WxConsts;
 import me.chanjar.weixin.cp.bean.WxCpMessage;
+import org.apache.commons.lang3.StringUtils;
 
 public class BaseBuilder {
   protected String msgType;
@@ -43,8 +44,7 @@ public WxCpMessage build() {
     m.setToUser(this.toUser);
     m.setToParty(this.toParty);
     m.setToTag(this.toTag);
-    m.setSafe(
-      (this.safe == null || "".equals(this.safe)) ? WxConsts.CUSTOM_MSG_SAFE_NO : this.safe);
+    m.setSafe(StringUtils.defaultIfBlank(this.safe, WxConsts.CUSTOM_MSG_SAFE_NO));
     return m;
   }
 
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/MpnewsBuilder.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/MpnewsBuilder.java
index 21ab2549a7..55ed20abab 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/MpnewsBuilder.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/MpnewsBuilder.java
@@ -5,6 +5,7 @@
 import me.chanjar.weixin.cp.bean.article.MpnewsArticle;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 /**
@@ -30,8 +31,13 @@ public MpnewsBuilder mediaId(String mediaId) {
     return this;
   }
 
-  public MpnewsBuilder addArticle(MpnewsArticle article) {
-    this.articles.add(article);
+  public MpnewsBuilder addArticle(MpnewsArticle... articles) {
+    Collections.addAll(this.articles, articles);
+    return this;
+  }
+
+  public MpnewsBuilder articles(List articles) {
+    this.articles = articles;
     return this;
   }
 
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/NewsBuilder.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/NewsBuilder.java
index e0df095e3d..dd23941244 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/NewsBuilder.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/NewsBuilder.java
@@ -5,6 +5,7 @@
 import me.chanjar.weixin.cp.bean.article.NewArticle;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 /**
@@ -24,8 +25,13 @@ public NewsBuilder() {
     this.msgType = WxConsts.CUSTOM_MSG_NEWS;
   }
 
-  public NewsBuilder addArticle(NewArticle article) {
-    this.articles.add(article);
+  public NewsBuilder addArticle(NewArticle... articles) {
+    Collections.addAll(this.articles, articles);
+    return this;
+  }
+
+  public NewsBuilder articles(List articles) {
+    this.articles = articles;
     return this;
   }
 
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/TextCardBuilder.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/TextCardBuilder.java
new file mode 100644
index 0000000000..a30b9d4059
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/TextCardBuilder.java
@@ -0,0 +1,47 @@
+package me.chanjar.weixin.cp.bean.messagebuilder;
+
+import me.chanjar.weixin.common.api.WxConsts;
+import me.chanjar.weixin.cp.bean.WxCpMessage;
+
+/**
+ * 
+ * 文本卡片消息Builder
+ * 用法: WxCustomMessage m = WxCustomMessage.TEXTCARD().title(...)....toUser(...).build();
+ * Created by Binary Wang on 2017-7-2.
+ * 
+ * + * @author Binary Wang + */ +public class TextCardBuilder extends BaseBuilder { + private String title; + private String description; + private String url; + + public TextCardBuilder() { + this.msgType = WxConsts.CUSTOM_MSG_TEXTCARD; + } + + public TextCardBuilder title(String title) { + this.title = title; + return this; + } + + public TextCardBuilder description(String description) { + this.description = description; + return this; + } + + public TextCardBuilder url(String url) { + this.url = url; + return this; + } + + @Override + public WxCpMessage build() { + WxCpMessage m = super.build(); + m.setTitle(this.title); + m.setDescription(this.description); + m.setUrl(this.url); + return m; + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/outxmlbuilder/NewsBuilder.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/outxmlbuilder/NewsBuilder.java index 3e910d8dba..5a67056ab6 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/outxmlbuilder/NewsBuilder.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/outxmlbuilder/NewsBuilder.java @@ -4,6 +4,7 @@ import me.chanjar.weixin.cp.bean.WxCpXmlOutNewsMessage.Item; import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** @@ -12,11 +13,15 @@ * @author Daniel Qian */ public final class NewsBuilder extends BaseBuilder { + private List articles = new ArrayList<>(); - protected final List articles = new ArrayList<>(); + public NewsBuilder addArticle(Item... items) { + Collections.addAll(this.articles, items); + return this; + } - public NewsBuilder addArticle(Item item) { - this.articles.add(item); + public NewsBuilder articles(List articles){ + this.articles = articles; return this; } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpConfigStorage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpConfigStorage.java similarity index 97% rename from weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpConfigStorage.java rename to weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpConfigStorage.java index 3484db3b88..65dd3affff 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpConfigStorage.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpConfigStorage.java @@ -1,4 +1,4 @@ -package me.chanjar.weixin.cp.api; +package me.chanjar.weixin.cp.config; import me.chanjar.weixin.common.bean.WxAccessToken; import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpInMemoryConfigStorage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpInMemoryConfigStorage.java similarity index 99% rename from weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpInMemoryConfigStorage.java rename to weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpInMemoryConfigStorage.java index b1eeb8274a..1b57f83bf8 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpInMemoryConfigStorage.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpInMemoryConfigStorage.java @@ -1,4 +1,4 @@ -package me.chanjar.weixin.cp.api; +package me.chanjar.weixin.cp.config; import me.chanjar.weixin.common.bean.WxAccessToken; import me.chanjar.weixin.common.util.ToStringUtils; diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpJedisConfigStorage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpJedisConfigStorage.java similarity index 99% rename from weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpJedisConfigStorage.java rename to weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpJedisConfigStorage.java index 25f5e2887b..39b2ae5023 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpJedisConfigStorage.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpJedisConfigStorage.java @@ -1,4 +1,4 @@ -package me.chanjar.weixin.cp.api; +package me.chanjar.weixin.cp.config; import me.chanjar.weixin.common.bean.WxAccessToken; import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageHandler.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/message/WxCpMessageHandler.java similarity index 91% rename from weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageHandler.java rename to weixin-java-cp/src/main/java/me/chanjar/weixin/cp/message/WxCpMessageHandler.java index 9a3920a285..615fa8f220 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageHandler.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/message/WxCpMessageHandler.java @@ -1,7 +1,8 @@ -package me.chanjar.weixin.cp.api; +package me.chanjar.weixin.cp.message; import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.common.session.WxSessionManager; +import me.chanjar.weixin.cp.api.WxCpService; import me.chanjar.weixin.cp.bean.WxCpXmlMessage; import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage; diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageInterceptor.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/message/WxCpMessageInterceptor.java similarity index 90% rename from weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageInterceptor.java rename to weixin-java-cp/src/main/java/me/chanjar/weixin/cp/message/WxCpMessageInterceptor.java index e6de0e69f4..21abb5cdb4 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageInterceptor.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/message/WxCpMessageInterceptor.java @@ -1,7 +1,8 @@ -package me.chanjar.weixin.cp.api; +package me.chanjar.weixin.cp.message; import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.common.session.WxSessionManager; +import me.chanjar.weixin.cp.api.WxCpService; import me.chanjar.weixin.cp.bean.WxCpXmlMessage; import java.util.Map; diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageMatcher.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/message/WxCpMessageMatcher.java similarity index 86% rename from weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageMatcher.java rename to weixin-java-cp/src/main/java/me/chanjar/weixin/cp/message/WxCpMessageMatcher.java index c2c62b9361..1bf36705b7 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageMatcher.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/message/WxCpMessageMatcher.java @@ -1,4 +1,4 @@ -package me.chanjar.weixin.cp.api; +package me.chanjar.weixin.cp.message; import me.chanjar.weixin.cp.bean.WxCpXmlMessage; diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageRouter.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/message/WxCpMessageRouter.java similarity index 91% rename from weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageRouter.java rename to weixin-java-cp/src/main/java/me/chanjar/weixin/cp/message/WxCpMessageRouter.java index da18e28e4f..3c53d26106 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageRouter.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/message/WxCpMessageRouter.java @@ -1,4 +1,4 @@ -package me.chanjar.weixin.cp.api; +package me.chanjar.weixin.cp.message; import me.chanjar.weixin.common.api.WxErrorExceptionHandler; import me.chanjar.weixin.common.api.WxMessageDuplicateChecker; @@ -8,13 +8,16 @@ import me.chanjar.weixin.common.session.StandardSessionManager; import me.chanjar.weixin.common.session.WxSessionManager; import me.chanjar.weixin.common.util.LogExceptionHandler; +import me.chanjar.weixin.cp.api.WxCpService; import me.chanjar.weixin.cp.bean.WxCpXmlMessage; import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -135,8 +138,9 @@ public WxCpMessageRouterRule rule() { * 处理微信消息 * * @param wxMessage + * @param context */ - public WxCpXmlOutMessage route(final WxCpXmlMessage wxMessage) { + public WxCpXmlOutMessage route(final WxCpXmlMessage wxMessage, final Map context) { if (isDuplicateMessage(wxMessage)) { // 如果是重复消息,那么就不做处理 return null; @@ -166,12 +170,12 @@ public WxCpXmlOutMessage route(final WxCpXmlMessage wxMessage) { this.executorService.submit(new Runnable() { @Override public void run() { - rule.service(wxMessage, WxCpMessageRouter.this.wxCpService, WxCpMessageRouter.this.sessionManager, WxCpMessageRouter.this.exceptionHandler); + rule.service(wxMessage, context, WxCpMessageRouter.this.wxCpService, WxCpMessageRouter.this.sessionManager, WxCpMessageRouter.this.exceptionHandler); } }) ); } else { - res = rule.service(wxMessage, this.wxCpService, this.sessionManager, this.exceptionHandler); + res = rule.service(wxMessage, context, this.wxCpService, this.sessionManager, this.exceptionHandler); // 在同步操作结束,session访问结束 this.log.debug("End session access: async=false, sessionId={}", wxMessage.getFromUserName()); sessionEndAccess(wxMessage); @@ -200,6 +204,16 @@ public void run() { return res; } + + /** + * 处理微信消息 + * + * @param wxMessage + */ + public WxCpXmlOutMessage route(final WxCpXmlMessage wxMessage) { + return this.route(wxMessage, new HashMap()); + } + protected boolean isDuplicateMessage(WxCpXmlMessage wxMessage) { String messageId = ""; diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageRouterRule.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/message/WxCpMessageRouterRule.java similarity index 86% rename from weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageRouterRule.java rename to weixin-java-cp/src/main/java/me/chanjar/weixin/cp/message/WxCpMessageRouterRule.java index 7bd332ad9b..b5912404ce 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageRouterRule.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/message/WxCpMessageRouterRule.java @@ -1,10 +1,12 @@ -package me.chanjar.weixin.cp.api; +package me.chanjar.weixin.cp.message; import me.chanjar.weixin.common.api.WxErrorExceptionHandler; import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.common.session.WxSessionManager; +import me.chanjar.weixin.cp.api.WxCpService; import me.chanjar.weixin.cp.bean.WxCpXmlMessage; import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage; +import org.apache.commons.lang3.StringUtils; import java.util.ArrayList; import java.util.HashMap; @@ -26,6 +28,8 @@ public class WxCpMessageRouterRule { private String eventKey; + private String eventKeyRegex; + private String content; private String rContent; @@ -94,6 +98,14 @@ public WxCpMessageRouterRule eventKey(String eventKey) { return this; } + /** + * 如果eventKey匹配该正则表达式 + */ + public WxCpMessageRouterRule eventKeyRegex(String regex) { + this.eventKeyRegex = regex; + return this; + } + /** * 如果content等于某值 * @@ -206,17 +218,17 @@ protected boolean test(WxCpXmlMessage wxMessage) { && (this.agentId == null || this.agentId.equals(wxMessage.getAgentId())) && - (this.msgType == null || this.msgType.equals(wxMessage.getMsgType())) + (this.msgType == null || this.msgType.equalsIgnoreCase(wxMessage.getMsgType())) + && + (this.event == null || this.event.equalsIgnoreCase(wxMessage.getEvent())) && - (this.event == null || this.event.equals(wxMessage.getEvent())) + (this.eventKey == null || this.eventKey.equalsIgnoreCase(wxMessage.getEventKey())) && - (this.eventKey == null || this.eventKey.equals(wxMessage.getEventKey())) + (this.eventKeyRegex == null || Pattern.matches(this.eventKeyRegex, StringUtils.trimToEmpty(wxMessage.getEventKey()))) && - (this.content == null || this.content - .equals(wxMessage.getContent() == null ? null : wxMessage.getContent().trim())) + (this.content == null || this.content.equals(StringUtils.trimToNull(wxMessage.getContent()))) && - (this.rContent == null || Pattern - .matches(this.rContent, wxMessage.getContent() == null ? "" : wxMessage.getContent().trim())) + (this.rContent == null || Pattern.matches(this.rContent, StringUtils.trimToEmpty(wxMessage.getContent()))) && (this.matcher == null || this.matcher.match(wxMessage)) ; @@ -229,13 +241,16 @@ protected boolean test(WxCpXmlMessage wxMessage) { * @return true 代表继续执行别的router,false 代表停止执行别的router */ protected WxCpXmlOutMessage service(WxCpXmlMessage wxMessage, + Map context, WxCpService wxCpService, WxSessionManager sessionManager, WxErrorExceptionHandler exceptionHandler) { - try { + if (context == null) { + context = new HashMap<>(); + } - Map context = new HashMap<>(); + try { // 如果拦截器不通过 for (WxCpMessageInterceptor interceptor : this.interceptors) { if (!interceptor.intercept(wxMessage, context, wxCpService, sessionManager)) { diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/crypto/WxCpCryptUtil.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/crypto/WxCpCryptUtil.java index 2b2dfe4715..770ef82797 100755 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/crypto/WxCpCryptUtil.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/crypto/WxCpCryptUtil.java @@ -22,7 +22,7 @@ package me.chanjar.weixin.cp.util.crypto; import me.chanjar.weixin.common.util.crypto.WxCryptUtil; -import me.chanjar.weixin.cp.api.WxCpConfigStorage; +import me.chanjar.weixin.cp.config.WxCpConfigStorage; import org.apache.commons.codec.binary.Base64; public class WxCpCryptUtil extends WxCryptUtil { diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpGsonBuilder.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpGsonBuilder.java index 3235864811..5efee45ce5 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpGsonBuilder.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpGsonBuilder.java @@ -2,6 +2,7 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import me.chanjar.weixin.common.bean.menu.WxMenu; import me.chanjar.weixin.common.bean.result.WxError; import me.chanjar.weixin.common.util.json.WxErrorAdapter; import me.chanjar.weixin.cp.bean.WxCpDepart; @@ -19,6 +20,7 @@ public class WxCpGsonBuilder { INSTANCE.registerTypeAdapter(WxCpDepart.class, new WxCpDepartGsonAdapter()); INSTANCE.registerTypeAdapter(WxCpUser.class, new WxCpUserGsonAdapter()); INSTANCE.registerTypeAdapter(WxError.class, new WxErrorAdapter()); + INSTANCE.registerTypeAdapter(WxMenu.class, new WxCpMenuGsonAdapter()); INSTANCE.registerTypeAdapter(WxCpTag.class, new WxCpTagGsonAdapter()); } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpMenuGsonAdapter.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpMenuGsonAdapter.java new file mode 100644 index 0000000000..c7cb05cc62 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpMenuGsonAdapter.java @@ -0,0 +1,24 @@ +package me.chanjar.weixin.cp.util.json; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import me.chanjar.weixin.common.bean.menu.WxMenu; +import me.chanjar.weixin.common.util.json.WxMenuGsonAdapter; + +import java.lang.reflect.Type; + +/** + *
+ * 企业号菜单json转换适配器
+ * Created by Binary Wang on 2017-6-25.
+ * @author Binary Wang
+ * 
+ */ +public class WxCpMenuGsonAdapter extends WxMenuGsonAdapter { + + @Override + public WxMenu deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + return this.buildMenuFromJson(json.getAsJsonObject().get("button").getAsJsonArray()); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpMessageGsonAdapter.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpMessageGsonAdapter.java index a74c17b64c..67e43c4ccc 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpMessageGsonAdapter.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpMessageGsonAdapter.java @@ -43,6 +43,14 @@ public JsonElement serialize(WxCpMessage message, Type typeOfSrc, JsonSerializat messageJson.add("text", text); } + if (WxConsts.CUSTOM_MSG_TEXTCARD.equals(message.getMsgType())) { + JsonObject text = new JsonObject(); + text.addProperty("title", message.getTitle()); + text.addProperty("description", message.getDescription()); + text.addProperty("url", message.getUrl()); + messageJson.add("textcard", text); + } + if (WxConsts.CUSTOM_MSG_IMAGE.equals(message.getMsgType())) { JsonObject image = new JsonObject(); image.addProperty("media_id", message.getMediaId()); diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpUserGsonAdapter.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpUserGsonAdapter.java index 4a8a9010e8..6531d07ba9 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpUserGsonAdapter.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpUserGsonAdapter.java @@ -24,8 +24,6 @@ public WxCpUser deserialize(JsonElement json, Type typeOfT, JsonDeserializationC throws JsonParseException { JsonObject o = json.getAsJsonObject(); WxCpUser user = new WxCpUser(); - user.setUserId(GsonHelper.getString(o, "userid")); - user.setName(GsonHelper.getString(o, "name")); if (o.get("department") != null) { JsonArray departJsonArray = o.get("department").getAsJsonArray(); @@ -37,14 +35,19 @@ public WxCpUser deserialize(JsonElement json, Type typeOfT, JsonDeserializationC user.setDepartIds(departIds); } + user.setUserId(GsonHelper.getString(o, "userid")); + user.setName(GsonHelper.getString(o, "name")); user.setPosition(GsonHelper.getString(o, "position")); user.setMobile(GsonHelper.getString(o, "mobile")); - user.setGender(GsonHelper.getString(o, "gender")); - user.setTel(GsonHelper.getString(o, "tel")); + user.setGender(WxCpUser.Gender.fromCode(GsonHelper.getString(o, "gender"))); user.setEmail(GsonHelper.getString(o, "email")); - user.setWeiXinId(GsonHelper.getString(o, "weixinid")); user.setAvatar(GsonHelper.getString(o, "avatar")); user.setStatus(GsonHelper.getInteger(o, "status")); + user.setEnable(GsonHelper.getInteger(o, "enable")); + user.setIsLeader(GsonHelper.getInteger(o, "isleader")); + user.setHideMobile(GsonHelper.getInteger(o, "hide_mobile")); + user.setEnglishName(GsonHelper.getString(o, "english_name")); + user.setTelephone(GsonHelper.getString(o, "telephone")); if (GsonHelper.isNotNull(o.get("extattr"))) { JsonArray attrJsonElements = o.get("extattr").getAsJsonObject().get("attrs").getAsJsonArray(); @@ -82,17 +85,11 @@ public JsonElement serialize(WxCpUser user, Type typeOfSrc, JsonSerializationCon o.addProperty("mobile", user.getMobile()); } if (user.getGender() != null) { - o.addProperty("gender", user.getGender()); - } - if (user.getTel() != null) { - o.addProperty("tel", user.getTel()); + o.addProperty("gender", user.getGender().getCode()); } if (user.getEmail() != null) { o.addProperty("email", user.getEmail()); } - if (user.getWeiXinId() != null) { - o.addProperty("weixinid", user.getWeiXinId()); - } if (user.getAvatar() != null) { o.addProperty("avatar", user.getAvatar()); } @@ -102,6 +99,18 @@ public JsonElement serialize(WxCpUser user, Type typeOfSrc, JsonSerializationCon if (user.getEnable() != null) { o.addProperty("enable", user.getEnable()); } + if (user.getIsLeader() != null) { + o.addProperty("isleader", user.getIsLeader()); + } + if (user.getHideMobile() != null) { + o.addProperty("hide_mobile", user.getHideMobile()); + } + if (user.getEnglishName() != null) { + o.addProperty("english_name", user.getEnglishName()); + } + if (user.getTelephone() != null) { + o.addProperty("telephone", user.getTelephone()); + } if (user.getExtAttrs().size() > 0) { JsonArray attrsJsonArray = new JsonArray(); for (WxCpUser.Attr attr : user.getExtAttrs()) { diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/ApiTestModule.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/ApiTestModule.java index d498cc5a92..179086f739 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/ApiTestModule.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/ApiTestModule.java @@ -5,7 +5,9 @@ import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.annotations.XStreamAlias; import me.chanjar.weixin.common.util.xml.XStreamInitializer; -import me.chanjar.weixin.cp.api.impl.apache.WxCpServiceImpl; +import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl; +import me.chanjar.weixin.cp.config.WxCpConfigStorage; +import me.chanjar.weixin.cp.config.WxCpInMemoryConfigStorage; import java.io.IOException; import java.io.InputStream; @@ -25,11 +27,11 @@ public void configure(Binder binder) { .getSystemResourceAsStream("test-config.xml")) { WxXmlCpInMemoryConfigStorage config = fromXml( WxXmlCpInMemoryConfigStorage.class, is1); - WxCpServiceImpl wxService = new WxCpServiceImpl(); + WxCpService wxService = new WxCpServiceImpl(); wxService.setWxCpConfigStorage(config); - binder.bind(WxCpServiceImpl.class).toInstance(wxService); - binder.bind(WxCpConfigStorage.class).toInstance(config); + binder.bind(WxCpService.class).toInstance(wxService); + binder.bind(WxXmlCpInMemoryConfigStorage.class).toInstance(config); } catch (IOException e) { e.printStackTrace(); } diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/apache/WxCpBaseAPITest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpBaseAPITest.java similarity index 75% rename from weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/apache/WxCpBaseAPITest.java rename to weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpBaseAPITest.java index b3271dc8fe..b535634a45 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/apache/WxCpBaseAPITest.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpBaseAPITest.java @@ -1,12 +1,13 @@ -package me.chanjar.weixin.cp.api.impl.apache; +package me.chanjar.weixin.cp.api; import com.google.inject.Inject; import me.chanjar.weixin.common.exception.WxErrorException; -import me.chanjar.weixin.cp.api.ApiTestModule; -import me.chanjar.weixin.cp.api.WxCpConfigStorage; +import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl; +import me.chanjar.weixin.cp.config.WxCpConfigStorage; import org.apache.commons.lang3.StringUtils; -import org.testng.*; -import org.testng.annotations.*; +import org.testng.Assert; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; /** * 基础API测试 diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/apache/WxCpBusyRetryTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpBusyRetryTest.java similarity index 92% rename from weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/apache/WxCpBusyRetryTest.java rename to weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpBusyRetryTest.java index 3b8e8fa3e4..2c964d309c 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/apache/WxCpBusyRetryTest.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpBusyRetryTest.java @@ -1,10 +1,11 @@ -package me.chanjar.weixin.cp.api.impl.apache; +package me.chanjar.weixin.cp.api; import me.chanjar.weixin.common.bean.result.WxError; import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.common.util.http.RequestExecutor; -import me.chanjar.weixin.cp.api.WxCpService; -import org.testng.annotations.*; +import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpDepartAPITest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpDepartAPITest.java deleted file mode 100644 index 58c142ff39..0000000000 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpDepartAPITest.java +++ /dev/null @@ -1,62 +0,0 @@ -package me.chanjar.weixin.cp.api; - -import com.google.inject.Inject; -import me.chanjar.weixin.common.exception.WxErrorException; -import me.chanjar.weixin.cp.api.impl.apache.WxCpServiceImpl; -import me.chanjar.weixin.cp.bean.WxCpDepart; -import org.testng.*; -import org.testng.annotations.*; - -import java.util.List; - -/** - * 测试部门接口 - * - * @author Daniel Qian - */ -@Test(groups = "departAPI", dependsOnGroups = "baseAPI") -@Guice(modules = ApiTestModule.class) -public class WxCpDepartAPITest { - - @Inject - protected WxCpServiceImpl wxCpService; - - protected WxCpDepart depart; - - public void testDepartCreate() throws WxErrorException { - WxCpDepart cpDepart = new WxCpDepart(); - cpDepart.setName("子部门" + System.currentTimeMillis()); - cpDepart.setParentId(1); - cpDepart.setOrder(1); - Integer departId = this.wxCpService.departCreate(cpDepart); - System.out.println(departId); - } - - @Test(dependsOnMethods = "testDepartCreate") - public void testDepartGet() throws WxErrorException { - System.out.println("=================获取部门"); - List departList = this.wxCpService.departGet(); - Assert.assertNotNull(departList); - Assert.assertTrue(departList.size() > 0); - for (WxCpDepart g : departList) { - this.depart = g; - System.out.println(this.depart.getId() + ":" + this.depart.getName()); - Assert.assertNotNull(g.getName()); - } - } - - @Test(dependsOnMethods = {"testDepartGet", "testDepartCreate"}) - public void testDepartUpdate() throws WxErrorException { - System.out.println("=================更新部门"); - this.depart.setName("子部门改名" + System.currentTimeMillis()); - this.wxCpService.departUpdate(this.depart); - } - - @Test(dependsOnMethods = "testDepartUpdate") - public void testDepartDelete() throws WxErrorException { - System.out.println("=================删除部门"); - System.out.println(this.depart.getId() + ":" + this.depart.getName()); - this.wxCpService.departDelete(this.depart.getId()); - } - -} diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMediaAPITest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMediaAPITest.java deleted file mode 100644 index b289bdbbfb..0000000000 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMediaAPITest.java +++ /dev/null @@ -1,75 +0,0 @@ -package me.chanjar.weixin.cp.api; - -import com.google.inject.Inject; -import me.chanjar.weixin.common.api.WxConsts; -import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; -import me.chanjar.weixin.common.exception.WxErrorException; -import me.chanjar.weixin.cp.api.impl.apache.WxCpServiceImpl; -import org.testng.*; -import org.testng.annotations.*; - -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; - -/** - * 测试多媒体文件上传下载 - * - * @author Daniel Qian - */ -//@Test(groups="mediaAPI", dependsOnGroups="baseAPI") -@Test -@Guice(modules = ApiTestModule.class) -public class WxCpMediaAPITest { - - @Inject - protected WxCpServiceImpl wxService; - - private List media_ids = new ArrayList<>(); - - @Test(dataProvider = "uploadMedia") - public void testUploadMedia(String mediaType, String fileType, String fileName) throws WxErrorException, IOException { - try (InputStream inputStream = ClassLoader - .getSystemResourceAsStream(fileName);) { - WxMediaUploadResult res = this.wxService.mediaUpload(mediaType, fileType, - inputStream); - Assert.assertNotNull(res.getType()); - Assert.assertNotNull(res.getCreatedAt()); - Assert.assertTrue( - res.getMediaId() != null || res.getThumbMediaId() != null); - - if (res.getMediaId() != null) { - this.media_ids.add(res.getMediaId()); - } - if (res.getThumbMediaId() != null) { - this.media_ids.add(res.getThumbMediaId()); - } - } - } - - @DataProvider - public Object[][] uploadMedia() { - return new Object[][]{ - new Object[]{WxConsts.MEDIA_IMAGE, TestConstants.FILE_JPG, "mm.jpeg"}, - new Object[]{WxConsts.MEDIA_VOICE, TestConstants.FILE_MP3, "mm.mp3"}, - new Object[]{WxConsts.MEDIA_VIDEO, TestConstants.FILE_MP4, "mm.mp4"}, - new Object[]{WxConsts.MEDIA_FILE, TestConstants.FILE_JPG, "mm.jpeg"} - }; - } - - @Test(dependsOnMethods = {"testUploadMedia"}, dataProvider = "downloadMedia") - public void testDownloadMedia(String media_id) throws WxErrorException { - this.wxService.mediaDownload(media_id); - } - - @DataProvider - public Object[][] downloadMedia() { - Object[][] params = new Object[this.media_ids.size()][]; - for (int i = 0; i < this.media_ids.size(); i++) { - params[i] = new Object[]{this.media_ids.get(i)}; - } - return params; - } - -} diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMessageAPITest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMessageAPITest.java new file mode 100644 index 0000000000..0cecc01588 --- /dev/null +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMessageAPITest.java @@ -0,0 +1,63 @@ +package me.chanjar.weixin.cp.api; + +import com.google.inject.Inject; +import me.chanjar.weixin.common.api.WxConsts; +import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl; +import me.chanjar.weixin.cp.bean.WxCpMessage; +import me.chanjar.weixin.cp.bean.WxCpMessageSendResult; +import org.testng.annotations.*; + +import static org.testng.Assert.*; + +/*** + * 测试发送消息 + * @author Daniel Qian + * + */ +@Test(groups = "customMessageAPI") +@Guice(modules = ApiTestModule.class) +public class WxCpMessageAPITest { + + @Inject + protected WxCpService wxService; + + private ApiTestModule.WxXmlCpInMemoryConfigStorage configStorage; + + @BeforeTest + public void setup() { + configStorage = (ApiTestModule.WxXmlCpInMemoryConfigStorage) this.wxService.getWxCpConfigStorage(); + } + + public void testSendMessage() throws WxErrorException { + WxCpMessage message = new WxCpMessage(); +// message.setAgentId(configStorage.getAgentId()); + message.setMsgType(WxConsts.CUSTOM_MSG_TEXT); + message.setToUser(configStorage.getUserId()); + message.setContent("欢迎欢迎,热烈欢迎\n换行测试\n超链接:Hello World"); + + WxCpMessageSendResult messageSendResult = this.wxService.messageSend(message); + assertNotNull(messageSendResult); + System.out.println(messageSendResult); + System.out.println(messageSendResult.getInvalidPartyList()); + System.out.println(messageSendResult.getInvalidUserList()); + System.out.println(messageSendResult.getInvalidTagList()); + } + + @Test + public void testSendMessage1() throws WxErrorException { + WxCpMessage message = WxCpMessage + .TEXT() +// .agentId(configStorage.getAgentId()) + .toUser(configStorage.getUserId()) + .content("欢迎欢迎,热烈欢迎\n换行测试\n超链接:Hello World") + .build(); + + WxCpMessageSendResult messageSendResult = this.wxService.messageSend(message); + assertNotNull(messageSendResult); + System.out.println(messageSendResult); + System.out.println(messageSendResult.getInvalidPartyList()); + System.out.println(messageSendResult.getInvalidUserList()); + System.out.println(messageSendResult.getInvalidTagList()); + } +} diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMessageRouterTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMessageRouterTest.java index 136109827b..e52101f508 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMessageRouterTest.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMessageRouterTest.java @@ -3,10 +3,14 @@ import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.common.session.StandardSessionManager; import me.chanjar.weixin.common.session.WxSessionManager; +import me.chanjar.weixin.cp.message.WxCpMessageHandler; +import me.chanjar.weixin.cp.message.WxCpMessageMatcher; +import me.chanjar.weixin.cp.message.WxCpMessageRouter; import me.chanjar.weixin.cp.bean.WxCpXmlMessage; import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage; -import org.testng.*; -import org.testng.annotations.*; +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; import java.util.Map; diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpTagAPITest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpTagAPITest.java deleted file mode 100644 index 170f386cdb..0000000000 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpTagAPITest.java +++ /dev/null @@ -1,66 +0,0 @@ -package me.chanjar.weixin.cp.api; - -import com.google.inject.Inject; -import me.chanjar.weixin.cp.api.impl.apache.WxCpServiceImpl; -import me.chanjar.weixin.cp.bean.WxCpTag; -import me.chanjar.weixin.cp.bean.WxCpUser; -import org.testng.*; -import org.testng.annotations.*; - -import java.util.ArrayList; -import java.util.List; - -@Test(groups = "departAPI", dependsOnGroups = "baseAPI") -@Guice(modules = ApiTestModule.class) -public class WxCpTagAPITest { - - @Inject - protected WxCpServiceImpl wxService; - - @Inject - protected WxCpConfigStorage configStorage; - - protected String tagId; - - public void testTagCreate() throws Exception { - this.tagId = this.wxService.tagCreate("测试标签4"); - System.out.println(this.tagId); - } - - @Test(dependsOnMethods = "testTagCreate") - public void testTagUpdate() throws Exception { - this.wxService.tagUpdate(this.tagId, "测试标签-改名"); - } - - @Test(dependsOnMethods = "testTagUpdate") - public void testTagGet() throws Exception { - List tags = this.wxService.tagGet(); - Assert.assertNotEquals(tags.size(), 0); - } - - @Test(dependsOnMethods = "testTagGet") - public void testTagAddUsers() throws Exception { - List userIds = new ArrayList<>(); - userIds.add(((ApiTestModule.WxXmlCpInMemoryConfigStorage) this.configStorage).getUserId()); - this.wxService.tagAddUsers(this.tagId, userIds, null); - } - - @Test(dependsOnMethods = "testTagAddUsers") - public void testTagGetUsers() throws Exception { - List users = this.wxService.tagGetUsers(this.tagId); - Assert.assertNotEquals(users.size(), 0); - } - - @Test(dependsOnMethods = "testTagGetUsers") - public void testTagRemoveUsers() throws Exception { - List userIds = new ArrayList<>(); - userIds.add(((ApiTestModule.WxXmlCpInMemoryConfigStorage) this.configStorage).getUserId()); - this.wxService.tagRemoveUsers(this.tagId, userIds); - } - - @Test(dependsOnMethods = "testTagRemoveUsers") - public void testTagDelete() throws Exception { - this.wxService.tagDelete(this.tagId); - } - -} diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpUserAPITest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpUserAPITest.java deleted file mode 100644 index 1bfb7c6060..0000000000 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpUserAPITest.java +++ /dev/null @@ -1,66 +0,0 @@ -package me.chanjar.weixin.cp.api; - -import com.google.inject.Inject; -import me.chanjar.weixin.common.exception.WxErrorException; -import me.chanjar.weixin.cp.api.impl.apache.WxCpServiceImpl; -import me.chanjar.weixin.cp.bean.WxCpDepart; -import me.chanjar.weixin.cp.bean.WxCpUser; -import org.testng.*; -import org.testng.annotations.*; - -import java.util.List; - -/** - * 测试用户接口 - * - * @author Daniel Qian - */ -@Test(groups = "userAPI", dependsOnGroups = "baseAPI") -@Guice(modules = ApiTestModule.class) -public class WxCpUserAPITest { - - @Inject - protected WxCpServiceImpl wxCpService; - - protected WxCpDepart depart; - - public void testUserCreate() throws WxErrorException { - WxCpUser user = new WxCpUser(); - user.setUserId("some.woman"); - user.setName("Some Woman"); - user.setDepartIds(new Integer[]{9, 8}); - user.setEmail("none@none.com"); - user.setGender("女"); - user.setMobile("13560084979"); - user.setPosition("woman"); - user.setTel("3300393"); - user.addExtAttr("爱好", "table"); - this.wxCpService.userCreate(user); - } - - @Test(dependsOnMethods = "testUserCreate") - public void testUserUpdate() throws WxErrorException { - WxCpUser user = new WxCpUser(); - user.setUserId("some.woman"); - user.setName("Some Woman"); - user.addExtAttr("爱好", "table2"); - this.wxCpService.userUpdate(user); - } - - @Test(dependsOnMethods = "testUserUpdate") - public void testUserGet() throws WxErrorException { - WxCpUser user = this.wxCpService.userGet("some.woman"); - Assert.assertNotNull(user); - } - - @Test(dependsOnMethods = "testUserGet") - public void testDepartGetUsers() throws WxErrorException { - List users = this.wxCpService.departGetUsers(1, true, 0); - Assert.assertNotEquals(users.size(), 0); - } - - @Test(dependsOnMethods = "testDepartGetUsers") - public void testUserDelete() throws WxErrorException { - this.wxCpService.userDelete("some.woman"); - } -} diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpDepartmentServiceImplTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpDepartmentServiceImplTest.java new file mode 100644 index 0000000000..685f37444f --- /dev/null +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpDepartmentServiceImplTest.java @@ -0,0 +1,66 @@ +package me.chanjar.weixin.cp.api.impl; + +import com.google.inject.Inject; +import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.cp.api.ApiTestModule; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.bean.WxCpDepart; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import java.util.List; + +import static org.testng.Assert.*; + +/** + *
+ *  Created by BinaryWang on 2017/6/24.
+ * 
+ * + * @author Binary Wang + */ +@Guice(modules = ApiTestModule.class) +public class WxCpDepartmentServiceImplTest { + @Inject + private WxCpService wxCpService; + + private WxCpDepart depart; + + @Test + public void testCreate() throws Exception { + WxCpDepart cpDepart = new WxCpDepart(); + cpDepart.setName("子部门" + System.currentTimeMillis()); + cpDepart.setParentId(1); + cpDepart.setOrder(1); + Integer departId = this.wxCpService.getDepartmentService().create(cpDepart); + System.out.println(departId); + } + + @Test + public void testListAll() throws Exception { + System.out.println("=================获取部门"); + List departList = this.wxCpService.getDepartmentService().listAll(); + assertNotNull(departList); + assertTrue(departList.size() > 0); + for (WxCpDepart g : departList) { + this.depart = g; + System.out.println(this.depart.getId() + ":" + this.depart.getName()); + assertNotNull(g.getName()); + } + } + + @Test(dependsOnMethods = {"testListAll", "testCreate"}) + public void testUpdate() throws Exception { + System.out.println("=================更新部门"); + this.depart.setName("子部门改名" + System.currentTimeMillis()); + this.wxCpService.getDepartmentService().update(this.depart); + } + + @Test(dependsOnMethods = "testUpdate") + public void testDelete() throws Exception { + System.out.println("=================删除部门"); + System.out.println(this.depart.getId() + ":" + this.depart.getName()); + this.wxCpService.getDepartmentService().delete(this.depart.getId()); + } + +} diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpMediaServiceImplTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpMediaServiceImplTest.java new file mode 100644 index 0000000000..4bdb73413d --- /dev/null +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpMediaServiceImplTest.java @@ -0,0 +1,78 @@ +package me.chanjar.weixin.cp.api.impl; + +import com.google.inject.Inject; +import me.chanjar.weixin.common.api.WxConsts; +import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; +import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.cp.api.ApiTestModule; +import me.chanjar.weixin.cp.api.TestConstants; +import me.chanjar.weixin.cp.api.WxCpService; +import org.testng.annotations.*; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +import static org.testng.Assert.*; + +/** + *
+ *
+ * Created by Binary Wang on 2017-6-25.
+ * @author Binary Wang
+ * 
+ */ +@Guice(modules = ApiTestModule.class) +public class WxCpMediaServiceImplTest { + @Inject + private WxCpService wxService; + + private List mediaIds = new ArrayList<>(); + + @DataProvider + public Object[][] mediaData() { + return new Object[][]{ + new Object[]{WxConsts.MEDIA_IMAGE, TestConstants.FILE_JPG, "mm.jpeg"}, + new Object[]{WxConsts.MEDIA_VOICE, TestConstants.FILE_MP3, "mm.mp3"}, + new Object[]{WxConsts.MEDIA_VOICE, TestConstants.FILE_AMR, "mm.amr"},//{"errcode":301017,"errmsg":"voice file only support amr like myvoice.amr"} + new Object[]{WxConsts.MEDIA_VIDEO, TestConstants.FILE_MP4, "mm.mp4"}, + new Object[]{WxConsts.MEDIA_FILE, TestConstants.FILE_JPG, "mm.jpeg"} + }; + } + + @Test(dataProvider = "mediaData") + public void testUploadMedia(String mediaType, String fileType, String fileName) throws WxErrorException, IOException { + try (InputStream inputStream = ClassLoader.getSystemResourceAsStream(fileName)) { + WxMediaUploadResult res = this.wxService.getMediaService().upload(mediaType, fileType, inputStream); + assertNotNull(res.getType()); + assertNotNull(res.getCreatedAt()); + assertTrue(res.getMediaId() != null || res.getThumbMediaId() != null); + + if (res.getMediaId() != null) { + this.mediaIds.add(res.getMediaId()); + } + if (res.getThumbMediaId() != null) { + this.mediaIds.add(res.getThumbMediaId()); + } + } + } + + @DataProvider + public Object[][] downloadMedia() { + Object[][] params = new Object[this.mediaIds.size()][]; + for (int i = 0; i < this.mediaIds.size(); i++) { + params[i] = new Object[]{this.mediaIds.get(i)}; + } + return params; + } + + @Test(dependsOnMethods = {"testUploadMedia"}, dataProvider = "downloadMedia") + public void testDownloadMedia(String media_id) throws WxErrorException { + File file = this.wxService.getMediaService().download(media_id); + assertNotNull(file); + System.out.println(file); + } + +} diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxMenuAPITest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpMenuServiceImplTest.java similarity index 63% rename from weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxMenuAPITest.java rename to weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpMenuServiceImplTest.java index cdeeadf2c4..def6419820 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxMenuAPITest.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpMenuServiceImplTest.java @@ -1,43 +1,29 @@ -package me.chanjar.weixin.cp.api; +package me.chanjar.weixin.cp.api.impl; import com.google.inject.Inject; import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.common.bean.menu.WxMenu; import me.chanjar.weixin.common.bean.menu.WxMenuButton; -import me.chanjar.weixin.common.exception.WxErrorException; -import me.chanjar.weixin.cp.api.impl.apache.WxCpServiceImpl; -import org.testng.*; +import me.chanjar.weixin.cp.api.ApiTestModule; +import me.chanjar.weixin.cp.api.WxCpService; import org.testng.annotations.*; +import static org.testng.Assert.*; + /** - * 测试菜单 + *
  *
- * @author Daniel Qian
+ * Created by Binary Wang on 2017-6-25.
+ * @author Binary Wang
+ * 
*/ -@Test(groups = "menuAPI", dependsOnGroups = "baseAPI") @Guice(modules = ApiTestModule.class) -public class WxMenuAPITest { - +public class WxCpMenuServiceImplTest { @Inject - protected WxCpServiceImpl wxService; - - @Test(dataProvider = "menu") - public void testCreateMenu(WxMenu wxMenu) throws WxErrorException { - this.wxService.menuCreate(wxMenu); - } + protected WxCpService wxService; - @Test(dependsOnMethods = {"testCreateMenu"}) - public void testGetMenu() throws WxErrorException { - Assert.assertNotNull(this.wxService.menuGet()); - } - - @Test(dependsOnMethods = {"testGetMenu"}) - public void testDeleteMenu() throws WxErrorException { - this.wxService.menuDelete(); - } - - @DataProvider(name = "menu") - public Object[][] getMenu() { + @DataProvider + public Object[][] menuData() { WxMenu menu = new WxMenu(); WxMenuButton button1 = new WxMenuButton(); button1.setType(WxConsts.BUTTON_CLICK); @@ -83,5 +69,21 @@ public Object[][] getMenu() { } + @Test(dataProvider = "menuData") + public void testCreate(WxMenu wxMenu) throws Exception { + this.wxService.getMenuService().create(wxMenu); + } + + @Test(dependsOnMethods = "testCreate") + public void testGet() throws Exception { + WxMenu menu = this.wxService.getMenuService().get(); + assertNotNull(menu); + System.out.println(menu.toJson()); + } + + @Test(dependsOnMethods = {"testGet", "testCreate"}) + public void testDelete() throws Exception { + this.wxService.getMenuService().delete(); + } } diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpTagServiceImplTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpTagServiceImplTest.java new file mode 100644 index 0000000000..94ac3b09db --- /dev/null +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpTagServiceImplTest.java @@ -0,0 +1,75 @@ +package me.chanjar.weixin.cp.api.impl; + +import com.google.common.base.Splitter; +import com.google.inject.Inject; +import me.chanjar.weixin.cp.api.ApiTestModule; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.bean.WxCpTag; +import me.chanjar.weixin.cp.bean.WxCpTagAddOrRemoveUsersResult; +import me.chanjar.weixin.cp.bean.WxCpUser; +import org.testng.annotations.*; + +import java.util.List; + +import static org.testng.Assert.*; + +/** + *
+ *
+ * Created by Binary Wang on 2017-6-25.
+ * @author Binary Wang
+ * 
+ */ +@Guice(modules = ApiTestModule.class) +public class WxCpTagServiceImplTest { + @Inject + protected WxCpService wxService; + + @Inject + protected ApiTestModule.WxXmlCpInMemoryConfigStorage configStorage; + + protected String tagId; + + @Test + public void testCreate() throws Exception { + this.tagId = this.wxService.getTagService().create("测试标签" + System.currentTimeMillis()); + System.out.println(this.tagId); + } + + @Test(dependsOnMethods = "testCreate") + public void testUpdate() throws Exception { + this.wxService.getTagService().update(this.tagId, "测试标签-改名" + System.currentTimeMillis()); + } + + @Test(dependsOnMethods = {"testUpdate", "testCreate"}) + public void testListAll() throws Exception { + List tags = this.wxService.getTagService().listAll(); + assertNotEquals(tags.size(), 0); + } + + @Test(dependsOnMethods = {"testListAll", "testUpdate", "testCreate"}) + public void testAddUsers2Tag() throws Exception { + List userIds = Splitter.on("|").splitToList(this.configStorage.getUserId()); + WxCpTagAddOrRemoveUsersResult result = this.wxService.getTagService().addUsers2Tag(this.tagId, userIds, null); + assertEquals(result.getErrCode(), Integer.valueOf(0)); + } + + @Test(dependsOnMethods = {"testAddUsers2Tag", "testListAll", "testUpdate", "testCreate"}) + public void testListUsersByTagId() throws Exception { + List users = this.wxService.getTagService().listUsersByTagId(this.tagId); + assertNotEquals(users.size(), 0); + } + + @Test(dependsOnMethods = {"testListUsersByTagId", "testAddUsers2Tag", "testListAll", "testUpdate", "testCreate"}) + public void testRemoveUsersFromTag() throws Exception { + List userIds = Splitter.on("|").splitToList(this.configStorage.getUserId()); + WxCpTagAddOrRemoveUsersResult result = this.wxService.getTagService().removeUsersFromTag(this.tagId, userIds); + assertEquals(result.getErrCode(), Integer.valueOf(0)); + } + + @Test(dependsOnMethods = {"testRemoveUsersFromTag", "testListUsersByTagId", "testAddUsers2Tag", "testListAll", "testUpdate", "testCreate"}) + public void testDelete() throws Exception { + this.wxService.getTagService().delete(this.tagId); + } + +} diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpUserServiceImplTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpUserServiceImplTest.java new file mode 100644 index 0000000000..a01f03436e --- /dev/null +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpUserServiceImplTest.java @@ -0,0 +1,86 @@ +package me.chanjar.weixin.cp.api.impl; + +import com.google.inject.Inject; +import me.chanjar.weixin.cp.api.ApiTestModule; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.bean.WxCpUser; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import org.testng.annotations.*; + +import java.util.List; + +import static org.testng.Assert.*; + +/** + *
+ *  Created by BinaryWang on 2017/6/24.
+ * 
+ * + * @author Binary Wang + */ +@Guice(modules = ApiTestModule.class) +public class WxCpUserServiceImplTest { + @Inject + private WxCpService wxCpService; + private String userId = "someone" + System.currentTimeMillis(); + + @Test + public void testAuthenticate() throws Exception { + this.wxCpService.getUserService().authenticate("abc"); + } + + @Test + public void testCreate() throws Exception { + WxCpUser user = new WxCpUser(); + user.setUserId(userId); + user.setName("Some Woman"); + user.setDepartIds(new Integer[]{2}); + user.setEmail("none@none.com"); + user.setGender(WxCpUser.Gender.FEMAIL); + user.setMobile("13560084979"); + user.setPosition("woman"); + user.setTelephone("3300393"); + user.addExtAttr("爱好", "table"); + this.wxCpService.getUserService().create(user); + } + + @Test(dependsOnMethods = "testCreate") + public void testUpdate() throws Exception { + WxCpUser user = new WxCpUser(); + user.setUserId(userId); + user.setName("Some Woman"); + user.addExtAttr("爱好", "table2"); + this.wxCpService.getUserService().update(user); + } + + @Test(dependsOnMethods = {"testCreate", "testUpdate"}) + public void testDelete() throws Exception { + this.wxCpService.getUserService().delete(userId); + } + + @Test(dependsOnMethods = "testUpdate") + public void testGetById() throws Exception { + WxCpUser user = this.wxCpService.getUserService().getById(userId); + assertNotNull(user); + } + + @Test + public void testListByDepartment() throws Exception { + List users = this.wxCpService.getUserService().listByDepartment(1, true, 0); + assertNotEquals(users.size(), 0); + for (WxCpUser user : users) { + System.out.println(ToStringBuilder.reflectionToString(user, ToStringStyle.MULTI_LINE_STYLE)); + } + } + + @Test + public void testListSimpleByDepartment() throws Exception { + List users = this.wxCpService.getUserService().listSimpleByDepartment(1, true, 0); + assertNotEquals(users.size(), 0); + for (WxCpUser user : users) { + System.out.println(ToStringBuilder.reflectionToString(user, ToStringStyle.MULTI_LINE_STYLE)); + } + } + +} diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/apache/WxCpMessageAPITest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/apache/WxCpMessageAPITest.java deleted file mode 100644 index d6004dee75..0000000000 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/apache/WxCpMessageAPITest.java +++ /dev/null @@ -1,41 +0,0 @@ -package me.chanjar.weixin.cp.api.impl.apache; - -import com.google.inject.Inject; -import me.chanjar.weixin.common.api.WxConsts; -import me.chanjar.weixin.common.exception.WxErrorException; -import me.chanjar.weixin.cp.api.ApiTestModule; -import me.chanjar.weixin.cp.bean.WxCpMessage; -import org.testng.annotations.*; - -/*** - * 测试发送消息 - * @author Daniel Qian - * - */ -@Test(groups = "customMessageAPI", dependsOnGroups = "baseAPI") -@Guice(modules = ApiTestModule.class) -public class WxCpMessageAPITest { - - @Inject - protected WxCpServiceImpl wxService; - - public void testSendCustomMessage() throws WxErrorException { - ApiTestModule.WxXmlCpInMemoryConfigStorage configStorage = (ApiTestModule.WxXmlCpInMemoryConfigStorage) this.wxService.getWxCpConfigStorage(); - WxCpMessage message1 = new WxCpMessage(); - message1.setAgentId(configStorage.getAgentId()); - message1.setMsgType(WxConsts.CUSTOM_MSG_TEXT); - message1.setToUser(configStorage.getUserId()); - message1.setContent("欢迎欢迎,热烈欢迎\n换行测试\n超链接:Hello World"); - this.wxService.messageSend(message1); - - WxCpMessage message2 = WxCpMessage - .TEXT() - .agentId(configStorage.getAgentId()) - .toUser(configStorage.getUserId()) - .content("欢迎欢迎,热烈欢迎\n换行测试\n超链接:Hello World") - .build(); - this.wxService.messageSend(message2); - - } - -} diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpMessageTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpMessageTest.java index cb3d2cdd29..e02cc6672d 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpMessageTest.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpMessageTest.java @@ -1,6 +1,5 @@ package me.chanjar.weixin.cp.bean; -import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.cp.bean.article.MpnewsArticle; import me.chanjar.weixin.cp.bean.article.NewArticle; import org.testng.annotations.*; @@ -10,82 +9,32 @@ @Test public class WxCpMessageTest { - public void testTextReply() { - WxCpMessage reply = new WxCpMessage(); - reply.setToUser("OPENID"); - reply.setMsgType(WxConsts.CUSTOM_MSG_TEXT); - reply.setContent("sfsfdsdf"); - assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"text\",\"text\":{\"content\":\"sfsfdsdf\"}}"); - } - public void testTextBuild() { WxCpMessage reply = WxCpMessage.TEXT().toUser("OPENID").content("sfsfdsdf").build(); - assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"text\",\"text\":{\"content\":\"sfsfdsdf\"}}"); + assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"text\",\"text\":{\"content\":\"sfsfdsdf\"},\"safe\":\"0\"}"); } - public void testImageReply() { - WxCpMessage reply = new WxCpMessage(); - reply.setToUser("OPENID"); - reply.setMsgType(WxConsts.CUSTOM_MSG_IMAGE); - reply.setMediaId("MEDIA_ID"); - assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"image\",\"image\":{\"media_id\":\"MEDIA_ID\"}}"); + public void testTextCardBuild() { + WxCpMessage reply = WxCpMessage.TEXTCARD().toUser("OPENID") + .title("领奖通知") + .description( "
2016年9月26日
恭喜你抽中iPhone 7一台,领奖码:xxxx
请于2016年10月10日前联系行政同事领取
") + .url("http://www.qq.com").build(); + assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"textcard\",\"textcard\":{\"title\":\"领奖通知\",\"description\":\"
2016年9月26日
恭喜你抽中iPhone 7一台,领奖码:xxxx
请于2016年10月10日前联系行政同事领取
\",\"url\":\"http://www.qq.com\"},\"safe\":\"0\"}"); } public void testImageBuild() { WxCpMessage reply = WxCpMessage.IMAGE().toUser("OPENID").mediaId("MEDIA_ID").build(); - assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"image\",\"image\":{\"media_id\":\"MEDIA_ID\"}}"); - } - - public void testVoiceReply() { - WxCpMessage reply = new WxCpMessage(); - reply.setToUser("OPENID"); - reply.setMsgType(WxConsts.CUSTOM_MSG_VOICE); - reply.setMediaId("MEDIA_ID"); - assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"voice\",\"voice\":{\"media_id\":\"MEDIA_ID\"}}"); + assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"image\",\"image\":{\"media_id\":\"MEDIA_ID\"},\"safe\":\"0\"}"); } public void testVoiceBuild() { WxCpMessage reply = WxCpMessage.VOICE().toUser("OPENID").mediaId("MEDIA_ID").build(); - assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"voice\",\"voice\":{\"media_id\":\"MEDIA_ID\"}}"); - } - - public void testVideoReply() { - WxCpMessage reply = new WxCpMessage(); - reply.setToUser("OPENID"); - reply.setMsgType(WxConsts.CUSTOM_MSG_VIDEO); - reply.setMediaId("MEDIA_ID"); - reply.setThumbMediaId("MEDIA_ID"); - reply.setTitle("TITLE"); - reply.setDescription("DESCRIPTION"); - assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"video\",\"video\":{\"media_id\":\"MEDIA_ID\",\"thumb_media_id\":\"MEDIA_ID\",\"title\":\"TITLE\",\"description\":\"DESCRIPTION\"}}"); + assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"voice\",\"voice\":{\"media_id\":\"MEDIA_ID\"},\"safe\":\"0\"}"); } public void testVideoBuild() { WxCpMessage reply = WxCpMessage.VIDEO().toUser("OPENID").title("TITLE").mediaId("MEDIA_ID").thumbMediaId("MEDIA_ID").description("DESCRIPTION").build(); - assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"video\",\"video\":{\"media_id\":\"MEDIA_ID\",\"thumb_media_id\":\"MEDIA_ID\",\"title\":\"TITLE\",\"description\":\"DESCRIPTION\"}}"); - } - - public void testNewsReply() { - WxCpMessage reply = new WxCpMessage(); - reply.setToUser("OPENID"); - reply.setMsgType(WxConsts.CUSTOM_MSG_NEWS); - - NewArticle article1 = new NewArticle(); - article1.setUrl("URL"); - article1.setPicUrl("PIC_URL"); - article1.setDescription("Is Really A Happy Day"); - article1.setTitle("Happy Day"); - reply.getArticles().add(article1); - - NewArticle article2 = new NewArticle(); - article2.setUrl("URL"); - article2.setPicUrl("PIC_URL"); - article2.setDescription("Is Really A Happy Day"); - article2.setTitle("Happy Day"); - reply.getArticles().add(article2); - - - assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"news\",\"news\":{\"articles\":[{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"},{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}]}}"); + assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"video\",\"safe\":\"0\",\"video\":{\"media_id\":\"MEDIA_ID\",\"thumb_media_id\":\"MEDIA_ID\",\"title\":\"TITLE\",\"description\":\"DESCRIPTION\"}}"); } public void testNewsBuild() { @@ -103,7 +52,7 @@ public void testNewsBuild() { WxCpMessage reply = WxCpMessage.NEWS().toUser("OPENID").addArticle(article1).addArticle(article2).build(); - assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"news\",\"news\":{\"articles\":[{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"},{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}]}}"); + assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"news\",\"safe\":\"0\",\"news\":{\"articles\":[{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"},{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}]}}"); } public void testMpnewsBuild_with_articles() { @@ -127,18 +76,16 @@ public void testMpnewsBuild_with_articles() { .thumbMediaId("thumb") .build(); - WxCpMessage reply = WxCpMessage.MPNEWS().toUser("OPENID").addArticle(article1).addArticle(article2).build(); + WxCpMessage reply = WxCpMessage.MPNEWS().toUser("OPENID").addArticle(article1, article2).build(); - assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"mpnews\"," + - "\"mpnews\":{\"articles\":[{\"title\":\"Happy Day\",\"thumb_media_id\":\"thumb\",\"author\":\"aaaaaa\",\"content_source_url\":\"nice url\",\"content\":\"hahaha\",\"digest\":\"digest\",\"show_cover_pic\":\"heihei\"}," + - "{\"title\":\"Happy Day\",\"thumb_media_id\":\"thumb\",\"author\":\"aaaaaa\",\"content_source_url\":\"nice url\",\"content\":\"hahaha\",\"digest\":\"digest\",\"show_cover_pic\":\"heihei\"}]}}"); + assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"mpnews\",\"safe\":\"0\",\"mpnews\":{\"articles\":[{\"title\":\"Happy Day\",\"thumb_media_id\":\"thumb\",\"author\":\"aaaaaa\",\"content_source_url\":\"nice url\",\"content\":\"hahaha\",\"digest\":\"digest\",\"show_cover_pic\":\"heihei\"},{\"title\":\"Happy Day\",\"thumb_media_id\":\"thumb\",\"author\":\"aaaaaa\",\"content_source_url\":\"nice url\",\"content\":\"hahaha\",\"digest\":\"digest\",\"show_cover_pic\":\"heihei\"}]}}"); } public void testMpnewsBuild_with_media_id() { WxCpMessage reply = WxCpMessage.MPNEWS().toUser("OPENID").mediaId("mmm").build(); assertEquals(reply.toJson(), - "{\"touser\":\"OPENID\",\"msgtype\":\"mpnews\",\"mpnews\":{\"media_id\":\"mmm\"}}"); + "{\"touser\":\"OPENID\",\"msgtype\":\"mpnews\",\"safe\":\"0\",\"mpnews\":{\"media_id\":\"mmm\"}}"); } } diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlMessageTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlMessageTest.java index 9a6eedf1af..87e16d773a 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlMessageTest.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlMessageTest.java @@ -1,9 +1,10 @@ package me.chanjar.weixin.cp.bean; import me.chanjar.weixin.common.api.WxConsts; -import org.testng.*; import org.testng.annotations.*; +import static org.testng.Assert.*; + @Test public class WxCpXmlMessageTest { @@ -54,38 +55,66 @@ public void testFromXml() { + "" + ""; WxCpXmlMessage wxMessage = WxCpXmlMessage.fromXml(xml); - Assert.assertEquals(wxMessage.getToUserName(), "toUser"); - Assert.assertEquals(wxMessage.getFromUserName(), "fromUser"); - Assert.assertEquals(wxMessage.getCreateTime(), new Long(1348831860l)); - Assert.assertEquals(wxMessage.getMsgType(), WxConsts.XML_MSG_TEXT); - Assert.assertEquals(wxMessage.getContent(), "this is a test"); - Assert.assertEquals(wxMessage.getMsgId(), new Long(1234567890123456l)); - Assert.assertEquals(wxMessage.getPicUrl(), "this is a url"); - Assert.assertEquals(wxMessage.getMediaId(), "media_id"); - Assert.assertEquals(wxMessage.getFormat(), "Format"); - Assert.assertEquals(wxMessage.getThumbMediaId(), "thumb_media_id"); - Assert.assertEquals(wxMessage.getLocationX(), new Double(23.134521d)); - Assert.assertEquals(wxMessage.getLocationY(), new Double(113.358803d)); - Assert.assertEquals(wxMessage.getScale(), new Double(20)); - Assert.assertEquals(wxMessage.getLabel(), "位置信息"); - Assert.assertEquals(wxMessage.getDescription(), "公众平台官网链接"); - Assert.assertEquals(wxMessage.getUrl(), "url"); - Assert.assertEquals(wxMessage.getTitle(), "公众平台官网链接"); - Assert.assertEquals(wxMessage.getEvent(), "subscribe"); - Assert.assertEquals(wxMessage.getEventKey(), "qrscene_123123"); - Assert.assertEquals(wxMessage.getTicket(), "TICKET"); - Assert.assertEquals(wxMessage.getLatitude(), new Double(23.137466)); - Assert.assertEquals(wxMessage.getLongitude(), new Double(113.352425)); - Assert.assertEquals(wxMessage.getPrecision(), new Double(119.385040)); - Assert.assertEquals(wxMessage.getScanCodeInfo().getScanType(), "qrcode"); - Assert.assertEquals(wxMessage.getScanCodeInfo().getScanResult(), "1"); - Assert.assertEquals(wxMessage.getSendPicsInfo().getCount(), new Long(1l)); - Assert.assertEquals(wxMessage.getSendPicsInfo().getPicList().get(0).getPicMd5Sum(), "1b5f7c23b5bf75682a53e7b6d163e185"); - Assert.assertEquals(wxMessage.getSendLocationInfo().getLocationX(), "23"); - Assert.assertEquals(wxMessage.getSendLocationInfo().getLocationY(), "113"); - Assert.assertEquals(wxMessage.getSendLocationInfo().getScale(), "15"); - Assert.assertEquals(wxMessage.getSendLocationInfo().getLabel(), " 广州市海珠区客村艺苑路 106号"); - Assert.assertEquals(wxMessage.getSendLocationInfo().getPoiname(), "wo de poi"); + assertEquals(wxMessage.getToUserName(), "toUser"); + assertEquals(wxMessage.getFromUserName(), "fromUser"); + assertEquals(wxMessage.getCreateTime(), new Long(1348831860l)); + assertEquals(wxMessage.getMsgType(), WxConsts.XML_MSG_TEXT); + assertEquals(wxMessage.getContent(), "this is a test"); + assertEquals(wxMessage.getMsgId(), new Long(1234567890123456l)); + assertEquals(wxMessage.getPicUrl(), "this is a url"); + assertEquals(wxMessage.getMediaId(), "media_id"); + assertEquals(wxMessage.getFormat(), "Format"); + assertEquals(wxMessage.getThumbMediaId(), "thumb_media_id"); + assertEquals(wxMessage.getLocationX(), 23.134521d); + assertEquals(wxMessage.getLocationY(), 113.358803d); + assertEquals(wxMessage.getScale(), 20d); + assertEquals(wxMessage.getLabel(), "位置信息"); + assertEquals(wxMessage.getDescription(), "公众平台官网链接"); + assertEquals(wxMessage.getUrl(), "url"); + assertEquals(wxMessage.getTitle(), "公众平台官网链接"); + assertEquals(wxMessage.getEvent(), "subscribe"); + assertEquals(wxMessage.getEventKey(), "qrscene_123123"); + assertEquals(wxMessage.getTicket(), "TICKET"); + assertEquals(wxMessage.getLatitude(), 23.137466); + assertEquals(wxMessage.getLongitude(), 113.352425); + assertEquals(wxMessage.getPrecision(), 119.385040); + assertEquals(wxMessage.getScanCodeInfo().getScanType(), "qrcode"); + assertEquals(wxMessage.getScanCodeInfo().getScanResult(), "1"); + assertEquals(wxMessage.getSendPicsInfo().getCount(), new Long(1l)); + assertEquals(wxMessage.getSendPicsInfo().getPicList().get(0).getPicMd5Sum(), "1b5f7c23b5bf75682a53e7b6d163e185"); + assertEquals(wxMessage.getSendLocationInfo().getLocationX(), "23"); + assertEquals(wxMessage.getSendLocationInfo().getLocationY(), "113"); + assertEquals(wxMessage.getSendLocationInfo().getScale(), "15"); + assertEquals(wxMessage.getSendLocationInfo().getLabel(), " 广州市海珠区客村艺苑路 106号"); + assertEquals(wxMessage.getSendLocationInfo().getPoiname(), "wo de poi"); } + public void testSendPicsInfo() { + String xml = "" + + "" + + "" + + "1502012364" + + "" + + "1000004" + + "" + + "" + + "" + + "" + + "" + + "2" + + "" + + ""; + WxCpXmlMessage wxMessage = WxCpXmlMessage.fromXml(xml.replace("","")); + assertEquals(wxMessage.getToUserName(), "wx45a0972125658be9"); + assertEquals(wxMessage.getFromUserName(), "xiaohe"); + assertEquals(wxMessage.getCreateTime(), new Long(1502012364L)); + assertEquals(wxMessage.getMsgType(), WxConsts.XML_MSG_EVENT); + assertEquals(wxMessage.getAgentId(), Integer.valueOf(1000004)); + assertEquals(wxMessage.getEvent(), "pic_weixin"); + assertEquals(wxMessage.getEventKey(), "faceSimilarity"); + assertNotNull(wxMessage.getSendPicsInfo()); + assertEquals(wxMessage.getSendPicsInfo().getCount(), new Long(2L)); + assertEquals(wxMessage.getSendPicsInfo().getPicList().get(0).getPicMd5Sum(), "aef52ae501537e552725c5d7f99c1741"); + assertEquals(wxMessage.getSendPicsInfo().getPicList().get(1).getPicMd5Sum(), "c4564632a4fab91378c39bea6aad6f9e"); + } } diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutImageMessageTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutImageMessageTest.java index 2660243435..6f366988eb 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutImageMessageTest.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutImageMessageTest.java @@ -1,7 +1,7 @@ package me.chanjar.weixin.cp.bean; -import org.testng.*; -import org.testng.annotations.*; +import org.testng.Assert; +import org.testng.annotations.Test; @Test public class WxCpXmlOutImageMessageTest { diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutNewsMessageTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutNewsMessageTest.java index 2013e7a3ea..71dbf4125d 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutNewsMessageTest.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutNewsMessageTest.java @@ -1,7 +1,7 @@ package me.chanjar.weixin.cp.bean; -import org.testng.*; -import org.testng.annotations.*; +import org.testng.Assert; +import org.testng.annotations.Test; @Test public class WxCpXmlOutNewsMessageTest { diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutTextMessageTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutTextMessageTest.java index 975eb5d178..4d73b27f5b 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutTextMessageTest.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutTextMessageTest.java @@ -1,7 +1,7 @@ package me.chanjar.weixin.cp.bean; -import org.testng.*; -import org.testng.annotations.*; +import org.testng.Assert; +import org.testng.annotations.Test; @Test public class WxCpXmlOutTextMessageTest { diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutVideoMessageTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutVideoMessageTest.java index 5ad42ba76f..b4124c6130 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutVideoMessageTest.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutVideoMessageTest.java @@ -1,7 +1,7 @@ package me.chanjar.weixin.cp.bean; -import org.testng.*; -import org.testng.annotations.*; +import org.testng.Assert; +import org.testng.annotations.Test; @Test public class WxCpXmlOutVideoMessageTest { diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutVoiceMessageTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutVoiceMessageTest.java index 293796011d..f414256a5f 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutVoiceMessageTest.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlOutVoiceMessageTest.java @@ -1,7 +1,7 @@ package me.chanjar.weixin.cp.bean; -import org.testng.*; -import org.testng.annotations.*; +import org.testng.Assert; +import org.testng.annotations.Test; @Test public class WxCpXmlOutVoiceMessageTest { diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpDemoInMemoryConfigStorage.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpDemoInMemoryConfigStorage.java index 4171119be1..93ff1bbc0a 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpDemoInMemoryConfigStorage.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpDemoInMemoryConfigStorage.java @@ -3,7 +3,7 @@ import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.annotations.XStreamAlias; import me.chanjar.weixin.common.util.xml.XStreamInitializer; -import me.chanjar.weixin.cp.api.WxCpInMemoryConfigStorage; +import me.chanjar.weixin.cp.config.WxCpInMemoryConfigStorage; import java.io.InputStream; diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpDemoServer.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpDemoServer.java index 6862347ba0..a9ab309c37 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpDemoServer.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpDemoServer.java @@ -1,14 +1,14 @@ package me.chanjar.weixin.cp.demo; import me.chanjar.weixin.common.session.WxSessionManager; -import me.chanjar.weixin.cp.api.WxCpConfigStorage; -import me.chanjar.weixin.cp.api.WxCpMessageHandler; -import me.chanjar.weixin.cp.api.WxCpMessageRouter; import me.chanjar.weixin.cp.api.WxCpService; -import me.chanjar.weixin.cp.api.impl.apache.WxCpServiceImpl; +import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl; import me.chanjar.weixin.cp.bean.WxCpXmlMessage; import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage; import me.chanjar.weixin.cp.bean.WxCpXmlOutTextMessage; +import me.chanjar.weixin.cp.config.WxCpConfigStorage; +import me.chanjar.weixin.cp.message.WxCpMessageHandler; +import me.chanjar.weixin.cp.message.WxCpMessageRouter; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletHandler; import org.eclipse.jetty.servlet.ServletHolder; @@ -69,8 +69,7 @@ public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map context, WxCpService wxService, WxSessionManager sessionManager) { String href = "测试oauth2"; return WxCpXmlOutMessage.TEXT().content(href) .fromUser(wxMessage.getToUserName()) diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpEndpointServlet.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpEndpointServlet.java index dec6faee6a..291cef403d 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpEndpointServlet.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpEndpointServlet.java @@ -1,7 +1,7 @@ package me.chanjar.weixin.cp.demo; -import me.chanjar.weixin.cp.api.WxCpConfigStorage; -import me.chanjar.weixin.cp.api.WxCpMessageRouter; +import me.chanjar.weixin.cp.config.WxCpConfigStorage; +import me.chanjar.weixin.cp.message.WxCpMessageRouter; import me.chanjar.weixin.cp.api.WxCpService; import me.chanjar.weixin.cp.bean.WxCpXmlMessage; import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage; diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpOAuth2Servlet.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpOAuth2Servlet.java index d65e242702..37c2b7a12f 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpOAuth2Servlet.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpOAuth2Servlet.java @@ -30,7 +30,7 @@ protected void service(HttpServletRequest request, HttpServletResponse response) response.getWriter().println("

code

"); response.getWriter().println(code); - String[] res = this.wxCpService.oauth2getUserInfo(code); + String[] res = this.wxCpService.getOauth2Service().getUserInfo(code); response.getWriter().println("

result

"); response.getWriter().println(Arrays.toString(res)); } catch (WxErrorException e) { diff --git a/weixin-java-cp/src/test/resources/logback-test.xml b/weixin-java-cp/src/test/resources/logback-test.xml index 998a68962d..e4a33acd88 100644 --- a/weixin-java-cp/src/test/resources/logback-test.xml +++ b/weixin-java-cp/src/test/resources/logback-test.xml @@ -1,16 +1,13 @@ - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + %d{HH:mm:ss.SSS} [%thread] %-5level %replace(%caller{1}){'Caller', ''} - %msg%n - + - diff --git a/weixin-java-cp/src/test/resources/mm.amr b/weixin-java-cp/src/test/resources/mm.amr new file mode 100644 index 0000000000..a4df01a0d9 Binary files /dev/null and b/weixin-java-cp/src/test/resources/mm.amr differ diff --git a/weixin-java-cp/src/test/resources/testng.xml b/weixin-java-cp/src/test/resources/testng.xml index 04f960ed63..ffa8a2edff 100644 --- a/weixin-java-cp/src/test/resources/testng.xml +++ b/weixin-java-cp/src/test/resources/testng.xml @@ -3,15 +3,10 @@ - - - - - - + + + - - diff --git a/weixin-java-miniapp/pom.xml b/weixin-java-miniapp/pom.xml new file mode 100644 index 0000000000..11ab7d392e --- /dev/null +++ b/weixin-java-miniapp/pom.xml @@ -0,0 +1,84 @@ + + + 4.0.0 + + com.github.binarywang + weixin-java-parent + 2.7.8.BETA + + weixin-java-miniapp + WeiXin Java Tools - MiniApp + 微信小程序Java SDK + + + + com.github.binarywang + weixin-java-common + ${project.version} + + + + org.jodd + jodd-http + provided + + + com.squareup.okhttp3 + okhttp + provided + + + + org.testng + testng + test + + + ch.qos.logback + logback-classic + test + + + com.google.inject + guice + test + + + org.eclipse.jetty + jetty-server + test + + + org.eclipse.jetty + jetty-servlet + test + + + joda-time + joda-time + test + + + redis.clients + jedis + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + src/test/resources/testng.xml + + + + + + + diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaMediaService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaMediaService.java new file mode 100644 index 0000000000..09c8aa5b9d --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaMediaService.java @@ -0,0 +1,65 @@ +package cn.binarywang.wx.miniapp.api; + +import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; +import me.chanjar.weixin.common.exception.WxErrorException; + +import java.io.File; +import java.io.InputStream; + +/** + *
+ * 临时素材接口
+ * Created by Binary Wang on 2016/7/21.
+ * 
+ * + * @author Binary Wang + */ +public interface WxMaMediaService { + String MEDIA_UPLOAD_URL = "https://api.weixin.qq.com/cgi-bin/media/upload?type=%s"; + String MEDIA_GET_URL = "https://api.weixin.qq.com/cgi-bin/media/get"; + + /** + *
+   * 新增临时素材
+   * 小程序可以使用本接口把媒体文件(目前仅支持图片)上传到微信服务器,用户发送客服消息或被动回复用户消息。
+   * 详情请见: 新增临时素材
+   * 接口url格式:https://api.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=TYPE
+   * 
+ * + * @param mediaType 媒体类型, + * @param file 文件对象 + * @see #uploadMedia(String, String, InputStream) + */ + WxMediaUploadResult uploadMedia(String mediaType, File file) throws WxErrorException; + + /** + *
+   * 新增临时素材
+   * 小程序可以使用本接口把媒体文件(目前仅支持图片)上传到微信服务器,用户发送客服消息或被动回复用户消息。
+   *
+   * 详情请见: 新增临时素材
+   * 接口url格式:https://api.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=TYPE
+   * 
+ * + * @param mediaType 媒体类型 + * @param fileType 文件类型 + * @param inputStream 输入流 + * @see #uploadMedia(java.lang.String, java.io.File) + */ + WxMediaUploadResult uploadMedia(String mediaType, String fileType, InputStream inputStream) throws WxErrorException; + + /** + *
+   * 获取临时素材
+   * 小程序可以使用本接口获取客服消息内的临时素材(即下载临时的多媒体文件)。目前小程序仅支持下载图片文件。
+   *
+   * 详情请见: 获取临时素材
+   * 接口url格式:https://api.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_ID
+   * 
+ * + * @param mediaId 媒体Id + * @return 保存到本地的临时文件 + */ + File getMedia(String mediaId) throws WxErrorException; + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaMsgService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaMsgService.java new file mode 100644 index 0000000000..65522f4b75 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaMsgService.java @@ -0,0 +1,35 @@ +package cn.binarywang.wx.miniapp.api; + +import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage; +import cn.binarywang.wx.miniapp.bean.WxMaTemplateMessage; +import me.chanjar.weixin.common.exception.WxErrorException; + +/** + *
+ * 消息发送接口
+ * 
+ * + * @author Binary Wang + */ +public interface WxMaMsgService { + String KEFU_MESSAGE_SEND_URL = "https://api.weixin.qq.com/cgi-bin/message/custom/send"; + String TEMPLATE_MSG_SEND_URL = "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send"; + + /** + *
+   * 发送客服消息
+   * 详情请见: 发送客服消息
+   * 接口url格式:https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN
+   * 
+ */ + boolean sendKefuMsg(WxMaKefuMessage message) throws WxErrorException; + + /** + *
+   * 发送模板消息
+   * 详情请见: 发送模板消息
+   * 接口url格式:https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=ACCESS_TOKEN
+   * 
+ */ + void sendTemplateMsg(WxMaTemplateMessage templateMessage) throws WxErrorException; +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaQrcodeService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaQrcodeService.java new file mode 100644 index 0000000000..c2222bc93e --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaQrcodeService.java @@ -0,0 +1,113 @@ +package cn.binarywang.wx.miniapp.api; + +import me.chanjar.weixin.common.exception.WxErrorException; + +import java.io.File; + +/** + *
+ * 二维码相关操作接口
+ *
+ * 接口A(createWxCode)加上接口C(createQrcode),总共生成的码数量限制为100,000,请谨慎调用。
+ *
+ * 文档地址:https://mp.weixin.qq.com/debug/wxadoc/dev/api/qrcode.html
+ * 
+ * + * @author Binary Wang + */ +public interface WxMaQrcodeService { + + /** + * 接口C + *
+   * 获取小程序页面二维码
+   * 适用于需要的码数量较少的业务场景
+   * 通过该接口,仅能生成已发布的小程序的二维码。
+   * 可以在开发者工具预览时生成开发版的带参二维码。
+   * 带参二维码只有 100000 个,请谨慎调用。
+   * 
+ * + * @param path 不能为空,最大长度 128 字节 + * @param width 默认430 二维码的宽度 + */ + File createQrcode(String path, int width) throws WxErrorException; + + File createQrcode(String path) throws WxErrorException; + + /** + * 接口A + * 获取小程序码 + * + * @param path 不能为空,最大长度 128 字节 + * @param width 默认430 二维码的宽度 + * @param autoColor 默认true 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调 + * @param lineColor auth_color 为 false 时生效,使用 rgb 设置颜色 例如 {"r":"xxx","g":"xxx","b":"xxx"} + * @return + * @throws WxErrorException + */ + File createWxCode(String path, int width, boolean autoColor, LineColor lineColor) throws WxErrorException; + + File createWxCode(String path, int width) throws WxErrorException; + + File createWxCode(String path) throws WxErrorException; + + /** + * 接口B + * 获取小程序码(永久有效、数量暂无限制) + *

+ * 通过该接口生成的小程序码,永久有效,数量暂无限制。 + * 用户扫描该码进入小程序后,将统一打开首页,开发者需在对应页面根据获取的码中 scene 字段的值,再做处理逻辑。 + * 使用如下代码可以获取到二维码中的 scene 字段的值。 + * 调试阶段可以使用开发工具的条件编译自定义参数 scene=xxxx 进行模拟,开发工具模拟时的 scene 的参数值需要进行 urlencode + * + * @param scene 最大32个可见字符,只支持数字,大小写英文以及部分特殊字符:!#$&'()*+,/:;=?@-._~,其它字符请自行编码为合法字符(因不支持%,中文无法使用 urlencode 处理,请使用其他编码方式) + * @param page 必须是已经发布的小程序页面,例如 "pages/index/index" ,如果不填写这个字段,默认跳主页面 + * @param width 默认false 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调 + * @param autoColor 默认true 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调 + * @param lineColor auth_color 为 false 时生效,使用 rgb 设置颜色 例如 {"r":"xxx","g":"xxx","b":"xxx"} + * @return + * @throws WxErrorException + */ + File createWxCodeLimit(String scene, String page, int width, boolean autoColor, LineColor lineColor) throws WxErrorException; + + File createWxCodeLimit(String scene, String page) throws WxErrorException; + + /** + * lineColor 包装类 + * 用于描述二维码(小程序码)颜色(RGB参数值),详情请查看文档 + */ + public static class LineColor { + + private String r = "0", g = "0", b = "0"; + + public LineColor(String r, String g, String b) { + this.r = r; + this.g = g; + this.b = b; + } + + public String getR() { + return r; + } + + public void setR(String r) { + this.r = r; + } + + public String getG() { + return g; + } + + public void setG(String g) { + this.g = g; + } + + public String getB() { + return b; + } + + public void setB(String b) { + this.b = b; + } + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaService.java new file mode 100644 index 0000000000..9098d4ac48 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaService.java @@ -0,0 +1,135 @@ +package cn.binarywang.wx.miniapp.api; + +import cn.binarywang.wx.miniapp.config.WxMaConfig; +import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; +import me.chanjar.weixin.common.util.http.RequestExecutor; +import me.chanjar.weixin.common.util.http.RequestHttp; + +/** + * @author Binary Wang + */ +public interface WxMaService { + /** + * 获取access_token + */ + String GET_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s"; + + /** + *

+   * 验证消息的确来自微信服务器
+   * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421135319&token=&lang=zh_CN
+   * 
+ */ + boolean checkSignature(String timestamp, String nonce, String signature); + + /** + * 获取access_token, 不强制刷新access_token + * + * @see #getAccessToken(boolean) + */ + String getAccessToken() throws WxErrorException; + + /** + *
+   * 获取access_token,本方法线程安全
+   * 且在多线程同时刷新时只刷新一次,避免超出2000次/日的调用次数上限
+   *
+   * 另:本service的所有方法都会在access_token过期是调用此方法
+   *
+   * 程序员在非必要情况下尽量不要主动调用此方法
+   *
+   * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140183&token=&lang=zh_CN
+   * 
+ * + * @param forceRefresh 强制刷新 + */ + String getAccessToken(boolean forceRefresh) throws WxErrorException; + + /** + * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求 + */ + String get(String url, String queryParam) throws WxErrorException; + + /** + * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的POST请求 + */ + String post(String url, String postData) throws WxErrorException; + + /** + *
+   * Service没有实现某个API的时候,可以用这个,
+   * 比{@link #get}和{@link #post}方法更灵活,可以自己构造RequestExecutor用来处理不同的参数和不同的返回类型。
+   * 可以参考,{@link MediaUploadRequestExecutor}的实现方法
+   * 
+ */ + T execute(RequestExecutor executor, String uri, E data) throws WxErrorException; + + /** + *
+   * 设置当微信系统响应系统繁忙时,要等待多少 retrySleepMillis(ms) * 2^(重试次数 - 1) 再发起重试
+   * 默认:1000ms
+   * 
+ */ + void setRetrySleepMillis(int retrySleepMillis); + + /** + *
+   * 设置当微信系统响应系统繁忙时,最大重试次数
+   * 默认:5次
+   * 
+ */ + void setMaxRetryTimes(int maxRetryTimes); + + /** + * 获取WxMaConfig 对象 + * + * @return WxMaConfig + */ + WxMaConfig getWxMaConfig(); + + /** + * 注入 {@link WxMaConfig} 的实现 + */ + void setWxMaConfig(WxMaConfig wxConfigProvider); + + /** + * 返回消息(客服消息和模版消息)发送接口方法实现类,以方便调用其各个接口 + * + * @return WxMaMsgService + */ + WxMaMsgService getMsgService(); + + /** + * 返回素材相关接口方法的实现类对象,以方便调用其各个接口 + * + * @return WxMaMediaService + */ + WxMaMediaService getMediaService(); + + /** + * 返回用户相关接口方法的实现类对象,以方便调用其各个接口 + * + * @return WxMaUserService + */ + WxMaUserService getUserService(); + + /** + * 返回二维码相关接口方法的实现类对象,以方便调用其各个接口 + * + * @return WxMaQrcodeService + */ + WxMaQrcodeService getQrcodeService(); + + /** + * 初始化http请求对象 + */ + void initHttp(); + + /** + * 请求http请求相关信息 + */ + RequestHttp getRequestHttp(); + + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaUserService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaUserService.java new file mode 100644 index 0000000000..84b17378ca --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaUserService.java @@ -0,0 +1,39 @@ +package cn.binarywang.wx.miniapp.api; + +import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; +import cn.binarywang.wx.miniapp.bean.WxMaUserInfo; +import me.chanjar.weixin.common.exception.WxErrorException; + +/** + * 用户信息相关操作接口 + * + * @author Binary Wang + */ +public interface WxMaUserService { + String JSCODE_TO_SESSION_URL = "https://api.weixin.qq.com/sns/jscode2session"; + + /** + * 获取登录后的session信息 + * + * @param jsCode 登录时获取的 code + */ + WxMaJscode2SessionResult getSessionInfo(String jsCode) throws WxErrorException; + + /** + * 解密用户敏感数据 + * + * @param sessionKey 会话密钥 + * @param encryptedData 消息密文 + * @param ivStr 加密算法的初始向量 + */ + WxMaUserInfo getUserInfo(String sessionKey, String encryptedData, String ivStr); + + /** + * 验证用户信息完整性 + * + * @param sessionKey 会话密钥 + * @param rawData 微信用户基本信息 + * @param signature 数据签名 + */ + boolean checkUserInfo(String sessionKey, String rawData, String signature); +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaMediaServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaMediaServiceImpl.java new file mode 100644 index 0000000000..66ad3c2a91 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaMediaServiceImpl.java @@ -0,0 +1,61 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaMediaService; +import cn.binarywang.wx.miniapp.api.WxMaService; +import me.chanjar.weixin.common.bean.result.WxError; +import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; +import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.util.fs.FileUtils; +import me.chanjar.weixin.common.util.http.MediaDownloadRequestExecutor; +import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; +import me.chanjar.weixin.common.util.http.RequestExecutor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.util.UUID; + +/** + * @author Binary Wang + */ +public class WxMaMediaServiceImpl implements WxMaMediaService { + private final Logger log = LoggerFactory.getLogger(this.getClass()); + + private WxMaService wxMaService; + + public WxMaMediaServiceImpl(WxMaService wxMaService) { + this.wxMaService = wxMaService; + } + + @Override + public WxMediaUploadResult uploadMedia(String mediaType, String fileType, InputStream inputStream) throws WxErrorException { + try { + return this.uploadMedia(mediaType, FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), fileType)); + } catch (IOException e) { + e.printStackTrace(); + throw new WxErrorException(WxError.newBuilder().setErrorMsg(e.getMessage()).build()); + } + } + + @Override + public WxMediaUploadResult uploadMedia(String mediaType, File file) throws WxErrorException { + String url = String.format(MEDIA_UPLOAD_URL, mediaType); + return this.wxMaService.execute(MediaUploadRequestExecutor.create(this.wxMaService.getRequestHttp()), url, file); + } + + @Override + public File getMedia(String mediaId) throws WxErrorException { + try { + RequestExecutor executor = MediaDownloadRequestExecutor + .create(this.wxMaService.getRequestHttp(), Files.createTempDirectory("wxma").toFile()); + return this.wxMaService.execute(executor, MEDIA_GET_URL, "media_id=" + mediaId); + } catch (IOException e) { + this.log.error(e.getMessage(), e); + throw new WxErrorException(WxError.newBuilder().setErrorMsg(e.getMessage()).build()); + } + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaMsgServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaMsgServiceImpl.java new file mode 100644 index 0000000000..70a4e50445 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaMsgServiceImpl.java @@ -0,0 +1,38 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaMsgService; +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage; +import cn.binarywang.wx.miniapp.bean.WxMaTemplateMessage; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import me.chanjar.weixin.common.bean.result.WxError; +import me.chanjar.weixin.common.exception.WxErrorException; + +/** + * @author Binary Wang + */ +public class WxMaMsgServiceImpl implements WxMaMsgService { + private static final JsonParser JSON_PARSER = new JsonParser(); + private WxMaService wxMaService; + + public WxMaMsgServiceImpl(WxMaService wxMaService) { + this.wxMaService = wxMaService; + } + + @Override + public boolean sendKefuMsg(WxMaKefuMessage message) throws WxErrorException { + String responseContent = this.wxMaService.post(KEFU_MESSAGE_SEND_URL, message.toJson()); + return responseContent != null; + } + + @Override + public void sendTemplateMsg(WxMaTemplateMessage templateMessage) throws WxErrorException { + String responseContent = this.wxMaService.post(TEMPLATE_MSG_SEND_URL, templateMessage.toJson()); + JsonObject jsonObject = JSON_PARSER.parse(responseContent).getAsJsonObject(); + if (jsonObject.get("errcode").getAsInt() != 0) { + throw new WxErrorException(WxError.fromJson(responseContent)); + } + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaQrcodeServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaQrcodeServiceImpl.java new file mode 100644 index 0000000000..fd58855b3d --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaQrcodeServiceImpl.java @@ -0,0 +1,75 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaQrcodeService; +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.WxMaQrcode; +import cn.binarywang.wx.miniapp.bean.WxMaWxcode; +import cn.binarywang.wx.miniapp.bean.WxMaWxcodeLimit; +import cn.binarywang.wx.miniapp.util.http.QrCodeRequestExecutor; +import me.chanjar.weixin.common.exception.WxErrorException; + +import java.io.File; + +/** + * @author Binary Wang + */ +public class WxMaQrcodeServiceImpl implements WxMaQrcodeService { + private WxMaService wxMaService; + + public WxMaQrcodeServiceImpl(WxMaService wxMaService) { + this.wxMaService = wxMaService; + } + + @Override + public File createQrcode(String path, int width) throws WxErrorException { + String url = "https://api.weixin.qq.com/cgi-bin/wxaapp/createwxaqrcode"; + return this.wxMaService.execute(new QrCodeRequestExecutor(this.wxMaService.getRequestHttp()), + url, new WxMaQrcode(path, width)); + } + + @Override + public File createQrcode(String path) throws WxErrorException { + return this.createQrcode(path, 430); + } + + @Override + public File createWxCode(String path, int width, boolean autoColor, LineColor lineColor) throws WxErrorException { + String url = "https://api.weixin.qq.com/wxa/getwxacode"; + WxMaWxcode wxMaWxcode = new WxMaWxcode(); + wxMaWxcode.setPath(path); + wxMaWxcode.setWidth(width); + wxMaWxcode.setAutoColor(autoColor); + wxMaWxcode.setLineColor(lineColor); + return this.wxMaService.execute(new QrCodeRequestExecutor(this.wxMaService.getRequestHttp()), + url, wxMaWxcode); + } + + @Override + public File createWxCode(String path, int width) throws WxErrorException { + return this.createWxCode(path, width, true, null); + } + + @Override + public File createWxCode(String path) throws WxErrorException { + return this.createWxCode(path, 430, true, null); + } + + @Override + public File createWxCodeLimit(String scene, String page, int width, boolean autoColor, LineColor lineColor) throws WxErrorException { + String url = "http://api.weixin.qq.com/wxa/getwxacodeunlimit"; + WxMaWxcodeLimit wxMaWxcodeLimit = new WxMaWxcodeLimit(); + wxMaWxcodeLimit.setScene(scene); + wxMaWxcodeLimit.setPage(page); + wxMaWxcodeLimit.setWidth(width); + wxMaWxcodeLimit.setAutoColor(autoColor); + wxMaWxcodeLimit.setLineColor(lineColor); + return this.wxMaService.execute(new QrCodeRequestExecutor(this.wxMaService.getRequestHttp()), + url, wxMaWxcodeLimit); + } + + @Override + public File createWxCodeLimit(String scene, String page) throws WxErrorException { + return this.createWxCodeLimit(scene, page, 430, true, null); + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImpl.java new file mode 100644 index 0000000000..023306634b --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImpl.java @@ -0,0 +1,265 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.*; +import cn.binarywang.wx.miniapp.config.WxMaConfig; +import com.google.gson.JsonParser; +import me.chanjar.weixin.common.bean.WxAccessToken; +import me.chanjar.weixin.common.bean.result.WxError; +import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.util.crypto.SHA1; +import me.chanjar.weixin.common.util.http.*; +import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; +import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder; +import org.apache.http.HttpHost; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.BasicResponseHandler; +import org.apache.http.impl.client.CloseableHttpClient; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.concurrent.locks.Lock; + +/** + * @author Binary Wang + */ +public class WxMaServiceImpl implements WxMaService, RequestHttp { + private static final JsonParser JSON_PARSER = new JsonParser(); + private final Logger log = LoggerFactory.getLogger(this.getClass()); + + private CloseableHttpClient httpClient; + private HttpHost httpProxy; + private WxMaConfig wxMaConfig; + + private WxMaMsgService kefuService = new WxMaMsgServiceImpl(this); + private WxMaMediaService materialService = new WxMaMediaServiceImpl(this); + private WxMaUserService userService = new WxMaUserServiceImpl(this); + private WxMaQrcodeService qrCodeService = new WxMaQrcodeServiceImpl(this); + + private int retrySleepMillis = 1000; + private int maxRetryTimes = 5; + + @Override + public CloseableHttpClient getRequestHttpClient() { + return httpClient; + } + + @Override + public HttpHost getRequestHttpProxy() { + return httpProxy; + } + + @Override + public HttpType getRequestType() { + return HttpType.APACHE_HTTP; + } + + @Override + public void initHttp() { + WxMaConfig configStorage = this.getWxMaConfig(); + ApacheHttpClientBuilder apacheHttpClientBuilder = configStorage.getApacheHttpClientBuilder(); + if (null == apacheHttpClientBuilder) { + apacheHttpClientBuilder = DefaultApacheHttpClientBuilder.get(); + } + + apacheHttpClientBuilder.httpProxyHost(configStorage.getHttpProxyHost()) + .httpProxyPort(configStorage.getHttpProxyPort()) + .httpProxyUsername(configStorage.getHttpProxyUsername()) + .httpProxyPassword(configStorage.getHttpProxyPassword()); + + if (configStorage.getHttpProxyHost() != null && configStorage.getHttpProxyPort() > 0) { + this.httpProxy = new HttpHost(configStorage.getHttpProxyHost(), configStorage.getHttpProxyPort()); + } + + this.httpClient = apacheHttpClientBuilder.build(); + } + + @Override + public RequestHttp getRequestHttp() { + return this; + } + + @Override + public String getAccessToken(boolean forceRefresh) throws WxErrorException { + Lock lock = this.getWxMaConfig().getAccessTokenLock(); + try { + lock.lock(); + + if (forceRefresh) { + this.getWxMaConfig().expireAccessToken(); + } + + if (this.getWxMaConfig().isAccessTokenExpired()) { + String url = String.format(WxMaService.GET_ACCESS_TOKEN_URL, this.getWxMaConfig().getAppid(), + this.getWxMaConfig().getSecret()); + try { + HttpGet httpGet = new HttpGet(url); + if (this.getRequestHttpProxy() != null) { + RequestConfig config = RequestConfig.custom().setProxy(this.getRequestHttpProxy()).build(); + httpGet.setConfig(config); + } + try (CloseableHttpResponse response = getRequestHttpClient().execute(httpGet)) { + String resultContent = new BasicResponseHandler().handleResponse(response); + WxError error = WxError.fromJson(resultContent); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); + this.getWxMaConfig().updateAccessToken(accessToken.getAccessToken(), + accessToken.getExpiresIn()); + } finally { + httpGet.releaseConnection(); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } finally { + lock.unlock(); + } + + return this.getWxMaConfig().getAccessToken(); + } + + @Override + public boolean checkSignature(String timestamp, String nonce, String signature) { + try { + return SHA1.gen(this.getWxMaConfig().getToken(), timestamp, nonce).equals(signature); + } catch (Exception e) { + this.log.error("Checking signature failed, and the reason is :" + e.getMessage()); + return false; + } + } + + @Override + public String getAccessToken() throws WxErrorException { + return getAccessToken(false); + } + + @Override + public String get(String url, String queryParam) throws WxErrorException { + return execute(SimpleGetRequestExecutor.create(this), url, queryParam); + } + + @Override + public String post(String url, String postData) throws WxErrorException { + return execute(SimplePostRequestExecutor.create(this), url, postData); + } + + /** + * 向微信端发送请求,在这里执行的策略是当发生access_token过期时才去刷新,然后重新执行请求,而不是全局定时请求 + */ + public T execute(RequestExecutor executor, String uri, E data) throws WxErrorException { + int retryTimes = 0; + do { + try { + return this.executeInternal(executor, uri, data); + } catch (WxErrorException e) { + if (retryTimes + 1 > this.maxRetryTimes) { + this.log.warn("重试达到最大次数【{}】", maxRetryTimes); + //最后一次重试失败后,直接抛出异常,不再等待 + throw new RuntimeException("微信服务端异常,超出重试次数"); + } + + WxError error = e.getError(); + // -1 系统繁忙, 1000ms后重试 + if (error.getErrorCode() == -1) { + int sleepMillis = this.retrySleepMillis * (1 << retryTimes); + try { + this.log.warn("微信系统繁忙,{} ms 后重试(第{}次)", sleepMillis, retryTimes + 1); + Thread.sleep(sleepMillis); + } catch (InterruptedException e1) { + throw new RuntimeException(e1); + } + } else { + throw e; + } + } + } while (retryTimes++ < this.maxRetryTimes); + + this.log.warn("重试达到最大次数【{}】", this.maxRetryTimes); + throw new RuntimeException("微信服务端异常,超出重试次数"); + } + + public synchronized T executeInternal(RequestExecutor executor, String uri, E data) throws WxErrorException { + if (uri.contains("access_token=")) { + throw new IllegalArgumentException("uri参数中不允许有access_token: " + uri); + } + String accessToken = getAccessToken(false); + + String uriWithAccessToken = uri + (uri.contains("?") ? "&" : "?") + "access_token=" + accessToken; + + try { + T result = executor.execute(uriWithAccessToken, data); + this.log.debug("\n【请求地址】: {}\n【请求参数】:{}\n【响应数据】:{}", uriWithAccessToken, data, result); + return result; + } catch (WxErrorException e) { + WxError error = e.getError(); + /* + * 发生以下情况时尝试刷新access_token + * 40001 获取access_token时AppSecret错误,或者access_token无效 + * 42001 access_token超时 + * 40014 不合法的access_token,请开发者认真比对access_token的有效性(如是否过期) + */ + if (error.getErrorCode() == 42001 || error.getErrorCode() == 40001 || error.getErrorCode() == 40014) { + // 强制设置wxMpConfigStorage它的access token过期了,这样在下一次请求里就会刷新access token + this.getWxMaConfig().expireAccessToken(); + if (this.getWxMaConfig().autoRefreshToken()) { + return this.execute(executor, uri, data); + } + } + + if (error.getErrorCode() != 0) { + this.log.error("\n【请求地址】: {}\n【请求参数】:{}\n【错误信息】:{}", uriWithAccessToken, data, error); + throw new WxErrorException(error, e); + } + return null; + } catch (IOException e) { + this.log.error("\n【请求地址】: {}\n【请求参数】:{}\n【异常信息】:{}", uriWithAccessToken, data, e.getMessage()); + throw new RuntimeException(e); + } + } + + @Override + public WxMaConfig getWxMaConfig() { + return this.wxMaConfig; + } + + @Override + public void setWxMaConfig(WxMaConfig wxConfigProvider) { + this.wxMaConfig = wxConfigProvider; + this.initHttp(); + } + + @Override + public void setRetrySleepMillis(int retrySleepMillis) { + this.retrySleepMillis = retrySleepMillis; + } + + @Override + public void setMaxRetryTimes(int maxRetryTimes) { + this.maxRetryTimes = maxRetryTimes; + } + + @Override + public WxMaMsgService getMsgService() { + return this.kefuService; + } + + @Override + public WxMaMediaService getMediaService() { + return this.materialService; + } + + @Override + public WxMaUserService getUserService() { + return this.userService; + } + + @Override + public WxMaQrcodeService getQrcodeService() { + return this.qrCodeService; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImpl.java new file mode 100644 index 0000000000..e2fdb9dc2b --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImpl.java @@ -0,0 +1,51 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.api.WxMaUserService; +import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; +import cn.binarywang.wx.miniapp.bean.WxMaUserInfo; +import cn.binarywang.wx.miniapp.config.WxMaConfig; +import cn.binarywang.wx.miniapp.util.crypt.WxMaCryptUtils; +import com.google.common.base.Joiner; +import me.chanjar.weixin.common.exception.WxErrorException; +import org.apache.commons.codec.digest.DigestUtils; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author Binary Wang + */ +public class WxMaUserServiceImpl implements WxMaUserService { + private WxMaService service; + + WxMaUserServiceImpl(WxMaService service) { + this.service = service; + } + + @Override + public WxMaJscode2SessionResult getSessionInfo(String jsCode) throws WxErrorException { + final WxMaConfig config = service.getWxMaConfig(); + Map params = new HashMap<>(); + params.put("appid", config.getAppid()); + params.put("secret", config.getSecret()); + params.put("js_code", jsCode); + params.put("grant_type", "authorization_code"); + + String result = this.service.get(JSCODE_TO_SESSION_URL, Joiner.on("&").withKeyValueSeparator("=").join(params)); + return WxMaJscode2SessionResult.fromJson(result); + } + + @Override + public WxMaUserInfo getUserInfo(String sessionKey, String encryptedData, String ivStr) { + return WxMaUserInfo.fromJson(WxMaCryptUtils.decrypt(sessionKey, encryptedData, ivStr)); + } + + @Override + public boolean checkUserInfo(String sessionKey, String rawData, String signature) { + final String generatedSignature = DigestUtils.sha1Hex(rawData + sessionKey); + System.out.println(generatedSignature); + return generatedSignature.equals(signature); + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaJscode2SessionResult.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaJscode2SessionResult.java new file mode 100644 index 0000000000..471e6c46ec --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaJscode2SessionResult.java @@ -0,0 +1,60 @@ +package cn.binarywang.wx.miniapp.bean; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; + +/** + * {"session_key":"nzoqhc3OnwHzeTxJs+inbQ==","expires_in":2592000,"openid":"oVBkZ0aYgDMDIywRdgPW8-joxXc4"} + * + * @author Binary Wang + */ +public class WxMaJscode2SessionResult { + @SerializedName("session_key") + private String sessionKey; + + @SerializedName("expires_in") + private Integer expiresin; + + @SerializedName("openid") + private String openid; + + @SerializedName("unionid") + private String unionid; + + public static WxMaJscode2SessionResult fromJson(String json) { + return WxMaGsonBuilder.create().fromJson(json, WxMaJscode2SessionResult.class); + } + + public String getSessionKey() { + return sessionKey; + } + + public void setSessionKey(String sessionKey) { + this.sessionKey = sessionKey; + } + + public Integer getExpiresin() { + return expiresin; + } + + public void setExpiresin(Integer expiresin) { + this.expiresin = expiresin; + } + + public String getOpenid() { + return openid; + } + + public void setOpenid(String openid) { + this.openid = openid; + } + + public String getUnionid() { + return unionid; + } + + public void setUnionid(String unionid) { + this.unionid = unionid; + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaKefuMessage.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaKefuMessage.java new file mode 100644 index 0000000000..d75fbf12ae --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaKefuMessage.java @@ -0,0 +1,99 @@ +package cn.binarywang.wx.miniapp.bean; + +import cn.binarywang.wx.miniapp.builder.ImageBuilder; +import cn.binarywang.wx.miniapp.builder.TextBuilder; +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; + +import java.io.Serializable; + +/** + * 客服消息 + * + * @author Binary Wang + */ +public class WxMaKefuMessage implements Serializable { + private static final long serialVersionUID = -9196732086954365246L; + + private String toUser; + private String msgType; + private String content; + private String mediaId; + private String thumbMediaId; + private String title; + private String description; + + /** + * 获得文本消息builder + */ + public static TextBuilder TEXT() { + return new TextBuilder(); + } + + /** + * 获得图片消息builder + */ + public static ImageBuilder IMAGE() { + return new ImageBuilder(); + } + + public String getToUser() { + return this.toUser; + } + + public void setToUser(String toUser) { + this.toUser = toUser; + } + + public String getMsgType() { + return this.msgType; + } + + public void setMsgType(String msgType) { + this.msgType = msgType; + } + + public String getContent() { + return this.content; + } + + public void setContent(String content) { + this.content = content; + } + + public String getMediaId() { + return this.mediaId; + } + + public void setMediaId(String mediaId) { + this.mediaId = mediaId; + } + + public String getThumbMediaId() { + return this.thumbMediaId; + } + + public void setThumbMediaId(String thumbMediaId) { + this.thumbMediaId = thumbMediaId; + } + + public String getTitle() { + return this.title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getDescription() { + return this.description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String toJson() { + return WxMaGsonBuilder.create().toJson(this); + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaMessage.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaMessage.java new file mode 100644 index 0000000000..4b201da1cd --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaMessage.java @@ -0,0 +1,236 @@ +package cn.binarywang.wx.miniapp.bean; + +import cn.binarywang.wx.miniapp.config.WxMaConfig; +import cn.binarywang.wx.miniapp.util.crypt.WxMaCryptUtils; +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import cn.binarywang.wx.miniapp.util.xml.XStreamTransformer; +import com.google.gson.annotations.SerializedName; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import com.thoughtworks.xstream.annotations.XStreamConverter; +import me.chanjar.weixin.common.util.ToStringUtils; +import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; +import org.apache.commons.io.IOUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Serializable; +import java.nio.charset.StandardCharsets; + +/** + * @author Binary Wang + */ +@XStreamAlias("xml") +public class WxMaMessage implements Serializable { + private static final long serialVersionUID = -3586245291677274914L; + + @SerializedName("Encrypt") + @XStreamAlias("Encrypt") + @XStreamConverter(value = XStreamCDataConverter.class) + private String encrypt; + + @SerializedName("ToUserName") + @XStreamAlias("ToUserName") + @XStreamConverter(value = XStreamCDataConverter.class) + private String toUser; + + @SerializedName("FromUserName") + @XStreamAlias("FromUserName") + @XStreamConverter(value = XStreamCDataConverter.class) + private String fromUser; + + @SerializedName("CreateTime") + @XStreamAlias("CreateTime") + @XStreamConverter(value = XStreamCDataConverter.class) + private Integer createTime; + + @SerializedName("MsgDataFormat") + @XStreamAlias("MsgDataFormat") + @XStreamConverter(value = XStreamCDataConverter.class) + private String msgType; + + // 文本消息 + @SerializedName("Content") + @XStreamAlias("Content") + @XStreamConverter(value = XStreamCDataConverter.class) + private String content; + + @SerializedName("MsgId") + @XStreamAlias("MsgId") + @XStreamConverter(value = XStreamCDataConverter.class) + private Long msgId; + + // 图片消息 + @SerializedName("PicUrl") + @XStreamAlias("PicUrl") + @XStreamConverter(value = XStreamCDataConverter.class) + private String picUrl; + + @SerializedName("MediaId") + @XStreamAlias("MediaId") + @XStreamConverter(value = XStreamCDataConverter.class) + private String mediaId; + + // 事件消息 + @SerializedName("Event") + @XStreamAlias("Event") + @XStreamConverter(value = XStreamCDataConverter.class) + private String event; + + @SerializedName("SessionFrom") + @XStreamAlias("SessionFrom") + @XStreamConverter(value = XStreamCDataConverter.class) + private String sessionFrom; + + public static WxMaMessage fromXml(String xml) { + return XStreamTransformer.fromXml(WxMaMessage.class, xml); + } + + public static WxMaMessage fromXml(InputStream is) { + return XStreamTransformer.fromXml(WxMaMessage.class, is); + } + + /** + * 从加密字符串转换 + * + * @param encryptedXml 密文 + * @param wxMaConfig 配置存储器对象 + * @param timestamp 时间戳 + * @param nonce 随机串 + * @param msgSignature 签名串 + */ + public static WxMaMessage fromEncryptedXml(String encryptedXml, + WxMaConfig wxMaConfig, String timestamp, String nonce, + String msgSignature) { + String plainText = new WxMaCryptUtils(wxMaConfig).decrypt(msgSignature, timestamp, nonce, encryptedXml); + return fromXml(plainText); + } + + public static WxMaMessage fromEncryptedXml(InputStream is, WxMaConfig wxMaConfig, String timestamp, + String nonce, String msgSignature) { + try { + return fromEncryptedXml(IOUtils.toString(is, StandardCharsets.UTF_8), wxMaConfig, + timestamp, nonce, msgSignature); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static WxMaMessage fromJson(String json) { + return WxMaGsonBuilder.create().fromJson(json, WxMaMessage.class); + } + + public static WxMaMessage fromEncryptedJson(String encryptedJson, WxMaConfig config) { + try { + WxMaMessage encryptedMessage = fromJson(encryptedJson); + String plainText = new WxMaCryptUtils(config).decrypt(encryptedMessage.getEncrypt()); + return fromJson(plainText); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static WxMaMessage fromEncryptedJson(InputStream inputStream, WxMaConfig config) { + try { + return fromEncryptedJson(IOUtils.toString(inputStream, StandardCharsets.UTF_8), config); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public String toString() { + return ToStringUtils.toSimpleString(this); + } + + public String toJson() { + return WxMaGsonBuilder.create().toJson(this); + } + + public String getToUser() { + return toUser; + } + + public void setToUser(String toUser) { + this.toUser = toUser; + } + + public String getFromUser() { + return fromUser; + } + + public void setFromUser(String fromUser) { + this.fromUser = fromUser; + } + + public Integer getCreateTime() { + return createTime; + } + + public void setCreateTime(Integer createTime) { + this.createTime = createTime; + } + + public String getMsgType() { + return msgType; + } + + public void setMsgType(String msgType) { + this.msgType = msgType; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public Long getMsgId() { + return msgId; + } + + public void setMsgId(Long msgId) { + this.msgId = msgId; + } + + public String getPicUrl() { + return picUrl; + } + + public void setPicUrl(String picUrl) { + this.picUrl = picUrl; + } + + public String getMediaId() { + return mediaId; + } + + public void setMediaId(String mediaId) { + this.mediaId = mediaId; + } + + public String getEvent() { + return event; + } + + public void setEvent(String event) { + this.event = event; + } + + public String getSessionFrom() { + return sessionFrom; + } + + public void setSessionFrom(String sessionFrom) { + this.sessionFrom = sessionFrom; + } + + public String getEncrypt() { + return encrypt; + } + + public void setEncrypt(String encrypt) { + this.encrypt = encrypt; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaQrcode.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaQrcode.java new file mode 100644 index 0000000000..a047d7c8b1 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaQrcode.java @@ -0,0 +1,44 @@ +package cn.binarywang.wx.miniapp.bean; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; + +import java.io.Serializable; + +/** + * @author Binary Wang + */ +public class WxMaQrcode extends WxMaQrcodeWrapper implements Serializable { + private static final long serialVersionUID = 5777119669111011584L; + private String path; + private int width = 430; + + public WxMaQrcode(String path, int width) { + this.path = path; + this.width = width; + } + + public static WxMaQrcode fromJson(String json) { + return WxMaGsonBuilder.create().fromJson(json, WxMaQrcode.class); + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public int getWidth() { + return width; + } + + public void setWidth(int width) { + this.width = width; + } + + @Override + public String toString() { + return WxMaGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaQrcodeWrapper.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaQrcodeWrapper.java new file mode 100644 index 0000000000..37c5c5db88 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaQrcodeWrapper.java @@ -0,0 +1,16 @@ +package cn.binarywang.wx.miniapp.bean; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; + +/** + * 微信二维码(小程序码)包装器 + * Created by Element on 2017/7/27. + */ +public abstract class WxMaQrcodeWrapper { + + @Override + public String toString() { + return WxMaGsonBuilder.create().toJson(this); + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaTemplateMessage.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaTemplateMessage.java new file mode 100644 index 0000000000..79f955c6d2 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaTemplateMessage.java @@ -0,0 +1,247 @@ +package cn.binarywang.wx.miniapp.bean; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * 参考 https://mp.weixin.qq.com/debug/wxadoc/dev/api/notice.html#接口说明 模板消息部分 + * + * @author Binary Wang + */ +public class WxMaTemplateMessage implements Serializable { + private static final long serialVersionUID = 5063374783759519418L; + + /** + *
+   * 参数:touser
+   * 是否必填: 是
+   * 描述: 接收者(用户)的 openid
+   * 
+ */ + private String toUser; + + /** + *
+   * 参数:template_id
+   * 是否必填: 是
+   * 描述: 所需下发的模板消息的id
+   * 
+ */ + private String templateId; + + /** + *
+   * 参数:page
+   * 是否必填: 否
+   * 描述: 点击模板卡片后的跳转页面,仅限本小程序内的页面。支持带参数,(示例index?foo=bar)。该字段不填则模板无跳转。
+   * 
+ */ + private String page; + + /** + *
+   * 参数:form_id
+   * 是否必填: 是
+   * 描述: 表单提交场景下,为 submit 事件带上的 formId;支付场景下,为本次支付的 prepay_id
+   * 
+ */ + private String formId; + + /** + *
+   * 参数:data
+   * 是否必填: 是
+   * 描述: 模板内容,不填则下发空模板
+   * 
+ */ + private List data = new ArrayList<>(); + + /** + *
+   * 参数:color
+   * 是否必填: 否
+   * 描述: 模板内容字体的颜色,不填默认黑色
+   * 
+ */ + private String color; + + /** + *
+   * 参数:emphasis_keyword
+   * 是否必填: 否
+   * 描述: 模板需要放大的关键词,不填则默认无放大
+   * 
+ */ + private String emphasisKeyword; + + private WxMaTemplateMessage(Builder builder) { + setToUser(builder.toUser); + setTemplateId(builder.templateId); + setPage(builder.page); + setFormId(builder.formId); + setData(builder.data); + setColor(builder.color); + setEmphasisKeyword(builder.emphasisKeyword); + } + + public static Builder newBuilder() { + return new Builder(); + } + + public String toJson() { + return WxMaGsonBuilder.create().toJson(this); + } + + public String getToUser() { + return toUser; + } + + public void setToUser(String toUser) { + this.toUser = toUser; + } + + public String getTemplateId() { + return templateId; + } + + public void setTemplateId(String templateId) { + this.templateId = templateId; + } + + public String getPage() { + return page; + } + + public void setPage(String page) { + this.page = page; + } + + public String getFormId() { + return formId; + } + + public void setFormId(String formId) { + this.formId = formId; + } + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } + + public String getColor() { + return color; + } + + public void setColor(String color) { + this.color = color; + } + + public String getEmphasisKeyword() { + return emphasisKeyword; + } + + public void setEmphasisKeyword(String emphasisKeyword) { + this.emphasisKeyword = emphasisKeyword; + } + + public static class Data { + private String name; + private String value; + private String color; + + public Data(String name, String value) { + this.name = name; + this.value = value; + } + + public Data(String name, String value, String color) { + this.name = name; + this.value = value; + this.color = color; + } + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public String getValue() { + return this.value; + } + + public void setValue(String value) { + this.value = value; + } + + public String getColor() { + return this.color; + } + + public void setColor(String color) { + this.color = color; + } + + } + + public static final class Builder { + private String toUser; + private String templateId; + private String page; + private String formId; + private List data; + private String color; + private String emphasisKeyword; + + private Builder() { + } + + public Builder toUser(String toUser) { + this.toUser = toUser; + return this; + } + + public Builder templateId(String templateId) { + this.templateId = templateId; + return this; + } + + public Builder page(String page) { + this.page = page; + return this; + } + + public Builder formId(String formId) { + this.formId = formId; + return this; + } + + public Builder data(List data) { + this.data = data; + return this; + } + + public Builder color(String color) { + this.color = color; + return this; + } + + public Builder emphasisKeyword(String emphasisKeyword) { + this.emphasisKeyword = emphasisKeyword; + return this; + } + + public WxMaTemplateMessage build() { + return new WxMaTemplateMessage(this); + } + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaUserInfo.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaUserInfo.java new file mode 100644 index 0000000000..e3cc69d913 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaUserInfo.java @@ -0,0 +1,131 @@ +package cn.binarywang.wx.miniapp.bean; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * @author Binary Wang + */ +public class WxMaUserInfo { + private String openId; + private String nickName; + private String gender; + private String language; + private String city; + private String province; + private String country; + private String avatarUrl; + private String unionId; + private Watermark watermark; + + public static WxMaUserInfo fromJson(String json) { + return WxMaGsonBuilder.create().fromJson(json, WxMaUserInfo.class); + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE); + } + + public String getOpenId() { + return openId; + } + + public void setOpenId(String openId) { + this.openId = openId; + } + + public String getNickName() { + return nickName; + } + + public void setNickName(String nickName) { + this.nickName = nickName; + } + + public String getGender() { + return gender; + } + + public void setGender(String gender) { + this.gender = gender; + } + + public String getLanguage() { + return language; + } + + public void setLanguage(String language) { + this.language = language; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public String getProvince() { + return province; + } + + public void setProvince(String province) { + this.province = province; + } + + public String getCountry() { + return country; + } + + public void setCountry(String country) { + this.country = country; + } + + public String getAvatarUrl() { + return avatarUrl; + } + + public void setAvatarUrl(String avatarUrl) { + this.avatarUrl = avatarUrl; + } + + public String getUnionId() { + return unionId; + } + + public void setUnionId(String unionId) { + this.unionId = unionId; + } + + public Watermark getWatermark() { + return watermark; + } + + public void setWatermark(Watermark watermark) { + this.watermark = watermark; + } + + public static class Watermark { + private String timestamp; + private String appid; + + public String getTimestamp() { + return timestamp; + } + + public void setTimestamp(String timestamp) { + this.timestamp = timestamp; + } + + public String getAppid() { + return appid; + } + + public void setAppid(String appid) { + this.appid = appid; + } + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaWxcode.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaWxcode.java new file mode 100644 index 0000000000..9ea674ed98 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaWxcode.java @@ -0,0 +1,64 @@ +package cn.binarywang.wx.miniapp.bean; + +import cn.binarywang.wx.miniapp.api.WxMaQrcodeService; +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; + +import java.io.Serializable; + +/** + * Created by Element on 2017/7/27. + */ +public class WxMaWxcode extends WxMaQrcodeWrapper implements Serializable { + + private static final long serialVersionUID = 1287399621649210322L; + private String path; + private int width = 430; + + @SerializedName("auto_color") + private boolean autoColor = true; + + @SerializedName("line_color") + private WxMaQrcodeService.LineColor lineColor = new WxMaQrcodeService.LineColor("0", "0", "0"); + + public static WxMaWxcode fromJson(String json) { + return WxMaGsonBuilder.create().fromJson(json, WxMaWxcode.class); + } + + public static long getSerialVersionUID() { + return serialVersionUID; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public int getWidth() { + return width; + } + + public void setWidth(int width) { + this.width = width; + } + + public boolean isAutoColor() { + return autoColor; + } + + public void setAutoColor(boolean autoColor) { + this.autoColor = autoColor; + } + + public WxMaQrcodeService.LineColor getLineColor() { + return lineColor; + } + + public void setLineColor(WxMaQrcodeService.LineColor lineColor) { + this.lineColor = lineColor; + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaWxcodeLimit.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaWxcodeLimit.java new file mode 100644 index 0000000000..7619fe46f2 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaWxcodeLimit.java @@ -0,0 +1,68 @@ +package cn.binarywang.wx.miniapp.bean; + +import cn.binarywang.wx.miniapp.api.WxMaQrcodeService; +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; + +import java.io.Serializable; + +/** + * Created by Element on 2017/7/27. + */ +public class WxMaWxcodeLimit extends WxMaQrcodeWrapper implements Serializable { + private static final long serialVersionUID = 4782193774524960401L; + private String scene; + private String page; + + private int width = 430; + + @SerializedName("auto_color") + private boolean autoColor = true; + + @SerializedName("line_color") + private WxMaQrcodeService.LineColor lineColor = new WxMaQrcodeService.LineColor("0", "0", "0"); + + public static WxMaWxcodeLimit fromJson(String json) { + return WxMaGsonBuilder.create().fromJson(json, WxMaWxcodeLimit.class); + } + + public String getPage() { + return page; + } + + public void setPage(String page) { + this.page = page; + } + + public String getScene() { + return scene; + } + + public void setScene(String scene) { + this.scene = scene; + } + + public int getWidth() { + return width; + } + + public void setWidth(int width) { + this.width = width; + } + + public boolean isAutoColor() { + return autoColor; + } + + public void setAutoColor(boolean autoColor) { + this.autoColor = autoColor; + } + + public WxMaQrcodeService.LineColor getLineColor() { + return lineColor; + } + + public void setLineColor(WxMaQrcodeService.LineColor lineColor) { + this.lineColor = lineColor; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/builder/BaseBuilder.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/builder/BaseBuilder.java new file mode 100644 index 0000000000..70d7cf4b7c --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/builder/BaseBuilder.java @@ -0,0 +1,24 @@ +package cn.binarywang.wx.miniapp.builder; + +import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage; + +/** + * @author Binary Wang + */ +public class BaseBuilder { + protected String msgType; + protected String toUser; + + @SuppressWarnings("unchecked") + public T toUser(String toUser) { + this.toUser = toUser; + return (T) this; + } + + public WxMaKefuMessage build() { + WxMaKefuMessage m = new WxMaKefuMessage(); + m.setMsgType(this.msgType); + m.setToUser(this.toUser); + return m; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/builder/ImageBuilder.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/builder/ImageBuilder.java new file mode 100644 index 0000000000..a903e97c43 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/builder/ImageBuilder.java @@ -0,0 +1,27 @@ +package cn.binarywang.wx.miniapp.builder; + +import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage; +import cn.binarywang.wx.miniapp.constant.WxMaConstants; + +/** + * @author Binary Wang + */ +public final class ImageBuilder extends BaseBuilder { + private String mediaId; + + public ImageBuilder() { + this.msgType = WxMaConstants.KefuMsgType.IMAGE; + } + + public ImageBuilder mediaId(String media_id) { + this.mediaId = media_id; + return this; + } + + @Override + public WxMaKefuMessage build() { + WxMaKefuMessage m = super.build(); + m.setMediaId(this.mediaId); + return m; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/builder/TextBuilder.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/builder/TextBuilder.java new file mode 100644 index 0000000000..35c58a67df --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/builder/TextBuilder.java @@ -0,0 +1,27 @@ +package cn.binarywang.wx.miniapp.builder; + +import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage; +import cn.binarywang.wx.miniapp.constant.WxMaConstants; + +/** + * @author Binary Wang + */ +public final class TextBuilder extends BaseBuilder { + private String content; + + public TextBuilder() { + this.msgType = WxMaConstants.KefuMsgType.TEXT; + } + + public TextBuilder content(String content) { + this.content = content; + return this; + } + + @Override + public WxMaKefuMessage build() { + WxMaKefuMessage m = super.build(); + m.setContent(this.content); + return m; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/WxMaConfig.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/WxMaConfig.java new file mode 100644 index 0000000000..df72bb2c7d --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/WxMaConfig.java @@ -0,0 +1,73 @@ +package cn.binarywang.wx.miniapp.config; + +import me.chanjar.weixin.common.bean.WxAccessToken; +import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; + +import java.util.concurrent.locks.Lock; + +/** + * 小程序配置 + * + * @author Binary Wang + */ +public interface WxMaConfig { + + String getAccessToken(); + + Lock getAccessTokenLock(); + + boolean isAccessTokenExpired(); + + /** + * 强制将access token过期掉 + */ + void expireAccessToken(); + + /** + * 应该是线程安全的 + * + * @param accessToken 要更新的WxAccessToken对象 + */ + void updateAccessToken(WxAccessToken accessToken); + + /** + * 应该是线程安全的 + * + * @param accessToken 新的accessToken值 + * @param expiresInSeconds 过期时间,以秒为单位 + */ + void updateAccessToken(String accessToken, int expiresInSeconds); + + String getAppid(); + + String getSecret(); + + String getToken(); + + String getAesKey(); + + String getMsgDataFormat(); + + long getExpiresTime(); + + String getHttpProxyHost(); + + int getHttpProxyPort(); + + String getHttpProxyUsername(); + + String getHttpProxyPassword(); + + /** + * http client builder + * + * @return ApacheHttpClientBuilder + */ + ApacheHttpClientBuilder getApacheHttpClientBuilder(); + + /** + * 是否自动刷新token + */ + boolean autoRefreshToken(); + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/WxMaInMemoryConfig.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/WxMaInMemoryConfig.java new file mode 100644 index 0000000000..735a52b56a --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/WxMaInMemoryConfig.java @@ -0,0 +1,185 @@ +package cn.binarywang.wx.miniapp.config; + +import me.chanjar.weixin.common.bean.WxAccessToken; +import me.chanjar.weixin.common.util.ToStringUtils; +import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; + +import java.io.File; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +/** + * 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化 + * + * @author Binary Wang + */ +public class WxMaInMemoryConfig implements WxMaConfig { + protected volatile String msgDataFormat; + protected volatile String appid; + protected volatile String secret; + protected volatile String token; + protected volatile String accessToken; + protected volatile String aesKey; + protected volatile long expiresTime; + + protected volatile String httpProxyHost; + protected volatile int httpProxyPort; + protected volatile String httpProxyUsername; + protected volatile String httpProxyPassword; + + protected Lock accessTokenLock = new ReentrantLock(); + + /** + * 临时文件目录 + */ + protected volatile File tmpDirFile; + + protected volatile ApacheHttpClientBuilder apacheHttpClientBuilder; + + @Override + public String getAccessToken() { + return this.accessToken; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } + + @Override + public Lock getAccessTokenLock() { + return this.accessTokenLock; + } + + public void setAccessTokenLock(Lock accessTokenLock) { + this.accessTokenLock = accessTokenLock; + } + + @Override + public boolean isAccessTokenExpired() { + return System.currentTimeMillis() > this.expiresTime; + } + + @Override + public synchronized void updateAccessToken(WxAccessToken accessToken) { + updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); + } + + @Override + public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) { + this.accessToken = accessToken; + this.expiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L; + } + + @Override + public void expireAccessToken() { + this.expiresTime = 0; + } + + @Override + public String getSecret() { + return this.secret; + } + + public void setSecret(String secret) { + this.secret = secret; + } + + @Override + public String getToken() { + return this.token; + } + + public void setToken(String token) { + this.token = token; + } + + @Override + public long getExpiresTime() { + return this.expiresTime; + } + + public void setExpiresTime(long expiresTime) { + this.expiresTime = expiresTime; + } + + @Override + public String getAesKey() { + return this.aesKey; + } + + public void setAesKey(String aesKey) { + this.aesKey = aesKey; + } + + @Override + public String getMsgDataFormat() { + return this.msgDataFormat; + } + + public void setMsgDataFormat(String msgDataFormat) { + this.msgDataFormat = msgDataFormat; + } + + @Override + public String getHttpProxyHost() { + return this.httpProxyHost; + } + + public void setHttpProxyHost(String httpProxyHost) { + this.httpProxyHost = httpProxyHost; + } + + @Override + public int getHttpProxyPort() { + return this.httpProxyPort; + } + + public void setHttpProxyPort(int httpProxyPort) { + this.httpProxyPort = httpProxyPort; + } + + @Override + public String getHttpProxyUsername() { + return this.httpProxyUsername; + } + + public void setHttpProxyUsername(String httpProxyUsername) { + this.httpProxyUsername = httpProxyUsername; + } + + @Override + public String getHttpProxyPassword() { + return this.httpProxyPassword; + } + + public void setHttpProxyPassword(String httpProxyPassword) { + this.httpProxyPassword = httpProxyPassword; + } + + @Override + public String toString() { + return ToStringUtils.toSimpleString(this); + } + + @Override + public ApacheHttpClientBuilder getApacheHttpClientBuilder() { + return this.apacheHttpClientBuilder; + } + + public void setApacheHttpClientBuilder(ApacheHttpClientBuilder apacheHttpClientBuilder) { + this.apacheHttpClientBuilder = apacheHttpClientBuilder; + } + + @Override + public boolean autoRefreshToken() { + return true; + } + + public String getAppid() { + return appid; + } + + public void setAppid(String appid) { + this.appid = appid; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaConstants.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaConstants.java new file mode 100644 index 0000000000..14016fa578 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaConstants.java @@ -0,0 +1,33 @@ +package cn.binarywang.wx.miniapp.constant; + +/** + *
+ *  小程序常量
+ * 
+ * + * @author Binary Wang + */ +public class WxMaConstants { + /** + * 素材类型 + */ + public static class MediaType { + public static final String IMAGE = "image";//图片 + } + + /** + * 消息格式 + */ + public static class MsgDataFormat { + public static final String XML = "XML"; + public static final String JSON = "JSON"; + } + + /** + * 客服消息的消息类型 + */ + public static class KefuMsgType { + public static final String TEXT = "text";//文本消息 + public static final String IMAGE = "image";//图片消息 + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaMessageHandler.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaMessageHandler.java new file mode 100644 index 0000000000..794d60e98a --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaMessageHandler.java @@ -0,0 +1,20 @@ +package cn.binarywang.wx.miniapp.message; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.WxMaMessage; +import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.session.WxSessionManager; + +import java.util.Map; + +/** + * 处理小程序推送消息的处理器接口 + * + * @author Binary Wang + */ +public interface WxMaMessageHandler { + + void handle(WxMaMessage message, Map context, + WxMaService service, WxSessionManager sessionManager) throws WxErrorException; + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaMessageInterceptor.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaMessageInterceptor.java new file mode 100644 index 0000000000..3443862fe1 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaMessageInterceptor.java @@ -0,0 +1,28 @@ +package cn.binarywang.wx.miniapp.message; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.WxMaMessage; +import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.session.WxSessionManager; + +import java.util.Map; + +/** + * 微信消息拦截器,可以用来做验证 + * + * @author Binary Wang + */ +public interface WxMaMessageInterceptor { + + /** + * 拦截微信消息 + * + * @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个 + * @return true代表OK,false代表不OK + */ + boolean intercept(WxMaMessage wxMessage, + Map context, + WxMaService wxMaService, + WxSessionManager sessionManager) throws WxErrorException; + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaMessageMatcher.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaMessageMatcher.java new file mode 100644 index 0000000000..9d4e54f89b --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaMessageMatcher.java @@ -0,0 +1,17 @@ +package cn.binarywang.wx.miniapp.message; + +import cn.binarywang.wx.miniapp.bean.WxMaMessage; + +/** + * 消息匹配器,用在消息路由的时候 + * + * @author Binary Wang + */ +public interface WxMaMessageMatcher { + + /** + * 消息是否匹配某种模式 + */ + boolean match(WxMaMessage message); + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaMessageRouter.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaMessageRouter.java new file mode 100644 index 0000000000..cbe42b1f26 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaMessageRouter.java @@ -0,0 +1,176 @@ +package cn.binarywang.wx.miniapp.message; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.WxMaMessage; +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.InternalSessionManager; +import me.chanjar.weixin.common.session.StandardSessionManager; +import me.chanjar.weixin.common.session.WxSessionManager; +import me.chanjar.weixin.common.util.LogExceptionHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +/** + * @author Binary Wang + */ +public class WxMaMessageRouter { + private static final int DEFAULT_THREAD_POOL_SIZE = 100; + private final Logger log = LoggerFactory.getLogger(WxMaMessageRouter.class); + private final List rules = new ArrayList<>(); + + private final WxMaService wxMaService; + + private ExecutorService executorService; + + private WxMessageDuplicateChecker messageDuplicateChecker; + + private WxSessionManager sessionManager; + + private WxErrorExceptionHandler exceptionHandler; + + public WxMaMessageRouter(WxMaService wxMaService) { + this.wxMaService = wxMaService; + this.executorService = Executors.newFixedThreadPool(DEFAULT_THREAD_POOL_SIZE); + this.messageDuplicateChecker = new WxMessageInMemoryDuplicateChecker(); + this.sessionManager = new StandardSessionManager(); + this.exceptionHandler = new LogExceptionHandler(); + } + + /** + *
+   * 设置自定义的 {@link ExecutorService}
+   * 如果不调用该方法,默认使用 Executors.newFixedThreadPool(100)
+   * 
+ */ + public void setExecutorService(ExecutorService executorService) { + this.executorService = executorService; + } + + /** + *
+   * 设置自定义的 {@link me.chanjar.weixin.common.api.WxMessageDuplicateChecker}
+   * 如果不调用该方法,默认使用 {@link me.chanjar.weixin.common.api.WxMessageInMemoryDuplicateChecker}
+   * 
+ */ + public void setMessageDuplicateChecker(WxMessageDuplicateChecker messageDuplicateChecker) { + this.messageDuplicateChecker = messageDuplicateChecker; + } + + /** + *
+   * 设置自定义的{@link me.chanjar.weixin.common.session.WxSessionManager}
+   * 如果不调用该方法,默认使用 {@link me.chanjar.weixin.common.session.StandardSessionManager}
+   * 
+ */ + public void setSessionManager(WxSessionManager sessionManager) { + this.sessionManager = sessionManager; + } + + /** + *
+   * 设置自定义的{@link me.chanjar.weixin.common.api.WxErrorExceptionHandler}
+   * 如果不调用该方法,默认使用 {@link me.chanjar.weixin.common.util.LogExceptionHandler}
+   * 
+ */ + public void setExceptionHandler(WxErrorExceptionHandler exceptionHandler) { + this.exceptionHandler = exceptionHandler; + } + + List getRules() { + return this.rules; + } + + /** + * 开始一个新的Route规则 + */ + public WxMaMessageRouterRule rule() { + return new WxMaMessageRouterRule(this); + } + + /** + * 处理微信消息 + */ + public void route(final WxMaMessage wxMessage, final Map context) { + final List matchRules = new ArrayList<>(); + // 收集匹配的规则 + for (final WxMaMessageRouterRule rule : this.rules) { + if (rule.test(wxMessage)) { + matchRules.add(rule); + if (!rule.isReEnter()) { + break; + } + } + } + + if (matchRules.size() == 0) { + return; + } + + final List> futures = new ArrayList<>(); + for (final WxMaMessageRouterRule rule : matchRules) { + // 返回最后一个非异步的rule的执行结果 + if (rule.isAsync()) { + futures.add( + this.executorService.submit(new Runnable() { + @Override + public void run() { + rule.service(wxMessage, context, WxMaMessageRouter.this.wxMaService, WxMaMessageRouter.this.sessionManager, WxMaMessageRouter.this.exceptionHandler); + } + }) + ); + } else { + rule.service(wxMessage, context, this.wxMaService, this.sessionManager, this.exceptionHandler); + // 在同步操作结束,session访问结束 + this.log.debug("End session access: async=false, sessionId={}", wxMessage.getFromUser()); + sessionEndAccess(wxMessage); + } + } + + if (futures.size() > 0) { + this.executorService.submit(new Runnable() { + @Override + public void run() { + for (Future future : futures) { + try { + future.get(); + WxMaMessageRouter.this.log.debug("End session access: async=true, sessionId={}", wxMessage.getFromUser()); + // 异步操作结束,session访问结束 + sessionEndAccess(wxMessage); + } catch (InterruptedException | ExecutionException e) { + WxMaMessageRouter.this.log.error("Error happened when wait task finish", e); + } + } + } + }); + } + + } + + public void route(final WxMaMessage wxMessage) { + this.route(wxMessage, new HashMap()); + } + + /** + * 对session的访问结束 + */ + protected void sessionEndAccess(WxMaMessage wxMessage) { + + InternalSession session = ((InternalSessionManager) this.sessionManager).findSession(wxMessage.getFromUser()); + if (session != null) { + session.endAccess(); + } + + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaMessageRouterRule.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaMessageRouterRule.java new file mode 100644 index 0000000000..835eb2894a --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/message/WxMaMessageRouterRule.java @@ -0,0 +1,314 @@ +package cn.binarywang.wx.miniapp.message; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.WxMaMessage; +import me.chanjar.weixin.common.api.WxErrorExceptionHandler; +import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.session.WxSessionManager; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +/** + * @author Binary Wang + */ +public class WxMaMessageRouterRule { + + private final WxMaMessageRouter routerBuilder; + + private boolean async = true; + + private String fromUser; + + private String msgType; + + private String event; + + private String eventKey; + + private String content; + + private String rContent; + + private WxMaMessageMatcher matcher; + + private boolean reEnter = false; + + private List handlers = new ArrayList<>(); + + private List interceptors = new ArrayList<>(); + + public WxMaMessageRouterRule(WxMaMessageRouter routerBuilder) { + this.routerBuilder = routerBuilder; + } + + /** + * 设置是否异步执行,默认是true + */ + public WxMaMessageRouterRule async(boolean async) { + this.async = async; + return this; + } + + /** + * 如果msgType等于某值 + */ + public WxMaMessageRouterRule msgType(String msgType) { + this.msgType = msgType; + return this; + } + + /** + * 如果event等于某值 + */ + public WxMaMessageRouterRule event(String event) { + this.event = event; + return this; + } + + /** + * 如果eventKey等于某值 + */ + public WxMaMessageRouterRule eventKey(String eventKey) { + this.eventKey = eventKey; + return this; + } + + /** + * 如果content等于某值 + */ + public WxMaMessageRouterRule content(String content) { + this.content = content; + return this; + } + + /** + * 如果content匹配该正则表达式 + */ + public WxMaMessageRouterRule rContent(String regex) { + this.rContent = regex; + return this; + } + + /** + * 如果fromUser等于某值 + */ + public WxMaMessageRouterRule fromUser(String fromUser) { + this.fromUser = fromUser; + return this; + } + + /** + * 如果消息匹配某个matcher,用在用户需要自定义更复杂的匹配规则的时候 + */ + public WxMaMessageRouterRule matcher(WxMaMessageMatcher matcher) { + this.matcher = matcher; + return this; + } + + /** + * 设置微信消息拦截器 + */ + public WxMaMessageRouterRule interceptor(WxMaMessageInterceptor interceptor) { + return interceptor(interceptor, (WxMaMessageInterceptor[]) null); + } + + /** + * 设置微信消息拦截器 + */ + public WxMaMessageRouterRule interceptor(WxMaMessageInterceptor interceptor, WxMaMessageInterceptor... otherInterceptors) { + this.interceptors.add(interceptor); + if (otherInterceptors != null && otherInterceptors.length > 0) { + for (WxMaMessageInterceptor i : otherInterceptors) { + this.interceptors.add(i); + } + } + return this; + } + + /** + * 设置微信消息处理器 + */ + public WxMaMessageRouterRule handler(WxMaMessageHandler handler) { + return handler(handler, (WxMaMessageHandler[]) null); + } + + /** + * 设置微信消息处理器 + */ + public WxMaMessageRouterRule handler(WxMaMessageHandler handler, WxMaMessageHandler... otherHandlers) { + this.handlers.add(handler); + if (otherHandlers != null && otherHandlers.length > 0) { + for (WxMaMessageHandler i : otherHandlers) { + this.handlers.add(i); + } + } + return this; + } + + /** + * 规则结束,代表如果一个消息匹配该规则,那么它将不再会进入其他规则 + */ + public WxMaMessageRouter end() { + this.routerBuilder.getRules().add(this); + return this.routerBuilder; + } + + /** + * 规则结束,但是消息还会进入其他规则 + */ + public WxMaMessageRouter next() { + this.reEnter = true; + return end(); + } + + /** + * 将微信自定义的事件修正为不区分大小写, + * 比如框架定义的事件常量为click,但微信传递过来的却是CLICK + */ + protected boolean test(WxMaMessage wxMessage) { + return + (this.fromUser == null || this.fromUser.equals(wxMessage.getFromUser())) + && + (this.msgType == null || this.msgType.toLowerCase().equals((wxMessage.getMsgType() == null ? null : wxMessage.getMsgType().toLowerCase()))) + && + (this.event == null || this.event.toLowerCase().equals((wxMessage.getEvent() == null ? null : wxMessage.getEvent().toLowerCase()))) + && + (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)) + ; + } + + /** + * 处理微信推送过来的消息 + */ + protected void service(WxMaMessage wxMessage, + Map context, + WxMaService wxMaService, + WxSessionManager sessionManager, + WxErrorExceptionHandler exceptionHandler) { + if (context == null) { + context = new HashMap<>(); + } + + try { + // 如果拦截器不通过 + for (WxMaMessageInterceptor interceptor : this.interceptors) { + if (!interceptor.intercept(wxMessage, context, wxMaService, sessionManager)) { + return; + } + } + + // 交给handler处理 + for (WxMaMessageHandler handler : this.handlers) { + // 返回最后handler的结果 + if (handler == null) { + continue; + } + handler.handle(wxMessage, context, wxMaService, sessionManager); + } + } catch (WxErrorException e) { + exceptionHandler.handle(e); + } + } + + public WxMaMessageRouter getRouterBuilder() { + return this.routerBuilder; + } + + public boolean isAsync() { + return this.async; + } + + public void setAsync(boolean async) { + this.async = async; + } + + public String getFromUser() { + return this.fromUser; + } + + public void setFromUser(String fromUser) { + this.fromUser = fromUser; + } + + public String getMsgType() { + return this.msgType; + } + + public void setMsgType(String msgType) { + this.msgType = msgType; + } + + public String getEvent() { + return this.event; + } + + public void setEvent(String event) { + this.event = event; + } + + public String getEventKey() { + return this.eventKey; + } + + public void setEventKey(String eventKey) { + this.eventKey = eventKey; + } + + public String getContent() { + return this.content; + } + + public void setContent(String content) { + this.content = content; + } + + public String getrContent() { + return this.rContent; + } + + public void setrContent(String rContent) { + this.rContent = rContent; + } + + public WxMaMessageMatcher getMatcher() { + return this.matcher; + } + + public void setMatcher(WxMaMessageMatcher matcher) { + this.matcher = matcher; + } + + public boolean isReEnter() { + return this.reEnter; + } + + public void setReEnter(boolean reEnter) { + this.reEnter = reEnter; + } + + public List getHandlers() { + return this.handlers; + } + + public void setHandlers(List handlers) { + this.handlers = handlers; + } + + public List getInterceptors() { + return this.interceptors; + } + + public void setInterceptors(List interceptors) { + this.interceptors = interceptors; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/crypt/WxMaCryptUtils.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/crypt/WxMaCryptUtils.java new file mode 100644 index 0000000000..e05bac5fb7 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/crypt/WxMaCryptUtils.java @@ -0,0 +1,43 @@ +package cn.binarywang.wx.miniapp.util.crypt; + +import cn.binarywang.wx.miniapp.config.WxMaConfig; +import me.chanjar.weixin.common.util.crypto.PKCS7Encoder; +import org.apache.commons.codec.binary.Base64; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.StandardCharsets; +import java.security.AlgorithmParameters; + +/** + * @author Binary Wang + */ +public class WxMaCryptUtils extends me.chanjar.weixin.common.util.crypto.WxCryptUtil { + public WxMaCryptUtils(WxMaConfig config) { + this.appidOrCorpid = config.getAppid(); + this.token = config.getToken(); + this.aesKey = Base64.decodeBase64(config.getAesKey() + "="); + } + + /** + * AES解密 + * + * @param encryptedData 消息密文 + * @param ivStr iv字符串 + */ + public static String decrypt(String sessionKey, String encryptedData, String ivStr) { + try { + AlgorithmParameters params = AlgorithmParameters.getInstance("AES"); + params.init(new IvParameterSpec(Base64.decodeBase64(ivStr))); + + Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); + cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(Base64.decodeBase64(sessionKey), "AES"), params); + + return new String(PKCS7Encoder.decode(cipher.doFinal(Base64.decodeBase64(encryptedData))), StandardCharsets.UTF_8); + } catch (Exception e) { + throw new RuntimeException("AES解密失败", e); + } + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/http/QrCodeRequestExecutor.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/http/QrCodeRequestExecutor.java new file mode 100644 index 0000000000..3c026f4a18 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/http/QrCodeRequestExecutor.java @@ -0,0 +1,61 @@ +package cn.binarywang.wx.miniapp.util.http; + +import cn.binarywang.wx.miniapp.bean.WxMaQrcodeWrapper; +import me.chanjar.weixin.common.bean.result.WxError; +import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.util.fs.FileUtils; +import me.chanjar.weixin.common.util.http.RequestExecutor; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.apache.InputStreamResponseHandler; +import me.chanjar.weixin.common.util.http.apache.Utf8ResponseHandler; +import org.apache.http.Header; +import org.apache.http.HttpHost; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.UUID; + +/** + * @author Binary Wang + */ +public class QrCodeRequestExecutor implements RequestExecutor { + protected RequestHttp requestHttp; + + public QrCodeRequestExecutor(RequestHttp requestHttp) { + this.requestHttp = requestHttp; + } + + @Override + public File execute(String uri, WxMaQrcodeWrapper ticket) throws WxErrorException, IOException { + HttpPost httpPost = new HttpPost(uri); + if (requestHttp.getRequestHttpProxy() != null) { + httpPost + .setConfig(RequestConfig.custom() + .setProxy(requestHttp.getRequestHttpProxy()) + .build() + ); + } + httpPost.setEntity(new StringEntity(ticket.toString())); + + try (CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpPost); + InputStream inputStream = InputStreamResponseHandler.INSTANCE.handleResponse(response);) { + Header[] contentTypeHeader = response.getHeaders("Content-Type"); + if (contentTypeHeader != null && contentTypeHeader.length > 0 + && ContentType.APPLICATION_JSON.getMimeType().equals(contentTypeHeader[0].getValue())) { + String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); + throw new WxErrorException(WxError.fromJson(responseContent)); + } + + return FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), "jpg"); + } finally { + httpPost.releaseConnection(); + } + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaGsonBuilder.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaGsonBuilder.java new file mode 100644 index 0000000000..4eb3a8adf0 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaGsonBuilder.java @@ -0,0 +1,24 @@ +package cn.binarywang.wx.miniapp.util.json; + +import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage; +import cn.binarywang.wx.miniapp.bean.WxMaTemplateMessage; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +/** + * @author Binary Wang + */ +public class WxMaGsonBuilder { + private static final GsonBuilder INSTANCE = new GsonBuilder(); + + static { + INSTANCE.disableHtmlEscaping(); + INSTANCE.registerTypeAdapter(WxMaKefuMessage.class, new WxMaKefuMessageGsonAdapter()); + INSTANCE.registerTypeAdapter(WxMaTemplateMessage.class, new WxMaTemplateMessageGsonAdapter()); + } + + public static Gson create() { + return INSTANCE.create(); + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaKefuMessageGsonAdapter.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaKefuMessageGsonAdapter.java new file mode 100644 index 0000000000..829469f872 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaKefuMessageGsonAdapter.java @@ -0,0 +1,46 @@ +/* + * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. + * + * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended + * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction + * arose from modification of the original source, or other redistribution of this source + * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. + */ +package cn.binarywang.wx.miniapp.util.json; + +import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage; +import cn.binarywang.wx.miniapp.constant.WxMaConstants; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +import java.lang.reflect.Type; + +/** + * @author Binary Wang + */ +public class WxMaKefuMessageGsonAdapter implements JsonSerializer { + + @Override + public JsonElement serialize(WxMaKefuMessage message, Type typeOfSrc, JsonSerializationContext context) { + JsonObject messageJson = new JsonObject(); + messageJson.addProperty("touser", message.getToUser()); + messageJson.addProperty("msgtype", message.getMsgType()); + + if (WxMaConstants.KefuMsgType.TEXT.equals(message.getMsgType())) { + JsonObject text = new JsonObject(); + text.addProperty("content", message.getContent()); + messageJson.add("text", text); + } + + if (WxMaConstants.KefuMsgType.IMAGE.equals(message.getMsgType())) { + JsonObject image = new JsonObject(); + image.addProperty("media_id", message.getMediaId()); + messageJson.add("image", image); + } + + return messageJson; + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaTemplateMessageGsonAdapter.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaTemplateMessageGsonAdapter.java new file mode 100644 index 0000000000..0b4edee286 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaTemplateMessageGsonAdapter.java @@ -0,0 +1,60 @@ +package cn.binarywang.wx.miniapp.util.json; + +import cn.binarywang.wx.miniapp.bean.WxMaTemplateMessage; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +import java.lang.reflect.Type; + +/** + * @author Binary Wang + */ +public class WxMaTemplateMessageGsonAdapter implements JsonSerializer { + + @Override + public JsonElement serialize(WxMaTemplateMessage message, Type typeOfSrc, JsonSerializationContext context) { + JsonObject messageJson = new JsonObject(); + messageJson.addProperty("touser", message.getToUser()); + messageJson.addProperty("template_id", message.getTemplateId()); + if (message.getPage() != null) { + messageJson.addProperty("page", message.getPage()); + } + + if (message.getFormId() != null) { + messageJson.addProperty("form_id", message.getFormId()); + } + + if (message.getPage() != null) { + messageJson.addProperty("page", message.getPage()); + } + + if (message.getColor() != null) { + messageJson.addProperty("color", message.getColor()); + } + + if (message.getEmphasisKeyword() != null) { + messageJson.addProperty("emphasis_keyword", message.getEmphasisKeyword()); + } + + JsonObject data = new JsonObject(); + messageJson.add("data", data); + + if (message.getData() == null) { + return messageJson; + } + + for (WxMaTemplateMessage.Data datum : message.getData()) { + JsonObject dataJson = new JsonObject(); + dataJson.addProperty("value", datum.getValue()); + if (datum.getColor() != null) { + dataJson.addProperty("color", datum.getColor()); + } + data.add(datum.getName(), dataJson); + } + + return messageJson; + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/xml/XStreamTransformer.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/xml/XStreamTransformer.java new file mode 100644 index 0000000000..f85c716acb --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/xml/XStreamTransformer.java @@ -0,0 +1,88 @@ +package cn.binarywang.wx.miniapp.util.xml; + +import cn.binarywang.wx.miniapp.bean.WxMaMessage; +import com.thoughtworks.xstream.XStream; +import me.chanjar.weixin.common.util.xml.XStreamInitializer; + +import java.io.InputStream; +import java.util.*; + +/** + * @author Binary Wang + */ +public class XStreamTransformer { + private static final Map, XStream> CLASS_2_XSTREAM_INSTANCE = new HashMap<>(); + + static { + registerClass(WxMaMessage.class); + } + + /** + * xml -> pojo + */ + @SuppressWarnings("unchecked") + public static T fromXml(Class clazz, String xml) { + T object = (T) CLASS_2_XSTREAM_INSTANCE.get(clazz).fromXML(xml); + return object; + } + + @SuppressWarnings("unchecked") + public static T fromXml(Class clazz, InputStream is) { + T object = (T) CLASS_2_XSTREAM_INSTANCE.get(clazz).fromXML(is); + return object; + } + + /** + * pojo -> xml + */ + public static String toXml(Class clazz, T object) { + return CLASS_2_XSTREAM_INSTANCE.get(clazz).toXML(object); + } + + /** + * 注册扩展消息的解析器 + * + * @param clz 类型 + * @param xStream xml解析器 + */ + private static void register(Class clz, XStream xStream) { + CLASS_2_XSTREAM_INSTANCE.put(clz, xStream); + } + + /** + * 会自动注册该类及其子类 + * + * @param clz 要注册的类 + */ + private static void registerClass(Class clz) { + XStream xstream = XStreamInitializer.getInstance(); + xstream.processAnnotations(clz); + xstream.processAnnotations(getInnerClasses(clz)); + if (clz.equals(WxMaMessage.class)) { + // 操蛋的微信,模板消息推送成功的消息是MsgID,其他消息推送过来是MsgId + xstream.aliasField("MsgID", WxMaMessage.class, "msgId"); + } + + register(clz, xstream); + } + + private static Class[] getInnerClasses(Class clz) { + Class[] innerClasses = clz.getClasses(); + if (innerClasses == null) { + return null; + } + + List> result = new ArrayList<>(); + result.addAll(Arrays.asList(innerClasses)); + for (Class inner : innerClasses) { + Class[] innerClz = getInnerClasses(inner); + if (innerClz == null) { + continue; + } + + result.addAll(Arrays.asList(innerClz)); + } + + return result.toArray(new Class[0]); + } +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaMediaServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaMediaServiceImplTest.java new file mode 100644 index 0000000000..825ad05f76 --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaMediaServiceImplTest.java @@ -0,0 +1,52 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.test.ApiTestModule; +import com.google.inject.Inject; +import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; +import me.chanjar.weixin.common.exception.WxErrorException; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + +/** + * 临时素材接口的测试 + * + * @author Binary Wang + */ +@Test +@Guice(modules = ApiTestModule.class) +public class WxMaMediaServiceImplTest { + @Inject + protected WxMaService wxService; + + private String mediaId; + + @Test + public void testUploadMedia() throws WxErrorException, IOException { + String mediaType = "image"; + String fileType = "png"; + String fileName = "tmp.png"; + try (InputStream inputStream = ClassLoader.getSystemResourceAsStream(fileName)) { + WxMediaUploadResult res = this.wxService.getMediaService().uploadMedia(mediaType, fileType, inputStream); + assertNotNull(res.getType()); + assertNotNull(res.getCreatedAt()); + assertTrue(res.getMediaId() != null || res.getThumbMediaId() != null); + this.mediaId = res.getMediaId(); + System.out.println(res); + } + } + + @Test(dependsOnMethods = {"testUploadMedia"}) + public void testGetMedia() throws WxErrorException { + File file = this.wxService.getMediaService().getMedia(this.mediaId); + assertNotNull(file); + System.out.println(file.getAbsolutePath()); + } +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaMsgServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaMsgServiceImplTest.java new file mode 100644 index 0000000000..37e3608bff --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaMsgServiceImplTest.java @@ -0,0 +1,73 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage; +import cn.binarywang.wx.miniapp.bean.WxMaTemplateMessage; +import cn.binarywang.wx.miniapp.test.ApiTestModule; +import cn.binarywang.wx.miniapp.test.TestConfig; +import com.google.common.collect.Lists; +import com.google.inject.Inject; +import me.chanjar.weixin.common.api.WxConsts; +import me.chanjar.weixin.common.exception.WxErrorException; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * 测试客服相关接口 + * + * @author Binary Wang + */ +@Test +@Guice(modules = ApiTestModule.class) +public class WxMaMsgServiceImplTest { + + @Inject + protected WxMaService wxService; + + public void testSendKefuMpNewsMessage() throws WxErrorException { + TestConfig configStorage = (TestConfig) this.wxService + .getWxMaConfig(); + WxMaKefuMessage message = new WxMaKefuMessage(); + message.setMsgType(WxConsts.CUSTOM_MSG_MPNEWS); + message.setToUser(configStorage.getOpenid()); + + this.wxService.getMsgService().sendKefuMsg(message); + } + + public void testSendKefuMessage() throws WxErrorException { + TestConfig config = (TestConfig) this.wxService + .getWxMaConfig(); + WxMaKefuMessage message = new WxMaKefuMessage(); + message.setMsgType(WxConsts.CUSTOM_MSG_TEXT); + message.setToUser(config.getOpenid()); + message.setContent( + "欢迎欢迎,热烈欢迎\n换行测试\n超链接:Hello World"); + + this.wxService.getMsgService().sendKefuMsg(message); + } + + @Test(invocationCount = 5, threadPoolSize = 3) + public void testSendTemplateMsg() throws WxErrorException { + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + TestConfig config = (TestConfig) this.wxService.getWxMaConfig(); + + WxMaTemplateMessage templateMessage = WxMaTemplateMessage.newBuilder() + .toUser(config.getOpenid()) + .formId("FORMID") + .page("index") + .data(Lists.newArrayList( + new WxMaTemplateMessage.Data("keyword1", "339208499", "#173177"), + new WxMaTemplateMessage.Data("keyword2", dateFormat.format(new Date()), "#173177"), + new WxMaTemplateMessage.Data("keyword3", "粤海喜来登酒店", "#173177"), + new WxMaTemplateMessage.Data("keyword4", "广州市天河区天河路208号", "#173177"))) + .templateId(config.getTemplateId()) + .emphasisKeyword("keyword1.DATA") + .build(); + + this.wxService.getMsgService().sendTemplateMsg(templateMessage); + } + +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaQrcodeServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaQrcodeServiceImplTest.java new file mode 100644 index 0000000000..eba65f7c34 --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaQrcodeServiceImplTest.java @@ -0,0 +1,38 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.test.ApiTestModule; +import com.google.inject.Inject; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import java.io.File; + +/** + * @author Binary Wang + */ +@Test +@Guice(modules = ApiTestModule.class) +public class WxMaQrcodeServiceImplTest { + @Inject + protected WxMaService wxService; + + @Test + public void testCreateQrCode() throws Exception { + final File qrCode = this.wxService.getQrcodeService().createQrcode("111", 122); + System.out.println(qrCode); + } + + @Test + public void testCreateWxCode() throws Exception { + final File wxCode = this.wxService.getQrcodeService().createWxCode("111", 122); + System.out.println(wxCode); + } + + @Test + public void testCreateWxCodeLimit() throws Exception { + final File wxCode = this.wxService.getQrcodeService().createWxCodeLimit("111", null); + System.out.println(wxCode); + } + +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImplTest.java new file mode 100644 index 0000000000..1c99530d36 --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImplTest.java @@ -0,0 +1,35 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.config.WxMaConfig; +import cn.binarywang.wx.miniapp.test.ApiTestModule; +import com.google.inject.Inject; +import me.chanjar.weixin.common.exception.WxErrorException; +import org.apache.commons.lang3.StringUtils; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertNotEquals; +import static org.testng.Assert.assertTrue; + +/** + * @author Binary Wang + */ +@Test +@Guice(modules = ApiTestModule.class) +public class WxMaServiceImplTest { + + @Inject + private WxMaService wxService; + + public void testRefreshAccessToken() throws WxErrorException { + WxMaConfig configStorage = this.wxService.getWxMaConfig(); + String before = configStorage.getAccessToken(); + this.wxService.getAccessToken(false); + + String after = configStorage.getAccessToken(); + assertNotEquals(before, after); + assertTrue(StringUtils.isNotBlank(after)); + } + +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImplTest.java new file mode 100644 index 0000000000..a19692763b --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImplTest.java @@ -0,0 +1,46 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.WxMaUserInfo; +import cn.binarywang.wx.miniapp.test.ApiTestModule; +import com.google.inject.Inject; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + +/** + * 测试用户相关的接口 + * + * @author Binary Wang + */ +@Test +@Guice(modules = ApiTestModule.class) +public class WxMaUserServiceImplTest { + + @Inject + private WxMaService wxService; + + @Test + public void testGetSessionKey() throws Exception { + assertNotNull(this.wxService.getUserService().getSessionInfo("aaa")); + } + + @Test + public void testGetUserInfo() throws Exception { + WxMaUserInfo userInfo = this.wxService.getUserService().getUserInfo("tiihtNczf5v6AKRyjwEUhQ==", + "CiyLU1Aw2KjvrjMdj8YKliAjtP4gsMZMQmRzooG2xrDcvSnxIMXFufNstNGTyaGS9uT5geRa0W4oTOb1WT7fJlAC+oNPdbB+3hVbJSRgv+4lGOETKUQz6OYStslQ142dNCuabNPGBzlooOmB231qMM85d2/fV6ChevvXvQP8Hkue1poOFtnEtpyxVLW1zAo6/1Xx1COxFvrc2d7UL/lmHInNlxuacJXwu0fjpXfz/YqYzBIBzD6WUfTIF9GRHpOn/Hz7saL8xz+W//FRAUid1OksQaQx4CMs8LOddcQhULW4ucetDf96JcR3g0gfRK4PC7E/r7Z6xNrXd2UIeorGj5Ef7b1pJAYB6Y5anaHqZ9J6nKEBvB4DnNLIVWSgARns/8wR2SiRS7MNACwTyrGvt9ts8p12PKFdlqYTopNHR1Vf7XjfhQlVsAJdNiKdYmYVoKlaRv85IfVunYzO0IKXsyl7JCUjCpoG20f0a04COwfneQAGGwd5oa+T8yO5hzuyDb/XcxxmK01EpqOyuxINew==", + "r7BXXKkLb8qrSNn05n0qiA=="); + assertNotNull(userInfo); + System.out.println(userInfo.toString()); + } + + @Test + public void testCheckUserInfo() throws Exception { + assertTrue(this.wxService.getUserService().checkUserInfo("HyVFkGl5F5OQWJZZaNzBBg==", + "{\"nickName\":\"Band\",\"gender\":1,\"language\":\"zh_CN\",\"city\":\"Guangzhou\",\"province\":\"Guangdong\",\"country\":\"CN\",\"avatarUrl\":\"http://wx.qlogo.cn/mmopen/vi_32/1vZvI39NWFQ9XM4LtQpFrQJ1xlgZxx3w7bQxKARol6503Iuswjjn6nIGBiaycAjAtpujxyzYsrztuuICqIM5ibXQ/0\"}", + "75e81ceda165f4ffa64f4068af58c64b8f54b88c")); + } + +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/WxMaKefuMessageTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/WxMaKefuMessageTest.java new file mode 100644 index 0000000000..f2ef8bfac5 --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/WxMaKefuMessageTest.java @@ -0,0 +1,40 @@ +package cn.binarywang.wx.miniapp.bean; + +import me.chanjar.weixin.common.api.WxConsts; +import org.testng.Assert; +import org.testng.annotations.Test; + +/** + * @author Binary Wang + */ +@Test +public class WxMaKefuMessageTest { + + public void testTextReply() { + WxMaKefuMessage reply = new WxMaKefuMessage(); + reply.setToUser("OPENID"); + reply.setMsgType(WxConsts.CUSTOM_MSG_TEXT); + reply.setContent("sfsfdsdf"); + Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"text\",\"text\":{\"content\":\"sfsfdsdf\"}}"); + } + + public void testTextBuild() { + WxMaKefuMessage reply = WxMaKefuMessage.TEXT().toUser("OPENID").content("sfsfdsdf").build(); + Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"text\",\"text\":{\"content\":\"sfsfdsdf\"}}"); + } + + public void testImageReply() { + WxMaKefuMessage reply = new WxMaKefuMessage(); + reply.setToUser("OPENID"); + reply.setMsgType(WxConsts.CUSTOM_MSG_IMAGE); + reply.setMediaId("MEDIA_ID"); + Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"image\",\"image\":{\"media_id\":\"MEDIA_ID\"}}"); + } + + public void testImageBuild() { + WxMaKefuMessage reply = WxMaKefuMessage.IMAGE().toUser("OPENID").mediaId("MEDIA_ID").build(); + Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"image\",\"image\":{\"media_id\":\"MEDIA_ID\"}}"); + } + + +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/WxMaMessageTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/WxMaMessageTest.java new file mode 100644 index 0000000000..cdf989a6af --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/WxMaMessageTest.java @@ -0,0 +1,129 @@ +package cn.binarywang.wx.miniapp.bean; + +import me.chanjar.weixin.common.api.WxConsts; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/** + * @author Binary Wang + */ +@Test +public class WxMaMessageTest { + + public void testFromXml() { + String xml = "" + + "" + + " " + + "1348831860" + + "" + + "" + + "1234567890123456" + + "" + + "" + + "" + + "" + + "23.134521" + + "113.358803" + + "20" + + "" + + "" + + "" + + "<![CDATA[公众平台官网链接]]>" + + "" + + "" + + "" + + "23.137466" + + "113.352425" + + "119.385040" + + "" + + " " + + " " + + "" + + "" + + " 1\n" + + " " + + " " + + " " + + " " + + " " + + "" + + "" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "" + + ""; + WxMaMessage wxMessage = WxMaMessage.fromXml(xml); + assertEquals(wxMessage.getToUser(), "toUser"); + assertEquals(wxMessage.getFromUser(), "fromUser"); + assertEquals(wxMessage.getCreateTime(), new Long(1348831860L)); + assertEquals(wxMessage.getMsgType(), WxConsts.XML_MSG_TEXT); + assertEquals(wxMessage.getContent(), "this is a test"); + assertEquals(wxMessage.getMsgId(), new Long(1234567890123456L)); + assertEquals(wxMessage.getPicUrl(), "this is a url"); + assertEquals(wxMessage.getMediaId(), "media_id"); + assertEquals(wxMessage.getEvent(), "subscribe"); + } + + public void testFromXml2() { + + String xml = "" + + "" + + " " + + "1348831860" + + "" + + "" + + "1234567890123456" + + "" + + "" + + "" + + "" + + "23.134521" + + "113.358803" + + "20" + + "" + + "" + + "" + + "<![CDATA[公众平台官网链接]]>" + + "" + + "" + + "" + + "23.137466" + + "113.352425" + + "119.385040" + + "" + + " " + + " " + + "" + + "" + + " 1\n" + + " " + + " " + + " " + + " " + + " " + + "" + + "" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "" + + ""; + WxMaMessage wxMessage = WxMaMessage.fromXml(xml); + assertEquals(wxMessage.getToUser(), "toUser"); + assertEquals(wxMessage.getFromUser(), "fromUser"); + assertEquals(wxMessage.getCreateTime(), new Integer(1348831860)); + assertEquals(wxMessage.getMsgType(), WxConsts.XML_MSG_TEXT); + assertEquals(wxMessage.getContent(), "this is a test"); + assertEquals(wxMessage.getMsgId(), new Long(1234567890123456L)); + assertEquals(wxMessage.getPicUrl(), "this is a url"); + assertEquals(wxMessage.getMediaId(), "media_id"); + assertEquals(wxMessage.getEvent(), "subscribe"); + } + +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/WxMaTemplateMessageTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/WxMaTemplateMessageTest.java new file mode 100644 index 0000000000..ad6e62c2ca --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/WxMaTemplateMessageTest.java @@ -0,0 +1,31 @@ +package cn.binarywang.wx.miniapp.bean; + +import com.google.common.collect.Lists; +import org.testng.annotations.Test; + +import static org.testng.AssertJUnit.assertEquals; + +/** + * @author Binary Wang + */ +public class WxMaTemplateMessageTest { + @Test + public void testToJson() throws Exception { + WxMaTemplateMessage tm = WxMaTemplateMessage.newBuilder() + .toUser("OPENID") + //.color("aaaaa") + .formId("FORMID") + .page("index") + .data(Lists.newArrayList( + new WxMaTemplateMessage.Data("keyword1", "339208499", "#173177"), + new WxMaTemplateMessage.Data("keyword2", "2015年01月05日12:30", "#173177"), + new WxMaTemplateMessage.Data("keyword3", "粤海喜来登酒店", "#173177"), + new WxMaTemplateMessage.Data("keyword4", "广州市天河区天河路208号", "#173177"))) + .templateId("TEMPLATE_ID") + .emphasisKeyword("keyword1.DATA") + .build(); + + assertEquals(tm.toJson(), "{\"touser\":\"OPENID\",\"template_id\":\"TEMPLATE_ID\",\"page\":\"index\",\"form_id\":\"FORMID\",\"emphasis_keyword\":\"keyword1.DATA\",\"data\":{\"keyword1\":{\"value\":\"339208499\",\"color\":\"#173177\"},\"keyword2\":{\"value\":\"2015年01月05日12:30\",\"color\":\"#173177\"},\"keyword3\":{\"value\":\"粤海喜来登酒店\",\"color\":\"#173177\"},\"keyword4\":{\"value\":\"广州市天河区天河路208号\",\"color\":\"#173177\"}}}"); + } + +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/demo/WxMaDemoServer.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/demo/WxMaDemoServer.java new file mode 100644 index 0000000000..f6dcbae27b --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/demo/WxMaDemoServer.java @@ -0,0 +1,148 @@ +package cn.binarywang.wx.miniapp.demo; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl; +import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage; +import cn.binarywang.wx.miniapp.bean.WxMaMessage; +import cn.binarywang.wx.miniapp.bean.WxMaTemplateMessage; +import cn.binarywang.wx.miniapp.config.WxMaConfig; +import cn.binarywang.wx.miniapp.constant.WxMaConstants; +import cn.binarywang.wx.miniapp.message.WxMaMessageHandler; +import cn.binarywang.wx.miniapp.message.WxMaMessageRouter; +import cn.binarywang.wx.miniapp.test.TestConfig; +import com.google.common.collect.Lists; +import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; +import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.session.WxSessionManager; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletHandler; +import org.eclipse.jetty.servlet.ServletHolder; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; +import java.util.concurrent.locks.ReentrantLock; + +/** + * @author Binary Wang + */ +public class WxMaDemoServer { + + private static final WxMaMessageHandler logHandler = new WxMaMessageHandler() { + @Override + public void handle(WxMaMessage wxMessage, Map context, + WxMaService service, WxSessionManager sessionManager) throws WxErrorException { + System.out.println("收到消息:" + wxMessage.toString()); + service.getMsgService().sendKefuMsg(WxMaKefuMessage.TEXT().content("收到信息为:" + wxMessage.toJson()) + .toUser(wxMessage.getFromUser()).build()); + } + }; + + private static final WxMaMessageHandler textHandler = new WxMaMessageHandler() { + @Override + public void handle(WxMaMessage wxMessage, Map context, + WxMaService service, WxSessionManager sessionManager) + throws WxErrorException { + service.getMsgService().sendKefuMsg(WxMaKefuMessage.TEXT().content("回复文本消息") + .toUser(wxMessage.getFromUser()).build()); + } + + }; + + private static final WxMaMessageHandler picHandler = new WxMaMessageHandler() { + @Override + public void handle(WxMaMessage wxMessage, Map context, + WxMaService service, WxSessionManager sessionManager) throws WxErrorException { + try { + WxMediaUploadResult uploadResult = service.getMediaService() + .uploadMedia(WxMaConstants.MediaType.IMAGE, "png", + ClassLoader.getSystemResourceAsStream("tmp.png")); + service.getMsgService().sendKefuMsg( + WxMaKefuMessage + .IMAGE() + .mediaId(uploadResult.getMediaId()) + .toUser(wxMessage.getFromUser()) + .build()); + } catch (WxErrorException e) { + e.printStackTrace(); + } + } + }; + + private static final WxMaMessageHandler qrcodeHandler = new WxMaMessageHandler() { + @Override + public void handle(WxMaMessage wxMessage, Map context, + WxMaService service, WxSessionManager sessionManager) throws WxErrorException { + try { + final File file = service.getQrcodeService().createQrcode("123", 430); + WxMediaUploadResult uploadResult = service.getMediaService().uploadMedia(WxMaConstants.MediaType.IMAGE, file); + service.getMsgService().sendKefuMsg( + WxMaKefuMessage + .IMAGE() + .mediaId(uploadResult.getMediaId()) + .toUser(wxMessage.getFromUser()) + .build()); + } catch (WxErrorException e) { + e.printStackTrace(); + } + } + }; + + private static final WxMaMessageHandler templateMsgHandler = new WxMaMessageHandler() { + @Override + public void handle(WxMaMessage wxMessage, Map context, + WxMaService service, WxSessionManager sessionManager) + throws WxErrorException { + service.getMsgService().sendTemplateMsg(WxMaTemplateMessage.newBuilder() + .templateId(templateId).data(Lists.newArrayList( + new WxMaTemplateMessage.Data("keyword1", "339208499", "#173177"))) + .toUser(wxMessage.getFromUser()) + .formId("自己替换可用的formid") + .build()); + } + + }; + + private static WxMaConfig config; + private static WxMaService service; + private static WxMaMessageRouter router; + private static String templateId; + + public static void main(String[] args) throws Exception { + init(); + + Server server = new Server(8080); + + ServletHandler servletHandler = new ServletHandler(); + server.setHandler(servletHandler); + + ServletHolder endpointServletHolder = new ServletHolder(new WxMaPortalServlet(config, service, router)); + servletHandler.addServletWithMapping(endpointServletHolder, "/*"); + + server.start(); + server.join(); + } + + private static void init() { + try (InputStream is1 = ClassLoader.getSystemResourceAsStream("test-config.xml")) { + TestConfig config = TestConfig.fromXml(is1); + config.setAccessTokenLock(new ReentrantLock()); + templateId = config.getTemplateId(); + + WxMaDemoServer.config = config; + service = new WxMaServiceImpl(); + service.setWxMaConfig(config); + + router = new WxMaMessageRouter(service); + + router.rule().handler(logHandler).next() + .rule().async(false).content("模板").handler(templateMsgHandler).end() + .rule().async(false).content("文本").handler(textHandler).end() + .rule().async(false).content("图片").handler(picHandler).end() + .rule().async(false).content("二维码").handler(qrcodeHandler).end(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/demo/WxMaPortalServlet.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/demo/WxMaPortalServlet.java new file mode 100644 index 0000000000..5cadb68925 --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/demo/WxMaPortalServlet.java @@ -0,0 +1,92 @@ +package cn.binarywang.wx.miniapp.demo; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.WxMaMessage; +import cn.binarywang.wx.miniapp.config.WxMaConfig; +import cn.binarywang.wx.miniapp.constant.WxMaConstants; +import cn.binarywang.wx.miniapp.message.WxMaMessageRouter; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; + +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Objects; + +/** + * @author Binary Wang + */ +public class WxMaPortalServlet extends HttpServlet { + private static final long serialVersionUID = 1L; + + private WxMaConfig wxMaConfig; + private WxMaService wxMaService; + private WxMaMessageRouter wxMaMessageRouter; + + WxMaPortalServlet(WxMaConfig wxMaConfig, WxMaService wxMaService, + WxMaMessageRouter wxMaMessageRouter) { + this.wxMaConfig = wxMaConfig; + this.wxMaService = wxMaService; + this.wxMaMessageRouter = wxMaMessageRouter; + } + + @Override + protected void service(HttpServletRequest request, HttpServletResponse response) + throws IOException { + response.setContentType("text/html;charset=utf-8"); + response.setStatus(HttpServletResponse.SC_OK); + + String signature = request.getParameter("signature"); + String nonce = request.getParameter("nonce"); + String timestamp = request.getParameter("timestamp"); + + if (!this.wxMaService.checkSignature(timestamp, nonce, signature)) { + // 消息签名不正确,说明不是公众平台发过来的消息 + response.getWriter().println("非法请求"); + return; + } + + String echoStr = request.getParameter("echostr"); + if (StringUtils.isNotBlank(echoStr)) { + // 说明是一个仅仅用来验证的请求,回显echostr + response.getWriter().println(echoStr); + return; + } + + String encryptType = request.getParameter("encrypt_type"); + final boolean isJson = Objects.equals(this.wxMaConfig.getMsgDataFormat(), WxMaConstants.MsgDataFormat.JSON); + if (StringUtils.isBlank(encryptType)) { + // 明文传输的消息 + WxMaMessage inMessage; + if (isJson) { + inMessage = WxMaMessage.fromJson(IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_8)); + } else {//xml + inMessage = WxMaMessage.fromXml(request.getInputStream()); + } + + this.wxMaMessageRouter.route(inMessage); + response.getWriter().write("success"); + return; + } + + if ("aes".equals(encryptType)) { + // 是aes加密的消息 + String msgSignature = request.getParameter("msg_signature"); + WxMaMessage inMessage; + if (isJson) { + inMessage = WxMaMessage.fromEncryptedJson(request.getInputStream(), this.wxMaConfig); + } else {//xml + inMessage = WxMaMessage.fromEncryptedXml(request.getInputStream(), this.wxMaConfig, timestamp, nonce, msgSignature); + } + + this.wxMaMessageRouter.route(inMessage); + response.getWriter().write("success"); + return; + } + + response.getWriter().println("不可识别的加密类型"); + } + +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/test/ApiTestModule.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/test/ApiTestModule.java new file mode 100644 index 0000000000..3b569e1b89 --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/test/ApiTestModule.java @@ -0,0 +1,33 @@ +package cn.binarywang.wx.miniapp.test; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.config.WxMaConfig; +import com.google.inject.Binder; +import com.google.inject.Module; + +import java.io.IOException; +import java.io.InputStream; +import java.util.concurrent.locks.ReentrantLock; + +/** + * @author Binary Wang + */ +public class ApiTestModule implements Module { + + @Override + public void configure(Binder binder) { + try (InputStream inputStream = ClassLoader.getSystemResourceAsStream("test-config.xml")) { + TestConfig config = TestConfig.fromXml(inputStream); + config.setAccessTokenLock(new ReentrantLock()); + + WxMaService wxService = new cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl(); + wxService.setWxMaConfig(config); + + binder.bind(WxMaService.class).toInstance(wxService); + binder.bind(WxMaConfig.class).toInstance(config); + } catch (IOException e) { + e.printStackTrace(); + } + } + +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/test/TestConfig.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/test/TestConfig.java new file mode 100644 index 0000000000..ee941ef349 --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/test/TestConfig.java @@ -0,0 +1,61 @@ +package cn.binarywang.wx.miniapp.test; + +import cn.binarywang.wx.miniapp.config.WxMaInMemoryConfig; +import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import me.chanjar.weixin.common.util.xml.XStreamInitializer; +import org.apache.commons.lang3.builder.ToStringBuilder; + +import java.io.InputStream; +import java.util.concurrent.locks.Lock; + +/** + * @author Binary Wang + */ +@XStreamAlias("xml") +public class TestConfig extends WxMaInMemoryConfig { + + private String openid; + private String kfAccount; + private String templateId; + + public static TestConfig fromXml(InputStream is) { + XStream xstream = XStreamInitializer.getInstance(); + xstream.processAnnotations(TestConfig.class); + return (TestConfig) xstream.fromXML(is); + } + + public String getOpenid() { + return this.openid; + } + + public void setOpenid(String openid) { + this.openid = openid; + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } + + public String getKfAccount() { + return this.kfAccount; + } + + public void setKfAccount(String kfAccount) { + this.kfAccount = kfAccount; + } + + public String getTemplateId() { + return this.templateId; + } + + public void setTemplateId(String templateId) { + this.templateId = templateId; + } + + public void setAccessTokenLock(Lock lock) { + super.accessTokenLock = lock; + } + +} diff --git a/weixin-java-miniapp/src/test/resources/logback-test.xml b/weixin-java-miniapp/src/test/resources/logback-test.xml new file mode 100644 index 0000000000..e4a33acd88 --- /dev/null +++ b/weixin-java-miniapp/src/test/resources/logback-test.xml @@ -0,0 +1,13 @@ + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %replace(%caller{1}){'Caller', ''} - %msg%n + + + + + + + + diff --git a/weixin-java-miniapp/src/test/resources/test-config.sample.xml b/weixin-java-miniapp/src/test/resources/test-config.sample.xml new file mode 100644 index 0000000000..1f6cc38887 --- /dev/null +++ b/weixin-java-miniapp/src/test/resources/test-config.sample.xml @@ -0,0 +1,11 @@ + + JSON或者XML + appid + secret + Token + EncodingAESKey + 可以不填写 + 可以不填写 + 某个用户的openId + 模版消息的模版ID + diff --git a/weixin-java-miniapp/src/test/resources/tmp.png b/weixin-java-miniapp/src/test/resources/tmp.png new file mode 100644 index 0000000000..5cb4a40605 Binary files /dev/null and b/weixin-java-miniapp/src/test/resources/tmp.png differ diff --git a/weixin-java-mp/build.gradle b/weixin-java-mp/build.gradle deleted file mode 100644 index bd91f2f88f..0000000000 --- a/weixin-java-mp/build.gradle +++ /dev/null @@ -1,12 +0,0 @@ -description = 'WeiXin Java Tools - MP' -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.RC0' - testCompile group: 'org.eclipse.jetty', name: 'jetty-servlet', version: '9.3.0.RC0' - testCompile group: 'joda-time', name: 'joda-time', version: '2.9.4' -} -test.useTestNG() diff --git a/weixin-java-mp/pom.xml b/weixin-java-mp/pom.xml index 1b97c00853..d8aa707aee 100644 --- a/weixin-java-mp/pom.xml +++ b/weixin-java-mp/pom.xml @@ -7,7 +7,7 @@ com.github.binarywang weixin-java-parent - 2.6.1.BETA + 2.7.8.BETA weixin-java-mp WeiXin Java Tools - MP @@ -19,6 +19,18 @@ weixin-java-common ${project.version} + + + org.jodd + jodd-http + provided + + + com.squareup.okhttp3 + okhttp + provided + + org.testng testng @@ -42,13 +54,17 @@ joda-time joda-time - 2.9.4 test redis.clients jedis + + ch.qos.logback + logback-classic + test + diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpCardService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpCardService.java index c1327776ef..08ea9a7007 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpCardService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpCardService.java @@ -10,6 +10,12 @@ * @author YuJian(mgcnrx11@hotmail.com) on 01/11/2016 */ public interface WxMpCardService { + String CARD_GET = "https://api.weixin.qq.com/card/get"; + String CARD_GET_TICKET = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=wx_card"; + String CARD_CODE_DECRYPT = "https://api.weixin.qq.com/card/code/decrypt"; + String CARD_CODE_GET = "https://api.weixin.qq.com/card/code/get"; + String CARD_CODE_CONSUME = "https://api.weixin.qq.com/card/code/consume"; + String CARD_CODE_MARK = "https://api.weixin.qq.com/card/code/mark"; /** * 得到WxMpService diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpDataCubeService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpDataCubeService.java index 644844338e..7610b03d3a 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpDataCubeService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpDataCubeService.java @@ -13,6 +13,24 @@ * @author binarywang (https://github.com/binarywang) */ public interface WxMpDataCubeService { + String GET_USER_SUMMARY = "https://api.weixin.qq.com/datacube/getusersummary"; + String GET_USER_CUMULATE = "https://api.weixin.qq.com/datacube/getusercumulate"; + String GET_ARTICLE_SUMMARY = "https://api.weixin.qq.com/datacube/getarticlesummary"; + String GET_ARTICLE_TOTAL = "https://api.weixin.qq.com/datacube/getarticletotal"; + String GET_USER_READ = "https://api.weixin.qq.com/datacube/getuserread"; + String GET_USER_READ_HOUR = "https://api.weixin.qq.com/datacube/getuserreadhour"; + String GET_USER_SHARE = "https://api.weixin.qq.com/datacube/getusershare"; + String GET_USER_SHARE_HOUR = "https://api.weixin.qq.com/datacube/getusersharehour"; + String GET_UPSTREAM_MSG = "https://api.weixin.qq.com/datacube/getupstreammsg"; + String GET_UPSTREAM_MSG_HOUR = "https://api.weixin.qq.com/datacube/getupstreammsghour"; + String GET_UPSTREAM_MSG_WEEK = "https://api.weixin.qq.com/datacube/getupstreammsgweek"; + String GET_UPSTREAM_MSG_MONTH = "https://api.weixin.qq.com/datacube/getupstreammsgmonth"; + String GET_UPSTREAM_MSG_DIST = "https://api.weixin.qq.com/datacube/getupstreammsgdist"; + String GET_UPSTREAM_MSG_DIST_WEEK = "https://api.weixin.qq.com/datacube/getupstreammsgdistweek"; + String GET_UPSTREAM_MSG_DIST_MONTH = "https://api.weixin.qq.com/datacube/getupstreammsgdistmonth"; + String GET_INTERFACE_SUMMARY = "https://api.weixin.qq.com/datacube/getinterfacesummary"; + String GET_INTERFACE_SUMMARY_HOUR = "https://api.weixin.qq.com/datacube/getinterfacesummaryhour"; + //*******************用户分析数据接口***********************// /** diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInRedisConfigStorage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInRedisConfigStorage.java index 706d59ecd1..d22214dfa1 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInRedisConfigStorage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInRedisConfigStorage.java @@ -25,7 +25,7 @@ public String getAccessToken() { @Override public boolean isAccessTokenExpired() { - return getAccessToken() == null ? true : false; + return jedis.ttl(ACCESS_TOKEN_KEY.concat(appId)) < 2; } @Override @@ -46,7 +46,7 @@ public String getJsapiTicket() { @Override public boolean isJsapiTicketExpired() { - return getJsapiTicket() == null ? true : false; + return jedis.ttl(JSAPI_TICKET_KEY.concat(appId)) < 2; } @Override @@ -70,7 +70,7 @@ public String getCardApiTicket() { @Override public boolean isCardApiTicketExpired() { - return getCardApiTicket() == null ? true : false; + return jedis.ttl(CARDAPI_TICKET_KEY.concat(appId)) < 2; } @Override diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMassMessageService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMassMessageService.java new file mode 100644 index 0000000000..e4a57de99b --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMassMessageService.java @@ -0,0 +1,95 @@ +package me.chanjar.weixin.mp.api; + +import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.mp.bean.*; +import me.chanjar.weixin.mp.bean.result.WxMpMassSendResult; +import me.chanjar.weixin.mp.bean.result.WxMpMassUploadResult; + +/** + *
+ * 群发消息服务类
+ * Created by Binary Wang on 2017-8-16.
+ * 
+ * + * @author Binary Wang + */ +public interface WxMpMassMessageService { + /** + * 上传群发用的图文消息 + */ + String MEDIA_UPLOAD_NEWS_URL = "https://api.weixin.qq.com/cgi-bin/media/uploadnews"; + /** + * 上传群发用的视频 + */ + String MEDIA_UPLOAD_VIDEO_URL = "https://api.weixin.qq.com/cgi-bin/media/uploadvideo"; + /** + * 分组群发消息 + */ + String MESSAGE_MASS_SENDALL_URL = "https://api.weixin.qq.com/cgi-bin/message/mass/sendall"; + /** + * 按openId列表群发消息 + */ + String MESSAGE_MASS_SEND_URL = "https://api.weixin.qq.com/cgi-bin/message/mass/send"; + /** + * 群发消息预览接口 + */ + String MESSAGE_MASS_PREVIEW_URL = "https://api.weixin.qq.com/cgi-bin/message/mass/preview"; + + /** + *
+   * 上传群发用的图文消息,上传后才能群发图文消息
+   *
+   * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN
+   * 
+ * + * @see #massGroupMessageSend(WxMpMassTagMessage) + * @see #massOpenIdsMessageSend(WxMpMassOpenIdsMessage) + */ + WxMpMassUploadResult massNewsUpload(WxMpMassNews news) throws WxErrorException; + + /** + *
+   * 上传群发用的视频,上传后才能群发视频消息
+   * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN
+   * 
+ * + * @see #massGroupMessageSend(WxMpMassTagMessage) + * @see #massOpenIdsMessageSend(WxMpMassOpenIdsMessage) + */ + WxMpMassUploadResult massVideoUpload(WxMpMassVideo video) throws WxErrorException; + + /** + *
+   * 分组群发消息
+   * 如果发送图文消息,必须先使用 {@link #massNewsUpload(WxMpMassNews)} 获得media_id,然后再发送
+   * 如果发送视频消息,必须先使用 {@link #massVideoUpload(WxMpMassVideo)} 获得media_id,然后再发送
+   * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN
+   * 
+ */ + WxMpMassSendResult massGroupMessageSend(WxMpMassTagMessage message) throws WxErrorException; + + /** + *
+   * 按openId列表群发消息
+   * 如果发送图文消息,必须先使用 {@link #massNewsUpload(WxMpMassNews)} 获得media_id,然后再发送
+   * 如果发送视频消息,必须先使用 {@link #massVideoUpload(WxMpMassVideo)} 获得media_id,然后再发送
+   * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN
+   * 
+ */ + WxMpMassSendResult massOpenIdsMessageSend(WxMpMassOpenIdsMessage message) throws WxErrorException; + + /** + *
+   * 群发消息预览接口
+   * 开发者可通过该接口发送消息给指定用户,在手机端查看消息的样式和排版。为了满足第三方平台开发者的需求,在保留对openID预览能力的同时,增加了对指定微信号发送预览的能力,但该能力每日调用次数有限制(100次),请勿滥用。
+   * 接口调用请求说明
+   *  http请求方式: POST
+   *  https://api.weixin.qq.com/cgi-bin/message/mass/preview?access_token=ACCESS_TOKEN
+   * 详情请见:http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN
+   * 
+ * + * @return wxMpMassSendResult + */ + WxMpMassSendResult massMessagePreview(WxMpMassPreviewMessage wxMpMassPreviewMessage) throws Exception; + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMaterialService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMaterialService.java index 8e769d3e2d..05ca87eab7 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMaterialService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMaterialService.java @@ -16,6 +16,16 @@ *
*/ public interface WxMpMaterialService { + String MEDIA_GET_URL = "https://api.weixin.qq.com/cgi-bin/media/get"; + String MEDIA_UPLOAD_URL = "https://api.weixin.qq.com/cgi-bin/media/upload?type=%s"; + String IMG_UPLOAD_URL = "https://api.weixin.qq.com/cgi-bin/media/uploadimg"; + String MATERIAL_ADD_URL = "https://api.weixin.qq.com/cgi-bin/material/add_material?type=%s"; + String NEWS_ADD_URL = "https://api.weixin.qq.com/cgi-bin/material/add_news"; + String MATERIAL_GET_URL = "https://api.weixin.qq.com/cgi-bin/material/get_material"; + String NEWS_UPDATE_URL = "https://api.weixin.qq.com/cgi-bin/material/update_news"; + String MATERIAL_DEL_URL = "https://api.weixin.qq.com/cgi-bin/material/del_material"; + String MATERIAL_GET_COUNT_URL = "https://api.weixin.qq.com/cgi-bin/material/get_materialcount"; + String MATERIAL_BATCHGET_URL = "https://api.weixin.qq.com/cgi-bin/material/batchget_material"; /** *
@@ -73,11 +83,11 @@ public interface WxMpMaterialService {
    * 接口url格式:https://api.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_ID
    * 
* - * @param media_id + * @param mediaId 媒体文件Id * @return 保存到本地的临时文件 * @throws WxErrorException */ - File mediaDownload(String media_id) throws WxErrorException; + File mediaDownload(String mediaId) throws WxErrorException; /** *
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMemberCardService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMemberCardService.java
new file mode 100644
index 0000000000..7cebb8c858
--- /dev/null
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMemberCardService.java
@@ -0,0 +1,53 @@
+package me.chanjar.weixin.mp.api;
+
+import me.chanjar.weixin.common.exception.WxErrorException;
+import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardActivatedMessage;
+import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardUpdateMessage;
+import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardUpdateResult;
+import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardUserInfoResult;
+
+/**
+ * 会员卡相关接口
+ *
+ * @author YuJian(mgcnrx11@gmail.com)
+ * @version 2017/7/8
+ */
+public interface WxMpMemberCardService {
+
+  /**
+   * 得到WxMpService
+   */
+  WxMpService getWxMpService();
+
+  /**
+   * 会员卡激活接口
+   *
+   * @param activatedMessage 激活所需参数
+   * @return 调用返回的JSON字符串。
+   * @throws WxErrorException 接口调用失败抛出的异常
+   */
+  String activateMemberCard(WxMpMemberCardActivatedMessage activatedMessage) throws WxErrorException;
+
+  /**
+   * 拉取会员信息接口
+   *
+   * @param cardId 会员卡的CardId,微信分配
+   * @param code 领取会员的会员卡Code
+   * @return 会员信息的结果对象
+   * @throws WxErrorException 接口调用失败抛出的异常
+   */
+  WxMpMemberCardUserInfoResult getUserInfo(String cardId, String code) throws WxErrorException;
+
+  /**
+   * 当会员持卡消费后,支持开发者调用该接口更新会员信息。会员卡交易后的每次信息变更需通过该接口通知微信,便于后续消息通知及其他扩展功能。
+   *
+   * 1.开发者可以同时传入add_bonus和bonus解决由于同步失败带来的幂等性问题。同时传入add_bonus和bonus时
+   * add_bonus作为积分变动消息中的变量值,而bonus作为卡面上的总积分额度显示。余额变动同理。
+   * 2.开发者可以传入is_notify_bonus控制特殊的积分对账变动不发送消息,余额变动同理。
+   *
+   * @param updateUserMessage 更新会员信息所需字段消息
+   * @return 调用返回的JSON字符串。
+   * @throws WxErrorException 接口调用失败抛出的异常
+   */
+  WxMpMemberCardUpdateResult updateUserMemberCard(WxMpMemberCardUpdateMessage updateUserMessage) throws WxErrorException;
+}
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageRouter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageRouter.java
index 958c302676..5acf0040aa 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageRouter.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageRouter.java
@@ -14,7 +14,9 @@
 import org.slf4j.LoggerFactory;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -77,8 +79,6 @@ public WxMpMessageRouter(WxMpService wxMpService) {
    * 设置自定义的 {@link ExecutorService}
    * 如果不调用该方法,默认使用 Executors.newFixedThreadPool(100)
    * 
- * - * @param executorService */ public void setExecutorService(ExecutorService executorService) { this.executorService = executorService; @@ -89,8 +89,6 @@ public void setExecutorService(ExecutorService executorService) { * 设置自定义的 {@link me.chanjar.weixin.common.api.WxMessageDuplicateChecker} * 如果不调用该方法,默认使用 {@link me.chanjar.weixin.common.api.WxMessageInMemoryDuplicateChecker} *
- * - * @param messageDuplicateChecker */ public void setMessageDuplicateChecker(WxMessageDuplicateChecker messageDuplicateChecker) { this.messageDuplicateChecker = messageDuplicateChecker; @@ -101,8 +99,6 @@ public void setMessageDuplicateChecker(WxMessageDuplicateChecker messageDuplicat * 设置自定义的{@link me.chanjar.weixin.common.session.WxSessionManager} * 如果不调用该方法,默认使用 {@link me.chanjar.weixin.common.session.StandardSessionManager} *
- * - * @param sessionManager */ public void setSessionManager(WxSessionManager sessionManager) { this.sessionManager = sessionManager; @@ -113,8 +109,6 @@ public void setSessionManager(WxSessionManager sessionManager) { * 设置自定义的{@link me.chanjar.weixin.common.api.WxErrorExceptionHandler} * 如果不调用该方法,默认使用 {@link me.chanjar.weixin.common.util.LogExceptionHandler} * - * - * @param exceptionHandler */ public void setExceptionHandler(WxErrorExceptionHandler exceptionHandler) { this.exceptionHandler = exceptionHandler; @@ -133,11 +127,9 @@ public WxMpMessageRouterRule rule() { /** * 处理微信消息 - * - * @param wxMessage */ - public WxMpXmlOutMessage route(final WxMpXmlMessage wxMessage) { - if (isDuplicateMessage(wxMessage)) { + public WxMpXmlOutMessage route(final WxMpXmlMessage wxMessage, final Map context) { + if (isMsgDuplicated(wxMessage)) { // 如果是重复消息,那么就不做处理 return null; } @@ -166,12 +158,12 @@ public WxMpXmlOutMessage route(final WxMpXmlMessage wxMessage) { this.executorService.submit(new Runnable() { @Override public void run() { - rule.service(wxMessage, WxMpMessageRouter.this.wxMpService, WxMpMessageRouter.this.sessionManager, WxMpMessageRouter.this.exceptionHandler); + rule.service(wxMessage, context, WxMpMessageRouter.this.wxMpService, WxMpMessageRouter.this.sessionManager, WxMpMessageRouter.this.exceptionHandler); } }) ); } else { - res = rule.service(wxMessage, this.wxMpService, this.sessionManager, this.exceptionHandler); + res = rule.service(wxMessage, context, this.wxMpService, this.sessionManager, this.exceptionHandler); // 在同步操作结束,session访问结束 this.log.debug("End session access: async=false, sessionId={}", wxMessage.getFromUser()); sessionEndAccess(wxMessage); @@ -188,9 +180,7 @@ public void run() { WxMpMessageRouter.this.log.debug("End session access: async=true, sessionId={}", wxMessage.getFromUser()); // 异步操作结束,session访问结束 sessionEndAccess(wxMessage); - } catch (InterruptedException e) { - WxMpMessageRouter.this.log.error("Error happened when wait task finish", e); - } catch (ExecutionException e) { + } catch (InterruptedException | ExecutionException e) { WxMpMessageRouter.this.log.error("Error happened when wait task finish", e); } } @@ -200,9 +190,13 @@ public void run() { return res; } - protected boolean isDuplicateMessage(WxMpXmlMessage wxMessage) { + public WxMpXmlOutMessage route(final WxMpXmlMessage wxMessage) { + return this.route(wxMessage, new HashMap()); + } + + protected boolean isMsgDuplicated(WxMpXmlMessage wxMessage) { - StringBuffer messageId = new StringBuffer(); + StringBuilder messageId = new StringBuilder(); if (wxMessage.getMsgId() == null) { messageId.append(wxMessage.getCreateTime()) .append("-").append(wxMessage.getFromUser()) @@ -219,8 +213,6 @@ protected boolean isDuplicateMessage(WxMpXmlMessage wxMessage) { /** * 对session的访问结束 - * - * @param wxMessage */ protected void sessionEndAccess(WxMpXmlMessage wxMessage) { diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageRouterRule.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageRouterRule.java index eda32b71a9..50e30af6b7 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageRouterRule.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageRouterRule.java @@ -5,6 +5,7 @@ import me.chanjar.weixin.common.session.WxSessionManager; import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; +import org.apache.commons.lang3.StringUtils; import java.util.ArrayList; import java.util.HashMap; @@ -26,6 +27,8 @@ public class WxMpMessageRouterRule { private String eventKey; + private String eventKeyRegex; + private String content; private String rContent; @@ -74,6 +77,14 @@ public WxMpMessageRouterRule eventKey(String eventKey) { return this; } + /** + * 如果eventKey匹配该正则表达式 + */ + public WxMpMessageRouterRule eventKeyRegex(String regex) { + this.eventKeyRegex = regex; + return this; + } + /** * 如果content等于某值 */ @@ -170,17 +181,17 @@ protected boolean test(WxMpXmlMessage wxMessage) { return (this.fromUser == null || this.fromUser.equals(wxMessage.getFromUser())) && - (this.msgType == null || this.msgType.toLowerCase().equals((wxMessage.getMsgType() == null ? null : wxMessage.getMsgType().toLowerCase()))) + (this.msgType == null || this.msgType.equalsIgnoreCase(wxMessage.getMsgType())) + && + (this.event == null || this.event.equalsIgnoreCase(wxMessage.getEvent())) && - (this.event == null || this.event.toLowerCase().equals((wxMessage.getEvent() == null ? null : wxMessage.getEvent().toLowerCase()))) + (this.eventKey == null || this.eventKey.equalsIgnoreCase(wxMessage.getEventKey())) && - (this.eventKey == null || this.eventKey.toLowerCase().equals((wxMessage.getEventKey() == null ? null : wxMessage.getEventKey().toLowerCase()))) + (this.eventKeyRegex == null || Pattern.matches(this.eventKeyRegex, StringUtils.trimToEmpty(wxMessage.getEventKey()))) && - (this.content == null || this.content - .equals(wxMessage.getContent() == null ? null : wxMessage.getContent().trim())) + (this.content == null || this.content.equals(StringUtils.trimToNull(wxMessage.getContent()))) && - (this.rContent == null || Pattern - .matches(this.rContent, wxMessage.getContent() == null ? "" : wxMessage.getContent().trim())) + (this.rContent == null || Pattern.matches(this.rContent, StringUtils.trimToEmpty(wxMessage.getContent()))) && (this.matcher == null || this.matcher.match(wxMessage)) ; @@ -193,13 +204,16 @@ protected boolean test(WxMpXmlMessage wxMessage) { * @return true 代表继续执行别的router,false 代表停止执行别的router */ protected WxMpXmlOutMessage service(WxMpXmlMessage wxMessage, + Map context, WxMpService wxMpService, WxSessionManager sessionManager, WxErrorExceptionHandler exceptionHandler) { - try { + if (context == null) { + context = new HashMap<>(); + } - Map context = new HashMap<>(); + try { // 如果拦截器不通过 for (WxMpMessageInterceptor interceptor : this.interceptors) { if (!interceptor.intercept(wxMessage, context, wxMpService, sessionManager)) { diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpQrcodeService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpQrcodeService.java index e2e17eb359..37ed7dcd12 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpQrcodeService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpQrcodeService.java @@ -26,6 +26,18 @@ public interface WxMpQrcodeService { */ WxMpQrCodeTicket qrCodeCreateTmpTicket(int sceneId, Integer expireSeconds) throws WxErrorException; + + /** + *
+   * 换取临时二维码ticket
+   * 详情请见: 生成带参数的二维码
+   * 
+ * + * @param sceneStr 场景值ID(字符串形式的ID),字符串类型,长度限制为1到64 + * @param expireSeconds 该二维码有效时间,以秒为单位。 最大不超过2592000(即30天),此字段如果不填,则默认有效期为30秒。 + */ + WxMpQrCodeTicket qrCodeCreateTmpTicket(String sceneStr, Integer expireSeconds) throws WxErrorException; + /** *
    * 换取永久二维码ticket
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java
index 33a4066b60..f684f4e8a4 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java
@@ -20,26 +20,6 @@ public interface WxMpService {
    * 获得jsapi_ticket
    */
   String GET_JSAPI_TICKET_URL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi";
-  /**
-   * 上传群发用的图文消息
-   */
-  String MEDIA_UPLOAD_NEWS_URL = "https://api.weixin.qq.com/cgi-bin/media/uploadnews";
-  /**
-   * 上传群发用的视频
-   */
-  String MEDIA_UPLOAD_VIDEO_URL = "https://api.weixin.qq.com/cgi-bin/media/uploadvideo";
-  /**
-   * 分组群发消息
-   */
-  String MESSAGE_MASS_SENDALL_URL = "https://api.weixin.qq.com/cgi-bin/message/mass/sendall";
-  /**
-   * 按openId列表群发消息
-   */
-  String MESSAGE_MASS_SEND_URL = "https://api.weixin.qq.com/cgi-bin/message/mass/send";
-  /**
-   * 群发消息预览接口
-   */
-  String MESSAGE_MASS_PREVIEW_URL = "https://api.weixin.qq.com/cgi-bin/message/mass/preview";
   /**
    * 长链接转短链接接口
    */
@@ -77,6 +57,11 @@ public interface WxMpService {
    */
   String CONNECT_OAUTH2_AUTHORIZE_URL = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=%s#wechat_redirect";
 
+  /**
+   * 获取公众号的自动回复规则
+   */
+  String GET_CURRENT_AUTOREPLY_INFO_URL = "https://api.weixin.qq.com/cgi-bin/get_current_autoreply_info";
+
   /**
    * 
    * 验证消息的确来自微信服务器
@@ -136,63 +121,6 @@ public interface WxMpService {
    */
   WxJsapiSignature createJsapiSignature(String url) throws WxErrorException;
 
-  /**
-   * 
-   * 上传群发用的图文消息,上传后才能群发图文消息
-   *
-   * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN
-   * 
- * - * @see #massGroupMessageSend(me.chanjar.weixin.mp.bean.WxMpMassTagMessage) - * @see #massOpenIdsMessageSend(me.chanjar.weixin.mp.bean.WxMpMassOpenIdsMessage) - */ - WxMpMassUploadResult massNewsUpload(WxMpMassNews news) throws WxErrorException; - - /** - *
-   * 上传群发用的视频,上传后才能群发视频消息
-   * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN
-   * 
- * - * @see #massGroupMessageSend(me.chanjar.weixin.mp.bean.WxMpMassTagMessage) - * @see #massOpenIdsMessageSend(me.chanjar.weixin.mp.bean.WxMpMassOpenIdsMessage) - */ - WxMpMassUploadResult massVideoUpload(WxMpMassVideo video) throws WxErrorException; - - /** - *
-   * 分组群发消息
-   * 如果发送图文消息,必须先使用 {@link #massNewsUpload(me.chanjar.weixin.mp.bean.WxMpMassNews)} 获得media_id,然后再发送
-   * 如果发送视频消息,必须先使用 {@link #massVideoUpload(me.chanjar.weixin.mp.bean.WxMpMassVideo)} 获得media_id,然后再发送
-   * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN
-   * 
- */ - WxMpMassSendResult massGroupMessageSend(WxMpMassTagMessage message) throws WxErrorException; - - /** - *
-   * 按openId列表群发消息
-   * 如果发送图文消息,必须先使用 {@link #massNewsUpload(me.chanjar.weixin.mp.bean.WxMpMassNews)} 获得media_id,然后再发送
-   * 如果发送视频消息,必须先使用 {@link #massVideoUpload(me.chanjar.weixin.mp.bean.WxMpMassVideo)} 获得media_id,然后再发送
-   * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN
-   * 
- */ - WxMpMassSendResult massOpenIdsMessageSend(WxMpMassOpenIdsMessage message) throws WxErrorException; - - /** - *
-   * 群发消息预览接口
-   * 开发者可通过该接口发送消息给指定用户,在手机端查看消息的样式和排版。为了满足第三方平台开发者的需求,在保留对openID预览能力的同时,增加了对指定微信号发送预览的能力,但该能力每日调用次数有限制(100次),请勿滥用。
-   * 接口调用请求说明
-   *  http请求方式: POST
-   *  https://api.weixin.qq.com/cgi-bin/message/mass/preview?access_token=ACCESS_TOKEN
-   * 详情请见:http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN
-   * 
- * - * @return wxMpMassSendResult - */ - WxMpMassSendResult massMessagePreview(WxMpMassPreviewMessage wxMpMassPreviewMessage) throws Exception; - /** *
    * 长链接转短链接接口
@@ -273,6 +201,24 @@ public interface WxMpService {
    */
   String[] getCallbackIP() throws WxErrorException;
 
+  /**
+   * 
+   * 获取公众号的自动回复规则
+   * http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433751299&token=&lang=zh_CN
+   * 开发者可以通过该接口,获取公众号当前使用的自动回复规则,包括关注后自动回复、消息自动回复(60分钟内触发一次)、关键词自动回复。
+   * 请注意:
+   * 1、第三方平台开发者可以通过本接口,在旗下公众号将业务授权给你后,立即通过本接口检测公众号的自动回复配置,并通过接口再次给公众号设置好自动回复规则,以提升公众号运营者的业务体验。
+   * 2、本接口仅能获取公众号在公众平台官网的自动回复功能中设置的自动回复规则,若公众号自行开发实现自动回复,或通过第三方平台开发者来实现,则无法获取。
+   * 3、认证/未认证的服务号/订阅号,以及接口测试号,均拥有该接口权限。
+   * 4、从第三方平台的公众号登录授权机制上来说,该接口从属于消息与菜单权限集。
+   * 5、本接口中返回的图片/语音/视频为临时素材(临时素材每次获取都不同,3天内有效,通过素材管理-获取临时素材接口来获取这些素材),本接口返回的图文消息为永久素材素材(通过素材管理-获取永久素材接口来获取这些素材)。
+   * 接口调用请求说明
+   * http请求方式: GET(请使用https协议)
+   * https://api.weixin.qq.com/cgi-bin/get_current_autoreply_info?access_token=ACCESS_TOKEN
+   * 
+ */ + WxMpCurrentAutoReplyInfo getCurrentAutoReplyInfo() throws WxErrorException; + /** * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求 */ @@ -292,15 +238,10 @@ public interface WxMpService { */ T execute(RequestExecutor executor, String uri, E data) throws WxErrorException; - /** - * 获取代理对象 - */ - //HttpHost getRequestHttpProxy(); - /** *
    * 设置当微信系统响应系统繁忙时,要等待多少 retrySleepMillis(ms) * 2^(重试次数 - 1) 再发起重试
-   * 默认:1000ms
+   * @param retrySleepMillis 默认:1000ms
    * 
*/ void setRetrySleepMillis(int retrySleepMillis); @@ -409,6 +350,19 @@ public interface WxMpService { */ WxMpDeviceService getDeviceService(); + /** + * 返回摇一摇周边相关接口方法的实现类对象,以方便调用其各个接口 + * + * @return WxMpShakeService + */ + WxMpShakeService getShakeService(); + + /** + * 返回会员卡相关接口方法的实现类对象,以方便调用其各个接口 + * + * @return WxMpMemberCardService + */ + WxMpMemberCardService getMemberCardService(); /** * 初始化http请求对象 @@ -416,9 +370,13 @@ public interface WxMpService { void initHttp(); /** - * @return + * @return RequestHttp对象 */ RequestHttp getRequestHttp(); - + /** + * 返回群发消息相关接口方法的实现类对象,以方便调用其各个接口 + * @return WxMpMassMessageService + */ + WxMpMassMessageService getMassMessageService(); } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpShakeService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpShakeService.java new file mode 100644 index 0000000000..87a6747af5 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpShakeService.java @@ -0,0 +1,27 @@ +package me.chanjar.weixin.mp.api; + +import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.mp.bean.WxMpShakeInfoResult; +import me.chanjar.weixin.mp.bean.WxMpShakeQuery; + +/** + * 摇一摇周边的相关接口 + * + * @author rememberber + */ +public interface WxMpShakeService { + + /** + *
+   * 获取设备及用户信息
+ * 获取设备信息,包括UUID、major、minor,以及距离、openID等信息。 + * 详情请见: https://mp.weixin.qq.com/wiki?action=doc&id=mp1443447963 + * http请求方式: POST(请使用https协议) + * 接口地址:https://api.weixin.qq.com/shakearound/user/getshakeinfo?access_token=ACCESS_TOKE + *
+ * + * @param wxMpShakeQuery 查询参数 + */ + WxMpShakeInfoResult getShakeInfo(WxMpShakeQuery wxMpShakeQuery) throws WxErrorException; + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpUserTagService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpUserTagService.java index cee28b73ba..4a666eff1b 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpUserTagService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpUserTagService.java @@ -76,7 +76,7 @@ WxTagListUser tagListUser(Long tagId, String nextOpenid) *
    * 批量为用户取消标签
    * 详情请见:用户标签管理
-   * 接口url格式: https://api.weixin.qq.com/cgi-bin/tags/members/batchtagging?access_token=ACCESS_TOKEN
+   * 接口url格式: https://api.weixin.qq.com/cgi-bin/tags/members/batchuntagging?access_token=ACCESS_TOKEN
    * 
*/ boolean batchUntagging(Long tagId, String[] openids) throws WxErrorException; diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpCardServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpCardServiceImpl.java index 7bb9b9cca3..8ec5945f7b 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpCardServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpCardServiceImpl.java @@ -74,8 +74,7 @@ public String getCardApiTicket(boolean forceRefresh) throws WxErrorException { } if (this.getWxMpService().getWxMpConfigStorage().isCardApiTicketExpired()) { - String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=wx_card"; - String responseContent = this.wxMpService.execute(SimpleGetRequestExecutor.create(this.getWxMpService().getRequestHttp()), url, null); + String responseContent = this.wxMpService.execute(SimpleGetRequestExecutor.create(this.getWxMpService().getRequestHttp()), CARD_GET_TICKET, null); JsonElement tmpJsonElement = new JsonParser().parse(responseContent); JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject(); String cardApiTicket = tmpJsonObject.get("ticket").getAsString(); @@ -129,10 +128,9 @@ public WxCardApiSignature createCardApiSignature(String... optionalSignParam) th */ @Override public String decryptCardCode(String encryptCode) throws WxErrorException { - String url = "https://api.weixin.qq.com/card/code/decrypt"; JsonObject param = new JsonObject(); param.addProperty("encrypt_code", encryptCode); - String responseContent = this.wxMpService.post(url, param.toString()); + String responseContent = this.wxMpService.post(CARD_CODE_DECRYPT, param.toString()); JsonElement tmpJsonElement = new JsonParser().parse(responseContent); JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject(); JsonPrimitive jsonPrimitive = tmpJsonObject.getAsJsonPrimitive("code"); @@ -149,12 +147,11 @@ public String decryptCardCode(String encryptCode) throws WxErrorException { */ @Override public WxMpCardResult queryCardCode(String cardId, String code, boolean checkConsume) throws WxErrorException { - String url = "https://api.weixin.qq.com/card/code/get"; JsonObject param = new JsonObject(); param.addProperty("card_id", cardId); param.addProperty("code", code); param.addProperty("check_consume", checkConsume); - String responseContent = this.wxMpService.post(url, param.toString()); + String responseContent = this.wxMpService.post(CARD_CODE_GET, param.toString()); JsonElement tmpJsonElement = new JsonParser().parse(responseContent); return WxMpGsonBuilder.INSTANCE.create().fromJson(tmpJsonElement, new TypeToken() { @@ -183,7 +180,6 @@ public String consumeCardCode(String code) throws WxErrorException { */ @Override public String consumeCardCode(String code, String cardId) throws WxErrorException { - String url = "https://api.weixin.qq.com/card/code/consume"; JsonObject param = new JsonObject(); param.addProperty("code", code); @@ -191,7 +187,7 @@ public String consumeCardCode(String code, String cardId) throws WxErrorExceptio param.addProperty("card_id", cardId); } - return this.wxMpService.post(url, param.toString()); + return this.wxMpService.post(CARD_CODE_CONSUME, param.toString()); } /** @@ -207,13 +203,12 @@ public String consumeCardCode(String code, String cardId) throws WxErrorExceptio @Override public void markCardCode(String code, String cardId, String openId, boolean isMark) throws WxErrorException { - String url = "https://api.weixin.qq.com/card/code/mark"; JsonObject param = new JsonObject(); param.addProperty("code", code); param.addProperty("card_id", cardId); param.addProperty("openid", openId); param.addProperty("is_mark", isMark); - String responseContent = this.getWxMpService().post(url, param.toString()); + String responseContent = this.getWxMpService().post(CARD_CODE_MARK, param.toString()); JsonElement tmpJsonElement = new JsonParser().parse(responseContent); WxMpCardResult cardResult = WxMpGsonBuilder.INSTANCE.create().fromJson(tmpJsonElement, new TypeToken() { @@ -225,10 +220,9 @@ public void markCardCode(String code, String cardId, String openId, boolean isMa @Override public String getCardDetail(String cardId) throws WxErrorException { - String url = "https://api.weixin.qq.com/card/get"; JsonObject param = new JsonObject(); param.addProperty("card_id", cardId); - String responseContent = this.wxMpService.post(url, param.toString()); + String responseContent = this.wxMpService.post(CARD_GET, param.toString()); // 判断返回值 JsonObject json = (new JsonParser()).parse(responseContent).getAsJsonObject(); diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpDataCubeServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpDataCubeServiceImpl.java index a1a8d82b0e..25bd3cfaab 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpDataCubeServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpDataCubeServiceImpl.java @@ -17,7 +17,6 @@ * @author binarywang (https://github.com/binarywang) */ public class WxMpDataCubeServiceImpl implements WxMpDataCubeService { - private static final String API_URL_PREFIX = "https://api.weixin.qq.com/datacube"; private final Format dateFormat = FastDateFormat.getInstance("yyyy-MM-dd"); @@ -29,180 +28,108 @@ public WxMpDataCubeServiceImpl(WxMpService wxMpService) { @Override public List getUserSummary(Date beginDate, Date endDate) throws WxErrorException { - String url = API_URL_PREFIX + "/getusersummary"; - JsonObject param = new JsonObject(); - param.addProperty("begin_date", this.dateFormat.format(beginDate)); - param.addProperty("end_date", this.dateFormat.format(endDate)); - String responseContent = this.wxMpService.post(url, param.toString()); + String responseContent = this.wxMpService.post(GET_USER_SUMMARY, buildParams(beginDate, endDate)); return WxDataCubeUserSummary.fromJson(responseContent); } @Override public List getUserCumulate(Date beginDate, Date endDate) throws WxErrorException { - String url = API_URL_PREFIX + "/getusercumulate"; - JsonObject param = new JsonObject(); - param.addProperty("begin_date", this.dateFormat.format(beginDate)); - param.addProperty("end_date", this.dateFormat.format(endDate)); - String responseContent = this.wxMpService.post(url, param.toString()); + String responseContent = this.wxMpService.post(GET_USER_CUMULATE, buildParams(beginDate, endDate)); return WxDataCubeUserCumulate.fromJson(responseContent); } @Override public List getArticleSummary(Date beginDate, Date endDate) throws WxErrorException { - String url = API_URL_PREFIX + "/getarticlesummary"; - JsonObject param = new JsonObject(); - param.addProperty("begin_date", this.dateFormat.format(beginDate)); - param.addProperty("end_date", this.dateFormat.format(endDate)); - String responseContent = this.wxMpService.post(url, param.toString()); - return WxDataCubeArticleResult.fromJson(responseContent); + return this.getArticleResults(GET_ARTICLE_SUMMARY, beginDate, endDate); } @Override public List getArticleTotal(Date beginDate, Date endDate) throws WxErrorException { - String url = API_URL_PREFIX + "/getarticletotal"; - JsonObject param = new JsonObject(); - param.addProperty("begin_date", this.dateFormat.format(beginDate)); - param.addProperty("end_date", this.dateFormat.format(endDate)); - String responseContent = this.wxMpService.post(url, param.toString()); + String responseContent = this.wxMpService.post(GET_ARTICLE_TOTAL, buildParams(beginDate, endDate)); return WxDataCubeArticleTotal.fromJson(responseContent); } @Override public List getUserRead(Date beginDate, Date endDate) throws WxErrorException { - String url = API_URL_PREFIX + "/getuserread"; - JsonObject param = new JsonObject(); - param.addProperty("begin_date", this.dateFormat.format(beginDate)); - param.addProperty("end_date", this.dateFormat.format(endDate)); - String responseContent = this.wxMpService.post(url, param.toString()); - return WxDataCubeArticleResult.fromJson(responseContent); + return this.getArticleResults(GET_USER_READ, beginDate, endDate); } @Override public List getUserReadHour(Date beginDate, Date endDate) throws WxErrorException { - String url = API_URL_PREFIX + "/getuserreadhour"; - JsonObject param = new JsonObject(); - param.addProperty("begin_date", this.dateFormat.format(beginDate)); - param.addProperty("end_date", this.dateFormat.format(endDate)); - String responseContent = this.wxMpService.post(url, param.toString()); - return WxDataCubeArticleResult.fromJson(responseContent); + return this.getArticleResults(GET_USER_READ_HOUR, beginDate, endDate); } @Override public List getUserShare(Date beginDate, Date endDate) throws WxErrorException { - String url = API_URL_PREFIX + "/getusershare"; - JsonObject param = new JsonObject(); - param.addProperty("begin_date", this.dateFormat.format(beginDate)); - param.addProperty("end_date", this.dateFormat.format(endDate)); - String responseContent = this.wxMpService.post(url, param.toString()); - return WxDataCubeArticleResult.fromJson(responseContent); + return this.getArticleResults(GET_USER_SHARE, beginDate, endDate); } @Override public List getUserShareHour(Date beginDate, Date endDate) throws WxErrorException { - String url = API_URL_PREFIX + "/getusersharehour"; - JsonObject param = new JsonObject(); - param.addProperty("begin_date", this.dateFormat.format(beginDate)); - param.addProperty("end_date", this.dateFormat.format(endDate)); - String responseContent = this.wxMpService.post(url, param.toString()); + return this.getArticleResults(GET_USER_SHARE_HOUR, beginDate, endDate); + } + + private List getArticleResults(String url, Date beginDate, Date endDate) throws WxErrorException { + String responseContent = this.wxMpService.post(url, buildParams(beginDate, endDate)); return WxDataCubeArticleResult.fromJson(responseContent); } @Override - public List getUpstreamMsg(Date beginDate, Date endDate) - throws WxErrorException { - String url = API_URL_PREFIX + "/getupstreammsg"; - JsonObject param = new JsonObject(); - param.addProperty("begin_date", this.dateFormat.format(beginDate)); - param.addProperty("end_date", this.dateFormat.format(endDate)); - String responseContent = this.wxMpService.post(url, param.toString()); - return WxDataCubeMsgResult.fromJson(responseContent); + public List getUpstreamMsg(Date beginDate, Date endDate) throws WxErrorException { + return this.getUpstreamMsg(GET_UPSTREAM_MSG, beginDate, endDate); } @Override - public List getUpstreamMsgHour(Date beginDate, - Date endDate) throws WxErrorException { - String url = API_URL_PREFIX + "/getupstreammsghour"; - JsonObject param = new JsonObject(); - param.addProperty("begin_date", this.dateFormat.format(beginDate)); - param.addProperty("end_date", this.dateFormat.format(endDate)); - String responseContent = this.wxMpService.post(url, param.toString()); - return WxDataCubeMsgResult.fromJson(responseContent); + public List getUpstreamMsgHour(Date beginDate, Date endDate) throws WxErrorException { + return this.getUpstreamMsg(GET_UPSTREAM_MSG_HOUR, beginDate, endDate); } @Override - public List getUpstreamMsgWeek(Date beginDate, - Date endDate) throws WxErrorException { - String url = API_URL_PREFIX + "/getupstreammsgweek"; - JsonObject param = new JsonObject(); - param.addProperty("begin_date", this.dateFormat.format(beginDate)); - param.addProperty("end_date", this.dateFormat.format(endDate)); - String responseContent = this.wxMpService.post(url, param.toString()); - return WxDataCubeMsgResult.fromJson(responseContent); + public List getUpstreamMsgWeek(Date beginDate, Date endDate) throws WxErrorException { + return this.getUpstreamMsg(GET_UPSTREAM_MSG_WEEK, beginDate, endDate); } @Override - public List getUpstreamMsgMonth(Date beginDate, - Date endDate) throws WxErrorException { - String url = API_URL_PREFIX + "/getupstreammsgmonth"; - JsonObject param = new JsonObject(); - param.addProperty("begin_date", this.dateFormat.format(beginDate)); - param.addProperty("end_date", this.dateFormat.format(endDate)); - String responseContent = this.wxMpService.post(url, param.toString()); - return WxDataCubeMsgResult.fromJson(responseContent); + public List getUpstreamMsgMonth(Date beginDate, Date endDate) throws WxErrorException { + return this.getUpstreamMsg(GET_UPSTREAM_MSG_MONTH, beginDate, endDate); } @Override - public List getUpstreamMsgDist(Date beginDate, - Date endDate) throws WxErrorException { - String url = API_URL_PREFIX + "/getupstreammsgdist"; - JsonObject param = new JsonObject(); - param.addProperty("begin_date", this.dateFormat.format(beginDate)); - param.addProperty("end_date", this.dateFormat.format(endDate)); - String responseContent = this.wxMpService.post(url, param.toString()); - return WxDataCubeMsgResult.fromJson(responseContent); + public List getUpstreamMsgDist(Date beginDate, Date endDate) throws WxErrorException { + return this.getUpstreamMsg(GET_UPSTREAM_MSG_DIST, beginDate, endDate); } @Override - public List getUpstreamMsgDistWeek(Date beginDate, - Date endDate) throws WxErrorException { - String url = API_URL_PREFIX + "/getupstreammsgdistweek"; - JsonObject param = new JsonObject(); - param.addProperty("begin_date", this.dateFormat.format(beginDate)); - param.addProperty("end_date", this.dateFormat.format(endDate)); - String responseContent = this.wxMpService.post(url, param.toString()); - return WxDataCubeMsgResult.fromJson(responseContent); + public List getUpstreamMsgDistWeek(Date beginDate, Date endDate) throws WxErrorException { + return this.getUpstreamMsg(GET_UPSTREAM_MSG_DIST_WEEK, beginDate, endDate); } @Override - public List getUpstreamMsgDistMonth(Date beginDate, - Date endDate) throws WxErrorException { - String url = API_URL_PREFIX + "/getupstreammsgdistmonth"; - JsonObject param = new JsonObject(); - param.addProperty("begin_date", this.dateFormat.format(beginDate)); - param.addProperty("end_date", this.dateFormat.format(endDate)); - String responseContent = this.wxMpService.post(url, param.toString()); + public List getUpstreamMsgDistMonth(Date beginDate, Date endDate) throws WxErrorException { + return this.getUpstreamMsg(GET_UPSTREAM_MSG_DIST_MONTH, beginDate, endDate); + } + + private List getUpstreamMsg(String url, Date beginDate, Date endDate) throws WxErrorException { + String responseContent = this.wxMpService.post(url, buildParams(beginDate, endDate)); return WxDataCubeMsgResult.fromJson(responseContent); } @Override - public List getInterfaceSummary(Date beginDate, - Date endDate) throws WxErrorException { - String url = API_URL_PREFIX + "/getinterfacesummary"; - JsonObject param = new JsonObject(); - param.addProperty("begin_date", this.dateFormat.format(beginDate)); - param.addProperty("end_date", this.dateFormat.format(endDate)); - String responseContent = this.wxMpService.post(url, param.toString()); + public List getInterfaceSummary(Date beginDate, Date endDate) throws WxErrorException { + String responseContent = this.wxMpService.post(GET_INTERFACE_SUMMARY, buildParams(beginDate, endDate)); return WxDataCubeInterfaceResult.fromJson(responseContent); } - @Override - public List getInterfaceSummaryHour(Date beginDate, - Date endDate) throws WxErrorException { - String url = API_URL_PREFIX + "/getinterfacesummaryhour"; + private String buildParams(Date beginDate, Date endDate) { JsonObject param = new JsonObject(); param.addProperty("begin_date", this.dateFormat.format(beginDate)); param.addProperty("end_date", this.dateFormat.format(endDate)); - String responseContent = this.wxMpService.post(url, param.toString()); + return param.toString(); + } + + @Override + public List getInterfaceSummaryHour(Date beginDate, Date endDate) throws WxErrorException { + String responseContent = this.wxMpService.post(GET_INTERFACE_SUMMARY_HOUR, buildParams(beginDate, endDate)); return WxDataCubeInterfaceResult.fromJson(responseContent); } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMassMessageServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMassMessageServiceImpl.java new file mode 100644 index 0000000000..015c465c0f --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMassMessageServiceImpl.java @@ -0,0 +1,58 @@ +package me.chanjar.weixin.mp.api.impl; + +import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.mp.api.WxMpMassMessageService; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.bean.*; +import me.chanjar.weixin.mp.bean.result.WxMpMassSendResult; +import me.chanjar.weixin.mp.bean.result.WxMpMassUploadResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + *
+ * 群发消息服务类
+ * Created by Binary Wang on 2017-8-16.
+ * 
+ * + * @author Binary Wang + */ +public class WxMpMassMessageServiceImpl implements WxMpMassMessageService { + protected final Logger log = LoggerFactory.getLogger(this.getClass()); + private WxMpService wxMpService; + + public WxMpMassMessageServiceImpl(WxMpService wxMpService) { + this.wxMpService = wxMpService; + } + + @Override + public WxMpMassUploadResult massNewsUpload(WxMpMassNews news) throws WxErrorException { + String responseContent = this.wxMpService.post(MEDIA_UPLOAD_NEWS_URL, news.toJson()); + return WxMpMassUploadResult.fromJson(responseContent); + } + + @Override + public WxMpMassUploadResult massVideoUpload(WxMpMassVideo video) throws WxErrorException { + String responseContent = this.wxMpService.post(MEDIA_UPLOAD_VIDEO_URL, video.toJson()); + return WxMpMassUploadResult.fromJson(responseContent); + } + + @Override + public WxMpMassSendResult massGroupMessageSend(WxMpMassTagMessage message) throws WxErrorException { + String responseContent = this.wxMpService.post(WxMpMassMessageService.MESSAGE_MASS_SENDALL_URL, message.toJson()); + return WxMpMassSendResult.fromJson(responseContent); + } + + @Override + public WxMpMassSendResult massOpenIdsMessageSend(WxMpMassOpenIdsMessage message) throws WxErrorException { + String responseContent = this.wxMpService.post(MESSAGE_MASS_SEND_URL, message.toJson()); + return WxMpMassSendResult.fromJson(responseContent); + } + + @Override + public WxMpMassSendResult massMessagePreview(WxMpMassPreviewMessage wxMpMassPreviewMessage) throws Exception { + String responseContent = this.wxMpService.post(MESSAGE_MASS_PREVIEW_URL, wxMpMassPreviewMessage.toJson()); + return WxMpMassSendResult.fromJson(responseContent); + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMaterialServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMaterialServiceImpl.java index bb5857be83..90594fe031 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMaterialServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMaterialServiceImpl.java @@ -25,8 +25,7 @@ * Created by Binary Wang on 2016/7/21. */ public class WxMpMaterialServiceImpl implements WxMpMaterialService { - private static final String MEDIA_API_URL_PREFIX = "https://api.weixin.qq.com/cgi-bin/media"; - private static final String MATERIAL_API_URL_PREFIX = "https://api.weixin.qq.com/cgi-bin/material"; + private WxMpService wxMpService; public WxMpMaterialServiceImpl(WxMpService wxMpService) { @@ -45,28 +44,26 @@ public WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputS @Override public WxMediaUploadResult mediaUpload(String mediaType, File file) throws WxErrorException { - String url = MEDIA_API_URL_PREFIX + "/upload?type=" + mediaType; + String url = String.format(MEDIA_UPLOAD_URL, mediaType); return this.wxMpService.execute(MediaUploadRequestExecutor.create(this.wxMpService.getRequestHttp()), url, file); } @Override - public File mediaDownload(String media_id) throws WxErrorException { - String url = MEDIA_API_URL_PREFIX + "/get"; + public File mediaDownload(String mediaId) throws WxErrorException { return this.wxMpService.execute( MediaDownloadRequestExecutor.create(this.wxMpService.getRequestHttp(), this.wxMpService.getWxMpConfigStorage().getTmpDirFile()), - url, - "media_id=" + media_id); + MEDIA_GET_URL, + "media_id=" + mediaId); } @Override public WxMediaImgUploadResult mediaImgUpload(File file) throws WxErrorException { - String url = MEDIA_API_URL_PREFIX + "/uploadimg"; - return this.wxMpService.execute(MediaImgUploadRequestExecutor.create(this.wxMpService.getRequestHttp()), url, file); + return this.wxMpService.execute(MediaImgUploadRequestExecutor.create(this.wxMpService.getRequestHttp()), IMG_UPLOAD_URL, file); } @Override public WxMpMaterialUploadResult materialFileUpload(String mediaType, WxMpMaterial material) throws WxErrorException { - String url = MATERIAL_API_URL_PREFIX + "/add_material?type=" + mediaType; + String url = String.format(MATERIAL_ADD_URL, mediaType); return this.wxMpService.execute(MaterialUploadRequestExecutor.create(this.wxMpService.getRequestHttp()), url, material); } @@ -75,33 +72,29 @@ public WxMpMaterialUploadResult materialNewsUpload(WxMpMaterialNews news) throws if (news == null || news.isEmpty()) { throw new IllegalArgumentException("news is empty!"); } - String url = MATERIAL_API_URL_PREFIX + "/add_news"; - String responseContent = this.wxMpService.post(url, news.toJson()); + String responseContent = this.wxMpService.post(NEWS_ADD_URL, news.toJson()); return WxMpMaterialUploadResult.fromJson(responseContent); } @Override - public InputStream materialImageOrVoiceDownload(String media_id) throws WxErrorException { - String url = MATERIAL_API_URL_PREFIX + "/get_material"; - return this.wxMpService.execute(MaterialVoiceAndImageDownloadRequestExecutor.create(this.wxMpService.getRequestHttp(), this.wxMpService.getWxMpConfigStorage().getTmpDirFile()), url, media_id); + public InputStream materialImageOrVoiceDownload(String mediaId) throws WxErrorException { + return this.wxMpService.execute(MaterialVoiceAndImageDownloadRequestExecutor + .create(this.wxMpService.getRequestHttp(), this.wxMpService.getWxMpConfigStorage().getTmpDirFile()), MATERIAL_GET_URL, mediaId); } @Override - public WxMpMaterialVideoInfoResult materialVideoInfo(String media_id) throws WxErrorException { - String url = MATERIAL_API_URL_PREFIX + "/get_material"; - return this.wxMpService.execute(MaterialVideoInfoRequestExecutor.create(this.wxMpService.getRequestHttp()), url, media_id); + public WxMpMaterialVideoInfoResult materialVideoInfo(String mediaId) throws WxErrorException { + return this.wxMpService.execute(MaterialVideoInfoRequestExecutor.create(this.wxMpService.getRequestHttp()), MATERIAL_GET_URL, mediaId); } @Override - public WxMpMaterialNews materialNewsInfo(String media_id) throws WxErrorException { - String url = MATERIAL_API_URL_PREFIX + "/get_material"; - return this.wxMpService.execute(MaterialNewsInfoRequestExecutor.create(this.wxMpService.getRequestHttp()), url, media_id); + public WxMpMaterialNews materialNewsInfo(String mediaId) throws WxErrorException { + return this.wxMpService.execute(MaterialNewsInfoRequestExecutor.create(this.wxMpService.getRequestHttp()), MATERIAL_GET_URL, mediaId); } @Override public boolean materialNewsUpdate(WxMpMaterialArticleUpdate wxMpMaterialArticleUpdate) throws WxErrorException { - String url = MATERIAL_API_URL_PREFIX + "/update_news"; - String responseText = this.wxMpService.post(url, wxMpMaterialArticleUpdate.toJson()); + String responseText = this.wxMpService.post(NEWS_UPDATE_URL, wxMpMaterialArticleUpdate.toJson()); WxError wxError = WxError.fromJson(responseText); if (wxError.getErrorCode() == 0) { return true; @@ -111,15 +104,13 @@ public boolean materialNewsUpdate(WxMpMaterialArticleUpdate wxMpMaterialArticleU } @Override - public boolean materialDelete(String media_id) throws WxErrorException { - String url = MATERIAL_API_URL_PREFIX + "/del_material"; - return this.wxMpService.execute(MaterialDeleteRequestExecutor.create(this.wxMpService.getRequestHttp()), url, media_id); + public boolean materialDelete(String mediaId) throws WxErrorException { + return this.wxMpService.execute(MaterialDeleteRequestExecutor.create(this.wxMpService.getRequestHttp()), MATERIAL_DEL_URL, mediaId); } @Override public WxMpMaterialCountResult materialCount() throws WxErrorException { - String url = MATERIAL_API_URL_PREFIX + "/get_materialcount"; - String responseText = this.wxMpService.get(url, null); + String responseText = this.wxMpService.get(MATERIAL_GET_COUNT_URL, null); WxError wxError = WxError.fromJson(responseText); if (wxError.getErrorCode() == 0) { return WxMpGsonBuilder.create().fromJson(responseText, WxMpMaterialCountResult.class); @@ -130,12 +121,11 @@ public WxMpMaterialCountResult materialCount() throws WxErrorException { @Override public WxMpMaterialNewsBatchGetResult materialNewsBatchGet(int offset, int count) throws WxErrorException { - String url = MATERIAL_API_URL_PREFIX + "/batchget_material"; Map params = new HashMap<>(); params.put("type", WxConsts.MATERIAL_NEWS); params.put("offset", offset); params.put("count", count); - String responseText = this.wxMpService.post(url, WxGsonBuilder.create().toJson(params)); + String responseText = this.wxMpService.post(MATERIAL_BATCHGET_URL, WxGsonBuilder.create().toJson(params)); WxError wxError = WxError.fromJson(responseText); if (wxError.getErrorCode() == 0) { return WxMpGsonBuilder.create().fromJson(responseText, WxMpMaterialNewsBatchGetResult.class); @@ -146,12 +136,11 @@ public WxMpMaterialNewsBatchGetResult materialNewsBatchGet(int offset, int count @Override public WxMpMaterialFileBatchGetResult materialFileBatchGet(String type, int offset, int count) throws WxErrorException { - String url = MATERIAL_API_URL_PREFIX + "/batchget_material"; Map params = new HashMap<>(); params.put("type", type); params.put("offset", offset); params.put("count", count); - String responseText = this.wxMpService.post(url, WxGsonBuilder.create().toJson(params)); + String responseText = this.wxMpService.post(MATERIAL_BATCHGET_URL, WxGsonBuilder.create().toJson(params)); WxError wxError = WxError.fromJson(responseText); if (wxError.getErrorCode() == 0) { return WxMpGsonBuilder.create().fromJson(responseText, WxMpMaterialFileBatchGetResult.class); diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMemberCardServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMemberCardServiceImpl.java new file mode 100644 index 0000000000..b3a43a765a --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMemberCardServiceImpl.java @@ -0,0 +1,104 @@ +package me.chanjar.weixin.mp.api.impl; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.reflect.TypeToken; +import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.mp.api.WxMpMemberCardService; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardActivatedMessage; +import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardUpdateMessage; +import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardUpdateResult; +import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardUserInfoResult; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 会员卡相关接口的实现类 + * + * @author YuJian(mgcnrx11@gmail.com) + * @version 2017/7/8 + */ +public class WxMpMemberCardServiceImpl implements WxMpMemberCardService { + + private final Logger log = LoggerFactory.getLogger(WxMpMemberCardServiceImpl.class); + + private static final String MEMBER_CARD_ACTIVATE = "https://api.weixin.qq.com/card/membercard/activate"; + private static final String MEMBER_CARD_USER_INFO_GET = "https://api.weixin.qq.com/card/membercard/userinfo/get"; + private static final String MEMBER_CARD_UPDATE_USER = "https://api.weixin.qq.com/card/membercard/updateuser"; + + private WxMpService wxMpService; + + private static final Gson GSON = new Gson(); + + WxMpMemberCardServiceImpl(WxMpService wxMpService) { + this.wxMpService = wxMpService; + } + + /** + * 得到WxMpService + */ + @Override + public WxMpService getWxMpService() { + return this.wxMpService; + } + + /** + * 会员卡激活接口 + * + * @param activatedMessage 激活所需参数 + * @return 调用返回的JSON字符串。 + * @throws WxErrorException 接口调用失败抛出的异常 + */ + @Override + public String activateMemberCard(WxMpMemberCardActivatedMessage activatedMessage) throws WxErrorException { + return this.wxMpService.post(MEMBER_CARD_ACTIVATE, GSON.toJson(activatedMessage)); + } + + /** + * 拉取会员信息接口 + * + * @param cardId 会员卡的CardId,微信分配 + * @param code 领取会员的会员卡Code + * @return 会员信息的结果对象 + * @throws WxErrorException 接口调用失败抛出的异常 + */ + @Override + public WxMpMemberCardUserInfoResult getUserInfo(String cardId, String code) throws WxErrorException { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("card_id", cardId); + jsonObject.addProperty("code",code); + + String responseContent = this.getWxMpService().post(MEMBER_CARD_USER_INFO_GET, jsonObject.toString()); + JsonElement tmpJsonElement = new JsonParser().parse(responseContent); + return WxMpGsonBuilder.INSTANCE.create().fromJson(tmpJsonElement, + new TypeToken() { + }.getType()); + } + + /** + * 当会员持卡消费后,支持开发者调用该接口更新会员信息。会员卡交易后的每次信息变更需通过该接口通知微信,便于后续消息通知及其他扩展功能。 + * + * 1.开发者可以同时传入add_bonus和bonus解决由于同步失败带来的幂等性问题。同时传入add_bonus和bonus时 + * add_bonus作为积分变动消息中的变量值,而bonus作为卡面上的总积分额度显示。余额变动同理。 + * 2.开发者可以传入is_notify_bonus控制特殊的积分对账变动不发送消息,余额变动同理。 + * + * @param updateUserMessage 更新会员信息所需字段消息 + * @return 调用返回的JSON字符串。 + * @throws WxErrorException 接口调用失败抛出的异常 + */ + @Override + public WxMpMemberCardUpdateResult updateUserMemberCard(WxMpMemberCardUpdateMessage updateUserMessage) + throws WxErrorException { + + String responseContent = this.getWxMpService().post(MEMBER_CARD_UPDATE_USER, GSON.toJson(updateUserMessage)); + + JsonElement tmpJsonElement = new JsonParser().parse(responseContent); + return WxMpGsonBuilder.INSTANCE.create().fromJson(tmpJsonElement, + new TypeToken() { + }.getType()); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpQrcodeServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpQrcodeServiceImpl.java index 817a0b68f2..5f6c3ad54f 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpQrcodeServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpQrcodeServiceImpl.java @@ -7,6 +7,7 @@ import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.bean.result.WxMpQrCodeTicket; import me.chanjar.weixin.mp.util.http.QrCodeRequestExecutor; +import org.apache.commons.lang3.StringUtils; import java.io.File; import java.io.UnsupportedEncodingException; @@ -54,6 +55,38 @@ public WxMpQrCodeTicket qrCodeCreateTmpTicket(int sceneId, Integer expireSeconds return WxMpQrCodeTicket.fromJson(responseContent); } + + @Override + public WxMpQrCodeTicket qrCodeCreateTmpTicket(String sceneStr, Integer expireSeconds) throws WxErrorException { + if (StringUtils.isBlank(sceneStr)) { + throw new WxErrorException(WxError.newBuilder().setErrorCode(-1).setErrorMsg("临时二维码场景值不能为空!").build()); + } + + //expireSeconds 该二维码有效时间,以秒为单位。 最大不超过2592000(即30天),此字段如果不填,则默认有效期为30秒。 + if (expireSeconds != null && expireSeconds > 2592000) { + throw new WxErrorException(WxError.newBuilder().setErrorCode(-1) + .setErrorMsg("临时二维码有效时间最大不能超过2592000(即30天)!").build()); + } + + if (expireSeconds == null) { + expireSeconds = 30; + } + + String url = API_URL_PREFIX + "/create"; + JsonObject json = new JsonObject(); + json.addProperty("action_name", "QR_STR_SCENE"); + json.addProperty("expire_seconds", expireSeconds); + + JsonObject actionInfo = new JsonObject(); + JsonObject scene = new JsonObject(); + scene.addProperty("scene_str", sceneStr); + actionInfo.add("scene", scene); + json.add("action_info", actionInfo); + String responseContent = this.wxMpService.post(url, json.toString()); + return WxMpQrCodeTicket.fromJson(responseContent); + } + + @Override public WxMpQrCodeTicket qrCodeCreateLastTicket(int sceneId) throws WxErrorException { if (sceneId < 1 || sceneId > 100000) { diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/AbstractWxMpServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceAbstractImpl.java similarity index 87% rename from weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/AbstractWxMpServiceImpl.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceAbstractImpl.java index 527f6fe2e1..91dff9babb 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/AbstractWxMpServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceAbstractImpl.java @@ -22,7 +22,7 @@ import java.io.IOException; import java.util.concurrent.locks.Lock; -public abstract class AbstractWxMpServiceImpl implements WxMpService, RequestHttp { +public abstract class WxMpServiceAbstractImpl implements WxMpService, RequestHttp { private static final JsonParser JSON_PARSER = new JsonParser(); @@ -41,6 +41,9 @@ public abstract class AbstractWxMpServiceImpl implements WxMpService, Requ private WxMpUserBlacklistService blackListService = new WxMpUserBlacklistServiceImpl(this); private WxMpTemplateMsgService templateMsgService = new WxMpTemplateMsgServiceImpl(this); private WxMpDeviceService deviceService = new WxMpDeviceServiceImpl(this); + private WxMpShakeService shakeService = new WxMpShakeServiceImpl(this); + private WxMpMemberCardService memberCardService = new WxMpMemberCardServiceImpl(this); + private WxMpMassMessageService massMessageService = new WxMpMassMessageServiceImpl(this); private int retrySleepMillis = 1000; private int maxRetryTimes = 5; @@ -52,6 +55,7 @@ public boolean checkSignature(String timestamp, String nonce, String signature) return SHA1.gen(this.getWxMpConfigStorage().getToken(), timestamp, nonce) .equals(signature); } catch (Exception e) { + this.log.error("Checking signature failed, and the reason is :" + e.getMessage()); return false; } } @@ -63,10 +67,9 @@ public String getJsapiTicket() throws WxErrorException { @Override public String getJsapiTicket(boolean forceRefresh) throws WxErrorException { - Lock lock = this.getWxMpConfigStorage().getJsapiTicketLock(); + Lock lock = this.getWxMpConfigStorage().getAccessTokenLock(); try { lock.lock(); - if (forceRefresh) { this.getWxMpConfigStorage().expireJsapiTicket(); } @@ -106,36 +109,6 @@ public String getAccessToken() throws WxErrorException { return getAccessToken(false); } - @Override - public WxMpMassUploadResult massNewsUpload(WxMpMassNews news) throws WxErrorException { - String responseContent = this.post(WxMpService.MEDIA_UPLOAD_NEWS_URL, news.toJson()); - return WxMpMassUploadResult.fromJson(responseContent); - } - - @Override - public WxMpMassUploadResult massVideoUpload(WxMpMassVideo video) throws WxErrorException { - String responseContent = this.post(WxMpService.MEDIA_UPLOAD_VIDEO_URL, video.toJson()); - return WxMpMassUploadResult.fromJson(responseContent); - } - - @Override - public WxMpMassSendResult massGroupMessageSend(WxMpMassTagMessage message) throws WxErrorException { - String responseContent = this.post(WxMpService.MESSAGE_MASS_SENDALL_URL, message.toJson()); - return WxMpMassSendResult.fromJson(responseContent); - } - - @Override - public WxMpMassSendResult massOpenIdsMessageSend(WxMpMassOpenIdsMessage message) throws WxErrorException { - String responseContent = this.post(WxMpService.MESSAGE_MASS_SEND_URL, message.toJson()); - return WxMpMassSendResult.fromJson(responseContent); - } - - @Override - public WxMpMassSendResult massMessagePreview(WxMpMassPreviewMessage wxMpMassPreviewMessage) throws Exception { - String responseContent = this.post(WxMpService.MESSAGE_MASS_PREVIEW_URL, wxMpMassPreviewMessage.toJson()); - return WxMpMassSendResult.fromJson(responseContent); - } - @Override public String shortUrl(String long_url) throws WxErrorException { JsonObject o = new JsonObject(); @@ -208,8 +181,7 @@ public boolean oauth2validateAccessToken(WxMpOAuth2AccessToken oAuth2AccessToken String url = String.format(WxMpService.OAUTH2_VALIDATE_TOKEN_URL, oAuth2AccessToken.getAccessToken(), oAuth2AccessToken.getOpenId()); try { - RequestExecutor executor = SimpleGetRequestExecutor.create(this); - executor.execute(url, null); + SimpleGetRequestExecutor.create(this).execute(url, null); } catch (IOException e) { throw new RuntimeException(e); } catch (WxErrorException e) { @@ -230,6 +202,11 @@ public String[] getCallbackIP() throws WxErrorException { return ipArray; } + @Override + public WxMpCurrentAutoReplyInfo getCurrentAutoReplyInfo() throws WxErrorException { + return WxMpCurrentAutoReplyInfo.fromJson(this.get(GET_CURRENT_AUTOREPLY_INFO_URL, null)); + } + @Override public String get(String url, String queryParam) throws WxErrorException { return execute(SimpleGetRequestExecutor.create(this), url, queryParam); @@ -285,7 +262,7 @@ public synchronized T executeInternal(RequestExecutor executor, Str try { T result = executor.execute(uriWithAccessToken, data); - this.log.debug("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}", uriWithAccessToken, data, result); + this.log.debug("\n【请求地址】: {}\n【请求参数】:{}\n【响应数据】:{}", uriWithAccessToken, data, result); return result; } catch (WxErrorException e) { WxError error = e.getError(); @@ -304,12 +281,12 @@ public synchronized T executeInternal(RequestExecutor executor, Str } if (error.getErrorCode() != 0) { - this.log.error("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}", uriWithAccessToken, data, error); - throw new WxErrorException(error); + this.log.error("\n【请求地址】: {}\n【请求参数】:{}\n【错误信息】:{}", uriWithAccessToken, data, error); + throw new WxErrorException(error, e); } return null; } catch (IOException e) { - this.log.error("\n[URL]: {}\n[PARAMS]: {}\n[EXCEPTION]: {}", uriWithAccessToken, data, e.getMessage()); + this.log.error("\n【请求地址】: {}\n【请求参数】:{}\n【异常信息】:{}", uriWithAccessToken, data, e.getMessage()); throw new RuntimeException(e); } } @@ -394,4 +371,24 @@ public WxMpTemplateMsgService getTemplateMsgService() { public WxMpDeviceService getDeviceService() { return this.deviceService; } + + @Override + public WxMpShakeService getShakeService() { + return this.shakeService; + } + + @Override + public WxMpMemberCardService getMemberCardService() { + return this.memberCardService; + } + + @Override + public RequestHttp getRequestHttp() { + return this; + } + + @Override + public WxMpMassMessageService getMassMessageService() { + return this.massMessageService; + } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/apache/WxMpServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceApacheHttpClientImpl.java similarity index 90% rename from weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/apache/WxMpServiceImpl.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceApacheHttpClientImpl.java index fdce402ed8..ceb0b783e2 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/apache/WxMpServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceApacheHttpClientImpl.java @@ -1,15 +1,13 @@ -package me.chanjar.weixin.mp.api.impl.apache; +package me.chanjar.weixin.mp.api.impl; import me.chanjar.weixin.common.bean.WxAccessToken; import me.chanjar.weixin.common.bean.result.WxError; import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.common.util.http.HttpType; -import me.chanjar.weixin.common.util.http.RequestHttp; import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder; import me.chanjar.weixin.mp.api.WxMpConfigStorage; import me.chanjar.weixin.mp.api.WxMpService; -import me.chanjar.weixin.mp.api.impl.AbstractWxMpServiceImpl; import org.apache.http.HttpHost; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; @@ -23,7 +21,7 @@ /** * apache-http方式实现 */ -public class WxMpServiceImpl extends AbstractWxMpServiceImpl { +public class WxMpServiceApacheHttpClientImpl extends WxMpServiceAbstractImpl { private CloseableHttpClient httpClient; private HttpHost httpProxy; @@ -39,7 +37,7 @@ public HttpHost getRequestHttpProxy() { @Override public HttpType getRequestType() { - return HttpType.apacheHttp; + return HttpType.APACHE_HTTP; } @Override @@ -62,11 +60,6 @@ public void initHttp() { this.httpClient = apacheHttpClientBuilder.build(); } - @Override - public RequestHttp getRequestHttp() { - return this; - } - @Override public String getAccessToken(boolean forceRefresh) throws WxErrorException { Lock lock = this.getWxMpConfigStorage().getAccessTokenLock(); diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImpl.java new file mode 100644 index 0000000000..4d2017dd63 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImpl.java @@ -0,0 +1,11 @@ +package me.chanjar.weixin.mp.api.impl; + +/** + *
+ * 默认接口实现类,使用apache httpclient实现
+ * Created by Binary Wang on 2017-5-27.
+ * @author binarywang(Binary Wang)
+ * 
+ */ +public class WxMpServiceImpl extends WxMpServiceApacheHttpClientImpl { +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/jodd/WxMpServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceJoddHttpImpl.java similarity index 87% rename from weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/jodd/WxMpServiceImpl.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceJoddHttpImpl.java index 6f02ff80c8..0551db67c6 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/jodd/WxMpServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceJoddHttpImpl.java @@ -1,4 +1,4 @@ -package me.chanjar.weixin.mp.api.impl.jodd; +package me.chanjar.weixin.mp.api.impl; import jodd.http.*; import jodd.http.net.SocketHttpConnectionProvider; @@ -6,17 +6,15 @@ import me.chanjar.weixin.common.bean.result.WxError; import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.common.util.http.HttpType; -import me.chanjar.weixin.common.util.http.RequestHttp; import me.chanjar.weixin.mp.api.WxMpConfigStorage; import me.chanjar.weixin.mp.api.WxMpService; -import me.chanjar.weixin.mp.api.impl.AbstractWxMpServiceImpl; import java.util.concurrent.locks.Lock; /** * jodd-http方式实现 */ -public class WxMpServiceImpl extends AbstractWxMpServiceImpl { +public class WxMpServiceJoddHttpImpl extends WxMpServiceAbstractImpl { private HttpConnectionProvider httpClient; private ProxyInfo httpProxy; @@ -32,7 +30,7 @@ public ProxyInfo getRequestHttpProxy() { @Override public HttpType getRequestType() { - return HttpType.joddHttp; + return HttpType.JODD_HTTP; } @Override @@ -47,12 +45,6 @@ public void initHttp() { httpClient = JoddHttp.httpConnectionProvider; } - @Override - public RequestHttp getRequestHttp() { - return this; - } - - @Override public String getAccessToken(boolean forceRefresh) throws WxErrorException { Lock lock = this.getWxMpConfigStorage().getAccessTokenLock(); diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/okhttp/WxMpServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceOkHttpImpl.java similarity index 53% rename from weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/okhttp/WxMpServiceImpl.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceOkHttpImpl.java index afa0ed7148..9f2a17add1 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/okhttp/WxMpServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceOkHttpImpl.java @@ -1,40 +1,44 @@ -package me.chanjar.weixin.mp.api.impl.okhttp; +package me.chanjar.weixin.mp.api.impl; import me.chanjar.weixin.common.bean.WxAccessToken; import me.chanjar.weixin.common.bean.result.WxError; import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.common.util.http.HttpType; -import me.chanjar.weixin.common.util.http.RequestHttp; -import me.chanjar.weixin.common.util.http.okhttp.OkhttpProxyInfo; +import me.chanjar.weixin.common.util.http.okhttp.OkHttpProxyInfo; import me.chanjar.weixin.mp.api.WxMpConfigStorage; import me.chanjar.weixin.mp.api.WxMpService; -import me.chanjar.weixin.mp.api.impl.AbstractWxMpServiceImpl; import okhttp3.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.concurrent.locks.Lock; -public class WxMpServiceImpl extends AbstractWxMpServiceImpl { - private ConnectionPool httpClient; - private OkhttpProxyInfo httpProxy; +public class WxMpServiceOkHttpImpl extends WxMpServiceAbstractImpl { + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + private OkHttpClient httpClient; + private OkHttpProxyInfo httpProxy; @Override - public ConnectionPool getRequestHttpClient() { + public OkHttpClient getRequestHttpClient() { return httpClient; } @Override - public OkhttpProxyInfo getRequestHttpProxy() { + public OkHttpProxyInfo getRequestHttpProxy() { return httpProxy; } @Override public HttpType getRequestType() { - return HttpType.okHttp; + return HttpType.OK_HTTP; } @Override public String getAccessToken(boolean forceRefresh) throws WxErrorException { + logger.debug("WxMpServiceOkHttpImpl is running"); Lock lock = this.getWxMpConfigStorage().getAccessTokenLock(); try { lock.lock(); @@ -47,26 +51,8 @@ public String getAccessToken(boolean forceRefresh) throws WxErrorException { String url = String.format(WxMpService.GET_ACCESS_TOKEN_URL, this.getWxMpConfigStorage().getAppId(), this.getWxMpConfigStorage().getSecret()); - OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder().connectionPool(httpClient); - //设置代理 - if (httpProxy != null) { - clientBuilder.proxy(getRequestHttpProxy().getProxy()); - } - //设置授权 - clientBuilder.authenticator(new Authenticator() { - @Override - public Request authenticate(Route route, Response response) throws IOException { - String credential = Credentials.basic(httpProxy.getProxyUsername(), httpProxy.getProxyPassword()); - return response.request().newBuilder() - .header("Authorization", credential) - .build(); - } - }); - //得到httpClient - OkHttpClient client = clientBuilder.build(); - Request request = new Request.Builder().url(url).get().build(); - Response response = client.newCall(request).execute(); + Response response = getRequestHttpClient().newCall(request).execute(); String resultContent = response.body().string(); WxError error = WxError.fromJson(resultContent); if (error.getErrorCode() != 0) { @@ -86,19 +72,29 @@ public Request authenticate(Route route, Response response) throws IOException { @Override public void initHttp() { + logger.debug("WxMpServiceOkHttpImpl initHttp"); WxMpConfigStorage configStorage = this.getWxMpConfigStorage(); if (configStorage.getHttpProxyHost() != null && configStorage.getHttpProxyPort() > 0) { - httpProxy = new OkhttpProxyInfo(OkhttpProxyInfo.ProxyType.SOCKS5, configStorage.getHttpProxyHost(), configStorage.getHttpProxyPort(), configStorage.getHttpProxyUsername(), configStorage.getHttpProxyPassword()); + httpProxy = OkHttpProxyInfo.socks5Proxy(configStorage.getHttpProxyHost(), configStorage.getHttpProxyPort(), configStorage.getHttpProxyUsername(), configStorage.getHttpProxyPassword()); } - - httpClient = new ConnectionPool(); - } - - @Override - public RequestHttp getRequestHttp() { - return this; + OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder(); + //设置代理 + if (httpProxy != null) { + clientBuilder.proxy(getRequestHttpProxy().getProxy()); + + //设置授权 + clientBuilder.authenticator(new Authenticator() { + @Override + public Request authenticate(Route route, Response response) throws IOException { + String credential = Credentials.basic(httpProxy.getProxyUsername(), httpProxy.getProxyPassword()); + return response.request().newBuilder() + .header("Authorization", credential) + .build(); + } + }); + } + httpClient = clientBuilder.build(); } - } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpShakeServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpShakeServiceImpl.java new file mode 100644 index 0000000000..bee636fd54 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpShakeServiceImpl.java @@ -0,0 +1,39 @@ +package me.chanjar.weixin.mp.api.impl; + +import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.api.WxMpShakeService; +import me.chanjar.weixin.mp.bean.WxMpShakeInfoResult; +import me.chanjar.weixin.mp.bean.WxMpShakeQuery; + +/** + * Created by rememberber on 2017/6/5. + * @author rememberber + */ +public class WxMpShakeServiceImpl implements WxMpShakeService { + + private WxMpService wxMpService; + + public WxMpShakeServiceImpl(WxMpService wxMpService) { + this.wxMpService = wxMpService; + } + + /** + *
+   * 获取设备及用户信息
+ * 获取设备信息,包括UUID、major、minor,以及距离、openID等信息。 + * 详情请见: https://mp.weixin.qq.com/wiki?action=doc&id=mp1443447963 + * http请求方式: POST(请使用https协议) + * 接口地址:https://api.weixin.qq.com/shakearound/user/getshakeinfo?access_token=ACCESS_TOKE + *
+ * + * @param wxMpShakeQuery 查询参数 + */ + @Override + public WxMpShakeInfoResult getShakeInfo(WxMpShakeQuery wxMpShakeQuery) throws WxErrorException { + String url = "https://api.weixin.qq.com/shakearound/user/getshakeinfo"; + String postData = wxMpShakeQuery.toJsonString(); + String responseContent = this.wxMpService.post(url, postData); + return WxMpShakeInfoResult.fromJson(responseContent); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpShakeInfoResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpShakeInfoResult.java new file mode 100644 index 0000000000..ac94b457b0 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpShakeInfoResult.java @@ -0,0 +1,164 @@ +package me.chanjar.weixin.mp.bean; + +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; + +/** + * 摇一摇周边:获取设备及用户信息接口返回JSON数据接收类 + * Created by rememberber on 2017/6/5. + * + * @author rememberber + */ +public class WxMpShakeInfoResult implements Serializable { + + private Integer errcode; + + private String errmsg; + + private Data data; + + public static WxMpShakeInfoResult fromJson(String json) { + return WxMpGsonBuilder.INSTANCE.create().fromJson(json, WxMpShakeInfoResult.class); + } + + public class Data { + + private String page_id; + + private String openid; + + private String poi_id; + + private String brand_userame; + + private BeaconInfo beacon_info; + + public class BeaconInfo { + + private double distance; + + private Integer major; + + private Integer measure_power; + + private Integer minor; + + private Integer rssi; + + private String uuid; + + public double getDistance() { + return distance; + } + + public void setDistance(double distance) { + this.distance = distance; + } + + public Integer getMajor() { + return major; + } + + public void setMajor(Integer major) { + this.major = major; + } + + public Integer getMeasure_power() { + return measure_power; + } + + public void setMeasure_power(Integer measure_power) { + this.measure_power = measure_power; + } + + public Integer getMinor() { + return minor; + } + + public void setMinor(Integer minor) { + this.minor = minor; + } + + public Integer getRssi() { + return rssi; + } + + public void setRssi(Integer rssi) { + this.rssi = rssi; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + } + + public String getPage_id() { + return page_id; + } + + public void setPage_id(String page_id) { + this.page_id = page_id; + } + + public String getOpenid() { + return openid; + } + + public void setOpenid(String openid) { + this.openid = openid; + } + + public String getPoi_id() { + return poi_id; + } + + public void setPoi_id(String poi_id) { + this.poi_id = poi_id; + } + + public BeaconInfo getBeacon_info() { + return beacon_info; + } + + public void setBeacon_info(BeaconInfo beacon_info) { + this.beacon_info = beacon_info; + } + + public String getBrand_userame() { + return brand_userame; + } + + public void setBrand_userame(String brand_userame) { + this.brand_userame = brand_userame; + } + } + + public Integer getErrcode() { + return errcode; + } + + public void setErrcode(Integer errcode) { + this.errcode = errcode; + } + + public String getErrmsg() { + return errmsg; + } + + public void setErrmsg(String errmsg) { + this.errmsg = errmsg; + } + + public Data getData() { + return data; + } + + public void setData(Data data) { + this.data = data; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpShakeQuery.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpShakeQuery.java new file mode 100644 index 0000000000..bb9d326fad --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpShakeQuery.java @@ -0,0 +1,41 @@ +package me.chanjar.weixin.mp.bean; + +import com.google.gson.Gson; + +import java.util.HashMap; +import java.util.Map; + +/** + * Created by rememberber on 2017/6/5. + * + * @author rememberber + */ +public class WxMpShakeQuery { + + private String ticket; + + private int needPoi; + + public String toJsonString() { + Map map = new HashMap<>(); + map.put("ticket", this.ticket); + map.put("need_poi", this.needPoi); + return new Gson().toJson(map); + } + + public String getTicket() { + return ticket; + } + + public void setTicket(String ticket) { + this.ticket = ticket; + } + + public int getNeedPoi() { + return needPoi; + } + + public void setNeedPoi(int needPoi) { + this.needPoi = needPoi; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialNews.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialNews.java index a107facaed..51f2d0c6bd 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialNews.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialNews.java @@ -4,12 +4,16 @@ import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; import java.io.Serializable; +import java.util.Date; import java.util.ArrayList; import java.util.List; public class WxMpMaterialNews implements Serializable { private static final long serialVersionUID = -3283203652013494976L; + private Date createdTime; + private Date updatedTime; + private List articles = new ArrayList<>(); public List getArticles() { @@ -28,9 +32,25 @@ public boolean isEmpty() { return this.articles == null || this.articles.isEmpty(); } + public Date getCreatedTime() { + return this.createdTime; + } + + public void setCreatedTime(Date createdTime) { + this.createdTime = createdTime; + } + + public Date getUpdatedTime() { + return this.updatedTime; + } + + public void setUpdatedTime(Date updatedTime) { + this.updatedTime = updatedTime; + } + @Override public String toString() { - return ToStringUtils.toSimpleString(this); + return this.toJson(); } /** @@ -44,6 +64,8 @@ public String toString() { * 6. digest 图文消息的描述 * 7. showCoverPic 是否显示封面,true为显示,false为不显示 * 8. url 点击图文消息跳转链接 + * 9. need_open_comment(新增字段) 否 Uint32 是否打开评论,0不打开,1打开 + * 10. only_fans_can_comment(新增字段) 否 Uint32 是否粉丝才可评论,0所有人可评论,1粉丝才可评论 *
* * @author chanjarster @@ -87,6 +109,18 @@ public static class WxMpMaterialNewsArticle { */ private String url; + /** + * need_open_comment + * 是否打开评论,0不打开,1打开 + */ + private Boolean needOpenComment; + + /** + * only_fans_can_comment + * 是否粉丝才可评论,0所有人可评论,1粉丝才可评论 + */ + private Boolean onlyFansCanComment; + public String getThumbMediaId() { return this.thumbMediaId; } @@ -159,6 +193,22 @@ public void setThumbUrl(String thumbUrl) { this.thumbUrl = thumbUrl; } + public Boolean getNeedOpenComment() { + return this.needOpenComment; + } + + public void setNeedOpenComment(Boolean needOpenComment) { + this.needOpenComment = needOpenComment; + } + + public Boolean getOnlyFansCanComment() { + return this.onlyFansCanComment; + } + + public void setOnlyFansCanComment(Boolean onlyFansCanComment) { + this.onlyFansCanComment = onlyFansCanComment; + } + @Override public String toString() { return ToStringUtils.toSimpleString(this); diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialUploadResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialUploadResult.java index ec1d115496..0e5546b9aa 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialUploadResult.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialUploadResult.java @@ -1,17 +1,16 @@ package me.chanjar.weixin.mp.bean.material; +import me.chanjar.weixin.common.util.ToStringUtils; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; import java.io.Serializable; public class WxMpMaterialUploadResult implements Serializable { - - /** - * - */ private static final long serialVersionUID = -128818731449449537L; private String mediaId; private String url; + private Integer errCode; + private String errMsg; public static WxMpMaterialUploadResult fromJson(String json) { return WxMpGsonBuilder.create().fromJson(json, WxMpMaterialUploadResult.class); @@ -33,9 +32,25 @@ public void setUrl(String url) { this.url = url; } + public Integer getErrCode() { + return this.errCode; + } + + public void setErrCode(Integer errCode) { + this.errCode = errCode; + } + + public String getErrMsg() { + return this.errMsg; + } + + public void setErrMsg(String errMsg) { + this.errMsg = errMsg; + } + @Override public String toString() { - return "WxMpMaterialUploadResult [media_id=" + this.mediaId + ", url=" + this.url + "]"; + return ToStringUtils.toSimpleString(this); } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/membercard/MemberCardUserInfo.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/membercard/MemberCardUserInfo.java new file mode 100644 index 0000000000..336008b085 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/membercard/MemberCardUserInfo.java @@ -0,0 +1,27 @@ +package me.chanjar.weixin.mp.bean.membercard; + +/** + * Created by YuJian on 2017/7/11. + */ +public class MemberCardUserInfo { + + private NameValues[] commonFieldList; + + private NameValues[] customFieldList; + + public NameValues[] getCommonFieldList() { + return commonFieldList; + } + + public void setCommonFieldList(NameValues[] commonFieldList) { + this.commonFieldList = commonFieldList; + } + + public NameValues[] getCustomFieldList() { + return customFieldList; + } + + public void setCustomFieldList(NameValues[] customFieldList) { + this.customFieldList = customFieldList; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/membercard/NameValues.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/membercard/NameValues.java new file mode 100644 index 0000000000..ae30d7a9a4 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/membercard/NameValues.java @@ -0,0 +1,36 @@ +package me.chanjar.weixin.mp.bean.membercard; + +/** + * Created by YuJian on 2017/7/11. + */ +public class NameValues { + private String name; + + private String value; + + private String[] valueList; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public String[] getValueList() { + return valueList; + } + + public void setValueList(String[] valueList) { + this.valueList = valueList; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/membercard/NotifyOptional.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/membercard/NotifyOptional.java new file mode 100644 index 0000000000..a8adae8ac7 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/membercard/NotifyOptional.java @@ -0,0 +1,73 @@ +package me.chanjar.weixin.mp.bean.membercard; + +import com.google.gson.annotations.SerializedName; + +/** + * 控制原生消息结构体,包含各字段的消息控制字段。 + * + * 用于 `7 更新会员信息` 的接口参数调用 + * https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1451025283 + * + * @author YuJian(mgcnrx11@gmail.com) + * @version 2017/7/15 + */ +public class NotifyOptional { + + // 积分变动时是否触发系统模板消息,默认为true + @SerializedName("is_notify_bonus") + private Boolean isNotifyBonus; + + // 余额变动时是否触发系统模板消息,默认为true + @SerializedName("is_notify_balance") + private Boolean isNotifyBalance; + + // 自定义group1变动时是否触发系统模板消息,默认为false。(2、3同理) + @SerializedName("is_notify_custom_field1") + private Boolean isNotifyCustomField1; + + @SerializedName("is_notify_custom_field2") + private Boolean isNotifyCustomField2; + + @SerializedName("is_notify_custom_field3") + private Boolean isNotifyCustomField3; + + public Boolean getNotifyBonus() { + return isNotifyBonus; + } + + public void setNotifyBonus(Boolean notifyBonus) { + isNotifyBonus = notifyBonus; + } + + public Boolean getNotifyBalance() { + return isNotifyBalance; + } + + public void setNotifyBalance(Boolean notifyBalance) { + isNotifyBalance = notifyBalance; + } + + public Boolean getNotifyCustomField1() { + return isNotifyCustomField1; + } + + public void setNotifyCustomField1(Boolean notifyCustomField1) { + isNotifyCustomField1 = notifyCustomField1; + } + + public Boolean getNotifyCustomField2() { + return isNotifyCustomField2; + } + + public void setNotifyCustomField2(Boolean notifyCustomField2) { + isNotifyCustomField2 = notifyCustomField2; + } + + public Boolean getNotifyCustomField3() { + return isNotifyCustomField3; + } + + public void setNotifyCustomField3(Boolean notifyCustomField3) { + isNotifyCustomField3 = notifyCustomField3; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/membercard/WxMpMemberCardActivatedMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/membercard/WxMpMemberCardActivatedMessage.java new file mode 100644 index 0000000000..8040224a09 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/membercard/WxMpMemberCardActivatedMessage.java @@ -0,0 +1,144 @@ +package me.chanjar.weixin.mp.bean.membercard; + +import com.google.gson.annotations.SerializedName; + +/** + * 会员卡激活接口的参数 + * + * @author YuJian(mgcnrx11@hotmail.com) + * @version 2017/7/8 + */ +public class WxMpMemberCardActivatedMessage { + + // 会员卡编号,由开发者填入,作为序列号显示在用户的卡包里。可与Code码保持等值。 + @SerializedName("membership_number") + private String membershipNumber; + // 领取会员卡用户获得的code + private String code; + // 卡券ID,自定义code卡券必填 + @SerializedName("card_id") + private String cardId; + // 商家自定义会员卡背景图,须先调用上传图片接口将背景图上传至CDN,否则报错。卡面设计请遵循微信会员卡自定义背景设计规范 + @SerializedName("background_pic_url") + private String backgroundPicUrl; + // 激活后的有效起始时间。若不填写默认以创建时的 data_info 为准。Unix时间戳格式。 + @SerializedName("activate_begin_time") + private Integer activateBeginTime; + // 激活后的有效截至时间。若不填写默认以创建时的 data_info 为准。Unix时间戳格式。 + @SerializedName("activate_end_time") + private Integer activateEndTime; + // 初始积分,不填为0。 + @SerializedName("init_bonus") + private Integer initBonus; + // 积分同步说明。 + @SerializedName("init_bonus_record") + private String initBonusRecord; + // 初始余额,不填为0。 + @SerializedName("init_balance") + private Integer initBalance; + // 创建时字段custom_field1定义类型的初始值,限制为4个汉字,12字节。 + @SerializedName("init_custom_field_value1") + private String initCustomFieldValue1; + // 创建时字段custom_field2定义类型的初始值,限制为4个汉字,12字节。 + @SerializedName("init_custom_field_value2") + private String initCustomFieldValue2; + // 创建时字段custom_field3定义类型的初始值,限制为4个汉字,12字节。 + @SerializedName("init_custom_field_value3") + private String initCustomFieldValue3; + + public String getMembershipNumber() { + return membershipNumber; + } + + public void setMembershipNumber(String membershipNumber) { + this.membershipNumber = membershipNumber; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getCardId() { + return cardId; + } + + public void setCardId(String cardId) { + this.cardId = cardId; + } + + public String getBackgroundPicUrl() { + return backgroundPicUrl; + } + + public void setBackgroundPicUrl(String backgroundPicUrl) { + this.backgroundPicUrl = backgroundPicUrl; + } + + public Integer getActivateBeginTime() { + return activateBeginTime; + } + + public void setActivateBeginTime(Integer activateBeginTime) { + this.activateBeginTime = activateBeginTime; + } + + public Integer getActivateEndTime() { + return activateEndTime; + } + + public void setActivateEndTime(Integer activateEndTime) { + this.activateEndTime = activateEndTime; + } + + public Integer getInitBonus() { + return initBonus; + } + + public void setInitBonus(Integer initBonus) { + this.initBonus = initBonus; + } + + public String getInitBonusRecord() { + return initBonusRecord; + } + + public void setInitBonusRecord(String initBonusRecord) { + this.initBonusRecord = initBonusRecord; + } + + public Integer getInitBalance() { + return initBalance; + } + + public void setInitBalance(Integer initBalance) { + this.initBalance = initBalance; + } + + public String getInitCustomFieldValue1() { + return initCustomFieldValue1; + } + + public void setInitCustomFieldValue1(String initCustomFieldValue1) { + this.initCustomFieldValue1 = initCustomFieldValue1; + } + + public String getInitCustomFieldValue2() { + return initCustomFieldValue2; + } + + public void setInitCustomFieldValue2(String initCustomFieldValue2) { + this.initCustomFieldValue2 = initCustomFieldValue2; + } + + public String getInitCustomFieldValue3() { + return initCustomFieldValue3; + } + + public void setInitCustomFieldValue3(String initCustomFieldValue3) { + this.initCustomFieldValue3 = initCustomFieldValue3; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/membercard/WxMpMemberCardUpdateMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/membercard/WxMpMemberCardUpdateMessage.java new file mode 100644 index 0000000000..f4d410185d --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/membercard/WxMpMemberCardUpdateMessage.java @@ -0,0 +1,156 @@ +package me.chanjar.weixin.mp.bean.membercard; + +import com.google.gson.annotations.SerializedName; + +/** + * 更新会员信息所需字段消息。 + * + * 1.开发者可以同时传入add_bonus和bonus解决由于同步失败带来的幂等性问题。同时传入add_bonus和bonus时 + * add_bonus作为积分变动消息中的变量值,而bonus作为卡面上的总积分额度显示。余额变动同理。 + * 2.开发者可以传入is_notify_bonus控制特殊的积分对账变动不发送消息,余额变动同理。 + * + * @author YuJian(mgcnrx11@gmail.com) + * @version 2017/7/15 + */ +public class WxMpMemberCardUpdateMessage { + + // 领取会员卡用户获得的code + private String code; + // 卡券ID,自定义code卡券必填 + @SerializedName("card_id") + private String cardId; + // 支持商家激活时针对单个会员卡分配自定义的会员卡背景 + @SerializedName("background_pic_url") + private String backgroundPicUrl; + // 需要设置的积分全量值,传入的数值会直接显示 + private Integer bonus; + // 本次积分变动值,传负数代表减少 + @SerializedName("add_bonus") + private Integer addBounus; + // 商家自定义积分消耗记录,不超过14个汉字 + @SerializedName("record_bonus") + private String recordBonus; + // 需要设置的余额全量值,传入的数值会直接显示在卡面 + private Integer balance; + // 本次余额变动值,传负数代表减少 + @SerializedName("add_balance") + private Integer addBalance; + // 商家自定义金额消耗记录,不超过14个汉字。 + @SerializedName("record_balance") + private String recordBalance; + + // 创建时字段custom_field定义类型的最新数值,限制为4个汉字,12字节。 + @SerializedName("custom_field_value1") + private String customFieldValue1; + @SerializedName("custom_field_value2") + private String customFieldValue2; + @SerializedName("custom_field_value3") + private String customFieldValue3; + + @SerializedName("notify_optional") + private NotifyOptional notifyOptional; + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getCardId() { + return cardId; + } + + public void setCardId(String cardId) { + this.cardId = cardId; + } + + public String getBackgroundPicUrl() { + return backgroundPicUrl; + } + + public void setBackgroundPicUrl(String backgroundPicUrl) { + this.backgroundPicUrl = backgroundPicUrl; + } + + public Integer getBonus() { + return bonus; + } + + public void setBonus(Integer bonus) { + this.bonus = bonus; + } + + public Integer getAddBounus() { + return addBounus; + } + + public void setAddBounus(Integer addBounus) { + this.addBounus = addBounus; + } + + public String getRecordBonus() { + return recordBonus; + } + + public void setRecordBonus(String recordBonus) { + this.recordBonus = recordBonus; + } + + public Integer getBalance() { + return balance; + } + + public void setBalance(Integer balance) { + this.balance = balance; + } + + public Integer getAddBalance() { + return addBalance; + } + + public void setAddBalance(Integer addBalance) { + this.addBalance = addBalance; + } + + public String getRecordBalance() { + return recordBalance; + } + + public void setRecordBalance(String recordBalance) { + this.recordBalance = recordBalance; + } + + public String getCustomFieldValue1() { + return customFieldValue1; + } + + public void setCustomFieldValue1(String customFieldValue1) { + this.customFieldValue1 = customFieldValue1; + } + + public String getCustomFieldValue2() { + return customFieldValue2; + } + + public void setCustomFieldValue2(String customFieldValue2) { + this.customFieldValue2 = customFieldValue2; + } + + public String getCustomFieldValue3() { + return customFieldValue3; + } + + public void setCustomFieldValue3(String customFieldValue3) { + this.customFieldValue3 = customFieldValue3; + } + + public NotifyOptional getNotifyOptional() { + return notifyOptional; + } + + public void setNotifyOptional(NotifyOptional notifyOptional) { + this.notifyOptional = notifyOptional; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/membercard/WxMpMemberCardUpdateResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/membercard/WxMpMemberCardUpdateResult.java new file mode 100644 index 0000000000..e615cb0721 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/membercard/WxMpMemberCardUpdateResult.java @@ -0,0 +1,82 @@ +package me.chanjar.weixin.mp.bean.membercard; + +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; + +/** + * 用于 `7 更新会员信息` 的接口调用后的返回结果 + * https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1451025283 + * + * @author YuJian(mgcnrx11@gmail.com) + * @version 2017/7/15 + */ +public class WxMpMemberCardUpdateResult implements Serializable { + + private static final long serialVersionUID = 9084886191442098311L; + + private String errorCode; + + private String errorMsg; + + private String openId; + + private Integer resultBonus; + + private Integer resultBalance; + + public String getErrorCode() { + return errorCode; + } + + public void setErrorCode(String errorCode) { + this.errorCode = errorCode; + } + + public String getErrorMsg() { + return errorMsg; + } + + public void setErrorMsg(String errorMsg) { + this.errorMsg = errorMsg; + } + + public String getOpenId() { + return openId; + } + + public void setOpenId(String openId) { + this.openId = openId; + } + + public Integer getResultBonus() { + return resultBonus; + } + + public void setResultBonus(Integer resultBonus) { + this.resultBonus = resultBonus; + } + + public Integer getResultBalance() { + return resultBalance; + } + + public void setResultBalance(Integer resultBalance) { + this.resultBalance = resultBalance; + } + + @Override + public String toString() { + return "WxMpMemberCardUpdateResult{" + + "errorCode='" + errorCode + '\'' + + ", errorMsg='" + errorMsg + '\'' + + ", openId='" + openId + '\'' + + ", resultBonus=" + resultBonus + + ", resultBalance=" + resultBalance + + '}'; + } + + public static WxMpMemberCardUpdateResult fromJson(String json) { + return WxMpGsonBuilder.INSTANCE.create().fromJson(json, WxMpMemberCardUpdateResult.class); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/membercard/WxMpMemberCardUserInfoResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/membercard/WxMpMemberCardUserInfoResult.java new file mode 100644 index 0000000000..1ddb57d34b --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/membercard/WxMpMemberCardUserInfoResult.java @@ -0,0 +1,143 @@ +package me.chanjar.weixin.mp.bean.membercard; + +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; + +/** + * 拉取会员信息返回的结果 + * + * 字段格式参考https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1451025283 6.2.1小节的步骤5 + * + * @author YuJian + * @version 2017/7/9 + */ +public class WxMpMemberCardUserInfoResult implements Serializable { + + private static final long serialVersionUID = 9084777967442098311L; + + private String errorCode; + + private String errorMsg; + + private String openId; + + private String nickname; + + private String membershipNumber; + + private Integer bonus; + + private String sex; + + private MemberCardUserInfo userInfo; + + private String userCardStatus; + + private Boolean hasActive; + + public static long getSerialVersionUID() { + return serialVersionUID; + } + + public String getErrorCode() { + return errorCode; + } + + public void setErrorCode(String errorCode) { + this.errorCode = errorCode; + } + + public String getErrorMsg() { + return errorMsg; + } + + public void setErrorMsg(String errorMsg) { + this.errorMsg = errorMsg; + } + + public String getOpenId() { + return openId; + } + + public void setOpenId(String openId) { + this.openId = openId; + } + + public String getNickname() { + return nickname; + } + + public void setNickname(String nickname) { + this.nickname = nickname; + } + + public String getMembershipNumber() { + return membershipNumber; + } + + public void setMembershipNumber(String membershipNumber) { + this.membershipNumber = membershipNumber; + } + + public Integer getBonus() { + return bonus; + } + + public void setBonus(Integer bonus) { + this.bonus = bonus; + } + + public String getSex() { + return sex; + } + + public void setSex(String sex) { + this.sex = sex; + } + + public MemberCardUserInfo getUserInfo() { + return userInfo; + } + + public void setUserInfo(MemberCardUserInfo userInfo) { + this.userInfo = userInfo; + } + + public String getUserCardStatus() { + return userCardStatus; + } + + public void setUserCardStatus(String userCardStatus) { + this.userCardStatus = userCardStatus; + } + + public Boolean getHasActive() { + return hasActive; + } + + public void setHasActive(Boolean hasActive) { + this.hasActive = hasActive; + } + + @Override + public String toString() { + return "WxMpMemberCardUserInfoResult{" + + "errorCode='" + errorCode + '\'' + + ", errorMsg='" + errorMsg + '\'' + + ", openId='" + openId + '\'' + + ", nickname='" + nickname + '\'' + + ", membershipNumber='" + membershipNumber + '\'' + + ", bonus=" + bonus + + ", sex='" + sex + '\'' + + ", userInfo=" + userInfo + + ", userCardStatus='" + userCardStatus + '\'' + + ", hasActive=" + hasActive + + '}'; + } + + public static WxMpMemberCardUserInfoResult fromJson(String json) { + return WxMpGsonBuilder.INSTANCE.create().fromJson(json, WxMpMemberCardUserInfoResult.class); + } +} + diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/menu/WxMpMenu.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/menu/WxMpMenu.java index 366ca2f1ba..6c7d3e8650 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/menu/WxMpMenu.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/menu/WxMpMenu.java @@ -6,6 +6,7 @@ import me.chanjar.weixin.common.util.ToStringUtils; import me.chanjar.weixin.common.util.json.WxGsonBuilder; +import java.io.Serializable; import java.util.List; /** @@ -15,7 +16,10 @@ * @author binarywang(Binary Wang) *
*/ -public class WxMpMenu { +public class WxMpMenu implements Serializable{ + + private static final long serialVersionUID = -5794350513426702252L; + @SerializedName("menu") private WxMpConditionalMenu menu; @@ -51,7 +55,10 @@ public String toJson() { return WxGsonBuilder.create().toJson(this); } - public static class WxMpConditionalMenu { + public static class WxMpConditionalMenu implements Serializable { + + private static final long serialVersionUID = -2279946921755382289L; + @SerializedName("button") private List buttons; @SerializedName("matchrule") diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/menu/WxMpSelfMenuInfo.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/menu/WxMpSelfMenuInfo.java index 588e8a9c05..699a41ecfb 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/menu/WxMpSelfMenuInfo.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/menu/WxMpSelfMenuInfo.java @@ -24,6 +24,14 @@ public String toString() { return ToStringUtils.toSimpleString(this); } + public List getButtons() { + return this.buttons; + } + + public void setButtons(List buttons) { + this.buttons = buttons; + } + public static class WxMpSelfMenuButton { /** *
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java
index fd5a1725ff..730310185c 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java
@@ -207,6 +207,9 @@ public class WxMpXmlMessage implements Serializable {
    * 
    * 领取场景值,用于领取渠道数据统计。可在生成二维码接口及添加Addcard接口中自定义该字段的字符串值。
    * 核销卡券时:开发者发起核销时传入的自定义参数,用于进行核销渠道统计
+   * 另外:
+   * 官网文档中,微信卡券>>卡券事件推送>>2.7 进入会员卡事件推送 user_view_card
+   * OuterStr:商户自定义二维码渠道参数,用于标识本次扫码打开会员卡来源来自于某个渠道值的二维码
    * 
*/ @XStreamAlias("OuterStr") @@ -254,6 +257,69 @@ public class WxMpXmlMessage implements Serializable { @XStreamAlias("RemarkAmount") private String remarkAmount; + /** + *
+   * 官网文档中,微信卡券>>卡券事件推送>>2.10 库存报警事件card_sku_remind
+   * Detail:报警详细信息
+   * 
+ */ + @XStreamAlias("Detail") + private String detail; + + /** + *
+   * 官网文档中,微信卡券>>卡券事件推送>>2.9 会员卡内容更新事件 update_member_card
+   * ModifyBonus:变动的积分值
+   * 
+ */ + @XStreamAlias("ModifyBonus") + private String modifyBonus; + + /** + *
+   * 官网文档中,微信卡券>>卡券事件推送>>2.9 会员卡内容更新事件 update_member_card
+   * ModifyBalance:变动的余额值
+   * 
+ */ + @XStreamAlias("ModifyBalance") + private String modifyBalance; + + /** + *
+   * 官网文档中,微信卡券>>卡券事件推送>>2.6 买单事件推送 User_pay_from_pay_cell
+   * TransId:微信支付交易订单号(只有使用买单功能核销的卡券才会出现)
+   * 
+ */ + @XStreamAlias("TransId") + private String transId; + + /** + *
+   * 官网文档中,微信卡券>>卡券事件推送>>2.6 买单事件推送 User_pay_from_pay_cell
+   * LocationId:门店ID,当前卡券核销的门店ID(只有通过卡券商户助手和买单核销时才会出现)
+   * 
+ */ + @XStreamAlias("LocationId") + private String locationId; + + /** + *
+   * 官网文档中,微信卡券>>卡券事件推送>>2.6 买单事件推送 User_pay_from_pay_cell
+   * Fee:实付金额,单位为分
+   * 
+ */ + @XStreamAlias("Fee") + private String fee; + + /** + *
+   * 官网文档中,微信卡券>>卡券事件推送>>2.6 买单事件推送 User_pay_from_pay_cell
+   * OriginalFee:应付金额,单位为分
+   * 
+ */ + @XStreamAlias("OriginalFee") + private String originalFee; + @XStreamAlias("ScanCodeInfo") private ScanCodeInfo scanCodeInfo = new ScanCodeInfo(); @@ -359,6 +425,8 @@ public class WxMpXmlMessage implements Serializable { private Integer deviceStatus; public static WxMpXmlMessage fromXml(String xml) { + //修改微信变态的消息内容格式,方便解析 + xml = xml.replace("", ""); return XStreamTransformer.fromXml(WxMpXmlMessage.class, xml); } @@ -468,6 +536,62 @@ public void setFailReason(String failReason) { this.failReason = failReason; } + public String getDetail() { + return detail; + } + + public void setDetail(String detail) { + this.detail = detail; + } + + public String getModifyBonus() { + return modifyBonus; + } + + public void setModifyBonus(String modifyBonus) { + this.modifyBonus = modifyBonus; + } + + public String getModifyBalance() { + return modifyBalance; + } + + public void setModifyBalance(String modifyBalance) { + this.modifyBalance = modifyBalance; + } + + public String getTransId() { + return transId; + } + + public void setTransId(String transId) { + this.transId = transId; + } + + public String getLocationId() { + return locationId; + } + + public void setLocationId(String locationId) { + this.locationId = locationId; + } + + public String getFee() { + return fee; + } + + public void setFee(String fee) { + this.fee = fee; + } + + public String getOriginalFee() { + return originalFee; + } + + public void setOriginalFee(String originalFee) { + this.originalFee = originalFee; + } + public String getStoreUniqId() { return this.storeUniqId; } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutNewsMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutNewsMessage.java index 1d4fb0c767..8865a39358 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutNewsMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutNewsMessage.java @@ -5,6 +5,7 @@ import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; +import java.io.Serializable; import java.util.ArrayList; import java.util.List; @@ -39,7 +40,9 @@ public List getArticles() { @XStreamAlias("item") - public static class Item { + public static class Item implements Serializable { + + private static final long serialVersionUID = -4971456355028904754L; @XStreamAlias("Title") @XStreamConverter(value = XStreamCDataConverter.class) diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpCurrentAutoReplyInfo.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpCurrentAutoReplyInfo.java new file mode 100644 index 0000000000..6738b8bc4a --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpCurrentAutoReplyInfo.java @@ -0,0 +1,359 @@ +package me.chanjar.weixin.mp.bean.result; + +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import me.chanjar.weixin.common.util.ToStringUtils; +import me.chanjar.weixin.common.util.json.WxBooleanTypeAdapter; +import me.chanjar.weixin.common.util.json.WxDateTypeAdapter; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.util.Date; +import java.util.List; + +/** + *
+ * 公众号的自动回复规则
+ * Created by Binary Wang on 2017-7-8.
+ * @author Binary Wang
+ * 
+ */ +public class WxMpCurrentAutoReplyInfo { + @Override + public String toString() { + return ToStringUtils.toSimpleString(this); + } + + public static WxMpCurrentAutoReplyInfo fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, WxMpCurrentAutoReplyInfo.class); + } + + @SerializedName("is_add_friend_reply_open") + @JsonAdapter(WxBooleanTypeAdapter.class) + private Boolean isAddFriendReplyOpen; + + @SerializedName("is_autoreply_open") + @JsonAdapter(WxBooleanTypeAdapter.class) + private Boolean isAutoReplyOpen; + + @SerializedName("add_friend_autoreply_info") + private AutoReplyInfo addFriendAutoReplyInfo; + + @SerializedName("message_default_autoreply_info") + private AutoReplyInfo messageDefaultAutoReplyInfo; + + @SerializedName("keyword_autoreply_info") + private KeywordAutoReplyInfo keywordAutoReplyInfo; + + public Boolean getAddFriendReplyOpen() { + return this.isAddFriendReplyOpen; + } + + public void setAddFriendReplyOpen(Boolean addFriendReplyOpen) { + isAddFriendReplyOpen = addFriendReplyOpen; + } + + public Boolean getAutoReplyOpen() { + return this.isAutoReplyOpen; + } + + public void setAutoReplyOpen(Boolean autoReplyOpen) { + isAutoReplyOpen = autoReplyOpen; + } + + public AutoReplyInfo getAddFriendAutoReplyInfo() { + return this.addFriendAutoReplyInfo; + } + + public void setAddFriendAutoReplyInfo(AutoReplyInfo addFriendAutoReplyInfo) { + this.addFriendAutoReplyInfo = addFriendAutoReplyInfo; + } + + public AutoReplyInfo getMessageDefaultAutoReplyInfo() { + return this.messageDefaultAutoReplyInfo; + } + + public void setMessageDefaultAutoReplyInfo(AutoReplyInfo messageDefaultAutoReplyInfo) { + this.messageDefaultAutoReplyInfo = messageDefaultAutoReplyInfo; + } + + public KeywordAutoReplyInfo getKeywordAutoReplyInfo() { + return this.keywordAutoReplyInfo; + } + + public void setKeywordAutoReplyInfo(KeywordAutoReplyInfo keywordAutoReplyInfo) { + this.keywordAutoReplyInfo = keywordAutoReplyInfo; + } + + public static class AutoReplyRule { + @Override + public String toString() { + return ToStringUtils.toSimpleString(this); + } + + @SerializedName("rule_name") + private String ruleName; + + @SerializedName("create_time") + @JsonAdapter(WxDateTypeAdapter.class) + private Date createTime; + + @SerializedName("reply_mode") + private String replyMode; + + @SerializedName("keyword_list_info") + private List keywordListInfo; + + @SerializedName("reply_list_info") + private List replyListInfo; + + public String getRuleName() { + return this.ruleName; + } + + public void setRuleName(String ruleName) { + this.ruleName = ruleName; + } + + public Date getCreateTime() { + return this.createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public String getReplyMode() { + return this.replyMode; + } + + public void setReplyMode(String replyMode) { + this.replyMode = replyMode; + } + + public List getKeywordListInfo() { + return this.keywordListInfo; + } + + public void setKeywordListInfo(List keywordListInfo) { + this.keywordListInfo = keywordListInfo; + } + + public List getReplyListInfo() { + return this.replyListInfo; + } + + public void setReplyListInfo(List replyListInfo) { + this.replyListInfo = replyListInfo; + } + } + + public static class ReplyInfo { + @Override + public String toString() { + return ToStringUtils.toSimpleString(this); + } + + private String type; + private String content; + + @SerializedName("news_info") + private NewsInfo newsInfo; + + public String getType() { + return this.type; + } + + public void setType(String type) { + this.type = type; + } + + public String getContent() { + return this.content; + } + + public void setContent(String content) { + this.content = content; + } + + public NewsInfo getNewsInfo() { + return this.newsInfo; + } + + public void setNewsInfo(NewsInfo newsInfo) { + this.newsInfo = newsInfo; + } + } + + public static class NewsInfo { + @Override + public String toString() { + return ToStringUtils.toSimpleString(this); + } + + private List list; + + public List getList() { + return this.list; + } + + public void setList(List list) { + this.list = list; + } + } + + public static class NewsItem { + @Override + public String toString() { + return ToStringUtils.toSimpleString(this); + } + + @SerializedName("cover_url") + private String coverUrl; + private String author; + @SerializedName("content_url") + private String contentUrl; + private String digest; + @SerializedName("show_cover") + @JsonAdapter(WxBooleanTypeAdapter.class) + private Boolean showCover; + @SerializedName("source_url") + private String sourceUrl; + private String title; + + public String getCoverUrl() { + return this.coverUrl; + } + + public void setCoverUrl(String coverUrl) { + this.coverUrl = coverUrl; + } + + public String getAuthor() { + return this.author; + } + + public void setAuthor(String author) { + this.author = author; + } + + public String getContentUrl() { + return this.contentUrl; + } + + public void setContentUrl(String contentUrl) { + this.contentUrl = contentUrl; + } + + public String getDigest() { + return this.digest; + } + + public void setDigest(String digest) { + this.digest = digest; + } + + public Boolean getShowCover() { + return this.showCover; + } + + public void setShowCover(Boolean showCover) { + this.showCover = showCover; + } + + public String getSourceUrl() { + return this.sourceUrl; + } + + public void setSourceUrl(String sourceUrl) { + this.sourceUrl = sourceUrl; + } + + public String getTitle() { + return this.title; + } + + public void setTitle(String title) { + this.title = title; + } + } + + public static class KeywordInfo { + @Override + public String toString() { + return ToStringUtils.toSimpleString(this); + } + + private String type; + @SerializedName("match_mode") + private String matchMode; + private String content; + + public String getType() { + return this.type; + } + + public void setType(String type) { + this.type = type; + } + + public String getMatchMode() { + return this.matchMode; + } + + public void setMatchMode(String matchMode) { + this.matchMode = matchMode; + } + + public String getContent() { + return this.content; + } + + public void setContent(String content) { + this.content = content; + } + } + + public static class KeywordAutoReplyInfo { + @Override + public String toString() { + return ToStringUtils.toSimpleString(this); + } + + private List list; + + public List getList() { + return this.list; + } + + public void setList(List list) { + this.list = list; + } + } + + public static class AutoReplyInfo { + @Override + public String toString() { + return ToStringUtils.toSimpleString(this); + } + + private String type; + private String content; + + public String getType() { + return this.type; + } + + public void setType(String type) { + this.type = type; + } + + public String getContent() { + return this.content; + } + + public void setContent(String content) { + this.content = content; + } + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/NewsBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/NewsBuilder.java index 1626f77e2c..11e866e84a 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/NewsBuilder.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/NewsBuilder.java @@ -4,6 +4,7 @@ import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** @@ -16,15 +17,19 @@ * @author chanjarster */ public final class NewsBuilder extends BaseBuilder { - private List articles = new ArrayList<>(); public NewsBuilder() { this.msgType = WxConsts.CUSTOM_MSG_NEWS; } - public NewsBuilder addArticle(WxMpKefuMessage.WxArticle article) { - this.articles.add(article); + public NewsBuilder addArticle(WxMpKefuMessage.WxArticle... articles) { + Collections.addAll(this.articles, articles); + return this; + } + + public NewsBuilder articles(List articles) { + this.articles = articles; return this; } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/NewsBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/NewsBuilder.java index 4f9e22e67f..01af0d627f 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/NewsBuilder.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/NewsBuilder.java @@ -3,6 +3,7 @@ import me.chanjar.weixin.mp.bean.message.WxMpXmlOutNewsMessage; import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** @@ -11,11 +12,15 @@ * @author chanjarster */ public final class NewsBuilder extends BaseBuilder { + private List articles = new ArrayList<>(); - protected final List articles = new ArrayList<>(); + public NewsBuilder addArticle(WxMpXmlOutNewsMessage.Item... items) { + Collections.addAll(this.articles, items); + return this; + } - public NewsBuilder addArticle(WxMpXmlOutNewsMessage.Item item) { - this.articles.add(item); + public NewsBuilder articles(List articles){ + this.articles = articles; return this; } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialDeleteRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialDeleteRequestExecutor.java index 307f55ff32..51b7a67764 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialDeleteRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialDeleteRequestExecutor.java @@ -15,11 +15,11 @@ public MaterialDeleteRequestExecutor(RequestHttp requestHttp) { public static RequestExecutor create(RequestHttp requestHttp) { switch (requestHttp.getRequestType()) { - case apacheHttp: + case APACHE_HTTP: return new ApacheMaterialDeleteRequestExecutor(requestHttp); - case joddHttp: + case JODD_HTTP: return new JoddMaterialDeleteRequestExecutor(requestHttp); - case okHttp: + case OK_HTTP: return new OkhttpMaterialDeleteRequestExecutor(requestHttp); default: return null; diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialNewsInfoRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialNewsInfoRequestExecutor.java index 0544fbe91b..24c00fe75a 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialNewsInfoRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialNewsInfoRequestExecutor.java @@ -16,11 +16,11 @@ public MaterialNewsInfoRequestExecutor(RequestHttp requestHttp) { public static RequestExecutor create(RequestHttp requestHttp) { switch (requestHttp.getRequestType()) { - case apacheHttp: + case APACHE_HTTP: return new ApacheMaterialNewsInfoRequestExecutor(requestHttp); - case joddHttp: + case JODD_HTTP: return new JoddMaterialNewsInfoRequestExecutor(requestHttp); - case okHttp: + case OK_HTTP: return new OkhttpMaterialNewsInfoRequestExecutor(requestHttp); default: //TODO 需要优化抛出异常 diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialUploadRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialUploadRequestExecutor.java index c44b5f1607..c850d28cf9 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialUploadRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialUploadRequestExecutor.java @@ -17,11 +17,11 @@ public MaterialUploadRequestExecutor(RequestHttp requestHttp) { public static RequestExecutor create(RequestHttp requestHttp) { switch (requestHttp.getRequestType()) { - case apacheHttp: + case APACHE_HTTP: return new ApacheMaterialUploadRequestExecutor(requestHttp); - case joddHttp: + case JODD_HTTP: return new JoddMaterialUploadRequestExecutor(requestHttp); - case okHttp: + case OK_HTTP: return new OkhttpMaterialUploadRequestExecutor(requestHttp); default: return null; diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialVideoInfoRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialVideoInfoRequestExecutor.java index 285e05c47e..73948052ff 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialVideoInfoRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialVideoInfoRequestExecutor.java @@ -1,30 +1,33 @@ package me.chanjar.weixin.mp.util.http; -import me.chanjar.weixin.common.util.http.RequestExecutor; -import me.chanjar.weixin.common.util.http.RequestHttp; -import me.chanjar.weixin.mp.bean.material.WxMpMaterialVideoInfoResult; -import me.chanjar.weixin.mp.util.http.apache.ApacheMaterialVideoInfoRequestExecutor; -import me.chanjar.weixin.mp.util.http.jodd.JoddMaterialVideoInfoRequestExecutor; -import me.chanjar.weixin.mp.util.http.okhttp.OkhttpMaterialVideoInfoRequestExecutor; + + import me.chanjar.weixin.common.util.http.RequestExecutor; + import me.chanjar.weixin.common.util.http.RequestHttp; + + import me.chanjar.weixin.mp.bean.material.WxMpMaterialVideoInfoResult; + import me.chanjar.weixin.mp.util.http.apache.ApacheMaterialVideoInfoRequestExecutor; + import me.chanjar.weixin.mp.util.http.jodd.JoddMaterialVideoInfoRequestExecutor; + import me.chanjar.weixin.mp.util.http.okhttp.OkhttpMaterialVideoInfoRequestExecutor; + public abstract class MaterialVideoInfoRequestExecutor implements RequestExecutor { protected RequestHttp requestHttp; public MaterialVideoInfoRequestExecutor(RequestHttp requestHttp) { - this.requestHttp = requestHttp; - } - - public static RequestExecutor create(RequestHttp requestHttp) { - switch (requestHttp.getRequestType()) { - case apacheHttp: - return new ApacheMaterialVideoInfoRequestExecutor(requestHttp); - case joddHttp: - return new JoddMaterialVideoInfoRequestExecutor(requestHttp); - case okHttp: - return new OkhttpMaterialVideoInfoRequestExecutor(requestHttp); - default: - return null; - } - } - -} + this.requestHttp = requestHttp; + } + + public static RequestExecutor create(RequestHttp requestHttp) { + switch (requestHttp.getRequestType()) { + case APACHE_HTTP: + return new ApacheMaterialVideoInfoRequestExecutor(requestHttp); + case JODD_HTTP: + return new JoddMaterialVideoInfoRequestExecutor(requestHttp); + case OK_HTTP: + return new OkhttpMaterialVideoInfoRequestExecutor(requestHttp); + default: + return null; + } + } + + } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialVoiceAndImageDownloadRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialVoiceAndImageDownloadRequestExecutor.java index 7c47532a24..09cb70d0f5 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialVoiceAndImageDownloadRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MaterialVoiceAndImageDownloadRequestExecutor.java @@ -13,20 +13,18 @@ public abstract class MaterialVoiceAndImageDownloadRequestExecutor impleme protected RequestHttp requestHttp; protected File tmpDirFile; - public MaterialVoiceAndImageDownloadRequestExecutor(RequestHttp requestHttp, File tmpDirFile) { this.requestHttp = requestHttp; this.tmpDirFile = tmpDirFile; } - public static RequestExecutor create(RequestHttp requestHttp, File tmpDirFile) { switch (requestHttp.getRequestType()) { - case apacheHttp: + case APACHE_HTTP: return new ApacheMaterialVoiceAndImageDownloadRequestExecutor(requestHttp, tmpDirFile); - case joddHttp: + case JODD_HTTP: return new JoddMaterialVoiceAndImageDownloadRequestExecutor(requestHttp, tmpDirFile); - case okHttp: + case OK_HTTP: return new OkhttpMaterialVoiceAndImageDownloadRequestExecutor(requestHttp, tmpDirFile); default: return null; diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MediaImgUploadRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MediaImgUploadRequestExecutor.java index dc5e037b4d..e187561fc8 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MediaImgUploadRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MediaImgUploadRequestExecutor.java @@ -21,11 +21,11 @@ public MediaImgUploadRequestExecutor(RequestHttp requestHttp) { public static RequestExecutor create(RequestHttp requestHttp) { switch (requestHttp.getRequestType()) { - case apacheHttp: + case APACHE_HTTP: return new ApacheMediaImgUploadRequestExecutor(requestHttp); - case joddHttp: + case JODD_HTTP: return new JoddMediaImgUploadRequestExecutor(requestHttp); - case okHttp: + case OK_HTTP: return new OkhttpMediaImgUploadRequestExecutor(requestHttp); default: return null; diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/QrCodeRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/QrCodeRequestExecutor.java index 3fc34d87c7..87c1920cd8 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/QrCodeRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/QrCodeRequestExecutor.java @@ -23,11 +23,11 @@ public QrCodeRequestExecutor(RequestHttp requestHttp) { public static RequestExecutor create(RequestHttp requestHttp) { switch (requestHttp.getRequestType()) { - case apacheHttp: + case APACHE_HTTP: return new ApacheQrCodeRequestExecutor(requestHttp); - case joddHttp: + case JODD_HTTP: return new JoddQrCodeRequestExecutor(requestHttp); - case okHttp: + case OK_HTTP: return new OkhttpQrCodeRequestExecutor(requestHttp); default: //TODO 需要优化,最好抛出异常 diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/apache/ApacheMaterialNewsInfoRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/apache/ApacheMaterialNewsInfoRequestExecutor.java index 77d22659ec..5298160f35 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/apache/ApacheMaterialNewsInfoRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/apache/ApacheMaterialNewsInfoRequestExecutor.java @@ -14,6 +14,8 @@ import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.HashMap; @@ -23,6 +25,8 @@ * Created by ecoolper on 2017/5/5. */ public class ApacheMaterialNewsInfoRequestExecutor extends MaterialNewsInfoRequestExecutor { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + public ApacheMaterialNewsInfoRequestExecutor(RequestHttp requestHttp) { super(requestHttp); } @@ -40,6 +44,7 @@ public WxMpMaterialNews execute(String uri, String materialId) throws WxErrorExc httpPost.setEntity(new StringEntity(WxGsonBuilder.create().toJson(params))); try (CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpPost)) { String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); + this.logger.debug("响应原始数据:{}", responseContent); WxError error = WxError.fromJson(responseContent); if (error.getErrorCode() != 0) { throw new WxErrorException(error); diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/apache/ApacheMaterialUploadRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/apache/ApacheMaterialUploadRequestExecutor.java index 474fcb9936..445ff2736e 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/apache/ApacheMaterialUploadRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/apache/ApacheMaterialUploadRequestExecutor.java @@ -8,6 +8,7 @@ import me.chanjar.weixin.mp.bean.material.WxMpMaterial; import me.chanjar.weixin.mp.bean.material.WxMpMaterialUploadResult; import me.chanjar.weixin.mp.util.http.MaterialUploadRequestExecutor; +import org.apache.http.Consts; import org.apache.http.HttpHost; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; @@ -15,6 +16,7 @@ import org.apache.http.entity.ContentType; import org.apache.http.entity.mime.HttpMultipartMode; import org.apache.http.entity.mime.MultipartEntityBuilder; +import org.apache.http.entity.mime.content.StringBody; import org.apache.http.impl.client.CloseableHttpClient; import java.io.File; @@ -53,7 +55,8 @@ public WxMpMaterialUploadResult execute(String uri, WxMpMaterial material) throw .setMode(HttpMultipartMode.RFC6532); Map form = material.getForm(); if (material.getForm() != null) { - multipartEntityBuilder.addTextBody("description", WxGsonBuilder.create().toJson(form)); + multipartEntityBuilder.addPart("description", + new StringBody(WxGsonBuilder.create().toJson(form), ContentType.create("text/plain", Consts.UTF_8))); } httpPost.setEntity(multipartEntityBuilder.build()); diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/apache/ApacheMaterialVoiceAndImageDownloadRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/apache/ApacheMaterialVoiceAndImageDownloadRequestExecutor.java index 87d7b545c6..b1011a5282 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/apache/ApacheMaterialVoiceAndImageDownloadRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/apache/ApacheMaterialVoiceAndImageDownloadRequestExecutor.java @@ -18,6 +18,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; @@ -41,10 +42,10 @@ public InputStream execute(String uri, String materialId) throws WxErrorExceptio params.put("media_id", materialId); httpPost.setEntity(new StringEntity(WxGsonBuilder.create().toJson(params))); try (CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpPost); - InputStream inputStream = InputStreamResponseHandler.INSTANCE.handleResponse(response);) { + InputStream inputStream = InputStreamResponseHandler.INSTANCE.handleResponse(response)) { // 下载媒体文件出错 byte[] responseContent = IOUtils.toByteArray(inputStream); - String responseContentString = new String(responseContent, "UTF-8"); + String responseContentString = new String(responseContent, StandardCharsets.UTF_8); if (responseContentString.length() < 100) { try { WxError wxError = WxGsonBuilder.create().fromJson(responseContentString, WxError.class); diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/jodd/JoddMaterialDeleteRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/jodd/JoddMaterialDeleteRequestExecutor.java index 5349a0fafa..0344e1cddc 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/jodd/JoddMaterialDeleteRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/jodd/JoddMaterialDeleteRequestExecutor.java @@ -4,6 +4,8 @@ import jodd.http.HttpRequest; import jodd.http.HttpResponse; import jodd.http.ProxyInfo; +import jodd.util.StringPool; + import me.chanjar.weixin.common.bean.result.WxError; import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.common.util.http.RequestHttp; @@ -29,6 +31,7 @@ public Boolean execute(String uri, String materialId) throws WxErrorException, I request.query("media_id", materialId); HttpResponse response = request.send(); + response.charset(StringPool.UTF_8); String responseContent = response.bodyText(); WxError error = WxError.fromJson(responseContent); if (error.getErrorCode() != 0) { diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/jodd/JoddMaterialNewsInfoRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/jodd/JoddMaterialNewsInfoRequestExecutor.java index 6d6b0120ce..181a640787 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/jodd/JoddMaterialNewsInfoRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/jodd/JoddMaterialNewsInfoRequestExecutor.java @@ -4,12 +4,16 @@ import jodd.http.HttpRequest; import jodd.http.HttpResponse; import jodd.http.ProxyInfo; +import jodd.util.StringPool; + import me.chanjar.weixin.common.bean.result.WxError; import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.common.util.http.RequestHttp; import me.chanjar.weixin.mp.bean.material.WxMpMaterialNews; import me.chanjar.weixin.mp.util.http.MaterialNewsInfoRequestExecutor; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; @@ -17,6 +21,7 @@ * Created by ecoolper on 2017/5/5. */ public class JoddMaterialNewsInfoRequestExecutor extends MaterialNewsInfoRequestExecutor { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); public JoddMaterialNewsInfoRequestExecutor(RequestHttp requestHttp) { super(requestHttp); } @@ -31,8 +36,10 @@ public WxMpMaterialNews execute(String uri, String materialId) throws WxErrorExc request.query("media_id", materialId); HttpResponse response = request.send(); + response.charset(StringPool.UTF_8); String responseContent = response.bodyText(); + this.logger.debug("响应原始数据:{}", responseContent); WxError error = WxError.fromJson(responseContent); if (error.getErrorCode() != 0) { throw new WxErrorException(error); diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/jodd/JoddMaterialUploadRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/jodd/JoddMaterialUploadRequestExecutor.java index 8c9fdd0d64..ce30323695 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/jodd/JoddMaterialUploadRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/jodd/JoddMaterialUploadRequestExecutor.java @@ -4,6 +4,8 @@ import jodd.http.HttpRequest; import jodd.http.HttpResponse; import jodd.http.ProxyInfo; +import jodd.util.StringPool; + import me.chanjar.weixin.common.bean.result.WxError; import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.common.util.http.RequestHttp; @@ -48,6 +50,7 @@ public WxMpMaterialUploadResult execute(String uri, WxMpMaterial material) throw } HttpResponse response = request.send(); + response.charset(StringPool.UTF_8); String responseContent = response.bodyText(); WxError error = WxError.fromJson(responseContent); if (error.getErrorCode() != 0) { diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/jodd/JoddMaterialVideoInfoRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/jodd/JoddMaterialVideoInfoRequestExecutor.java index 8d600bc607..ab4ce6682c 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/jodd/JoddMaterialVideoInfoRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/jodd/JoddMaterialVideoInfoRequestExecutor.java @@ -4,6 +4,8 @@ import jodd.http.HttpRequest; import jodd.http.HttpResponse; import jodd.http.ProxyInfo; +import jodd.util.StringPool; + import me.chanjar.weixin.common.bean.result.WxError; import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.common.util.http.RequestHttp; @@ -30,6 +32,7 @@ public WxMpMaterialVideoInfoResult execute(String uri, String materialId) throws request.query("media_id", materialId); HttpResponse response = request.send(); + response.charset(StringPool.UTF_8); String responseContent = response.bodyText(); WxError error = WxError.fromJson(responseContent); if (error.getErrorCode() != 0) { diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/jodd/JoddMaterialVoiceAndImageDownloadRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/jodd/JoddMaterialVoiceAndImageDownloadRequestExecutor.java index bc8696009b..ca43a1455d 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/jodd/JoddMaterialVoiceAndImageDownloadRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/jodd/JoddMaterialVoiceAndImageDownloadRequestExecutor.java @@ -4,6 +4,8 @@ import jodd.http.HttpRequest; import jodd.http.HttpResponse; import jodd.http.ProxyInfo; +import jodd.util.StringPool; + import me.chanjar.weixin.common.bean.result.WxError; import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.common.util.http.RequestHttp; @@ -15,6 +17,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.nio.charset.StandardCharsets; /** * Created by ecoolper on 2017/5/5. @@ -34,10 +37,11 @@ public InputStream execute(String uri, String materialId) throws WxErrorExceptio request.query("media_id", materialId); HttpResponse response = request.send(); + response.charset(StringPool.UTF_8); try (InputStream inputStream = new ByteArrayInputStream(response.bodyBytes())) { // 下载媒体文件出错 byte[] responseContent = IOUtils.toByteArray(inputStream); - String responseContentString = new String(responseContent, "UTF-8"); + String responseContentString = new String(responseContent, StandardCharsets.UTF_8); if (responseContentString.length() < 100) { try { WxError wxError = WxGsonBuilder.create().fromJson(responseContentString, WxError.class); diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/jodd/JoddMediaImgUploadRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/jodd/JoddMediaImgUploadRequestExecutor.java index 09309890db..d0fdbfb856 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/jodd/JoddMediaImgUploadRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/jodd/JoddMediaImgUploadRequestExecutor.java @@ -4,6 +4,8 @@ import jodd.http.HttpRequest; import jodd.http.HttpResponse; import jodd.http.ProxyInfo; +import jodd.util.StringPool; + import me.chanjar.weixin.common.bean.result.WxError; import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.common.util.http.RequestHttp; @@ -35,6 +37,7 @@ public WxMediaImgUploadResult execute(String uri, File data) throws WxErrorExcep request.form("media", data); HttpResponse response = request.send(); + response.charset(StringPool.UTF_8); String responseContent = response.bodyText(); WxError error = WxError.fromJson(responseContent); if (error.getErrorCode() != 0) { diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/jodd/JoddQrCodeRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/jodd/JoddQrCodeRequestExecutor.java index 36ae7b4dbc..8b15dccf00 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/jodd/JoddQrCodeRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/jodd/JoddQrCodeRequestExecutor.java @@ -5,6 +5,8 @@ import jodd.http.HttpResponse; import jodd.http.ProxyInfo; import jodd.util.MimeTypes; +import jodd.util.StringPool; + import me.chanjar.weixin.common.bean.result.WxError; import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.common.util.fs.FileUtils; @@ -45,6 +47,7 @@ public File execute(String uri, WxMpQrCodeTicket ticket) throws WxErrorException request.withConnectionProvider(requestHttp.getRequestHttpClient()); HttpResponse response = request.send(); + response.charset(StringPool.UTF_8); String contentTypeHeader = response.header("Content-Type"); if (MimeTypes.MIME_TEXT_PLAIN.equals(contentTypeHeader)) { String responseContent = response.bodyText(); diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/okhttp/OkhttpMaterialDeleteRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/okhttp/OkhttpMaterialDeleteRequestExecutor.java index 17e3ae5547..40ac27465e 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/okhttp/OkhttpMaterialDeleteRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/okhttp/OkhttpMaterialDeleteRequestExecutor.java @@ -3,16 +3,19 @@ import me.chanjar.weixin.common.bean.result.WxError; import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.common.util.http.RequestHttp; -import me.chanjar.weixin.common.util.http.okhttp.OkhttpProxyInfo; +import me.chanjar.weixin.common.util.http.okhttp.OkHttpProxyInfo; import me.chanjar.weixin.mp.util.http.MaterialDeleteRequestExecutor; import okhttp3.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; /** * Created by ecoolper on 2017/5/5. */ -public class OkhttpMaterialDeleteRequestExecutor extends MaterialDeleteRequestExecutor { +public class OkhttpMaterialDeleteRequestExecutor extends MaterialDeleteRequestExecutor { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); public OkhttpMaterialDeleteRequestExecutor(RequestHttp requestHttp) { @@ -21,23 +24,9 @@ public OkhttpMaterialDeleteRequestExecutor(RequestHttp requestHttp) { @Override public Boolean execute(String uri, String materialId) throws WxErrorException, IOException { - OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder().connectionPool(requestHttp.getRequestHttpClient()); - //设置代理 - if (requestHttp.getRequestHttpProxy() != null) { - clientBuilder.proxy(requestHttp.getRequestHttpProxy().getProxy()); - } - //设置授权 - clientBuilder.authenticator(new Authenticator() { - @Override - public Request authenticate(Route route, Response response) throws IOException { - String credential = Credentials.basic(requestHttp.getRequestHttpProxy().getProxyUsername(), requestHttp.getRequestHttpProxy().getProxyPassword()); - return response.request().newBuilder() - .header("Authorization", credential) - .build(); - } - }); + logger.debug("OkhttpMaterialDeleteRequestExecutor is running"); //得到httpClient - OkHttpClient client = clientBuilder.build(); + OkHttpClient client = requestHttp.getRequestHttpClient(); RequestBody requestBody = new FormBody.Builder().add("media_id", materialId).build(); Request request = new Request.Builder().url(uri).post(requestBody).build(); diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/okhttp/OkhttpMaterialNewsInfoRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/okhttp/OkhttpMaterialNewsInfoRequestExecutor.java index 921915fff5..3f46ab7880 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/okhttp/OkhttpMaterialNewsInfoRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/okhttp/OkhttpMaterialNewsInfoRequestExecutor.java @@ -3,47 +3,37 @@ import me.chanjar.weixin.common.bean.result.WxError; import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.common.util.http.RequestHttp; -import me.chanjar.weixin.common.util.http.okhttp.OkhttpProxyInfo; +import me.chanjar.weixin.common.util.http.okhttp.OkHttpProxyInfo; import me.chanjar.weixin.mp.bean.material.WxMpMaterialNews; import me.chanjar.weixin.mp.util.http.MaterialNewsInfoRequestExecutor; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; import okhttp3.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; /** * Created by ecoolper on 2017/5/5. */ -public class OkhttpMaterialNewsInfoRequestExecutor extends MaterialNewsInfoRequestExecutor { +public class OkhttpMaterialNewsInfoRequestExecutor extends MaterialNewsInfoRequestExecutor { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); public OkhttpMaterialNewsInfoRequestExecutor(RequestHttp requestHttp) { super(requestHttp); } @Override public WxMpMaterialNews execute(String uri, String materialId) throws WxErrorException, IOException { - OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder().connectionPool(requestHttp.getRequestHttpClient()); - //设置代理 - if (requestHttp.getRequestHttpProxy() != null) { - clientBuilder.proxy(requestHttp.getRequestHttpProxy().getProxy()); - } - //设置授权 - clientBuilder.authenticator(new Authenticator() { - @Override - public Request authenticate(Route route, Response response) throws IOException { - String credential = Credentials.basic(requestHttp.getRequestHttpProxy().getProxyUsername(), requestHttp.getRequestHttpProxy().getProxyPassword()); - return response.request().newBuilder() - .header("Authorization", credential) - .build(); - } - }); + //得到httpClient - OkHttpClient client = clientBuilder.build(); + OkHttpClient client = requestHttp.getRequestHttpClient(); RequestBody requestBody = new FormBody.Builder().add("media_id", materialId).build(); Request request = new Request.Builder().url(uri).post(requestBody).build(); Response response = client.newCall(request).execute(); String responseContent = response.body().string(); + this.logger.debug("响应原始数据:{}", responseContent); WxError error = WxError.fromJson(responseContent); if (error.getErrorCode() != 0) { diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/okhttp/OkhttpMaterialUploadRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/okhttp/OkhttpMaterialUploadRequestExecutor.java index a1e3f6189b..ef4bcbb299 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/okhttp/OkhttpMaterialUploadRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/okhttp/OkhttpMaterialUploadRequestExecutor.java @@ -3,12 +3,14 @@ import me.chanjar.weixin.common.bean.result.WxError; import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.common.util.http.RequestHttp; -import me.chanjar.weixin.common.util.http.okhttp.OkhttpProxyInfo; +import me.chanjar.weixin.common.util.http.okhttp.OkHttpProxyInfo; import me.chanjar.weixin.common.util.json.WxGsonBuilder; import me.chanjar.weixin.mp.bean.material.WxMpMaterial; import me.chanjar.weixin.mp.bean.material.WxMpMaterialUploadResult; import me.chanjar.weixin.mp.util.http.MaterialUploadRequestExecutor; import okhttp3.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileNotFoundException; @@ -18,48 +20,39 @@ /** * Created by ecoolper on 2017/5/5. */ -public class OkhttpMaterialUploadRequestExecutor extends MaterialUploadRequestExecutor { +public class OkhttpMaterialUploadRequestExecutor extends MaterialUploadRequestExecutor { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + public OkhttpMaterialUploadRequestExecutor(RequestHttp requestHttp) { super(requestHttp); } @Override public WxMpMaterialUploadResult execute(String uri, WxMpMaterial material) throws WxErrorException, IOException { - OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder().connectionPool(requestHttp.getRequestHttpClient()); - //设置代理 - if (requestHttp.getRequestHttpProxy() != null) { - clientBuilder.proxy(requestHttp.getRequestHttpProxy().getProxy()); - } - //设置授权 - clientBuilder.authenticator(new Authenticator() { - @Override - public Request authenticate(Route route, Response response) throws IOException { - String credential = Credentials.basic(requestHttp.getRequestHttpProxy().getProxyUsername(), requestHttp.getRequestHttpProxy().getProxyPassword()); - return response.request().newBuilder() - .header("Authorization", credential) - .build(); - } - }); - //得到httpClient - OkHttpClient client = clientBuilder.build(); - - + logger.debug("OkhttpMaterialUploadRequestExecutor is running"); if (material == null) { throw new WxErrorException(WxError.newBuilder().setErrorMsg("非法请求,material参数为空").build()); } - File file = material.getFile(); if (file == null || !file.exists()) { throw new FileNotFoundException(); } - RequestBody fileBody = RequestBody.create(MediaType.parse("multipart/form-data"), file); - MultipartBody.Builder bodyBuilder = new MultipartBody.Builder().addFormDataPart("media", null, fileBody); + + //得到httpClient + + OkHttpClient client = requestHttp.getRequestHttpClient(); + + MultipartBody.Builder bodyBuilder = new MultipartBody.Builder() + .setType(MediaType.parse("multipart/form-data")) + .addFormDataPart("media", + file.getName(), + RequestBody.create(MediaType.parse("application/octet-stream"), file)); Map form = material.getForm(); - if (material.getForm() != null) { + if (form != null) { bodyBuilder.addFormDataPart("description", WxGsonBuilder.create().toJson(form)); } - RequestBody body = bodyBuilder.build(); - Request request = new Request.Builder().url(uri).post(body).build(); + + Request request = new Request.Builder().url(uri).post(bodyBuilder.build()).build(); Response response = client.newCall(request).execute(); String responseContent = response.body().string(); WxError error = WxError.fromJson(responseContent); @@ -69,4 +62,5 @@ public Request authenticate(Route route, Response response) throws IOException { return WxMpMaterialUploadResult.fromJson(responseContent); } } + } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/okhttp/OkhttpMaterialVideoInfoRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/okhttp/OkhttpMaterialVideoInfoRequestExecutor.java index e296a84966..79dd12be53 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/okhttp/OkhttpMaterialVideoInfoRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/okhttp/OkhttpMaterialVideoInfoRequestExecutor.java @@ -3,40 +3,30 @@ import me.chanjar.weixin.common.bean.result.WxError; import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.common.util.http.RequestHttp; -import me.chanjar.weixin.common.util.http.okhttp.OkhttpProxyInfo; +import me.chanjar.weixin.common.util.http.okhttp.OkHttpProxyInfo; import me.chanjar.weixin.mp.bean.material.WxMpMaterialVideoInfoResult; import me.chanjar.weixin.mp.util.http.MaterialVideoInfoRequestExecutor; import okhttp3.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; /** * Created by ecoolper on 2017/5/5. */ -public class OkhttpMaterialVideoInfoRequestExecutor extends MaterialVideoInfoRequestExecutor { +public class OkhttpMaterialVideoInfoRequestExecutor extends MaterialVideoInfoRequestExecutor { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + public OkhttpMaterialVideoInfoRequestExecutor(RequestHttp requestHttp) { super(requestHttp); } @Override public WxMpMaterialVideoInfoResult execute(String uri, String materialId) throws WxErrorException, IOException { - OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder().connectionPool(requestHttp.getRequestHttpClient()); - //设置代理 - if (requestHttp.getRequestHttpProxy() != null) { - clientBuilder.proxy(requestHttp.getRequestHttpProxy().getProxy()); - } - //设置授权 - clientBuilder.authenticator(new Authenticator() { - @Override - public Request authenticate(Route route, Response response) throws IOException { - String credential = Credentials.basic(requestHttp.getRequestHttpProxy().getProxyUsername(), requestHttp.getRequestHttpProxy().getProxyPassword()); - return response.request().newBuilder() - .header("Authorization", credential) - .build(); - } - }); + logger.debug("OkhttpMaterialVideoInfoRequestExecutor is running"); //得到httpClient - OkHttpClient client = clientBuilder.build(); + OkHttpClient client = requestHttp.getRequestHttpClient(); RequestBody requestBody = new FormBody.Builder().add("media_id", materialId).build(); Request request = new Request.Builder().url(uri).post(requestBody).build(); diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/okhttp/OkhttpMaterialVoiceAndImageDownloadRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/okhttp/OkhttpMaterialVoiceAndImageDownloadRequestExecutor.java index e6393b8267..8c48139183 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/okhttp/OkhttpMaterialVoiceAndImageDownloadRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/okhttp/OkhttpMaterialVoiceAndImageDownloadRequestExecutor.java @@ -3,65 +3,43 @@ import me.chanjar.weixin.common.bean.result.WxError; import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.common.util.http.RequestHttp; -import me.chanjar.weixin.common.util.http.okhttp.OkhttpProxyInfo; +import me.chanjar.weixin.common.util.http.okhttp.OkHttpProxyInfo; import me.chanjar.weixin.common.util.json.WxGsonBuilder; import me.chanjar.weixin.mp.util.http.MaterialVoiceAndImageDownloadRequestExecutor; import okhttp3.*; +import okio.BufferedSink; +import okio.Okio; import org.apache.commons.io.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; +import java.io.*; /** * Created by ecoolper on 2017/5/5. */ -public class OkhttpMaterialVoiceAndImageDownloadRequestExecutor extends MaterialVoiceAndImageDownloadRequestExecutor { +public class OkhttpMaterialVoiceAndImageDownloadRequestExecutor extends MaterialVoiceAndImageDownloadRequestExecutor { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + public OkhttpMaterialVoiceAndImageDownloadRequestExecutor(RequestHttp requestHttp, File tmpDirFile) { super(requestHttp, tmpDirFile); } @Override public InputStream execute(String uri, String materialId) throws WxErrorException, IOException { - OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder().connectionPool(requestHttp.getRequestHttpClient()); - //设置代理 - if (requestHttp.getRequestHttpProxy() != null) { - clientBuilder.proxy(requestHttp.getRequestHttpProxy().getProxy()); - } - //设置授权 - clientBuilder.authenticator(new Authenticator() { - @Override - public Request authenticate(Route route, Response response) throws IOException { - String credential = Credentials.basic(requestHttp.getRequestHttpProxy().getProxyUsername(), requestHttp.getRequestHttpProxy().getProxyPassword()); - return response.request().newBuilder() - .header("Authorization", credential) - .build(); - } - }); - //得到httpClient - OkHttpClient client = clientBuilder.build(); - + logger.debug("OkhttpMaterialVoiceAndImageDownloadRequestExecutor is running"); + OkHttpClient client = requestHttp.getRequestHttpClient(); RequestBody requestBody = new FormBody.Builder().add("media_id", materialId).build(); Request request = new Request.Builder().url(uri).get().post(requestBody).build(); Response response = client.newCall(request).execute(); - - try (InputStream inputStream = new ByteArrayInputStream(response.body().bytes())) { - - // 下载媒体文件出错 - byte[] responseContent = IOUtils.toByteArray(inputStream); - String responseContentString = new String(responseContent, "UTF-8"); - if (responseContentString.length() < 100) { - try { - WxError wxError = WxGsonBuilder.create().fromJson(responseContentString, WxError.class); - if (wxError.getErrorCode() != 0) { - throw new WxErrorException(wxError); - } - } catch (com.google.gson.JsonSyntaxException ex) { - return new ByteArrayInputStream(responseContent); - } - } - return new ByteArrayInputStream(responseContent); + String contentTypeHeader = response.header("Content-Type"); + if ("text/plain".equals(contentTypeHeader)) { + String responseContent = response.body().string(); + throw new WxErrorException(WxError.fromJson(responseContent)); + } + try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); BufferedSink sink = Okio.buffer(Okio.sink(outputStream))) { + sink.writeAll(response.body().source()); + return new ByteArrayInputStream(outputStream.toByteArray()); } } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/okhttp/OkhttpMediaImgUploadRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/okhttp/OkhttpMediaImgUploadRequestExecutor.java index af29e532d1..f8e5859287 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/okhttp/OkhttpMediaImgUploadRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/okhttp/OkhttpMediaImgUploadRequestExecutor.java @@ -3,10 +3,12 @@ import me.chanjar.weixin.common.bean.result.WxError; import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.common.util.http.RequestHttp; -import me.chanjar.weixin.common.util.http.okhttp.OkhttpProxyInfo; +import me.chanjar.weixin.common.util.http.okhttp.OkHttpProxyInfo; import me.chanjar.weixin.mp.bean.material.WxMediaImgUploadResult; import me.chanjar.weixin.mp.util.http.MediaImgUploadRequestExecutor; import okhttp3.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; @@ -14,36 +16,27 @@ /** * Created by ecoolper on 2017/5/5. */ -public class OkhttpMediaImgUploadRequestExecutor extends MediaImgUploadRequestExecutor { +public class OkhttpMediaImgUploadRequestExecutor extends MediaImgUploadRequestExecutor { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); public OkhttpMediaImgUploadRequestExecutor(RequestHttp requestHttp) { super(requestHttp); } @Override - public WxMediaImgUploadResult execute(String uri, File data) throws WxErrorException, IOException { - OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder().connectionPool(requestHttp.getRequestHttpClient()); - //设置代理 - if (requestHttp.getRequestHttpProxy() != null) { - clientBuilder.proxy(requestHttp.getRequestHttpProxy().getProxy()); - } - //设置授权 - clientBuilder.authenticator(new Authenticator() { - @Override - public Request authenticate(Route route, Response response) throws IOException { - String credential = Credentials.basic(requestHttp.getRequestHttpProxy().getProxyUsername(), requestHttp.getRequestHttpProxy().getProxyPassword()); - return response.request().newBuilder() - .header("Authorization", credential) - .build(); - } - }); + public WxMediaImgUploadResult execute(String uri, File file) throws WxErrorException, IOException { + logger.debug("OkhttpMediaImgUploadRequestExecutor is running"); //得到httpClient - OkHttpClient client = clientBuilder.build(); + OkHttpClient client = requestHttp.getRequestHttpClient(); - RequestBody fileBody = RequestBody.create(MediaType.parse("multipart/form-data"), data); - RequestBody body = new MultipartBody.Builder().addFormDataPart("media", null, fileBody).build(); - Request request = new Request.Builder().url(uri).post(body).build(); + RequestBody body = new MultipartBody.Builder() + .setType(MediaType.parse("multipart/form-data")) + .addFormDataPart("media", + file.getName(), + RequestBody.create(MediaType.parse("application/octet-stream"), file)) + .build(); + Request request = new Request.Builder().url(uri).post(body).build(); Response response = client.newCall(request).execute(); String responseContent = response.body().string(); WxError error = WxError.fromJson(responseContent); diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/okhttp/OkhttpQrCodeRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/okhttp/OkhttpQrCodeRequestExecutor.java index 463d69ac25..6feb137925 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/okhttp/OkhttpQrCodeRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/okhttp/OkhttpQrCodeRequestExecutor.java @@ -1,14 +1,18 @@ package me.chanjar.weixin.mp.util.http.okhttp; -import jodd.util.MimeTypes; import me.chanjar.weixin.common.bean.result.WxError; import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.common.util.fs.FileUtils; import me.chanjar.weixin.common.util.http.RequestHttp; -import me.chanjar.weixin.common.util.http.okhttp.OkhttpProxyInfo; +import me.chanjar.weixin.common.util.http.okhttp.OkHttpProxyInfo; import me.chanjar.weixin.mp.bean.result.WxMpQrCodeTicket; import me.chanjar.weixin.mp.util.http.QrCodeRequestExecutor; + import okhttp3.*; +import okio.BufferedSink; +import okio.Okio; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.ByteArrayInputStream; import java.io.File; @@ -19,40 +23,31 @@ /** * Created by ecoolper on 2017/5/5. */ -public class OkhttpQrCodeRequestExecutor extends QrCodeRequestExecutor { +public class OkhttpQrCodeRequestExecutor extends QrCodeRequestExecutor { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + public OkhttpQrCodeRequestExecutor(RequestHttp requestHttp) { super(requestHttp); } @Override public File execute(String uri, WxMpQrCodeTicket data) throws WxErrorException, IOException { - OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder().connectionPool(requestHttp.getRequestHttpClient()); - //设置代理 - if (requestHttp.getRequestHttpProxy() != null) { - clientBuilder.proxy(requestHttp.getRequestHttpProxy().getProxy()); - } - //设置授权 - clientBuilder.authenticator(new Authenticator() { - @Override - public Request authenticate(Route route, Response response) throws IOException { - String credential = Credentials.basic(requestHttp.getRequestHttpProxy().getProxyUsername(), requestHttp.getRequestHttpProxy().getProxyPassword()); - return response.request().newBuilder() - .header("Authorization", credential) - .build(); - } - }); + logger.debug("OkhttpQrCodeRequestExecutor is running"); //得到httpClient - OkHttpClient client = clientBuilder.build(); - + OkHttpClient client = requestHttp.getRequestHttpClient(); Request request = new Request.Builder().url(uri).get().build(); Response response = client.newCall(request).execute(); String contentTypeHeader = response.header("Content-Type"); - if (MimeTypes.MIME_TEXT_PLAIN.equals(contentTypeHeader)) { + if ("text/plain".equals(contentTypeHeader)) { String responseContent = response.body().string(); throw new WxErrorException(WxError.fromJson(responseContent)); } - try (InputStream inputStream = new ByteArrayInputStream(response.body().bytes())) { - return FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), "jpg"); + File temp = File.createTempFile(UUID.randomUUID().toString(), ".png"); + try (BufferedSink sink = Okio.buffer(Okio.sink(temp))) { + sink.writeAll(response.body().source()); } + temp.deleteOnExit(); + + return temp; } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpGsonBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpGsonBuilder.java index 286778ad4b..124845ed6b 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpGsonBuilder.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpGsonBuilder.java @@ -6,6 +6,8 @@ import me.chanjar.weixin.mp.bean.datacube.WxDataCubeUserCumulate; import me.chanjar.weixin.mp.bean.datacube.WxDataCubeUserSummary; import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; +import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardUpdateResult; +import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardUserInfoResult; import me.chanjar.weixin.mp.bean.material.*; import me.chanjar.weixin.mp.bean.result.*; import me.chanjar.weixin.mp.bean.template.WxMpTemplateIndustry; @@ -49,6 +51,8 @@ public class WxMpGsonBuilder { INSTANCE.registerTypeAdapter(WxMediaImgUploadResult.class, new WxMediaImgUploadResultGsonAdapter()); INSTANCE.registerTypeAdapter(WxMpTemplateIndustry.class, new WxMpIndustryGsonAdapter()); INSTANCE.registerTypeAdapter(WxMpUserBlacklistGetResult.class, new WxUserBlacklistGetResultGsonAdapter()); + INSTANCE.registerTypeAdapter(WxMpMemberCardUserInfoResult.class, new WxMpMemberCardUserInfoResultGsonAdapter()); + INSTANCE.registerTypeAdapter(WxMpMemberCardUpdateResult.class, new WxMpMemberCardUpdateResultGsonAdapter()); } public static Gson create() { diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialNewsArticleGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialNewsArticleGsonAdapter.java index a74877f126..5a17a7bdb8 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialNewsArticleGsonAdapter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialNewsArticleGsonAdapter.java @@ -11,6 +11,7 @@ import com.google.gson.*; import me.chanjar.weixin.common.util.json.GsonHelper; import me.chanjar.weixin.mp.bean.material.WxMpMaterialNews; +import org.apache.commons.lang3.BooleanUtils; import java.lang.reflect.Type; @@ -37,6 +38,16 @@ public JsonElement serialize(WxMpMaterialNews.WxMpMaterialNewsArticle article, T if (null != article.getUrl()) { articleJson.addProperty("url", article.getUrl()); } + + if (null != article.getNeedOpenComment()) { + articleJson.addProperty("need_open_comment", + BooleanUtils.toInteger(article.getNeedOpenComment(), 1, 0)); + } + + if (null != article.getOnlyFansCanComment()) { + articleJson.addProperty("only_fans_can_comment", + BooleanUtils.toInteger(article.getOnlyFansCanComment(), 1, 0)); + } return articleJson; } @@ -81,6 +92,16 @@ public WxMpMaterialNews.WxMpMaterialNewsArticle deserialize(JsonElement jsonElem if (url != null && !url.isJsonNull()) { article.setUrl(GsonHelper.getAsString(url)); } + + JsonElement needOpenComment = articleInfo.get("need_open_comment"); + if (needOpenComment != null && !needOpenComment.isJsonNull()) { + article.setNeedOpenComment(GsonHelper.getAsBoolean(needOpenComment)); + } + + JsonElement onlyFansCanComment = articleInfo.get("only_fans_can_comment"); + if (onlyFansCanComment != null && !onlyFansCanComment.isJsonNull()) { + article.setOnlyFansCanComment(GsonHelper.getAsBoolean(onlyFansCanComment)); + } return article; } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialNewsGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialNewsGsonAdapter.java index 8da6dacdc4..084e355cbc 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialNewsGsonAdapter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMaterialNewsGsonAdapter.java @@ -9,9 +9,12 @@ package me.chanjar.weixin.mp.util.json; import com.google.gson.*; +import me.chanjar.weixin.common.util.json.GsonHelper; import me.chanjar.weixin.mp.bean.material.WxMpMaterialNews; import java.lang.reflect.Type; +import java.text.SimpleDateFormat; +import java.util.Date; public class WxMpMaterialNewsGsonAdapter implements JsonSerializer, JsonDeserializer { @@ -26,6 +29,16 @@ public JsonElement serialize(WxMpMaterialNews wxMpMaterialNews, Type typeOfSrc, } newsJson.add("articles", articleJsonArray); + if (wxMpMaterialNews.getCreatedTime() != null) { + newsJson.addProperty("create_time", + SimpleDateFormat.getDateTimeInstance().format(wxMpMaterialNews.getCreatedTime())); + } + + if (wxMpMaterialNews.getUpdatedTime() != null) { + newsJson.addProperty("update_time", + SimpleDateFormat.getDateTimeInstance().format(wxMpMaterialNews.getUpdatedTime())); + } + return newsJson; } @@ -41,6 +54,17 @@ public WxMpMaterialNews deserialize(JsonElement jsonElement, Type type, JsonDese wxMpMaterialNews.addArticle(article); } } + + if (json.get("create_time") != null && !json.get("create_time").isJsonNull()) { + Date createTime = new Date(GsonHelper.getAsLong(json.get("create_time"))* 1000); + wxMpMaterialNews.setCreatedTime(createTime); + } + + if (json.get("update_time") != null && !json.get("update_time").isJsonNull()) { + Date updateTime = new Date(GsonHelper.getAsLong(json.get("update_time"))* 1000); + wxMpMaterialNews.setUpdatedTime(updateTime); + } + return wxMpMaterialNews; } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMemberCardUpdateResultGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMemberCardUpdateResultGsonAdapter.java new file mode 100644 index 0000000000..dc068db1e8 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMemberCardUpdateResultGsonAdapter.java @@ -0,0 +1,37 @@ +package me.chanjar.weixin.mp.util.json; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import me.chanjar.weixin.common.util.json.GsonHelper; +import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardUpdateResult; + +import java.lang.reflect.Type; + +/** + * Json to WxMpMemberCardUpdateResult 的转换适配器 + * + * @author YuJian + * @version 2017/7/15 + */ +public class WxMpMemberCardUpdateResultGsonAdapter implements JsonDeserializer { + + @Override + public WxMpMemberCardUpdateResult deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext + jsonDeserializationContext) throws JsonParseException { + + WxMpMemberCardUpdateResult result = new WxMpMemberCardUpdateResult(); + + JsonObject jsonObject = jsonElement.getAsJsonObject(); + + result.setOpenId(GsonHelper.getString(jsonObject, "openid")); + result.setErrorCode(GsonHelper.getString(jsonObject, "errcode")); + result.setErrorMsg(GsonHelper.getString(jsonObject, "errmsg")); + result.setResultBalance(GsonHelper.getInteger(jsonObject, "result_balance")); + result.setResultBonus(GsonHelper.getInteger(jsonObject, "result_bonus")); + + return result; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMemberCardUserInfoResultGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMemberCardUserInfoResultGsonAdapter.java new file mode 100644 index 0000000000..5a97ee7496 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMemberCardUserInfoResultGsonAdapter.java @@ -0,0 +1,71 @@ +package me.chanjar.weixin.mp.util.json; + +import com.google.gson.*; +import me.chanjar.weixin.common.util.json.GsonHelper; +import me.chanjar.weixin.mp.bean.membercard.MemberCardUserInfo; +import me.chanjar.weixin.mp.bean.membercard.NameValues; +import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardUserInfoResult; + +import java.lang.reflect.Type; + +/** + * Json to WxMpMemberCardUserInfoResult 的转换适配器 + * + * @author YuJian(mgcnrx11@gmail.com) + * @version 2017/7/11 + */ +public class WxMpMemberCardUserInfoResultGsonAdapter implements JsonDeserializer { + + @Override + public WxMpMemberCardUserInfoResult deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { + WxMpMemberCardUserInfoResult result = new WxMpMemberCardUserInfoResult(); + + JsonObject jsonObject = jsonElement.getAsJsonObject(); + + result.setOpenId(GsonHelper.getString(jsonObject, "openid")); + result.setErrorCode(GsonHelper.getString(jsonObject, "errcode")); + result.setErrorMsg(GsonHelper.getString(jsonObject, "errmsg")); + result.setNickname(GsonHelper.getString(jsonObject, "nickname")); + result.setMembershipNumber(GsonHelper.getString(jsonObject, "membership_number")); + result.setBonus(GsonHelper.getInteger(jsonObject, "bonus")); + result.setSex(GsonHelper.getString(jsonObject, "sex")); + result.setUserCardStatus(GsonHelper.getString(jsonObject, "user_card_status")); + result.setHasActive(GsonHelper.getBoolean(jsonObject, "has_active")); + + JsonObject userInfoJsonObject = jsonObject.getAsJsonObject("user_info"); + MemberCardUserInfo cardUserInfo = new MemberCardUserInfo(); + + JsonArray commonFieldListObj = userInfoJsonObject.getAsJsonArray("common_field_list"); + NameValues[] commonFieldListValues = new NameValues[commonFieldListObj.size()]; + for (int i = 0; i < commonFieldListObj.size(); i++) { + JsonObject commonField = commonFieldListObj.get(i).getAsJsonObject(); + NameValues commonNameValues = new NameValues(); + commonNameValues.setName(GsonHelper.getString(commonField, "name")); + commonNameValues.setValue(GsonHelper.getString(commonField, "value")); + commonFieldListValues[i] = commonNameValues; + } + cardUserInfo.setCommonFieldList(commonFieldListValues); + + JsonArray customFieldListObj = userInfoJsonObject.getAsJsonArray("custom_field_list"); + NameValues[] customFieldListValues = new NameValues[customFieldListObj.size()]; + for (int i = 0; i < customFieldListObj.size(); i++) { + JsonObject customField = customFieldListObj.get(i).getAsJsonObject(); + NameValues customNameValues = new NameValues(); + customNameValues.setName(GsonHelper.getString(customField, "name")); + customNameValues.setValue(GsonHelper.getString(customField, "value")); + + JsonArray valueListArray = customField.getAsJsonArray("value_list"); + String[] valueList = new String[valueListArray.size()]; + for (int j = 0; j < valueListArray.size(); j++) { + valueList[j] = valueListArray.get(j).getAsString(); + } + customNameValues.setValueList(valueList); + customFieldListValues[i] = customNameValues; + } + cardUserInfo.setCustomFieldList(customFieldListValues); + + result.setUserInfo(cardUserInfo); + + return result; + } +} diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpBaseAPITest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpBaseAPITest.java index c8b616fa96..7fb10716f0 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpBaseAPITest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpBaseAPITest.java @@ -29,4 +29,10 @@ public void testRefreshAccessToken() throws WxErrorException { Assert.assertTrue(StringUtils.isNotBlank(after)); } + public void testJsapiTicket() throws WxErrorException { + String jsapiTicket = this.wxService.getJsapiTicket(false); + System.out.println(jsapiTicket); + Assert.assertNotNull(jsapiTicket); + } + } diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpBusyRetryTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpBusyRetryTest.java index cc4dcf9619..fc0f70b776 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpBusyRetryTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpBusyRetryTest.java @@ -3,7 +3,7 @@ import me.chanjar.weixin.common.bean.result.WxError; import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.common.util.http.RequestExecutor; -import me.chanjar.weixin.mp.api.impl.apache.WxMpServiceImpl; +import me.chanjar.weixin.mp.api.impl.WxMpServiceApacheHttpClientImpl; import org.testng.annotations.*; import java.util.concurrent.ExecutionException; @@ -16,7 +16,7 @@ public class WxMpBusyRetryTest { @DataProvider(name = "getService") public Object[][] getService() { - WxMpService service = new WxMpServiceImpl() { + WxMpService service = new WxMpServiceApacheHttpClientImpl() { @Override public synchronized T executeInternal( diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpJsAPITest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpJsAPITest.java index 9fd3227893..e6be87302d 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpJsAPITest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpJsAPITest.java @@ -20,11 +20,7 @@ public class WxMpJsAPITest { protected WxMpService wxService; - public void testJsapiTicket() throws WxErrorException { - String jsapiTicket = this.wxService.getJsapiTicket(false); - System.out.println(jsapiTicket); - Assert.assertNotNull(jsapiTicket); - } + public void test() { long timestamp = 1419835025l; diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMessageRouterTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMessageRouterTest.java index 4360440f04..35d13ee1e9 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMessageRouterTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMessageRouterTest.java @@ -39,6 +39,7 @@ public void prepare(boolean async, StringBuffer sb, WxMpMessageRouter router) { .rule().async(async).msgType(WxConsts.XML_MSG_TEXT).handler(new WxEchoMpMessageHandler(sb, WxConsts.XML_MSG_TEXT)).end() .rule().async(async).event(WxConsts.EVT_CLICK).handler(new WxEchoMpMessageHandler(sb, WxConsts.EVT_CLICK)).end() .rule().async(async).eventKey("KEY_1").handler(new WxEchoMpMessageHandler(sb, "KEY_1")).end() + .rule().async(async).eventKeyRegex("KEY_1*").handler(new WxEchoMpMessageHandler(sb, "KEY_123")).end() .rule().async(async).content("CONTENT_1").handler(new WxEchoMpMessageHandler(sb, "CONTENT_1")).end() .rule().async(async).rContent(".*bc.*").handler(new WxEchoMpMessageHandler(sb, "abcd")).end() .rule().async(async).matcher(new WxMpMessageMatcher() { diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMassMessageAPITest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMassMessageServiceImplTest.java similarity index 80% rename from weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMassMessageAPITest.java rename to weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMassMessageServiceImplTest.java index dd7541ff8c..b896723ab0 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMassMessageAPITest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMassMessageServiceImplTest.java @@ -1,9 +1,10 @@ -package me.chanjar.weixin.mp.api; +package me.chanjar.weixin.mp.api.impl; import com.google.inject.Inject; import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.api.test.ApiTestModule; import me.chanjar.weixin.mp.api.test.TestConfigStorage; import me.chanjar.weixin.mp.api.test.TestConstants; @@ -13,21 +14,21 @@ import me.chanjar.weixin.mp.bean.WxMpMassVideo; import me.chanjar.weixin.mp.bean.result.WxMpMassSendResult; import me.chanjar.weixin.mp.bean.result.WxMpMassUploadResult; -import org.testng.*; import org.testng.annotations.*; import java.io.IOException; import java.io.InputStream; +import static org.testng.Assert.*; + /** * 测试群发消息 * * @author chanjarster */ -@Test(groups = "massAPI", dependsOnGroups = {"baseAPI", "mediaAPI", "groupAPI"}) +@Test @Guice(modules = ApiTestModule.class) -public class WxMpMassMessageAPITest { - +public class WxMpMassMessageServiceImplTest { @Inject protected WxMpService wxService; @@ -41,10 +42,10 @@ public void testTextMassOpenIdsMessageSend() throws WxErrorException { massMessage.setContent("测试群发消息\n欢迎欢迎,热烈欢迎\n换行测试\n超链接:Hello World"); massMessage.getToUsers().add(configProvider.getOpenid()); - WxMpMassSendResult massResult = this.wxService + WxMpMassSendResult massResult = this.wxService.getMassMessageService() .massOpenIdsMessageSend(massMessage); - Assert.assertNotNull(massResult); - Assert.assertNotNull(massResult.getMsgId()); + assertNotNull(massResult); + assertNotNull(massResult.getMsgId()); } @Test(dataProvider = "massMessages") @@ -57,10 +58,10 @@ public void testMediaMassOpenIdsMessageSend(String massMsgType, String mediaId) massMessage.setMediaId(mediaId); massMessage.getToUsers().add(configProvider.getOpenid()); - WxMpMassSendResult massResult = this.wxService + WxMpMassSendResult massResult = this.wxService.getMassMessageService() .massOpenIdsMessageSend(massMessage); - Assert.assertNotNull(massResult); - Assert.assertNotNull(massResult.getMsgId()); + assertNotNull(massResult); + assertNotNull(massResult.getMsgId()); } @Test @@ -71,10 +72,10 @@ public void testTextMassGroupMessageSend() throws WxErrorException { massMessage .setTagId(this.wxService.getUserTagService().tagGet().get(0).getId()); - WxMpMassSendResult massResult = this.wxService + WxMpMassSendResult massResult = this.wxService.getMassMessageService() .massGroupMessageSend(massMessage); - Assert.assertNotNull(massResult); - Assert.assertNotNull(massResult.getMsgId()); + assertNotNull(massResult); + assertNotNull(massResult.getMsgId()); } @Test(dataProvider = "massMessages") @@ -85,10 +86,10 @@ public void testMediaMassGroupMessageSend(String massMsgType, String mediaId) massMessage.setMediaId(mediaId); massMessage.setTagId(this.wxService.getUserTagService().tagGet().get(0).getId()); - WxMpMassSendResult massResult = this.wxService + WxMpMassSendResult massResult = this.wxService.getMassMessageService() .massGroupMessageSend(massMessage); - Assert.assertNotNull(massResult); - Assert.assertNotNull(massResult.getMsgId()); + assertNotNull(massResult); + assertNotNull(massResult.getMsgId()); } @DataProvider @@ -103,17 +104,17 @@ public Object[][] massMessages() throws WxErrorException, IOException { // 上传视频到媒体库 WxMediaUploadResult uploadMediaRes = this.wxService.getMaterialService() .mediaUpload(WxConsts.MEDIA_VIDEO, TestConstants.FILE_MP4, inputStream); - Assert.assertNotNull(uploadMediaRes); - Assert.assertNotNull(uploadMediaRes.getMediaId()); + assertNotNull(uploadMediaRes); + assertNotNull(uploadMediaRes.getMediaId()); // 把视频变成可被群发的媒体 WxMpMassVideo video = new WxMpMassVideo(); video.setTitle("测试标题"); video.setDescription("测试描述"); video.setMediaId(uploadMediaRes.getMediaId()); - WxMpMassUploadResult uploadResult = this.wxService.massVideoUpload(video); - Assert.assertNotNull(uploadResult); - Assert.assertNotNull(uploadResult.getMediaId()); + WxMpMassUploadResult uploadResult = this.wxService.getMassMessageService().massVideoUpload(video); + assertNotNull(uploadResult); + assertNotNull(uploadResult.getMediaId()); messages[0] = new Object[]{WxConsts.MASS_MSG_VIDEO, uploadResult.getMediaId()}; } @@ -124,8 +125,8 @@ public Object[][] massMessages() throws WxErrorException, IOException { .getSystemResourceAsStream("mm.jpeg")) { WxMediaUploadResult uploadMediaRes = this.wxService.getMaterialService() .mediaUpload(WxConsts.MEDIA_IMAGE, TestConstants.FILE_JPG, inputStream); - Assert.assertNotNull(uploadMediaRes); - Assert.assertNotNull(uploadMediaRes.getMediaId()); + assertNotNull(uploadMediaRes); + assertNotNull(uploadMediaRes.getMediaId()); messages[1] = new Object[]{WxConsts.MASS_MSG_IMAGE, uploadMediaRes.getMediaId()}; } @@ -136,8 +137,8 @@ public Object[][] massMessages() throws WxErrorException, IOException { .getSystemResourceAsStream("mm.mp3")) { WxMediaUploadResult uploadMediaRes = this.wxService.getMaterialService() .mediaUpload(WxConsts.MEDIA_VOICE, TestConstants.FILE_MP3, inputStream); - Assert.assertNotNull(uploadMediaRes); - Assert.assertNotNull(uploadMediaRes.getMediaId()); + assertNotNull(uploadMediaRes); + assertNotNull(uploadMediaRes.getMediaId()); messages[2] = new Object[]{WxConsts.MASS_MSG_VOICE, uploadMediaRes.getMediaId()}; } @@ -149,8 +150,8 @@ public Object[][] massMessages() throws WxErrorException, IOException { // 上传照片到媒体库 WxMediaUploadResult uploadMediaRes = this.wxService.getMaterialService() .mediaUpload(WxConsts.MEDIA_IMAGE, TestConstants.FILE_JPG, inputStream); - Assert.assertNotNull(uploadMediaRes); - Assert.assertNotNull(uploadMediaRes.getMediaId()); + assertNotNull(uploadMediaRes); + assertNotNull(uploadMediaRes.getMediaId()); // 上传图文消息 WxMpMassNews news = new WxMpMassNews(); @@ -170,10 +171,10 @@ public Object[][] massMessages() throws WxErrorException, IOException { article2.setDigest("摘要2"); news.addArticle(article2); - WxMpMassUploadResult massUploadResult = this.wxService + WxMpMassUploadResult massUploadResult = this.wxService.getMassMessageService() .massNewsUpload(news); - Assert.assertNotNull(massUploadResult); - Assert.assertNotNull(uploadMediaRes.getMediaId()); + assertNotNull(massUploadResult); + assertNotNull(uploadMediaRes.getMediaId()); messages[3] = new Object[]{WxConsts.MASS_MSG_NEWS, massUploadResult.getMediaId()}; } diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMaterialServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMaterialServiceImplTest.java index 6bb1e58bff..309c4e0e47 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMaterialServiceImplTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMaterialServiceImplTest.java @@ -16,7 +16,9 @@ import java.io.InputStream; import java.util.*; -import static org.junit.Assert.*; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; /** * 素材管理相关接口的测试 @@ -173,7 +175,7 @@ public void testDownloadMaterial(String mediaId) throws WxErrorException, IOExce } } - @Test(dependsOnMethods = {"testAddNews"}) + @Test(dependsOnMethods = {"testAddNews","testUploadMaterial"}) public void testGetNewsInfo() throws WxErrorException { WxMpMaterialNews wxMpMaterialNewsSingle = this.wxService .getMaterialService().materialNewsInfo(this.singleNewsMediaId); @@ -181,6 +183,9 @@ public void testGetNewsInfo() throws WxErrorException { .getMaterialService().materialNewsInfo(this.multiNewsMediaId); assertNotNull(wxMpMaterialNewsSingle); assertNotNull(wxMpMaterialNewsMultiple); + + System.out.println(wxMpMaterialNewsSingle); + System.out.println(wxMpMaterialNewsMultiple); } @Test(dependsOnMethods = {"testGetNewsInfo"}) diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMemberCardServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMemberCardServiceImplTest.java new file mode 100644 index 0000000000..67eb5817cc --- /dev/null +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMemberCardServiceImplTest.java @@ -0,0 +1,61 @@ +package me.chanjar.weixin.mp.api.impl; + +import com.google.inject.Inject; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.api.test.ApiTestModule; +import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardActivatedMessage; +import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardUpdateMessage; +import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardUpdateResult; +import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardUserInfoResult; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import static org.testng.AssertJUnit.assertNotNull; + +/** + * 会员卡相关接口的测试类。 + * 数据均为测试数据,由于直接与调用微信的接口,需要填写真实数据进行测试才能通过。 + */ +@Test +@Guice(modules = ApiTestModule.class) +public class WxMpMemberCardServiceImplTest { + + @Inject + protected WxMpService wxService; + private String cardId = "abc"; + private String code = "123"; + private String openId = "xyz"; + + @Test + public void testActivateMemberCard() throws Exception { + WxMpMemberCardActivatedMessage activatedMessage = new WxMpMemberCardActivatedMessage(); + activatedMessage.setMembershipNumber(openId); + activatedMessage.setCode(code); + activatedMessage.setCardId(cardId); + activatedMessage.setInitBonus(2000); + activatedMessage.setInitBonusRecord("测试激活送积分"); + String response = this.wxService.getMemberCardService().activateMemberCard(activatedMessage); + assertNotNull(response); + System.out.println(response); + } + + @Test + public void testGetUserInfo() throws Exception { + WxMpMemberCardUserInfoResult result = this.wxService.getMemberCardService().getUserInfo(cardId, code); + assertNotNull(result); + System.out.println(result); + } + + @Test + public void testUpdateUserMemberCard() throws Exception { + WxMpMemberCardUpdateMessage updateMessage = new WxMpMemberCardUpdateMessage(); + updateMessage.setAddBounus(100); + updateMessage.setBonus(1000); + updateMessage.setCardId(cardId); + updateMessage.setCode(code); + WxMpMemberCardUpdateResult result = this.wxService.getMemberCardService().updateUserMemberCard(updateMessage); + assertNotNull(result); + System.out.println(result); + } + +} diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMenuServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMenuServiceImplTest.java index dac37f266f..8846809001 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMenuServiceImplTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMenuServiceImplTest.java @@ -138,7 +138,7 @@ public void testMenuGet() throws WxErrorException { System.out.println(wxMenu.toJson()); } - @Test(dependsOnMethods = {"testMenuGet"}) + @Test(dependsOnMethods = {"testMenuGet","testMenuCreate"}) public void testMenuDelete() throws WxErrorException { this.wxService.getMenuService().menuDelete(); } @@ -151,18 +151,18 @@ public Object[][] getMenu() { button1.setName("今日歌曲"); button1.setKey("V1001_TODAY_MUSIC"); - WxMenuButton button2 = new WxMenuButton(); - button2.setType(WxConsts.BUTTON_MINIPROGRAM); - button2.setName("小程序"); - button2.setAppId("wx286b93c14bbf93aa"); - button2.setPagePath("pages/lunar/index.html"); - button2.setUrl("http://mp.weixin.qq.com"); +// WxMenuButton button2 = new WxMenuButton(); +// button2.setType(WxConsts.BUTTON_MINIPROGRAM); +// button2.setName("小程序"); +// button2.setAppId("wx286b93c14bbf93aa"); +// button2.setPagePath("pages/lunar/index.html"); +// button2.setUrl("http://mp.weixin.qq.com"); WxMenuButton button3 = new WxMenuButton(); button3.setName("菜单"); menu.getButtons().add(button1); - menu.getButtons().add(button2); +// menu.getButtons().add(button2); menu.getButtons().add(button3); WxMenuButton button31 = new WxMenuButton(); diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpQrcodeServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpQrcodeServiceImplTest.java index c61e38339c..216eec77f0 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpQrcodeServiceImplTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpQrcodeServiceImplTest.java @@ -5,6 +5,7 @@ import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.api.test.ApiTestModule; import me.chanjar.weixin.mp.bean.result.WxMpQrCodeTicket; +import org.apache.commons.lang3.RandomStringUtils; import org.testng.*; import org.testng.annotations.*; @@ -26,6 +27,11 @@ public Object[][] sceneIds() { return new Object[][]{{-1}, {0}, {1}, {200000}}; } + @DataProvider + public Object[][] sceneStrs() { + return new Object[][]{{null}, {""}, {"test"}, {RandomStringUtils.randomAlphanumeric(100)}}; + } + @Test(dataProvider = "sceneIds") public void testQrCodeCreateTmpTicket(int sceneId) throws WxErrorException { WxMpQrCodeTicket ticket = this.wxService.getQrcodeService().qrCodeCreateTmpTicket(sceneId, null); @@ -35,6 +41,16 @@ public void testQrCodeCreateTmpTicket(int sceneId) throws WxErrorException { System.out.println(ticket); } + + @Test(dataProvider = "sceneStrs") + public void testQrCodeCreateTmpTicketWithSceneStr(String sceneStr) throws WxErrorException { + WxMpQrCodeTicket ticket = this.wxService.getQrcodeService().qrCodeCreateTmpTicket(sceneStr, null); + Assert.assertNotNull(ticket.getUrl()); + Assert.assertNotNull(ticket.getTicket()); + Assert.assertTrue(ticket.getExpire_seconds() != -1); + System.out.println(ticket); + } + @Test(dataProvider = "sceneIds") public void testQrCodeCreateLastTicket(int sceneId) throws WxErrorException { WxMpQrCodeTicket ticket = this.wxService.getQrcodeService().qrCodeCreateLastTicket(sceneId); diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImplTest.java index d5bf621287..c3f9f69d7b 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImplTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImplTest.java @@ -2,12 +2,16 @@ import com.google.inject.Inject; import me.chanjar.weixin.common.api.WxConsts; +import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.api.test.ApiTestModule; import me.chanjar.weixin.mp.api.test.TestConfigStorage; +import me.chanjar.weixin.mp.bean.result.WxMpCurrentAutoReplyInfo; import org.testng.*; import org.testng.annotations.*; +import static org.testng.Assert.*; + @Test @Guice(modules = ApiTestModule.class) public class WxMpServiceImplTest { @@ -15,6 +19,14 @@ public class WxMpServiceImplTest { @Inject private WxMpService wxService; + @Test + public void testGetCurrentAutoReplyInfo() throws WxErrorException { + WxMpCurrentAutoReplyInfo autoReplyInfo = this.wxService.getCurrentAutoReplyInfo(); + + assertNotNull(autoReplyInfo); + System.out.println(autoReplyInfo); + } + @Test public void testCheckSignature() { Assert.fail("Not yet implemented"); diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpShakeServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpShakeServiceImplTest.java new file mode 100644 index 0000000000..f608cd19b0 --- /dev/null +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpShakeServiceImplTest.java @@ -0,0 +1,31 @@ +package me.chanjar.weixin.mp.api.impl; + +import com.google.inject.Inject; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.api.test.ApiTestModule; +import me.chanjar.weixin.mp.bean.WxMpShakeInfoResult; +import me.chanjar.weixin.mp.bean.WxMpShakeQuery; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +/** + * 测试摇一摇周边相关的接口 + * + * @author rememberber + */ +@Test(groups = "userAPI") +@Guice(modules = ApiTestModule.class) +public class WxMpShakeServiceImplTest { + @Inject + private WxMpService wxService; + + public void testGetShakeInfo() throws Exception { + WxMpShakeQuery wxMpShakeQuery = new WxMpShakeQuery(); + wxMpShakeQuery.setTicket("b87db7df490e5cbe4f598272f77f46be"); + wxMpShakeQuery.setNeedPoi(1); + WxMpShakeInfoResult wxMpShakeInfoResult = this.wxService.getShakeService().getShakeInfo(wxMpShakeQuery); + + System.out.println(); + } + +} diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpStoreServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpStoreServiceImplTest.java index baa34443f2..ed5845088b 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpStoreServiceImplTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpStoreServiceImplTest.java @@ -12,7 +12,7 @@ import java.math.BigDecimal; import java.util.List; -import static org.junit.Assert.assertNotNull; +import static org.testng.AssertJUnit.assertNotNull; /** * @author binarywang(Binary Wang) diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/test/ApiTestModule.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/test/ApiTestModule.java index 2cfaec543c..2a9421a499 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/test/ApiTestModule.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/test/ApiTestModule.java @@ -6,7 +6,8 @@ import me.chanjar.weixin.common.util.xml.XStreamInitializer; import me.chanjar.weixin.mp.api.WxMpConfigStorage; import me.chanjar.weixin.mp.api.WxMpService; -import me.chanjar.weixin.mp.api.impl.apache.WxMpServiceImpl; +import me.chanjar.weixin.mp.api.impl.WxMpServiceApacheHttpClientImpl; +import me.chanjar.weixin.mp.api.impl.WxMpServiceOkHttpImpl; import java.io.IOException; import java.io.InputStream; @@ -19,7 +20,7 @@ public void configure(Binder binder) { try (InputStream is1 = ClassLoader.getSystemResourceAsStream("test-config.xml")) { TestConfigStorage config = this.fromXml(TestConfigStorage.class, is1); config.setAccessTokenLock(new ReentrantLock()); - WxMpService wxService = new WxMpServiceImpl(); + WxMpService wxService = new WxMpServiceOkHttpImpl(); wxService.setWxMpConfigStorage(config); binder.bind(WxMpService.class).toInstance(wxService); diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/membercard/WxMpMemberCardUpdateResultTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/membercard/WxMpMemberCardUpdateResultTest.java new file mode 100644 index 0000000000..f7860e7c85 --- /dev/null +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/membercard/WxMpMemberCardUpdateResultTest.java @@ -0,0 +1,33 @@ +package me.chanjar.weixin.mp.bean.membercard; + +import org.testng.annotations.Test; + +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + +/** + * + * @author YuJian + * @version 2017/7/15 + */ +public class WxMpMemberCardUpdateResultTest { + + @Test + public void testFromJson() throws Exception { + String json = "{\n" + + " \"errcode\": 0,\n" + + " \"errmsg\": \"ok\",\n" + + " \"result_bonus\": 100,\n" + + " \"result_balance\": 200,\n" + + " \"openid\": \"oFS7Fjl0WsZ9AMZqrI80nbIq8xrA\"\n" + + "}"; + + WxMpMemberCardUpdateResult result = WxMpMemberCardUpdateResult.fromJson(json); + + assertNotNull(result); + assertTrue(result.getErrorCode().equalsIgnoreCase("0")); + + System.out.println(result); + } +} diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/membercard/WxMpMemberCardUserInfoResultTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/membercard/WxMpMemberCardUserInfoResultTest.java new file mode 100644 index 0000000000..da8ff3da36 --- /dev/null +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/membercard/WxMpMemberCardUserInfoResultTest.java @@ -0,0 +1,80 @@ +package me.chanjar.weixin.mp.bean.membercard; + +import org.testng.annotations.Test; + +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + +/** + * + * @author YuJian + * @version 2017/7/15 + */ +public class WxMpMemberCardUserInfoResultTest { + + @Test + public void testFromJson() throws Exception { + String json = "{\n" + + " \"errcode\": 0,\n" + + " \"errmsg\": \"ok\",\n" + + " \"openid\": \"obLatjjwDolFj******wNqRXw\",\n" + + " \"nickname\": \"*******\",\n" + + " \"membership_number\": \"658*****445\",\n" + + " \"bonus\": 995,\n" + + " \"sex\": \"MALE\",\n" + + " \"user_info\": {\n" + + " \"common_field_list\": [\n" + + " {\n" + + " \"name\": \"USER_FORM_INFO_FLAG_MOBILE\",\n" + + " \"value\": \"15*****518\"\n" + + " },\n" + + " {\n" + + " \"name\": \"USER_FORM_INFO_FLAG_NAME\",\n" + + " \"value\": \"HK\"\n" + + " },\n" + + " {\n" + + " \"name\": \"USER_FORM_INFO_FLAG_EDUCATION_BACKGROUND\",\n" + + " \"value\": \"研究生\"\n" + + " }\n" + + " ],\n" + + " \"custom_field_list\": [\n" + + " {\n" + + " \"name\": \"兴趣\",\n" + + " \"value\": \"钢琴\",\n" + + " \"value_list\": []\n" + + " },\n" + + " {\n" + + " \"name\": \"喜好\",\n" + + " \"value\": \"郭敬明\",\n" + + " \"value_list\": []\n" + + " },\n" + + " {\n" + + " \"name\": \"职业\",\n" + + " \"value\": \"\",\n" + + " \"value_list\": [\n" + + " \"赛车手\",\n" + + " \"旅行家\"\n" + + " ]\n" + + " }\n" + + " ]\n" + + " },\n" + + " \"user_card_status\": \"NORMAL\",\n" + + " \"has_active\": false\n" + + "}"; + + + WxMpMemberCardUserInfoResult userInfoResult = WxMpMemberCardUserInfoResult.fromJson(json); + + assertNotNull(userInfoResult); + assertFalse(userInfoResult.getHasActive()); + assertTrue(userInfoResult.getSex().equalsIgnoreCase("MALE")); + assertNotNull(userInfoResult.getUserInfo()); + assertNotNull(userInfoResult.getUserInfo().getCommonFieldList()); + assertNotNull(userInfoResult.getUserInfo().getCustomFieldList()); + assertTrue(userInfoResult.getUserInfo().getCommonFieldList().length == 3); + assertTrue(userInfoResult.getUserInfo().getCustomFieldList()[2].getValueList()[0].equalsIgnoreCase("赛车手")); + + System.out.println(userInfoResult); + } +} diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessageTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessageTest.java index bbb8ce83ee..9c2bbf56ac 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessageTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessageTest.java @@ -1,9 +1,10 @@ package me.chanjar.weixin.mp.bean.message; import me.chanjar.weixin.common.api.WxConsts; -import org.testng.*; import org.testng.annotations.*; +import static org.testng.Assert.*; + @Test public class WxMpXmlMessageTest { @@ -54,38 +55,38 @@ public void testFromXml() { + "" + ""; WxMpXmlMessage wxMessage = WxMpXmlMessage.fromXml(xml); - Assert.assertEquals(wxMessage.getToUser(), "toUser"); - Assert.assertEquals(wxMessage.getFromUser(), "fromUser"); - Assert.assertEquals(wxMessage.getCreateTime(), new Long(1348831860L)); - Assert.assertEquals(wxMessage.getMsgType(), WxConsts.XML_MSG_TEXT); - Assert.assertEquals(wxMessage.getContent(), "this is a test"); - Assert.assertEquals(wxMessage.getMsgId(), new Long(1234567890123456L)); - Assert.assertEquals(wxMessage.getPicUrl(), "this is a url"); - Assert.assertEquals(wxMessage.getMediaId(), "media_id"); - Assert.assertEquals(wxMessage.getFormat(), "Format"); - Assert.assertEquals(wxMessage.getThumbMediaId(), "thumb_media_id"); - Assert.assertEquals(wxMessage.getLocationX(), 23.134521d); - Assert.assertEquals(wxMessage.getLocationY(), 113.358803d); - Assert.assertEquals(wxMessage.getScale(), 20d); - Assert.assertEquals(wxMessage.getLabel(), "位置信息"); - Assert.assertEquals(wxMessage.getDescription(), "公众平台官网链接"); - Assert.assertEquals(wxMessage.getUrl(), "url"); - Assert.assertEquals(wxMessage.getTitle(), "公众平台官网链接"); - Assert.assertEquals(wxMessage.getEvent(), "subscribe"); - Assert.assertEquals(wxMessage.getEventKey(), "qrscene_123123"); - Assert.assertEquals(wxMessage.getTicket(), "TICKET"); - Assert.assertEquals(wxMessage.getLatitude(), 23.137466); - Assert.assertEquals(wxMessage.getLongitude(), 113.352425); - Assert.assertEquals(wxMessage.getPrecision(), 119.385040); - Assert.assertEquals(wxMessage.getScanCodeInfo().getScanType(), "qrcode"); - Assert.assertEquals(wxMessage.getScanCodeInfo().getScanResult(), "1"); - Assert.assertEquals(wxMessage.getSendPicsInfo().getCount(), new Long(1L)); - Assert.assertEquals(wxMessage.getSendPicsInfo().getPicList().get(0).getPicMd5Sum(), "1b5f7c23b5bf75682a53e7b6d163e185"); - Assert.assertEquals(wxMessage.getSendLocationInfo().getLocationX(), "23"); - Assert.assertEquals(wxMessage.getSendLocationInfo().getLocationY(), "113"); - Assert.assertEquals(wxMessage.getSendLocationInfo().getScale(), "15"); - Assert.assertEquals(wxMessage.getSendLocationInfo().getLabel(), " 广州市海珠区客村艺苑路 106号"); - Assert.assertEquals(wxMessage.getSendLocationInfo().getPoiname(), "wo de poi"); + assertEquals(wxMessage.getToUser(), "toUser"); + assertEquals(wxMessage.getFromUser(), "fromUser"); + assertEquals(wxMessage.getCreateTime(), new Long(1348831860L)); + assertEquals(wxMessage.getMsgType(), WxConsts.XML_MSG_TEXT); + assertEquals(wxMessage.getContent(), "this is a test"); + assertEquals(wxMessage.getMsgId(), new Long(1234567890123456L)); + assertEquals(wxMessage.getPicUrl(), "this is a url"); + assertEquals(wxMessage.getMediaId(), "media_id"); + assertEquals(wxMessage.getFormat(), "Format"); + assertEquals(wxMessage.getThumbMediaId(), "thumb_media_id"); + assertEquals(wxMessage.getLocationX(), 23.134521d); + assertEquals(wxMessage.getLocationY(), 113.358803d); + assertEquals(wxMessage.getScale(), 20d); + assertEquals(wxMessage.getLabel(), "位置信息"); + assertEquals(wxMessage.getDescription(), "公众平台官网链接"); + assertEquals(wxMessage.getUrl(), "url"); + assertEquals(wxMessage.getTitle(), "公众平台官网链接"); + assertEquals(wxMessage.getEvent(), "subscribe"); + assertEquals(wxMessage.getEventKey(), "qrscene_123123"); + assertEquals(wxMessage.getTicket(), "TICKET"); + assertEquals(wxMessage.getLatitude(), 23.137466); + assertEquals(wxMessage.getLongitude(), 113.352425); + assertEquals(wxMessage.getPrecision(), 119.385040); + assertEquals(wxMessage.getScanCodeInfo().getScanType(), "qrcode"); + assertEquals(wxMessage.getScanCodeInfo().getScanResult(), "1"); + assertEquals(wxMessage.getSendPicsInfo().getCount(), new Long(1L)); + assertEquals(wxMessage.getSendPicsInfo().getPicList().get(0).getPicMd5Sum(), "1b5f7c23b5bf75682a53e7b6d163e185"); + assertEquals(wxMessage.getSendLocationInfo().getLocationX(), "23"); + assertEquals(wxMessage.getSendLocationInfo().getLocationY(), "113"); + assertEquals(wxMessage.getSendLocationInfo().getScale(), "15"); + assertEquals(wxMessage.getSendLocationInfo().getLabel(), " 广州市海珠区客村艺苑路 106号"); + assertEquals(wxMessage.getSendLocationInfo().getPoiname(), "wo de poi"); } public void testFromXml2() { @@ -135,38 +136,38 @@ public void testFromXml2() { + "" + ""; WxMpXmlMessage wxMessage = WxMpXmlMessage.fromXml(xml); - Assert.assertEquals(wxMessage.getToUser(), "toUser"); - Assert.assertEquals(wxMessage.getFromUser(), "fromUser"); - Assert.assertEquals(wxMessage.getCreateTime(), new Long(1348831860L)); - Assert.assertEquals(wxMessage.getMsgType(), WxConsts.XML_MSG_TEXT); - Assert.assertEquals(wxMessage.getContent(), "this is a test"); - Assert.assertEquals(wxMessage.getMsgId(), new Long(1234567890123456L)); - Assert.assertEquals(wxMessage.getPicUrl(), "this is a url"); - Assert.assertEquals(wxMessage.getMediaId(), "media_id"); - Assert.assertEquals(wxMessage.getFormat(), "Format"); - Assert.assertEquals(wxMessage.getThumbMediaId(), "thumb_media_id"); - Assert.assertEquals(wxMessage.getLocationX(), 23.134521d); - Assert.assertEquals(wxMessage.getLocationY(), 113.358803d); - Assert.assertEquals(wxMessage.getScale(), 20d); - Assert.assertEquals(wxMessage.getLabel(), "位置信息"); - Assert.assertEquals(wxMessage.getDescription(), "公众平台官网链接"); - Assert.assertEquals(wxMessage.getUrl(), "url"); - Assert.assertEquals(wxMessage.getTitle(), "公众平台官网链接"); - Assert.assertEquals(wxMessage.getEvent(), "subscribe"); - Assert.assertEquals(wxMessage.getEventKey(), "qrscene_123123"); - Assert.assertEquals(wxMessage.getTicket(), "TICKET"); - Assert.assertEquals(wxMessage.getLatitude(), 23.137466); - Assert.assertEquals(wxMessage.getLongitude(), 113.352425); - Assert.assertEquals(wxMessage.getPrecision(), 119.385040); - Assert.assertEquals(wxMessage.getScanCodeInfo().getScanType(), "qrcode"); - Assert.assertEquals(wxMessage.getScanCodeInfo().getScanResult(), "1"); - Assert.assertEquals(wxMessage.getSendPicsInfo().getCount(), new Long(1L)); - Assert.assertEquals(wxMessage.getSendPicsInfo().getPicList().get(0).getPicMd5Sum(), "1b5f7c23b5bf75682a53e7b6d163e185"); - Assert.assertEquals(wxMessage.getSendLocationInfo().getLocationX(), "23"); - Assert.assertEquals(wxMessage.getSendLocationInfo().getLocationY(), "113"); - Assert.assertEquals(wxMessage.getSendLocationInfo().getScale(), "15"); - Assert.assertEquals(wxMessage.getSendLocationInfo().getLabel(), " 广州市海珠区客村艺苑路 106号"); - Assert.assertEquals(wxMessage.getSendLocationInfo().getPoiname(), "wo de poi"); + assertEquals(wxMessage.getToUser(), "toUser"); + assertEquals(wxMessage.getFromUser(), "fromUser"); + assertEquals(wxMessage.getCreateTime(), new Long(1348831860L)); + assertEquals(wxMessage.getMsgType(), WxConsts.XML_MSG_TEXT); + assertEquals(wxMessage.getContent(), "this is a test"); + assertEquals(wxMessage.getMsgId(), new Long(1234567890123456L)); + assertEquals(wxMessage.getPicUrl(), "this is a url"); + assertEquals(wxMessage.getMediaId(), "media_id"); + assertEquals(wxMessage.getFormat(), "Format"); + assertEquals(wxMessage.getThumbMediaId(), "thumb_media_id"); + assertEquals(wxMessage.getLocationX(), 23.134521d); + assertEquals(wxMessage.getLocationY(), 113.358803d); + assertEquals(wxMessage.getScale(), 20d); + assertEquals(wxMessage.getLabel(), "位置信息"); + assertEquals(wxMessage.getDescription(), "公众平台官网链接"); + assertEquals(wxMessage.getUrl(), "url"); + assertEquals(wxMessage.getTitle(), "公众平台官网链接"); + assertEquals(wxMessage.getEvent(), "subscribe"); + assertEquals(wxMessage.getEventKey(), "qrscene_123123"); + assertEquals(wxMessage.getTicket(), "TICKET"); + assertEquals(wxMessage.getLatitude(), 23.137466); + assertEquals(wxMessage.getLongitude(), 113.352425); + assertEquals(wxMessage.getPrecision(), 119.385040); + assertEquals(wxMessage.getScanCodeInfo().getScanType(), "qrcode"); + assertEquals(wxMessage.getScanCodeInfo().getScanResult(), "1"); + assertEquals(wxMessage.getSendPicsInfo().getCount(), new Long(1L)); + assertEquals(wxMessage.getSendPicsInfo().getPicList().get(0).getPicMd5Sum(), "1b5f7c23b5bf75682a53e7b6d163e185"); + assertEquals(wxMessage.getSendLocationInfo().getLocationX(), "23"); + assertEquals(wxMessage.getSendLocationInfo().getLocationY(), "113"); + assertEquals(wxMessage.getSendLocationInfo().getScale(), "15"); + assertEquals(wxMessage.getSendLocationInfo().getLabel(), " 广州市海珠区客村艺苑路 106号"); + assertEquals(wxMessage.getSendLocationInfo().getPoiname(), "wo de poi"); } } diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutNewsMessageTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutNewsMessageTest.java index c9a1f98264..f8f72c9e5e 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutNewsMessageTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutNewsMessageTest.java @@ -54,15 +54,13 @@ public void testBuild() { WxMpXmlOutNewsMessage m = WxMpXmlOutMessage.NEWS() .fromUser("fromUser") .toUser("toUser") - .addArticle(item) - .addArticle(item) + .addArticle(item,item) .build(); String expected = "" + "" + "" + "1122" + "" - + " 2" + " " + " " + " <![CDATA[title]]>" @@ -77,6 +75,7 @@ public void testBuild() { + " " + " " + " " + + " 2" + ""; System.out.println(m.toXml()); Assert.assertEquals( diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/result/WxMpCurrentAutoReplyInfoTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/result/WxMpCurrentAutoReplyInfoTest.java new file mode 100644 index 0000000000..da2cddff40 --- /dev/null +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/result/WxMpCurrentAutoReplyInfoTest.java @@ -0,0 +1,157 @@ +package me.chanjar.weixin.mp.bean.result; + +import org.testng.annotations.*; + +import static org.testng.Assert.*; + +/** + *
+ * Created by Binary Wang on 2017-7-8.
+ * @author Binary Wang
+ * 
+ */ +public class WxMpCurrentAutoReplyInfoTest { + @Test + public void testFromJson() throws Exception { + String json = "{ \n" + + " \"is_add_friend_reply_open\": 1, \n" + + " \"is_autoreply_open\": 1, \n" + + " \"add_friend_autoreply_info\": { \n" + + " \"type\": \"text\", \n" + + " \"content\": \"Thanks for your attention!\"\n" + + " }, \n" + + " \"message_default_autoreply_info\": { \n" + + " \"type\": \"text\", \n" + + " \"content\": \"Hello, this is autoreply!\"\n" + + " }, \n" + + " \"keyword_autoreply_info\": { \n" + + " \"list\": [ \n" + + " { \n" + + " \"rule_name\": \"autoreply-news\", \n" + + " \"create_time\": 1423028166, \n" + + " \"reply_mode\": \"reply_all\", \n" + + " \"keyword_list_info\": [ \n" + + " { \n" + + " \"type\": \"text\", \n" + + " \"match_mode\": \"contain\", \n" + + " \"content\": \"news测试\"//此处content即为关键词内容\n" + + " }\n" + + " ], \n" + + " \"reply_list_info\": [ \n" + + " { \n" + + " \"type\": \"news\", \n" + + " \"news_info\": { \n" + + " \"list\": [ \n" + + " { \n" + + " \"title\": \"it's news\", \n" + + " \"author\": \"jim\", \n" + + " \"digest\": \"it's digest\", \n" + + " \"show_cover\": 1, \"cover_url\": \"http://mmbiz.qpic.cn/mmbiz/GE7et87vE9vicuCibqXsX9GPPLuEtBfXfKbE8sWdt2DDcL0dMfQWJWTVn1N8DxI0gcRmrtqBOuwQH\n" + + " euPKmFLK0ZQ/0\", \n" + + " \"content_url\": \"http://mp.weixin.qq.com/s?__biz=MjM5ODUwNTM3Ng==&mid=203929886&idx=1&sn=628f964cf0c6d84c026881b6959aea8b#rd\", \n" + + " \"source_url\": \"http://www.url.com\"\n" + + " }\n" + + " ]\n" + + " }\n" + + " }, \n" + + " { \n" + + " \"type\": \"news\",\n" + + " \"content\":\"KQb_w_Tiz-nSdVLoTV35Psmty8hGBulGhEdbb9SKs-o\", \n" + + " \"news_info\": { \n" + + " \"list\": [ \n" + + " { \n" + + " \"title\": \"MULTI_NEWS\", \n" + + " \"author\": \"JIMZHENG\", \n" + + " \"digest\": \"text\", \n" + + " \"show_cover\": 0, \n" + + " \"cover_url\": \"http://mmbiz.qpic.cn/mmbiz/GE7et87vE9vicuCibqXsX9GPPLuEtBfXfK0HKuBIa1A1cypS0uY1wickv70iaY1gf3I1DTszuJoS3lAVLv\n" + + "hTcm9sDA/0\", \n" + + " \"content_url\": \"http://mp.weixin.qq.com/s?__biz=MjM5ODUwNTM3Ng==&mid=204013432&idx=1&sn=80ce6d9abcb832237bf86c87e50fda15#rd\", \n" + + " \"source_url\": \"\"\n" + + " },\n" + + " { \n" + + " \"title\": \"MULTI_NEWS4\", \n" + + " \"author\": \"JIMZHENG\", \n" + + " \"digest\": \"MULTI_NEWSMULTI_NEWSMULTI_NEWSMULTI_NEWSMULTI_NEWSMULT\", \n" + + " \"show_cover\": 1, \n" + + "\"cover_url\": \"http://mmbiz.qpic.cn/mmbiz/GE7et87vE9vicuCibqXsX9GPPLuEtBfXfKbE8sWdt2DDcL0dMfQWJWTVn1N8DxI0gcRmrtqBOuwQ\n" + + "HeuPKmFLK0ZQ/0\", \n" + + " \"content_url\": \"http://mp.weixin.qq.com/s?__biz=MjM5ODUwNTM3Ng==&mid=204013432&idx=5&sn=b4ef73a915e7c2265e437096582774af#rd\", \n" + + " \"source_url\": \"\"\n" + + " }\n" + + " ]\n" + + " }\n" + + " }\n" + + " ]\n" + + " }, \n" + + " { \n" + + " \"rule_name\": \"autoreply-voice\", \n" + + " \"create_time\": 1423027971, \n" + + " \"reply_mode\": \"random_one\", \n" + + " \"keyword_list_info\": [ \n" + + " { \n" + + " \"type\": \"text\", \n" + + " \"match_mode\": \"contain\", \n" + + " \"content\": \"voice测试\"\n" + + " }\n" + + " ], \n" + + " \"reply_list_info\": [ \n" + + " { \n" + + " \"type\": \"voice\", \n" + + " \"content\": \"NESsxgHEvAcg3egJTtYj4uG1PTL6iPhratdWKDLAXYErhN6oEEfMdVyblWtBY5vp\"\n" + + " }\n" + + " ]\n" + + " }, \n" + + " { \n" + + " \"rule_name\": \"autoreply-text\", \n" + + " \"create_time\": 1423027926, \n" + + " \"reply_mode\": \"random_one\", \n" + + " \"keyword_list_info\": [ \n" + + " { \n" + + " \"type\": \"text\", \n" + + " \"match_mode\": \"contain\", \n" + + " \"content\": \"text测试\"\n" + + " }\n" + + " ], \n" + + " \"reply_list_info\": [ \n" + + " { \n" + + " \"type\": \"text\", \n" + + " \"content\": \"hello!text!\"\n" + + " }\n" + + " ]\n" + + " }, \n" + + " { \n" + + " \"rule_name\": \"autoreply-video\", \n" + + " \"create_time\": 1423027801, \n" + + " \"reply_mode\": \"random_one\", \n" + + " \"keyword_list_info\": [ \n" + + " { \n" + + " \"type\": \"text\", \n" + + " \"match_mode\": \"equal\", \n" + + " \"content\": \"video测试\"\n" + + " }\n" + + " ], \n" + + " \"reply_list_info\": [ \n" + + " { \n" + + " \"type\": \"video\", \n" + + "\"content\": \"http://61.182.133.153/vweixinp.tc.qq.com/1007_114bcede9a2244eeb5ab7f76d951df5f.f10.mp4?vkey=7183E5C952B16C3AB1991BA8138673DE1037CB82A29801A504B64A77F691BF9DF7AD054A9B7FE683&sha=0&save=1\"\n" + + " }\n" + + " ]\n" + + " }\n" + + " ]\n" + + " }\n" + + "}"; + + WxMpCurrentAutoReplyInfo autoReplyInfo = WxMpCurrentAutoReplyInfo.fromJson(json); + + assertNotNull(autoReplyInfo); + assertTrue(autoReplyInfo.getAddFriendReplyOpen()); + assertTrue(autoReplyInfo.getAutoReplyOpen()); + assertNotNull(autoReplyInfo.getAddFriendAutoReplyInfo()); + assertNotNull(autoReplyInfo.getMessageDefaultAutoReplyInfo()); + assertTrue(autoReplyInfo.getKeywordAutoReplyInfo().getList().size() > 0); + + System.out.println(autoReplyInfo); + } + +} diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpDemoServer.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpDemoServer.java index e2c75f83d6..3e16042d7f 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpDemoServer.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpDemoServer.java @@ -5,7 +5,7 @@ import me.chanjar.weixin.mp.api.WxMpMessageHandler; import me.chanjar.weixin.mp.api.WxMpMessageRouter; import me.chanjar.weixin.mp.api.WxMpService; -import me.chanjar.weixin.mp.api.impl.apache.WxMpServiceImpl; +import me.chanjar.weixin.mp.api.impl.WxMpServiceApacheHttpClientImpl; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletHandler; import org.eclipse.jetty.servlet.ServletHolder; @@ -47,7 +47,7 @@ private static void initWeixin() { .fromXml(is1); wxMpConfigStorage = config; - wxMpService = new WxMpServiceImpl(); + wxMpService = new WxMpServiceApacheHttpClientImpl(); wxMpService.setWxMpConfigStorage(config); WxMpMessageHandler logHandler = new DemoLogHandler(); diff --git a/weixin-java-mp/src/test/resources/logback-test.xml b/weixin-java-mp/src/test/resources/logback-test.xml index 9be51c9a5d..e4a33acd88 100644 --- a/weixin-java-mp/src/test/resources/logback-test.xml +++ b/weixin-java-mp/src/test/resources/logback-test.xml @@ -1,16 +1,13 @@ - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + %d{HH:mm:ss.SSS} [%thread] %-5level %replace(%caller{1}){'Caller', ''} - %msg%n - + - diff --git a/weixin-java-mp/src/test/resources/testng.xml b/weixin-java-mp/src/test/resources/testng.xml index d864635569..ea264e6717 100644 --- a/weixin-java-mp/src/test/resources/testng.xml +++ b/weixin-java-mp/src/test/resources/testng.xml @@ -5,7 +5,7 @@ - + diff --git a/weixin-java-osgi/pom.xml b/weixin-java-osgi/pom.xml index 43874695de..91c84d24f3 100644 --- a/weixin-java-osgi/pom.xml +++ b/weixin-java-osgi/pom.xml @@ -6,7 +6,7 @@ com.github.binarywang weixin-java-parent - 2.5.2.BETA + 2.6.0 weixin-java-osgi diff --git a/weixin-java-pay/build.gradle b/weixin-java-pay/build.gradle deleted file mode 100644 index 2f0c7e9a87..0000000000 --- a/weixin-java-pay/build.gradle +++ /dev/null @@ -1,12 +0,0 @@ -description = 'WeiXin Java Tools - PAY' -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.RC0' - testCompile group: 'org.eclipse.jetty', name: 'jetty-servlet', version: '9.3.0.RC0' - testCompile group: 'joda-time', name: 'joda-time', version: '2.9.4' -} -test.useTestNG() diff --git a/weixin-java-pay/pom.xml b/weixin-java-pay/pom.xml index 989fd9fac3..74704e6a87 100644 --- a/weixin-java-pay/pom.xml +++ b/weixin-java-pay/pom.xml @@ -5,7 +5,7 @@ weixin-java-parent com.github.binarywang - 2.6.1.BETA + 2.7.8.BETA 4.0.0 @@ -22,9 +22,19 @@ com.github.binarywang qrcode-utils - 1.1 + + org.jodd + jodd-http + provided + + + + ch.qos.logback + logback-classic + test + org.testng testng diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/WxPayOrderNotifyCoupon.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/WxPayOrderNotifyCoupon.java index c72ddcb222..c2b4e22e74 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/WxPayOrderNotifyCoupon.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/WxPayOrderNotifyCoupon.java @@ -1,6 +1,5 @@ package com.github.binarywang.wxpay.bean; -import com.thoughtworks.xstream.annotations.XStreamAlias; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; @@ -12,16 +11,10 @@ * 支付异步通知代金券详细 */ public class WxPayOrderNotifyCoupon implements Serializable { - /** - * @fields serialVersionUID - */ private static final long serialVersionUID = -4165343733538156097L; - @XStreamAlias("coupon_id") private String couponId; - @XStreamAlias("coupon_type") private String couponType; - @XStreamAlias("coupon_fee") private Integer couponFee; public String getCouponId() { diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponInfoQueryRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponInfoQueryRequest.java new file mode 100644 index 0000000000..3d83d0a6c6 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponInfoQueryRequest.java @@ -0,0 +1,280 @@ +package com.github.binarywang.wxpay.bean.coupon; + +import com.github.binarywang.wxpay.bean.request.WxPayBaseRequest; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import me.chanjar.weixin.common.annotation.Required; + +/** + *
+ * 查询代金券信息请求对象类
+ * Created by Binary Wang on 2017-7-15.
+ * 
+ * + * @author Binary Wang + */ +@XStreamAlias("xml") +public class WxPayCouponInfoQueryRequest extends WxPayBaseRequest { + /** + *
+   * 字段名:代金券id
+   * 变量名:coupon_id
+   * 是否必填:是
+   * 示例值:1757
+   * 类型:String
+   * 说明:代金券id
+   * 
+ */ + @Required + @XStreamAlias("coupon_id") + private String couponId; + + /** + *
+   * 字段名:代金券批次号
+   * 变量名:stock_id
+   * 是否必填:是
+   * 示例值:58818
+   * 类型:String
+   * 说明:代金劵对应的批次号
+   * 
+ */ + @Required + @XStreamAlias("stock_id") + private String stockId; + + /** + *
+   * 字段名:用户openid
+   * 变量名:openid
+   * 是否必填:是
+   * 示例值:onqOjjrXT-776SpHnfexGm1_P7iE
+   * 类型:String
+   * 说明:Openid信息,用户在appid下的openid。
+   * 
+ */ + @Required + @XStreamAlias("openid") + private String openid; + + /** + *
+   * 字段名:操作员
+   * 变量名:op_user_id
+   * 是否必填:否
+   * 示例值:10000098
+   * 类型:String(32)
+   * 说明:操作员帐号, 默认为商户号,可在商户平台配置操作员对应的api权限
+   * 
+ */ + @XStreamAlias("op_user_id") + private String opUserId; + + /** + *
+   * 字段名:设备号
+   * 变量名:device_info
+   * 是否必填:否
+   * 示例值:
+   * 类型:String(32)
+   * 说明:微信支付分配的终端设备号
+   * 
+ */ + @XStreamAlias("device_info") + private String deviceInfo; + + /** + *
+   * 字段名:协议版本
+   * 变量名:version
+   * 是否必填:否
+   * 示例值:1.0
+   * 类型:String(32)
+   * 说明:默认1.0
+   * 
+ */ + @XStreamAlias("version") + private String version; + + /** + *
+   * 字段名:协议类型
+   * 变量名:type
+   * 是否必填:否
+   * 示例值:XML
+   * 类型:String(32)
+   * 说明:XML【目前仅支持默认XML】
+   * 
+ */ + @XStreamAlias("type") + private String type; + + private WxPayCouponInfoQueryRequest(Builder builder) { + setAppid(builder.appid); + setMchId(builder.mchId); + setSubAppId(builder.subAppId); + setSubMchId(builder.subMchId); + setNonceStr(builder.nonceStr); + setSign(builder.sign); + setCouponId(builder.couponId); + setStockId(builder.stockId); + setOpenid(builder.openid); + setOpUserId(builder.opUserId); + setDeviceInfo(builder.deviceInfo); + setVersion(builder.version); + setType(builder.type); + } + + public static Builder newBuilder() { + return new Builder(); + } + + public String getCouponId() { + return this.couponId; + } + + public void setCouponId(String couponId) { + this.couponId = couponId; + } + + public String getStockId() { + return this.stockId; + } + + public void setStockId(String stockId) { + this.stockId = stockId; + } + + public String getOpenid() { + return this.openid; + } + + public void setOpenid(String openid) { + this.openid = openid; + } + + public String getOpUserId() { + return this.opUserId; + } + + public void setOpUserId(String opUserId) { + this.opUserId = opUserId; + } + + public String getDeviceInfo() { + return this.deviceInfo; + } + + public void setDeviceInfo(String deviceInfo) { + this.deviceInfo = deviceInfo; + } + + public String getVersion() { + return this.version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getType() { + return this.type; + } + + public void setType(String type) { + this.type = type; + } + + @Override + protected void checkConstraints() { + //do nothing + } + + + public static final class Builder { + private String appid; + private String mchId; + private String subAppId; + private String subMchId; + private String nonceStr; + private String sign; + private String couponId; + private String stockId; + private String openid; + private String opUserId; + private String deviceInfo; + private String version; + private String type; + + private Builder() { + } + + public Builder appid(String appid) { + this.appid = appid; + return this; + } + + public Builder mchId(String mchId) { + this.mchId = mchId; + return this; + } + + public Builder subAppId(String subAppId) { + this.subAppId = subAppId; + return this; + } + + public Builder subMchId(String subMchId) { + this.subMchId = subMchId; + return this; + } + + public Builder nonceStr(String nonceStr) { + this.nonceStr = nonceStr; + return this; + } + + public Builder sign(String sign) { + this.sign = sign; + return this; + } + + public Builder couponId(String couponId) { + this.couponId = couponId; + return this; + } + + public Builder stockId(String stockId) { + this.stockId = stockId; + return this; + } + + public Builder openid(String openid) { + this.openid = openid; + return this; + } + + public Builder opUserId(String opUserId) { + this.opUserId = opUserId; + return this; + } + + public Builder deviceInfo(String deviceInfo) { + this.deviceInfo = deviceInfo; + return this; + } + + public Builder version(String version) { + this.version = version; + return this; + } + + public Builder type(String type) { + this.type = type; + return this; + } + + public WxPayCouponInfoQueryRequest build() { + return new WxPayCouponInfoQueryRequest(this); + } + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponInfoQueryResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponInfoQueryResult.java new file mode 100644 index 0000000000..bec1c9e10a --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponInfoQueryResult.java @@ -0,0 +1,351 @@ +package com.github.binarywang.wxpay.bean.coupon; + +import com.github.binarywang.wxpay.bean.result.WxPayBaseResult; +import com.thoughtworks.xstream.annotations.XStreamAlias; + +/** + *
+ * 查询代金券信息响应结果类
+ * Created by Binary Wang on 2017-7-15.
+ * 
+ * + * @author Binary Wang + */ +@XStreamAlias("xml") +public class WxPayCouponInfoQueryResult extends WxPayBaseResult { + /** + *
+   * 字段名:设备号
+   * 变量名:device_info
+   * 是否必填:否
+   * 示例值:123456sb
+   * 类型:String(32)
+   * 说明:微信支付分配的终端设备号,
+   * 
+ */ + @XStreamAlias("device_info") + private String deviceInfo; + + /** + *
+   * 字段名:批次ID
+   * 变量名:coupon_stock_id
+   * 是否必填:是
+   * 示例值:1567
+   * 类型:String
+   * 说明:代金券批次Id
+   * 
+ */ + @XStreamAlias("coupon_stock_id") + private String couponStockId; + + /** + *
+   * 字段名:代金券id
+   * 变量名:coupon_id
+   * 是否必填:是
+   * 示例值:4242
+   * 类型:String
+   * 说明:代金券id
+   * 
+ */ + @XStreamAlias("coupon_id") + private String couponId; + + /** + *
+   * 字段名:代金券面额
+   * 变量名:coupon_value
+   * 是否必填:是
+   * 示例值:4
+   * 类型:Unsinged int
+   * 说明:代金券面值,单位是分
+   * 
+ */ + @XStreamAlias("coupon_value") + private Integer couponValue; + + /** + *
+   * 字段名:代金券使用门槛
+   * 变量名:coupon_mininum
+   * 是否必填:是
+   * 示例值:10
+   * 类型:Unsinged int
+   * 说明:代金券使用最低限额,单位是分
+   * 
+ */ + @XStreamAlias("coupon_mininum") + private Integer couponMininum; + + /** + *
+   * 字段名:代金券名称
+   * 变量名:coupon_name
+   * 是否必填:是
+   * 示例值:测试代金券
+   * 类型:String
+   * 说明:代金券名称
+   * 
+ */ + @XStreamAlias("coupon_name") + private String couponName; + + /** + *
+   * 字段名:代金券状态
+   * 变量名:coupon_state
+   * 是否必填:是
+   * 示例值:SENDED
+   * 类型:int
+   * 说明:代金券状态:SENDED-可用,USED-已实扣,EXPIRED-已过期
+   * 
+ */ + @XStreamAlias("coupon_state") + private Integer couponState; + + /** + *
+   * 字段名:代金券描述
+   * 变量名:coupon_desc
+   * 是否必填:是
+   * 示例值:微信支付-代金券
+   * 类型:String
+   * 说明:代金券描述
+   * 
+ */ + @XStreamAlias("coupon_desc") + private String couponDesc; + + /** + *
+   * 字段名:实际优惠金额
+   * 变量名:coupon_use_value
+   * 是否必填:是
+   * 示例值:0
+   * 类型:Unsinged int
+   * 说明:代金券实际使用金额
+   * 
+ */ + @XStreamAlias("coupon_use_value") + private Integer couponUseValue; + + /** + *
+   * 字段名:优惠剩余可用额
+   * 变量名:coupon_remain_value
+   * 是否必填:是
+   * 示例值:4
+   * 类型:Unsinged int
+   * 说明:代金券剩余金额:部分使用情况下,可能会存在券剩余金额
+   * 
+ */ + @XStreamAlias("coupon_remain_value") + private Integer couponRemainValue; + + /** + *
+   * 字段名:生效开始时间
+   * 变量名:begin_time
+   * 是否必填:是
+   * 示例值:1943787483
+   * 类型:String
+   * 说明:格式为时间戳
+   * 
+ */ + @XStreamAlias("begin_time") + private String beginTime; + + /** + *
+   * 字段名:生效结束时间
+   * 变量名:end_time
+   * 是否必填:是
+   * 示例值:1943787484
+   * 类型:String
+   * 说明:格式为时间戳
+   * 
+ */ + @XStreamAlias("end_time") + private String endTime; + + /** + *
+   * 字段名:发放时间
+   * 变量名:send_time
+   * 是否必填:是
+   * 示例值:1943787420
+   * 类型:String
+   * 说明:格式为时间戳
+   * 
+ */ + @XStreamAlias("send_time") + private String sendTime; + + /** + *
+   * 字段名:消耗方商户id
+   * 变量名:consumer_mch_id
+   * 是否必填:否
+   * 示例值:10000098
+   * 类型:String
+   * 说明:代金券使用后,消耗方商户id
+   * 
+ */ + @XStreamAlias("consumer_mch_id") + private String consumerMchId; + + /** + *
+   * 字段名:发放来源
+   * 变量名:send_source
+   * 是否必填:是
+   * 示例值:FULL_SEND
+   * 类型:String
+   * 说明:代金券发放来源:FULL_SEND-满送 NORMAL-普通发放场景
+   * 
+ */ + @XStreamAlias("send_source") + private String sendSource; + + /** + *
+   * 字段名:是否允许部分使用
+   * 变量名:is_partial_use
+   * 是否必填:否
+   * 示例值:1
+   * 类型:String
+   * 说明:该代金券是否允许部分使用标识:1-表示支持部分使用
+   * 
+ */ + @XStreamAlias("is_partial_use") + private String isPartialUse; + + public String getDeviceInfo() { + return this.deviceInfo; + } + + public void setDeviceInfo(String deviceInfo) { + this.deviceInfo = deviceInfo; + } + + public String getCouponStockId() { + return this.couponStockId; + } + + public void setCouponStockId(String couponStockId) { + this.couponStockId = couponStockId; + } + + public String getCouponId() { + return this.couponId; + } + + public void setCouponId(String couponId) { + this.couponId = couponId; + } + + public Integer getCouponValue() { + return this.couponValue; + } + + public void setCouponValue(Integer couponValue) { + this.couponValue = couponValue; + } + + public Integer getCouponMininum() { + return this.couponMininum; + } + + public void setCouponMininum(Integer couponMininum) { + this.couponMininum = couponMininum; + } + + public String getCouponName() { + return this.couponName; + } + + public void setCouponName(String couponName) { + this.couponName = couponName; + } + + public Integer getCouponState() { + return this.couponState; + } + + public void setCouponState(Integer couponState) { + this.couponState = couponState; + } + + public String getCouponDesc() { + return this.couponDesc; + } + + public void setCouponDesc(String couponDesc) { + this.couponDesc = couponDesc; + } + + public Integer getCouponUseValue() { + return this.couponUseValue; + } + + public void setCouponUseValue(Integer couponUseValue) { + this.couponUseValue = couponUseValue; + } + + public Integer getCouponRemainValue() { + return this.couponRemainValue; + } + + public void setCouponRemainValue(Integer couponRemainValue) { + this.couponRemainValue = couponRemainValue; + } + + public String getBeginTime() { + return this.beginTime; + } + + public void setBeginTime(String beginTime) { + this.beginTime = beginTime; + } + + public String getEndTime() { + return this.endTime; + } + + public void setEndTime(String endTime) { + this.endTime = endTime; + } + + public String getSendTime() { + return this.sendTime; + } + + public void setSendTime(String sendTime) { + this.sendTime = sendTime; + } + + public String getConsumerMchId() { + return this.consumerMchId; + } + + public void setConsumerMchId(String consumerMchId) { + this.consumerMchId = consumerMchId; + } + + public String getSendSource() { + return this.sendSource; + } + + public void setSendSource(String sendSource) { + this.sendSource = sendSource; + } + + public String getIsPartialUse() { + return this.isPartialUse; + } + + public void setIsPartialUse(String isPartialUse) { + this.isPartialUse = isPartialUse; + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponSendRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponSendRequest.java new file mode 100644 index 0000000000..9a98fd64a2 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponSendRequest.java @@ -0,0 +1,311 @@ +package com.github.binarywang.wxpay.bean.coupon; + +import com.github.binarywang.wxpay.bean.request.WxPayBaseRequest; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import me.chanjar.weixin.common.annotation.Required; + +/** + *
+ * 发送代金券请求对象类
+ * Created by Binary Wang on 2017-7-15.
+ * 
+ * + * @author Binary Wang + */ +@XStreamAlias("xml") +public class WxPayCouponSendRequest extends WxPayBaseRequest { + /** + *
+   * 字段名:代金券批次id
+   * 变量名:coupon_stock_id
+   * 是否必填:是
+   * 示例值:1757
+   * 类型:String
+   * 说明:代金券批次id
+   * 
+ */ + @Required + @XStreamAlias("coupon_stock_id") + private String couponStockId; + + /** + *
+   * 字段名:openid记录数
+   * 变量名:openid_count
+   * 是否必填:是
+   * 示例值:1
+   * 类型:int
+   * 说明:openid记录数(目前支持num=1)
+   * 
+ */ + @Required + @XStreamAlias("openid_count") + private Integer openidCount; + + /** + *
+   * 字段名:商户单据号
+   * 变量名:partner_trade_no
+   * 是否必填:是
+   * 示例值:1000009820141203515766
+   * 类型:String
+   * 说明:商户此次发放凭据号(格式:商户id+日期+流水号),商户侧需保持唯一性
+   * 
+ */ + @Required + @XStreamAlias("partner_trade_no") + private String partnerTradeNo; + + /** + *
+   * 字段名:用户openid
+   * 变量名:openid
+   * 是否必填:是
+   * 示例值:onqOjjrXT-776SpHnfexGm1_P7iE
+   * 类型:String
+   * 说明:Openid信息,用户在appid下的openid。
+   * 
+ */ + @Required + @XStreamAlias("openid") + private String openid; + + /** + *
+   * 字段名:操作员
+   * 变量名:op_user_id
+   * 是否必填:否
+   * 示例值:10000098
+   * 类型:String(32)
+   * 说明:操作员帐号, 默认为商户号,可在商户平台配置操作员对应的api权限
+   * 
+ */ + @XStreamAlias("op_user_id") + private String opUserId; + + /** + *
+   * 字段名:设备号
+   * 变量名:device_info
+   * 是否必填:否
+   * 示例值:
+   * 类型:String(32)
+   * 说明:微信支付分配的终端设备号
+   * 
+ */ + @XStreamAlias("device_info") + private String deviceInfo; + + /** + *
+   * 字段名:协议版本
+   * 变量名:version
+   * 是否必填:否
+   * 示例值:1.0
+   * 类型:String(32)
+   * 说明:默认1.0
+   * 
+ */ + @XStreamAlias("version") + private String version; + + /** + *
+   * 字段名:协议类型
+   * 变量名:type
+   * 是否必填:否
+   * 示例值:XML
+   * 类型:String(32)
+   * 说明:XML【目前仅支持默认XML】
+   * 
+ */ + @XStreamAlias("type") + private String type; + + public WxPayCouponSendRequest() { + } + + private WxPayCouponSendRequest(Builder builder) { + setAppid(builder.appid); + setMchId(builder.mchId); + setSubAppId(builder.subAppId); + setSubMchId(builder.subMchId); + setNonceStr(builder.nonceStr); + setSign(builder.sign); + setCouponStockId(builder.couponStockId); + setOpenidCount(builder.openidCount); + setPartnerTradeNo(builder.partnerTradeNo); + setOpenid(builder.openid); + setOpUserId(builder.opUserId); + setDeviceInfo(builder.deviceInfo); + setVersion(builder.version); + setType(builder.type); + } + + public static Builder newBuilder() { + return new Builder(); + } + + public String getCouponStockId() { + return this.couponStockId; + } + + public void setCouponStockId(String couponStockId) { + this.couponStockId = couponStockId; + } + + public Integer getOpenidCount() { + return this.openidCount; + } + + public void setOpenidCount(Integer openidCount) { + this.openidCount = openidCount; + } + + public String getPartnerTradeNo() { + return this.partnerTradeNo; + } + + public void setPartnerTradeNo(String partnerTradeNo) { + this.partnerTradeNo = partnerTradeNo; + } + + public String getOpenid() { + return this.openid; + } + + public void setOpenid(String openid) { + this.openid = openid; + } + + public String getOpUserId() { + return this.opUserId; + } + + public void setOpUserId(String opUserId) { + this.opUserId = opUserId; + } + + public String getDeviceInfo() { + return this.deviceInfo; + } + + public void setDeviceInfo(String deviceInfo) { + this.deviceInfo = deviceInfo; + } + + public String getVersion() { + return this.version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getType() { + return this.type; + } + + public void setType(String type) { + this.type = type; + } + + @Override + protected void checkConstraints() { + //do nothing + } + + public static final class Builder { + private String appid; + private String mchId; + private String subAppId; + private String subMchId; + private String nonceStr; + private String sign; + private String couponStockId; + private Integer openidCount; + private String partnerTradeNo; + private String openid; + private String opUserId; + private String deviceInfo; + private String version; + private String type; + + private Builder() { + } + + public Builder appid(String appid) { + this.appid = appid; + return this; + } + + public Builder mchId(String mchId) { + this.mchId = mchId; + return this; + } + + public Builder subAppId(String subAppId) { + this.subAppId = subAppId; + return this; + } + + public Builder subMchId(String subMchId) { + this.subMchId = subMchId; + return this; + } + + public Builder nonceStr(String nonceStr) { + this.nonceStr = nonceStr; + return this; + } + + public Builder sign(String sign) { + this.sign = sign; + return this; + } + + public Builder couponStockId(String couponStockId) { + this.couponStockId = couponStockId; + return this; + } + + public Builder openidCount(Integer openidCount) { + this.openidCount = openidCount; + return this; + } + + public Builder partnerTradeNo(String partnerTradeNo) { + this.partnerTradeNo = partnerTradeNo; + return this; + } + + public Builder openid(String openid) { + this.openid = openid; + return this; + } + + public Builder opUserId(String opUserId) { + this.opUserId = opUserId; + return this; + } + + public Builder deviceInfo(String deviceInfo) { + this.deviceInfo = deviceInfo; + return this; + } + + public Builder version(String version) { + this.version = version; + return this; + } + + public Builder type(String type) { + this.type = type; + return this; + } + + public WxPayCouponSendRequest build() { + return new WxPayCouponSendRequest(this); + } + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponSendResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponSendResult.java new file mode 100644 index 0000000000..1c562adae2 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponSendResult.java @@ -0,0 +1,204 @@ +package com.github.binarywang.wxpay.bean.coupon; + +import com.github.binarywang.wxpay.bean.result.WxPayBaseResult; +import com.thoughtworks.xstream.annotations.XStreamAlias; + +/** + *
+ * 发送代金券响应结果类
+ * Created by Binary Wang on 2017-7-15.
+ * 
+ * + * @author Binary Wang + */ +@XStreamAlias("xml") +public class WxPayCouponSendResult extends WxPayBaseResult { + /** + *
+   * 字段名:设备号
+   * 变量名:device_info
+   * 是否必填:否
+   * 示例值:123456sb
+   * 类型:String(32)
+   * 描述:微信支付分配的终端设备号,
+   * 
+ */ + @XStreamAlias("device_info") + private String deviceInfo; + + /** + *
+   * 字段名:代金券批次id
+   * 变量名:coupon_stock_id
+   * 是否必填:是
+   * 示例值:1757
+   * 类型:String
+   * 描述:用户在商户appid下的唯一标识
+   * 
+ */ + @XStreamAlias("coupon_stock_id") + private String couponStockId; + + /** + *
+   * 字段名:返回记录数
+   * 变量名:resp_count
+   * 是否必填:是
+   * 示例值:1
+   * 类型:Int
+   * 描述:返回记录数
+   * 
+ */ + @XStreamAlias("resp_count") + private Integer respCount; + + /** + *
+   * 字段名:成功记录数
+   * 变量名:success_count
+   * 是否必填:是
+   * 示例值:1或者0
+   * 类型:Int
+   * 描述:成功记录数
+   * 
+ */ + @XStreamAlias("success_count") + private Integer successCount; + + /** + *
+   * 字段名:失败记录数
+   * 变量名:failed_count
+   * 是否必填:是
+   * 示例值:1或者0
+   * 类型:Int
+   * 描述:失败记录数
+   * 
+ */ + @XStreamAlias("failed_count") + private Integer failedCount; + + /** + *
+   * 字段名:用户标识
+   * 变量名:openid
+   * 是否必填:是
+   * 示例值:onqOjjrXT-776SpHnfexGm1_P7iE
+   * 类型:String
+   * 描述:用户在商户appid下的唯一标识
+   * 
+ */ + @XStreamAlias("openid") + private String openid; + + /** + *
+   * 字段名:返回码
+   * 变量名:ret_code
+   * 是否必填:是
+   * 示例值:SUCCESS或者FAILED
+   * 类型:String
+   * 描述:返回码,SUCCESS/FAILED
+   * 
+ */ + @XStreamAlias("ret_code") + private String retCode; + + /** + *
+   * 字段名:代金券id
+   * 变量名:coupon_id
+   * 是否必填:是
+   * 示例值:1870
+   * 类型:String
+   * 描述:对一个用户成功发放代金券则返回代金券id,即ret_code为SUCCESS的时候;如果ret_code为FAILED则填写空串""
+   * 
+ */ + @XStreamAlias("coupon_id") + private String couponId; + + /** + *
+   * 字段名:返回信息
+   * 变量名:ret_msg
+   * 是否必填:是
+   * 示例值:失败描述信息,例如:“用户已达领用上限”
+   * 类型:String
+   * 描述:返回信息,当返回码是FAILED的时候填写,否则填空串“”
+   * 
+ */ + @XStreamAlias("ret_msg") + private String retMsg; + + public String getDeviceInfo() { + return this.deviceInfo; + } + + public void setDeviceInfo(String deviceInfo) { + this.deviceInfo = deviceInfo; + } + + public String getCouponStockId() { + return this.couponStockId; + } + + public void setCouponStockId(String couponStockId) { + this.couponStockId = couponStockId; + } + + public Integer getRespCount() { + return this.respCount; + } + + public void setRespCount(Integer respCount) { + this.respCount = respCount; + } + + public Integer getSuccessCount() { + return this.successCount; + } + + public void setSuccessCount(Integer successCount) { + this.successCount = successCount; + } + + public Integer getFailedCount() { + return this.failedCount; + } + + public void setFailedCount(Integer failedCount) { + this.failedCount = failedCount; + } + + public String getOpenid() { + return this.openid; + } + + public void setOpenid(String openid) { + this.openid = openid; + } + + public String getRetCode() { + return this.retCode; + } + + public void setRetCode(String retCode) { + this.retCode = retCode; + } + + public String getCouponId() { + return this.couponId; + } + + public void setCouponId(String couponId) { + this.couponId = couponId; + } + + public String getRetMsg() { + return this.retMsg; + } + + public void setRetMsg(String retMsg) { + this.retMsg = retMsg; + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponStockQueryRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponStockQueryRequest.java new file mode 100644 index 0000000000..24cec5523c --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponStockQueryRequest.java @@ -0,0 +1,221 @@ +package com.github.binarywang.wxpay.bean.coupon; + +import com.github.binarywang.wxpay.bean.request.WxPayBaseRequest; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import me.chanjar.weixin.common.annotation.Required; + +/** + *
+ * 查询代金券批次请求对象类
+ * Created by Binary Wang on 2017-7-15.
+ * 
+ * + * @author Binary Wang + */ +@XStreamAlias("xml") +public class WxPayCouponStockQueryRequest extends WxPayBaseRequest { + /** + *
+   * 字段名:代金券批次id
+   * 变量名:coupon_stock_id
+   * 是否必填:是
+   * 示例值:1757
+   * 类型:String
+   * 说明:代金券批次id
+   * 
+ */ + @Required + @XStreamAlias("coupon_stock_id") + private String couponStockId; + + /** + *
+   * 字段名:操作员
+   * 变量名:op_user_id
+   * 是否必填:否
+   * 示例值:10000098
+   * 类型:String(32)
+   * 说明:操作员帐号, 默认为商户号,可在商户平台配置操作员对应的api权限
+   * 
+ */ + @XStreamAlias("op_user_id") + private String opUserId; + + /** + *
+   * 字段名:设备号
+   * 变量名:device_info
+   * 是否必填:否
+   * 示例值:
+   * 类型:String(32)
+   * 说明:微信支付分配的终端设备号
+   * 
+ */ + @XStreamAlias("device_info") + private String deviceInfo; + + /** + *
+   * 字段名:协议版本
+   * 变量名:version
+   * 是否必填:否
+   * 示例值:1.0
+   * 类型:String(32)
+   * 说明:默认1.0
+   * 
+ */ + @XStreamAlias("version") + private String version; + + /** + *
+   * 字段名:协议类型
+   * 变量名:type
+   * 是否必填:否
+   * 示例值:XML
+   * 类型:String(32)
+   * 说明:XML【目前仅支持默认XML】
+   * 
+ */ + @XStreamAlias("type") + private String type; + + private WxPayCouponStockQueryRequest(Builder builder) { + setAppid(builder.appid); + setMchId(builder.mchId); + setSubAppId(builder.subAppId); + setSubMchId(builder.subMchId); + setNonceStr(builder.nonceStr); + setSign(builder.sign); + setCouponStockId(builder.couponStockId); + setOpUserId(builder.opUserId); + setDeviceInfo(builder.deviceInfo); + setVersion(builder.version); + setType(builder.type); + } + + public static Builder newBuilder() { + return new Builder(); + } + + public String getCouponStockId() { + return this.couponStockId; + } + + public void setCouponStockId(String couponStockId) { + this.couponStockId = couponStockId; + } + + public String getOpUserId() { + return this.opUserId; + } + + public void setOpUserId(String opUserId) { + this.opUserId = opUserId; + } + + public String getDeviceInfo() { + return this.deviceInfo; + } + + public void setDeviceInfo(String deviceInfo) { + this.deviceInfo = deviceInfo; + } + + public String getVersion() { + return this.version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getType() { + return this.type; + } + + public void setType(String type) { + this.type = type; + } + + @Override + protected void checkConstraints() { + //do nothing + } + + public static final class Builder { + private String appid; + private String mchId; + private String subAppId; + private String subMchId; + private String nonceStr; + private String sign; + private String couponStockId; + private String opUserId; + private String deviceInfo; + private String version; + private String type; + + private Builder() { + } + + public Builder appid(String appid) { + this.appid = appid; + return this; + } + + public Builder mchId(String mchId) { + this.mchId = mchId; + return this; + } + + public Builder subAppId(String subAppId) { + this.subAppId = subAppId; + return this; + } + + public Builder subMchId(String subMchId) { + this.subMchId = subMchId; + return this; + } + + public Builder nonceStr(String nonceStr) { + this.nonceStr = nonceStr; + return this; + } + + public Builder sign(String sign) { + this.sign = sign; + return this; + } + + public Builder couponStockId(String couponStockId) { + this.couponStockId = couponStockId; + return this; + } + + public Builder opUserId(String opUserId) { + this.opUserId = opUserId; + return this; + } + + public Builder deviceInfo(String deviceInfo) { + this.deviceInfo = deviceInfo; + return this; + } + + public Builder version(String version) { + this.version = version; + return this; + } + + public Builder type(String type) { + this.type = type; + return this; + } + + public WxPayCouponStockQueryRequest build() { + return new WxPayCouponStockQueryRequest(this); + } + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponStockQueryResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponStockQueryResult.java new file mode 100644 index 0000000000..065c3f606d --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponStockQueryResult.java @@ -0,0 +1,288 @@ +package com.github.binarywang.wxpay.bean.coupon; + +import com.github.binarywang.wxpay.bean.result.WxPayBaseResult; +import com.thoughtworks.xstream.annotations.XStreamAlias; + +/** + *
+ * 查询代金券批次响应结果类
+ * Created by Binary Wang on 2017-7-15.
+ * 
+ * + * @author Binary Wang + */ +@XStreamAlias("xml") +public class WxPayCouponStockQueryResult extends WxPayBaseResult { + /** + *
+   * 字段名:设备号
+   * 变量名:device_info
+   * 是否必填:否
+   * 示例值:123456sb
+   * 类型:String(32)
+   * 说明:微信支付分配的终端设备号
+   * 
+ */ + @XStreamAlias("device_info") + private String deviceInfo; + + /** + *
+   * 字段名:代金券批次ID
+   * 变量名:coupon_stock_id
+   * 是否必填:是
+   * 示例值:1757
+   * 类型:String
+   * 说明:代金券批次Id
+   * 
+ */ + @XStreamAlias("coupon_stock_id") + private String couponStockId; + + /** + *
+   * 字段名:代金券名称
+   * 变量名:coupon_name
+   * 是否必填:否
+   * 示例值:测试代金券
+   * 类型:String
+   * 说明:代金券名称
+   * 
+ */ + @XStreamAlias("coupon_name") + private String couponName; + + /** + *
+   * 字段名:代金券面额
+   * 变量名:coupon_value
+   * 是否必填:是
+   * 示例值:5
+   * 类型:Unsinged int
+   * 说明:代金券面值,单位是分
+   * 
+ */ + @XStreamAlias("coupon_value") + private Integer couponValue; + + /** + *
+   * 字段名:代金券使用最低限额
+   * 变量名:coupon_mininumn
+   * 是否必填:否
+   * 示例值:10
+   * 类型:Unsinged int
+   * 说明:代金券使用最低限额,单位是分
+   * 
+ */ + @XStreamAlias("coupon_mininumn") + private Integer couponMininumn; + + /** + *
+   * 字段名:代金券批次状态
+   * 变量名:coupon_stock_status
+   * 是否必填:是
+   * 示例值:4
+   * 类型:int
+   * 说明:批次状态: 1-未激活;2-审批中;4-已激活;8-已作废;16-中止发放;
+   * 
+ */ + @XStreamAlias("coupon_stock_status") + private Integer couponStockStatus; + + /** + *
+   * 字段名:代金券数量
+   * 变量名:coupon_total
+   * 是否必填:是
+   * 示例值:100
+   * 类型:Unsigned int
+   * 说明:代金券数量
+   * 
+ */ + @XStreamAlias("coupon_total") + private Integer couponTotal; + + /** + *
+   * 字段名:代金券最大领取数量
+   * 变量名:max_quota
+   * 是否必填:否
+   * 示例值:1
+   * 类型:Unsigned int
+   * 说明:代金券每个人最多能领取的数量, 如果为0,则表示没有限制
+   * 
+ */ + @XStreamAlias("max_quota") + private Integer maxQuota; + + /** + *
+   * 字段名:代金券已经发送的数量
+   * 变量名:is_send_num
+   * 是否必填:否
+   * 示例值:0
+   * 类型:Unsigned int
+   * 说明:代金券已经发送的数量
+   * 
+ */ + @XStreamAlias("is_send_num") + private Integer isSendNum; + + /** + *
+   * 字段名:生效开始时间
+   * 变量名:begin_time
+   * 是否必填:是
+   * 示例值:1943787483
+   * 类型:String
+   * 说明:格式为时间戳
+   * 
+ */ + @XStreamAlias("begin_time") + private String beginTime; + + /** + *
+   * 字段名:生效结束时间
+   * 变量名:end_time
+   * 是否必填:是
+   * 示例值:1943787490
+   * 类型:String
+   * 说明:格式为时间戳
+   * 
+ */ + @XStreamAlias("end_time") + private String endTime; + + /** + *
+   * 字段名:创建时间
+   * 变量名:create_time
+   * 是否必填:是
+   * 示例值:1943787420
+   * 类型:String
+   * 说明:格式为时间戳
+   * 
+ */ + @XStreamAlias("create_time") + private String createTime; + + /** + *
+   * 字段名:代金券预算额度
+   * 变量名:coupon_budget
+   * 是否必填:否
+   * 示例值:500
+   * 类型:Unsigned int
+   * 说明:代金券预算额度
+   * 
+ */ + @XStreamAlias("coupon_budget") + private Integer couponBudget; + + public String getDeviceInfo() { + return this.deviceInfo; + } + + public void setDeviceInfo(String deviceInfo) { + this.deviceInfo = deviceInfo; + } + + public String getCouponStockId() { + return this.couponStockId; + } + + public void setCouponStockId(String couponStockId) { + this.couponStockId = couponStockId; + } + + public String getCouponName() { + return this.couponName; + } + + public void setCouponName(String couponName) { + this.couponName = couponName; + } + + public Integer getCouponValue() { + return this.couponValue; + } + + public void setCouponValue(Integer couponValue) { + this.couponValue = couponValue; + } + + public Integer getCouponMininumn() { + return this.couponMininumn; + } + + public void setCouponMininumn(Integer couponMininumn) { + this.couponMininumn = couponMininumn; + } + + public Integer getCouponStockStatus() { + return this.couponStockStatus; + } + + public void setCouponStockStatus(Integer couponStockStatus) { + this.couponStockStatus = couponStockStatus; + } + + public Integer getCouponTotal() { + return this.couponTotal; + } + + public void setCouponTotal(Integer couponTotal) { + this.couponTotal = couponTotal; + } + + public Integer getMaxQuota() { + return this.maxQuota; + } + + public void setMaxQuota(Integer maxQuota) { + this.maxQuota = maxQuota; + } + + public Integer getIsSendNum() { + return this.isSendNum; + } + + public void setIsSendNum(Integer isSendNum) { + this.isSendNum = isSendNum; + } + + public String getBeginTime() { + return this.beginTime; + } + + public void setBeginTime(String beginTime) { + this.beginTime = beginTime; + } + + public String getEndTime() { + return this.endTime; + } + + public void setEndTime(String endTime) { + this.endTime = endTime; + } + + public String getCreateTime() { + return this.createTime; + } + + public void setCreateTime(String createTime) { + this.createTime = createTime; + } + + public Integer getCouponBudget() { + return this.couponBudget; + } + + public void setCouponBudget(Integer couponBudget) { + this.couponBudget = couponBudget; + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxEntPayRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxEntPayRequest.java index fd43fb1aaa..7897ad02da 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxEntPayRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxEntPayRequest.java @@ -1,18 +1,12 @@ package com.github.binarywang.wxpay.bean.request; import com.thoughtworks.xstream.annotations.XStreamAlias; +import me.chanjar.weixin.common.annotation.Required; import me.chanjar.weixin.common.util.ToStringUtils; /** *
  * 企业付款请求对象
- * 注释中各行每个字段描述对应如下:
- * 
  • 字段名 - *
  • 变量名 - *
  • 是否必填 - *
  • 类型 - *
  • 示例值 - *
  • 描述 *
  • * Created by Binary Wang on 2016/10/02. * @@ -22,12 +16,12 @@ public class WxEntPayRequest extends WxPayBaseRequest { /** *
    -   * 公众账号appid
    -   * mch_appid
    -   * 是
    -   * wx8888888888888888
    -   * String
    -   * 微信分配的公众账号ID(企业号corpid即为此appId)
    +   * 字段名:公众账号appid
    +   * 变量名:mch_appid
    +   * 是否必填:是
    +   * 示例值:wx8888888888888888
    +   * 类型:String
    +   * 描述:微信分配的公众账号ID(企业号corpid即为此appId)
        * 
    */ @XStreamAlias("mch_appid") @@ -35,12 +29,12 @@ public class WxEntPayRequest extends WxPayBaseRequest { /** *
    -   *  商户号
    -   *  mchid
    -   *  是
    -   *  1900000109
    -   *  String(32)
    -   *  微信支付分配的商户号
    +   * 字段名:商户号
    +   * 变量名:mchid
    +   * 是否必填:是
    +   * 示例值:1900000109
    +   * 类型:String(32)
    +   * 描述:微信支付分配的商户号
        * 
    */ @XStreamAlias("mchid") @@ -48,12 +42,12 @@ public class WxEntPayRequest extends WxPayBaseRequest { /** *
    -   * 设备号
    -   * device_info
    -   * 否
    -   * 13467007045764
    -   * String(32)
    -   * 微信支付分配的终端设备号
    +   * 字段名:设备号
    +   * 变量名:device_info
    +   * 是否必填:否
    +   * 示例值:13467007045764
    +   * 类型:String(32)
    +   * 描述:微信支付分配的终端设备号
        * 
    */ @XStreamAlias("device_info") @@ -61,53 +55,56 @@ public class WxEntPayRequest extends WxPayBaseRequest { /** *
    -   * 商户订单号
    -   * partner_trade_no
    -   * 是
    -   * 10000098201411111234567890
    -   * String
    -   * 商户订单号
    +   * 字段名:商户订单号
    +   * 变量名:partner_trade_no
    +   * 是否必填:是
    +   * 示例值:10000098201411111234567890
    +   * 类型:String
    +   * 描述:商户订单号
        * 
    */ + @Required @XStreamAlias("partner_trade_no") private String partnerTradeNo; /** *
    -   * 需保持唯一性 用户openid
    -   * openid
    -   * 是
    -   * oxTWIuGaIt6gTKsQRLau2M0yL16E
    -   * String
    -   * 商户appid下,某用户的openid
    +   * 字段名:需保持唯一性 用户openid
    +   * 变量名:openid
    +   * 是否必填:是
    +   * 示例值:oxTWIuGaIt6gTKsQRLau2M0yL16E
    +   * 类型:String
    +   * 描述:商户appid下,某用户的openid
        * 
    */ + @Required @XStreamAlias("openid") private String openid; /** *
    -   * 校验用户姓名选项
    -   * check_name
    -   * 是
    -   * OPTION_CHECK
    -   * String
    -   * NO_CHECK:不校验真实姓名 
    +   * 字段名:校验用户姓名选项
    +   * 变量名:check_name
    +   * 是否必填:是
    +   * 示例值:OPTION_CHECK
    +   * 类型:String
    +   * 描述:NO_CHECK:不校验真实姓名 
        * FORCE_CHECK:强校验真实姓名(未实名认证的用户会校验失败,无法转账) 
        * OPTION_CHECK:针对已实名认证的用户才校验真实姓名(未实名认证用户不校验,可以转账成功)
        * 
    */ + @Required @XStreamAlias("check_name") private String checkName; /** *
    -   * 收款用户姓名
    -   * re_user_name
    -   * 可选
    -   * 马花花
    -   * String
    -   * 收款用户真实姓名。
    +   * 字段名:收款用户姓名
    +   * 变量名:re_user_name
    +   * 是否必填:可选
    +   * 示例值:马花花
    +   * 类型:String
    +   * 描述:收款用户真实姓名。
        * 如果check_name设置为FORCE_CHECK或OPTION_CHECK,  则必填用户真实姓名
        * 
    */ @@ -116,43 +113,71 @@ public class WxEntPayRequest extends WxPayBaseRequest { /** *
    -   * 金额
    -   * amount
    -   * 是
    -   * 10099
    -   * int
    -   * 企业付款金额, 单位为分
    +   * 字段名:金额
    +   * 变量名:amount
    +   * 是否必填:是
    +   * 示例值:10099
    +   * 类型:int
    +   * 描述:企业付款金额, 单位为分
        * 
    */ + @Required @XStreamAlias("amount") private Integer amount; /** *
    -   * 企业付款描述信息
    -   * desc
    -   * 是
    -   * 理赔
    -   * String
    -   * 企业付款操作说明信息。必填。
    +   * 字段名:企业付款描述信息
    +   * 变量名:desc
    +   * 是否必填:是
    +   * 示例值:理赔
    +   * 类型:String
    +   * 描述:企业付款操作说明信息。必填。
        * 
    */ + @Required @XStreamAlias("desc") private String description; /** *
    -   * Ip地址
    -   * spbill_create_ip
    -   * 是
    -   * 192.168.0.1
    -   * String(32)
    -   * 调用接口的机器Ip地址
    +   * 字段名:Ip地址
    +   * 变量名:spbill_create_ip
    +   * 是否必填:是
    +   * 示例值:192.168.0.1
    +   * 类型:String(32)
    +   * 描述:调用接口的机器Ip地址
        * 
    */ + @Required @XStreamAlias("spbill_create_ip") private String spbillCreateIp; + public WxEntPayRequest() { + } + + private WxEntPayRequest(Builder builder) { + setAppid(builder.appid); + setMchId(builder.mchId); + setSubAppId(builder.subAppId); + setSubMchId(builder.subMchId); + setNonceStr(builder.nonceStr); + setSign(builder.sign); + mchAppid = builder.mchAppid; + setMchId(builder.mchId); + setDeviceInfo(builder.deviceInfo); + setPartnerTradeNo(builder.partnerTradeNo); + setOpenid(builder.openid); + setCheckName(builder.checkName); + setReUserName(builder.reUserName); + setAmount(builder.amount); + setDescription(builder.description); + setSpbillCreateIp(builder.spbillCreateIp); + } + + public static Builder newBuilder() { + return new Builder(); + } @Override protected void checkConstraints() { @@ -248,4 +273,103 @@ public String toString() { return ToStringUtils.toSimpleString(this); } + public static final class Builder { + private String appid; + private String mchId; + private String deviceInfo; + private String partnerTradeNo; + private String openid; + private String checkName; + private String reUserName; + private Integer amount; + private String description; + private String spbillCreateIp; + private String subAppId; + private String subMchId; + private String nonceStr; + private String sign; + private String mchAppid; + + private Builder() { + } + + public Builder appid(String appid) { + this.appid = appid; + return this; + } + + public Builder mchId(String mchId) { + this.mchId = mchId; + return this; + } + + public Builder deviceInfo(String deviceInfo) { + this.deviceInfo = deviceInfo; + return this; + } + + public Builder partnerTradeNo(String partnerTradeNo) { + this.partnerTradeNo = partnerTradeNo; + return this; + } + + public Builder openid(String openid) { + this.openid = openid; + return this; + } + + public Builder checkName(String checkName) { + this.checkName = checkName; + return this; + } + + public Builder reUserName(String reUserName) { + this.reUserName = reUserName; + return this; + } + + public Builder amount(Integer amount) { + this.amount = amount; + return this; + } + + public Builder description(String description) { + this.description = description; + return this; + } + + public Builder spbillCreateIp(String spbillCreateIp) { + this.spbillCreateIp = spbillCreateIp; + return this; + } + + public WxEntPayRequest build() { + return new WxEntPayRequest(this); + } + + public Builder subAppId(String subAppId) { + this.subAppId = subAppId; + return this; + } + + public Builder subMchId(String subMchId) { + this.subMchId = subMchId; + return this; + } + + public Builder nonceStr(String nonceStr) { + this.nonceStr = nonceStr; + return this; + } + + public Builder sign(String sign) { + this.sign = sign; + return this; + } + + public Builder mchAppid(String mchAppid) { + this.mchAppid = mchAppid; + return this; + } + } } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayBaseRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayBaseRequest.java index 65f10f5cd2..cc0490d22e 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayBaseRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayBaseRequest.java @@ -1,6 +1,7 @@ package com.github.binarywang.wxpay.bean.request; import com.github.binarywang.wxpay.config.WxPayConfig; +import com.github.binarywang.wxpay.exception.WxPayException; import com.github.binarywang.wxpay.util.SignUtils; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.annotations.XStreamAlias; @@ -16,13 +17,6 @@ *
      * Created by Binary Wang on 2016-10-24.
      *  微信支付请求对象共用的参数存放类
    - * 注释中各行每个字段描述对应如下:
    - * 
  • 字段名 - *
  • 变量名 - *
  • 是否必填 - *
  • 类型 - *
  • 示例值 - *
  • 描述 *
  • * * @author binarywang(Binary Wang) @@ -30,72 +24,72 @@ public abstract class WxPayBaseRequest { /** *
    -   * 公众账号ID
    -   * appid
    -   * 是
    -   * String(32)
    -   * wxd678efh567hg6787
    -   * 微信分配的公众账号ID(企业号corpid即为此appId)
    +   * 字段名:公众账号ID
    +   * 变量名:appid
    +   * 是否必填:是
    +   * 类型:String(32)
    +   * 示例值:wxd678efh567hg6787
    +   * 描述:微信分配的公众账号ID(企业号corpid即为此appId)
        * 
    */ @XStreamAlias("appid") protected String appid; /** *
    -   * 商户号
    -   * mch_id
    -   * 是
    -   * String(32)
    -   * 1230000109
    -   * 微信支付分配的商户号
    +   * 字段名:商户号
    +   * 变量名:mch_id
    +   * 是否必填:是
    +   * 类型:String(32)
    +   * 示例值:1230000109
    +   * 描述:微信支付分配的商户号
        * 
    */ @XStreamAlias("mch_id") protected String mchId; /** *
    -   * 服务商模式下的子商户公众账号ID
    -   * sub_appid
    -   * 是
    -   * String(32)
    -   * wxd678efh567hg6787
    -   * 微信分配的子商户公众账号ID
    +   * 字段名:服务商模式下的子商户公众账号ID
    +   * 变量名:sub_appid
    +   * 是否必填:是
    +   * 类型:String(32)
    +   * 示例值:wxd678efh567hg6787
    +   * 描述:微信分配的子商户公众账号ID
        * 
    */ @XStreamAlias("sub_appid") protected String subAppId; /** *
    -   * 服务商模式下的子商户号
    -   * sub_mch_id
    -   * 是
    -   * String(32)
    -   * 1230000109
    -   * 微信支付分配的子商户号,开发者模式下必填
    +   * 字段名:服务商模式下的子商户号
    +   * 变量名:sub_mch_id
    +   * 是否必填:是
    +   * 类型:String(32)
    +   * 示例值:1230000109
    +   * 描述:微信支付分配的子商户号,开发者模式下必填
        * 
    */ @XStreamAlias("sub_mch_id") protected String subMchId; /** *
    -   * 随机字符串
    -   * nonce_str
    -   * 是
    -   * String(32)
    -   * 5K8264ILTKCH16CQ2502SI8ZNMTM67VS
    -   * 随机字符串,不长于32位。推荐随机数生成算法
    +   * 字段名:随机字符串
    +   * 变量名:nonce_str
    +   * 是否必填:是
    +   * 类型:String(32)
    +   * 示例值:5K8264ILTKCH16CQ2502SI8ZNMTM67VS
    +   * 描述:随机字符串,不长于32位。推荐随机数生成算法
        * 
    */ @XStreamAlias("nonce_str") protected String nonceStr; /** *
    -   * 签名
    -   * sign
    -   * 是
    -   * String(32)
    -   * C380BEC2BFD727A4B6845133519F3AD6
    -   * 签名,详见签名生成算法
    +   * 字段名:签名
    +   * 变量名:sign
    +   * 是否必填:是
    +   * 类型:String(32)
    +   * 示例值:C380BEC2BFD727A4B6845133519F3AD6
    +   * 描述:签名,详见签名生成算法
        * 
    */ @XStreamAlias("sign") @@ -113,9 +107,13 @@ public static Integer yuanToFee(String yuan) { /** * 检查请求参数内容,包括必填参数以及特殊约束 */ - protected void checkFields() throws WxErrorException { + protected void checkFields() throws WxPayException { //check required fields - BeanUtils.checkRequiredFields(this); + try { + BeanUtils.checkRequiredFields(this); + } catch (WxErrorException e) { + throw new WxPayException(e.getError().getErrorMsg(), e); + } //check other parameters this.checkConstraints(); @@ -124,7 +122,7 @@ protected void checkFields() throws WxErrorException { /** * 检查约束情况 */ - protected abstract void checkConstraints(); + protected abstract void checkConstraints() throws WxPayException; public String getAppid() { return this.appid; @@ -210,7 +208,7 @@ public String toXML() { * * @param config 支付配置对象,用于读取相应系统配置信息 */ - public void checkAndSign(WxPayConfig config) throws WxErrorException { + public void checkAndSign(WxPayConfig config) throws WxPayException { this.checkFields(); if (StringUtils.isBlank(getAppid())) { diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayDefaultRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayDefaultRequest.java new file mode 100644 index 0000000000..ff36bfaad7 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayDefaultRequest.java @@ -0,0 +1,19 @@ +package com.github.binarywang.wxpay.bean.request; + +import com.thoughtworks.xstream.annotations.XStreamAlias; + +/** + *
    + *  支付请求默认对象类
    + *  Created by BinaryWang on 2017/6/18.
    + * 
    + * + * @author Binary Wang + */ +@XStreamAlias("xml") +public class WxPayDefaultRequest extends WxPayBaseRequest { + @Override + protected void checkConstraints() { + //do nothing + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayDownloadBillRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayDownloadBillRequest.java index ed02060f83..2838284c80 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayDownloadBillRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayDownloadBillRequest.java @@ -1,5 +1,6 @@ package com.github.binarywang.wxpay.bean.request; +import com.github.binarywang.wxpay.exception.WxPayException; import com.thoughtworks.xstream.annotations.XStreamAlias; import me.chanjar.weixin.common.annotation.Required; import org.apache.commons.lang3.ArrayUtils; @@ -128,13 +129,13 @@ public void setTarType(String tarType) { } @Override - protected void checkConstraints() { + protected void checkConstraints() throws WxPayException { if (StringUtils.isNotBlank(this.getTarType()) && !"GZIP".equals(this.getTarType())) { - throw new IllegalArgumentException("tar_type值如果存在,只能为GZIP"); + throw new WxPayException("tar_type值如果存在,只能为GZIP"); } if (!ArrayUtils.contains(BILL_TYPE, this.getBillType())) { - throw new IllegalArgumentException(String.format("bill_tpye目前必须为%s其中之一,实际值:%s", + throw new WxPayException(String.format("bill_tpye目前必须为%s其中之一,实际值:%s", Arrays.toString(BILL_TYPE), this.getBillType())); } } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderQueryRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderQueryRequest.java index 499727995e..e7767da607 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderQueryRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderQueryRequest.java @@ -1,5 +1,6 @@ package com.github.binarywang.wxpay.bean.request; +import com.github.binarywang.wxpay.exception.WxPayException; import com.thoughtworks.xstream.annotations.XStreamAlias; import org.apache.commons.lang3.StringUtils; @@ -64,10 +65,10 @@ public void setOutTradeNo(String outTradeNo) { } @Override - protected void checkConstraints() { + protected void checkConstraints() throws WxPayException { if ((StringUtils.isBlank(transactionId) && StringUtils.isBlank(outTradeNo)) || (StringUtils.isNotBlank(transactionId) && StringUtils.isNotBlank(outTradeNo))) { - throw new IllegalArgumentException("transaction_id 和 out_trade_no 不能同时存在或同时为空,必须二选一"); + throw new WxPayException("transaction_id 和 out_trade_no 不能同时存在或同时为空,必须二选一"); } } } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderReverseRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderReverseRequest.java index 6aef2aa0fa..075295238e 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderReverseRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderReverseRequest.java @@ -1,5 +1,6 @@ package com.github.binarywang.wxpay.bean.request; +import com.github.binarywang.wxpay.exception.WxPayException; import com.thoughtworks.xstream.annotations.XStreamAlias; import org.apache.commons.lang3.StringUtils; @@ -91,9 +92,9 @@ public void setSignType(String signType) { } @Override - protected void checkConstraints() { + protected void checkConstraints() throws WxPayException { if (StringUtils.isBlank(transactionId) && StringUtils.isBlank(outTradeNo)) { - throw new IllegalArgumentException("transaction_id 和 out_trade_no不能同时为空!"); + throw new WxPayException("transaction_id 和 out_trade_no不能同时为空!"); } } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundQueryRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundQueryRequest.java index 1efa23db7a..00545f004c 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundQueryRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundQueryRequest.java @@ -1,5 +1,6 @@ package com.github.binarywang.wxpay.bean.request; +import com.github.binarywang.wxpay.exception.WxPayException; import com.thoughtworks.xstream.annotations.XStreamAlias; import org.apache.commons.lang3.StringUtils; @@ -135,12 +136,12 @@ public void setRefundId(String refundId) { } @Override - protected void checkConstraints() { + protected void checkConstraints() throws WxPayException { if ((StringUtils.isBlank(transactionId) && StringUtils.isBlank(outTradeNo) && StringUtils.isBlank(outRefundNo) && StringUtils.isBlank(refundId)) || (StringUtils.isNotBlank(transactionId) && StringUtils.isNotBlank(outTradeNo) && StringUtils.isNotBlank(outRefundNo) && StringUtils.isNotBlank(refundId))) { - throw new IllegalArgumentException("transaction_id,out_trade_no,out_refund_no,refund_id 必须四选一"); + throw new WxPayException("transaction_id,out_trade_no,out_refund_no,refund_id 必须四选一"); } } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundRequest.java index 8d80148c41..66808738ca 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundRequest.java @@ -1,9 +1,9 @@ package com.github.binarywang.wxpay.bean.request; import com.github.binarywang.wxpay.config.WxPayConfig; +import com.github.binarywang.wxpay.exception.WxPayException; import com.thoughtworks.xstream.annotations.XStreamAlias; import me.chanjar.weixin.common.annotation.Required; -import me.chanjar.weixin.common.exception.WxErrorException; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; @@ -142,17 +142,26 @@ public class WxPayRefundRequest extends WxPayBaseRequest { */ @XStreamAlias("refund_account") private String refundAccount; - - public WxPayRefundRequest() { - } + /** + *
    +   * 退款原因
    +   * refund_account
    +   * 否
    +   * String(80)
    +   * 商品已售完
    +   * 若商户传入,会在下发给用户的退款消息中体现退款原因
    +   * 
    + */ + @XStreamAlias("refund_desc") + private String refundDesc; private WxPayRefundRequest(Builder builder) { setDeviceInfo(builder.deviceInfo); setAppid(builder.appid); setTransactionId(builder.transactionId); setMchId(builder.mchId); - setOutTradeNo(builder.outTradeNo); setSubAppId(builder.subAppId); + setOutTradeNo(builder.outTradeNo); setSubMchId(builder.subMchId); setOutRefundNo(builder.outRefundNo); setNonceStr(builder.nonceStr); @@ -162,21 +171,13 @@ private WxPayRefundRequest(Builder builder) { setRefundFeeType(builder.refundFeeType); setOpUserId(builder.opUserId); setRefundAccount(builder.refundAccount); + setRefundDesc(builder.refundDesc); } public static Builder newBuilder() { return new Builder(); } - @Override - public void checkAndSign(WxPayConfig config) throws WxErrorException { - if (StringUtils.isBlank(this.getOpUserId())) { - this.setOpUserId(config.getMchId()); - } - - super.checkAndSign(config); - } - public String getDeviceInfo() { return this.deviceInfo; } @@ -249,17 +250,37 @@ public void setRefundAccount(String refundAccount) { this.refundAccount = refundAccount; } + public String getRefundDesc() { + return this.refundDesc; + } + + public void setRefundDesc(String refundDesc) { + this.refundDesc = refundDesc; + } + + public WxPayRefundRequest() { + } + + @Override + public void checkAndSign(WxPayConfig config) throws WxPayException { + if (StringUtils.isBlank(this.getOpUserId())) { + this.setOpUserId(config.getMchId()); + } + + super.checkAndSign(config); + } + @Override - protected void checkConstraints() { + protected void checkConstraints() throws WxPayException { if (StringUtils.isNotBlank(this.getRefundAccount())) { if (!ArrayUtils.contains(REFUND_ACCOUNT, this.getRefundAccount())) { - throw new IllegalArgumentException(String.format("refund_account目前必须为%s其中之一,实际值:%s", + throw new WxPayException(String.format("refund_account目前必须为%s其中之一,实际值:%s", Arrays.toString(REFUND_ACCOUNT), this.getRefundAccount())); } } if (StringUtils.isBlank(this.getOutTradeNo()) && StringUtils.isBlank(this.getTransactionId())) { - throw new IllegalArgumentException("transaction_id 和 out_trade_no 不能同时为空,必须提供一个"); + throw new WxPayException("transaction_id 和 out_trade_no 不能同时为空,必须提供一个"); } } @@ -268,8 +289,8 @@ public static final class Builder { private String appid; private String transactionId; private String mchId; - private String outTradeNo; private String subAppId; + private String outTradeNo; private String subMchId; private String outRefundNo; private String nonceStr; @@ -279,6 +300,7 @@ public static final class Builder { private String refundFeeType; private String opUserId; private String refundAccount; + private String refundDesc; private Builder() { } @@ -303,13 +325,13 @@ public Builder mchId(String mchId) { return this; } - public Builder outTradeNo(String outTradeNo) { - this.outTradeNo = outTradeNo; + public Builder subAppId(String subAppId) { + this.subAppId = subAppId; return this; } - public Builder subAppId(String subAppId) { - this.subAppId = subAppId; + public Builder outTradeNo(String outTradeNo) { + this.outTradeNo = outTradeNo; return this; } @@ -358,6 +380,11 @@ public Builder refundAccount(String refundAccount) { return this; } + public Builder refundDesc(String refundDesc) { + this.refundDesc = refundDesc; + return this; + } + public WxPayRefundRequest build() { return new WxPayRefundRequest(this); } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayUnifiedOrderRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayUnifiedOrderRequest.java index 48b059d36a..8061556cc7 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayUnifiedOrderRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayUnifiedOrderRequest.java @@ -1,25 +1,15 @@ package com.github.binarywang.wxpay.bean.request; import com.github.binarywang.wxpay.config.WxPayConfig; +import com.github.binarywang.wxpay.exception.WxPayException; import com.thoughtworks.xstream.annotations.XStreamAlias; import me.chanjar.weixin.common.annotation.Required; -import me.chanjar.weixin.common.exception.WxErrorException; -import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; -import java.util.Arrays; - /** *
      * 统一下单请求参数对象
      * 参考文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
    - * 注释中各行每个字段描述对应如下:
    - * 
  • 字段名 - *
  • 变量名 - *
  • 是否必填 - *
  • 类型 - *
  • 示例值 - *
  • 描述 *
  • * Created by Binary Wang on 2016/9/25. * @@ -27,16 +17,16 @@ */ @XStreamAlias("xml") public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { - private static final String[] TRADE_TYPES = new String[]{"JSAPI", "NATIVE", "APP"}; + private static final String[] TRADE_TYPES = new String[]{"JSAPI", "NATIVE", "APP", "MWEB"}; /** *
    -   * 设备号
    -   * device_info
    -   * 否
    -   * String(32)
    -   * 013467007045764
    -   * 终端设备号(门店号或收银设备Id),注意:PC网页或公众号内支付请传"WEB"
    +   * 字段名:设备号
    +   * 变量名:device_info
    +   * 是否必填:否
    +   * 类型:String(32)
    +   * 示例值:013467007045764
    +   * 描述:终端设备号(门店号或收银设备Id),注意:PC网页或公众号内支付请传"WEB"
        * 
    */ @XStreamAlias("device_info") @@ -44,12 +34,12 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { /** *
    -   * 商品描述
    -   * body
    -   * 是
    -   * String(128)
    -   * 腾讯充值中心-QQ会员充值
    -   * 商品简单描述,该字段须严格按照规范传递,具体请见参数规定
    +   * 字段名:商品描述
    +   * 变量名:body
    +   * 是否必填:是
    +   * 类型:String(128)
    +   * 示例值: 腾讯充值中心-QQ会员充值
    +   * 描述:商品简单描述,该字段须严格按照规范传递,具体请见参数规定
        * 
    */ @Required @@ -58,12 +48,12 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { /** *
    -   * 商品详情
    -   * detail
    -   * 否
    -   * String(6000)
    -   *  {  "goods_detail":[
    -   * {
    +   * 字段名:商品详情
    +   * 变量名:detail
    +   * 是否必填:否
    +   * 类型:String(6000)
    +   * 示例值: {  "goods_detail":[
    +   *  {
        * "goods_id":"iphone6s_16G",
        * "wxpay_goods_id":"1001",
        * "goods_name":"iPhone6s 16G",
    @@ -83,7 +73,7 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest {
        * }
        * ]
        * }
    -   * 商品详细列表,使用Json格式,传输签名前请务必使用CDATA标签将JSON文本串保护起来。
    +   * 描述:商品详细列表,使用Json格式,传输签名前请务必使用CDATA标签将JSON文本串保护起来。
        * goods_detail []:
        * └ goods_id String 必填 32 商品的编号
        * └ wxpay_goods_id String 可选 32 微信支付定义的统一商品编号
    @@ -99,12 +89,12 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest {
     
       /**
        * 
    -   * 附加数据
    -   * attach
    -   * 否
    -   * String(127)
    -   * 深圳分店
    -   *  附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据
    +   * 字段名:附加数据
    +   * 变量名:attach
    +   * 是否必填:否
    +   * 类型:String(127)
    +   * 示例值: 深圳分店
    +   * 描述:  附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据
        * 
    */ @XStreamAlias("attach") @@ -112,12 +102,12 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { /** *
    -   * 商户订单号
    -   * out_trade_no
    -   * 是
    -   * String(32)
    -   * 20150806125346
    -   * 商户系统内部的订单号,32个字符内、可包含字母, 其他说明见商户订单号
    +   * 字段名:商户订单号
    +   * 变量名:out_trade_no
    +   * 是否必填:是
    +   * 类型:String(32)
    +   * 示例值:20150806125346
    +   * 描述:商户系统内部的订单号,32个字符内、可包含字母, 其他说明见商户订单号
        * 
    */ @Required @@ -126,12 +116,12 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { /** *
    -   * 货币类型
    -   * fee_type
    -   * 否
    -   * String(16)
    -   * CNY
    -   * 符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
    +   * 字段名:货币类型
    +   * 变量名:fee_type
    +   * 是否必填:否
    +   * 类型:String(16)
    +   * 示例值:CNY
    +   * 描述: 符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
        * 
    */ @XStreamAlias("fee_type") @@ -139,12 +129,12 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { /** *
    -   * 总金额
    -   * total_fee
    -   * 是
    -   * Int
    -   * 888
    -   * 订单总金额,单位为分,详见支付金额
    +   * 字段名:总金额
    +   * 变量名:total_fee
    +   * 是否必填:是
    +   * 类型:Int
    +   * 示例值: 888
    +   * 描述:订单总金额,单位为分,详见支付金额
        * 
    */ @Required @@ -153,12 +143,12 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { /** *
    -   * 终端IP
    -   * spbill_create_ip
    -   * 是
    -   * String(16)
    -   * 123.12.12.123
    -   * APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP。
    +   * 字段名:终端IP
    +   * 变量名:spbill_create_ip
    +   * 是否必填:是
    +   * 类型:String(16)
    +   * 示例值:123.12.12.123
    +   * 描述:APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP。
        * 
    */ @Required @@ -167,12 +157,12 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { /** *
    -   * 交易起始时间
    -   * time_start
    -   * 否
    -   * String(14)
    -   * 20091225091010
    -   * 订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则
    +   * 字段名:交易起始时间
    +   * 变量名:time_start
    +   * 是否必填:否
    +   * 类型:String(14)
    +   * 示例值:20091225091010
    +   * 描述:订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则
        * 
    */ @XStreamAlias("time_start") @@ -180,12 +170,12 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { /** *
    -   * 交易结束时间
    -   * time_expire
    -   * 否
    -   * String(14)
    -   * 20091227091010
    -   * 订单失效时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010。其他详见时间规则
    +   * 字段名:交易结束时间
    +   * 变量名:time_expire
    +   * 是否必填:否
    +   * 类型:String(14)
    +   * 示例值:20091227091010
    +   * 描述:订单失效时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010。其他详见时间规则
        *   注意:最短失效时间间隔必须大于5分钟
        * 
    */ @@ -194,12 +184,12 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { /** *
    -   * 商品标记
    -   * goods_tag
    -   * 否
    -   * String(32)
    -   * WXG
    -   * 商品标记,代金券或立减优惠功能的参数,说明详见代金券或立减优惠
    +   * 字段名:商品标记
    +   * 变量名:goods_tag
    +   * 是否必填:否
    +   * 类型:String(32)
    +   * 示例值:WXG
    +   * 描述:商品标记,代金券或立减优惠功能的参数,说明详见代金券或立减优惠
        * 
    */ @XStreamAlias("goods_tag") @@ -207,12 +197,12 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { /** *
    -   * 通知地址
    -   * notify_url
    -   * 是
    -   * String(256)
    -   * http://www.weixin.qq.com/wxpay/pay.php
    -   * 接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。
    +   * 字段名:通知地址
    +   * 变量名:notify_url
    +   * 是否必填:是
    +   * 类型:String(256)
    +   * 示例值:http://www.weixin.qq.com/wxpay/pay.php
    +   * 描述:接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。
        * 
    */ @Required @@ -221,12 +211,12 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { /** *
    -   * 交易类型
    -   * trade_type
    -   * 是
    -   * String(16)
    -   * JSAPI
    -   * 取值如下:JSAPI,NATIVE,APP,详细说明见参数规定:
    +   * 字段名:交易类型
    +   * 变量名:trade_type
    +   * 是否必填:是
    +   * 类型:String(16)
    +   * 示例值: JSAPI
    +   * 描述: 取值如下:JSAPI,NATIVE,APP,详细说明见参数规定:
        * JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付,统一下单接口trade_type的传参可参考这里
        * 
    */ @@ -236,12 +226,12 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { /** *
    -   * 商品Id
    -   * product_id
    -   * 否
    -   * String(32)
    -   * 12235413214070356458058
    -   * trade_type=NATIVE,此参数必传。此id为二维码中包含的商品Id,商户自行定义。
    +   * 字段名:商品Id
    +   * 变量名:product_id
    +   * 是否必填:否
    +   * 类型:String(32)
    +   * 示例值:12235413214070356458058
    +   * 描述:trade_type=NATIVE,此参数必传。此id为二维码中包含的商品Id,商户自行定义。
        * 
    */ @XStreamAlias("product_id") @@ -249,11 +239,12 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { /** *
    -   * 指定支付方式
    -   * limit_pay
    -   * 否
    -   * String(32)
    -   * no_credit no_credit--指定不能使用信用卡支付
    +   * 字段名:指定支付方式
    +   * 变量名:limit_pay
    +   * 是否必填:否
    +   * 类型:String(32)
    +   * 示例值:no_credit
    +   * 描述:no_credit--指定不能使用信用卡支付
        * 
    */ @XStreamAlias("limit_pay") @@ -261,12 +252,12 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { /** *
    -   * 用户标识
    -   * openid
    -   * 否
    -   * String(128)
    -   * oUpF8uMuAJO_M2pxb1Q9zNjWeS6o
    -   * trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识。
    +   * 字段名:用户标识
    +   * 变量名:openid
    +   * 是否必填:否
    +   * 类型:String(128)
    +   * 示例值:oUpF8uMuAJO_M2pxb1Q9zNjWeS6o
    +   * 描述:trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识。
        * openid如何获取,可参考【获取openid】。
        * 企业号请使用【企业号OAuth2.0接口】获取企业号内成员userid,再调用【企业号userid转openid接口】进行转换
        * 
    @@ -274,8 +265,84 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { @XStreamAlias("openid") private String openid; - public static WxUnifiedOrderRequestBuilder builder() { - return new WxUnifiedOrderRequestBuilder(); + /** + *
    +   * 字段名:用户子标识
    +   * 变量名:sub_openid
    +   * 是否必填:否
    +   * 类型:String(128)
    +   * 示例值:oUpF8uMuAJO_M2pxb1Q9zNjWeS6o
    +   * 描述:trade_type=JSAPI,此参数必传,用户在子商户appid下的唯一标识。
    +   * openid和sub_openid可以选传其中之一,如果选择传sub_openid,则必须传sub_appid。
    +   * 下单前需要调用【网页授权获取用户信息】接口获取到用户的Openid。
    +   * 
    + */ + @XStreamAlias("sub_openid") + private String subOpenid; + + /** + *
    +   * 字段名:场景信息
    +   * 变量名:scene_info
    +   * 是否必填:否,对H5支付来说是必填
    +   * 类型:String(256)
    +   * 示例值:{
    +   * "store_id": "SZT10000",
    +   * "store_name":"腾讯大厦腾大餐厅"
    +   * }
    +   * 描述:该字段用于统一下单时上报场景信息,目前支持上报实际门店信息。
    +   * {
    +   * "store_id": "", //门店唯一标识,选填,String(32)
    +   * "store_name":"”//门店名称,选填,String(64)
    +   * }
    +   * 
    + */ + @XStreamAlias("scene_info") + private String sceneInfo; + /** + *
    +   * 字段名:浏览器指纹
    +   * 变量名:fingerprint
    +   * 是否必填:否
    +   * 详细参考 https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=15_7&index=6
    +   * 
    + */ + @XStreamAlias("fingerprint") + private String fingerprint; + + public WxPayUnifiedOrderRequest() { + } + + private WxPayUnifiedOrderRequest(Builder builder) { + setDeviceInfo(builder.deviceInfo); + setAppid(builder.appid); + setBody(builder.body); + setMchId(builder.mchId); + setSubAppId(builder.subAppId); + setSubMchId(builder.subMchId); + setNonceStr(builder.nonceStr); + setSign(builder.sign); + setDetail(builder.detail); + setAttach(builder.attach); + setOutTradeNo(builder.outTradeNo); + setFeeType(builder.feeType); + setTotalFee(builder.totalFee); + setSpbillCreateIp(builder.spbillCreateIp); + setTimeStart(builder.timeStart); + setTimeExpire(builder.timeExpire); + setGoodsTag(builder.goodsTag); + setNotifyURL(builder.notifyURL); + setTradeType(builder.tradeType); + setProductId(builder.productId); + setLimitPay(builder.limitPay); + setOpenid(builder.openid); + setSubOpenid(builder.subOpenid); + setSceneInfo(builder.sceneInfo); + fingerprint = builder.fingerprint; + } + + public static Builder newBuilder() { + return new Builder(); } public String getDeviceInfo() { @@ -416,24 +483,40 @@ public void setOpenid(String openid) { this.openid = openid; } + public String getSubOpenid() { + return this.subOpenid; + } + + public void setSubOpenid(String subOpenid) { + this.subOpenid = subOpenid; + } + + public String getSceneInfo() { + return this.sceneInfo; + } + + public void setSceneInfo(String sceneInfo) { + this.sceneInfo = sceneInfo; + } + @Override - protected void checkConstraints() { - if (!ArrayUtils.contains(TRADE_TYPES, this.getTradeType())) { - throw new IllegalArgumentException(String.format("trade_type目前必须为%s其中之一,实际值:%s", - Arrays.toString(TRADE_TYPES), this.getTradeType())); - } + protected void checkConstraints() throws WxPayException { +// if (!ArrayUtils.contains(TRADE_TYPES, this.getTradeType())) { +// throw new WxPayException(String.format("trade_type目前必须为%s其中之一,实际值:%s", +// Arrays.toString(TRADE_TYPES), this.getTradeType())); +// } if ("JSAPI".equals(this.getTradeType()) && this.getOpenid() == null) { - throw new IllegalArgumentException("当 trade_type是'JSAPI'时未指定openid"); + throw new WxPayException("当 trade_type是'JSAPI'时未指定openid"); } if ("NATIVE".equals(this.getTradeType()) && this.getProductId() == null) { - throw new IllegalArgumentException("当 trade_type是'NATIVE'时未指定product_id"); + throw new WxPayException("当 trade_type是'NATIVE'时未指定product_id"); } } @Override - public void checkAndSign(WxPayConfig config) throws WxErrorException { + public void checkAndSign(WxPayConfig config) throws WxPayException { if (StringUtils.isBlank(this.getNotifyURL())) { this.setNotifyURL(config.getNotifyUrl()); } @@ -445,12 +528,14 @@ public void checkAndSign(WxPayConfig config) throws WxErrorException { super.checkAndSign(config); } - public static class WxUnifiedOrderRequestBuilder { + public static final class Builder { private String appid; private String mchId; - private String deviceInfo; + private String subAppId; + private String subMchId; private String nonceStr; private String sign; + private String deviceInfo; private String body; private String detail; private String attach; @@ -466,155 +551,140 @@ public static class WxUnifiedOrderRequestBuilder { private String productId; private String limitPay; private String openid; + private String subOpenid; + private String sceneInfo; + private String fingerprint; - public WxUnifiedOrderRequestBuilder appid(String appid) { + private Builder() { + } + + public Builder appid(String appid) { this.appid = appid; return this; } - public WxUnifiedOrderRequestBuilder mchId(String mchId) { + public Builder mchId(String mchId) { this.mchId = mchId; return this; } - public WxUnifiedOrderRequestBuilder deviceInfo(String deviceInfo) { - this.deviceInfo = deviceInfo; + public Builder subAppId(String subAppId) { + this.subAppId = subAppId; + return this; + } + + public Builder subMchId(String subMchId) { + this.subMchId = subMchId; return this; } - public WxUnifiedOrderRequestBuilder nonceStr(String nonceStr) { + public Builder nonceStr(String nonceStr) { this.nonceStr = nonceStr; return this; } - public WxUnifiedOrderRequestBuilder sign(String sign) { + public Builder sign(String sign) { this.sign = sign; return this; } - public WxUnifiedOrderRequestBuilder body(String body) { + public Builder deviceInfo(String deviceInfo) { + this.deviceInfo = deviceInfo; + return this; + } + + public Builder body(String body) { this.body = body; return this; } - public WxUnifiedOrderRequestBuilder detail(String detail) { + public Builder detail(String detail) { this.detail = detail; return this; } - public WxUnifiedOrderRequestBuilder attach(String attach) { + public Builder attach(String attach) { this.attach = attach; return this; } - public WxUnifiedOrderRequestBuilder outTradeNo(String outTradeNo) { + public Builder outTradeNo(String outTradeNo) { this.outTradeNo = outTradeNo; return this; } - public WxUnifiedOrderRequestBuilder feeType(String feeType) { + public Builder feeType(String feeType) { this.feeType = feeType; return this; } - public WxUnifiedOrderRequestBuilder totalFee(Integer totalFee) { + public Builder totalFee(Integer totalFee) { this.totalFee = totalFee; return this; } - public WxUnifiedOrderRequestBuilder spbillCreateIp(String spbillCreateIp) { + public Builder spbillCreateIp(String spbillCreateIp) { this.spbillCreateIp = spbillCreateIp; return this; } - public WxUnifiedOrderRequestBuilder timeStart(String timeStart) { + public Builder timeStart(String timeStart) { this.timeStart = timeStart; return this; } - public WxUnifiedOrderRequestBuilder timeExpire(String timeExpire) { + public Builder timeExpire(String timeExpire) { this.timeExpire = timeExpire; return this; } - public WxUnifiedOrderRequestBuilder goodsTag(String goodsTag) { + public Builder goodsTag(String goodsTag) { this.goodsTag = goodsTag; return this; } - public WxUnifiedOrderRequestBuilder notifyURL(String notifyURL) { + public Builder notifyURL(String notifyURL) { this.notifyURL = notifyURL; return this; } - public WxUnifiedOrderRequestBuilder tradeType(String tradeType) { + public Builder tradeType(String tradeType) { this.tradeType = tradeType; return this; } - public WxUnifiedOrderRequestBuilder productId(String productId) { + public Builder productId(String productId) { this.productId = productId; return this; } - public WxUnifiedOrderRequestBuilder limitPay(String limitPay) { + public Builder limitPay(String limitPay) { this.limitPay = limitPay; return this; } - public WxUnifiedOrderRequestBuilder openid(String openid) { + public Builder openid(String openid) { this.openid = openid; return this; } - public WxUnifiedOrderRequestBuilder from(WxPayUnifiedOrderRequest origin) { - this.appid(origin.appid); - this.mchId(origin.mchId); - this.deviceInfo(origin.deviceInfo); - this.nonceStr(origin.nonceStr); - this.sign(origin.sign); - this.body(origin.body); - this.detail(origin.detail); - this.attach(origin.attach); - this.outTradeNo(origin.outTradeNo); - this.feeType(origin.feeType); - this.totalFee(origin.totalFee); - this.spbillCreateIp(origin.spbillCreateIp); - this.timeStart(origin.timeStart); - this.timeExpire(origin.timeExpire); - this.goodsTag(origin.goodsTag); - this.notifyURL(origin.notifyURL); - this.tradeType(origin.tradeType); - this.productId(origin.productId); - this.limitPay(origin.limitPay); - this.openid(origin.openid); + public Builder subOpenid(String subOpenid) { + this.subOpenid = subOpenid; + return this; + } + + public Builder sceneInfo(String sceneInfo) { + this.sceneInfo = sceneInfo; + return this; + } + + public Builder fingerprint(String fingerprint) { + this.fingerprint = fingerprint; return this; } public WxPayUnifiedOrderRequest build() { - WxPayUnifiedOrderRequest m = new WxPayUnifiedOrderRequest(); - m.appid = this.appid; - m.mchId = this.mchId; - m.deviceInfo = this.deviceInfo; - m.nonceStr = this.nonceStr; - m.sign = this.sign; - m.body = this.body; - m.detail = this.detail; - m.attach = this.attach; - m.outTradeNo = this.outTradeNo; - m.feeType = this.feeType; - m.totalFee = this.totalFee; - m.spbillCreateIp = this.spbillCreateIp; - m.timeStart = this.timeStart; - m.timeExpire = this.timeExpire; - m.goodsTag = this.goodsTag; - m.notifyURL = this.notifyURL; - m.tradeType = this.tradeType; - m.productId = this.productId; - m.limitPay = this.limitPay; - m.openid = this.openid; - return m; + return new WxPayUnifiedOrderRequest(this); } } - } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBaseResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBaseResult.java index 899703c84d..d4b96dc408 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBaseResult.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBaseResult.java @@ -1,13 +1,12 @@ package com.github.binarywang.wxpay.bean.result; -import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.impl.WxPayServiceAbstractImpl; import com.github.binarywang.wxpay.util.SignUtils; import com.google.common.base.Joiner; import com.google.common.collect.Maps; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.annotations.XStreamAlias; -import me.chanjar.weixin.common.bean.result.WxError; -import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.common.util.ToStringUtils; import me.chanjar.weixin.common.util.xml.XStreamInitializer; import org.apache.commons.lang3.StringUtils; @@ -308,17 +307,17 @@ protected Integer getXmlValueAsInt(String... path) { /** * 校验返回结果签名 */ - public void checkResult(WxPayServiceImpl wxPayService) throws WxErrorException { + public void checkResult(WxPayServiceAbstractImpl wxPayService) throws WxPayException { //校验返回结果签名 Map map = toMap(); if (getSign() != null && !SignUtils.checkSign(map, wxPayService.getConfig().getMchKey())) { this.getLogger().debug("校验结果签名失败,参数:{}", map); - throw new WxErrorException(WxError.newBuilder().setErrorCode(-1).setErrorMsg("参数格式校验错误!").build()); + throw new WxPayException("参数格式校验错误!"); } //校验结果是否成功 - if (!"SUCCESS".equalsIgnoreCase(getReturnCode()) - || !"SUCCESS".equalsIgnoreCase(getResultCode())) { + if (!StringUtils.equalsAny(StringUtils.trimToEmpty(getReturnCode()).toUpperCase(), "SUCCESS", "") + || !StringUtils.equalsAny(StringUtils.trimToEmpty(getResultCode()).toUpperCase(), "SUCCESS", "")) { StringBuilder errorMsg = new StringBuilder(); if (getReturnCode() != null) { errorMsg.append("返回代码:").append(getReturnCode()); @@ -336,12 +335,8 @@ public void checkResult(WxPayServiceImpl wxPayService) throws WxErrorException { errorMsg.append(",错误详情:").append(getErrCodeDes()); } - WxError error = WxError.newBuilder() - .setErrorCode(-1) - .setErrorMsg(errorMsg.toString()) - .build(); - this.getLogger().error("\n结果业务代码异常,返回結果:{},\n{}", map, error); - throw new WxErrorException(error); + this.getLogger().error("\n结果业务代码异常,返回結果:{},\n{}", map, errorMsg.toString()); + throw WxPayException.from(this); } } } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBillBaseResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBillBaseResult.java new file mode 100644 index 0000000000..e469ff4538 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBillBaseResult.java @@ -0,0 +1,305 @@ +package com.github.binarywang.wxpay.bean.result; + +import java.io.Serializable; + +public class WxPayBillBaseResult implements Serializable { + /* + * 交易时间:2017-04-06 01:00:02 公众账号ID: 商户号: 子商户号:0 设备号:WEB 微信订单号: 商户订单号:2017040519091071873216 用户标识: 交易类型:NATIVE + * 交易状态:REFUND 付款银行:CFT 货币种类:CNY 总金额:0.00 企业红包金额:0.00 微信退款单号: 商户退款单号:20170406010000933 退款金额:0.01 企业红包退款金额:0.00 + * 退款类型:ORIGINAL 退款状态:SUCCESS 商品名称: 商户数据包: 手续费:0.00000 费率 :0.60% + */ + private static final long serialVersionUID = 1L; + /** + * 交易时间 + */ + private String tradeTime; + /** + * 公众账号ID + */ + private String appId; + /** + * 商户号 + */ + private String mchId; + /** + * 子商户号 + */ + private String subMchId; + /** + * 设备号 + */ + private String deviceInfo; + /** + * 微信订单号 + */ + private String transationId; + /** + * 商户订单号 + */ + private String outTradeNo; + /** + * 用户标识 + */ + private String openId; + /** + * 交易类型 + */ + private String tradeType; + /** + * 交易状态 + */ + private String tradeState; + /** + * 付款银行 + */ + private String bankType; + /** + * 货币种类 + */ + private String feeType; + /** + * 总金额 + */ + private String totalFee; + /** + * 企业红包金额 + */ + private String couponFee; + /** + * 微信退款单号 + */ + private String refundId; + /** + * 商户退款单号 + */ + private String outRefundNo; + /** + * 退款金额 + */ + private String settlementRefundFee; + /** + * 企业红包退款金额 + */ + private String couponRefundFee; + /** + * 退款类型 + */ + private String refundChannel; + /** + * 退款状态 + */ + private String refundState; + /** + * 商品名称 + */ + private String body; + /** + * 商户数据包 + */ + private String attach; + /** + * 手续费 + */ + private String poundage; + /** + * 费率 + */ + private String poundageRate; + + public static long getSerialversionuid() { + return serialVersionUID; + } + + public String getTradeTime() { + return tradeTime; + } + + public void setTradeTime(String tradeTime) { + this.tradeTime = tradeTime; + } + + public String getAppId() { + return appId; + } + + public void setAppId(String appId) { + this.appId = appId; + } + + public String getMchId() { + return mchId; + } + + public void setMchId(String mchId) { + this.mchId = mchId; + } + + public String getSubMchId() { + return subMchId; + } + + public void setSubMchId(String subMchId) { + this.subMchId = subMchId; + } + + public String getDeviceInfo() { + return deviceInfo; + } + + public void setDeviceInfo(String deviceInfo) { + this.deviceInfo = deviceInfo; + } + + public String getTransationId() { + return transationId; + } + + public void setTransationId(String transationId) { + this.transationId = transationId; + } + + public String getOutTradeNo() { + return outTradeNo; + } + + public void setOutTradeNo(String outTradeNo) { + this.outTradeNo = outTradeNo; + } + + public String getOpenId() { + return openId; + } + + public void setOpenId(String openId) { + this.openId = openId; + } + + public String getTradeType() { + return tradeType; + } + + public void setTradeType(String tradeType) { + this.tradeType = tradeType; + } + + public String getTradeState() { + return tradeState; + } + + public void setTradeState(String tradeState) { + this.tradeState = tradeState; + } + + public String getBankType() { + return bankType; + } + + public void setBankType(String bankType) { + this.bankType = bankType; + } + + public String getFeeType() { + return feeType; + } + + public void setFeeType(String feeType) { + this.feeType = feeType; + } + + public String getTotalFee() { + return totalFee; + } + + public void setTotalFee(String totalFee) { + this.totalFee = totalFee; + } + + public String getCouponFee() { + return couponFee; + } + + public void setCouponFee(String couponFee) { + this.couponFee = couponFee; + } + + public String getRefundId() { + return refundId; + } + + public void setRefundId(String refundId) { + this.refundId = refundId; + } + + public String getOutRefundNo() { + return outRefundNo; + } + + public void setOutRefundNo(String outRefundNo) { + this.outRefundNo = outRefundNo; + } + + public String getSettlementRefundFee() { + return settlementRefundFee; + } + + public void setSettlementRefundFee(String settlementRefundFee) { + this.settlementRefundFee = settlementRefundFee; + } + + public String getCouponRefundFee() { + return couponRefundFee; + } + + public void setCouponRefundFee(String couponRefundFee) { + this.couponRefundFee = couponRefundFee; + } + + public String getRefundChannel() { + return refundChannel; + } + + public void setRefundChannel(String refundChannel) { + this.refundChannel = refundChannel; + } + + public String getRefundState() { + return refundState; + } + + public void setRefundState(String refundState) { + this.refundState = refundState; + } + + public String getBody() { + return body; + } + + public void setBody(String body) { + this.body = body; + } + + public String getAttach() { + return attach; + } + + public void setAttach(String attach) { + this.attach = attach; + } + + public String getPoundage() { + return poundage; + } + + public void setPoundage(String poundage) { + this.poundage = poundage; + } + + public String getPoundageRate() { + return poundageRate; + } + + public void setPoundageRate(String poundageRate) { + this.poundageRate = poundageRate; + } + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBillResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBillResult.java new file mode 100644 index 0000000000..a7fb25bef2 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBillResult.java @@ -0,0 +1,83 @@ +package com.github.binarywang.wxpay.bean.result; + +import java.io.Serializable; +import java.util.List; + +public class WxPayBillResult implements Serializable { + /** + * 对账返回对象 + */ + private static final long serialVersionUID = 1L; + + private List wxPayBillBaseResultLst; + /** + * 总交易单数 + */ + private String totalRecord; + /** + * 总交易额 + */ + private String totalFee; + /** + * 总退款金额 + */ + private String totalRefundFee; + /** + * 总代金券或立减优惠退款金额 + */ + private String totalCouponFee; + /** + * 手续费总金额 + */ + private String totalPoundageFee; + + public List getWxPayBillBaseResultLst() { + return wxPayBillBaseResultLst; + } + + public void setWxPayBillBaseResultLst(List wxPayBillBaseResultLst) { + this.wxPayBillBaseResultLst = wxPayBillBaseResultLst; + } + + public String getTotalRecord() { + return totalRecord; + } + + public void setTotalRecord(String totalRecord) { + this.totalRecord = totalRecord; + } + + public String getTotalFee() { + return totalFee; + } + + public void setTotalFee(String totalFee) { + this.totalFee = totalFee; + } + + public String getTotalRefundFee() { + return totalRefundFee; + } + + public void setTotalRefundFee(String totalRefundFee) { + this.totalRefundFee = totalRefundFee; + } + + public String getTotalCouponFee() { + return totalCouponFee; + } + + public void setTotalCouponFee(String totalCouponFee) { + this.totalCouponFee = totalCouponFee; + } + + public String getTotalPoundageFee() { + return totalPoundageFee; + } + + public void setTotalPoundageFee(String totalPoundageFee) { + this.totalPoundageFee = totalPoundageFee; + } + + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderNotifyResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderNotifyResult.java index 723702a455..a411cd2c70 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderNotifyResult.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderNotifyResult.java @@ -25,12 +25,12 @@ public class WxPayOrderNotifyResult extends WxPayBaseResult implements Serializa /** *
    -   * 设备号
    -   * device_info
    -   * 否
    -   * String(32)
    -   * 013467007045764
    -   * 微信支付分配的终端设备号,
    +   * 字段名:设备号
    +   * 变量名:device_info
    +   * 是否必填:否
    +   * 类型:String(32)
    +   * 示例值:013467007045764
    +   * 描述:微信支付分配的终端设备号,
        * 
    */ @XStreamAlias("device_info") @@ -38,12 +38,12 @@ public class WxPayOrderNotifyResult extends WxPayBaseResult implements Serializa /** *
    -   * 用户标识
    -   * openid
    -   * 是
    -   * String(128)
    -   * wxd930ea5d5a258f4f
    -   * 用户在商户appid下的唯一标识
    +   * 字段名:用户标识
    +   * 变量名:openid
    +   * 是否必填:是
    +   * 类型:String(128)
    +   * 示例值:wxd930ea5d5a258f4f
    +   * 描述:用户在商户appid下的唯一标识
        * 
    */ @XStreamAlias("openid") @@ -51,25 +51,52 @@ public class WxPayOrderNotifyResult extends WxPayBaseResult implements Serializa /** *
    -   * 是否关注公众账号
    -   * is_subscribe
    -   * 否
    -   * String(1)
    -   * Y
    -   * 用户是否关注公众账号,Y-关注,N-未关注,仅在公众账号类型支付有效
    +   * 字段名:是否关注公众账号
    +   * 变量名:is_subscribe
    +   * 是否必填:否
    +   * 类型:String(1)
    +   * 示例值:Y
    +   * 描述:用户是否关注公众账号,Y-关注,N-未关注,仅在公众账号类型支付有效
        * 
    */ @XStreamAlias("is_subscribe") private String isSubscribe; + /** + *
    +   * 字段名:用户子标识
    +   * 变量名:sub_openid
    +   * 是否必填:是
    +   * 类型:String(128)
    +   * 示例值:wxd930ea5d5a258f4f
    +   * 描述:用户在子商户appid下的唯一标识
    +   * 
    + */ + @XStreamAlias("sub_openid") + private String subOpenid; + + /** + *
    +   * 字段名:是否关注子公众账号
    +   * 变量名:sub_is_subscribe
    +   * 是否必填:否
    +   * 类型:String(1)
    +   * 示例值:Y
    +   * 描述:用户是否关注子公众账号,Y-关注,N-未关注,仅在公众账号类型支付有效
    +   * 
    + */ + @XStreamAlias("sub_is_subscribe") + private String subIsSubscribe; + /** *
    -   * 交易类型
    -   * trade_type
    -   * 是
    -   * String(16)
    -   * JSAPI	JSAPI、NATIVE、APP
    +   * 字段名:交易类型
    +   * 变量名:trade_type
    +   * 是否必填:是
    +   * 类型:String(16)
    +   * 示例值:JSAPI
    +   * JSA描述:PI、NATIVE、APP
        * 
    */ @XStreamAlias("trade_type") @@ -78,86 +105,85 @@ public class WxPayOrderNotifyResult extends WxPayBaseResult implements Serializa /** *
    -   * 付款银行
    -   * bank_type
    -   * 是
    -   * String(16)
    -   * CMC
    -   * 银行类型,采用字符串类型的银行标识,银行类型见银行列表
    +   * 字段名:付款银行
    +   * 变量名:bank_type
    +   * 是否必填:是
    +   * 类型:String(16)
    +   * 示例值:CMC
    +   * 描述:银行类型,采用字符串类型的银行标识,银行类型见银行列表
        * 
    */ @XStreamAlias("bank_type") private String bankType; - /** *
    -   * 订单金额
    -   * total_fee
    -   * 是
    -   * Int
    -   * 100
    -   * 订单总金额,单位为分
    +   * 字段名:订单金额
    +   * 变量名:total_fee
    +   * 是否必填:是
    +   * 类型:Int
    +   * 示例值:100
    +   * 描述:订单总金额,单位为分
        * 
    */ @XStreamAlias("total_fee") private Integer totalFee; /** *
    -   * 应结订单金额
    -   * settlement_total_fee
    -   * 否
    -   * Int
    -   * 100
    -   * 应结订单金额=订单金额-非充值代金券金额,应结订单金额<=订单金额。
    +   * 字段名:应结订单金额
    +   * 变量名:settlement_total_fee
    +   * 是否必填:否
    +   * 类型:Int
    +   * 示例值:100
    +   * 描述:应结订单金额=订单金额-非充值代金券金额,应结订单金额<=订单金额。
        * 
    */ @XStreamAlias("settlement_total_fee") private Integer settlementTotalFee; /** *
    -   * 货币种类
    -   * fee_type
    -   * 否
    -   * String(8)
    -   * CNY
    -   * 货币类型,符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
    +   * 字段名:货币种类
    +   * 变量名:fee_type
    +   * 是否必填:否
    +   * 类型:String(8)
    +   * 示例值:CNY
    +   * 描述:货币类型,符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
        * 
    */ @XStreamAlias("fee_type") private String feeType; /** *
    -   * 现金支付金额
    -   * cash_fee
    -   * 是
    -   * Int
    -   * 100
    -   * 现金支付金额订单现金支付金额,详见支付金额
    +   * 字段名:现金支付金额
    +   * 变量名:cash_fee
    +   * 是否必填:是
    +   * 类型:Int
    +   * 示例值:100
    +   * 描述:现金支付金额订单现金支付金额,详见支付金额
        * 
    */ @XStreamAlias("cash_fee") private Integer cashFee; /** *
    -   * 现金支付货币类型
    -   * cash_fee_type
    -   * 否
    -   * String(16)
    -   * CNY
    -   * 货币类型,符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
    +   * 字段名:现金支付货币类型
    +   * 变量名:cash_fee_type
    +   * 是否必填:否
    +   * 类型:String(16)
    +   * 示例值:CNY
    +   * 描述:货币类型,符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
        * 
    */ @XStreamAlias("cash_fee_type") private String cashFeeType; /** *
    -   * 总代金券金额
    -   * coupon_fee
    -   * 否
    -   * Int
    -   * 10
    -   * 代金券金额<=订单金额,订单金额-代金券金额=现金支付金额,详见支付金额
    +   * 字段名:总代金券金额
    +   * 变量名:coupon_fee
    +   * 是否必填:否
    +   * 类型:Int
    +   * 示例值:10
    +   * 描述:代金券金额<=订单金额,订单金额-代金券金额=现金支付金额,详见支付金额
        * 
    */ @XStreamAlias("coupon_fee") @@ -165,12 +191,12 @@ public class WxPayOrderNotifyResult extends WxPayBaseResult implements Serializa /** *
    -   * 代金券使用数量
    -   * coupon_count
    -   * 否
    -   * Int
    -   * 1
    -   * 代金券使用数量
    +   * 字段名:代金券使用数量
    +   * 变量名:coupon_count
    +   * 是否必填:否
    +   * 类型:Int
    +   * 示例值:1
    +   * 描述:代金券使用数量
        * 
    */ @XStreamAlias("coupon_count") @@ -180,12 +206,12 @@ public class WxPayOrderNotifyResult extends WxPayBaseResult implements Serializa /** *
    -   * 微信支付订单号
    -   * transaction_id
    -   * 是
    -   * String(32)
    -   * 1217752501201407033233368018
    -   * 微信支付订单号
    +   * 字段名:微信支付订单号
    +   * 变量名:transaction_id
    +   * 是否必填:是
    +   * 类型:String(32)
    +   * 示例值:1217752501201407033233368018
    +   * 描述:微信支付订单号
        * 
    */ @XStreamAlias("transaction_id") @@ -193,36 +219,36 @@ public class WxPayOrderNotifyResult extends WxPayBaseResult implements Serializa /** *
    -   * 商户订单号
    -   * out_trade_no
    -   * 是
    -   * String(32)
    -   * 1212321211201407033568112322
    -   * 商户系统的订单号,与请求一致。
    +   * 字段名:商户订单号
    +   * 变量名:out_trade_no
    +   * 是否必填:是
    +   * 类型:String(32)
    +   * 示例值:1212321211201407033568112322
    +   * 描述:商户系统的订单号,与请求一致。
        * 
    */ @XStreamAlias("out_trade_no") private String outTradeNo; /** *
    -   * 商家数据包
    -   * attach
    -   * 否
    -   * String(128)
    -   * 123456
    -   * 商家数据包,原样返回
    +   * 字段名:商家数据包
    +   * 变量名:attach
    +   * 是否必填:否
    +   * 类型:String(128)
    +   * 示例值:123456
    +   * 描述:商家数据包,原样返回
        * 
    */ @XStreamAlias("attach") private String attach; /** *
    -   * 支付完成时间
    -   * time_end
    -   * 是
    -   * String(14)
    -   * 20141030133525
    -   * 支付完成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则
    +   * 字段名:支付完成时间
    +   * 变量名:time_end
    +   * 是否必填:是
    +   * 类型:String(14)
    +   * 示例值:20141030133525
    +   * 描述:支付完成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则
        * 
    */ @XStreamAlias("time_end") @@ -373,6 +399,22 @@ public void setTimeEnd(String timeEnd) { this.timeEnd = timeEnd; } + public String getSubOpenid() { + return this.subOpenid; + } + + public void setSubOpenid(String subOpenid) { + this.subOpenid = subOpenid; + } + + public String getSubIsSubscribe() { + return this.subIsSubscribe; + } + + public void setSubIsSubscribe(String subIsSubscribe) { + this.subIsSubscribe = subIsSubscribe; + } + @Override public Map toMap() { Map resultMap = BeanUtils.xmlBean2Map(this); diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPaySandboxSignKeyResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPaySandboxSignKeyResult.java new file mode 100644 index 0000000000..6326f694d3 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPaySandboxSignKeyResult.java @@ -0,0 +1,35 @@ +package com.github.binarywang.wxpay.bean.result; + +import com.thoughtworks.xstream.annotations.XStreamAlias; + +/** + *
    + *  Created by BinaryWang on 2017/6/18.
    + * 
    + * + * @author Binary Wang + */ +@XStreamAlias("xml") +public class WxPaySandboxSignKeyResult extends WxPayBaseResult { + + /** + *
    +   * 沙箱密钥
    +   * sandbox_signkey
    +   * 否
    +   * 013467007045764
    +   * String(32)
    +   * 返回的沙箱密钥
    +   * 
    + */ + @XStreamAlias("sandbox_signkey") + private String sandboxSignKey; + + public String getSandboxSignKey() { + return sandboxSignKey; + } + + public void setSandboxSignKey(String sandboxSignKey) { + this.sandboxSignKey = sandboxSignKey; + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayUnifiedOrderResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayUnifiedOrderResult.java index f6d82bd184..92c98bda48 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayUnifiedOrderResult.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayUnifiedOrderResult.java @@ -25,6 +25,12 @@ public class WxPayUnifiedOrderResult extends WxPayBaseResult { @XStreamAlias("trade_type") private String tradeType; + /** + * mweb_url 支付跳转链接 + */ + @XStreamAlias("mweb_url") + private String mwebUrl; + /** * trade_type为NATIVE时有返回,用于生成二维码,展示给用户进行扫码支付 */ @@ -54,4 +60,12 @@ public String getCodeURL() { public void setCodeURL(String codeURL) { this.codeURL = codeURL; } + + public String getMwebUrl() { + return mwebUrl; + } + + public void setMwebUrl(String mwebUrl) { + this.mwebUrl = mwebUrl; + } } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java index 838b57914f..b51f28e94a 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java @@ -1,10 +1,15 @@ package com.github.binarywang.wxpay.config; +import com.github.binarywang.wxpay.exception.WxPayException; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.http.ssl.SSLContexts; import javax.net.ssl.SSLContext; import java.io.File; import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; import java.security.KeyStore; /** @@ -13,6 +18,17 @@ * @author Binary Wang (https://github.com/binarywang) */ public class WxPayConfig { + + /** + * http请求连接超时时间 + */ + private int httpConnectionTimeout = 5000; + + /** + * http请求数据读取等待时间 + */ + private int httpTimeout = 10000; + private String appId; private String subAppId; private String mchId; @@ -142,25 +158,73 @@ public void setUseSandboxEnv(boolean useSandboxEnv) { this.useSandboxEnv = useSandboxEnv; } - public SSLContext initSSLContext() { - if (null == mchId) { - throw new IllegalArgumentException("请确保商户号mch_id已设置"); + public SSLContext initSSLContext() throws WxPayException { + if (StringUtils.isBlank(mchId)) { + throw new WxPayException("请确保商户号mchId已设置"); + } + + if (StringUtils.isBlank(this.keyPath)) { + throw new WxPayException("请确保证书文件地址keyPath已配置"); } - File file = new File(this.keyPath); - if (!file.exists()) { - throw new RuntimeException("证书文件:【" + file.getPath() + "】不存在!"); + InputStream inputStream; + final String prefix = "classpath:"; + String fileHasProblemMsg = "证书文件【" + this.keyPath + "】有问题,请核实!"; + String fileNotFoundMsg = "证书文件【" + this.keyPath + "】不存在,请核实!"; + if (this.keyPath.startsWith(prefix)) { + String path = StringUtils.removeFirst(this.keyPath, prefix); + if (!path.startsWith("/")) { + path = "/" + path; + } + inputStream = WxPayConfig.class.getResourceAsStream(path); + if (inputStream == null) { + throw new WxPayException(fileNotFoundMsg); + } + } else { + try { + File file = new File(this.keyPath); + if (!file.exists()) { + throw new WxPayException(fileNotFoundMsg); + } + + inputStream = new FileInputStream(file); + } catch (IOException e) { + throw new WxPayException(fileHasProblemMsg, e); + } } try { - FileInputStream inputStream = new FileInputStream(file); KeyStore keystore = KeyStore.getInstance("PKCS12"); char[] partnerId2charArray = mchId.toCharArray(); keystore.load(inputStream, partnerId2charArray); this.sslContext = SSLContexts.custom().loadKeyMaterial(keystore, partnerId2charArray).build(); return this.sslContext; } catch (Exception e) { - throw new RuntimeException("证书文件有问题,请核实!", e); + throw new WxPayException(fileHasProblemMsg, e); + } finally { + IOUtils.closeQuietly(inputStream); } } + + /** + * http请求连接超时时间 + */ + public int getHttpConnectionTimeout() { + return this.httpConnectionTimeout; + } + + public void setHttpConnectionTimeout(int httpConnectionTimeout) { + this.httpConnectionTimeout = httpConnectionTimeout; + } + + /** + * http请求数据读取等待时间 + */ + public int getHttpTimeout() { + return this.httpTimeout; + } + + public void setHttpTimeout(int httpTimeout) { + this.httpTimeout = httpTimeout; + } } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/exception/WxPayException.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/exception/WxPayException.java new file mode 100644 index 0000000000..948c7a4995 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/exception/WxPayException.java @@ -0,0 +1,158 @@ +package com.github.binarywang.wxpay.exception; + +import com.github.binarywang.wxpay.bean.result.WxPayBaseResult; +import com.google.common.base.Joiner; + +/** + *
    + * 微信支付异常结果类
    + * Created by Binary Wang on 2017-6-6.
    + * 
    + */ +public class WxPayException extends Exception { + private String customErrorMsg; + /** + * 返回状态码 + */ + private String returnCode; + /** + * 返回信息 + */ + private String returnMsg; + + /** + * 业务结果 + */ + private String resultCode; + + /** + * 错误代码 + */ + private String errCode; + + /** + * 错误代码描述 + */ + private String errCodeDes; + + /** + * 微信支付返回的结果xml字符串 + */ + private String xmlString; + + public WxPayException(String customErrorMsg) { + super(customErrorMsg); + this.customErrorMsg = customErrorMsg; + } + + public WxPayException(String customErrorMsg, Throwable tr) { + super(customErrorMsg, tr); + this.customErrorMsg = customErrorMsg; + } + + private WxPayException(Builder builder) { + super(builder.buildErrorMsg()); + returnCode = builder.returnCode; + returnMsg = builder.returnMsg; + resultCode = builder.resultCode; + errCode = builder.errCode; + errCodeDes = builder.errCodeDes; + xmlString = builder.xmlString; + } + + public static WxPayException from(WxPayBaseResult payBaseResult) { + return WxPayException.newBuilder() + .xmlString(payBaseResult.getXmlString()) + .returnMsg(payBaseResult.getReturnMsg()) + .returnCode(payBaseResult.getReturnCode()) + .resultCode(payBaseResult.getResultCode()) + .errCode(payBaseResult.getErrCode()) + .errCodeDes(payBaseResult.getErrCodeDes()) + .build(); + } + + public String getXmlString() { + return this.xmlString; + } + + public String getReturnCode() { + return this.returnCode; + } + + public String getReturnMsg() { + return this.returnMsg; + } + + public String getResultCode() { + return this.resultCode; + } + + public String getErrCode() { + return this.errCode; + } + + public String getErrCodeDes() { + return this.errCodeDes; + } + + public static Builder newBuilder() { + return new Builder(); + } + + public static final class Builder { + private String returnCode; + private String returnMsg; + private String resultCode; + private String errCode; + private String errCodeDes; + private String xmlString; + + private Builder() { + } + + public Builder returnCode(String returnCode) { + this.returnCode = returnCode; + return this; + } + + public Builder returnMsg(String returnMsg) { + this.returnMsg = returnMsg; + return this; + } + + public Builder resultCode(String resultCode) { + this.resultCode = resultCode; + return this; + } + + public Builder errCode(String errCode) { + this.errCode = errCode; + return this; + } + + public Builder errCodeDes(String errCodeDes) { + this.errCodeDes = errCodeDes; + return this; + } + + public Builder xmlString(String xmlString) { + this.xmlString = xmlString; + return this; + } + + public WxPayException build() { + return new WxPayException(this); + } + + public String buildErrorMsg() { + return Joiner.on(",").skipNulls().join(new String[]{ + returnCode == null ? null : String.format("返回代码:[%s]", returnCode), + returnMsg == null ? null : String.format("返回信息:[%s]", returnMsg), + resultCode == null ? null : String.format("结果代码:[%s]", resultCode), + errCode == null ? null : String.format("错误代码:[%s]", errCode), + errCodeDes == null ? null : String.format("错误详情:[%s]", errCodeDes), + xmlString == null ? null : "微信返回的原始报文:\n" + xmlString, + }); + } + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java index c04d77061f..2eef29b85a 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java @@ -1,9 +1,10 @@ package com.github.binarywang.wxpay.service; +import com.github.binarywang.wxpay.bean.coupon.*; import com.github.binarywang.wxpay.bean.request.*; import com.github.binarywang.wxpay.bean.result.*; import com.github.binarywang.wxpay.config.WxPayConfig; -import me.chanjar.weixin.common.exception.WxErrorException; +import com.github.binarywang.wxpay.exception.WxPayException; import java.io.File; import java.util.Map; @@ -33,7 +34,7 @@ public interface WxPayService { * @param transactionId 微信订单号 * @param outTradeNo 商户系统内部的订单号,当没提供transactionId时需要传这个。 */ - WxPayOrderQueryResult queryOrder(String transactionId, String outTradeNo) throws WxErrorException; + WxPayOrderQueryResult queryOrder(String transactionId, String outTradeNo) throws WxPayException; /** *
    @@ -49,7 +50,7 @@ public interface WxPayService {
        *
        * @param outTradeNo 商户系统内部的订单号
        */
    -  WxPayOrderCloseResult closeOrder(String outTradeNo) throws WxErrorException;
    +  WxPayOrderCloseResult closeOrder(String outTradeNo) throws WxPayException;
     
       /**
        * 统一下单(详见http://com.github.binarywang.wechat.pay.bean.pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1)
    @@ -58,7 +59,7 @@ public interface WxPayService {
        *
        * @param request 请求对象,注意一些参数如appid、mchid等不用设置,方法内会自动从配置对象中获取到(前提是对应配置中已经设置)
        */
    -  WxPayUnifiedOrderResult unifiedOrder(WxPayUnifiedOrderRequest request) throws WxErrorException;
    +  WxPayUnifiedOrderResult unifiedOrder(WxPayUnifiedOrderRequest request) throws WxPayException;
     
       /**
        * 该接口调用“统一下单”接口,并拼装发起支付请求需要的参数
    @@ -66,7 +67,7 @@ public interface WxPayService {
        *
        * @param request 请求对象,注意一些参数如appid、mchid等不用设置,方法内会自动从配置对象中获取到(前提是对应配置中已经设置)
        */
    -  Map getPayInfo(WxPayUnifiedOrderRequest request) throws WxErrorException;
    +  Map getPayInfo(WxPayUnifiedOrderRequest request) throws WxPayException;
     
       /**
        * 获取配置
    @@ -88,7 +89,7 @@ public interface WxPayService {
        * @param request 请求对象
        * @return 退款操作结果
        */
    -  WxPayRefundResult refund(WxPayRefundRequest request) throws WxErrorException;
    +  WxPayRefundResult refund(WxPayRefundRequest request) throws WxPayException;
     
       /**
        * 
    @@ -108,13 +109,13 @@ public interface WxPayService {
        * @return 退款信息
        */
       WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, String outRefundNo, String refundId)
    -    throws WxErrorException;
    +    throws WxPayException;
     
       /**
        * 读取支付结果通知
    -   * 详见http://com.github.binarywang.wechat.pay.bean.pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7
    +   * 详见https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7
        */
    -  WxPayOrderNotifyResult getOrderNotifyResult(String xmlData) throws WxErrorException;
    +  WxPayOrderNotifyResult getOrderNotifyResult(String xmlData) throws WxPayException;
     
       /**
        * 发送微信红包给个人用户
    @@ -128,7 +129,7 @@ WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, Stri
        *
        * @param request 请求对象
        */
    -  WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request) throws WxErrorException;
    +  WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request) throws WxPayException;
     
       /**
        * 
    @@ -141,7 +142,7 @@ WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, Stri
        *
        * @param mchBillNo 商户发放红包的商户订单号,比如10000098201411111234567890
        */
    -  WxPayRedpackQueryResult queryRedpack(String mchBillNo) throws WxErrorException;
    +  WxPayRedpackQueryResult queryRedpack(String mchBillNo) throws WxPayException;
     
       /**
        * 
    @@ -155,7 +156,7 @@ WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, Stri
        *
        * @param request 请求对象
        */
    -  WxEntPayResult entPay(WxEntPayRequest request) throws WxErrorException;
    +  WxEntPayResult entPay(WxEntPayRequest request) throws WxPayException;
     
       /**
        * 
    @@ -167,7 +168,7 @@ WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, Stri
        *
        * @param partnerTradeNo 商户订单号
        */
    -  WxEntPayQueryResult queryEntPay(String partnerTradeNo) throws WxErrorException;
    +  WxEntPayQueryResult queryEntPay(String partnerTradeNo) throws WxPayException;
     
       /**
        * 
    @@ -225,7 +226,7 @@ WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, Stri
        * 是否需要证书:不需要
        * 
    */ - void report(WxPayReportRequest request) throws WxErrorException; + void report(WxPayReportRequest request) throws WxPayException; /** *
    @@ -246,7 +247,7 @@ WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, Stri
        * @param deviceInfo 设备号	device_info	非必传参数,终端设备号
        * @return 保存到本地的临时文件
        */
    -  File downloadBill(String billDate, String billType, String tarType, String deviceInfo) throws WxErrorException;
    +  WxPayBillResult downloadBill(String billDate, String billType, String tarType, String deviceInfo) throws WxPayException;
     
       /**
        * 
    @@ -260,7 +261,7 @@ WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, Stri
        * 是否需要证书:不需要。
        * 
    */ - WxPayMicropayResult micropay(WxPayMicropayRequest request) throws WxErrorException; + WxPayMicropayResult micropay(WxPayMicropayRequest request) throws WxPayException; /** *
    @@ -276,7 +277,7 @@ WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, Stri
        *  是否需要证书:请求需要双向证书。
        * 
    */ - WxPayOrderReverseResult reverseOrder(WxPayOrderReverseRequest request) throws WxErrorException; + WxPayOrderReverseResult reverseOrder(WxPayOrderReverseRequest request) throws WxPayException; /** *
    @@ -291,7 +292,7 @@ WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, Stri
        *
        * @param request 请求对象
        */
    -  String shorturl(WxPayShorturlRequest request) throws WxErrorException;
    +  String shorturl(WxPayShorturlRequest request) throws WxPayException;
     
       /**
        * 
    @@ -301,7 +302,7 @@ WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, Stri
        * @param longUrl 需要被压缩的网址
        * @see WxPayService#shorturl(WxPayShorturlRequest)
        */
    -  String shorturl(String longUrl) throws WxErrorException;
    +  String shorturl(String longUrl) throws WxPayException;
     
       /**
        * 
    @@ -316,7 +317,7 @@ WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, Stri
        * @param request 请求对象
        * @return openid
        */
    -  String authcode2Openid(WxPayAuthcode2OpenidRequest request) throws WxErrorException;
    +  String authcode2Openid(WxPayAuthcode2OpenidRequest request) throws WxPayException;
     
       /**
        * 
    @@ -327,5 +328,44 @@ WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, Stri
        * @return openid
        * @see WxPayService#authcode2Openid(WxPayAuthcode2OpenidRequest)
        */
    -  String authcode2Openid(String authCode) throws WxErrorException;
    +  String authcode2Openid(String authCode) throws WxPayException;
    +
    +  /**
    +   * 
    +   * 获取仿真测试系统的验签密钥
    +   * 请求Url: https://api.mch.weixin.qq.com/sandboxnew/pay/getsignkey
    +   * 是否需要证书: 否
    +   * 请求方式: POST
    +   * 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=23_1
    +   * 
    + */ + String getSandboxSignKey() throws WxPayException; + + /** + *
    +   * 发放代金券
    +   * 接口请求链接:https://api.mch.weixin.qq.com/mmpaymkttransfers/send_coupon
    +   * 是否需要证书:请求需要双向证书。
    +   * 文档地址:https://pay.weixin.qq.com/wiki/doc/api/tools/sp_coupon.php?chapter=12_3
    +   * 
    + */ + WxPayCouponSendResult sendCoupon(WxPayCouponSendRequest request) throws WxPayException; + + /** + *
    +   * 查询代金券批次
    +   * 接口请求链接:https://api.mch.weixin.qq.com/mmpaymkttransfers/query_coupon_stock
    +   * 文档地址:https://pay.weixin.qq.com/wiki/doc/api/tools/sp_coupon.php?chapter=12_4
    +   * 
    + */ + WxPayCouponStockQueryResult queryCouponStock(WxPayCouponStockQueryRequest request) throws WxPayException; + + /** + *
    +   * 查询代金券信息
    +   * 接口请求链接:https://api.mch.weixin.qq.com/mmpaymkttransfers/querycouponsinfo
    +   * 文档地址:https://pay.weixin.qq.com/wiki/doc/api/tools/sp_coupon.php?chapter=12_5
    +   * 
    + */ + WxPayCouponInfoQueryResult queryCouponInfo(WxPayCouponInfoQueryRequest request) throws WxPayException; } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceAbstractImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceAbstractImpl.java new file mode 100644 index 0000000000..616f860988 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceAbstractImpl.java @@ -0,0 +1,505 @@ +package com.github.binarywang.wxpay.service.impl; + +import com.github.binarywang.utils.qrcode.QrcodeUtils; +import com.github.binarywang.wxpay.bean.coupon.*; +import com.github.binarywang.wxpay.bean.request.*; +import com.github.binarywang.wxpay.bean.result.*; +import com.github.binarywang.wxpay.config.WxPayConfig; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.WxPayService; +import com.github.binarywang.wxpay.util.SignUtils; +import com.google.common.collect.Maps; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + *
    + *  微信支付接口请求抽象实现类
    + * Created by Binary Wang on 2017-7-8.
    + * 
    + * + * @author Binary Wang + */ +public abstract class WxPayServiceAbstractImpl implements WxPayService { + private static final String PAY_BASE_URL = "https://api.mch.weixin.qq.com"; + protected final Logger log = LoggerFactory.getLogger(this.getClass()); + + protected WxPayConfig config; + + @Override + public WxPayConfig getConfig() { + return this.config; + } + + @Override + public void setConfig(WxPayConfig config) { + this.config = config; + } + + private String getPayBaseUrl() { + if (this.getConfig().useSandbox()) { + return PAY_BASE_URL + "/sandboxnew"; + } + + return PAY_BASE_URL; + } + + /** + * 发送post请求 + * + * @param url 请求地址 + * @param requestStr 请求信息 + * @param useKey 是否使用证书 + * @return 返回请求结果字符串 + */ + protected abstract String post(String url, String requestStr, boolean useKey) throws WxPayException; + + @Override + public WxPayRefundResult refund(WxPayRefundRequest request) throws WxPayException { + request.checkAndSign(this.getConfig()); + + String url = this.getPayBaseUrl() + "/secapi/pay/refund"; + String responseContent = this.post(url, request.toXML(), true); + WxPayRefundResult result = WxPayBaseResult.fromXML(responseContent, WxPayRefundResult.class); + result.checkResult(this); + return result; + } + + @Override + public WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, String outRefundNo, String refundId) + throws WxPayException { + WxPayRefundQueryRequest request = new WxPayRefundQueryRequest(); + request.setOutTradeNo(StringUtils.trimToNull(outTradeNo)); + request.setTransactionId(StringUtils.trimToNull(transactionId)); + request.setOutRefundNo(StringUtils.trimToNull(outRefundNo)); + request.setRefundId(StringUtils.trimToNull(refundId)); + + request.checkAndSign(this.getConfig()); + + String url = this.getPayBaseUrl() + "/pay/refundquery"; + String responseContent = this.post(url, request.toXML(), false); + WxPayRefundQueryResult result = WxPayBaseResult.fromXML(responseContent, WxPayRefundQueryResult.class); + result.composeRefundRecords(); + result.checkResult(this); + return result; + } + + @Override + public WxPayOrderNotifyResult getOrderNotifyResult(String xmlData) throws WxPayException { + try { + log.debug("微信支付回调参数详细:{}", xmlData); + WxPayOrderNotifyResult result = WxPayOrderNotifyResult.fromXML(xmlData); + log.debug("微信支付回调结果对象:{}", result); + result.checkResult(this); + return result; + } catch (WxPayException e) { + log.error(e.getMessage(), e); + throw e; + } catch (Exception e) { + log.error(e.getMessage(), e); + throw new WxPayException("发生异常," + e.getMessage(), e); + } + } + + @Override + public WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request) throws WxPayException { + request.checkAndSign(this.getConfig()); + + String url = this.getPayBaseUrl() + "/mmpaymkttransfers/sendredpack"; + if (request.getAmtType() != null) { + //裂变红包 + url = this.getPayBaseUrl() + "/mmpaymkttransfers/sendgroupredpack"; + } + + String responseContent = this.post(url, request.toXML(), true); + WxPaySendRedpackResult result = WxPayBaseResult.fromXML(responseContent, WxPaySendRedpackResult.class); + //毋须校验,因为没有返回签名信息 + // this.checkResult(result); + return result; + } + + @Override + public WxPayRedpackQueryResult queryRedpack(String mchBillNo) throws WxPayException { + WxPayRedpackQueryRequest request = new WxPayRedpackQueryRequest(); + request.setMchBillNo(mchBillNo); + request.setBillType("MCHT"); + request.checkAndSign(this.getConfig()); + + String url = this.getPayBaseUrl() + "/mmpaymkttransfers/gethbinfo"; + String responseContent = this.post(url, request.toXML(), true); + WxPayRedpackQueryResult result = WxPayBaseResult.fromXML(responseContent, WxPayRedpackQueryResult.class); + result.checkResult(this); + return result; + } + + @Override + public WxPayOrderQueryResult queryOrder(String transactionId, String outTradeNo) throws WxPayException { + WxPayOrderQueryRequest request = new WxPayOrderQueryRequest(); + request.setOutTradeNo(StringUtils.trimToNull(outTradeNo)); + request.setTransactionId(StringUtils.trimToNull(transactionId)); + request.checkAndSign(this.getConfig()); + + String url = this.getPayBaseUrl() + "/pay/orderquery"; + String responseContent = this.post(url, request.toXML(), false); + if (StringUtils.isBlank(responseContent)) { + throw new WxPayException("无响应结果"); + } + + WxPayOrderQueryResult result = WxPayBaseResult.fromXML(responseContent, WxPayOrderQueryResult.class); + result.composeCoupons(); + result.checkResult(this); + return result; + } + + @Override + public WxPayOrderCloseResult closeOrder(String outTradeNo) throws WxPayException { + if (StringUtils.isBlank(outTradeNo)) { + throw new WxPayException("out_trade_no不能为空"); + } + + WxPayOrderCloseRequest request = new WxPayOrderCloseRequest(); + request.setOutTradeNo(StringUtils.trimToNull(outTradeNo)); + request.checkAndSign(this.getConfig()); + + String url = this.getPayBaseUrl() + "/pay/closeorder"; + String responseContent = this.post(url, request.toXML(), false); + WxPayOrderCloseResult result = WxPayBaseResult.fromXML(responseContent, WxPayOrderCloseResult.class); + result.checkResult(this); + + return result; + } + + @Override + public WxPayUnifiedOrderResult unifiedOrder(WxPayUnifiedOrderRequest request) throws WxPayException { + request.checkAndSign(this.getConfig()); + + String url = this.getPayBaseUrl() + "/pay/unifiedorder"; + String responseContent = this.post(url, request.toXML(), false); + WxPayUnifiedOrderResult result = WxPayBaseResult.fromXML(responseContent, WxPayUnifiedOrderResult.class); + result.checkResult(this); + return result; + } + + @Override + public Map getPayInfo(WxPayUnifiedOrderRequest request) throws WxPayException { + WxPayUnifiedOrderResult unifiedOrderResult = this.unifiedOrder(request); + String prepayId = unifiedOrderResult.getPrepayId(); + if (StringUtils.isBlank(prepayId)) { + throw new RuntimeException(String.format("无法获取prepay id,错误代码: '%s',信息:%s。", + unifiedOrderResult.getErrCode(), unifiedOrderResult.getErrCodeDes())); + } + + Map payInfo = new HashMap<>(); + String timestamp = String.valueOf(System.currentTimeMillis() / 1000); + String nonceStr = String.valueOf(System.currentTimeMillis()); + if ("NATIVE".equals(request.getTradeType())) { + payInfo.put("codeUrl", unifiedOrderResult.getCodeURL()); + } else if ("APP".equals(request.getTradeType())) { + // APP支付绑定的是微信开放平台上的账号,APPID为开放平台上绑定APP后发放的参数 + String appId = getConfig().getAppId(); + Map configMap = new HashMap<>(); + // 此map用于参与调起sdk支付的二次签名,格式全小写,timestamp只能是10位,格式固定,切勿修改 + String partnerid = getConfig().getMchId(); + configMap.put("prepayid", prepayId); + configMap.put("partnerid", partnerid); + configMap.put("package", "Sign=WXPay"); + configMap.put("timestamp", timestamp); + configMap.put("noncestr", nonceStr); + configMap.put("appid", appId); + // 此map用于客户端与微信服务器交互 + payInfo.put("sign", SignUtils.createSign(configMap, this.getConfig().getMchKey())); + payInfo.put("prepayId", prepayId); + payInfo.put("partnerId", partnerid); + payInfo.put("appId", appId); + payInfo.put("packageValue", "Sign=WXPay"); + payInfo.put("timeStamp", timestamp); + payInfo.put("nonceStr", nonceStr); + } else if ("JSAPI".equals(request.getTradeType())) { + payInfo.put("appId", unifiedOrderResult.getAppid()); + // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符 + payInfo.put("timeStamp", timestamp); + payInfo.put("nonceStr", nonceStr); + payInfo.put("package", "prepay_id=" + prepayId); + payInfo.put("signType", "MD5"); + payInfo.put("paySign", SignUtils.createSign(payInfo, this.getConfig().getMchKey())); + } + + return payInfo; + } + + @Override + public WxEntPayResult entPay(WxEntPayRequest request) throws WxPayException { + request.checkAndSign(this.getConfig()); + String url = this.getPayBaseUrl() + "/mmpaymkttransfers/promotion/transfers"; + + String responseContent = this.post(url, request.toXML(), true); + WxEntPayResult result = WxPayBaseResult.fromXML(responseContent, WxEntPayResult.class); + result.checkResult(this); + return result; + } + + @Override + public WxEntPayQueryResult queryEntPay(String partnerTradeNo) throws WxPayException { + WxEntPayQueryRequest request = new WxEntPayQueryRequest(); + request.setPartnerTradeNo(partnerTradeNo); + request.checkAndSign(this.getConfig()); + + String url = this.getPayBaseUrl() + "/mmpaymkttransfers/gettransferinfo"; + String responseContent = this.post(url, request.toXML(), true); + WxEntPayQueryResult result = WxPayBaseResult.fromXML(responseContent, WxEntPayQueryResult.class); + result.checkResult(this); + return result; + } + + @Override + public byte[] createScanPayQrcodeMode1(String productId, File logoFile, Integer sideLength) { + String content = this.createScanPayQrcodeMode1(productId); + return this.createQrcode(content, logoFile, sideLength); + } + + @Override + public String createScanPayQrcodeMode1(String productId) { + //weixin://wxpay/bizpayurl?sign=XXXXX&appid=XXXXX&mch_id=XXXXX&product_id=XXXXXX&time_stamp=XXXXXX&nonce_str=XXXXX + StringBuilder codeUrl = new StringBuilder("weixin://wxpay/bizpayurl?"); + Map params = Maps.newHashMap(); + params.put("appid", this.getConfig().getAppId()); + params.put("mch_id", this.getConfig().getMchId()); + params.put("product_id", productId); + params.put("time_stamp", String.valueOf(System.currentTimeMillis() / 1000));//这里需要秒,10位数字 + params.put("nonce_str", String.valueOf(System.currentTimeMillis())); + + String sign = SignUtils.createSign(params, this.getConfig().getMchKey()); + params.put("sign", sign); + + + for (String key : params.keySet()) { + codeUrl.append(key + "=" + params.get(key) + "&"); + } + + String content = codeUrl.toString().substring(0, codeUrl.length() - 1); + log.debug("扫码支付模式一生成二维码的URL:{}", content); + return content; + } + + @Override + public byte[] createScanPayQrcodeMode2(String codeUrl, File logoFile, Integer sideLength) { + return this.createQrcode(codeUrl, logoFile, sideLength); + } + + private byte[] createQrcode(String content, File logoFile, Integer sideLength) { + if (sideLength == null || sideLength < 1) { + return QrcodeUtils.createQrcode(content, logoFile); + } + + return QrcodeUtils.createQrcode(content, sideLength, logoFile); + } + + public void report(WxPayReportRequest request) throws WxPayException { + request.checkAndSign(this.getConfig()); + + String url = this.getPayBaseUrl() + "/payitil/report"; + String responseContent = this.post(url, request.toXML(), false); + WxPayCommonResult result = WxPayBaseResult.fromXML(responseContent, WxPayCommonResult.class); + result.checkResult(this); + } + + @Override + public WxPayBillResult downloadBill(String billDate, String billType, String tarType, String deviceInfo) throws WxPayException { + WxPayDownloadBillRequest request = new WxPayDownloadBillRequest(); + request.setBillType(billType); + request.setBillDate(billDate); + request.setTarType(tarType); + request.setDeviceInfo(deviceInfo); + + request.checkAndSign(this.getConfig()); + + String url = this.getPayBaseUrl() + "/pay/downloadbill"; + String responseContent = this.post(url, request.toXML(), false); + if (responseContent.startsWith("<")) { + WxPayCommonResult result = WxPayBaseResult.fromXML(responseContent, WxPayCommonResult.class); + result.checkResult(this); + return null; + } else { + WxPayBillResult wxPayBillResult = billInformationDeal(responseContent); + return wxPayBillResult; + } + } + + private WxPayBillResult billInformationDeal(String responseContent) { + WxPayBillResult wxPayBillResult = new WxPayBillResult(); + + String listStr = ""; + String objStr = ""; + if (responseContent.contains("总交易单数")) { + listStr = responseContent.substring(0, responseContent.indexOf("总交易单数")); + objStr = responseContent.substring(responseContent.indexOf("总交易单数")); + } + + /* + * 交易时间:2017-04-06 01:00:02 公众账号ID: 商户号: 子商户号:0 设备号:WEB 微信订单号: 商户订单号:2017040519091071873216 用户标识: 交易类型:NATIVE + * 交易状态:REFUND 付款银行:CFT 货币种类:CNY 总金额:0.00 企业红包金额:0.00 微信退款单号: 商户退款单号:20170406010000933 退款金额:0.01 企业红包退款金额:0.00 + * 退款类型:ORIGINAL 退款状态:SUCCESS 商品名称: 商户数据包: 手续费:0.00000 费率 :0.60% + */ + + // 参考以上格式进行取值 + + List wxPayBillBaseResultLst = new LinkedList<>(); + String newStr = listStr.replaceAll(",", " "); // 去空格 + String[] tempStr = newStr.split("`"); // 数据分组 + String[] t = tempStr[0].split(" ");// 分组标题 + int j = tempStr.length / t.length; // 计算循环次数 + int k = 1; // 纪录数组下标 + for (int i = 0; i < j; i++) { + WxPayBillBaseResult wxPayBillBaseResult = new WxPayBillBaseResult(); + + wxPayBillBaseResult.setTradeTime(tempStr[k].trim()); + wxPayBillBaseResult.setAppId(tempStr[k + 1].trim()); + wxPayBillBaseResult.setMchId(tempStr[k + 2].trim()); + wxPayBillBaseResult.setSubMchId(tempStr[k + 3].trim()); + wxPayBillBaseResult.setDeviceInfo(tempStr[k + 4].trim()); + wxPayBillBaseResult.setTransationId(tempStr[k + 5].trim()); + wxPayBillBaseResult.setOutTradeNo(tempStr[k + 6].trim()); + wxPayBillBaseResult.setOpenId(tempStr[k + 7].trim()); + wxPayBillBaseResult.setTradeType(tempStr[k + 8].trim()); + wxPayBillBaseResult.setTradeState(tempStr[k + 9].trim()); + wxPayBillBaseResult.setBankType(tempStr[k + 10].trim()); + wxPayBillBaseResult.setFeeType(tempStr[k + 11].trim()); + wxPayBillBaseResult.setTotalFee(tempStr[k + 12].trim()); + wxPayBillBaseResult.setCouponFee(tempStr[k + 13].trim()); + wxPayBillBaseResult.setRefundId(tempStr[k + 14].trim()); + wxPayBillBaseResult.setOutRefundNo(tempStr[k + 15].trim()); + wxPayBillBaseResult.setSettlementRefundFee(tempStr[k + 16].trim()); + wxPayBillBaseResult.setCouponRefundFee(tempStr[k + 17].trim()); + wxPayBillBaseResult.setRefundChannel(tempStr[k + 18].trim()); + wxPayBillBaseResult.setRefundState(tempStr[k + 19].trim()); + wxPayBillBaseResult.setBody(tempStr[k + 20].trim()); + wxPayBillBaseResult.setAttach(tempStr[k + 21].trim()); + wxPayBillBaseResult.setPoundage(tempStr[k + 22].trim()); + wxPayBillBaseResult.setPoundageRate(tempStr[k + 23].trim()); + wxPayBillBaseResultLst.add(wxPayBillBaseResult); + k += t.length; + } + wxPayBillResult.setWxPayBillBaseResultLst(wxPayBillBaseResultLst); + /* + * 总交易单数,总交易额,总退款金额,总代金券或立减优惠退款金额,手续费总金额 `2,`0.02,`0.0,`0.0,`0 + */ + + // 参考以上格式进行取值 + + String totalStr = objStr.replaceAll(",", " "); + String[] totalTempStr = totalStr.split("`"); + wxPayBillResult.setTotalRecord(totalTempStr[1]); + wxPayBillResult.setTotalFee(totalTempStr[2]); + wxPayBillResult.setTotalRefundFee(totalTempStr[3]); + wxPayBillResult.setTotalCouponFee(totalTempStr[4]); + wxPayBillResult.setTotalPoundageFee(totalTempStr[5]); + + return wxPayBillResult; + } + + @Override + public WxPayMicropayResult micropay(WxPayMicropayRequest request) throws WxPayException { + request.checkAndSign(this.getConfig()); + + String url = this.getPayBaseUrl() + "/pay/micropay"; + String responseContent = this.post(url, request.toXML(), false); + WxPayMicropayResult result = WxPayBaseResult.fromXML(responseContent, WxPayMicropayResult.class); + result.checkResult(this); + return result; + } + + @Override + public WxPayOrderReverseResult reverseOrder(WxPayOrderReverseRequest request) throws WxPayException { + request.checkAndSign(this.getConfig()); + + String url = this.getPayBaseUrl() + "/secapi/pay/reverse"; + String responseContent = this.post(url, request.toXML(), true); + WxPayOrderReverseResult result = WxPayBaseResult.fromXML(responseContent, WxPayOrderReverseResult.class); + result.checkResult(this); + return result; + } + + @Override + public String shorturl(WxPayShorturlRequest request) throws WxPayException { + request.checkAndSign(this.getConfig()); + + String url = this.getPayBaseUrl() + "/tools/shorturl"; + String responseContent = this.post(url, request.toXML(), false); + WxPayShorturlResult result = WxPayBaseResult.fromXML(responseContent, WxPayShorturlResult.class); + result.checkResult(this); + return result.getShortUrl(); + } + + @Override + public String shorturl(String longUrl) throws WxPayException { + return this.shorturl(new WxPayShorturlRequest(longUrl)); + } + + @Override + public String authcode2Openid(WxPayAuthcode2OpenidRequest request) throws WxPayException { + request.checkAndSign(this.getConfig()); + + String url = this.getPayBaseUrl() + "/tools/authcodetoopenid"; + String responseContent = this.post(url, request.toXML(), false); + WxPayAuthcode2OpenidResult result = WxPayBaseResult.fromXML(responseContent, WxPayAuthcode2OpenidResult.class); + result.checkResult(this); + return result.getOpenid(); + } + + @Override + public String authcode2Openid(String authCode) throws WxPayException { + return this.authcode2Openid(new WxPayAuthcode2OpenidRequest(authCode)); + } + + @Override + public String getSandboxSignKey() throws WxPayException { + WxPayDefaultRequest request = new WxPayDefaultRequest(); + request.checkAndSign(this.getConfig()); + + String url = "https://api.mch.weixin.qq.com/sandboxnew/pay/getsignkey"; + String responseContent = this.post(url, request.toXML(), false); + WxPaySandboxSignKeyResult result = WxPayBaseResult.fromXML(responseContent, WxPaySandboxSignKeyResult.class); + result.checkResult(this); + return result.getSandboxSignKey(); + } + + @Override + public WxPayCouponSendResult sendCoupon(WxPayCouponSendRequest request) throws WxPayException { + request.checkAndSign(this.getConfig()); + + String url = this.getPayBaseUrl() + "/mmpaymkttransfers/send_coupon"; + String responseContent = this.post(url, request.toXML(), true); + WxPayCouponSendResult result = WxPayBaseResult.fromXML(responseContent, WxPayCouponSendResult.class); + result.checkResult(this); + return result; + } + + @Override + public WxPayCouponStockQueryResult queryCouponStock(WxPayCouponStockQueryRequest request) throws WxPayException { + request.checkAndSign(this.getConfig()); + + String url = this.getPayBaseUrl() + "/mmpaymkttransfers/query_coupon_stock"; + String responseContent = this.post(url, request.toXML(), false); + WxPayCouponStockQueryResult result = WxPayBaseResult.fromXML(responseContent, WxPayCouponStockQueryResult.class); + result.checkResult(this); + return result; + } + + @Override + public WxPayCouponInfoQueryResult queryCouponInfo(WxPayCouponInfoQueryRequest request) throws WxPayException { + request.checkAndSign(this.getConfig()); + + String url = this.getPayBaseUrl() + "/mmpaymkttransfers/querycouponsinfo"; + String responseContent = this.post(url, request.toXML(), false); + WxPayCouponInfoQueryResult result = WxPayBaseResult.fromXML(responseContent, WxPayCouponInfoQueryResult.class); + result.checkResult(this); + return result; + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceApacheHttpImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceApacheHttpImpl.java new file mode 100644 index 0000000000..c5da982c13 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceApacheHttpImpl.java @@ -0,0 +1,66 @@ +package com.github.binarywang.wxpay.service.impl; + +import com.github.binarywang.wxpay.exception.WxPayException; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.conn.ssl.DefaultHostnameVerifier; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; + +import javax.net.ssl.SSLContext; +import java.nio.charset.StandardCharsets; + +/** + *
    + * 微信支付请求实现类,apache httpclient实现
    + * Created by Binary Wang on 2016/7/28.
    + * 
    + * + * @author binarywang (https://github.com/binarywang) + */ +public class WxPayServiceApacheHttpImpl extends WxPayServiceAbstractImpl { + + @Override + protected String post(String url, String requestStr, boolean useKey) throws WxPayException { + try { + HttpClientBuilder httpClientBuilder = HttpClients.custom(); + if (useKey) { + SSLContext sslContext = this.getConfig().getSslContext(); + if (null == sslContext) { + sslContext = this.getConfig().initSSLContext(); + } + + SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, + new String[]{"TLSv1"}, null, new DefaultHostnameVerifier()); + httpClientBuilder.setSSLSocketFactory(sslsf); + } + + HttpPost httpPost = new HttpPost(url); + httpPost.setConfig(RequestConfig.custom() + .setConnectionRequestTimeout(this.getConfig().getHttpConnectionTimeout()) + .setConnectTimeout(this.getConfig().getHttpConnectionTimeout()) + .setSocketTimeout(this.getConfig().getHttpTimeout()) + .build()); + + try (CloseableHttpClient httpclient = httpClientBuilder.build()) { + httpPost.setEntity(new StringEntity(new String(requestStr.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1))); + try (CloseableHttpResponse response = httpclient.execute(httpPost)) { + String result = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); + this.log.info("\n【请求地址】:{}\n【请求数据】:{}\n【响应数据】:{}", url, requestStr, result); + return result; + } + } finally { + httpPost.releaseConnection(); + } + } catch (Exception e) { + this.log.error("\n【请求地址】:{}\n【请求数据】:{}\n【异常信息】:{}", url, requestStr, e.getMessage()); + throw new WxPayException(e.getMessage(), e); + } + } + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImpl.java index 90778999ac..8e795966f4 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImpl.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImpl.java @@ -1,419 +1,12 @@ package com.github.binarywang.wxpay.service.impl; -import com.github.binarywang.utils.qrcode.QrcodeUtils; -import com.github.binarywang.wxpay.bean.request.*; -import com.github.binarywang.wxpay.bean.result.*; -import com.github.binarywang.wxpay.config.WxPayConfig; -import com.github.binarywang.wxpay.service.WxPayService; -import com.github.binarywang.wxpay.util.SignUtils; -import com.google.common.collect.Maps; -import jodd.http.HttpRequest; -import jodd.http.HttpResponse; -import jodd.http.net.SSLSocketHttpConnectionProvider; -import me.chanjar.weixin.common.bean.result.WxError; -import me.chanjar.weixin.common.exception.WxErrorException; -import org.apache.commons.lang3.CharEncoding; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.net.ssl.SSLContext; -import java.io.File; -import java.io.UnsupportedEncodingException; -import java.util.HashMap; -import java.util.Map; - /** - * Created by Binary Wang on 2016/7/28. + *
    + * 微信支付接口请求实现类,默认使用Apache HttpClient实现
    + * Created by Binary Wang on 2017-7-8.
    + * 
    * - * @author binarywang (https://github.com/binarywang) + * @author Binary Wang */ -public class WxPayServiceImpl implements WxPayService { - private static final String PAY_BASE_URL = "https://api.mch.weixin.qq.com"; - private final Logger log = LoggerFactory.getLogger(this.getClass()); - - private WxPayConfig config; - - @Override - public WxPayConfig getConfig() { - return this.config; - } - - @Override - public void setConfig(WxPayConfig config) { - this.config = config; - } - - private String getPayBaseUrl() { - if (this.getConfig().useSandbox()) { - return PAY_BASE_URL + "/sandboxnew"; - } - - return PAY_BASE_URL; - } - - @Override - public WxPayRefundResult refund(WxPayRefundRequest request) throws WxErrorException { - request.checkAndSign(this.getConfig()); - - String url = this.getPayBaseUrl() + "/secapi/pay/refund"; - String responseContent = this.postWithKey(url, request.toXML()); - WxPayRefundResult result = WxPayBaseResult.fromXML(responseContent, WxPayRefundResult.class); - result.checkResult(this); - return result; - } - - @Override - public WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, String outRefundNo, String refundId) - throws WxErrorException { - WxPayRefundQueryRequest request = new WxPayRefundQueryRequest(); - request.setOutTradeNo(StringUtils.trimToNull(outTradeNo)); - request.setTransactionId(StringUtils.trimToNull(transactionId)); - request.setOutRefundNo(StringUtils.trimToNull(outRefundNo)); - request.setRefundId(StringUtils.trimToNull(refundId)); - - request.checkAndSign(this.getConfig()); - - String url = this.getPayBaseUrl() + "/pay/refundquery"; - String responseContent = this.post(url, request.toXML()); - WxPayRefundQueryResult result = WxPayBaseResult.fromXML(responseContent, WxPayRefundQueryResult.class); - result.composeRefundRecords(); - result.checkResult(this); - return result; - } - - @Override - public WxPayOrderNotifyResult getOrderNotifyResult(String xmlData) throws WxErrorException { - try { - log.debug("微信支付回调参数详细:{}", xmlData); - WxPayOrderNotifyResult result = WxPayOrderNotifyResult.fromXML(xmlData); - log.debug("微信支付回调结果对象:{}", result); - result.checkResult(this); - return result; - } catch (WxErrorException e) { - log.error(e.getMessage(), e); - throw e; - } catch (Exception e) { - log.error(e.getMessage(), e); - throw new WxErrorException(WxError.newBuilder().setErrorMsg("发生异常" + e.getMessage()).build()); - } - } - - @Override - public WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request) throws WxErrorException { - request.checkAndSign(this.getConfig()); - - String url = this.getPayBaseUrl() + "/mmpaymkttransfers/sendredpack"; - if (request.getAmtType() != null) { - //裂变红包 - url = this.getPayBaseUrl() + "/mmpaymkttransfers/sendgroupredpack"; - } - - String responseContent = this.postWithKey(url, request.toXML()); - WxPaySendRedpackResult result = WxPayBaseResult.fromXML(responseContent, WxPaySendRedpackResult.class); - //毋须校验,因为没有返回签名信息 - // this.checkResult(result); - return result; - } - - @Override - public WxPayRedpackQueryResult queryRedpack(String mchBillNo) throws WxErrorException { - WxPayRedpackQueryRequest request = new WxPayRedpackQueryRequest(); - request.setMchBillNo(mchBillNo); - request.setBillType("MCHT"); - request.checkAndSign(this.getConfig()); - - String url = this.getPayBaseUrl() + "/mmpaymkttransfers/gethbinfo"; - String responseContent = this.postWithKey(url, request.toXML()); - WxPayRedpackQueryResult result = WxPayBaseResult.fromXML(responseContent, WxPayRedpackQueryResult.class); - result.checkResult(this); - return result; - } - - @Override - public WxPayOrderQueryResult queryOrder(String transactionId, String outTradeNo) throws WxErrorException { - WxPayOrderQueryRequest request = new WxPayOrderQueryRequest(); - request.setOutTradeNo(StringUtils.trimToNull(outTradeNo)); - request.setTransactionId(StringUtils.trimToNull(transactionId)); - request.checkAndSign(this.getConfig()); - - String url = this.getPayBaseUrl() + "/pay/orderquery"; - String responseContent = this.post(url, request.toXML()); - if (StringUtils.isBlank(responseContent)) { - throw new WxErrorException(WxError.newBuilder().setErrorMsg("无响应结果").build()); - } - - WxPayOrderQueryResult result = WxPayBaseResult.fromXML(responseContent, WxPayOrderQueryResult.class); - result.composeCoupons(); - result.checkResult(this); - return result; - } - - @Override - public WxPayOrderCloseResult closeOrder(String outTradeNo) throws WxErrorException { - if (StringUtils.isBlank(outTradeNo)) { - throw new IllegalArgumentException("out_trade_no不能为空"); - } - - WxPayOrderCloseRequest request = new WxPayOrderCloseRequest(); - request.setOutTradeNo(StringUtils.trimToNull(outTradeNo)); - request.checkAndSign(this.getConfig()); - - String url = this.getPayBaseUrl() + "/pay/closeorder"; - String responseContent = this.post(url, request.toXML()); - WxPayOrderCloseResult result = WxPayBaseResult.fromXML(responseContent, WxPayOrderCloseResult.class); - result.checkResult(this); - - return result; - } - - @Override - public WxPayUnifiedOrderResult unifiedOrder(WxPayUnifiedOrderRequest request) throws WxErrorException { - request.checkAndSign(this.getConfig()); - - String url = this.getPayBaseUrl() + "/pay/unifiedorder"; - String responseContent = this.post(url, request.toXML()); - WxPayUnifiedOrderResult result = WxPayBaseResult.fromXML(responseContent, WxPayUnifiedOrderResult.class); - result.checkResult(this); - return result; - } - - @Override - public Map getPayInfo(WxPayUnifiedOrderRequest request) throws WxErrorException { - WxPayUnifiedOrderResult unifiedOrderResult = this.unifiedOrder(request); - String prepayId = unifiedOrderResult.getPrepayId(); - if (StringUtils.isBlank(prepayId)) { - throw new RuntimeException(String.format("无法获取prepay id,错误代码: '%s',信息:%s。", - unifiedOrderResult.getErrCode(), unifiedOrderResult.getErrCodeDes())); - } - - Map payInfo = new HashMap<>(); - if ("NATIVE".equals(request.getTradeType())) { - payInfo.put("codeUrl", unifiedOrderResult.getCodeURL()); - } else if ("APP".equals(request.getTradeType())) { - // APP支付绑定的是微信开放平台上的账号,APPID为开放平台上绑定APP后发放的参数 - String appId = getConfig().getAppId(); - Map configMap = new HashMap<>(); - // 此map用于参与调起sdk支付的二次签名,格式全小写,timestamp只能是10位,格式固定,切勿修改 - String partnerid = getConfig().getMchId(); - configMap.put("prepayid", prepayId); - configMap.put("partnerid", partnerid); - configMap.put("package", "Sign=WXPay"); - configMap.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000)); - configMap.put("noncestr", String.valueOf(System.currentTimeMillis())); - configMap.put("appid", appId); - // 此map用于客户端与微信服务器交互 - payInfo.put("sign", SignUtils.createSign(configMap, this.getConfig().getMchKey())); - payInfo.put("prepayId", prepayId); - payInfo.put("partnerId", partnerid); - payInfo.put("appId", appId); - payInfo.put("packageValue", "Sign=WXPay"); - payInfo.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000)); - payInfo.put("nonceStr", String.valueOf(System.currentTimeMillis())); - } else if ("JSAPI".equals(request.getTradeType())) { - payInfo.put("appId", unifiedOrderResult.getAppid()); - // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符 - payInfo.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000)); - payInfo.put("nonceStr", String.valueOf(System.currentTimeMillis())); - payInfo.put("package", "prepay_id=" + prepayId); - payInfo.put("signType", "MD5"); - payInfo.put("paySign", SignUtils.createSign(payInfo, this.getConfig().getMchKey())); - } - return payInfo; - } - - @Override - public WxEntPayResult entPay(WxEntPayRequest request) throws WxErrorException { - request.checkAndSign(this.getConfig()); - String url = this.getPayBaseUrl() + "/mmpaymkttransfers/promotion/transfers"; - - String responseContent = this.postWithKey(url, request.toXML()); - WxEntPayResult result = WxPayBaseResult.fromXML(responseContent, WxEntPayResult.class); - result.checkResult(this); - return result; - } - - @Override - public WxEntPayQueryResult queryEntPay(String partnerTradeNo) throws WxErrorException { - WxEntPayQueryRequest request = new WxEntPayQueryRequest(); - request.setPartnerTradeNo(partnerTradeNo); - request.checkAndSign(this.getConfig()); - - String url = this.getPayBaseUrl() + "/mmpaymkttransfers/gettransferinfo"; - String responseContent = this.postWithKey(url, request.toXML()); - WxEntPayQueryResult result = WxPayBaseResult.fromXML(responseContent, WxEntPayQueryResult.class); - result.checkResult(this); - return result; - } - - @Override - public byte[] createScanPayQrcodeMode1(String productId, File logoFile, Integer sideLength) { - String content = this.createScanPayQrcodeMode1(productId); - return this.createQrcode(content, logoFile, sideLength); - } - - @Override - public String createScanPayQrcodeMode1(String productId) { - //weixin://wxpay/bizpayurl?sign=XXXXX&appid=XXXXX&mch_id=XXXXX&product_id=XXXXXX&time_stamp=XXXXXX&nonce_str=XXXXX - StringBuilder codeUrl = new StringBuilder("weixin://wxpay/bizpayurl?"); - Map params = Maps.newHashMap(); - params.put("appid", this.getConfig().getAppId()); - params.put("mch_id", this.getConfig().getMchId()); - params.put("product_id", productId); - params.put("time_stamp", String.valueOf(System.currentTimeMillis() / 1000));//这里需要秒,10位数字 - params.put("nonce_str", String.valueOf(System.currentTimeMillis())); - - String sign = SignUtils.createSign(params, this.getConfig().getMchKey()); - params.put("sign", sign); - - - for (String key : params.keySet()) { - codeUrl.append(key + "=" + params.get(key) + "&"); - } - - String content = codeUrl.toString().substring(0, codeUrl.length() - 1); - log.debug("扫码支付模式一生成二维码的URL:{}", content); - return content; - } - - @Override - public byte[] createScanPayQrcodeMode2(String codeUrl, File logoFile, Integer sideLength) { - return this.createQrcode(codeUrl, logoFile, sideLength); - } - - private byte[] createQrcode(String content, File logoFile, Integer sideLength) { - if (sideLength == null || sideLength < 1) { - return QrcodeUtils.createQrcode(content, logoFile); - } - - return QrcodeUtils.createQrcode(content, sideLength, logoFile); - } - - public void report(WxPayReportRequest request) throws WxErrorException { - request.checkAndSign(this.getConfig()); - - String url = this.getPayBaseUrl() + "/payitil/report"; - String responseContent = this.post(url, request.toXML()); - WxPayCommonResult result = WxPayBaseResult.fromXML(responseContent, WxPayCommonResult.class); - result.checkResult(this); - } - - @Override - public File downloadBill(String billDate, String billType, String tarType, String deviceInfo) throws WxErrorException { - WxPayDownloadBillRequest request = new WxPayDownloadBillRequest(); - request.setBillType(billType); - request.setBillDate(billDate); - request.setTarType(tarType); - request.setDeviceInfo(deviceInfo); - - request.checkAndSign(this.getConfig()); - - String url = this.getPayBaseUrl() + "/pay/downloadbill"; - //TODO 返回的内容可能是文件流,也有可能是xml,需要区分对待 - String responseContent = this.post(url, request.toXML()); - - WxPayCommonResult result = WxPayBaseResult.fromXML(responseContent, WxPayCommonResult.class); - result.checkResult(this); - //TODO 待实现,暂时无测试帐号,无法调试 - return null; - } - - @Override - public WxPayMicropayResult micropay(WxPayMicropayRequest request) throws WxErrorException { - request.checkAndSign(this.getConfig()); - - String url = this.getPayBaseUrl() + "/pay/micropay"; - String responseContent = this.post(url, request.toXML()); - WxPayMicropayResult result = WxPayBaseResult.fromXML(responseContent, WxPayMicropayResult.class); - result.checkResult(this); - return result; - } - - @Override - public WxPayOrderReverseResult reverseOrder(WxPayOrderReverseRequest request) throws WxErrorException { - request.checkAndSign(this.getConfig()); - - String url = this.getPayBaseUrl() + "/secapi/pay/reverse"; - String responseContent = this.postWithKey(url, request.toXML()); - WxPayOrderReverseResult result = WxPayBaseResult.fromXML(responseContent, WxPayOrderReverseResult.class); - result.checkResult(this); - return result; - } - - @Override - public String shorturl(WxPayShorturlRequest request) throws WxErrorException { - request.checkAndSign(this.getConfig()); - - String url = this.getPayBaseUrl() + "/tools/shorturl"; - String responseContent = this.post(url, request.toXML()); - WxPayShorturlResult result = WxPayBaseResult.fromXML(responseContent, WxPayShorturlResult.class); - result.checkResult(this); - return result.getShortUrl(); - } - - @Override - public String shorturl(String longUrl) throws WxErrorException { - return this.shorturl(new WxPayShorturlRequest(longUrl)); - } - - @Override - public String authcode2Openid(WxPayAuthcode2OpenidRequest request) throws WxErrorException { - request.checkAndSign(this.getConfig()); - - String url = this.getPayBaseUrl() + "/tools/authcodetoopenid"; - String responseContent = this.post(url, request.toXML()); - WxPayAuthcode2OpenidResult result = WxPayBaseResult.fromXML(responseContent, WxPayAuthcode2OpenidResult.class); - result.checkResult(this); - return result.getOpenid(); - } - - @Override - public String authcode2Openid(String authCode) throws WxErrorException { - return this.authcode2Openid(new WxPayAuthcode2OpenidRequest(authCode)); - } - - private String post(String url, String xmlParam) { - String requestString = xmlParam; - try { - requestString = new String(xmlParam.getBytes(CharEncoding.UTF_8), CharEncoding.ISO_8859_1); - } catch (UnsupportedEncodingException e) { - //实际上不会发生该异常 - e.printStackTrace(); - } - - HttpRequest request = HttpRequest.post(url).body(requestString); - HttpResponse response = request.send(); - String responseString = null; - try { - responseString = new String(response.bodyText().getBytes(CharEncoding.ISO_8859_1), CharEncoding.UTF_8); - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); - } - - this.log.debug("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}", url, xmlParam, responseString); - return responseString; - } - - /** - * ecoolper(20170418),修改为jodd-http方式 - */ - private String postWithKey(String url, String requestStr) throws WxErrorException { - try { - SSLContext sslContext = this.getConfig().getSslContext(); - if (null == sslContext) { - sslContext = this.getConfig().initSSLContext(); - } - - HttpRequest request = HttpRequest.post(url).withConnectionProvider(new SSLSocketHttpConnectionProvider(sslContext)); - request.bodyText(requestStr); - HttpResponse response = request.send(); - String result = response.bodyText(); - this.log.debug("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}", url, requestStr, result); - return result; - } catch (Exception e) { - this.log.error("\n[URL]: {}\n[PARAMS]: {}\n[EXCEPTION]: {}", url, requestStr, e.getMessage()); - throw new WxErrorException(WxError.newBuilder().setErrorCode(-1).setErrorMsg(e.getMessage()).build(), e); - } - } - +public class WxPayServiceImpl extends WxPayServiceApacheHttpImpl { } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceJoddHttpImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceJoddHttpImpl.java new file mode 100644 index 0000000000..29008e26bd --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceJoddHttpImpl.java @@ -0,0 +1,68 @@ +package com.github.binarywang.wxpay.service.impl; + +import com.github.binarywang.wxpay.exception.WxPayException; +import jodd.http.HttpRequest; +import jodd.http.HttpResponse; +import jodd.http.net.SSLSocketHttpConnectionProvider; +import org.apache.commons.lang3.StringUtils; + +import javax.net.ssl.SSLContext; +import java.nio.charset.StandardCharsets; + +/** + * 微信支付请求实现类,jodd-http实现 + * Created by Binary Wang on 2016/7/28. + * + * @author binarywang (https://github.com/binarywang) + */ +public class WxPayServiceJoddHttpImpl extends WxPayServiceAbstractImpl { + + @Override + protected String post(String url, String requestStr, boolean useKey) throws WxPayException { + try { + HttpRequest request = HttpRequest + .post(url) + .timeout(this.config.getHttpTimeout()) + .connectionTimeout(this.config.getHttpConnectionTimeout()) + .bodyText(requestStr); + + if (useKey) { + SSLContext sslContext = this.getConfig().getSslContext(); + if (null == sslContext) { + sslContext = this.getConfig().initSSLContext(); + } + request.withConnectionProvider(new SSLSocketHttpConnectionProvider(sslContext)); + } + + String responseString = this.getResponseString(request.send()); + + this.log.info("\n【请求地址】:{}\n【请求数据】:{}\n【响应数据】:{}", url, requestStr, responseString); + return responseString; + } catch (Exception e) { + this.log.error("\n【请求地址】:{}\n【请求数据】:{}\n【异常信息】:{}", url, requestStr, e.getMessage()); + throw new WxPayException(e.getMessage(), e); + } + } + + private String getResponseString(HttpResponse response) throws WxPayException { + try { + this.log.debug("【微信服务器响应头信息】:\n{}", response.toString(false)); + } catch (NullPointerException e) { + throw new WxPayException("response.toString() 居然抛出空指针异常了", e); + } + + String responseString = response.bodyText(); + + if (StringUtils.isBlank(responseString)) { + throw new WxPayException("响应信息为空"); + } + + if (StringUtils.isBlank(response.charset())) { + responseString = new String(responseString.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); + } + + return responseString; + } + + +} diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayOrderNotifyResultTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayOrderNotifyResultTest.java new file mode 100644 index 0000000000..389b374d9e --- /dev/null +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayOrderNotifyResultTest.java @@ -0,0 +1,58 @@ +package com.github.binarywang.wxpay.bean.result; + +import org.testng.*; +import org.testng.annotations.*; + +/** + *
    + * Created by Binary Wang on 2017-6-15.
    + * @author Binary Wang
    + * 
    + */ +public class WxPayOrderNotifyResultTest { + @Test + public void testFromXML() throws Exception { + String xmlString = "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " 1\n" + + " \n" + + " \n" + + " 2\n" + + " \n" + + " 10000\n" + + " 100\n" + + " \n" + + " 10001\n" + + " 200\n" + + ""; + + WxPayOrderNotifyResult result = WxPayOrderNotifyResult.fromXML(xmlString); + + Assert.assertEquals(result.getCouponCount().intValue(), 2); + Assert.assertNotNull(result.getCouponList()); + Assert.assertEquals(result.getCouponList().size(), 2); + + Assert.assertEquals(result.getCouponList().get(0).getCouponFee().intValue(), 100); + Assert.assertEquals(result.getCouponList().get(1).getCouponFee().intValue(), 200); + + Assert.assertEquals(result.getCouponList().get(0).getCouponType(), "CASH"); + Assert.assertEquals(result.getCouponList().get(1).getCouponType(), "NO_CASH"); + + Assert.assertEquals(result.getCouponList().get(0).getCouponId(), "10000"); + Assert.assertEquals(result.getCouponList().get(1).getCouponId(), "10001"); + } + +} diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/config/WxPayConfigTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/config/WxPayConfigTest.java new file mode 100644 index 0000000000..83bcbf2c17 --- /dev/null +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/config/WxPayConfigTest.java @@ -0,0 +1,24 @@ +package com.github.binarywang.wxpay.config; + +import org.testng.annotations.Test; + +import static org.testng.Assert.*; + +/** + *
    + *  Created by BinaryWang on 2017/6/18.
    + * 
    + * + * @author Binary Wang + */ +public class WxPayConfigTest { + private WxPayConfig payConfig = new WxPayConfig(); + + @Test + public void testInitSSLContext() throws Exception { + payConfig.setMchId("123"); + payConfig.setKeyPath("classpath:/abc.p12"); + payConfig.initSSLContext(); + } + +} diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImplTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/WxPayServiceAbstractImplTest.java similarity index 74% rename from weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImplTest.java rename to weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/WxPayServiceAbstractImplTest.java index 1ca8d2e6ff..7ac35a6061 100644 --- a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImplTest.java +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/WxPayServiceAbstractImplTest.java @@ -1,18 +1,18 @@ package com.github.binarywang.wxpay.service.impl; import com.github.binarywang.utils.qrcode.QrcodeUtils; +import com.github.binarywang.wxpay.bean.coupon.*; import com.github.binarywang.wxpay.bean.request.*; import com.github.binarywang.wxpay.bean.result.*; +import com.github.binarywang.wxpay.exception.WxPayException; import com.github.binarywang.wxpay.service.WxPayService; import com.github.binarywang.wxpay.testbase.ApiTestModule; import com.github.binarywang.wxpay.testbase.XmlWxPayConfig; import com.google.inject.Inject; -import me.chanjar.weixin.common.exception.WxErrorException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testng.annotations.*; -import java.io.File; import java.nio.file.Files; import java.nio.file.Path; import java.util.Map; @@ -27,7 +27,7 @@ */ @Test @Guice(modules = ApiTestModule.class) -public class WxPayServiceImplTest { +public class WxPayServiceAbstractImplTest { private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Inject @@ -35,7 +35,7 @@ public class WxPayServiceImplTest { @Test public void testGetPayInfo() throws Exception { - Map payInfo = this.payService.getPayInfo(WxPayUnifiedOrderRequest.builder() + Map payInfo = this.payService.getPayInfo(WxPayUnifiedOrderRequest.newBuilder() .body("我去") .totalFee(1) .spbillCreateIp("111111") @@ -49,8 +49,9 @@ public void testGetPayInfo() throws Exception { @Test public void testDownloadBill() throws Exception { - File file = this.payService.downloadBill("20170101", "ALL", "GZIP", "1111111"); - assertNotNull(file); + WxPayBillResult wxPayBillResult = this.payService.downloadBill("20170101", "ALL", "GZIP", "1111111"); + //前一天没有账单记录返回null + assertNotNull(wxPayBillResult); //必填字段为空时,抛出异常 this.payService.downloadBill("", "", "", null); } @@ -83,7 +84,7 @@ public void testRefund() throws Exception { } /** - * Test method for {@link WxPayService#refundQuery(java.lang.String, java.lang.String, java.lang.String, java.lang.String)} . + * Test method for {@link WxPayService#refundQuery(String, String, String, String)} . */ @Test public void testRefundQuery() throws Exception { @@ -121,7 +122,7 @@ public void testSendRedpack() throws Exception { } /** - * Test method for {@link WxPayService#queryRedpack(java.lang.String)}. + * Test method for {@link WxPayService#queryRedpack(String)}. */ @Test public void testQueryRedpack() throws Exception { @@ -133,12 +134,12 @@ public void testQueryRedpack() throws Exception { * Test method for {@link WxPayService#unifiedOrder(WxPayUnifiedOrderRequest)}. */ @Test - public void testUnifiedOrder() throws WxErrorException { + public void testUnifiedOrder() throws WxPayException { WxPayUnifiedOrderResult result = this.payService - .unifiedOrder(WxPayUnifiedOrderRequest.builder() + .unifiedOrder(WxPayUnifiedOrderRequest.newBuilder() .body("我去") .totalFee(1) - .spbillCreateIp("111111") + .spbillCreateIp("11.1.11.1") .notifyURL("111111") .tradeType("JSAPI") .openid(((XmlWxPayConfig) this.payService.getConfig()).getOpenid()) @@ -148,19 +149,19 @@ public void testUnifiedOrder() throws WxErrorException { } /** - * Test method for {@link WxPayService#queryOrder(java.lang.String, java.lang.String)} . + * Test method for {@link WxPayService#queryOrder(String, String)} . */ @Test - public void testQueryOrder() throws WxErrorException { + public void testQueryOrder() throws WxPayException { this.logger.info(this.payService.queryOrder("11212121", null).toString()); this.logger.info(this.payService.queryOrder(null, "11111").toString()); } /** - * Test method for {@link WxPayService#closeOrder(java.lang.String)} . + * Test method for {@link WxPayService#closeOrder(String)} . */ @Test - public void testCloseOrder() throws WxErrorException { + public void testCloseOrder() throws WxPayException { this.logger.info(this.payService.closeOrder("11212121").toString()); } @@ -168,16 +169,24 @@ public void testCloseOrder() throws WxErrorException { * Test method for {@link WxPayService#entPay(WxEntPayRequest)}. */ @Test - public void testEntPay() throws WxErrorException { - WxEntPayRequest request = new WxEntPayRequest(); + public void testEntPay() throws WxPayException { + WxEntPayRequest request = WxEntPayRequest.newBuilder() + .partnerTradeNo("Eb6Aep7uVTdbkJqrP4") + .openid("ojOQA0y9o-Eb6Aep7uVTdbkJqrP4") + .amount(1) + .spbillCreateIp("10.10.10.10") + .checkName("NO_CHECK") + .description("描述信息") + .build(); + this.logger.info(this.payService.entPay(request).toString()); } /** - * Test method for {@link WxPayService#queryEntPay(java.lang.String)}. + * Test method for {@link WxPayService#queryEntPay(String)}. */ @Test - public void testQueryEntPay() throws WxErrorException { + public void testQueryEntPay() throws WxPayException { this.logger.info(this.payService.queryEntPay("11212121").toString()); } @@ -264,5 +273,39 @@ public void testAuthcode2Openid() throws Exception { this.logger.info(result); } + @Test + public void testGetSandboxSignKey() throws Exception { + final String signKey = this.payService.getSandboxSignKey(); + assertNotNull(signKey); + this.logger.info(signKey); + } + @Test + public void testSendCoupon() throws Exception { + WxPayCouponSendResult result = this.payService.sendCoupon(WxPayCouponSendRequest.newBuilder() + .couponStockId("123") + .openid("122") + .partnerTradeNo("1212") + .openidCount(1) + .build()); + this.logger.info(result.toString()); + } + + @Test + public void testQueryCouponStock() throws Exception { + WxPayCouponStockQueryResult result = this.payService.queryCouponStock(WxPayCouponStockQueryRequest.newBuilder() + .couponStockId("123") + .build()); + this.logger.info(result.toString()); + } + + @Test + public void testQueryCouponInfo() throws Exception { + WxPayCouponInfoQueryResult result = this.payService.queryCouponInfo(WxPayCouponInfoQueryRequest.newBuilder() + .openid("onqOjjrXT-776SpHnfexGm1_P7iE") + .couponId("11") + .stockId("1121") + .build()); + this.logger.info(result.toString()); + } } diff --git a/weixin-java-pay/src/test/resources/.gitignore b/weixin-java-pay/src/test/resources/.gitignore new file mode 100644 index 0000000000..edd25bc776 --- /dev/null +++ b/weixin-java-pay/src/test/resources/.gitignore @@ -0,0 +1 @@ +*.p12 diff --git a/weixin-java-pay/src/test/resources/logback-test.xml b/weixin-java-pay/src/test/resources/logback-test.xml index 75de28c04c..35deb2ed28 100644 --- a/weixin-java-pay/src/test/resources/logback-test.xml +++ b/weixin-java-pay/src/test/resources/logback-test.xml @@ -12,5 +12,4 @@ -