@@ -53,3 +53,6 @@ sonar-project.properties | |||
!/.mvn/wrapper/maven-wrapper.jar | |||
*.versionsBackup | |||
# STS | |||
.factorypath |
@@ -8,10 +8,10 @@ | |||
- (***暂停此种方式,请使用第一种***)另外一种贡献代码的方式就是加入SDK Developers开发组,前提是对自己的代码足够自信就可以申请加入,加入之后可以随时直接提交代码,但要注意对所做的修改或新增的代码进行单元测试,保证提交代码没有明显问题。 | |||
### PR方式贡献代码步骤 | |||
* 在 GitHub 上 `fork` 到自己的仓库,如 `my_user/weixin-java-tools`,然后 `clone` 到本地,并设置用户信息。 | |||
* 在 GitHub 上 `fork` 到自己的仓库,如 `my_user/WxJava`,然后 `clone` 到本地,并设置用户信息。 | |||
```bash | |||
$ git clone git@github.com:my_user/weixin-java-tools.git | |||
$ git clone git@github.com:my_user/WxJava.git | |||
$ cd weixin-java-tools | |||
$ git config user.name "yourname" | |||
$ git config user.email "your email" | |||
@@ -27,7 +27,7 @@ $ git push | |||
* 定期使用项目仓库内容更新自己仓库内容。 | |||
```bash | |||
$ git remote add upstream https://github.com/wechat-group/weixin-java-tools | |||
$ git remote add upstream https://github.com/Wechat-Group/WxJava | |||
$ git fetch upstream | |||
$ git checkout develop | |||
$ git rebase upstream/develop | |||
@@ -5,12 +5,12 @@ | |||
xmlns="http://maven.apache.org/POM/4.0.0"> | |||
<modelVersion>4.0.0</modelVersion> | |||
<groupId>com.github.binarywang</groupId> | |||
<artifactId>weixin-java-parent</artifactId> | |||
<version>3.2.0</version> | |||
<artifactId>wx-java</artifactId> | |||
<version>3.3.0</version> | |||
<packaging>pom</packaging> | |||
<name>Weixin Java Tools - Parent</name> | |||
<name>WxJava - Weixin/Wechat Java SDK</name> | |||
<description>微信开发Java SDK</description> | |||
<url>https://github.com/wechat-group/weixin-java-tools</url> | |||
<url>https://github.com/Wechat-Group/WxJava</url> | |||
<licenses> | |||
<license> | |||
@@ -85,6 +85,11 @@ | |||
<email>007gzs@gmail.com</email> | |||
<url>https://github.com/007gzs</url> | |||
</developer> | |||
<developer> | |||
<name>Howard Liu</name> | |||
<email>liuxinghao1988@gmail.com</email> | |||
<url>https://github.com/howardliu-cn</url> | |||
</developer> | |||
</developers> | |||
<scm> | |||
@@ -166,7 +171,7 @@ | |||
<dependency> | |||
<groupId>com.thoughtworks.xstream</groupId> | |||
<artifactId>xstream</artifactId> | |||
<version>1.4.9</version> | |||
<version>1.4.10</version> | |||
</dependency> | |||
<!-- 由于guava较新的21.0版本需要jdk8,故而此处采用较低版本 --> | |||
<dependency> | |||
@@ -140,14 +140,6 @@ | |||
</module> | |||
<module name="OverloadMethodsDeclarationOrder"/> | |||
<module name="VariableDeclarationUsageDistance"/> | |||
<module name="CustomImportOrder"> | |||
<property name="customImportOrderRules" | |||
value="SPECIAL_IMPORTS###STANDARD_JAVA_PACKAGE###THIRD_PARTY_PACKAGE###STATIC"/> | |||
<property name="specialImportsRegExp" value="^javax\."/> | |||
<property name="standardPackageRegExp" value="^java\."/> | |||
<property name="sortImportsInGroupAlphabetically" value="true"/> | |||
<property name="separateLineBetweenGroups" value="false"/> | |||
</module> | |||
<module name="MethodParamPad"/> | |||
<module name="ParenPad"/> | |||
<module name="OperatorWrap"> | |||
@@ -3,9 +3,9 @@ | |||
[![码云Gitee](https://gitee.com/binary/weixin-java-tools/badge/star.svg?theme=blue)](https://gitee.com/binary/weixin-java-tools) | |||
[![Github](http://github-svg-buttons.herokuapp.com/star.svg?user=Wechat-Group&repo=WxJava&style=flat&background=1081C1)](https://github.com/Wechat-Group/WxJava) | |||
[![GitHub release](https://img.shields.io/github/release/Wechat-Group/WxJava.svg)](https://github.com/Wechat-Group/WxJava/releases) | |||
[![Maven Central](https://img.shields.io/maven-central/v/com.github.binarywang/weixin-java-parent.svg)](http://mvnrepository.com/artifact/com.github.binarywang/weixin-java-parent) | |||
[![Maven Central](https://img.shields.io/maven-central/v/com.github.binarywang/wx-java.svg)](http://mvnrepository.com/artifact/com.github.binarywang/wx-java) | |||
[![Build Status](https://travis-ci.org/Wechat-Group/WxJava.svg?branch=develop)](https://travis-ci.org/Wechat-Group/WxJava) | |||
[![使用IntelliJ IDEA开发维护](https://img.shields.io/badge/IntelliJ%20IDEA-提供支持-blue.svg)](https://www.jetbrains.com/?from=weixin-java-tools) | |||
[![使用IntelliJ IDEA开发维护](https://img.shields.io/badge/IntelliJ%20IDEA-提供支持-blue.svg)](https://www.jetbrains.com/?from=WxJava-weixin-java-tools) | |||
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) | |||
--------------------------------- | |||
@@ -13,8 +13,7 @@ | |||
--------------------------------- | |||
### 重要信息 | |||
1. **2018-09-24 发布 [【3.2.0正式版】](https://github.com/Wechat-Group/WxJava/releases)**! | |||
1. **2018-12-23 项目更名为WxJava,并发布 [【3.3.0正式版】](https://github.com/Wechat-Group/WxJava/releases)**! | |||
1. 新手重要提示:本项目仅是一个SDK开发工具包,未提供Web实现,建议使用maven或gradle引用本项目即可使用本SDK提供的各种功能,详情可参考 **[【Demo项目】](demo.md)** 或本项目中的部分单元测试代码;另外微信开发新手请务必阅读[【开发文档 Wiki 首页】](https://github.com/Wechat-Group/WxJava/wiki)的常见问题部分,可以少走很多弯路,节省不少时间。 | |||
--------------------------------- | |||
@@ -152,3 +151,4 @@ | |||
1. [qsjia (QSJia)](http://github.com/qsjia) | |||
1. [webcreazy (webcreazy)](http://github.com/webcreazy) | |||
1. [cwivan (鱼丸Cwivan)](http://github.com/cwivan) | |||
1. [howardliu-cn (看山)](https://github.com/howardliu-cn) |
@@ -6,12 +6,12 @@ | |||
<modelVersion>4.0.0</modelVersion> | |||
<parent> | |||
<groupId>com.github.binarywang</groupId> | |||
<artifactId>weixin-java-parent</artifactId> | |||
<version>3.2.0</version> | |||
<artifactId>wx-java</artifactId> | |||
<version>3.3.0</version> | |||
</parent> | |||
<artifactId>weixin-java-common</artifactId> | |||
<name>Weixin Java Tools - Common</name> | |||
<name>WxJava - Common</name> | |||
<description>微信开发Java SDK公共模块</description> | |||
<dependencies> | |||
@@ -114,6 +114,11 @@ | |||
<artifactId>assertj-guava</artifactId> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.dom4j</groupId> | |||
<artifactId>dom4j</artifactId> | |||
<version>2.0.0</version> | |||
</dependency> | |||
</dependencies> | |||
<build> | |||
@@ -250,6 +250,15 @@ public class WxConsts { | |||
*/ | |||
public static final String CARD_PAY_ORDER = "card_pay_order"; | |||
/** | |||
* 小程序审核事件:审核通过 | |||
*/ | |||
public static final String WEAPP_AUDIT_SUCCESS = "weapp_audit_success"; | |||
/** | |||
* 小程序审核事件:审核不通过 | |||
*/ | |||
public static final String WEAPP_AUDIT_FAIL = "weapp_audit_fail"; | |||
} | |||
@@ -76,7 +76,7 @@ public class WxMessageInMemoryDuplicateChecker implements WxMessageDuplicateChec | |||
} | |||
} | |||
} catch (InterruptedException e) { | |||
e.printStackTrace(); | |||
Thread.currentThread().interrupt(); | |||
} | |||
} | |||
}); | |||
@@ -2,10 +2,8 @@ package me.chanjar.weixin.common.bean; | |||
import java.io.Serializable; | |||
import org.apache.commons.lang3.builder.ToStringBuilder; | |||
import org.apache.commons.lang3.builder.ToStringStyle; | |||
import lombok.Data; | |||
import me.chanjar.weixin.common.util.json.WxGsonBuilder; | |||
/** | |||
* 卡券Api签名. | |||
@@ -37,6 +35,6 @@ public class WxCardApiSignature implements Serializable { | |||
@Override | |||
public String toString() { | |||
return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); | |||
return WxGsonBuilder.create().toJson(this); | |||
} | |||
} |
@@ -7,9 +7,6 @@ import java.nio.charset.StandardCharsets; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import org.apache.commons.lang3.builder.ToStringBuilder; | |||
import org.apache.commons.lang3.builder.ToStringStyle; | |||
import lombok.Data; | |||
import me.chanjar.weixin.common.util.json.WxGsonBuilder; | |||
@@ -49,7 +46,7 @@ public class WxMenu implements Serializable { | |||
@Override | |||
public String toString() { | |||
return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); | |||
return this.toJson(); | |||
} | |||
} |
@@ -4,11 +4,9 @@ import java.io.Serializable; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import org.apache.commons.lang3.builder.ToStringBuilder; | |||
import org.apache.commons.lang3.builder.ToStringStyle; | |||
import com.google.gson.annotations.SerializedName; | |||
import lombok.Data; | |||
import me.chanjar.weixin.common.util.json.WxGsonBuilder; | |||
/** | |||
* menu button. | |||
@@ -83,7 +81,7 @@ public class WxMenuButton implements Serializable { | |||
@Override | |||
public String toString() { | |||
return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); | |||
return WxGsonBuilder.create().toJson(this); | |||
} | |||
} |
@@ -2,11 +2,9 @@ package me.chanjar.weixin.common.bean.menu; | |||
import java.io.Serializable; | |||
import org.apache.commons.lang3.builder.ToStringBuilder; | |||
import org.apache.commons.lang3.builder.ToStringStyle; | |||
import com.google.gson.annotations.SerializedName; | |||
import lombok.Data; | |||
import me.chanjar.weixin.common.util.json.WxGsonBuilder; | |||
/** | |||
* menu rule. | |||
@@ -31,6 +29,6 @@ public class WxMenuRule implements Serializable { | |||
@Override | |||
public String toString() { | |||
return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); | |||
return WxGsonBuilder.create().toJson(this); | |||
} | |||
} |
@@ -2,9 +2,6 @@ package me.chanjar.weixin.common.bean.result; | |||
import java.io.Serializable; | |||
import org.apache.commons.lang3.builder.ToStringBuilder; | |||
import org.apache.commons.lang3.builder.ToStringStyle; | |||
import lombok.Data; | |||
import me.chanjar.weixin.common.util.json.WxGsonBuilder; | |||
@@ -28,7 +25,7 @@ public class WxMediaUploadResult implements Serializable { | |||
@Override | |||
public String toString() { | |||
return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); | |||
return WxGsonBuilder.create().toJson(this); | |||
} | |||
} |
@@ -794,8 +794,7 @@ public enum WxCpErrorMsgEnum { | |||
* 通过错误代码查找其中文含义. | |||
*/ | |||
public static String findMsgByCode(int code) { | |||
WxCpErrorMsgEnum[] values = WxCpErrorMsgEnum.values(); | |||
for (WxCpErrorMsgEnum value : values) { | |||
for (WxCpErrorMsgEnum value : WxCpErrorMsgEnum.values()) { | |||
if (value.code == code) { | |||
return value.msg; | |||
} | |||
@@ -64,6 +64,11 @@ public class WxError implements Serializable { | |||
if (msg != null) { | |||
wxError.setErrorMsg(msg); | |||
} | |||
} else if (type == WxType.MiniApp) { | |||
final String msg = WxMaErrorMsgEnum.findMsgByCode(wxError.getErrorCode()); | |||
if (msg != null) { | |||
wxError.setErrorMsg(msg); | |||
} | |||
} | |||
return wxError; | |||
@@ -0,0 +1,253 @@ | |||
package me.chanjar.weixin.common.error; | |||
import lombok.Getter; | |||
/** | |||
* 微信小程序错误码 | |||
* | |||
* @author <a href="https://github.com/biggates">biggates</a> | |||
*/ | |||
@Getter | |||
public enum WxMaErrorMsgEnum { | |||
/** | |||
* <pre> | |||
* 获取 access_token 时 AppSecret 错误, | |||
* 或者 access_token 无效。请开发者认真比对 AppSecret 的正确性,或查看是否正在为恰当的小程序调用接口 | |||
* 对应操作:<code>sendCustomerMessage</code> | |||
* 对应地址: | |||
* POST https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN | |||
* 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/customer-message/sendCustomerMessage.html | |||
* </pre> | |||
*/ | |||
CODE_40001(40001, "access_token 无效或 AppSecret 错误"), | |||
/** | |||
* <pre> | |||
* 不合法的凭证类型 | |||
* 对应操作:<code>sendCustomerMessage</code> | |||
* 对应地址: | |||
* POST https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN | |||
* 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/customer-message/sendCustomerMessage.html | |||
* </pre> | |||
*/ | |||
CODE_40002(40002, "不合法的凭证类型"), | |||
/** | |||
* <pre> | |||
* touser不是正确的openid. | |||
* 对应操作:<code>sendCustomerMessage</code>, <code>sendUniformMessage</code> | |||
* 对应地址: | |||
* POST https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN | |||
* POST https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token=ACCESS_TOKEN | |||
* 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/customer-message/sendCustomerMessage.html | |||
* https://developers.weixin.qq.com/miniprogram/dev/api/open-api/uniform-message/sendUniformMessage.html | |||
* </pre> | |||
*/ | |||
CODE_40003(40003, "openid 不正确"), | |||
/** | |||
* <pre> | |||
* 无效媒体文件类型 | |||
* 对应操作:<code>uploadTempMedia</code> | |||
* 对应地址: | |||
* POST https://api.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=TYPE | |||
* 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/customer-message/uploadTempMedia.html | |||
* </pre> | |||
*/ | |||
CODE_40004(40004, "无效媒体文件类型"), | |||
/** | |||
* <pre> | |||
* 无效媒体文件 ID. | |||
* 对应操作:<code>getTempMedia</code> | |||
* 对应地址: | |||
* GET https://api.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_ID | |||
* 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/customer-message/getTempMedia.html | |||
* </pre> | |||
*/ | |||
CODE_40007(40007, "无效媒体文件 ID"), | |||
/** | |||
* <pre> | |||
* appid不正确,或者不符合绑定关系要求. | |||
* 对应操作:<code>sendUniformMessage</code> | |||
* 对应地址: | |||
* POST https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token=ACCESS_TOKEN | |||
* 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/uniform-message/sendUniformMessage.html | |||
* </pre> | |||
*/ | |||
CODE_40013(40013, "appid不正确,或者不符合绑定关系要求"), | |||
/** | |||
* <pre> | |||
* template_id 不正确. | |||
* 对应操作:<code>sendUniformMessage</code>, <code>sendTemplateMessage</code> | |||
* 对应地址: | |||
* POST https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token=ACCESS_TOKEN | |||
* POST https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=ACCESS_TOKEN | |||
* 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/uniform-message/sendUniformMessage.html | |||
* https://developers.weixin.qq.com/miniprogram/dev/api/open-api/template-message/sendTemplateMessage.html | |||
* </pre> | |||
*/ | |||
CODE_40037(40037, "template_id 不正确"), | |||
/** | |||
* <pre> | |||
* form_id不正确,或者过期. | |||
* 对应操作:<code>sendUniformMessage</code>, <code>sendTemplateMessage</code> | |||
* 对应地址: | |||
* POST https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token=ACCESS_TOKEN | |||
* POST https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=ACCESS_TOKEN | |||
* 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/uniform-message/sendUniformMessage.html | |||
* https://developers.weixin.qq.com/miniprogram/dev/api/open-api/template-message/sendTemplateMessage.html | |||
* </pre> | |||
*/ | |||
CODE_41028(41028, "form_id 不正确,或者过期"), | |||
/** | |||
* <pre> | |||
* code 或 template_id 不正确. | |||
* 对应操作:<code>code2Session</code>, <code>sendUniformMessage</code>, <code>sendTemplateMessage</code> | |||
* 对应地址: | |||
* GET https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code | |||
* POST https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token=ACCESS_TOKEN | |||
* POST https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=ACCESS_TOKEN | |||
* 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/login/code2Session.html | |||
* https://developers.weixin.qq.com/miniprogram/dev/api/open-api/uniform-message/sendUniformMessage.html | |||
* https://developers.weixin.qq.com/miniprogram/dev/api/open-api/template-message/sendTemplateMessage.html | |||
* </pre> | |||
*/ | |||
CODE_41029(41029, "请求的参数不正确"), | |||
/** | |||
* <pre> | |||
* form_id 已被使用,或者所传page页面不存在,或者小程序没有发布 | |||
* 对应操作:<code>sendUniformMessage</coce>, <code>getWXACodeUnlimit</code> | |||
* 对应地址: | |||
* POST https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token=ACCESS_TOKEN | |||
* POST https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=ACCESS_TOKEN | |||
* 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/uniform-message/sendUniformMessage.html | |||
* https://developers.weixin.qq.com/miniprogram/dev/api/open-api/qr-code/getWXACodeUnlimit.html | |||
* </pre> | |||
*/ | |||
CODE_41030(41030, "请求的参数不正确"), | |||
/** | |||
* <pre> | |||
* 调用分钟频率受限. | |||
* 对应操作:<code>getWXACodeUnlimit</code>, <code>sendUniformMessage</code>, <code>sendTemplateMessage</code> | |||
* 对应地址: | |||
* POST https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token=ACCESS_TOKEN | |||
* POST https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=ACCESS_TOKEN | |||
* POST https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=ACCESS_TOKEN | |||
* 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/uniform-message/sendUniformMessage.html | |||
* https://developers.weixin.qq.com/miniprogram/dev/api/open-api/qr-code/getWXACodeUnlimit.html | |||
* </pre> | |||
*/ | |||
CODE_45009(45009, "调用分钟频率受限"), | |||
/** | |||
* <pre> | |||
* 频率限制,每个用户每分钟100次. | |||
* 对应操作:<code>code2Session</code> | |||
* 对应地址: | |||
* GET https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code | |||
* 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/login/code2Session.html | |||
* </pre> | |||
*/ | |||
CODE_45011(45011, "频率限制,每个用户每分钟100次"), | |||
/** | |||
* <pre> | |||
* 回复时间超过限制. | |||
* 对应操作:<code>sendCustomerMessage</code> | |||
* 对应地址: | |||
* POST https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN | |||
* 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/customer-message/sendCustomerMessage.html | |||
* </pre> | |||
*/ | |||
CODE_45015(45015, "回复时间超过限制"), | |||
/** | |||
* <pre> | |||
* 接口调用超过限额, 或生成码个数总和到达最大个数限制. | |||
* 对应操作:<code>createWXAQRCode</code>, <code>sendTemplateMessage</code> | |||
* 对应地址: | |||
* POST https://api.weixin.qq.com/cgi-bin/wxaapp/createwxaqrcode?access_token=ACCESS_TOKEN | |||
* POST https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=ACCESS_TOKEN | |||
* 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/qr-code/getWXACode.html | |||
* https://developers.weixin.qq.com/miniprogram/dev/api/open-api/template-message/sendTemplateMessage.html | |||
* </pre> | |||
*/ | |||
CODE_45029(45029, "接口调用超过限额"), | |||
/** | |||
* <pre> | |||
* 客服接口下行条数超过上限. | |||
* 对应操作:<code>sendCustomerMessage</code> | |||
* 对应地址: | |||
* POST https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN | |||
* 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/customer-message/sendCustomerMessage.html | |||
* </pre> | |||
*/ | |||
CODE_45047(45047, "客服接口下行条数超过上限"), | |||
/** | |||
* <pre> | |||
* command字段取值不对 | |||
* 对应操作:<code>customerTyping</code> | |||
* 对应地址: | |||
* POST https://api.weixin.qq.com/cgi-bin/message/custom/typing?access_token=ACCESS_TOKEN | |||
* 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/customer-message/customerTyping.html | |||
* </pre> | |||
*/ | |||
CODE_45072(45072, "command字段取值不对"), | |||
/** | |||
* <pre> | |||
* 下发输入状态,需要之前30秒内跟用户有过消息交互. | |||
* 对应操作:<code>customerTyping</code> | |||
* 对应地址: | |||
* POST https://api.weixin.qq.com/cgi-bin/message/custom/typing?access_token=ACCESS_TOKEN | |||
* 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/customer-message/customerTyping.html | |||
*/ | |||
CODE_45080(45080, "下发输入状态,需要之前30秒内跟用户有过消息交互"), | |||
/** | |||
* <pre> | |||
* 已经在输入状态,不可重复下发. | |||
* 对应操作:<code>customerTyping</code> | |||
* 对应地址: | |||
* POST https://api.weixin.qq.com/cgi-bin/message/custom/typing?access_token=ACCESS_TOKEN | |||
* 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/customer-message/customerTyping.html | |||
* </pre> | |||
*/ | |||
CODE_45081(45081, "已经在输入状态,不可重复下发"), | |||
/** | |||
* <pre> | |||
* API 功能未授权,请确认小程序已获得该接口. | |||
* 对应操作:<code>sendCustomerMessage</code> | |||
* 对应地址: | |||
* POST https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN | |||
* 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/customer-message/sendCustomerMessage.html | |||
* </pre> | |||
*/ | |||
CODE_48001(48001, "API 功能未授权"), | |||
/** | |||
* <pre> | |||
* 内容含有违法违规内容. | |||
* 对应操作:<code>imgSecCheck</code>, <code>msgSecCheck</code> | |||
* 对应地址: | |||
* POST https://api.weixin.qq.com/wxa/img_sec_check?access_token=ACCESS_TOKEN | |||
* POST https://api.weixin.qq.com/wxa/msg_sec_check?access_token=ACCESS_TOKEN | |||
* 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/sec-check/imgSecCheck.html | |||
* https://developers.weixin.qq.com/miniprogram/dev/api/open-api/sec-check/msgSecCheck.html | |||
* </pre> | |||
*/ | |||
CODE_87014(87014, "内容含有违法违规内容"); | |||
private int code; | |||
private String msg; | |||
WxMaErrorMsgEnum(int code, String msg) { | |||
this.code = code; | |||
this.msg = msg; | |||
} | |||
/** | |||
* <pre> | |||
* 通过错误代码查找其中文含义. | |||
*/ | |||
public static String findMsgByCode(int code) { | |||
for (WxMaErrorMsgEnum value : WxMaErrorMsgEnum.values()) { | |||
if (value.code == code) { | |||
return value.msg; | |||
} | |||
} | |||
return null; | |||
} | |||
} |
@@ -642,8 +642,7 @@ public enum WxMpErrorMsgEnum { | |||
* 通过错误代码查找其中文含义. | |||
*/ | |||
public static String findMsgByCode(int code) { | |||
WxMpErrorMsgEnum[] values = WxMpErrorMsgEnum.values(); | |||
for (WxMpErrorMsgEnum value : values) { | |||
for (WxMpErrorMsgEnum value : WxMpErrorMsgEnum.values()) { | |||
if (value.code == code) { | |||
return value.msg; | |||
} | |||
@@ -196,6 +196,7 @@ public class StandardSessionManager implements WxSessionManager, InternalSession | |||
Thread.sleep(StandardSessionManager.this.backgroundProcessorDelay * 1000L); | |||
backgroundProcess(); | |||
} catch (InterruptedException e) { | |||
Thread.currentThread().interrupt(); | |||
StandardSessionManager.this.log.error("SessionManagerImpl.backgroundProcess error", e); | |||
} | |||
} | |||
@@ -1,12 +1,14 @@ | |||
package me.chanjar.weixin.common.util; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.apache.commons.codec.binary.Hex; | |||
import javax.crypto.Mac; | |||
import javax.crypto.spec.SecretKeySpec; | |||
import java.nio.charset.StandardCharsets; | |||
import java.security.InvalidKeyException; | |||
import java.security.NoSuchAlgorithmException; | |||
import javax.crypto.Mac; | |||
import javax.crypto.spec.SecretKeySpec; | |||
import org.apache.commons.codec.binary.Hex; | |||
import lombok.extern.slf4j.Slf4j; | |||
/** | |||
* <pre> | |||
@@ -27,9 +29,9 @@ public class SignUtils { | |||
public static String createHmacSha256Sign(String message, String key) { | |||
try { | |||
Mac sha256 = Mac.getInstance("HmacSHA256"); | |||
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), "HmacSHA256"); | |||
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "HmacSHA256"); | |||
sha256.init(secretKeySpec); | |||
byte[] bytes = sha256.doFinal(message.getBytes()); | |||
byte[] bytes = sha256.doFinal(message.getBytes(StandardCharsets.UTF_8)); | |||
return Hex.encodeHexString(bytes).toUpperCase(); | |||
} catch (NoSuchAlgorithmException | InvalidKeyException e) { | |||
SignUtils.log.error(e.getMessage(), e); | |||
@@ -0,0 +1,96 @@ | |||
package me.chanjar.weixin.common.util; | |||
import java.io.StringReader; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import org.dom4j.Document; | |||
import org.dom4j.DocumentException; | |||
import org.dom4j.Element; | |||
import org.dom4j.Node; | |||
import org.dom4j.io.SAXReader; | |||
import org.dom4j.tree.DefaultText; | |||
import com.google.common.collect.Lists; | |||
import com.google.common.collect.Maps; | |||
import com.google.common.collect.Sets; | |||
/** | |||
* <pre> | |||
* XML转换工具类. | |||
* Created by Binary Wang on 2018/11/4. | |||
* </pre> | |||
* | |||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
*/ | |||
public class XmlUtils { | |||
public static Map<String, Object> xml2Map(String xmlString) { | |||
Map<String, Object> map = new HashMap<>(16); | |||
try { | |||
SAXReader saxReader = new SAXReader(); | |||
Document doc = saxReader.read(new StringReader(xmlString)); | |||
Element root = doc.getRootElement(); | |||
List<Element> elements = root.elements(); | |||
for (Element element : elements) { | |||
map.put(element.getName(), element2MapOrString(element)); | |||
} | |||
} catch (DocumentException e) { | |||
throw new RuntimeException(e); | |||
} | |||
return map; | |||
} | |||
private static Object element2MapOrString(Element element) { | |||
Map<String, Object> result = Maps.newHashMap(); | |||
final List<Node> content = element.content(); | |||
if (content.size() <= 1) { | |||
return element.getText(); | |||
} | |||
final Set<String> names = names(content); | |||
if (names.size() == 1) { | |||
// 说明是个列表,各个子对象是相同的name | |||
List<Object> list = Lists.newArrayList(); | |||
for (Node node : content) { | |||
if (node instanceof DefaultText) { | |||
continue; | |||
} | |||
if (node instanceof Element) { | |||
list.add(element2MapOrString((Element) node)); | |||
} | |||
} | |||
result.put(names.iterator().next(), list); | |||
} else { | |||
for (Node node : content) { | |||
if (node instanceof DefaultText) { | |||
continue; | |||
} | |||
if (node instanceof Element) { | |||
result.put(node.getName(), element2MapOrString((Element) node)); | |||
} | |||
} | |||
} | |||
return result; | |||
} | |||
private static Set<String> names(List<Node> nodes) { | |||
Set<String> names = Sets.newHashSet(); | |||
for (Node node : nodes) { | |||
if (node instanceof DefaultText) { | |||
continue; | |||
} | |||
names.add(node.getName()); | |||
} | |||
return names; | |||
} | |||
} |
@@ -1,22 +1,22 @@ | |||
/** | |||
/* | |||
* 对公众平台发送给公众账号的消息加解密示例代码. | |||
* | |||
* @copyright Copyright (c) 1998-2014 Tencent Inc. | |||
*/ | |||
// ------------------------------------------------------------------------ | |||
package me.chanjar.weixin.common.util.crypto; | |||
import java.nio.charset.Charset; | |||
import java.nio.charset.StandardCharsets; | |||
import java.util.Arrays; | |||
/** | |||
* 提供基于PKCS7算法的加解 | |||
* 提供基于PKCS7算法的加解. | |||
* | |||
* @author tencent | |||
*/ | |||
public class PKCS7Encoder { | |||
private static final Charset CHARSET = Charset.forName("utf-8"); | |||
private static final Charset CHARSET = StandardCharsets.UTF_8; | |||
private static final int BLOCK_SIZE = 32; | |||
/** | |||
@@ -28,20 +28,17 @@ public class PKCS7Encoder { | |||
public static byte[] encode(int count) { | |||
// 计算需要填充的位数 | |||
int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE); | |||
if (amountToPad == 0) { | |||
amountToPad = BLOCK_SIZE; | |||
} | |||
// 获得补位所用的字符 | |||
char padChr = chr(amountToPad); | |||
String tmp = new String(); | |||
StringBuilder tmp = new StringBuilder(); | |||
for (int index = 0; index < amountToPad; index++) { | |||
tmp += padChr; | |||
tmp.append(padChr); | |||
} | |||
return tmp.getBytes(CHARSET); | |||
return tmp.toString().getBytes(CHARSET); | |||
} | |||
/** | |||
* 删除解密后明文的补位字符 | |||
* 删除解密后明文的补位字符. | |||
* | |||
* @param decrypted 解密后的明文 | |||
* @return 删除补位字符后的明文 | |||
@@ -55,12 +52,12 @@ public class PKCS7Encoder { | |||
} | |||
/** | |||
* 将数字转化成ASCII码对应的字符,用于对明文进行补码 | |||
* 将数字转化成ASCII码对应的字符,用于对明文进行补码. | |||
* | |||
* @param a 需要转化的数字 | |||
* @return 转化得到的字符 | |||
*/ | |||
public static char chr(int a) { | |||
private static char chr(int a) { | |||
byte target = (byte) (a & 0xFF); | |||
return (char) target; | |||
} | |||
@@ -1,21 +1,21 @@ | |||
package me.chanjar.weixin.common.util.crypto; | |||
import org.apache.commons.codec.binary.Base64; | |||
import org.w3c.dom.Document; | |||
import org.w3c.dom.Element; | |||
import org.xml.sax.InputSource; | |||
import java.io.StringReader; | |||
import java.nio.charset.Charset; | |||
import java.nio.charset.StandardCharsets; | |||
import java.util.Arrays; | |||
import java.util.Random; | |||
import javax.crypto.Cipher; | |||
import javax.crypto.spec.IvParameterSpec; | |||
import javax.crypto.spec.SecretKeySpec; | |||
import javax.xml.parsers.DocumentBuilder; | |||
import javax.xml.parsers.DocumentBuilderFactory; | |||
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; | |||
import org.apache.commons.codec.binary.Base64; | |||
import org.w3c.dom.Document; | |||
import org.w3c.dom.Element; | |||
import org.xml.sax.InputSource; | |||
/** | |||
* <pre> | |||
@@ -25,6 +25,8 @@ import java.util.Random; | |||
* 需要导入架包commons-codec-1.9(或commons-codec-1.8等其他版本) | |||
* 官方下载地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi | |||
* </pre> | |||
* | |||
* @author Tencent | |||
*/ | |||
public class WxCryptUtil { | |||
@@ -35,7 +37,9 @@ public class WxCryptUtil { | |||
@Override | |||
protected DocumentBuilder initialValue() { | |||
try { | |||
return DocumentBuilderFactory.newInstance().newDocumentBuilder(); | |||
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); | |||
factory.setExpandEntityReferences(false); | |||
return factory.newDocumentBuilder(); | |||
} catch (ParserConfigurationException exc) { | |||
throw new IllegalArgumentException(exc); | |||
} | |||
@@ -164,8 +168,7 @@ public class WxCryptUtil { | |||
ByteGroup byteCollector = new ByteGroup(); | |||
byte[] randomStringBytes = randomStr.getBytes(CHARSET); | |||
byte[] plainTextBytes = plainText.getBytes(CHARSET); | |||
byte[] bytesOfSizeInNetworkOrder = number2BytesInNetworkOrder( | |||
plainTextBytes.length); | |||
byte[] bytesOfSizeInNetworkOrder = number2BytesInNetworkOrder(plainTextBytes.length); | |||
byte[] appIdBytes = this.appidOrCorpid.getBytes(CHARSET); | |||
// randomStr + networkBytesOrder + text + appid | |||
@@ -252,7 +255,7 @@ public class WxCryptUtil { | |||
throw new RuntimeException(e); | |||
} | |||
String xmlContent, from_appid; | |||
String xmlContent, fromAppid; | |||
try { | |||
// 去除补位字符 | |||
byte[] bytes = PKCS7Encoder.decode(original); | |||
@@ -264,14 +267,14 @@ public class WxCryptUtil { | |||
xmlContent = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength), | |||
CHARSET); | |||
from_appid = new String( | |||
fromAppid = new String( | |||
Arrays.copyOfRange(bytes, 20 + xmlLength, bytes.length), CHARSET); | |||
} catch (Exception e) { | |||
throw new RuntimeException(e); | |||
} | |||
// appid不相同的情况 | |||
if (!from_appid.equals(this.appidOrCorpid)) { | |||
if (!fromAppid.equals(this.appidOrCorpid)) { | |||
throw new RuntimeException("AppID不正确"); | |||
} | |||
@@ -1,13 +1,16 @@ | |||
package me.chanjar.weixin.common.util.http; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import me.chanjar.weixin.common.util.http.apache.ApacheMediaDownloadRequestExecutor; | |||
import me.chanjar.weixin.common.util.http.jodd.JoddHttpMediaDownloadRequestExecutor; | |||
import me.chanjar.weixin.common.util.http.okhttp.OkHttpMediaDownloadRequestExecutor; | |||
import java.io.File; | |||
/** | |||
* 下载媒体文件请求执行器,请求的参数是String, 返回的结果是File | |||
* 下载媒体文件请求执行器. | |||
* 请求的参数是String, 返回的结果是File | |||
* 视频文件不支持下载 | |||
* | |||
* @author Daniel Qian | |||
@@ -21,6 +24,11 @@ public abstract class BaseMediaDownloadRequestExecutor<H, P> implements RequestE | |||
this.tmpDirFile = tmpDirFile; | |||
} | |||
@Override | |||
public void execute(String uri, String data, ResponseHandler<File> handler) throws WxErrorException, IOException { | |||
handler.handle(this.execute(uri, data)); | |||
} | |||
public static RequestExecutor<File, String> create(RequestHttp requestHttp, File tmpDirFile) { | |||
switch (requestHttp.getRequestType()) { | |||
case APACHE_HTTP: | |||
@@ -58,7 +58,7 @@ public class HttpResponseProxy { | |||
private String getFileName(CloseableHttpResponse response) throws WxErrorException { | |||
Header[] contentDispositionHeader = response.getHeaders("Content-disposition"); | |||
if (contentDispositionHeader == null || contentDispositionHeader.length == 0) { | |||
throw new WxErrorException(WxError.builder().errorMsg("无法获取到文件名").build()); | |||
throw new WxErrorException(WxError.builder().errorMsg("无法获取到文件名").errorCode(99999).build()); | |||
} | |||
return this.extractFileNameFromContentString(contentDispositionHeader[0].getValue()); | |||
@@ -76,7 +76,7 @@ public class HttpResponseProxy { | |||
private String extractFileNameFromContentString(String content) throws WxErrorException { | |||
if (content == null || content.length() == 0) { | |||
throw new WxErrorException(WxError.builder().errorMsg("无法获取到文件名").build()); | |||
throw new WxErrorException(WxError.builder().errorMsg("无法获取到文件名").errorCode(99999).build()); | |||
} | |||
Matcher m = PATTERN.matcher(content); | |||
@@ -84,7 +84,7 @@ public class HttpResponseProxy { | |||
return m.group(1); | |||
} | |||
throw new WxErrorException(WxError.builder().errorMsg("无法获取到文件名").build()); | |||
throw new WxErrorException(WxError.builder().errorMsg("无法获取到文件名").errorCode(99999).build()); | |||
} | |||
} |
@@ -1,14 +1,17 @@ | |||
package me.chanjar.weixin.common.util.http; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import me.chanjar.weixin.common.util.http.apache.ApacheMediaUploadRequestExecutor; | |||
import me.chanjar.weixin.common.util.http.jodd.JoddHttpMediaUploadRequestExecutor; | |||
import me.chanjar.weixin.common.util.http.okhttp.OkHttpMediaUploadRequestExecutor; | |||
import java.io.File; | |||
/** | |||
* 上传媒体文件请求执行器,请求的参数是File, 返回的结果是String | |||
* 上传媒体文件请求执行器. | |||
* 请求的参数是File, 返回的结果是String | |||
* | |||
* @author Daniel Qian | |||
*/ | |||
@@ -19,6 +22,11 @@ public abstract class MediaUploadRequestExecutor<H, P> implements RequestExecuto | |||
this.requestHttp = requestHttp; | |||
} | |||
@Override | |||
public void execute(String uri, File data, ResponseHandler<WxMediaUploadResult> handler) throws WxErrorException, IOException { | |||
handler.handle(this.execute(uri, data)); | |||
} | |||
public static RequestExecutor<WxMediaUploadResult, File> create(RequestHttp requestHttp) { | |||
switch (requestHttp.getRequestType()) { | |||
case APACHE_HTTP: | |||
@@ -1,11 +1,11 @@ | |||
package me.chanjar.weixin.common.util.http; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import java.io.IOException; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
/** | |||
* http请求执行器 | |||
* http请求执行器. | |||
* | |||
* @param <T> 返回值类型 | |||
* @param <E> 请求参数类型 | |||
@@ -14,8 +14,25 @@ import java.io.IOException; | |||
public interface RequestExecutor<T, E> { | |||
/** | |||
* 执行http请求. | |||
* | |||
* @param uri uri | |||
* @param data 数据 | |||
* @return 响应结果 | |||
* @throws WxErrorException 自定义异常 | |||
* @throws IOException io异常 | |||
*/ | |||
T execute(String uri, E data) throws WxErrorException, IOException; | |||
/** | |||
* 执行http请求. | |||
* | |||
* @param uri uri | |||
* @param data 数据 | |||
* @param handler http响应处理器 | |||
* @throws WxErrorException 自定义异常 | |||
* @throws IOException io异常 | |||
*/ | |||
void execute(String uri, E data, ResponseHandler<T> handler) throws WxErrorException, IOException; | |||
} |
@@ -2,21 +2,30 @@ package me.chanjar.weixin.common.util.http; | |||
/** | |||
* Created by ecoolper on 2017/4/22. | |||
* | |||
* @author ecoolper | |||
*/ | |||
public interface RequestHttp<H, P> { | |||
/** | |||
* 返回httpClient | |||
* 返回httpClient. | |||
* | |||
* @return 返回httpClient | |||
*/ | |||
H getRequestHttpClient(); | |||
/** | |||
* 返回httpProxy | |||
* 返回httpProxy. | |||
* | |||
* @return 返回httpProxy | |||
*/ | |||
P getRequestHttpProxy(); | |||
/** | |||
* 返回HttpType. | |||
* | |||
* @return HttpType | |||
*/ | |||
HttpType getRequestType(); | |||
} |
@@ -0,0 +1,19 @@ | |||
package me.chanjar.weixin.common.util.http; | |||
/** | |||
* <pre> | |||
* http请求响应回调处理接口. | |||
* Created by Binary Wang on 2018/12/8. | |||
* </pre> | |||
* | |||
* @param <T> 返回值类型 | |||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
*/ | |||
public interface ResponseHandler<T> { | |||
/** | |||
* 响应结果处理. | |||
* | |||
* @param t 要处理的对象 | |||
*/ | |||
void handle(T t); | |||
} |
@@ -1,11 +1,15 @@ | |||
package me.chanjar.weixin.common.util.http; | |||
import java.io.IOException; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
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 | |||
* 简单的GET请求执行器. | |||
* 请求的参数是String, 返回的结果也是String | |||
* | |||
* @author Daniel Qian | |||
*/ | |||
@@ -16,6 +20,11 @@ public abstract class SimpleGetRequestExecutor<H, P> implements RequestExecutor< | |||
this.requestHttp = requestHttp; | |||
} | |||
@Override | |||
public void execute(String uri, String data, ResponseHandler<String> handler) throws WxErrorException, IOException { | |||
handler.handle(this.execute(uri, data)); | |||
} | |||
public static RequestExecutor<String, String> create(RequestHttp requestHttp) { | |||
switch (requestHttp.getRequestType()) { | |||
case APACHE_HTTP: | |||
@@ -1,5 +1,8 @@ | |||
package me.chanjar.weixin.common.util.http; | |||
import java.io.IOException; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import me.chanjar.weixin.common.util.http.apache.ApacheSimplePostRequestExecutor; | |||
import me.chanjar.weixin.common.util.http.jodd.JoddHttpSimplePostRequestExecutor; | |||
import me.chanjar.weixin.common.util.http.okhttp.OkHttpSimplePostRequestExecutor; | |||
@@ -17,6 +20,11 @@ public abstract class SimplePostRequestExecutor<H, P> implements RequestExecutor | |||
this.requestHttp = requestHttp; | |||
} | |||
@Override | |||
public void execute(String uri, String data, ResponseHandler<String> handler) throws WxErrorException, IOException { | |||
handler.handle(this.execute(uri, data)); | |||
} | |||
public static RequestExecutor<String, String> create(RequestHttp requestHttp) { | |||
switch (requestHttp.getRequestType()) { | |||
case APACHE_HTTP: | |||
@@ -1,9 +1,9 @@ | |||
package me.chanjar.weixin.common.util.http; | |||
import org.apache.commons.lang3.StringUtils; | |||
import java.nio.charset.StandardCharsets; | |||
import java.io.UnsupportedEncodingException; | |||
import org.apache.commons.lang3.StringUtils; | |||
public class URIUtil { | |||
@@ -16,27 +16,22 @@ public class URIUtil { | |||
int l = input.length(); | |||
StringBuilder o = new StringBuilder(l * 3); | |||
try { | |||
for (int i = 0; i < l; i++) { | |||
String e = input.substring(i, i + 1); | |||
if (ALLOWED_CHARS.indexOf(e) == -1) { | |||
byte[] b = e.getBytes("utf-8"); | |||
o.append(getHex(b)); | |||
continue; | |||
} | |||
o.append(e); | |||
for (int i = 0; i < l; i++) { | |||
String e = input.substring(i, i + 1); | |||
if (!ALLOWED_CHARS.contains(e)) { | |||
byte[] b = e.getBytes(StandardCharsets.UTF_8); | |||
o.append(getHex(b)); | |||
continue; | |||
} | |||
return o.toString(); | |||
} catch (UnsupportedEncodingException e) { | |||
e.printStackTrace(); | |||
o.append(e); | |||
} | |||
return input; | |||
return o.toString(); | |||
} | |||
private static String getHex(byte buf[]) { | |||
private static String getHex(byte[] buf) { | |||
StringBuilder o = new StringBuilder(buf.length * 3); | |||
for (int i = 0; i < buf.length; i++) { | |||
int n = buf[i] & 0xff; | |||
for (byte aBuf : buf) { | |||
int n = aBuf & 0xff; | |||
o.append("%"); | |||
if (n < 0x10) { | |||
o.append("0"); | |||
@@ -1,16 +1,17 @@ | |||
package me.chanjar.weixin.common.util.http.apache; | |||
import me.chanjar.weixin.common.error.WxError; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import me.chanjar.weixin.common.util.http.RequestHttp; | |||
import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; | |||
import java.io.IOException; | |||
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.CloseableHttpClient; | |||
import java.io.IOException; | |||
import me.chanjar.weixin.common.error.WxError; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import me.chanjar.weixin.common.util.http.RequestHttp; | |||
import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; | |||
/** | |||
* Created by ecoolper on 2017/5/4. | |||
@@ -1,5 +1,9 @@ | |||
package me.chanjar.weixin.common.util.http.apache; | |||
import java.io.IOException; | |||
import java.util.concurrent.TimeUnit; | |||
import java.util.concurrent.atomic.AtomicBoolean; | |||
import org.apache.commons.lang3.StringUtils; | |||
import org.apache.http.HttpHost; | |||
import org.apache.http.annotation.NotThreadSafe; | |||
@@ -25,10 +29,6 @@ import org.apache.http.protocol.HttpContext; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import java.io.IOException; | |||
import java.util.concurrent.TimeUnit; | |||
import java.util.concurrent.atomic.AtomicBoolean; | |||
/** | |||
* httpclient 连接管理器 自带DNS解析. | |||
* <p>大部分代码拷贝自:DefaultApacheHttpClientBuilder</p> | |||
@@ -292,6 +292,7 @@ public class ApacheHttpDnsClientBuilder implements ApacheHttpClientBuilder { | |||
} | |||
} | |||
} catch (InterruptedException ignore) { | |||
Thread.currentThread().interrupt(); | |||
} | |||
} | |||
@@ -25,7 +25,6 @@ import me.chanjar.weixin.common.util.http.RequestHttp; | |||
* Created by ecoolper on 2017/5/5. | |||
*/ | |||
public class ApacheMediaDownloadRequestExecutor extends BaseMediaDownloadRequestExecutor<CloseableHttpClient, HttpHost> { | |||
public ApacheMediaDownloadRequestExecutor(RequestHttp requestHttp, File tmpDirFile) { | |||
super(requestHttp, tmpDirFile); | |||
} | |||
@@ -1,5 +1,8 @@ | |||
package me.chanjar.weixin.common.util.http.apache; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import org.apache.http.HttpEntity; | |||
import org.apache.http.HttpResponse; | |||
import org.apache.http.StatusLine; | |||
@@ -7,18 +10,20 @@ import org.apache.http.client.HttpResponseException; | |||
import org.apache.http.client.ResponseHandler; | |||
import org.apache.http.util.EntityUtils; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
/** | |||
* 输入流响应处理器. | |||
* | |||
* @author Daniel Qian | |||
*/ | |||
public class InputStreamResponseHandler implements ResponseHandler<InputStream> { | |||
public static final ResponseHandler<InputStream> INSTANCE = new InputStreamResponseHandler(); | |||
private static final int STATUS_CODE_300 = 300; | |||
@Override | |||
public InputStream handleResponse(final HttpResponse response) throws IOException { | |||
final StatusLine statusLine = response.getStatusLine(); | |||
final HttpEntity entity = response.getEntity(); | |||
if (statusLine.getStatusCode() >= 300) { | |||
if (statusLine.getStatusCode() >= STATUS_CODE_300) { | |||
EntityUtils.consume(entity); | |||
throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase()); | |||
} | |||
@@ -7,9 +7,13 @@ import me.chanjar.weixin.common.bean.menu.WxMenu; | |||
import me.chanjar.weixin.common.error.WxError; | |||
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | |||
/** | |||
* . | |||
* @author chanjarster | |||
*/ | |||
public class WxGsonBuilder { | |||
public static final GsonBuilder INSTANCE = new GsonBuilder(); | |||
private static final GsonBuilder INSTANCE = new GsonBuilder(); | |||
static { | |||
INSTANCE.disableHtmlEscaping(); | |||
@@ -8,47 +8,47 @@ import com.thoughtworks.xstream.core.util.QuickWriter; | |||
import com.thoughtworks.xstream.io.HierarchicalStreamWriter; | |||
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter; | |||
import com.thoughtworks.xstream.io.xml.XppDriver; | |||
import com.thoughtworks.xstream.security.NullPermission; | |||
import com.thoughtworks.xstream.security.PrimitiveTypePermission; | |||
public class XStreamInitializer { | |||
public static XStream getInstance() { | |||
XStream xstream = new XStream(new PureJavaReflectionProvider(), new XppDriver() { | |||
@Override | |||
public HierarchicalStreamWriter createWriter(Writer out) { | |||
return new PrettyPrintWriter(out, getNameCoder()) { | |||
protected String PREFIX_CDATA = "<![CDATA["; | |||
protected String SUFFIX_CDATA = "]]>"; | |||
protected String PREFIX_MEDIA_ID = "<MediaId>"; | |||
protected String SUFFIX_MEDIA_ID = "</MediaId>"; | |||
@Override | |||
protected void writeText(QuickWriter writer, String text) { | |||
if (text.startsWith(this.PREFIX_CDATA) && text.endsWith(this.SUFFIX_CDATA)) { | |||
writer.write(text); | |||
} else if (text.startsWith(this.PREFIX_MEDIA_ID) && text.endsWith(this.SUFFIX_MEDIA_ID)) { | |||
writer.write(text); | |||
} else { | |||
super.writeText(writer, text); | |||
} | |||
private static final XppDriver XPP_DRIVER = new XppDriver() { | |||
@Override | |||
public HierarchicalStreamWriter createWriter(Writer out) { | |||
return new PrettyPrintWriter(out, getNameCoder()) { | |||
private static final String PREFIX_CDATA = "<![CDATA["; | |||
private static final String SUFFIX_CDATA = "]]>"; | |||
private static final String PREFIX_MEDIA_ID = "<MediaId>"; | |||
private static final String SUFFIX_MEDIA_ID = "</MediaId>"; | |||
@Override | |||
protected void writeText(QuickWriter writer, String text) { | |||
if (text.startsWith(PREFIX_CDATA) && text.endsWith(SUFFIX_CDATA)) { | |||
writer.write(text); | |||
} else if (text.startsWith(PREFIX_MEDIA_ID) && text.endsWith(SUFFIX_MEDIA_ID)) { | |||
writer.write(text); | |||
} else { | |||
super.writeText(writer, text); | |||
} | |||
@Override | |||
public String encodeNode(String name) { | |||
//防止将_转换成__ | |||
return name; | |||
} | |||
}; | |||
} | |||
}); | |||
} | |||
@Override | |||
public String encodeNode(String name) { | |||
//防止将_转换成__ | |||
return name; | |||
} | |||
}; | |||
} | |||
}; | |||
public static XStream getInstance() { | |||
XStream xstream = new XStream(new PureJavaReflectionProvider(), XPP_DRIVER); | |||
xstream.ignoreUnknownElements(); | |||
xstream.setMode(XStream.NO_REFERENCES); | |||
xstream.addPermission(NullPermission.NULL); | |||
xstream.addPermission(PrimitiveTypePermission.PRIMITIVES); | |||
XStream.setupDefaultSecurity(xstream); | |||
xstream.allowTypesByWildcard(new String[]{ | |||
"me.chanjar.weixin.**", "cn.binarywang.wx.**", "com.github.binarywang.**" | |||
}); | |||
xstream.setClassLoader(Thread.currentThread().getContextClassLoader()); | |||
return xstream; | |||
} | |||
@@ -1,8 +1,7 @@ | |||
package me.chanjar.weixin.common.util; | |||
import org.testng.annotations.Test; | |||
import org.testng.annotations.*; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.testng.Assert.*; | |||
/** | |||
@@ -18,6 +17,6 @@ public class DataUtilsTest { | |||
public void testHandleDataWithSecret() { | |||
String data = "js_code=001tZveq0SMoiq1AEXeq0ECJeq0tZveZ&secret=5681022fa1643845392367ea88888888&grant_type=authorization_code&appid=wxe156d4848d999999"; | |||
final String s = DataUtils.handleDataWithSecret(data); | |||
assertThat(s).contains("&secret=******&"); | |||
assertTrue(s.contains("&secret=******&")); | |||
} | |||
} |
@@ -0,0 +1,78 @@ | |||
package me.chanjar.weixin.common.util; | |||
import java.util.List; | |||
import java.util.Map; | |||
import org.testng.annotations.*; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
/** | |||
* <pre> | |||
* | |||
* Created by Binary Wang on 2018/11/4. | |||
* </pre> | |||
* | |||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
*/ | |||
public class XmlUtilsTest { | |||
@Test | |||
public void testXml2Map() { | |||
String xml = "<xml>\n" + | |||
"<CopyrightCheckResult>\n" + | |||
"<Count>2</Count>\n" + | |||
"<ResultList>\n" + | |||
"<item>\n" + | |||
"<ArticleIdx>1</ArticleIdx>\n" + | |||
"<UserDeclareState>0</UserDeclareState>\n" + | |||
"<AuditState>2</AuditState>\n" + | |||
"<OriginalArticleUrl><![CDATA[Url_1]]></OriginalArticleUrl>\n" + | |||
"<OriginalArticleType>1</OriginalArticleType>\n" + | |||
"<CanReprint>1</CanReprint>\n" + | |||
"<NeedReplaceContent>1</NeedReplaceContent>\n" + | |||
"<NeedShowReprintSource>1</NeedShowReprintSource>\n" + | |||
"</item>\n" + | |||
"<item>\n" + | |||
"<ArticleIdx>2</ArticleIdx>\n" + | |||
"<UserDeclareState>0</UserDeclareState>\n" + | |||
"<AuditState>2</AuditState>\n" + | |||
"<OriginalArticleUrl><![CDATA[Url_2]]></OriginalArticleUrl>\n" + | |||
"<OriginalArticleType>1</OriginalArticleType>\n" + | |||
"<CanReprint>1</CanReprint>\n" + | |||
"<NeedReplaceContent>1</NeedReplaceContent>\n" + | |||
"<NeedShowReprintSource>1</NeedShowReprintSource>\n" + | |||
"</item>\n" + | |||
"</ResultList>\n" + | |||
"<CheckState>2</CheckState>\n" + | |||
"</CopyrightCheckResult>\n" + | |||
"</xml>"; | |||
final Map<String, Object> map = XmlUtils.xml2Map(xml); | |||
assertThat(map).isNotNull(); | |||
final Map<String, Object> copyrightCheckResult = (Map<String, Object>) map.get("CopyrightCheckResult"); | |||
List<Map<String, Object>> resultList = (List<Map<String, Object>>) ((Map<String, Object>) copyrightCheckResult.get("ResultList")).get("item"); | |||
assertThat(copyrightCheckResult).isNotNull(); | |||
assertThat(copyrightCheckResult.get("Count")).isEqualTo("2"); | |||
assertThat(copyrightCheckResult.get("CheckState")).isEqualTo("2"); | |||
assertThat(resultList.get(0).get("ArticleIdx")).isEqualTo("1"); | |||
assertThat(resultList.get(0).get("UserDeclareState")).isEqualTo("0"); | |||
assertThat(resultList.get(0).get("AuditState")).isEqualTo("2"); | |||
assertThat(resultList.get(0).get("OriginalArticleUrl")).isEqualTo("Url_1"); | |||
assertThat(resultList.get(0).get("OriginalArticleType")).isEqualTo("1"); | |||
assertThat(resultList.get(0).get("CanReprint")).isEqualTo("1"); | |||
assertThat(resultList.get(0).get("NeedReplaceContent")).isEqualTo("1"); | |||
assertThat(resultList.get(0).get("NeedShowReprintSource")).isEqualTo("1"); | |||
assertThat(resultList.get(1).get("ArticleIdx")).isEqualTo("2"); | |||
assertThat(resultList.get(1).get("UserDeclareState")).isEqualTo("0"); | |||
assertThat(resultList.get(1).get("AuditState")).isEqualTo("2"); | |||
assertThat(resultList.get(1).get("OriginalArticleUrl")).isEqualTo("Url_2"); | |||
assertThat(resultList.get(1).get("OriginalArticleType")).isEqualTo("1"); | |||
assertThat(resultList.get(1).get("CanReprint")).isEqualTo("1"); | |||
assertThat(resultList.get(1).get("NeedReplaceContent")).isEqualTo("1"); | |||
assertThat(resultList.get(1).get("NeedShowReprintSource")).isEqualTo("1"); | |||
} | |||
} |
@@ -1,5 +1,11 @@ | |||
package me.chanjar.weixin.common.util.crypto; | |||
import java.io.IOException; | |||
import java.io.StringReader; | |||
import javax.xml.parsers.DocumentBuilder; | |||
import javax.xml.parsers.DocumentBuilderFactory; | |||
import javax.xml.parsers.ParserConfigurationException; | |||
import org.testng.annotations.*; | |||
import org.w3c.dom.Document; | |||
import org.w3c.dom.Element; | |||
@@ -7,12 +13,6 @@ import org.w3c.dom.NodeList; | |||
import org.xml.sax.InputSource; | |||
import org.xml.sax.SAXException; | |||
import javax.xml.parsers.DocumentBuilder; | |||
import javax.xml.parsers.DocumentBuilderFactory; | |||
import javax.xml.parsers.ParserConfigurationException; | |||
import java.io.IOException; | |||
import java.io.StringReader; | |||
import static org.testng.Assert.*; | |||
@Test | |||
@@ -39,6 +39,7 @@ public class WxCryptUtilTest { | |||
System.out.println(encryptedXml); | |||
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); | |||
documentBuilderFactory.setExpandEntityReferences(false); | |||
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); | |||
Document document = documentBuilder.parse(new InputSource(new StringReader(encryptedXml))); | |||
@@ -81,6 +82,7 @@ public class WxCryptUtilTest { | |||
WxCryptUtil pc = new WxCryptUtil(this.token, this.encodingAesKey, this.appId); | |||
String afterEncrpt = pc.encrypt(this.replyMsg); | |||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); | |||
dbf.setExpandEntityReferences(false); | |||
DocumentBuilder db = dbf.newDocumentBuilder(); | |||
StringReader sr = new StringReader(afterEncrpt); | |||
InputSource is = new InputSource(sr); | |||
@@ -6,13 +6,13 @@ | |||
<modelVersion>4.0.0</modelVersion> | |||
<parent> | |||
<groupId>com.github.binarywang</groupId> | |||
<artifactId>weixin-java-parent</artifactId> | |||
<version>3.2.0</version> | |||
<artifactId>wx-java</artifactId> | |||
<version>3.3.0</version> | |||
</parent> | |||
<artifactId>weixin-java-cp</artifactId> | |||
<name>Weixin Java Tools - CP</name> | |||
<description>微信企业号Java SDK</description> | |||
<name>WxJava - CP</name> | |||
<description>微信企业号/企业微信 Java SDK</description> | |||
<dependencies> | |||
<dependency> | |||
@@ -0,0 +1,48 @@ | |||
package me.chanjar.weixin.cp.api; | |||
import java.util.List; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import me.chanjar.weixin.cp.bean.WxCpChat; | |||
/** | |||
* 群聊服务 | |||
* | |||
* @author gaigeshen | |||
*/ | |||
public interface WxCpChatService { | |||
/** | |||
* 创建群聊会话,注意:刚创建的群,如果没有下发消息,在企业微信不会出现该群。 | |||
* | |||
* @param name 群聊名,最多50个utf8字符,超过将截断 | |||
* @param owner 指定群主的id。如果不指定,系统会随机从userlist中选一人作为群主 | |||
* @param users 群成员id列表。至少2人,至多500人 | |||
* @param chatId 群聊的唯一标志,不能与已有的群重复;字符串类型,最长32个字符。只允许字符0-9及字母a-zA-Z。如果不填,系统会随机生成群id | |||
* @return 创建群聊会话的结果,群聊的唯一标志 | |||
* @throws WxErrorException 发生异常 | |||
*/ | |||
String chatCreate(String name, String owner, List<String> users, String chatId) throws WxErrorException; | |||
/** | |||
* 修改群聊会话 | |||
* | |||
* @param chatId 群聊id | |||
* @param name 新的群聊名。若不需更新,请忽略此参数(null or empty)。最多50个utf8字符,超过将截断 | |||
* @param owner 新群主的id。若不需更新,请忽略此参数(null or empty) | |||
* @param usersToAdd 添加成员的id列表,若不需要更新,则传递空对象或者空集合 | |||
* @param usersToDelete 踢出成员的id列表,若不需要更新,则传递空对象或者空集合 | |||
* @throws WxErrorException 发生异常 | |||
*/ | |||
void chatUpdate(String chatId, String name, String owner, List<String> usersToAdd, List<String> usersToDelete) throws WxErrorException; | |||
/** | |||
* 获取群聊会话 | |||
* | |||
* @param chatId 群聊编号 | |||
* @return 群聊会话 | |||
* @throws WxErrorException 发生异常 | |||
*/ | |||
WxCpChat chatGet(String chatId) throws WxErrorException; | |||
} |
@@ -1,10 +1,10 @@ | |||
package me.chanjar.weixin.cp.api; | |||
import java.util.List; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import me.chanjar.weixin.cp.bean.WxCpDepart; | |||
import java.util.List; | |||
/** | |||
* <pre> | |||
* 部门管理接口 | |||
@@ -17,42 +17,47 @@ public interface WxCpDepartmentService { | |||
/** | |||
* <pre> | |||
* 部门管理接口 - 创建部门 | |||
* 部门管理接口 - 创建部门. | |||
* 最多支持创建500个部门 | |||
* 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=部门管理接口 | |||
* </pre> | |||
* | |||
* @param depart 部门 | |||
* @return 部门id | |||
* @throws WxErrorException 异常 | |||
*/ | |||
Integer create(WxCpDepart depart) throws WxErrorException; | |||
/** | |||
* <pre> | |||
* 部门管理接口 - 查询部门 | |||
* 部门管理接口 - 查询部门. | |||
* 详情请见: http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E9%83%A8%E9%97%A8#.E8.8E.B7.E5.8F.96.E9.83.A8.E9.97.A8.E5.88.97.E8.A1.A8 | |||
* </pre> | |||
* | |||
* @param id 部门id。获取指定部门及其下的子部门。非必需,可为null | |||
* @throws WxErrorException 异常 | |||
*/ | |||
List<WxCpDepart> list(Integer id) throws WxErrorException; | |||
List<WxCpDepart> list(Long id) throws WxErrorException; | |||
/** | |||
* <pre> | |||
* 部门管理接口 - 修改部门名 | |||
* 部门管理接口 - 修改部门名. | |||
* 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=部门管理接口 | |||
* 如果id为0(未部门),1(黑名单),2(星标组),或者不存在的id,微信会返回系统繁忙的错误 | |||
* </pre> | |||
* | |||
* @param group 要更新的group,group的id,name必须设置 | |||
* @throws WxErrorException 异常 | |||
*/ | |||
void update(WxCpDepart group) throws WxErrorException; | |||
/** | |||
* <pre> | |||
* 部门管理接口 - 删除部门 | |||
* 部门管理接口 - 删除部门. | |||
* </pre> | |||
* | |||
* @param departId 部门id | |||
* @throws WxErrorException 异常 | |||
*/ | |||
void delete(Integer departId) throws WxErrorException; | |||
void delete(Long departId) throws WxErrorException; | |||
} |
@@ -19,6 +19,7 @@ public interface WxCpMediaService { | |||
String MEDIA_GET_URL = "https://qyapi.weixin.qq.com/cgi-bin/media/get"; | |||
String MEDIA_UPLOAD_URL = "https://qyapi.weixin.qq.com/cgi-bin/media/upload?type="; | |||
String IMG_UPLOAD_URL = "https://qyapi.weixin.qq.com/cgi-bin/media/uploadimg"; | |||
String JSSDK_MEDIA_GET_URL = "https://qyapi.weixin.qq.com/cgi-bin/media/get/jssdk"; | |||
/** | |||
* <pre> | |||
@@ -59,6 +60,21 @@ public interface WxCpMediaService { | |||
*/ | |||
File download(String mediaId) throws WxErrorException; | |||
/** | |||
* <pre> | |||
* 获取高清语音素材. | |||
* 可以使用本接口获取从JSSDK的uploadVoice接口上传的临时语音素材,格式为speex,16K采样率。该音频比上文的临时素材获取接口(格式为amr,8K采样率)更加清晰,适合用作语音识别等对音质要求较高的业务。 | |||
* 请求方式:GET(HTTPS) | |||
* 请求地址:https://qyapi.weixin.qq.com/cgi-bin/media/get/jssdk?access_token=ACCESS_TOKEN&media_id=MEDIA_ID | |||
* 仅企业微信2.4及以上版本支持。 | |||
* 文档地址:https://work.weixin.qq.com/api/doc#90000/90135/90255 | |||
* </pre> | |||
* | |||
* @param mediaId 媒体id | |||
* @return 保存到本地的临时文件 | |||
*/ | |||
File getJssdkFile(String mediaId) throws WxErrorException; | |||
/** | |||
* <pre> | |||
* 上传图片. | |||
@@ -69,7 +85,7 @@ public interface WxCpMediaService { | |||
* </pre> | |||
* | |||
* @param file 上传的文件对象 | |||
* @return 返回图片url | |||
* @return 返回图片url | |||
*/ | |||
String uploadImg(File file) throws WxErrorException; | |||
} |
@@ -7,7 +7,8 @@ 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.common.util.http.RequestHttp; | |||
import me.chanjar.weixin.cp.bean.*; | |||
import me.chanjar.weixin.cp.bean.WxCpMessage; | |||
import me.chanjar.weixin.cp.bean.WxCpMessageSendResult; | |||
import me.chanjar.weixin.cp.config.WxCpConfigStorage; | |||
/** | |||
@@ -241,13 +242,20 @@ public interface WxCpService { | |||
* 获取用户相关接口的服务类对象 | |||
*/ | |||
WxCpUserService getUserService(); | |||
/** | |||
* 获取群聊服务 | |||
* | |||
* @return 群聊服务 | |||
*/ | |||
WxCpChatService getChatService(); | |||
WxCpAgentService getAgentService(); | |||
/** | |||
* http请求对象 | |||
*/ | |||
RequestHttp getRequestHttp(); | |||
RequestHttp<?, ?> getRequestHttp(); | |||
void setUserService(WxCpUserService userService); | |||
@@ -38,7 +38,7 @@ public interface WxCpUserService { | |||
* @param fetchChild 非必填。1/0:是否递归获取子部门下面的成员 | |||
* @param status 非必填。0获取全部员工,1获取已关注成员列表,2获取禁用成员列表,4获取未关注成员列表。status可叠加 | |||
*/ | |||
List<WxCpUser> listByDepartment(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException; | |||
List<WxCpUser> listByDepartment(Long departId, Boolean fetchChild, Integer status) throws WxErrorException; | |||
/** | |||
* <pre> | |||
@@ -51,7 +51,7 @@ public interface WxCpUserService { | |||
* @param fetchChild 非必填。1/0:是否递归获取子部门下面的成员 | |||
* @param status 非必填。0获取全部员工,1获取已关注成员列表,2获取禁用成员列表,4获取未关注成员列表。status可叠加 | |||
*/ | |||
List<WxCpUser> listSimpleByDepartment(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException; | |||
List<WxCpUser> listSimpleByDepartment(Long departId, Boolean fetchChild, Integer status) throws WxErrorException; | |||
/** | |||
* 新建用户. | |||
@@ -1,9 +1,11 @@ | |||
package me.chanjar.weixin.cp.api.impl; | |||
import com.google.gson.Gson; | |||
import java.util.List; | |||
import com.google.gson.JsonObject; | |||
import com.google.gson.JsonParser; | |||
import com.google.gson.reflect.TypeToken; | |||
import me.chanjar.weixin.common.error.WxError; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import me.chanjar.weixin.cp.api.WxCpAgentService; | |||
@@ -11,8 +13,6 @@ import me.chanjar.weixin.cp.api.WxCpService; | |||
import me.chanjar.weixin.cp.bean.WxCpAgent; | |||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||
import java.util.List; | |||
/** | |||
* <pre> | |||
@@ -0,0 +1,83 @@ | |||
package me.chanjar.weixin.cp.api.impl; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
import org.apache.commons.lang3.StringUtils; | |||
import com.google.gson.JsonParser; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import me.chanjar.weixin.common.util.json.WxGsonBuilder; | |||
import me.chanjar.weixin.cp.api.WxCpChatService; | |||
import me.chanjar.weixin.cp.api.WxCpService; | |||
import me.chanjar.weixin.cp.bean.WxCpChat; | |||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||
/** | |||
* 群聊服务实现 | |||
* | |||
* @author gaigeshen | |||
*/ | |||
public class WxCpChatServiceImpl implements WxCpChatService { | |||
private final WxCpService internalService; | |||
/** | |||
* 创建群聊服务实现的实例 | |||
* | |||
* @param internalService 企业微信的服务 | |||
*/ | |||
public WxCpChatServiceImpl(WxCpService internalService) { | |||
this.internalService = internalService; | |||
} | |||
@Override | |||
public String chatCreate(String name, String owner, List<String> users, String chatId) throws WxErrorException { | |||
Map<String, Object> data = new HashMap<>(4); | |||
if (StringUtils.isNotBlank(name)) { | |||
data.put("name", name); | |||
} | |||
if (StringUtils.isNotBlank(owner)) { | |||
data.put("owner", owner); | |||
} | |||
if (users != null) { | |||
data.put("userlist", users); | |||
} | |||
if (StringUtils.isNotBlank(chatId)) { | |||
data.put("chatid", chatId); | |||
} | |||
String result = internalService.post("https://qyapi.weixin.qq.com/cgi-bin/appchat/create", WxGsonBuilder.create().toJson(data)); | |||
return new JsonParser().parse(result).getAsJsonObject().get("chatid").getAsString(); | |||
} | |||
@Override | |||
public void chatUpdate(String chatId, String name, String owner, List<String> usersToAdd, List<String> usersToDelete) throws WxErrorException { | |||
Map<String, Object> data = new HashMap<>(5); | |||
if (StringUtils.isNotBlank(chatId)) { | |||
data.put("chatid", chatId); | |||
} | |||
if (StringUtils.isNotBlank(name)) { | |||
data.put("name", name); | |||
} | |||
if (StringUtils.isNotBlank(owner)) { | |||
data.put("owner", owner); | |||
} | |||
if (usersToAdd != null && !usersToAdd.isEmpty()) { | |||
data.put("add_user_list", usersToAdd); | |||
} | |||
if (usersToDelete != null && !usersToDelete.isEmpty()) { | |||
data.put("del_user_list", usersToDelete); | |||
} | |||
internalService.post("https://qyapi.weixin.qq.com/cgi-bin/appchat/update", WxGsonBuilder.create().toJson(data)); | |||
} | |||
@Override | |||
public WxCpChat chatGet(String chatId) throws WxErrorException { | |||
String result = internalService.get("https://qyapi.weixin.qq.com/cgi-bin/appchat/get?chatid=" + chatId, null); | |||
return WxCpGsonBuilder.create().fromJson( | |||
new JsonParser().parse(result).getAsJsonObject().getAsJsonObject("chat_info").toString(), WxCpChat.class); | |||
} | |||
} |
@@ -1,5 +1,7 @@ | |||
package me.chanjar.weixin.cp.api.impl; | |||
import java.util.List; | |||
import com.google.gson.JsonElement; | |||
import com.google.gson.JsonParser; | |||
import com.google.gson.reflect.TypeToken; | |||
@@ -10,8 +12,6 @@ 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; | |||
/** | |||
* <pre> | |||
* 部门管理接口 | |||
@@ -42,13 +42,13 @@ public class WxCpDepartmentServiceImpl implements WxCpDepartmentService { | |||
} | |||
@Override | |||
public void delete(Integer departId) throws WxErrorException { | |||
public void delete(Long departId) throws WxErrorException { | |||
String url = "https://qyapi.weixin.qq.com/cgi-bin/department/delete?id=" + departId; | |||
this.mainService.get(url, null); | |||
} | |||
@Override | |||
public List<WxCpDepart> list(Integer id) throws WxErrorException { | |||
public List<WxCpDepart> list(Long id) throws WxErrorException { | |||
String url = "https://qyapi.weixin.qq.com/cgi-bin/department/list"; | |||
if (id != null) { | |||
url += "?id=" + id; | |||
@@ -56,7 +56,7 @@ public class WxCpDepartmentServiceImpl implements WxCpDepartmentService { | |||
String responseContent = this.mainService.get(url, null); | |||
JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | |||
return WxCpGsonBuilder.INSTANCE.create() | |||
return WxCpGsonBuilder.create() | |||
.fromJson(tmpJsonElement.getAsJsonObject().get("department"), | |||
new TypeToken<List<WxCpDepart>>() { | |||
}.getType() | |||
@@ -48,6 +48,14 @@ public class WxCpMediaServiceImpl implements WxCpMediaService { | |||
MEDIA_GET_URL, "media_id=" + mediaId); | |||
} | |||
@Override | |||
public File getJssdkFile(String mediaId) throws WxErrorException { | |||
return this.mainService.execute( | |||
BaseMediaDownloadRequestExecutor.create(this.mainService.getRequestHttp(), | |||
this.mainService.getWxCpConfigStorage().getTmpDirFile()), | |||
JSSDK_MEDIA_GET_URL, "media_id=" + mediaId); | |||
} | |||
@Override | |||
public String uploadImg(File file) throws WxErrorException { | |||
final WxMediaUploadResult result = this.mainService | |||
@@ -1,6 +1,5 @@ | |||
package me.chanjar.weixin.cp.api.impl; | |||
import com.google.gson.GsonBuilder; | |||
import com.google.gson.JsonElement; | |||
import com.google.gson.JsonObject; | |||
import com.google.gson.JsonParser; | |||
@@ -11,6 +10,7 @@ import me.chanjar.weixin.common.util.json.GsonHelper; | |||
import me.chanjar.weixin.cp.api.WxCpOAuth2Service; | |||
import me.chanjar.weixin.cp.api.WxCpService; | |||
import me.chanjar.weixin.cp.bean.WxCpUserDetail; | |||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||
/** | |||
* <pre> | |||
@@ -87,6 +87,6 @@ public class WxCpOAuth2ServiceImpl implements WxCpOAuth2Service { | |||
JsonObject param = new JsonObject(); | |||
param.addProperty("user_ticket", userTicket); | |||
String responseText = this.mainService.post(url, param.toString()); | |||
return new GsonBuilder().create().fromJson(responseText, WxCpUserDetail.class); | |||
return WxCpGsonBuilder.create().fromJson(responseText, WxCpUserDetail.class); | |||
} | |||
} |
@@ -1,9 +1,16 @@ | |||
package me.chanjar.weixin.cp.api.impl; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
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.error.WxError; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
@@ -17,19 +24,24 @@ 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.api.WxCpAgentService; | |||
import me.chanjar.weixin.cp.api.WxCpChatService; | |||
import me.chanjar.weixin.cp.api.WxCpDepartmentService; | |||
import me.chanjar.weixin.cp.api.WxCpMediaService; | |||
import me.chanjar.weixin.cp.api.WxCpMenuService; | |||
import me.chanjar.weixin.cp.api.WxCpOAuth2Service; | |||
import me.chanjar.weixin.cp.api.WxCpService; | |||
import me.chanjar.weixin.cp.api.WxCpTagService; | |||
import me.chanjar.weixin.cp.api.WxCpUserService; | |||
import me.chanjar.weixin.cp.bean.WxCpMessage; | |||
import me.chanjar.weixin.cp.bean.WxCpMessageSendResult; | |||
import me.chanjar.weixin.cp.config.WxCpConfigStorage; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import java.io.File; | |||
import java.io.IOException; | |||
public abstract class WxCpServiceAbstractImpl<H, P> implements WxCpService, RequestHttp<H, P> { | |||
protected final Logger log = LoggerFactory.getLogger(this.getClass()); | |||
private WxCpUserService userService = new WxCpUserServiceImpl(this); | |||
private WxCpChatService chatService = new WxCpChatServiceImpl(this); | |||
private WxCpDepartmentService departmentService = new WxCpDepartmentServiceImpl(this); | |||
private WxCpMediaService mediaService = new WxCpMediaServiceImpl(this); | |||
private WxCpMenuService menuService = new WxCpMenuServiceImpl(this); | |||
@@ -184,7 +196,7 @@ public abstract class WxCpServiceAbstractImpl<H, P> implements WxCpService, Requ | |||
this.log.debug("微信系统繁忙,{} ms 后重试(第{}次)", sleepMillis, retryTimes + 1); | |||
Thread.sleep(sleepMillis); | |||
} catch (InterruptedException e1) { | |||
throw new RuntimeException(e1); | |||
Thread.currentThread().interrupt(); | |||
} | |||
} else { | |||
throw e; | |||
@@ -334,7 +346,12 @@ public abstract class WxCpServiceAbstractImpl<H, P> implements WxCpService, Requ | |||
} | |||
@Override | |||
public RequestHttp getRequestHttp() { | |||
public WxCpChatService getChatService() { | |||
return chatService; | |||
} | |||
@Override | |||
public RequestHttp<?, ?> getRequestHttp() { | |||
return this; | |||
} | |||
@@ -1,6 +1,12 @@ | |||
package me.chanjar.weixin.cp.api.impl; | |||
import com.google.gson.*; | |||
import java.util.List; | |||
import com.google.gson.JsonArray; | |||
import com.google.gson.JsonElement; | |||
import com.google.gson.JsonObject; | |||
import com.google.gson.JsonParser; | |||
import com.google.gson.JsonPrimitive; | |||
import com.google.gson.reflect.TypeToken; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import me.chanjar.weixin.cp.api.WxCpService; | |||
@@ -11,8 +17,6 @@ import me.chanjar.weixin.cp.bean.WxCpTagGetResult; | |||
import me.chanjar.weixin.cp.bean.WxCpUser; | |||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||
import java.util.List; | |||
/** | |||
* <pre> | |||
* 标签管理接口 | |||
@@ -57,7 +61,7 @@ public class WxCpTagServiceImpl implements WxCpTagService { | |||
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() | |||
return WxCpGsonBuilder.create() | |||
.fromJson( | |||
tmpJsonElement.getAsJsonObject().get("taglist"), | |||
new TypeToken<List<WxCpTag>>() { | |||
@@ -70,7 +74,7 @@ public class WxCpTagServiceImpl implements WxCpTagService { | |||
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() | |||
return WxCpGsonBuilder.create() | |||
.fromJson( | |||
tmpJsonElement.getAsJsonObject().get("userlist"), | |||
new TypeToken<List<WxCpUser>>() { | |||
@@ -76,7 +76,7 @@ public class WxCpUserServiceImpl implements WxCpUserService { | |||
} | |||
@Override | |||
public List<WxCpUser> listByDepartment(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException { | |||
public List<WxCpUser> listByDepartment(Long 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) { | |||
@@ -90,7 +90,7 @@ public class WxCpUserServiceImpl implements WxCpUserService { | |||
String responseContent = this.mainService.get(url, params); | |||
JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | |||
return WxCpGsonBuilder.INSTANCE.create() | |||
return WxCpGsonBuilder.create() | |||
.fromJson(tmpJsonElement.getAsJsonObject().get("userlist"), | |||
new TypeToken<List<WxCpUser>>() { | |||
}.getType() | |||
@@ -98,7 +98,7 @@ public class WxCpUserServiceImpl implements WxCpUserService { | |||
} | |||
@Override | |||
public List<WxCpUser> listSimpleByDepartment(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException { | |||
public List<WxCpUser> listSimpleByDepartment(Long 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) { | |||
@@ -112,7 +112,7 @@ public class WxCpUserServiceImpl implements WxCpUserService { | |||
String responseContent = this.mainService.get(url, params); | |||
JsonElement tmpJsonElement = new JsonParser().parse(responseContent); | |||
return WxCpGsonBuilder.INSTANCE.create() | |||
return WxCpGsonBuilder.create() | |||
.fromJson( | |||
tmpJsonElement.getAsJsonObject().get("userlist"), | |||
new TypeToken<List<WxCpUser>>() { | |||
@@ -1,5 +1,8 @@ | |||
package me.chanjar.weixin.cp.bean; | |||
import java.io.Serializable; | |||
import java.util.List; | |||
import com.google.gson.annotations.SerializedName; | |||
import lombok.AllArgsConstructor; | |||
import lombok.Builder; | |||
@@ -7,9 +10,6 @@ import lombok.Data; | |||
import lombok.NoArgsConstructor; | |||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||
import java.io.Serializable; | |||
import java.util.List; | |||
/** | |||
* <pre> | |||
* 企业号应用信息. | |||
@@ -80,12 +80,14 @@ public class WxCpAgent implements Serializable { | |||
@Data | |||
public static class Users implements Serializable { | |||
private static final long serialVersionUID = 8801100463558788565L; | |||
@SerializedName("user") | |||
private List<User> users; | |||
} | |||
@Data | |||
public class User implements Serializable { | |||
private static final long serialVersionUID = 7287632514385508024L; | |||
@SerializedName("userid") | |||
private String userId; | |||
} | |||
@@ -0,0 +1,20 @@ | |||
package me.chanjar.weixin.cp.bean; | |||
import java.util.List; | |||
import lombok.Data; | |||
/** | |||
* 群聊 | |||
* | |||
* @author gaigeshen | |||
*/ | |||
@Data | |||
public class WxCpChat { | |||
private String id; | |||
private String name; | |||
private String owner; | |||
private List<String> users; | |||
} |
@@ -1,10 +1,10 @@ | |||
package me.chanjar.weixin.cp.bean; | |||
import java.io.Serializable; | |||
import lombok.Data; | |||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||
import java.io.Serializable; | |||
/** | |||
* 微信部门. | |||
* | |||
@@ -14,9 +14,9 @@ import java.io.Serializable; | |||
public class WxCpDepart implements Serializable { | |||
private static final long serialVersionUID = -5028321625140879571L; | |||
private Integer id; | |||
private Long id; | |||
private String name; | |||
private Integer parentId; | |||
private Long parentId; | |||
private Long order; | |||
public static WxCpDepart fromJson(String json) { | |||
@@ -5,8 +5,6 @@ import java.util.Collections; | |||
import java.util.List; | |||
import org.apache.commons.lang3.StringUtils; | |||
import org.apache.commons.lang3.builder.ToStringBuilder; | |||
import org.apache.commons.lang3.builder.ToStringStyle; | |||
import com.google.common.base.Splitter; | |||
import com.google.gson.annotations.SerializedName; | |||
@@ -25,11 +23,11 @@ public class WxCpInviteResult implements Serializable { | |||
@Override | |||
public String toString() { | |||
return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); | |||
return WxCpGsonBuilder.create().toJson(this); | |||
} | |||
public static WxCpInviteResult fromJson(String json) { | |||
return WxCpGsonBuilder.INSTANCE.create().fromJson(json, WxCpInviteResult.class); | |||
return WxCpGsonBuilder.create().fromJson(json, WxCpInviteResult.class); | |||
} | |||
@SerializedName("errcode") | |||
@@ -1,16 +1,23 @@ | |||
package me.chanjar.weixin.cp.bean; | |||
import java.io.Serializable; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import lombok.Data; | |||
import me.chanjar.weixin.common.api.WxConsts; | |||
import me.chanjar.weixin.cp.bean.article.MpnewsArticle; | |||
import me.chanjar.weixin.cp.bean.article.NewArticle; | |||
import me.chanjar.weixin.cp.bean.messagebuilder.*; | |||
import me.chanjar.weixin.cp.bean.messagebuilder.FileBuilder; | |||
import me.chanjar.weixin.cp.bean.messagebuilder.ImageBuilder; | |||
import me.chanjar.weixin.cp.bean.messagebuilder.MpnewsBuilder; | |||
import me.chanjar.weixin.cp.bean.messagebuilder.NewsBuilder; | |||
import me.chanjar.weixin.cp.bean.messagebuilder.TextBuilder; | |||
import me.chanjar.weixin.cp.bean.messagebuilder.TextCardBuilder; | |||
import me.chanjar.weixin.cp.bean.messagebuilder.VideoBuilder; | |||
import me.chanjar.weixin.cp.bean.messagebuilder.VoiceBuilder; | |||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||
import java.io.Serializable; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
/** | |||
* 消息. | |||
* | |||
@@ -114,7 +121,7 @@ public class WxCpMessage implements Serializable { | |||
} | |||
public String toJson() { | |||
return WxCpGsonBuilder.INSTANCE.create().toJson(this); | |||
return WxCpGsonBuilder.create().toJson(this); | |||
} | |||
} |
@@ -5,8 +5,6 @@ import java.util.Collections; | |||
import java.util.List; | |||
import org.apache.commons.lang3.StringUtils; | |||
import org.apache.commons.lang3.builder.ToStringBuilder; | |||
import org.apache.commons.lang3.builder.ToStringStyle; | |||
import com.google.common.base.Splitter; | |||
import com.google.gson.annotations.SerializedName; | |||
@@ -25,11 +23,11 @@ public class WxCpMessageSendResult implements Serializable { | |||
@Override | |||
public String toString() { | |||
return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); | |||
return WxCpGsonBuilder.create().toJson(this); | |||
} | |||
public static WxCpMessageSendResult fromJson(String json) { | |||
return WxCpGsonBuilder.INSTANCE.create().fromJson(json, WxCpMessageSendResult.class); | |||
return WxCpGsonBuilder.create().fromJson(json, WxCpMessageSendResult.class); | |||
} | |||
@SerializedName("errcode") | |||
@@ -1,14 +1,15 @@ | |||
package me.chanjar.weixin.cp.bean; | |||
import java.io.Serializable; | |||
import lombok.AllArgsConstructor; | |||
import lombok.Data; | |||
import lombok.NoArgsConstructor; | |||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||
import java.io.Serializable; | |||
/** | |||
* Created by Daniel Qian. | |||
* @author Daniel Qian | |||
*/ | |||
@Data | |||
@AllArgsConstructor | |||
@@ -5,8 +5,6 @@ import java.util.Collections; | |||
import java.util.List; | |||
import org.apache.commons.lang3.StringUtils; | |||
import org.apache.commons.lang3.builder.ToStringBuilder; | |||
import org.apache.commons.lang3.builder.ToStringStyle; | |||
import com.google.common.base.Splitter; | |||
import com.google.gson.annotations.SerializedName; | |||
@@ -25,11 +23,11 @@ public class WxCpTagAddOrRemoveUsersResult implements Serializable { | |||
@Override | |||
public String toString() { | |||
return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); | |||
return WxCpGsonBuilder.create().toJson(this); | |||
} | |||
public static WxCpTagAddOrRemoveUsersResult fromJson(String json) { | |||
return WxCpGsonBuilder.INSTANCE.create().fromJson(json, WxCpTagAddOrRemoveUsersResult.class); | |||
return WxCpGsonBuilder.create().fromJson(json, WxCpTagAddOrRemoveUsersResult.class); | |||
} | |||
@SerializedName("errcode") | |||
@@ -50,11 +50,11 @@ public class WxCpUser implements Serializable { | |||
} | |||
public static WxCpUser fromJson(String json) { | |||
return WxCpGsonBuilder.INSTANCE.create().fromJson(json, WxCpUser.class); | |||
return WxCpGsonBuilder.create().fromJson(json, WxCpUser.class); | |||
} | |||
public String toJson() { | |||
return WxCpGsonBuilder.INSTANCE.create().toJson(this); | |||
return WxCpGsonBuilder.create().toJson(this); | |||
} | |||
@Data | |||
@@ -3,12 +3,11 @@ package me.chanjar.weixin.cp.bean; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.Serializable; | |||
import java.nio.charset.StandardCharsets; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import org.apache.commons.io.IOUtils; | |||
import org.apache.commons.lang3.builder.ToStringBuilder; | |||
import org.apache.commons.lang3.builder.ToStringStyle; | |||
import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
import com.thoughtworks.xstream.annotations.XStreamConverter; | |||
@@ -18,6 +17,7 @@ import me.chanjar.weixin.common.api.WxConsts; | |||
import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; | |||
import me.chanjar.weixin.cp.config.WxCpConfigStorage; | |||
import me.chanjar.weixin.cp.util.crypto.WxCpCryptUtil; | |||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||
import me.chanjar.weixin.cp.util.xml.XStreamTransformer; | |||
/** | |||
@@ -370,7 +370,7 @@ public class WxCpXmlMessage implements Serializable { | |||
public static WxCpXmlMessage fromEncryptedXml(InputStream is, WxCpConfigStorage wxCpConfigStorage, | |||
String timestamp, String nonce, String msgSignature) { | |||
try { | |||
return fromEncryptedXml(IOUtils.toString(is, "UTF-8"), wxCpConfigStorage, timestamp, nonce, msgSignature); | |||
return fromEncryptedXml(IOUtils.toString(is, StandardCharsets.UTF_8), wxCpConfigStorage, timestamp, nonce, msgSignature); | |||
} catch (IOException e) { | |||
throw new RuntimeException(e); | |||
} | |||
@@ -378,7 +378,7 @@ public class WxCpXmlMessage implements Serializable { | |||
@Override | |||
public String toString() { | |||
return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); | |||
return WxCpGsonBuilder.create().toJson(this); | |||
} | |||
@Data | |||
@@ -2,11 +2,9 @@ package me.chanjar.weixin.cp.config; | |||
import java.io.File; | |||
import org.apache.commons.lang3.builder.ToStringBuilder; | |||
import org.apache.commons.lang3.builder.ToStringStyle; | |||
import me.chanjar.weixin.common.bean.WxAccessToken; | |||
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; | |||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||
/** | |||
* 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化 | |||
@@ -203,7 +201,7 @@ public class WxCpInMemoryConfigStorage implements WxCpConfigStorage { | |||
@Override | |||
public String toString() { | |||
return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); | |||
return WxCpGsonBuilder.create().toJson(this); | |||
} | |||
@Override | |||
@@ -1,5 +1,18 @@ | |||
package me.chanjar.weixin.cp.message; | |||
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; | |||
import org.apache.commons.lang3.StringUtils; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import me.chanjar.weixin.common.api.WxErrorExceptionHandler; | |||
import me.chanjar.weixin.common.api.WxMessageDuplicateChecker; | |||
import me.chanjar.weixin.common.api.WxMessageInMemoryDuplicateChecker; | |||
@@ -11,18 +24,6 @@ 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.apache.commons.lang3.StringUtils; | |||
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; | |||
/** | |||
* <pre> | |||
@@ -195,6 +196,7 @@ public class WxCpMessageRouter { | |||
sessionEndAccess(wxMessage); | |||
} catch (InterruptedException e) { | |||
WxCpMessageRouter.this.log.error("Error happened when wait task finish", e); | |||
Thread.currentThread().interrupt(); | |||
} catch (ExecutionException e) { | |||
WxCpMessageRouter.this.log.error("Error happened when wait task finish", e); | |||
} | |||
@@ -207,12 +209,11 @@ public class WxCpMessageRouter { | |||
/** | |||
* 处理微信消息 | |||
* 处理微信消息. | |||
* | |||
* @param wxMessage | |||
*/ | |||
public WxCpXmlOutMessage route(final WxCpXmlMessage wxMessage) { | |||
return this.route(wxMessage, new HashMap<String, Object>()); | |||
return this.route(wxMessage, new HashMap<String, Object>(2)); | |||
} | |||
protected boolean isDuplicateMessage(WxCpXmlMessage wxMessage) { | |||
@@ -0,0 +1,77 @@ | |||
/* | |||
* 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 me.chanjar.weixin.cp.util.json; | |||
import java.lang.reflect.Type; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import com.google.gson.JsonArray; | |||
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 com.google.gson.JsonSerializationContext; | |||
import com.google.gson.JsonSerializer; | |||
import me.chanjar.weixin.common.util.json.GsonHelper; | |||
import me.chanjar.weixin.cp.bean.WxCpChat; | |||
/** | |||
* 群聊适配器 | |||
* | |||
* @author gaigeshen | |||
*/ | |||
public class WxCpChatGsonAdapter implements JsonSerializer<WxCpChat>, JsonDeserializer<WxCpChat> { | |||
@Override | |||
public JsonElement serialize(WxCpChat chat, Type typeOfSrc, JsonSerializationContext context) { | |||
JsonObject json = new JsonObject(); | |||
if (chat.getId() != null) { | |||
json.addProperty("chatid", chat.getId()); | |||
} | |||
if (chat.getName() != null) { | |||
json.addProperty("name", chat.getName()); | |||
} | |||
if (chat.getOwner() != null) { | |||
json.addProperty("owner", chat.getOwner()); | |||
} | |||
if (chat.getUsers() != null) { | |||
JsonArray users = new JsonArray(); | |||
for (String user : chat.getUsers()) { | |||
users.add(user); | |||
} | |||
json.add("userlist", users); | |||
} | |||
return json; | |||
} | |||
@Override | |||
public WxCpChat deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { | |||
JsonObject chatJson = json.getAsJsonObject(); | |||
WxCpChat chat = new WxCpChat(); | |||
chat.setId(GsonHelper.getAsString(chatJson.get("chatid"))); | |||
chat.setName(GsonHelper.getAsString(chatJson.get("name"))); | |||
chat.setOwner(GsonHelper.getAsString(chatJson.get("owner"))); | |||
JsonArray usersJson = chatJson.getAsJsonArray("userlist"); | |||
if (usersJson != null) { | |||
List<String> users = new ArrayList<>(usersJson.size()); | |||
chat.setUsers(users); | |||
for (JsonElement userJson : usersJson) { | |||
users.add(userJson.getAsString()); | |||
} | |||
} | |||
return chat; | |||
} | |||
} |
@@ -8,31 +8,43 @@ | |||
*/ | |||
package me.chanjar.weixin.cp.util.json; | |||
import com.google.gson.*; | |||
import java.lang.reflect.Type; | |||
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 com.google.gson.JsonSerializationContext; | |||
import com.google.gson.JsonSerializer; | |||
import me.chanjar.weixin.common.util.json.GsonHelper; | |||
import me.chanjar.weixin.cp.bean.WxCpDepart; | |||
import java.lang.reflect.Type; | |||
/** | |||
* WxCpDepart的gson适配器. | |||
* | |||
* @author Daniel Qian | |||
*/ | |||
public class WxCpDepartGsonAdapter implements JsonSerializer<WxCpDepart>, JsonDeserializer<WxCpDepart> { | |||
private static final String ID = "id"; | |||
private static final String NAME = "name"; | |||
private static final String PARENT_ID = "parentid"; | |||
private static final String ORDER = "order"; | |||
@Override | |||
public JsonElement serialize(WxCpDepart group, Type typeOfSrc, JsonSerializationContext context) { | |||
JsonObject json = new JsonObject(); | |||
if (group.getId() != null) { | |||
json.addProperty("id", group.getId()); | |||
json.addProperty(ID, group.getId()); | |||
} | |||
if (group.getName() != null) { | |||
json.addProperty("name", group.getName()); | |||
json.addProperty(NAME, group.getName()); | |||
} | |||
if (group.getParentId() != null) { | |||
json.addProperty("parentid", group.getParentId()); | |||
json.addProperty(PARENT_ID, group.getParentId()); | |||
} | |||
if (group.getOrder() != null) { | |||
json.addProperty("order", String.valueOf(group.getOrder())); | |||
json.addProperty(ORDER, String.valueOf(group.getOrder())); | |||
} | |||
return json; | |||
} | |||
@@ -42,17 +54,17 @@ public class WxCpDepartGsonAdapter implements JsonSerializer<WxCpDepart>, JsonDe | |||
throws JsonParseException { | |||
WxCpDepart depart = new WxCpDepart(); | |||
JsonObject departJson = json.getAsJsonObject(); | |||
if (departJson.get("id") != null && !departJson.get("id").isJsonNull()) { | |||
depart.setId(GsonHelper.getAsInteger(departJson.get("id"))); | |||
if (departJson.get(ID) != null && !departJson.get(ID).isJsonNull()) { | |||
depart.setId(GsonHelper.getAsLong(departJson.get(ID))); | |||
} | |||
if (departJson.get("name") != null && !departJson.get("name").isJsonNull()) { | |||
depart.setName(GsonHelper.getAsString(departJson.get("name"))); | |||
if (departJson.get(NAME) != null && !departJson.get(NAME).isJsonNull()) { | |||
depart.setName(GsonHelper.getAsString(departJson.get(NAME))); | |||
} | |||
if (departJson.get("order") != null && !departJson.get("order").isJsonNull()) { | |||
depart.setOrder(GsonHelper.getAsLong(departJson.get("order"))); | |||
if (departJson.get(ORDER) != null && !departJson.get(ORDER).isJsonNull()) { | |||
depart.setOrder(GsonHelper.getAsLong(departJson.get(ORDER))); | |||
} | |||
if (departJson.get("parentid") != null && !departJson.get("parentid").isJsonNull()) { | |||
depart.setParentId(GsonHelper.getAsInteger(departJson.get("parentid"))); | |||
if (departJson.get(PARENT_ID) != null && !departJson.get(PARENT_ID).isJsonNull()) { | |||
depart.setParentId(GsonHelper.getAsLong(departJson.get(PARENT_ID))); | |||
} | |||
return depart; | |||
} | |||
@@ -5,6 +5,7 @@ import com.google.gson.GsonBuilder; | |||
import me.chanjar.weixin.common.bean.menu.WxMenu; | |||
import me.chanjar.weixin.common.error.WxError; | |||
import me.chanjar.weixin.common.util.json.WxErrorAdapter; | |||
import me.chanjar.weixin.cp.bean.WxCpChat; | |||
import me.chanjar.weixin.cp.bean.WxCpDepart; | |||
import me.chanjar.weixin.cp.bean.WxCpMessage; | |||
import me.chanjar.weixin.cp.bean.WxCpTag; | |||
@@ -15,11 +16,12 @@ import me.chanjar.weixin.cp.bean.WxCpUser; | |||
*/ | |||
public class WxCpGsonBuilder { | |||
public static final GsonBuilder INSTANCE = new GsonBuilder(); | |||
private static final GsonBuilder INSTANCE = new GsonBuilder(); | |||
static { | |||
INSTANCE.disableHtmlEscaping(); | |||
INSTANCE.registerTypeAdapter(WxCpMessage.class, new WxCpMessageGsonAdapter()); | |||
INSTANCE.registerTypeAdapter(WxCpChat.class, new WxCpChatGsonAdapter()); | |||
INSTANCE.registerTypeAdapter(WxCpDepart.class, new WxCpDepartGsonAdapter()); | |||
INSTANCE.registerTypeAdapter(WxCpUser.class, new WxCpUserGsonAdapter()); | |||
INSTANCE.registerTypeAdapter(WxError.class, new WxErrorAdapter()); | |||
@@ -0,0 +1,46 @@ | |||
package me.chanjar.weixin.cp.api.impl; | |||
import java.util.Arrays; | |||
import me.chanjar.weixin.cp.bean.WxCpChat; | |||
import org.testng.Assert; | |||
import org.testng.annotations.Guice; | |||
import org.testng.annotations.Test; | |||
import com.google.inject.Inject; | |||
import me.chanjar.weixin.cp.api.ApiTestModule; | |||
import me.chanjar.weixin.cp.api.WxCpService; | |||
/** | |||
* 测试群聊服务 | |||
* | |||
* @author gaigeshen | |||
*/ | |||
@Guice(modules = ApiTestModule.class) | |||
public class WxCpChatServiceImplTest { | |||
@Inject | |||
private WxCpService wxCpService; | |||
@Test | |||
public void create() throws Exception { | |||
wxCpService.getChatService().chatCreate("测试群聊", "gaige_shen", Arrays.asList("gaige_shen", "ZhangXiaoMing"), "mychatid"); | |||
} | |||
@Test | |||
public void get() throws Exception { | |||
WxCpChat chat = wxCpService.getChatService().chatGet("mychatid"); | |||
System.out.println(chat); | |||
Assert.assertEquals(chat.getName(), "测试群聊"); | |||
} | |||
@Test | |||
public void update() throws Exception { | |||
wxCpService.getChatService().chatUpdate("mychatid", "", "", Arrays.asList("ZhengWuYao"), null); | |||
WxCpChat chat = wxCpService.getChatService().chatGet("mychatid"); | |||
System.out.println(chat); | |||
Assert.assertEquals(chat.getUsers().size(), 3); | |||
} | |||
} |
@@ -1,15 +1,15 @@ | |||
package me.chanjar.weixin.cp.api.impl; | |||
import java.util.List; | |||
import org.testng.annotations.*; | |||
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.WxCpDepart; | |||
import org.testng.annotations.*; | |||
import java.util.List; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.testng.Assert.*; | |||
/** | |||
* <pre> | |||
@@ -29,7 +29,7 @@ public class WxCpDepartmentServiceImplTest { | |||
public void testCreate() throws Exception { | |||
WxCpDepart cpDepart = new WxCpDepart(); | |||
cpDepart.setName("子部门" + System.currentTimeMillis()); | |||
cpDepart.setParentId(1); | |||
cpDepart.setParentId(1L); | |||
cpDepart.setOrder(1L); | |||
Integer departId = this.wxCpService.getDepartmentService().create(cpDepart); | |||
System.out.println(departId); | |||
@@ -45,7 +45,7 @@ public class WxCpDepartmentServiceImplTest { | |||
} | |||
@Test(dataProvider = "departIds") | |||
public void testList(Integer id) throws Exception { | |||
public void testList(Long id) throws Exception { | |||
System.out.println("=================获取部门"); | |||
List<WxCpDepart> departList = this.wxCpService.getDepartmentService().list(id); | |||
assertThat(departList).isNotEmpty(); | |||
@@ -36,7 +36,8 @@ public class WxCpMediaServiceImplTest { | |||
public Object[][] mediaData() { | |||
return new Object[][]{ | |||
new Object[]{WxConsts.MediaFileType.IMAGE, TestConstants.FILE_JPG, "mm.jpeg"}, | |||
new Object[]{WxConsts.MediaFileType.VOICE, TestConstants.FILE_MP3, "mm.mp3"},//{"errcode":301017,"errmsg":"voice file only support amr like myvoice.amr"} | |||
//new Object[]{WxConsts.MediaFileType.VOICE, TestConstants.FILE_MP3, "mm.mp3"}, | |||
// {"errcode":301017,"errmsg":"voice file only support amr like myvoice.amr"} | |||
new Object[]{WxConsts.MediaFileType.VOICE, TestConstants.FILE_AMR, "mm.amr"}, | |||
new Object[]{WxConsts.MediaFileType.VIDEO, TestConstants.FILE_MP4, "mm.mp4"}, | |||
new Object[]{WxConsts.MediaFileType.FILE, TestConstants.FILE_JPG, "mm.jpeg"} | |||
@@ -47,8 +48,9 @@ public class WxCpMediaServiceImplTest { | |||
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()); | |||
assertThat(res).isNotNull(); | |||
assertThat(res.getType()).isNotEmpty(); | |||
assertThat(res.getCreatedAt()).isGreaterThan(0); | |||
assertTrue(res.getMediaId() != null || res.getThumbMediaId() != null); | |||
if (res.getMediaId() != null) { | |||
@@ -70,9 +72,9 @@ public class WxCpMediaServiceImplTest { | |||
} | |||
@Test(dependsOnMethods = {"testUploadMedia"}, dataProvider = "downloadMedia") | |||
public void testDownloadMedia(String media_id) throws WxErrorException { | |||
File file = this.wxService.getMediaService().download(media_id); | |||
assertNotNull(file); | |||
public void testDownload(String mediaId) throws WxErrorException { | |||
File file = this.wxService.getMediaService().download(mediaId); | |||
assertThat(file).isNotNull(); | |||
System.out.println(file); | |||
} | |||
@@ -82,4 +84,11 @@ public class WxCpMediaServiceImplTest { | |||
String res = this.wxService.getMediaService().uploadImg(new File(url.getFile())); | |||
assertThat(res).isNotEmpty(); | |||
} | |||
@Test | |||
public void testGetJssdkFile() throws WxErrorException { | |||
File file = this.wxService.getMediaService().getJssdkFile("...."); | |||
assertThat(file).isNotNull(); | |||
System.out.println(file); | |||
} | |||
} |
@@ -74,7 +74,7 @@ public class WxCpUserServiceImplTest { | |||
@Test | |||
public void testListByDepartment() throws Exception { | |||
List<WxCpUser> users = this.wxCpService.getUserService().listByDepartment(1, true, 0); | |||
List<WxCpUser> users = this.wxCpService.getUserService().listByDepartment(1L, true, 0); | |||
assertNotEquals(users.size(), 0); | |||
for (WxCpUser user : users) { | |||
System.out.println(ToStringBuilder.reflectionToString(user, ToStringStyle.MULTI_LINE_STYLE)); | |||
@@ -83,7 +83,7 @@ public class WxCpUserServiceImplTest { | |||
@Test | |||
public void testListSimpleByDepartment() throws Exception { | |||
List<WxCpUser> users = this.wxCpService.getUserService().listSimpleByDepartment(1, true, 0); | |||
List<WxCpUser> users = this.wxCpService.getUserService().listSimpleByDepartment(1L, true, 0); | |||
assertNotEquals(users.size(), 0); | |||
for (WxCpUser user : users) { | |||
System.out.println(ToStringBuilder.reflectionToString(user, ToStringStyle.MULTI_LINE_STYLE)); | |||
@@ -6,11 +6,11 @@ | |||
<modelVersion>4.0.0</modelVersion> | |||
<parent> | |||
<groupId>com.github.binarywang</groupId> | |||
<artifactId>weixin-java-parent</artifactId> | |||
<version>3.2.0</version> | |||
<artifactId>wx-java</artifactId> | |||
<version>3.3.0</version> | |||
</parent> | |||
<artifactId>weixin-java-miniapp</artifactId> | |||
<name>Weixin Java Tools - MiniApp</name> | |||
<name>WxJava - MiniApp</name> | |||
<description>微信小程序Java SDK</description> | |||
<dependencies> | |||
@@ -15,7 +15,26 @@ public interface WxMaJsapiService { | |||
/** | |||
* 获得jsapi_ticket的url | |||
*/ | |||
String GET_JSAPI_TICKET_URL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi"; | |||
String GET_JSAPI_TICKET_URL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket"; | |||
/** | |||
* 获得卡券api_ticket,不强制刷新api_ticket | |||
* | |||
* @see #getJsapiTicket(boolean) | |||
*/ | |||
String getCardApiTicket() throws WxErrorException; | |||
/** | |||
* <pre> | |||
* 获得卡券api_ticket | |||
* 获得时会检查apiToken是否过期,如果过期了,那么就刷新一下,否则就什么都不干 | |||
* | |||
* 详情请见:http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115&token=&lang=zh_CN | |||
* </pre> | |||
* | |||
* @param forceRefresh 强制刷新 | |||
*/ | |||
String getCardApiTicket(boolean forceRefresh) throws WxErrorException; | |||
/** | |||
* 获得jsapi_ticket,不强制刷新jsapi_ticket | |||
@@ -1,10 +1,10 @@ | |||
package cn.binarywang.wx.miniapp.api; | |||
import java.io.File; | |||
import cn.binarywang.wx.miniapp.bean.WxMaCodeLineColor; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import java.io.File; | |||
/** | |||
* <pre> | |||
* 二维码相关操作接口. | |||
@@ -32,24 +32,87 @@ public interface WxMaQrcodeService { | |||
* | |||
* @param path 不能为空,最大长度 128 字节 | |||
* @param width 默认430 二维码的宽度 | |||
* @return 文件内容字节数组 | |||
* @throws WxErrorException 异常 | |||
*/ | |||
byte[] createQrcodeBytes(String path, int width) throws WxErrorException; | |||
/** | |||
* 接口C: 获取小程序页面二维码. | |||
* <pre> | |||
* 适用于需要的码数量较少的业务场景 | |||
* 通过该接口,仅能生成已发布的小程序的二维码。 | |||
* 可以在开发者工具预览时生成开发版的带参二维码。 | |||
* 带参二维码只有 100000 个,请谨慎调用。 | |||
* </pre> | |||
* | |||
* @param path 不能为空,最大长度 128 字节 | |||
* @param width 默认430 二维码的宽度 | |||
* @return 文件对象 | |||
* @throws WxErrorException 异常 | |||
*/ | |||
File createQrcode(String path, int width) throws WxErrorException; | |||
/** | |||
* 接口C: 获取小程序页面二维码. | |||
* <pre> | |||
* 适用于需要的码数量较少的业务场景 | |||
* 通过该接口,仅能生成已发布的小程序的二维码。 | |||
* 可以在开发者工具预览时生成开发版的带参二维码。 | |||
* 带参二维码只有 100000 个,请谨慎调用。 | |||
* </pre> | |||
* | |||
* @param path 不能为空,最大长度 128 字节 | |||
* @return 文件对象 | |||
* @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"} | |||
* @param is_hyaline 是否需要透明底色, is_hyaline 为true时,生成透明底色的小程序码 | |||
* @param path 不能为空,最大长度 128 字节 | |||
* @param width 默认430 二维码的宽度 | |||
* @param autoColor 默认true 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调 | |||
* @param lineColor auth_color 为 false 时生效,使用 rgb 设置颜色 例如 {"r":"xxx","g":"xxx","b":"xxx"} | |||
* @param isHyaline 是否需要透明底色, isHyaline 为true时,生成透明底色的小程序码 | |||
* @return 文件内容字节数组 | |||
* @throws WxErrorException 异常 | |||
*/ | |||
byte[] createWxaCodeBytes(String path, int width, boolean autoColor, WxMaCodeLineColor lineColor, boolean isHyaline) | |||
throws WxErrorException; | |||
/** | |||
* 接口A: 获取小程序码. | |||
* | |||
* @param path 不能为空,最大长度 128 字节 | |||
* @param width 默认430 二维码的宽度 | |||
* @param autoColor 默认true 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调 | |||
* @param lineColor auth_color 为 false 时生效,使用 rgb 设置颜色 例如 {"r":"xxx","g":"xxx","b":"xxx"} | |||
* @param isHyaline 是否需要透明底色, isHyaline 为true时,生成透明底色的小程序码 | |||
* @return 文件对象 | |||
* @throws WxErrorException 异常 | |||
*/ | |||
File createWxaCode(String path, int width, boolean autoColor, WxMaCodeLineColor lineColor, boolean is_hyaline) throws WxErrorException; | |||
File createWxaCode(String path, int width, boolean autoColor, WxMaCodeLineColor lineColor, boolean isHyaline) | |||
throws WxErrorException; | |||
/** | |||
* 接口A: 获取小程序码. | |||
* | |||
* @param path 不能为空,最大长度 128 字节 | |||
* @param width 默认430 二维码的宽度 | |||
* @return 文件对象 | |||
* @throws WxErrorException 异常 | |||
*/ | |||
File createWxaCode(String path, int width) throws WxErrorException; | |||
/** | |||
* 接口A: 获取小程序码. | |||
* | |||
* @param path 不能为空,最大长度 128 字节 | |||
* @return 文件对象 | |||
* @throws WxErrorException 异常 | |||
*/ | |||
File createWxaCode(String path) throws WxErrorException; | |||
/** | |||
@@ -61,15 +124,56 @@ public interface WxMaQrcodeService { | |||
* 调试阶段可以使用开发工具的条件编译自定义参数 scene=xxxx 进行模拟,开发工具模拟时的 scene 的参数值需要进行 urlencode | |||
* </pre> | |||
* | |||
* @param scene 最大32个可见字符,只支持数字,大小写英文以及部分特殊字符:!#$&'()*+,/:;=?@-._~,其它字符请自行编码为合法字符(因不支持%,中文无法使用 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"} | |||
* @param isHyaline 是否需要透明底色, is_hyaline 为true时,生成透明底色的小程序码 | |||
* @return 文件内容字节数组 | |||
* @throws WxErrorException 异常 | |||
*/ | |||
byte[] createWxaCodeUnlimitBytes(String scene, String page, int width, boolean autoColor, | |||
WxMaCodeLineColor lineColor, boolean isHyaline) throws WxErrorException; | |||
/** | |||
* 接口B: 获取小程序码(永久有效、数量暂无限制). | |||
* <pre> | |||
* 通过该接口生成的小程序码,永久有效,数量暂无限制。 | |||
* 用户扫描该码进入小程序后,将统一打开首页,开发者需在对应页面根据获取的码中 scene 字段的值,再做处理逻辑。 | |||
* 使用如下代码可以获取到二维码中的 scene 字段的值。 | |||
* 调试阶段可以使用开发工具的条件编译自定义参数 scene=xxxx 进行模拟,开发工具模拟时的 scene 的参数值需要进行 urlencode | |||
* </pre> | |||
* | |||
* @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"} | |||
* @param isHyaline 是否需要透明底色, is_hyaline 为true时,生成透明底色的小程序码 | |||
* @return 文件对象 | |||
* @throws WxErrorException 异常 | |||
*/ | |||
File createWxaCodeUnlimit(String scene, String page, int width, boolean autoColor, WxMaCodeLineColor lineColor, boolean isHyaline) throws WxErrorException; | |||
File createWxaCodeUnlimit(String scene, String page, int width, boolean autoColor, | |||
WxMaCodeLineColor lineColor, boolean isHyaline) throws WxErrorException; | |||
/** | |||
* 接口B: 获取小程序码(永久有效、数量暂无限制). | |||
* <pre> | |||
* 通过该接口生成的小程序码,永久有效,数量暂无限制。 | |||
* 用户扫描该码进入小程序后,将统一打开首页,开发者需在对应页面根据获取的码中 scene 字段的值,再做处理逻辑。 | |||
* 使用如下代码可以获取到二维码中的 scene 字段的值。 | |||
* 调试阶段可以使用开发工具的条件编译自定义参数 scene=xxxx 进行模拟,开发工具模拟时的 scene 的参数值需要进行 urlencode | |||
* </pre> | |||
* | |||
* @param scene 最大32个可见字符,只支持数字,大小写英文以及部分特殊字符:!#$&'()*+,/:;=?@-._~, | |||
* 其它字符请自行编码为合法字符(因不支持%,中文无法使用 urlencode 处理,请使用其他编码方式) | |||
* @param page 必须是已经发布的小程序页面,例如 "pages/index/index" ,如果不填写这个字段,默认跳主页面 | |||
* @return 文件对象 | |||
* @throws WxErrorException 异常 | |||
*/ | |||
File createWxaCodeUnlimit(String scene, String page) throws WxErrorException; | |||
} |
@@ -0,0 +1,24 @@ | |||
package cn.binarywang.wx.miniapp.api; | |||
import java.util.List; | |||
import cn.binarywang.wx.miniapp.bean.WxMaRunStepInfo; | |||
/** | |||
* 微信运动相关操作接口. | |||
* | |||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
*/ | |||
public interface WxMaRunService { | |||
/** | |||
* 解密分享敏感数据. | |||
* 文档地址:https://developers.weixin.qq.com/miniprogram/dev/api/open-api/werun/wx.getWeRunData.html | |||
* | |||
* @param sessionKey 会话密钥 | |||
* @param encryptedData 消息密文 | |||
* @param ivStr 加密算法的初始向量 | |||
*/ | |||
List<WxMaRunStepInfo> getRunStepInfo(String sessionKey, String encryptedData, String ivStr); | |||
} |
@@ -0,0 +1,43 @@ | |||
package cn.binarywang.wx.miniapp.api; | |||
import java.io.File; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
/** | |||
* <pre> | |||
* 内容安全相关接口. | |||
* Created by Binary Wang on 2018/11/24. | |||
* </pre> | |||
* | |||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
*/ | |||
public interface WxMaSecCheckService { | |||
String IMG_SEC_CHECK_URL = "https://api.weixin.qq.com/wxa/img_sec_check"; | |||
String MSG_SEC_CHECK_URL = "https://api.weixin.qq.com/wxa/msg_sec_check"; | |||
/** | |||
* <pre> | |||
* 校验一张图片是否含有违法违规内容. | |||
* 应用场景举例: | |||
* 1)图片智能鉴黄:涉及拍照的工具类应用(如美拍,识图类应用)用户拍照上传检测;电商类商品上架图片检测;媒体类用户文章里的图片检测等; | |||
* 2)敏感人脸识别:用户头像;媒体类用户文章里的图片检测;社交类用户上传的图片检测等。频率限制:单个 appId 调用上限为 1000 次/分钟,100,000 次/天 | |||
* 详情请见: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/sec-check/imgSecCheck.html | |||
* </pre> | |||
*/ | |||
boolean checkImage(File file) throws WxErrorException; | |||
/** | |||
* <pre> | |||
* 检查一段文本是否含有违法违规内容。 | |||
* 应用场景举例: | |||
* 用户个人资料违规文字检测; | |||
* 媒体新闻类用户发表文章,评论内容检测; | |||
* 游戏类用户编辑上传的素材(如答题类小游戏用户上传的问题及答案)检测等。 频率限制:单个 appId 调用上限为 4000 次/分钟,2,000,000 次/天* | |||
* 详情请见: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/sec-check/msgSecCheck.html | |||
* </pre> | |||
*/ | |||
boolean checkMessage(String msgString); | |||
} |
@@ -1,7 +1,5 @@ | |||
package cn.binarywang.wx.miniapp.api; | |||
import java.io.File; | |||
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; | |||
import cn.binarywang.wx.miniapp.config.WxMaConfig; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
@@ -20,17 +18,6 @@ public interface WxMaService { | |||
String JSCODE_TO_SESSION_URL = "https://api.weixin.qq.com/sns/jscode2session"; | |||
String IMG_SEC_CHECK_URL = "https://api.weixin.qq.com/wxa/img_sec_check"; | |||
/** | |||
* <pre> | |||
* 校验一张图片是否含有违法违规内容. | |||
* 应用场景举例:1)图片智能鉴黄:涉及拍照的工具类应用(如美拍,识图类应用)用户拍照上传检测;电商类商品上架图片检测;媒体类用户文章里的图片检测等;2)敏感人脸识别:用户头像;媒体类用户文章里的图片检测;社交类用户上传的图片检测等。频率限制:单个 appId 调用上限为 1000 次/分钟,100,000 次/天 | |||
* 详情请见: https://developers.weixin.qq.com/miniprogram/dev/api/imgSecCheck.html | |||
* </pre> | |||
*/ | |||
boolean imgSecCheck(File file) throws WxErrorException; | |||
/** | |||
* 获取登录后的session信息. | |||
* | |||
@@ -152,33 +139,51 @@ public interface WxMaService { | |||
WxMaTemplateService getTemplateService(); | |||
/** | |||
* 数据分析相关查询服务 | |||
* 数据分析相关查询服务. | |||
* | |||
* @return WxMaAnalysisService | |||
*/ | |||
WxMaAnalysisService getAnalysisService(); | |||
/** | |||
* 返回代码操作相关的 API | |||
* 返回代码操作相关的 API. | |||
* | |||
* @return WxMaCodeService | |||
*/ | |||
WxMaCodeService getCodeService(); | |||
/** | |||
* 返回jsapi操作相关的 API服务类对象 | |||
* 返回jsapi操作相关的 API服务类对象. | |||
* | |||
* @return WxMaJsapiService | |||
*/ | |||
WxMaJsapiService getJsapiService(); | |||
/** | |||
* 小程序修改服务器地址、成员管理 API | |||
* 小程序修改服务器地址、成员管理 API. | |||
* | |||
* @return WxMaSettingService | |||
*/ | |||
WxMaSettingService getSettingService(); | |||
/** | |||
* 返回分享相关查询服务. | |||
* @return WxMaShareService | |||
*/ | |||
WxMaShareService getShareService(); | |||
/** | |||
* 返回微信运动相关接口服务对象. | |||
* @return WxMaShareService | |||
*/ | |||
WxMaRunService getRunService(); | |||
/** | |||
* 返回内容安全相关接口服务对象. | |||
* @return WxMaShareService | |||
*/ | |||
WxMaSecCheckService getSecCheckService(); | |||
/** | |||
* 初始化http请求对象. | |||
*/ | |||
@@ -0,0 +1,21 @@ | |||
package cn.binarywang.wx.miniapp.api; | |||
import cn.binarywang.wx.miniapp.bean.WxMaShareInfo; | |||
/** | |||
* 分享信息相关操作接口. | |||
* | |||
* @author zhfish | |||
*/ | |||
public interface WxMaShareService { | |||
/** | |||
* 解密分享敏感数据. | |||
* | |||
* @param sessionKey 会话密钥 | |||
* @param encryptedData 消息密文 | |||
* @param ivStr 加密算法的初始向量 | |||
*/ | |||
WxMaShareInfo getShareInfo(String sessionKey, String encryptedData, String ivStr); | |||
} |
@@ -28,6 +28,32 @@ public class WxMaJsapiServiceImpl implements WxMaJsapiService { | |||
this.wxMaService = wxMaService; | |||
} | |||
public String getCardApiTicket() throws WxErrorException { | |||
return getCardApiTicket(false); | |||
} | |||
public String getCardApiTicket(boolean forceRefresh) throws WxErrorException { | |||
Lock lock = this.wxMaService.getWxMaConfig().getCardApiTicketLock(); | |||
try { | |||
lock.lock(); | |||
if (forceRefresh) { | |||
this.wxMaService.getWxMaConfig().expireCardApiTicket(); | |||
} | |||
if (this.wxMaService.getWxMaConfig().isCardApiTicketExpired()) { | |||
String responseContent = this.wxMaService.get(GET_JSAPI_TICKET_URL + "?type=wx_card", null); | |||
JsonElement tmpJsonElement = JSON_PARSER.parse(responseContent); | |||
JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject(); | |||
String jsapiTicket = tmpJsonObject.get("ticket").getAsString(); | |||
int expiresInSeconds = tmpJsonObject.get("expires_in").getAsInt(); | |||
this.wxMaService.getWxMaConfig().updateCardApiTicket(jsapiTicket, expiresInSeconds); | |||
} | |||
} finally { | |||
lock.unlock(); | |||
} | |||
return this.wxMaService.getWxMaConfig().getJsapiTicket(); | |||
} | |||
@Override | |||
public String getJsapiTicket() throws WxErrorException { | |||
return getJsapiTicket(false); | |||
@@ -43,7 +69,7 @@ public class WxMaJsapiServiceImpl implements WxMaJsapiService { | |||
} | |||
if (this.wxMaService.getWxMaConfig().isJsapiTicketExpired()) { | |||
String responseContent = this.wxMaService.get(GET_JSAPI_TICKET_URL, null); | |||
String responseContent = this.wxMaService.get(GET_JSAPI_TICKET_URL + "?type=jsapi", null); | |||
JsonElement tmpJsonElement = JSON_PARSER.parse(responseContent); | |||
JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject(); | |||
String jsapiTicket = tmpJsonObject.get("ticket").getAsString(); | |||
@@ -1,16 +1,17 @@ | |||
package cn.binarywang.wx.miniapp.api.impl; | |||
import java.io.File; | |||
import cn.binarywang.wx.miniapp.api.WxMaQrcodeService; | |||
import cn.binarywang.wx.miniapp.api.WxMaService; | |||
import cn.binarywang.wx.miniapp.bean.WxMaCodeLineColor; | |||
import cn.binarywang.wx.miniapp.bean.WxMaQrcode; | |||
import cn.binarywang.wx.miniapp.bean.WxMaWxcode; | |||
import cn.binarywang.wx.miniapp.bean.WxaCode; | |||
import cn.binarywang.wx.miniapp.bean.WxaCodeUnlimit; | |||
import cn.binarywang.wx.miniapp.util.http.QrCodeRequestExecutor; | |||
import cn.binarywang.wx.miniapp.util.QrcodeBytesRequestExecutor; | |||
import cn.binarywang.wx.miniapp.util.QrcodeRequestExecutor; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import java.io.File; | |||
/** | |||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
*/ | |||
@@ -21,10 +22,16 @@ public class WxMaQrcodeServiceImpl implements WxMaQrcodeService { | |||
this.wxMaService = wxMaService; | |||
} | |||
@Override | |||
public byte[] createQrcodeBytes(String path, int width) throws WxErrorException { | |||
final QrcodeBytesRequestExecutor executor = new QrcodeBytesRequestExecutor(this.wxMaService.getRequestHttp()); | |||
return this.wxMaService.execute(executor, CREATE_QRCODE_URL, new WxMaQrcode(path, width)); | |||
} | |||
@Override | |||
public File createQrcode(String path, int width) throws WxErrorException { | |||
return this.wxMaService.execute(new QrCodeRequestExecutor(this.wxMaService.getRequestHttp()), | |||
CREATE_QRCODE_URL, new WxMaQrcode(path, width)); | |||
final QrcodeRequestExecutor executor = new QrcodeRequestExecutor(this.wxMaService.getRequestHttp()); | |||
return this.wxMaService.execute(executor, CREATE_QRCODE_URL, new WxMaQrcode(path, width)); | |||
} | |||
@Override | |||
@@ -33,15 +40,29 @@ public class WxMaQrcodeServiceImpl implements WxMaQrcodeService { | |||
} | |||
@Override | |||
public File createWxaCode(String path, int width, boolean autoColor, WxMaCodeLineColor lineColor, boolean isHyaline) throws WxErrorException { | |||
WxMaWxcode wxMaWxcode = new WxMaWxcode(); | |||
wxMaWxcode.setPath(path); | |||
wxMaWxcode.setWidth(width); | |||
wxMaWxcode.setAutoColor(autoColor); | |||
wxMaWxcode.setLineColor(lineColor); | |||
wxMaWxcode.setHyaline(isHyaline); | |||
return this.wxMaService.execute(new QrCodeRequestExecutor(this.wxMaService.getRequestHttp()), | |||
GET_WXACODE_URL, wxMaWxcode); | |||
public byte[] createWxaCodeBytes(String path, int width, boolean autoColor, WxMaCodeLineColor lineColor, boolean isHyaline) | |||
throws WxErrorException { | |||
final QrcodeBytesRequestExecutor executor = new QrcodeBytesRequestExecutor(this.wxMaService.getRequestHttp()); | |||
return this.wxMaService.execute(executor, GET_WXACODE_URL, WxaCode.builder() | |||
.path(path) | |||
.width(width) | |||
.autoColor(autoColor) | |||
.lineColor(lineColor) | |||
.isHyaline(isHyaline) | |||
.build()); | |||
} | |||
@Override | |||
public File createWxaCode(String path, int width, boolean autoColor, WxMaCodeLineColor lineColor, boolean isHyaline) | |||
throws WxErrorException { | |||
final QrcodeRequestExecutor executor = new QrcodeRequestExecutor(this.wxMaService.getRequestHttp()); | |||
return this.wxMaService.execute(executor, GET_WXACODE_URL, WxaCode.builder() | |||
.path(path) | |||
.width(width) | |||
.autoColor(autoColor) | |||
.lineColor(lineColor) | |||
.isHyaline(isHyaline) | |||
.build()); | |||
} | |||
@Override | |||
@@ -51,12 +72,27 @@ public class WxMaQrcodeServiceImpl implements WxMaQrcodeService { | |||
@Override | |||
public File createWxaCode(String path) throws WxErrorException { | |||
return this.createWxaCode(path, 430, true, null, false); | |||
return this.createWxaCode(path, 430); | |||
} | |||
@Override | |||
public File createWxaCodeUnlimit(String scene, String page, int width, boolean autoColor, WxMaCodeLineColor lineColor, boolean isHyaline) | |||
throws WxErrorException { | |||
public byte[] createWxaCodeUnlimitBytes(String scene, String page, int width, boolean autoColor, | |||
WxMaCodeLineColor lineColor, boolean isHyaline) throws WxErrorException { | |||
return this.wxMaService.execute(new QrcodeBytesRequestExecutor(this.wxMaService.getRequestHttp()), | |||
GET_WXACODE_UNLIMIT_URL, | |||
this.buildWxaCodeUnlimit(scene, page, width, autoColor, lineColor, isHyaline)); | |||
} | |||
@Override | |||
public File createWxaCodeUnlimit(String scene, String page, int width, boolean autoColor, | |||
WxMaCodeLineColor lineColor, boolean isHyaline) throws WxErrorException { | |||
return this.wxMaService.execute(new QrcodeRequestExecutor(this.wxMaService.getRequestHttp()), | |||
GET_WXACODE_UNLIMIT_URL, | |||
this.buildWxaCodeUnlimit(scene, page, width, autoColor, lineColor, isHyaline)); | |||
} | |||
private WxaCodeUnlimit buildWxaCodeUnlimit(String scene, String page, int width, boolean autoColor, | |||
WxMaCodeLineColor lineColor, boolean isHyaline) { | |||
WxaCodeUnlimit wxaCodeUnlimit = new WxaCodeUnlimit(); | |||
wxaCodeUnlimit.setScene(scene); | |||
wxaCodeUnlimit.setPage(page); | |||
@@ -64,8 +100,8 @@ public class WxMaQrcodeServiceImpl implements WxMaQrcodeService { | |||
wxaCodeUnlimit.setAutoColor(autoColor); | |||
wxaCodeUnlimit.setLineColor(lineColor); | |||
wxaCodeUnlimit.setHyaline(isHyaline); | |||
return this.wxMaService.execute(new QrCodeRequestExecutor(this.wxMaService.getRequestHttp()), | |||
GET_WXACODE_UNLIMIT_URL, wxaCodeUnlimit); | |||
return wxaCodeUnlimit; | |||
} | |||
@Override | |||
@@ -0,0 +1,29 @@ | |||
package cn.binarywang.wx.miniapp.api.impl; | |||
import java.util.List; | |||
import cn.binarywang.wx.miniapp.api.WxMaRunService; | |||
import cn.binarywang.wx.miniapp.api.WxMaService; | |||
import cn.binarywang.wx.miniapp.bean.WxMaRunStepInfo; | |||
import cn.binarywang.wx.miniapp.util.crypt.WxMaCryptUtils; | |||
/** | |||
* <pre> | |||
* | |||
* Created by Binary Wang on 2018/11/4. | |||
* </pre> | |||
* | |||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
*/ | |||
public class WxMaRunServiceImpl implements WxMaRunService { | |||
private WxMaService service; | |||
public WxMaRunServiceImpl(WxMaService service) { | |||
this.service = service; | |||
} | |||
@Override | |||
public List<WxMaRunStepInfo> getRunStepInfo(String sessionKey, String encryptedData, String ivStr) { | |||
return WxMaRunStepInfo.fromJson(WxMaCryptUtils.decrypt(sessionKey, encryptedData, ivStr)); | |||
} | |||
} |
@@ -0,0 +1,48 @@ | |||
package cn.binarywang.wx.miniapp.api.impl; | |||
import java.io.File; | |||
import cn.binarywang.wx.miniapp.api.WxMaSecCheckService; | |||
import cn.binarywang.wx.miniapp.api.WxMaService; | |||
import com.google.gson.JsonObject; | |||
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; | |||
/** | |||
* <pre> | |||
* | |||
* Created by Binary Wang on 2018/11/24. | |||
* </pre> | |||
* | |||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
*/ | |||
public class WxMaSecCheckServiceImpl implements WxMaSecCheckService { | |||
private WxMaService service; | |||
public WxMaSecCheckServiceImpl(WxMaService service) { | |||
this.service = service; | |||
} | |||
@Override | |||
public boolean checkImage(File file) throws WxErrorException { | |||
//这里只是借用MediaUploadRequestExecutor,并不使用其返回值WxMediaUploadResult | |||
WxMediaUploadResult result = this.service.execute(MediaUploadRequestExecutor | |||
.create(this.service.getRequestHttp()), IMG_SEC_CHECK_URL, file); | |||
return result != null; | |||
} | |||
@Override | |||
public boolean checkMessage(String msgString) { | |||
JsonObject jsonObject = new JsonObject(); | |||
jsonObject.addProperty("content", msgString); | |||
try { | |||
this.service.post(MSG_SEC_CHECK_URL, jsonObject.toString()); | |||
} catch (WxErrorException e) { | |||
return false; | |||
} | |||
return true; | |||
} | |||
} |
@@ -1,6 +1,5 @@ | |||
package cn.binarywang.wx.miniapp.api.impl; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
@@ -12,8 +11,6 @@ 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 cn.binarywang.wx.miniapp.api.WxMaAnalysisService; | |||
import cn.binarywang.wx.miniapp.api.WxMaCodeService; | |||
@@ -21,22 +18,24 @@ import cn.binarywang.wx.miniapp.api.WxMaJsapiService; | |||
import cn.binarywang.wx.miniapp.api.WxMaMediaService; | |||
import cn.binarywang.wx.miniapp.api.WxMaMsgService; | |||
import cn.binarywang.wx.miniapp.api.WxMaQrcodeService; | |||
import cn.binarywang.wx.miniapp.api.WxMaRunService; | |||
import cn.binarywang.wx.miniapp.api.WxMaSecCheckService; | |||
import cn.binarywang.wx.miniapp.api.WxMaService; | |||
import cn.binarywang.wx.miniapp.api.WxMaSettingService; | |||
import cn.binarywang.wx.miniapp.api.WxMaShareService; | |||
import cn.binarywang.wx.miniapp.api.WxMaTemplateService; | |||
import cn.binarywang.wx.miniapp.api.WxMaUserService; | |||
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; | |||
import cn.binarywang.wx.miniapp.config.WxMaConfig; | |||
import com.google.common.base.Joiner; | |||
import com.google.gson.Gson; | |||
import lombok.extern.slf4j.Slf4j; | |||
import me.chanjar.weixin.common.bean.WxAccessToken; | |||
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | |||
import me.chanjar.weixin.common.error.WxError; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import me.chanjar.weixin.common.util.DataUtils; | |||
import me.chanjar.weixin.common.util.crypto.SHA1; | |||
import me.chanjar.weixin.common.util.http.HttpType; | |||
import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; | |||
import me.chanjar.weixin.common.util.http.RequestExecutor; | |||
import me.chanjar.weixin.common.util.http.RequestHttp; | |||
import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; | |||
@@ -49,9 +48,8 @@ import static cn.binarywang.wx.miniapp.constant.WxMaConstants.ErrorCode.*; | |||
/** | |||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
*/ | |||
@Slf4j | |||
public class WxMaServiceImpl implements WxMaService, RequestHttp<CloseableHttpClient, HttpHost> { | |||
private final Logger log = LoggerFactory.getLogger(this.getClass()); | |||
private CloseableHttpClient httpClient; | |||
private HttpHost httpProxy; | |||
private WxMaConfig wxMaConfig; | |||
@@ -65,6 +63,9 @@ public class WxMaServiceImpl implements WxMaService, RequestHttp<CloseableHttpCl | |||
private WxMaCodeService codeService = new WxMaCodeServiceImpl(this); | |||
private WxMaSettingService settingService = new WxMaSettingServiceImpl(this); | |||
private WxMaJsapiService jsapiService = new WxMaJsapiServiceImpl(this); | |||
private WxMaShareService shareService = new WxMaShareServiceImpl(this); | |||
private WxMaRunService runService = new WxMaRunServiceImpl(this); | |||
private WxMaSecCheckService secCheckService = new WxMaSecCheckServiceImpl(this); | |||
private int retrySleepMillis = 1000; | |||
private int maxRetryTimes = 5; | |||
@@ -149,13 +150,6 @@ public class WxMaServiceImpl implements WxMaService, RequestHttp<CloseableHttpCl | |||
return this.getWxMaConfig().getAccessToken(); | |||
} | |||
@Override | |||
public boolean imgSecCheck(File file) throws WxErrorException { | |||
//这里只是借用MediaUploadRequestExecutor,并不使用其返回值WxMediaUploadResult | |||
WxMediaUploadResult result = this.execute(MediaUploadRequestExecutor.create(this.getRequestHttp()), IMG_SEC_CHECK_URL, file); | |||
return result != null; | |||
} | |||
@Override | |||
public WxMaJscode2SessionResult jsCode2SessionInfo(String jsCode) throws WxErrorException { | |||
final WxMaConfig config = getWxMaConfig(); | |||
@@ -205,7 +199,7 @@ public class WxMaServiceImpl implements WxMaService, RequestHttp<CloseableHttpCl | |||
return this.executeInternal(executor, uri, data); | |||
} catch (WxErrorException e) { | |||
if (retryTimes + 1 > this.maxRetryTimes) { | |||
this.log.warn("重试达到最大次数【{}】", maxRetryTimes); | |||
log.warn("重试达到最大次数【{}】", maxRetryTimes); | |||
//最后一次重试失败后,直接抛出异常,不再等待 | |||
throw new RuntimeException("微信服务端异常,超出重试次数"); | |||
} | |||
@@ -215,10 +209,10 @@ public class WxMaServiceImpl implements WxMaService, RequestHttp<CloseableHttpCl | |||
if (error.getErrorCode() == -1) { | |||
int sleepMillis = this.retrySleepMillis * (1 << retryTimes); | |||
try { | |||
this.log.warn("微信系统繁忙,{} ms 后重试(第{}次)", sleepMillis, retryTimes + 1); | |||
log.warn("微信系统繁忙,{} ms 后重试(第{}次)", sleepMillis, retryTimes + 1); | |||
Thread.sleep(sleepMillis); | |||
} catch (InterruptedException e1) { | |||
throw new RuntimeException(e1); | |||
Thread.currentThread().interrupt(); | |||
} | |||
} else { | |||
throw e; | |||
@@ -226,7 +220,7 @@ public class WxMaServiceImpl implements WxMaService, RequestHttp<CloseableHttpCl | |||
} | |||
} while (retryTimes++ < this.maxRetryTimes); | |||
this.log.warn("重试达到最大次数【{}】", this.maxRetryTimes); | |||
log.warn("重试达到最大次数【{}】", this.maxRetryTimes); | |||
throw new RuntimeException("微信服务端异常,超出重试次数"); | |||
} | |||
@@ -242,7 +236,7 @@ public class WxMaServiceImpl implements WxMaService, RequestHttp<CloseableHttpCl | |||
try { | |||
T result = executor.execute(uriWithAccessToken, data); | |||
this.log.debug("\n【请求地址】: {}\n【请求参数】:{}\n【响应数据】:{}", uriWithAccessToken, dataForLog, result); | |||
log.debug("\n【请求地址】: {}\n【请求参数】:{}\n【响应数据】:{}", uriWithAccessToken, dataForLog, result); | |||
return result; | |||
} catch (WxErrorException e) { | |||
WxError error = e.getError(); | |||
@@ -260,12 +254,12 @@ public class WxMaServiceImpl implements WxMaService, RequestHttp<CloseableHttpCl | |||
} | |||
if (error.getErrorCode() != 0) { | |||
this.log.error("\n【请求地址】: {}\n【请求参数】:{}\n【错误信息】:{}", uriWithAccessToken, dataForLog, error); | |||
log.error("\n【请求地址】: {}\n【请求参数】:{}\n【错误信息】:{}", uriWithAccessToken, dataForLog, error); | |||
throw new WxErrorException(error, e); | |||
} | |||
return null; | |||
} catch (IOException e) { | |||
this.log.error("\n【请求地址】: {}\n【请求参数】:{}\n【异常信息】:{}", uriWithAccessToken, dataForLog, e.getMessage()); | |||
log.error("\n【请求地址】: {}\n【请求参数】:{}\n【异常信息】:{}", uriWithAccessToken, dataForLog, e.getMessage()); | |||
throw new RuntimeException(e); | |||
} | |||
} | |||
@@ -335,4 +329,19 @@ public class WxMaServiceImpl implements WxMaService, RequestHttp<CloseableHttpCl | |||
public WxMaSettingService getSettingService() { | |||
return this.settingService; | |||
} | |||
@Override | |||
public WxMaShareService getShareService() { | |||
return this.shareService; | |||
} | |||
@Override | |||
public WxMaRunService getRunService() { | |||
return this.runService; | |||
} | |||
@Override | |||
public WxMaSecCheckService getSecCheckService() { | |||
return this.secCheckService; | |||
} | |||
} |
@@ -0,0 +1,23 @@ | |||
package cn.binarywang.wx.miniapp.api.impl; | |||
import cn.binarywang.wx.miniapp.api.WxMaService; | |||
import cn.binarywang.wx.miniapp.api.WxMaShareService; | |||
import cn.binarywang.wx.miniapp.bean.WxMaShareInfo; | |||
import cn.binarywang.wx.miniapp.util.crypt.WxMaCryptUtils; | |||
/** | |||
* @author zhfish | |||
*/ | |||
public class WxMaShareServiceImpl implements WxMaShareService { | |||
private WxMaService service; | |||
public WxMaShareServiceImpl(WxMaService service) { | |||
this.service = service; | |||
} | |||
@Override | |||
public WxMaShareInfo getShareInfo(String sessionKey, String encryptedData, String ivStr) { | |||
return WxMaShareInfo.fromJson(WxMaCryptUtils.decrypt(sessionKey, encryptedData, ivStr)); | |||
} | |||
} |
@@ -1,5 +1,9 @@ | |||
package cn.binarywang.wx.miniapp.api.impl; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
import cn.binarywang.wx.miniapp.api.WxMaService; | |||
import cn.binarywang.wx.miniapp.api.WxMaTemplateService; | |||
import cn.binarywang.wx.miniapp.bean.template.WxMaTemplateAddResult; | |||
@@ -10,15 +14,11 @@ import me.chanjar.weixin.common.error.WxError; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import me.chanjar.weixin.common.util.json.WxGsonBuilder; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
public class WxMaTemplateServiceImpl implements WxMaTemplateService { | |||
private WxMaService wxMaService; | |||
public WxMaTemplateServiceImpl(WxMaService wxMaService){ | |||
public WxMaTemplateServiceImpl(WxMaService wxMaService) { | |||
this.wxMaService = wxMaService; | |||
} | |||
@@ -31,9 +31,9 @@ public class WxMaTemplateServiceImpl implements WxMaTemplateService { | |||
String responseText = this.wxMaService.post(TEMPLATE_LIBRARY_LIST_URL, WxGsonBuilder.create().toJson(params)); | |||
WxError wxError = WxError.fromJson(responseText); | |||
if(wxError.getErrorCode() == 0){ | |||
if (wxError.getErrorCode() == 0) { | |||
return WxMaTemplateLibraryListResult.fromJson(responseText); | |||
}else { | |||
} else { | |||
throw new WxErrorException(wxError); | |||
} | |||
} | |||
@@ -46,9 +46,9 @@ public class WxMaTemplateServiceImpl implements WxMaTemplateService { | |||
String responseText = this.wxMaService.post(TEMPLATE_LIBRARY_KEYWORD_URL, WxGsonBuilder.create().toJson(params)); | |||
WxError wxError = WxError.fromJson(responseText); | |||
if(wxError.getErrorCode() == 0){ | |||
if (wxError.getErrorCode() == 0) { | |||
return WxMaTemplateLibraryGetResult.fromJson(responseText); | |||
}else { | |||
} else { | |||
throw new WxErrorException(wxError); | |||
} | |||
} | |||
@@ -62,9 +62,9 @@ public class WxMaTemplateServiceImpl implements WxMaTemplateService { | |||
String responseText = this.wxMaService.post(TEMPLATE_ADD_URL, WxGsonBuilder.create().toJson(params)); | |||
WxError wxError = WxError.fromJson(responseText); | |||
if(wxError.getErrorCode() == 0){ | |||
if (wxError.getErrorCode() == 0) { | |||
return WxMaTemplateAddResult.fromJson(responseText); | |||
}else { | |||
} else { | |||
throw new WxErrorException(wxError); | |||
} | |||
} | |||
@@ -78,9 +78,9 @@ public class WxMaTemplateServiceImpl implements WxMaTemplateService { | |||
String responseText = this.wxMaService.post(TEMPLATE_LIST_URL, WxGsonBuilder.create().toJson(params)); | |||
WxError wxError = WxError.fromJson(responseText); | |||
if(wxError.getErrorCode() == 0){ | |||
if (wxError.getErrorCode() == 0) { | |||
return WxMaTemplateListResult.fromJson(responseText); | |||
}else { | |||
} else { | |||
throw new WxErrorException(wxError); | |||
} | |||
} | |||
@@ -92,9 +92,9 @@ public class WxMaTemplateServiceImpl implements WxMaTemplateService { | |||
String responseText = this.wxMaService.post(TEMPLATE_DEL_URL, WxGsonBuilder.create().toJson(params)); | |||
WxError wxError = WxError.fromJson(responseText); | |||
if(wxError.getErrorCode() == 0){ | |||
if (wxError.getErrorCode() == 0) { | |||
return true; | |||
}else { | |||
} else { | |||
throw new WxErrorException(wxError); | |||
} | |||
} | |||
@@ -6,8 +6,6 @@ import java.io.Serializable; | |||
import java.nio.charset.StandardCharsets; | |||
import org.apache.commons.io.IOUtils; | |||
import org.apache.commons.lang3.builder.ToStringBuilder; | |||
import org.apache.commons.lang3.builder.ToStringStyle; | |||
import cn.binarywang.wx.miniapp.config.WxMaConfig; | |||
import cn.binarywang.wx.miniapp.util.crypt.WxMaCryptUtils; | |||
@@ -168,7 +166,7 @@ public class WxMaMessage implements Serializable { | |||
@Override | |||
public String toString() { | |||
return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); | |||
return this.toJson(); | |||
} | |||
public String toJson() { | |||
@@ -0,0 +1,38 @@ | |||
package cn.binarywang.wx.miniapp.bean; | |||
import java.io.Serializable; | |||
import java.util.List; | |||
import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; | |||
import com.google.gson.JsonObject; | |||
import com.google.gson.JsonParser; | |||
import com.google.gson.reflect.TypeToken; | |||
import lombok.Data; | |||
/** | |||
* 微信运动步数信息. | |||
* | |||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
*/ | |||
@Data | |||
public class WxMaRunStepInfo implements Serializable { | |||
private static final JsonParser JSON_PARSER = new JsonParser(); | |||
private static final long serialVersionUID = -7496372171398607044L; | |||
/** | |||
* 时间戳,表示数据对应的时间. | |||
*/ | |||
private Long timestamp; | |||
/** | |||
* 微信运动步数. | |||
*/ | |||
private Integer step; | |||
public static List<WxMaRunStepInfo> fromJson(String json) { | |||
JsonObject jsonObject = JSON_PARSER.parse(json).getAsJsonObject(); | |||
return WxMaGsonBuilder.create().fromJson(jsonObject.get("stepInfoList").toString(), | |||
new TypeToken<List<WxMaRunStepInfo>>() { | |||
}.getType()); | |||
} | |||
} |
@@ -0,0 +1,20 @@ | |||
package cn.binarywang.wx.miniapp.bean; | |||
import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; | |||
import lombok.Data; | |||
import java.io.Serializable; | |||
/** | |||
* @author zhfish | |||
*/ | |||
@Data | |||
public class WxMaShareInfo implements Serializable { | |||
private static final long serialVersionUID = -8053613683499632226L; | |||
private String openGId; | |||
public static WxMaShareInfo fromJson(String json) { | |||
return WxMaGsonBuilder.create().fromJson(json, WxMaShareInfo.class); | |||
} | |||
} |
@@ -1,36 +1,48 @@ | |||
package cn.binarywang.wx.miniapp.bean; | |||
import java.io.Serializable; | |||
import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; | |||
import com.google.gson.annotations.SerializedName; | |||
import lombok.AllArgsConstructor; | |||
import lombok.Builder; | |||
import lombok.Data; | |||
import lombok.EqualsAndHashCode; | |||
import java.io.Serializable; | |||
import lombok.NoArgsConstructor; | |||
/** | |||
* 小程序码. | |||
* | |||
* @author Element | |||
* @date 2017/7/27 | |||
*/ | |||
@Data | |||
@EqualsAndHashCode(callSuper = false) | |||
public class WxMaWxcode extends AbstractWxMaQrcodeWrapper implements Serializable { | |||
@Builder | |||
@NoArgsConstructor | |||
@AllArgsConstructor | |||
public class WxaCode extends AbstractWxMaQrcodeWrapper implements Serializable { | |||
private static final long serialVersionUID = 1287399621649210322L; | |||
private String path; | |||
@Builder.Default | |||
private int width = 430; | |||
@SerializedName("auto_color") | |||
@Builder.Default | |||
private boolean autoColor = true; | |||
@SerializedName("is_hyaline") | |||
@Builder.Default | |||
private boolean isHyaline = false; | |||
@SerializedName("line_color") | |||
@Builder.Default | |||
private WxMaCodeLineColor lineColor = new WxMaCodeLineColor("0", "0", "0"); | |||
public static WxMaWxcode fromJson(String json) { | |||
return WxMaGsonBuilder.create().fromJson(json, WxMaWxcode.class); | |||
public static WxaCode fromJson(String json) { | |||
return WxMaGsonBuilder.create().fromJson(json, WxaCode.class); | |||
} | |||
} |
@@ -1,10 +1,10 @@ | |||
package cn.binarywang.wx.miniapp.config; | |||
import java.util.concurrent.locks.Lock; | |||
import me.chanjar.weixin.common.bean.WxAccessToken; | |||
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; | |||
import java.util.concurrent.locks.Lock; | |||
/** | |||
* 小程序配置 | |||
* | |||
@@ -57,6 +57,28 @@ public interface WxMaConfig { | |||
*/ | |||
void updateJsapiTicket(String jsapiTicket, int expiresInSeconds); | |||
/** | |||
* 卡券api_ticket. | |||
*/ | |||
String getCardApiTicket(); | |||
Lock getCardApiTicketLock(); | |||
boolean isCardApiTicketExpired(); | |||
/** | |||
* 强制将卡券api ticket过期掉. | |||
*/ | |||
void expireCardApiTicket(); | |||
/** | |||
* 应该是线程安全的. | |||
* | |||
* @param apiTicket 新的卡券api ticket值 | |||
* @param expiresInSeconds 过期时间,以秒为单位 | |||
*/ | |||
void updateCardApiTicket(String apiTicket, int expiresInSeconds); | |||
String getAppid(); | |||
String getSecret(); | |||
@@ -4,9 +4,7 @@ import java.io.File; | |||
import java.util.concurrent.locks.Lock; | |||
import java.util.concurrent.locks.ReentrantLock; | |||
import org.apache.commons.lang3.builder.ToStringBuilder; | |||
import org.apache.commons.lang3.builder.ToStringStyle; | |||
import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; | |||
import me.chanjar.weixin.common.bean.WxAccessToken; | |||
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; | |||
@@ -32,8 +30,14 @@ public class WxMaInMemoryConfig implements WxMaConfig { | |||
protected volatile String jsapiTicket; | |||
protected volatile long jsapiTicketExpiresTime; | |||
//微信卡券的ticket单独缓存 | |||
protected volatile String cardApiTicket; | |||
protected volatile long cardApiTicketExpiresTime; | |||
protected Lock accessTokenLock = new ReentrantLock(); | |||
protected Lock jsapiTicketLock = new ReentrantLock(); | |||
protected Lock cardApiTicketLock = new ReentrantLock(); | |||
/** | |||
* 临时文件目录 | |||
@@ -103,6 +107,34 @@ public class WxMaInMemoryConfig implements WxMaConfig { | |||
this.jsapiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L; | |||
} | |||
@Override | |||
public String getCardApiTicket() { | |||
return this.cardApiTicket; | |||
} | |||
@Override | |||
public Lock getCardApiTicketLock() { | |||
return this.cardApiTicketLock; | |||
} | |||
@Override | |||
public boolean isCardApiTicketExpired() { | |||
return System.currentTimeMillis() > this.cardApiTicketExpiresTime; | |||
} | |||
@Override | |||
public void expireCardApiTicket() { | |||
this.cardApiTicketExpiresTime = 0; | |||
} | |||
@Override | |||
public void updateCardApiTicket(String cardApiTicket, int expiresInSeconds) { | |||
this.cardApiTicket = cardApiTicket; | |||
// 预留200秒的时间 | |||
this.cardApiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L; | |||
} | |||
@Override | |||
public void expireAccessToken() { | |||
this.expiresTime = 0; | |||
@@ -191,7 +223,7 @@ public class WxMaInMemoryConfig implements WxMaConfig { | |||
@Override | |||
public String toString() { | |||
return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); | |||
return WxMaGsonBuilder.create().toJson(this); | |||
} | |||
@Override | |||
@@ -0,0 +1,66 @@ | |||
package cn.binarywang.wx.miniapp.util; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import org.apache.commons.io.IOUtils; | |||
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 cn.binarywang.wx.miniapp.bean.AbstractWxMaQrcodeWrapper; | |||
import me.chanjar.weixin.common.error.WxError; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import me.chanjar.weixin.common.util.http.RequestExecutor; | |||
import me.chanjar.weixin.common.util.http.RequestHttp; | |||
import me.chanjar.weixin.common.util.http.ResponseHandler; | |||
import me.chanjar.weixin.common.util.http.apache.InputStreamResponseHandler; | |||
import me.chanjar.weixin.common.util.http.apache.Utf8ResponseHandler; | |||
/** | |||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
*/ | |||
public class QrcodeBytesRequestExecutor implements RequestExecutor<byte[], AbstractWxMaQrcodeWrapper> { | |||
protected RequestHttp<CloseableHttpClient, HttpHost> requestHttp; | |||
public QrcodeBytesRequestExecutor(RequestHttp requestHttp) { | |||
this.requestHttp = requestHttp; | |||
} | |||
@Override | |||
public void execute(String uri, AbstractWxMaQrcodeWrapper data, ResponseHandler<byte[]> handler) throws WxErrorException, IOException { | |||
handler.handle(this.execute(uri, data)); | |||
} | |||
@Override | |||
public byte[] execute(String uri, AbstractWxMaQrcodeWrapper qrcodeWrapper) throws WxErrorException, IOException { | |||
HttpPost httpPost = new HttpPost(uri); | |||
if (requestHttp.getRequestHttpProxy() != null) { | |||
httpPost.setConfig( | |||
RequestConfig.custom().setProxy(requestHttp.getRequestHttpProxy()).build() | |||
); | |||
} | |||
httpPost.setEntity(new StringEntity(qrcodeWrapper.toJson())); | |||
try (final CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpPost); | |||
final InputStream inputStream = InputStreamResponseHandler.INSTANCE.handleResponse(response)) { | |||
Header[] contentTypeHeader = response.getHeaders("Content-Type"); | |||
if (contentTypeHeader != null && contentTypeHeader.length > 0 | |||
&& ContentType.APPLICATION_JSON.getMimeType() | |||
.equals(ContentType.parse(contentTypeHeader[0].getValue()).getMimeType())) { | |||
String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); | |||
throw new WxErrorException(WxError.fromJson(responseContent)); | |||
} | |||
return IOUtils.toByteArray(inputStream); | |||
} finally { | |||
httpPost.releaseConnection(); | |||
} | |||
} | |||
} |
@@ -1,13 +1,10 @@ | |||
package cn.binarywang.wx.miniapp.util.http; | |||
package cn.binarywang.wx.miniapp.util; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.util.UUID; | |||
import cn.binarywang.wx.miniapp.bean.AbstractWxMaQrcodeWrapper; | |||
import me.chanjar.weixin.common.error.WxError; | |||
import me.chanjar.weixin.common.error.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; | |||
@@ -17,33 +14,44 @@ 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; | |||
import cn.binarywang.wx.miniapp.bean.AbstractWxMaQrcodeWrapper; | |||
import me.chanjar.weixin.common.error.WxError; | |||
import me.chanjar.weixin.common.error.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.ResponseHandler; | |||
import me.chanjar.weixin.common.util.http.apache.InputStreamResponseHandler; | |||
import me.chanjar.weixin.common.util.http.apache.Utf8ResponseHandler; | |||
/** | |||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
*/ | |||
public class QrCodeRequestExecutor implements RequestExecutor<File, AbstractWxMaQrcodeWrapper> { | |||
public class QrcodeRequestExecutor implements RequestExecutor<File, AbstractWxMaQrcodeWrapper> { | |||
protected RequestHttp<CloseableHttpClient, HttpHost> requestHttp; | |||
public QrCodeRequestExecutor(RequestHttp requestHttp) { | |||
public QrcodeRequestExecutor(RequestHttp requestHttp) { | |||
this.requestHttp = requestHttp; | |||
} | |||
@Override | |||
public File execute(String uri, AbstractWxMaQrcodeWrapper ticket) throws WxErrorException, IOException { | |||
public void execute(String uri, AbstractWxMaQrcodeWrapper data, ResponseHandler<File> handler) throws WxErrorException, IOException { | |||
handler.handle(this.execute(uri, data)); | |||
} | |||
@Override | |||
public File execute(String uri, AbstractWxMaQrcodeWrapper qrcodeWrapper) 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.toJson())); | |||
try (CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpPost); | |||
InputStream inputStream = InputStreamResponseHandler.INSTANCE.handleResponse(response);) { | |||
httpPost.setEntity(new StringEntity(qrcodeWrapper.toJson())); | |||
try (final CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpPost); | |||
final InputStream inputStream = InputStreamResponseHandler.INSTANCE.handleResponse(response)) { | |||
Header[] contentTypeHeader = response.getHeaders("Content-Type"); | |||
if (contentTypeHeader != null && contentTypeHeader.length > 0 | |||
&& ContentType.APPLICATION_JSON.getMimeType() |
@@ -7,6 +7,9 @@ import org.testng.annotations.*; | |||
import cn.binarywang.wx.miniapp.api.WxMaService; | |||
import cn.binarywang.wx.miniapp.test.ApiTestModule; | |||
import com.google.inject.Inject; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
/** | |||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
@@ -18,21 +21,38 @@ public class WxMaQrcodeServiceImplTest { | |||
private WxMaService wxService; | |||
@Test | |||
public void testCreateQrCode() throws Exception { | |||
public void testCreateQrcode() throws Exception { | |||
final File qrCode = this.wxService.getQrcodeService().createQrcode("111", 122); | |||
System.out.println(qrCode); | |||
assertThat(qrCode).isNotNull(); | |||
} | |||
@Test | |||
public void testCreateWxaCode() throws Exception { | |||
final File wxCode = this.wxService.getQrcodeService().createWxaCode("111", 122); | |||
System.out.println(wxCode); | |||
assertThat(wxCode).isNotNull(); | |||
} | |||
@Test | |||
public void testCreateWxaCodeUnlimit() throws Exception { | |||
final File wxCode = this.wxService.getQrcodeService().createWxaCodeUnlimit("111", null); | |||
System.out.println(wxCode); | |||
assertThat(wxCode).isNotNull(); | |||
} | |||
@Test | |||
public void testCreateQrcodeBytes() throws WxErrorException { | |||
final byte[] qrCode = this.wxService.getQrcodeService().createQrcodeBytes("111", 122); | |||
assertThat(qrCode).isNotNull(); | |||
} | |||
@Test | |||
public void testCreateWxaCodeBytes() throws WxErrorException { | |||
final byte[] wxCode = this.wxService.getQrcodeService().createWxaCodeBytes("111", 122, true, null, false); | |||
assertThat(wxCode).isNotNull(); | |||
} | |||
@Test | |||
public void testCreateWxaCodeUnlimitBytes() throws WxErrorException { | |||
final byte[] wxCode = this.wxService.getQrcodeService().createWxaCodeUnlimitBytes("111", null, 122, true, null, false); | |||
assertThat(wxCode).isNotNull(); | |||
} | |||
} |
@@ -0,0 +1,51 @@ | |||
package cn.binarywang.wx.miniapp.api.impl; | |||
import java.io.File; | |||
import org.testng.annotations.*; | |||
import cn.binarywang.wx.miniapp.api.WxMaService; | |||
import cn.binarywang.wx.miniapp.test.ApiTestModule; | |||
import com.google.inject.Inject; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.testng.Assert.*; | |||
/** | |||
* <pre> | |||
* | |||
* Created by Binary Wang on 2018/11/24. | |||
* </pre> | |||
* | |||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
*/ | |||
@Test | |||
@Guice(modules = ApiTestModule.class) | |||
public class WxMaSecCheckServiceImplTest { | |||
@Inject | |||
private WxMaService wxService; | |||
@Test | |||
public void testCheckImage() throws WxErrorException { | |||
boolean result = this.wxService.getSecCheckService() | |||
.checkImage(new File(ClassLoader.getSystemResource("tmp.png").getFile())); | |||
assertTrue(result); | |||
} | |||
@DataProvider | |||
public Object[][] secData() { | |||
return new Object[][]{ | |||
{"特3456书yuuo莞6543李zxcz蒜7782法fgnv级", false}, | |||
{"完2347全dfji试3726测asad感3847知qwez到", false}, | |||
{"hello world!", true} | |||
}; | |||
} | |||
@Test(dataProvider = "secData") | |||
public void testCheckMessage(String msg, boolean result) { | |||
assertThat(this.wxService.getSecCheckService() | |||
.checkMessage(msg)) | |||
.isEqualTo(result); | |||
} | |||
} |
@@ -33,9 +33,4 @@ public class WxMaServiceImplTest { | |||
assertTrue(StringUtils.isNotBlank(after)); | |||
} | |||
@Test | |||
public void testImgSecCheck() throws WxErrorException { | |||
boolean result = this.wxService.imgSecCheck(new File(ClassLoader.getSystemResource("tmp.png").getFile())); | |||
assertTrue(result); | |||
} | |||
} |
@@ -0,0 +1,46 @@ | |||
package cn.binarywang.wx.miniapp.api.impl; | |||
import cn.binarywang.wx.miniapp.api.WxMaService; | |||
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; | |||
import cn.binarywang.wx.miniapp.bean.WxMaShareInfo; | |||
import cn.binarywang.wx.miniapp.bean.WxMaUserInfo; | |||
import cn.binarywang.wx.miniapp.test.ApiTestModule; | |||
import cn.binarywang.wx.miniapp.test.TestConfig; | |||
import com.google.common.collect.ImmutableMap; | |||
import com.google.inject.Inject; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import org.testng.annotations.Guice; | |||
import org.testng.annotations.Test; | |||
import static org.testng.Assert.assertNotNull; | |||
import static org.testng.Assert.assertTrue; | |||
/** | |||
* 测试分享相关的接口 | |||
* | |||
* @author zhfish | |||
*/ | |||
@Test | |||
@Guice(modules = ApiTestModule.class) | |||
public class WxMaShareServiceImplTest { | |||
@Inject | |||
private WxMaService wxService; | |||
@Test | |||
public void testGetSessionKey() throws Exception { | |||
assertNotNull(this.wxService.getUserService().getSessionInfo("aaa")); | |||
} | |||
/** | |||
* TODO 测试数据有问题,需要替换为正确的数据 | |||
*/ | |||
@Test | |||
public void testGetShareInfo() { | |||
WxMaShareInfo shareInfo = this.wxService.getShareService().getShareInfo("tiihtNczf5v6AKRyjwEUhQ==", | |||
"CiyLU1Aw2KjvrjMdj8YKliAjtP4gsMZMQmRzooG2xrDcvSnxIMXFufNstNGTyaGS9uT5geRa0W4oTOb1WT7fJlAC+oNPdbB+3hVbJSRgv+4lGOETKUQz6OYStslQ142dNCuabNPGBzlooOmB231qMM85d2/fV6ChevvXvQP8Hkue1poOFtnEtpyxVLW1zAo6/1Xx1COxFvrc2d7UL/lmHInNlxuacJXwu0fjpXfz/YqYzBIBzD6WUfTIF9GRHpOn/Hz7saL8xz+W//FRAUid1OksQaQx4CMs8LOddcQhULW4ucetDf96JcR3g0gfRK4PC7E/r7Z6xNrXd2UIeorGj5Ef7b1pJAYB6Y5anaHqZ9J6nKEBvB4DnNLIVWSgARns/8wR2SiRS7MNACwTyrGvt9ts8p12PKFdlqYTopNHR1Vf7XjfhQlVsAJdNiKdYmYVoKlaRv85IfVunYzO0IKXsyl7JCUjCpoG20f0a04COwfneQAGGwd5oa+T8yO5hzuyDb/XcxxmK01EpqOyuxINew==", | |||
"r7BXXKkLb8qrSNn05n0qiA=="); | |||
assertNotNull(shareInfo); | |||
System.out.println(shareInfo.toString()); | |||
} | |||
} |
@@ -0,0 +1,43 @@ | |||
package cn.binarywang.wx.miniapp.bean; | |||
import java.util.List; | |||
import org.testng.annotations.*; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.testng.Assert.*; | |||
/** | |||
* <pre> | |||
* | |||
* Created by Binary Wang on 2018/11/4. | |||
* </pre> | |||
* | |||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
*/ | |||
public class WxMaRunStepInfoTest { | |||
@Test | |||
public void testFromJson() { | |||
// 数据来源:https://developers.weixin.qq.com/miniprogram/dev/api/open-api/werun/wx.getWeRunData.html | |||
String json = "{\n" + | |||
" \"stepInfoList\": [\n" + | |||
" {\n" + | |||
" \"timestamp\": 1445866601,\n" + | |||
" \"step\": 100\n" + | |||
" },\n" + | |||
" {\n" + | |||
" \"timestamp\": 1445876601,\n" + | |||
" \"step\": 120\n" + | |||
" }\n" + | |||
" ]\n" + | |||
"}"; | |||
final List<WxMaRunStepInfo> stepInfoList = WxMaRunStepInfo.fromJson(json); | |||
assertThat(stepInfoList).isNotEmpty(); | |||
assertThat(stepInfoList.get(0).getStep()).isEqualTo(100); | |||
assertThat(stepInfoList.get(0).getTimestamp()).isEqualTo(1445866601); | |||
assertThat(stepInfoList.get(1).getStep()).isEqualTo(120); | |||
assertThat(stepInfoList.get(1).getTimestamp()).isEqualTo(1445876601); | |||
} | |||
} |