diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index d87b6c73f..c15adca96 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -8,7 +8,8 @@ assignees: '' --- # 提问前,请确保阅读过项目首页说明以及wiki开发文档相关内容,尤其是常见问题部分。完成内容后,请务必移除包括本句在内的无用内容,以免影响阅读,否则直接关闭,谢谢合作~ -# 另外如果确认属于bug,而且已明确如何修复,请参考贡献指南直接提交PR,省的浪费时间在这里描述问题,非常感谢配合 + +## 另外如果确认属于bug,而且已明确如何修复,请参考贡献指南直接提交PR,省的浪费时间在这里描述问题,非常感谢配合 ### 简要描述 __简单概括描述下你所遇到的问题。__ @@ -21,4 +22,7 @@ __简单概括描述下你所遇到的问题。__ __尽量详细描述。请不要使用截图,尽量使用文字描述,代码直接贴上来,日志则请附在后面所示区域。__ ### 日志 -__将日志放在 [pastebin](https://paste.ubuntu.com/) 或者其他地方,并将其url地址贴在这里__ +__如果日志不多,直接使用md代码引用格式贴在此处,否则如果太长,请将日志放在 [pastebin](https://paste.ubuntu.com/) 或者其他地方,然后将其url地址贴在这里__ +``` +日志请写于此处 +``` diff --git a/README.md b/README.md index e8636426c..df517de76 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -## WxJava - 微信开发 Java SDK(开发工具包) [![LICENSE](https://img.shields.io/badge/License-Anti%20996-blue.svg)](https://github.com/996icu/996.ICU/blob/master/LICENSE) [![Badge](https://img.shields.io/badge/Link-996.icu-red.svg)](https://996.icu/#/zh_CN) +## WxJava - 微信开发 Java SDK [![码云Gitee](https://gitee.com/binary/weixin-java-tools/badge/star.svg?theme=blue)](https://gitee.com/binary/weixin-java-tools) [![Github](https://img.shields.io/github/stars/Wechat-Group/WxJava?logo=github&style=flat)](https://github.com/Wechat-Group/WxJava) @@ -8,42 +8,54 @@ [![使用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) -#### 支持包括微信支付、开放平台、公众号、企业微信/企业号、小程序等微信功能的后端开发。 +#### 微信`Java`开发工具包,支持包括微信支付、开放平台、公众号、企业微信/企业号、小程序等微信功能模块的后端开发。 -

+

特别赞助 -

-
- - - - - - - + +
- - - - - - - - - -
1. 驰骋快速开发平台、工作流/表单引擎采用GPL协议。 -
2. 驰骋.NET版称为ccflow,Java版称为jflow,代码100%开源。 -
3. 支持10多个国内外数据库,有单机版\集团版\SAAS版本。 -
-
+ + + + + + + + + + +
+ + + +
+ + 计全支付Jeepay,开源支付系统 + +
+ + + + + + diboot低代码开发平台 + + + + + +
### 重要信息 -1. **2021-06-02 发布 [【4.1.0正式版】](https://mp.weixin.qq.com/s/nIk_xOf6dxkhKfqq830Cuw)**! -1. 新手重要提示:本项目仅是一个SDK开发工具包,未提供Web实现,建议使用 `maven` 或 `gradle` 引用本项目即可使用本SDK提供的各种功能,详情可参考 **[【Demo项目】](demo.md)** 或本项目中的部分单元测试代码;另外微信开发新手请务必阅读[【开发文档 Wiki 首页】](https://github.com/Wechat-Group/WxJava/wiki)的常见问题部分,可以少走很多弯路,节省不少时间。 -1. 技术交流群:想获得QQ群/微信群/钉钉企业群等信息的同学,请使用微信扫描上面的微信公众号二维码关注 `WxJava` 后点击相关菜单即可获取加入方式,同时也可以在微信中搜索 `weixin-java-tools` 或 `WxJava` 后选择正确的公众号进行关注,该公众号会及时通知SDK相关更新信息,并不定期分享微信Java开发相关技术知识; -1. 钉钉技术交流群:`32206329`(技术交流2群), `30294972`(技术交流1群,目前已满),`35724728`(通知群,实时通知Github项目变更记录)。 -1. 微信开发新手或者Java开发新手在群内提问或新开Issue提问前,请先阅读[【提问的智慧】](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/master/README-zh_CN.md),并确保已查阅过 [【开发文档Wiki】](https://github.com/wechat-group/WxJava/wiki) ,避免浪费大家的宝贵时间; -1. 寻求帮助时需贴代码或大长串异常信息的,请利用 http://paste.ubuntu.com +1. 项目合作洽谈,请联系微信`binary0000`(在微信里自行搜索并添加好友即可,请注明来意)。 +2. **2021-11-01 发布 [【4.2.0正式版】](https://mp.weixin.qq.com/s/nIk_xOf6dxkhKfqq830Cuw)**! +3. 贡献源码可以参考视频:[【贡献源码全过程(上集)】](https://mp.weixin.qq.com/s/3xUZSATWwHR_gZZm207h7Q)、[【贡献源码全过程(下集)】](https://mp.weixin.qq.com/s/nyzJwVVoYSJ4hSbwyvTx9A) ,友情提供:[程序员小山与Bug](https://space.bilibili.com/473631007) +4. 新手重要提示:本项目仅是一个SDK开发工具包,未提供Web实现,建议使用 `maven` 或 `gradle` 引用本项目即可使用本SDK提供的各种功能,详情可参考 **[【Demo项目】](demo.md)** 或本项目中的部分单元测试代码;另外微信开发新手请务必阅读[【开发文档 Wiki 首页】](https://github.com/Wechat-Group/WxJava/wiki)的常见问题部分,可以少走很多弯路,节省不少时间。 +5. 技术交流群:想获得QQ群/微信群/钉钉企业群等信息的同学,请使用微信扫描上面的微信公众号二维码关注 `WxJava` 后点击相关菜单即可获取加入方式,同时也可以在微信中搜索 `weixin-java-tools` 或 `WxJava` 后选择正确的公众号进行关注,该公众号会及时通知SDK相关更新信息,并不定期分享微信Java开发相关技术知识; +6. 钉钉技术交流群:`32206329`(技术交流2群), `30294972`(技术交流1群,目前已满),`35724728`(通知群,实时通知Github项目变更记录)。 +7. 微信开发新手或者Java开发新手在群内提问或新开Issue提问前,请先阅读[【提问的智慧】](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/master/README-zh_CN.md),并确保已查阅过 [【开发文档Wiki】](https://github.com/wechat-group/WxJava/wiki) ,避免浪费大家的宝贵时间; +8. 寻求帮助时需贴代码或大长串异常信息的,请利用 http://paste.ubuntu.com -------------------------------- ### 其他说明 @@ -67,7 +79,7 @@ com.github.binarywang (不同模块参考下文) - 4.1.0 + 4.2.0 ``` diff --git a/images/banners/ccflow.png b/images/banners/ccflow.png new file mode 100644 index 000000000..0b7d2b424 Binary files /dev/null and b/images/banners/ccflow.png differ diff --git a/images/banners/diboot.png b/images/banners/diboot.png new file mode 100644 index 000000000..74b28b4fe Binary files /dev/null and b/images/banners/diboot.png differ diff --git a/others/weixin-java-osgi/pom.xml b/others/weixin-java-osgi/pom.xml index 7207e9b99..0018b73e5 100644 --- a/others/weixin-java-osgi/pom.xml +++ b/others/weixin-java-osgi/pom.xml @@ -28,7 +28,7 @@ com.thoughtworks.xstream xstream - 1.4.18 + 1.4.19 provided diff --git a/pom.xml b/pom.xml index f2e64f533..899d90d37 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.github.binarywang wx-java - 4.2.0 + 4.3.0 pom WxJava - Weixin/Wechat Java SDK 微信开发Java SDK @@ -175,7 +175,7 @@ com.thoughtworks.xstream xstream - 1.4.18 + 1.4.19 com.google.guava @@ -187,6 +187,11 @@ gson 2.8.0 + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + 2.13.0 + @@ -212,6 +217,16 @@ testng 7.1.0 test + + + guice + com.google.inject + + + org.yaml + snakeyaml + + org.mockito @@ -262,6 +277,20 @@ 3.12.0 true provided + + + com.fasterxml.jackson.core + jackson-core + + + org.jodd + jodd-core + + + org.reactivestreams + reactive-streams + + org.springframework.data diff --git a/spring-boot-starters/pom.xml b/spring-boot-starters/pom.xml index c6fbf375f..36e1ac5d0 100644 --- a/spring-boot-starters/pom.xml +++ b/spring-boot-starters/pom.xml @@ -4,7 +4,7 @@ com.github.binarywang wx-java - 4.2.0 + 4.3.0 pom wx-java-spring-boot-starters @@ -21,6 +21,7 @@ wx-java-pay-spring-boot-starter wx-java-open-spring-boot-starter wx-java-qidian-spring-boot-starter + wx-java-cp-spring-boot-starter diff --git a/spring-boot-starters/wx-java-cp-spring-boot-starter/README.md b/spring-boot-starters/wx-java-cp-spring-boot-starter/README.md new file mode 100644 index 000000000..439ee5c72 --- /dev/null +++ b/spring-boot-starters/wx-java-cp-spring-boot-starter/README.md @@ -0,0 +1,39 @@ +# wx-java-cp-spring-boot-starter + +## 快速开始 + +1. 引入依赖 + ```xml + + com.github.binarywang + wx-java-cp-spring-boot-starter + ${version} + + ``` +2. 添加配置(application.properties) + ```properties + # 企业微信号配置(必填) + wx.cp.corp-id = @corp-id + wx.cp.corp-secret = @corp-secret + # 选填 + wx.cp.token = @token + wx.cp.aes-key = @aes-key + wx.cp.agent-id = @agent-id + # ConfigStorage 配置(选填) + wx.cp.config-storage.type=memory # memory 默认,目前只支持 memory 类型,可以自行扩展 redis 等类型 + # http 客户端配置(选填) + wx.cp.config-storage.http-proxy-host= + wx.cp.config-storage.http-proxy-port= + wx.cp.config-storage.http-proxy-username= + wx.cp.config-storage.http-proxy-password= + # 最大重试次数,默认:5 次,如果小于 0,则为 0 + wx.cp.config-storage.max-retry-times=5 + # 重试时间间隔步进,默认:1000 毫秒,如果小于 0,则为 1000 + wx.cp.config-storage.retry-sleep-millis=1000 + ``` +3. 支持自动注入的类型: `WxCpService`, `WxCpConfigStorage` + +4. 覆盖自动配置: 自定义注入的bean会覆盖自动注入的 + +- WxCpService +- WxCpConfigStorage diff --git a/spring-boot-starters/wx-java-cp-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-cp-spring-boot-starter/pom.xml new file mode 100644 index 000000000..4d3776171 --- /dev/null +++ b/spring-boot-starters/wx-java-cp-spring-boot-starter/pom.xml @@ -0,0 +1,45 @@ + + + + wx-java-spring-boot-starters + com.github.binarywang + 4.3.0 + + 4.0.0 + + wx-java-cp-spring-boot-starter + WxJava - Spring Boot Starter for WxCp + 微信企业号开发的 Spring Boot Starter + + + + com.github.binarywang + weixin-java-cp + ${project.version} + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.version} + + + org.apache.maven.plugins + maven-source-plugin + 2.2.1 + + + attach-sources + + jar-no-fork + + + + + + + diff --git a/spring-boot-starters/wx-java-cp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/config/WxCpAutoConfiguration.java b/spring-boot-starters/wx-java-cp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/config/WxCpAutoConfiguration.java new file mode 100644 index 000000000..194cf5c40 --- /dev/null +++ b/spring-boot-starters/wx-java-cp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/config/WxCpAutoConfiguration.java @@ -0,0 +1,21 @@ +package com.binarywang.spring.starter.wxjava.cp.config; + +import com.binarywang.spring.starter.wxjava.cp.properties.WxCpProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +/** + * 企业微信自动注册 + * + * @author yl + * @date 2021/12/6 + */ +@Configuration +@EnableConfigurationProperties(WxCpProperties.class) +@Import({ + WxCpStorageAutoConfiguration.class, + WxCpServiceAutoConfiguration.class +}) +public class WxCpAutoConfiguration { +} diff --git a/spring-boot-starters/wx-java-cp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/config/WxCpServiceAutoConfiguration.java b/spring-boot-starters/wx-java-cp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/config/WxCpServiceAutoConfiguration.java new file mode 100644 index 000000000..0e1db87a3 --- /dev/null +++ b/spring-boot-starters/wx-java-cp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/config/WxCpServiceAutoConfiguration.java @@ -0,0 +1,44 @@ +package com.binarywang.spring.starter.wxjava.cp.config; + +import com.binarywang.spring.starter.wxjava.cp.properties.WxCpProperties; +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl; +import me.chanjar.weixin.cp.config.WxCpConfigStorage; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * 企业微信平台相关服务自动注册 + * + * @author yl + * @date 2021/12/6 + */ +@Configuration +@RequiredArgsConstructor +public class WxCpServiceAutoConfiguration { + private final WxCpProperties wxCpProperties; + + @Bean + @ConditionalOnMissingBean + @ConditionalOnBean(WxCpConfigStorage.class) + public WxCpService wxCpService(WxCpConfigStorage wxCpConfigStorage) { + WxCpService wxCpService = new WxCpServiceImpl(); + wxCpService.setWxCpConfigStorage(wxCpConfigStorage); + + WxCpProperties.ConfigStorage storage = wxCpProperties.getConfigStorage(); + int maxRetryTimes = storage.getMaxRetryTimes(); + if (maxRetryTimes < 0) { + maxRetryTimes = 0; + } + int retrySleepMillis = storage.getRetrySleepMillis(); + if (retrySleepMillis < 0) { + retrySleepMillis = 1000; + } + wxCpService.setRetrySleepMillis(retrySleepMillis); + wxCpService.setMaxRetryTimes(maxRetryTimes); + return wxCpService; + } +} diff --git a/spring-boot-starters/wx-java-cp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/config/WxCpStorageAutoConfiguration.java b/spring-boot-starters/wx-java-cp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/config/WxCpStorageAutoConfiguration.java new file mode 100644 index 000000000..5092b3b34 --- /dev/null +++ b/spring-boot-starters/wx-java-cp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/config/WxCpStorageAutoConfiguration.java @@ -0,0 +1,18 @@ +package com.binarywang.spring.starter.wxjava.cp.config; + +import com.binarywang.spring.starter.wxjava.cp.storage.WxCpInMemoryConfigStorageConfiguration; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +/** + * 企业微信存储策略自动配置 + * + * @author yl + * @date 2021/12/6 + */ +@Configuration +@Import({ + WxCpInMemoryConfigStorageConfiguration.class +}) +public class WxCpStorageAutoConfiguration { +} diff --git a/spring-boot-starters/wx-java-cp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/properties/WxCpProperties.java b/spring-boot-starters/wx-java-cp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/properties/WxCpProperties.java new file mode 100644 index 000000000..b2cc778ac --- /dev/null +++ b/spring-boot-starters/wx-java-cp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/properties/WxCpProperties.java @@ -0,0 +1,105 @@ +package com.binarywang.spring.starter.wxjava.cp.properties; + +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.boot.context.properties.ConfigurationProperties; + +import java.io.Serializable; + +/** + * 企业微信接入相关配置属性 + * + * @author yl + * @date 2021/12/6 + */ +@Data +@NoArgsConstructor +@ConfigurationProperties(prefix = WxCpProperties.PREFIX) +public class WxCpProperties { + public static final String PREFIX = "wx.cp"; + + /** + * 微信企业号 corpId + */ + private String corpId; + /** + * 微信企业号 corpSecret + */ + private String corpSecret; + /** + * 微信企业号应用 token + */ + private String token; + /** + * 微信企业号应用 ID + */ + private Integer agentId; + /** + * 微信企业号应用 EncodingAESKey + */ + private String aesKey; + /** + * 微信企业号应用 会话存档类库路径 + */ + private String msgAuditLibPath; + + /** + * 配置存储策略,默认内存 + */ + private ConfigStorage configStorage = new ConfigStorage(); + + @Data + @NoArgsConstructor + public static class ConfigStorage implements Serializable { + private static final long serialVersionUID = 4815731027000065434L; + /** + * 存储类型 + */ + private StorageType type = StorageType.memory; + + /** + * http代理主机 + */ + private String httpProxyHost; + + /** + * http代理端口 + */ + private Integer httpProxyPort; + + /** + * http代理用户名 + */ + private String httpProxyUsername; + + /** + * http代理密码 + */ + private String httpProxyPassword; + + /** + * http 请求最大重试次数 + *
+     *   {@link me.chanjar.weixin.cp.api.WxCpService#setMaxRetryTimes(int)}
+     *   {@link me.chanjar.weixin.cp.api.impl.BaseWxCpServiceImpl#setMaxRetryTimes(int)}
+     * 
+ */ + private int maxRetryTimes = 5; + + /** + * http 请求重试间隔 + *
+     *   {@link me.chanjar.weixin.cp.api.WxCpService#setRetrySleepMillis(int)}
+     *   {@link me.chanjar.weixin.cp.api.impl.BaseWxCpServiceImpl#setRetrySleepMillis(int)}
+     * 
+ */ + private int retrySleepMillis = 1000; + } + + public enum StorageType { + /** + * 内存 + */ + memory + } +} diff --git a/spring-boot-starters/wx-java-cp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/storage/AbstractWxCpConfigStorageConfiguration.java b/spring-boot-starters/wx-java-cp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/storage/AbstractWxCpConfigStorageConfiguration.java new file mode 100644 index 000000000..cfcb16fe0 --- /dev/null +++ b/spring-boot-starters/wx-java-cp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/storage/AbstractWxCpConfigStorageConfiguration.java @@ -0,0 +1,59 @@ +package com.binarywang.spring.starter.wxjava.cp.storage; + +import com.binarywang.spring.starter.wxjava.cp.properties.WxCpProperties; +import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl; +import org.apache.commons.lang3.StringUtils; + +/** + * WxCpConfigStorage 抽象配置类 + * + * @author yl & Wang_Wong + * @date 2021/12/6 + */ +public abstract class AbstractWxCpConfigStorageConfiguration { + + protected WxCpDefaultConfigImpl config(WxCpDefaultConfigImpl config, WxCpProperties properties) { + String corpId = properties.getCorpId(); + String corpSecret = properties.getCorpSecret(); + String token = properties.getToken(); + Integer agentId = properties.getAgentId(); + String aesKey = properties.getAesKey(); + // 企业微信,会话存档路径 + String msgAuditLibPath = properties.getMsgAuditLibPath(); + + config.setCorpId(corpId); + config.setCorpSecret(corpSecret); + if (StringUtils.isNotBlank(token)) { + config.setToken(token); + } + if (agentId != null) { + config.setAgentId(agentId); + } + if (StringUtils.isNotBlank(aesKey)) { + config.setAesKey(aesKey); + } + if (StringUtils.isNotBlank(msgAuditLibPath)) { + config.setMsgAuditLibPath(msgAuditLibPath); + } + + WxCpProperties.ConfigStorage storage = properties.getConfigStorage(); + String httpProxyHost = storage.getHttpProxyHost(); + Integer httpProxyPort = storage.getHttpProxyPort(); + String httpProxyUsername = storage.getHttpProxyUsername(); + String httpProxyPassword = storage.getHttpProxyPassword(); + if (StringUtils.isNotBlank(httpProxyHost)) { + config.setHttpProxyHost(httpProxyHost); + if (httpProxyPort != null) { + config.setHttpProxyPort(httpProxyPort); + } + if (StringUtils.isNotBlank(httpProxyUsername)) { + config.setHttpProxyUsername(httpProxyUsername); + } + if (StringUtils.isNotBlank(httpProxyPassword)) { + config.setHttpProxyPassword(httpProxyPassword); + } + } + return config; + } + +} diff --git a/spring-boot-starters/wx-java-cp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/storage/WxCpInMemoryConfigStorageConfiguration.java b/spring-boot-starters/wx-java-cp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/storage/WxCpInMemoryConfigStorageConfiguration.java new file mode 100644 index 000000000..e713e4394 --- /dev/null +++ b/spring-boot-starters/wx-java-cp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/cp/storage/WxCpInMemoryConfigStorageConfiguration.java @@ -0,0 +1,33 @@ +package com.binarywang.spring.starter.wxjava.cp.storage; + +import com.binarywang.spring.starter.wxjava.cp.properties.WxCpProperties; +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.cp.config.WxCpConfigStorage; +import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * 自动装配基于内存策略配置 + * + * @author yl + * @date 2021/12/6 + */ +@Configuration +@ConditionalOnProperty( + prefix = WxCpProperties.PREFIX + ".config-storage", name = "type", + matchIfMissing = true, havingValue = "memory" +) +@RequiredArgsConstructor +public class WxCpInMemoryConfigStorageConfiguration extends AbstractWxCpConfigStorageConfiguration { + private final WxCpProperties wxCpProperties; + + @Bean + @ConditionalOnMissingBean(WxCpConfigStorage.class) + public WxCpConfigStorage wxCpConfigStorage() { + WxCpDefaultConfigImpl config = new WxCpDefaultConfigImpl(); + return this.config(config, wxCpProperties); + } +} diff --git a/spring-boot-starters/wx-java-cp-spring-boot-starter/src/main/resources/META-INF/spring.factories b/spring-boot-starters/wx-java-cp-spring-boot-starter/src/main/resources/META-INF/spring.factories new file mode 100644 index 000000000..c2ef7f635 --- /dev/null +++ b/spring-boot-starters/wx-java-cp-spring-boot-starter/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +com.binarywang.spring.starter.wxjava.cp.config.WxCpAutoConfiguration diff --git a/spring-boot-starters/wx-java-miniapp-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-miniapp-spring-boot-starter/pom.xml index cd6e6da4b..43d3c7b45 100644 --- a/spring-boot-starters/wx-java-miniapp-spring-boot-starter/pom.xml +++ b/spring-boot-starters/wx-java-miniapp-spring-boot-starter/pom.xml @@ -5,7 +5,7 @@ wx-java-spring-boot-starters com.github.binarywang - 4.2.0 + 4.3.0 4.0.0 diff --git a/spring-boot-starters/wx-java-miniapp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/enums/StorageType.java b/spring-boot-starters/wx-java-miniapp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/enums/StorageType.java index 9328980bb..bf9fd6b17 100644 --- a/spring-boot-starters/wx-java-miniapp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/enums/StorageType.java +++ b/spring-boot-starters/wx-java-miniapp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/enums/StorageType.java @@ -15,6 +15,10 @@ public enum StorageType { * redis(JedisClient). */ Jedis, + /** + * redis(Redisson). + */ + Redisson, /** * redis(RedisTemplate). */ diff --git a/spring-boot-starters/wx-java-mp-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-mp-spring-boot-starter/pom.xml index aceef96ca..62f3628c6 100644 --- a/spring-boot-starters/wx-java-mp-spring-boot-starter/pom.xml +++ b/spring-boot-starters/wx-java-mp-spring-boot-starter/pom.xml @@ -5,7 +5,7 @@ wx-java-spring-boot-starters com.github.binarywang - 4.2.0 + 4.3.0 4.0.0 diff --git a/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/WxMpStorageAutoConfiguration.java b/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/WxMpStorageAutoConfiguration.java index 145c663ef..cf3c48656 100644 --- a/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/WxMpStorageAutoConfiguration.java +++ b/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/WxMpStorageAutoConfiguration.java @@ -30,7 +30,7 @@ /** * 微信公众号存储策略自动配置. * - * @author someone + * @author Luo */ @Slf4j @Configuration @@ -40,12 +40,6 @@ public class WxMpStorageAutoConfiguration { private final WxMpProperties wxMpProperties; - @Value("${wx.mp.config-storage.redis.host:") - private String redisHost; - - @Value("${wx.mp.configStorage.redis.host:") - private String redisHost2; - @Bean @ConditionalOnMissingBean(WxMpConfigStorage.class) public WxMpConfigStorage wxMpConfigStorage() { @@ -81,14 +75,15 @@ private WxMpConfigStorage defaultConfigStorage() { private WxMpConfigStorage jedisConfigStorage() { JedisPoolAbstract jedisPool; - if (StringUtils.isNotEmpty(redisHost) || StringUtils.isNotEmpty(redisHost2)) { + if (wxMpProperties.getConfigStorage() != null && wxMpProperties.getConfigStorage().getRedis() != null + && StringUtils.isNotEmpty(wxMpProperties.getConfigStorage().getRedis().getHost())) { jedisPool = getJedisPool(); } else { jedisPool = applicationContext.getBean(JedisPool.class); } WxRedisOps redisOps = new JedisWxRedisOps(jedisPool); WxMpRedisConfigImpl wxMpRedisConfig = new WxMpRedisConfigImpl(redisOps, - wxMpProperties.getConfigStorage().getKeyPrefix()); + wxMpProperties.getConfigStorage().getKeyPrefix()); setWxMpInfo(wxMpRedisConfig); return wxMpRedisConfig; } @@ -114,7 +109,7 @@ private WxMpConfigStorage redisTemplateConfigStorage() { WxRedisOps redisOps = new RedisTemplateWxRedisOps(redisTemplate); WxMpRedisConfigImpl wxMpRedisConfig = new WxMpRedisConfigImpl(redisOps, - wxMpProperties.getConfigStorage().getKeyPrefix()); + wxMpProperties.getConfigStorage().getKeyPrefix()); setWxMpInfo(wxMpRedisConfig); return wxMpRedisConfig; @@ -160,6 +155,6 @@ private JedisPoolAbstract getJedisPool() { } return new JedisPool(config, redis.getHost(), redis.getPort(), redis.getTimeout(), redis.getPassword(), - redis.getDatabase()); + redis.getDatabase()); } } diff --git a/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/enums/StorageType.java b/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/enums/StorageType.java index 7dcb5a115..4bf4b0789 100644 --- a/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/enums/StorageType.java +++ b/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/enums/StorageType.java @@ -15,6 +15,10 @@ public enum StorageType { * redis(JedisClient). */ Jedis, + /** + * redis(Redisson). + */ + Redisson, /** * redis(RedisTemplate). */ diff --git a/spring-boot-starters/wx-java-open-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-open-spring-boot-starter/pom.xml index 78fc9bdc4..dd55913b4 100644 --- a/spring-boot-starters/wx-java-open-spring-boot-starter/pom.xml +++ b/spring-boot-starters/wx-java-open-spring-boot-starter/pom.xml @@ -5,7 +5,7 @@ wx-java-spring-boot-starters com.github.binarywang - 4.2.0 + 4.3.0 4.0.0 diff --git a/spring-boot-starters/wx-java-open-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/config/storage/AbstractWxOpenConfigStorageConfiguration.java b/spring-boot-starters/wx-java-open-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/config/storage/AbstractWxOpenConfigStorageConfiguration.java index 0f77633e4..ee0443c9a 100644 --- a/spring-boot-starters/wx-java-open-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/config/storage/AbstractWxOpenConfigStorageConfiguration.java +++ b/spring-boot-starters/wx-java-open-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/config/storage/AbstractWxOpenConfigStorageConfiguration.java @@ -9,19 +9,20 @@ public abstract class AbstractWxOpenConfigStorageConfiguration { protected WxOpenInMemoryConfigStorage config(WxOpenInMemoryConfigStorage config, WxOpenProperties properties) { - WxOpenProperties.ConfigStorage configStorageProperties = properties.getConfigStorage(); + WxOpenProperties.ConfigStorage storage = properties.getConfigStorage(); config.setWxOpenInfo(properties.getAppId(), properties.getSecret(), properties.getToken(), properties.getAesKey()); - config.setHttpProxyHost(configStorageProperties.getHttpProxyHost()); - config.setHttpProxyUsername(configStorageProperties.getHttpProxyUsername()); - config.setHttpProxyPassword(configStorageProperties.getHttpProxyPassword()); - if (configStorageProperties.getHttpProxyPort() != null) { - config.setHttpProxyPort(configStorageProperties.getHttpProxyPort()); + config.setHttpProxyHost(storage.getHttpProxyHost()); + config.setHttpProxyUsername(storage.getHttpProxyUsername()); + config.setHttpProxyPassword(storage.getHttpProxyPassword()); + Integer httpProxyPort = storage.getHttpProxyPort(); + if (httpProxyPort != null) { + config.setHttpProxyPort(httpProxyPort); } - int maxRetryTimes = configStorageProperties.getMaxRetryTimes(); - if (configStorageProperties.getMaxRetryTimes() < 0) { + int maxRetryTimes = storage.getMaxRetryTimes(); + if (maxRetryTimes < 0) { maxRetryTimes = 0; } - int retrySleepMillis = configStorageProperties.getRetrySleepMillis(); + int retrySleepMillis = storage.getRetrySleepMillis(); if (retrySleepMillis < 0) { retrySleepMillis = 1000; } diff --git a/spring-boot-starters/wx-java-pay-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-pay-spring-boot-starter/pom.xml index 588f4b8f5..bb22aff45 100644 --- a/spring-boot-starters/wx-java-pay-spring-boot-starter/pom.xml +++ b/spring-boot-starters/wx-java-pay-spring-boot-starter/pom.xml @@ -5,7 +5,7 @@ wx-java-spring-boot-starters com.github.binarywang - 4.2.0 + 4.3.0 4.0.0 diff --git a/spring-boot-starters/wx-java-qidian-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-qidian-spring-boot-starter/pom.xml index 63cf2f2a5..51f64628b 100644 --- a/spring-boot-starters/wx-java-qidian-spring-boot-starter/pom.xml +++ b/spring-boot-starters/wx-java-qidian-spring-boot-starter/pom.xml @@ -3,7 +3,7 @@ wx-java-spring-boot-starters com.github.binarywang - 4.2.0 + 4.3.0 4.0.0 diff --git a/spring-boot-starters/wx-java-qidian-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/qidian/enums/StorageType.java b/spring-boot-starters/wx-java-qidian-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/qidian/enums/StorageType.java index 0a7a6b85d..e6ae0cab4 100644 --- a/spring-boot-starters/wx-java-qidian-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/qidian/enums/StorageType.java +++ b/spring-boot-starters/wx-java-qidian-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/qidian/enums/StorageType.java @@ -15,6 +15,10 @@ public enum StorageType { * redis(JedisClient). */ Jedis, + /** + * redis(Redisson). + */ + Redisson, /** * redis(RedisTemplate). */ diff --git a/weixin-graal/pom.xml b/weixin-graal/pom.xml index 7b5d9ad66..1673ba243 100644 --- a/weixin-graal/pom.xml +++ b/weixin-graal/pom.xml @@ -6,7 +6,7 @@ com.github.binarywang wx-java - 4.2.0 + 4.3.0 weixin-graal diff --git a/weixin-java-common/pom.xml b/weixin-java-common/pom.xml index c5b0918c9..fc389d484 100644 --- a/weixin-java-common/pom.xml +++ b/weixin-java-common/pom.xml @@ -6,7 +6,7 @@ com.github.binarywang wx-java - 4.2.0 + 4.3.0 weixin-java-common @@ -117,6 +117,12 @@ org.dom4j dom4j 2.1.3 + + + pull-parser + pull-parser + +
redis.clients diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java index 01b1d3648..35a201edb 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java @@ -126,6 +126,11 @@ public static class KefuMsgType { * 模板卡片消息. */ public static final String TEMPLATE_CARD = "template_card"; + + /** + * 发送图文消息(点击跳转到图文消息页面)使用通过 “发布” 系列接口得到的 article_id(草稿箱功能上线后不再支持客服接口中带 media_id 的 mpnews 类型的图文消息) + */ + public static final String MP_NEWS_ARTICLE = "mpnewsarticle"; } /** @@ -340,6 +345,11 @@ public static class EventType { */ public static final String WEAPP_AUDIT_FAIL = "weapp_audit_fail"; + /** + * 点击菜单跳转小程序的事件推送 + */ + public static final String VIEW_MINIPROGRAM = "view_miniprogram"; + } /** diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/WxOAuth2UserInfo.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/WxOAuth2UserInfo.java index 69518b256..e64756002 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/WxOAuth2UserInfo.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/WxOAuth2UserInfo.java @@ -25,6 +25,23 @@ public class WxOAuth2UserInfo implements Serializable { * nickname 普通用户昵称 */ private String nickname; + /** + * sex 普通用户性别,1为男性,2为女性 + */ + private Integer sex; + /** + * city 普通用户个人资料填写的城市 + */ + private String city; + + /** + * province 普通用户个人资料填写的省份 + */ + private String province; + /** + * country 国家,如中国为CN + */ + private String country; /** * headimgurl 用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像), * 用户没有头像时该项为空 diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/menu/WxMenuButton.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/menu/WxMenuButton.java index 114e267d4..344f544ba 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/menu/WxMenuButton.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/menu/WxMenuButton.java @@ -58,6 +58,15 @@ public class WxMenuButton implements Serializable { @SerializedName("media_id") private String mediaId; + /** + *
+   * 调用发布图文接口获得的article_id.
+   * article_id类型和article_view_limited类型必须
+   * 
+ */ + @SerializedName("article_id") + private String articleId; + /** *
    * 小程序的appid.
diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxCpErrorMsgEnum.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxCpErrorMsgEnum.java
index c742959bb..61b863bf1 100644
--- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxCpErrorMsgEnum.java
+++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxCpErrorMsgEnum.java
@@ -1,7 +1,10 @@
 package me.chanjar.weixin.common.error;
 
+import com.google.common.collect.Maps;
 import lombok.Getter;
 
+import java.util.Map;
+
 /**
  * 
  * 企业微信全局错误码.
@@ -1072,7 +1075,7 @@ public enum WxCpErrorMsgEnum {
   /**
    * 提交审批单请求参数错误
    */
-  CODE_301025(301025,"提交审批单请求参数错误"),
+  CODE_301025(301025, "提交审批单请求参数错误"),
   /**
    * 不允许更新该用户的userid.
    */
@@ -1080,15 +1083,15 @@ public enum WxCpErrorMsgEnum {
   /**
    * 无审批应用权限,或者提单者不在审批应用/自建应用的可见范围
    */
-  CODE_301055(301055,"无审批应用权限,或者提单者不在审批应用/自建应用的可见范围"),
+  CODE_301055(301055, "无审批应用权限,或者提单者不在审批应用/自建应用的可见范围"),
   /**
    * 审批应用已停用
    */
-  CODE_301056(301056,"审批应用已停用"),
+  CODE_301056(301056, "审批应用已停用"),
   /**
    * 通用错误码,提交审批单内部接口失败
    */
-  CODE_301057(301057,"通用错误码,提交审批单内部接口失败"),
+  CODE_301057(301057, "通用错误码,提交审批单内部接口失败"),
   /**
    * 批量导入任务的文件中userid有重复.
    */
@@ -1114,24 +1117,26 @@ public enum WxCpErrorMsgEnum {
    */
   CODE_2000002(2000002, "CorpId参数无效;指定的CorpId不存在");
 
-  private int code;
-  private String msg;
+  private final int code;
+  private final String msg;
 
   WxCpErrorMsgEnum(int code, String msg) {
     this.code = code;
     this.msg = msg;
   }
 
+  static final Map valueMap = Maps.newHashMap();
+
+  static {
+    for (WxCpErrorMsgEnum value : WxCpErrorMsgEnum.values()) {
+      valueMap.put(value.code, value.msg);
+    }
+  }
+
   /**
    * 通过错误代码查找其中文含义..
    */
   public static String findMsgByCode(int code) {
-    for (WxCpErrorMsgEnum value : WxCpErrorMsgEnum.values()) {
-      if (value.code == code) {
-        return value.msg;
-      }
-    }
-
-    return null;
+    return valueMap.getOrDefault(code, null);
   }
 }
diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxError.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxError.java
index f1be84382..1156636ea 100644
--- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxError.java
+++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxError.java
@@ -1,7 +1,9 @@
 package me.chanjar.weixin.common.error;
 
+import lombok.AllArgsConstructor;
 import lombok.Builder;
 import lombok.Data;
+import lombok.NoArgsConstructor;
 import me.chanjar.weixin.common.enums.WxType;
 import me.chanjar.weixin.common.util.json.WxGsonBuilder;
 import org.apache.commons.lang3.StringUtils;
@@ -11,12 +13,14 @@
 /**
  * 微信错误码.
  * 请阅读:
- * 公众平台:全局返回码说明
+ * 公众平台:全局返回码说明
  * 企业微信:全局错误码
  *
  * @author Daniel Qian & Binary Wang
  */
 @Data
+@NoArgsConstructor
+@AllArgsConstructor
 @Builder
 public class WxError implements Serializable {
   private static final long serialVersionUID = 7869786563361406291L;
@@ -39,6 +43,11 @@ public class WxError implements Serializable {
 
   private String json;
 
+  public WxError(int errorCode, String errorMsg) {
+    this.errorCode = errorCode;
+    this.errorMsg = errorMsg;
+  }
+
   public static WxError fromJson(String json) {
     return fromJson(json, null);
   }
@@ -75,6 +84,13 @@ public static WxError fromJson(String json, WxType type) {
         }
         break;
       }
+      case Open: {
+        final String msg = WxOpenErrorMsgEnum.findMsgByCode(wxError.getErrorCode());
+        if (msg != null) {
+          wxError.setErrorMsg(msg);
+        }
+        break;
+      }
       default:
         return wxError;
     }
diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxMaErrorMsgEnum.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxMaErrorMsgEnum.java
index 2a9fe0184..10cbe5436 100644
--- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxMaErrorMsgEnum.java
+++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxMaErrorMsgEnum.java
@@ -1,7 +1,10 @@
 package me.chanjar.weixin.common.error;
 
+import com.google.common.collect.Maps;
 import lombok.Getter;
 
+import java.util.Map;
+
 /**
  * 微信小程序错误码
  *
@@ -409,7 +412,7 @@ public enum WxMaErrorMsgEnum {
 
   CODE_85012(85012, "无效的审核 id"),
 
-  CODE_87013(87013, "撤回次数达到上限(每天一次,每个月 10 次)"),
+  CODE_87013(87013, "撤回次数达到上限(每天5次,每个月 10 次)"),
 
   CODE_85019(85019, "没有审核版本"),
 
@@ -664,16 +667,18 @@ public enum WxMaErrorMsgEnum {
     this.msg = msg;
   }
 
+  static final Map valueMap = Maps.newHashMap();
+
+  static {
+    for (WxMaErrorMsgEnum value : WxMaErrorMsgEnum.values()) {
+      valueMap.put(value.code, value.msg);
+    }
+  }
+
   /**
    * 通过错误代码查找其中文含义.
    */
   public static String findMsgByCode(int code) {
-    for (WxMaErrorMsgEnum value : WxMaErrorMsgEnum.values()) {
-      if (value.code == code) {
-        return value.msg;
-      }
-    }
-
-    return null;
+    return valueMap.getOrDefault(code, null);
   }
 }
diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxMpErrorMsgEnum.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxMpErrorMsgEnum.java
index 58dc4f345..fdfb397cb 100644
--- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxMpErrorMsgEnum.java
+++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxMpErrorMsgEnum.java
@@ -1,11 +1,14 @@
 package me.chanjar.weixin.common.error;
 
+import com.google.common.collect.Maps;
 import lombok.Getter;
 
+import java.util.Map;
+
 /**
  * 
  * 微信公众平台全局返回码.
- * 参考文档:公众平台全局返回码
+ * 参考文档:公众平台全局返回码
  * Created by Binary Wang on 2018/5/13.
  * 
* @@ -373,6 +376,18 @@ public enum WxMpErrorMsgEnum { * 非法的tag_id. */ CODE_45159(45159, "非法的tag_id"), + /** + * 相同 clientmsgid 已存在群发记录,返回数据中带有已存在的群发任务的 msgid + */ + CODE_45065(45065, "相同 clientmsgid 已存在群发记录,返回数据中带有已存在的群发任务的 msgid"), + /** + * 相同 clientmsgid 重试速度过快,请间隔1分钟重试 + */ + CODE_45066(45066, "相同 clientmsgid 重试速度过快,请间隔1分钟重试"), + /** + * clientmsgid 长度超过限制 + */ + CODE_45067(45067, "clientmsgid 长度超过限制"), /** * 不存在媒体数据. */ @@ -648,24 +663,26 @@ public enum WxMpErrorMsgEnum { */ CODE_45084(45084, "没有设置 speed 参数"); - private int code; - private String msg; + private final int code; + private final String msg; WxMpErrorMsgEnum(int code, String msg) { this.code = code; this.msg = msg; } + static final Map valueMap = Maps.newHashMap(); + + static { + for (WxMpErrorMsgEnum value : WxMpErrorMsgEnum.values()) { + valueMap.put(value.code, value.msg); + } + } + /** * 通过错误代码查找其中文含义.. */ public static String findMsgByCode(int code) { - for (WxMpErrorMsgEnum value : WxMpErrorMsgEnum.values()) { - if (value.code == code) { - return value.msg; - } - } - - return null; + return valueMap.getOrDefault(code, null); } } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxOpenErrorMsgEnum.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxOpenErrorMsgEnum.java new file mode 100644 index 000000000..edcc0a28d --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxOpenErrorMsgEnum.java @@ -0,0 +1,9202 @@ +package me.chanjar.weixin.common.error; + +import com.google.common.collect.Maps; +import lombok.Getter; + +import java.util.Map; + +/** + *
+ *     微信开放平台全局返回码.
+ *     参考文档:开放平台全局返回码
+ * 
+ * + * @author Lam Jerry + */ + +@Getter +public enum WxOpenErrorMsgEnum { + /** + * 系统繁忙,此时请开发者稍候再试 system error + */ + CODE_1(-1, "系统繁忙,此时请开发者稍候再试"), + + /** + * 请求成功 ok + */ + CODE_0(0, "请求成功"), + + /** + * POST参数非法 + */ + CODE_1003(1003, "POST参数非法"), + + /** + * 商品id不存在 + */ + CODE_20002(20002, "商品id不存在"), + + /** + * 获取 access_token 时 AppSecret 错误,或者 access_token 无效。请开发者认真比对 AppSecret 的正确性,或查看是否正在为恰当的公众号调用接口 invalid credential, access_token is invalid or not latest + */ + CODE_40001(40001, "获取 access_token 时 AppSecret 错误,或者 access_token 无效。请开发者认真比对 AppSecret 的正确性,或查看是否正在为恰当的公众号调用接口"), + + /** + * 不合法的凭证类型 invalid grant_type + */ + CODE_40002(40002, "不合法的凭证类型"), + + /** + * 不合法的 OpenID ,请开发者确认 OpenID (该用户)是否已关注公众号,或是否是其他公众号的 OpenID invalid openid + */ + CODE_40003(40003, "不合法的 OpenID ,请开发者确认 OpenID (该用户)是否已关注公众号,或是否是其他公众号的 OpenID"), + + /** + * 不合法的媒体文件类型 invalid media type + */ + CODE_40004(40004, "不合法的媒体文件类型"), + + /** + * 上传素材文件格式不对 invalid file type + */ + CODE_40005(40005, "上传素材文件格式不对"), + + /** + * 上传素材文件大小超出限制 invalid meida size + */ + CODE_40006(40006, "上传素材文件大小超出限制"), + + /** + * 不合法的媒体文件 id invalid media_id + */ + CODE_40007(40007, "不合法的媒体文件 id"), + + /** + * 不合法的消息类型 invalid message type + */ + CODE_40008(40008, "不合法的消息类型"), + + /** + * 图片尺寸太大 invalid image size + */ + CODE_40009(40009, "图片尺寸太大"), + + /** + * 不合法的语音文件大小 invalid voice size + */ + CODE_40010(40010, "不合法的语音文件大小"), + + /** + * 不合法的视频文件大小 invalid video size + */ + CODE_40011(40011, "不合法的视频文件大小"), + + /** + * 不合法的缩略图文件大小 invalid thumb size + */ + CODE_40012(40012, "不合法的缩略图文件大小"), + + /** + * 不合法的appid invalid appid + */ + CODE_40013(40013, "不合法的appid"), + + /** + * 不合法的 access_token ,请开发者认真比对 access_token 的有效性(如是否过期),或查看是否正在为恰当的公众号调用接口 invalid access_token + */ + CODE_40014(40014, "不合法的 access_token ,请开发者认真比对 access_token 的有效性(如是否过期),或查看是否正在为恰当的公众号调用接口"), + + /** + * 不合法的菜单类型 invalid menu type + */ + CODE_40015(40015, "不合法的菜单类型"), + + /** + * 不合法的按钮个数 invalid button size + */ + CODE_40016(40016, "不合法的按钮个数"), + + /** + * 不合法的按钮类型 invalid button type + */ + CODE_40017(40017, "不合法的按钮类型"), + + /** + * 不合法的按钮名字长度 invalid button name size + */ + CODE_40018(40018, "不合法的按钮名字长度"), + + /** + * 不合法的按钮 KEY 长度 invalid button key size + */ + CODE_40019(40019, "不合法的按钮 KEY 长度"), + + /** + * 不合法的按钮 URL 长度 invalid button url size + */ + CODE_40020(40020, "不合法的按钮 URL 长度"), + + /** + * 不合法的菜单版本号 invalid menu version + */ + CODE_40021(40021, "不合法的菜单版本号"), + + /** + * 不合法的子菜单级数 invalid sub_menu level + */ + CODE_40022(40022, "不合法的子菜单级数"), + + /** + * 不合法的子菜单按钮个数 invalid sub button size + */ + CODE_40023(40023, "不合法的子菜单按钮个数"), + + /** + * 不合法的子菜单按钮类型 invalid sub button type + */ + CODE_40024(40024, "不合法的子菜单按钮类型"), + + /** + * 不合法的子菜单按钮名字长度 invalid sub button name size + */ + CODE_40025(40025, "不合法的子菜单按钮名字长度"), + + /** + * 不合法的子菜单按钮 KEY 长度 invalid sub button key size + */ + CODE_40026(40026, "不合法的子菜单按钮 KEY 长度"), + + /** + * 不合法的子菜单按钮 URL 长度 invalid sub button url size + */ + CODE_40027(40027, "不合法的子菜单按钮 URL 长度"), + + /** + * 不合法的自定义菜单使用用户 invalid menu api user + */ + CODE_40028(40028, "不合法的自定义菜单使用用户"), + + /** + * 无效的 oauth_code invalid code + */ + CODE_40029(40029, "无效的 oauth_code"), + + /** + * 不合法的 refresh_token invalid refresh_token + */ + CODE_40030(40030, "不合法的 refresh_token"), + + /** + * 不合法的 openid 列表 invalid openid list + */ + CODE_40031(40031, "不合法的 openid 列表"), + + /** + * 不合法的 openid 列表长度 invalid openid list size + */ + CODE_40032(40032, "不合法的 openid 列表长度"), + + /** + * 不合法的请求字符,不能包含 \\uxxxx 格式的字符 invalid charset. please check your request, if include \\uxxxx will create fail! + */ + CODE_40033(40033, "不合法的请求字符,不能包含 \\uxxxx 格式的字符"), + + /** + * invalid template size + */ + CODE_40034(40034, "invalid template size"), + + /** + * 不合法的参数 invalid args size + */ + CODE_40035(40035, "不合法的参数"), + + /** + * 不合法的 template_id 长度 invalid template_id size + */ + CODE_40036(40036, "不合法的 template_id 长度"), + + /** + * 不合法的 template_id invalid template_id + */ + CODE_40037(40037, "不合法的 template_id"), + + /** + * 不合法的请求格式 invalid packaging type + */ + CODE_40038(40038, "不合法的请求格式"), + + /** + * 不合法的 URL 长度 invalid url size + */ + CODE_40039(40039, "不合法的 URL 长度"), + + /** + * invalid plugin token + */ + CODE_40040(40040, "invalid plugin token"), + + /** + * invalid plugin id + */ + CODE_40041(40041, "invalid plugin id"), + + /** + * invalid plugin session + */ + CODE_40042(40042, "invalid plugin session"), + + /** + * invalid fav type + */ + CODE_40043(40043, "invalid fav type"), + + /** + * invalid size in link.title + */ + CODE_40044(40044, "invalid size in link.title"), + + /** + * invalid size in link.description + */ + CODE_40045(40045, "invalid size in link.description"), + + /** + * invalid size in link.iconurl + */ + CODE_40046(40046, "invalid size in link.iconurl"), + + /** + * invalid size in link.url + */ + CODE_40047(40047, "invalid size in link.url"), + + /** + * 无效的url invalid url domain + */ + CODE_40048(40048, "无效的url"), + + /** + * invalid score report type + */ + CODE_40049(40049, "invalid score report type"), + + /** + * 不合法的分组 id invalid timeline type + */ + CODE_40050(40050, "不合法的分组 id"), + + /** + * 分组名字不合法 invalid group name + */ + CODE_40051(40051, "分组名字不合法"), + + /** + * invalid action name + */ + CODE_40052(40052, "invalid action name"), + + /** + * invalid action info, please check document + */ + CODE_40053(40053, "invalid action info, please check document"), + + /** + * 不合法的子菜单按钮 url 域名 invalid sub button url domain + */ + CODE_40054(40054, "不合法的子菜单按钮 url 域名"), + + /** + * 不合法的菜单按钮 url 域名 invalid button url domain + */ + CODE_40055(40055, "不合法的菜单按钮 url 域名"), + + /** + * invalid serial code + */ + CODE_40056(40056, "invalid serial code"), + + /** + * invalid tabbar size + */ + CODE_40057(40057, "invalid tabbar size"), + + /** + * invalid tabbar name size + */ + CODE_40058(40058, "invalid tabbar name size"), + + /** + * invalid msg id + */ + CODE_40059(40059, "invalid msg id"), + + /** + * 删除单篇图文时,指定的 article_idx 不合法 invalid article idx + */ + CODE_40060(40060, "删除单篇图文时,指定的 article_idx 不合法"), + + /** + * invalid title size + */ + CODE_40062(40062, "invalid title size"), + + /** + * invalid message_ext size + */ + CODE_40063(40063, "invalid message_ext size"), + + /** + * invalid app type + */ + CODE_40064(40064, "invalid app type"), + + /** + * invalid msg status + */ + CODE_40065(40065, "invalid msg status"), + + /** + * 不合法的 url ,递交的页面被sitemap标记为拦截 invalid url + */ + CODE_40066(40066, "不合法的 url ,递交的页面被sitemap标记为拦截"), + + /** + * invalid tvid + */ + CODE_40067(40067, "invalid tvid"), + + /** + * contain mailcious url + */ + CODE_40068(40068, "contain mailcious url"), + + /** + * invalid hardware type + */ + CODE_40069(40069, "invalid hardware type"), + + /** + * invalid sku info + */ + CODE_40070(40070, "invalid sku info"), + + /** + * invalid card type + */ + CODE_40071(40071, "invalid card type"), + + /** + * invalid location id + */ + CODE_40072(40072, "invalid location id"), + + /** + * invalid card id + */ + CODE_40073(40073, "invalid card id"), + + /** + * invalid pay template id + */ + CODE_40074(40074, "invalid pay template id"), + + /** + * invalid encrypt code + */ + CODE_40075(40075, "invalid encrypt code"), + + /** + * invalid color id + */ + CODE_40076(40076, "invalid color id"), + + /** + * invalid score type + */ + CODE_40077(40077, "invalid score type"), + + /** + * invalid card status + */ + CODE_40078(40078, "invalid card status"), + + /** + * invalid time + */ + CODE_40079(40079, "invalid time"), + + /** + * invalid card ext + */ + CODE_40080(40080, "invalid card ext"), + + /** + * invalid template_id + */ + CODE_40081(40081, "invalid template_id"), + + /** + * invalid banner picture size + */ + CODE_40082(40082, "invalid banner picture size"), + + /** + * invalid banner url size + */ + CODE_40083(40083, "invalid banner url size"), + + /** + * invalid button desc size + */ + CODE_40084(40084, "invalid button desc size"), + + /** + * invalid button url size + */ + CODE_40085(40085, "invalid button url size"), + + /** + * invalid sharelink logo size + */ + CODE_40086(40086, "invalid sharelink logo size"), + + /** + * invalid sharelink desc size + */ + CODE_40087(40087, "invalid sharelink desc size"), + + /** + * invalid sharelink title size + */ + CODE_40088(40088, "invalid sharelink title size"), + + /** + * invalid platform id + */ + CODE_40089(40089, "invalid platform id"), + + /** + * invalid request source (bad client ip) + */ + CODE_40090(40090, "invalid request source (bad client ip)"), + + /** + * invalid component ticket + */ + CODE_40091(40091, "invalid component ticket"), + + /** + * invalid remark name + */ + CODE_40092(40092, "invalid remark name"), + + /** + * not completely ok, err_item will return location_id=-1,check your required_fields in json. + */ + CODE_40093(40093, "not completely ok, err_item will return location_id=-1,check your required_fields in json."), + + /** + * invalid component credential + */ + CODE_40094(40094, "invalid component credential"), + + /** + * bad source of caller + */ + CODE_40095(40095, "bad source of caller"), + + /** + * invalid biztype + */ + CODE_40096(40096, "invalid biztype"), + + /** + * 参数错误 invalid args + */ + CODE_40097(40097, "参数错误"), + + /** + * invalid poiid + */ + CODE_40098(40098, "invalid poiid"), + + /** + * invalid code, this code has consumed. + */ + CODE_40099(40099, "invalid code, this code has consumed."), + + /** + * invalid DateInfo, Make Sure OldDateInfoType==NewDateInfoType && NewBeginTime<=OldBeginTime && OldEndTime<= NewEndTime + */ + CODE_40100(40100, "invalid DateInfo, Make Sure OldDateInfoType==NewDateInfoType && NewBeginTime<=OldBeginTime && OldEndTime<= NewEndTime"), + + /** + * missing parameter + */ + CODE_40101(40101, "missing parameter"), + + /** + * invalid industry id + */ + CODE_40102(40102, "invalid industry id"), + + /** + * invalid industry index + */ + CODE_40103(40103, "invalid industry index"), + + /** + * invalid category id + */ + CODE_40104(40104, "invalid category id"), + + /** + * invalid view type + */ + CODE_40105(40105, "invalid view type"), + + /** + * invalid user name + */ + CODE_40106(40106, "invalid user name"), + + /** + * invalid card id! 1,card status must verify ok; 2,this card must have location_id + */ + CODE_40107(40107, "invalid card id! 1,card status must verify ok; 2,this card must have location_id"), + + /** + * invalid client version + */ + CODE_40108(40108, "invalid client version"), + + /** + * too many code size, must <= 100 + */ + CODE_40109(40109, "too many code size, must <= 100"), + + /** + * have empty code + */ + CODE_40110(40110, "have empty code"), + + /** + * have same code + */ + CODE_40111(40111, "have same code"), + + /** + * can not set bind openid + */ + CODE_40112(40112, "can not set bind openid"), + + /** + * unsupported file type + */ + CODE_40113(40113, "unsupported file type"), + + /** + * invalid index value + */ + CODE_40114(40114, "invalid index value"), + + /** + * invalid session from + */ + CODE_40115(40115, "invalid session from"), + + /** + * invalid code + */ + CODE_40116(40116, "invalid code"), + + /** + * 分组名字不合法 invalid button media_id size + */ + CODE_40117(40117, "分组名字不合法"), + + /** + * media_id 大小不合法 invalid sub button media_id size + */ + CODE_40118(40118, "media_id 大小不合法"), + + /** + * button 类型错误 invalid use button type + */ + CODE_40119(40119, "button 类型错误"), + + /** + * 子 button 类型错误 invalid use sub button type + */ + CODE_40120(40120, "子 button 类型错误"), + + /** + * 不合法的 media_id 类型 invalid media type in view_limited + */ + CODE_40121(40121, "不合法的 media_id 类型"), + + /** + * invalid card quantity + */ + CODE_40122(40122, "invalid card quantity"), + + /** + * invalid task_id + */ + CODE_40123(40123, "invalid task_id"), + + /** + * too many custom field! + */ + CODE_40124(40124, "too many custom field!"), + + /** + * 不合法的 AppID ,请开发者检查 AppID 的正确性,避免异常字符,注意大小写 invalid appsecret + */ + CODE_40125(40125, "不合法的 AppID ,请开发者检查 AppID 的正确性,避免异常字符,注意大小写"), + + /** + * invalid text size + */ + CODE_40126(40126, "invalid text size"), + + /** + * invalid user-card status! Hint: the card was given to user, but may be deleted or expired or set unavailable ! + */ + CODE_40127(40127, "invalid user-card status! Hint: the card was given to user, but may be deleted or expired or set unavailable !"), + + /** + * invalid media id! must be uploaded by api(cgi-bin/material/add_material) + */ + CODE_40128(40128, "invalid media id! must be uploaded by api(cgi-bin/material/add_material)"), + + /** + * invalid scene + */ + CODE_40129(40129, "invalid scene"), + + /** + * invalid openid list size, at least two openid + */ + CODE_40130(40130, "invalid openid list size, at least two openid"), + + /** + * out of limit of ticket + */ + CODE_40131(40131, "out of limit of ticket"), + + /** + * 微信号不合法 invalid username + */ + CODE_40132(40132, "微信号不合法"), + + /** + * invalid encryt data + */ + CODE_40133(40133, "invalid encryt data"), + + /** + * invalid not supply bonus, can not change card_id which supply bonus to be not supply + */ + CODE_40135(40135, "invalid not supply bonus, can not change card_id which supply bonus to be not supply"), + + /** + * invalid use DepositCodeMode, make sure sku.quantity>DepositCode.quantity + */ + CODE_40136(40136, "invalid use DepositCodeMode, make sure sku.quantity>DepositCode.quantity"), + + /** + * 不支持的图片格式 invalid image format + */ + CODE_40137(40137, "不支持的图片格式"), + + /** + * emphasis word can not be first neither remark + */ + CODE_40138(40138, "emphasis word can not be first neither remark"), + + /** + * invalid sub merchant id + */ + CODE_40139(40139, "invalid sub merchant id"), + + /** + * invalid sub merchant status + */ + CODE_40140(40140, "invalid sub merchant status"), + + /** + * invalid image url + */ + CODE_40141(40141, "invalid image url"), + + /** + * invalid sharecard parameters + */ + CODE_40142(40142, "invalid sharecard parameters"), + + /** + * invalid least cost info, should be 0 + */ + CODE_40143(40143, "invalid least cost info, should be 0"), + + /** + * 1)maybe share_card_list.num or consume_share_self_num too big; 2)maybe card_id_list also has self-card_id;3)maybe card_id_list has many different card_id;4)maybe both consume_share_self_num and share_card_list.num bigger than 0 + */ + CODE_40144(40144, "1)maybe share_card_list.num or consume_share_self_num too big; 2)maybe card_id_list also has self-card_id;3)maybe card_id_list has many different card_id;4)maybe both consume_share_self_num and share_card_list.num bigger than 0"), + + /** + * invalid update! Can not both set PayCell and CenterCellInfo(include: center_title, center_sub_title, center_url). + */ + CODE_40145(40145, "invalid update! Can not both set PayCell and CenterCellInfo(include: center_title, center_sub_title, center_url)."), + + /** + * invalid openid! card may be marked by other user! + */ + CODE_40146(40146, "invalid openid! card may be marked by other user!"), + + /** + * invalid consume! Consume time overranging restricts. + */ + CODE_40147(40147, "invalid consume! Consume time overranging restricts."), + + /** + * invalid friends card type + */ + CODE_40148(40148, "invalid friends card type"), + + /** + * invalid use time limit + */ + CODE_40149(40149, "invalid use time limit"), + + /** + * invalid card parameters + */ + CODE_40150(40150, "invalid card parameters"), + + /** + * invalid card info, text/pic hit antispam + */ + CODE_40151(40151, "invalid card info, text/pic hit antispam"), + + /** + * invalid group id + */ + CODE_40152(40152, "invalid group id"), + + /** + * self consume cell for friends card must need verify code + */ + CODE_40153(40153, "self consume cell for friends card must need verify code"), + + /** + * invalid voip parameters + */ + CODE_40154(40154, "invalid voip parameters"), + + /** + * 请勿添加其他公众号的主页链接 please don't contain other home page url + */ + CODE_40155(40155, "请勿添加其他公众号的主页链接"), + + /** + * invalid face recognize parameters + */ + CODE_40156(40156, "invalid face recognize parameters"), + + /** + * invalid picture, has no face + */ + CODE_40157(40157, "invalid picture, has no face"), + + /** + * invalid use_custom_code, need be false + */ + CODE_40158(40158, "invalid use_custom_code, need be false"), + + /** + * invalid length for path, or the data is not json string + */ + CODE_40159(40159, "invalid length for path, or the data is not json string"), + + /** + * invalid image file + */ + CODE_40160(40160, "invalid image file"), + + /** + * image file not match + */ + CODE_40161(40161, "image file not match"), + + /** + * invalid lifespan + */ + CODE_40162(40162, "invalid lifespan"), + + /** + * oauth_code已使用 code been used + */ + CODE_40163(40163, "oauth_code已使用"), + + /** + * invalid ip, not in whitelist + */ + CODE_40164(40164, "invalid ip, not in whitelist"), + + /** + * invalid weapp pagepath + */ + CODE_40165(40165, "invalid weapp pagepath"), + + /** + * invalid weapp appid + */ + CODE_40166(40166, "invalid weapp appid"), + + /** + * there is no relation with plugin appid + */ + CODE_40167(40167, "there is no relation with plugin appid"), + + /** + * unlinked weapp card + */ + CODE_40168(40168, "unlinked weapp card"), + + /** + * invalid length for scene, or the data is not json string + */ + CODE_40169(40169, "invalid length for scene, or the data is not json string"), + + /** + * args count exceed count limit + */ + CODE_40170(40170, "args count exceed count limit"), + + /** + * product id can not empty and the length cannot exceed 32 + */ + CODE_40171(40171, "product id can not empty and the length cannot exceed 32"), + + /** + * can not have same product id + */ + CODE_40172(40172, "can not have same product id"), + + /** + * there is no bind relation + */ + CODE_40173(40173, "there is no bind relation"), + + /** + * not card user + */ + CODE_40174(40174, "not card user"), + + /** + * invalid material id + */ + CODE_40175(40175, "invalid material id"), + + /** + * invalid template id + */ + CODE_40176(40176, "invalid template id"), + + /** + * invalid product id + */ + CODE_40177(40177, "invalid product id"), + + /** + * invalid sign + */ + CODE_40178(40178, "invalid sign"), + + /** + * Function is adjusted, rules are not allowed to add or update + */ + CODE_40179(40179, "Function is adjusted, rules are not allowed to add or update"), + + /** + * invalid client tmp token + */ + CODE_40180(40180, "invalid client tmp token"), + + /** + * invalid opengid + */ + CODE_40181(40181, "invalid opengid"), + + /** + * invalid pack_id + */ + CODE_40182(40182, "invalid pack_id"), + + /** + * invalid product_appid, product_appid should bind with wxa_appid + */ + CODE_40183(40183, "invalid product_appid, product_appid should bind with wxa_appid"), + + /** + * invalid url path + */ + CODE_40184(40184, "invalid url path"), + + /** + * invalid auth_token, or auth_token is expired + */ + CODE_40185(40185, "invalid auth_token, or auth_token is expired"), + + /** + * invalid delegate + */ + CODE_40186(40186, "invalid delegate"), + + /** + * invalid ip + */ + CODE_40187(40187, "invalid ip"), + + /** + * invalid scope + */ + CODE_40188(40188, "invalid scope"), + + /** + * invalid width + */ + CODE_40189(40189, "invalid width"), + + /** + * invalid delegate time + */ + CODE_40190(40190, "invalid delegate time"), + + /** + * invalid pic_url + */ + CODE_40191(40191, "invalid pic_url"), + + /** + * invalid author in news + */ + CODE_40192(40192, "invalid author in news"), + + /** + * invalid recommend length + */ + CODE_40193(40193, "invalid recommend length"), + + /** + * illegal recommend + */ + CODE_40194(40194, "illegal recommend"), + + /** + * invalid show_num + */ + CODE_40195(40195, "invalid show_num"), + + /** + * invalid smartmsg media_id + */ + CODE_40196(40196, "invalid smartmsg media_id"), + + /** + * invalid smartmsg media num + */ + CODE_40197(40197, "invalid smartmsg media num"), + + /** + * invalid default msg article size, must be same as show_num + */ + CODE_40198(40198, "invalid default msg article size, must be same as show_num"), + + /** + * 运单 ID 不存在,未查到运单 waybill_id not found + */ + CODE_40199(40199, "运单 ID 不存在,未查到运单"), + + /** + * invalid account type + */ + CODE_40200(40200, "invalid account type"), + + /** + * invalid check url + */ + CODE_40201(40201, "invalid check url"), + + /** + * invalid check action + */ + CODE_40202(40202, "invalid check action"), + + /** + * invalid check operator + */ + CODE_40203(40203, "invalid check operator"), + + /** + * can not delete wash or rumor article + */ + CODE_40204(40204, "can not delete wash or rumor article"), + + /** + * invalid check keywords string + */ + CODE_40205(40205, "invalid check keywords string"), + + /** + * invalid check begin stamp + */ + CODE_40206(40206, "invalid check begin stamp"), + + /** + * invalid check alive seconds + */ + CODE_40207(40207, "invalid check alive seconds"), + + /** + * invalid check notify id + */ + CODE_40208(40208, "invalid check notify id"), + + /** + * invalid check notify msg + */ + CODE_40209(40209, "invalid check notify msg"), + + /** + * pages 中的path参数不存在或为空 invalid check wxa path + */ + CODE_40210(40210, "pages 中的path参数不存在或为空"), + + /** + * invalid scope_data + */ + CODE_40211(40211, "invalid scope_data"), + + /** + * paegs 当中存在不合法的query,query格式遵循URL标准,即k1=v1&k2=v2 invalid query + */ + CODE_40212(40212, "paegs 当中存在不合法的query,query格式遵循URL标准,即k1=v1&k2=v2"), + + /** + * invalid href tag + */ + CODE_40213(40213, "invalid href tag"), + + /** + * invalid href text + */ + CODE_40214(40214, "invalid href text"), + + /** + * invalid image count + */ + CODE_40215(40215, "invalid image count"), + + /** + * invalid desc + */ + CODE_40216(40216, "invalid desc"), + + /** + * invalid video count + */ + CODE_40217(40217, "invalid video count"), + + /** + * invalid video id + */ + CODE_40218(40218, "invalid video id"), + + /** + * pages不存在或者参数为空 pages is empty + */ + CODE_40219(40219, "pages不存在或者参数为空"), + + /** + * data_list is empty + */ + CODE_40220(40220, "data_list is empty"), + + /** + * invalid Content-Encoding + */ + CODE_40221(40221, "invalid Content-Encoding"), + + /** + * invalid request idc domain + */ + CODE_40222(40222, "invalid request idc domain"), + + /** + * 缺少 access_token 参数 access_token missing + */ + CODE_41001(41001, "缺少 access_token 参数"), + + /** + * 缺少 appid 参数 appid missing + */ + CODE_41002(41002, "缺少 appid 参数"), + + /** + * 缺少 refresh_token 参数 refresh_token missing + */ + CODE_41003(41003, "缺少 refresh_token 参数"), + + /** + * 缺少 secret 参数 appsecret missing + */ + CODE_41004(41004, "缺少 secret 参数"), + + /** + * 缺少多媒体文件数据,传输素材无视频或图片内容 media data missing + */ + CODE_41005(41005, "缺少多媒体文件数据,传输素材无视频或图片内容"), + + /** + * 缺少 media_id 参数 media_id missing + */ + CODE_41006(41006, "缺少 media_id 参数"), + + /** + * 缺少子菜单数据 sub_menu data missing + */ + CODE_41007(41007, "缺少子菜单数据"), + + /** + * 缺少 oauth code missing code + */ + CODE_41008(41008, "缺少 oauth code"), + + /** + * 缺少 openid missing openid + */ + CODE_41009(41009, "缺少 openid"), + + /** + * 缺失 url 参数 missing url + */ + CODE_41010(41010, "缺失 url 参数"), + + /** + * missing required fields! please check document and request json! + */ + CODE_41011(41011, "missing required fields! please check document and request json!"), + + /** + * missing card id + */ + CODE_41012(41012, "missing card id"), + + /** + * missing code + */ + CODE_41013(41013, "missing code"), + + /** + * missing ticket_class + */ + CODE_41014(41014, "missing ticket_class"), + + /** + * missing show_time + */ + CODE_41015(41015, "missing show_time"), + + /** + * missing screening_room + */ + CODE_41016(41016, "missing screening_room"), + + /** + * missing seat_number + */ + CODE_41017(41017, "missing seat_number"), + + /** + * missing component_appid + */ + CODE_41018(41018, "missing component_appid"), + + /** + * missing platform_secret + */ + CODE_41019(41019, "missing platform_secret"), + + /** + * missing platform_ticket + */ + CODE_41020(41020, "missing platform_ticket"), + + /** + * missing component_access_token + */ + CODE_41021(41021, "missing component_access_token"), + + /** + * missing "display" field + */ + CODE_41024(41024, "missing \"display\" field"), + + /** + * poi_list empty + */ + CODE_41025(41025, "poi_list empty"), + + /** + * missing image list info, text maybe empty + */ + CODE_41026(41026, "missing image list info, text maybe empty"), + + /** + * missing voip call key + */ + CODE_41027(41027, "missing voip call key"), + + /** + * invalid form id + */ + CODE_41028(41028, "invalid form id"), + + /** + * form id used count reach limit + */ + CODE_41029(41029, "form id used count reach limit"), + + /** + * page路径不正确,需要保证在现网版本小程序中存在,与app.json保持一致 invalid page + */ + CODE_41030(41030, "page路径不正确,需要保证在现网版本小程序中存在,与app.json保持一致"), + + /** + * the form id have been blocked! + */ + CODE_41031(41031, "the form id have been blocked!"), + + /** + * not allow to send message with submitted form id, for punishment + */ + CODE_41032(41032, "not allow to send message with submitted form id, for punishment"), + + /** + * 只允许通过api创建的小程序使用 invaid register type + */ + CODE_41033(41033, "只允许通过api创建的小程序使用"), + + /** + * not allow to send message with submitted form id, for punishment + */ + CODE_41034(41034, "not allow to send message with submitted form id, for punishment"), + + /** + * not allow to send message with prepay id, for punishment + */ + CODE_41035(41035, "not allow to send message with prepay id, for punishment"), + + /** + * appid ad cid + */ + CODE_41036(41036, "appid ad cid"), + + /** + * appid ad_mch_appid + */ + CODE_41037(41037, "appid ad_mch_appid"), + + /** + * appid pos_type + */ + CODE_41038(41038, "appid pos_type"), + + /** + * access_token 超时,请检查 access_token 的有效期,请参考基础支持 - 获取 access_token 中,对 access_token 的详细机制说明 access_token expired + */ + CODE_42001(42001, "access_token 超时,请检查 access_token 的有效期,请参考基础支持 - 获取 access_token 中,对 access_token 的详细机制说明"), + + /** + * refresh_token 超时 refresh_token expired + */ + CODE_42002(42002, "refresh_token 超时"), + + /** + * oauth_code 超时 code expired + */ + CODE_42003(42003, "oauth_code 超时"), + + /** + * plugin token expired + */ + CODE_42004(42004, "plugin token expired"), + + /** + * api usage expired + */ + CODE_42005(42005, "api usage expired"), + + /** + * component_access_token expired + */ + CODE_42006(42006, "component_access_token expired"), + + /** + * 用户修改微信密码, accesstoken 和 refreshtoken 失效,需要重新授权 access_token and refresh_token exception + */ + CODE_42007(42007, "用户修改微信密码, accesstoken 和 refreshtoken 失效,需要重新授权"), + + /** + * voip call key expired + */ + CODE_42008(42008, "voip call key expired"), + + /** + * client tmp token expired + */ + CODE_42009(42009, "client tmp token expired"), + + /** + * 需要 GET 请求 require GET method + */ + CODE_43001(43001, "需要 GET 请求"), + + /** + * 需要 POST 请求 require POST method + */ + CODE_43002(43002, "需要 POST 请求"), + + /** + * 需要 HTTPS 请求 require https + */ + CODE_43003(43003, "需要 HTTPS 请求"), + + /** + * 需要接收者关注 require subscribe + */ + CODE_43004(43004, "需要接收者关注"), + + /** + * 需要好友关系 require friend relations + */ + CODE_43005(43005, "需要好友关系"), + + /** + * require not block + */ + CODE_43006(43006, "require not block"), + + /** + * require bizuser authorize + */ + CODE_43007(43007, "require bizuser authorize"), + + /** + * require biz pay auth + */ + CODE_43008(43008, "require biz pay auth"), + + /** + * can not use custom code, need authorize + */ + CODE_43009(43009, "can not use custom code, need authorize"), + + /** + * can not use balance, need authorize + */ + CODE_43010(43010, "can not use balance, need authorize"), + + /** + * can not use bonus, need authorize + */ + CODE_43011(43011, "can not use bonus, need authorize"), + + /** + * can not use custom url, need authorize + */ + CODE_43012(43012, "can not use custom url, need authorize"), + + /** + * can not use shake card, need authorize + */ + CODE_43013(43013, "can not use shake card, need authorize"), + + /** + * require check agent + */ + CODE_43014(43014, "require check agent"), + + /** + * require authorize by wechat team to use this function! + */ + CODE_43015(43015, "require authorize by wechat team to use this function!"), + + /** + * 小程序未认证 require verify + */ + CODE_43016(43016, "小程序未认证"), + + /** + * require location id! + */ + CODE_43017(43017, "require location id!"), + + /** + * code has no been mark! + */ + CODE_43018(43018, "code has no been mark!"), + + /** + * 需要将接收者从黑名单中移除 require remove blacklist + */ + CODE_43019(43019, "需要将接收者从黑名单中移除"), + + /** + * change template too frequently + */ + CODE_43100(43100, "change template too frequently"), + + /** + * 用户拒绝接受消息,如果用户之前曾经订阅过,则表示用户取消了订阅关系 user refuse to accept the msg + */ + CODE_43101(43101, "用户拒绝接受消息,如果用户之前曾经订阅过,则表示用户取消了订阅关系"), + + /** + * the tempalte is not subscriptiontype + */ + CODE_43102(43102, "the tempalte is not subscriptiontype"), + + /** + * the api only can cancel the subscription + */ + CODE_43103(43103, "the api only can cancel the subscription"), + + /** + * this appid does not have permission + */ + CODE_43104(43104, "this appid does not have permission"), + + /** + * news has no binding relation with template_id + */ + CODE_43105(43105, "news has no binding relation with template_id"), + + /** + * not allow to add template, for punishment + */ + CODE_43106(43106, "not allow to add template, for punishment"), + + /** + * 多媒体文件为空 empty media data + */ + CODE_44001(44001, "多媒体文件为空"), + + /** + * POST 的数据包为空 empty post data + */ + CODE_44002(44002, "POST 的数据包为空"), + + /** + * 图文消息内容为空 empty news data + */ + CODE_44003(44003, "图文消息内容为空"), + + /** + * 文本消息内容为空 empty content + */ + CODE_44004(44004, "文本消息内容为空"), + + /** + * 空白的列表 empty list size + */ + CODE_44005(44005, "空白的列表"), + + /** + * empty file data + */ + CODE_44006(44006, "empty file data"), + + /** + * repeated msg id + */ + CODE_44007(44007, "repeated msg id"), + + /** + * image url size out of limit + */ + CODE_44997(44997, "image url size out of limit"), + + /** + * keyword string media size out of limit + */ + CODE_44998(44998, "keyword string media size out of limit"), + + /** + * keywords list size out of limit + */ + CODE_44999(44999, "keywords list size out of limit"), + + /** + * msg_id size out of limit + */ + CODE_45000(45000, "msg_id size out of limit"), + + /** + * 多媒体文件大小超过限制 media size out of limit + */ + CODE_45001(45001, "多媒体文件大小超过限制"), + + /** + * 消息内容超过限制 content size out of limit + */ + CODE_45002(45002, "消息内容超过限制"), + + /** + * 标题字段超过限制 title size out of limit + */ + CODE_45003(45003, "标题字段超过限制"), + + /** + * 描述字段超过限制 description size out of limit + */ + CODE_45004(45004, "描述字段超过限制"), + + /** + * 链接字段超过限制 url size out of limit + */ + CODE_45005(45005, "链接字段超过限制"), + + /** + * 图片链接字段超过限制 picurl size out of limit + */ + CODE_45006(45006, "图片链接字段超过限制"), + + /** + * 语音播放时间超过限制 playtime out of limit + */ + CODE_45007(45007, "语音播放时间超过限制"), + + /** + * 图文消息超过限制 article size out of limit + */ + CODE_45008(45008, "图文消息超过限制"), + + /** + * 接口调用超过限制 reach max api daily quota limit + */ + CODE_45009(45009, "接口调用超过限制"), + + /** + * 创建菜单个数超过限制 create menu limit + */ + CODE_45010(45010, "创建菜单个数超过限制"), + + /** + * API 调用太频繁,请稍候再试 api minute-quota reach limit, must slower, retry next minute + */ + CODE_45011(45011, "API 调用太频繁,请稍候再试"), + + /** + * 模板大小超过限制 template size out of limit + */ + CODE_45012(45012, "模板大小超过限制"), + + /** + * too many template args + */ + CODE_45013(45013, "too many template args"), + + /** + * template message size out of limit + */ + CODE_45014(45014, "template message size out of limit"), + + /** + * 回复时间超过限制 response out of time limit or subscription is canceled + */ + CODE_45015(45015, "回复时间超过限制"), + + /** + * 系统分组,不允许修改 can't modify sys group + */ + CODE_45016(45016, "系统分组,不允许修改"), + + /** + * 分组名字过长 can't set group name too long sys group + */ + CODE_45017(45017, "分组名字过长"), + + /** + * 分组数量超过上限 too many group now, no need to add new + */ + CODE_45018(45018, "分组数量超过上限"), + + /** + * too many openid, please input less + */ + CODE_45019(45019, "too many openid, please input less"), + + /** + * too many image, please input less + */ + CODE_45020(45020, "too many image, please input less"), + + /** + * some argument may be out of length limit! please check document and request json! + */ + CODE_45021(45021, "some argument may be out of length limit! please check document and request json!"), + + /** + * bonus is out of limit + */ + CODE_45022(45022, "bonus is out of limit"), + + /** + * balance is out of limit + */ + CODE_45023(45023, "balance is out of limit"), + + /** + * rank template number is out of limit + */ + CODE_45024(45024, "rank template number is out of limit"), + + /** + * poiid count is out of limit + */ + CODE_45025(45025, "poiid count is out of limit"), + + /** + * template num exceeds limit + */ + CODE_45026(45026, "template num exceeds limit"), + + /** + * template conflict with industry + */ + CODE_45027(45027, "template conflict with industry"), + + /** + * has no masssend quota + */ + CODE_45028(45028, "has no masssend quota"), + + /** + * qrcode count out of limit + */ + CODE_45029(45029, "qrcode count out of limit"), + + /** + * limit cardid, not support this function + */ + CODE_45030(45030, "limit cardid, not support this function"), + + /** + * stock is out of limit + */ + CODE_45031(45031, "stock is out of limit"), + + /** + * not inner ip for special acct in white-list + */ + CODE_45032(45032, "not inner ip for special acct in white-list"), + + /** + * user get card num is out of get_limit + */ + CODE_45033(45033, "user get card num is out of get_limit"), + + /** + * media file count is out of limit + */ + CODE_45034(45034, "media file count is out of limit"), + + /** + * access clientip is not registered, not in ip-white-list + */ + CODE_45035(45035, "access clientip is not registered, not in ip-white-list"), + + /** + * User receive announcement limit + */ + CODE_45036(45036, "User receive announcement limit"), + + /** + * user out of time limit or never talked in tempsession + */ + CODE_45037(45037, "user out of time limit or never talked in tempsession"), + + /** + * user subscribed, cannot use tempsession api + */ + CODE_45038(45038, "user subscribed, cannot use tempsession api"), + + /** + * card_list_size out of limit + */ + CODE_45039(45039, "card_list_size out of limit"), + + /** + * reach max monthly quota limit + */ + CODE_45040(45040, "reach max monthly quota limit"), + + /** + * this card reach total sku quantity limit! + */ + CODE_45041(45041, "this card reach total sku quantity limit!"), + + /** + * limit card type, this card type can NOT create by sub merchant + */ + CODE_45042(45042, "limit card type, this card type can NOT create by sub merchant"), + + /** + * can not set share_friends=true because has no Abstract Or Text_Img_List has no img Or image url not valid + */ + CODE_45043(45043, "can not set share_friends=true because has no Abstract Or Text_Img_List has no img Or image url not valid"), + + /** + * icon url size in abstract is out of limit + */ + CODE_45044(45044, "icon url size in abstract is out of limit"), + + /** + * unauthorized friends card, please contact administrator + */ + CODE_45045(45045, "unauthorized friends card, please contact administrator"), + + /** + * operate field conflict, CenterCell, PayCell, SelfConsumeCell conflict + */ + CODE_45046(45046, "operate field conflict, CenterCell, PayCell, SelfConsumeCell conflict"), + + /** + * 客服接口下行条数超过上限 out of response count limit + */ + CODE_45047(45047, "客服接口下行条数超过上限"), + + /** + * menu use invalid type + */ + CODE_45048(45048, "menu use invalid type"), + + /** + * ivr use invalid type + */ + CODE_45049(45049, "ivr use invalid type"), + + /** + * custom msg use invalid type + */ + CODE_45050(45050, "custom msg use invalid type"), + + /** + * template msg use invalid link + */ + CODE_45051(45051, "template msg use invalid link"), + + /** + * masssend msg use invalid type + */ + CODE_45052(45052, "masssend msg use invalid type"), + + /** + * exceed consume verify code size + */ + CODE_45053(45053, "exceed consume verify code size"), + + /** + * below consume verify code size + */ + CODE_45054(45054, "below consume verify code size"), + + /** + * the code is not in consume verify code charset + */ + CODE_45055(45055, "the code is not in consume verify code charset"), + + /** + * too many tag now, no need to add new + */ + CODE_45056(45056, "too many tag now, no need to add new"), + + /** + * can't delete the tag that has too many fans + */ + CODE_45057(45057, "can't delete the tag that has too many fans"), + + /** + * can't modify sys tag + */ + CODE_45058(45058, "can't modify sys tag"), + + /** + * can not tagging one user too much + */ + CODE_45059(45059, "can not tagging one user too much"), + + /** + * media is applied in ivr or menu, can not be deleted + */ + CODE_45060(45060, "media is applied in ivr or menu, can not be deleted"), + + /** + * maybe the update frequency is too often, please try again + */ + CODE_45061(45061, "maybe the update frequency is too often, please try again"), + + /** + * has agreement ad. please use mp.weixin.qq.com + */ + CODE_45062(45062, "has agreement ad. please use mp.weixin.qq.com"), + + /** + * accesstoken is not xiaochengxu + */ + CODE_45063(45063, "accesstoken is not xiaochengxu"), + + /** + * 创建菜单包含未关联的小程序 no permission to use weapp in menu + */ + CODE_45064(45064, "创建菜单包含未关联的小程序"), + + /** + * 相同 clientmsgid 已存在群发记录,返回数据中带有已存在的群发任务的 msgid clientmsgid exist + */ + CODE_45065(45065, "相同 clientmsgid 已存在群发记录,返回数据中带有已存在的群发任务的 msgid"), + + /** + * 相同 clientmsgid 重试速度过快,请间隔1分钟重试 same clientmsgid retry too fast + */ + CODE_45066(45066, "相同 clientmsgid 重试速度过快,请间隔1分钟重试"), + + /** + * clientmsgid 长度超过限制 clientmsgid size out of limit + */ + CODE_45067(45067, "clientmsgid 长度超过限制"), + + /** + * file size out of limit + */ + CODE_45068(45068, "file size out of limit"), + + /** + * product list size out of limit + */ + CODE_45069(45069, "product list size out of limit"), + + /** + * the business account have been created + */ + CODE_45070(45070, "the business account have been created"), + + /** + * business account not found + */ + CODE_45071(45071, "business account not found"), + + /** + * command字段取值不对 invalid command + */ + CODE_45072(45072, "command字段取值不对"), + + /** + * not inner vip for sns in white list + */ + CODE_45073(45073, "not inner vip for sns in white list"), + + /** + * material list size out of limit, you should delete the useless material + */ + CODE_45074(45074, "material list size out of limit, you should delete the useless material"), + + /** + * invalid keyword id + */ + CODE_45075(45075, "invalid keyword id"), + + /** + * invalid count + */ + CODE_45076(45076, "invalid count"), + + /** + * number of business account reach limit + */ + CODE_45077(45077, "number of business account reach limit"), + + /** + * nickname is illegal! + */ + CODE_45078(45078, "nickname is illegal!"), + + /** + * nickname is forbidden!(matched forbidden keyword) + */ + CODE_45079(45079, "nickname is forbidden!(matched forbidden keyword)"), + + /** + * 下发输入状态,需要之前30秒内跟用户有过消息交互 need sending message to user, or recving message from user in the last 30 seconds before typing + */ + CODE_45080(45080, "下发输入状态,需要之前30秒内跟用户有过消息交互"), + + /** + * 已经在输入状态,不可重复下发 you are already typing + */ + CODE_45081(45081, "已经在输入状态,不可重复下发"), + + /** + * need icp license for the url domain + */ + CODE_45082(45082, "need icp license for the url domain"), + + /** + * the speed out of range + */ + CODE_45083(45083, "the speed out of range"), + + /** + * No speed message + */ + CODE_45084(45084, "No speed message"), + + /** + * speed server err + */ + CODE_45085(45085, "speed server err"), + + /** + * invalid attrbute 'data-miniprogram-appid' + */ + CODE_45086(45086, "invalid attrbute 'data-miniprogram-appid'"), + + /** + * customer service message from this account have been blocked! + */ + CODE_45087(45087, "customer service message from this account have been blocked!"), + + /** + * action size out of limit + */ + CODE_45088(45088, "action size out of limit"), + + /** + * expired + */ + CODE_45089(45089, "expired"), + + /** + * invalid group msg ticket + */ + CODE_45090(45090, "invalid group msg ticket"), + + /** + * account_name is illegal! + */ + CODE_45091(45091, "account_name is illegal!"), + + /** + * no voice data + */ + CODE_45092(45092, "no voice data"), + + /** + * no quota to send msg + */ + CODE_45093(45093, "no quota to send msg"), + + /** + * not allow to send custom message when user enter session, for punishment + */ + CODE_45094(45094, "not allow to send custom message when user enter session, for punishment"), + + /** + * not allow to modify stock for the advertisement batch + */ + CODE_45095(45095, "not allow to modify stock for the advertisement batch"), + + /** + * invalid qrcode + */ + CODE_45096(45096, "invalid qrcode"), + + /** + * invalid qrcode prefix + */ + CODE_45097(45097, "invalid qrcode prefix"), + + /** + * msgmenu list size is out of limit + */ + CODE_45098(45098, "msgmenu list size is out of limit"), + + /** + * msgmenu item content size is out of limit + */ + CODE_45099(45099, "msgmenu item content size is out of limit"), + + /** + * invalid size of keyword_id_list + */ + CODE_45100(45100, "invalid size of keyword_id_list"), + + /** + * hit upload limit + */ + CODE_45101(45101, "hit upload limit"), + + /** + * this api have been blocked temporarily. + */ + CODE_45102(45102, "this api have been blocked temporarily."), + + /** + * This API has been unsupported + */ + CODE_45103(45103, "This API has been unsupported"), + + /** + * reach max domain quota limit + */ + CODE_45104(45104, "reach max domain quota limit"), + + /** + * the consume verify code not found + */ + CODE_45154(45154, "the consume verify code not found"), + + /** + * the consume verify code is existed + */ + CODE_45155(45155, "the consume verify code is existed"), + + /** + * the consume verify code's length not invalid + */ + CODE_45156(45156, "the consume verify code's length not invalid"), + + /** + * invalid tag name + */ + CODE_45157(45157, "invalid tag name"), + + /** + * tag name too long + */ + CODE_45158(45158, "tag name too long"), + + /** + * invalid tag id + */ + CODE_45159(45159, "invalid tag id"), + + /** + * invalid category to create card + */ + CODE_45160(45160, "invalid category to create card"), + + /** + * this video id must be generated by calling upload api + */ + CODE_45161(45161, "this video id must be generated by calling upload api"), + + /** + * invalid type + */ + CODE_45162(45162, "invalid type"), + + /** + * invalid sort_method + */ + CODE_45163(45163, "invalid sort_method"), + + /** + * invalid offset + */ + CODE_45164(45164, "invalid offset"), + + /** + * invalid limit + */ + CODE_45165(45165, "invalid limit"), + + /** + * invalid content + */ + CODE_45166(45166, "invalid content"), + + /** + * invalid voip call key + */ + CODE_45167(45167, "invalid voip call key"), + + /** + * keyword in blacklist + */ + CODE_45168(45168, "keyword in blacklist"), + + /** + * part or whole of the requests from the very app is temporary blocked by supervisor + */ + CODE_45501(45501, "part or whole of the requests from the very app is temporary blocked by supervisor"), + + /** + * 不存在媒体数据,media_id 不存在 media data no exist + */ + CODE_46001(46001, "不存在媒体数据,media_id 不存在"), + + /** + * 不存在的菜单版本 menu version no exist + */ + CODE_46002(46002, "不存在的菜单版本"), + + /** + * 不存在的菜单数据 menu no exist + */ + CODE_46003(46003, "不存在的菜单数据"), + + /** + * 不存在的用户 user no exist + */ + CODE_46004(46004, "不存在的用户"), + + /** + * poi no exist + */ + CODE_46005(46005, "poi no exist"), + + /** + * voip file not exist + */ + CODE_46006(46006, "voip file not exist"), + + /** + * file being transcoded, please try later + */ + CODE_46007(46007, "file being transcoded, please try later"), + + /** + * result id not exist + */ + CODE_46008(46008, "result id not exist"), + + /** + * there is no user data + */ + CODE_46009(46009, "there is no user data"), + + /** + * this api have been not supported since 2020-01-11 00:00:00, please use new api(subscribeMessage)! + */ + CODE_46101(46101, "this api have been not supported since 2020-01-11 00:00:00, please use new api(subscribeMessage)!"), + + /** + * 解析 JSON/XML 内容错误 data format error + */ + CODE_47001(47001, "解析 JSON/XML 内容错误"), + + /** + * data format error, do NOT use json unicode encode (\\uxxxx\\uxxxx), please use utf8 encoded text! + */ + CODE_47002(47002, "data format error, do NOT use json unicode encode (\\uxxxx\\uxxxx), please use utf8 encoded text!"), + + /** + * 模板参数不准确,可能为空或者不满足规则,errmsg会提示具体是哪个字段出错 argument invalid! + */ + CODE_47003(47003, "模板参数不准确,可能为空或者不满足规则,errmsg会提示具体是哪个字段出错"), + + /** + * 每次提交的页面数超过1000(备注:每次提交页面数应小于或等于1000) submit pages count more than each quota + */ + CODE_47004(47004, "每次提交的页面数超过1000(备注:每次提交页面数应小于或等于1000)"), + + /** + * tabbar no exist + */ + CODE_47005(47005, "tabbar no exist"), + + /** + * 当天提交页面数达到了配额上限,请明天再试 submit pages count reach daily limit, please try tomorrow + */ + CODE_47006(47006, "当天提交页面数达到了配额上限,请明天再试"), + + /** + * 搜索结果总数超过了1000条 search results count more than limit + */ + CODE_47101(47101, "搜索结果总数超过了1000条"), + + /** + * next_page_info参数错误 next_page_info error + */ + CODE_47102(47102, "next_page_info参数错误"), + + /** + * 参数 activity_id 错误 activity_id error + */ + CODE_47501(47501, "参数 activity_id 错误"), + + /** + * 参数 target_state 错误 target_state error + */ + CODE_47502(47502, "参数 target_state 错误"), + + /** + * 参数 version_type 错误 version_type error + */ + CODE_47503(47503, "参数 version_type 错误"), + + /** + * activity_id activity_id expired time + */ + CODE_47504(47504, "activity_id"), + + /** + * api 功能未授权,请确认公众号已获得该接口,可以在公众平台官网 - 开发者中心页中查看接口权限 api unauthorized + */ + CODE_48001(48001, "api 功能未授权,请确认公众号已获得该接口,可以在公众平台官网 - 开发者中心页中查看接口权限"), + + /** + * 粉丝拒收消息(粉丝在公众号选项中,关闭了 “ 接收消息 ” ) user block receive message + */ + CODE_48002(48002, "粉丝拒收消息(粉丝在公众号选项中,关闭了 “ 接收消息 ” )"), + + /** + * user not agree mass-send protocol + */ + CODE_48003(48003, "user not agree mass-send protocol"), + + /** + * api 接口被封禁,请登录 mp.weixin.qq.com 查看详情 api forbidden for irregularities, view detail on mp.weixin.qq.com + */ + CODE_48004(48004, "api 接口被封禁,请登录 mp.weixin.qq.com 查看详情"), + + /** + * api 禁止删除被自动回复和自定义菜单引用的素材 forbid to delete material used by auto-reply or menu + */ + CODE_48005(48005, "api 禁止删除被自动回复和自定义菜单引用的素材"), + + /** + * api 禁止清零调用次数,因为清零次数达到上限 forbid to clear quota because of reaching the limit + */ + CODE_48006(48006, "api 禁止清零调用次数,因为清零次数达到上限"), + + /** + * forbid to use other's voip call key + */ + CODE_48007(48007, "forbid to use other's voip call key"), + + /** + * 没有该类型消息的发送权限 no permission for this msgtype + */ + CODE_48008(48008, "没有该类型消息的发送权限"), + + /** + * this api is expired + */ + CODE_48009(48009, "this api is expired"), + + /** + * forbid to modify the material, please see more information on mp.weixin.qq.com + */ + CODE_48010(48010, "forbid to modify the material, please see more information on mp.weixin.qq.com"), + + /** + * disabled template id + */ + CODE_48011(48011, "disabled template id"), + + /** + * invalid token + */ + CODE_48012(48012, "invalid token"), + + /** + * 该视频非新接口上传,不能用于视频消息群发 + */ + CODE_48013(48013, "该视频非新接口上传,不能用于视频消息群发"), + + /** + * 该视频审核状态异常,请检查后重试 + */ + CODE_48014(48014, "该视频审核状态异常,请检查后重试"), + + /** + * 该账号无留言功能权限 + */ + CODE_48015(48015, "该账号无留言功能权限"), + + /** + * 该账号不满足智能配置"观看更多"视频条件 + */ + CODE_48016(48016, "该账号不满足智能配置\"观看更多\"视频条件"), + + /** + * not same appid with appid of access_token + */ + CODE_49001(49001, "not same appid with appid of access_token"), + + /** + * empty openid or transid + */ + CODE_49002(49002, "empty openid or transid"), + + /** + * not match openid with appid + */ + CODE_49003(49003, "not match openid with appid"), + + /** + * not match signature + */ + CODE_49004(49004, "not match signature"), + + /** + * not existed transid + */ + CODE_49005(49005, "not existed transid"), + + /** + * missing arg two_dim_code + */ + CODE_49006(49006, "missing arg two_dim_code"), + + /** + * invalid two_dim_code + */ + CODE_49007(49007, "invalid two_dim_code"), + + /** + * invalid qrcode + */ + CODE_49008(49008, "invalid qrcode"), + + /** + * missing arg qrcode + */ + CODE_49009(49009, "missing arg qrcode"), + + /** + * invalid partner id + */ + CODE_49010(49010, "invalid partner id"), + + /** + * not existed feedbackid + */ + CODE_49300(49300, "not existed feedbackid"), + + /** + * feedback exist + */ + CODE_49301(49301, "feedback exist"), + + /** + * feedback status already changed + */ + CODE_49302(49302, "feedback status already changed"), + + /** + * 用户未授权该 api api unauthorized or user unauthorized + */ + CODE_50001(50001, "用户未授权该 api"), + + /** + * 用户受限,可能是用户帐号被冻结或注销 user limited + */ + CODE_50002(50002, "用户受限,可能是用户帐号被冻结或注销"), + + /** + * user unexpected, maybe not in white list + */ + CODE_50003(50003, "user unexpected, maybe not in white list"), + + /** + * user not allow to use accesstoken, maybe for punishment + */ + CODE_50004(50004, "user not allow to use accesstoken, maybe for punishment"), + + /** + * 用户未关注公众号 user is unsubscribed + */ + CODE_50005(50005, "用户未关注公众号"), + + /** + * user has switched off friends authorization + */ + CODE_50006(50006, "user has switched off friends authorization"), + + /** + * enterprise father account not exist + */ + CODE_51000(51000, "enterprise father account not exist"), + + /** + * enterprise child account not belong to the father + */ + CODE_51001(51001, "enterprise child account not belong to the father"), + + /** + * enterprise verify message not correct + */ + CODE_51002(51002, "enterprise verify message not correct"), + + /** + * invalid enterprise child list size + */ + CODE_51003(51003, "invalid enterprise child list size"), + + /** + * not a enterprise father account + */ + CODE_51004(51004, "not a enterprise father account"), + + /** + * not a enterprise child account + */ + CODE_51005(51005, "not a enterprise child account"), + + /** + * invalid nick name + */ + CODE_51006(51006, "invalid nick name"), + + /** + * not a enterprise account + */ + CODE_51007(51007, "not a enterprise account"), + + /** + * invalid email + */ + CODE_51008(51008, "invalid email"), + + /** + * invalid pwd + */ + CODE_51009(51009, "invalid pwd"), + + /** + * repeated email + */ + CODE_51010(51010, "repeated email"), + + /** + * access deny + */ + CODE_51011(51011, "access deny"), + + /** + * need verify code + */ + CODE_51012(51012, "need verify code"), + + /** + * wrong verify code + */ + CODE_51013(51013, "wrong verify code"), + + /** + * need modify pwd + */ + CODE_51014(51014, "need modify pwd"), + + /** + * user not exist + */ + CODE_51015(51015, "user not exist"), + + /** + * tv info not exist + */ + CODE_51020(51020, "tv info not exist"), + + /** + * stamp crossed + */ + CODE_51021(51021, "stamp crossed"), + + /** + * invalid stamp range + */ + CODE_51022(51022, "invalid stamp range"), + + /** + * stamp not match date + */ + CODE_51023(51023, "stamp not match date"), + + /** + * empty program name + */ + CODE_51024(51024, "empty program name"), + + /** + * empty action url + */ + CODE_51025(51025, "empty action url"), + + /** + * program name size out of limit + */ + CODE_51026(51026, "program name size out of limit"), + + /** + * action url size out of limit + */ + CODE_51027(51027, "action url size out of limit"), + + /** + * invalid program name + */ + CODE_51028(51028, "invalid program name"), + + /** + * invalid action url + */ + CODE_51029(51029, "invalid action url"), + + /** + * invalid action id + */ + CODE_51030(51030, "invalid action id"), + + /** + * invalid action offset + */ + CODE_51031(51031, "invalid action offset"), + + /** + * empty action title + */ + CODE_51032(51032, "empty action title"), + + /** + * action title size out of limit + */ + CODE_51033(51033, "action title size out of limit"), + + /** + * empty action icon url + */ + CODE_51034(51034, "empty action icon url"), + + /** + * action icon url out of limit + */ + CODE_51035(51035, "action icon url out of limit"), + + /** + * pic is not from cdn + */ + CODE_52000(52000, "pic is not from cdn"), + + /** + * wechat price is not less than origin price + */ + CODE_52001(52001, "wechat price is not less than origin price"), + + /** + * category/sku is wrong + */ + CODE_52002(52002, "category/sku is wrong"), + + /** + * product id not existed + */ + CODE_52003(52003, "product id not existed"), + + /** + * category id is not exist, or doesn't has sub category + */ + CODE_52004(52004, "category id is not exist, or doesn't has sub category"), + + /** + * quantity is zero + */ + CODE_52005(52005, "quantity is zero"), + + /** + * area code is invalid + */ + CODE_52006(52006, "area code is invalid"), + + /** + * express template param is error + */ + CODE_52007(52007, "express template param is error"), + + /** + * express template id is not existed + */ + CODE_52008(52008, "express template id is not existed"), + + /** + * group name is empty + */ + CODE_52009(52009, "group name is empty"), + + /** + * group id is not existed + */ + CODE_52010(52010, "group id is not existed"), + + /** + * mod_action is invalid + */ + CODE_52011(52011, "mod_action is invalid"), + + /** + * shelf components count is greater than 20 + */ + CODE_52012(52012, "shelf components count is greater than 20"), + + /** + * shelf component is empty + */ + CODE_52013(52013, "shelf component is empty"), + + /** + * shelf id is not existed + */ + CODE_52014(52014, "shelf id is not existed"), + + /** + * order id is not existed + */ + CODE_52015(52015, "order id is not existed"), + + /** + * order filter param is invalid + */ + CODE_52016(52016, "order filter param is invalid"), + + /** + * order express param is invalid + */ + CODE_52017(52017, "order express param is invalid"), + + /** + * order delivery param is invalid + */ + CODE_52018(52018, "order delivery param is invalid"), + + /** + * brand name empty + */ + CODE_52019(52019, "brand name empty"), + + /** + * principal limit exceed + */ + CODE_53000(53000, "principal limit exceed"), + + /** + * principal in black list + */ + CODE_53001(53001, "principal in black list"), + + /** + * mobile limit exceed + */ + CODE_53002(53002, "mobile limit exceed"), + + /** + * idcard limit exceed + */ + CODE_53003(53003, "idcard limit exceed"), + + /** + * 名称格式不合法 nickname invalid + */ + CODE_53010(53010, "名称格式不合法"), + + /** + * 名称检测命中频率限制 check nickname too frequently + */ + CODE_53011(53011, "名称检测命中频率限制"), + + /** + * 禁止使用该名称 nickname ban + */ + CODE_53012(53012, "禁止使用该名称"), + + /** + * 公众号:名称与已有公众号名称重复;小程序:该名称与已有小程序名称重复 nickname has been occupied + */ + CODE_53013(53013, "公众号:名称与已有公众号名称重复;小程序:该名称与已有小程序名称重复"), + + /** + * 公众号:公众号已有{名称 A+}时,需与该帐号相同主体才可申请{名称 A};小程序:小程序已有{名称 A+}时,需与该帐号相同主体才可申请{名称 A} + */ + CODE_53014(53014, "公众号:公众号已有{名称 A+}时,需与该帐号相同主体才可申请{名称 A};小程序:小程序已有{名称 A+}时,需与该帐号相同主体才可申请{名称 A}"), + + /** + * 公众号:该名称与已有小程序名称重复,需与该小程序帐号相同主体才可申请;小程序:该名称与已有公众号名称重复,需与该公众号帐号相同主体才可申请 + */ + CODE_53015(53015, "公众号:该名称与已有小程序名称重复,需与该小程序帐号相同主体才可申请;小程序:该名称与已有公众号名称重复,需与该公众号帐号相同主体才可申请"), + + /** + * 公众号:该名称与已有多个小程序名称重复,暂不支持申请;小程序:该名称与已有多个公众号名称重复,暂不支持申请 + */ + CODE_53016(53016, "公众号:该名称与已有多个小程序名称重复,暂不支持申请;小程序:该名称与已有多个公众号名称重复,暂不支持申请"), + + /** + * 公众号:小程序已有{名称 A+}时,需与该帐号相同主体才可申请{名称 A};小程序:公众号已有{名称 A+}时,需与该帐号相同主体才可申请{名称 A} + */ + CODE_53017(53017, "公众号:小程序已有{名称 A+}时,需与该帐号相同主体才可申请{名称 A};小程序:公众号已有{名称 A+}时,需与该帐号相同主体才可申请{名称 A}"), + + /** + * 名称命中微信号 nickname hit alias + */ + CODE_53018(53018, "名称命中微信号"), + + /** + * 名称在保护期内 nickname protected by infringement + */ + CODE_53019(53019, "名称在保护期内"), + + /** + * order not found + */ + CODE_53100(53100, "订单不存在"), + + /** + * order already paid + */ + CODE_53101(53101, "已经支付的订单"), + + /** + * already has checking order, can not apply + */ + CODE_53102(53102, "已有检查单,不能申请"), + + /** + * order can not do refill + */ + CODE_53103(53103, "order can not do refill"), + + /** + * 本月功能介绍修改次数已用完 modify signature quota limit exceed + */ + CODE_53200(53200, "本月功能介绍修改次数已用完"), + + /** + * 功能介绍内容命中黑名单关键字 signature in black list, can not use + */ + CODE_53201(53201, "功能介绍内容命中黑名单关键字"), + + /** + * 本月头像修改次数已用完 modify avatar quota limit exceed + */ + CODE_53202(53202, "本月头像修改次数已用完"), + + /** + * can't be modified for the time being + */ + CODE_53203(53203, "暂时还不能修改"), + + /** + * signature invalid + */ + CODE_53204(53204, "无效签名"), + + /** + * 超出每月次数限制 + */ + CODE_53300(53300, "超出每月次数限制"), + + /** + * 超出可配置类目总数限制 + */ + CODE_53301(53301, "超出可配置类目总数限制"), + + /** + * 当前账号主体类型不允许设置此种类目 + */ + CODE_53302(53302, "当前账号主体类型不允许设置此种类目"), + + /** + * 提交的参数不合法 + */ + CODE_53303(53303, "提交的参数不合法"), + + /** + * 与已有类目重复 + */ + CODE_53304(53304, "与已有类目重复"), + + /** + * 包含未通过IPC校验的类目 + */ + CODE_53305(53305, "包含未通过IPC校验的类目"), + + /** + * 修改类目只允许修改类目资质,不允许修改类目ID + */ + CODE_53306(53306, "修改类目只允许修改类目资质,不允许修改类目ID"), + + /** + * 只有审核失败的类目允许修改 + */ + CODE_53307(53307, "只有审核失败的类目允许修改"), + + /** + * 审核中的类目不允许删除 + */ + CODE_53308(53308, "审核中的类目不允许删除"), + + /** + * 社交红包不允许删除 + */ + CODE_53309(53309, "社交红包不允许删除"), + + /** + * 类目超过上限,但是可以添加apply_reason参数申请更多类目 + */ + CODE_53310(53310, "类目超过上限,但是可以添加apply_reason参数申请更多类目"), + + /** + * 需要提交资料信息 + */ + CODE_53311(53311, "需要提交资料信息"), + + /** + * empty jsapi name + */ + CODE_60005(60005, "空的jsapi名称"), + + /** + * user cancel the auth + */ + CODE_60006(60006, "用户取消该授权"), + + /** + * invalid component type + */ + CODE_61000(61000, "无效的第三方类型"), + + /** + * component type and component appid is not match + */ + CODE_61001(61001, "第三方类型与第三方APPID不匹配"), + + /** + * the third appid is not open KF + */ + CODE_61002(61002, "第三方APPID没有开放客服"), + + /** + * component is not authorized by this account + */ + CODE_61003(61003, "帐号未授权"), + + /** + * api 功能未授权,请确认公众号/小程序已获得该接口,可以在公众平台官网 - 开发者中心页中查看接口权限 access clientip is not registered + */ + CODE_61004(61004, "api 功能未授权,请确认公众号/小程序已获得该接口,可以在公众平台官网 - 开发者中心页中查看接口权限"), + + /** + * component ticket is expired + */ + CODE_61005(61005, "ticket 已过期"), + + /** + * component ticket is invalid + */ + CODE_61006(61006, "无效 ticket"), + + /** + * api is unauthorized to component + */ + CODE_61007(61007, "接口未授权给第三方平台"), + + /** + * component req key is duplicated + */ + CODE_61008(61008, "第三方请求的key存在重复"), + + /** + * code is invalid + */ + CODE_61009(61009, "无效code"), + + /** + * code is expired + */ + CODE_61010(61010, "code已过期"), + + /** + * invalid component + */ + CODE_61011(61011, "无效的第三方平台"), + + /** + * invalid option name + */ + CODE_61012(61012, "无效的选项名称"), + + /** + * invalid option value + */ + CODE_61013(61013, "无效的选项值"), + + /** + * must use component token for component api + */ + CODE_61014(61014, "必须使用component接口的token"), + + /** + * must use biz account token for not component api + */ + CODE_61015(61015, "必须使用商业帐号token,而不是component api"), + + /** + * function category of API need be confirmed by component + */ + CODE_61016(61016, "function category of API need be confirmed by component"), + + /** + * function category is not authorized + */ + CODE_61017(61017, "function category is not authorized"), + + /** + * already confirm + */ + CODE_61018(61018, "已确认"), + + /** + * not need confirm + */ + CODE_61019(61019, "不需要确认"), + + /** + * err parameter + */ + CODE_61020(61020, "err parameter"), + + /** + * can't confirm + */ + CODE_61021(61021, "can't confirm"), + + /** + * can't resubmit + */ + CODE_61022(61022, "can't resubmit"), + + /** + * refresh_token is invalid + */ + CODE_61023(61023, "refresh_token is invalid"), + + /** + * must use api(api_component_token) to get token for component acct + */ + CODE_61024(61024, "must use api(api_component_token) to get token for component acct"), + + /** + * read-only option + */ + CODE_61025(61025, "read-only option"), + + /** + * register access deny + */ + CODE_61026(61026, "register access deny"), + + /** + * register limit exceed + */ + CODE_61027(61027, "register limit exceed"), + + /** + * component is unpublished + */ + CODE_61028(61028, "component is unpublished"), + + /** + * component need republish with base category + */ + CODE_61029(61029, "component need republish with base category"), + + /** + * component cancel authorization not allowed + */ + CODE_61030(61030, "component cancel authorization not allowed"), + + /** + * invalid realname type + */ + CODE_61051(61051, "invalid realname type"), + + /** + * need to be certified + */ + CODE_61052(61052, "need to be certified"), + + /** + * realname exceed limits + */ + CODE_61053(61053, "realname exceed limits"), + + /** + * realname in black list + */ + CODE_61054(61054, "realname in black list"), + + /** + * exceed quota per month + */ + CODE_61055(61055, "exceed quota per month"), + + /** + * copy_wx_verify is required option + */ + CODE_61056(61056, "copy_wx_verify is required option"), + + /** + * invalid ticket + */ + CODE_61058(61058, "invalid ticket"), + + /** + * overseas access deny + */ + CODE_61061(61061, "overseas access deny"), + + /** + * admin exceed limits + */ + CODE_61063(61063, "admin exceed limits"), + + /** + * admin in black list + */ + CODE_61064(61064, "admin in black list"), + + /** + * idcard exceed limits + */ + CODE_61065(61065, "idcard exceed limits"), + + /** + * idcard in black list + */ + CODE_61066(61066, "idcard in black list"), + + /** + * mobile exceed limits + */ + CODE_61067(61067, "mobile exceed limits"), + + /** + * mobile in black list + */ + CODE_61068(61068, "mobile in black list"), + + /** + * invalid admin + */ + CODE_61069(61069, "invalid admin"), + + /** + * name, idcard, wechat name not in accordance + */ + CODE_61070(61070, "name, idcard, wechat name not in accordance"), + + /** + * invalid url + */ + CODE_61100(61100, "invalid url"), + + /** + * invalid openid + */ + CODE_61101(61101, "invalid openid"), + + /** + * share relation not existed + */ + CODE_61102(61102, "share relation not existed"), + + /** + * product wording not set + */ + CODE_61200(61200, "product wording not set"), + + /** + * invalid base info + */ + CODE_61300(61300, "invalid base info"), + + /** + * invalid detail info + */ + CODE_61301(61301, "invalid detail info"), + + /** + * invalid action info + */ + CODE_61302(61302, "invalid action info"), + + /** + * brand info not exist + */ + CODE_61303(61303, "brand info not exist"), + + /** + * invalid product id + */ + CODE_61304(61304, "invalid product id"), + + /** + * invalid key info + */ + CODE_61305(61305, "invalid key info"), + + /** + * invalid appid + */ + CODE_61306(61306, "invalid appid"), + + /** + * invalid card id + */ + CODE_61307(61307, "invalid card id"), + + /** + * base info not exist + */ + CODE_61308(61308, "base info not exist"), + + /** + * detail info not exist + */ + CODE_61309(61309, "detail info not exist"), + + /** + * action info not exist + */ + CODE_61310(61310, "action info not exist"), + + /** + * invalid media info + */ + CODE_61311(61311, "invalid media info"), + + /** + * invalid buffer size + */ + CODE_61312(61312, "invalid buffer size"), + + /** + * invalid buffer + */ + CODE_61313(61313, "invalid buffer"), + + /** + * invalid qrcode extinfo + */ + CODE_61314(61314, "invalid qrcode extinfo"), + + /** + * invalid local ext info + */ + CODE_61315(61315, "invalid local ext info"), + + /** + * key conflict + */ + CODE_61316(61316, "key conflict"), + + /** + * ticket invalid + */ + CODE_61317(61317, "ticket invalid"), + + /** + * verify not pass + */ + CODE_61318(61318, "verify not pass"), + + /** + * category invalid + */ + CODE_61319(61319, "category invalid"), + + /** + * merchant info not exist + */ + CODE_61320(61320, "merchant info not exist"), + + /** + * cate id is a leaf node + */ + CODE_61321(61321, "cate id is a leaf node"), + + /** + * category id no permision + */ + CODE_61322(61322, "category id no permision"), + + /** + * barcode no permision + */ + CODE_61323(61323, "barcode no permision"), + + /** + * exceed max action num + */ + CODE_61324(61324, "exceed max action num"), + + /** + * brandinfo invalid store mgr type + */ + CODE_61325(61325, "brandinfo invalid store mgr type"), + + /** + * anti-spam blocked + */ + CODE_61326(61326, "anti-spam blocked"), + + /** + * comment reach limit + */ + CODE_61327(61327, "comment reach limit"), + + /** + * comment data is not the newest + */ + CODE_61328(61328, "comment data is not the newest"), + + /** + * comment hit ban word + */ + CODE_61329(61329, "comment hit ban word"), + + /** + * image already add + */ + CODE_61330(61330, "image already add"), + + /** + * image never add + */ + CODE_61331(61331, "image never add"), + + /** + * warning, image quanlity too low + */ + CODE_61332(61332, "warning, image quanlity too low"), + + /** + * warning, image simility too high + */ + CODE_61333(61333, "warning, image simility too high"), + + /** + * product not exists + */ + CODE_61334(61334, "product not exists"), + + /** + * key apply fail + */ + CODE_61335(61335, "key apply fail"), + + /** + * check status fail + */ + CODE_61336(61336, "check status fail"), + + /** + * product already exists + */ + CODE_61337(61337, "product already exists"), + + /** + * forbid delete + */ + CODE_61338(61338, "forbid delete"), + + /** + * firmcode claimed + */ + CODE_61339(61339, "firmcode claimed"), + + /** + * check firm info fail + */ + CODE_61340(61340, "check firm info fail"), + + /** + * too many white list uin + */ + CODE_61341(61341, "too many white list uin"), + + /** + * keystandard not match + */ + CODE_61342(61342, "keystandard not match"), + + /** + * keystandard error + */ + CODE_61343(61343, "keystandard error"), + + /** + * id map not exists + */ + CODE_61344(61344, "id map not exists"), + + /** + * invalid action code + */ + CODE_61345(61345, "invalid action code"), + + /** + * invalid actioninfo store + */ + CODE_61346(61346, "invalid actioninfo store"), + + /** + * invalid actioninfo media + */ + CODE_61347(61347, "invalid actioninfo media"), + + /** + * invalid actioninfo text + */ + CODE_61348(61348, "invalid actioninfo text"), + + /** + * invalid input data + */ + CODE_61350(61350, "invalid input data"), + + /** + * input data exceed max size + */ + CODE_61351(61351, "input data exceed max size"), + + /** + * kf_account error + */ + CODE_61400(61400, "kf_account error"), + + /** + * kf system alredy transfer + */ + CODE_61401(61401, "kf system alredy transfer"), + + /** + * 系统错误 (system error) + */ + CODE_61450(61450, "系统错误 (system error)"), + + /** + * 参数错误 (invalid parameter) + */ + CODE_61451(61451, "参数错误 (invalid parameter)"), + + /** + * 无效客服账号 (invalid kf_account) + */ + CODE_61452(61452, "无效客服账号 (invalid kf_account)"), + + /** + * 客服帐号已存在 (kf_account exsited) + */ + CODE_61453(61453, "客服帐号已存在 (kf_account exsited)"), + + /** + * 客服帐号名长度超过限制 ( 仅允许 10 个英文字符,不包括 @ 及 @ 后的公众号的微信号 )(invalid kf_acount length) + */ + CODE_61454(61454, "客服帐号名长度超过限制 ( 仅允许 10 个英文字符,不包括 @ 及 @ 后的公众号的微信号 )(invalid kf_acount length)"), + + /** + * 客服帐号名包含非法字符 ( 仅允许英文 + 数字 )(illegal character in kf_account) + */ + CODE_61455(61455, "客服帐号名包含非法字符 ( 仅允许英文 + 数字 )(illegal character in kf_account)"), + + /** + * 客服帐号个数超过限制 (10 个客服账号 )(kf_account count exceeded) + */ + CODE_61456(61456, "客服帐号个数超过限制 (10 个客服账号 )(kf_account count exceeded)"), + + /** + * 无效头像文件类型 (invalid file type) + */ + CODE_61457(61457, "无效头像文件类型 (invalid file type)"), + + /** + * 日期格式错误 date format error + */ + CODE_61500(61500, "日期格式错误"), + + /** + * date range error + */ + CODE_61501(61501, "date range error"), + + /** + * this is game miniprogram, data api is not supported + */ + CODE_61502(61502, "this is game miniprogram, data api is not supported"), + + /** + * data not ready, please try later + */ + CODE_61503(61503, "data not ready, please try later"), + + /** + * trying to access other's app + */ + CODE_62001(62001, "trying to access other's app"), + + /** + * app name already exists + */ + CODE_62002(62002, "app name already exists"), + + /** + * please provide at least one platform + */ + CODE_62003(62003, "please provide at least one platform"), + + /** + * invalid app name + */ + CODE_62004(62004, "invalid app name"), + + /** + * invalid app id + */ + CODE_62005(62005, "invalid app id"), + + /** + * 部分参数为空 some arguments is empty + */ + CODE_63001(63001, "部分参数为空"), + + /** + * 无效的签名 invalid signature + */ + CODE_63002(63002, "无效的签名"), + + /** + * invalid signature method + */ + CODE_63003(63003, "invalid signature method"), + + /** + * no authroize + */ + CODE_63004(63004, "no authroize"), + + /** + * gen ticket fail + */ + CODE_63149(63149, "gen ticket fail"), + + /** + * set ticket fail + */ + CODE_63152(63152, "set ticket fail"), + + /** + * shortid decode fail + */ + CODE_63153(63153, "shortid decode fail"), + + /** + * invalid status + */ + CODE_63154(63154, "invalid status"), + + /** + * invalid color + */ + CODE_63155(63155, "invalid color"), + + /** + * invalid tag + */ + CODE_63156(63156, "invalid tag"), + + /** + * invalid recommend + */ + CODE_63157(63157, "invalid recommend"), + + /** + * branditem out of limits + */ + CODE_63158(63158, "branditem out of limits"), + + /** + * retail_price empty + */ + CODE_63159(63159, "retail_price empty"), + + /** + * priceinfo invalid + */ + CODE_63160(63160, "priceinfo invalid"), + + /** + * antifake module num limit + */ + CODE_63161(63161, "antifake module num limit"), + + /** + * antifake native_type err + */ + CODE_63162(63162, "antifake native_type err"), + + /** + * antifake link not exists + */ + CODE_63163(63163, "antifake link not exists"), + + /** + * module type not exist + */ + CODE_63164(63164, "module type not exist"), + + /** + * module info not exist + */ + CODE_63165(63165, "module info not exist"), + + /** + * item is beding verified + */ + CODE_63166(63166, "item is beding verified"), + + /** + * item not published + */ + CODE_63167(63167, "item not published"), + + /** + * verify not pass + */ + CODE_63168(63168, "verify not pass"), + + /** + * already published + */ + CODE_63169(63169, "already published"), + + /** + * only banner or media + */ + CODE_63170(63170, "only banner or media"), + + /** + * card num limit + */ + CODE_63171(63171, "card num limit"), + + /** + * user num limit + */ + CODE_63172(63172, "user num limit"), + + /** + * text num limit + */ + CODE_63173(63173, "text num limit"), + + /** + * link card user sum limit + */ + CODE_63174(63174, "link card user sum limit"), + + /** + * detail info error + */ + CODE_63175(63175, "detail info error"), + + /** + * not this type + */ + CODE_63176(63176, "not this type"), + + /** + * src or secretkey or version or expired_time is wrong + */ + CODE_63177(63177, "src or secretkey or version or expired_time is wrong"), + + /** + * appid wrong + */ + CODE_63178(63178, "appid wrong"), + + /** + * openid num limit + */ + CODE_63179(63179, "openid num limit"), + + /** + * this app msg not found + */ + CODE_63180(63180, "this app msg not found"), + + /** + * get history app msg end + */ + CODE_63181(63181, "get history app msg end"), + + /** + * openid_list empty + */ + CODE_63182(63182, "openid_list empty"), + + /** + * unknown deeplink type + */ + CODE_65001(65001, "unknown deeplink type"), + + /** + * deeplink unauthorized + */ + CODE_65002(65002, "deeplink unauthorized"), + + /** + * bad deeplink + */ + CODE_65003(65003, "bad deeplink"), + + /** + * deeplinks of the very type are supposed to have short-life + */ + CODE_65004(65004, "deeplinks of the very type are supposed to have short-life"), + + /** + * invalid categories + */ + CODE_65104(65104, "invalid categories"), + + /** + * invalid photo url + */ + CODE_65105(65105, "invalid photo url"), + + /** + * poi audit state must be approved + */ + CODE_65106(65106, "poi audit state must be approved"), + + /** + * poi not allowed modify now + */ + CODE_65107(65107, "poi not allowed modify now"), + + /** + * invalid business name + */ + CODE_65109(65109, "invalid business name"), + + /** + * invalid address + */ + CODE_65110(65110, "invalid address"), + + /** + * invalid telephone + */ + CODE_65111(65111, "invalid telephone"), + + /** + * invalid city + */ + CODE_65112(65112, "invalid city"), + + /** + * invalid province + */ + CODE_65113(65113, "invalid province"), + + /** + * photo list empty + */ + CODE_65114(65114, "photo list empty"), + + /** + * poi_id is not exist + */ + CODE_65115(65115, "poi_id is not exist"), + + /** + * poi has been deleted + */ + CODE_65116(65116, "poi has been deleted"), + + /** + * cannot delete poi + */ + CODE_65117(65117, "cannot delete poi"), + + /** + * store status is invalid + */ + CODE_65118(65118, "store status is invalid"), + + /** + * lack of qualification for relevant principals + */ + CODE_65119(65119, "lack of qualification for relevant principals"), + + /** + * category info is not found + */ + CODE_65120(65120, "category info is not found"), + + /** + * room_name is empty, please check your input + */ + CODE_65201(65201, "room_name is empty, please check your input"), + + /** + * user_id is empty, please check your input + */ + CODE_65202(65202, "user_id is empty, please check your input"), + + /** + * invalid check ticket + */ + CODE_65203(65203, "invalid check ticket"), + + /** + * invalid check ticket opt code + */ + CODE_65204(65204, "invalid check ticket opt code"), + + /** + * check ticket out of time + */ + CODE_65205(65205, "check ticket out of time"), + + /** + * 不存在此 menuid 对应的个性化菜单 this menu is not conditionalmenu + */ + CODE_65301(65301, "不存在此 menuid 对应的个性化菜单"), + + /** + * 没有相应的用户 no such user + */ + CODE_65302(65302, "没有相应的用户"), + + /** + * 没有默认菜单,不能创建个性化菜单 there is no selfmenu, please create selfmenu first + */ + CODE_65303(65303, "没有默认菜单,不能创建个性化菜单"), + + /** + * MatchRule 信息为空 match rule empty + */ + CODE_65304(65304, "MatchRule 信息为空"), + + /** + * 个性化菜单数量受限 menu count limit + */ + CODE_65305(65305, "个性化菜单数量受限"), + + /** + * 不支持个性化菜单的帐号 conditional menu not support + */ + CODE_65306(65306, "不支持个性化菜单的帐号"), + + /** + * 个性化菜单信息为空 conditional menu is empty + */ + CODE_65307(65307, "个性化菜单信息为空"), + + /** + * 包含没有响应类型的 button exist empty button act + */ + CODE_65308(65308, "包含没有响应类型的 button"), + + /** + * 个性化菜单开关处于关闭状态 conditional menu switch is closed + */ + CODE_65309(65309, "个性化菜单开关处于关闭状态"), + + /** + * 填写了省份或城市信息,国家信息不能为空 region info: country is empty + */ + CODE_65310(65310, "填写了省份或城市信息,国家信息不能为空"), + + /** + * 填写了城市信息,省份信息不能为空 region info: province is empty + */ + CODE_65311(65311, "填写了城市信息,省份信息不能为空"), + + /** + * 不合法的国家信息 invalid country info + */ + CODE_65312(65312, "不合法的国家信息"), + + /** + * 不合法的省份信息 invalid province info + */ + CODE_65313(65313, "不合法的省份信息"), + + /** + * 不合法的城市信息 invalid city info + */ + CODE_65314(65314, "不合法的城市信息"), + + /** + * not fans + */ + CODE_65315(65315, "not fans"), + + /** + * 该公众号的菜单设置了过多的域名外跳(最多跳转到 3 个域名的链接) domain count reach limit + */ + CODE_65316(65316, "该公众号的菜单设置了过多的域名外跳(最多跳转到 3 个域名的链接)"), + + /** + * 不合法的 URL contain invalid url + */ + CODE_65317(65317, "不合法的 URL"), + + /** + * must use utf-8 charset + */ + CODE_65318(65318, "must use utf-8 charset"), + + /** + * not allow to create menu + */ + CODE_65319(65319, "not allow to create menu"), + + /** + * please enable new custom service, or wait for a while if you have enabled + */ + CODE_65400(65400, "please enable new custom service, or wait for a while if you have enabled"), + + /** + * invalid custom service account + */ + CODE_65401(65401, "invalid custom service account"), + + /** + * the custom service account need to bind a wechat user + */ + CODE_65402(65402, "the custom service account need to bind a wechat user"), + + /** + * illegal nickname + */ + CODE_65403(65403, "illegal nickname"), + + /** + * illegal custom service account + */ + CODE_65404(65404, "illegal custom service account"), + + /** + * custom service account number reach limit + */ + CODE_65405(65405, "custom service account number reach limit"), + + /** + * custom service account exists + */ + CODE_65406(65406, "custom service account exists"), + + /** + * the wechat user have been one of your workers + */ + CODE_65407(65407, "the wechat user have been one of your workers"), + + /** + * you have already invited the wechat user + */ + CODE_65408(65408, "you have already invited the wechat user"), + + /** + * wechat account invalid + */ + CODE_65409(65409, "wechat account invalid"), + + /** + * too many custom service accounts bound by the worker + */ + CODE_65410(65410, "too many custom service accounts bound by the worker"), + + /** + * a effective invitation to bind the custom service account exists + */ + CODE_65411(65411, "a effective invitation to bind the custom service account exists"), + + /** + * the custom service account have been bound by a wechat user + */ + CODE_65412(65412, "the custom service account have been bound by a wechat user"), + + /** + * no effective session for the customer + */ + CODE_65413(65413, "no effective session for the customer"), + + /** + * another worker is serving the customer + */ + CODE_65414(65414, "another worker is serving the customer"), + + /** + * the worker is not online + */ + CODE_65415(65415, "the worker is not online"), + + /** + * param invalid, please check + */ + CODE_65416(65416, "param invalid, please check"), + + /** + * it is too long from the starttime to endtime + */ + CODE_65417(65417, "it is too long from the starttime to endtime"), + + /** + * homepage not exists + */ + CODE_65450(65450, "homepage not exists"), + + /** + * invalid store type + */ + CODE_68002(68002, "invalid store type"), + + /** + * invalid store name + */ + CODE_68003(68003, "invalid store name"), + + /** + * invalid store wxa path + */ + CODE_68004(68004, "invalid store wxa path"), + + /** + * miss store wxa path + */ + CODE_68005(68005, "miss store wxa path"), + + /** + * invalid kefu type + */ + CODE_68006(68006, "invalid kefu type"), + + /** + * invalid kefu wxa path + */ + CODE_68007(68007, "invalid kefu wxa path"), + + /** + * invalid kefu phone number + */ + CODE_68008(68008, "invalid kefu phone number"), + + /** + * invalid sub mch id + */ + CODE_68009(68009, "invalid sub mch id"), + + /** + * store id has exist + */ + CODE_68010(68010, "store id has exist"), + + /** + * miss store name + */ + CODE_68011(68011, "miss store name"), + + /** + * miss create time + */ + CODE_68012(68012, "miss create time"), + + /** + * invalid status + */ + CODE_68013(68013, "invalid status"), + + /** + * invalid receiver info + */ + CODE_68014(68014, "invalid receiver info"), + + /** + * invalid product + */ + CODE_68015(68015, "invalid product"), + + /** + * invalid pay type + */ + CODE_68016(68016, "invalid pay type"), + + /** + * invalid fast mail no + */ + CODE_68017(68017, "invalid fast mail no"), + + /** + * invalid busi id + */ + CODE_68018(68018, "invalid busi id"), + + /** + * miss product sku + */ + CODE_68019(68019, "miss product sku"), + + /** + * invalid service type + */ + CODE_68020(68020, "invalid service type"), + + /** + * invalid service status + */ + CODE_68021(68021, "invalid service status"), + + /** + * invalid service_id + */ + CODE_68022(68022, "invalid service_id"), + + /** + * service_id has exist + */ + CODE_68023(68023, "service_id has exist"), + + /** + * miss service wxa path + */ + CODE_68024(68024, "miss service wxa path"), + + /** + * invalid product sku + */ + CODE_68025(68025, "invalid product sku"), + + /** + * invalid product spu + */ + CODE_68026(68026, "invalid product spu"), + + /** + * miss product spu + */ + CODE_68027(68027, "miss product spu"), + + /** + * can not find product spu and spu in order list + */ + CODE_68028(68028, "can not find product spu and spu in order list"), + + /** + * sku and spu duplicated + */ + CODE_68029(68029, "sku and spu duplicated"), + + /** + * busi_id has exist + */ + CODE_68030(68030, "busi_id has exist"), + + /** + * update fail + */ + CODE_68031(68031, "update fail"), + + /** + * busi_id not exist + */ + CODE_68032(68032, "busi_id not exist"), + + /** + * store no exist + */ + CODE_68033(68033, "store no exist"), + + /** + * miss product number + */ + CODE_68034(68034, "miss product number"), + + /** + * miss wxa order detail path + */ + CODE_68035(68035, "miss wxa order detail path"), + + /** + * there is no enough products to refund + */ + CODE_68036(68036, "there is no enough products to refund"), + + /** + * invalid refund info + */ + CODE_68037(68037, "invalid refund info"), + + /** + * shipped but no fast mail info + */ + CODE_68038(68038, "shipped but no fast mail info"), + + /** + * invalid wechat pay no + */ + CODE_68039(68039, "invalid wechat pay no"), + + /** + * all product has been refunded, the order can not be finished + */ + CODE_68040(68040, "all product has been refunded, the order can not be finished"), + + /** + * invalid service create time, it must bigger than the time of order + */ + CODE_68041(68041, "invalid service create time, it must bigger than the time of order"), + + /** + * invalid total cost, it must be smaller than the sum of product and shipping cost + */ + CODE_68042(68042, "invalid total cost, it must be smaller than the sum of product and shipping cost"), + + /** + * invalid role + */ + CODE_68043(68043, "invalid role"), + + /** + * invalid service_available args + */ + CODE_68044(68044, "invalid service_available args"), + + /** + * invalid order type + */ + CODE_68045(68045, "invalid order type"), + + /** + * invalid order deliver type + */ + CODE_68046(68046, "invalid order deliver type"), + + /** + * require store_id + */ + CODE_68500(68500, "require store_id"), + + /** + * invalid store_id + */ + CODE_68501(68501, "invalid store_id"), + + /** + * invalid parameter, parameter is zero or missing + */ + CODE_71001(71001, "invalid parameter, parameter is zero or missing"), + + /** + * invalid orderid, may be the other parameter not fit with orderid + */ + CODE_71002(71002, "invalid orderid, may be the other parameter not fit with orderid"), + + /** + * coin not enough + */ + CODE_71003(71003, "coin not enough"), + + /** + * card is expired + */ + CODE_71004(71004, "card is expired"), + + /** + * limit exe count + */ + CODE_71005(71005, "limit exe count"), + + /** + * limit coin count, 1 <= coin_count <= 100000 + */ + CODE_71006(71006, "limit coin count, 1 <= coin_count <= 100000"), + + /** + * order finish + */ + CODE_71007(71007, "order finish"), + + /** + * order time out + */ + CODE_71008(71008, "order time out"), + + /** + * no match card + */ + CODE_72001(72001, "no match card"), + + /** + * mchid is not bind appid + */ + CODE_72002(72002, "mchid is not bind appid"), + + /** + * invalid card type, need member card + */ + CODE_72003(72003, "invalid card type, need member card"), + + /** + * mchid is occupied by the other appid + */ + CODE_72004(72004, "mchid is occupied by the other appid"), + + /** + * out of mchid size limit + */ + CODE_72005(72005, "out of mchid size limit"), + + /** + * invald title + */ + CODE_72006(72006, "invald title"), + + /** + * invalid reduce cost, can not less than 100 + */ + CODE_72007(72007, "invalid reduce cost, can not less than 100"), + + /** + * invalid least cost, most larger than reduce cost + */ + CODE_72008(72008, "invalid least cost, most larger than reduce cost"), + + /** + * invalid get limit, can not over 50 + */ + CODE_72009(72009, "invalid get limit, can not over 50"), + + /** + * invalid mchid + */ + CODE_72010(72010, "invalid mchid"), + + /** + * invalid activate_ticket.Maybe this ticket is not belong this AppId + */ + CODE_72011(72011, "invalid activate_ticket.Maybe this ticket is not belong this AppId"), + + /** + * activate_ticket has been expired + */ + CODE_72012(72012, "activate_ticket has been expired"), + + /** + * unauthorized order_id or authorization is expired + */ + CODE_72013(72013, "unauthorized order_id or authorization is expired"), + + /** + * task card share stock can not modify stock + */ + CODE_72014(72014, "task card share stock can not modify stock"), + + /** + * unauthorized create invoice + */ + CODE_72015(72015, "unauthorized create invoice"), + + /** + * unauthorized create member card + */ + CODE_72016(72016, "unauthorized create member card"), + + /** + * invalid invoice title + */ + CODE_72017(72017, "invalid invoice title"), + + /** + * duplicate order id, invoice had inserted to user + */ + CODE_72018(72018, "duplicate order id, invoice had inserted to user"), + + /** + * limit msg operation card list size, must <= 5 + */ + CODE_72019(72019, "limit msg operation card list size, must <= 5"), + + /** + * limit consume in use limit + */ + CODE_72020(72020, "limit consume in use limit"), + + /** + * unauthorized create general card + */ + CODE_72021(72021, "unauthorized create general card"), + + /** + * user unexpected, please add user to white list + */ + CODE_72022(72022, "user unexpected, please add user to white list"), + + /** + * invoice has been lock by others + */ + CODE_72023(72023, "invoice has been lock by others"), + + /** + * invoice status error + */ + CODE_72024(72024, "invoice status error"), + + /** + * invoice token error + */ + CODE_72025(72025, "invoice token error"), + + /** + * need set wx_activate true + */ + CODE_72026(72026, "need set wx_activate true"), + + /** + * invoice action error + */ + CODE_72027(72027, "invoice action error"), + + /** + * invoice never set pay mch info + */ + CODE_72028(72028, "invoice never set pay mch info"), + + /** + * invoice never set auth field + */ + CODE_72029(72029, "invoice never set auth field"), + + /** + * invalid mchid + */ + CODE_72030(72030, "invalid mchid"), + + /** + * invalid params + */ + CODE_72031(72031, "invalid params"), + + /** + * pay gift card rule expired + */ + CODE_72032(72032, "pay gift card rule expired"), + + /** + * pay gift card rule status err + */ + CODE_72033(72033, "pay gift card rule status err"), + + /** + * invlid rule id + */ + CODE_72034(72034, "invlid rule id"), + + /** + * biz reject insert + */ + CODE_72035(72035, "biz reject insert"), + + /** + * invoice is busy, try again please + */ + CODE_72036(72036, "invoice is busy, try again please"), + + /** + * invoice owner error + */ + CODE_72037(72037, "invoice owner error"), + + /** + * invoice order never auth + */ + CODE_72038(72038, "invoice order never auth"), + + /** + * invoice must be lock first + */ + CODE_72039(72039, "invoice must be lock first"), + + /** + * invoice pdf error + */ + CODE_72040(72040, "invoice pdf error"), + + /** + * billing_code and billing_no invalid + */ + CODE_72041(72041, "billing_code and billing_no invalid"), + + /** + * billing_code and billing_no repeated + */ + CODE_72042(72042, "billing_code and billing_no repeated"), + + /** + * billing_code or billing_no size error + */ + CODE_72043(72043, "billing_code or billing_no size error"), + + /** + * scan text out of time + */ + CODE_72044(72044, "scan text out of time"), + + /** + * check_code is empty + */ + CODE_72045(72045, "check_code is empty"), + + /** + * pdf_url is invalid + */ + CODE_72046(72046, "pdf_url is invalid"), + + /** + * pdf billing_code or pdf billing_no is error + */ + CODE_72047(72047, "pdf billing_code or pdf billing_no is error"), + + /** + * insert too many invoice, need auth again + */ + CODE_72048(72048, "insert too many invoice, need auth again"), + + /** + * never auth + */ + CODE_72049(72049, "never auth"), + + /** + * auth expired, need auth again + */ + CODE_72050(72050, "auth expired, need auth again"), + + /** + * app type error + */ + CODE_72051(72051, "app type error"), + + /** + * get too many invoice + */ + CODE_72052(72052, "get too many invoice"), + + /** + * user never auth + */ + CODE_72053(72053, "user never auth"), + + /** + * invoices is inserting, wait a moment please + */ + CODE_72054(72054, "invoices is inserting, wait a moment please"), + + /** + * too many invoices + */ + CODE_72055(72055, "too many invoices"), + + /** + * order_id repeated, please check order_id + */ + CODE_72056(72056, "order_id repeated, please check order_id"), + + /** + * today insert limit + */ + CODE_72057(72057, "today insert limit"), + + /** + * callback biz error + */ + CODE_72058(72058, "callback biz error"), + + /** + * this invoice is giving to others, wait a moment please + */ + CODE_72059(72059, "this invoice is giving to others, wait a moment please"), + + /** + * this invoice has been cancelled, check the reimburse_status please + */ + CODE_72060(72060, "this invoice has been cancelled, check the reimburse_status please"), + + /** + * this invoice has been closed, check the reimburse_status please + */ + CODE_72061(72061, "this invoice has been closed, check the reimburse_status please"), + + /** + * this code_auth_key is limited, try other code_auth_key please + */ + CODE_72062(72062, "this code_auth_key is limited, try other code_auth_key please"), + + /** + * biz contact is empty, set contact first please + */ + CODE_72063(72063, "biz contact is empty, set contact first please"), + + /** + * tbc error + */ + CODE_72064(72064, "tbc error"), + + /** + * tbc logic error + */ + CODE_72065(72065, "tbc logic error"), + + /** + * the card is send for advertisement, not allow modify time and budget + */ + CODE_72066(72066, "the card is send for advertisement, not allow modify time and budget"), + + /** + * BatchInsertAuthKey_Expired + */ + CODE_72067(72067, "BatchInsertAuthKey_Expired"), + + /** + * BatchInsertAuthKey_Owner + */ + CODE_72068(72068, "BatchInsertAuthKey_Owner"), + + /** + * BATCHTASKRUN_ERROR + */ + CODE_72069(72069, "BATCHTASKRUN_ERROR"), + + /** + * BIZ_TITLE_KEY_OUT_TIME + */ + CODE_72070(72070, "BIZ_TITLE_KEY_OUT_TIME"), + + /** + * Discern_GaoPeng_Error + */ + CODE_72071(72071, "Discern_GaoPeng_Error"), + + /** + * Discern_Type_Error + */ + CODE_72072(72072, "Discern_Type_Error"), + + /** + * Fee_Error + */ + CODE_72073(72073, "Fee_Error"), + + /** + * HAS_Auth + */ + CODE_72074(72074, "HAS_Auth"), + + /** + * HAS_SEND + */ + CODE_72075(72075, "HAS_SEND"), + + /** + * INVOICESIGN + */ + CODE_72076(72076, "INVOICESIGN"), + + /** + * KEY_DELETED + */ + CODE_72077(72077, "KEY_DELETED"), + + /** + * KEY_EXPIRED + */ + CODE_72078(72078, "KEY_EXPIRED"), + + /** + * MOUNT_ERROR + */ + CODE_72079(72079, "MOUNT_ERROR"), + + /** + * NO_FOUND + */ + CODE_72080(72080, "NO_FOUND"), + + /** + * No_Pull_Pdf + */ + CODE_72081(72081, "No_Pull_Pdf"), + + /** + * PDF_CHECK_ERROR + */ + CODE_72082(72082, "PDF_CHECK_ERROR"), + + /** + * PULL_PDF_FAIL + */ + CODE_72083(72083, "PULL_PDF_FAIL"), + + /** + * PUSH_BIZ_EMPTY + */ + CODE_72084(72084, "PUSH_BIZ_EMPTY"), + + /** + * SDK_APPID_ERROR + */ + CODE_72085(72085, "SDK_APPID_ERROR"), + + /** + * SDK_BIZ_ERROR + */ + CODE_72086(72086, "SDK_BIZ_ERROR"), + + /** + * SDK_URL_ERROR + */ + CODE_72087(72087, "SDK_URL_ERROR"), + + /** + * Search_Title_Fail + */ + CODE_72088(72088, "Search_Title_Fail"), + + /** + * TITLE_BUSY + */ + CODE_72089(72089, "TITLE_BUSY"), + + /** + * TITLE_NO_FOUND + */ + CODE_72090(72090, "TITLE_NO_FOUND"), + + /** + * TOKEN_ERR + */ + CODE_72091(72091, "TOKEN_ERR"), + + /** + * USER_TITLE_NOT_FOUND + */ + CODE_72092(72092, "USER_TITLE_NOT_FOUND"), + + /** + * Verify_3rd_Fail + */ + CODE_72093(72093, "Verify_3rd_Fail"), + + /** + * sys error make out invoice failed + */ + CODE_73000(73000, "sys error make out invoice failed"), + + /** + * wxopenid error + */ + CODE_73001(73001, "wxopenid error"), + + /** + * ddh orderid empty + */ + CODE_73002(73002, "ddh orderid empty"), + + /** + * wxopenid empty + */ + CODE_73003(73003, "wxopenid empty"), + + /** + * fpqqlsh empty + */ + CODE_73004(73004, "fpqqlsh empty"), + + /** + * not a commercial + */ + CODE_73005(73005, "not a commercial"), + + /** + * kplx empty + */ + CODE_73006(73006, "kplx empty"), + + /** + * nsrmc empty + */ + CODE_73007(73007, "nsrmc empty"), + + /** + * nsrdz empty + */ + CODE_73008(73008, "nsrdz empty"), + + /** + * nsrdh empty + */ + CODE_73009(73009, "nsrdh empty"), + + /** + * ghfmc empty + */ + CODE_73010(73010, "ghfmc empty"), + + /** + * kpr empty + */ + CODE_73011(73011, "kpr empty"), + + /** + * jshj empty + */ + CODE_73012(73012, "jshj empty"), + + /** + * hjje empty + */ + CODE_73013(73013, "hjje empty"), + + /** + * hjse empty + */ + CODE_73014(73014, "hjse empty"), + + /** + * hylx empty + */ + CODE_73015(73015, "hylx empty"), + + /** + * nsrsbh empty + */ + CODE_73016(73016, "nsrsbh empty"), + + /** + * kaipiao plat error + */ + CODE_73100(73100, "kaipiao plat error"), + + /** + * nsrsbh not cmp + */ + CODE_73101(73101, "nsrsbh not cmp"), + + /** + * invalid wxa appid in url_cell, wxa appid is need to bind biz appid + */ + CODE_73103(73103, "invalid wxa appid in url_cell, wxa appid is need to bind biz appid"), + + /** + * reach frequency limit + */ + CODE_73104(73104, "reach frequency limit"), + + /** + * Kp plat make invoice timeout, please try again with the same fpqqlsh + */ + CODE_73105(73105, "Kp plat make invoice timeout, please try again with the same fpqqlsh"), + + /** + * Fpqqlsh exist with different ddh + */ + CODE_73106(73106, "Fpqqlsh exist with different ddh"), + + /** + * Fpqqlsh is processing, please wait and query later + */ + CODE_73107(73107, "Fpqqlsh is processing, please wait and query later"), + + /** + * This ddh with other fpqqlsh already exist + */ + CODE_73108(73108, "This ddh with other fpqqlsh already exist"), + + /** + * This Fpqqlsh not exist in kpplat + */ + CODE_73109(73109, "This Fpqqlsh not exist in kpplat"), + + /** + * get card detail by card id and code fail + */ + CODE_73200(73200, "get card detail by card id and code fail"), + + /** + * get cloud invoice record fail + */ + CODE_73201(73201, "get cloud invoice record fail"), + + /** + * get appinfo fail + */ + CODE_73202(73202, "get appinfo fail"), + + /** + * get invoice category or rule kv error + */ + CODE_73203(73203, "get invoice category or rule kv error"), + + /** + * request card not exist + */ + CODE_73204(73204, "request card not exist"), + + /** + * 朋友的券玩法升级中,当前暂停创建,请创建其他类型卡券 + */ + CODE_73205(73205, "朋友的券玩法升级中,当前暂停创建,请创建其他类型卡券"), + + /** + * 朋友的券玩法升级中,当前暂停券点充值,请创建其他类型卡券 + */ + CODE_73206(73206, "朋友的券玩法升级中,当前暂停券点充值,请创建其他类型卡券"), + + /** + * 朋友的券玩法升级中,当前暂停开通券点账户 + */ + CODE_73207(73207, "朋友的券玩法升级中,当前暂停开通券点账户"), + + /** + * 朋友的券玩法升级中,当前不支持修改库存 + */ + CODE_73208(73208, "朋友的券玩法升级中,当前不支持修改库存"), + + /** + * 朋友的券玩法升级中,当前不支持修改有效期 + */ + CODE_73209(73209, "朋友的券玩法升级中,当前不支持修改有效期"), + + /** + * 当前批次不支持修改卡券批次库存 + */ + CODE_73210(73210, "当前批次不支持修改卡券批次库存"), + + /** + * 不再支持配置网页链接跳转,请选择小程序替代 + */ + CODE_73211(73211, "不再支持配置网页链接跳转,请选择小程序替代"), + + /** + * unauthorized backup member + */ + CODE_73212(73212, "unauthorized backup member"), + + /** + * invalid code type + */ + CODE_73213(73213, "invalid code type"), + + /** + * the user is already a member + */ + CODE_73214(73214, "the user is already a member"), + + /** + * 支付打通券能力已下线,请直接使用微信支付代金券API:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/marketing/convention/chapter1_1.shtml + */ + CODE_73215(73215, "支付打通券能力已下线,请直接使用微信支付代金券API:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/marketing/convention/chapter1_1.shtml"), + + /** + * 不合法的按钮名字,请从中选择一个:使用礼品卡/立即使用/去点外卖 + */ + CODE_73216(73216, "不合法的按钮名字,请从中选择一个:使用礼品卡/立即使用/去点外卖"), + + /** + * 礼品卡本身没有设置appname和path,不允许在修改接口设置 + */ + CODE_73217(73217, "礼品卡本身没有设置appname和path,不允许在修改接口设置"), + + /** + * 未授权使用礼品卡落地页跳转小程序功能 + */ + CODE_73218(73218, "未授权使用礼品卡落地页跳转小程序功能"), + + /** + * not find this wx_hotel_id info + */ + CODE_74000(74000, "not find this wx_hotel_id info"), + + /** + * request some param empty + */ + CODE_74001(74001, "request some param empty"), + + /** + * request some param error + */ + CODE_74002(74002, "request some param error"), + + /** + * request some param error + */ + CODE_74003(74003, "request some param error"), + + /** + * datetime error + */ + CODE_74004(74004, "datetime error"), + + /** + * checkin mode error + */ + CODE_74005(74005, "checkin mode error"), + + /** + * carid from error + */ + CODE_74007(74007, "carid from error"), + + /** + * this hotel routecode not exist + */ + CODE_74008(74008, "this hotel routecode not exist"), + + /** + * this hotel routecode info error contract developer + */ + CODE_74009(74009, "this hotel routecode info error contract developer"), + + /** + * maybe not support report mode + */ + CODE_74010(74010, "maybe not support report mode"), + + /** + * pic deocde not ok maybe its not good picdata + */ + CODE_74011(74011, "pic deocde not ok maybe its not good picdata"), + + /** + * verify sys erro + */ + CODE_74021(74021, "verify sys erro"), + + /** + * inner police erro + */ + CODE_74022(74022, "inner police erro"), + + /** + * unable to detect the face + */ + CODE_74023(74023, "unable to detect the face"), + + /** + * report checkin 2 lvye sys erro + */ + CODE_74040(74040, "report checkin 2 lvye sys erro"), + + /** + * report checkou 2 lvye sys erro + */ + CODE_74041(74041, "report checkou 2 lvye sys erro"), + + /** + * some param emtpy please check + */ + CODE_75001(75001, "some param emtpy please check"), + + /** + * param illegal please check + */ + CODE_75002(75002, "param illegal please check"), + + /** + * sys error kv store error + */ + CODE_75003(75003, "sys error kv store error"), + + /** + * sys error kvstring store error + */ + CODE_75004(75004, "sys error kvstring store error"), + + /** + * product not exist please check your product_id + */ + CODE_75005(75005, "product not exist please check your product_id"), + + /** + * order not exist please check order_id and buyer_appid + */ + CODE_75006(75006, "order not exist please check order_id and buyer_appid"), + + /** + * do not allow this status to change please check this order_id status now + */ + CODE_75007(75007, "do not allow this status to change please check this order_id status now"), + + /** + * product has exist please use new id + */ + CODE_75008(75008, "product has exist please use new id"), + + /** + * notify order status failed + */ + CODE_75009(75009, "notify order status failed"), + + /** + * buyer bussiness info not exist + */ + CODE_75010(75010, "buyer bussiness info not exist"), + + /** + * you had registered + */ + CODE_75011(75011, "you had registered"), + + /** + * store image key to kv error, please try again + */ + CODE_75012(75012, "store image key to kv error, please try again"), + + /** + * get image fail, please check you image key + */ + CODE_75013(75013, "get image fail, please check you image key"), + + /** + * this key is not belong to you + */ + CODE_75014(75014, "this key is not belong to you"), + + /** + * this key is expired + */ + CODE_75015(75015, "this key is expired"), + + /** + * encrypt decode key fail + */ + CODE_75016(75016, "encrypt decode key fail"), + + /** + * encrypt encode key fail + */ + CODE_75017(75017, "encrypt encode key fail"), + + /** + * bind buyer business info fail please contact us + */ + CODE_75018(75018, "bind buyer business info fail please contact us"), + + /** + * this key is empty, user may not upload file + */ + CODE_75019(75019, "this key is empty, user may not upload file"), + + /** + * 系统错误,请稍后再试 + */ + CODE_80000(80000, "系统错误,请稍后再试"), + + /** + * 参数格式校验错误 + */ + CODE_80001(80001, "参数格式校验错误"), + + /** + * 签名失败 + */ + CODE_80002(80002, "签名失败"), + + /** + * 该日期订单未生成 + */ + CODE_80003(80003, "该日期订单未生成"), + + /** + * 用户未绑卡 + */ + CODE_80004(80004, "用户未绑卡"), + + /** + * 姓名不符 + */ + CODE_80005(80005, "姓名不符"), + + /** + * 身份证不符 + */ + CODE_80006(80006, "身份证不符"), + + /** + * 获取城市信息失败 + */ + CODE_80007(80007, "获取城市信息失败"), + + /** + * 未找到指定少儿信息 + */ + CODE_80008(80008, "未找到指定少儿信息"), + + /** + * 少儿身份证不符 + */ + CODE_80009(80009, "少儿身份证不符"), + + /** + * 少儿未绑定 + */ + CODE_80010(80010, "少儿未绑定"), + + /** + * 签约号不符 + */ + CODE_80011(80011, "签约号不符"), + + /** + * 该地区局方配置不存在 + */ + CODE_80012(80012, "该地区局方配置不存在"), + + /** + * 调用方appid与局方配置不匹配 + */ + CODE_80013(80013, "调用方appid与局方配置不匹配"), + + /** + * 获取消息账号失败 + */ + CODE_80014(80014, "获取消息账号失败"), + + /** + * 非法的插件版本 + */ + CODE_80066(80066, "非法的插件版本"), + + /** + * 找不到使用的插件 + */ + CODE_80067(80067, "找不到使用的插件"), + + /** + * 没有权限使用该插件 + */ + CODE_80082(80082, "没有权限使用该插件"), + + /** + * 商家未接入 + */ + CODE_80101(80101, "商家未接入"), + + /** + * 实名校验code不存在 + */ + CODE_80111(80111, "实名校验code不存在"), + + /** + * code并发冲突 + */ + CODE_80112(80112, "code并发冲突"), + + /** + * 无效code + */ + CODE_80113(80113, "无效code"), + + /** + * report_type无效 + */ + CODE_80201(80201, "report_type无效"), + + /** + * service_type无效 + */ + CODE_80202(80202, "service_type无效"), + + /** + * 申请单不存在 + */ + CODE_80300(80300, "申请单不存在"), + + /** + * 申请单不属于该账号 + */ + CODE_80301(80301, "申请单不属于该账号"), + + /** + * 激活号段有重叠 + */ + CODE_80302(80302, "激活号段有重叠"), + + /** + * 码格式错误 + */ + CODE_80303(80303, "码格式错误"), + + /** + * 该码未激活 + */ + CODE_80304(80304, "该码未激活"), + + /** + * 激活失败 + */ + CODE_80305(80305, "激活失败"), + + /** + * 码索引超出申请范围 + */ + CODE_80306(80306, "码索引超出申请范围"), + + /** + * 申请已存在 + */ + CODE_80307(80307, "申请已存在"), + + /** + * 子任务未完成 + */ + CODE_80308(80308, "子任务未完成"), + + /** + * 子任务文件过期 + */ + CODE_80309(80309, "子任务文件过期"), + + /** + * 子任务不存在 + */ + CODE_80310(80310, "子任务不存在"), + + /** + * 获取文件失败 + */ + CODE_80311(80311, "获取文件失败"), + + /** + * 加密数据失败 + */ + CODE_80312(80312, "加密数据失败"), + + /** + * 加密数据密钥不存在,请联系接口人申请 + */ + CODE_80313(80313, "加密数据密钥不存在,请联系接口人申请"), + + /** + * can not set page_id in AddGiftCardPage + */ + CODE_81000(81000, "can not set page_id in AddGiftCardPage"), + + /** + * card_list is empty + */ + CODE_81001(81001, "card_list is empty"), + + /** + * card_id is not giftcard + */ + CODE_81002(81002, "card_id is not giftcard"), + + /** + * banner_pic_url is empty + */ + CODE_81004(81004, "banner_pic_url is empty"), + + /** + * banner_pic_url is not from cdn + */ + CODE_81005(81005, "banner_pic_url is not from cdn"), + + /** + * giftcard_wrap_pic_url_list is empty + */ + CODE_81006(81006, "giftcard_wrap_pic_url_list is empty"), + + /** + * giftcard_wrap_pic_url_list is not from cdn + */ + CODE_81007(81007, "giftcard_wrap_pic_url_list is not from cdn"), + + /** + * address is empty + */ + CODE_81008(81008, "address is empty"), + + /** + * service_phone is invalid + */ + CODE_81009(81009, "service_phone is invalid"), + + /** + * biz_description is empty + */ + CODE_81010(81010, "biz_description is empty"), + + /** + * invalid page_id + */ + CODE_81011(81011, "invalid page_id"), + + /** + * invalid order_id + */ + CODE_81012(81012, "invalid order_id"), + + /** + * invalid TIME_RANGE, begin_time + 31day must less than end_time + */ + CODE_81013(81013, "invalid TIME_RANGE, begin_time + 31day must less than end_time"), + + /** + * invalid count! count must equal or less than 100 + */ + CODE_81014(81014, "invalid count! count must equal or less than 100"), + + /** + * invalid category_index OR category.title is empty OR is_banner but has_category_index + */ + CODE_81015(81015, "invalid category_index OR category.title is empty OR is_banner but has_category_index"), + + /** + * is_banner is more than 1 + */ + CODE_81016(81016, "is_banner is more than 1"), + + /** + * order status error, please check pay status or gifting_status + */ + CODE_81017(81017, "order status error, please check pay status or gifting_status"), + + /** + * refund reduplicate, the order is already refunded + */ + CODE_81018(81018, "refund reduplicate, the order is already refunded"), + + /** + * lock order fail! the order is refunding by another request + */ + CODE_81019(81019, "lock order fail! the order is refunding by another request"), + + /** + * Invalid Args! page_id.size!=0 but all==true, or page_id.size==0 but all==false. + */ + CODE_81020(81020, "Invalid Args! page_id.size!=0 but all==true, or page_id.size==0 but all==false."), + + /** + * Empty theme_pic_url. + */ + CODE_81021(81021, "Empty theme_pic_url."), + + /** + * Empty theme.title. + */ + CODE_81022(81022, "Empty theme.title."), + + /** + * Empty theme.title_title. + */ + CODE_81023(81023, "Empty theme.title_title."), + + /** + * Empty theme.item_list. + */ + CODE_81024(81024, "Empty theme.item_list."), + + /** + * Empty theme.pic_item_list. + */ + CODE_81025(81025, "Empty theme.pic_item_list."), + + /** + * Invalid theme.title.length . + */ + CODE_81026(81026, "Invalid theme.title.length ."), + + /** + * Empty background_pic_url. + */ + CODE_81027(81027, "Empty background_pic_url."), + + /** + * Empty default_gifting_msg. + */ + CODE_81028(81028, "Empty default_gifting_msg."), + + /** + * Duplicate order_id + */ + CODE_81029(81029, "Duplicate order_id"), + + /** + * PreAlloc code fail + */ + CODE_81030(81030, "PreAlloc code fail"), + + /** + * Too many theme participate_activity + */ + CODE_81031(81031, "Too many theme participate_activity"), + + /** + * biz_template_id not exist + */ + CODE_82000(82000, "biz_template_id not exist"), + + /** + * result_page_style_id not exist + */ + CODE_82001(82001, "result_page_style_id not exist"), + + /** + * deal_msg_style_id not exist + */ + CODE_82002(82002, "deal_msg_style_id not exist"), + + /** + * card_style_id not exist + */ + CODE_82003(82003, "card_style_id not exist"), + + /** + * biz template not audit OK + */ + CODE_82010(82010, "biz template not audit OK"), + + /** + * biz template banned + */ + CODE_82011(82011, "biz template banned"), + + /** + * user not use service first + */ + CODE_82020(82020, "user not use service first"), + + /** + * exceed long period + */ + CODE_82021(82021, "exceed long period"), + + /** + * exceed long period max send cnt + */ + CODE_82022(82022, "exceed long period max send cnt"), + + /** + * exceed short period max send cnt + */ + CODE_82023(82023, "exceed short period max send cnt"), + + /** + * exceed data size limit + */ + CODE_82024(82024, "exceed data size limit"), + + /** + * invalid url + */ + CODE_82025(82025, "invalid url"), + + /** + * service disabled + */ + CODE_82026(82026, "service disabled"), + + /** + * invalid miniprogram appid + */ + CODE_82027(82027, "invalid miniprogram appid"), + + /** + * wx_cs_code should not be empty. + */ + CODE_82100(82100, "wx_cs_code should not be empty."), + + /** + * wx_cs_code is invalid. + */ + CODE_82101(82101, "wx_cs_code is invalid."), + + /** + * wx_cs_code is expire. + */ + CODE_82102(82102, "wx_cs_code is expire."), + + /** + * user_ip should not be empty. + */ + CODE_82103(82103, "user_ip should not be empty."), + + /** + * 公众平台账号与服务id不匹配 + */ + CODE_82200(82200, "公众平台账号与服务id不匹配"), + + /** + * 该停车场已存在,请勿重复添加 + */ + CODE_82201(82201, "该停车场已存在,请勿重复添加"), + + /** + * 该停车场信息不存在,请先导入 + */ + CODE_82202(82202, "该停车场信息不存在,请先导入"), + + /** + * 停车场价格格式不正确 + */ + CODE_82203(82203, "停车场价格格式不正确"), + + /** + * appid与code不匹配 + */ + CODE_82204(82204, "appid与code不匹配"), + + /** + * wx_park_code字段为空 + */ + CODE_82205(82205, "wx_park_code字段为空"), + + /** + * wx_park_code无效或已过期 + */ + CODE_82206(82206, "wx_park_code无效或已过期"), + + /** + * 电话字段为空 + */ + CODE_82207(82207, "电话字段为空"), + + /** + * 关闭时间格式不正确 + */ + CODE_82208(82208, "关闭时间格式不正确"), + + /** + * 该appid不支持开通城市服务插件 + */ + CODE_82300(82300, "该appid不支持开通城市服务插件"), + + /** + * 添加插件失败 + */ + CODE_82301(82301, "添加插件失败"), + + /** + * 未添加城市服务插件 + */ + CODE_82302(82302, "未添加城市服务插件"), + + /** + * fileid无效 + */ + CODE_82303(82303, "fileid无效"), + + /** + * 临时文件过期 + */ + CODE_82304(82304, "临时文件过期"), + + /** + * there is some param not exist + */ + CODE_83000(83000, "there is some param not exist"), + + /** + * system error + */ + CODE_83001(83001, "system error"), + + /** + * create_url_sence_failed + */ + CODE_83002(83002, "create_url_sence_failed"), + + /** + * appid maybe error or retry + */ + CODE_83003(83003, "appid maybe error or retry"), + + /** + * create appid auth failed or retry + */ + CODE_83004(83004, "create appid auth failed or retry"), + + /** + * wxwebencrytoken errro + */ + CODE_83005(83005, "wxwebencrytoken errro"), + + /** + * wxwebencrytoken expired or no exist + */ + CODE_83006(83006, "wxwebencrytoken expired or no exist"), + + /** + * wxwebencrytoken expired + */ + CODE_83007(83007, "wxwebencrytoken expired"), + + /** + * wxwebencrytoken no auth + */ + CODE_83008(83008, "wxwebencrytoken no auth"), + + /** + * wxwebencrytoken not the mate with openid + */ + CODE_83009(83009, "wxwebencrytoken not the mate with openid"), + + /** + * no exist service + */ + CODE_83200(83200, "no exist service"), + + /** + * uin has not the service + */ + CODE_83201(83201, "uin has not the service"), + + /** + * params is not json or not json array + */ + CODE_83202(83202, "params is not json or not json array"), + + /** + * params num exceed 10 + */ + CODE_83203(83203, "params num exceed 10"), + + /** + * object has not key + */ + CODE_83204(83204, "object has not key"), + + /** + * key is not string + */ + CODE_83205(83205, "key is not string"), + + /** + * object has not value + */ + CODE_83206(83206, "object has not value"), + + /** + * value is not string + */ + CODE_83207(83207, "value is not string"), + + /** + * key or value is empty + */ + CODE_83208(83208, "key or value is empty"), + + /** + * key exist repeated + */ + CODE_83209(83209, "key exist repeated"), + + /** + * invalid identify id + */ + CODE_84001(84001, "invalid identify id"), + + /** + * user data expired + */ + CODE_84002(84002, "user data expired"), + + /** + * user data not exist + */ + CODE_84003(84003, "user data not exist"), + + /** + * video upload fail! + */ + CODE_84004(84004, "video upload fail!"), + + /** + * video download fail! please try again + */ + CODE_84005(84005, "video download fail! please try again"), + + /** + * name or id_card_number empty + */ + CODE_84006(84006, "name or id_card_number empty"), + + /** + * 微信号不存在或微信号设置为不可搜索 user not exist or user cannot be searched + */ + CODE_85001(85001, "微信号不存在或微信号设置为不可搜索"), + + /** + * 小程序绑定的体验者数量达到上限 number of tester reach bind limit + */ + CODE_85002(85002, "小程序绑定的体验者数量达到上限"), + + /** + * 微信号绑定的小程序体验者达到上限 user already bind too many weapps + */ + CODE_85003(85003, "微信号绑定的小程序体验者达到上限"), + + /** + * 微信号已经绑定 user already bind + */ + CODE_85004(85004, "微信号已经绑定"), + + /** + * appid not bind weapp + */ + CODE_85005(85005, "appid not bind weapp"), + + /** + * 标签格式错误 tag is in invalid format + */ + CODE_85006(85006, "标签格式错误"), + + /** + * 页面路径错误 page is in invalid format + */ + CODE_85007(85007, "页面路径错误"), + + /** + * 当前小程序没有已经审核通过的类目,请添加类目成功后重试 category is in invalid format + */ + CODE_85008(85008, "当前小程序没有已经审核通过的类目,请添加类目成功后重试"), + + /** + * 已经有正在审核的版本 already submit a version under auditing + */ + CODE_85009(85009, "已经有正在审核的版本"), + + /** + * item_list 有项目为空 missing required data + */ + CODE_85010(85010, "item_list 有项目为空"), + + /** + * 标题填写错误 title is in invalid format + */ + CODE_85011(85011, "标题填写错误"), + + /** + * 无效的审核 id invalid audit id + */ + CODE_85012(85012, "无效的审核 id"), + + /** + * 无效的自定义配置 invalid ext_json, parse fail or containing invalid path + */ + CODE_85013(85013, "无效的自定义配置"), + + /** + * 无效的模板编号 template not exist + */ + CODE_85014(85014, "无效的模板编号"), + + /** + * 该账号不是小程序账号/版本输入错误 + */ + CODE_85015(85015, "该账号不是小程序账号/版本输入错误"), + + /** + * 版本输入错误 + */ +// CODE_85015(85015, "版本输入错误"), + + /** + * 域名数量超过限制 ,总数不能超过1000 exceed valid domain count + */ + CODE_85016(85016, "域名数量超过限制 ,总数不能超过1000"), + + /** + * 没有新增域名,请确认小程序已经添加了域名或该域名是否没有在第三方平台添加 no domain to modify after filtered, please confirm the domain has been set in miniprogram or open + */ + CODE_85017(85017, "没有新增域名,请确认小程序已经添加了域名或该域名是否没有在第三方平台添加"), + + /** + * 域名没有在第三方平台设置 + */ + CODE_85018(85018, "域名没有在第三方平台设置"), + + /** + * 没有审核版本 no version is under auditing + */ + CODE_85019(85019, "没有审核版本"), + + /** + * 审核状态未满足发布 status not allowed + */ + CODE_85020(85020, "审核状态未满足发布"), + + /** + * status not allowed + */ + CODE_85021(85021, "status not allowed"), + + /** + * invalid action + */ + CODE_85022(85022, "invalid action"), + + /** + * 审核列表填写的项目数不在 1-5 以内 item size is not in valid range + */ + CODE_85023(85023, "审核列表填写的项目数不在 1-5 以内"), + + /** + * need complete material + */ + CODE_85024(85024, "need complete material"), + + /** + * this phone reach bind limit + */ + CODE_85025(85025, "this phone reach bind limit"), + + /** + * this wechat account reach bind limit + */ + CODE_85026(85026, "this wechat account reach bind limit"), + + /** + * this idcard reach bind limit + */ + CODE_85027(85027, "this idcard reach bind limit"), + + /** + * this contractor reach bind limit + */ + CODE_85028(85028, "this contractor reach bind limit"), + + /** + * nickname has used + */ + CODE_85029(85029, "nickname has used"), + + /** + * invalid nickname size(4-30) + */ + CODE_85030(85030, "invalid nickname size(4-30)"), + + /** + * nickname is forbidden + */ + CODE_85031(85031, "nickname is forbidden"), + + /** + * nickname is complained + */ + CODE_85032(85032, "nickname is complained"), + + /** + * nickname is illegal + */ + CODE_85033(85033, "nickname is illegal"), + + /** + * nickname is protected + */ + CODE_85034(85034, "nickname is protected"), + + /** + * nickname is forbidden for different contractor + */ + CODE_85035(85035, "nickname is forbidden for different contractor"), + + /** + * introduction is illegal + */ + CODE_85036(85036, "introduction is illegal"), + + /** + * store has added + */ + CODE_85038(85038, "store has added"), + + /** + * store has added by others + */ + CODE_85039(85039, "store has added by others"), + + /** + * store has added by yourseld + */ + CODE_85040(85040, "store has added by yourseld"), + + /** + * credential has used + */ + CODE_85041(85041, "credential has used"), + + /** + * nearby reach limit + */ + CODE_85042(85042, "nearby reach limit"), + + /** + * 模板错误 invalid template, something wrong? + */ + CODE_85043(85043, "模板错误"), + + /** + * 代码包超过大小限制 package exceed max limit + */ + CODE_85044(85044, "代码包超过大小限制"), + + /** + * ext_json 有不存在的路径 some path in ext_json not exist + */ + CODE_85045(85045, "ext_json 有不存在的路径"), + + /** + * tabBar 中缺少 path pagepath missing in tabbar list + */ + CODE_85046(85046, "tabBar 中缺少 path"), + + /** + * pages 字段为空 pages are empty + */ + CODE_85047(85047, "pages 字段为空"), + + /** + * ext_json 解析失败 parse ext_json fail + */ + CODE_85048(85048, "ext_json 解析失败"), + + /** + * reach headimg or introduction quota limit + */ + CODE_85049(85049, "reach headimg or introduction quota limit"), + + /** + * verifying, don't apply again + */ + CODE_85050(85050, "verifying, don't apply again"), + + /** + * version_desc或者preview_info超限 data too large + */ + CODE_85051(85051, "version_desc或者preview_info超限"), + + /** + * app is already released + */ + CODE_85052(85052, "app is already released"), + + /** + * please apply merchant first + */ + CODE_85053(85053, "please apply merchant first"), + + /** + * poi_id is null, please upgrade first + */ + CODE_85054(85054, "poi_id is null, please upgrade first"), + + /** + * map_poi_id is invalid + */ + CODE_85055(85055, "map_poi_id is invalid"), + + /** + * mediaid is invalid + */ + CODE_85056(85056, "mediaid is invalid"), + + /** + * invalid widget data format + */ + CODE_85057(85057, "invalid widget data format"), + + /** + * no valid audit_id exist + */ + CODE_85058(85058, "no valid audit_id exist"), + + /** + * overseas access deny + */ + CODE_85059(85059, "overseas access deny"), + + /** + * invalid taskid + */ + CODE_85060(85060, "invalid taskid"), + + /** + * this phone reach bind limit + */ + CODE_85061(85061, "this phone reach bind limit"), + + /** + * this phone in black list + */ + CODE_85062(85062, "this phone in black list"), + + /** + * idcard in black list + */ + CODE_85063(85063, "idcard in black list"), + + /** + * 找不到模板 template not found + */ + CODE_85064(85064, "找不到模板"), + + /** + * 模板库已满 template list is full + */ + CODE_85065(85065, "模板库已满"), + + /** + * 链接错误 illegal prefix + */ + CODE_85066(85066, "链接错误"), + + /** + * input data error + */ + CODE_85067(85067, "input data error"), + + /** + * 测试链接不是子链接 test url is not the sub prefix + */ + CODE_85068(85068, "测试链接不是子链接"), + + /** + * 校验文件失败 check confirm file fail + */ + CODE_85069(85069, "校验文件失败"), + + /** + * 个人类型小程序无法设置二维码规则 prefix in black list + */ + CODE_85070(85070, "个人类型小程序无法设置二维码规则"), + + /** + * 已添加该链接,请勿重复添加 prefix added repeated + */ + CODE_85071(85071, "已添加该链接,请勿重复添加"), + + /** + * 该链接已被占用 prefix owned by other + */ + CODE_85072(85072, "该链接已被占用"), + + /** + * 二维码规则已满 prefix beyond limit + */ + CODE_85073(85073, "二维码规则已满"), + + /** + * 小程序未发布, 小程序必须先发布代码才可以发布二维码跳转规则 not published + */ + CODE_85074(85074, "小程序未发布, 小程序必须先发布代码才可以发布二维码跳转规则"), + + /** + * 个人类型小程序无法设置二维码规则 can not access + */ + CODE_85075(85075, "个人类型小程序无法设置二维码规则"), + + /** + * 小程序类目信息失效(类目中含有官方下架的类目,请重新选择类目) some category you choose is no longger supported, please choose other category + */ + CODE_85077(85077, "小程序类目信息失效(类目中含有官方下架的类目,请重新选择类目)"), + + /** + * operator info error + */ + CODE_85078(85078, "operator info error"), + + /** + * 小程序没有线上版本,不能进行灰度 miniprogram has no online release + */ + CODE_85079(85079, "小程序没有线上版本,不能进行灰度"), + + /** + * 小程序提交的审核未审核通过 miniprogram commit not approved + */ + CODE_85080(85080, "小程序提交的审核未审核通过"), + + /** + * 无效的发布比例 invalid gray percentage + */ + CODE_85081(85081, "无效的发布比例"), + + /** + * 当前的发布比例需要比之前设置的高 gray percentage too low + */ + CODE_85082(85082, "当前的发布比例需要比之前设置的高"), + + /** + * 搜索标记位被封禁,无法修改 search status is banned + */ + CODE_85083(85083, "搜索标记位被封禁,无法修改"), + + /** + * 非法的 status 值,只能填 0 或者 1 search status invalid + */ + CODE_85084(85084, "非法的 status 值,只能填 0 或者 1"), + + /** + * 小程序提审数量已达本月上限,请点击查看 submit audit reach limit pleasetry later + */ + CODE_85085(85085, "小程序提审数量已达本月上限,请点击查看"), + + /** + * 提交代码审核之前需提前上传代码 must commit before submit audit + */ + CODE_85086(85086, "提交代码审核之前需提前上传代码"), + + /** + * 小程序已使用 api navigateToMiniProgram,请声明跳转 appid 列表后再次提交 navigatetominiprogram appid list empty + */ + CODE_85087(85087, "小程序已使用 api navigateToMiniProgram,请声明跳转 appid 列表后再次提交"), + + /** + * no qbase privilege + */ + CODE_85088(85088, "no qbase privilege"), + + /** + * config not found + */ + CODE_85089(85089, "config not found"), + + /** + * wait and commit for this exappid later + */ + CODE_85090(85090, "wait and commit for this exappid later"), + + /** + * 小程序的搜索开关被关闭。请访问设置页面打开开关再重试 search status was turned off + */ + CODE_85091(85091, "小程序的搜索开关被关闭。请访问设置页面打开开关再重试"), + + /** + * preview_info格式错误 invalid preview_info format + */ + CODE_85092(85092, "preview_info格式错误"), + + /** + * preview_info 视频或者图片个数超限 invalid preview_info size + */ + CODE_85093(85093, "preview_info 视频或者图片个数超限"), + + /** + * 需提供审核机制说明信息 need add ugc declare + */ + CODE_85094(85094, "需提供审核机制说明信息"), + + /** + * 小程序不能发送该运动类型或运动类型不存在 + */ + CODE_85101(85101, "小程序不能发送该运动类型或运动类型不存在"), + + /** + * 数值异常 + */ + CODE_85102(85102, "数值异常"), + + /** + * 不是由第三方代小程序进行调用 should be called only from third party + */ + CODE_86000(86000, "不是由第三方代小程序进行调用"), + + /** + * 不存在第三方的已经提交的代码 component experience version not exists + */ + CODE_86001(86001, "不存在第三方的已经提交的代码"), + + /** + * 小程序还未设置昵称、头像、简介。请先设置完后再重新提交 miniprogram have not completed init procedure + */ + CODE_86002(86002, "小程序还未设置昵称、头像、简介。请先设置完后再重新提交"), + + /** + * component do not has category mall + */ + CODE_86003(86003, "component do not has category mall"), + + /** + * invalid wechat + */ + CODE_86004(86004, "invalid wechat"), + + /** + * wechat limit frequency + */ + CODE_86005(86005, "wechat limit frequency"), + + /** + * has no quota to send group msg + */ + CODE_86006(86006, "has no quota to send group msg"), + + /** + * 小程序禁止提交 + */ + CODE_86007(86007, "小程序禁止提交"), + + /** + * 服务商被处罚,限制全部代码提审能力 + */ + CODE_86008(86008, "服务商被处罚,限制全部代码提审能力"), + + /** + * 服务商新增小程序代码提审能力被限制 + */ + CODE_86009(86009, "服务商新增小程序代码提审能力被限制"), + + /** + * 服务商迭代小程序代码提审能力被限制 + */ + CODE_86010(86010, "服务商迭代小程序代码提审能力被限制"), + + /** + * 小游戏不能提交 this is game miniprogram, submit audit is forbidden + */ + CODE_87006(87006, "小游戏不能提交"), + + /** + * session_key is not existd or expired + */ + CODE_87007(87007, "session_key is not existd or expired"), + + /** + * invalid sig_method + */ + CODE_87008(87008, "invalid sig_method"), + + /** + * 无效的签名 invalid signature + */ + CODE_87009(87009, "无效的签名"), + + /** + * invalid buffer size + */ + CODE_87010(87010, "invalid buffer size"), + + /** + * 现网已经在灰度发布,不能进行版本回退 wxa has a gray release plan, forbid revert release + */ + CODE_87011(87011, "现网已经在灰度发布,不能进行版本回退"), + + /** + * 该版本不能回退,可能的原因:1:无上一个线上版用于回退 2:此版本为已回退版本,不能回退 3:此版本为回退功能上线之前的版本,不能回退 forbid revert this version release + */ + CODE_87012(87012, "该版本不能回退,可能的原因:1:无上一个线上版用于回退 2:此版本为已回退版本,不能回退 3:此版本为回退功能上线之前的版本,不能回退"), + + /** + * 撤回次数达到上限(每天5次,每个月 10 次) no quota to undo code + */ + CODE_87013(87013, "撤回次数达到上限(每天5次,每个月 10 次)"), + + /** + * risky content + */ + CODE_87014(87014, "risky content"), + + /** + * query timeout, try a content with less size + */ + CODE_87015(87015, "query timeout, try a content with less size"), + + /** + * some key-value in list meet length exceed + */ + CODE_87016(87016, "some key-value in list meet length exceed"), + + /** + * user storage size exceed, delete some keys and try again + */ + CODE_87017(87017, "user storage size exceed, delete some keys and try again"), + + /** + * user has stored too much keys. delete some keys and try again + */ + CODE_87018(87018, "user has stored too much keys. delete some keys and try again"), + + /** + * some keys in list meet length exceed + */ + CODE_87019(87019, "some keys in list meet length exceed"), + + /** + * need friend + */ + CODE_87080(87080, "need friend"), + + /** + * invalid openid + */ + CODE_87081(87081, "invalid openid"), + + /** + * invalid key + */ + CODE_87082(87082, "invalid key"), + + /** + * invalid operation + */ + CODE_87083(87083, "invalid operation"), + + /** + * invalid opnum + */ + CODE_87084(87084, "invalid opnum"), + + /** + * check fail + */ + CODE_87085(87085, "check fail"), + + /** + * without comment privilege + */ + CODE_88000(88000, "without comment privilege"), + + /** + * msg_data is not exists + */ + CODE_88001(88001, "msg_data is not exists"), + + /** + * the article is limit for safety + */ + CODE_88002(88002, "the article is limit for safety"), + + /** + * elected comment upper limit + */ + CODE_88003(88003, "elected comment upper limit"), + + /** + * comment was deleted by user + */ + CODE_88004(88004, "comment was deleted by user"), + + /** + * already reply + */ + CODE_88005(88005, "already reply"), + + /** + * reply content beyond max len or content len is zero + */ + CODE_88007(88007, "reply content beyond max len or content len is zero"), + + /** + * comment is not exists + */ + CODE_88008(88008, "comment is not exists"), + + /** + * reply is not exists + */ + CODE_88009(88009, "reply is not exists"), + + /** + * count range error. cout <= 0 or count > 50 + */ + CODE_88010(88010, "count range error. cout <= 0 or count > 50"), + + /** + * the article is limit for safety + */ + CODE_88011(88011, "the article is limit for safety"), + + /** + * account has bound open,该公众号/小程序已经绑定了开放平台帐号 account has bound open + */ + CODE_89000(89000, "account has bound open,该公众号/小程序已经绑定了开放平台帐号"), + + /** + * not same contractor,Authorizer 与开放平台帐号主体不相同 not same contractor + */ + CODE_89001(89001, "not same contractor,Authorizer 与开放平台帐号主体不相同"), + + /** + * open not exists,该公众号/小程序未绑定微信开放平台帐号。 open not exists + */ + CODE_89002(89002, "open not exists,该公众号/小程序未绑定微信开放平台帐号。"), + + /** + * 该开放平台帐号并非通过 api 创建,不允许操作 open is not created by api + */ + CODE_89003(89003, "该开放平台帐号并非通过 api 创建,不允许操作"), + + /** + * 该开放平台帐号所绑定的公众号/小程序已达上限(100 个) + */ + CODE_89004(89004, "该开放平台帐号所绑定的公众号/小程序已达上限(100 个)"), + + /** + * without add video ability, the ability was banned + */ + CODE_89005(89005, "without add video ability, the ability was banned"), + + /** + * without upload video ability, the ability was banned + */ + CODE_89006(89006, "without upload video ability, the ability was banned"), + + /** + * wxa quota limit + */ + CODE_89007(89007, "wxa quota limit"), + + /** + * overseas account can not link + */ + CODE_89008(89008, "overseas account can not link"), + + /** + * wxa reach link limit + */ + CODE_89009(89009, "wxa reach link limit"), + + /** + * link message has sent + */ + CODE_89010(89010, "link message has sent"), + + /** + * can not unlink nearby wxa + */ + CODE_89011(89011, "can not unlink nearby wxa"), + + /** + * can not unlink store or mall + */ + CODE_89012(89012, "can not unlink store or mall"), + + /** + * wxa is banned + */ + CODE_89013(89013, "wxa is banned"), + + /** + * support version error + */ + CODE_89014(89014, "support version error"), + + /** + * has linked wxa + */ + CODE_89015(89015, "has linked wxa"), + + /** + * reach same realname quota + */ + CODE_89016(89016, "reach same realname quota"), + + /** + * reach different realname quota + */ + CODE_89017(89017, "reach different realname quota"), + + /** + * unlink message has sent + */ + CODE_89018(89018, "unlink message has sent"), + + /** + * 业务域名无更改,无需重复设置 webview domain not change + */ + CODE_89019(89019, "业务域名无更改,无需重复设置"), + + /** + * 尚未设置小程序业务域名,请先在第三方平台中设置小程序业务域名后在调用本接口 open's webview domain is null! Need to set open's webview domain first! + */ + CODE_89020(89020, "尚未设置小程序业务域名,请先在第三方平台中设置小程序业务域名后在调用本接口"), + + /** + * 请求保存的域名不是第三方平台中已设置的小程序业务域名或子域名 request domain is not open's webview domain! + */ + CODE_89021(89021, "请求保存的域名不是第三方平台中已设置的小程序业务域名或子域名"), + + /** + * delete domain is not exist! + */ + CODE_89022(89022, "delete domain is not exist!"), + + /** + * 业务域名数量超过限制,最多可以添加100个业务域名 webview domain exceed limit + */ + CODE_89029(89029, "业务域名数量超过限制,最多可以添加100个业务域名"), + + /** + * operation reach month limit + */ + CODE_89030(89030, "operation reach month limit"), + + /** + * user bind reach limit + */ + CODE_89031(89031, "user bind reach limit"), + + /** + * weapp bind members reach limit + */ + CODE_89032(89032, "weapp bind members reach limit"), + + /** + * empty wx or openid + */ + CODE_89033(89033, "empty wx or openid"), + + /** + * userstr is invalid + */ + CODE_89034(89034, "userstr is invalid"), + + /** + * linking from mp + */ + CODE_89035(89035, "linking from mp"), + + /** + * 个人小程序不支持调用 setwebviewdomain 接口 not support single + */ + CODE_89231(89231, "个人小程序不支持调用 setwebviewdomain 接口"), + + /** + * hit black contractor + */ + CODE_89235(89235, "hit black contractor"), + + /** + * 该插件不能申请 this plugin can not apply + */ + CODE_89236(89236, "该插件不能申请"), + + /** + * 已经添加该插件 plugin has send apply message or already bind + */ + CODE_89237(89237, "已经添加该插件"), + + /** + * 申请或使用的插件已经达到上限 plugin count reach limit + */ + CODE_89238(89238, "申请或使用的插件已经达到上限"), + + /** + * 该插件不存在 plugin no exist + */ + CODE_89239(89239, "该插件不存在"), + + /** + * only applying status can be agreed or refused + */ + CODE_89240(89240, "only applying status can be agreed or refused"), + + /** + * only refused status can be deleted, please refused first + */ + CODE_89241(89241, "only refused status can be deleted, please refused first"), + + /** + * appid is no in the apply list, make sure appid is right + */ + CODE_89242(89242, "appid is no in the apply list, make sure appid is right"), + + /** + * 该申请为“待确认”状态,不可删除 can not delete apply request in 24 hours + */ + CODE_89243(89243, "该申请为“待确认”状态,不可删除"), + + /** + * 不存在该插件 appid plugin appid is no in the plugin list, make sure plugin appid is right + */ + CODE_89244(89244, "不存在该插件 appid"), + + /** + * mini program forbidden to link + */ + CODE_89245(89245, "mini program forbidden to link"), + + /** + * plugins with special category are used only by specific apps + */ + CODE_89246(89246, "plugins with special category are used only by specific apps"), + + /** + * 系统内部错误 inner error, retry after some while + */ + CODE_89247(89247, "系统内部错误"), + + /** + * invalid code type + */ + CODE_89248(89248, "invalid code type"), + + /** + * task running + */ + CODE_89249(89249, "task running"), + + /** + * 内部错误 inner error, retry after some while + */ + CODE_89250(89250, "内部错误"), + + /** + * 模板消息已下发,待法人人脸核身校验 legal person checking + */ + CODE_89251(89251, "模板消息已下发,待法人人脸核身校验"), + + /** + * 法人&企业信息一致性校验中 front checking + */ + CODE_89253(89253, "法人&企业信息一致性校验中"), + + /** + * lack of some component rights + */ + CODE_89254(89254, "lack of some component rights"), + + /** + * code参数无效,请检查code长度以及内容是否正确;注意code_type的值不同需要传的code长度不一样 enterprise code invalid + */ + CODE_89255(89255, "code参数无效,请检查code长度以及内容是否正确;注意code_type的值不同需要传的code长度不一样"), + + /** + * token 信息有误 no component info + */ + CODE_89256(89256, "token 信息有误"), + + /** + * 该插件版本不支持快速更新 no such version + */ + CODE_89257(89257, "该插件版本不支持快速更新"), + + /** + * 当前小程序帐号存在灰度发布中的版本,不可操作快速更新 code is gray online + */ + CODE_89258(89258, "当前小程序帐号存在灰度发布中的版本,不可操作快速更新"), + + /** + * zhibo plugin is not allow to delete + */ + CODE_89259(89259, "zhibo plugin is not allow to delete"), + + /** + * 订单无效 invalid trade + */ + CODE_89300(89300, "订单无效"), + + /** + * 系统不稳定,请稍后再试,如多次失败请通过社区反馈 + */ + CODE_89401(89401, "系统不稳定,请稍后再试,如多次失败请通过社区反馈"), + + /** + * 该小程序不在待审核队列,请检查是否已提交审核或已审完 + */ + CODE_89402(89402, "该小程序不在待审核队列,请检查是否已提交审核或已审完"), + + /** + * 本单属于平台不支持加急种类,请等待正常审核流程 + */ + CODE_89403(89403, "本单属于平台不支持加急种类,请等待正常审核流程"), + + /** + * 本单已加速成功,请勿重复提交 + */ + CODE_89404(89404, "本单已加速成功,请勿重复提交"), + + /** + * 本月加急额度已用完,请提高提审质量以获取更多额度 + */ + CODE_89405(89405, "本月加急额度已用完,请提高提审质量以获取更多额度"), + + /** + * 公众号有未处理的确认请求,请稍候重试 + */ + CODE_89501(89501, "公众号有未处理的确认请求,请稍候重试"), + + /** + * 请耐心等待管理员确认 + */ + CODE_89502(89502, "请耐心等待管理员确认"), + + /** + * 此次调用需要管理员确认,请耐心等候 + */ + CODE_89503(89503, "此次调用需要管理员确认,请耐心等候"), + + /** + * 正在等管理员确认,请耐心等待 + */ + CODE_89504(89504, "正在等管理员确认,请耐心等待"), + + /** + * 正在等管理员确认,请稍候重试 + */ + CODE_89505(89505, "正在等管理员确认,请稍候重试"), + + /** + * 该IP调用求请求已被公众号管理员拒绝,请24小时后再试,建议调用前与管理员沟通确认 + */ + CODE_89506(89506, "该IP调用求请求已被公众号管理员拒绝,请24小时后再试,建议调用前与管理员沟通确认"), + + /** + * 该IP调用求请求已被公众号管理员拒绝,请1小时后再试,建议调用前与管理员沟通确认 + */ + CODE_89507(89507, "该IP调用求请求已被公众号管理员拒绝,请1小时后再试,建议调用前与管理员沟通确认"), + + /** + * invalid order id + */ + CODE_90001(90001, "invalid order id"), + + /** + * invalid busi id + */ + CODE_90002(90002, "invalid busi id"), + + /** + * invalid bill date + */ + CODE_90003(90003, "invalid bill date"), + + /** + * invalid bill type + */ + CODE_90004(90004, "invalid bill type"), + + /** + * invalid platform + */ + CODE_90005(90005, "invalid platform"), + + /** + * bill not exists + */ + CODE_90006(90006, "bill not exists"), + + /** + * invalid openid + */ + CODE_90007(90007, "invalid openid"), + + /** + * mp_sig error + */ + CODE_90009(90009, "mp_sig error"), + + /** + * no session + */ + CODE_90010(90010, "no session"), + + /** + * sig error + */ + CODE_90011(90011, "sig error"), + + /** + * order exist + */ + CODE_90012(90012, "order exist"), + + /** + * balance not enough + */ + CODE_90013(90013, "balance not enough"), + + /** + * order has been confirmed + */ + CODE_90014(90014, "order has been confirmed"), + + /** + * order has been canceled + */ + CODE_90015(90015, "order has been canceled"), + + /** + * order is being processed + */ + CODE_90016(90016, "order is being processed"), + + /** + * no privilege + */ + CODE_90017(90017, "no privilege"), + + /** + * invalid parameter + */ + CODE_90018(90018, "invalid parameter"), + + /** + * 不是公众号快速创建的小程序 not fast register + */ + CODE_91001(91001, "不是公众号快速创建的小程序"), + + /** + * 小程序发布后不可改名 has published + */ + CODE_91002(91002, "小程序发布后不可改名"), + + /** + * 改名状态不合法 invalid change stat + */ + CODE_91003(91003, "改名状态不合法"), + + /** + * 昵称不合法 invalid nickname + */ + CODE_91004(91004, "昵称不合法"), + + /** + * 昵称 15 天主体保护 nickname protected + */ + CODE_91005(91005, "昵称 15 天主体保护"), + + /** + * 昵称命中微信号 nickname used by username + */ + CODE_91006(91006, "昵称命中微信号"), + + /** + * 昵称已被占用 nickname used + */ + CODE_91007(91007, "昵称已被占用"), + + /** + * 昵称命中 7 天侵权保护期 nickname protected + */ + CODE_91008(91008, "昵称命中 7 天侵权保护期"), + + /** + * 需要提交材料 nickname need proof + */ + CODE_91009(91009, "需要提交材料"), + + /** + * 其他错误 + */ + CODE_91010(91010, "其他错误"), + + /** + * 查不到昵称修改审核单信息 + */ + CODE_91011(91011, "查不到昵称修改审核单信息"), + + /** + * 其它错误 + */ + CODE_91012(91012, "其它错误"), + + /** + * 占用名字过多 lock name too more + */ + CODE_91013(91013, "占用名字过多"), + + /** + * +号规则 同一类型关联名主体不一致 diff master plus + */ + CODE_91014(91014, "+号规则 同一类型关联名主体不一致"), + + /** + * 原始名不同类型主体不一致 diff master + */ + CODE_91015(91015, "原始名不同类型主体不一致"), + + /** + * 名称占用者 ≥2 name more owner + */ + CODE_91016(91016, "名称占用者 ≥2"), + + /** + * +号规则 不同类型关联名主体不一致 other diff master plus + */ + CODE_91017(91017, "+号规则 不同类型关联名主体不一致"), + + /** + * 组织类型小程序发布后,侵权被清空昵称,需走认证改名 + */ + CODE_91018(91018, "组织类型小程序发布后,侵权被清空昵称,需走认证改名"), + + /** + * 小程序正在审核中 + */ + CODE_91019(91019, "小程序正在审核中"), + + /** + * 该经营资质已添加,请勿重复添加 + */ + CODE_92000(92000, "该经营资质已添加,请勿重复添加"), + + /** + * 附近地点添加数量达到上线,无法继续添加 + */ + CODE_92002(92002, "附近地点添加数量达到上线,无法继续添加"), + + /** + * 地点已被其它小程序占用 + */ + CODE_92003(92003, "地点已被其它小程序占用"), + + /** + * 附近功能被封禁 + */ + CODE_92004(92004, "附近功能被封禁"), + + /** + * 地点正在审核中 + */ + CODE_92005(92005, "地点正在审核中"), + + /** + * 地点正在展示小程序 + */ + CODE_92006(92006, "地点正在展示小程序"), + + /** + * 地点审核失败 + */ + CODE_92007(92007, "地点审核失败"), + + /** + * 小程序未展示在该地点 + */ + CODE_92008(92008, "小程序未展示在该地点"), + + /** + * 小程序未上架或不可见 + */ + CODE_93009(93009, "小程序未上架或不可见"), + + /** + * 地点不存在 + */ + CODE_93010(93010, "地点不存在"), + + /** + * 个人类型小程序不可用 + */ + CODE_93011(93011, "个人类型小程序不可用"), + + /** + * 非普通类型小程序(门店小程序、小店小程序等)不可用 + */ + CODE_93012(93012, "非普通类型小程序(门店小程序、小店小程序等)不可用"), + + /** + * 从腾讯地图获取地址详细信息失败 + */ + CODE_93013(93013, "从腾讯地图获取地址详细信息失败"), + + /** + * 同一资质证件号重复添加 + */ + CODE_93014(93014, "同一资质证件号重复添加"), + + /** + * 附近类目审核中 + */ + CODE_93015(93015, "附近类目审核中"), + + /** + * 服务标签个数超限制(官方最多5个,自定义最多4个) + */ + CODE_93016(93016, "服务标签个数超限制(官方最多5个,自定义最多4个)"), + + /** + * 服务标签或者客服的名字不符合要求 + */ + CODE_93017(93017, "服务标签或者客服的名字不符合要求"), + + /** + * 服务能力中填写的小程序appid不是同主体小程序 + */ + CODE_93018(93018, "服务能力中填写的小程序appid不是同主体小程序"), + + /** + * 申请类目之后才能添加附近地点 + */ + CODE_93019(93019, "申请类目之后才能添加附近地点"), + + /** + * qualification_list无效 + */ + CODE_93020(93020, "qualification_list无效"), + + /** + * company_name字段为空 + */ + CODE_93021(93021, "company_name字段为空"), + + /** + * credential字段为空 + */ + CODE_93022(93022, "credential字段为空"), + + /** + * address字段为空 + */ + CODE_93023(93023, "address字段为空"), + + /** + * qualification_list字段为空 + */ + CODE_93024(93024, "qualification_list字段为空"), + + /** + * 服务appid对应的path不存在 + */ + CODE_93025(93025, "服务appid对应的path不存在"), + + /** + * missing cert_serialno + */ + CODE_94001(94001, "missing cert_serialno"), + + /** + * use not register wechat pay + */ + CODE_94002(94002, "use not register wechat pay"), + + /** + * invalid sign + */ + CODE_94003(94003, "invalid sign"), + + /** + * user do not has real name info + */ + CODE_94004(94004, "user do not has real name info"), + + /** + * invalid user token + */ + CODE_94005(94005, "invalid user token"), + + /** + * appid unauthorized + */ + CODE_94006(94006, "appid unauthorized"), + + /** + * appid unbind mchid + */ + CODE_94007(94007, "appid unbind mchid"), + + /** + * invalid timestamp + */ + CODE_94008(94008, "invalid timestamp"), + + /** + * invalid cert_serialno, cert_serialno's size should be 40 + */ + CODE_94009(94009, "invalid cert_serialno, cert_serialno's size should be 40"), + + /** + * invalid mch_id + */ + CODE_94010(94010, "invalid mch_id"), + + /** + * timestamp expired + */ + CODE_94011(94011, "timestamp expired"), + + /** + * appid和商户号的绑定关系不存在 + */ + CODE_94012(94012, "appid和商户号的绑定关系不存在"), + + /** + * wxcode decode fail + */ + CODE_95001(95001, "wxcode decode fail"), + + /** + * wxcode recognize unautuorized + */ + CODE_95002(95002, "wxcode recognize unautuorized"), + + /** + * get product by page args invalid + */ + CODE_95101(95101, "get product by page args invalid"), + + /** + * get product materials by cond args invalid + */ + CODE_95102(95102, "get product materials by cond args invalid"), + + /** + * material id list size out of limit + */ + CODE_95103(95103, "material id list size out of limit"), + + /** + * import product frequence out of limit + */ + CODE_95104(95104, "import product frequence out of limit"), + + /** + * mp is importing products, api is rejected to import + */ + CODE_95105(95105, "mp is importing products, api is rejected to import"), + + /** + * api is rejected to import, need to set commission ratio on mp first + */ + CODE_95106(95106, "api is rejected to import, need to set commission ratio on mp first"), + + /** + * invalid image url + */ + CODE_101000(101000, "无效图片链接"), + + /** + * certificate not found + */ + CODE_101001(101001, "未找到证书"), + + /** + * not enough market quota + */ + CODE_101002(101002, "not enough market quota"), + + /** + * 入参错误 + */ + CODE_200002(200002, "入参错误"), + + /** + * 此账号已被封禁,无法操作 + */ + CODE_200011(200011, "此账号已被封禁,无法操作"), + + /** + * 个人模板数已达上限,上限25个 + */ + CODE_200012(200012, "个人模板数已达上限,上限25个"), + + /** + * 此模板已被封禁,无法选用 + */ + CODE_200013(200013, "此模板已被封禁,无法选用"), + + /** + * 模板 tid 参数错误 + */ + CODE_200014(200014, "模板 tid 参数错误"), + + /** + * start 参数错误 + */ + CODE_200016(200016, "start 参数错误"), + + /** + * limit 参数错误 + */ + CODE_200017(200017, "limit 参数错误"), + + /** + * 类目 ids 缺失 + */ + CODE_200018(200018, "类目 ids 缺失"), + + /** + * 类目 ids 不合法 + */ + CODE_200019(200019, "类目 ids 不合法"), + + /** + * 关键词列表 kidList 参数错误 + */ + CODE_200020(200020, "关键词列表 kidList 参数错误"), + + /** + * 场景描述 sceneDesc 参数错误 + */ + CODE_200021(200021, "场景描述 sceneDesc 参数错误"), + + /** + * 禁止创建/更新商品(如商品创建功能被封禁) 或 禁止编辑&更新房间 + */ + CODE_300001(300001, "禁止创建/更新商品(如商品创建功能被封禁) 或 禁止编辑&更新房间"), + + /** + * 名称长度不符合规则 + */ + CODE_300002(300002, "名称长度不符合规则"), + + /** + * 价格输入不合规(如现价比原价大、传入价格非数字等) + */ + CODE_300003(300003, "价格输入不合规(如现价比原价大、传入价格非数字等)"), + + /** + * 商品名称存在违规违法内容 + */ + CODE_300004(300004, "商品名称存在违规违法内容"), + + /** + * 商品图片存在违规违法内容 + */ + CODE_300005(300005, "商品图片存在违规违法内容"), + + /** + * 图片上传失败(如mediaID过期) + */ + CODE_300006(300006, "图片上传失败(如mediaID过期)"), + + /** + * 线上小程序版本不存在该链接 + */ + CODE_300007(300007, "线上小程序版本不存在该链接"), + + /** + * 添加商品失败 + */ + CODE_300008(300008, "添加商品失败"), + + /** + * 商品审核撤回失败 + */ + CODE_300009(300009, "商品审核撤回失败"), + + /** + * 商品审核状态不对(如商品审核中) + */ + CODE_300010(300010, "商品审核状态不对(如商品审核中)"), + + /** + * 操作非法(API不允许操作非API创建的商品) + */ + CODE_300011(300011, "操作非法(API不允许操作非API创建的商品)"), + + /** + * 没有提审额度(每天500次提审额度) + */ + CODE_300012(300012, "没有提审额度(每天500次提审额度)"), + + /** + * 提审失败 + */ + CODE_300013(300013, "提审失败"), + + /** + * 审核中,无法删除(非零代表失败) + */ + CODE_300014(300014, "审核中,无法删除(非零代表失败)"), + + /** + * 商品未提审 + */ + CODE_300017(300017, "商品未提审"), + + /** + * 商品添加成功,审核失败 + */ + CODE_300021(300021, "商品添加成功,审核失败"), + + /** + * 此房间号不存在 + */ + CODE_300022(300022, "此房间号不存在"), + + /** + * 房间状态 拦截(当前房间状态不允许此操作) + */ + CODE_300023(300023, "房间状态 拦截(当前房间状态不允许此操作)"), + + /** + * 商品不存在 + */ + CODE_300024(300024, "商品不存在"), + + /** + * 商品审核未通过 + */ + CODE_300025(300025, "商品审核未通过"), + + /** + * 房间商品数量已经满额 + */ + CODE_300026(300026, "房间商品数量已经满额"), + + /** + * 导入商品失败 + */ + CODE_300027(300027, "导入商品失败"), + + /** + * 房间名称违规 + */ + CODE_300028(300028, "房间名称违规"), + + /** + * 主播昵称违规 + */ + CODE_300029(300029, "主播昵称违规"), + + /** + * 主播微信号不合法 + */ + CODE_300030(300030, "主播微信号不合法"), + + /** + * 直播间封面图不合规 + */ + CODE_300031(300031, "直播间封面图不合规"), + + /** + * 直播间分享图违规 + */ + CODE_300032(300032, "直播间分享图违规"), + + /** + * 添加商品超过直播间上限 + */ + CODE_300033(300033, "添加商品超过直播间上限"), + + /** + * 主播微信昵称长度不符合要求 + */ + CODE_300034(300034, "主播微信昵称长度不符合要求"), + + /** + * 主播微信号不存在 + */ + CODE_300035(300035, "主播微信号不存在"), + + /** + * 主播微信号未实名认证 + */ + CODE_300036(300036, "主播微信号未实名认证"), + + /** + * invalid file name + */ + CODE_600001(600001, "invalid file name"), + + /** + * no permission to upload file + */ + CODE_600002(600002, "no permission to upload file"), + + /** + * invalid size of source + */ + CODE_600003(600003, "invalid size of source"), + + /** + * 票据已存在 + */ + CODE_928000(928000, "票据已存在"), + + /** + * 票据不存在 + */ + CODE_928001(928001, "票据不存在"), + + /** + * sysem error + */ + CODE_930555(930555, "sysem error"), + + /** + * delivery timeout + */ + CODE_930556(930556, "delivery timeout"), + + /** + * delivery system error + */ + CODE_930557(930557, "delivery system error"), + + /** + * delivery logic error + */ + CODE_930558(930558, "delivery logic error"), + + /** + * 沙盒环境openid无效 invaild openid + */ + CODE_930559(930559, "沙盒环境openid无效"), + + /** + * shopid need bind first + */ + CODE_930560(930560, "shopid need bind first"), + + /** + * 参数错误 args error + */ + CODE_930561(930561, "参数错误"), + + /** + * order already exists + */ + CODE_930562(930562, "order already exists"), + + /** + * 订单不存在 order not exists + */ + CODE_930563(930563, "订单不存在"), + + /** + * 沙盒环境调用无配额 quota run out, try next day + */ + CODE_930564(930564, "沙盒环境调用无配额"), + + /** + * order finished + */ + CODE_930565(930565, "order finished"), + + /** + * not support, plz auth at mp.weixin.qq.com + */ + CODE_930566(930566, "not support, plz auth at mp.weixin.qq.com"), + + /** + * shop arg error + */ + CODE_930567(930567, "shop arg error"), + + /** + * 不支持个人类型小程序 not personal account + */ + CODE_930568(930568, "不支持个人类型小程序"), + + /** + * 已经开通不需要再开通 already open + */ + CODE_930569(930569, "已经开通不需要再开通"), + + /** + * cargo_first_class or cargo_second_class invalid + */ + CODE_930570(930570, "cargo_first_class or cargo_second_class invalid"), + + /** + * 该商户没有内测权限,请先申请权限: https://wj.qq.com/s2/7243532/fcfb/ + */ + CODE_930571(930571, "该商户没有内测权限,请先申请权限: https://wj.qq.com/s2/7243532/fcfb/"), + + /** + * fee already set + */ + CODE_931010(931010, "fee already set"), + + /** + * unbind download url + */ + CODE_6000100(6000100, "unbind download url"), + + /** + * no response data + */ + CODE_6000101(6000101, "no response data"), + + /** + * response data too big + */ + CODE_6000102(6000102, "response data too big"), + + /** + * POST 数据参数不合法 + */ + CODE_9001001(9001001, "POST 数据参数不合法"), + + /** + * 远端服务不可用 + */ + CODE_9001002(9001002, "远端服务不可用"), + + /** + * Ticket 不合法 + */ + CODE_9001003(9001003, "Ticket 不合法"), + + /** + * 获取摇周边用户信息失败 + */ + CODE_9001004(9001004, "获取摇周边用户信息失败"), + + /** + * 获取商户信息失败 + */ + CODE_9001005(9001005, "获取商户信息失败"), + + /** + * 获取 OpenID 失败 + */ + CODE_9001006(9001006, "获取 OpenID 失败"), + + /** + * 上传文件缺失 + */ + CODE_9001007(9001007, "上传文件缺失"), + + /** + * 上传素材的文件类型不合法 + */ + CODE_9001008(9001008, "上传素材的文件类型不合法"), + + /** + * 上传素材的文件尺寸不合法 + */ + CODE_9001009(9001009, "上传素材的文件尺寸不合法"), + + /** + * 上传失败 + */ + CODE_9001010(9001010, "上传失败"), + + /** + * 帐号不合法 + */ + CODE_9001020(9001020, "帐号不合法"), + + /** + * 已有设备激活率低于 50% ,不能新增设备 + */ + CODE_9001021(9001021, "已有设备激活率低于 50% ,不能新增设备"), + + /** + * 设备申请数不合法,必须为大于 0 的数字 + */ + CODE_9001022(9001022, "设备申请数不合法,必须为大于 0 的数字"), + + /** + * 已存在审核中的设备 ID 申请 + */ + CODE_9001023(9001023, "已存在审核中的设备 ID 申请"), + + /** + * 一次查询设备 ID 数量不能超过 50 + */ + CODE_9001024(9001024, "一次查询设备 ID 数量不能超过 50"), + + /** + * 设备 ID 不合法 + */ + CODE_9001025(9001025, "设备 ID 不合法"), + + /** + * 页面 ID 不合法 + */ + CODE_9001026(9001026, "页面 ID 不合法"), + + /** + * 页面参数不合法 + */ + CODE_9001027(9001027, "页面参数不合法"), + + /** + * 一次删除页面 ID 数量不能超过 10 + */ + CODE_9001028(9001028, "一次删除页面 ID 数量不能超过 10"), + + /** + * 页面已应用在设备中,请先解除应用关系再删除 + */ + CODE_9001029(9001029, "页面已应用在设备中,请先解除应用关系再删除"), + + /** + * 一次查询页面 ID 数量不能超过 50 + */ + CODE_9001030(9001030, "一次查询页面 ID 数量不能超过 50"), + + /** + * 时间区间不合法 + */ + CODE_9001031(9001031, "时间区间不合法"), + + /** + * 保存设备与页面的绑定关系参数错误 + */ + CODE_9001032(9001032, "保存设备与页面的绑定关系参数错误"), + + /** + * 门店 ID 不合法 + */ + CODE_9001033(9001033, "门店 ID 不合法"), + + /** + * 设备备注信息过长 + */ + CODE_9001034(9001034, "设备备注信息过长"), + + /** + * 设备申请参数不合法 + */ + CODE_9001035(9001035, "设备申请参数不合法"), + + /** + * 查询起始值 begin 不合法 + */ + CODE_9001036(9001036, "查询起始值 begin 不合法"), + + /** + * params invalid + */ + CODE_9002008(9002008, "params invalid"), + + /** + * shop id not exist + */ + CODE_9002009(9002009, "shop id not exist"), + + /** + * ssid or password should start with "WX" + */ + CODE_9002010(9002010, "ssid or password should start with \"WX\""), + + /** + * ssid can not include chinese + */ + CODE_9002011(9002011, "ssid can not include chinese"), + + /** + * passsword can not include chinese + */ + CODE_9002012(9002012, "passsword can not include chinese"), + + /** + * password must be between 8 and 24 characters + */ + CODE_9002013(9002013, "password must be between 8 and 24 characters"), + + /** + * device exist + */ + CODE_9002016(9002016, "device exist"), + + /** + * device not exist + */ + CODE_9002017(9002017, "device not exist"), + + /** + * the size of query list reach limit + */ + CODE_9002026(9002026, "the size of query list reach limit"), + + /** + * not allowed to modify, ensure you are an certified or component account + */ + CODE_9002041(9002041, "not allowed to modify, ensure you are an certified or component account"), + + /** + * invalid ssid, can not include none utf8 characters, and should be between 1 and 32 bytes + */ + CODE_9002044(9002044, "invalid ssid, can not include none utf8 characters, and should be between 1 and 32 bytes"), + + /** + * shop id has not be audited, this bar type is not enable + */ + CODE_9002052(9002052, "shop id has not be audited, this bar type is not enable"), + + /** + * protocol type is not same with the exist device + */ + CODE_9007003(9007003, "protocol type is not same with the exist device"), + + /** + * ssid not exist + */ + CODE_9007004(9007004, "ssid not exist"), + + /** + * device count limit + */ + CODE_9007005(9007005, "device count limit"), + + /** + * card info not exist + */ + CODE_9008001(9008001, "card info not exist"), + + /** + * card expiration time is invalid + */ + CODE_9008002(9008002, "card expiration time is invalid"), + + /** + * url size invalid, keep less than 255 + */ + CODE_9008003(9008003, "url size invalid, keep less than 255"), + + /** + * url can not include chinese + */ + CODE_9008004(9008004, "url can not include chinese"), + + /** + * order_id not exist + */ + CODE_9200001(9200001, "order_id not exist"), + + /** + * order of other biz + */ + CODE_9200002(9200002, "order of other biz"), + + /** + * blocked + */ + CODE_9200003(9200003, "blocked"), + + /** + * payment notice disabled + */ + CODE_9200211(9200211, "payment notice disabled"), + + /** + * payment notice not exist + */ + CODE_9200231(9200231, "payment notice not exist"), + + /** + * payment notice paid + */ + CODE_9200232(9200232, "payment notice paid"), + + /** + * payment notice canceled + */ + CODE_9200233(9200233, "payment notice canceled"), + + /** + * payment notice expired + */ + CODE_9200235(9200235, "payment notice expired"), + + /** + * bank not allow + */ + CODE_9200236(9200236, "bank not allow"), + + /** + * freq limit + */ + CODE_9200295(9200295, "freq limit"), + + /** + * suspend payment at current time + */ + CODE_9200297(9200297, "suspend payment at current time"), + + /** + * 3rd resp decrypt error + */ + CODE_9200298(9200298, "3rd resp decrypt error"), + + /** + * 3rd resp system error + */ + CODE_9200299(9200299, "3rd resp system error"), + + /** + * 3rd resp sign error + */ + CODE_9200300(9200300, "3rd resp sign error"), + + /** + * desc empty + */ + CODE_9201000(9201000, "desc empty"), + + /** + * fee not equal items' + */ + CODE_9201001(9201001, "fee not equal items'"), + + /** + * payment info incorrect + */ + CODE_9201002(9201002, "payment info incorrect"), + + /** + * fee is 0 + */ + CODE_9201003(9201003, "fee is 0"), + + /** + * payment expire date format error + */ + CODE_9201004(9201004, "payment expire date format error"), + + /** + * appid error + */ + CODE_9201005(9201005, "appid error"), + + /** + * payment order id error + */ + CODE_9201006(9201006, "payment order id error"), + + /** + * openid error + */ + CODE_9201007(9201007, "openid error"), + + /** + * return_url error + */ + CODE_9201008(9201008, "return_url error"), + + /** + * ip error + */ + CODE_9201009(9201009, "ip error"), + + /** + * order_id error + */ + CODE_9201010(9201010, "order_id error"), + + /** + * reason error + */ + CODE_9201011(9201011, "reason error"), + + /** + * mch_id error + */ + CODE_9201012(9201012, "mch_id error"), + + /** + * bill_date error + */ + CODE_9201013(9201013, "bill_date error"), + + /** + * bill_type error + */ + CODE_9201014(9201014, "bill_type error"), + + /** + * trade_type error + */ + CODE_9201015(9201015, "trade_type error"), + + /** + * bank_id error + */ + CODE_9201016(9201016, "bank_id error"), + + /** + * bank_account error + */ + CODE_9201017(9201017, "bank_account error"), + + /** + * payment_notice_no error + */ + CODE_9201018(9201018, "payment_notice_no error"), + + /** + * department_code error + */ + CODE_9201019(9201019, "department_code error"), + + /** + * payment_notice_type error + */ + CODE_9201020(9201020, "payment_notice_type error"), + + /** + * region_code error + */ + CODE_9201021(9201021, "region_code error"), + + /** + * department_name error + */ + CODE_9201022(9201022, "department_name error"), + + /** + * fee not equal finance's + */ + CODE_9201023(9201023, "fee not equal finance's"), + + /** + * refund_out_id error + */ + CODE_9201024(9201024, "refund_out_id error"), + + /** + * not combined order_id + */ + CODE_9201026(9201026, "not combined order_id"), + + /** + * partial sub order is test + */ + CODE_9201027(9201027, "partial sub order is test"), + + /** + * desc too long + */ + CODE_9201029(9201029, "desc too long"), + + /** + * sub order list size error + */ + CODE_9201031(9201031, "sub order list size error"), + + /** + * sub order repeated + */ + CODE_9201032(9201032, "sub order repeated"), + + /** + * auth_code empty + */ + CODE_9201033(9201033, "auth_code empty"), + + /** + * bank_id empty but mch_id not empty + */ + CODE_9201034(9201034, "bank_id empty but mch_id not empty"), + + /** + * sum of other fees exceed total fee + */ + CODE_9201035(9201035, "sum of other fees exceed total fee"), + + /** + * other user paying + */ + CODE_9202000(9202000, "other user paying"), + + /** + * pay process not finish + */ + CODE_9202001(9202001, "pay process not finish"), + + /** + * no refund permission + */ + CODE_9202002(9202002, "no refund permission"), + + /** + * ip limit + */ + CODE_9202003(9202003, "ip limit"), + + /** + * freq limit + */ + CODE_9202004(9202004, "freq limit"), + + /** + * user weixin account abnormal + */ + CODE_9202005(9202005, "user weixin account abnormal"), + + /** + * account balance not enough + */ + CODE_9202006(9202006, "account balance not enough"), + + /** + * refund request repeated + */ + CODE_9202010(9202010, "refund request repeated"), + + /** + * has refunded + */ + CODE_9202011(9202011, "has refunded"), + + /** + * refund exceed total fee + */ + CODE_9202012(9202012, "refund exceed total fee"), + + /** + * busi_id dup + */ + CODE_9202013(9202013, "busi_id dup"), + + /** + * not check sign + */ + CODE_9202016(9202016, "not check sign"), + + /** + * check sign failed + */ + CODE_9202017(9202017, "check sign failed"), + + /** + * sub order error + */ + CODE_9202018(9202018, "sub order error"), + + /** + * order status error + */ + CODE_9202020(9202020, "order status error"), + + /** + * unified order repeatedly + */ + CODE_9202021(9202021, "unified order repeatedly"), + + /** + * request to notification url fail + */ + CODE_9203000(9203000, "request to notification url fail"), + + /** + * http request fail + */ + CODE_9203001(9203001, "http request fail"), + + /** + * http response data error + */ + CODE_9203002(9203002, "http response data error"), + + /** + * http response data RSA decrypt fail + */ + CODE_9203003(9203003, "http response data RSA decrypt fail"), + + /** + * http response data AES decrypt fail + */ + CODE_9203004(9203004, "http response data AES decrypt fail"), + + /** + * system busy, please try again later + */ + CODE_9203999(9203999, "system busy, please try again later"), + + /** + * getrealname token error + */ + CODE_9204000(9204000, "getrealname token error"), + + /** + * getrealname user or token error + */ + CODE_9204001(9204001, "getrealname user or token error"), + + /** + * getrealname appid or token error + */ + CODE_9204002(9204002, "getrealname appid or token error"), + + /** + * finance conf not exist + */ + CODE_9205000(9205000, "finance conf not exist"), + + /** + * bank conf not exist + */ + CODE_9205001(9205001, "bank conf not exist"), + + /** + * wei ban ju conf not exist + */ + CODE_9205002(9205002, "wei ban ju conf not exist"), + + /** + * symmetric key conf not exist + */ + CODE_9205010(9205010, "symmetric key conf not exist"), + + /** + * out order id not exist + */ + CODE_9205101(9205101, "out order id not exist"), + + /** + * bill not exist + */ + CODE_9205201(9205201, "bill not exist"), + + /** + * 3rd resp pay_channel empty + */ + CODE_9206000(9206000, "3rd resp pay_channel empty"), + + /** + * 3rd resp order_id empty + */ + CODE_9206001(9206001, "3rd resp order_id empty"), + + /** + * 3rd resp bill_type_code empty + */ + CODE_9206002(9206002, "3rd resp bill_type_code empty"), + + /** + * 3rd resp bill_no empty + */ + CODE_9206003(9206003, "3rd resp bill_no empty"), + + /** + * 3rd resp empty + */ + CODE_9206200(9206200, "3rd resp empty"), + + /** + * 3rd resp not json + */ + CODE_9206201(9206201, "3rd resp not json"), + + /** + * connect 3rd error + */ + CODE_9206900(9206900, "connect 3rd error"), + + /** + * connect 3rd timeout + */ + CODE_9206901(9206901, "connect 3rd timeout"), + + /** + * read 3rd resp error + */ + CODE_9206910(9206910, "read 3rd resp error"), + + /** + * read 3rd resp timeout + */ + CODE_9206911(9206911, "read 3rd resp timeout"), + + /** + * boss error + */ + CODE_9207000(9207000, "boss error"), + + /** + * wechat pay error + */ + CODE_9207001(9207001, "wechat pay error"), + + /** + * boss param error + */ + CODE_9207002(9207002, "boss param error"), + + /** + * pay error + */ + CODE_9207003(9207003, "pay error"), + + /** + * auth_code expired + */ + CODE_9207004(9207004, "auth_code expired"), + + /** + * user balance not enough + */ + CODE_9207005(9207005, "user balance not enough"), + + /** + * card not support + */ + CODE_9207006(9207006, "card not support"), + + /** + * order reversed + */ + CODE_9207007(9207007, "order reversed"), + + /** + * user paying, need input password + */ + CODE_9207008(9207008, "user paying, need input password"), + + /** + * auth_code error + */ + CODE_9207009(9207009, "auth_code error"), + + /** + * auth_code invalid + */ + CODE_9207010(9207010, "auth_code invalid"), + + /** + * not allow to reverse when user paying + */ + CODE_9207011(9207011, "not allow to reverse when user paying"), + + /** + * order paid + */ + CODE_9207012(9207012, "order paid"), + + /** + * order closed + */ + CODE_9207013(9207013, "order closed"), + + /** + * vehicle not exists + */ + CODE_9207028(9207028, "vehicle not exists"), + + /** + * vehicle request blocked + */ + CODE_9207029(9207029, "vehicle request blocked"), + + /** + * vehicle auth error + */ + CODE_9207030(9207030, "vehicle auth error"), + + /** + * contract over limit + */ + CODE_9207031(9207031, "contract over limit"), + + /** + * trade error + */ + CODE_9207032(9207032, "trade error"), + + /** + * trade time invalid + */ + CODE_9207033(9207033, "trade time invalid"), + + /** + * channel type invalid + */ + CODE_9207034(9207034, "channel type invalid"), + + /** + * expire_time range error + */ + CODE_9207050(9207050, "expire_time range error"), + + /** + * query finance error + */ + CODE_9210000(9210000, "query finance error"), + + /** + * openid error + */ + CODE_9291000(9291000, "openid error"), + + /** + * openid appid not match + */ + CODE_9291001(9291001, "openid appid not match"), + + /** + * app_appid not exist + */ + CODE_9291002(9291002, "app_appid not exist"), + + /** + * app_appid not app + */ + CODE_9291003(9291003, "app_appid not app"), + + /** + * appid empty + */ + CODE_9291004(9291004, "appid empty"), + + /** + * appid not match access_token + */ + CODE_9291005(9291005, "appid not match access_token"), + + /** + * invalid sign + */ + CODE_9291006(9291006, "invalid sign"), + + /** + * backend logic error + */ + CODE_9299999(9299999, "backend logic error"), + + /** + * begin_time can not before now + */ + CODE_9300001(9300001, "begin_time can not before now"), + + /** + * end_time can not before now + */ + CODE_9300002(9300002, "end_time can not before now"), + + /** + * begin_time must less than end_time + */ + CODE_9300003(9300003, "begin_time must less than end_time"), + + /** + * end_time - begin_time > 1year + */ + CODE_9300004(9300004, "end_time - begin_time > 1year"), + + /** + * invalid max_partic_times + */ + CODE_9300005(9300005, "invalid max_partic_times"), + + /** + * invalid activity status + */ + CODE_9300006(9300006, "invalid activity status"), + + /** + * gift_num must >0 and <=15 + */ + CODE_9300007(9300007, "gift_num must >0 and <=15"), + + /** + * invalid tiny appid + */ + CODE_9300008(9300008, "invalid tiny appid"), + + /** + * activity can not finish + */ + CODE_9300009(9300009, "activity can not finish"), + + /** + * card_info_list must >= 2 + */ + CODE_9300010(9300010, "card_info_list must >= 2"), + + /** + * invalid card_id + */ + CODE_9300011(9300011, "invalid card_id"), + + /** + * card_id must belong this appid + */ + CODE_9300012(9300012, "card_id must belong this appid"), + + /** + * card_id is not swipe_card or pay.cash + */ + CODE_9300013(9300013, "card_id is not swipe_card or pay.cash"), + + /** + * some card_id is out of stock + */ + CODE_9300014(9300014, "some card_id is out of stock"), + + /** + * some card_id is invalid status + */ + CODE_9300015(9300015, "some card_id is invalid status"), + + /** + * membership or new/old tinyapp user only support one + */ + CODE_9300016(9300016, "membership or new/old tinyapp user only support one"), + + /** + * invalid logic for membership + */ + CODE_9300017(9300017, "invalid logic for membership"), + + /** + * invalid logic for tinyapp new/old user + */ + CODE_9300018(9300018, "invalid logic for tinyapp new/old user"), + + /** + * invalid activity type + */ + CODE_9300019(9300019, "invalid activity type"), + + /** + * invalid activity_id + */ + CODE_9300020(9300020, "invalid activity_id"), + + /** + * invalid help_max_times + */ + CODE_9300021(9300021, "invalid help_max_times"), + + /** + * invalid cover_url + */ + CODE_9300022(9300022, "invalid cover_url"), + + /** + * invalid gen_limit + */ + CODE_9300023(9300023, "invalid gen_limit"), + + /** + * card's end_time cannot early than act's end_time + */ + CODE_9300024(9300024, "card's end_time cannot early than act's end_time"), + + /** + * 快递侧逻辑错误,详细原因需要看 delivery_resultcode, 请先确认一下编码方式,python建议 json.dumps(b, ensure_ascii=False),php建议 json_encode($arr, JSON_UNESCAPED_UNICODE) Delivery side error + */ + CODE_9300501(9300501, "快递侧逻辑错误,详细原因需要看 delivery_resultcode, 请先确认一下编码方式,python建议 json.dumps(b, ensure_ascii=False),php建议 json_encode($arr, JSON_UNESCAPED_UNICODE)"), + + /** + * 快递公司系统错误 Delivery side sys error + */ + CODE_9300502(9300502, "快递公司系统错误"), + + /** + * delivery_id 不存在 Specified delivery id is not registerred + */ + CODE_9300503(9300503, "delivery_id 不存在"), + + /** + * service_type 不存在 Specified delivery id has beed banned + */ + CODE_9300504(9300504, "service_type 不存在"), + + /** + * Shop banned + */ + CODE_9300505(9300505, "Shop banned"), + + /** + * 运单 ID 已经存在轨迹,不可取消 Order can't cancel + */ + CODE_9300506(9300506, "运单 ID 已经存在轨迹,不可取消"), + + /** + * Token 不正确 invalid token, can't decryption or decryption result is different from the plaintext + */ + CODE_9300507(9300507, "Token 不正确"), + + /** + * order id has been used + */ + CODE_9300508(9300508, "order id has been used"), + + /** + * speed limit, retry too fast + */ + CODE_9300509(9300509, "speed limit, retry too fast"), + + /** + * invalid service type + */ + CODE_9300510(9300510, "invalid service type"), + + /** + * invalid branch id + */ + CODE_9300511(9300511, "invalid branch id"), + + /** + * 模板格式错误,渲染失败 invalid waybill template format + */ + CODE_9300512(9300512, "模板格式错误,渲染失败"), + + /** + * out of quota + */ + CODE_9300513(9300513, "out of quota"), + + /** + * add net branch fail, try update branch api + */ + CODE_9300514(9300514, "add net branch fail, try update branch api"), + + /** + * wxa appid not exist + */ + CODE_9300515(9300515, "wxa appid not exist"), + + /** + * wxa appid and current bizuin is not linked or not the same owner + */ + CODE_9300516(9300516, "wxa appid and current bizuin is not linked or not the same owner"), + + /** + * update_type 不正确,请使用"bind" 或者“unbind” invalid update_type, please use [bind] or [unbind] + */ + CODE_9300517(9300517, "update_type 不正确,请使用\"bind\" 或者“unbind”"), + + /** + * invalid delivery id + */ + CODE_9300520(9300520, "invalid delivery id"), + + /** + * the orderid is in our system, and waybill is generating + */ + CODE_9300521(9300521, "the orderid is in our system, and waybill is generating"), + + /** + * this orderid is repeated + */ + CODE_9300522(9300522, "this orderid is repeated"), + + /** + * quota is not enough; go to charge please + */ + CODE_9300523(9300523, "quota is not enough; go to charge please"), + + /** + * 订单已取消(一般为重复取消订单) order already canceled + */ + CODE_9300524(9300524, "订单已取消(一般为重复取消订单)"), + + /** + * bizid未绑定 biz id not bind + */ + CODE_9300525(9300525, "bizid未绑定"), + + /** + * 参数字段长度不正确 arg size exceed limit + */ + CODE_9300526(9300526, "参数字段长度不正确"), + + /** + * delivery does not support quota + */ + CODE_9300527(9300527, "delivery does not support quota"), + + /** + * invalid waybill_id + */ + CODE_9300528(9300528, "invalid waybill_id"), + + /** + * 账号已绑定过 biz_id already binded + */ + CODE_9300529(9300529, "账号已绑定过"), + + /** + * 解绑的biz_id不存在 biz_id is not exist + */ + CODE_9300530(9300530, "解绑的biz_id不存在"), + + /** + * bizid无效 或者密码错误 invalid biz_id or password + */ + CODE_9300531(9300531, "bizid无效 或者密码错误"), + + /** + * 绑定已提交,审核中 bind submit, and is checking + */ + CODE_9300532(9300532, "绑定已提交,审核中"), + + /** + * invalid tagid_list + */ + CODE_9300533(9300533, "invalid tagid_list"), + + /** + * add_source=2时,wx_appid和当前小程序不同主体 invalid appid, not same body + */ + CODE_9300534(9300534, "add_source=2时,wx_appid和当前小程序不同主体"), + + /** + * shop字段商品缩略图 url、商品名称为空或者非法,或者商品数量为0 invalid shop arg + */ + CODE_9300535(9300535, "shop字段商品缩略图 url、商品名称为空或者非法,或者商品数量为0"), + + /** + * add_source=2时,wx_appid无效 invalid wxa_appid + */ + CODE_9300536(9300536, "add_source=2时,wx_appid无效"), + + /** + * freq limit + */ + CODE_9300537(9300537, "freq limit"), + + /** + * input task empty + */ + CODE_9300538(9300538, "input task empty"), + + /** + * too many task + */ + CODE_9300539(9300539, "too many task"), + + /** + * task not exist + */ + CODE_9300540(9300540, "task not exist"), + + /** + * delivery callback error + */ + CODE_9300541(9300541, "delivery callback error"), + + /** + * id_card_no is invalid + */ + CODE_9300601(9300601, "id_card_no is invalid"), + + /** + * name is invalid + */ + CODE_9300602(9300602, "name is invalid"), + + /** + * plate_no is invalid + */ + CODE_9300603(9300603, "plate_no is invalid"), + + /** + * auth_key decode error + */ + CODE_9300604(9300604, "auth_key decode error"), + + /** + * auth_key is expired + */ + CODE_9300605(9300605, "auth_key is expired"), + + /** + * auth_key and appinfo not match + */ + CODE_9300606(9300606, "auth_key and appinfo not match"), + + /** + * user not confirm + */ + CODE_9300607(9300607, "user not confirm"), + + /** + * user confirm is expired + */ + CODE_9300608(9300608, "user confirm is expired"), + + /** + * api exceed limit + */ + CODE_9300609(9300609, "api exceed limit"), + + /** + * car license info is invalid + */ + CODE_9300610(9300610, "car license info is invalid"), + + /** + * varification type not support + */ + CODE_9300611(9300611, "varification type not support"), + + /** + * input param error + */ + CODE_9300701(9300701, "input param error"), + + /** + * this code has been used + */ + CODE_9300702(9300702, "this code has been used"), + + /** + * invalid date + */ + CODE_9300703(9300703, "invalid date"), + + /** + * not currently available + */ + CODE_9300704(9300704, "not currently available"), + + /** + * code not exist or expired + */ + CODE_9300705(9300705, "code not exist or expired"), + + /** + * code not exist or expired + */ + CODE_9300706(9300706, "code not exist or expired"), + + /** + * wxpay error + */ + CODE_9300707(9300707, "wxpay error"), + + /** + * wxpay overlimit + */ + CODE_9300708(9300708, "wxpay overlimit"), + + /** + * 无效的微信号 + */ + CODE_9300801(9300801, "无效的微信号"), + + /** + * 服务号未开通导购功能 + */ + CODE_9300802(9300802, "服务号未开通导购功能"), + + /** + * 微信号已经绑定为导购 + */ + CODE_9300803(9300803, "微信号已经绑定为导购"), + + /** + * 该微信号不是导购 + */ + CODE_9300804(9300804, "该微信号不是导购"), + + /** + * 微信号已经被其他账号绑定为导购 + */ + CODE_9300805(9300805, "微信号已经被其他账号绑定为导购"), + + /** + * 粉丝和导购不存在绑定关系 + */ + CODE_9300806(9300806, "粉丝和导购不存在绑定关系"), + + /** + * 标签值无效,不是可选标签值 + */ + CODE_9300807(9300807, "标签值无效,不是可选标签值"), + + /** + * 标签值不存在 + */ + CODE_9300808(9300808, "标签值不存在"), + + /** + * 展示标签值不存在 + */ + CODE_9300809(9300809, "展示标签值不存在"), + + /** + * 导购昵称太长,最多16个字符 + */ + CODE_9300810(9300810, "导购昵称太长,最多16个字符"), + + /** + * 只支持mmbiz.qpic.cn域名的图片 + */ + CODE_9300811(9300811, "只支持mmbiz.qpic.cn域名的图片"), + + /** + * 达到导购绑定个数限制 + */ + CODE_9300812(9300812, "达到导购绑定个数限制"), + + /** + * 达到导购粉丝绑定个数限制 + */ + CODE_9300813(9300813, "达到导购粉丝绑定个数限制"), + + /** + * 敏感词个数超过上限 + */ + CODE_9300814(9300814, "敏感词个数超过上限"), + + /** + * 快捷回复个数超过上限 + */ + CODE_9300815(9300815, "快捷回复个数超过上限"), + + /** + * 文字素材个数超过上限 + */ + CODE_9300816(9300816, "文字素材个数超过上限"), + + /** + * 小程序卡片素材个数超过上限 + */ + CODE_9300817(9300817, "小程序卡片素材个数超过上限"), + + /** + * 图片素材个数超过上限 + */ + CODE_9300818(9300818, "图片素材个数超过上限"), + + /** + * mediaid 有误 + */ + CODE_9300819(9300819, "mediaid 有误"), + + /** + * 可查询标签类别超过上限 + */ + CODE_9300820(9300820, "可查询标签类别超过上限"), + + /** + * 小程序卡片内appid不符合要求 + */ + CODE_9300821(9300821, "小程序卡片内appid不符合要求"), + + /** + * 标签类别的名字无效 + */ + CODE_9300822(9300822, "标签类别的名字无效"), + + /** + * 查询聊天记录时间参数有误 + */ + CODE_9300823(9300823, "查询聊天记录时间参数有误"), + + /** + * 自动回复字数太长 + */ + CODE_9300824(9300824, "自动回复字数太长"), + + /** + * 导购群组id错误 + */ + CODE_9300825(9300825, "导购群组id错误"), + + /** + * 维护中 + */ + CODE_9300826(9300826, "维护中"), + + /** + * invalid parameter + */ + CODE_9301001(9301001, "invalid parameter"), + + /** + * call api service failed + */ + CODE_9301002(9301002, "call api service failed"), + + /** + * internal exception + */ + CODE_9301003(9301003, "internal exception"), + + /** + * save data error + */ + CODE_9301004(9301004, "save data error"), + + /** + * invalid appid + */ + CODE_9301006(9301006, "invalid appid"), + + /** + * invalid api config + */ + CODE_9301007(9301007, "invalid api config"), + + /** + * invalid api info + */ + CODE_9301008(9301008, "invalid api info"), + + /** + * add result check failed + */ + CODE_9301009(9301009, "add result check failed"), + + /** + * consumption failure + */ + CODE_9301010(9301010, "consumption failure"), + + /** + * frequency limit reached + */ + CODE_9301011(9301011, "frequency limit reached"), + + /** + * service timeout + */ + CODE_9301012(9301012, "service timeout"), + + /** + * 该开发小程序已开通小程序直播权限,不支持发布版本。如需发版,请解绑开发小程序后再操作。 + */ + CODE_9400001(9400001, "该开发小程序已开通小程序直播权限,不支持发布版本。如需发版,请解绑开发小程序后再操作。"), + + /** + * 商品已存在 + */ + CODE_9401001(9401001, "商品已存在"), + + /** + * 商品不存在 + */ + CODE_9401002(9401002, "商品不存在"), + + /** + * 类目已存在 + */ + CODE_9401003(9401003, "类目已存在"), + + /** + * 类目不存在 + */ + CODE_9401004(9401004, "类目不存在"), + + /** + * SKU已存在 + */ + CODE_9401005(9401005, "SKU已存在"), + + /** + * SKU不存在 + */ + CODE_9401006(9401006, "SKU不存在"), + + /** + * 属性已存在 + */ + CODE_9401007(9401007, "属性已存在"), + + /** + * 属性不存在 + */ + CODE_9401008(9401008, "属性不存在"), + + /** + * 非法参数 + */ + CODE_9401020(9401020, "非法参数"), + + /** + * 没有商品权限 + */ + CODE_9401021(9401021, "没有商品权限"), + + /** + * SPU NOT ALLOW + */ + CODE_9401022(9401022, "SPU NOT ALLOW"), + + /** + * SPU_NOT_ALLOW_EDIT + */ + CODE_9401023(9401023, "SPU_NOT_ALLOW_EDIT"), + + /** + * SKU NOT ALLOW + */ + CODE_9401024(9401024, "SKU NOT ALLOW"), + + /** + * SKU_NOT_ALLOW_EDIT + */ + CODE_9401025(9401025, "SKU_NOT_ALLOW_EDIT"), + + /** + * limit too large + */ + CODE_9402001(9402001, "limit too large"), + + /** + * single send been blocked + */ + CODE_9402002(9402002, "single send been blocked"), + + /** + * all send been blocked + */ + CODE_9402003(9402003, "all send been blocked"), + + /** + * invalid msg id + */ + CODE_9402004(9402004, "invalid msg id"), + + /** + * send msg too quick + */ + CODE_9402005(9402005, "send msg too quick"), + + /** + * send to single user too quick + */ + CODE_9402006(9402006, "send to single user too quick"), + + /** + * send to all user too quick + */ + CODE_9402007(9402007, "send to all user too quick"), + + /** + * send type error + */ + CODE_9402008(9402008, "send type error"), + + /** + * can not send this msg + */ + CODE_9402009(9402009, "can not send this msg"), + + /** + * content too long or no content + */ + CODE_9402010(9402010, "content too long or no content"), + + /** + * path not exist + */ + CODE_9402011(9402011, "path not exist"), + + /** + * contain evil word + */ + CODE_9402012(9402012, "contain evil word"), + + /** + * path need html suffix + */ + CODE_9402013(9402013, "path need html suffix"), + + /** + * not open to personal body type + */ + CODE_9402014(9402014, "not open to personal body type"), + + /** + * not open to violation body type + */ + CODE_9402015(9402015, "not open to violation body type"), + + /** + * not open to low quality provider + */ + CODE_9402016(9402016, "not open to low quality provider"), + + /** + * invalid product_id + */ + CODE_9402101(9402101, "invalid product_id"), + + /** + * device_id count more than limit + */ + CODE_9402102(9402102, "device_id count more than limit"), + + /** + * 请勿频繁提交,待上一次操作完成后再提交 concurrent limit + */ + CODE_9402202(9402202, "请勿频繁提交,待上一次操作完成后再提交"), + + /** + * user not book this ad id + */ + CODE_9402301(9402301, "user not book this ad id"), + + /** + * 消息类型错误! + */ + CODE_9403000(9403000, "消息类型错误!"), + + /** + * 消息字段的内容过长! + */ + CODE_9403001(9403001, "消息字段的内容过长!"), + + /** + * 消息字段的内容违规! + */ + CODE_9403002(9403002, "消息字段的内容违规!"), + + /** + * 发送的微信号太多! + */ + CODE_9403003(9403003, "发送的微信号太多!"), + + /** + * 存在错误的微信号! + */ + CODE_9403004(9403004, "存在错误的微信号!"), + + /** + * 直播间列表为空 live room not exsits + */ + CODE_9410000(9410000, "直播间列表为空"), + + /** + * 获取房间失败 inner error: get room fail + */ + CODE_9410001(9410001, "获取房间失败"), + + /** + * 获取商品失败 inner error: get goods fail + */ + CODE_9410002(9410002, "获取商品失败"), + + /** + * 获取回放失败 inner error: get replay url fail + */ + CODE_9410003(9410003, "获取回放失败"); + + + private final int code; + private final String msg; + + WxOpenErrorMsgEnum(int code, String msg) { + this.code = code; + this.msg = msg; + } + + static final Map valueMap = Maps.newHashMap(); + + static { + for (WxOpenErrorMsgEnum value : WxOpenErrorMsgEnum.values()) { + valueMap.put(value.code, value.msg); + } + } + + /** + * 通过错误代码查找其中文含义. + */ + public static String findMsgByCode(int code) { + return valueMap.getOrDefault(code, null); + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/XmlUtils.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/XmlUtils.java index 91c6b8f2e..facf564e3 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/XmlUtils.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/XmlUtils.java @@ -3,7 +3,6 @@ import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; -import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.error.WxRuntimeException; import org.dom4j.*; import org.dom4j.io.SAXReader; @@ -15,6 +14,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; /** *
@@ -39,7 +39,18 @@ public static Map xml2Map(String xmlString) {
       Element root = doc.getRootElement();
       List elements = root.elements();
       for (Element element : elements) {
-        map.put(element.getName(), element2MapOrString(element));
+        String elementName = element.getName();
+        if (map.containsKey(elementName)) {
+          if (map.get(elementName) instanceof List) {
+            ((List) map.get(elementName)).add(element2MapOrString(element));
+          } else {
+            List value = Lists.newArrayList(map.get(elementName));
+            value.add(element2MapOrString(element));
+            map.put(elementName, value);
+          }
+        } else {
+          map.put(elementName, element2MapOrString(element));
+        }
       }
     } catch (DocumentException | SAXException e) {
       throw new WxRuntimeException(e);
@@ -50,8 +61,8 @@ public static Map xml2Map(String xmlString) {
 
   private static Object element2MapOrString(Element element) {
 
-    final List content = element.content();
-    final Set names = names(content);
+    final List nodes = element.content();
+    final List names = names(nodes);
 
     // 判断节点下有无非文本节点(非Text和CDATA),如无,直接取Text文本内容
     if (names.size() < 1) {
@@ -59,10 +70,11 @@ private static Object element2MapOrString(Element element) {
     }
 
     Map result = Maps.newHashMap();
-    if (names.size() == 1) {
+    Set distinctNames = Sets.newHashSet(names);
+    if (distinctNames.size() == 1) {
       // 说明是个列表,各个子对象是相同的name
       List list = Lists.newArrayList();
-      for (Node node : content) {
+      for (Node node : nodes) {
         if (node instanceof DefaultText) {
           continue;
         }
@@ -73,8 +85,8 @@ private static Object element2MapOrString(Element element) {
       }
 
       result.put(names.iterator().next(), list);
-    } else {
-      for (Node node : content) {
+    } else if (distinctNames.size() == names.size()) {
+      for (Node node : nodes) {
         if (node instanceof DefaultText) {
           continue;
         }
@@ -83,13 +95,38 @@ private static Object element2MapOrString(Element element) {
           result.put(node.getName(), element2MapOrString((Element) node));
         }
       }
+    } else {
+      // 说明有重复name,但不是全部都相同
+      Map namesCountMap = names.stream().collect(Collectors.groupingBy(a -> a, Collectors.counting()));
+      for (Node node : nodes) {
+        if (node instanceof DefaultText) {
+          continue;
+        }
+
+        if (node instanceof Element) {
+          String nodeName = node.getName();
+          if (namesCountMap.get(nodeName) == 1) {
+            result.put(nodeName, element2MapOrString((Element) node));
+          } else {
+            List values;
+            if (result.containsKey(nodeName)) {
+              values = (List) result.get(nodeName);
+            } else {
+              values = Lists.newArrayList();
+              result.put(nodeName, values);
+            }
+
+            values.add(element2MapOrString((Element) node));
+          }
+        }
+      }
     }
 
     return result;
   }
 
-  private static Set names(List nodes) {
-    Set names = Sets.newHashSet();
+  private static List names(List nodes) {
+    List names = Lists.newArrayList();
     for (Node node : nodes) {
       // 如果节点类型是Text或CDATA跳过
       if (node instanceof DefaultText || node instanceof CDATA) {
diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/WxCryptUtil.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/WxCryptUtil.java
index a73e01d0d..d44791eb1 100755
--- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/WxCryptUtil.java
+++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/WxCryptUtil.java
@@ -1,25 +1,26 @@
 package me.chanjar.weixin.common.util.crypto;
 
-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 com.google.common.base.CharMatcher;
-import com.google.common.io.BaseEncoding;
+import lombok.AllArgsConstructor;
+import lombok.Data;
 import me.chanjar.weixin.common.error.WxRuntimeException;
 import org.apache.commons.codec.binary.Base64;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.xml.sax.InputSource;
 
+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;
+
 /**
  * 
  * 对公众平台发送给公众账号的消息加解密示例代码.
@@ -157,6 +158,29 @@ public String encrypt(String plainText) {
     return generateXml(encryptedXml, signature, timeStamp, nonce);
   }
 
+  /**
+   * 将公众平台回复用户的消息加密打包.
+   * 
    + *
  1. 对要发送的消息进行AES-CBC加密
  2. + *
  3. 生成安全签名
  4. + *
  5. 将消息密文和安全签名打包成xml格式
  6. + *
+ * + * @param plainText 公众平台待回复用户的消息,xml格式的字符串 + * @return 加密消息所需的值对象 + */ + public EncryptContext encryptContext(String plainText) { + // 加密 + String encryptedXml = encrypt(genRandomStr(), plainText); + + // 生成安全签名 + String timeStamp = Long.toString(System.currentTimeMillis() / 1000L); + String nonce = genRandomStr(); + + String signature = SHA1.gen(this.token, timeStamp, nonce, encryptedXml); + return new EncryptContext(encryptedXml, signature, timeStamp, nonce); + } + /** * 对明文进行加密. * @@ -211,22 +235,58 @@ public String encrypt(String randomStr, String plainText) { * @param msgSignature 签名串,对应URL参数的msg_signature * @param timeStamp 时间戳,对应URL参数的timestamp * @param nonce 随机串,对应URL参数的nonce - * @param encryptedXml 密文,对应POST请求的数据 + * @param encryptedXml 包含 Encrypt 密文的 xml,对应POST请求的数据 * @return 解密后的原文 */ - public String decrypt(String msgSignature, String timeStamp, String nonce, String encryptedXml) { + public String decryptXml(String msgSignature, String timeStamp, String nonce, String encryptedXml) { // 密钥,公众账号的app corpSecret // 提取密文 String cipherText = extractEncryptPart(encryptedXml); + return decryptContent(msgSignature, timeStamp, nonce, cipherText); + } + /** + * 检验消息的真实性,并且获取解密后的明文. + *
    + *
  1. 利用收到的密文生成安全签名,进行签名验证
  2. + *
  3. 若验证通过,则提取xml中的加密消息
  4. + *
  5. 对消息进行解密
  6. + *
+ * + * @param msgSignature 签名串,对应URL参数的msg_signature + * @param timeStamp 时间戳,对应URL参数的timestamp + * @param nonce 随机串,对应URL参数的nonce + * @param encryptedXml 包含 Encrypt 密文的 xml,对应POST请求的数据 + * @return 解密后的原文 + * @deprecated 由于语义不清晰,置为过时方法,请查看替代方法 {@link #decryptXml} + */ + @Deprecated + public String decrypt(String msgSignature, String timeStamp, String nonce, String encryptedXml) { + return decryptXml(msgSignature, timeStamp, nonce, encryptedXml); + } + + /** + * 检验消息的真实性,并且获取解密后的明文. + *
    + *
  1. 利用收到的密文生成安全签名,进行签名验证
  2. + *
  3. 若验证通过,则提取xml中的加密消息
  4. + *
  5. 对消息进行解密
  6. + *
+ * + * @param msgSignature 签名串,对应URL参数的msg_signature + * @param timeStamp 时间戳,对应URL参数的timestamp + * @param nonce 随机串,对应URL参数的nonce + * @param encryptedContent 加密文本体 + * @return 解密后的原文 + */ + public String decryptContent(String msgSignature, String timeStamp, String nonce, String encryptedContent) { // 验证安全签名 - String signature = SHA1.gen(this.token, timeStamp, nonce, cipherText); + String signature = SHA1.gen(this.token, timeStamp, nonce, encryptedContent); if (!signature.equals(msgSignature)) { throw new WxRuntimeException("加密消息签名校验失败"); } - // 解密 - return decrypt(cipherText); + return decrypt(encryptedContent); } /** @@ -271,12 +331,20 @@ public String decrypt(String cipherText) { } // appid不相同的情况 暂时忽略这段判断 -// if (!fromAppid.equals(this.appidOrCorpid)) { -// throw new WxRuntimeException("AppID不正确,请核实!"); -// } + // if (!fromAppid.equals(this.appidOrCorpid)) { + // throw new WxRuntimeException("AppID不正确,请核实!"); + // } return xmlContent; } + @Data + @AllArgsConstructor + public static class EncryptContext { + private String encrypt; + private String signature; + private String timeStamp; + private String nonce; + } } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/fs/FileUtils.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/fs/FileUtils.java index d60f5cedd..65bc48da1 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/fs/FileUtils.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/fs/FileUtils.java @@ -43,7 +43,7 @@ private static void copyToFile(final InputStream source, final File destination) * @param ext 扩展名 */ public static File createTmpFile(InputStream inputStream, String name, String ext) throws IOException { - return createTmpFile(inputStream, name, ext, Files.createTempDirectory("weixin-java-tools-temp").toFile()); + return createTmpFile(inputStream, name, ext, Files.createTempDirectory("wxjava-temp").toFile()); } /** diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/InputStreamData.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/InputStreamData.java new file mode 100644 index 000000000..fe80af11e --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/InputStreamData.java @@ -0,0 +1,27 @@ +package me.chanjar.weixin.common.util.http; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.InputStream; +import java.io.Serializable; + +/** + * 输入流数据. + *

+ * InputStreamData + * + * @author zichuan.zhou91@gmail.com + * @date 2022/2/15 + */ +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +public class InputStreamData implements Serializable { + private static final long serialVersionUID = -4627006604779378520L; + private InputStream inputStream; + private String filename; +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaInputStreamUploadRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaInputStreamUploadRequestExecutor.java new file mode 100644 index 000000000..de4be2170 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaInputStreamUploadRequestExecutor.java @@ -0,0 +1,43 @@ +package me.chanjar.weixin.common.util.http; + +import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.apache.ApacheMediaInputStreamUploadRequestExecutor; +import me.chanjar.weixin.common.util.http.jodd.JoddHttpMediaInputStreamUploadRequestExecutor; +import me.chanjar.weixin.common.util.http.okhttp.OkHttpMediaInputStreamUploadRequestExecutor; + +import java.io.IOException; + +/** + * 上传媒体文件请求执行器. + * 请求的参数是File, 返回的结果是String + * + * @author Daniel Qian + */ +public abstract class MediaInputStreamUploadRequestExecutor implements RequestExecutor { + protected RequestHttp requestHttp; + + public MediaInputStreamUploadRequestExecutor(RequestHttp requestHttp) { + this.requestHttp = requestHttp; + } + + @Override + public void execute(String uri, InputStreamData data, ResponseHandler handler, WxType wxType) throws WxErrorException, IOException { + handler.handle(this.execute(uri, data, wxType)); + } + + public static RequestExecutor create(RequestHttp requestHttp) { + switch (requestHttp.getRequestType()) { + case APACHE_HTTP: + return new ApacheMediaInputStreamUploadRequestExecutor(requestHttp); + case JODD_HTTP: + return new JoddHttpMediaInputStreamUploadRequestExecutor(requestHttp); + case OK_HTTP: + return new OkHttpMediaInputStreamUploadRequestExecutor(requestHttp); + default: + return null; + } + } + +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MinishopUploadRequestCustomizeExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MinishopUploadRequestCustomizeExecutor.java index 23309202d..e94b2d8d6 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MinishopUploadRequestCustomizeExecutor.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MinishopUploadRequestCustomizeExecutor.java @@ -13,10 +13,19 @@ public abstract class MinishopUploadRequestCustomizeExecutor implements RequestExecutor { protected RequestHttp requestHttp; protected String respType; + protected String uploadType; + protected String imgUrl; - public MinishopUploadRequestCustomizeExecutor(RequestHttp requestHttp, String respType) { + public MinishopUploadRequestCustomizeExecutor(RequestHttp requestHttp, String respType, String imgUrl) { this.requestHttp = requestHttp; this.respType = respType; + if (imgUrl == null || imgUrl.isEmpty()) { + this.uploadType = "0"; + } + else { + this.uploadType = "1"; + this.imgUrl = imgUrl; + } } @Override @@ -24,14 +33,14 @@ public void execute(String uri, File data, ResponseHandler create(RequestHttp requestHttp, String respType) { + public static RequestExecutor create(RequestHttp requestHttp, String respType, String imgUrl) { switch (requestHttp.getRequestType()) { case APACHE_HTTP: - return new ApacheMinishopMediaUploadRequestCustomizeExecutor(requestHttp, respType); + return new ApacheMinishopMediaUploadRequestCustomizeExecutor(requestHttp, respType, imgUrl); case JODD_HTTP: - return new JoddHttpMinishopMediaUploadRequestCustomizeExecutor(requestHttp, respType); + return new JoddHttpMinishopMediaUploadRequestCustomizeExecutor(requestHttp, respType, imgUrl); case OK_HTTP: - return new OkHttpMinishopMediaUploadRequestCustomizeExecutor(requestHttp, respType); + return new OkHttpMinishopMediaUploadRequestCustomizeExecutor(requestHttp, respType, imgUrl); default: return null; } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/RequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/RequestExecutor.java index da1292ba6..b5e394756 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/RequestExecutor.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/RequestExecutor.java @@ -37,4 +37,6 @@ public interface RequestExecutor { * @throws IOException io异常 */ void execute(String uri, E data, ResponseHandler handler, WxType wxType) throws WxErrorException, IOException; + + } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheHttpClientBuilder.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheHttpClientBuilder.java index fcd56c48a..0d5073de1 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheHttpClientBuilder.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheHttpClientBuilder.java @@ -1,5 +1,7 @@ package me.chanjar.weixin.common.util.http.apache; +import org.apache.http.client.HttpRequestRetryHandler; +import org.apache.http.conn.ConnectionKeepAliveStrategy; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.impl.client.CloseableHttpClient; @@ -37,6 +39,16 @@ public interface ApacheHttpClientBuilder { */ ApacheHttpClientBuilder httpProxyPassword(String httpProxyPassword); + /** + * 重试策略. + */ + ApacheHttpClientBuilder httpRequestRetryHandler(HttpRequestRetryHandler httpRequestRetryHandler ); + + /** + * 超时时间. + */ + ApacheHttpClientBuilder keepAliveStrategy(ConnectionKeepAliveStrategy keepAliveStrategy); + /** * ssl连接socket工厂. */ diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheHttpDnsClientBuilder.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheHttpDnsClientBuilder.java index e1959f08f..6a136600e 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheHttpDnsClientBuilder.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheHttpDnsClientBuilder.java @@ -10,6 +10,7 @@ import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; import org.apache.http.config.SocketConfig; +import org.apache.http.conn.ConnectionKeepAliveStrategy; import org.apache.http.conn.DnsResolver; import org.apache.http.conn.HttpClientConnectionManager; import org.apache.http.conn.socket.ConnectionSocketFactory; @@ -48,12 +49,13 @@ public class ApacheHttpDnsClientBuilder implements ApacheHttpClientBuilder { private int maxTotalConn = 50; private String userAgent; - private DnsResolver dnsResover; + private DnsResolver dnsResolver; private HttpRequestRetryHandler httpRequestRetryHandler = (IOException exception, int executionCount, HttpContext context) -> false; private SSLConnectionSocketFactory sslConnectionSocketFactory = SSLConnectionSocketFactory.getSocketFactory(); private PlainConnectionSocketFactory plainConnectionSocketFactory = PlainConnectionSocketFactory.getSocketFactory(); private String httpProxyHost; + private ConnectionKeepAliveStrategy keepAliveStrategy; private int httpProxyPort; private String httpProxyUsername; @@ -97,6 +99,18 @@ public ApacheHttpClientBuilder httpProxyPassword(String httpProxyPassword) { return this; } + @Override + public ApacheHttpClientBuilder httpRequestRetryHandler(HttpRequestRetryHandler httpRequestRetryHandler) { + this.httpRequestRetryHandler = httpRequestRetryHandler; + return this; + } + + @Override + public ApacheHttpClientBuilder keepAliveStrategy(ConnectionKeepAliveStrategy keepAliveStrategy) { + this.keepAliveStrategy = keepAliveStrategy; + return this; + } + @Override public ApacheHttpClientBuilder sslConnectionSocketFactory(SSLConnectionSocketFactory sslConnectionSocketFactory) { this.sslConnectionSocketFactory = sslConnectionSocketFactory; @@ -202,11 +216,11 @@ private synchronized void prepare() { @SuppressWarnings("resource") PoolingHttpClientConnectionManager connectionManager; - if (dnsResover != null) { + if (dnsResolver != null) { if (log.isDebugEnabled()) { log.debug("specified dns resolver."); } - connectionManager = new PoolingHttpClientConnectionManager(registry, dnsResover); + connectionManager = new PoolingHttpClientConnectionManager(registry, dnsResolver); } else { if (log.isDebugEnabled()) { log.debug("Not specified dns resolver."); @@ -254,12 +268,12 @@ public CloseableHttpClient build() { return this.httpClientBuilder.build(); } - public DnsResolver getDnsResover() { - return dnsResover; + public DnsResolver getDnsResolver() { + return dnsResolver; } - public void setDnsResover(DnsResolver dnsResover) { - this.dnsResover = dnsResover; + public void setDnsResolver(DnsResolver dnsResolver) { + this.dnsResolver = dnsResolver; } public static class IdleConnectionMonitorThread extends Thread { diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheMediaInputStreamUploadRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheMediaInputStreamUploadRequestExecutor.java new file mode 100644 index 000000000..3e6d189e8 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheMediaInputStreamUploadRequestExecutor.java @@ -0,0 +1,59 @@ +package me.chanjar.weixin.common.util.http.apache; + +import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.InputStreamData; +import me.chanjar.weixin.common.util.http.MediaInputStreamUploadRequestExecutor; +import me.chanjar.weixin.common.util.http.RequestHttp; +import org.apache.http.HttpEntity; +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.mime.HttpMultipartMode; +import org.apache.http.entity.mime.MultipartEntityBuilder; +import org.apache.http.impl.client.CloseableHttpClient; + +import java.io.IOException; + +/** + * 文件输入流上传. + * + * @author meiqin.zhou91@gmail.com + * @date 2022/02/15 + */ +public class ApacheMediaInputStreamUploadRequestExecutor extends MediaInputStreamUploadRequestExecutor { + public ApacheMediaInputStreamUploadRequestExecutor(RequestHttp requestHttp) { + super(requestHttp); + } + + @Override + public WxMediaUploadResult execute(String uri, InputStreamData data, WxType wxType) throws WxErrorException, IOException { + HttpPost httpPost = new HttpPost(uri); + if (requestHttp.getRequestHttpProxy() != null) { + RequestConfig config = RequestConfig.custom().setProxy(requestHttp.getRequestHttpProxy()).build(); + httpPost.setConfig(config); + } + if (data != null) { + HttpEntity entity = MultipartEntityBuilder + .create() + .addBinaryBody("media", data.getInputStream(), ContentType.DEFAULT_BINARY, data.getFilename()) + .setMode(HttpMultipartMode.RFC6532) + .build(); + httpPost.setEntity(entity); + } + try (CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpPost)) { + String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); + WxError error = WxError.fromJson(responseContent, wxType); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + return WxMediaUploadResult.fromJson(responseContent); + } finally { + httpPost.releaseConnection(); + } + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheMinishopMediaUploadRequestCustomizeExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheMinishopMediaUploadRequestCustomizeExecutor.java index 64888c08d..9af02af5d 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheMinishopMediaUploadRequestCustomizeExecutor.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheMinishopMediaUploadRequestCustomizeExecutor.java @@ -24,8 +24,8 @@ */ @Slf4j public class ApacheMinishopMediaUploadRequestCustomizeExecutor extends MinishopUploadRequestCustomizeExecutor { - public ApacheMinishopMediaUploadRequestCustomizeExecutor(RequestHttp requestHttp, String respType) { - super(requestHttp, respType); + public ApacheMinishopMediaUploadRequestCustomizeExecutor(RequestHttp requestHttp, String respType, String imgUrl) { + super(requestHttp, respType, imgUrl); } @Override @@ -35,15 +35,29 @@ public WxMinishopImageUploadCustomizeResult execute(String uri, File file, WxTyp RequestConfig config = RequestConfig.custom().setProxy(requestHttp.getRequestHttpProxy()).build(); httpPost.setConfig(config); } - if (file != null) { + if (this.uploadType.equals("0")) { + if (file == null) { + throw new WxErrorException("上传文件为空"); + } HttpEntity entity = MultipartEntityBuilder .create() .addBinaryBody("media", file) .addTextBody("resp_type", this.respType) + .addTextBody("upload_type", this.uploadType) .setMode(HttpMultipartMode.RFC6532) .build(); httpPost.setEntity(entity); } + else { + HttpEntity entity = MultipartEntityBuilder + .create() + .addTextBody("resp_type", this.respType) + .addTextBody("upload_type", this.uploadType) + .addTextBody("img_url", this.imgUrl) + .setMode(HttpMultipartMode.RFC6532) + .build(); + httpPost.setEntity(entity); + } try (CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpPost)) { String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); WxError error = WxError.fromJson(responseContent, wxType); diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/DefaultApacheHttpClientBuilder.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/DefaultApacheHttpClientBuilder.java index 3bb0d6114..198798c05 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/DefaultApacheHttpClientBuilder.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/DefaultApacheHttpClientBuilder.java @@ -12,6 +12,7 @@ import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; import org.apache.http.config.SocketConfig; +import org.apache.http.conn.ConnectionKeepAliveStrategy; import org.apache.http.conn.HttpClientConnectionManager; import org.apache.http.conn.socket.ConnectionSocketFactory; import org.apache.http.conn.socket.PlainConnectionSocketFactory; @@ -92,12 +93,18 @@ public class DefaultApacheHttpClientBuilder implements ApacheHttpClientBuilder { */ private String userAgent; - private final HttpRequestRetryHandler httpRequestRetryHandler = new HttpRequestRetryHandler() { - @Override - public boolean retryRequest(IOException exception, int executionCount, HttpContext context) { - return false; - } - }; + /** + * 自定义重试策略 + */ + private HttpRequestRetryHandler httpRequestRetryHandler; + + /** + * 自定义KeepAlive策略 + */ + private ConnectionKeepAliveStrategy connectionKeepAliveStrategy; + + private final HttpRequestRetryHandler defaultHttpRequestRetryHandler = (exception, executionCount, context) -> false; + private SSLConnectionSocketFactory sslConnectionSocketFactory = SSLConnectionSocketFactory.getSocketFactory(); private final PlainConnectionSocketFactory plainConnectionSocketFactory = PlainConnectionSocketFactory.getSocketFactory(); private String httpProxyHost; @@ -144,6 +151,18 @@ public ApacheHttpClientBuilder httpProxyPassword(String httpProxyPassword) { return this; } + @Override + public ApacheHttpClientBuilder httpRequestRetryHandler(HttpRequestRetryHandler httpRequestRetryHandler) { + this.httpRequestRetryHandler = httpRequestRetryHandler; + return this; + } + + @Override + public ApacheHttpClientBuilder keepAliveStrategy(ConnectionKeepAliveStrategy keepAliveStrategy) { + this.connectionKeepAliveStrategy = keepAliveStrategy; + return this; + } + @Override public ApacheHttpClientBuilder sslConnectionSocketFactory(SSLConnectionSocketFactory sslConnectionSocketFactory) { this.sslConnectionSocketFactory = sslConnectionSocketFactory; @@ -187,7 +206,16 @@ private synchronized void prepare() { .setConnectTimeout(this.connectionTimeout) .setConnectionRequestTimeout(this.connectionRequestTimeout) .build() - ).setRetryHandler(this.httpRequestRetryHandler); + ); + + // 设置重试策略,没有则使用默认 + httpRequestRetryHandler = httpRequestRetryHandler == null ? defaultHttpRequestRetryHandler : httpRequestRetryHandler; + httpClientBuilder.setRetryHandler(httpRequestRetryHandler); + + // 设置KeepAliveStrategy,没有使用默认 + if (connectionKeepAliveStrategy != null) { + httpClientBuilder.setKeepAliveStrategy(connectionKeepAliveStrategy); + } if (StringUtils.isNotBlank(this.httpProxyHost) && StringUtils.isNotBlank(this.httpProxyUsername)) { // 使用代理服务器 需要用户认证的代理服务器 diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddHttpMediaInputStreamUploadRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddHttpMediaInputStreamUploadRequestExecutor.java new file mode 100644 index 000000000..d0591aee9 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddHttpMediaInputStreamUploadRequestExecutor.java @@ -0,0 +1,61 @@ +package me.chanjar.weixin.common.util.http.jodd; + +import jodd.http.HttpConnectionProvider; +import jodd.http.HttpRequest; +import jodd.http.HttpResponse; +import jodd.http.ProxyInfo; +import jodd.http.up.ByteArrayUploadable; +import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.InputStreamData; +import me.chanjar.weixin.common.util.http.MediaInputStreamUploadRequestExecutor; +import me.chanjar.weixin.common.util.http.RequestHttp; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +/** + * 文件输入流上传. + * + * @author meiqin.zhou91@gmail.com + * @date 2022/02/15 + */ +public class JoddHttpMediaInputStreamUploadRequestExecutor extends MediaInputStreamUploadRequestExecutor { + public JoddHttpMediaInputStreamUploadRequestExecutor(RequestHttp requestHttp) { + super(requestHttp); + } + + @Override + public WxMediaUploadResult execute(String uri, InputStreamData data, WxType wxType) throws WxErrorException, IOException { + HttpRequest request = HttpRequest.post(uri); + if (requestHttp.getRequestHttpProxy() != null) { + requestHttp.getRequestHttpClient().useProxy(requestHttp.getRequestHttpProxy()); + } + request.withConnectionProvider(requestHttp.getRequestHttpClient()); + request.form("media", new ByteArrayUploadable(this.toByteArray(data.getInputStream()), data.getFilename())); + HttpResponse response = request.send(); + response.charset(StandardCharsets.UTF_8.name()); + + String responseContent = response.bodyText(); + WxError error = WxError.fromJson(responseContent, wxType); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + return WxMediaUploadResult.fromJson(responseContent); + } + + public byte[] toByteArray(InputStream input) throws IOException { + try (ByteArrayOutputStream output = new ByteArrayOutputStream()) { + byte[] buffer = new byte[4096]; + int n = 0; + while (-1 != (n = input.read(buffer))) { + output.write(buffer, 0, n); + } + return output.toByteArray(); + } + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddHttpMinishopMediaUploadRequestCustomizeExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddHttpMinishopMediaUploadRequestCustomizeExecutor.java index a79eb8eda..e36f5a7a1 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddHttpMinishopMediaUploadRequestCustomizeExecutor.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/JoddHttpMinishopMediaUploadRequestCustomizeExecutor.java @@ -22,8 +22,8 @@ */ @Slf4j public class JoddHttpMinishopMediaUploadRequestCustomizeExecutor extends MinishopUploadRequestCustomizeExecutor { - public JoddHttpMinishopMediaUploadRequestCustomizeExecutor(RequestHttp requestHttp, String respType) { - super(requestHttp, respType); + public JoddHttpMinishopMediaUploadRequestCustomizeExecutor(RequestHttp requestHttp, String respType, String imgUrl) { + super(requestHttp, respType, imgUrl); } @Override @@ -33,7 +33,16 @@ public WxMinishopImageUploadCustomizeResult execute(String uri, File file, WxTyp requestHttp.getRequestHttpClient().useProxy(requestHttp.getRequestHttpProxy()); } request.withConnectionProvider(requestHttp.getRequestHttpClient()); - request.form("media", file); + if (this.uploadType.equals("0")) { + request.form("resp_type", this.respType, + "upload_type", this.uploadType, + "media", file); + } + else { + request.form("resp_type", this.respType, + "upload_type", this.uploadType, + "img_url", this.imgUrl); + } HttpResponse response = request.send(); response.charset(StandardCharsets.UTF_8.name()); diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkHttpMediaInputStreamUploadRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkHttpMediaInputStreamUploadRequestExecutor.java new file mode 100644 index 000000000..ec85015b2 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkHttpMediaInputStreamUploadRequestExecutor.java @@ -0,0 +1,56 @@ +package me.chanjar.weixin.common.util.http.okhttp; + +import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.InputStreamData; +import me.chanjar.weixin.common.util.http.MediaInputStreamUploadRequestExecutor; +import me.chanjar.weixin.common.util.http.RequestHttp; +import okhttp3.*; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * 文件输入流上传. + * + * @author meiqin.zhou91@gmail.com + * @date 2022/02/15 + */ +public class OkHttpMediaInputStreamUploadRequestExecutor extends MediaInputStreamUploadRequestExecutor { + public OkHttpMediaInputStreamUploadRequestExecutor(RequestHttp requestHttp) { + super(requestHttp); + } + + @Override + public WxMediaUploadResult execute(String uri, InputStreamData data, WxType wxType) throws WxErrorException, IOException { + + RequestBody body = new MultipartBody.Builder() + .setType(MediaType.parse("multipart/form-data")) + .addFormDataPart("media", data.getFilename(), RequestBody.create(this.toByteArray(data.getInputStream()), MediaType.parse("application/octet-stream"))) + .build(); + Request request = new Request.Builder().url(uri).post(body).build(); + + Response response = requestHttp.getRequestHttpClient().newCall(request).execute(); + String responseContent = response.body().string(); + WxError error = WxError.fromJson(responseContent, wxType); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + return WxMediaUploadResult.fromJson(responseContent); + } + + + public byte[] toByteArray(InputStream input) throws IOException { + try (ByteArrayOutputStream output = new ByteArrayOutputStream()) { + byte[] buffer = new byte[4096]; + int n = 0; + while (-1 != (n = input.read(buffer))) { + output.write(buffer, 0, n); + } + return output.toByteArray(); + } + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkHttpMinishopMediaUploadRequestCustomizeExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkHttpMinishopMediaUploadRequestCustomizeExecutor.java index 45d112cd6..367bddd60 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkHttpMinishopMediaUploadRequestCustomizeExecutor.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/okhttp/OkHttpMinishopMediaUploadRequestCustomizeExecutor.java @@ -18,19 +18,30 @@ */ @Slf4j public class OkHttpMinishopMediaUploadRequestCustomizeExecutor extends MinishopUploadRequestCustomizeExecutor { - public OkHttpMinishopMediaUploadRequestCustomizeExecutor(RequestHttp requestHttp, String respType) { - super(requestHttp, respType); + public OkHttpMinishopMediaUploadRequestCustomizeExecutor(RequestHttp requestHttp, String respType, String imgUrl) { + super(requestHttp, respType, imgUrl); } @Override public WxMinishopImageUploadCustomizeResult execute(String uri, File file, WxType wxType) throws WxErrorException, IOException { - RequestBody body = new MultipartBody.Builder() - .setType(MediaType.parse("multipart/form-data")) - .addFormDataPart("media", - file.getName(), - RequestBody.create(MediaType.parse("application/octet-stream"), file)) - .build(); + RequestBody body = null; + if (this.uploadType.equals("0")) { + body = new MultipartBody.Builder() + .setType(MediaType.parse("multipart/form-data")) + .addFormDataPart("resp_type", this.respType) + .addFormDataPart("upload_type", this.uploadType) + .addFormDataPart("media", file.getName(), RequestBody.create(MediaType.parse("application/octet-stream"), file)) + .build(); + } + else { + body = new MultipartBody.Builder() + .setType(MediaType.parse("multipart/form-data")) + .addFormDataPart("resp_type", this.respType) + .addFormDataPart("upload_type", this.uploadType) + .addFormDataPart("img_url", this.imgUrl) + .build(); + } Request request = new Request.Builder().url(uri).post(body).build(); Response response = requestHttp.getRequestHttpClient().newCall(request).execute(); diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/GsonParser.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/GsonParser.java index 53f51e0f3..061a3cb2e 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/GsonParser.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/GsonParser.java @@ -3,7 +3,6 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.google.gson.stream.JsonReader; -import lombok.NoArgsConstructor; import java.io.Reader; diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxGsonBuilder.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxGsonBuilder.java index 062492350..ff260c16f 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxGsonBuilder.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxGsonBuilder.java @@ -7,6 +7,7 @@ import me.chanjar.weixin.common.bean.menu.WxMenu; import me.chanjar.weixin.common.error.WxError; import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; +import java.util.Objects; /** * . @@ -14,6 +15,7 @@ */ public class WxGsonBuilder { private static final GsonBuilder INSTANCE = new GsonBuilder(); + private static volatile Gson GSON_INSTANCE; static { INSTANCE.disableHtmlEscaping(); @@ -26,7 +28,14 @@ public class WxGsonBuilder { } public static Gson create() { - return INSTANCE.create(); + if (Objects.isNull(GSON_INSTANCE)) { + synchronized (INSTANCE) { + if (Objects.isNull(GSON_INSTANCE)) { + GSON_INSTANCE = INSTANCE.create(); + } + } + } + return GSON_INSTANCE; } } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxMenuGsonAdapter.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxMenuGsonAdapter.java index 31c3c0204..50d3b0d63 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxMenuGsonAdapter.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxMenuGsonAdapter.java @@ -46,6 +46,7 @@ protected JsonObject convertToJson(WxMenuButton button) { buttonJson.addProperty("key", button.getKey()); buttonJson.addProperty("url", button.getUrl()); buttonJson.addProperty("media_id", button.getMediaId()); + buttonJson.addProperty("article_id", button.getArticleId()); buttonJson.addProperty("appid", button.getAppId()); buttonJson.addProperty("pagepath", button.getPagePath()); if (button.getSubButtons() != null && button.getSubButtons().size() > 0) { @@ -122,6 +123,7 @@ protected WxMenuButton convertFromJson(JsonObject json) { button.setUrl(GsonHelper.getString(json, "url")); button.setType(GsonHelper.getString(json, "type")); button.setMediaId(GsonHelper.getString(json, "media_id")); + button.setArticleId(GsonHelper.getString(json, "article_id")); button.setAppId(GsonHelper.getString(json, "appid")); button.setPagePath(GsonHelper.getString(json, "pagepath")); return button; diff --git a/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/XmlUtilsTest.java b/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/XmlUtilsTest.java index 7b6bb536f..ff34475ef 100644 --- a/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/XmlUtilsTest.java +++ b/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/XmlUtilsTest.java @@ -63,27 +63,54 @@ public void testXml2Map() { assertThat(map).isNotNull(); final Map copyrightCheckResult = (Map) map.get("CopyrightCheckResult"); List> resultList = (List>) ((Map) copyrightCheckResult.get("ResultList")).get("item"); - assertThat(copyrightCheckResult).isNotNull(); - assertThat(copyrightCheckResult.get("Count")).isEqualTo("2"); - assertThat(copyrightCheckResult.get("CheckState")).isEqualTo("2"); + assertThat(copyrightCheckResult) + .isNotNull() + .containsEntry("Count", "2") + .containsEntry("CheckState", "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(0)).containsEntry("ArticleIdx", "1") + .containsEntry("UserDeclareState", "0") + .containsEntry("AuditState", "2") + .containsEntry("OriginalArticleUrl", "Url_1") + .containsEntry("OriginalArticleType", "1") + .containsEntry("CanReprint", "1") + .containsEntry("NeedReplaceContent", "1") + .containsEntry("NeedShowReprintSource", "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"); + assertThat(resultList.get(1)).containsEntry("ArticleIdx", "2") + .containsEntry("UserDeclareState", "0") + .containsEntry("AuditState", "2") + .containsEntry("OriginalArticleUrl", "Url_2") + .containsEntry("OriginalArticleType", "1") + .containsEntry("CanReprint", "1") + .containsEntry("NeedReplaceContent", "1") + .containsEntry("NeedShowReprintSource", "1"); + } + + @Test + public void testXml2Map_another() { + String xml = " 1481013459 2247503051 0 1 1 2 "; + + final Map map = XmlUtils.xml2Map(xml); + assertThat(map).isNotNull() + .containsEntry("ToUserName", "gh_4d00ed8d6399") + .containsEntry("FromUserName", "oV5CrjpxgaGXNHIQigzNlgLTnwic") + .containsEntry("CreateTime", "1481013459") + .containsEntry("MsgType", "event"); + + Map publishEventInfo = (Map) map.get("PublishEventInfo"); + assertThat(publishEventInfo).containsEntry("publish_id", "2247503051") + .containsEntry("publish_status", "0") + .containsEntry("article_id", "b5O2OUs25HBxRceL7hfReg-U9QGeq9zQjiDvy WP4Hq4"); + + Map articleDetail = (Map) publishEventInfo.get("article_detail"); + assertThat(articleDetail).containsEntry("count", "1"); + List< Map> item = (List>) articleDetail.get("item"); + assertThat(item.get(0)).containsEntry("idx", "1") + .containsEntry("article_url", "ARTICLE_URL"); + + assertThat(item.get(1)).containsEntry("idx", "2") + .containsEntry("article_url", "ARTICLE_URL_2"); } } diff --git a/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/crypto/WxCryptUtilTest.java b/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/crypto/WxCryptUtilTest.java index 82cfa9d2d..b61696c1e 100755 --- a/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/crypto/WxCryptUtilTest.java +++ b/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/crypto/WxCryptUtilTest.java @@ -96,7 +96,7 @@ public void testValidateSignatureError() throws ParserConfigurationException, SA String encrypt = nodelist1.item(0).getTextContent(); String fromXML = String.format(this.xmlFormat, encrypt); - pc.decrypt("12345", this.timestamp, this.nonce, fromXML); // 这里签名错误 + pc.decryptXml("12345", this.timestamp, this.nonce, fromXML); // 这里签名错误 } catch (RuntimeException e) { assertEquals(e.getMessage(), "加密消息签名校验失败"); return; diff --git a/weixin-java-cp/pom.xml b/weixin-java-cp/pom.xml index 85b573866..9c8c3f86e 100644 --- a/weixin-java-cp/pom.xml +++ b/weixin-java-cp/pom.xml @@ -7,7 +7,7 @@ com.github.binarywang wx-java - 4.2.0 + 4.3.0 weixin-java-cp diff --git a/weixin-java-cp/src/main/java/com/tencent/wework/Finance.java b/weixin-java-cp/src/main/java/com/tencent/wework/Finance.java new file mode 100644 index 000000000..e653fd0f4 --- /dev/null +++ b/weixin-java-cp/src/main/java/com/tencent/wework/Finance.java @@ -0,0 +1,207 @@ +package com.tencent.wework; + +import lombok.extern.slf4j.Slf4j; + +/** + * 注意: + * 此类必须配置在com.tencent.wework路径底下,否则会报错: + * java.lang.UnsatisfiedLinkError: com.xxx.Finance.NewSdk() + *

+ * Q:JAVA版本的sdk报错UnsatisfiedLinkError? + * A:请检查是否修改了sdk的包名。 + *

+ * 官方文档: + * https://developer.work.weixin.qq.com/document/path/91552 + * + * @author Wang_Wong + * @date 2022-01-17 + */ +@Slf4j +public class Finance { + + private static volatile long sdk = -1L; + private static Finance finance = null; + private static final String SO_FILE = "so"; + private static final String DLL_FILE = "dll"; + + public native static long NewSdk(); + + /** + * 初始化函数 + * Return值=0表示该API调用成功 + * + * @param [in] sdk NewSdk返回的sdk指针 + * @param [in] corpid 调用企业的企业id,例如:wwd08c8exxxx5ab44d,可以在企业微信管理端--我的企业--企业信息查看 + * @param [in] secret 聊天内容存档的Secret,可以在企业微信管理端--管理工具--聊天内容存档查看 + * @return 返回是否初始化成功 + * 0 - 成功 + * !=0 - 失败 + */ + public native static int Init(long sdk, String corpid, String secret); + + /** + * 拉取聊天记录函数 + * Return值=0表示该API调用成功 + * + * @param [in] sdk NewSdk返回的sdk指针 + * @param [in] seq 从指定的seq开始拉取消息,注意的是返回的消息从seq+1开始返回,seq为之前接口返回的最大seq值。首次使用请使用seq:0 + * @param [in] limit 一次拉取的消息条数,最大值1000条,超过1000条会返回错误 + * @param [in] proxy 使用代理的请求,需要传入代理的链接。如:socks5://10.0.0.1:8081 或者 http://10.0.0.1:8081 + * @param [in] passwd 代理账号密码,需要传入代理的账号密码。如 user_name:passwd_123 + * @param [out] chatDatas 返回本次拉取消息的数据,slice结构体.内容包括errcode/errmsg,以及每条消息内容。 + * @return 返回是否调用成功 + * 0 - 成功 + * !=0 - 失败 + */ + public native static int GetChatData(long sdk, long seq, long limit, String proxy, String passwd, long timeout, long chatData); + + /** + * 拉取媒体消息函数 + * Return值=0表示该API调用成功 + * + * @param [in] sdk NewSdk返回的sdk指针 + * @param [in] sdkFileid 从GetChatData返回的聊天消息中,媒体消息包括的sdkfileid + * @param [in] proxy 使用代理的请求,需要传入代理的链接。如:socks5://10.0.0.1:8081 或者 http://10.0.0.1:8081 + * @param [in] passwd 代理账号密码,需要传入代理的账号密码。如 user_name:passwd_123 + * @param [in] indexbuf 媒体消息分片拉取,需要填入每次拉取的索引信息。首次不需要填写,默认拉取512k,后续每次调用只需要将上次调用返回的outindexbuf填入即可。 + * @param [out] media_data 返回本次拉取的媒体数据.MediaData结构体.内容包括data(数据内容)/outindexbuf(下次索引)/is_finish(拉取完成标记) + * @return 返回是否调用成功 + * 0 - 成功 + * !=0 - 失败 + */ + public native static int GetMediaData(long sdk, String indexbuf, String sdkField, String proxy, String passwd, long timeout, long mediaData); + + /** + * @param [in] encrypt_key, getchatdata返回的encrypt_key + * @param [in] encrypt_msg, getchatdata返回的content + * @param [out] msg, 解密的消息明文 + * @return 返回是否调用成功 + * 0 - 成功 + * !=0 - 失败 + * @brief 解析密文 + */ + public native static int DecryptData(long sdk, String encrypt_key, String encrypt_msg, long msg); + + public native static void DestroySdk(long sdk); + + public native static long NewSlice(); + + /** + * @return + * @brief 释放slice,和NewSlice成对使用 + */ + public native static void FreeSlice(long slice); + + /** + * @return 内容 + * @brief 获取slice内容 + */ + public native static String GetContentFromSlice(long slice); + + /** + * @return 内容 + * @brief 获取slice内容长度 + */ + public native static int GetSliceLen(long slice); + + public native static long NewMediaData(); + + public native static void FreeMediaData(long mediaData); + + /** + * @return outindex + * @brief 获取mediadata outindex + */ + public native static String GetOutIndexBuf(long mediaData); + + /** + * @return data + * @brief 获取mediadata data数据 + */ + public native static byte[] GetData(long mediaData); + + public native static int GetIndexLen(long mediaData); + + public native static int GetDataLen(long mediaData); + + /** + * @return 1完成、0未完成 + * @brief 判断mediadata是否结束 + */ + public native static int IsMediaDataFinish(long mediaData); + + /** + * 判断Windows环境 + * + * @return + */ + public static boolean isWindows() { + String osName = System.getProperties().getProperty("os.name"); + log.info("Loading System Libraries, Current OS Version Is: {}", osName); + return osName.toUpperCase().contains("WINDOWS"); + } + + /** + * 加载系统类库 + * + * @param libFiles 类库配置文件 + * @param prefixPath 类库文件的前缀路径 + */ + public Finance(String[] libFiles, String prefixPath) { + boolean isWindows = Finance.isWindows(); + for (String file : libFiles) { + String suffix = file.substring(file.lastIndexOf(".") + 1); + if (isWindows) { + // 加载dll文件 + if (suffix.equalsIgnoreCase(DLL_FILE)) { + System.load(prefixPath + file); + } + } else { + // 加载so文件 + if (suffix.equalsIgnoreCase(SO_FILE)) { + System.load(prefixPath + file); + } + } + } + + } + + /** + * 初始化类库文件 + * + * @param libFiles + * @param prefixPath + * @return + */ + public synchronized static Finance loadingLibraries(String[] libFiles, String prefixPath) { + if (finance != null) { + return finance; + } + finance = new Finance(libFiles, prefixPath); + return finance; + } + + /** + * 单例sdk + * + * @return + */ + public synchronized static long SingletonSDK() { + if (sdk > 0) { + return sdk; + } + sdk = Finance.NewSdk(); + return sdk; + } + + /** + * 销毁sdk,保证线程可见性 + * + * @return + */ + public synchronized static void DestroySingletonSDK(long destroySDK) { + sdk = 0L; + Finance.DestroySdk(destroySDK); + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpDepartmentService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpDepartmentService.java index c86816b7f..b8e43cbdc 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpDepartmentService.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpDepartmentService.java @@ -28,6 +28,18 @@ public interface WxCpDepartmentService { */ Long create(WxCpDepart depart) throws WxErrorException; + /** + *

+   * 部门管理接口 - 获取单个部门详情.
+   * 详情请见: https://developer.work.weixin.qq.com/document/path/95351
+   * 
+ * + * @param id 部门id + * @return 部门信息 + * @throws WxErrorException 异常 + */ + WxCpDepart get(Long id) throws WxErrorException; + /** *
    * 部门管理接口 - 获取部门列表.
@@ -40,6 +52,18 @@ public interface WxCpDepartmentService {
    */
   List list(Long id) throws WxErrorException;
 
+  /**
+   * 
+   * 部门管理接口 - 获取子部门ID列表.
+   * 详情请见: https://developer.work.weixin.qq.com/document/path/95350
+   * 
+ * + * @param id 部门id。获取指定部门及其下的子部门(以及子部门的子部门等等,递归)。 如果不填,默认获取全量组织架构 + * @return 子部门ID列表 + * @throws WxErrorException 异常 + */ + List simpleList(Long id) throws WxErrorException; + /** *
    * 部门管理接口 - 更新部门.
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpExternalContactService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpExternalContactService.java
index d252fb831..ae6b59ed6 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpExternalContactService.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpExternalContactService.java
@@ -1,33 +1,13 @@
 package me.chanjar.weixin.cp.api;
 
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
 import lombok.NonNull;
+import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
 import me.chanjar.weixin.common.error.WxErrorException;
 import me.chanjar.weixin.cp.bean.WxCpBaseResp;
-import me.chanjar.weixin.cp.bean.external.WxCpAddMomentResult;
-import me.chanjar.weixin.cp.bean.external.WxCpAddMomentTask;
-import me.chanjar.weixin.cp.bean.external.WxCpContactWayInfo;
-import me.chanjar.weixin.cp.bean.external.WxCpContactWayResult;
-import me.chanjar.weixin.cp.bean.external.WxCpGetMomentComments;
-import me.chanjar.weixin.cp.bean.external.WxCpGetMomentCustomerList;
-import me.chanjar.weixin.cp.bean.external.WxCpGetMomentList;
-import me.chanjar.weixin.cp.bean.external.WxCpGetMomentSendResult;
-import me.chanjar.weixin.cp.bean.external.WxCpGetMomentTask;
-import me.chanjar.weixin.cp.bean.external.WxCpGetMomentTaskResult;
-import me.chanjar.weixin.cp.bean.external.WxCpMsgTemplate;
-import me.chanjar.weixin.cp.bean.external.WxCpMsgTemplateAddResult;
-import me.chanjar.weixin.cp.bean.external.WxCpUpdateRemarkRequest;
-import me.chanjar.weixin.cp.bean.external.WxCpUserExternalGroupChatInfo;
-import me.chanjar.weixin.cp.bean.external.WxCpUserExternalGroupChatList;
-import me.chanjar.weixin.cp.bean.external.WxCpUserExternalGroupChatStatistic;
-import me.chanjar.weixin.cp.bean.external.WxCpUserExternalGroupChatTransferResp;
-import me.chanjar.weixin.cp.bean.external.WxCpUserExternalTagGroupInfo;
-import me.chanjar.weixin.cp.bean.external.WxCpUserExternalTagGroupList;
-import me.chanjar.weixin.cp.bean.external.WxCpUserExternalUnassignList;
-import me.chanjar.weixin.cp.bean.external.WxCpUserExternalUserBehaviorStatistic;
-import me.chanjar.weixin.cp.bean.external.WxCpUserTransferCustomerReq;
-import me.chanjar.weixin.cp.bean.external.WxCpUserTransferCustomerResp;
-import me.chanjar.weixin.cp.bean.external.WxCpUserTransferResultResp;
-import me.chanjar.weixin.cp.bean.external.WxCpWelcomeMsg;
+import me.chanjar.weixin.cp.bean.external.*;
 import me.chanjar.weixin.cp.bean.external.contact.*;
 import me.chanjar.weixin.cp.bean.oa.WxCpApprovalInfoQueryFilter;
 import org.jetbrains.annotations.NotNull;
@@ -133,7 +113,7 @@ public interface WxCpExternalContactService {
    * @param userId 外部联系人的userid
    * @return . external contact
    * @throws WxErrorException the wx error exception
-   * @deprecated 建议使用 {@link #getContactDetail(String)}
+   * @deprecated 建议使用 {@link #getContactDetail(String, String)}
    */
   @Deprecated
   WxCpExternalContactInfo getExternalContact(String userId) throws WxErrorException;
@@ -154,10 +134,11 @@ public interface WxCpExternalContactService {
    * 
* * @param userId 外部联系人的userid,注意不是企业成员的帐号 + * @param cursor 用于分页查询的游标,字符串类型,由上一次调用返回,首次调用可不填 * @return . contact detail * @throws WxErrorException . */ - WxCpExternalContactInfo getContactDetail(String userId) throws WxErrorException; + WxCpExternalContactInfo getContactDetail(String userId, String cursor) throws WxErrorException; /** * 企业和服务商可通过此接口,将微信外部联系人的userid转为微信openid,用于调用支付相关接口。暂不支持企业微信外部联系人(ExternalUserid为wo开头)的userid转openid。 @@ -191,6 +172,121 @@ public interface WxCpExternalContactService { */ String unionidToExternalUserid(@NotNull String unionid,String openid) throws WxErrorException; + /** + * 代开发应用external_userid转换 + *
+   *
+   * 文档地址:https://work.weixin.qq.com/api/doc/90001/90143/95195
+   *
+   * 企业同时安装服务商第三方应用以及授权代开发自建应用的时,服务商可使用该接口将代开发应用获取到的外部联系人id跟第三方应用的id进行关联,
+   * 该接口可将代开发自建应用获取到的external_userid转换为服务商第三方应用的external_userid。
+   *
+   * 权限说明:
+   *
+   * 该企业授权了该服务商第三方应用,且授权的第三方应用具备“企业客户权限->客户基础信息”权限
+   * 该客户的跟进人必须在应用的可见范围之内
+   * 应用需具备“企业客户权限->客户基础信息”权限
+   * 
+ * + * @param externalUserid 代开发自建应用获取到的外部联系人ID + * @return 该服务商第三方应用下的企业的外部联系人ID + * @throws WxErrorException . + */ + String toServiceExternalUserid(@NotNull String externalUserid) throws WxErrorException; + + /** + * 企业客户微信unionid的升级 - unionid查询external_userid + *
+   *
+   * 文档地址:https://open.work.weixin.qq.com/api/doc/35863#4.2%20unionid%E6%9F%A5%E8%AF%A2external_userid
+   *
+   * 当微信用户在微信中使用第三方应用的小程序或公众号时,第三方可将获取到的unionid与openid,调用此接口转换为企业客户external_userid。
+   * 该接口调用频次有限,每个服务商每小时仅可调用1万次,仅用于微信用户主动使用第三方应用的场景来调用,服务商切不可滥用。
+   * 同时建议服务商的小程序路径或公众号页面链接带上corpid参数,如此可明确地转换出该企业对应的external_userid,以获得更好的性能。
+   *
+   * 权限说明:
+   *
+   * 该企业授权了该服务商第三方应用
+   * 调用频率最大为10000次/小时
+   * unionid和openid的主体需与服务商的主体一致
+   * openid与unionid必须是在同一个小程序或同一个公众号获取到的
+   * 
+ * + * @param unionid 微信客户的unionid + * @param openid 微信客户的openid + * @param corpid 需要换取的企业corpid,不填则拉取所有企业 + * @return 该服务商第三方应用下的企业的外部联系人ID + * @throws WxErrorException . + */ + WxCpExternalUserIdList unionidToExternalUserid3rd(@NotNull String unionid, @NotNull String openid, String corpid) throws WxErrorException; + + /** + * 转换external_userid + *
+   *
+   * 文档地址:https://open.work.weixin.qq.com/api/doc/35863#转换external_userid
+   *
+   * 对于历史已授权的企业,在2022年3月1号之前,所有接口与回调返回的external_userid仍然为旧的external_userid,
+   * 从2022年3月1号0点开始,所有输入与返回的external_userid字段,将启用升级后的external_userid。
+   * 所以服务商需要在此之前完成历史数据的迁移整改
+   *
+   * 权限说明:
+   *
+   * 该企业授权了该服务商第三方应用
+   * external_userid对应的跟进人需要在应用可见范围内
+   * 
+ * + * @param externalUserIdList 微信客户的unionid + * @return List 新外部联系人id + * @throws WxErrorException . + */ + WxCpNewExternalUserIdList getNewExternalUserId(String[] externalUserIdList) throws WxErrorException; + + /** + * 设置迁移完成 + *
+   *
+   * 文档地址:https://open.work.weixin.qq.com/api/doc/35863#转换external_userid
+   *
+   * 企业授权确认之后,且服务商完成了新旧external_userid的迁移,即可主动将该企业设置为“迁移完成”,
+   * 设置之后,从该企业获取到的将是新的external_userid。注意,该接口需要使用provider_access_token来调用,
+   * 对于有多个应用的服务商,可逐个应用进行external_userid的转换,最后再使用provider_access_token调用该接口完成设置。
+   *
+   * 权限说明:
+   *
+   * 该企业授权了该服务商第三方应用
+   * 
+ * + * @param corpid 企业corpid + * @return wx cp base resp + * @throws WxErrorException . + */ + WxCpBaseResp finishExternalUserIdMigration(@NotNull String corpid) throws WxErrorException; + + /** + * 客户群opengid转换 + *
+   *
+   * 文档地址:https://open.work.weixin.qq.com/api/doc/90000/90135/94822
+   *
+   * 用户在微信里的客户群里打开小程序时,某些场景下可以获取到群的opengid,如果该群是企业微信的客户群,
+   * 则企业或第三方可以调用此接口将一个opengid转换为客户群chat_id
+   *
+   * 权限说明:
+   *
+   * 企业需要使用“客户联系”secret或配置到“可调用应用”列表中的自建应用secret所获取的accesstoken来调用(accesstoken如何获取?)
+   * 第三方应用需具有“企业客户权限->客户基础信息”权限
+   * 对于第三方/自建应用,群主必须在应用的可见范围
+   * 仅支持企业服务人员创建的客户群
+   * 仅可转换出自己企业下的客户群chat_id
+   * 
+ * + * @param opengid 小程序在微信获取到的群ID,参见wx.getGroupEnterInfo(https://developers.weixin.qq.com/miniprogram/dev/api/open-api/group/wx.getGroupEnterInfo.html) + * @return 客户群ID,可以用来调用获取客户群详情 + * @throws WxErrorException . + */ + String opengidToChatid(@NotNull String opengid) throws WxErrorException; + /** * 批量获取客户详情. *
@@ -265,14 +361,19 @@ WxCpExternalContactBatchInfo getContactDetailBatch(String[] userIdList, String c
   List listFollowers() throws WxErrorException;
 
   /**
-   * 企业和第三方可通过此接口,获取所有离职成员的客户列表,并可进一步调用离职成员的外部联系人再分配接口将这些客户重新分配给其他企业成员。
+   * 获取待分配的离职成员列表
+   * 企业和第三方可通过此接口,获取所有离职成员的客户列表,并可进一步调用分配离职成员的客户接口将这些客户重新分配给其他企业成员。
    *
-   * @param page     the page
-   * @param pageSize the page size
-   * @return wx cp user external unassign list
-   * @throws WxErrorException the wx error exception
+   * 请求方式:POST(HTTPS)
+   * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/externalcontact/get_unassigned_list?access_token=ACCESS_TOKEN
+   *
+   * @param pageId 分页查询,要查询页号,从0开始
+   * @param cursor 分页查询游标,字符串类型,适用于数据量较大的情况,如果使用该参数则无需填写page_id,该参数由上一次调用返回
+   * @param pageSize 每次返回的最大记录数,默认为1000,最大值为1000
+   * @return
+   * @throws WxErrorException
    */
-  WxCpUserExternalUnassignList listUnassignedList(Integer page, Integer pageSize) throws WxErrorException;
+  WxCpUserExternalUnassignList listUnassignedList(Integer pageId, String cursor, Integer pageSize) throws WxErrorException;
 
   /**
    * 企业可通过此接口,将已离职成员的外部联系人分配给另一个成员接替联系。
@@ -726,6 +827,20 @@ WxCpGetMomentComments getMomentComments(String momentId, String userId)
    */
   WxCpGroupMsgSendResult getGroupMsgSendResult(String msgid, String userid, Integer limit, String cursor) throws WxErrorException;
 
+  /**
+   * 
+   * 企业跟第三方应用可通过该接口获取到创建企业群发的群发发送结果。
+   * https://work.weixin.qq.com/api/doc/16251
+   * 
+ * + * @param msgid 群发消息的id,通过创建企业群发接口返回 + * @param limit 返回的最大记录数,整型,最大值10000,默认值10000 + * @param cursor 用于分页查询的游标,字符串类型,由上一次调用返回,首次调用可不填 + * @return wx cp base resp + * @throws WxErrorException the wx error exception + */ + public WxCpGroupMsgResult getGroupMsgResult(String msgid, Integer limit, String cursor) throws WxErrorException; + /** *
    * 获取群发成员发送任务列表。
@@ -740,4 +855,110 @@ WxCpGetMomentComments getMomentComments(String momentId, String userId)
    */
    WxCpGroupMsgTaskResult getGroupMsgTask(String msgid, Integer limit, String cursor) throws WxErrorException;
 
+  /**
+   * 
+   * 添加入群欢迎语素材。
+   * https://open.work.weixin.qq.com/api/doc/90000/90135/92366#添加入群欢迎语素材
+   * 
+ * + * @param template 素材内容 + * @return template_id 欢迎语素材id + * @throws WxErrorException the wx error exception + */ + String addGroupWelcomeTemplate(WxCpGroupWelcomeTemplateResult template) throws WxErrorException; + + /** + *
+   * 编辑入群欢迎语素材。
+   * https://open.work.weixin.qq.com/api/doc/90000/90135/92366#编辑入群欢迎语素材
+   * 
+ * + * @param template + * @return wx cp base resp + * @throws WxErrorException the wx error exception + */ + WxCpBaseResp editGroupWelcomeTemplate(WxCpGroupWelcomeTemplateResult template) throws WxErrorException; + + /** + *
+   * 获取入群欢迎语素材。
+   * https://open.work.weixin.qq.com/api/doc/90000/90135/92366#获取入群欢迎语素材
+   * 
+ * + * @param templateId 群欢迎语的素材id + * @return wx cp base resp + * @throws WxErrorException the wx error exception + */ + WxCpGroupWelcomeTemplateResult getGroupWelcomeTemplate(@NotNull String templateId) throws WxErrorException; + + /** + *
+   * 删除入群欢迎语素材。
+   * 企业可通过此API删除入群欢迎语素材,且仅能删除调用方自己创建的入群欢迎语素材。
+   * https://open.work.weixin.qq.com/api/doc/90000/90135/92366#删除入群欢迎语素材
+   * 
+ * + * @param templateId 群欢迎语的素材id + * @param templateId 授权方安装的应用agentid。仅旧的第三方多应用套件需要填此参数 + * @return wx cp base resp + * @throws WxErrorException the wx error exception + */ + WxCpBaseResp delGroupWelcomeTemplate(@NotNull String templateId, String agentId) throws WxErrorException; + + /** + *
+   * 获取商品图册
+   * https://work.weixin.qq.com/api/doc/90000/90135/95096#获取商品图册列表
+   * 
+ * + * @param limit 返回的最大记录数,整型,最大值100,默认值50,超过最大值时取默认值 + * @param cursor 用于分页查询的游标,字符串类型,由上一次调用返回,首次调用可不填 + * @return wx cp base resp + * @throws WxErrorException the wx error exception + */ + WxCpProductAlbumListResult getProductAlbumList(Integer limit, String cursor) throws WxErrorException; + + /** + *
+   * 获取商品图册
+   * https://work.weixin.qq.com/api/doc/90000/90135/95096#获取商品图册
+   * 
+ * + * @param productId 商品id + * @return wx cp base resp + * @throws WxErrorException the wx error exception + */ + WxCpProductAlbumResult getProductAlbum(String productId) throws WxErrorException; + + /** + *
+   * 上传附件资源
+   * https://open.work.weixin.qq.com/api/doc/90001/90143/95178
+   * 
+ * @param mediaType + * @param fileType + * @param attachmentType + * @param inputStream + * @return + * @throws WxErrorException + * @throws IOException + */ + WxMediaUploadResult uploadAttachment(String mediaType, String fileType, Integer attachmentType, + InputStream inputStream) throws WxErrorException, IOException; + + /** + *
+   * 上传附件资源
+   * https://open.work.weixin.qq.com/api/doc/90001/90143/95178
+   * 
+ * @param mediaType + * @param attachmentType + * @param file + * @return + * @throws WxErrorException + */ + WxMediaUploadResult uploadAttachment(String mediaType, Integer attachmentType, File file) + throws WxErrorException; + + } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpKfService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpKfService.java new file mode 100644 index 000000000..0da548905 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpKfService.java @@ -0,0 +1,191 @@ +package me.chanjar.weixin.cp.api; + +import java.util.List; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.cp.bean.WxCpBaseResp; +import me.chanjar.weixin.cp.bean.kf.WxCpKfAccountAdd; +import me.chanjar.weixin.cp.bean.kf.WxCpKfAccountAddResp; +import me.chanjar.weixin.cp.bean.kf.WxCpKfAccountDel; +import me.chanjar.weixin.cp.bean.kf.WxCpKfAccountLink; +import me.chanjar.weixin.cp.bean.kf.WxCpKfAccountLinkResp; +import me.chanjar.weixin.cp.bean.kf.WxCpKfAccountListResp; +import me.chanjar.weixin.cp.bean.kf.WxCpKfAccountUpd; +import me.chanjar.weixin.cp.bean.kf.WxCpKfCustomerBatchGetResp; +import me.chanjar.weixin.cp.bean.kf.WxCpKfMsgListResp; +import me.chanjar.weixin.cp.bean.kf.WxCpKfMsgSendRequest; +import me.chanjar.weixin.cp.bean.kf.WxCpKfMsgSendResp; +import me.chanjar.weixin.cp.bean.kf.WxCpKfServiceStateResp; +import me.chanjar.weixin.cp.bean.kf.WxCpKfServiceStateTransResp; +import me.chanjar.weixin.cp.bean.kf.WxCpKfServicerListResp; +import me.chanjar.weixin.cp.bean.kf.WxCpKfServicerOpResp; + +/** + * 微信客服接口 + * + * 微信客服由腾讯微信团队为企业打造,用于满足企业的客服需求,帮助企业做好客户服务。企业可以在微信内、外各个场景中接入微信客服, + * 用户可以发起咨询,企业可以进行回复。 + * 企业可在微信客服官网使用企业微信扫码开通微信客服,开通后即可使用。 + * + * @author Fu + * @date 2022/1/19 19:25 + */ +public interface WxCpKfService { + + /** + * 添加客服帐号,并可设置客服名称和头像。目前一家企业最多可添加10个客服帐号 + * + * @param add 客服帐号信息 + * @return result-新创建的客服帐号ID + * @throws WxErrorException 异常 + */ + WxCpKfAccountAddResp addAccount(WxCpKfAccountAdd add) throws WxErrorException; + + /** + * 修改已有的客服帐号,可修改客服名称和头像。 + * + * @param upd 新的客服账号信息 + * @return result + * @throws WxErrorException 异常 + */ + WxCpBaseResp updAccount(WxCpKfAccountUpd upd) throws WxErrorException; + + /** + * 删除已有的客服帐号 + * + * @param del 要删除的客服帐号 + * @return result + * @throws WxErrorException 异常 + */ + WxCpBaseResp delAccount(WxCpKfAccountDel del) throws WxErrorException; + + /** + * 获取客服帐号列表,包括所有的客服帐号的客服ID、名称和头像。 + * + * @return 客服帐号列表 + * @throws WxErrorException 异常 + */ + WxCpKfAccountListResp listAccount() throws WxErrorException; + + /** + * 企业可通过此接口获取带有不同参数的客服链接,不同客服帐号对应不同的客服链接。获取后,企业可将链接嵌入到网页等场景中, + * 微信用户点击链接即可向对应的客服帐号发起咨询。企业可依据参数来识别用户的咨询来源等 + * + * @param link 参数 + * @return 链接 + * @throws WxErrorException 异常 + */ + WxCpKfAccountLinkResp getAccountLink(WxCpKfAccountLink link) throws WxErrorException; + + /** + * 接待人员管理 + * 添加指定客服帐号的接待人员,每个客服帐号目前最多可添加500个接待人员。 + * @param openKfid 客服帐号ID + * @param userIdList 接待人员userid列表。第三方应用填密文userid,即open_userid + * 可填充个数:1 ~ 100。超过100个需分批调用。 + * @return 添加客服账号结果 + * @throws WxErrorException 异常 + */ + WxCpKfServicerOpResp addServicer(String openKfid, List userIdList) throws WxErrorException; + + /** + * 接待人员管理 + * 从客服帐号删除接待人员 + * @param openKfid 客服帐号ID + * @param userIdList 接待人员userid列表。第三方应用填密文userid,即open_userid + * 可填充个数:1 ~ 100。超过100个需分批调用。 + * @return 删除客服账号结果 + * @throws WxErrorException 异常 + */ + WxCpKfServicerOpResp delServicer(String openKfid, List userIdList) throws WxErrorException; + + /** + * 接待人员管理 + * 获取某个客服帐号的接待人员列表 + * @param openKfid 客服帐号ID + * @return 接待人员列表 + * @throws WxErrorException 异常 + */ + WxCpKfServicerListResp listServicer(String openKfid) throws WxErrorException; + + /** + * 分配客服会话 + * 获取会话状态 + * @param openKfid 客服帐号ID + * @param externalUserId 微信客户的external_userid + * @return + * @throws WxErrorException + */ + WxCpKfServiceStateResp getServiceState(String openKfid, String externalUserId) + throws WxErrorException; + + /** + * 分配客服会话 + * 变更会话状态 + * @param openKfid 客服帐号ID + * @param externalUserId 微信客户的external_userid + * @param serviceState 变更的目标状态,状态定义和所允许的变更可参考概述中的流程图和表格 + * @param servicerUserId 接待人员的userid。第三方应用填密文userid,即open_userid。当state=3时要求必填,接待人员须处于“正在接待”中。 + * @return 部分状态返回回复语code + * @throws WxErrorException + */ + WxCpKfServiceStateTransResp transServiceState(String openKfid, String externalUserId, + Integer serviceState, String servicerUserId) throws WxErrorException; + + /** + * 读取消息 + * 微信客户发送的消息、接待人员在企业微信回复的消息、发送消息接口发送失败事件(如被用户拒收)、客户点击菜单消息的回复消息, + * 可以通过该接口获取具体的消息内容和事件。不支持读取通过发送消息接口发送的消息。 + * 支持的消息类型:文本、图片、语音、视频、文件、位置、链接、名片、小程序、菜单、事件。 + * @param cursor 上一次调用时返回的next_cursor,第一次拉取可以不填。不多于64字节 + * @param token 回调事件返回的token字段,10分钟内有效;可不填,如果不填接口有严格的频率限制。不多于128字节 + * @param limit 期望请求的数据量,默认值和最大值都为1000。 + * 注意:可能会出现返回条数少于limit的情况,需结合返回的has_more字段判断是否继续请求。 + * @param voiceFormat 语音消息类型,0-Amr 1-Silk,默认0。可通过该参数控制返回的语音格式 + * @return 微信消息 + * @throws WxErrorException 异常 + */ + WxCpKfMsgListResp syncMsg(String cursor, String token, Integer limit, Integer voiceFormat) + throws WxErrorException; + + /** + * 发送消息 + * 当微信客户处于“新接入待处理”或“由智能助手接待”状态下,可调用该接口给用户发送消息。 + * 注意仅当微信客户在主动发送消息给客服后的48小时内,企业可发送消息给客户,最多可发送5条消息;若用户继续发送消息,企业可再次下发消息。 + * 支持发送消息类型:文本、图片、语音、视频、文件、图文、小程序、菜单消息、地理位置。 + * @param request 发送信息 + * @return 发送结果 + * @throws WxErrorException 异常 + */ + WxCpKfMsgSendResp sendMsg(WxCpKfMsgSendRequest request) throws WxErrorException; + + /** + * 发送欢迎语等事件响应消息 + * 当特定的事件回调消息包含code字段,或通过接口变更到特定的会话状态,会返回code字段。 + * 开发者可以此code为凭证,调用该接口给用户发送相应事件场景下的消息,如客服欢迎语、客服提示语和会话结束语等。 + * 除"用户进入会话事件"以外,响应消息仅支持会话处于获取该code的会话状态时发送,如将会话转入待接入池时获得的code仅能在会话状态为”待接入池排队中“时发送。 + * + * 目前支持的事件场景和相关约束如下: + * + * 事件场景 允许下发条数 code有效期 支持的消息类型 获取code途径 + * 用户进入会话,用于发送客服欢迎语 1条 20秒 文本、菜单 事件回调 + * 进入接待池,用于发送排队提示语等 1条 48小时 文本 转接会话接口 + * 从接待池接入会话,用于发送非工作 + * 时间的提示语或超时未回复的提示语 + * 等 1条 48小时 文本 事件回调、转接会话接口 + * 结束会话,用于发送结束会话提示语 + * 或满意度评价等 1条 20秒 文本、菜单 事件回调、转接会话接口 + * @param request + * @return + * @throws WxErrorException + */ + WxCpKfMsgSendResp sendMsgOnEvent(WxCpKfMsgSendRequest request) throws WxErrorException; + + /** + * 获取客户基础信息 + * @param externalUserIdList + * @return + * @throws WxErrorException + */ + WxCpKfCustomerBatchGetResp customerBatchGet(List externalUserIdList) + throws WxErrorException; +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpLivingService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpLivingService.java new file mode 100644 index 000000000..4b417e90f --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpLivingService.java @@ -0,0 +1,125 @@ +package me.chanjar.weixin.cp.api; + +import lombok.NonNull; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.cp.bean.living.*; + +/** + * 企业微信直播接口. + * 官方文档:https://work.weixin.qq.com/api/doc/90000/90135/93633 + * + * @author Wang_Wong + * @date 2021-12-21 + */ +public interface WxCpLivingService { + + /** + * 获取微信观看直播凭证 + * 请求方式: POST(HTTPS) + * 请求地址: https://qyapi.weixin.qq.com/cgi-bin/living/get_living_code?access_token=ACCESS_TOKEN + * + * @param openId 用户openid + * @param livingId 直播id + * @return living_code 微信观看直播凭证 + * @throws WxErrorException the wx error exception + */ + String getLivingCode(@NonNull String openId, @NonNull String livingId) throws WxErrorException; + + /** + * 获取直播详情 + * 请求方式:GET(HTTPS) + * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/living/get_living_info?access_token=ACCESS_TOKEN&livingid=LIVINGID + * + * @param livingId 直播id + * @return 获取的直播详情 + * @throws WxErrorException the wx error exception + */ + WxCpLivingInfo getLivingInfo(@NonNull String livingId) throws WxErrorException; + + /** + * 获取直播观看明细 + * 通过该接口可以获取所有观看直播的人员统计 + * + * 请求方式:POST(HTTPS) + * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/living/get_watch_stat?access_token=ACCESS_TOKEN + * + * @param livingId 直播id + * @param nextKey 上一次调用时返回的next_key,初次调用可以填”0” + * @return + * @throws WxErrorException + */ + WxCpWatchStat getWatchStat(@NonNull String livingId, Integer nextKey) throws WxErrorException; + + /** + * 获取成员直播ID列表 + * 通过此接口可以获取指定成员的所有直播ID + * + * 请求方式:POST(HTTPS) + * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/living/get_user_all_livingid?access_token=ACCESS_TOKEN + * + * @param userId 企业成员的userid + * @param cursor 上一次调用时返回的next_cursor,第一次拉取可以不填 + * @param limit 每次拉取的数据量,默认值和最大值都为100 + * @return + * @throws WxErrorException + */ + WxCpLivingResult.LivingIdResult getUserAllLivingId(@NonNull String userId, String cursor, Integer limit) throws WxErrorException; + + /** + * 获取跳转小程序商城的直播观众信息 + * 通过此接口,开发者可获取跳转小程序商城的直播间(“推广产品”直播)观众id、邀请人id及对应直播间id,以打通卖货直播的“人货场”信息闭环。 + * + * 请求方式:POST(HTTPS) + * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/living/get_living_share_info?access_token=ACCESS_TOKEN + * + * @param wwShareCode "推广产品"直播观众跳转小程序商城时会在小程序path中带上ww_share_code=xxxxx参数 + * @return + * @throws WxErrorException + */ + WxCpLivingShareInfo getLivingShareInfo(@NonNull String wwShareCode) throws WxErrorException; + + /** + * 创建预约直播 + * 请求方式: POST(HTTPS) + * 请求地址: https://qyapi.weixin.qq.com/cgi-bin/living/create?access_token=ACCESS_TOKEN + * + * @param request 创建预约直播请求参数. + * @return + * @throws WxErrorException + */ + String livingCreate(WxCpLivingCreateRequest request) throws WxErrorException; + + /** + * 修改预约直播 + * 请求方式: POST(HTTPS) + * 请求地址: https://qyapi.weixin.qq.com/cgi-bin/living/modify?access_token=ACCESS_TOKEN + * + * @param request 修改预约直播请求参数. + * @return + * @throws WxErrorException + */ + WxCpLivingResult livingModify(WxCpLivingModifyRequest request) throws WxErrorException; + + /** + * 取消预约直播 + * 请求方式: POST(HTTPS) + * 请求地址: https://qyapi.weixin.qq.com/cgi-bin/living/cancel?access_token=ACCESS_TOKEN + * + * @param livingId 直播id,仅允许取消预约状态下的直播id + * @return + * @throws WxErrorException + */ + WxCpLivingResult livingCancel(@NonNull String livingId) throws WxErrorException; + + /** + * 删除直播回放 + * 请求方式: POST(HTTPS) + * 请求地址: https://qyapi.weixin.qq.com/cgi-bin/living/delete_replay_data?access_token=ACCESS_TOKEN + * + * @param livingId 直播id + * @return + * @throws WxErrorException + */ + WxCpLivingResult deleteReplayData(@NonNull String livingId) throws WxErrorException; + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMediaService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMediaService.java index a51e04e17..d9b53f250 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMediaService.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMediaService.java @@ -35,6 +35,21 @@ public interface WxCpMediaService { WxMediaUploadResult upload(String mediaType, String fileType, InputStream inputStream) throws WxErrorException, IOException; + /** + *
+   *   上传多媒体文件.
+   * 
+ * + * @param mediaType 媒体类型, 请看{@link me.chanjar.weixin.common.api.WxConsts} + * @param filename 文件名.例如:wework.txt + * @param url 远程链接 + * @return + * @throws WxErrorException + * @throws IOException + */ + WxMediaUploadResult upload(String mediaType, String filename, String url) + throws WxErrorException, IOException; + /** * 上传多媒体文件. * diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMsgAuditService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMsgAuditService.java new file mode 100644 index 000000000..63389aeb8 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMsgAuditService.java @@ -0,0 +1,108 @@ +package me.chanjar.weixin.cp.api; + +import lombok.NonNull; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.cp.bean.msgaudit.*; + +import java.util.List; + +/** + * 会话内容存档接口. + * 官方文档:https://developer.work.weixin.qq.com/document/path/91360 + *

+ * 如需自行实现,亦可调用Finance类库函数,进行实现: + * com.tencent.wework.Finance + * + * @author Wang_Wong + * @date 2022-01-14 + */ +public interface WxCpMsgAuditService { + + /** + * 拉取聊天记录函数 + * + * @param seq 从指定的seq开始拉取消息,注意的是返回的消息从seq+1开始返回,seq为之前接口返回的最大seq值。首次使用请使用seq:0 + * @param limit 一次拉取的消息条数,最大值1000条,超过1000条会返回错误 + * @param proxy 使用代理的请求,需要传入代理的链接。如:socks5://10.0.0.1:8081 或者 http://10.0.0.1:8081,如果没有传null + * @param passwd 代理账号密码,需要传入代理的账号密码。如 user_name:passwd_123,如果没有传null + * @param timeout 超时时间,根据实际需要填写 + * @return 返回是否调用成功 + */ + WxCpChatDatas getChatDatas(long seq, @NonNull long limit, String proxy, String passwd, @NonNull long timeout) throws Exception; + + /** + * 获取解密的聊天数据Model + * + * @param chatData getChatDatas()获取到的聊天数据 + * @return 解密后的聊天数据 + * @throws Exception + */ + WxCpChatModel getDecryptData(@NonNull WxCpChatDatas.WxCpChatData chatData) throws Exception; + + /** + * 获取解密的聊天数据明文 + * + * @param chatData getChatDatas()获取到的聊天数据 + * @return 解密后的明文 + * @throws Exception + */ + String getChatPlainText(@NonNull WxCpChatDatas.WxCpChatData chatData) throws Exception; + + /** + * 获取媒体文件 + * 针对图片、文件等媒体数据,提供sdk接口拉取数据内容。 + * + * 注意: + * 根据上面返回的文件类型,拼接好存放文件的绝对路径即可。此时绝对路径写入文件流,来达到获取媒体文件的目的。 + * 详情可以看官方文档,亦可阅读此接口源码。 + * + * @param sdkfileid 消息体内容中的sdkfileid信息 + * @param proxy 使用代理的请求,需要传入代理的链接。如:socks5://10.0.0.1:8081 或者 http://10.0.0.1:8081,如果没有传null + * @param passwd 代理账号密码,需要传入代理的账号密码。如 user_name:passwd_123,如果没有传null + * @param timeout 超时时间,分片数据需累加到文件存储。单次最大返回512K字节,如果文件比较大,自行设置长一点,比如timeout=10000 + * @param targetFilePath 目标文件绝对路径+实际文件名,比如:/usr/local/file/20220114/474f866b39d10718810d55262af82662.gif + * @throws WxErrorException + */ + void getMediaFile(@NonNull String sdkfileid, String proxy, String passwd, @NonNull long timeout, @NonNull String targetFilePath) throws WxErrorException; + + /** + * 获取会话内容存档开启成员列表 + * 企业可通过此接口,获取企业开启会话内容存档的成员列表 + *

+ * 请求方式:POST(HTTPS) + * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/msgaudit/get_permit_user_list?access_token=ACCESS_TOKEN + * + * @param type 拉取对应版本的开启成员列表。1表示办公版;2表示服务版;3表示企业版。非必填,不填写的时候返回全量成员列表。 + * @return + * @throws WxErrorException + */ + List getPermitUserList(Integer type) throws WxErrorException; + + /** + * 获取会话内容存档内部群信息 + * 企业可通过此接口,获取会话内容存档本企业的内部群信息,包括群名称、群主id、公告、群创建时间以及所有群成员的id与加入时间。 + *

+ * 请求方式:POST(HTTPS) + * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/msgaudit/groupchat/get?access_token=ACCESS_TOKEN + * + * @param roomid 待查询的群id + * @return + * @throws WxErrorException + */ + WxCpGroupChat getGroupChat(@NonNull String roomid) throws WxErrorException; + + /** + * 获取会话同意情况 + * 企业可通过下述接口,获取会话中外部成员的同意情况 + *

+ * 单聊请求地址:https://qyapi.weixin.qq.com/cgi-bin/msgaudit/check_single_agree?access_token=ACCESS_TOKEN + *

+ * 请求方式:POST(HTTPS) + * + * @param checkAgreeRequest 待查询的会话信息 + * @return + * @throws WxErrorException + */ + WxCpAgreeInfo checkSingleAgree(@NonNull WxCpCheckAgreeRequest checkAgreeRequest) throws WxErrorException; + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOaAgentService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOaAgentService.java new file mode 100644 index 000000000..6f4fae85d --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOaAgentService.java @@ -0,0 +1,29 @@ +package me.chanjar.weixin.cp.api; + +import lombok.NonNull; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.cp.bean.oa.selfagent.WxCpOpenApprovalData; + +/** + * 企业微信自建应用接口. + * https://developer.work.weixin.qq.com/document/path/90269 + * + * @author Wang_Wong + * @date 2022-04-06 + */ +public interface WxCpOaAgentService { + + /** + * 查询第三方应用审批申请当前状态 + * 开发者也可主动查询审批单的当前审批状态。 + * + * 请求方式: POST(HTTPS) + * 请求地址: https://qyapi.weixin.qq.com/cgi-bin/corp/getopenapprovaldata?access_token=ACCESS_TOKEN + * + * @param thirdNo + * @return + * @throws WxErrorException + */ + WxCpOpenApprovalData getOpenApprovalData(@NonNull String thirdNo) throws WxErrorException; + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOaService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOaService.java index 7eb986dbb..bbbcb6095 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOaService.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOaService.java @@ -10,7 +10,7 @@ /** * 企业微信OA相关接口. * - * @author Element + * @author Element & Wang_Wong * @date 2019-04-06 10:52 */ public interface WxCpOaService { @@ -107,6 +107,7 @@ WxCpApprovalInfo getApprovalInfo(@NonNull Date startTime, @NonNull Date endTime, */ WxCpApprovalInfo getApprovalInfo(@NonNull Date startTime, @NonNull Date endTime) throws WxErrorException; + /** *

    *   获取审批申请详情
@@ -122,6 +123,21 @@ WxCpApprovalInfo getApprovalInfo(@NonNull Date startTime, @NonNull Date endTime,
    */
   WxCpApprovalDetailResult getApprovalDetail(@NonNull String spNo) throws WxErrorException;
 
+
+  /**
+   * 获取企业假期管理配置
+   * 企业可通过审批应用或自建应用Secret调用本接口,获取可见范围内员工的“假期管理”配置,包括:各个假期的id、名称、请假单位、时长计算方式、发放规则等。
+   * 第三方应用可获取应用可见范围内员工的“假期管理”配置,包括:各个假期的id、名称、请假单位、时长计算方式、发放规则等。
+   *
+   * 请求方式:GET(HTTPS)
+   * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/oa/vacation/getcorpconf?access_token=ACCESS_TOKEN
+   *
+   * @return
+   * @throws WxErrorException
+   */
+  WxCpCorpConfInfo getCorpConf() throws WxErrorException;
+
+
   /**
    * 获取公费电话拨打记录
    *
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java
index 94cd21263..ddb3968c2 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java
@@ -386,12 +386,33 @@ public interface WxCpService extends WxService {
   WxCpMessageService getMessageService();
 
   /**
-   * Gets oa service.
+   * 获取OA相关接口的服务类对象.
    *
    * @return the oa service
    */
   WxCpOaService getOaService();
 
+  /**
+   * 获取直播相关接口的服务类对象
+   *
+   * @return the Living service
+   */
+  WxCpLivingService getLivingService();
+
+  /**
+   * 获取OA 自建应用相关接口的服务类对象
+   *
+   * @return
+   */
+  WxCpOaAgentService getOaAgentService();
+
+  /**
+   * 获取会话存档相关接口的服务类对象
+   *
+   * @return
+   */
+  WxCpMsgAuditService getMsgAuditService();
+
   /**
    * 获取日历相关接口的服务类对象
    *
@@ -420,6 +441,13 @@ public interface WxCpService extends WxService {
    */
   WxCpAgentWorkBenchService getWorkBenchService();
 
+  /**
+   * 获取微信客服服务
+   *
+   * @return 微信客服服务
+   */
+  WxCpKfService getKfService();
+
   /**
    * http请求对象
    *
@@ -469,4 +497,10 @@ public interface WxCpService extends WxService {
    */
   void setTagService(WxCpTagService tagService);
 
+  /**
+   * Sets kf service.
+   *
+   * @param kfService the kf service
+   */
+  void setKfService(WxCpKfService kfService);
 }
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImpl.java
index 89221f1a1..55ddcf9e2 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImpl.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImpl.java
@@ -49,6 +49,9 @@ public abstract class BaseWxCpServiceImpl implements WxCpService, RequestH
   private WxCpTagService tagService = new WxCpTagServiceImpl(this);
   private WxCpAgentService agentService = new WxCpAgentServiceImpl(this);
   private WxCpOaService oaService = new WxCpOaServiceImpl(this);
+  private WxCpLivingService livingService = new WxCpLivingServiceImpl(this);
+  private WxCpOaAgentService oaAgentService = new WxCpOaAgentServiceImpl(this);
+  private WxCpMsgAuditService msgAuditService = new WxCpMsgAuditServiceImpl(this);
   private WxCpTaskCardService taskCardService = new WxCpTaskCardServiceImpl(this);
   private WxCpExternalContactService externalContactService = new WxCpExternalContactServiceImpl(this);
   private WxCpGroupRobotService groupRobotService = new WxCpGroupRobotServiceImpl(this);
@@ -56,6 +59,7 @@ public abstract class BaseWxCpServiceImpl implements WxCpService, RequestH
   private WxCpOaCalendarService oaCalendarService = new WxCpOaCalendarServiceImpl(this);
   private WxCpOaScheduleService oaScheduleService = new WxCpOaOaScheduleServiceImpl(this);
   private WxCpAgentWorkBenchService workBenchService = new WxCpAgentWorkBenchServiceImpl(this);
+  private WxCpKfService kfService = new WxCpKfServiceImpl(this);
 
   /**
    * 全局的是否正在刷新access token的锁.
@@ -477,6 +481,21 @@ public WxCpOaService getOaService() {
     return oaService;
   }
 
+  @Override
+  public WxCpLivingService getLivingService() {
+    return livingService;
+  }
+
+  @Override
+  public WxCpOaAgentService getOaAgentService() {
+    return oaAgentService;
+  }
+
+  @Override
+  public WxCpMsgAuditService getMsgAuditService() {
+    return msgAuditService;
+  }
+
   @Override
   public WxCpOaCalendarService getOaCalendarService() {
     return this.oaCalendarService;
@@ -550,4 +569,14 @@ public void setAgentService(WxCpAgentService agentService) {
   public WxCpOaScheduleService getOaScheduleService() {
     return this.oaScheduleService;
   }
+
+  @Override
+  public WxCpKfService getKfService() {
+    return kfService;
+  }
+
+  @Override
+  public void setKfService(WxCpKfService kfService) {
+    this.kfService = kfService;
+  }
 }
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpDepartmentServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpDepartmentServiceImpl.java
index 3a5ef8798..b6d9cf29b 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpDepartmentServiceImpl.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpDepartmentServiceImpl.java
@@ -35,6 +35,18 @@ public Long create(WxCpDepart depart) throws WxErrorException {
     return GsonHelper.getAsLong(tmpJsonObject.get("id"));
   }
 
+  @Override
+  public WxCpDepart get(Long id) throws WxErrorException {
+    String url = String.format(this.mainService.getWxCpConfigStorage().getApiUrl(DEPARTMENT_GET), id);
+    String responseContent = this.mainService.get(url, null);
+    JsonObject tmpJsonObject = GsonParser.parse(responseContent);
+    return WxCpGsonBuilder.create()
+      .fromJson(tmpJsonObject.get("department"),
+        new TypeToken() {
+        }.getType()
+      );
+  }
+
   @Override
   public void update(WxCpDepart group) throws WxErrorException {
     String url = this.mainService.getWxCpConfigStorage().getApiUrl(DEPARTMENT_UPDATE);
@@ -62,4 +74,20 @@ public List list(Long id) throws WxErrorException {
         }.getType()
       );
   }
+
+  @Override
+  public List simpleList(Long id) throws WxErrorException {
+    String url = this.mainService.getWxCpConfigStorage().getApiUrl(DEPARTMENT_SIMPLE_LIST);
+    if (id != null) {
+      url += "?id=" + id;
+    }
+
+    String responseContent = this.mainService.get(url, null);
+    JsonObject tmpJsonObject = GsonParser.parse(responseContent);
+    return WxCpGsonBuilder.create()
+      .fromJson(tmpJsonObject.get("department_id"),
+        new TypeToken>() {
+        }.getType()
+      );
+  }
 }
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpExternalContactServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpExternalContactServiceImpl.java
index 72c2c93b9..def24cf8b 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpExternalContactServiceImpl.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpExternalContactServiceImpl.java
@@ -2,43 +2,24 @@
 
 import com.google.gson.Gson;
 import com.google.gson.JsonObject;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.UUID;
 import lombok.NonNull;
 import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
 import me.chanjar.weixin.common.error.WxCpErrorMsgEnum;
 import me.chanjar.weixin.common.error.WxErrorException;
 import me.chanjar.weixin.common.error.WxRuntimeException;
 import me.chanjar.weixin.common.util.BeanUtils;
+import me.chanjar.weixin.common.util.fs.FileUtils;
+import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor;
 import me.chanjar.weixin.common.util.json.GsonParser;
 import me.chanjar.weixin.cp.api.WxCpExternalContactService;
 import me.chanjar.weixin.cp.api.WxCpService;
 import me.chanjar.weixin.cp.bean.WxCpBaseResp;
-import me.chanjar.weixin.cp.bean.external.WxCpAddMomentResult;
-import me.chanjar.weixin.cp.bean.external.WxCpAddMomentTask;
-import me.chanjar.weixin.cp.bean.external.WxCpContactWayInfo;
-import me.chanjar.weixin.cp.bean.external.WxCpContactWayResult;
-import me.chanjar.weixin.cp.bean.external.WxCpGetMomentComments;
-import me.chanjar.weixin.cp.bean.external.WxCpGetMomentCustomerList;
-import me.chanjar.weixin.cp.bean.external.WxCpGetMomentList;
-import me.chanjar.weixin.cp.bean.external.WxCpGetMomentSendResult;
-import me.chanjar.weixin.cp.bean.external.WxCpGetMomentTask;
-import me.chanjar.weixin.cp.bean.external.WxCpGetMomentTaskResult;
-import me.chanjar.weixin.cp.bean.external.WxCpMsgTemplate;
-import me.chanjar.weixin.cp.bean.external.WxCpMsgTemplateAddResult;
-import me.chanjar.weixin.cp.bean.external.WxCpUpdateRemarkRequest;
-import me.chanjar.weixin.cp.bean.external.WxCpUserExternalContactList;
-import me.chanjar.weixin.cp.bean.external.WxCpUserExternalGroupChatInfo;
-import me.chanjar.weixin.cp.bean.external.WxCpUserExternalGroupChatList;
-import me.chanjar.weixin.cp.bean.external.WxCpUserExternalGroupChatStatistic;
-import me.chanjar.weixin.cp.bean.external.WxCpUserExternalGroupChatTransferResp;
-import me.chanjar.weixin.cp.bean.external.WxCpUserExternalTagGroupInfo;
-import me.chanjar.weixin.cp.bean.external.WxCpUserExternalTagGroupList;
-import me.chanjar.weixin.cp.bean.external.WxCpUserExternalUnassignList;
-import me.chanjar.weixin.cp.bean.external.WxCpUserExternalUserBehaviorStatistic;
-import me.chanjar.weixin.cp.bean.external.WxCpUserTransferCustomerReq;
-import me.chanjar.weixin.cp.bean.external.WxCpUserTransferCustomerResp;
-import me.chanjar.weixin.cp.bean.external.WxCpUserTransferResultResp;
-import me.chanjar.weixin.cp.bean.external.WxCpUserWithExternalPermission;
-import me.chanjar.weixin.cp.bean.external.WxCpWelcomeMsg;
+import me.chanjar.weixin.cp.bean.external.*;
 import me.chanjar.weixin.cp.bean.external.contact.*;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.StringUtils;
@@ -51,7 +32,7 @@
 import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.ExternalContact.*;
 
 /**
- * @author 曹祖鹏 & yuanqixun
+ * @author 曹祖鹏 & yuanqixun & Mr.Pan & Wang_Wong
  */
 @RequiredArgsConstructor
 public class WxCpExternalContactServiceImpl implements WxCpExternalContactService {
@@ -128,8 +109,12 @@ public WxCpExternalContactInfo getExternalContact(String userId) throws WxErrorE
   }
 
   @Override
-  public WxCpExternalContactInfo getContactDetail(String userId) throws WxErrorException {
-    final String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_CONTACT_DETAIL + userId);
+  public WxCpExternalContactInfo getContactDetail(String userId, String cursor) throws WxErrorException {
+    String params = userId;
+    if(StringUtils.isNotEmpty(cursor)){
+      params = params + "&cursor=" + cursor;
+    }
+    final String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_CONTACT_DETAIL + params);
     String responseContent = this.mainService.get(url, null);
     return WxCpExternalContactInfo.fromJson(responseContent);
   }
@@ -157,6 +142,59 @@ public String unionidToExternalUserid(@NotNull String unionid,String openid) thr
     return tmpJson.get("external_userid").getAsString();
   }
 
+  @Override
+  public String toServiceExternalUserid(@NotNull String externalUserid) throws WxErrorException {
+    JsonObject json = new JsonObject();
+    json.addProperty("external_userid", externalUserid);
+    final String url = this.mainService.getWxCpConfigStorage().getApiUrl(TO_SERVICE_EXTERNAL_USERID);
+    String responseContent = this.mainService.post(url, json.toString());
+    JsonObject tmpJson = GsonParser.parse(responseContent);
+    return tmpJson.get("external_userid").getAsString();
+  }
+
+  @Override
+  public WxCpExternalUserIdList unionidToExternalUserid3rd(@NotNull String unionid, @NotNull String openid, String corpid) throws WxErrorException {
+    JsonObject json = new JsonObject();
+    json.addProperty("unionid", unionid);
+    json.addProperty("openid", openid);
+    if(StringUtils.isNotEmpty(corpid)){
+      json.addProperty("corpid",corpid);
+    }
+    final String url = this.mainService.getWxCpConfigStorage().getApiUrl(UNIONID_TO_EXTERNAL_USERID_3RD);
+    String responseContent = this.mainService.post(url, json.toString());
+    return WxCpExternalUserIdList.fromJson(responseContent);
+  }
+
+  @Override
+  public WxCpNewExternalUserIdList getNewExternalUserId(String[] externalUserIdList) throws WxErrorException {
+    JsonObject json = new JsonObject();
+    if (ArrayUtils.isNotEmpty(externalUserIdList)) {
+      json.add("external_userid_list", new Gson().toJsonTree(externalUserIdList).getAsJsonArray());
+    }
+    final String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_NEW_EXTERNAL_USERID);
+    String responseContent = this.mainService.post(url, json.toString());
+    return WxCpNewExternalUserIdList.fromJson(responseContent);
+  }
+
+  @Override
+  public WxCpBaseResp finishExternalUserIdMigration(@NotNull String corpid) throws WxErrorException {
+    JsonObject json = new JsonObject();
+    json.addProperty("corpid", corpid);
+    final String url = this.mainService.getWxCpConfigStorage().getApiUrl(FINISH_EXTERNAL_USERID_MIGRATION);
+    String responseContent = this.mainService.post(url, json.toString());
+    return WxCpBaseResp.fromJson(responseContent);
+  }
+
+  @Override
+  public String opengidToChatid(@NotNull String opengid) throws WxErrorException {
+    JsonObject json = new JsonObject();
+    json.addProperty("opengid",opengid);
+    final String url = this.mainService.getWxCpConfigStorage().getApiUrl(OPENID_TO_CHATID);
+    String responseContent = this.mainService.post(url, json.toString());
+    JsonObject tmpJson = GsonParser.parse(responseContent);
+    return tmpJson.get("chat_id").getAsString();
+  }
+
   @Override
   public WxCpExternalContactBatchInfo getContactDetailBatch(String[] userIdList,
                                                             String cursor,
@@ -207,10 +245,13 @@ public List listFollowers() throws WxErrorException {
   }
 
   @Override
-  public WxCpUserExternalUnassignList listUnassignedList(Integer pageIndex, Integer pageSize) throws WxErrorException {
+  public WxCpUserExternalUnassignList listUnassignedList(Integer pageIndex, String cursor, Integer pageSize) throws WxErrorException {
     JsonObject json = new JsonObject();
-    json.addProperty("page_id", pageIndex == null ? 0 : pageIndex);
-    json.addProperty("page_size", pageSize == null ? 100 : pageSize);
+    if(pageIndex != null){
+      json.addProperty("page_id", pageIndex);
+    }
+    json.addProperty("cursor", StringUtils.isEmpty(cursor) ? "" : cursor);
+    json.addProperty("page_size", pageSize == null ? 1000 : pageSize);
     final String url = this.mainService.getWxCpConfigStorage().getApiUrl(LIST_UNASSIGNED_CONTACT);
     final String result = this.mainService.post(url, json.toString());
     return WxCpUserExternalUnassignList.fromJson(result);
@@ -586,7 +627,7 @@ public WxCpGroupMsgListResult getGroupMsgListV2(String chatType, @NonNull Date s
     json.addProperty("limit", limit);
     json.addProperty("cursor", cursor);
 
-    final String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_GROUP_MSG_SEND_RESULT);
+    final String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_GROUP_MSG_LIST_V2);
     final String result = this.mainService.post(url, json.toString());
     return WxCpGroupMsgListResult.fromJson(result);
   }
@@ -617,6 +658,30 @@ public WxCpGroupMsgSendResult getGroupMsgSendResult(String msgid, String userid,
     return WxCpGroupMsgSendResult.fromJson(result);
   }
 
+  /**
+   * 
+   * 企业跟第三方应用可通过该接口获取到创建企业群发的群发发送结果。
+   * https://work.weixin.qq.com/api/doc/16251
+   * 
+ * + * @param msgid 群发消息的id,通过创建企业群发接口返回 + * @param limit 返回的最大记录数,整型,最大值10000,默认值10000 + * @param cursor 用于分页查询的游标,字符串类型,由上一次调用返回,首次调用可不填 + * @return wx cp base resp + * @throws WxErrorException the wx error exception + */ + @Override + public WxCpGroupMsgResult getGroupMsgResult(String msgid, Integer limit, String cursor) throws WxErrorException { + JsonObject json = new JsonObject(); + json.addProperty("msgid", msgid); + json.addProperty("limit", limit); + json.addProperty("cursor", cursor); + + final String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_GROUP_MSG_RESULT); + final String result = this.mainService.post(url, json.toString()); + return WxCpGroupMsgResult.fromJson(result); + } + /** *
    * 获取群发成员发送任务列表。
@@ -636,8 +701,142 @@ public WxCpGroupMsgTaskResult getGroupMsgTask(String msgid, Integer limit, Strin
     json.addProperty("limit", limit);
     json.addProperty("cursor", cursor);
 
-    final String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_GROUP_MSG_SEND_RESULT);
+    final String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_GROUP_MSG_TASK);
     final String result = this.mainService.post(url, json.toString());
     return WxCpGroupMsgTaskResult.fromJson(result);
   }
+
+  /**
+   * 
+   * 添加入群欢迎语素材。
+   * https://open.work.weixin.qq.com/api/doc/90000/90135/92366#添加入群欢迎语素材
+   * 
+ * + * @param template 素材内容 + * @return template_id 欢迎语素材id + * @throws WxErrorException the wx error exception + */ + @Override + public String addGroupWelcomeTemplate(WxCpGroupWelcomeTemplateResult template) throws WxErrorException { + final String url = this.mainService.getWxCpConfigStorage().getApiUrl(GROUP_WELCOME_TEMPLATE_ADD); + final String responseContent = this.mainService.post(url, template.toJson()); + JsonObject tmpJson = GsonParser.parse(responseContent); + return tmpJson.get("template_id").getAsString(); + } + + /** + *
+   * 编辑入群欢迎语素材。
+   * https://open.work.weixin.qq.com/api/doc/90000/90135/92366#编辑入群欢迎语素材
+   * 
+ * + * @param template + * @return wx cp base resp + * @throws WxErrorException the wx error exception + */ + @Override + public WxCpBaseResp editGroupWelcomeTemplate(WxCpGroupWelcomeTemplateResult template) throws WxErrorException { + final String url = this.mainService.getWxCpConfigStorage().getApiUrl(GROUP_WELCOME_TEMPLATE_EDIT); + final String result = this.mainService.post(url, template.toJson()); + return WxCpGroupWelcomeTemplateResult.fromJson(result); + } + + /** + *
+   * 获取入群欢迎语素材。
+   * https://open.work.weixin.qq.com/api/doc/90000/90135/92366#获取入群欢迎语素材
+   * 
+ * + * @param templateId 群欢迎语的素材id + * @return wx cp base resp + * @throws WxErrorException the wx error exception + */ + @Override + public WxCpGroupWelcomeTemplateResult getGroupWelcomeTemplate(@NotNull String templateId) throws WxErrorException { + JsonObject json = new JsonObject(); + json.addProperty("template_id", templateId); + final String url = this.mainService.getWxCpConfigStorage().getApiUrl(GROUP_WELCOME_TEMPLATE_GET); + final String result = this.mainService.post(url, json.toString()); + return WxCpGroupWelcomeTemplateResult.fromJson(result); + } + + /** + *
+   * 删除入群欢迎语素材。
+   * 企业可通过此API删除入群欢迎语素材,且仅能删除调用方自己创建的入群欢迎语素材。
+   * https://open.work.weixin.qq.com/api/doc/90000/90135/92366#删除入群欢迎语素材
+   * 
+ * + * @param templateId 群欢迎语的素材id + * @param agentId + * @return wx cp base resp + * @throws WxErrorException the wx error exception + */ + @Override + public WxCpBaseResp delGroupWelcomeTemplate(@NotNull String templateId, String agentId) throws WxErrorException { + JsonObject json = new JsonObject(); + json.addProperty("template_id", templateId); + if (!StringUtils.isEmpty(agentId)) { + json.addProperty("agentid", agentId); + } + final String url = this.mainService.getWxCpConfigStorage().getApiUrl(GROUP_WELCOME_TEMPLATE_DEL); + final String result = this.mainService.post(url, json.toString()); + return WxCpBaseResp.fromJson(result); + } + + /** + *
+   * 获取商品图册
+   * https://work.weixin.qq.com/api/doc/90000/90135/95096#获取商品图册列表
+   * 
+ * + * @param limit 返回的最大记录数,整型,最大值100,默认值50,超过最大值时取默认值 + * @param cursor 用于分页查询的游标,字符串类型,由上一次调用返回,首次调用可不填 + * @return wx cp base resp + * @throws WxErrorException the wx error exception + */ + @Override + public WxCpProductAlbumListResult getProductAlbumList(Integer limit, String cursor) throws WxErrorException { + JsonObject json = new JsonObject(); + json.addProperty("limit", limit); + json.addProperty("cursor", cursor); + final String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_PRODUCT_ALBUM_LIST); + final String result = this.mainService.post(url, json.toString()); + return WxCpProductAlbumListResult.fromJson(result); + } + + /** + *
+   * 获取商品图册
+   * https://work.weixin.qq.com/api/doc/90000/90135/95096#获取商品图册
+   * 
+ * + * @param productId 商品id + * @return wx cp base resp + * @throws WxErrorException the wx error exception + */ + @Override + public WxCpProductAlbumResult getProductAlbum(String productId) throws WxErrorException { + JsonObject json = new JsonObject(); + json.addProperty("product_id", productId); + final String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_PRODUCT_ALBUM); + final String result = this.mainService.post(url, json.toString()); + return WxCpProductAlbumResult.fromJson(result); + } + + @Override + public WxMediaUploadResult uploadAttachment(String mediaType, String fileType, Integer attachmentType, + InputStream inputStream) throws WxErrorException, IOException { + return uploadAttachment(mediaType, attachmentType, FileUtils.createTmpFile(inputStream, + UUID.randomUUID().toString(), fileType)); + } + + @Override + public WxMediaUploadResult uploadAttachment(String mediaType, Integer attachmentType, File file) + throws WxErrorException { + String params = "?media_type=" + mediaType + "&attachment_type=" + attachmentType; + final String url = this.mainService.getWxCpConfigStorage().getApiUrl(UPLOAD_ATTACHMENT + params); + return this.mainService.execute(MediaUploadRequestExecutor.create( + this.mainService.getRequestHttp()), url, file); + } } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpKfServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpKfServiceImpl.java new file mode 100644 index 000000000..12f1062b0 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpKfServiceImpl.java @@ -0,0 +1,191 @@ +package me.chanjar.weixin.cp.api.impl; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import java.util.List; +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.cp.api.WxCpKfService; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.bean.WxCpBaseResp; +import me.chanjar.weixin.cp.bean.kf.WxCpKfAccountAdd; +import me.chanjar.weixin.cp.bean.kf.WxCpKfAccountAddResp; +import me.chanjar.weixin.cp.bean.kf.WxCpKfAccountDel; +import me.chanjar.weixin.cp.bean.kf.WxCpKfAccountLink; +import me.chanjar.weixin.cp.bean.kf.WxCpKfAccountLinkResp; +import me.chanjar.weixin.cp.bean.kf.WxCpKfAccountListResp; +import me.chanjar.weixin.cp.bean.kf.WxCpKfAccountUpd; +import me.chanjar.weixin.cp.bean.kf.WxCpKfCustomerBatchGetResp; +import me.chanjar.weixin.cp.bean.kf.WxCpKfMsgListResp; +import me.chanjar.weixin.cp.bean.kf.WxCpKfMsgSendRequest; +import me.chanjar.weixin.cp.bean.kf.WxCpKfMsgSendResp; +import me.chanjar.weixin.cp.bean.kf.WxCpKfServiceStateResp; +import me.chanjar.weixin.cp.bean.kf.WxCpKfServiceStateTransResp; +import me.chanjar.weixin.cp.bean.kf.WxCpKfServicerListResp; +import me.chanjar.weixin.cp.bean.kf.WxCpKfServicerOpResp; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Kf.*; + +/** + * 微信客服接口-服务实现 + * + * @author Fu + * @date 2022/1/19 19:41 + */ +@RequiredArgsConstructor +public class WxCpKfServiceImpl implements WxCpKfService { + private final WxCpService cpService; + private static final Gson GSON = new GsonBuilder().create(); + + @Override + public WxCpKfAccountAddResp addAccount(WxCpKfAccountAdd add) throws WxErrorException { + String url = cpService.getWxCpConfigStorage().getApiUrl(ACCOUNT_ADD); + String responseContent = cpService.post(url, WxCpGsonBuilder.create().toJson(add)); + return WxCpKfAccountAddResp.fromJson(responseContent); + } + + @Override + public WxCpBaseResp updAccount(WxCpKfAccountUpd upd) throws WxErrorException { + String url = cpService.getWxCpConfigStorage().getApiUrl(ACCOUNT_UPD); + String responseContent = cpService.post(url, WxCpGsonBuilder.create().toJson(upd)); + return WxCpBaseResp.fromJson(responseContent); + } + + @Override + public WxCpBaseResp delAccount(WxCpKfAccountDel del) throws WxErrorException { + String url = cpService.getWxCpConfigStorage().getApiUrl(ACCOUNT_DEL); + String responseContent = cpService.post(url, WxCpGsonBuilder.create().toJson(del)); + return WxCpBaseResp.fromJson(responseContent); + } + + @Override + public WxCpKfAccountListResp listAccount() throws WxErrorException { + String url = cpService.getWxCpConfigStorage().getApiUrl(ACCOUNT_LIST); + String responseContent = cpService.post(url, "{}"); + return WxCpKfAccountListResp.fromJson(responseContent); + } + + @Override + public WxCpKfAccountLinkResp getAccountLink(WxCpKfAccountLink link) throws WxErrorException { + String url = cpService.getWxCpConfigStorage().getApiUrl(ADD_CONTACT_WAY); + String responseContent = cpService.post(url, WxCpGsonBuilder.create().toJson(link)); + return WxCpKfAccountLinkResp.fromJson(responseContent); + } + + @Override + public WxCpKfServicerOpResp addServicer(String openKfid, List userIdList) throws WxErrorException { + return servicerOp(openKfid, userIdList, SERVICER_ADD); + } + + @Override + public WxCpKfServicerOpResp delServicer(String openKfid, List userIdList) throws WxErrorException { + return servicerOp(openKfid, userIdList, SERVICER_DEL); + } + + private WxCpKfServicerOpResp servicerOp(String openKfid, List userIdList, String uri) throws WxErrorException { + String url = cpService.getWxCpConfigStorage().getApiUrl(uri); + + JsonObject json = new JsonObject(); + json.addProperty("open_kfid", openKfid); + JsonArray userIdArray = new JsonArray(); + userIdList.forEach(userIdArray::add); + json.add("userid_list", userIdArray); + + String responseContent = cpService.post(url, json.toString()); + return WxCpKfServicerOpResp.fromJson(responseContent); + } + + @Override + public WxCpKfServicerListResp listServicer(String openKfid) throws WxErrorException { + String url = cpService.getWxCpConfigStorage().getApiUrl(SERVICER_LIST + openKfid); + String responseContent = cpService.get(url, null); + return WxCpKfServicerListResp.fromJson(responseContent); + } + + @Override + public WxCpKfServiceStateResp getServiceState(String openKfid, String externalUserId) + throws WxErrorException { + String url = cpService.getWxCpConfigStorage().getApiUrl(SERVICE_STATE_GET); + + JsonObject json = new JsonObject(); + json.addProperty("open_kfid", openKfid); + json.addProperty("external_userid", externalUserId); + + String responseContent = cpService.post(url, json.toString()); + return WxCpKfServiceStateResp.fromJson(responseContent); + } + + @Override + public WxCpKfServiceStateTransResp transServiceState(String openKfid, String externalUserId, + Integer serviceState, String servicerUserId) throws WxErrorException { + String url = cpService.getWxCpConfigStorage().getApiUrl(SERVICE_STATE_TRANS); + + JsonObject json = new JsonObject(); + json.addProperty("open_kfid", openKfid); + json.addProperty("external_userid", externalUserId); + json.addProperty("service_state", serviceState); + json.addProperty("servicer_userid", servicerUserId); + + String responseContent = cpService.post(url, json.toString()); + return WxCpKfServiceStateTransResp.fromJson(responseContent); + } + + @Override + public WxCpKfMsgListResp syncMsg(String cursor, String token, Integer limit, Integer voiceFormat) + throws WxErrorException { + String url = cpService.getWxCpConfigStorage().getApiUrl(SYNC_MSG); + + JsonObject json = new JsonObject(); + if (cursor!=null) { + json.addProperty("cursor", cursor); + } + if (token!=null) { + json.addProperty("token", token); + } + if (limit!=null) { + json.addProperty("limit", limit); + } + if (voiceFormat!=null) { + json.addProperty("voice_format", voiceFormat); + } + + String responseContent = cpService.post(url, json); + return WxCpKfMsgListResp.fromJson(responseContent); + } + + @Override + public WxCpKfMsgSendResp sendMsg(WxCpKfMsgSendRequest request) throws WxErrorException { + String url = cpService.getWxCpConfigStorage().getApiUrl(SEND_MSG); + + String responseContent = cpService.post(url, GSON.toJson(request)); + + return WxCpKfMsgSendResp.fromJson(responseContent); + } + + @Override + public WxCpKfMsgSendResp sendMsgOnEvent(WxCpKfMsgSendRequest request) throws WxErrorException { + String url = cpService.getWxCpConfigStorage().getApiUrl(SEND_MSG_ON_EVENT); + + String responseContent = cpService.post(url, GSON.toJson(request)); + + return WxCpKfMsgSendResp.fromJson(responseContent); + } + + @Override + public WxCpKfCustomerBatchGetResp customerBatchGet(List externalUserIdList) + throws WxErrorException { + String url = cpService.getWxCpConfigStorage().getApiUrl(CUSTOMER_BATCH_GET); + + JsonArray array = new JsonArray(); + + externalUserIdList.forEach(array::add); + JsonObject json = new JsonObject(); + json.add("external_userid_list", array); + String responseContent = cpService.post(url, json.toString()); + return WxCpKfCustomerBatchGetResp.fromJson(responseContent); + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpLivingServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpLivingServiceImpl.java new file mode 100644 index 000000000..5fdf18cf8 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpLivingServiceImpl.java @@ -0,0 +1,118 @@ +package me.chanjar.weixin.cp.api.impl; + +import com.google.gson.JsonObject; +import com.google.gson.reflect.TypeToken; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.json.GsonHelper; +import me.chanjar.weixin.common.util.json.GsonParser; +import me.chanjar.weixin.cp.api.WxCpLivingService; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.bean.living.*; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Living.*; + +/** + * 企业微信直播接口实现类. + * + * @author Wang_Wong + * @date 2021-12-21 + */ +@Slf4j +@RequiredArgsConstructor +public class WxCpLivingServiceImpl implements WxCpLivingService { + private final WxCpService cpService; + + @Override + public String getLivingCode(String openId, String livingId) throws WxErrorException { + final String apiUrl = this.cpService.getWxCpConfigStorage().getApiUrl(GET_LIVING_CODE); + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("openid", openId); + jsonObject.addProperty("livingid", livingId); + String responseContent = this.cpService.post(apiUrl, jsonObject.toString()); + return GsonHelper.getString(GsonParser.parse(responseContent), "living_code"); + } + + @Override + public WxCpLivingInfo getLivingInfo(String livingId) throws WxErrorException { + String apiUrl = this.cpService.getWxCpConfigStorage().getApiUrl(GET_LIVING_INFO) + livingId; + String responseContent = this.cpService.get(apiUrl, null); + return WxCpGsonBuilder.create() + .fromJson(GsonParser.parse(responseContent).get("living_info"), + new TypeToken() { + }.getType() + ); + } + + @Override + public WxCpWatchStat getWatchStat(String livingId, Integer nextKey) throws WxErrorException { + String apiUrl = this.cpService.getWxCpConfigStorage().getApiUrl(GET_WATCH_STAT); + JsonObject jsonObject = new JsonObject(); + if (nextKey != null) { + jsonObject.addProperty("next_key", String.valueOf(nextKey)); + } + jsonObject.addProperty("livingid", livingId); + String responseContent = this.cpService.post(apiUrl, jsonObject.toString()); + return WxCpWatchStat.fromJson(responseContent); + } + + @Override + public WxCpLivingResult.LivingIdResult getUserAllLivingId(String userId, String cursor, Integer limit) throws WxErrorException { + String apiUrl = this.cpService.getWxCpConfigStorage().getApiUrl(GET_USER_ALL_LIVINGID); + JsonObject jsonObject = new JsonObject(); + if (cursor != null) { + jsonObject.addProperty("cursor", cursor); + } + if (limit != null) { + jsonObject.addProperty("limit", limit); + } + jsonObject.addProperty("userid", userId); + String responseContent = this.cpService.post(apiUrl, jsonObject.toString()); + return WxCpLivingResult.LivingIdResult.fromJson(responseContent); + } + + @Override + public WxCpLivingShareInfo getLivingShareInfo(String wwShareCode) throws WxErrorException { + String apiUrl = this.cpService.getWxCpConfigStorage().getApiUrl(GET_LIVING_SHARE_INFO); + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("ww_share_code", wwShareCode); + String responseContent = this.cpService.post(apiUrl, jsonObject.toString()); + return WxCpLivingShareInfo.fromJson(responseContent); + } + + @Override + public String livingCreate(WxCpLivingCreateRequest request) throws WxErrorException { + String apiUrl = this.cpService.getWxCpConfigStorage().getApiUrl(CREATE); + String responseContent = this.cpService.post(apiUrl, request.toJson()); + return GsonHelper.getString(GsonParser.parse(responseContent), "livingid"); + } + + @Override + public WxCpLivingResult livingModify(WxCpLivingModifyRequest request) throws WxErrorException { + String apiUrl = this.cpService.getWxCpConfigStorage().getApiUrl(MODIFY); + String responseContent = this.cpService.post(apiUrl, request.toJson()); + return WxCpLivingResult.fromJson(responseContent); + } + + @Override + public WxCpLivingResult livingCancel(@NonNull String livingId) throws WxErrorException { + String apiUrl = this.cpService.getWxCpConfigStorage().getApiUrl(CANCEL); + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("livingid", livingId); + String responseContent = this.cpService.post(apiUrl, jsonObject.toString()); + return WxCpLivingResult.fromJson(responseContent); + } + + @Override + public WxCpLivingResult deleteReplayData(@NonNull String livingId) throws WxErrorException { + String apiUrl = this.cpService.getWxCpConfigStorage().getApiUrl(DELETE_REPLAY_DATA); + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("livingid", livingId); + String responseContent = this.cpService.post(apiUrl, jsonObject.toString()); + return WxCpLivingResult.fromJson(responseContent); + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMediaServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMediaServiceImpl.java index b83b6d39a..8e88aa20e 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMediaServiceImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMediaServiceImpl.java @@ -5,6 +5,8 @@ import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.util.fs.FileUtils; import me.chanjar.weixin.common.util.http.BaseMediaDownloadRequestExecutor; +import me.chanjar.weixin.common.util.http.InputStreamData; +import me.chanjar.weixin.common.util.http.MediaInputStreamUploadRequestExecutor; import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; import me.chanjar.weixin.cp.api.WxCpMediaService; import me.chanjar.weixin.cp.api.WxCpService; @@ -12,6 +14,8 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; import java.util.UUID; import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Media.*; @@ -34,6 +38,32 @@ public WxMediaUploadResult upload(String mediaType, String fileType, InputStream return this.upload(mediaType, FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), fileType)); } + @Override + public WxMediaUploadResult upload(String mediaType, String filename, String url) throws WxErrorException, IOException { + HttpURLConnection conn = null; + InputStream inputStream = null; + try { + URL remote = new URL(url); + conn = (HttpURLConnection) remote.openConnection(); + //设置超时间为3秒 + conn.setConnectTimeout(60 * 1000); + //防止屏蔽程序抓取而返回403错误 + conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)"); + inputStream = conn.getInputStream(); + return this.mainService.execute(MediaInputStreamUploadRequestExecutor.create(this.mainService.getRequestHttp()), this.mainService.getWxCpConfigStorage().getApiUrl(MEDIA_UPLOAD + mediaType), new InputStreamData(inputStream, filename)); + } finally { + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + } + } + if (conn != null) { + conn.disconnect(); + } + } + } + @Override public WxMediaUploadResult upload(String mediaType, File file) throws WxErrorException { return this.mainService.execute(MediaUploadRequestExecutor.create(this.mainService.getRequestHttp()), diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMsgAuditServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMsgAuditServiceImpl.java new file mode 100644 index 000000000..6e47a4648 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMsgAuditServiceImpl.java @@ -0,0 +1,196 @@ +package me.chanjar.weixin.cp.api.impl; + +import com.google.gson.JsonObject; +import com.google.gson.reflect.TypeToken; +import com.tencent.wework.Finance; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.json.GsonParser; +import me.chanjar.weixin.cp.api.WxCpMsgAuditService; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.bean.msgaudit.*; +import me.chanjar.weixin.cp.util.crypto.WxCpCryptUtil; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; +import org.apache.commons.lang3.StringUtils; + +import java.io.File; +import java.io.FileOutputStream; +import java.util.List; + +import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.MsgAudit.*; + +/** + * 会话内容存档接口实现类. + * + * @author Wang_Wong + * @date 2022-01-17 + */ +@Slf4j +@RequiredArgsConstructor +public class WxCpMsgAuditServiceImpl implements WxCpMsgAuditService { + private final WxCpService cpService; + + @Override + public WxCpChatDatas getChatDatas(long seq, @NonNull long limit, String proxy, String passwd, @NonNull long timeout) throws Exception { + String configPath = cpService.getWxCpConfigStorage().getMsgAuditLibPath(); + if (StringUtils.isEmpty(configPath)) { + throw new WxErrorException("请配置会话存档sdk文件的路径,不要配错了!!"); + } + + // 替换斜杠 + String replacePath = configPath.replace("\\", "/"); + // 所有的后缀文件 + String suffixFiles = replacePath.substring(replacePath.lastIndexOf("/") + 1); + // 获取的前缀路径 + String prefixPath = replacePath.substring(0, replacePath.lastIndexOf("/") + 1); + + // 包含so文件 + String[] libFiles = suffixFiles.split(","); + if (libFiles.length <= 0) { + throw new WxErrorException("请仔细配置会话存档文件路径!!"); + } + + Finance.loadingLibraries(libFiles, prefixPath); + long sdk = Finance.SingletonSDK(); + + long ret = Finance.Init(sdk, cpService.getWxCpConfigStorage().getCorpId(), cpService.getWxCpConfigStorage().getCorpSecret()); + if (ret != 0) { + Finance.DestroySingletonSDK(sdk); + throw new WxErrorException("init sdk err ret " + ret); + } + + long slice = Finance.NewSlice(); + ret = Finance.GetChatData(sdk, seq, limit, proxy, passwd, timeout, slice); + if (ret != 0) { + Finance.FreeSlice(slice); + Finance.DestroySingletonSDK(sdk); + throw new WxErrorException("getchatdata err ret " + ret); + } + + // 拉取会话存档 + String content = Finance.GetContentFromSlice(slice); + Finance.FreeSlice(slice); + WxCpChatDatas chatDatas = WxCpChatDatas.fromJson(content); + if (chatDatas.getErrCode().intValue() != 0) { + throw new WxErrorException(chatDatas.toJson()); + } + + return chatDatas; + } + + @Override + public WxCpChatModel getDecryptData(@NonNull WxCpChatDatas.WxCpChatData chatData) throws Exception { + String plainText = this.decryptChatData(chatData); + return WxCpChatModel.fromJson(plainText); + } + + public String decryptChatData(WxCpChatDatas.WxCpChatData chatData) throws Exception { + // 企业获取的会话内容将用公钥加密,企业可用自行保存的私钥解开会话内容数据,aeskey不能为空 + String priKey = cpService.getWxCpConfigStorage().getAesKey(); + if (StringUtils.isEmpty(priKey)) { + throw new WxErrorException("请配置会话存档私钥【aesKey】"); + } + + String decryptByPriKey = WxCpCryptUtil.decryptByPriKey(chatData.getEncryptRandomKey(), priKey); + // 每次使用DecryptData解密会话存档前需要调用NewSlice获取一个slice,在使用完slice中数据后,还需要调用FreeSlice释放。 + long sdk = Finance.SingletonSDK(); + long msg = Finance.NewSlice(); + + int ret = Finance.DecryptData(sdk, decryptByPriKey, chatData.getEncryptChatMsg(), msg); + if (ret != 0) { + Finance.FreeSlice(msg); + Finance.DestroySingletonSDK(sdk); + throw new WxErrorException("msg err ret " + ret); + } + + // 明文 + String plainText = Finance.GetContentFromSlice(msg); + Finance.FreeSlice(msg); + return plainText; + } + + @Override + public String getChatPlainText(WxCpChatDatas.@NonNull WxCpChatData chatData) throws Exception { + return this.decryptChatData(chatData); + } + + @Override + public void getMediaFile(@NonNull String sdkfileid, String proxy, String passwd, @NonNull long timeout, @NonNull String targetFilePath) throws WxErrorException { + /** + * 1、媒体文件每次拉取的最大size为512k,因此超过512k的文件需要分片拉取。 + * 2、若该文件未拉取完整,sdk的IsMediaDataFinish接口会返回0,同时通过GetOutIndexBuf接口返回下次拉取需要传入GetMediaData的indexbuf。 + * 3、indexbuf一般格式如右侧所示,”Range:bytes=524288-1048575“:表示这次拉取的是从524288到1048575的分片。单个文件首次拉取填写的indexbuf为空字符串,拉取后续分片时直接填入上次返回的indexbuf即可。 + */ + File targetFile = new File(targetFilePath); + if (!targetFile.getParentFile().exists()) { + targetFile.getParentFile().mkdirs(); + } + + String indexbuf = ""; + int ret, data_len = 0; + while (true) { + long mediaData = Finance.NewMediaData(); + long sdk = Finance.SingletonSDK(); + ret = Finance.GetMediaData(sdk, indexbuf, sdkfileid, proxy, passwd, timeout, mediaData); + if (ret != 0) { + Finance.FreeMediaData(mediaData); + Finance.DestroySingletonSDK(sdk); + throw new WxErrorException("getmediadata err ret " + ret); + } + + data_len += Finance.GetDataLen(mediaData); + log.info("正在分片拉取媒体文件 len:{}, data_len:{}, is_finis:{} \n", Finance.GetIndexLen(mediaData), data_len, Finance.IsMediaDataFinish(mediaData)); + + try { + // 大于512k的文件会分片拉取,此处需要使用追加写,避免后面的分片覆盖之前的数据。 + FileOutputStream outputStream = new FileOutputStream(new File(targetFilePath), true); + outputStream.write(Finance.GetData(mediaData)); + outputStream.close(); + } catch (Exception e) { + e.printStackTrace(); + } + + if (Finance.IsMediaDataFinish(mediaData) == 1) { + // 已经拉取完成最后一个分片 + Finance.FreeMediaData(mediaData); + break; + } else { + // 获取下次拉取需要使用的indexbuf + indexbuf = Finance.GetOutIndexBuf(mediaData); + Finance.FreeMediaData(mediaData); + } + } + } + + @Override + public List getPermitUserList(Integer type) throws WxErrorException { + final String apiUrl = this.cpService.getWxCpConfigStorage().getApiUrl(GET_PERMIT_USER_LIST); + JsonObject jsonObject = new JsonObject(); + if (type != null) { + jsonObject.addProperty("type", type); + } + String responseContent = this.cpService.post(apiUrl, jsonObject.toString()); + return WxCpGsonBuilder.create().fromJson(GsonParser.parse(responseContent).getAsJsonArray("ids"), + new TypeToken>() { + }.getType()); + } + + @Override + public WxCpGroupChat getGroupChat(@NonNull String roomid) throws WxErrorException { + final String apiUrl = this.cpService.getWxCpConfigStorage().getApiUrl(GET_GROUP_CHAT); + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("roomid", roomid); + String responseContent = this.cpService.post(apiUrl, jsonObject.toString()); + return WxCpGroupChat.fromJson(responseContent); + } + + @Override + public WxCpAgreeInfo checkSingleAgree(@NonNull WxCpCheckAgreeRequest checkAgreeRequest) throws WxErrorException { + String apiUrl = this.cpService.getWxCpConfigStorage().getApiUrl(CHECK_SINGLE_AGREE); + String responseContent = this.cpService.post(apiUrl, checkAgreeRequest.toJson()); + return WxCpAgreeInfo.fromJson(responseContent); + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpOaAgentServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpOaAgentServiceImpl.java new file mode 100644 index 000000000..5acdf0cf0 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpOaAgentServiceImpl.java @@ -0,0 +1,41 @@ +package me.chanjar.weixin.cp.api.impl; + +import com.google.gson.JsonObject; +import com.google.gson.reflect.TypeToken; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.json.GsonParser; +import me.chanjar.weixin.cp.api.WxCpOaAgentService; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.bean.oa.selfagent.WxCpOpenApprovalData; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Oa.GET_OPEN_APPROVAL_DATA; + +/** + * 企业微信自建应用接口实现类. + * + * @author Wang_Wong + * @date 2022-04-06 + */ +@Slf4j +@RequiredArgsConstructor +public class WxCpOaAgentServiceImpl implements WxCpOaAgentService { + private final WxCpService cpService; + + @Override + public WxCpOpenApprovalData getOpenApprovalData(@NonNull String thirdNo) throws WxErrorException { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("thirdNo", thirdNo); + String apiUrl = this.cpService.getWxCpConfigStorage().getApiUrl(GET_OPEN_APPROVAL_DATA); + String responseContent = this.cpService.post(apiUrl, jsonObject.toString()); + return WxCpGsonBuilder.create() + .fromJson(GsonParser.parse(responseContent).get("data"), + new TypeToken() { + }.getType() + ); + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpOaServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpOaServiceImpl.java index b921798e7..afee242fe 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpOaServiceImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpOaServiceImpl.java @@ -29,7 +29,7 @@ public class WxCpOaServiceImpl implements WxCpOaService { private final WxCpService mainService; - private static final int MONTH_SECONDS = 30 * 24 * 60 * 60; + private static final int MONTH_SECONDS = 31 * 24 * 60 * 60; private static final int USER_IDS_LIMIT = 100; @Override @@ -49,7 +49,7 @@ public List getCheckinData(Integer openCheckinDataType, @NonNul long endTimestamp = endTime.getTime() / 1000L; long startTimestamp = startTime.getTime() / 1000L; - if (endTimestamp - startTimestamp < 0 || endTimestamp - startTimestamp >= MONTH_SECONDS) { + if (endTimestamp - startTimestamp < 0 || endTimestamp - startTimestamp > MONTH_SECONDS) { throw new WxRuntimeException("获取记录时间跨度不超过一个月"); } @@ -165,6 +165,13 @@ public WxCpApprovalDetailResult getApprovalDetail(@NonNull String spNo) throws W return WxCpGsonBuilder.create().fromJson(responseContent, WxCpApprovalDetailResult.class); } + @Override + public WxCpCorpConfInfo getCorpConf() throws WxErrorException { + final String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_CORP_CONF); + String responseContent = this.mainService.get(url, null); + return WxCpCorpConfInfo.fromJson(responseContent); + } + @Override public List getDialRecord(Date startTime, Date endTime, Integer offset, Integer limit) throws WxErrorException { diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/Gender.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/Gender.java index d56bd57da..99da96262 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/Gender.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/Gender.java @@ -31,8 +31,8 @@ public enum Gender { private final String code; public static Gender fromCode(String code) { - for(Gender a: Gender.values()){ - if(a.code.equals(code)){ + for (Gender a : Gender.values()) { + if (a.code.equals(code)) { return a; } } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpAgent.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpAgent.java index 614bc9791..04b0dd72e 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpAgent.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpAgent.java @@ -1,8 +1,5 @@ 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; @@ -10,6 +7,9 @@ import lombok.NoArgsConstructor; import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; +import java.io.Serializable; +import java.util.List; + /** *
  * 企业号应用信息.
@@ -81,25 +81,27 @@ public String toJson() {
   @Data
   public static class Users implements Serializable {
     private static final long serialVersionUID = 8801100463558788565L;
+
     @SerializedName("user")
     private List users;
   }
 
   @Data
-  public class User implements Serializable {
+  public static class User implements Serializable {
     private static final long serialVersionUID = 7287632514385508024L;
+
     @SerializedName("userid")
     private String userId;
   }
 
   @Data
-  public class Parties {
+  public static class Parties {
     @SerializedName("partyid")
     private List partyIds = null;
   }
 
   @Data
-  public class Tags {
+  public static class Tags {
     @SerializedName("tagid")
     private List tagIds = null;
   }
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpAgentWorkBench.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpAgentWorkBench.java
index c97faa636..bda927a80 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpAgentWorkBench.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpAgentWorkBench.java
@@ -16,49 +16,51 @@
 /**
  * @author songshiyu
  * @date : create in 16:09 2020/9/27
- * @description: 工作台自定义展示
+ * 工作台自定义展示
  */
 @Data
 @Builder
 @NoArgsConstructor
 @AllArgsConstructor
 public class WxCpAgentWorkBench implements Serializable {
-  private static final long serialVersionUid = 1L;
+  private static final long serialVersionUID = -4136604790232843229L;
 
-  /*
-  * 展示类型,目前支持 “keydata”、 “image”、 “list” 、”webview”
-  * */
+  /**
+   * 展示类型,目前支持 “keydata”、 “image”、 “list” 、”webview”
+   */
   private String type;
-  /*
-  * 用户的userid
-  * */
+  /**
+   * 用户的userid
+   */
   private String userId;
-  /*
-  * 应用id
-  * */
+  /**
+   * 应用id
+   */
   private Long agentId;
-  /*
-  * 点击跳转url,若不填且应用设置了主页url,则跳转到主页url,否则跳到应用会话窗口
-  * */
+  /**
+   * 点击跳转url,若不填且应用设置了主页url,则跳转到主页url,否则跳到应用会话窗口
+   */
   private String jumpUrl;
-  /*
-  * 若应用为小程序类型,该字段填小程序pagepath,若未设置,跳到小程序主页
-  * */
+  /**
+   * 若应用为小程序类型,该字段填小程序pagepath,若未设置,跳到小程序主页
+   */
   private String pagePath;
-  /*
-  * 图片url:图片的最佳比例为3.35:1;webview:渲染展示的url
-  * */
+  /**
+   * 图片url:图片的最佳比例为3.35:1;webview:渲染展示的url
+   */
   private String url;
-  /*
-  * 是否覆盖用户工作台的数据。设置为true的时候,会覆盖企业所有用户当前设置的数据。若设置为false,则不会覆盖用户当前设置的所有数据
-  * */
+  /**
+   * 是否覆盖用户工作台的数据。设置为true的时候,会覆盖企业所有用户当前设置的数据。若设置为false,则不会覆盖用户当前设置的所有数据
+   */
   private Boolean replaceUserData;
 
   private List keyDataList;
 
   private List lists;
 
-  // 生成模板Json字符串
+  /**
+   * 生成模板Json字符串
+   */
   public String toTemplateString() {
     JsonObject templateObject = new JsonObject();
     templateObject.addProperty("agentid", this.agentId);
@@ -70,7 +72,9 @@ public String toTemplateString() {
     return templateObject.toString();
   }
 
-  // 生成用户数据Json字符串
+  /**
+   * 生成用户数据Json字符串
+   */
   public String toUserDataString() {
     JsonObject userDataObject = new JsonObject();
     userDataObject.addProperty("agentid", this.agentId);
@@ -80,7 +84,9 @@ public String toUserDataString() {
     return userDataObject.toString();
   }
 
-  // 处理不用类型的工作台数据
+  /**
+   * 处理不用类型的工作台数据
+   */
   private void handle(JsonObject templateObject) {
     switch (this.getType()) {
       case WxCpConsts.WorkBenchType.KEYDATA: {
@@ -116,7 +122,7 @@ private void handle(JsonObject templateObject) {
           listObject.addProperty("pagepath", listItem.getPagePath());
           listArray.add(listObject);
         }
-        itemsObject.add("items",listArray);
+        itemsObject.add("items", listArray);
         templateObject.add("list", itemsObject);
         break;
       }
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpChat.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpChat.java
index 03b9aaa7d..eb014c595 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpChat.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpChat.java
@@ -1,22 +1,22 @@
-package me.chanjar.weixin.cp.bean;
-
-import java.io.Serializable;
-import java.util.List;
-
-import lombok.Data;
-
-/**
- * 群聊
- *
- * @author gaigeshen
- */
-@Data
-public class WxCpChat implements Serializable {
-  private static final long serialVersionUID = -4301684507150486556L;
-  
-  private String id;
-  private String name;
-  private String owner;
-  private List users;
-
-}
+package me.chanjar.weixin.cp.bean;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 群聊
+ *
+ * @author gaigeshen
+ */
+@Data
+public class WxCpChat implements Serializable {
+  private static final long serialVersionUID = -4301684507150486556L;
+
+  private String id;
+  private String name;
+  private String owner;
+  private List users;
+
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpDepart.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpDepart.java
index f5b9b3259..5c640c51c 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpDepart.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpDepart.java
@@ -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;
+
 /**
  * 企业微信的部门.
  *
@@ -17,6 +17,7 @@ public class WxCpDepart implements Serializable {
   private Long id;
   private String name;
   private String enName;
+  private String[] departmentLeader;
   private Long parentId;
   private Long order;
 
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpInviteResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpInviteResult.java
index f5a0a66bf..5ab4f5246 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpInviteResult.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpInviteResult.java
@@ -1,11 +1,11 @@
 package me.chanjar.weixin.cp.bean;
 
-import java.io.Serializable;
-
 import com.google.gson.annotations.SerializedName;
 import lombok.Data;
 import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
 
+import java.io.Serializable;
+
 /**
  * 邀请成员的结果对象类.
  * Created by Binary Wang on 2018-5-13.
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMaJsCode2SessionResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMaJsCode2SessionResult.java
index 90f1ae840..7291489d9 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMaJsCode2SessionResult.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMaJsCode2SessionResult.java
@@ -11,6 +11,7 @@
  * 小程序登录凭证校验
  * 文档地址:https://work.weixin.qq.com/api/doc#90000/90136/90289/wx.qy.login
  * 
+ * * @author Binary Wang */ @Data diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpOauth2UserInfo.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpOauth2UserInfo.java index 56e65b900..0e10737bf 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpOauth2UserInfo.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpOauth2UserInfo.java @@ -1,13 +1,13 @@ package me.chanjar.weixin.cp.bean; -import java.io.Serializable; - import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; +import java.io.Serializable; + /** *
  *  用oauth2获取用户信息的结果类
@@ -23,7 +23,7 @@
 @Builder
 public class WxCpOauth2UserInfo implements Serializable {
   private static final long serialVersionUID = -4301684507150486556L;
-  
+
   private String openId;
   private String deviceId;
   private String userId;
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpProviderToken.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpProviderToken.java
index 6a33f1c48..7b2887f03 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpProviderToken.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpProviderToken.java
@@ -1,11 +1,11 @@
 package me.chanjar.weixin.cp.bean;
 
-import java.io.Serializable;
-
 import com.google.gson.annotations.SerializedName;
 import lombok.Data;
 import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
 
+import java.io.Serializable;
+
 /**
  * 服务商凭证.
  *
@@ -15,6 +15,7 @@
 @Data
 public class WxCpProviderToken implements Serializable {
   private static final long serialVersionUID = -4301684507150486556L;
+
   /**
    * 服务商的access_token,最长为512字节。
    */
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTag.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTag.java
index f6b9fa027..8649f0ced 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTag.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTag.java
@@ -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
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTagAddOrRemoveUsersResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTagAddOrRemoveUsersResult.java
index 037740ca9..adac17488 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTagAddOrRemoveUsersResult.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTagAddOrRemoveUsersResult.java
@@ -1,15 +1,14 @@
 package me.chanjar.weixin.cp.bean;
 
-import java.io.Serializable;
-import java.util.Collections;
-import java.util.List;
-
-import org.apache.commons.lang3.StringUtils;
-
 import com.google.common.base.Splitter;
 import com.google.gson.annotations.SerializedName;
 import lombok.Data;
 import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.List;
 
 /**
  * 为标签添加或移除用户结果对象类.
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpAdmin.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpAdmin.java
index 5d77c975d..a950e0c3f 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpAdmin.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpAdmin.java
@@ -12,6 +12,7 @@
 
 /**
  * 应用的管理员
+ *
  * @author huangxiaoming
  */
 @Data
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpContactSearch.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpContactSearch.java
index ee1de69f5..cc84dfd4d 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpContactSearch.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpContactSearch.java
@@ -1,12 +1,12 @@
 package me.chanjar.weixin.cp.bean;
 
-import java.io.Serializable;
-
 import com.google.gson.annotations.SerializedName;
 import lombok.Data;
 import lombok.experimental.Accessors;
 import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
 
+import java.io.Serializable;
+
 /**
  * @author uianz
  * @description
@@ -15,51 +15,51 @@
 @Data
 @Accessors(chain = true)
 public class WxCpTpContactSearch implements Serializable {
-    private static final long serialVersionUID = -4301684507150486556L;
+  private static final long serialVersionUID = -4301684507150486556L;
 
-    /**
-     * 查询的企业corpid
-     */
-    @SerializedName("auth_corpid")
-    private String authCorpId;
+  /**
+   * 查询的企业corpid
+   */
+  @SerializedName("auth_corpid")
+  private String authCorpId;
 
-    /**
-     * 搜索关键词。当查询用户时应为用户名称、名称拼音或者英文名;当查询部门时应为部门名称或者部门名称拼音
-     */
-    @SerializedName("query_word")
-    private String queryWord;
+  /**
+   * 搜索关键词。当查询用户时应为用户名称、名称拼音或者英文名;当查询部门时应为部门名称或者部门名称拼音
+   */
+  @SerializedName("query_word")
+  private String queryWord;
 
-    /**
-     * 查询类型 1:查询用户,返回用户userid列表 2:查询部门,返回部门id列表。 不填该字段或者填0代表同时查询部门跟用户
-     */
-    @SerializedName("query_type")
-    private Integer type;
+  /**
+   * 查询类型 1:查询用户,返回用户userid列表 2:查询部门,返回部门id列表。 不填该字段或者填0代表同时查询部门跟用户
+   */
+  @SerializedName("query_type")
+  private Integer type;
 
-    /**
-     * 应用id,若非0则只返回应用可见范围内的用户或者部门信息
-     */
-    @SerializedName("agentid")
-    private Integer agentId;
+  /**
+   * 应用id,若非0则只返回应用可见范围内的用户或者部门信息
+   */
+  @SerializedName("agentid")
+  private Integer agentId;
 
-    /**
-     * 查询的偏移量,每次调用的offset在上一次offset基础上加上limit
-     */
-    @SerializedName("offset")
-    private Integer offset;
+  /**
+   * 查询的偏移量,每次调用的offset在上一次offset基础上加上limit
+   */
+  @SerializedName("offset")
+  private Integer offset;
 
-    /**
-     * 查询返回的最大数量,默认为50,最多为200,查询返回的数量可能小于limit指定的值
-     */
-    @SerializedName("limit")
-    private Integer limit;
+  /**
+   * 查询返回的最大数量,默认为50,最多为200,查询返回的数量可能小于limit指定的值
+   */
+  @SerializedName("limit")
+  private Integer limit;
 
-    /**
-     * 如果需要精确匹配用户名称或者部门名称或者英文名,不填则默认为模糊匹配;1:匹配用户名称或者部门名称 2:匹配用户英文名
-     */
-    @SerializedName("full_match_field")
-    private Integer fullMatchField;
+  /**
+   * 如果需要精确匹配用户名称或者部门名称或者英文名,不填则默认为模糊匹配;1:匹配用户名称或者部门名称 2:匹配用户英文名
+   */
+  @SerializedName("full_match_field")
+  private Integer fullMatchField;
 
-    public String toJson() {
-        return WxCpGsonBuilder.create().toJson(this);
-    }
+  public String toJson() {
+    return WxCpGsonBuilder.create().toJson(this);
+  }
 }
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpContactSearchResp.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpContactSearchResp.java
index 1998a4230..21db4e083 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpContactSearchResp.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpContactSearchResp.java
@@ -17,42 +17,42 @@
 @Data
 public class WxCpTpContactSearchResp extends WxCpBaseResp {
 
-    @SerializedName("is_last")
-    private Boolean isLast;
+  @SerializedName("is_last")
+  private Boolean isLast;
 
-    @SerializedName("query_result")
-    private QueryResult queryResult;
+  @SerializedName("query_result")
+  private QueryResult queryResult;
 
-    @Data
-    public static class QueryResult implements Serializable {
-        private static final long serialVersionUID = -4301684507150486556L;
-
-        @SerializedName("user")
-        private User user;
-        @SerializedName("party")
-        private Party party;
-
-        @Data
-        public static class User implements Serializable {
-            private static final long serialVersionUID = -4301684507150486556L;
-            @SerializedName("userid")
-            private List userid;
-            @SerializedName("open_userid")
-            private List openUserId;
-        }
-
-        @Data
-        public static class Party implements Serializable {
-            private static final long serialVersionUID = -4301684507150486556L;
-
-            @SerializedName("department_id")
-            private List departmentId;
-        }
+  @Data
+  public static class QueryResult implements Serializable {
+    private static final long serialVersionUID = -4301684507150486556L;
+
+    @SerializedName("user")
+    private User user;
+    @SerializedName("party")
+    private Party party;
 
+    @Data
+    public static class User implements Serializable {
+      private static final long serialVersionUID = -4301684507150486556L;
+      @SerializedName("userid")
+      private List userid;
+      @SerializedName("open_userid")
+      private List openUserId;
     }
 
-    public static WxCpTpContactSearchResp fromJson(String json) {
-      return WxCpGsonBuilder.create().fromJson(json, WxCpTpContactSearchResp.class);
+    @Data
+    public static class Party implements Serializable {
+      private static final long serialVersionUID = -4301684507150486556L;
+
+      @SerializedName("department_id")
+      private List departmentId;
     }
 
+  }
+
+  public static WxCpTpContactSearchResp fromJson(String json) {
+    return WxCpGsonBuilder.create().fromJson(json, WxCpTpContactSearchResp.class);
+  }
+
 }
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpCorp.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpCorp.java
index f77fdb78d..efe6d8285 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpCorp.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpCorp.java
@@ -1,12 +1,11 @@
 package me.chanjar.weixin.cp.bean;
 
-import java.io.Serializable;
-
 import com.google.gson.annotations.SerializedName;
-
 import lombok.Data;
 import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
 
+import java.io.Serializable;
+
 /**
  * 微信部门.
  *
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpPreauthCode.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpPreauthCode.java
index 8c102ae4a..82df9f456 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpPreauthCode.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpPreauthCode.java
@@ -7,6 +7,7 @@
 
 /**
  * 预授权码返回
+ *
  * @author yqx
  * @date 2020/3/19
  */
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpTag.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpTag.java
index b584b31dd..73d7a5157 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpTag.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpTag.java
@@ -7,13 +7,11 @@
 import java.io.Serializable;
 
 /**
- *
  * @author zhangq 
  * @since 2021-02-14 16:15 16:15
  */
 @Data
 public class WxCpTpTag implements Serializable {
-
   private static final long serialVersionUID = 581740383760234134L;
 
   @SerializedName("tagid")
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpTagAddOrRemoveUsersResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpTagAddOrRemoveUsersResult.java
index 35319b1ba..8a9fecf21 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpTagAddOrRemoveUsersResult.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpTagAddOrRemoveUsersResult.java
@@ -4,6 +4,7 @@
 
 /**
  * 企业微信第三方开发-增加标签成员成员api响应体
+ *
  * @author zhangq 
  * @since 2021/2/14 16:44
  */
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpTagGetResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpTagGetResult.java
index d77e99b13..4fdc9a58a 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpTagGetResult.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpTagGetResult.java
@@ -4,6 +4,7 @@
 
 /**
  * 获取标签成员接口响应体
+ *
  * @author zhangq 
  * @since 2021/2/14 16:28
  */
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpUserDetail.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpUserDetail.java
index c949b0a1b..280939125 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpUserDetail.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpUserDetail.java
@@ -6,7 +6,6 @@
 import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
 
 /**
- *
  * @author huangxiaoming
  */
 @Data
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpXmlPackage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpXmlPackage.java
index e7af1dd61..4d9d9493a 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpXmlPackage.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpXmlPackage.java
@@ -1,8 +1,5 @@
 package me.chanjar.weixin.cp.bean;
 
-import java.io.Serializable;
-import java.util.Map;
-
 import com.thoughtworks.xstream.annotations.XStreamAlias;
 import com.thoughtworks.xstream.annotations.XStreamConverter;
 import lombok.Data;
@@ -10,6 +7,9 @@
 import me.chanjar.weixin.common.util.xml.XStreamCDataConverter;
 import me.chanjar.weixin.cp.util.xml.XStreamTransformer;
 
+import java.io.Serializable;
+import java.util.Map;
+
 /**
  * 回调消息包.
  * https://work.weixin.qq.com/api/doc#90001/90143/91116
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpUser.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpUser.java
index 18ef125ab..76a8f9330 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpUser.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpUser.java
@@ -70,7 +70,7 @@ public class WxCpUser implements Serializable {
   private String externalCorpName;
   private WechatChannels wechatChannels;
 
-
+  private String[] directLeader;
 
 
   public void addExternalAttr(ExternalAttribute externalAttr) {
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpUserDetail.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpUserDetail.java
index 7c4af4df6..295acfdbc 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpUserDetail.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpUserDetail.java
@@ -1,10 +1,10 @@
 package me.chanjar.weixin.cp.bean;
 
-import java.io.Serializable;
-
 import com.google.gson.annotations.SerializedName;
 import lombok.Data;
 
+import java.io.Serializable;
+
 /**
  * 
  *  使用user_ticket获取成员详情接口返回类.
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpUserExternalContactInfo.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpUserExternalContactInfo.java
index 6a9e2c8e7..ca6ebb8bb 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpUserExternalContactInfo.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpUserExternalContactInfo.java
@@ -78,6 +78,7 @@ public static class ExternalProfile implements Serializable {
   @AllArgsConstructor
   public static class ExternalAttribute implements Serializable {
     private static final long serialVersionUID = -5696099236344075582L;
+
     @Setter
     @Getter
     public static class Text implements Serializable {
@@ -122,7 +123,7 @@ public static class MiniProgram implements Serializable {
   @Getter
   public static class FollowedUser implements Serializable {
     private static final long serialVersionUID = -5696099236344075582L;
-    
+
     @SerializedName("userid")
     private String userId;
     private String remark;
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpAddMomentTask.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpAddMomentTask.java
index 3e952ccb9..efa0c1bfc 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpAddMomentTask.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpAddMomentTask.java
@@ -1,17 +1,18 @@
 package me.chanjar.weixin.cp.bean.external;
 
 import com.google.gson.annotations.SerializedName;
-import java.io.Serializable;
-import java.util.List;
 import lombok.AllArgsConstructor;
 import lombok.Builder;
 import lombok.Data;
 import lombok.NoArgsConstructor;
+import me.chanjar.weixin.cp.bean.external.moment.VisibleRange;
 import me.chanjar.weixin.cp.bean.external.msg.Attachment;
 import me.chanjar.weixin.cp.bean.external.msg.Text;
-import me.chanjar.weixin.cp.bean.external.moment.VisibleRange;
 import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
 
+import java.io.Serializable;
+import java.util.List;
+
 /**
  * 企业发表内容到客户的朋友圈 创建发表任务
  *
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpContactWayInfo.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpContactWayInfo.java
index 35f6a6eaa..66d94da2c 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpContactWayInfo.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpContactWayInfo.java
@@ -208,7 +208,7 @@ public enum TYPE {
      * 多人
      */
     @SerializedName("2")
-    MULTI;
+    MULTI
 
   }
 
@@ -224,7 +224,7 @@ public enum SCENE {
      * 通过二维码联系
      */
     @SerializedName("2")
-    QRCODE;
+    QRCODE
 
   }
 
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpContactWayResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpContactWayResult.java
index 0a49719a9..789dac318 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpContactWayResult.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpContactWayResult.java
@@ -8,11 +8,14 @@
 
 /**
  * 「联系我」方式 处理结果
+ *
+ * @author 爱因斯唐
  */
 @Data
 @EqualsAndHashCode(callSuper = true)
 public class WxCpContactWayResult extends WxCpBaseResp {
   private static final long serialVersionUID = -2612867517869192015L;
+
   @SerializedName("config_id")
   private String configId;
 
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpExternalUserIdList.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpExternalUserIdList.java
new file mode 100644
index 000000000..3b06a0a07
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpExternalUserIdList.java
@@ -0,0 +1,54 @@
+package me.chanjar.weixin.cp.bean.external;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Getter;
+import lombok.Setter;
+import me.chanjar.weixin.cp.bean.WxCpBaseResp;
+import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 企业客户微信unionid的升级 - 企业客户external_userid列表
+ *
+ * @author Mr.Pan
+ * @date 2021/11/18
+ */
+@Getter
+@Setter
+public class WxCpExternalUserIdList extends WxCpBaseResp {
+  private static final long serialVersionUID = 3922210865083522513L;
+
+  @SerializedName("external_userid_info")
+  private List externalUserIdInfo;
+
+  @Getter
+  @Setter
+  public static class ExternalUserIdInfo implements Serializable {
+    private static final long serialVersionUID = 8846290993790709261L;
+
+    /**
+     * 所属企业id
+     */
+    @SerializedName("corpid")
+    private String corpId;
+
+    /**
+     * 外部联系人id
+     */
+    @SerializedName("external_userid")
+    private String externalUserId;
+
+    /**
+     * 新外部联系人id
+     */
+    @SerializedName("new_external_userid")
+    private String newExternalUserId;
+
+  }
+
+  public static WxCpExternalUserIdList fromJson(String json) {
+    return WxCpGsonBuilder.create().fromJson(json, WxCpExternalUserIdList.class);
+  }
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentComments.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentComments.java
index 697670261..a0228e3ac 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentComments.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentComments.java
@@ -1,7 +1,6 @@
 package me.chanjar.weixin.cp.bean.external;
 
 import com.google.gson.annotations.SerializedName;
-import java.util.List;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.Getter;
@@ -9,6 +8,8 @@
 import me.chanjar.weixin.cp.bean.WxCpBaseResp;
 import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
 
+import java.util.List;
+
 /**
  * 企业发表内容到客户的朋友圈 获取客户朋友圈的互动数据
  *
@@ -19,6 +20,7 @@
 @EqualsAndHashCode(callSuper = true)
 public class WxCpGetMomentComments extends WxCpBaseResp {
   private static final long serialVersionUID = -9056664072546234965L;
+
   @SerializedName("comment_list")
   private List commentList;
   @SerializedName("like_list")
@@ -29,6 +31,8 @@ public class WxCpGetMomentComments extends WxCpBaseResp {
   public static class CommentLikeItem {
     @SerializedName("external_userid")
     private String externalUserId;
+    @SerializedName("userid")
+    private String userid;
     @SerializedName("create_time")
     private Long createTime;
   }
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentList.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentList.java
index 6ba154df8..32cce1dd4 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentList.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentList.java
@@ -1,13 +1,14 @@
 package me.chanjar.weixin.cp.bean.external;
 
 import com.google.gson.annotations.SerializedName;
-import java.util.List;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import me.chanjar.weixin.cp.bean.WxCpBaseResp;
 import me.chanjar.weixin.cp.bean.external.moment.MomentInfo;
 import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
 
+import java.util.List;
+
 /**
  * 企业发表内容到客户的朋友圈 获取企业全部的发表列表
  *
@@ -18,6 +19,7 @@
 @EqualsAndHashCode(callSuper = true)
 public class WxCpGetMomentList extends WxCpBaseResp {
   private static final long serialVersionUID = 106159971765109008L;
+
   @SerializedName("next_cursor")
   private String nextCursor;
   @SerializedName("moment_list")
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentSendResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentSendResult.java
index 38cd89a45..30df9c43a 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentSendResult.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentSendResult.java
@@ -1,13 +1,14 @@
 package me.chanjar.weixin.cp.bean.external;
 
 import com.google.gson.annotations.SerializedName;
-import java.util.List;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import me.chanjar.weixin.cp.bean.WxCpBaseResp;
 import me.chanjar.weixin.cp.bean.external.moment.CustomerItem;
 import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
 
+import java.util.List;
+
 /**
  * 企业发表内容到客户的朋友圈 获取客户朋友圈发表后的可见客户列表
  *
@@ -18,6 +19,7 @@
 @EqualsAndHashCode(callSuper = true)
 public class WxCpGetMomentSendResult extends WxCpBaseResp {
   private static final long serialVersionUID = -5782811995184523379L;
+
   @SerializedName("next_cursor")
   private String nextCursor;
   @SerializedName("customer_list")
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentTask.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentTask.java
index aa45bec4e..2b7032f79 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentTask.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentTask.java
@@ -1,7 +1,6 @@
 package me.chanjar.weixin.cp.bean.external;
 
 import com.google.gson.annotations.SerializedName;
-import java.util.List;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.Getter;
@@ -9,6 +8,8 @@
 import me.chanjar.weixin.cp.bean.WxCpBaseResp;
 import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
 
+import java.util.List;
+
 /**
  * 企业发表内容到客户的朋友圈 获取客户朋友圈企业发表的列表
  *
@@ -19,6 +20,7 @@
 @EqualsAndHashCode(callSuper = true)
 public class WxCpGetMomentTask extends WxCpBaseResp {
   private static final long serialVersionUID = 5621905029624794129L;
+
   @SerializedName("next_cursor")
   private String nextCursor;
 
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentTaskResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentTaskResult.java
index 019e7fdf1..b0ab78f1e 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentTaskResult.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGetMomentTaskResult.java
@@ -20,6 +20,7 @@
 @EqualsAndHashCode(callSuper = true)
 public class WxCpGetMomentTaskResult extends WxCpBaseResp {
   private static final long serialVersionUID = 2515140928288915077L;
+
   private Integer status;
   private String type;
   private TaskResult result;
@@ -27,6 +28,8 @@ public class WxCpGetMomentTaskResult extends WxCpBaseResp {
   @Getter
   @Setter
   public static class TaskResult extends WxCpBaseResp {
+    private static final long serialVersionUID = 2162642873632126707L;
+
     @SerializedName("moment_id")
     private String momentId;
     @SerializedName("invalid_sender_list")
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGroupWelcomeTemplateResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGroupWelcomeTemplateResult.java
new file mode 100644
index 000000000..631d6be26
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpGroupWelcomeTemplateResult.java
@@ -0,0 +1,43 @@
+package me.chanjar.weixin.cp.bean.external;
+
+import lombok.*;
+import me.chanjar.weixin.cp.bean.WxCpBaseResp;
+import me.chanjar.weixin.cp.bean.external.msg.*;
+import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
+
+import java.io.Serializable;
+
+/**
+ * 入群欢迎语素材.
+ *
+ * @author Mr.Pan
+ * @date 2021-11-3
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@EqualsAndHashCode(callSuper = true)
+public class WxCpGroupWelcomeTemplateResult extends WxCpBaseResp implements Serializable {
+  private static final long serialVersionUID = -6406667238670580612L;
+
+  private Text text;
+
+  private Image image;
+
+  private Link link;
+
+  private MiniProgram miniprogram;
+
+  private File file;
+
+  private Video video;
+
+  public static WxCpGroupWelcomeTemplateResult fromJson(String json) {
+    return WxCpGsonBuilder.create().fromJson(json, WxCpGroupWelcomeTemplateResult.class);
+  }
+
+  public String toJson() {
+    return WxCpGsonBuilder.create().toJson(this);
+  }
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpMsgTemplate.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpMsgTemplate.java
index 295eecb63..2d5343459 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpMsgTemplate.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpMsgTemplate.java
@@ -5,9 +5,7 @@
 import lombok.Builder;
 import lombok.Data;
 import lombok.NoArgsConstructor;
-import me.chanjar.weixin.cp.bean.external.msg.Image;
-import me.chanjar.weixin.cp.bean.external.msg.Link;
-import me.chanjar.weixin.cp.bean.external.msg.MiniProgram;
+import me.chanjar.weixin.cp.bean.external.msg.Attachment;
 import me.chanjar.weixin.cp.bean.external.msg.Text;
 import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
 
@@ -19,7 +17,7 @@
  * 

* Created by songfan on 2020/7/14. * - * @author songfan + * @author songfan & Mr.Pan */ @Data @Builder @@ -28,21 +26,32 @@ public class WxCpMsgTemplate implements Serializable { private static final long serialVersionUID = 3172331565173474358L; + /** + * 群发任务的类型,默认为single,表示发送给客户,group表示发送给客户群 + */ @SerializedName("chat_type") private String chatType; + /** + * 客户的外部联系人id列表,仅在chat_type为single时有效,不可与sender同时为空,最多可传入1万个客户 + */ @SerializedName("external_userid") private List externalUserid; + /** + * 发送企业群发消息的成员userid,当类型为发送给客户群时必填 + */ private String sender; + /** + * 消息文本内容,最多4000个字节 + */ private Text text; - private Image image; - - private Link link; - - private MiniProgram miniprogram; + /** + * 附件,最多支持添加9个附件 + */ + private List attachments; public static WxCpMsgTemplate fromJson(String json) { return WxCpGsonBuilder.create().fromJson(json, WxCpMsgTemplate.class); diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpNewExternalUserIdList.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpNewExternalUserIdList.java new file mode 100644 index 000000000..4bc68f957 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpNewExternalUserIdList.java @@ -0,0 +1,47 @@ +package me.chanjar.weixin.cp.bean.external; + +import com.google.gson.annotations.SerializedName; +import lombok.Getter; +import lombok.Setter; +import me.chanjar.weixin.cp.bean.WxCpBaseResp; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.io.Serializable; +import java.util.List; + +/** + * 企业客户微信unionid的升级 - 企业客户external_userid列表 + * + * @author Mr.Pan + * @date 2021/11/18 + */ +@Getter +@Setter +public class WxCpNewExternalUserIdList extends WxCpBaseResp { + + @SerializedName("items") + private List items; + + @Getter + @Setter + public static class NewExternalUserIdInfo implements Serializable { + private static final long serialVersionUID = 8846290993790709261L; + + /** + * 外部联系人id + */ + @SerializedName("external_userid") + private String externalUserId; + + /** + * 新外部联系人id + */ + @SerializedName("new_external_userid") + private String newExternalUserId; + + } + + public static WxCpNewExternalUserIdList fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpNewExternalUserIdList.class); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpProductAlbumInfo.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpProductAlbumInfo.java new file mode 100644 index 000000000..e0ad62ea3 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpProductAlbumInfo.java @@ -0,0 +1,53 @@ +package me.chanjar.weixin.cp.bean.external; + +import com.google.gson.annotations.SerializedName; +import lombok.Getter; +import lombok.Setter; +import me.chanjar.weixin.cp.bean.external.product.Attachment; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.io.Serializable; +import java.util.List; + +/** + *

+ * 获取商品图册
+ * 参考文档:https://work.weixin.qq.com/api/doc/90000/90135/95096#获取商品图册
+ * 
+ * + * @author Lo_ading + */ +@Getter +@Setter +public class WxCpProductAlbumInfo implements Serializable { + private static final long serialVersionUID = -8338202601802366899L; + + @SerializedName("product_id") + private String productId; + + @SerializedName("product_sn") + private String productSn; + + @SerializedName("description") + private String description; + + /** + * NOTE: 20211110 价钱返回全部为0 + */ + @SerializedName("price") + private Integer price; + + /** + * NOTE: 20211110 商品列表接口不返回此字段, 商品详情接口返回 + */ + @SerializedName("create_time") + private Long createTime; + + @SerializedName("attachments") + private List attachments; + + public static WxCpProductAlbumInfo fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpProductAlbumInfo.class); + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpProductAlbumListResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpProductAlbumListResult.java new file mode 100644 index 000000000..2b6f4b208 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpProductAlbumListResult.java @@ -0,0 +1,35 @@ +package me.chanjar.weixin.cp.bean.external; + +import com.google.gson.annotations.SerializedName; +import lombok.Getter; +import lombok.Setter; +import me.chanjar.weixin.cp.bean.WxCpBaseResp; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.io.Serializable; +import java.util.List; + +/** + *
+ * 获取商品图册列表执行结果
+ * 参考文档:https://work.weixin.qq.com/api/doc/90000/90135/95096#获取商品图册列表
+ * 
+ * + * @author Lo_ading + */ +@Getter +@Setter +public class WxCpProductAlbumListResult extends WxCpBaseResp implements Serializable { + private static final long serialVersionUID = 121265727802015428L; + + @SerializedName("product_list") + private List productList; + + @SerializedName("next_cursor") + private String nextCursor; + + public static WxCpProductAlbumListResult fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpProductAlbumListResult.class); + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpProductAlbumResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpProductAlbumResult.java new file mode 100644 index 000000000..527bfb6eb --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpProductAlbumResult.java @@ -0,0 +1,31 @@ +package me.chanjar.weixin.cp.bean.external; + +import com.google.gson.annotations.SerializedName; +import lombok.Getter; +import lombok.Setter; +import me.chanjar.weixin.cp.bean.WxCpBaseResp; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.io.Serializable; + +/** + *
+ * 获取商品图册执行结果
+ * 参考文档:https://work.weixin.qq.com/api/doc/90000/90135/95096#获取商品图册
+ * 
+ * + * @author Lo_ading + */ +@Getter +@Setter +public class WxCpProductAlbumResult extends WxCpBaseResp implements Serializable { + private static final long serialVersionUID = 4076734101839851497L; + + @SerializedName("product") + private WxCpProductAlbumInfo product; + + public static WxCpProductAlbumResult fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpProductAlbumResult.class); + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalUnassignList.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalUnassignList.java index 68d065a2d..d27334836 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalUnassignList.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalUnassignList.java @@ -12,7 +12,7 @@ /** * 离职员工外部联系人列表 * - * @author yqx + * @author yqx & Wang_Wong * @date 2020/3/15 */ @Getter @@ -25,6 +25,9 @@ public class WxCpUserExternalUnassignList extends WxCpBaseResp { @SerializedName("is_last") private boolean isLast; + @SerializedName("next_cursor") + private String nextCursor; + @Getter @Setter public static class UnassignInfo implements Serializable { @@ -52,4 +55,9 @@ public static class UnassignInfo implements Serializable { public static WxCpUserExternalUnassignList fromJson(String json) { return WxCpGsonBuilder.create().fromJson(json, WxCpUserExternalUnassignList.class); } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/contact/ExternalContact.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/contact/ExternalContact.java index 5b7f9e67b..07d490ac1 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/contact/ExternalContact.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/contact/ExternalContact.java @@ -26,6 +26,9 @@ public class ExternalContact implements Serializable { @SerializedName("name") private String name; + @SerializedName("nickname") + private String nickname; + @SerializedName("avatar") private String avatar; @@ -51,10 +54,30 @@ public class ExternalContact implements Serializable { public static class ExternalProfile implements Serializable { private static final long serialVersionUID = -2899906589789022765L; + @SerializedName("external_corp_name") + private String externalCorpName; + + @SerializedName("wechat_channels") + private WechatChannel wechatChannels; + @SerializedName("external_attr") private List externalAttrs; } + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class WechatChannel implements Serializable { + + @SerializedName("nickname") + private String nickname; + + @SerializedName("status") + private Integer status; + + } + @Data @Builder @NoArgsConstructor diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/contact/WxCpGroupMsgResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/contact/WxCpGroupMsgResult.java new file mode 100644 index 000000000..5cae404f0 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/contact/WxCpGroupMsgResult.java @@ -0,0 +1,59 @@ +package me.chanjar.weixin.cp.bean.external.contact; + +import com.google.gson.annotations.SerializedName; +import lombok.Getter; +import lombok.Setter; +import me.chanjar.weixin.cp.bean.WxCpBaseResp; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.io.Serializable; +import java.util.List; + +/** + *
+ * 获取企业群发成员执行结果
+ * 参考文档:https://work.weixin.qq.com/api/doc/16251
+ * 
+ * + * @author Tim Sims + */ +@Getter +@Setter +public class WxCpGroupMsgResult extends WxCpBaseResp implements Serializable { + private static final long serialVersionUID = -5166048319463473186L; + + @SerializedName("detail_list") + private List detailList; + + @SerializedName("next_cursor") + private String nextCursor; + + @Getter + @Setter + public static class ExternalContactGroupMsgDetailInfo implements Serializable { + private static final long serialVersionUID = 1500416806087532531L; + + // 外部联系人userid,群发消息到企业的客户群不吐出该字段 + @SerializedName("external_userid") + private String externalUserId; + + // 外部客户群id,群发消息到客户不吐出该字段 + @SerializedName("chat_id") + private String chatId; + + // 企业服务人员的userid + @SerializedName("userid") + private String userId; + + // 发送状态 0-未发送 1-已发送 2-因客户不是好友导致发送失败 3-因客户已经收到其他群发消息导致发送失败 + private Integer status; + + // 发送时间,发送状态为1时返回 + @SerializedName("send_time") + private Long sendTime; + } + + public static WxCpGroupMsgResult fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpGroupMsgResult.class); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/moment/CustomerItem.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/moment/CustomerItem.java index 08b124239..3bcbe03e0 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/moment/CustomerItem.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/moment/CustomerItem.java @@ -4,6 +4,9 @@ import lombok.Getter; import lombok.Setter; +/** + * @author Boris + */ @Getter @Setter public class CustomerItem { diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/moment/ExternalContactList.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/moment/ExternalContactList.java index 4d08bf358..c9f2e0a58 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/moment/ExternalContactList.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/moment/ExternalContactList.java @@ -1,10 +1,11 @@ package me.chanjar.weixin.cp.bean.external.moment; import com.google.gson.annotations.SerializedName; -import java.util.List; import lombok.Getter; import lombok.Setter; +import java.util.List; + @Getter @Setter public class ExternalContactList { diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/moment/MomentInfo.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/moment/MomentInfo.java index 2ed770e10..3fd364ddb 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/moment/MomentInfo.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/moment/MomentInfo.java @@ -2,12 +2,13 @@ import com.google.gson.annotations.SerializedName; import lombok.Data; -import me.chanjar.weixin.cp.bean.external.msg.Image; -import me.chanjar.weixin.cp.bean.external.msg.Link; -import me.chanjar.weixin.cp.bean.external.msg.Location; -import me.chanjar.weixin.cp.bean.external.msg.Text; -import me.chanjar.weixin.cp.bean.external.msg.Video; +import me.chanjar.weixin.cp.bean.external.msg.*; +import java.util.List; + +/** + * @author Borisg + */ @Data public class MomentInfo { @SerializedName("moment_id") @@ -21,7 +22,7 @@ public class MomentInfo { @SerializedName("visible_type") private Integer visibleType; private Text text; - private Image image; + private List image; private Video video; private Link link; private Location location; diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/moment/SenderList.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/moment/SenderList.java index 45889684c..b3f2c387e 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/moment/SenderList.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/moment/SenderList.java @@ -1,10 +1,14 @@ package me.chanjar.weixin.cp.bean.external.moment; import com.google.gson.annotations.SerializedName; -import java.util.List; import lombok.Getter; import lombok.Setter; +import java.util.List; + +/** + * @author Boris + */ @Getter @Setter public class SenderList { diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/moment/VisibleRange.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/moment/VisibleRange.java index 251fb5e64..1bf6c46cb 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/moment/VisibleRange.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/moment/VisibleRange.java @@ -1,11 +1,19 @@ package me.chanjar.weixin.cp.bean.external.moment; import com.google.gson.annotations.SerializedName; -import java.io.Serializable; import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serializable; +/** + * @author Boris + */ @Data +@Accessors(chain = true) public class VisibleRange implements Serializable { + private static final long serialVersionUID = 5356285705365931051L; + @SerializedName("sender_list") private SenderList senderList; @SerializedName("external_contact_list") diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/msg/Attachment.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/msg/Attachment.java index 7dce73ad0..d714b093c 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/msg/Attachment.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/msg/Attachment.java @@ -47,7 +47,7 @@ public void setVideo(Video video) { this.msgType = WxCpConsts.WelcomeMsgType.VIDEO; } - public void setFile(File file ) { + public void setFile(File file) { this.file = file; this.msgType = WxCpConsts.WelcomeMsgType.FILE; } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/msg/Text.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/msg/Text.java index 2b5ae5fc6..06746e3d5 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/msg/Text.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/msg/Text.java @@ -1,6 +1,7 @@ package me.chanjar.weixin.cp.bean.external.msg; import lombok.Data; +import lombok.experimental.Accessors; import java.io.Serializable; @@ -11,6 +12,7 @@ * @date 2020-08-16 */ @Data +@Accessors(chain = true) public class Text implements Serializable { private static final long serialVersionUID = 6608288753719551600L; private String content; diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/product/Attachment.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/product/Attachment.java new file mode 100644 index 000000000..c1480fbb7 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/product/Attachment.java @@ -0,0 +1,31 @@ +package me.chanjar.weixin.cp.bean.external.product; + +import lombok.Data; +import me.chanjar.weixin.cp.constant.WxCpConsts; + +import java.io.Serializable; + +/** + * 商品画册附件 + * + * @author Lo_ading + */ +@Data +public class Attachment implements Serializable { + private static final long serialVersionUID = -4545283630169056262L; + + /** + * NOTE: 20211110 字段接口未返回 + */ + private String type; + + /** + * 附件类型,目前仅支持image + */ + private Image image; + + public void setImage(Image image) { + this.image = image; + this.type = WxCpConsts.ProductAttachmentType.IMAGE; + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/product/Image.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/product/Image.java new file mode 100644 index 000000000..7628a6367 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/product/Image.java @@ -0,0 +1,20 @@ +package me.chanjar.weixin.cp.bean.external.product; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; + +/** + * 商品画册图片 + * + * @author Lo_ading + */ +@Data +public class Image implements Serializable { + private static final long serialVersionUID = -2737415903252627814L; + + @SerializedName("media_id") + private String mediaId; + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfAccountAdd.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfAccountAdd.java new file mode 100644 index 000000000..14ca9f0f8 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfAccountAdd.java @@ -0,0 +1,33 @@ +package me.chanjar.weixin.cp.bean.kf; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 添加客服帐号-请求参数 + * + * @author Fu + * @date 2022/1/19 18:59 + */ +@NoArgsConstructor +@Data +public class WxCpKfAccountAdd implements Serializable { + + private static final long serialVersionUID = 3565729481246537411L; + + /** + * 客服名称;不多于16个字符 + */ + @SerializedName("name") + private String name; + + /** + * 客服头像临时素材。可以调用上传临时素材接口获取。 + * 不多于128个字节 + */ + @SerializedName("media_id") + private String mediaId; +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfAccountAddResp.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfAccountAddResp.java new file mode 100644 index 000000000..1e47d696b --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfAccountAddResp.java @@ -0,0 +1,32 @@ +package me.chanjar.weixin.cp.bean.kf; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.cp.bean.WxCpBaseResp; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +/** + * 添加客服帐号-返回结果 + * + * @author Fu + * @date 2022/1/19 19:04 + */ +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@Data +public class WxCpKfAccountAddResp extends WxCpBaseResp { + + private static final long serialVersionUID = -6649323005421772827L; + + /** + * 新创建的客服帐号ID + */ + @SerializedName("open_kfid") + private String openKfid; + + public static WxCpKfAccountAddResp fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpKfAccountAddResp.class); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfAccountDel.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfAccountDel.java new file mode 100644 index 000000000..026e05510 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfAccountDel.java @@ -0,0 +1,28 @@ +package me.chanjar.weixin.cp.bean.kf; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 删除客服帐号-请求参数 + * + * @author Fu + * @date 2022/1/19 19:09 + */ +@NoArgsConstructor +@Data +public class WxCpKfAccountDel implements Serializable { + + private static final long serialVersionUID = 1997221467585676772L; + + /** + * 客服帐号ID。 + * 不多于64字节 + */ + @SerializedName("open_kfid") + private String openKfid; + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfAccountLink.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfAccountLink.java new file mode 100644 index 000000000..a46a186db --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfAccountLink.java @@ -0,0 +1,40 @@ +package me.chanjar.weixin.cp.bean.kf; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 获取客服帐号链接-请求参数 + * + * @author Fu + * @date 2022/1/19 19:18 + */ +@NoArgsConstructor +@Data +public class WxCpKfAccountLink implements Serializable { + + private static final long serialVersionUID = -1920926948347984256L; + + /** + * 客服帐号ID + */ + @SerializedName("open_kfid") + private String openKfid; + + /** + * 场景值,字符串类型,由开发者自定义。 + * 不多于32字节 + * 字符串取值范围(正则表达式):[0-9a-zA-Z_-]* + *

+ * 1. 若scene非空,返回的客服链接开发者可拼接scene_param=SCENE_PARAM参数使用,用户进入会话事件会将SCENE_PARAM原样返回。 + * 其中SCENE_PARAM需要urlencode,且长度不能超过128字节。 + * 如 https://work.weixin.qq.com/kf/kfcbf8f8d07ac7215f?enc_scene=ENCGFSDF567DF&scene_param=a%3D1%26b%3D2 + * 2. 历史调用接口返回的客服链接(包含encScene=XXX参数),不支持scene_param参数。 + * 3. 返回的客服链接,不能修改或复制参数到其他链接使用。否则进入会话事件参数校验不通过,导致无法回调。 + */ + @SerializedName("scene") + private String scene; +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfAccountLinkResp.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfAccountLinkResp.java new file mode 100644 index 000000000..89bf63595 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfAccountLinkResp.java @@ -0,0 +1,32 @@ +package me.chanjar.weixin.cp.bean.kf; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.cp.bean.WxCpBaseResp; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +/** + * 获取客服帐号链接-结果 + * + * @author Fu + * @date 2022/1/19 19:18 + */ +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@Data +public class WxCpKfAccountLinkResp extends WxCpBaseResp { + + private static final long serialVersionUID = 910205439597092481L; + + /** + * 客服链接,开发者可将该链接嵌入到H5页面中,用户点击链接即可向对应的微信客服帐号发起咨询。开发者也可根据该url自行生成需要的二维码图片 + */ + @SerializedName("url") + private String url; + + public static WxCpKfAccountLinkResp fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpKfAccountLinkResp.class); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfAccountListResp.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfAccountListResp.java new file mode 100644 index 000000000..5fb6c84e9 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfAccountListResp.java @@ -0,0 +1,57 @@ +package me.chanjar.weixin.cp.bean.kf; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.cp.bean.WxCpBaseResp; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.util.List; + +/** + * 获取客服帐号列表-结果 + * + * @author Fu + * @date 2022/1/19 19:13 + */ +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@Data +public class WxCpKfAccountListResp extends WxCpBaseResp { + + private static final long serialVersionUID = -1317201649692262217L; + + /** + * 帐号信息列表 + */ + @SerializedName("account_list") + private List accountList; + + @NoArgsConstructor + @Data + public static class AccountListDTO { + /** + * 客服帐号ID + */ + @SerializedName("open_kfid") + private String openKfid; + + /** + * 客服名称 + */ + @SerializedName("name") + private String name; + + /** + * 客服头像URL + */ + @SerializedName("avatar") + private String avatar; + } + + public static WxCpKfAccountListResp fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpKfAccountListResp.class); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfAccountUpd.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfAccountUpd.java new file mode 100644 index 000000000..d3ce7269a --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfAccountUpd.java @@ -0,0 +1,41 @@ +package me.chanjar.weixin.cp.bean.kf; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 修改客服帐号-请求参数 + * + * @author Fu + * @date 2022/1/19 19:10 + */ +@NoArgsConstructor +@Data +public class WxCpKfAccountUpd implements Serializable { + + private static final long serialVersionUID = -900712046553752529L; + + /** + * 要修改的客服帐号ID。 + * 不多于64字节 + */ + @SerializedName("open_kfid") + private String openKfid; + + /** + * 新的客服名称,如不需要修改可不填。 + * 不多于16个字符 + */ + @SerializedName("name") + private String name; + + /** + * 新的客服头像临时素材,如不需要修改可不填。可以调用上传临时素材接口获取。 + * 不多于128个字节 + */ + @SerializedName("media_id") + private String mediaId; +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfCustomerBatchGetResp.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfCustomerBatchGetResp.java new file mode 100644 index 000000000..dd3ea38b1 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfCustomerBatchGetResp.java @@ -0,0 +1,32 @@ +package me.chanjar.weixin.cp.bean.kf; + +import com.google.gson.annotations.SerializedName; +import java.util.List; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.cp.bean.WxCpBaseResp; +import me.chanjar.weixin.cp.bean.external.contact.ExternalContact; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +/** + * @author leiin + * @date 2022/1/26 7:56 下午 + */ +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@Data +public class WxCpKfCustomerBatchGetResp extends WxCpBaseResp { + + private static final long serialVersionUID = -3697709507605389887L; + + @SerializedName("customer_list") + private List customerList; + + @SerializedName("invalid_external_userid") + private List invalidExternalUserId; + + public static WxCpKfCustomerBatchGetResp fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpKfCustomerBatchGetResp.class); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfMsgListResp.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfMsgListResp.java new file mode 100644 index 000000000..140670afa --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfMsgListResp.java @@ -0,0 +1,73 @@ +package me.chanjar.weixin.cp.bean.kf; + +import com.google.gson.annotations.SerializedName; +import java.util.List; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.cp.bean.WxCpBaseResp; +import me.chanjar.weixin.cp.bean.kf.msg.WxCpKfBusinessCardMsg; +import me.chanjar.weixin.cp.bean.kf.msg.WxCpKfEventMsg; +import me.chanjar.weixin.cp.bean.kf.msg.WxCpKfLinkMsg; +import me.chanjar.weixin.cp.bean.kf.msg.WxCpKfLocationMsg; +import me.chanjar.weixin.cp.bean.kf.msg.WxCpKfMenuMsg; +import me.chanjar.weixin.cp.bean.kf.msg.WxCpKfMiniProgramMsg; +import me.chanjar.weixin.cp.bean.kf.msg.WxCpKfResourceMsg; +import me.chanjar.weixin.cp.bean.kf.msg.WxCpKfTextMsg; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +/** + * @author leiin + * @date 2022/1/26 5:24 下午 + */ +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@Data +public class WxCpKfMsgListResp extends WxCpBaseResp { + + private static final long serialVersionUID = -3115552079069452091L; + @SerializedName("next_cursor") + private String nextCursor; + + @SerializedName("has_more") + private Integer hasMore; + + @SerializedName("msg_list") + private List msgList; + + @NoArgsConstructor + @Data + public static class WxCpKfMsgItem { + @SerializedName("msgid") + private String msgId; + @SerializedName("open_kfid") + private String openKfid; + @SerializedName("external_userid") + private String externalUserId; + @SerializedName("send_time") + private Long sendTime; + private Integer origin; + @SerializedName("servicer_userid") + private String servicerUserId; + @SerializedName("msgtype") + private String msgType; + private WxCpKfTextMsg text; + private WxCpKfResourceMsg image; + private WxCpKfResourceMsg voice; + private WxCpKfResourceMsg video; + private WxCpKfResourceMsg file; + private WxCpKfLocationMsg location; + private WxCpKfLinkMsg link; + @SerializedName("business_card") + private WxCpKfBusinessCardMsg businessCard; + @SerializedName("miniprogram") + private WxCpKfMiniProgramMsg miniProgram; + @SerializedName("msgmenu") + private WxCpKfMenuMsg msgMenu; + private WxCpKfEventMsg event; + } + + public static WxCpKfMsgListResp fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpKfMsgListResp.class); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfMsgSendRequest.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfMsgSendRequest.java new file mode 100644 index 000000000..25dd5c764 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfMsgSendRequest.java @@ -0,0 +1,151 @@ +package me.chanjar.weixin.cp.bean.kf; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.cp.bean.kf.msg.WxCpKfLinkMsg; +import me.chanjar.weixin.cp.bean.kf.msg.WxCpKfLocationMsg; +import me.chanjar.weixin.cp.bean.kf.msg.WxCpKfMenuMsg; +import me.chanjar.weixin.cp.bean.kf.msg.WxCpKfMiniProgramMsg; +import me.chanjar.weixin.cp.bean.kf.msg.WxCpKfResourceMsg; +import me.chanjar.weixin.cp.bean.kf.msg.WxCpKfTextMsg; + +/** + * @author leiin + * @date 2022/1/26 7:00 下午 + */ +@NoArgsConstructor +@Data +public class WxCpKfMsgSendRequest { + /** + * (发送欢迎语等事件响应消息) 事件响应消息对应的code。通过事件回调下发,仅可使用一次。 + */ + private String code; + /** + *

+   *   参数:touser
+   *   是否必须:是
+   *   类型:string
+   *   说明:指定接收消息的客户UserID
+   * 
+ */ + @SerializedName("touser") + private String toUser; + /** + *
+   *   参数:open_kfid
+   *   是否必须:是
+   *   类型:string
+   *   说明:指定发送消息的客服帐号ID
+   * 
+ */ + @SerializedName("open_kfid") + private String openKfid; + /** + *
+   *   参数:msgid
+   *   是否必须:否
+   *   类型:string
+   *   说明:指定消息ID
+   * 
+ */ + @SerializedName("msgid") + private String msgId; + /** + *
+   *   参数:msgtype
+   *   是否必须:是
+   *   类型:string
+   *   说明:消息类型,(text,image,voice,video,file,link,miniprogram,msgmenu,location)
+   * 
+ */ + @SerializedName("msgtype") + private String msgType; + /** + *
+   *   参数:text
+   *   是否必须:是
+   *   类型:obj
+   *   说明:文本消息
+   * 
+ */ + private WxCpKfTextMsg text; + + /** + *
+   *   参数:image
+   *   是否必须:是
+   *   类型:obj
+   *   说明:图片消息
+   * 
+ */ + private WxCpKfResourceMsg image; + /** + *
+   *   参数:voice
+   *   是否必须:是
+   *   类型:obj
+   *   说明:语音消息
+   * 
+ */ + private WxCpKfResourceMsg voice; + /** + *
+   *   参数:video
+   *   是否必须:是
+   *   类型:obj
+   *   说明:视频消息
+   * 
+ */ + private WxCpKfResourceMsg video; + /** + *
+   *   参数:file
+   *   是否必须:是
+   *   类型:obj
+   *   说明:文件消息
+   * 
+ */ + private WxCpKfResourceMsg file; + /** + *
+   *   参数:link
+   *   是否必须:是
+   *   类型:obj
+   *   说明:链接消息
+   * 
+ */ + private WxCpKfLinkMsg link; + /** + *
+   *   参数:miniprogram
+   *   是否必须:是
+   *   类型:obj
+   *   说明:小程序消息
+   * 
+ */ + @SerializedName("miniprogram") + private WxCpKfMiniProgramMsg miniProgram; + + /** + *
+   *   参数:msgmenu
+   *   是否必须:是
+   *   类型:obj
+   *   说明:菜单消息
+   * 
+ */ + @SerializedName("msgmenu") + private WxCpKfMenuMsg msgMenu; + + /** + *
+   *   参数:location
+   *   是否必须:是
+   *   类型:obj
+   *   说明:菜单消息
+   * 
+ */ + @SerializedName("location") + private WxCpKfLocationMsg location; +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfMsgSendResp.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfMsgSendResp.java new file mode 100644 index 000000000..416edba3b --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfMsgSendResp.java @@ -0,0 +1,24 @@ +package me.chanjar.weixin.cp.bean.kf; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.cp.bean.WxCpBaseResp; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +/** + * @author leiin + * @date 2022/1/26 7:41 下午 + */ +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@Data +public class WxCpKfMsgSendResp extends WxCpBaseResp { + @SerializedName("msgid") + private String msgId; + + public static WxCpKfMsgSendResp fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpKfMsgSendResp.class); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfServiceStateResp.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfServiceStateResp.java new file mode 100644 index 000000000..da4aabcdb --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfServiceStateResp.java @@ -0,0 +1,28 @@ +package me.chanjar.weixin.cp.bean.kf; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.cp.bean.WxCpBaseResp; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +/** + * @author leiin + * @date 2022/1/26 5:00 下午 + */ +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@Data +public class WxCpKfServiceStateResp extends WxCpBaseResp { + + private static final long serialVersionUID = 8077134413448067090L; + @SerializedName("service_state") + private Integer serviceState; + @SerializedName("servicer_userid") + private String servicerUserId; + + public static WxCpKfServiceStateResp fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpKfServiceStateResp.class); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfServiceStateTransResp.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfServiceStateTransResp.java new file mode 100644 index 000000000..a4988873c --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfServiceStateTransResp.java @@ -0,0 +1,27 @@ +package me.chanjar.weixin.cp.bean.kf; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.cp.bean.WxCpBaseResp; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +/** + * @author leiin + * @date 2022/1/26 5:03 下午 + */ +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@Data +public class WxCpKfServiceStateTransResp extends WxCpBaseResp { + + private static final long serialVersionUID = -7874378445629022791L; + + @SerializedName("msg_code") + private String msgCode; + + public static WxCpKfServiceStateTransResp fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpKfServiceStateTransResp.class); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfServicerListResp.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfServicerListResp.java new file mode 100644 index 000000000..c37e5df13 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfServicerListResp.java @@ -0,0 +1,35 @@ +package me.chanjar.weixin.cp.bean.kf; + +import com.google.gson.annotations.SerializedName; +import java.util.List; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.cp.bean.WxCpBaseResp; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +/** + * @author leiin + * @date 2022/1/26 4:29 下午 + */ +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@Data +public class WxCpKfServicerListResp extends WxCpBaseResp { + + private static final long serialVersionUID = -5079770046571012449L; + @SerializedName("servicer_list") + private List servicerList; + + @NoArgsConstructor + @Data + public static class WxCpKfServicerStatus { + @SerializedName("userid") + private String userId; + private Integer status; + } + + public static WxCpKfServicerListResp fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpKfServicerListResp.class); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfServicerOpResp.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfServicerOpResp.java new file mode 100644 index 000000000..e14dccd03 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfServicerOpResp.java @@ -0,0 +1,38 @@ +package me.chanjar.weixin.cp.bean.kf; + + +import com.google.gson.annotations.SerializedName; +import java.util.List; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.cp.bean.WxCpBaseResp; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +/** + * 添加/删除客服接待人员返回结果 + * @author leiin + * @date 2022/1/26 4:11 下午 + */ +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@Data +public class WxCpKfServicerOpResp extends WxCpBaseResp { + + private static final long serialVersionUID = -4082459764202987034L; + + @SerializedName("result_list") + private List resultList; + + @Data + @NoArgsConstructor + public static class WxCpKfServicerResp extends WxCpBaseResp { + + @SerializedName("userid") + private String userId; + } + + public static WxCpKfServicerOpResp fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpKfServicerOpResp.class); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/msg/WxCpKfBusinessCardMsg.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/msg/WxCpKfBusinessCardMsg.java new file mode 100644 index 000000000..e739112ec --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/msg/WxCpKfBusinessCardMsg.java @@ -0,0 +1,16 @@ +package me.chanjar.weixin.cp.bean.kf.msg; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author leiin + * @date 2022/1/26 5:35 下午 + */ +@NoArgsConstructor +@Data +public class WxCpKfBusinessCardMsg { + @SerializedName("userid") + private String userId; +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/msg/WxCpKfEventMsg.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/msg/WxCpKfEventMsg.java new file mode 100644 index 000000000..96782a101 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/msg/WxCpKfEventMsg.java @@ -0,0 +1,40 @@ +package me.chanjar.weixin.cp.bean.kf.msg; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author leiin + * @date 2022/1/26 6:44 下午 + */ +@NoArgsConstructor +@Data +public class WxCpKfEventMsg { + @SerializedName("event_type") + private String eventType; + @SerializedName("open_kfid") + private String openKfid; + @SerializedName("external_userid") + private String externalUserId; + @SerializedName("servicer_userid") + private String servicerUserId; + @SerializedName("old_servicer_userid") + private String oldServicerUserId; + @SerializedName("new_servicer_userid") + private String newServicerUserId; + private String scene; + @SerializedName("scene_param") + private String sceneParam; + @SerializedName("welcome_code") + private String welcomeCode; + @SerializedName("fail_msgid") + private String failMsgId; + @SerializedName("fail_type") + private Integer failType; + private Integer status; + @SerializedName("change_type") + private Integer changeType; + @SerializedName("msg_code") + private String msgCode; +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/msg/WxCpKfLinkMsg.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/msg/WxCpKfLinkMsg.java new file mode 100644 index 000000000..1281389a1 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/msg/WxCpKfLinkMsg.java @@ -0,0 +1,53 @@ +package me.chanjar.weixin.cp.bean.kf.msg; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author leiin + * @date 2022/1/26 5:33 下午 + */ +@NoArgsConstructor +@Data +public class WxCpKfLinkMsg { + + /** + * 参数:title + * 是否必须:是 + * 类型:string + * 说明:标题,不超过128个字节,超过会自动截断 + */ + @SerializedName("title") + private String title; + /** + * 参数:desc + * 是否必须:否 + * 类型:string + * 说明:描述,不超过512个字节,超过会自动截断 + */ + @SerializedName("desc") + private String desc; + /** + * 参数:url + * 是否必须:是 + * 类型:string + * 说明:点击后跳转的链接。 最长2048字节,请确保包含了协议头(http/https) + */ + @SerializedName("url") + private String url; + /** + * 参数:thumb_media_id + * 是否必须:是 + * 类型:string + * 说明:发送消息参数,缩略图的media_id, 可以通过素材管理接口获得。此处thumb_media_id即上传接口返回的media_id + */ + @SerializedName("thumb_media_id") + private String thumb_media_id; + + /** + * 返回消息参数 + */ + @SerializedName("pic_url") + private String picUrl; +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/msg/WxCpKfLocationMsg.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/msg/WxCpKfLocationMsg.java new file mode 100644 index 000000000..245da5261 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/msg/WxCpKfLocationMsg.java @@ -0,0 +1,41 @@ +package me.chanjar.weixin.cp.bean.kf.msg; + +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author leiin + * @date 2022/1/26 5:32 下午 + */ +@NoArgsConstructor +@Data +public class WxCpKfLocationMsg { + /** + * 参数:name + * 是否必须:否 + * 类型:string + * 说明:位置名 + */ + private String name; + /** + * 参数:address + * 是否必须:否 + * 类型:string + * 说明:地址详情说明 + */ + private String address; + /** + * 参数:latitude + * 是否必须:是 + * 类型:float + * 说明:纬度,浮点数,范围为90 ~ -90 + */ + private Float latitude; + /** + * 参数:longitude + * 是否必须:是 + * 类型:float + * 说明:经度,浮点数,范围为180 ~ -180 + */ + private Float longitude; +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/msg/WxCpKfMenuMsg.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/msg/WxCpKfMenuMsg.java new file mode 100644 index 000000000..73df6abf9 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/msg/WxCpKfMenuMsg.java @@ -0,0 +1,128 @@ +package me.chanjar.weixin.cp.bean.kf.msg; + +import com.google.gson.annotations.SerializedName; +import java.util.List; +import lombok.Data; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * @author leiin + * @date 2022/1/26 6:33 下午 + */ +@NoArgsConstructor +@Data +public class WxCpKfMenuMsg { + + /** + * 参数:head_content + * 是否必须:否 + * 类型:string + * 说明:起始文本 不多于1024字节 + */ + @SerializedName("head_content") + private String headContent; + + private List list; + + /** + * 参数:tail_content + * 是否必须:否 + * 类型:string + * 说明:结束文本 不多于1024字节 + */ + @SerializedName("tail_content") + private String tailContent; + + @NoArgsConstructor + @Data + public static class WxCpKfMenuItem { + /** + * 参数:type + * 是否必须:是 + * 类型:string + * 说明:菜单类型。click-回复菜单 view-超链接菜单 miniprogram-小程序菜单 + */ + private String type; + + /** + * type为click的菜单项 + */ + private MenuClick click; + /** + * type为view的菜单项 + */ + private MenuView view; + /** + * type为miniprogram的菜单项 + */ + @SerializedName("miniprogram") + private MiniProgram miniProgram; + } + + @Getter + @Setter + public static class MenuClick { + + /** + *
+     *   是否必须:否
+     *   说明:菜单ID。不少于1字节 不多于128字节
+     * 
+ */ + private String id; + /** + *
+     *   是否必须:是
+     *   说明:菜单显示内容。不少于1字节 不多于128字节
+     * 
+ */ + private String content; + } + + @Getter + @Setter + public static class MenuView { + /** + *
+     *   是否必须:是
+     *   说明:点击后跳转的链接。不少于1字节 不多于2048字节
+     * 
+ */ + private String url; + /** + *
+     *   是否必须:是
+     *   说明:菜单显示内容。不少于1字节 不多于1024字节
+     * 
+ */ + private String content; + } + + @Getter + @Setter + public static class MiniProgram { + /** + *
+     *   是否必须:是
+     *   说明:小程序appid。
+     * 
+ */ + @SerializedName("appid") + private String appId; + /** + *
+     *   点击后进入的小程序页面。
+     * 
+ */ + @SerializedName("pagepath") + private String pagePath; + /** + *
+     *   菜单显示内容。不多于1024字节
+     * 
+ */ + private String content; + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/msg/WxCpKfMiniProgramMsg.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/msg/WxCpKfMiniProgramMsg.java new file mode 100644 index 000000000..4e5ce5f2d --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/msg/WxCpKfMiniProgramMsg.java @@ -0,0 +1,46 @@ +package me.chanjar.weixin.cp.bean.kf.msg; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author leiin + * @date 2022/1/26 6:22 下午 + */ +@NoArgsConstructor +@Data +public class WxCpKfMiniProgramMsg { + /** + * 参数:appid + * 是否必须:是 + * 类型:string + * 说明:小程序appid + */ + @SerializedName("appid") + private String appId; + /** + * 参数:title + * 是否必须:否 + * 类型:string + * 说明:小程序消息标题,最多64个字节,超过会自动截断 + */ + @SerializedName("title") + private String title; + /** + * 参数:thumb_media_id + * 是否必须:是 + * 类型:string + * 说明:小程序消息封面的mediaid,封面图建议尺寸为520*416 + */ + @SerializedName("thumb_media_id") + private String thumbMediaId; + /** + * 参数:pagepath + * 是否必须:是 + * 类型:string + * 说明:点击消息卡片后进入的小程序页面路径。注意路径要以.html为后缀,否则在微信中打开会提示找不到页面 + */ + @SerializedName("pagepath") + private String pagePath; +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/msg/WxCpKfResourceMsg.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/msg/WxCpKfResourceMsg.java new file mode 100644 index 000000000..43cba65b6 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/msg/WxCpKfResourceMsg.java @@ -0,0 +1,16 @@ +package me.chanjar.weixin.cp.bean.kf.msg; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author leiin + * @date 2022/1/26 5:31 下午 + */ +@NoArgsConstructor +@Data +public class WxCpKfResourceMsg { + @SerializedName("media_id") + private String mediaId; +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/msg/WxCpKfTextMsg.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/msg/WxCpKfTextMsg.java new file mode 100644 index 000000000..1e0ccf076 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/msg/WxCpKfTextMsg.java @@ -0,0 +1,26 @@ +package me.chanjar.weixin.cp.bean.kf.msg; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author leiin + * @date 2022/1/26 5:30 下午 + */ +@NoArgsConstructor +@Data +public class WxCpKfTextMsg { + + /** + *
+   *   参数:content
+   *   是否必须:是
+   *   类型:string
+   *   说明:消息内容,最长不超过2048个字节
+   * 
+ */ + private String content; + @SerializedName("menu_id") + private String menuId; +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/living/WxCpLivingCreateRequest.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/living/WxCpLivingCreateRequest.java new file mode 100644 index 000000000..6da6b81e5 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/living/WxCpLivingCreateRequest.java @@ -0,0 +1,86 @@ +package me.chanjar.weixin.cp.bean.living; + +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.io.Serializable; + +/** + * 创建预约直播请求. + * + * @author Wang_Wong + * @date 2021-12-23 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +public class WxCpLivingCreateRequest implements Serializable { + private static final long serialVersionUID = -4960239393895754138L; + + @SerializedName("anchor_userid") + private String anchorUserid; + + @SerializedName("theme") + private String theme; + + @SerializedName("living_start") + private Long livingStart; + + @SerializedName("living_duration") + private Long livingDuration; + + @SerializedName("remind_time") + private Long remindTime; + + @SerializedName("description") + private String description; + + @SerializedName("type") + private Integer type; + + @SerializedName("agentid") + private Integer agentId; + + @SerializedName("activity_cover_mediaid") + private String activityCoverMediaid; + + @SerializedName("activity_share_mediaid") + private String activityShareMediaid; + + @SerializedName("activity_detail") + private ActivityDetail activityDetail; + + public static class ActivityDetail implements Serializable { + + @SerializedName("image_list") + private String[] imageList; + + @SerializedName("description") + private String description; + + public static ActivityDetail fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, ActivityDetail.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + } + + public static WxCpLivingCreateRequest fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpLivingCreateRequest.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/living/WxCpLivingInfo.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/living/WxCpLivingInfo.java new file mode 100644 index 000000000..b7010e57e --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/living/WxCpLivingInfo.java @@ -0,0 +1,80 @@ +package me.chanjar.weixin.cp.bean.living; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.io.Serializable; + +/** + * 直播详情信息. + * + * @author Wang_Wong + */ +@Data +public class WxCpLivingInfo implements Serializable { + private static final long serialVersionUID = -5028321625140879571L; + + @SerializedName("theme") + private String theme; + + @SerializedName("living_start") + private Long livingStart; + + @SerializedName("living_duration") + private Long livingDurationme; + + @SerializedName("status") + private Integer status; + + @SerializedName("reserve_living_duration") + private Long reserveLivingDuration; + + @SerializedName("reserve_start") + private Long reserveStart; + + @SerializedName("description") + private String description; + + @SerializedName("anchor_userid") + private String anchorUserid; + + @SerializedName("main_department") + private Long mainDepartment; + + @SerializedName("viewer_num") + private Integer viewerNum; + + @SerializedName("comment_num") + private Integer commentNum; + + @SerializedName("mic_num") + private Integer micNum; + + @SerializedName("open_replay") + private Integer openReplay; + + @SerializedName("replay_status") + private Integer replayStatus; + + @SerializedName("type") + private Integer type; + + @SerializedName("push_stream_url") + private String pushStreamUrl; + + @SerializedName("online_count") + private Integer onlineCount; + + @SerializedName("subscribe_count") + private Integer subscribeCount; + + public static WxCpLivingInfo fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpLivingInfo.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/living/WxCpLivingModifyRequest.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/living/WxCpLivingModifyRequest.java new file mode 100644 index 000000000..00d193820 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/living/WxCpLivingModifyRequest.java @@ -0,0 +1,56 @@ +package me.chanjar.weixin.cp.bean.living; + +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.io.Serializable; + +/** + * 创建预约直播请求. + * + * @author Wang_Wong + * @date 2021-12-23 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +public class WxCpLivingModifyRequest implements Serializable { + private static final long serialVersionUID = -4960239393895754138L; + + @SerializedName("livingid") + private String livingId; + + @SerializedName("theme") + private String theme; + + @SerializedName("living_start") + private Long livingStart; + + @SerializedName("living_duration") + private Long livingDuration; + + @SerializedName("remind_time") + private Long remindTime; + + @SerializedName("description") + private String description; + + @SerializedName("type") + private Integer type; + + public static WxCpLivingModifyRequest fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpLivingModifyRequest.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/living/WxCpLivingResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/living/WxCpLivingResult.java new file mode 100644 index 000000000..3312eec77 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/living/WxCpLivingResult.java @@ -0,0 +1,55 @@ +package me.chanjar.weixin.cp.bean.living; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.io.Serializable; + +/** + * 直播返回对象. + * + * @author Wang_Wong + */ +@Data +public class WxCpLivingResult implements Serializable { + private static final long serialVersionUID = -5028321625140879571L; + + @SerializedName("errcode") + private Integer errcode; + + @SerializedName("errmsg") + private String errmsg; + + @Getter + @Setter + public static class LivingIdResult implements Serializable { + private static final long serialVersionUID = -5696099236344075582L; + + @SerializedName("next_cursor") + private String nextCursor; + + @SerializedName("livingid_list") + private String[] livingidList; + + public static LivingIdResult fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, LivingIdResult.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + } + + public static WxCpLivingResult fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpLivingResult.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/living/WxCpLivingShareInfo.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/living/WxCpLivingShareInfo.java new file mode 100644 index 000000000..f0b96cc96 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/living/WxCpLivingShareInfo.java @@ -0,0 +1,40 @@ +package me.chanjar.weixin.cp.bean.living; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.io.Serializable; + +/** + * 跳转小程序商城的直播观众信息. + * + * @author Wang_Wong + */ +@Data +public class WxCpLivingShareInfo implements Serializable { + private static final long serialVersionUID = -5028321625140879571L; + + private String livingid; + + @SerializedName("viewer_userid") + private String viewerUserid; + + @SerializedName("viewer_external_userid") + private String viewerExternalUserid; + + @SerializedName("invitor_userid") + private String invitorUserid; + + @SerializedName("invitor_external_userid") + private String invitorExternalUserid; + + public static WxCpLivingShareInfo fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpLivingShareInfo.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/living/WxCpWatchStat.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/living/WxCpWatchStat.java new file mode 100644 index 000000000..4a77bdd45 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/living/WxCpWatchStat.java @@ -0,0 +1,90 @@ +package me.chanjar.weixin.cp.bean.living; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.io.Serializable; +import java.util.List; + +/** + * 直播观看明细. + * + * @author Wang_Wong + */ +@Data +public class WxCpWatchStat implements Serializable { + private static final long serialVersionUID = -5028321625140879571L; + + private Integer ending; + + @SerializedName("next_key") + private String nextKey; + + @SerializedName("stat_info") + private StatInfo statInfo; + + @Getter + @Setter + public static class StatInfo implements Serializable { + private static final long serialVersionUID = -5696099236344075582L; + + @SerializedName("users") + private List users; + + @SerializedName("external_users") + private List externalUsers; + + } + + @Getter + @Setter + public static class User implements Serializable { + private static final long serialVersionUID = -5696099236344075582L; + + private String userid; + + @SerializedName("watch_time") + private Long watchTime; + + @SerializedName("is_comment") + private Integer isComment; + + @SerializedName("is_mic") + private Integer isMic; + + } + + @Getter + @Setter + public static class ExternalUser implements Serializable { + private static final long serialVersionUID = -5696099236344075582L; + + private String name; + private Integer type; + + @SerializedName("external_userid") + private String externalUserid; + + @SerializedName("watch_time") + private Long watchTime; + + @SerializedName("is_comment") + private Integer isComment; + + @SerializedName("is_mic") + private Integer isMic; + + } + + public static WxCpWatchStat fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpWatchStat.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpGroupRobotMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpGroupRobotMessage.java index de7e88d79..387b454cd 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpGroupRobotMessage.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpGroupRobotMessage.java @@ -25,7 +25,7 @@ @Data public class WxCpGroupRobotMessage implements Serializable { private static final long serialVersionUID = -4301684507150486556L; - + /** * 消息类型 */ diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpMessage.java index a2f6e6c5c..77bc0960a 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpMessage.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpMessage.java @@ -71,9 +71,7 @@ public class WxCpMessage implements Serializable { private String taskId; private List taskButtons = new ArrayList<>(); - /** - * 模板型卡片特有属性 - */ + // 模板型卡片特有属性 /** * 模板卡片类型,文本通知型卡片填写 “text_notice”, * 图文展示型卡片此处填写 “news_notice”, @@ -81,62 +79,62 @@ public class WxCpMessage implements Serializable { * 投票选择型卡片填写”vote_interaction”, * 多项选择型卡片填写 “multiple_interaction” */ - private String card_type; + private String cardType; /** * 卡片来源样式信息,不需要来源样式可不填写 * 来源图片的url */ - private String source_icon_url; + private String sourceIconUrl; /** * 卡片来源样式信息,不需要来源样式可不填写 * 来源图片的描述,建议不超过20个字 */ - private String source_desc; + private String sourceDesc; /** * 一级标题,建议不超过36个字 */ - private String main_title_title; + private String mainTitleTitle; /** * 标题辅助信息,建议不超过44个字 */ - private String main_title_desc; + private String mainTitleDesc; /** * 图文展示型的卡片必须有图片字段。 * 图片的url. */ - private String card_image_url; + private String cardImageUrl; /** * 图片的宽高比,宽高比要小于2.25,大于1.3,不填该参数默认1.3 */ - private Float card_image_aspect_ratio; + private Float cardImageAspectRatio; /** * 关键数据样式 * 关键数据样式的数据内容,建议不超过14个字 */ - private String emphasis_content_title; + private String emphasisContentTitle; /** * 关键数据样式的数据描述内容,建议不超过22个字 */ - private String emphasis_content_desc; + private String emphasisContentDesc; /** * 二级普通文本,建议不超过160个字 */ - private String sub_title_text; + private String subTitleText; /** * 卡片二级垂直内容,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过4 */ - private List vertical_contents; + private List verticalContents; /** * 二级标题+文本列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过6 */ - private List horizontal_contents; + private List horizontalContents; /** * 跳转指引样式的列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过3 @@ -147,21 +145,21 @@ public class WxCpMessage implements Serializable { * 整体卡片的点击跳转事件,text_notice必填本字段 * 跳转事件类型,1 代表跳转url,2 代表打开小程序。text_notice卡片模版中该字段取值范围为[1,2] */ - private Integer card_action_type; + private Integer cardActionType; /** * 跳转事件的url,card_action.type是1时必填 */ - private String card_action_url; + private String cardActionUrl; /** * 跳转事件的小程序的appid,必须是与当前应用关联的小程序,card_action.type是2时必填 */ - private String card_action_appid; + private String cardActionAppid; /** * 跳转事件的小程序的pagepath,card_action.type是2时选填 */ - private String card_action_pagepath; + private String cardActionPagepath; /** * 按钮交互型卡片需指定。 @@ -173,12 +171,12 @@ public class WxCpMessage implements Serializable { * 投票选择型卡片需要指定 * 选择题key值,用户提交选项后,会产生回调事件,回调事件会带上该key值表示该题,最长支持1024字节 */ - private String checkbox_question_key; + private String checkboxQuestionKey; /** * 选择题模式,单选:0,多选:1,不填默认0 */ - private Integer checkbox_mode; + private Integer checkboxMode; /** * 选项list,选项个数不超过 20 个,最少1个 @@ -189,16 +187,21 @@ public class WxCpMessage implements Serializable { * 提交按钮样式 * 按钮文案,建议不超过10个字,不填默认为提交 */ - private String submit_button_text; + private String submitButtonText; /** * 提交按钮的key,会产生回调事件将本参数作为EventKey返回,最长支持1024字节 */ - private String submit_button_key; + private String submitButtonKey; /** * 下拉式的选择器列表,multiple_interaction类型的卡片该字段不可为空,一个消息最多支持 3 个选择器 */ private List selects; + /** + * 引用文献样式 + */ + private QuoteArea quoteArea; + /** * 获得文本消息builder. */ @@ -467,64 +470,75 @@ private void handleMsgType(JsonObject messageJson) { } case TEMPLATE_CARD: { JsonObject template = new JsonObject(); - template.addProperty("card_type", this.getCard_type()); + template.addProperty("card_type", this.getCardType()); - if (StringUtils.isNotBlank(this.getSource_icon_url()) || StringUtils.isNotBlank(this.getSource_desc())) { + if (StringUtils.isNotBlank(this.getSourceIconUrl()) || StringUtils.isNotBlank(this.getSourceDesc())) { JsonObject source = new JsonObject(); - if (StringUtils.isNotBlank(this.getSource_icon_url())) { - source.addProperty("icon_url", this.getSource_icon_url()); + if (StringUtils.isNotBlank(this.getSourceIconUrl())) { + source.addProperty("icon_url", this.getSourceIconUrl()); } - if (StringUtils.isNotBlank(this.getSource_desc())) { - source.addProperty("desc", this.getSource_desc()); + if (StringUtils.isNotBlank(this.getSourceDesc())) { + source.addProperty("desc", this.getSourceDesc()); } template.add("source", source); } - if (StringUtils.isNotBlank(this.getMain_title_title()) || StringUtils.isNotBlank(this.getMain_title_desc())) { - JsonObject main_title = new JsonObject(); - if (StringUtils.isNotBlank(this.getMain_title_title())) { - main_title.addProperty("title", this.getMain_title_title()); + if (StringUtils.isNotBlank(this.getMainTitleTitle()) || StringUtils.isNotBlank(this.getMainTitleDesc())) { + JsonObject mainTitle = new JsonObject(); + if (StringUtils.isNotBlank(this.getMainTitleTitle())) { + mainTitle.addProperty("title", this.getMainTitleTitle()); } - if (StringUtils.isNotBlank(this.getMain_title_desc())) { - main_title.addProperty("desc", this.getMain_title_desc()); + if (StringUtils.isNotBlank(this.getMainTitleDesc())) { + mainTitle.addProperty("desc", this.getMainTitleDesc()); } - template.add("main_title", main_title); + template.add("main_title", mainTitle); } - if (StringUtils.isNotBlank(this.getEmphasis_content_title()) || StringUtils.isNotBlank(this.getEmphasis_content_desc())) { - JsonObject emphasis_content = new JsonObject(); - if (StringUtils.isNotBlank(this.getEmphasis_content_title())) { - emphasis_content.addProperty("title", this.getEmphasis_content_title()); + if (StringUtils.isNotBlank(this.getCardImageUrl()) || this.getCardImageAspectRatio() != null) { + JsonObject cardImage = new JsonObject(); + if (StringUtils.isNotBlank(this.getCardImageUrl())) { + cardImage.addProperty("url", this.getCardImageUrl()); } - if (StringUtils.isNotBlank(this.getEmphasis_content_desc())) { - emphasis_content.addProperty("desc", this.getEmphasis_content_desc()); + if (null != this.getCardImageAspectRatio()) { + cardImage.addProperty("aspect_ratio", this.getCardImageAspectRatio()); } - template.add("emphasis_content", emphasis_content); + template.add("card_image", cardImage); + } + + if (StringUtils.isNotBlank(this.getEmphasisContentTitle()) || StringUtils.isNotBlank(this.getEmphasisContentDesc())) { + JsonObject emphasisContent = new JsonObject(); + if (StringUtils.isNotBlank(this.getEmphasisContentTitle())) { + emphasisContent.addProperty("title", this.getEmphasisContentTitle()); + } + if (StringUtils.isNotBlank(this.getEmphasisContentDesc())) { + emphasisContent.addProperty("desc", this.getEmphasisContentDesc()); + } + template.add("emphasis_content", emphasisContent); } - if (StringUtils.isNotBlank(this.getSub_title_text())) { - template.addProperty("sub_title_text", this.getSub_title_text()); + if (StringUtils.isNotBlank(this.getSubTitleText())) { + template.addProperty("sub_title_text", this.getSubTitleText()); } if (StringUtils.isNotBlank(this.getTaskId())) { template.addProperty("task_id", this.getTaskId()); } - List verticalContents = this.getVertical_contents(); - if(null != verticalContents && verticalContents.size() > 0) { + List verticalContents = this.getVerticalContents(); + if (null != verticalContents && !verticalContents.isEmpty()) { JsonArray vContentJsonArray = new JsonArray(); - for (VerticalContent vContent : this.getVertical_contents()) { + for (VerticalContent vContent : this.getVerticalContents()) { JsonObject tempObject = vContent.toJson(); vContentJsonArray.add(tempObject); } template.add("vertical_content_list", vContentJsonArray); } - List horizontalContents = this.getHorizontal_contents(); - if(null != horizontalContents && horizontalContents.size() > 0) { + List horizontalContents = this.getHorizontalContents(); + if (null != horizontalContents && !horizontalContents.isEmpty()) { JsonArray hContentJsonArray = new JsonArray(); - for (HorizontalContent hContent : this.getHorizontal_contents()) { + for (HorizontalContent hContent : this.getHorizontalContents()) { JsonObject tempObject = hContent.toJson(); hContentJsonArray.add(tempObject); } @@ -532,7 +546,7 @@ private void handleMsgType(JsonObject messageJson) { } List jumps = this.getJumps(); - if(null != jumps && jumps.size() > 0) { + if (null != jumps && !jumps.isEmpty()) { JsonArray jumpJsonArray = new JsonArray(); for (TemplateCardJump jump : this.getJumps()) { JsonObject tempObject = jump.toJson(); @@ -541,23 +555,23 @@ private void handleMsgType(JsonObject messageJson) { template.add("jump_list", jumpJsonArray); } - if (null != this.getCard_action_type()) { + if (null != this.getCardActionType()) { JsonObject cardAction = new JsonObject(); - cardAction.addProperty("type", this.getCard_action_type()); - if (StringUtils.isNotBlank(this.getCard_action_url())) { - cardAction.addProperty("url", this.getCard_action_url()); + cardAction.addProperty("type", this.getCardActionType()); + if (StringUtils.isNotBlank(this.getCardActionUrl())) { + cardAction.addProperty("url", this.getCardActionUrl()); } - if (StringUtils.isNotBlank(this.getCard_action_appid())) { - cardAction.addProperty("appid", this.getCard_action_appid()); + if (StringUtils.isNotBlank(this.getCardActionAppid())) { + cardAction.addProperty("appid", this.getCardActionAppid()); } - if (StringUtils.isNotBlank(this.getCard_action_pagepath())) { - cardAction.addProperty("pagepath", this.getCard_action_pagepath()); + if (StringUtils.isNotBlank(this.getCardActionPagepath())) { + cardAction.addProperty("pagepath", this.getCardActionPagepath()); } template.add("card_action", cardAction); } List buttons = this.getButtons(); - if(null != buttons && buttons.size() > 0) { + if (null != buttons && !buttons.isEmpty()) { JsonArray btnJsonArray = new JsonArray(); for (TemplateCardButton btn : this.getButtons()) { JsonObject tempObject = btn.toJson(); @@ -567,11 +581,11 @@ private void handleMsgType(JsonObject messageJson) { } // checkbox - if (StringUtils.isNotBlank(this.getCheckbox_question_key())) { + if (StringUtils.isNotBlank(this.getCheckboxQuestionKey())) { JsonObject checkBox = new JsonObject(); - checkBox.addProperty("question_key", this.getCheckbox_question_key()); - if (null != this.getCheckbox_mode()) { - checkBox.addProperty("mode", this.getCheckbox_mode()); + checkBox.addProperty("question_key", this.getCheckboxQuestionKey()); + if (null != this.getCheckboxMode()) { + checkBox.addProperty("mode", this.getCheckboxMode()); } JsonArray optionArray = new JsonArray(); for (CheckboxOption option : this.getOptions()) { @@ -584,20 +598,20 @@ private void handleMsgType(JsonObject messageJson) { } // submit_button - if (StringUtils.isNotBlank(this.getSubmit_button_text()) || StringUtils.isNotBlank(this.getSubmit_button_key())) { + if (StringUtils.isNotBlank(this.getSubmitButtonText()) || StringUtils.isNotBlank(this.getSubmitButtonKey())) { JsonObject submit_button = new JsonObject(); - if (StringUtils.isNotBlank(this.getSubmit_button_text())) { - submit_button.addProperty("text", this.getSubmit_button_text()); + if (StringUtils.isNotBlank(this.getSubmitButtonText())) { + submit_button.addProperty("text", this.getSubmitButtonText()); } - if (StringUtils.isNotBlank(this.getSubmit_button_key())) { - submit_button.addProperty("key", this.getSubmit_button_key()); + if (StringUtils.isNotBlank(this.getSubmitButtonKey())) { + submit_button.addProperty("key", this.getSubmitButtonKey()); } template.add("submit_button", submit_button); } // select_list List selects = this.getSelects(); - if(null != selects && selects.size() > 0) { + if (null != selects && !selects.isEmpty()) { JsonArray selectJsonArray = new JsonArray(); for (MultipleSelect select : this.getSelects()) { JsonObject tempObject = select.toJson(); @@ -606,6 +620,12 @@ private void handleMsgType(JsonObject messageJson) { template.add("select_list", selectJsonArray); } + QuoteArea quoteArea = this.getQuoteArea(); + if (null != quoteArea) { + JsonObject quoteAreaJson = quoteArea.toJson(); + template.add("quote_area", quoteAreaJson); + } + messageJson.add("template_card", template); break; } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpMessageSendResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpMessageSendResult.java index 4c41d631b..6b02941dd 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpMessageSendResult.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpMessageSendResult.java @@ -1,15 +1,14 @@ package me.chanjar.weixin.cp.bean.message; -import java.io.Serializable; -import java.util.Collections; -import java.util.List; - -import org.apache.commons.lang3.StringUtils; - import com.google.common.base.Splitter; import com.google.gson.annotations.SerializedName; import lombok.Data; import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; +import org.apache.commons.lang3.StringUtils; + +import java.io.Serializable; +import java.util.Collections; +import java.util.List; /** * 消息发送结果对象类. @@ -44,7 +43,7 @@ public static WxCpMessageSendResult fromJson(String json) { @SerializedName("invalidtag") private String invalidTag; - + @SerializedName("msgid") private String msgId; diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpMessageSendStatistics.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpMessageSendStatistics.java index 7cef0564d..be652c50b 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpMessageSendStatistics.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpMessageSendStatistics.java @@ -24,9 +24,9 @@ public static WxCpMessageSendStatistics fromJson(String json) { private List statistics; @Data - public static class StatisticItem implements Serializable { + public static class StatisticItem implements Serializable { private static final long serialVersionUID = 6031833682211475786L; - + /** * 应用名 */ diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpTpXmlMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpTpXmlMessage.java index fc159a9a3..579673594 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpTpXmlMessage.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpTpXmlMessage.java @@ -1,9 +1,5 @@ package me.chanjar.weixin.cp.bean.message; -import java.io.Serializable; -import java.util.List; -import java.util.Map; - import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.annotations.XStreamConverter; import com.thoughtworks.xstream.converters.basic.IntConverter; @@ -15,6 +11,10 @@ import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; import me.chanjar.weixin.cp.util.xml.XStreamTransformer; +import java.io.Serializable; +import java.util.List; +import java.util.Map; + /** * 回调推送的message * https://work.weixin.qq.com/api/doc#90001/90143/90612 @@ -449,7 +449,7 @@ public static class NotifyNode implements Serializable { @XStreamAlias("ItemUserId") protected Integer itemUserId; @XStreamAlias("ItemImage") - protected String itemImage; + protected String itemImage; } } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlMessage.java index b791fffb5..40d72c3a5 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlMessage.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlMessage.java @@ -16,6 +16,7 @@ import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; import me.chanjar.weixin.cp.util.xml.XStreamTransformer; import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; import java.io.IOException; import java.io.InputStream; @@ -51,7 +52,7 @@ public class WxCpXmlMessage implements Serializable { /////////////////////// @XStreamAlias("AgentID") - private Integer agentId; + private String agentId; @XStreamAlias("ToUserName") @XStreamConverter(value = XStreamCDataConverter.class) @@ -138,6 +139,29 @@ public class WxCpXmlMessage implements Serializable { @XStreamConverter(value = XStreamCDataConverter.class) private String event; + @XStreamAlias("UpdateDetail") + @XStreamConverter(value = XStreamCDataConverter.class) + private String updateDetail; + + @XStreamAlias("JoinScene") + @XStreamConverter(value = XStreamCDataConverter.class) + private String joinScene; + + @XStreamAlias("QuitScene") + @XStreamConverter(value = XStreamCDataConverter.class) + private String quitScene; + + @XStreamAlias("MemChangeCnt") + @XStreamConverter(value = XStreamCDataConverter.class) + private String memChangeCnt; + + @XStreamAlias("Source") + @XStreamConverter(value = XStreamCDataConverter.class) + private String source; + + @XStreamAlias("StrategyId") + private String strategyId; + @XStreamAlias("EventKey") @XStreamConverter(value = XStreamCDataConverter.class) private String eventKey; @@ -220,6 +244,12 @@ public class WxCpXmlMessage implements Serializable { @XStreamConverter(value = LongArrayConverter.class) private Long[] departments; + /** + * 主部门 + */ + @XStreamAlias("MainDepartment") + private Long mainDepartment; + /** * 手机号码. */ @@ -447,6 +477,14 @@ protected static WxCpXmlMessage fromXml(String xml) { return xmlMessage; } + public static WxCpXmlMessage fromXml(String xml, String agentId) { + //修改微信变态的消息内容格式,方便解析 + xml = xml.replace("", ""); + final WxCpXmlMessage xmlMessage = fromXml(xml); + xmlMessage.setAgentId(agentId); + return xmlMessage; + } + protected static WxCpXmlMessage fromXml(InputStream is) { return XStreamTransformer.fromXml(WxCpXmlMessage.class, is); } @@ -457,9 +495,15 @@ protected static WxCpXmlMessage fromXml(InputStream is) { public static WxCpXmlMessage fromEncryptedXml(String encryptedXml, WxCpConfigStorage wxCpConfigStorage, String timestamp, String nonce, String msgSignature) { WxCpCryptUtil cryptUtil = new WxCpCryptUtil(wxCpConfigStorage); + WxCpXmlMessage wxCpXmlMessage = fromXml(encryptedXml); String plainText = cryptUtil.decrypt(msgSignature, timestamp, nonce, encryptedXml); log.debug("解密后的原始xml消息内容:{}", plainText); - return fromXml(plainText); + if (StringUtils.isNotEmpty(wxCpXmlMessage.getAgentId())) { + return fromXml(plainText, wxCpXmlMessage.getAgentId()); + } else { + return fromXml(plainText); + } + } public static WxCpXmlMessage fromEncryptedXml(InputStream is, WxCpConfigStorage wxCpConfigStorage, @@ -533,7 +577,7 @@ public static class SendPicsInfo implements Serializable { @Data public static class Item implements Serializable { private static final long serialVersionUID = -6549728838848064881L; - + @XStreamAlias("PicMd5Sum") @XStreamConverter(value = XStreamCDataConverter.class) private String picMd5Sum; @@ -615,20 +659,20 @@ public static class ApprovalInfo implements Serializable { /** * 审批流程信息,可能有多个审批节点。 */ - @XStreamImplicit(itemFieldName="SpRecord") + @XStreamImplicit(itemFieldName = "SpRecord") private List spRecords; /** * 抄送信息,可能有多个抄送节点 * 这回查字典,notifier通知人,Notifyer这不知道是什么 */ - @XStreamImplicit(itemFieldName="Notifyer") + @XStreamImplicit(itemFieldName = "Notifyer") private List notifier; /** * 审批申请备注信息,可能有多个备注节点 */ - @XStreamImplicit(itemFieldName="Comments") + @XStreamImplicit(itemFieldName = "Comments") private List comments; /** @@ -663,7 +707,7 @@ public static class Applier implements Serializable { */ @XStreamAlias("SpRecord") @Data - public static class SpRecord implements Serializable{ + public static class SpRecord implements Serializable { private static final long serialVersionUID = 1247535623941881764L; @@ -682,7 +726,7 @@ public static class SpRecord implements Serializable{ /** * 审批节点详情。当节点为标签或上级时,一个节点可能有多个分支 */ - @XStreamImplicit(itemFieldName="Details") + @XStreamImplicit(itemFieldName = "Details") private List details; } @@ -692,8 +736,7 @@ public static class SpRecord implements Serializable{ */ @XStreamAlias("Details") @Data - public static class Detail implements Serializable{ - + public static class Detail implements Serializable { private static final long serialVersionUID = -8446107461495047603L; /** @@ -722,8 +765,10 @@ public static class Detail implements Serializable{ /** * 节点分支审批人审批意见附件,赋值为media_id具体使用请参考:文档-获取临时素材 + * TODO 居然可以返回多个,坑爹的,暂时屏蔽注解以免报错,有兴趣挑战的,尽管把代码砸过来吧! + * 请先通过allFieldsMap解析需要的参数! */ - @XStreamAlias("Attach") + // @XStreamAlias("Attach") private String attach; } @@ -732,7 +777,7 @@ public static class Detail implements Serializable{ */ @Data @XStreamAlias("Approver") - public static class Approver implements Serializable{ + public static class Approver implements Serializable { private static final long serialVersionUID = 7360442444186683191L; @@ -748,7 +793,7 @@ public static class Approver implements Serializable{ */ @Data @XStreamAlias("Notifyer") - public static class Notifier implements Serializable{ + public static class Notifier implements Serializable { private static final long serialVersionUID = -4524071522890013920L; @@ -764,7 +809,7 @@ public static class Notifier implements Serializable{ */ @Data @XStreamAlias("Comments") - public static class Comment implements Serializable{ + public static class Comment implements Serializable { private static final long serialVersionUID = 6912156206252719485L; @@ -796,7 +841,7 @@ public static class Comment implements Serializable{ @Data @XStreamAlias("CommentUserInfo") - private static class CommentUserInfo implements Serializable{ + private static class CommentUserInfo implements Serializable { private static final long serialVersionUID = 5031739716823000947L; diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlOutEventMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlOutEventMessage.java new file mode 100644 index 000000000..430e63a3a --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlOutEventMessage.java @@ -0,0 +1,83 @@ +package me.chanjar.weixin.cp.bean.message; + +import com.thoughtworks.xstream.annotations.XStreamAlias; +import com.thoughtworks.xstream.annotations.XStreamConverter; +import lombok.Data; +import lombok.EqualsAndHashCode; +import me.chanjar.weixin.common.api.WxConsts; +import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; + +/** + * @author eYoung + * @description: + * @date create at 2021/12/3 16:36 + */ +@XStreamAlias("xml") +@Data +@EqualsAndHashCode(callSuper = false) +public class WxCpXmlOutEventMessage extends WxCpXmlOutMessage { + + @XStreamAlias("Event") + @XStreamConverter(value = XStreamCDataConverter.class) + private String event; + + @XStreamAlias("ChatId") + @XStreamConverter(value = XStreamCDataConverter.class) + private String chatId; + + @XStreamAlias("ChangeType") + @XStreamConverter(value = XStreamCDataConverter.class) + private String changeType; + + @XStreamAlias("UpdateDetail") + @XStreamConverter(value = XStreamCDataConverter.class) + private String updateDetail; + + @XStreamAlias("JoinScene") + private String joinScene; + + @XStreamAlias("QuitScene") + private String quitScene; + + @XStreamAlias("MemChangeCnt") + private String memChangeCnt; + + @XStreamAlias("TagType") + @XStreamConverter(value = XStreamCDataConverter.class) + private String tagType; + + @XStreamAlias("StrategyId") + private String strategyId; + + @XStreamAlias("UserID") + @XStreamConverter(value = XStreamCDataConverter.class) + private String userID; + + @XStreamAlias("ExternalUserID") + @XStreamConverter(value = XStreamCDataConverter.class) + private String externalUserID; + + @XStreamAlias("State") + @XStreamConverter(value = XStreamCDataConverter.class) + private String state; + + @XStreamAlias("WelcomeCode") + @XStreamConverter(value = XStreamCDataConverter.class) + private String welcomeCode; + + @XStreamAlias("Source") + @XStreamConverter(value = XStreamCDataConverter.class) + private String source; + + @XStreamAlias("FailReason") + @XStreamConverter(value = XStreamCDataConverter.class) + private String failReason; + + @XStreamAlias("Id") + @XStreamConverter(value = XStreamCDataConverter.class) + private String id; + + public WxCpXmlOutEventMessage() { + this.msgType = WxConsts.XmlMsgType.EVENT; + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlOutMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlOutMessage.java index 70882561b..89c29e25c 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlOutMessage.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlOutMessage.java @@ -1,7 +1,5 @@ package me.chanjar.weixin.cp.bean.message; -import java.io.Serializable; - import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.annotations.XStreamConverter; import lombok.Data; @@ -11,6 +9,8 @@ import me.chanjar.weixin.cp.util.crypto.WxCpCryptUtil; import me.chanjar.weixin.cp.util.xml.XStreamTransformer; +import java.io.Serializable; + /** * 被动回复消息. * https://work.weixin.qq.com/api/doc#12975 @@ -86,6 +86,13 @@ public static UpdateButtonBuilder UPDATE_BUTTON() { return new UpdateButtonBuilder(); } + /** + * 获得事件消息builder. + */ + public static EventBuilder EVENT() { + return new EventBuilder(); + } + protected String toXml() { return XStreamTransformer.toXml((Class) this.getClass(), this); } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlOutUpdateBtnMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlOutUpdateBtnMessage.java index b0428469f..9e7222901 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlOutUpdateBtnMessage.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlOutUpdateBtnMessage.java @@ -5,12 +5,8 @@ import lombok.Data; import lombok.EqualsAndHashCode; import me.chanjar.weixin.common.api.WxConsts; -import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; -import me.chanjar.weixin.common.util.xml.XStreamMediaIdConverter; import me.chanjar.weixin.common.util.xml.XStreamReplaceNameConverter; -import java.io.Serializable; - /** * @author nickname263 * @date 2021-09-23 @@ -18,7 +14,7 @@ @XStreamAlias("xml") @Data @EqualsAndHashCode(callSuper = false) -public class WxCpXmlOutUpdateBtnMessage extends WxCpXmlOutMessage { +public class WxCpXmlOutUpdateBtnMessage extends WxCpXmlOutMessage { private static final long serialVersionUID = 976182367423048138L; @XStreamAlias("Button") @XStreamConverter(value = XStreamReplaceNameConverter.class) @@ -29,5 +25,4 @@ public WxCpXmlOutUpdateBtnMessage() { } - } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlOutVideoMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlOutVideoMessage.java index 031dc02cb..add435a87 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlOutVideoMessage.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlOutVideoMessage.java @@ -1,7 +1,5 @@ package me.chanjar.weixin.cp.bean.message; -import java.io.Serializable; - import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.annotations.XStreamConverter; import lombok.Data; @@ -9,6 +7,8 @@ import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; +import java.io.Serializable; + @XStreamAlias("xml") @Data @EqualsAndHashCode(callSuper = false) diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/MpnewsBuilder.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/MpnewsBuilder.java index bc1467e14..1d2108900 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/MpnewsBuilder.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/MpnewsBuilder.java @@ -1,8 +1,8 @@ package me.chanjar.weixin.cp.bean.messagebuilder; import me.chanjar.weixin.common.api.WxConsts; -import me.chanjar.weixin.cp.bean.message.WxCpMessage; import me.chanjar.weixin.cp.bean.article.MpnewsArticle; +import me.chanjar.weixin.cp.bean.message.WxCpMessage; import java.util.ArrayList; import java.util.Collections; diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/NewsBuilder.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/NewsBuilder.java index ef661e6ed..4d12a51ce 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/NewsBuilder.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/NewsBuilder.java @@ -1,8 +1,8 @@ package me.chanjar.weixin.cp.bean.messagebuilder; import me.chanjar.weixin.common.api.WxConsts; -import me.chanjar.weixin.cp.bean.message.WxCpMessage; import me.chanjar.weixin.cp.bean.article.NewArticle; +import me.chanjar.weixin.cp.bean.message.WxCpMessage; import java.util.ArrayList; import java.util.Collections; diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/TemplateCardBuilder.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/TemplateCardBuilder.java index 7a29491ab..09a506d8e 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/TemplateCardBuilder.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/TemplateCardBuilder.java @@ -5,16 +5,17 @@ import me.chanjar.weixin.cp.bean.templatecard.*; import java.util.List; + /** *
  * 模板卡片消息Builder
  * 用法: WxCustomMessage m = WxCustomMessage.TEMPLATECARD().title(...)....toUser(...).build();
  * 
* - * @author yzts + * @author yzts * @date 2019-05-16 */ -public class TemplateCardBuilder extends BaseBuilder{ +public class TemplateCardBuilder extends BaseBuilder { /** * 模板卡片类型,文本通知型卡片填写 “text_notice”, * 图文展示型卡片此处填写 “news_notice”, @@ -22,62 +23,62 @@ public class TemplateCardBuilder extends BaseBuilder{ * 投票选择型卡片填写”vote_interaction”, * 多项选择型卡片填写 “multiple_interaction” */ - private String card_type; + private String cardType; /** * 卡片来源样式信息,不需要来源样式可不填写 * 来源图片的url */ - private String source_icon_url; + private String sourceIconUrl; /** * 卡片来源样式信息,不需要来源样式可不填写 * 来源图片的描述,建议不超过20个字 */ - private String source_desc; + private String sourceDesc; /** * 一级标题,建议不超过36个字 */ - private String main_title_title; + private String mainTitleTitle; /** * 标题辅助信息,建议不超过44个字 */ - private String main_title_desc; + private String mainTitleDesc; /** * 图文展示型的卡片必须有图片字段。 * 图片的url. */ - private String card_image_url; + private String cardImageUrl; /** * 图片的宽高比,宽高比要小于2.25,大于1.3,不填该参数默认1.3 */ - private Float card_image_aspect_ratio; + private Float cardImageAspectRatio; /** * 关键数据样式 * 关键数据样式的数据内容,建议不超过14个字 */ - private String emphasis_content_title; + private String emphasisContentTitle; /** * 关键数据样式的数据描述内容,建议不超过22个字 */ - private String emphasis_content_desc; + private String emphasisContentDesc; /** * 二级普通文本,建议不超过160个字 */ - private String sub_title_text; + private String subTitleText; /** * 卡片二级垂直内容,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过4 */ - private List vertical_contents; + private List verticalContents; /** * 二级标题+文本列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过6 */ - private List horizontal_contents; + private List horizontalContents; /** * 跳转指引样式的列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过3 @@ -88,26 +89,26 @@ public class TemplateCardBuilder extends BaseBuilder{ * 整体卡片的点击跳转事件,text_notice必填本字段 * 跳转事件类型,1 代表跳转url,2 代表打开小程序。text_notice卡片模版中该字段取值范围为[1,2] */ - private Integer card_action_type; + private Integer cardActionType; /** * 跳转事件的url,card_action.type是1时必填 */ - private String card_action_url; + private String cardActionUrl; /** * 跳转事件的小程序的appid,必须是与当前应用关联的小程序,card_action.type是2时必填 */ - private String card_action_appid; + private String cardActionAppid; /** * 跳转事件的小程序的pagepath,card_action.type是2时选填 */ - private String card_action_pagepath; + private String cardActionPagepath; /** * 任务id,同一个应用任务id不能重复,只能由数字、字母和“_-@”组成,最长128字节 */ - private String task_id; + private String taskId; /** * 按钮交互型卡片需指定。 @@ -119,12 +120,12 @@ public class TemplateCardBuilder extends BaseBuilder{ * 投票选择型卡片需要指定 * 选择题key值,用户提交选项后,会产生回调事件,回调事件会带上该key值表示该题,最长支持1024字节 */ - private String checkbox_question_key; + private String checkboxQuestionKey; /** * 选择题模式,单选:0,多选:1,不填默认0 */ - private Integer checkbox_mode; + private Integer checkboxMode; /** * 选项list,选项个数不超过 20 个,最少1个 @@ -135,69 +136,84 @@ public class TemplateCardBuilder extends BaseBuilder{ * 提交按钮样式 * 按钮文案,建议不超过10个字,不填默认为提交 */ - private String submit_button_text; + private String submitButtonText; /** * 提交按钮的key,会产生回调事件将本参数作为EventKey返回,最长支持1024字节 */ - private String submit_button_key; + private String submitButtonKey; /** * 下拉式的选择器列表,multiple_interaction类型的卡片该字段不可为空,一个消息最多支持 3 个选择器 */ private List selects; + /** + * 引用文献样式 + */ + private QuoteArea quoteArea; + public TemplateCardBuilder() { this.msgType = WxConsts.KefuMsgType.TEMPLATE_CARD; } - public TemplateCardBuilder card_type(String card_type) { - this.card_type = card_type; + public TemplateCardBuilder cardType(String cardType) { + this.cardType = cardType; + return this; + } + + public TemplateCardBuilder cardImageUrl(String cardImageUrl) { + this.cardImageUrl = cardImageUrl; return this; } - public TemplateCardBuilder source_icon_url(String source_icon_url) { - this.source_icon_url = source_icon_url; + public TemplateCardBuilder cardImageAspectRatio(Float cardImageAspectRatio) { + this.cardImageAspectRatio = cardImageAspectRatio; return this; } - public TemplateCardBuilder source_desc(String source_desc) { - this.source_desc = source_desc; + public TemplateCardBuilder sourceIconUrl(String sourceIconUrl) { + this.sourceIconUrl = sourceIconUrl; return this; } - public TemplateCardBuilder main_title_title(String main_title_title) { - this.main_title_title = main_title_title; + public TemplateCardBuilder sourceDesc(String sourceDesc) { + this.sourceDesc = sourceDesc; return this; } - public TemplateCardBuilder main_title_desc(String mainTitleDesc) { - this.main_title_desc = mainTitleDesc; + public TemplateCardBuilder mainTitleTitle(String mainTitleTitle) { + this.mainTitleTitle = mainTitleTitle; return this; } - public TemplateCardBuilder emphasis_content_title(String emphasis_content_title) { - this.emphasis_content_title = emphasis_content_title; + public TemplateCardBuilder mainTitleDesc(String mainTitleDesc) { + this.mainTitleDesc = mainTitleDesc; return this; } - public TemplateCardBuilder emphasis_content_desc(String emphasis_content_desc) { - this.emphasis_content_desc = emphasis_content_desc; + public TemplateCardBuilder emphasisContentTitle(String emphasisContentTitle) { + this.emphasisContentTitle = emphasisContentTitle; return this; } - public TemplateCardBuilder sub_title_text(String sub_title_text) { - this.sub_title_text = sub_title_text; + public TemplateCardBuilder emphasisContentDesc(String emphasisContentDesc) { + this.emphasisContentDesc = emphasisContentDesc; return this; } - public TemplateCardBuilder vertical_contents(List vertical_contents) { - this.vertical_contents = vertical_contents; + public TemplateCardBuilder subTitleText(String subTitleText) { + this.subTitleText = subTitleText; return this; } - public TemplateCardBuilder horizontal_contents(List horizontal_contents) { - this.horizontal_contents = horizontal_contents; + public TemplateCardBuilder verticalContents(List verticalContents) { + this.verticalContents = verticalContents; + return this; + } + + public TemplateCardBuilder horizontalContents(List horizontalContents) { + this.horizontalContents = horizontalContents; return this; } @@ -206,28 +222,28 @@ public TemplateCardBuilder jumps(List jumps) { return this; } - public TemplateCardBuilder card_action_type(Integer card_action_type) { - this.card_action_type = card_action_type; + public TemplateCardBuilder cardActionType(Integer cardActionType) { + this.cardActionType = cardActionType; return this; } - public TemplateCardBuilder card_action_url(String card_action_url) { - this.card_action_url = card_action_url; + public TemplateCardBuilder cardActionUrl(String cardActionUrl) { + this.cardActionUrl = cardActionUrl; return this; } - public TemplateCardBuilder card_action_appid(String card_action_appid) { - this.card_action_appid = card_action_appid; + public TemplateCardBuilder cardActionAppid(String cardActionAppid) { + this.cardActionAppid = cardActionAppid; return this; } - public TemplateCardBuilder card_action_pagepath(String card_action_pagepath) { - this.card_action_pagepath = card_action_pagepath; + public TemplateCardBuilder cardActionPagepath(String cardActionPagepath) { + this.cardActionPagepath = cardActionPagepath; return this; } - public TemplateCardBuilder task_id(String taskId) { - this.task_id = taskId; + public TemplateCardBuilder taskId(String taskId) { + this.taskId = taskId; return this; } @@ -236,13 +252,13 @@ public TemplateCardBuilder buttons(List buttons) { return this; } - public TemplateCardBuilder checkbox_question_key(String checkbox_question_key) { - this.checkbox_question_key = checkbox_question_key; + public TemplateCardBuilder checkboxQuestionKey(String checkboxQuestionKey) { + this.checkboxQuestionKey = checkboxQuestionKey; return this; } - public TemplateCardBuilder checkbox_mode(Integer checkbox_mode) { - this.checkbox_mode = checkbox_mode; + public TemplateCardBuilder checkboxMode(Integer checkboxMode) { + this.checkboxMode = checkboxMode; return this; } @@ -251,13 +267,13 @@ public TemplateCardBuilder options(List options) { return this; } - public TemplateCardBuilder submit_button_text(String submit_button_text) { - this.submit_button_text = submit_button_text; + public TemplateCardBuilder submitButtonText(String submitButtonText) { + this.submitButtonText = submitButtonText; return this; } - public TemplateCardBuilder submit_button_key(String submit_button_key) { - this.submit_button_key = submit_button_key; + public TemplateCardBuilder submitButtonKey(String submitButtonKey) { + this.submitButtonKey = submitButtonKey; return this; } @@ -266,35 +282,41 @@ public TemplateCardBuilder selects(List selects) { return this; } + public TemplateCardBuilder quoteArea(QuoteArea quoteArea) { + this.quoteArea = quoteArea; + return this; + } + @Override public WxCpMessage build() { WxCpMessage m = super.build(); m.setSafe(null); - m.setCard_type(this.card_type); - m.setSource_icon_url(this.source_icon_url); - m.setSource_desc(this.source_desc); - m.setMain_title_title(this.main_title_title); - m.setMain_title_desc(this.main_title_desc); - m.setCard_image_url(this.card_image_url); - m.setCard_image_aspect_ratio(this.card_image_aspect_ratio); - m.setEmphasis_content_title(this.emphasis_content_title); - m.setEmphasis_content_desc(this.emphasis_content_desc); - m.setSub_title_text(this.sub_title_text); - m.setVertical_contents(this.vertical_contents); - m.setHorizontal_contents(this.horizontal_contents); + m.setCardType(this.cardType); + m.setSourceIconUrl(this.sourceIconUrl); + m.setSourceDesc(this.sourceDesc); + m.setMainTitleTitle(this.mainTitleTitle); + m.setMainTitleDesc(this.mainTitleDesc); + m.setCardImageUrl(this.cardImageUrl); + m.setCardImageAspectRatio(this.cardImageAspectRatio); + m.setEmphasisContentTitle(this.emphasisContentTitle); + m.setEmphasisContentDesc(this.emphasisContentDesc); + m.setSubTitleText(this.subTitleText); + m.setVerticalContents(this.verticalContents); + m.setHorizontalContents(this.horizontalContents); m.setJumps(this.jumps); - m.setCard_action_type(this.card_action_type); - m.setCard_action_appid(this.card_action_appid); - m.setCard_action_pagepath(this.card_action_pagepath); - m.setCard_action_url(this.card_action_url); - m.setTaskId(this.task_id); + m.setCardActionType(this.cardActionType); + m.setCardActionAppid(this.cardActionAppid); + m.setCardActionPagepath(this.cardActionPagepath); + m.setCardActionUrl(this.cardActionUrl); + m.setTaskId(this.taskId); m.setButtons(this.buttons); - m.setCheckbox_mode(this.checkbox_mode); - m.setCheckbox_question_key(this.checkbox_question_key); + m.setCheckboxMode(this.checkboxMode); + m.setCheckboxQuestionKey(this.checkboxQuestionKey); m.setOptions(this.options); - m.setSubmit_button_text(this.submit_button_text); - m.setSubmit_button_key(this.submit_button_key); + m.setSubmitButtonText(this.submitButtonText); + m.setSubmitButtonKey(this.submitButtonKey); m.setSelects(this.selects); + m.setQuoteArea(this.quoteArea); return m; } } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/msgaudit/WxCpAgreeInfo.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/msgaudit/WxCpAgreeInfo.java new file mode 100644 index 000000000..43a36681e --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/msgaudit/WxCpAgreeInfo.java @@ -0,0 +1,65 @@ +package me.chanjar.weixin.cp.bean.msgaudit; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.io.Serializable; +import java.util.List; + +/** + * 获取会话同意情况返回对象. + * + * @author Wang_Wong + */ +@Data +public class WxCpAgreeInfo implements Serializable { + private static final long serialVersionUID = -5028321625140879571L; + + @SerializedName("errcode") + private Integer errcode; + + @SerializedName("errmsg") + private String errmsg; + + @SerializedName("agreeinfo") + private List agreeInfo; + + @Getter + @Setter + public static class AgreeInfo implements Serializable { + private static final long serialVersionUID = -5696099236344075582L; + + @SerializedName("status_change_time") + private Long statusChangeTime; + + @SerializedName("userid") + private String userid; + + @SerializedName("exteranalopenid") + private String exteranalOpenId; + + @SerializedName("agree_status") + private String agreeStatus; + + public static AgreeInfo fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, AgreeInfo.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + } + + public static WxCpAgreeInfo fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpAgreeInfo.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/msgaudit/WxCpChatDatas.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/msgaudit/WxCpChatDatas.java new file mode 100644 index 000000000..8359bc087 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/msgaudit/WxCpChatDatas.java @@ -0,0 +1,68 @@ +package me.chanjar.weixin.cp.bean.msgaudit; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.io.Serializable; +import java.util.List; + +/** + * 聊天记录数据内容. + * + * @author Wang_Wong + */ +@Data +public class WxCpChatDatas implements Serializable { + private static final long serialVersionUID = -5028321625140879571L; + + @SerializedName("errcode") + private Integer errCode; + + @SerializedName("errmsg") + private String errMsg; + + @SerializedName("chatdata") + private List chatData; + + @Getter + @Setter + public static class WxCpChatData implements Serializable { + private static final long serialVersionUID = -5028321625140879571L; + + @SerializedName("seq") + private Long seq; + + @SerializedName("msgid") + private String msgId; + + @SerializedName("publickey_ver") + private Integer publickeyVer; + + @SerializedName("encrypt_random_key") + private String encryptRandomKey; + + @SerializedName("encrypt_chat_msg") + private String encryptChatMsg; + + public static WxCpChatData fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpChatData.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + } + + public static WxCpChatDatas fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpChatDatas.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/msgaudit/WxCpChatModel.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/msgaudit/WxCpChatModel.java new file mode 100644 index 000000000..888f7f399 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/msgaudit/WxCpChatModel.java @@ -0,0 +1,987 @@ +package me.chanjar.weixin.cp.bean.msgaudit; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.io.Serializable; +import java.util.List; + +/** + * 聊天记录数据内容. + * + * @author Wang_Wong + */ +@Data +public class WxCpChatModel implements Serializable { + private static final long serialVersionUID = -5028321625140879571L; + + @SerializedName("msgid") + private String msgId; + + @SerializedName("action") + private String action; + + @SerializedName("send") + private String send; + + @SerializedName("from") + private String from; + + @SerializedName("tolist") + private String[] tolist; + + @SerializedName("roomid") + private String roomId; + + @SerializedName("msgtime") + private Long msgTime; + + @SerializedName("msgtype") + private String msgType; + + /** + * 文本 + */ + @SerializedName("text") + private Text text; + + /** + * 图片 + */ + @SerializedName("image") + private Image image; + + /** + * 撤回消息 + */ + @SerializedName("revoke") + private Revoke revoke; + + /** + * 同意会话聊天内容 + */ + @SerializedName(value = "agree") + private Agree agree; + + @SerializedName(value = "disagree") + private Agree disagree; + + /** + * 语音 + */ + @SerializedName(value = "voice") + private Voice voice; + + /** + * 视频 + */ + @SerializedName(value = "video") + private Video video; + + /** + * 名片 + */ + @SerializedName(value = "card") + private Card card; + + /** + * 位置 + */ + @SerializedName(value = "location") + private Location location; + + /** + * 表情 + */ + @SerializedName(value = "emotion") + private Emotion emotion; + + /** + * 文件 + */ + @SerializedName(value = "file") + private File file; + + /** + * 链接 + */ + @SerializedName(value = "link") + private Link link; + + /** + * 小程序消息 + */ + @SerializedName(value = "weapp") + private Weapp weapp; + + /** + * 会话记录消息 + */ + @SerializedName(value = "chatrecord") + private ChatRecord chatRecord; + + /** + * 待办消息 官网暂无 + */ + + /** + * 投票消息 官网暂无 + */ + + /** + * 填表消息 + */ + @SerializedName(value = "collect") + private Collect collect; + + /** + * 红包消息 + * 互通红包消息 + */ + @SerializedName("redpacket") + private Redpacket redPacket; + + /** + * 会议邀请消息 + */ + @SerializedName("meeting") + private Meeting meeting; + + /** + * 切换企业日志 + */ + @SerializedName("time") + private Long time; + + @SerializedName("user") + private String user; + + /** + * 在线文档消息 + */ + @SerializedName("doc") + private Doc doc; + + @SerializedName("info") + private Info info; + + /** + * 日程消息 + */ + @SerializedName("calendar") + private Calendar calendar; + + /** + * 混合消息 + */ + @SerializedName("mixed") + private Mixed mixed; + + /** + * 音频存档消息 + */ + @SerializedName("voiceid") + private String voiceId; + + @SerializedName("meeting_voice_call") + private MeetingVoiceCall meetingVoiceCall; + + /** + * 音频共享文档消息 + */ + @SerializedName("voipid") + private String voipId; + + @SerializedName("voip_doc_share") + private WxCpFileItem voipDocShare; + + /** + * 视频号消息 + */ + @SerializedName("sphfeed") + private SphFeed sphFeed; + + public static WxCpChatModel fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpChatModel.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + + @Getter + @Setter + public static class Text implements Serializable { + private static final long serialVersionUID = -5028321625140879571L; + + @SerializedName("content") + private String content; + + public static Text fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, Text.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + } + + + @Getter + @Setter + public static class Image implements Serializable { + private static final long serialVersionUID = -5028321625140879571L; + + @SerializedName("md5sum") + private String md5Sum; + + @SerializedName("sdkfileid") + private String sdkFileId; + + @SerializedName("filesize") + private Long fileSize; + + public static Image fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, Image.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + } + + + @Getter + @Setter + public static class Revoke implements Serializable { + private static final long serialVersionUID = -5028321625140879571L; + + @SerializedName("pre_msgid") + private String preMsgId; + + public static Revoke fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, Revoke.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + } + + + @Getter + @Setter + public static class Agree implements Serializable { + private static final long serialVersionUID = -5028321625140879571L; + + @SerializedName("userid") + private String userId; + + @SerializedName(value = "agree_time") + private Long agreeTime; + + @SerializedName(value = "disagree_time") + private Long disagreeTime; + + public static Agree fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, Agree.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + } + + + @Getter + @Setter + public static class Voice implements Serializable { + private static final long serialVersionUID = -5028321625140879571L; + + @SerializedName("md5sum") + private String md5Sum; + + @SerializedName("sdkfileid") + private String sdkFileId; + + @SerializedName("voice_size") + private Long voiceSize; + + @SerializedName("play_length") + private Long playLength; + + public static Voice fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, Voice.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + } + + + @Getter + @Setter + public static class Video implements Serializable { + private static final long serialVersionUID = -5028321625140879571L; + + @SerializedName("md5sum") + private String md5Sum; + + @SerializedName("sdkfileid") + private String sdkFileId; + + @SerializedName("filesize") + private Long fileSize; + + @SerializedName("play_length") + private Long playLength; + + public static Video fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, Video.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + } + + + @Getter + @Setter + public static class Card implements Serializable { + private static final long serialVersionUID = -5028321625140879571L; + + @SerializedName("corpname") + private String corpName; + + @SerializedName("userid") + private String userId; + + public static Card fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, Card.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + } + + + @Getter + @Setter + public static class Location implements Serializable { + private static final long serialVersionUID = -5028321625140879571L; + + @SerializedName("longitude") + private Double longitude; + + @SerializedName("latitude") + private Double latitude; + + @SerializedName("address") + private String address; + + @SerializedName("title") + private String title; + + @SerializedName("zoom") + private Integer zoom; + + public static Location fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, Location.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + } + + + @Getter + @Setter + public static class Emotion implements Serializable { + private static final long serialVersionUID = -5028321625140879571L; + + @SerializedName("type") + private Integer type; + + @SerializedName("width") + private Integer width; + + @SerializedName("height") + private Integer height; + + @SerializedName("title") + private String title; + + @SerializedName("imagesize") + private Integer imageSize; + + @SerializedName("md5sum") + private String md5Sum; + + @SerializedName("sdkfileid") + private String sdkFileId; + + public static Emotion fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, Emotion.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + } + + + @Getter + @Setter + public static class File implements Serializable { + private static final long serialVersionUID = -5028321625140879571L; + + @SerializedName("md5sum") + private String md5Sum; + + @SerializedName("filename") + private String fileName; + + @SerializedName("fileext") + private String fileExt; + + @SerializedName("sdkfileid") + private String sdkFileId; + + @SerializedName("filesize") + private Integer fileSize; + + public static File fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, File.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + } + + + @Getter + @Setter + public static class Link implements Serializable { + private static final long serialVersionUID = -5028321625140879571L; + + @SerializedName("title") + private String title; + + @SerializedName("description") + private String description; + + @SerializedName("link_url") + private String linkUrl; + + @SerializedName("image_url") + private String imageUrl; + + public static Link fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, Link.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + } + + + /** + * 小程序消息 + */ + @Getter + @Setter + public static class Weapp implements Serializable { + private static final long serialVersionUID = -5028321625140879571L; + + @SerializedName("title") + private String title; + + @SerializedName("description") + private String description; + + @SerializedName("username") + private String userName; + + @SerializedName("displayname") + private String displayName; + + public static Weapp fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, Weapp.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + } + + + /** + * 会话记录消息 + */ + @Getter + @Setter + public static class ChatRecord implements Serializable { + private static final long serialVersionUID = -5028321625140879571L; + + @SerializedName(value = "item") + private List item; + + @SerializedName("title") + private String title; + + public static ChatRecord fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, ChatRecord.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + } + + + @Getter + @Setter + public static class ChatRecordItem implements Serializable { + private static final long serialVersionUID = -5028321625140879571L; + + @SerializedName("type") + private String type; + + @SerializedName("msgtime") + private Long msgTime; + + @SerializedName("content") + private String content; + + @SerializedName("from_chatroom") + private Boolean fromChatRoom; + + public static ChatRecordItem fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, ChatRecordItem.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + } + + + /** + * 填表消息 + */ + @Getter + @Setter + public static class Collect implements Serializable { + private static final long serialVersionUID = -5028321625140879571L; + + @SerializedName("room_name") + private String roomName; + + @SerializedName("creator") + private String creator; + + @SerializedName("create_time") + private String createTime; + + @SerializedName("title") + private String title; + + @SerializedName("details") + private List
details; + + public static Collect fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, Collect.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + } + + + @Getter + @Setter + public static class Details implements Serializable { + private static final long serialVersionUID = -5028321625140879571L; + + @SerializedName("id") + private Long id; + + @SerializedName("ques") + private String ques; + + @SerializedName("type") + private String type; + + public static Details fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, Details.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + } + + + /** + * 红包消息 + */ + @Getter + @Setter + public static class Redpacket implements Serializable { + private static final long serialVersionUID = -5028321625140879571L; + + @SerializedName("type") + private Integer type; + + @SerializedName("totalcnt") + private Integer totalCnt; + + @SerializedName("totalamount") + private Integer totalAmount; + + @SerializedName("wish") + private String wish; + + public static Redpacket fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, Redpacket.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + } + + + /** + * 会议邀请消息 + */ + @Getter + @Setter + public static class Meeting implements Serializable { + private static final long serialVersionUID = -5028321625140879571L; + + @SerializedName("topic") + private String topic; + + @SerializedName("starttime") + private Long startTime; + + @SerializedName("endtime") + private Long endTime; + + @SerializedName("address") + private String address; + + @SerializedName("remarks") + private String remarks; + + @SerializedName("meetingtype") + private Integer meetingType; + + @SerializedName("meetingid") + private Long meetingId; + + @SerializedName("status") + private Integer status; + + public static Meeting fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, Meeting.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + } + + + /** + * 在线文档消息 + */ + @Getter + @Setter + public static class Doc implements Serializable { + private static final long serialVersionUID = -5028321625140879571L; + + @SerializedName("title") + private String title; + + @SerializedName("doc_creator") + private String docCreator; + + @SerializedName("link_url") + private String linkUrl; + + public static Doc fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, Doc.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + } + + + /** + * MarkDown格式消息 + */ + @Getter + @Setter + public static class Info implements Serializable { + private static final long serialVersionUID = -5028321625140879571L; + + @SerializedName("content") + private String content; + + @SerializedName("item") + private List newsItem; + + public static Info fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, Info.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + } + + + /** + * 图文消息 + */ + @Getter + @Setter + public static class NewsItem implements Serializable { + private static final long serialVersionUID = -5028321625140879571L; + + @SerializedName("title") + private String title; + + @SerializedName("description") + private String description; + + @SerializedName("url") + private String url; + + @SerializedName("picurl") + private String picUrl; + + public static NewsItem fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, NewsItem.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + } + + + /** + * 日程消息 + */ + @Getter + @Setter + public static class Calendar implements Serializable { + private static final long serialVersionUID = -5028321625140879571L; + + @SerializedName("title") + private String title; + + @SerializedName("creatorname") + private String creatorName; + + @SerializedName("attendeename") + private String[] attendeeName; + + @SerializedName("starttime") + private Long startTime; + + @SerializedName("endtime") + private Long endTime; + + @SerializedName("place") + private String place; + + @SerializedName("remarks") + private String remarks; + + public static Calendar fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, Calendar.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + } + + + /** + * 混合消息 + */ + @Getter + @Setter + public static class Mixed implements Serializable { + private static final long serialVersionUID = -5028321625140879571L; + + @SerializedName("item") + private List item; + + @Getter + @Setter + public static class Item implements Serializable { + private static final long serialVersionUID = -5028321625140879571L; + + @SerializedName("type") + private String type; + + @SerializedName("content") + private String content; + + } + + } + + + /** + * 音频存档消息 + */ + @Getter + @Setter + public static class MeetingVoiceCall implements Serializable { + private static final long serialVersionUID = -5028321625140879571L; + + @SerializedName("endtime") + private Long endTime; + + @SerializedName("sdkfileid") + private String sdkFileId; + + @SerializedName("demofiledata") + private List demoFileData; + + @SerializedName("sharescreendata") + private List shareScreenData; + + public static MeetingVoiceCall fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, MeetingVoiceCall.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + @Getter + @Setter + public static class DemoFileData implements Serializable { + private static final long serialVersionUID = -5028321625140879571L; + + @SerializedName("filename") + private String fileName; + + @SerializedName("demooperator") + private String demoOperator; + + @SerializedName("starttime") + private Long startTime; + + @SerializedName("endtime") + private Long endTime; + + public static DemoFileData fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, DemoFileData.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + } + + @Getter + @Setter + public static class ShareScreenData implements Serializable { + private static final long serialVersionUID = -5028321625140879571L; + + @SerializedName("share") + private String share; + + @SerializedName("starttime") + private Long startTime; + + @SerializedName("endtime") + private Long endTime; + + public static ShareScreenData fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, ShareScreenData.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + } + + } + + + /** + * 视频号消息 + */ + @Getter + @Setter + public static class SphFeed implements Serializable { + private static final long serialVersionUID = -5028321625140879571L; + + @SerializedName("feed_type") + private Integer feedType; + + @SerializedName("sph_name") + private String sphName; + + @SerializedName("feed_desc") + private String feedDesc; + + public static SphFeed fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, SphFeed.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + } + + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/msgaudit/WxCpCheckAgreeRequest.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/msgaudit/WxCpCheckAgreeRequest.java new file mode 100644 index 000000000..83d1b1812 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/msgaudit/WxCpCheckAgreeRequest.java @@ -0,0 +1,57 @@ +package me.chanjar.weixin.cp.bean.msgaudit; + +import com.google.gson.annotations.SerializedName; +import lombok.*; +import lombok.experimental.Accessors; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.io.Serializable; +import java.util.List; + +/** + * 获取会话同意情况请求参数. + * + * @author Wang_Wong + * @date 2022-01-21 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +public class WxCpCheckAgreeRequest implements Serializable { + private static final long serialVersionUID = -4960239393895754138L; + + @SerializedName("info") + private List info; + + public static WxCpCheckAgreeRequest fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpCheckAgreeRequest.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + @Getter + @Setter + public static class Info implements Serializable { + private static final long serialVersionUID = -4960239393895754138L; + + @SerializedName("userid") + private String userid; + + @SerializedName("exteranalopenid") + private String exteranalOpenId; + + public static Info fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, Info.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/msgaudit/WxCpFileItem.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/msgaudit/WxCpFileItem.java new file mode 100644 index 000000000..7b7be15c5 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/msgaudit/WxCpFileItem.java @@ -0,0 +1,39 @@ +package me.chanjar.weixin.cp.bean.msgaudit; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.io.Serializable; + +/** + * 会话存档 文档信息对象 + * + * @author Wang_Wong + */ +@Data +public class WxCpFileItem implements Serializable { + + private static final long serialVersionUID = -5028321625140879571L; + + @SerializedName("filename") + private String fileName; + + @SerializedName("md5sum") + private String md5Sum; + + @SerializedName("sdkfileid") + private String sdkFileId; + + @SerializedName("filesize") + private Long fileSize; + + public static WxCpFileItem fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpFileItem.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/msgaudit/WxCpGroupChat.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/msgaudit/WxCpGroupChat.java new file mode 100644 index 000000000..3a2656bfb --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/msgaudit/WxCpGroupChat.java @@ -0,0 +1,61 @@ +package me.chanjar.weixin.cp.bean.msgaudit; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.io.Serializable; +import java.util.List; + +/** + * 内部群信息 + * + * @author Wang_Wong + */ +@Data +public class WxCpGroupChat implements Serializable { + + private static final long serialVersionUID = -5028321625140879571L; + + @SerializedName("roomname") + private String roomName; + + @SerializedName("creator") + private String creator; + + @SerializedName("room_create_time") + private Long roomCreateTime; + + @SerializedName("notice") + private String notice; + + private List members; + + @Getter + @Setter + public class Member implements Serializable { + private static final long serialVersionUID = -5028321625140879571L; + + @SerializedName("memberid") + private String memberId; + + @SerializedName("jointime") + private Long joinTime; + + public Member fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, Member.class); + } + + } + + public static WxCpGroupChat fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpGroupChat.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/SummaryInfo.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/SummaryInfo.java index 49222cd0a..85954ba88 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/SummaryInfo.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/SummaryInfo.java @@ -3,7 +3,6 @@ import com.google.gson.annotations.SerializedName; import lombok.Data; import lombok.experimental.Accessors; -import me.chanjar.weixin.cp.bean.oa.WxCpOaApplyEventRequest; import java.io.Serializable; import java.util.List; @@ -18,6 +17,7 @@ @Accessors(chain = true) public class SummaryInfo implements Serializable { private static final long serialVersionUID = 8262265774851382414L; + /** * 摘要行信息,用于定义某一行摘要显示的内容 */ @@ -28,6 +28,7 @@ public class SummaryInfo implements Serializable { @Accessors(chain = true) public static class SummaryInfoData implements Serializable { private static final long serialVersionUID = 5314161929610113856L; + /** * 摘要行显示文字,用于记录列表和消息通知的显示,不要超过20个字符 */ diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalApplier.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalApplier.java index 7d372cdfc..8b16aefa8 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalApplier.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalApplier.java @@ -8,6 +8,7 @@ /** * 申请人信息 + * * @author element */ @Data diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalDetail.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalDetail.java deleted file mode 100644 index cba31fc27..000000000 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalDetail.java +++ /dev/null @@ -1,78 +0,0 @@ -package me.chanjar.weixin.cp.bean.oa; - -import com.google.gson.annotations.SerializedName; -import lombok.Data; - -import java.io.Serializable; -import java.util.List; - -/** - * 审批申请详情 - * - * @author element - */ -@Data -public class WxCpApprovalDetail implements Serializable { - private static final long serialVersionUID = 1353393306564207170L; - - /** - * 审批编号 - */ - @SerializedName("sp_no") - private String spNo; - - /** - * 审批申请类型名称(审批模板名称) - */ - @SerializedName("sp_name") - private String spName; - - /** - * 申请单状态:1-审批中;2-已通过;3-已驳回;4-已撤销;6-通过后撤销;7-已删除;10-已支付 - */ - @SerializedName("sp_status") - private WxCpSpStatus spStatus; - - /** - * 审批模板id。可在“获取审批申请详情”、“审批状态变化回调通知”中获得,也可在审批模板的模板编辑页面链接中获得。 - */ - @SerializedName("template_id") - private String templateId; - - /** - * 审批申请提交时间,Unix时间戳 - */ - @SerializedName("apply_time") - private Long applyTime; - - /** - * 申请人信息 - */ - @SerializedName("applyer") - private WxCpApprovalApplier applier; - - /** - * 审批流程信息,可能有多个审批节点 - */ - @SerializedName("sp_record") - private WxCpApprovalRecord[] spRecords; - - /** - * 抄送信息,可能有多个抄送节点 - */ - @SerializedName("notifyer") - private WxCpOperator[] notifiers; - - /** - * 审批申请数据 - */ - @SerializedName("apply_data") - private WxCpApprovalApplyData applyData; - - /** - * 审批申请备注信息,可能有多个备注节点 - */ - @SerializedName("comments") - private List comments; - -} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalDetailResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalDetailResult.java index fdbc096a5..2714cc95f 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalDetailResult.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalDetailResult.java @@ -4,6 +4,7 @@ import lombok.Data; import java.io.Serializable; +import java.util.List; /** * 审批申请详情响应结果 @@ -23,4 +24,70 @@ public class WxCpApprovalDetailResult implements Serializable { @SerializedName("info") private WxCpApprovalDetail info; + @Data + public static class WxCpApprovalDetail implements Serializable { + private static final long serialVersionUID = 1353393306564207170L; + + /** + * 审批编号 + */ + @SerializedName("sp_no") + private String spNo; + + /** + * 审批申请类型名称(审批模板名称) + */ + @SerializedName("sp_name") + private String spName; + + /** + * 申请单状态:1-审批中;2-已通过;3-已驳回;4-已撤销;6-通过后撤销;7-已删除;10-已支付 + */ + @SerializedName("sp_status") + private WxCpSpStatus spStatus; + + /** + * 审批模板id。可在“获取审批申请详情”、“审批状态变化回调通知”中获得,也可在审批模板的模板编辑页面链接中获得。 + */ + @SerializedName("template_id") + private String templateId; + + /** + * 审批申请提交时间,Unix时间戳 + */ + @SerializedName("apply_time") + private Long applyTime; + + /** + * 申请人信息 + */ + @SerializedName("applyer") + private WxCpApprovalApplier applier; + + /** + * 审批流程信息,可能有多个审批节点 + */ + @SerializedName("sp_record") + private WxCpApprovalRecord[] spRecords; + + /** + * 抄送信息,可能有多个抄送节点 + */ + @SerializedName("notifyer") + private WxCpOperator[] notifiers; + + /** + * 审批申请数据 + */ + @SerializedName("apply_data") + private WxCpApprovalApplyData applyData; + + /** + * 审批申请备注信息,可能有多个备注节点 + */ + @SerializedName("comments") + private List comments; + + } + } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalInfo.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalInfo.java index 4856af419..b9c1235f1 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalInfo.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalInfo.java @@ -1,11 +1,11 @@ package me.chanjar.weixin.cp.bean.oa; -import java.io.Serializable; -import java.util.List; - import com.google.gson.annotations.SerializedName; import lombok.Data; +import java.io.Serializable; +import java.util.List; + /** * @author element */ diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalInfoQueryFilter.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalInfoQueryFilter.java index 527131208..73e6d81d2 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalInfoQueryFilter.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalInfoQueryFilter.java @@ -29,7 +29,7 @@ public String toJson() { return WxGsonBuilder.create().toJson(this); } - public static enum KEY { + public enum KEY { /** * template_id - 模板类型/模板id; @@ -52,9 +52,9 @@ public static enum KEY { @SerializedName("sp_status") SP_STATUS("sp_status"); - private String value; + private final String value; - private KEY(String value) { + KEY(String value) { this.value = value; } } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalRecord.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalRecord.java index 3325eaa4a..c2ac33176 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalRecord.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalRecord.java @@ -8,6 +8,7 @@ /** * 审批流程信息 + * * @author element */ @Data diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalRecordDetail.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalRecordDetail.java index 4c966c9d6..371546d73 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalRecordDetail.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalRecordDetail.java @@ -8,11 +8,11 @@ /** * 审批节点详情 + * * @author element */ @Data public class WxCpApprovalRecordDetail implements Serializable { - private static final long serialVersionUID = -9142079764088495301L; /** diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApproverAttr.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApproverAttr.java index 15e55f2f7..ae65a8208 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApproverAttr.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApproverAttr.java @@ -19,9 +19,9 @@ public enum WxCpApproverAttr { @SerializedName("2") ALL_SIGN(2); - private Integer attr; + private final Integer attr; - private WxCpApproverAttr(Integer attr) { + WxCpApproverAttr(Integer attr) { this.attr = attr; } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpCheckinData.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpCheckinData.java index 93e975508..9fb385a93 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpCheckinData.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpCheckinData.java @@ -96,26 +96,26 @@ public class WxCpCheckinData implements Serializable { */ @SerializedName("deviceid") private String deviceId; - + /** * 标准打卡时间,指此次打卡时间对应的标准上班时间或标准下班时间 */ @SerializedName("sch_checkin_time") private Long schCheckinTime; - - /** + + /** * 规则id,表示打卡记录所属规则的id */ @SerializedName("groupid") private Integer groupId; - - /** + + /** * 班次id,表示打卡记录所属规则中,所属班次的id */ @SerializedName("schedule_id") private Integer scheduleId; - - /** + + /** * 时段id,表示打卡记录所属规则中,某一班次中的某一时段的id,如上下班时间为9:00-12:00、13:00-18:00的班次中,9:00-12:00为其中一组时段 */ @SerializedName("timeline_id") diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpCheckinDayData.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpCheckinDayData.java index 0c0bd8a07..ef3ae1c08 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpCheckinDayData.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpCheckinDayData.java @@ -8,7 +8,6 @@ /** * 企业微信打卡日报数据 - * */ @Data public class WxCpCheckinDayData implements Serializable { @@ -22,7 +21,7 @@ public class WxCpCheckinDayData implements Serializable { private BaseInfo baseInfo; @Data - public class BaseInfo implements Serializable{ + public class BaseInfo implements Serializable { private static final long serialVersionUID = 3679745559788648438L; @@ -102,7 +101,7 @@ public class RuleInfo implements Serializable { private List checkinTime; @Data - public class CheckinTime implements Serializable{ + public class CheckinTime implements Serializable { private static final long serialVersionUID = 1582835435812966332L; /** * work_sec 上班时间,为距离0点的时间差 @@ -117,6 +116,7 @@ public class CheckinTime implements Serializable{ private Integer offWorkSec; } } + /** * day_type 日报类型:0-工作日日报;1-休息日日报 */ @@ -131,7 +131,7 @@ public class CheckinTime implements Serializable{ private SummaryInfo summaryInfo; @Data - public class SummaryInfo implements Serializable{ + public class SummaryInfo implements Serializable { private static final long serialVersionUID = 3428576099259666595L; /** * checkin_count 当日打卡次数 @@ -171,7 +171,7 @@ public class SummaryInfo implements Serializable{ private List holidayInfos; @Data - public class HolidayInfos implements Serializable{ + public class HolidayInfos implements Serializable { private static final long serialVersionUID = -6671577072585561527L; /** * sp_number 假勤相关信息 @@ -195,7 +195,7 @@ public class SpTitle implements Serializable { private List data; @lombok.Data - public class Data implements Serializable{ + public class Data implements Serializable { private static final long serialVersionUID = -1672692024530543180L; /** * text 假勤信息摘要-标题文本 @@ -218,7 +218,7 @@ public class Data implements Serializable{ private SpDescription spDescription; @Data - public class SpDescription implements Serializable{ + public class SpDescription implements Serializable { private static final long serialVersionUID = 77680581771933449L; /** @@ -228,7 +228,7 @@ public class SpDescription implements Serializable{ private List data; @lombok.Data - public class Data implements Serializable{ + public class Data implements Serializable { private static final long serialVersionUID = 3555479101375365805L; /** * text 假勤信息摘要-标题文本 @@ -252,7 +252,7 @@ public class Data implements Serializable{ private List exceptionInfos; @Data - public class ExceptionInfos implements Serializable{ + public class ExceptionInfos implements Serializable { private static final long serialVersionUID = -5987438373762518299L; /** * exception 校准状态类型:1-迟到;2-早退;3-缺卡;4-旷工;5-地点异常;6-设备异常 @@ -280,7 +280,7 @@ public class ExceptionInfos implements Serializable{ private OtInfo otInfo; @Data - public class OtInfo implements Serializable{ + public class OtInfo implements Serializable { private static final long serialVersionUID = -6557759801572150175L; /** * ot_status 状态:0-无加班;1-正常;2-缺时长 @@ -308,7 +308,7 @@ public class OtInfo implements Serializable{ private List spItems; @Data - public class SpItem implements Serializable{ + public class SpItem implements Serializable { private static final long serialVersionUID = 2423158264958352024L; /** * type 类型:1-请假;2-补卡;3-出差;4-外出;100-外勤 diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpCheckinMonthData.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpCheckinMonthData.java index 003c68d2e..559c8e46a 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpCheckinMonthData.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpCheckinMonthData.java @@ -8,6 +8,8 @@ /** * 企业微信打卡月报数据 + * + * @author longliveh */ @Data @@ -21,8 +23,9 @@ public class WxCpCheckinMonthData implements Serializable { private BaseInfo baseInfo; @Data - public class BaseInfo implements Serializable { + public static class BaseInfo implements Serializable { private static final long serialVersionUID = -5368331890851903885L; + /** * record_type 记录类型:1-固定上下班;2-外出(此报表中不会出现外出打卡数据);3-按班次上下班;4-自由签到;5-加班;7-无规则 */ @@ -60,8 +63,9 @@ public class BaseInfo implements Serializable { private RuleInfo ruleInfo; @Data - public class RuleInfo implements Serializable { + public static class RuleInfo implements Serializable { private static final long serialVersionUID = 9152263355916880710L; + /** * groupid 所属规则Id */ @@ -76,10 +80,6 @@ public class RuleInfo implements Serializable { } } - - - - /** * summary_info 打卡人员所属规则信息 */ @@ -87,7 +87,7 @@ public class RuleInfo implements Serializable { private SummaryInfo summaryInfo; @Data - public class SummaryInfo implements Serializable { + public static class SummaryInfo implements Serializable { private static final long serialVersionUID = -1956770107240513983L; /** * work_days 应打卡天数 @@ -128,7 +128,7 @@ public class SummaryInfo implements Serializable { private List exceptionInfos; @Data - public class ExceptionInfo implements Serializable { + public static class ExceptionInfo implements Serializable { private static final long serialVersionUID = -4855850255704089359L; /** * exception 异常类型:1-迟到;2-早退;3-缺卡;4-旷工;5-地点异常;6-设备异常 @@ -156,9 +156,9 @@ public class ExceptionInfo implements Serializable { private List spItems; @Data - public class SpItem implements Serializable { - + public static class SpItem implements Serializable { private static final long serialVersionUID = 224472626753597080L; + /** * type 假勤类型:1-请假;2-补卡;3-出差;4-外出;100-外勤 */ @@ -203,8 +203,9 @@ public class SpItem implements Serializable { private OverWorkInfo overworkInfo; @Data - public class OverWorkInfo implements Serializable { + public static class OverWorkInfo implements Serializable { private static final long serialVersionUID = -9149524232645899305L; + /** * workday_over_sec 工作日加班时长 */ diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpCorpConfInfo.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpCorpConfInfo.java new file mode 100644 index 000000000..514cb421f --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpCorpConfInfo.java @@ -0,0 +1,74 @@ +package me.chanjar.weixin.cp.bean.oa; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; +import me.chanjar.weixin.cp.bean.WxCpBaseResp; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.io.Serializable; +import java.util.List; + +/** + * 企业假期管理配置信息. + * + * @author Wang_Wong + */ +@Data +public class WxCpCorpConfInfo extends WxCpBaseResp implements Serializable { + private static final long serialVersionUID = 7387181805254287157L; + + @SerializedName("lists") + private List lists; + + @Getter + @Setter + public static class CorpConf implements Serializable { + private static final long serialVersionUID = -5696099236344075582L; + + @SerializedName("id") + private Integer id; + + @SerializedName("name") + private String name; + + @SerializedName("time_attr") + private Integer timeAttr; + + @SerializedName("duration_type") + private Integer durationType; + + @SerializedName("quota_attr") + private QuotaAttr quotaAttr; + + @SerializedName("perday_duration") + private Integer perdayDuration; + + } + + @Getter + @Setter + public static class QuotaAttr implements Serializable { + private static final long serialVersionUID = -5696099236344075582L; + + @SerializedName("type") + private Integer type; + + @SerializedName("autoreset_time") + private Integer autoresetTime; + + @SerializedName("autoreset_duration") + private Integer autoresetDuration; + + } + + public static WxCpCorpConfInfo fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpCorpConfInfo.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpCropCheckinOption.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpCropCheckinOption.java index c68741fca..f9ab9dd15 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpCropCheckinOption.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpCropCheckinOption.java @@ -22,61 +22,61 @@ public class WxCpCropCheckinOption implements Serializable { private Long groupType; /** - * 打卡规则id + * 打卡规则id */ @SerializedName("groupid") private Long groupId; /** - * 打卡规则名称 + * 打卡规则名称 */ @SerializedName("groupname") private String groupName; /** - * 打卡时间,当规则类型为排班时没有意义 + * 打卡时间,当规则类型为排班时没有意义 */ @SerializedName("checkindate") private List checkinDate; /** - * 特殊日期-必须打卡日期信息,timestamp表示具体时间 + * 特殊日期-必须打卡日期信息,timestamp表示具体时间 */ @SerializedName("spe_workdays") private List speWorkdays; /** - * 特殊日期-不用打卡日期信息, timestamp表示具体时间 + * 特殊日期-不用打卡日期信息, timestamp表示具体时间 */ @SerializedName("spe_offdays") private List speOffDays; /** - * 是否同步法定节假日,true为同步,false为不同步,当前排班不支持 + * 是否同步法定节假日,true为同步,false为不同步,当前排班不支持 */ @SerializedName("sync_holidays") private Boolean syncHolidays; /** - * 是否打卡必须拍照,true为必须拍照,false为不必须拍照 + * 是否打卡必须拍照,true为必须拍照,false为不必须拍照 */ @SerializedName("need_photo") private Boolean needPhoto; /** - * 是否备注时允许上传本地图片,true为允许,false为不允许 + * 是否备注时允许上传本地图片,true为允许,false为不允许 */ @SerializedName("note_can_use_local_pic") private Boolean noteCanUseLocalPic; /** - * 是否非工作日允许打卡,true为允许,false为不允许 + * 是否非工作日允许打卡,true为允许,false为不允许 */ @SerializedName("allow_checkin_offworkday") private Boolean allowCheckinOffWorkDay; /** - * 是否允许提交补卡申请,true为允许,false为不允许 + * 是否允许提交补卡申请,true为允许,false为不允许 */ @SerializedName("allow_apply_offworkday") private Boolean allowApplyOffWorkDay; @@ -106,217 +106,217 @@ public class WxCpCropCheckinOption implements Serializable { private Long createTime; /** - * 打卡人员白名单,即不需要打卡人员,需要有设置白名单才能查看 + * 打卡人员白名单,即不需要打卡人员,需要有设置白名单才能查看 */ @SerializedName("white_users") private List whiteUsers; /** - * 打卡方式,0:手机;2:智慧考勤机;3:手机+智慧考勤机 + * 打卡方式,0:手机;2:智慧考勤机;3:手机+智慧考勤机 */ @SerializedName("type") private Integer type; /** - * 打卡方式,0:手机;2:智慧考勤机;3:手机+智慧考勤机 + * 打卡方式,0:手机;2:智慧考勤机;3:手机+智慧考勤机 */ @SerializedName("reporterinfo") private ReporterInfo reporterInfo; /** - * 加班信息,相关信息需要设置后才能显示 + * 加班信息,相关信息需要设置后才能显示 */ @SerializedName("ot_info") private OtInfo otInfo; /** - * 每月最多补卡次数,默认-1表示不限制 + * 每月最多补卡次数,默认-1表示不限制 */ @SerializedName("allow_apply_bk_cnt") private Integer allowApplyBkCnt; /** - * 范围外打卡处理方式,0-视为范围外异常,默认值;1-视为正常外勤;2:不允许范围外打卡 + * 范围外打卡处理方式,0-视为范围外异常,默认值;1-视为正常外勤;2:不允许范围外打卡 */ @SerializedName("option_out_range") private Integer optionOutRange; /** - * 规则创建人userid + * 规则创建人userid */ @SerializedName("create_userid") private String createUserid; /** - * 人脸识别打卡开关,true为启用,false为不启用 + * 人脸识别打卡开关,true为启用,false为不启用 */ @SerializedName("use_face_detect") private Boolean useFaceDetect; /** - * 允许补卡时限,默认-1表示不限制。单位天 + * 允许补卡时限,默认-1表示不限制。单位天 */ @SerializedName("allow_apply_bk_day_limit") private Integer allowApplyBkDayLimit; /** - * 规则最近编辑人userid + * 规则最近编辑人userid */ @SerializedName("update_userid") private String updateUserid; /** - * 加班信息,相关信息需要设置后才能显示 + * 加班信息,相关信息需要设置后才能显示 */ @SerializedName("schedulelist") private List schedulelist; /** - * 自由签到,上班打卡后xx秒可打下班卡 + * 自由签到,上班打卡后xx秒可打下班卡 */ @SerializedName("offwork_interval_time") private Integer offWorkIntervalTime; @Data - public static class CheckinDate implements Serializable{ + public static class CheckinDate implements Serializable { private static final long serialVersionUID = -8560643656775167406L; /** - * 工作日。若为固定时间上下班或自由上下班,则1到6分别表示星期一到星期六,0表示星期日 + * 工作日。若为固定时间上下班或自由上下班,则1到6分别表示星期一到星期六,0表示星期日 */ @SerializedName("workdays") private List workdays; /** - * 工作日上下班打卡时间信息 + * 工作日上下班打卡时间信息 */ @SerializedName("checkintime") private List checkinTime; /** - * 下班不需要打卡,true为下班不需要打卡,false为下班需要打卡 + * 下班不需要打卡,true为下班不需要打卡,false为下班需要打卡 */ @SerializedName("noneed_offwork") private Boolean noneedOffwork; /** - * 打卡时间限制(毫秒) + * 打卡时间限制(毫秒) */ @SerializedName("limit_aheadtime") private Long limitAheadtime; /** - * 允许迟到时间,单位ms + * 允许迟到时间,单位ms */ @SerializedName("flex_on_duty_time") private Integer flexOnDutyTime; /** - * 允许早退时间,单位ms + * 允许早退时间,单位ms */ @SerializedName("flex_off_duty_time") private Integer flexOffDutyTime; } @Data - public static class CheckinTime implements Serializable{ + public static class CheckinTime implements Serializable { private static final long serialVersionUID = -5507709858609705279L; /** - * 上班时间,表示为距离当天0点的秒数。 + * 上班时间,表示为距离当天0点的秒数。 */ @SerializedName("work_sec") private Integer workSec; /** - * 下班时间,表示为距离当天0点的秒数。 + * 下班时间,表示为距离当天0点的秒数。 */ @SerializedName("off_work_sec") private Integer offWorkSec; /** - * 上班提醒时间,表示为距离当天0点的秒数。。 + * 上班提醒时间,表示为距离当天0点的秒数。。 */ @SerializedName("remind_work_sec") private Integer remindWorkSec; /** - * 下班提醒时间,表示为距离当天0点的秒数。 + * 下班提醒时间,表示为距离当天0点的秒数。 */ @SerializedName("remind_off_work_sec") private Integer remindOffWorkSec; } @Data - public static class SpeWorkday implements Serializable{ + public static class SpeWorkday implements Serializable { private static final long serialVersionUID = -4620710297258742666L; /** - * 特殊日期-必须打卡日期时间戳 + * 特殊日期-必须打卡日期时间戳 */ @SerializedName("timestamp") private Long timestamp; /** - * 特殊日期备注 + * 特殊日期备注 */ @SerializedName("notes") private String notes; /** - * 特殊日期-必须打卡日期-上下班打卡时间 + * 特殊日期-必须打卡日期-上下班打卡时间 */ @SerializedName("checkintime") private List checkinTime; } @Data - public static class SpeOffDay implements Serializable{ + public static class SpeOffDay implements Serializable { private static final long serialVersionUID = 9214798931489490993L; /** - * 特殊日期-不用打卡日期时间戳 + * 特殊日期-不用打卡日期时间戳 */ @SerializedName("timestamp") private Long timestamp; /** - * 特殊日期备注 + * 特殊日期备注 */ @SerializedName("notes") private String notes; } @Data - public static class WifiMacInfo implements Serializable{ + public static class WifiMacInfo implements Serializable { private static final long serialVersionUID = 6742659716677227089L; /** - * WiFi打卡地点名称 + * WiFi打卡地点名称 */ @SerializedName("wifiname") private String wifiname; /** - * WiFi打卡地点MAC地址/bssid + * WiFi打卡地点MAC地址/bssid */ @SerializedName("wifimac") private String wifimac; } @Data - public static class LocInfo implements Serializable{ + public static class LocInfo implements Serializable { private static final long serialVersionUID = -5591379191341944101L; /** - * 位置打卡地点纬度,是实际纬度的1000000倍,与腾讯地图一致采用GCJ-02坐标系统标准 + * 位置打卡地点纬度,是实际纬度的1000000倍,与腾讯地图一致采用GCJ-02坐标系统标准 */ @SerializedName("lat") private Long lat; /** - * 位置打卡地点经度,是实际经度的1000000倍,与腾讯地图一致采用GCJ-02坐标系统标准 + * 位置打卡地点经度,是实际经度的1000000倍,与腾讯地图一致采用GCJ-02坐标系统标准 */ @SerializedName("lng") private Long lng; @@ -334,31 +334,31 @@ public static class LocInfo implements Serializable{ private String locDetail; /** - * 允许打卡范围(米) + * 允许打卡范围(米) */ @SerializedName("distance") private Integer distance; } @Data - public static class Range implements Serializable{ + public static class Range implements Serializable { private static final long serialVersionUID = 8940086218556453088L; /** - * 打卡人员中,单个打卡人员节点的userid + * 打卡人员中,单个打卡人员节点的userid */ @SerializedName("party_id") private List partyid; /** - * 打卡人员中,部门节点的id + * 打卡人员中,部门节点的id */ @SerializedName("userid") private List userid; /** - * 打卡人员中,标签节点的标签id + * 打卡人员中,标签节点的标签id */ @SerializedName("tagid") private List tagid; @@ -367,16 +367,16 @@ public static class Range implements Serializable{ } @Data - public static class ReporterInfo implements Serializable{ + public static class ReporterInfo implements Serializable { private static final long serialVersionUID = 1132450350458936772L; /** - * 汇报对象,每个汇报人用userid表示 + * 汇报对象,每个汇报人用userid表示 */ @SerializedName("reporters") private List reporters; /** - * 汇报对象更新时间 + * 汇报对象更新时间 */ @SerializedName("updatetime") private long updateTime; @@ -392,7 +392,7 @@ public static class Reporter implements Serializable { } @Data - public static class OtInfo implements Serializable{ + public static class OtInfo implements Serializable { private static final long serialVersionUID = 1610150484871066199L; @@ -406,31 +406,31 @@ public static class OtInfo implements Serializable{ private Integer type; /** - * 允许工作日加班,true为允许,false为不允许 + * 允许工作日加班,true为允许,false为不允许 */ @SerializedName("allow_ot_workingday") private Boolean allowOtWorkingDay; /** - * 允许非工作日加班,true为允许,flase为不允许 + * 允许非工作日加班,true为允许,flase为不允许 */ @SerializedName("allow_ot_nonworkingday") private Boolean allowOtNonworkingDay; /** - * 允许非工作日加班,true为允许,flase为不允许 + * 允许非工作日加班,true为允许,flase为不允许 */ @SerializedName("otcheckinfo") private OtCheckInfo otcheckinfo; /** - * 更新时间 + * 更新时间 */ @SerializedName("uptime") private Long uptime; /** - * 允许非工作日加班,true为允许,flase为不允许 + * 允许非工作日加班,true为允许,flase为不允许 */ @SerializedName("otapplyinfo") private OtApplyInfo otapplyinfo; @@ -442,91 +442,91 @@ public static class OtCheckInfo implements Serializable { private static final long serialVersionUID = -2363047492489556390L; /** - * 允许工作日加班-加班开始时间:下班后xx秒开始计算加班,距离最晚下班时间的秒数,例如,1800(30分钟 乘以 60秒),默认值30分钟 + * 允许工作日加班-加班开始时间:下班后xx秒开始计算加班,距离最晚下班时间的秒数,例如,1800(30分钟 乘以 60秒),默认值30分钟 */ @SerializedName("ot_workingday_time_start") private Integer otWorkingDayTimeStart; /** - * 允许工作日加班-最短加班时长:不足xx秒视为未加班,单位秒,默认值30分钟 + * 允许工作日加班-最短加班时长:不足xx秒视为未加班,单位秒,默认值30分钟 */ @SerializedName("ot_workingday_time_min") private Integer otWorkingDayTimeMin; /** - * 允许工作日加班-最长加班时长:超过则视为加班xx秒,单位秒,默认值240分钟 + * 允许工作日加班-最长加班时长:超过则视为加班xx秒,单位秒,默认值240分钟 */ @SerializedName("ot_workingday_time_max") private Integer otWorkingDayTimeMax; /** - * 允许非工作日加班-最短加班时长:不足xx秒视为未加班,单位秒,默认值30分钟 + * 允许非工作日加班-最短加班时长:不足xx秒视为未加班,单位秒,默认值30分钟 */ @SerializedName("ot_nonworkingday_time_min") private Integer otNonworkingDayTimeMin; /** - * 允许非工作日加班-最长加班时长:超过则视为加班xx秒 单位秒,默认值240分钟 + * 允许非工作日加班-最长加班时长:超过则视为加班xx秒 单位秒,默认值240分钟 */ @SerializedName("ot_nonworkingday_time_max") private Integer otNonworkingDayTimeMax; /** - * 非工作日加班,跨天时间,距离当天00:00的秒数 + * 非工作日加班,跨天时间,距离当天00:00的秒数 */ @SerializedName("ot_nonworkingday_spanday_time") private Integer otNonworkingDaySpanDayTime; /** - * 允许非工作日加班,true为允许,flase为不允许 + * 允许非工作日加班,true为允许,flase为不允许 */ @SerializedName("ot_workingday_restinfo") private OtWorkingDayRestInfo otWorkingdayRestinfo; /** - * 允许非工作日加班,true为允许,flase为不允许 + * 允许非工作日加班,true为允许,flase为不允许 */ @SerializedName("ot_nonworkingday_restinfo") private OtNonworkingDayRestInfo otNonworkingdayRestinfo; } @Data - public static class OtWorkingDayRestInfo implements Serializable{ + public static class OtWorkingDayRestInfo implements Serializable { private static final long serialVersionUID = -4011047369711928306L; /** - * 工作日加班-休息扣除类型:0-不开启扣除;1-指定休息时间扣除;2-按加班时长扣除休息时间 + * 工作日加班-休息扣除类型:0-不开启扣除;1-指定休息时间扣除;2-按加班时长扣除休息时间 */ @SerializedName("type") private Integer type; /** - * 工作日加班-指定休息时间配置信息,当group.ot_info.otcheckinfo.ot_workingday_restinfo.type为1时有意义 + * 工作日加班-指定休息时间配置信息,当group.ot_info.otcheckinfo.ot_workingday_restinfo.type为1时有意义 */ @SerializedName("fix_time_rule") private FixTimeRule fixTimeRule; /** - * 工作日加班-按加班时长扣除配置信息,当group.ot_info.otcheckinfo.ot_workingday_restinfo.type为2时有意义 + * 工作日加班-按加班时长扣除配置信息,当group.ot_info.otcheckinfo.ot_workingday_restinfo.type为2时有意义 */ @SerializedName("cal_ottime_rule") private CalOtTimeRule calOttimeRule; } @Data - public static class FixTimeRule implements Serializable{ + public static class FixTimeRule implements Serializable { private static final long serialVersionUID = 5709478500196619664L; /** - * 工作日加班-指定休息时间的开始时间, 距离当天00:00的秒数 + * 工作日加班-指定休息时间的开始时间, 距离当天00:00的秒数 */ @SerializedName("fix_time_begin_sec") private Integer fixTimeBeginSec; /** - * 工作日加班-指定休息时间的结束时间, 距离当天00:00的秒数 + * 工作日加班-指定休息时间的结束时间, 距离当天00:00的秒数 */ @SerializedName("fix_time_end_sec") private Integer fixTimeEndSec; @@ -538,91 +538,92 @@ public static class CalOtTimeRule implements Serializable { private static final long serialVersionUID = -2407839982631243413L; /** - * 工作日加班-按加班时长扣除条件信息 + * 工作日加班-按加班时长扣除条件信息 */ @SerializedName("items") private List items; } + @Data - public static class Item implements Serializable{ + public static class Item implements Serializable { private static final long serialVersionUID = 5235770378506228461L; /** - * 加班满-时长(秒) + * 加班满-时长(秒) */ @SerializedName("ot_time") private Integer otTime; /** - * 对应扣除-时长(秒) + * 对应扣除-时长(秒) */ @SerializedName("rest_time") private Integer restTime; } @Data - public static class OtNonworkingDayRestInfo implements Serializable{ + public static class OtNonworkingDayRestInfo implements Serializable { private static final long serialVersionUID = 3773846077049838088L; /** - * 非工作日加班-休息扣除类型:0-不开启扣除;1-指定休息时间扣除;2-按加班时长扣除休息时间 + * 非工作日加班-休息扣除类型:0-不开启扣除;1-指定休息时间扣除;2-按加班时长扣除休息时间 */ @SerializedName("type") private Integer type; /** - * 非工作日加班-指定休息时间配置信息,当group.ot_info.otcheckinfo.ot_workingday_restinfo.type为1时有意义 + * 非工作日加班-指定休息时间配置信息,当group.ot_info.otcheckinfo.ot_workingday_restinfo.type为1时有意义 */ @SerializedName("fix_time_rule") private FixTimeRule fixTimeRule; /** - * 非工作日加班-按加班时长扣除配置信息,当group.ot_info.otcheckinfo.ot_workingday_restinfo.type为2时有意义 + * 非工作日加班-按加班时长扣除配置信息,当group.ot_info.otcheckinfo.ot_workingday_restinfo.type为2时有意义 */ @SerializedName("cal_ottime_rule") private CalOtTimeRule calOttimeRule; } @Data - public static class OtApplyInfo implements Serializable{ + public static class OtApplyInfo implements Serializable { private static final long serialVersionUID = 961217471918884103L; /** - * 允许工作日加班,true为允许,false为不允许 + * 允许工作日加班,true为允许,false为不允许 */ @SerializedName("allow_ot_workingday") private Boolean allowOtWorkingDay; /** - * 允许非工作日加班,true为允许,flase为不允许 + * 允许非工作日加班,true为允许,flase为不允许 */ @SerializedName("allow_ot_nonworkingday") private Boolean allowOtNonworkingDay; /** - * 更新时间 + * 更新时间 */ @SerializedName("uptime") private Long uptime; /** - * 允许非工作日加班,true为允许,flase为不允许 + * 允许非工作日加班,true为允许,flase为不允许 */ @SerializedName("ot_workingday_restinfo") private OtWorkingDayRestInfo otWorkingdayRestinfo; /** - * 允许非工作日加班,true为允许,flase为不允许 + * 允许非工作日加班,true为允许,flase为不允许 */ @SerializedName("ot_nonworkingday_restinfo") private OtNonworkingDayRestInfo otNonworkingdayRestinfo; /** - * 非工作日加班,跨天时间,距离当天00:00的秒数 + * 非工作日加班,跨天时间,距离当天00:00的秒数 */ @SerializedName("ot_nonworkingday_spanday_time") private Integer otNonworkingDaySpanDayTime; @@ -630,78 +631,78 @@ public static class OtApplyInfo implements Serializable{ } @Data - public static class Schedule implements Serializable{ + public static class Schedule implements Serializable { private static final long serialVersionUID = -2461113644925307266L; /** - * 班次id + * 班次id */ @SerializedName("schedule_id") private Integer scheduleId; /** - * 班次名称 + * 班次名称 */ @SerializedName("schedule_name") private String scheduleName; /** - * 班次上下班时段信息 + * 班次上下班时段信息 */ @SerializedName("time_section") private List timeSection; /** - * 允许提前打卡时间 + * 允许提前打卡时间 */ @SerializedName("limit_aheadtime") private Long limitAheadTime; /** - * 下班xx秒后不允许打下班卡 + * 下班xx秒后不允许打下班卡 */ @SerializedName("limit_offtime") private Integer limitOffTime; /** - * 下班不需要打卡 + * 下班不需要打卡 */ @SerializedName("noneed_offwork") private Boolean noNeedOffWork; /** - * 是否允许弹性时间 + * 是否允许弹性时间 */ @SerializedName("allow_flex") private Boolean allowFlex; /** - * 允许迟到时间 + * 允许迟到时间 */ @SerializedName("flex_on_duty_time") private Integer flexOnDutyTime; /** - * 允许早退时间 + * 允许早退时间 */ @SerializedName("flex_off_duty_time") private Integer flexOffDutyTime; /** - * 非工作日加班,跨天时间,距离当天00:00的秒数 + * 非工作日加班,跨天时间,距离当天00:00的秒数 */ @SerializedName("late_rule") private LateRule lateRule; /** - * 最早可打卡时间限制 + * 最早可打卡时间限制 */ @SerializedName("max_allow_arrive_early") private Integer maxAllowArriveEarly; /** - * 最晚可打卡时间限制,max_allow_arrive_early、max_allow_arrive_early与flex_on_duty_time、flex_off_duty_time互斥,当设置其中一组时,另一组数值置0 + * 最晚可打卡时间限制,max_allow_arrive_early、max_allow_arrive_early与flex_on_duty_time、flex_off_duty_time互斥,当设置其中一组时,另一组数值置0 */ @SerializedName("max_allow_arrive_late") private Integer maxAllowArriveLate; @@ -714,49 +715,49 @@ public static class TimeSection implements Serializable { private static final long serialVersionUID = 7497252128339062724L; /** - * 时段id,为班次中某一堆上下班时间组合的id + * 时段id,为班次中某一堆上下班时间组合的id */ @SerializedName("time_id") private Integer timeId; /** - * 上班时间,表示为距离当天0点的秒数。 + * 上班时间,表示为距离当天0点的秒数。 */ @SerializedName("work_sec") private Integer workSec; /** - * 下班时间,表示为距离当天0点的秒数。 + * 下班时间,表示为距离当天0点的秒数。 */ @SerializedName("off_work_sec") private Integer offWorkSec; /** - * 上班提醒时间,表示为距离当天0点的秒数。 + * 上班提醒时间,表示为距离当天0点的秒数。 */ @SerializedName("remind_work_sec") private Long remindWorkSec; /** - * 下班提醒时间,表示为距离当天0点的秒数。 + * 下班提醒时间,表示为距离当天0点的秒数。 */ @SerializedName("remind_off_work_sec") private Integer remindOffWorkSec; /** - * 休息开始时间,仅单时段支持,距离0点的秒 + * 休息开始时间,仅单时段支持,距离0点的秒 */ @SerializedName("rest_begin_time") private Integer restBeginTime; /** - * 休息结束时间,仅单时段支持,距离0点的秒 + * 休息结束时间,仅单时段支持,距离0点的秒 */ @SerializedName("rest_end_time") private Integer restEndTime; /** - * 是否允许休息 + * 是否允许休息 */ @SerializedName("allow_rest") private Boolean allowRest; @@ -764,37 +765,37 @@ public static class TimeSection implements Serializable { @Data - public static class LateRule implements Serializable{ + public static class LateRule implements Serializable { private static final long serialVersionUID = 5604969713950037053L; /** - * 是否允许超时下班(下班晚走次日晚到)允许时onwork_flex_time,offwork_after_time才有意义 + * 是否允许超时下班(下班晚走次日晚到)允许时onwork_flex_time,offwork_after_time才有意义 */ @SerializedName("allow_offwork_after_time") private Boolean allowOffWorkAfterTime; /** - * 迟到规则时间 + * 迟到规则时间 */ @SerializedName("timerules") private List timerules; } @Data - public static class TimeRule implements Serializable{ + public static class TimeRule implements Serializable { private static final long serialVersionUID = 5680614050081598333L; /** - * 晚走的时间 距离最晚一个下班的时间单位:秒 + * 晚走的时间 距离最晚一个下班的时间单位:秒 */ @SerializedName("offwork_after_time") private Integer offWorkAfterTime; /** - * 第二天第一个班次允许迟到的弹性时间单位:秒 + * 第二天第一个班次允许迟到的弹性时间单位:秒 */ @SerializedName("onwork_flex_time") private Integer onWorkFlexTime; diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpRecordSpStatus.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpRecordSpStatus.java index 206a0aa04..055871e23 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpRecordSpStatus.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpRecordSpStatus.java @@ -1,14 +1,18 @@ package me.chanjar.weixin.cp.bean.oa; import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Getter; /** * 审批记录(节点)分支审批状态 - * - * 1-审批中;2-已同意;3-已驳回;4-已转审 + *

+ * 1-审批中;2-已同意;3-已驳回;4-已转审;11-已退回 * * @author element */ +@AllArgsConstructor +@Getter public enum WxCpRecordSpStatus { /** @@ -30,12 +34,13 @@ public enum WxCpRecordSpStatus { * 已转审 */ @SerializedName("4") - TURNED(4); - - private Integer status; + TURNED(4), + /** + * 已退回 + */ + @SerializedName("11") + WITHDRAWN(11); - private WxCpRecordSpStatus(Integer status) { - this.status = status; - } + private final Integer status; } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpSetCheckinSchedule.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpSetCheckinSchedule.java index 3d0d1f87f..d34d233a3 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpSetCheckinSchedule.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpSetCheckinSchedule.java @@ -33,7 +33,7 @@ public class WxCpSetCheckinSchedule implements Serializable { @Data - public static class Item implements Serializable{ + public static class Item implements Serializable { private static final long serialVersionUID = -918057757709951513L; @@ -44,7 +44,7 @@ public static class Item implements Serializable{ private String userid; /** - * 要设置的天日期,取值在1-31之间。联合yearmonth组成唯一日期 比如20201205 + * 要设置的天日期,取值在1-31之间。联合yearmonth组成唯一日期 比如20201205 */ @SerializedName("day") private Integer day; diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpSpStatus.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpSpStatus.java index c3d8005f5..029b7c144 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpSpStatus.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpSpStatus.java @@ -46,9 +46,9 @@ public enum WxCpSpStatus { @SerializedName("10") ALREADY_PAY(10); - private Integer status; + private final Integer status; - private WxCpSpStatus(Integer status) { + WxCpSpStatus(Integer status) { this.status = status; } } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/applydata/ContentValue.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/applydata/ContentValue.java index 1ad07bf2a..6ae69c189 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/applydata/ContentValue.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/applydata/ContentValue.java @@ -35,6 +35,9 @@ public class ContentValue implements Serializable { private List children; + @SerializedName("related_approval") + private List relatedApproval; + private Attendance attendance; private Vacation vacation; @@ -124,4 +127,43 @@ public static class Vacation implements Serializable { private Attendance attendance; } + /** + * 关联审批单控件 + */ + @Data + public static class RelatedApproval implements Serializable { + private static final long serialVersionUID = 8629601623267510738L; + /** + * 关联审批单的模板名 + */ + @SerializedName("template_names") + private List templateNames; + /** + * 关联审批单的状态 + */ + @SerializedName("sp_status") + private Integer spStatus; + /** + * 关联审批单的提单者 + */ + private String name; + /** + * 关联审批单的提单时间 + */ + @SerializedName("create_time") + private Long createTime; + /** + * 关联审批单的审批单号 + */ + @SerializedName("sp_no") + private String spNo; + } + + @Data + public static class TemplateName implements Serializable { + private static final long serialVersionUID = 3152481506054355937L; + private String text; + private String lang; + } + } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/selfagent/WxCpOpenApprovalData.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/selfagent/WxCpOpenApprovalData.java new file mode 100644 index 000000000..261a0f8de --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/selfagent/WxCpOpenApprovalData.java @@ -0,0 +1,161 @@ +package me.chanjar.weixin.cp.bean.oa.selfagent; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.io.Serializable; +import java.util.List; + +/** + * 审批申请当前状态信息. + * + * @author Wang_Wong + */ +@Data +public class WxCpOpenApprovalData implements Serializable { + private static final long serialVersionUID = -5028321625140879591L; + + @SerializedName("ThirdNo") + private String thirdNo; + + @SerializedName("OpenTemplateId") + private String openTemplateId; + + @SerializedName("OpenSpName") + private String openSpName; + + @SerializedName("OpenSpstatus") + private Integer openSpstatus; + + @SerializedName("ApplyTime") + private Long applyTime; + + @SerializedName("ApplyUsername") + private String applyUserName; + + @SerializedName("ApplyUserParty") + private String applyUserParty; + + @SerializedName("ApplyUserImage") + private String applyUserImage; + + @SerializedName("ApplyUserId") + private String applyUserId; + + @SerializedName("ApprovalNodes") + private ApprovalNodes approvalNodes; + + @SerializedName("NotifyNodes") + private NotifyNodes notifyNodes; + + @SerializedName("ApproverStep") + private Integer approverStep; + + @Getter + @Setter + public static class ApprovalNodes implements Serializable { + private static final long serialVersionUID = -5696099236344075582L; + + @SerializedName("ApprovalNode") + private List approvalNode; + + } + + @Getter + @Setter + public static class ApprovalNode implements Serializable { + private static final long serialVersionUID = -5696099236344075582L; + + @SerializedName("NodeStatus") + private Integer nodeStatus; + + @SerializedName("NodeAttr") + private Integer nodeAttr; + + @SerializedName("NodeType") + private Integer nodeType; + + @SerializedName("Items") + private Items items; + + } + + @Getter + @Setter + public static class NotifyNodes implements Serializable { + private static final long serialVersionUID = -5696099236344075582L; + + @SerializedName("NotifyNode") + private List notifyNode; + + } + + @Getter + @Setter + public static class NotifyNode implements Serializable { + private static final long serialVersionUID = -5696099236344075582L; + + @SerializedName("ItemName") + private String itemName; + + @SerializedName("ItemParty") + private String itemParty; + + @SerializedName("ItemImage") + private String itemImage; + + @SerializedName("ItemUserId") + private String itemUserId; + + } + + @Getter + @Setter + public static class Items implements Serializable { + private static final long serialVersionUID = -5696099236344075582L; + + @SerializedName("Item") + private List item; + + } + + @Getter + @Setter + public static class Item implements Serializable { + private static final long serialVersionUID = -5696099236344075582L; + + @SerializedName("ItemName") + private String itemName; + + @SerializedName("ItemParty") + private String itemParty; + + @SerializedName("ItemImage") + private String itemImage; + + @SerializedName("ItemUserId") + private String itemUserId; + + @SerializedName("ItemSpeech") + private String itemSpeech; + + @SerializedName("ItemStatus") + private Integer itemStatus; + + @SerializedName("ItemOpTime") + private Long itemOpTime; + + } + + public static WxCpOpenApprovalData fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpOpenApprovalData.class); + } + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateConfig.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateConfig.java index 54213d9d9..3bbb4b2cb 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateConfig.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateConfig.java @@ -14,6 +14,7 @@ * Contact-成员/部门; * Table-明细; * Attendance-假勤组件(请假、外出、出差、加班) + * * @author gyv12345@163.com */ @Data diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/control/TemplateTable.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/control/TemplateTable.java index 08cdd5d4e..1022e1662 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/control/TemplateTable.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/control/TemplateTable.java @@ -7,7 +7,6 @@ import java.util.List; /** - * * @author gyv12345@163.com */ @Data diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/outxmlbuilder/EventBuilder.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/outxmlbuilder/EventBuilder.java new file mode 100644 index 000000000..19dc5f38e --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/outxmlbuilder/EventBuilder.java @@ -0,0 +1,131 @@ +package me.chanjar.weixin.cp.bean.outxmlbuilder; + +import me.chanjar.weixin.cp.bean.message.WxCpXmlOutEventMessage; + +/** + * @author eYoung + * @description: + * @date create at 2021/12/3 16:34 + */ +public class EventBuilder extends BaseBuilder { + + private String event; + private String chatId; + private String changeType; + private String updateDetail; + private String joinScene; + private String quitScene; + private String memChangeCnt; + private String tagType; + private String strategyId; + private String userID; + private String externalUserID; + private String state; + private String welcomeCode; + private String source; + private String failReason; + private String id; + + public EventBuilder chatId(String chatId) { + this.chatId = chatId; + return this; + } + + public EventBuilder event(String event) { + this.event = event; + return this; + } + + public EventBuilder changeType(String changeType) { + this.changeType = changeType; + return this; + } + + public EventBuilder updateDetail(String updateDetail) { + this.updateDetail = updateDetail; + return this; + } + + public EventBuilder joinScene(String joinScene) { + this.joinScene = joinScene; + return this; + } + + public EventBuilder quitScene(String quitScene) { + this.quitScene = quitScene; + return this; + } + + public EventBuilder memChangeCnt(String memChangeCnt) { + this.memChangeCnt = memChangeCnt; + return this; + } + + public EventBuilder tagType(String tagType) { + this.tagType = tagType; + return this; + } + + public EventBuilder strategyId(String strategyId) { + this.strategyId = strategyId; + return this; + } + + public EventBuilder userID(String userID) { + this.userID = userID; + return this; + } + + public EventBuilder externalUserID(String externalUserID) { + this.externalUserID = externalUserID; + return this; + } + + public EventBuilder state(String state) { + this.state = state; + return this; + } + + public EventBuilder source(String source) { + this.source = source; + return this; + } + + public EventBuilder welcomeCode(String welcomeCode) { + this.welcomeCode = welcomeCode; + return this; + } + + public EventBuilder failReason(String failReason) { + this.failReason = failReason; + return this; + } + + public EventBuilder id(String id) { + this.id = id; + return this; + } + + @Override + public WxCpXmlOutEventMessage build() { + WxCpXmlOutEventMessage m = new WxCpXmlOutEventMessage(); + super.setCommon(m); + m.setEvent(this.event); + m.setChatId(this.chatId); + m.setChangeType(this.changeType); + m.setUpdateDetail(this.updateDetail); + m.setJoinScene(this.joinScene); + m.setQuitScene(this.quitScene); + m.setMemChangeCnt(this.memChangeCnt); + m.setTagType(this.tagType); + m.setStrategyId(this.strategyId); + m.setUserID(this.userID); + m.setExternalUserID(this.externalUserID); + m.setState(this.state); + m.setWelcomeCode(this.welcomeCode); + m.setSource(this.source); + m.setFailReason(this.failReason); + m.setId(this.id); + return m; + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/outxmlbuilder/NewsBuilder.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/outxmlbuilder/NewsBuilder.java index 190ab1c97..60fb3b8a1 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/outxmlbuilder/NewsBuilder.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/outxmlbuilder/NewsBuilder.java @@ -20,7 +20,7 @@ public NewsBuilder addArticle(Item... items) { return this; } - public NewsBuilder articles(List articles){ + public NewsBuilder articles(List articles) { this.articles = articles; return this; } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/outxmlbuilder/UpdateButtonBuilder.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/outxmlbuilder/UpdateButtonBuilder.java index d4dd4b04d..352abeb04 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/outxmlbuilder/UpdateButtonBuilder.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/outxmlbuilder/UpdateButtonBuilder.java @@ -1,6 +1,5 @@ package me.chanjar.weixin.cp.bean.outxmlbuilder; -import me.chanjar.weixin.cp.bean.message.WxCpXmlOutTaskCardMessage; import me.chanjar.weixin.cp.bean.message.WxCpXmlOutUpdateBtnMessage; /** diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/taskcard/TaskCardButton.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/taskcard/TaskCardButton.java index 4a7e3c00e..bff28c415 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/taskcard/TaskCardButton.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/taskcard/TaskCardButton.java @@ -1,12 +1,12 @@ package me.chanjar.weixin.cp.bean.taskcard; -import java.io.Serializable; - import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + /** *

  *  任务卡片按钮
@@ -22,7 +22,7 @@
 @AllArgsConstructor
 public class TaskCardButton implements Serializable {
   private static final long serialVersionUID = -4301684507150486556L;
-  
+
   private String key;
   private String name;
   private String replaceName;
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/CheckboxOption.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/CheckboxOption.java
index 2f6b5b1a9..8e76ca00b 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/CheckboxOption.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/CheckboxOption.java
@@ -5,12 +5,12 @@
 import lombok.Builder;
 import lombok.Data;
 import lombok.NoArgsConstructor;
-import org.apache.commons.lang3.StringUtils;
 
 import java.io.Serializable;
 
 /**
  * 按钮列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过6
+ *
  * @author yzts
  * @date 2021/9/22
  */
@@ -18,7 +18,7 @@
 @Builder
 @NoArgsConstructor
 @AllArgsConstructor
-public class CheckboxOption  implements Serializable {
+public class CheckboxOption implements Serializable {
   private static final long serialVersionUID = 5405702239190050250L;
 
   /**
@@ -41,7 +41,7 @@ public JsonObject toJson() {
     JsonObject optionJson = new JsonObject();
     optionJson.addProperty("id", this.getId());
     optionJson.addProperty("text", this.getText());
-    if(null != this.getIs_checked()) {
+    if (null != this.getIs_checked()) {
       optionJson.addProperty("is_checked", this.getIs_checked());
     }
     return optionJson;
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/HorizontalContent.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/HorizontalContent.java
index 4f1160e93..397420820 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/HorizontalContent.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/HorizontalContent.java
@@ -11,6 +11,7 @@
 
 /**
  * 二级标题+文本列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过6
+ *
  * @author yzts
  * @date 2021/9/22
  */
@@ -46,7 +47,7 @@ public class HorizontalContent implements Serializable {
   public JsonObject toJson() {
     JsonObject hContentJson = new JsonObject();
 
-    if(null !=  this.getType()){
+    if (null != this.getType()) {
       hContentJson.addProperty("type", this.getType());
     }
     hContentJson.addProperty("keyname", this.getKeyname());
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/MultipleSelect.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/MultipleSelect.java
index 145a6c442..95ab92f3f 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/MultipleSelect.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/MultipleSelect.java
@@ -13,6 +13,7 @@
 
 /**
  * 下拉式的选择器列表,multiple_interaction类型的卡片该字段不可为空,一个消息最多支持 3 个选择器
+ *
  * @author yzts
  * @date 2021/9/22
  */
@@ -54,7 +55,7 @@ public JsonObject toJson() {
     }
 // select_list
     List options = this.getOptions();
-    if(null != options && options.size() > 0) {
+    if (null != options && options.size() > 0) {
       JsonArray optionJsonArray = new JsonArray();
       for (CheckboxOption option : this.getOptions()) {
         JsonObject tempObject = option.toJson();
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/QuoteArea.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/QuoteArea.java
new file mode 100644
index 000000000..564500a45
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/QuoteArea.java
@@ -0,0 +1,74 @@
+package me.chanjar.weixin.cp.bean.templatecard;
+
+import com.google.gson.JsonObject;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.Serializable;
+
+/**
+ * 引用文献样式
+ *
+ * @author zp
+ * @date 2022/1/2
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class QuoteArea implements Serializable {
+
+  private static final long serialVersionUID = -2209656515382964356L;
+
+  /**
+   * 非必填 引用文献样式区域点击事件,0或不填代表没有点击事件,1 代表跳转url,2 代表跳转小程序
+   */
+  private Integer type;
+  /**
+   * 点击跳转的url,quote_area.type是1时必填
+   */
+  private String url;
+  /**
+   * 点击跳转的小程序的appid,必须是与当前应用关联的小程序,quote_area.type是2时必填
+   */
+  private String appid;
+  /**
+   * 点击跳转的小程序的pagepath,quote_area.type是2时选填
+   */
+  private String pagepath;
+  /**
+   * 引用文献样式的标题
+   */
+  private String title;
+  /**
+   * 引用文献样式的引用文案
+   */
+  private String quoteText;
+
+  public JsonObject toJson() {
+    JsonObject quoteAreaJson = new JsonObject();
+    if (null != this.getType()) {
+      quoteAreaJson.addProperty("type", this.getType());
+    }
+    if (StringUtils.isNotBlank(this.getUrl())) {
+      quoteAreaJson.addProperty("url", this.getUrl());
+    }
+    if (StringUtils.isNotBlank(this.getAppid())) {
+      quoteAreaJson.addProperty("appid", this.getAppid());
+    }
+    if (StringUtils.isNotBlank(this.getPagepath())) {
+      quoteAreaJson.addProperty("pagepath", this.getPagepath());
+    }
+    if (StringUtils.isNotBlank(this.getTitle())) {
+      quoteAreaJson.addProperty("title", this.getTitle());
+    }
+    if (StringUtils.isNotBlank(this.getQuoteText())) {
+      quoteAreaJson.addProperty("quote_text", this.getQuoteText());
+    }
+    return quoteAreaJson;
+  }
+
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/TemplateCardButton.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/TemplateCardButton.java
index 4ac9e005e..28722c8d7 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/TemplateCardButton.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/TemplateCardButton.java
@@ -5,12 +5,12 @@
 import lombok.Builder;
 import lombok.Data;
 import lombok.NoArgsConstructor;
-import org.apache.commons.lang3.StringUtils;
 
 import java.io.Serializable;
 
 /**
  * 按钮列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过6
+ *
  * @author yzts
  * @date 2021/9/22
  */
@@ -18,7 +18,7 @@
 @Builder
 @NoArgsConstructor
 @AllArgsConstructor
-public class TemplateCardButton  implements Serializable {
+public class TemplateCardButton implements Serializable {
   private static final long serialVersionUID = -4826551822490837002L;
 
   /**
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/TemplateCardJump.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/TemplateCardJump.java
index 6d297e9c0..79fd92ff9 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/TemplateCardJump.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/TemplateCardJump.java
@@ -11,6 +11,7 @@
 
 /**
  * 跳转指引样式的列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过3
+ *
  * @author yzts
  * @date 2021/9/22
  */
@@ -45,7 +46,7 @@ public class TemplateCardJump implements Serializable {
   public JsonObject toJson() {
     JsonObject hContentJson = new JsonObject();
 
-    if(null !=  this.getType()){
+    if (null != this.getType()) {
       hContentJson.addProperty("type", this.getType());
     }
     hContentJson.addProperty("title", this.getTitle());
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/VerticalContent.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/VerticalContent.java
index 7d364ff10..2dc402184 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/VerticalContent.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/templatecard/VerticalContent.java
@@ -1,7 +1,6 @@
 package me.chanjar.weixin.cp.bean.templatecard;
 
 import com.google.gson.JsonObject;
-import kotlin.text.UStringsKt;
 import lombok.AllArgsConstructor;
 import lombok.Builder;
 import lombok.Data;
@@ -12,6 +11,7 @@
 
 /**
  * 卡片二级垂直内容,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过4
+ *
  * @author yzts
  * @date 2021/9/22
  */
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpConfigStorage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpConfigStorage.java
index 02a7af880..1d7e9685d 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpConfigStorage.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpConfigStorage.java
@@ -174,6 +174,13 @@ public interface WxCpConfigStorage {
    */
   String getAesKey();
 
+  /**
+   * 获取企微会话存档系统库 绝对路径
+   *
+   * @return
+   */
+  String getMsgAuditLibPath();
+
   /**
    * Gets expires time.
    *
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpDefaultConfigImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpDefaultConfigImpl.java
index 0fbf61724..c716eb735 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpDefaultConfigImpl.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpDefaultConfigImpl.java
@@ -43,6 +43,7 @@ public class WxCpDefaultConfigImpl implements WxCpConfigStorage, Serializable {
   private volatile String token;
   private volatile String aesKey;
   private volatile long expiresTime;
+  private volatile String msgAuditLibPath;
   private volatile String oauth2redirectUri;
   private volatile String httpProxyHost;
   private volatile int httpProxyPort;
@@ -256,6 +257,11 @@ public String getAesKey() {
     return this.aesKey;
   }
 
+  @Override
+  public String getMsgAuditLibPath() {
+    return this.msgAuditLibPath;
+  }
+
   /**
    * Sets aes key.
    *
@@ -279,6 +285,15 @@ public void setAgentId(Integer agentId) {
     this.agentId = agentId;
   }
 
+  /**
+   * 设置企微会话存档路径.
+   *
+   * @param msgAuditLibPath 会话存档具体路径
+   */
+  public void setMsgAuditLibPath(String msgAuditLibPath) {
+    this.msgAuditLibPath = msgAuditLibPath;
+  }
+
   @Override
   public String getOauth2redirectUri() {
     return this.oauth2redirectUri;
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpRedisConfigImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpRedisConfigImpl.java
index 027ab825c..89b939e61 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpRedisConfigImpl.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpRedisConfigImpl.java
@@ -40,6 +40,7 @@ public class WxCpRedisConfigImpl implements WxCpConfigStorage {
   private volatile String token;
   private volatile String aesKey;
   private volatile Integer agentId;
+  private volatile String msgAuditLibPath;
   private volatile String oauth2redirectUri;
   private volatile String httpProxyHost;
   private volatile int httpProxyPort;
@@ -320,6 +321,11 @@ public String getAesKey() {
     return this.aesKey;
   }
 
+  @Override
+  public String getMsgAuditLibPath() {
+    return this.msgAuditLibPath;
+  }
+
   /**
    * Sets aes key.
    *
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpApiPathConsts.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpApiPathConsts.java
index c09116d75..b3773eeab 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpApiPathConsts.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpApiPathConsts.java
@@ -1,8 +1,6 @@
 package me.chanjar.weixin.cp.constant;
 
 
-import lombok.experimental.UtilityClass;
-
 /**
  * 
  *  企业微信api地址常量类
@@ -73,8 +71,10 @@ interface Chat {
   interface Department {
     String DEPARTMENT_CREATE = "/cgi-bin/department/create";
     String DEPARTMENT_UPDATE = "/cgi-bin/department/update";
+    String DEPARTMENT_GET = "/cgi-bin/department/get?id=%d";
     String DEPARTMENT_DELETE = "/cgi-bin/department/delete?id=%d";
     String DEPARTMENT_LIST = "/cgi-bin/department/list";
+    String DEPARTMENT_SIMPLE_LIST = "/cgi-bin/department/simplelist";
   }
 
   interface Media {
@@ -91,6 +91,10 @@ interface Menu {
   }
 
   interface Oa {
+    /**
+     * 打卡
+     * https://developer.work.weixin.qq.com/document/path/94204
+     */
     String GET_CORP_CHECKIN_OPTION = "/cgi-bin/checkin/getcorpcheckinoption";
     String GET_CHECKIN_DATA = "/cgi-bin/checkin/getcheckindata";
     String GET_CHECKIN_OPTION = "/cgi-bin/checkin/getcheckinoption";
@@ -98,12 +102,32 @@ interface Oa {
     String GET_CHECKIN_MONTH_DATA = "/cgi-bin/checkin/getcheckin_monthdata";
     String GET_CHECKIN_SCHEDULE_DATA = "/cgi-bin/checkin/getcheckinschedulist";
     String SET_CHECKIN_SCHEDULE_DATA = "/cgi-bin/checkin/setcheckinschedulist";
+
+    /**
+     * 审批
+     * https://developer.work.weixin.qq.com/document/path/91956
+     */
+    String COPY_TEMPLATE = "/cgi-bin/oa/approval/copytemplate";
+    String GET_TEMPLATE_DETAIL = "/cgi-bin/oa/gettemplatedetail";
+    String APPLY_EVENT = "/cgi-bin/oa/applyevent";
     String GET_APPROVAL_INFO = "/cgi-bin/oa/getapprovalinfo";
     String GET_APPROVAL_DETAIL = "/cgi-bin/oa/getapprovaldetail";
+    String GET_APPROVAL_DATA = "/cgi-bin/oa/getapprovaldata";
+
+    String GET_CORP_CONF = "/cgi-bin/oa/vacation/getcorpconf";
+    String GET_USER_VACATION_QUOTA = "/cgi-bin/oa/vacation/getuservacationquota";
+    String SET_ONE_USER_QUOTA = "/cgi-bin/oa/vacation/setoneuserquota";
+
+    /**
+     * 公费电话
+     * https://developer.work.weixin.qq.com/document/path/93662
+     */
     String GET_DIAL_RECORD = "/cgi-bin/dial/get_dial_record";
-    String GET_TEMPLATE_DETAIL = "/cgi-bin/oa/gettemplatedetail";
-    String APPLY_EVENT = "/cgi-bin/oa/applyevent";
 
+    /**
+     * 日程
+     * https://developer.work.weixin.qq.com/document/path/93624
+     */
     String CALENDAR_ADD = "/cgi-bin/oa/calendar/add";
     String CALENDAR_UPDATE = "/cgi-bin/oa/calendar/update";
     String CALENDAR_GET = "/cgi-bin/oa/calendar/get";
@@ -115,7 +139,30 @@ interface Oa {
     String SCHEDULE_DEL = "/cgi-bin/oa/schedule/del";
     String SCHEDULE_LIST = "/cgi-bin/oa/schedule/get_by_calendar";
 
-    String COPY_TEMPLATE = "/cgi-bin/oa/approval/copytemplate";
+    /**
+     * 审批流程引擎
+     * https://developer.work.weixin.qq.com/document/path/90269
+     */
+    String GET_OPEN_APPROVAL_DATA = "/cgi-bin/corp/getopenapprovaldata";
+  }
+
+  interface Living {
+    String GET_LIVING_CODE = "/cgi-bin/living/get_living_code";
+    String GET_LIVING_INFO = "/cgi-bin/living/get_living_info?livingid=";
+    String GET_WATCH_STAT = "/cgi-bin/living/get_watch_stat";
+    String GET_LIVING_SHARE_INFO = "/cgi-bin/living/get_living_share_info";
+    String GET_USER_ALL_LIVINGID = "/cgi-bin/living/get_user_all_livingid";
+
+    String CREATE = "/cgi-bin/living/create";
+    String MODIFY = "/cgi-bin/living/modify";
+    String CANCEL = "/cgi-bin/living/cancel";
+    String DELETE_REPLAY_DATA = "/cgi-bin/living/delete_replay_data";
+  }
+
+  interface MsgAudit {
+    String GET_PERMIT_USER_LIST = "/cgi-bin/msgaudit/get_permit_user_list";
+    String GET_GROUP_CHAT = "/cgi-bin/msgaudit/groupchat/get";
+    String CHECK_SINGLE_AGREE = "/cgi-bin/msgaudit/check_single_agree";
   }
 
   interface Tag {
@@ -182,6 +229,10 @@ interface ExternalContact {
     String GET_CONTACT_DETAIL = "/cgi-bin/externalcontact/get?external_userid=";
     String CONVERT_TO_OPENID = "/cgi-bin/externalcontact/convert_to_openid";
     String UNIONID_TO_EXTERNAL_USERID = "/cgi-bin/externalcontact/unionid_to_external_userid";
+    String UNIONID_TO_EXTERNAL_USERID_3RD = "/cgi-bin/service/externalcontact/unionid_to_external_userid_3rd";
+    String GET_NEW_EXTERNAL_USERID = "/cgi-bin/service/externalcontact/get_new_external_userid";
+    String TO_SERVICE_EXTERNAL_USERID = "/cgi-bin/externalcontact/to_service_external_userid";
+    String FINISH_EXTERNAL_USERID_MIGRATION = "/cgi-bin/externalcontact/finish_external_userid_migration";
     String GET_CONTACT_DETAIL_BATCH = "/cgi-bin/externalcontact/batch/get_by_user?";
     String UPDATE_REMARK = "/cgi-bin/externalcontact/remark";
     String LIST_EXTERNAL_CONTACT = "/cgi-bin/externalcontact/list?userid=";
@@ -194,6 +245,7 @@ interface ExternalContact {
     String RESIGNED_TRANSFER_RESULT = "/cgi-bin/externalcontact/resigned/transfer_result";
     String GROUP_CHAT_LIST = "/cgi-bin/externalcontact/groupchat/list";
     String GROUP_CHAT_INFO = "/cgi-bin/externalcontact/groupchat/get";
+    String OPENID_TO_CHATID= "/cgi-bin/externalcontact/opengid_to_chatid";
     String GROUP_CHAT_TRANSFER = "/cgi-bin/externalcontact/groupchat/transfer";
     String LIST_USER_BEHAVIOR_DATA = "/cgi-bin/externalcontact/get_user_behavior_data";
     String LIST_GROUP_CHAT_DATA = "/cgi-bin/externalcontact/groupchat/statistic";
@@ -217,6 +269,39 @@ interface ExternalContact {
     String GET_GROUP_MSG_SEND_RESULT = "/cgi-bin/externalcontact/get_groupmsg_send_result";
     String GET_GROUP_MSG_TASK = "/cgi-bin/externalcontact/get_groupmsg_task";
     String GET_GROUP_MSG_LIST_V2 = "/cgi-bin/externalcontact/get_groupmsg_list_v2";
+    String GET_GROUP_MSG_RESULT = "/cgi-bin/externalcontact/get_group_msg_result";
+
+    String GET_PRODUCT_ALBUM = "/cgi-bin/externalcontact/get_product_album";
+    String GET_PRODUCT_ALBUM_LIST = "/cgi-bin/externalcontact/get_product_album_list";
+
+    String GROUP_WELCOME_TEMPLATE_ADD = "/cgi-bin/externalcontact/group_welcome_template/add";
+    String GROUP_WELCOME_TEMPLATE_EDIT = "/cgi-bin/externalcontact/group_welcome_template/edit";
+    String GROUP_WELCOME_TEMPLATE_GET = "/cgi-bin/externalcontact/group_welcome_template/get";
+    String GROUP_WELCOME_TEMPLATE_DEL = "/cgi-bin/externalcontact/group_welcome_template/del";
+
+    String UPLOAD_ATTACHMENT = "/cgi-bin/media/upload_attachment";
+
+  }
+
+  interface Kf {
+    String ACCOUNT_ADD = "/cgi-bin/kf/account/add";
+    String ACCOUNT_UPD = "/cgi-bin/kf/account/update";
+    String ACCOUNT_DEL = "/cgi-bin/kf/account/del";
+    String ACCOUNT_LIST = "/cgi-bin/kf/account/list";
+    String ADD_CONTACT_WAY = "/cgi-bin/kf/add_contact_way";
+
+    String SERVICER_ADD = "/cgi-bin/kf/servicer/add";
+    String SERVICER_DEL = "/cgi-bin/kf/servicer/del";
+    String SERVICER_LIST = "/cgi-bin/kf/servicer/list?open_kfid=";
+
+    String SERVICE_STATE_GET = "/cgi-bin/kf/service_state/get";
+    String SERVICE_STATE_TRANS = "/cgi-bin/kf/service_state/trans";
+
+    String SYNC_MSG = "/cgi-bin/kf/sync_msg";
+    String SEND_MSG = "/cgi-bin/kf/send_msg";
+
+    String SEND_MSG_ON_EVENT = "/cgi-bin/kf/send_msg_on_event";
+    String CUSTOMER_BATCH_GET = "/cgi-bin/kf/customer/batchget";
 
   }
 }
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpConsts.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpConsts.java
index 601c7dbb3..0c7648a9e 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpConsts.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpConsts.java
@@ -98,6 +98,16 @@ public static class EventType {
      */
     public static final String CHANGE_EXTERNAL_CONTACT = "change_external_contact";
 
+    /**
+     * 客户群事件推送
+     */
+    public static final String CHANGE_EXTERNAL_CHAT = "change_external_chat";
+
+    /**
+     * 企业客户标签事件推送
+     */
+    public static final String CHANGE_EXTERNAL_TAG = "change_external_tag";
+
     /**
      * 企业微信审批事件推送(自建应用审批)
      */
@@ -144,6 +154,10 @@ public static class ExternalContactChangeType {
      * 新增外部联系人
      */
     public static final String ADD_EXTERNAL_CONTACT = "add_external_contact";
+    /**
+     * 编辑外部联系人
+     */
+    public static final String EDIT_EXTERNAL_CONTACT = "edit_external_contact";
     /**
      * 删除外部联系人
      */
@@ -157,8 +171,97 @@ public static class ExternalContactChangeType {
      * 删除跟进成员事件
      */
     public static final String DEL_FOLLOW_USER = "del_follow_user";
+    /**
+     * 客户接替失败事件
+     */
+    public static final String TRANSFER_FAIL = "transfer_fail";
+
+    @UtilityClass
+    public static class ExternalContactTransferFailReason {
+      /**
+       * 客户拒绝
+       */
+      public static final String CUSTOMER_REFUSED = "customer_refused";
+      /**
+       * 接替成员的客户数达到上限
+       */
+      public static final String CUSTOMER_LIMIT_EXCEED = "customer_limit_exceed";
+    }
   }
 
+  @UtilityClass
+  public static class ExternalChatChangeType {
+    /**
+     * 客户群变更事件
+     */
+    public static final String CREATE = "create";
+    /**
+     * 客户群变更事件
+     */
+    public static final String UPDATE = "update";
+    /**
+     * 客户群解散事件
+     */
+    public static final String DISMISS = "dismiss";
+
+    @UtilityClass
+    public static class ExternalChatUpdateDetail {
+      /**
+       * 成员入群
+       */
+      public static final String ADD_MEMBER = "add_member";
+      /**
+       * 成员退群
+       */
+      public static final String DEL_MEMBER = "del_member";
+      /**
+       * 成员退群
+       */
+      public static final String CHANGE_OWNER = "change_owner";
+      /**
+       * 群名变更
+       */
+      public static final String CHANGE_NAME = "change_name";
+      /**
+       * 群公告变更
+       */
+      public static final String CHANGE_NOTICE = "change_notice";
+    }
+  }
+  @UtilityClass
+  public static class ExternalTagChangeType{
+
+    /**
+     * 创建企业客户标签
+     */
+    public static final String CREATE = "create";
+    /**
+     * 变更企业客户标签
+     */
+    public static final String UPDATE = "update";
+    /**
+     * 删除企业客户标签
+     */
+    public static final String DELETE = "delete";
+    /**
+     * 重排企业客户标签
+     */
+    public static final String SHUFFLE = "shuffle";
+  }
+
+  @UtilityClass
+  public static class TageType{
+    /**
+     * 标签
+     */
+    public static final String TAG = "tag";
+    /**
+     * 标签组
+     */
+    public static final String TAG_GROUP = "tag_group";
+  }
+
+
   /**
    * 企业微信通讯录变更事件.
    */
@@ -318,20 +421,20 @@ public static class AppChatMsgType {
   @UtilityClass
   public static class WorkBenchType {
     /*
-    * 关键数据型
-    * */
+     * 关键数据型
+     * */
     public static final String KEYDATA = "keydata";
     /*
-    * 图片型
-    * */
+     * 图片型
+     * */
     public static final String IMAGE = "image";
     /*
-    * 列表型
-    * */
+     * 列表型
+     * */
     public static final String LIST = "list";
     /*
-    * webview型
-    * */
+     * webview型
+     * */
     public static final String WEBVIEW = "webview";
   }
 
@@ -359,4 +462,15 @@ public static class WelcomeMsgType {
      */
     public static final String FILE = "file";
   }
+
+  @UtilityClass
+  public static class ProductAttachmentType {
+
+    /**
+     * 图片消息.
+     */
+    public static final String IMAGE = "image";
+
+  }
+
 }
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/message/WxCpMessageRouterRule.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/message/WxCpMessageRouterRule.java
index 739bb0330..bbae22693 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/message/WxCpMessageRouterRule.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/message/WxCpMessageRouterRule.java
@@ -246,7 +246,7 @@ protected boolean test(WxCpXmlMessage wxMessage) {
     return
       (this.fromUser == null || this.fromUser.equals(wxMessage.getFromUserName()))
         &&
-        (this.agentId == null || this.agentId.equals(wxMessage.getAgentId()))
+        (this.agentId == null || this.agentId.equals(Integer.valueOf(wxMessage.getAgentId())))
         &&
         (this.msgType == null || this.msgType.equalsIgnoreCase(wxMessage.getMsgType()))
         &&
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/crypto/WxCpCryptUtil.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/crypto/WxCpCryptUtil.java
index 2914945b8..d36a1ce34 100755
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/crypto/WxCpCryptUtil.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/crypto/WxCpCryptUtil.java
@@ -4,6 +4,12 @@
 import com.google.common.io.BaseEncoding;
 import me.chanjar.weixin.common.util.crypto.WxCryptUtil;
 import me.chanjar.weixin.cp.config.WxCpConfigStorage;
+import org.apache.commons.codec.binary.Base64;
+
+import javax.crypto.Cipher;
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.spec.PKCS8EncodedKeySpec;
 
 public class WxCpCryptUtil extends WxCryptUtil {
   public WxCpCryptUtil(WxCpConfigStorage wxCpConfigStorage) {
@@ -21,4 +27,31 @@ public WxCpCryptUtil(WxCpConfigStorage wxCpConfigStorage) {
     this.aesKey = BaseEncoding.base64().decode(CharMatcher.whitespace().removeFrom(encodingAesKey));
   }
 
+  /**
+   * 会话存档接口解密私钥
+   * 企业获取的会话内容将用公钥加密,企业用自行保存的私钥解开会话内容数据
+   *
+   * @param encryptRandomKey
+   * @param msgAuditPriKey
+   * @return
+   * @throws Exception
+   */
+  public static String decryptByPriKey(String encryptRandomKey, String msgAuditPriKey) throws Exception {
+    String privateKey = msgAuditPriKey.replaceAll("\\n", "")
+      .replace("-----BEGIN PRIVATE KEY-----", "")
+      .replace("-----END PRIVATE KEY-----", "")
+      .replaceAll(" ", "");
+
+    byte[] keyByte = Base64.decodeBase64(privateKey);
+    PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyByte);
+    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+    PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
+
+    Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
+    cipher.init(Cipher.DECRYPT_MODE, priKey);
+    byte[] utf8 = cipher.doFinal(Base64.decodeBase64(encryptRandomKey));
+
+    return new String(utf8, "UTF-8");
+  }
+
 }
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpDepartGsonAdapter.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpDepartGsonAdapter.java
index 4340855fd..af9344b70 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpDepartGsonAdapter.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpDepartGsonAdapter.java
@@ -8,18 +8,12 @@
  */
 package me.chanjar.weixin.cp.util.json;
 
-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 com.google.gson.*;
 import me.chanjar.weixin.common.util.json.GsonHelper;
 import me.chanjar.weixin.cp.bean.WxCpDepart;
 
+import java.lang.reflect.Type;
+
 /**
  * WxCpDepart的gson适配器.
  *
@@ -29,6 +23,7 @@ public class WxCpDepartGsonAdapter implements JsonSerializer, JsonDe
   private static final String ID = "id";
   private static final String NAME = "name";
   private static final String EN_NAME = "name_en";
+  private static final String DEPARTMENT_LEADER = "department_leader";
   private static final String PARENT_ID = "parentid";
   private static final String ORDER = "order";
 
@@ -44,6 +39,13 @@ public JsonElement serialize(WxCpDepart group, Type typeOfSrc, JsonSerialization
     if (group.getEnName() != null) {
       json.addProperty(EN_NAME, group.getEnName());
     }
+    if (group.getDepartmentLeader() != null) {
+      JsonArray jsonArray = new JsonArray();
+      for (String department : group.getDepartmentLeader()) {
+        jsonArray.add(new JsonPrimitive(department));
+      }
+      json.add(DEPARTMENT_LEADER, jsonArray);
+    }
     if (group.getParentId() != null) {
       json.addProperty(PARENT_ID, group.getParentId());
     }
@@ -67,6 +69,15 @@ public WxCpDepart deserialize(JsonElement json, Type typeOfT, JsonDeserializatio
     if (departJson.get(EN_NAME) != null && !departJson.get(EN_NAME).isJsonNull()) {
       depart.setEnName(GsonHelper.getAsString(departJson.get(EN_NAME)));
     }
+    if (departJson.getAsJsonArray(DEPARTMENT_LEADER) != null && !departJson.get(DEPARTMENT_LEADER).isJsonNull()) {
+      JsonArray jsonArray = departJson.getAsJsonArray(DEPARTMENT_LEADER);
+      String[] departments = new String[jsonArray.size()];
+      int i = 0;
+      for (JsonElement jsonElement : jsonArray) {
+        departments[i++] = jsonElement.getAsString();
+      }
+      depart.setDepartmentLeader(departments);
+    }
     if (departJson.get(ORDER) != null && !departJson.get(ORDER).isJsonNull()) {
       depart.setOrder(GsonHelper.getAsLong(departJson.get(ORDER)));
     }
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpGsonBuilder.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpGsonBuilder.java
index 16f0108e0..098935bf0 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpGsonBuilder.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpGsonBuilder.java
@@ -9,6 +9,7 @@
 import me.chanjar.weixin.cp.bean.WxCpDepart;
 import me.chanjar.weixin.cp.bean.WxCpTag;
 import me.chanjar.weixin.cp.bean.WxCpUser;
+import java.util.Objects;
 
 /**
  * @author Daniel Qian
@@ -16,6 +17,7 @@
 public class WxCpGsonBuilder {
 
   private static final GsonBuilder INSTANCE = new GsonBuilder();
+  private static volatile Gson GSON_INSTANCE;
 
   static {
     INSTANCE.disableHtmlEscaping();
@@ -28,7 +30,14 @@ public class WxCpGsonBuilder {
   }
 
   public static Gson create() {
-    return INSTANCE.create();
+    if (Objects.isNull(GSON_INSTANCE)) {
+      synchronized (INSTANCE) {
+        if (Objects.isNull(GSON_INSTANCE)) {
+          GSON_INSTANCE = INSTANCE.create();
+        }
+      }
+    }
+    return GSON_INSTANCE;
   }
 
 }
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpUserGsonAdapter.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpUserGsonAdapter.java
index 6cecde0b4..f75c0ef1e 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpUserGsonAdapter.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpUserGsonAdapter.java
@@ -89,6 +89,7 @@ public WxCpUser deserialize(JsonElement json, Type typeOfT, JsonDeserializationC
     user.setToInvite(GsonHelper.getBoolean(o, "to_invite"));
     user.setOpenUserId(GsonHelper.getString(o, "open_userid"));
     user.setMainDepartment(GsonHelper.getString(o, "main_department"));
+    user.setDirectLeader(GsonHelper.getStringArray(o, "direct_leader"));
 
     if (GsonHelper.isNotNull(o.get(EXTRA_ATTR))) {
       this.buildExtraAttrs(o, user);
@@ -97,9 +98,9 @@ public WxCpUser deserialize(JsonElement json, Type typeOfT, JsonDeserializationC
     if (GsonHelper.isNotNull(o.get(EXTERNAL_PROFILE))) {
       user.setExternalCorpName(GsonHelper.getString(o.getAsJsonObject().get(EXTERNAL_PROFILE).getAsJsonObject(), EXTERNAL_CORP_NAME));
       JsonElement jsonElement = o.get(EXTERNAL_PROFILE).getAsJsonObject().get(WECHAT_CHANNELS);
-      if(jsonElement !=null){
+      if (jsonElement != null) {
         JsonObject asJsonObject = jsonElement.getAsJsonObject();
-        user.setWechatChannels(WechatChannels.builder().nickname(GsonHelper.getString(asJsonObject,"nickname")).status(GsonHelper.getInteger(asJsonObject,"status")).build());
+        user.setWechatChannels(WechatChannels.builder().nickname(GsonHelper.getString(asJsonObject, "nickname")).status(GsonHelper.getInteger(asJsonObject, "status")).build());
       }
       this.buildExternalAttrs(o, user);
     }
@@ -139,7 +140,12 @@ private void buildExtraAttrs(JsonObject o, WxCpUser user) {
   }
 
   private void buildExternalAttrs(JsonObject o, WxCpUser user) {
-    JsonArray attrJsonElements = o.get(EXTERNAL_PROFILE).getAsJsonObject().get(EXTERNAL_ATTR).getAsJsonArray();
+    JsonElement jsonElement = o.get(EXTERNAL_PROFILE).getAsJsonObject().get(EXTERNAL_ATTR);
+    if (jsonElement == null) {
+      return;
+    }
+
+    JsonArray attrJsonElements = jsonElement.getAsJsonArray();
     for (JsonElement element : attrJsonElements) {
       final Integer type = GsonHelper.getInteger(element.getAsJsonObject(), "type");
       final String name = GsonHelper.getString(element.getAsJsonObject(), "name");
@@ -327,8 +333,8 @@ public JsonElement serialize(WxCpUser user, Type typeOfSrc, JsonSerializationCon
       attrsJson.addProperty(EXTERNAL_CORP_NAME, user.getExternalCorpName());
     }
 
-    if(user.getWechatChannels() != null){
-      attrsJson.add(WECHAT_CHANNELS,GsonHelper.buildJsonObject("nickname", user.getWechatChannels().getNickname(), "status", user.getWechatChannels().getStatus()));
+    if (user.getWechatChannels() != null) {
+      attrsJson.add(WECHAT_CHANNELS, GsonHelper.buildJsonObject("nickname", user.getWechatChannels().getNickname(), "status", user.getWechatChannels().getStatus()));
     }
 
     if (!user.getExternalAttrs().isEmpty()) {
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/xml/XStreamTransformer.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/xml/XStreamTransformer.java
index 421765bc0..62ea5072e 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/xml/XStreamTransformer.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/xml/XStreamTransformer.java
@@ -57,6 +57,7 @@ private static Map configXStreamInstance() {
     map.put(WxCpXmlOutUpdateBtnMessage.class, configWxCpXmlOutUpdateBtnMessage());
     map.put(WxCpTpXmlPackage.class, configWxCpTpXmlPackage());
     map.put(WxCpTpXmlMessage.class, configWxCpTpXmlMessage());
+    map.put(WxCpXmlOutEventMessage.class, configWxCpXmlOutEventMessage());
     return map;
   }
 
@@ -141,4 +142,11 @@ private static XStream configWxCpTpXmlMessage() {
     return xstream;
   }
 
+  private static XStream configWxCpXmlOutEventMessage() {
+    XStream xstream = XStreamInitializer.getInstance();
+    xstream.processAnnotations(WxCpXmlOutMessage.class);
+    xstream.processAnnotations(WxCpXmlOutEventMessage.class);
+    return xstream;
+  }
+
 }
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpExternalContactTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpExternalContactTest.java
new file mode 100644
index 000000000..7fb1650da
--- /dev/null
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpExternalContactTest.java
@@ -0,0 +1,54 @@
+package me.chanjar.weixin.cp.api;
+
+import com.google.common.collect.Lists;
+import com.google.inject.Inject;
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.cp.bean.WxCpBaseResp;
+import me.chanjar.weixin.cp.bean.external.*;
+import me.chanjar.weixin.cp.bean.external.contact.WxCpExternalContactBatchInfo;
+import me.chanjar.weixin.cp.bean.external.contact.WxCpExternalContactInfo;
+import me.chanjar.weixin.cp.bean.external.msg.Attachment;
+import me.chanjar.weixin.cp.bean.external.msg.Image;
+import me.chanjar.weixin.cp.bean.external.msg.Video;
+import org.apache.commons.lang3.time.DateFormatUtils;
+import org.testng.annotations.Guice;
+import org.testng.annotations.Test;
+import org.testng.collections.CollectionUtils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+
+import static org.testng.Assert.assertNotNull;
+
+/**
+ * 离职继承测试类
+ *
+ * 官方文档:
+ * https://developer.work.weixin.qq.com/document/path/92124
+ */
+@Slf4j
+@Guice(modules = ApiTestModule.class)
+public class WxCpExternalContactTest {
+
+  @Inject
+  private WxCpService wxCpService;
+  @Inject
+  protected ApiTestModule.WxXmlCpInMemoryConfigStorage configStorage;
+
+  @Test
+  public void testGetExternalContact() throws WxErrorException {
+    String externalUserId = this.configStorage.getExternalUserId();
+    WxCpUserExternalUnassignList unassignList = this.wxCpService.getExternalContactService().listUnassignedList(null, null, 100);
+    log.info(unassignList.toJson());
+
+    // test str
+    String result = "{\"errcode\":0,\"errmsg\":\"ok\",\"info\":[{\"handover_userid\":\"zhangsan\",\"external_userid\":\"woAJ2GCAAAd4uL12hdfsdasassdDmAAAAA\",\"dimission_time\":1550838571},{\"handover_userid\":\"lisi\",\"external_userid\":\"wmAJ2GCAAAzLTI123ghsdfoGZNqqAAAA\",\"dimission_time\":1550661468}],\"is_last\":false,\"next_cursor\":\"aSfwejksvhToiMMfFeIGZZ\"}";
+    WxCpUserExternalUnassignList json = WxCpUserExternalUnassignList.fromJson(result);
+    log.info(json.toJson());
+
+  }
+
+}
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpLivingTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpLivingTest.java
new file mode 100644
index 000000000..295f0497f
--- /dev/null
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpLivingTest.java
@@ -0,0 +1,99 @@
+package me.chanjar.weixin.cp.api;
+
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl;
+import me.chanjar.weixin.cp.bean.living.*;
+import me.chanjar.weixin.cp.config.WxCpConfigStorage;
+import me.chanjar.weixin.cp.demo.WxCpDemoInMemoryConfigStorage;
+import org.eclipse.jetty.util.ajax.JSON;
+import org.testng.annotations.Test;
+
+import java.io.InputStream;
+import java.util.Date;
+
+/**
+ * 企业微信直播测试类.
+ * 官方文档:https://open.work.weixin.qq.com/api/doc/90000/90135/93632
+ *
+ * @author Wang_Wong
+ */
+@Slf4j
+public class WxCpLivingTest {
+
+  private static WxCpConfigStorage wxCpConfigStorage;
+  private static WxCpService wxCpService;
+
+  @Test
+  public void test() throws WxErrorException {
+
+    InputStream inputStream = ClassLoader.getSystemResourceAsStream("test-config.xml");
+    WxCpDemoInMemoryConfigStorage config = WxCpDemoInMemoryConfigStorage.fromXml(inputStream);
+
+    wxCpConfigStorage = config;
+    wxCpService = new WxCpServiceImpl();
+    wxCpService.setWxCpConfigStorage(config);
+
+    String livingCode = wxCpService.getLivingService().getLivingCode("o50by5NezHciWnoexJsrI49ILNqI", "lvOQpTDwAAD2MYuOq9y_bmLNMJfbbdGw");
+    log.info(JSON.toString(livingCode));
+
+    // 直播详情
+    WxCpLivingInfo livingInfo = wxCpService.getLivingService().getLivingInfo("lvOQpTDwAAcP9wNOSSxTwpbni-TMPNSg");
+    log.info(livingInfo.toJson());
+
+    // 直播观看明细
+    WxCpWatchStat watchStat = wxCpService.getLivingService().getWatchStat("lvOQpTDwAAcP9wNOSSxTwpbni-TMPNSg", 0);
+    log.info(watchStat.toJson());
+
+    final String watchStateJson = "{\"errcode\":0,\"errmsg\":\"ok\",\"ending\":1,\"next_key\":\"NEXT_KEY\",\"stat_info\":{\"users\":[{\"userid\":\"userid\",\"watch_time\":30,\"is_comment\":1,\"is_mic\":1}],\"external_users\":[{\"external_userid\":\"external_userid1\",\"type\":1,\"name\":\"user name\",\"watch_time\":30,\"is_comment\":1,\"is_mic\":1},{\"external_userid\":\"external_userid2\",\"type\":2,\"name\":\"user_name\",\"watch_time\":30,\"is_comment\":1,\"is_mic\":1}]}}";
+
+    WxCpWatchStat wxCpWatchStat = WxCpWatchStat.fromJson(watchStateJson);
+    log.info(wxCpWatchStat.toJson());
+
+    // 直播观众信息
+    final String livingShareInfo = "{\"errcode\":0,\"errmsg\":\"ok\",\"livingid\":\"livingid\",\"viewer_userid\":\"viewer_userid\",\"viewer_external_userid\":\"viewer_external_userid\",\"invitor_userid\":\"invitor_userid\",\"invitor_external_userid\":\"invitor_external_userid\"}";
+
+    WxCpLivingShareInfo wxCpLivingShareInfo = WxCpLivingShareInfo.fromJson(livingShareInfo);
+    log.info(wxCpLivingShareInfo.toJson());
+
+    // 获取成员直播ID列表
+    WxCpLivingResult.LivingIdResult livingResult = wxCpService.getLivingService().getUserAllLivingId("ChenHu", null, null);
+    log.info(livingResult.toJson());
+
+    String livinglist = "{\"errcode\":0,\"errmsg\":\"ok\",\"next_cursor\":\"next_cursor\",\"livingid_list\":[\"livingid1\",\"livingid2\"]}";
+    WxCpLivingResult.LivingIdResult livingIdResult = WxCpLivingResult.LivingIdResult.fromJson(livinglist);
+    log.info(livingIdResult.toJson());
+
+
+    log.info("{}", new Date().getTime());
+    // 创建预约直播
+    String create = "{\"anchor_userid\":\"ChenHu\",\"theme\":\"theme\",\"living_start\":164037820420,\"living_duration\":3600,\"description\":\"test description\",\"type\":4,\"remind_time\":60,\"activity_cover_mediaid\":\"MEDIA_ID\",\"activity_share_mediaid\":\"MEDIA_ID\",\"activity_detail\":{\"description\":\"活动描述,非活动类型的直播不用传\",\"image_list\":[\"xxxx1\",\"xxxx1\"]}}";
+    WxCpLivingCreateRequest request = WxCpLivingCreateRequest.fromJson(create);
+    String livingId = wxCpService.getLivingService().livingCreate(request);
+    log.info("livingId为:{}", livingId);
+
+
+    String modify = "{\"livingid\": \""+ livingId +"\",\"theme\":\"theme\",\"living_start\":164047820420,\"living_duration\":3600,\"description\":\"描述:description\",\"type\":1,\"remind_time\":60}";
+    WxCpLivingModifyRequest modifyReq = WxCpLivingModifyRequest.fromJson(modify);
+    WxCpLivingResult result = wxCpService.getLivingService().livingModify(modifyReq);
+    log.info("result:{}", result.toJson());
+
+
+    // 取消预约直播
+//    WxCpLivingResult result2 = wxCpService.getLivingService().livingCancel("lvOQpTDwAA0KyLmWZhf_LIENzYIBVD2g");
+    WxCpLivingResult result2 = wxCpService.getLivingService().livingCancel(livingId);
+    log.info("取消预约直播为:{}", result2.toJson());
+
+
+    // 删除直播回放
+//    WxCpLivingResult response = wxCpService.getLivingService().deleteReplayData("lvOQpTDwAAVdCHyXMgSK63TKPfKDII7w");
+//    log.info(response.toJson());
+
+
+  }
+
+  public static void main(String[] args){
+    log.info("{}", new Date().getTime());
+  }
+
+}
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMsgAuditTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMsgAuditTest.java
new file mode 100644
index 000000000..457996a0e
--- /dev/null
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMsgAuditTest.java
@@ -0,0 +1,433 @@
+package me.chanjar.weixin.cp.api;
+import com.google.common.collect.Lists;
+
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl;
+import me.chanjar.weixin.cp.bean.msgaudit.*;
+import me.chanjar.weixin.cp.config.WxCpConfigStorage;
+import me.chanjar.weixin.cp.demo.WxCpDemoInMemoryConfigStorage;
+import org.testng.annotations.Test;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * 企业微信会话内容存档测试类.
+ * 官方文档:https://developer.work.weixin.qq.com/document/path/91360
+ *
+ * @author Wang_Wong
+ * @date 2022-01-17
+ */
+@Slf4j
+public class WxCpMsgAuditTest {
+
+  private static WxCpConfigStorage wxCpConfigStorage;
+  private static WxCpService cpService;
+
+  // com.binarywang.spring.starter.wxjava.cp.config.WxCpServiceAutoConfiguration
+  @Test
+  public void test() throws Exception {
+
+    InputStream inputStream = ClassLoader.getSystemResourceAsStream("test-config.xml");
+    WxCpDemoInMemoryConfigStorage config = WxCpDemoInMemoryConfigStorage.fromXml(inputStream);
+
+    wxCpConfigStorage = config;
+    cpService = new WxCpServiceImpl();
+    cpService.setWxCpConfigStorage(config);
+
+    /**
+     * 配置:
+     * 
+     *   wwa3bexxXXXXXX
+     *   自定义agentId
+     *   xIpum7Yt4NMXXXXXXX
+     *   2bSNqXXXXXXXX
+     *   MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDZuPVMyVyMvJkdSCZA893B2pggd1r95T8k2QZgz+VejtaDJCbD60mYoW1Uwwlwuqy8W78M6MmXsskn+5XunyR1WJlJGqgi0OMVGYvSfkNb9kD50fM21CGLcN1y4miL9fVNBIsvJmIUeJCNS8TioAVGFvh2EgzjqTR1gHfDwu8If7rfGmTHkPYL8hQxMg/EQ3451JOIBHSa7EQSx64SIoVWEgSDFQjGEpjUiJRfciyyz+nTSkEDgFa9hpyTS6E0c/3Q5lVDFgIwTArC19XBFKb00PbcFuLriOIsTBX4K9XWBtefVXowAdqUVQDH6BNUIK7/iVPQ4L3p+F5DBOrx8I/7AgMBAAECggEBAK53C/nwEX2lU3ynaB/8SuMga274ta1mmmbIkdfaQA65nyOPQJEWZe8szBN0BoiSzgBR9JI/p+srlQ25CLgiRnDSAmMWPU1I3e72fZi7HPcAKakGmEKDUi4OzyVUUDp3aY3B6lZqB4Yn5o2S/b4sRI2ZspfKdxGncSYHP/Far3i6hzq2C1hbyYM6HkHPcrQ+z6ir6GxjLvHXssVJ+/C0HMsVIQAWPyEGbzWozS+EswmQ+itk+7cewiLWbaCSp6lsjHKGTxJwCxRes0nUt2SfkLnIUkDLxB7c6zDQJCn1K2UckCjNBlCWl+oDWLkLQ7UAJ+4IYYSslR4wXzRg8PplW8ECgYEA9VlEprEoG2oSn3HXIMFg0MANViQe89QJQdwd7D5h4FLxXQLItxqmZj77iktlzlICcK9WT9WHRY1AOilsuMaDmY0VH3Z8r/X9BU712KFJqMYH5CNxrqHOya3BG+CclEKToaOTmo9kiOpFAMNSuuWs6gvILJ0CKEmSUo5G9fJu4fkCgYEA4yypHoRZIP0mDdVDeVtdHHcq5JdWF6xbAFs4P57VHG1KDMWouk3IHSeO279gEIwcBAdaLcMMgFfzyQBwcisxjC76oyoZnbSntB7ZMFdPqALKfxIdleLilbASuRKesVAF+OgOx/yp/aQUeLG2pVBivgn2TyGMwjnxznTh9vh+vpMCgYEAmOva7krdRLkIgnjiLXhab8JEjbxVzoQKgRJBVE5NkxQffGmP0RC7Rl9bSQdVnRNgkfu3QGtGtQMlVRscuM6Cl+JnmASyErqvye89LJja4GcN5BRzdvVDflDeXBHThlU4zza1eVCGyQ+7ko4rsnIVJIvTaHs0LQguO2aStBk3I4ECgYAyBsO3VK3L9fNLWItjThtTCWsIq8rpq6reiTf5yqBjgi2sYlqlrDtFMFDlU190RWZl/Lh/G1TFbpjgypf4jEp89Ft9UugRMpc7sw9g9dk0xmiRUwvw1eXP0NZOqysHIPgvt+qJX7qPgHKBoaD3Bpy3/Lmg82Jr4xa8wECCgnZmwQKBgH7hirPs1/HqBrbxS726IZUf9QTmVkyOYIwzuwFYKb/+4caSah+iaXexVux0xS5tchj/6c1dQSKJmlegV8smIb6EEcko7llA1y1P5QFtXtaaRd07tTsv3BKEg496YLRjbxPzgJn6Fsoz3TTdGwESL8Q3I2h0WmVVhmr/rjr+RkWQ
+     *
+     * 注意:最好先配置lib开头的系统库,再配置sdk类库,配置绝对路径,最好配置为linux路径
+     *   windows:
+     *   D:/WorkSpace/libcrypto-1_1-x64.dll,libssl-1_1-x64.dll,libcurl-x64.dll,WeWorkFinanceSdk.dll,libWeWorkFinanceSdk_Java.so
+     *   linux:
+     *   /www/osfile/work_msg_storage/libcrypto-1_1-x64.dll,libssl-1_1-x64.dll,libcurl-x64.dll,WeWorkFinanceSdk.dll,libWeWorkFinanceSdk_Java.so
+     * 
+     */
+
+    /**
+     * 建议放到redis,本次请求获取消息记录开始的seq值。首次访问填写0,非首次使用上次企业微信返回的最大seq。允许从任意seq重入拉取。
+     */
+    long seq = 0L;
+
+    /**
+     * 图片,语音,视频,表情,文件,音频存档消息,音频共享文档消息调用  获取媒体消息
+     */
+    List mediaType = Arrays.asList(new String[]{"image", "voice", "video", "emotion", "file", "meeting_voice_call", "voip_doc_share"});
+
+    // 模拟多次拉取数据,根据seq拉取
+    for (int i = 0; i < 3; i++) {
+      // 本次请求获取消息记录开始的seq值。首次访问填写0,非首次使用上次企业微信返回的最大seq。允许从任意seq重入拉取。
+      WxCpChatDatas chatDatas = cpService.getMsgAuditService().getChatDatas(seq, 10L, null, null, 1000L);
+      if (chatDatas != null && chatDatas.getChatData().size() > 0) {
+
+        List chatdata = chatDatas.getChatData();
+        Iterator iterator = chatdata.iterator();
+        while (iterator.hasNext()) {
+          WxCpChatDatas.WxCpChatData chatData = iterator.next();
+          seq = chatData.getSeq();
+
+          // 数据
+//          String msgId = chatData.getMsgId();
+//          String encryptChatMsg = chatData.getEncryptChatMsg();
+//          String encryptRandomKey = chatData.getEncryptRandomKey();
+//          Integer publickeyVer = chatData.getPublickeyVer();
+
+          // 获取明文数据
+          final String chatPlainText = cpService.getMsgAuditService().getChatPlainText(chatData);
+          final WxCpChatModel wxCpChatModel = WxCpChatModel.fromJson(chatPlainText);
+          log.info("明文数据为:{}", wxCpChatModel.toJson());
+
+          // 获取消息数据
+          final WxCpChatModel decryptData = cpService.getMsgAuditService().getDecryptData(chatData);
+          log.info("获取消息数据为:{}", decryptData.toJson());
+
+          /**
+           * 注意:
+           * 根据上面返回的文件类型来获取媒体文件,
+           * 不同的文件类型,拼接好存放文件的绝对路径,写入文件流,获取媒体文件。(拼接绝对文件路径的原因,以便上传到腾讯云或阿里云对象存储)
+           *
+           * 目标文件绝对路径+实际文件名,比如:/usr/local/file/20220114/474f866b39d10718810d55262af82662.gif
+           */
+          String path = "/usr/local/file/";
+          String msgType = decryptData.getMsgType();
+          if (mediaType.contains(decryptData.getMsgType())) {
+            // 文件后缀
+            String suffix = "";
+            // 文件名md5
+            String md5Sum = "";
+            // sdkFileId
+            String sdkFileId = "";
+            switch (msgType) {
+              case "image":
+                suffix = ".jpg";
+                md5Sum = decryptData.getImage().getMd5Sum();
+                sdkFileId = decryptData.getImage().getSdkFileId();
+                break;
+              case "voice":
+                suffix = ".amr";
+                md5Sum = decryptData.getVoice().getMd5Sum();
+                sdkFileId = decryptData.getVoice().getSdkFileId();
+                break;
+              case "video":
+                suffix = ".mp4";
+                md5Sum = decryptData.getVideo().getMd5Sum();
+                sdkFileId = decryptData.getVideo().getSdkFileId();
+                break;
+              case "emotion":
+                md5Sum = decryptData.getEmotion().getMd5Sum();
+                sdkFileId = decryptData.getEmotion().getSdkFileId();
+                int type = decryptData.getEmotion().getType();
+                switch (type) {
+                  case 1:
+                    suffix = ".gif";
+                    break;
+                  case 2:
+                    suffix = ".png";
+                    break;
+                  default:
+                    return;
+                }
+                break;
+              case "file":
+                md5Sum = decryptData.getFile().getMd5Sum();
+                suffix = "." + decryptData.getFile().getFileExt();
+                sdkFileId = decryptData.getFile().getSdkFileId();
+                break;
+              // 音频存档消息
+              case "meeting_voice_call":
+
+                md5Sum = decryptData.getVoiceId();
+                sdkFileId = decryptData.getMeetingVoiceCall().getSdkFileId();
+                for (WxCpChatModel.MeetingVoiceCall.DemoFileData demofiledata : decryptData.getMeetingVoiceCall().getDemoFileData()) {
+                  String demoFileDataFileName = demofiledata.getFileName();
+                  suffix = demoFileDataFileName.substring(demoFileDataFileName.lastIndexOf(".") + 1);
+                }
+
+                break;
+              // 音频共享文档消息
+              case "voip_doc_share":
+
+                md5Sum = decryptData.getVoipId();
+                WxCpFileItem docShare = decryptData.getVoipDocShare();
+                String fileName = docShare.getFileName();
+                suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
+
+                break;
+              default:
+                return;
+            }
+
+            // 拉取媒体文件
+            String targetPath = path + md5Sum + suffix;
+            cpService.getMsgAuditService().getMediaFile(sdkFileId, null, null, 1000L, targetPath);
+
+          }
+
+        }
+
+      }
+
+    }
+
+
+    /**
+     * 文本
+     */
+//    String text = "{\"msgid\":\"CAQQluDa4QUY0On2rYSAgAMgzPrShAE=\",\"action\":\"send\",\"from\":\"XuJinSheng\",\"tolist\":[\"icefog\"],\"roomid\":\"\",\"msgtime\":1547087894783,\"msgtype\":\"text\",\"text\":{\"content\":\"test\"}}";
+    String text = "{\"msgid\":\"CAQQluDa4QUY0On2rYSAgAMgzPrShAE=\",\"action\":\"send\",\"from\":\"XuJinSheng\",\"tolist\":[\"icefog\"],\"roomid\":\"\",\"msgtime\":1547087894783,\"msgtype\":\"text\",\"text\":{\"content\":\"这是一条引用/回复消息:\\\"\\n------\\n@nick777\"}}";
+    WxCpChatModel modelText = WxCpChatModel.fromJson(text);
+    log.info("数据为:" + modelText.toJson());
+
+
+    /**
+     * 图片
+     */
+    String image = "{\"msgid\":\"CAQQvPnc4QUY0On2rYSAgAMgooLa0Q8=\",\"action\":\"send\",\"from\":\"XuJinSheng\",\"tolist\":[\"icefog\"],\"roomid\":\"\",\"msgtime\":0,\"msgtype\":\"image\",\"image\":{\"md5sum\":\"50de8e5ae8ffe4f1df7a93841f71993a\",\"filesize\":70961,\"sdkfileid\":\"CtYBMzA2OTAyMDEwMjA0NjIzMDYwMDIwMTAwMDIwNGI3ZmU0MDZlMDIwMzBmNTliMTAyMDQ1YzliNTQ3NzAyMDQ1YzM3M2NiYzA0MjQ2NjM0MzgzNTM0NjEzNTY1MmQzNDYxMzQzODJkMzQzMTYxNjEyZDM5NjEzOTM2MmQ2MTM2NjQ2NDY0NjUzMDY2NjE2NjM1MzcwMjAxMDAwMjAzMDExNTQwMDQxMDUwZGU4ZTVhZThmZmU0ZjFkZjdhOTM4NDFmNzE5OTNhMDIwMTAyMDIwMTAwMDQwMBI4TkRkZk1UWTRPRGcxTVRBek1ETXlORFF6TWw4eE9UUTVOamN6TkRZMlh6RTFORGN4TWpNNU1ERT0aIGEwNGQwYWUyM2JlYzQ3NzQ5MjZhNWZjMjk0ZTEyNTkz\"}}";
+    WxCpChatModel modelImage = WxCpChatModel.fromJson(image);
+    log.info("数据为:" + modelImage.toJson());
+
+
+    /**
+     * 撤回消息
+     */
+    String revoke = "{\"msgid\":\"15775510700152506326_1603875615\",\"action\":\"recall\",\"from\":\"kenshin\",\"tolist\":[\"wmUu0zBgAALV7ZymkcMyxvbTe8YdWxxA\"],\"roomid\":\"\",\"msgtime\":1603875615723,\"msgtype\":\"revoke\",\"revoke\":{\"pre_msgid\":\"14822339130656386894_1603875600\"}}";
+    WxCpChatModel modelRevoke = WxCpChatModel.fromJson(revoke);
+    log.info("数据为:" + modelRevoke.toJson());
+
+
+    /**
+     * 同意会话聊天内容
+     */
+    String agree = "{\"msgid\":\"8891446340739254950_1603875826\",\"action\":\"send\",\"from\":\"wmGAgeDQAAvQeaTqWwkMTxGMkvI7OOuQ\",\"tolist\":[\"kenshin\"],\"roomid\":\"\",\"msgtime\":1603875826656,\"msgtype\":\"agree\",\"agree\":{\"userid\":\"wmGAgeDQAAvQeaTqWwkMTxGMkvI7OOuQ\",\"agree_time\":1603875826656}}";
+    String disagree = "{\"msgid\":\"17972321270926900092_1603875944\",\"action\":\"send\",\"from\":\"wmErxtDgAA9AW32YyyuYRimKr7D1KWlw\",\"tolist\":[\"kenshin\"],\"roomid\":\"\",\"msgtime\":1603875944122,\"msgtype\":\"disagree\",\"disagree\":{\"userid\":\"wmErxtDgAA9AW32YyyuYRimKr7D1KWlw\",\"disagree_time\":1603875944122}}";
+    WxCpChatModel modelAgree = WxCpChatModel.fromJson(agree);
+    WxCpChatModel modelDisagree = WxCpChatModel.fromJson(disagree);
+    log.info("数据为:" + modelAgree.toJson());
+    log.info("数据为:" + modelDisagree.toJson());
+
+
+    /**
+     * 语音
+     */
+    String voice = "{\"msgid\":\"10958372969718811103_1603875609\",\"action\":\"send\",\"from\":\"wmGAgeDQAAdBjb8CK4ieMPRm7Cqm-9VA\",\"tolist\":[\"kenshin\"],\"roomid\":\"\",\"msgtime\":1603875609704,\"msgtype\":\"voice\",\"voice\":{\"md5sum\":\"9db09c7fa627c9e53f17736c786a74d5\",\"voice_size\":6810,\"play_length\":10,\"sdkfileid\":\"kcyZjZqOXhETGYxajB2Zkp5Rk8zYzh4RVF3ZzZGdXlXNWRjMUoxVGZxbzFTTDJnQ2YxL0NraVcxUUJNK3VUamhEVGxtNklCbjZmMEEwSGRwN0h2cU1GQTU1MDRSMWdTSmN3b25ZMkFOeG5hMS90Y3hTQ0VXRlVxYkR0Ymt5c3JmV2VVcGt6UlNXR1ZuTFRWVGtudXVldDRjQ3hscDBrMmNhMFFXVnAwT3Y5NGVqVGpOcWNQV2wrbUJwV01TRm9xWmNDRVVrcFY5Nk9OUS9GbXIvSmZvOVVZZjYxUXBkWnMvUENkVFQxTHc2N0drb2pJT0FLZnhVekRKZ1FSNDU3ZnZtdmYvTzZDOG9DRXl2SUNIOHc9PRI0TkRkZk56ZzRNVE13TVRjMk5qQTRNak0yTmw4ek5qRTVOalExTjE4eE5qQXpPRGMxTmpBNRogNzM3MDY2NmM2YTc5Njg3NDdhNzU3NDY0NzY3NTY4NjY=\"}}";
+    WxCpChatModel modelVoice = WxCpChatModel.fromJson(voice);
+    log.info("数据为:" + modelVoice.toJson());
+
+
+    /**
+     * 视频
+     */
+    String video = "{\"msgid\":\"17955920891003447432_1603875627\",\"action\":\"send\",\"from\":\"kenshin\",\"tolist\":[\"wmGAgeDQAAHuRJbt4ZQI_1cqoQcf41WQ\"],\"roomid\":\"\",\"msgtime\":1603875626823,\"msgtype\":\"video\",\"video\":{\"md5sum\":\"d06fc80c01d6fbffcca3b229ba41eac6\",\"filesize\":15169724,\"play_length\":108,\"sdkfileid\":\"MzAzMjYxMzAzNTYzMzgzMjMyMzQwMjAxMDAwMjA0MDBlNzc4YzAwNDEwZDA2ZmM4MGMwMWQ2ZmJmZmNjYTNiMjI5YmE0MWVhYzYwMjAxMDQwMjAxMDAwNDAwEjhORGRmTVRZNE9EZzFNREEyTlRjM056QXpORjgxTWpZeE9USTBOek5mTVRZd016ZzNOVFl5Tnc9PRogNTIzNGQ1NTQ5N2RhNDM1ZDhlZTU5ODk4NDQ4NzRhNDk=\"}}";
+    WxCpChatModel modelVideo = WxCpChatModel.fromJson(video);
+    log.info("数据为:" + modelVideo.toJson());
+
+
+    /**
+     * 名片
+     */
+    String card = "{\"msgid\":\"13714216591700685558_1603875680\",\"action\":\"send\",\"from\":\"kenshin\",\"tolist\":[\"wmGAgeDQAAy2Dtr0F8aK4dTuatfm-5Rg\"],\"roomid\":\"\",\"msgtime\":1603875680377,\"msgtype\":\"card\",\"card\":{\"corpname\":\"微信联系人\",\"userid\":\"wmGAgeDQAAGjFmfnP7A3j2JxQDdLNhSw\"}}";
+    WxCpChatModel modelCard = WxCpChatModel.fromJson(card);
+    log.info("数据为:" + modelCard.toJson());
+
+
+    /**
+     * 位置
+     */
+    String location = "{\"msgid\":\"2641513858500683770_1603876152\",\"action\":\"send\",\"from\":\"icefog\",\"tolist\":[\"wmN6etBgAA0sbJ3invMvRxPQDFoq9uWA\"],\"roomid\":\"\",\"msgtime\":1603876152141,\"msgtype\":\"location\",\"location\":{\"longitude\":116.586285899,\"latitude\":39.911125799,\"address\":\"北京市xxx区xxx路xxx大厦x座\",\"title\":\"xxx管理中心\",\"zoom\":15}}";
+    WxCpChatModel modelLocation = WxCpChatModel.fromJson(location);
+    log.info("数据为:" + modelLocation.toJson());
+
+
+    /**
+     * 表情
+     */
+    String emotion = "{\"msgid\":\"6623217619416669654_1603875612\",\"action\":\"send\",\"from\":\"icef\",\"tolist\":[\"wmErxtDgAAhteCglUZH2kUt3rq431qmg\"],\"roomid\":\"\",\"msgtime\":1603875611148,\"msgtype\":\"emotion\",\"emotion\":{\"type\":1,\"width\":290,\"height\":290,\"imagesize\":962604,\"md5sum\":\"94c2b0bba52cc456cb8221b248096612\",\"sdkfileid\":\"4eE1ESTVNalE1TnprMFh6RTJNRE00TnpVMk1UST0aIDc0NzI2NjY1NzE3NTc0Nzg2ZDZlNzg2YTY5NjY2MTYx\"}}";
+    WxCpChatModel modelEmotion = WxCpChatModel.fromJson(emotion);
+    log.info("数据为:" + modelEmotion.toJson());
+
+
+    /**
+     * 文件
+     */
+    String file = "{\"msgid\":\"18039699423706571225_1603875608\",\"action\":\"send\",\"from\":\"kens\",\"tolist\":[\"wmErxtDgAArDlFIhf76O6w4GxU81al8w\"],\"roomid\":\"\",\"msgtime\":1603875608214,\"msgtype\":\"file\",\"file\":{\"md5sum\":\"18e93fc2ea884df23b3d2d3b8667b9f0\",\"filename\":\"资料.docx\",\"fileext\":\"docx\",\"filesize\":18181,\"sdkfileid\":\"E4ODRkZjIzYjNkMmQzYjg2NjdiOWYwMDIwMTA1MDIwMTAwMDQwMBI4TkRkZk1UWTRPRGcxTURrek9UZzBPVEF6TTE4eE1EUXpOVGcxTlRVNVh6RTJNRE00TnpVMk1EZz0aIDMwMzkzMzY0NjEzNjM3NjY2NDY1NjMzNjYxMzIzNzYx\"}}";
+    WxCpChatModel modelFile = WxCpChatModel.fromJson(file);
+    log.info("数据为:" + modelFile.toJson());
+
+
+    /**
+     * 链接
+     */
+    String link = "{\"msgid\":\"11788441727514772650_1603875624\",\"action\":\"send\",\"from\":\"kenshin\",\"tolist\":[\"0000726\"],\"roomid\":\"\",\"msgtime\":1603875624476,\"msgtype\":\"link\",\"link\":{\"title\":\"邀请你加入群聊\",\"description\":\"技术支持群,进入可查看详情\",\"link_url\":\"https://work.weixin.qq.com/wework_admin/external_room/join/exceed?vcode=xxx\",\"image_url\":\"https://wework.qpic.cn/wwpic/xxx/0\"}}";
+    WxCpChatModel modelLink = WxCpChatModel.fromJson(link);
+    log.info("数据为:" + modelLink.toJson());
+
+
+    /**
+     * 小程序消息
+     */
+    String weapp = "{\"msgid\":\"11930598857592605935_1603875608\",\"action\":\"send\",\"from\":\"kens\",\"tolist\":[\"wmGAgeDQAAsgQetTQGqRbMxrkodpM3fA\"],\"roomid\":\"\",\"msgtime\":1603875608691,\"msgtype\":\"weapp\",\"weapp\":{\"title\":\"开始聊天前请仔细阅读服务须知事项\",\"description\":\"客户需同意存档聊天记录\",\"username\":\"xxx@app\",\"displayname\":\"服务须知\"}}";
+    WxCpChatModel modelWeapp = WxCpChatModel.fromJson(weapp);
+    log.info("数据为:" + modelWeapp.toJson());
+
+
+    /**
+     * 会话记录消息
+     */
+    String chatrecord = "{\"msgid\":\"11354299838102555191_1603875658\",\"action\":\"send\",\"from\":\"ken\",\"tolist\":[\"icef\"],\"roomid\":\"\",\"msgtime\":1603875657905,\"msgtype\":\"chatrecord\",\"chatrecord\":{\"title\":\"群聊\",\"item\":[{\"type\":\"ChatRecordText\",\"msgtime\":1603875610,\"content\":\"{\\\"content\\\":\\\"test\\\"}\",\"from_chatroom\":false},{\"type\":\"ChatRecordText\",\"msgtime\":1603875620,\"content\":\"{\\\"content\\\":\\\"test2\\\"}\",\"from_chatroom\":false}]}}";
+    WxCpChatModel modelChatRecord = WxCpChatModel.fromJson(chatrecord);
+    log.info("数据为:" + modelChatRecord.toJson());
+
+
+    /**
+     * 填表消息
+     */
+    String collect = "{\"msgid\":\"2500536226619379797_1576034482\",\"action\":\"send\",\"from\":\"nick\",\"tolist\":[\"XuJinSheng\",\"15108264797\"],\"roomid\":\"wrjc7bDwYAOAhf9quEwRRxyyoMm0QAAA\",\"msgtime\":1576034482344,\"msgtype\":\"collect\",\"collect\":{\"room_name\":\"这是一个群\",\"creator\":\"nick\",\"create_time\":\"2019-12-11 11:21:22\",\"title\":\"这是填表title\",\"details\":[{\"id\":1,\"ques\":\"表项1,文本\",\"type\":\"Text\"},{\"id\":2,\"ques\":\"表项2,数字\",\"type\":\"Number\"},{\"id\":3,\"ques\":\"表项3,日期\",\"type\":\"Date\"},{\"id\":4,\"ques\":\"表项4,时间\",\"type\":\"Time\"}]}}";
+    WxCpChatModel modelCollect = WxCpChatModel.fromJson(collect);
+    log.info("数据为:" + modelCollect.toJson());
+
+
+    /**
+     * 红包消息
+     */
+    String redpacket = "{\"msgid\":\"333590477316965370_1603877439\",\"action\":\"send\",\"from\":\"kens\",\"tolist\":[\"1000000444696\"],\"roomid\":\"\",\"msgtime\":1603877439038,\"msgtype\":\"redpacket\",\"redpacket\":{\"type\":1,\"wish\":\"恭喜发财,大吉大利\",\"totalcnt\":1,\"totalamount\":3000}}";
+    WxCpChatModel modelRedpacket = WxCpChatModel.fromJson(redpacket);
+    log.info("数据为:" + modelRedpacket.toJson());
+
+
+    /**
+     * 会议邀请信息
+     */
+    String meeting = "{\"msgid\":\"5935786683775673543_1603877328\",\"action\":\"send\",\"from\":\"ken\",\"tolist\":[\"icef\",\"test\"],\"roomid\":\"wr2vOpDgAAN4zVWKbS\",\"msgtime\":1603877328914,\"msgtype\":\"meeting\",\"meeting\":{\"topic\":\"夕会\",\"starttime\":1603877400,\"endtime\":1603881000,\"address\":\"\",\"remarks\":\"\",\"meetingtype\":102,\"meetingid\":1210342560,\"status\":1}}";
+    WxCpChatModel modelMeeting = WxCpChatModel.fromJson(meeting);
+    log.info("数据为:" + modelMeeting.toJson());
+
+
+    /**
+     * 切换企业日志
+     */
+    String switchlog = "{\"msgid\":\"125289002219525886280\",\"action\":\"switch\",\"time\":1554119421840,\"user\":\"XuJinSheng\"}";
+    WxCpChatModel modelSwitchLog = WxCpChatModel.fromJson(switchlog);
+    log.info("数据为:" + modelSwitchLog.toJson());
+
+
+    /**
+     * 在线文档消息
+     */
+    String docMsg = "{\"msgid\":\"9732089160923053207_1603877765\",\"action\":\"send\",\"from\":\"ken\",\"tolist\":[\"icef\",\"test\"],\"roomid\":\"wrJawBCQAAStr3jxVxEH\",\"msgtime\":1603877765291,\"msgtype\":\"docmsg\",\"doc\":{\"title\":\"测试&演示客户\",\"doc_creator\":\"test\",\"link_url\":\"https://doc.weixin.qq.com/txdoc/excel?docid=xxx\"}}";
+    WxCpChatModel modelDocMsg = WxCpChatModel.fromJson(docMsg);
+    log.info("数据为:" + modelDocMsg.toJson());
+
+
+    /**
+     * MarkDown格式消息
+     */
+    String markDown = "{\"msgid\":\"7546287934688259248_1603875715\",\"action\":\"send\",\"from\":\"ken\",\"tolist\":[\"icef\",\"test\"],\"roomid\":\"wr0SfLCgAAgCaCPeM33UNe\",\"msgtime\":1603875715782,\"msgtype\":\"markdown\",\"info\":{\"content\":\"请前往系统查看,谢谢。\"}}";
+    WxCpChatModel modelMarkDown = WxCpChatModel.fromJson(markDown);
+    log.info("数据为:" + modelMarkDown.toJson());
+
+
+    /**
+     * 图文消息
+     */
+    String news = "{\"msgid\":\"118732825779547782215\",\"action\":\"send\",\"from\":\"kens\",\"tolist\":[\"icef\",\"test\"],\"roomid\":\"wrErxtDgAA0jgXE5\",\"msgtime\":1603876045165,\"msgtype\":\"news\",\"info\":{\"item\":[{\"title\":\"service \",\"description\":\"test\",\"url\":\"http://xxx\",\"picurl\":\"https://www.qq.com/xxx.jpg\"}]}}";
+    WxCpChatModel modelNews = WxCpChatModel.fromJson(news);
+    log.info("数据为:" + modelNews.toJson());
+
+
+    /**
+     * 日程消息
+     */
+    String calendar = "{\"msgid\":\"2345881211604379705_1603877680\",\"action\":\"send\",\"from\":\"ken\",\"tolist\":[\"icef\",\"test\"],\"roomid\":\"wr2LO0CAAAFrTZCGWWAxBA\",\"msgtime\":1603877680795,\"msgtype\":\"calendar\",\"calendar\":{\"title\":\"xxx业绩复盘会\",\"creatorname\":\"test\",\"attendeename\":[\"aaa\",\"bbb\"],\"starttime\":1603882800,\"endtime\":1603886400,\"place\":\"\",\"remarks\":\"\"}}";
+    WxCpChatModel modelCalendar = WxCpChatModel.fromJson(calendar);
+    log.info("数据为:" + modelCalendar.toJson());
+
+
+    /**
+     * 混合消息
+     */
+    String mixed = "{\"msgid\":\"DAQQluDa4QUY0On4kYSABAMgzPrShAE=\",\"action\":\"send\",\"from\":\"HeMiao\",\"tolist\":[\"HeChangTian\",\"LiuZeYu\"],\"roomid\":\"wr_tZ2BwAAUwHpYMwy9cIWqnlU3Hzqfg\",\"msgtime\":1577414359072,\"msgtype\":\"mixed\",\"mixed\":{\"item\":[{\"type\":\"text\",\"content\":\"{\\\"content\\\":\\\"你好[微笑]\\\\n\\\"}\"},{\"type\":\"image\",\"content\":\"{\\\"md5sum\\\":\\\"368b6c18c82e6441bfd89b343e9d2429\\\",\\\"filesize\\\":13177,\\\"sdkfileid\\\":\\\"CtYBMzA2OTAyMDEwMjA0NjIzMDYwMDIwMTAwMDWwNDVmYWY4Y2Q3MDIwMzBmNTliMTAyMDQwYzljNTQ3NzAyMDQ1ZTA1NmFlMjA0MjQ2NjM0NjIzNjY2MzYzNTMyMmQzNzYxMzQ2NDJkMzQ2MjYxNjQyZDM4MzMzMzM4MmQ3MTYyMzczMTM4NjM2NDYxMzczMjY2MzkwMjAxMDAwMjAzMDIwMDEwMDQxMDM2OGI2YzE4YzgyZTY0NDFiZmQ4OWIyNDNlOWQyNDI4MDIwMTAyMDIwMTAwMDQwMBI4TkRkZk2UWTRPRGcxTVRneE5URTFNRGc1TVY4eE1UTTFOak0yTURVeFh6RTFOemMwTVRNek5EYz0aIDQzMTY5NDFlM2MxZDRmZjhhMjEwY2M0NDQzZGUXOTEy\\\"}\"}]}}";
+    WxCpChatModel modelMixed = WxCpChatModel.fromJson(mixed);
+    log.info("获取混合消息,文件对象为:{}", modelMixed.getMixed().getItem().get(0).getContent());
+
+    // 返回文件对象
+    WxCpFileItem wxCpFileItem = WxCpFileItem.fromJson(modelMixed.getMixed().getItem().get(1).getContent());
+    log.info("获取混合消息,文件对象为:{}", wxCpFileItem.toJson());
+    log.info("数据为:" + modelMixed.toJson());
+
+
+    /**
+     * 音频存档消息
+     */
+    String meetingVoiceCall = "{\"msgid\":\"17952229780246929345_1594197637\",\"action\":\"send\",\"from\":\"wo137MCgAAYW6pIiKKrDe5SlzEhSgwbA\",\"tolist\":[\"wo137MCgAAYW6pIiKKrDe5SlzEhSgwbA\"],\"msgtime\":1594197581203,\"msgtype\":\"meeting_voice_call\",\"voiceid\":\"grb8a4c48a3c094a70982c518d55e40557\",\"meeting_voice_call\":{\"endtime\":1594197635,\"sdkfileid\":\"CpsBKjAqd0xhb2JWRUJldGtwcE5DVTB6UjRUalN6c09vTjVyRnF4YVJ5M24rZC9YcHF3cHRPVzRwUUlaMy9iTytFcnc0SlBkZDU1YjRNb0MzbTZtRnViOXV5WjUwZUIwKzhjbU9uRUlxZ3pyK2VXSVhUWVN2ejAyWFJaTldGSkRJVFl0aUhkcVdjbDJ1L2RPbjJsRlBOamJaVDNnPT0SOE5EZGZNVFk0T0RnMU16YzVNVGt5T1RJMk9GOHhNalk0TXpBeE9EZzJYekUxT1RReE9UYzJNemM9GiA3YTYyNzA3NTY4Nzc2MTY3NzQ2MTY0NzA2ZTc4NjQ2OQ==\",\"demofiledata\":[{\"filename\":\"65eb1cdd3e7a3c1740ecd74220b6c627.docx\",\"demooperator\":\"wo137MCgAAYW6pIiKKrDe5SlzEhSgwbA\",\"starttime\":1594197599,\"endtime\":1594197609}],\"sharescreendata\":[{\"share\":\"wo137MCgAAYW6pIiKKrDe5SlzEhSgwbA\",\"starttime\":1594197624,\"endtime\":1594197624}]}}";
+    WxCpChatModel modelMeetingVoiceCall = WxCpChatModel.fromJson(meetingVoiceCall);
+    log.info("数据为:" + modelMeetingVoiceCall.toJson());
+
+
+    /**
+     * 音频共享文档消息
+     */
+    String voipDocShare = "{\"msgid\":\"16527954622422422847_1594199256\",\"action\":\"send\",\"from\":\"18002520162\",\"tolist\":[\"wo137MCgAAYW6pIiKKrDe5SlzEhSgwbA\"],\"msgtime\":1594199235014,\"msgtype\":\"voip_doc_share\",\"voipid\":\"gr2751c98b19300571f8afb3b74514bd32\",\"voip_doc_share\":{\"filename\":\"欢迎使用微盘.pdf.pdf\",\"md5sum\":\"ff893900f24e55e216e617a40e5c4648\",\"filesize\":4400654,\"sdkfileid\":\"CpsBKjAqZUlLdWJMd2gvQ1JxMzd0ZjlpdW5mZzJOOE9JZm5kbndvRmRqdnBETjY0QlcvdGtHSFFTYm95dHM2VlllQXhkUUN5KzRmSy9KT3pudnA2aHhYZFlPemc2aVZ6YktzaVh3YkFPZHlqNnl2L2MvcGlqcVRjRTlhZEZsOGlGdHJpQ2RWSVNVUngrVFpuUmo3TGlPQ1BJemlRPT0SOE5EZGZNVFk0T0RnMU16YzVNVGt5T1RJMk9GODFNelUyTlRBd01qQmZNVFU1TkRFNU9USTFOZz09GiA3YTcwNmQ2Zjc5NjY3MDZjNjY2Zjc4NzI3NTZmN2E2YQ==\"}}";
+    WxCpChatModel modelVoipDocShare = WxCpChatModel.fromJson(voipDocShare);
+    log.info("数据为:" + modelVoipDocShare.toJson());
+
+
+    /**
+     * 互通红包消息
+     */
+    String externalRedpacket = "{\"msgid\":\"8632214264349267353_1603786184\",\"action\":\"send\",\"from\":\"woJ7ijBwAAmqwojT8r_DaNMbr_NAvaag\",\"tolist\":[\"woJ7ijBwAA6SjS_sIyPLZtyEPJlT7Cfw\",\"tiny-six768\"],\"roomid\":\"wrJ7ijBwAAG1vly_DzVI72Ghc-PtA5Dw\",\"msgtime\":1603786183955,\"msgtype\":\"external_redpacket\",\"redpacket\":{\"type\":1,\"wish\":\"恭喜发财,大吉大利\",\"totalcnt\":2,\"totalamount\":20}}";
+    WxCpChatModel modelExternalRedpacket = WxCpChatModel.fromJson(externalRedpacket);
+    log.info("数据为:" + modelExternalRedpacket.toJson());
+
+
+    /**
+     * 视频号消息
+     */
+    String sphfeed = "{\"msgid\":\"5702551662099334532_1619511584_external\",\"action\":\"send\",\"from\":\"yangzhu1\",\"tolist\":[\"wmJSb5CgAA4aWXWndJspQGpJMDbsMwMA\"],\"roomid\":\"\",\"msgtime\":1619511584444,\"msgtype\":\"sphfeed\",\"sphfeed\":{\"feed_type\":4,\"sph_name\":\"云游天地旅行家\",\"feed_desc\":\"瑞士丨盖尔默缆车,名副其实的过山车~\\n\\n#旅行#风景#热门\"}}";
+    WxCpChatModel modelSphFeed = WxCpChatModel.fromJson(sphfeed);
+    log.info("数据为:" + modelSphFeed.toJson());
+
+
+    /**
+     * 获取会话内容存档开启成员列表
+     */
+    List permitUserList = cpService.getMsgAuditService().getPermitUserList(null);
+    log.info(permitUserList.toString());
+
+
+    ArrayList userList = Lists.newArrayList();
+    WxCpCheckAgreeRequest checkAgreeRequest = new WxCpCheckAgreeRequest();
+    /**
+     * 获取会话同意情况
+     */
+    WxCpCheckAgreeRequest.Info info = new WxCpCheckAgreeRequest.Info();
+    info.setUserid("wangkai");
+    info.setExteranalOpenId("wmOQpTDwAAkOscTrtUlSli0YLU2jcpUg");
+    if(info != null){
+      userList.add(info);
+      checkAgreeRequest.setInfo(userList);
+    }
+
+    WxCpAgreeInfo wxCpAgreeInfo = cpService.getMsgAuditService().checkSingleAgree(checkAgreeRequest);
+    log.info(wxCpAgreeInfo.toJson());
+
+
+    /**
+     * 获取会话内容存档内部群信息
+     */
+    WxCpGroupChat room = cpService.getMsgAuditService().getGroupChat("wrOQpTDwAAyPl84GBJ40W5eWxWtixSCA");
+    log.info(room.toJson());
+
+  }
+
+}
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpOaAgentTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpOaAgentTest.java
new file mode 100644
index 000000000..88d990e4f
--- /dev/null
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpOaAgentTest.java
@@ -0,0 +1,59 @@
+package me.chanjar.weixin.cp.api;
+
+import com.google.gson.reflect.TypeToken;
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.common.util.json.GsonParser;
+import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl;
+import me.chanjar.weixin.cp.bean.oa.selfagent.WxCpOpenApprovalData;
+import me.chanjar.weixin.cp.config.WxCpConfigStorage;
+import me.chanjar.weixin.cp.demo.WxCpDemoInMemoryConfigStorage;
+import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
+import org.testng.annotations.Test;
+
+import java.io.InputStream;
+
+/**
+ * 企业微信自建应用接口测试类.
+ * https://developer.work.weixin.qq.com/document/path/90269
+ *
+ * @author Wang_Wong
+ * @date 2022-04-06
+ */
+@Slf4j
+public class WxCpOaAgentTest {
+
+  // extends WxCpBaseResp
+  private static WxCpConfigStorage wxCpConfigStorage;
+  private static WxCpService cpService;
+
+  @Test
+  public void test() throws WxErrorException {
+
+    InputStream inputStream = ClassLoader.getSystemResourceAsStream("test-config.xml");
+    WxCpDemoInMemoryConfigStorage config = WxCpDemoInMemoryConfigStorage.fromXml(inputStream);
+
+    wxCpConfigStorage = config;
+    cpService = new WxCpServiceImpl();
+    cpService.setWxCpConfigStorage(config);
+
+    /**
+     * Test
+     */
+    String test = "{\"errcode\":0,\"errmsg\":\"ok\",\"data\":{\"ThirdNo\":\"thirdNoxxx\",\"OpenTemplateId\":\"1234567111\",\"OpenSpName\":\"付款\",\"OpenSpstatus\":1,\"ApplyTime\":1527837645,\"ApplyUsername\":\"jackiejjwu\",\"ApplyUserParty\":\"产品部\",\"ApplyUserImage\":\"http://www.qq.com/xxx.png\",\"ApplyUserId\":\"WuJunJie\",\"ApprovalNodes\":{\"ApprovalNode\":[{\"NodeStatus\":1,\"NodeAttr\":1,\"NodeType\":1,\"Items\":{\"Item\":[{\"ItemName\":\"chauvetxiao\",\"ItemParty\":\"产品部\",\"ItemImage\":\"http://www.qq.com/xxx.png\",\"ItemUserId\":\"XiaoWen\",\"ItemStatus\":1,\"ItemSpeech\":\"\",\"ItemOpTime\":0}]}}]},\"NotifyNodes\":{\"NotifyNode\":[{\"ItemName\":\"jinhuiguo\",\"ItemParty\":\"行政部\",\"ItemImage\":\"http://www.qq.com/xxx.png\",\"ItemUserId\":\"GuoJinHui\"}]},\"ApproverStep\":0}}";
+
+    final WxCpOpenApprovalData data = WxCpGsonBuilder.create()
+      .fromJson(GsonParser.parse(test).get("data"),
+        new TypeToken() {
+        }.getType()
+      );
+
+    log.info(data.toJson());
+
+
+    WxCpOpenApprovalData openApprovalData = cpService.getOaAgentService().getOpenApprovalData("943225459735269376");
+    log.info(openApprovalData.toJson());
+
+  }
+
+}
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpDepartmentServiceImplTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpDepartmentServiceImplTest.java
index 57957d3fb..7417f8055 100644
--- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpDepartmentServiceImplTest.java
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpDepartmentServiceImplTest.java
@@ -1,13 +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.common.error.WxErrorException;
 import me.chanjar.weixin.cp.api.ApiTestModule;
 import me.chanjar.weixin.cp.api.WxCpService;
 import me.chanjar.weixin.cp.bean.WxCpDepart;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Guice;
+import org.testng.annotations.Test;
+
+import java.util.List;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
@@ -36,11 +38,11 @@ public void testCreate() throws Exception {
   }
 
   @DataProvider
-  public Object[][] departIds(){
+  public Object[][] departIds() {
     return new Object[][]{
       {null},
-      {1},
-      {5}
+      {12L},
+      {5L}
     };
   }
 
@@ -70,4 +72,22 @@ public void testDelete() throws Exception {
     this.wxCpService.getDepartmentService().delete(this.depart.getId());
   }
 
+  @Test(dataProvider = "departIds")
+  public void testSimpleList(Long id) throws WxErrorException {
+    System.out.println("=================获取子部门ID列表");
+    List departList = this.wxCpService.getDepartmentService().simpleList(id);
+    assertThat(departList).isNotEmpty();
+    departList.forEach(System.out::println);
+  }
+
+  @Test(dataProvider = "departIds")
+  public void testGet(Long id) throws WxErrorException {
+    if (id == null) {
+      return;
+    }
+
+    WxCpDepart depart = this.wxCpService.getDepartmentService().get(id);
+    assertThat(depart).isNotNull();
+    System.out.println(depart);
+  }
 }
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpExternalContactServiceImplTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpExternalContactServiceImplTest.java
index 60fdeb9b2..3f7cc0b3b 100644
--- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpExternalContactServiceImplTest.java
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpExternalContactServiceImplTest.java
@@ -20,6 +20,7 @@
 import java.util.Collections;
 import java.util.Date;
 import java.util.List;
+import org.testng.collections.CollectionUtils;
 
 import static org.testng.Assert.assertNotNull;
 
@@ -111,7 +112,7 @@ public void testListExternalWithPermission() throws WxErrorException {
   @Test
   public void testGetContactDetail() throws WxErrorException {
     String externalUserId = this.configStorage.getExternalUserId();
-    WxCpExternalContactInfo result = this.wxCpService.getExternalContactService().getContactDetail(externalUserId);
+    WxCpExternalContactInfo result = this.wxCpService.getExternalContactService().getContactDetail(externalUserId, null);
     System.out.println(result);
     assertNotNull(result);
   }
@@ -314,4 +315,26 @@ public void testUpdateRemark() throws WxErrorException {
       .remarkPicMediaId("aaa")
       .build());
   }
+
+  @Test
+  public void testGetProductListAlbum() throws WxErrorException {
+    WxCpProductAlbumListResult result = this.wxCpService.getExternalContactService()
+      .getProductAlbumList(100, null);
+    System.out.println(result);
+    assertNotNull(result);
+    if(CollectionUtils.hasElements(result.getProductList())){
+      WxCpProductAlbumResult result1 = this.wxCpService.getExternalContactService().getProductAlbum(result.getProductList().get(0).getProductId());
+      System.out.println(result1);
+      assertNotNull(result1);
+    }
+  }
+
+  @Test
+  public void testGetMomentList() throws WxErrorException {
+    WxCpGetMomentList result = this.wxCpService.getExternalContactService()
+      .getMomentList(1636732800L, 1636991999L, null, null, null, null);
+    System.out.println(result);
+    assertNotNull(result);
+  }
+
 }
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpKfServiceImplTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpKfServiceImplTest.java
new file mode 100644
index 000000000..09a4f568c
--- /dev/null
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpKfServiceImplTest.java
@@ -0,0 +1,82 @@
+package me.chanjar.weixin.cp.api.impl;
+
+import com.google.inject.Inject;
+import me.chanjar.weixin.common.api.WxConsts;
+import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
+import me.chanjar.weixin.cp.api.ApiTestModule;
+import me.chanjar.weixin.cp.api.WxCpService;
+import me.chanjar.weixin.cp.bean.WxCpBaseResp;
+import me.chanjar.weixin.cp.bean.kf.WxCpKfAccountAdd;
+import me.chanjar.weixin.cp.bean.kf.WxCpKfAccountAddResp;
+import me.chanjar.weixin.cp.bean.kf.WxCpKfAccountDel;
+import me.chanjar.weixin.cp.bean.kf.WxCpKfAccountLink;
+import me.chanjar.weixin.cp.bean.kf.WxCpKfAccountLinkResp;
+import me.chanjar.weixin.cp.bean.kf.WxCpKfAccountListResp;
+import me.chanjar.weixin.cp.bean.kf.WxCpKfAccountUpd;
+import org.testng.annotations.Guice;
+import org.testng.annotations.Test;
+
+import java.io.InputStream;
+
+/**
+ * WxCpKfServiceImpl-测试类
+ * 需要用到专门的 secret https://kf.weixin.qq.com/api/doc/path/93304#secret
+ *
+ * @author Fu
+ * @date 2022/1/19 20:12
+ */
+@Guice(modules = ApiTestModule.class)
+public class WxCpKfServiceImplTest {
+
+  @Inject
+  private WxCpService wxService;
+
+  private static String kfid = "wkPzhXVAAAJD9oR75LrO1DmURSOUFBIg";
+
+  @Test(priority = 1)
+  public void testAccountAdd() throws Exception {
+    try (InputStream in = ClassLoader.getSystemResourceAsStream("mm.jpeg")) {
+      WxMediaUploadResult result = this.wxService.getMediaService().upload(WxConsts.MediaFileType.IMAGE, "jpeg", in);
+      String mediaId = result.getMediaId();
+      WxCpKfAccountAdd add = new WxCpKfAccountAdd();
+      add.setMediaId(mediaId);
+      add.setName("kefu01");
+      WxCpKfAccountAddResp resp = this.wxService.getKfService().addAccount(add);
+      System.out.println(resp);
+      kfid = resp.getOpenKfid();
+    }
+  }
+
+  @Test(priority = 2)
+  public void testAccountUpd() throws Exception {
+    WxCpKfAccountUpd upd = new WxCpKfAccountUpd();
+    upd.setOpenKfid(kfid);
+    upd.setName("kefu01-upd");
+    WxCpBaseResp resp = this.wxService.getKfService().updAccount(upd);
+    System.out.println(resp);
+  }
+
+  @Test(priority = 3)
+  public void testAccountList() throws Exception {
+    WxCpKfAccountListResp resp = this.wxService.getKfService().listAccount();
+    System.out.println(resp);
+  }
+
+  @Test(priority = 4)
+  public void testAccountLink() throws Exception {
+    WxCpKfAccountLink link = new WxCpKfAccountLink();
+    link.setOpenKfid(kfid);
+    link.setScene("scene");
+    WxCpKfAccountLinkResp resp = this.wxService.getKfService().getAccountLink(link);
+    System.out.println(resp);
+  }
+
+  @Test(priority = 5)
+  public void testAccountDel() throws Exception {
+    WxCpKfAccountDel del = new WxCpKfAccountDel();
+    del.setOpenKfid(kfid);
+    WxCpBaseResp resp = this.wxService.getKfService().delAccount(del);
+    System.out.println(resp);
+  }
+
+}
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpOaServiceImplTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpOaServiceImplTest.java
index 4370bb3d8..d3f52561a 100644
--- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpOaServiceImplTest.java
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpOaServiceImplTest.java
@@ -1,15 +1,12 @@
 package me.chanjar.weixin.cp.api.impl;
 
 import com.google.gson.Gson;
-import com.google.gson.JsonObject;
-import com.google.gson.reflect.TypeToken;
 import com.google.inject.Inject;
+import lombok.extern.slf4j.Slf4j;
 import me.chanjar.weixin.common.error.WxErrorException;
-import me.chanjar.weixin.common.util.json.GsonParser;
 import me.chanjar.weixin.cp.api.ApiTestModule;
 import me.chanjar.weixin.cp.api.WxCpService;
 import me.chanjar.weixin.cp.bean.oa.*;
-import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
 import org.apache.commons.lang3.time.DateFormatUtils;
 import org.testng.annotations.Guice;
 import org.testng.annotations.Test;
@@ -25,9 +22,9 @@
 /**
  * 企业微信 OA数据接口 测试用例
  *
- * @author Element
+ * @author Element & Wang_Wong
  */
-
+@Slf4j
 @Guice(modules = ApiTestModule.class)
 public class WxCpOaServiceImplTest {
 
@@ -171,4 +168,15 @@ public void testGetApprovalData() {
   @Test
   public void testGetDialRecord() {
   }
+
+  /**
+   * https://developer.work.weixin.qq.com/document/path/93375
+   * @throws WxErrorException
+   */
+  @Test
+  public void testGetCorpConf() throws WxErrorException{
+    WxCpCorpConfInfo corpConf = this.wxService.getOaService().getCorpConf();
+    log.info(corpConf.toJson());
+  }
+
 }
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalContactInfoTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalContactInfoTest.java
index c666c1b94..ce5475358 100644
--- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalContactInfoTest.java
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalContactInfoTest.java
@@ -1,8 +1,13 @@
 package me.chanjar.weixin.cp.bean.external;
 
+import com.google.gson.reflect.TypeToken;
+import lombok.val;
+import me.chanjar.weixin.common.util.json.GsonParser;
+import me.chanjar.weixin.cp.bean.WxCpDepart;
 import me.chanjar.weixin.cp.bean.external.contact.ExternalContact;
 import me.chanjar.weixin.cp.bean.external.contact.FollowedUser;
 import me.chanjar.weixin.cp.bean.external.contact.WxCpExternalContactInfo;
+import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
 import org.testng.annotations.*;
 
 import java.util.List;
@@ -79,6 +84,44 @@ public void testFromJson() {
       "  ]\n" +
       "}";
 
+    final String testJson = "{\n" +
+      "   \"errcode\": 0,\n" +
+      "   \"errmsg\": \"ok\",\n" +
+      "   \"department\": [\n" +
+      "       {\n" +
+      "           \"id\": 2,\n" +
+      "           \"name\": \"广州研发中心\",\n" +
+      "           \"name_en\": \"RDGZ\",\n" +
+      "           \"department_leader\":[\"zhangsan\",\"lisi\"],\n" +
+      "           \"parentid\": 1,\n" +
+      "           \"order\": 10\n" +
+      "       },\n" +
+      "       {\n" +
+      "           \"id\": 3,\n" +
+      "           \"name\": \"邮箱产品部\",\n" +
+      "           \"name_en\": \"mail\",\n" +
+      "           \"department_leader\":[\"lisi\",\"wangwu\"],\n" +
+      "           \"parentid\": 2,\n" +
+      "           \"order\": 40\n" +
+      "       }\n" +
+      "   ]\n" +
+      "}\n";
+
+    // 测试序列化
+    val depart = new WxCpDepart();
+    depart.setId(8L);
+    depart.setName("name");
+    depart.setEnName("enName");
+    depart.setDepartmentLeader(new String[]{"zhangsan", "lisi"});
+    depart.setParentId(88L);
+    depart.setOrder(99L);
+
+    String toJson = depart.toJson();
+
+    // 测试企业微信字段返回
+    List department = WxCpGsonBuilder.create().fromJson(GsonParser.parse(testJson).get("department"), new TypeToken>() {
+    }.getType());
+
     final WxCpExternalContactInfo contactInfo = WxCpExternalContactInfo.fromJson(json);
     assertThat(contactInfo).isNotNull();
     assertThat(contactInfo.getExternalContact()).isNotNull();
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/external/contact/WxCpGroupMsgResultTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/external/contact/WxCpGroupMsgResultTest.java
new file mode 100644
index 000000000..a0bc98b10
--- /dev/null
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/external/contact/WxCpGroupMsgResultTest.java
@@ -0,0 +1,37 @@
+package me.chanjar.weixin.cp.bean.external.contact;
+
+import org.testng.annotations.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class WxCpGroupMsgResultTest {
+
+  @Test
+  public void testToJson() {
+    /*
+      @see https://work.weixin.qq.com/api/doc/16251
+     */
+    String json = "{ " +
+      "\"errcode\": 0, " +
+      "\"errmsg\": \"ok\", " +
+      "\"detail_list\": [ " +
+      "   { " +
+      "     \"external_userid\": \"wmqfasd1e19278asdasAAAA\", " +
+      "     \"chat_id\":\"wrOgQhDgAAMYQiS5ol9G7gK9JVAAAA\", " +
+      "     \"userid\": \"zhangsan\", " +
+      "     \"status\": 1, " +
+      "     \"send_time\": 1552536375 " +
+      "   } " +
+      " ] " +
+      "}";
+
+    WxCpGroupMsgResult result = WxCpGroupMsgResult.fromJson(json);
+    assertThat(result.getDetailList().size()).isEqualTo(1);
+    WxCpGroupMsgResult.ExternalContactGroupMsgDetailInfo detail = result.getDetailList().get(0);
+    assertThat(detail.getChatId()).isEqualTo("wrOgQhDgAAMYQiS5ol9G7gK9JVAAAA");
+    assertThat(detail.getExternalUserId()).isEqualTo("wmqfasd1e19278asdasAAAA");
+    assertThat(detail.getUserId()).isEqualTo("zhangsan");
+    assertThat(detail.getStatus()).isEqualTo(1);
+    assertThat(detail.getSendTime()).isEqualTo(1552536375L);
+  }
+}
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/message/WxCpMessageTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/message/WxCpMessageTest.java
index a1cfee801..c8a367614 100644
--- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/message/WxCpMessageTest.java
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/message/WxCpMessageTest.java
@@ -178,30 +178,38 @@ public void TestTemplateCardBuilder_text_notice() {
       .appid("小程序的appid")
       .pagepath("/index.html")
       .build();
-
+    QuoteArea quoteArea=QuoteArea.builder()
+      .type(1)
+      .title("引用文献标题")
+      .appid("小程序的appid")
+      .pagepath("/index.html")
+      .url("https://work.weixin.qq.com")
+      .quoteText("引用文献样式的引用文案")
+      .build();
     WxCpMessage reply = WxCpMessage.TEMPLATECARD().toUser("OPENID")
       .agentId(1000002)
-      .card_type(WxConsts.TemplateCardType.TEXT_NOTICE)
-      .source_icon_url("图片的url")
-      .source_desc("企业微信")
-      .main_title_title("欢迎使用企业微信")
-      .main_title_desc("您的好友正在邀请您加入企业微信")
-      .emphasis_content_title("100")
-      .emphasis_content_desc("核心数据")
-      .sub_title_text("下载企业微信还能抢红包!")
-      .horizontal_contents(Arrays.asList(hContent1,hContent2,hContent3))
+      .cardType(WxConsts.TemplateCardType.TEXT_NOTICE)
+      .sourceIconUrl("图片的url")
+      .sourceDesc("企业微信")
+      .mainTitleTitle("欢迎使用企业微信")
+      .mainTitleDesc("您的好友正在邀请您加入企业微信")
+      .emphasisContentTitle("100")
+      .emphasisContentDesc("核心数据")
+      .subTitleText("下载企业微信还能抢红包!")
+      .horizontalContents(Arrays.asList(hContent1,hContent2,hContent3))
       .jumps(Arrays.asList(jump1,jump2))
-      .card_action_type(2)
-      .card_action_appid("小程序的appid")
-      .card_action_url("https://work.weixin.qq.com")
-      .card_action_pagepath("/index.html")
+      .cardActionType(2)
+      .cardActionAppid("小程序的appid")
+      .cardActionUrl("https://work.weixin.qq.com")
+      .cardActionPagepath("/index.html")
+      .quoteArea(quoteArea)
       .build();
     reply.setEnableIdTrans(false);
     reply.setEnableDuplicateCheck(false);
     reply.setDuplicateCheckInterval(1800);
 //    System.out.println(reply.toJson());
     assertThat(reply.toJson())
-      .isEqualTo("{\"agentid\":1000002,\"touser\":\"OPENID\",\"msgtype\":\"template_card\",\"duplicate_check_interval\":1800,\"template_card\":{\"card_type\":\"text_notice\",\"source\":{\"icon_url\":\"图片的url\",\"desc\":\"企业微信\"},\"main_title\":{\"title\":\"欢迎使用企业微信\",\"desc\":\"您的好友正在邀请您加入企业微信\"},\"emphasis_content\":{\"title\":\"100\",\"desc\":\"核心数据\"},\"sub_title_text\":\"下载企业微信还能抢红包!\",\"horizontal_content_list\":[{\"keyname\":\"邀请人\",\"value\":\"张三\"},{\"type\":1,\"keyname\":\"企业微信官网\",\"value\":\"点击访问\",\"url\":\"https://work.weixin.qq.com\"},{\"type\":2,\"keyname\":\"企业微信下载\",\"value\":\"企业微信.apk\",\"media_id\":\"文件的media_id\"}],\"jump_list\":[{\"type\":1,\"title\":\"企业微信官网\",\"url\":\"https://work.weixin.qq.com\"},{\"type\":2,\"title\":\"跳转小程序\",\"appid\":\"小程序的appid\",\"pagepath\":\"/index.html\"}],\"card_action\":{\"type\":2,\"url\":\"https://work.weixin.qq.com\",\"appid\":\"小程序的appid\",\"pagepath\":\"/index.html\"}}}");
+      .isEqualTo("{\"agentid\":1000002,\"touser\":\"OPENID\",\"msgtype\":\"template_card\",\"duplicate_check_interval\":1800,\"template_card\":{\"card_type\":\"text_notice\",\"source\":{\"icon_url\":\"图片的url\",\"desc\":\"企业微信\"},\"main_title\":{\"title\":\"欢迎使用企业微信\",\"desc\":\"您的好友正在邀请您加入企业微信\"},\"emphasis_content\":{\"title\":\"100\",\"desc\":\"核心数据\"},\"sub_title_text\":\"下载企业微信还能抢红包!\",\"horizontal_content_list\":[{\"keyname\":\"邀请人\",\"value\":\"张三\"},{\"type\":1,\"keyname\":\"企业微信官网\",\"value\":\"点击访问\",\"url\":\"https://work.weixin.qq.com\"},{\"type\":2,\"keyname\":\"企业微信下载\",\"value\":\"企业微信.apk\",\"media_id\":\"文件的media_id\"}],\"jump_list\":[{\"type\":1,\"title\":\"企业微信官网\",\"url\":\"https://work.weixin.qq.com\"},{\"type\":2,\"title\":\"跳转小程序\",\"appid\":\"小程序的appid\",\"pagepath\":\"/index.html\"}],\"card_action\":{\"type\":2,\"url\":\"https://work.weixin.qq.com\",\"appid\":\"小程序的appid\",\"pagepath\":\"/index.html\"},\"quote_area\":{\"type\":1,\"url\":\"https://work.weixin.qq.com\",\"appid\":\"小程序的appid\",\"pagepath\":\"/index.html\",\"title\":\"引用文献标题\",\"quote_text\":\"引用文献样式的引用文案\"}}}");
 
   }
 
@@ -251,18 +259,18 @@ public void TestTemplateCardBuilder_news_notice() {
 
     WxCpMessage reply = WxCpMessage.TEMPLATECARD().toUser("OPENID")
       .agentId(1000002)
-      .card_type(WxConsts.TemplateCardType.NEWS_NOTICE)
-      .source_icon_url("图片的url")
-      .source_desc("企业微信")
-      .main_title_title("欢迎使用企业微信")
-      .main_title_desc("您的好友正在邀请您加入企业微信")
-      .vertical_contents(Arrays.asList(vContent1,vContent2))
-      .horizontal_contents(Arrays.asList(hContent1,hContent2,hContent3))
+      .cardType(WxConsts.TemplateCardType.NEWS_NOTICE)
+      .sourceIconUrl("图片的url")
+      .sourceDesc("企业微信")
+      .mainTitleTitle("欢迎使用企业微信")
+      .mainTitleDesc("您的好友正在邀请您加入企业微信")
+      .verticalContents(Arrays.asList(vContent1,vContent2))
+      .horizontalContents(Arrays.asList(hContent1,hContent2,hContent3))
       .jumps(Arrays.asList(jump1,jump2))
-      .card_action_type(2)
-      .card_action_appid("小程序的appid")
-      .card_action_url("https://work.weixin.qq.com")
-      .card_action_pagepath("/index.html")
+      .cardActionType(2)
+      .cardActionAppid("小程序的appid")
+      .cardActionUrl("https://work.weixin.qq.com")
+      .cardActionPagepath("/index.html")
       .build();
     reply.setEnableIdTrans(false);
     reply.setEnableDuplicateCheck(false);
@@ -308,18 +316,18 @@ public void TestTemplateCardBuilder_button_interaction() {
 
     WxCpMessage reply = WxCpMessage.TEMPLATECARD().toUser("OPENID")
       .agentId(1000002)
-      .card_type(WxConsts.TemplateCardType.BUTTON_INTERACTION)
-      .source_icon_url("图片的url")
-      .source_desc("企业微信")
-      .main_title_title("欢迎使用企业微信")
-      .main_title_desc("您的好友正在邀请您加入企业微信")
-      .sub_title_text("下载企业微信还能抢红包!")
-      .horizontal_contents(Arrays.asList(hContent1,hContent2,hContent3))
-      .card_action_type(2)
-      .card_action_appid("小程序的appid")
-      .card_action_url("https://work.weixin.qq.com")
-      .card_action_pagepath("/index.html")
-      .task_id("task_id")
+      .cardType(WxConsts.TemplateCardType.BUTTON_INTERACTION)
+      .sourceIconUrl("图片的url")
+      .sourceDesc("企业微信")
+      .mainTitleTitle("欢迎使用企业微信")
+      .mainTitleDesc("您的好友正在邀请您加入企业微信")
+      .subTitleText("下载企业微信还能抢红包!")
+      .horizontalContents(Arrays.asList(hContent1,hContent2,hContent3))
+      .cardActionType(2)
+      .cardActionAppid("小程序的appid")
+      .cardActionUrl("https://work.weixin.qq.com")
+      .cardActionPagepath("/index.html")
+      .taskId("task_id")
       .buttons(Arrays.asList(tButton1,tButton2))
       .build();
     reply.setEnableIdTrans(false);
@@ -348,22 +356,24 @@ public void TestTemplateCardBuilder_vote_interaction() {
 
     WxCpMessage reply = WxCpMessage.TEMPLATECARD().toUser("OPENID")
       .agentId(1000002)
-      .card_type(WxConsts.TemplateCardType.VOTE_INTERACTION)
-      .source_icon_url("图片的url")
-      .source_desc("企业微信")
-      .main_title_title("欢迎使用企业微信")
-      .main_title_desc("您的好友正在邀请您加入企业微信")
-      .task_id("task_id")
-      .checkbox_question_key("question_key1")
-      .checkbox_mode(1)
+      .cardType(WxConsts.TemplateCardType.VOTE_INTERACTION)
+      .sourceIconUrl("图片的url")
+      .sourceDesc("企业微信")
+      .mainTitleTitle("欢迎使用企业微信")
+      .mainTitleDesc("您的好友正在邀请您加入企业微信")
+      .taskId("task_id")
+      .checkboxQuestionKey("question_key1")
+      .checkboxMode(1)
       .options(Arrays.asList(option1,option2))
-      .submit_button_key("key")
-      .submit_button_text("提交")
+      .submitButtonKey("key")
+      .submitButtonText("提交")
       .build();
+
     reply.setEnableIdTrans(false);
     reply.setEnableDuplicateCheck(false);
     reply.setDuplicateCheckInterval(1800);
     System.out.println(reply.toJson());
+
     assertThat(reply.toJson())
       .isEqualTo("{\"agentid\":1000002,\"touser\":\"OPENID\",\"msgtype\":\"template_card\",\"duplicate_check_interval\":1800,\"template_card\":{\"card_type\":\"vote_interaction\",\"source\":{\"icon_url\":\"图片的url\",\"desc\":\"企业微信\"},\"main_title\":{\"title\":\"欢迎使用企业微信\",\"desc\":\"您的好友正在邀请您加入企业微信\"},\"task_id\":\"task_id\",\"checkbox\":{\"question_key\":\"question_key1\",\"mode\":1,\"option_list\":[{\"id\":\"option_id1\",\"text\":\"选择题选项1\",\"is_checked\":true},{\"id\":\"option_id2\",\"text\":\"选择题选项2\",\"is_checked\":false}]},\"submit_button\":{\"text\":\"提交\",\"key\":\"key\"}}}");
   }
@@ -406,15 +416,15 @@ public void TestTemplateCardBuilder_multiple_interaction() {
 
     WxCpMessage reply = WxCpMessage.TEMPLATECARD().toUser("OPENID")
       .agentId(1000002)
-      .card_type(WxConsts.TemplateCardType.MULTIPLE_INTERACTION)
-      .source_icon_url("图片的url")
-      .source_desc("企业微信")
-      .main_title_title("欢迎使用企业微信")
-      .main_title_desc("您的好友正在邀请您加入企业微信")
-      .task_id("task_id")
+      .cardType(WxConsts.TemplateCardType.MULTIPLE_INTERACTION)
+      .sourceIconUrl("图片的url")
+      .sourceDesc("企业微信")
+      .mainTitleTitle("欢迎使用企业微信")
+      .mainTitleDesc("您的好友正在邀请您加入企业微信")
+      .taskId("task_id")
       .selects(Arrays.asList(mSelect1,mSelect2))
-      .submit_button_key("key")
-      .submit_button_text("提交")
+      .submitButtonKey("key")
+      .submitButtonText("提交")
       .build();
     reply.setEnableIdTrans(false);
     reply.setEnableDuplicateCheck(false);
diff --git a/weixin-java-cp/src/test/resources/test-config.sample.xml b/weixin-java-cp/src/test/resources/test-config.sample.xml
index 76a74a25a..23e83e942 100644
--- a/weixin-java-cp/src/test/resources/test-config.sample.xml
+++ b/weixin-java-cp/src/test/resources/test-config.sample.xml
@@ -11,4 +11,6 @@
   企业号通讯录里的某个tagid
   网页授权获取用户信息回调地址
   webhook链接地址的key值
+  
+  /www/osfile/libcrypto-1_1-x64.dll,libssl-1_1-x64.dll,libcurl-x64.dll,WeWorkFinanceSdk.dll,libWeWorkFinanceSdk_Java.so
 
diff --git a/weixin-java-miniapp/pom.xml b/weixin-java-miniapp/pom.xml
index 39d3442b4..e01606833 100644
--- a/weixin-java-miniapp/pom.xml
+++ b/weixin-java-miniapp/pom.xml
@@ -7,7 +7,7 @@
   
     com.github.binarywang
     wx-java
-    4.2.0
+    4.3.0
   
 
   weixin-java-miniapp
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaDeviceSubscribeService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaDeviceSubscribeService.java
new file mode 100644
index 000000000..f44f64e48
--- /dev/null
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaDeviceSubscribeService.java
@@ -0,0 +1,40 @@
+package cn.binarywang.wx.miniapp.api;
+
+import cn.binarywang.wx.miniapp.bean.device.WxMaDeviceSubscribeMessageRequest;
+import cn.binarywang.wx.miniapp.bean.device.WxMaDeviceTicketRequest;
+import me.chanjar.weixin.common.error.WxErrorException;
+
+/**
+ * 小程序设备订阅消息相关 API
+ * 文档:
+ *
+ * @author JCLee
+ * @since 2021-12-16 17:13:35
+ */
+public interface WxMaDeviceSubscribeService {
+
+  /**
+   * 
+   * 获取设备票据
+   * 应用场景:
+   * 小程序前端界面拉起设备消息授权订阅弹框界面
+   * 注意:
+   * 设备ticket有效时间为5分钟
+   * 
+ * @param deviceTicketRequest + * @return + * @throws WxErrorException + */ + String getSnTicket(WxMaDeviceTicketRequest deviceTicketRequest) throws WxErrorException; + + /** + *
+   * 发送设备订阅消息
+   * 
+ * + * @param deviceSubscribeMessageRequest 订阅消息 + * @throws WxErrorException . + */ + void sendDeviceSubscribeMsg(WxMaDeviceSubscribeMessageRequest deviceSubscribeMessageRequest) throws WxErrorException; + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaExpressService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaExpressService.java index b8229ceeb..cbac84c74 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaExpressService.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaExpressService.java @@ -136,4 +136,7 @@ public interface WxMaExpressService { * @throws WxErrorException 模拟更新订单状态失败时返回 */ void testUpdateOrder(WxMaExpressTestUpdateOrderRequest wxMaExpressTestUpdateOrderRequest) throws WxErrorException; + + + } diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaImmediateDeliveryService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaImmediateDeliveryService.java new file mode 100644 index 000000000..f08f510e3 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaImmediateDeliveryService.java @@ -0,0 +1,132 @@ +package cn.binarywang.wx.miniapp.api; + +import cn.binarywang.wx.miniapp.bean.delivery.AbnormalConfirmRequest; +import cn.binarywang.wx.miniapp.bean.delivery.AbnormalConfirmResponse; +import cn.binarywang.wx.miniapp.bean.delivery.AddOrderRequest; +import cn.binarywang.wx.miniapp.bean.delivery.AddOrderResponse; +import cn.binarywang.wx.miniapp.bean.delivery.BindAccountResponse; +import cn.binarywang.wx.miniapp.bean.delivery.CancelOrderRequest; +import cn.binarywang.wx.miniapp.bean.delivery.CancelOrderResponse; +import cn.binarywang.wx.miniapp.bean.delivery.GetOrderRequest; +import cn.binarywang.wx.miniapp.bean.delivery.GetOrderResponse; +import cn.binarywang.wx.miniapp.bean.delivery.MockUpdateOrderRequest; +import cn.binarywang.wx.miniapp.bean.delivery.MockUpdateOrderResponse; +import cn.binarywang.wx.miniapp.bean.delivery.QueryWaybillTraceRequest; +import cn.binarywang.wx.miniapp.bean.delivery.QueryWaybillTraceResponse; +import cn.binarywang.wx.miniapp.bean.delivery.TraceWaybillRequest; +import cn.binarywang.wx.miniapp.bean.delivery.TraceWaybillResponse; +import me.chanjar.weixin.common.error.WxErrorException; + +/** + * 微信小程序即时配送服务. + *
+ *     文档地址:https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/industry/immediate-delivery/overview.html
+ * 
+ * + * @author Luo + * @version 1.0 + * @date 2021-10-13 16:40 + */ +public interface WxMaImmediateDeliveryService { + + /** + * 拉取已绑定账号. + *
+   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/immediate-delivery/by-business/immediateDelivery.getBindAccount.html
+   * 
+ * + * @return 响应 + * @throws WxErrorException 异常 + */ + BindAccountResponse getBindAccount() throws WxErrorException; + + /** + * 下配送单接口. + *
+   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/immediate-delivery/by-business/immediateDelivery.addOrder.html
+   * 
+ * + * @param request request + * @return 响应 + * @throws WxErrorException 异常 + */ + AddOrderResponse addOrder(AddOrderRequest request) throws WxErrorException; + + /** + * 拉取配送单信息. + *
+   * 商家可使用本接口查询某一配送单的配送状态,便于商家掌握配送情况。
+   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/immediate-delivery/by-business/immediateDelivery.getOrder.html
+   * 
+ * + * @param request request + * @return 响应 + * @throws WxErrorException 异常 + */ + GetOrderResponse getOrder(GetOrderRequest request) throws WxErrorException; + + /** + * 取消配送单接口. + *
+   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/immediate-delivery/by-business/immediateDelivery.cancelOrder.html
+   * 
+ * + * @param request request + * @return 响应 + * @throws WxErrorException 异常 + */ + CancelOrderResponse cancelOrder(CancelOrderRequest request) throws WxErrorException; + + /** + * 异常件退回商家商家确认收货接口. + *
+   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/immediate-delivery/by-business/immediateDelivery.abnormalConfirm.html
+   * 
+ * + * @param request request + * @return 响应 + * @throws WxErrorException 异常 + */ + AbnormalConfirmResponse abnormalConfirm(AbnormalConfirmRequest request) throws WxErrorException; + + /** + * 模拟配送公司更新配送单状态, 该接口只用于沙盒环境,即订单并没有真实流转到运力方. + *
+   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/immediate-delivery/by-business/immediateDelivery.mockUpdateOrder.html
+   * 
+ * + * @param request request + * @return 响应 + * @throws WxErrorException 异常 + */ + MockUpdateOrderResponse mockUpdateOrder(MockUpdateOrderRequest request) throws WxErrorException; + + + /** + * 商户使用此接口向微信提供某交易单号对应的运单号。微信后台会跟踪运单的状态变化 + *
+   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/industry/express/express_search.html
+   * 
+ * + * @param request request + * @return 响应 + * @throws WxErrorException 异常 + */ + TraceWaybillResponse traceWaybill(TraceWaybillRequest request) throws WxErrorException; + + + /** + * 商户在调用完trace_waybill接口后,可以使用本接口查询到对应运单的详情信息 + *
+   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/industry/express/express_search.html
+   * 
+ * + * @param request request + * @return 响应 + * @throws WxErrorException 异常 + */ + QueryWaybillTraceResponse queryWaybillTrace(QueryWaybillTraceRequest request) + throws WxErrorException; + + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaInternetService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaInternetService.java index 8e6a3e2d8..4d055ba2d 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaInternetService.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaInternetService.java @@ -8,20 +8,36 @@ * 【小程序-服务端-网络】网络相关接口. * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/internet/internet.getUserEncryptKey.html *
+ * * @author chutian0124 */ public interface WxMaInternetService { /** + *
+   * 获取用户encryptKey。 会获取用户最近3次的key,每个key的存活时间为3600s。
+   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/internet/internet.getUserEncryptKey.html
+   * 接口地址:POST https://api.weixin.qq.com/wxa/business/getuserencryptkey?access_token=ACCESS_TOKEN&openid=OPENID&signature=SIGNATURE&sig_method=hmac_sha256
+   * @param openid 用户的openid
+   * @param signature 用sessionkey对空字符串签名得到的结果
+   * @param sigMethod 签名方法,只支持 hmac_sha256
+   * 
* - * + * @return {@link WxMaInternetResponse} + * @throws WxErrorException + */ + WxMaInternetResponse getUserEncryptKey(String openid, String signature, String sigMethod) throws WxErrorException; + + /** *
    * 获取用户encryptKey。 会获取用户最近3次的key,每个key的存活时间为3600s。
    * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/internet/internet.getUserEncryptKey.html
    * 接口地址:POST https://api.weixin.qq.com/wxa/business/getuserencryptkey?access_token=ACCESS_TOKEN&openid=OPENID&signature=SIGNATURE&sig_method=hmac_sha256
+   * @param openid 用户的openid
+   * @param sessionKey 用户的sessionKey
    * 
* * @return {@link WxMaInternetResponse} * @throws WxErrorException */ - WxMaInternetResponse getUserEncryptKey() throws WxErrorException; + WxMaInternetResponse getUserEncryptKey(String openid, String sessionKey) throws WxErrorException; } diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaLiveGoodsService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaLiveGoodsService.java index d0e06d480..7b41e0f78 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaLiveGoodsService.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaLiveGoodsService.java @@ -114,4 +114,32 @@ public interface WxMaLiveGoodsService { * @throws WxErrorException . */ WxMaLiveResult getApprovedGoods(Integer offset, Integer limit, Integer status) throws WxErrorException; + + /** + * 直播挂件设置全局key + *
+   * 若已设置此全局key,且添加商品时未指定goodsKey字段,则我们会使用此全局key作为该商品的goodsKey。 须注意的是,若全局key已设定,并添加了未指定goodsKey字段的商品之后,再重新设定不一样的全局key则会导致先前的映射失效。 为了避免映射失效,建议全局key只设定一次。
+   * 注意:key必须为字符串数组
+   * 调用额度:500次/一天
+   * http请求方式:POST https://api.weixin.qq.com/wxaapi/broadcast/goods/setkey?access_token=
+   * 
+ * + * @param goodsKey 全局key + * @return 设置是否成功 + * @throws WxErrorException . + */ + boolean setKey(List goodsKey) throws WxErrorException; + + /** + * 查看当前设定的全局key + *
+   * 查看当前设定的全局key。
+   * 调用额度:500次/一天
+   * http请求方式:GET https://api.weixin.qq.com/wxaapi/broadcast/goods/getkey?access_token=
+   * 
+ * + * @return 全局key + * @throws WxErrorException . + */ + List getKey() throws WxErrorException; } diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaLiveService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaLiveService.java index a90f4756b..2d7751948 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaLiveService.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaLiveService.java @@ -4,6 +4,7 @@ import me.chanjar.weixin.common.error.WxErrorException; import java.util.List; +import java.util.Map; /** *
@@ -207,4 +208,190 @@ public interface WxMaLiveService {
    * @throws WxErrorException .
    */
   List getAssistantList(Integer roomId) throws WxErrorException;
+
+  /**
+   * 添加主播副号
+   * 

+ * 调用接口添加主播副号 + *

+ * 调用额度:10000次/一天 + *

+ * http请求方式:POST https://api.weixin.qq.com/wxaapi/broadcast/room/addsubanchor?access_token=ACCESS_TOKEN + *

+   * @param roomId 房间ID
+   * @param username 用户微信号
+   * @return 是否成功
+   * @throws WxErrorException .
+   */
+  boolean addSubanchor(Integer roomId, String username) throws WxErrorException;
+
+  /**
+   * 修改主播副号
+   * 

+ * 调用接口修改主播副号 + *

+ * 调用频率: 10000次/一天 + *

+ * http请求方式:POST https://api.weixin.qq.com/wxaapi/broadcast/room/modifyassistant?access_token=ACCESS_TOKEN + *

+   * @param roomId 房间ID
+   * @param username 小助手微信号
+   * @param username 用户微信号
+   * @return 是否成功
+   * @throws WxErrorException .
+   */
+  boolean modifySubanchor(Integer roomId, String username) throws WxErrorException;
+
+  /**
+   * 删除主播副号
+   * 

+ * 调用频率: 10000次/一天 + *

+ * http请求方式:POST https://api.weixin.qq.com/wxaapi/broadcast/room/deletesubanchor?access_token=ACCESS_TOKEN + *

+   * @param roomId 房间ID
+   * @return 是否成功
+   * @throws WxErrorException .
+   */
+  boolean deleteSubanchor(Integer roomId) throws WxErrorException;
+
+  /**
+   * 获取主播副号
+   * 

+ * 调用额度:10000次/一天 + *

+ * http请求方式:GET https://api.weixin.qq.com/wxaapi/broadcast/room/getsubanchor?access_token=ACCESS_TOKEN + *

+ * @param roomId 直播间id + * @return . + * @throws WxErrorException . + */ + String getSubanchor(Integer roomId) throws WxErrorException; + + /** + * 开启/关闭直播间官方收录 + *

+ * 调用额度:10000次/一天 + *

+ * http请求方式:POST https://api.weixin.qq.com/wxaapi/broadcast/room/updatefeedpublic?access_token=ACCESS_TOKEN + *

+   * @param roomId 房间ID
+   * @param isFeedsPublic 是否开启官方收录 【1: 开启,0:关闭】
+   * @return 是否成功
+   * @throws WxErrorException .
+   */
+  boolean updatefeedpublic(Integer roomId, Integer isFeedsPublic) throws WxErrorException;
+
+  /**
+   * 开启/关闭回放功能
+   * 

+ * 调用额度:10000次/一天 + *

+ * http请求方式:POST https://api.weixin.qq.com/wxaapi/broadcast/room/updatereplay?access_token=ACCESS_TOKEN + *

+   * @param roomId 房间ID
+   * @param closeReplay 是否关闭回放 【0:开启,1:关闭】
+   * @return 是否成功
+   * @throws WxErrorException .
+   */
+  boolean updatereplay(Integer roomId, Integer closeReplay) throws WxErrorException;
+
+  /**
+   * 开启/关闭客服功能
+   * 

+ * 调用额度:10000次/一天 + *

+ * http请求方式:POST https://api.weixin.qq.com/wxaapi/broadcast/room/updatekf?access_token=ACCESS_TOKEN + *

+   * @param roomId 房间ID
+   * @param closeKf 是否关闭客服 【0:开启,1:关闭】
+   * @return 是否成功
+   * @throws WxErrorException .
+   */
+  boolean updatekf(Integer roomId, Integer closeKf) throws WxErrorException;
+
+  /**
+   * 开启/关闭直播间全局禁言
+   * 

+ * 调用额度:10000次/一天 + *

+ * http请求方式:POST https://api.weixin.qq.com/wxaapi/broadcast/room/updatecomment?access_token=ACCESS_TOKEN + *

+   * @param roomId 房间ID
+   * @param banComment 1-禁言,0-取消禁言
+   * @return 是否成功
+   * @throws WxErrorException .
+   */
+  boolean updatecomment(Integer roomId, Integer banComment) throws WxErrorException;
+
+  /**
+   * 上下架商品
+   * 

+ * 调用额度:10000次/一天 + *

+ * http请求方式:POST https://api.weixin.qq.com/wxaapi/broadcast/goods/onsale?access_token=ACCESS_TOKEN + *

+   * @param roomId 房间ID
+   * @param goodsId 商品ID
+   * @param onSale 上下架 【0:下架,1:上架】
+   * @return 是否成功
+   * @throws WxErrorException .
+   */
+  boolean onsale(Integer roomId, Integer goodsId, Integer onSale) throws WxErrorException;
+
+  /**
+   * 删除直播间商品
+   * 

+ * 调用额度:10000次/一天 + *

+ * http请求方式:POST https://api.weixin.qq.com/wxaapi/broadcast/goods/deleteInRoom?access_token=ACCESS_TOKEN + *

+   * @param roomId 房间ID
+   * @param goodsId 商品ID
+   * @return 是否成功
+   * @throws WxErrorException .
+   */
+  boolean deleteInRoom(Integer roomId, Integer goodsId) throws WxErrorException;
+
+  /**
+   * 推送商品
+   * 

+ * 调用额度:10000次/一天 + *

+ * http请求方式:POST https://api.weixin.qq.com/wxaapi/broadcast/goods/push?access_token=ACCESS_TOKEN + *

+   * @param roomId 房间ID
+   * @param goodsId 商品ID
+   * @return 是否成功
+   * @throws WxErrorException .
+   */
+  boolean push(Integer roomId, Integer goodsId) throws WxErrorException;
+
+  /**
+   * 直播间商品排序
+   * 

+ * 调用额度:10000次/一天 + *

+ * http请求方式:POST https://api.weixin.qq.com/wxaapi/broadcast/goods/sort?access_token=ACCESS_TOKEN + *

+   * @param roomId 房间ID
+   * @param goods 商品ID列表, 例如: [{"goodsId":"123"}, {"goodsId":"234"}]
+   * @return 是否成功
+   * @throws WxErrorException .
+   */
+  boolean sort(Integer roomId, List> goods) throws WxErrorException;
+
+  /**
+   * 下载商品讲解视频
+   * 

+ * 调用额度:10000次/一天 + *

+ * http请求方式:POST https://api.weixin.qq.com/wxaapi/broadcast/goods/getVideo?access_token=ACCESS_TOKEN + *

+ * @param roomId 直播间id + * @param goodsId 商品ID + * @return . + * @throws WxErrorException . + */ + String getVideo(Integer roomId, Integer goodsId) throws WxErrorException; } diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaMarketingService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaMarketingService.java new file mode 100644 index 000000000..be40288d6 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaMarketingService.java @@ -0,0 +1,35 @@ +package cn.binarywang.wx.miniapp.api; + +import cn.binarywang.wx.miniapp.bean.marketing.WxMaUserAction; +import me.chanjar.weixin.common.error.WxErrorException; +import java.util.List; + +/** + * + * @Description :微信营销接口 + * @author 184759547 + * @since : 2021/12/28 + */ +public interface WxMaMarketingService { + /** + *
+   * 创建数据源.
+   * 接口调用请求说明
+   * https://ad.weixin.qq.com/guide/457
+   * 
+ * + * @param type 用户行为源类型 + * @param name 用户行为源名称 必填 + * @param description 用户行为源描述,字段长度最小 1 字节,长度最大 128 字节 + */ + long addUserActionSets(String type, String name, String description) throws WxErrorException; + + /** + * 回传数据. + * 接口调用请求说明 + * https://ad.weixin.qq.com/guide/457 + * + * @param actions 用户行为源类型 + */ + String addUserAction(List actions, Long userActionSetId) throws WxErrorException; +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaQrcodeService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaQrcodeService.java index 5ef3ba798..ececed036 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaQrcodeService.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaQrcodeService.java @@ -101,45 +101,48 @@ public interface WxMaQrcodeService { /** * 接口A: 获取小程序码. * - * @param path 不能为空,最大长度 128 字节 - * @param width 默认430 二维码的宽度 - * @param autoColor 默认true 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调 - * @param lineColor autoColor 为 false 时生效,使用 rgb 设置颜色 例如 {"r":"xxx","g":"xxx","b":"xxx"} - * @param isHyaline 是否需要透明底色, isHyaline 为true时,生成透明底色的小程序码 + * @param path 不能为空,最大长度 128 字节 + * @param envVersion 默认"release" 要打开的小程序版本。正式版为 "release",体验版为 "trial",开发版为 "develop" + * @param width 默认430 二维码的宽度 + * @param autoColor 默认true 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调 + * @param lineColor autoColor 为 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; + byte[] createWxaCodeBytes(String path, String envVersion, int width, boolean autoColor, WxMaCodeLineColor lineColor, + boolean isHyaline) throws WxErrorException; /** * 接口A: 获取小程序码. * - * @param path 不能为空,最大长度 128 字节 - * @param width 默认430 二维码的宽度 - * @param filePath 二维码生成的文件路径,例如: /var/temp - * @param autoColor 默认true 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调 - * @param lineColor autoColor 为 false 时生效,使用 rgb 设置颜色 例如 {"r":"xxx","g":"xxx","b":"xxx"} - * @param isHyaline 是否需要透明底色, isHyaline 为true时,生成透明底色的小程序码 + * @param path 不能为空,最大长度 128 字节 + * @param envVersion 默认"release" 要打开的小程序版本。正式版为 "release",体验版为 "trial",开发版为 "develop" + * @param width 默认430 二维码的宽度 + * @param filePath 二维码生成的文件路径,例如: /var/temp + * @param autoColor 默认true 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调 + * @param lineColor autoColor 为 false 时生效,使用 rgb 设置颜色 例如 {"r":"xxx","g":"xxx","b":"xxx"} + * @param isHyaline 是否需要透明底色, isHyaline 为true时,生成透明底色的小程序码 * @return 文件对象 * @throws WxErrorException 异常 */ - File createWxaCode(String path, int width, String filePath, boolean autoColor, WxMaCodeLineColor lineColor, boolean isHyaline) - throws WxErrorException; + File createWxaCode(String path, String envVersion, int width, String filePath, boolean autoColor, + WxMaCodeLineColor lineColor, boolean isHyaline) throws WxErrorException; /** * 接口A: 获取小程序码. * - * @param path 不能为空,最大长度 128 字节 - * @param width 默认430 二维码的宽度 - * @param autoColor 默认true 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调 - * @param lineColor autoColor 为 false 时生效,使用 rgb 设置颜色 例如 {"r":"xxx","g":"xxx","b":"xxx"} - * @param isHyaline 是否需要透明底色, isHyaline 为true时,生成透明底色的小程序码 + * @param path 不能为空,最大长度 128 字节 + * @param envVersion 默认"release" 要打开的小程序版本。正式版为 "release",体验版为 "trial",开发版为 "develop" + * @param width 默认430 二维码的宽度 + * @param autoColor 默认true 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调 + * @param lineColor autoColor 为 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 isHyaline) - throws WxErrorException; + File createWxaCode(String path, String envVersion, int width, boolean autoColor, WxMaCodeLineColor lineColor, + boolean isHyaline) throws WxErrorException; /** * 接口A: 获取小程序码. @@ -190,17 +193,20 @@ File createWxaCode(String path, int width, boolean autoColor, WxMaCodeLineColor * 调试阶段可以使用开发工具的条件编译自定义参数 scene=xxxx 进行模拟,开发工具模拟时的 scene 的参数值需要进行 urlencode *
* - * @param scene 最大32个可见字符,只支持数字,大小写英文以及部分特殊字符:!#$&'()*+,/:;=?@-._~, - * 其它字符请自行编码为合法字符(因不支持%,中文无法使用 urlencode 处理,请使用其他编码方式) - * @param page 必须是已经发布的小程序页面,例如 "pages/index/index" ,如果不填写这个字段,默认跳主页面 - * @param width 默认430 二维码的宽度 - * @param autoColor 默认true 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调 - * @param lineColor autoColor 为 false 时生效,使用 rgb 设置颜色 例如 {"r":"xxx","g":"xxx","b":"xxx"} - * @param isHyaline 是否需要透明底色, is_hyaline 为true时,生成透明底色的小程序码 + * @param scene 最大32个可见字符,只支持数字,大小写英文以及部分特殊字符:!#$&'()*+,/:;=?@-._~, + * 其它字符请自行编码为合法字符(因不支持%,中文无法使用 urlencode 处理,请使用其他编码方式) + * @param page 必须是已经发布的小程序页面,例如 "pages/index/index" ,如果不填写这个字段,默认跳主页面 + * @param checkPath 默认true 检查 page 是否存在,为 true 时 page 必须是已经发布的小程序存在的页面(否则报错); + * 为 false 时允许小程序未发布或者 page 不存在,但 page 有数量上限(60000个)请勿滥用 + * @param envVersion 默认"release" 要打开的小程序版本。正式版为 "release",体验版为 "trial",开发版为 "develop" + * @param width 默认430 二维码的宽度 + * @param autoColor 默认true 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调 + * @param lineColor autoColor 为 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, + byte[] createWxaCodeUnlimitBytes(String scene, String page, boolean checkPath, String envVersion, int width, boolean autoColor, WxMaCodeLineColor lineColor, boolean isHyaline) throws WxErrorException; /** @@ -212,19 +218,22 @@ byte[] createWxaCodeUnlimitBytes(String scene, String page, int width, boolean a * 调试阶段可以使用开发工具的条件编译自定义参数 scene=xxxx 进行模拟,开发工具模拟时的 scene 的参数值需要进行 urlencode *
* - * @param scene 最大32个可见字符,只支持数字,大小写英文以及部分特殊字符:!#$&'()*+,/:;=?@-._~, - * 其它字符请自行编码为合法字符(因不支持%,中文无法使用 urlencode 处理,请使用其他编码方式) - * @param page 必须是已经发布的小程序页面,例如 "pages/index/index" ,如果不填写这个字段,默认跳主页面 - * @param filePath 二维码生成的文件路径,例如: /var/temp - * @param width 默认430 二维码的宽度 - * @param autoColor 默认true 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调 - * @param lineColor autoColor 为 false 时生效,使用 rgb 设置颜色 例如 {"r":"xxx","g":"xxx","b":"xxx"} - * @param isHyaline 是否需要透明底色, is_hyaline 为true时,生成透明底色的小程序码 + * @param scene 最大32个可见字符,只支持数字,大小写英文以及部分特殊字符:!#$&'()*+,/:;=?@-._~, + * 其它字符请自行编码为合法字符(因不支持%,中文无法使用 urlencode 处理,请使用其他编码方式) + * @param page 必须是已经发布的小程序页面,例如 "pages/index/index" ,如果不填写这个字段,默认跳主页面 + * @param filePath 二维码生成的文件路径,例如: /var/temp + * @param checkPath 默认true 检查 page 是否存在,为 true 时 page 必须是已经发布的小程序存在的页面(否则报错); + * 为 false 时允许小程序未发布或者 page 不存在,但 page 有数量上限(60000个)请勿滥用 + * @param envVersion 默认"release" 要打开的小程序版本。正式版为 "release",体验版为 "trial",开发版为 "develop" + * @param width 默认430 二维码的宽度 + * @param autoColor 默认true 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调 + * @param lineColor autoColor 为 false 时生效,使用 rgb 设置颜色 例如 {"r":"xxx","g":"xxx","b":"xxx"} + * @param isHyaline 是否需要透明底色, is_hyaline 为true时,生成透明底色的小程序码 * @return 文件对象 * @throws WxErrorException 异常 */ - File createWxaCodeUnlimit(String scene, String page, String filePath, int width, boolean autoColor, - WxMaCodeLineColor lineColor, boolean isHyaline) throws WxErrorException; + File createWxaCodeUnlimit(String scene, String page, String filePath, boolean checkPath, String envVersion, int width, + boolean autoColor, WxMaCodeLineColor lineColor, boolean isHyaline) throws WxErrorException; /** * 接口B: 获取小程序码(永久有效、数量暂无限制). @@ -235,17 +244,20 @@ File createWxaCodeUnlimit(String scene, String page, String filePath, int width, * 调试阶段可以使用开发工具的条件编译自定义参数 scene=xxxx 进行模拟,开发工具模拟时的 scene 的参数值需要进行 urlencode *
* - * @param scene 最大32个可见字符,只支持数字,大小写英文以及部分特殊字符:!#$&'()*+,/:;=?@-._~, - * 其它字符请自行编码为合法字符(因不支持%,中文无法使用 urlencode 处理,请使用其他编码方式) - * @param page 必须是已经发布的小程序页面,例如 "pages/index/index" ,如果不填写这个字段,默认跳主页面 - * @param width 默认430 二维码的宽度 - * @param autoColor 默认true 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调 - * @param lineColor autoColor 为 false 时生效,使用 rgb 设置颜色 例如 {"r":"xxx","g":"xxx","b":"xxx"} - * @param isHyaline 是否需要透明底色, is_hyaline 为true时,生成透明底色的小程序码 + * @param scene 最大32个可见字符,只支持数字,大小写英文以及部分特殊字符:!#$&'()*+,/:;=?@-._~, + * 其它字符请自行编码为合法字符(因不支持%,中文无法使用 urlencode 处理,请使用其他编码方式) + * @param page 必须是已经发布的小程序页面,例如 "pages/index/index" ,如果不填写这个字段,默认跳主页面 + * @param checkPath 默认true 检查 page 是否存在,为 true 时 page 必须是已经发布的小程序存在的页面(否则报错); + * 为 false 时允许小程序未发布或者 page 不存在,但 page 有数量上限(60000个)请勿滥用 + * @param envVersion 默认"release" 要打开的小程序版本。正式版为 "release",体验版为 "trial",开发版为 "develop" + * @param width 默认430 二维码的宽度 + * @param autoColor 默认true 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调 + * @param lineColor autoColor 为 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, + File createWxaCodeUnlimit(String scene, String page, boolean checkPath, String envVersion, int width, boolean autoColor, WxMaCodeLineColor lineColor, boolean isHyaline) throws WxErrorException; /** diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaSafetyRiskControlService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaSafetyRiskControlService.java new file mode 100644 index 000000000..c84a93d13 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaSafetyRiskControlService.java @@ -0,0 +1,27 @@ +package cn.binarywang.wx.miniapp.api; + +import cn.binarywang.wx.miniapp.bean.safety.request.WxMaUserSafetyRiskRankRequest; +import cn.binarywang.wx.miniapp.bean.safety.response.WxMaUserSafetyRiskRankResponse; +import me.chanjar.weixin.common.error.WxErrorException; + +/** + *
+ * 小程序安全风控相关接口
+ * 
+ * + * @author azouever + */ +public interface WxMaSafetyRiskControlService { + + /** + *
+   * 根据提交的用户信息数据获取用户的安全等级,无需用户授权
+   * 文档:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/safety-control-capability/riskControl.getUserRiskRank.html
+   * 
+ * + * @param wxMaUserSafetyRiskRankRequest 获取用户安全等级请求 + * @throws WxErrorException 通用异常 + */ + WxMaUserSafetyRiskRankResponse getUserRiskRank(WxMaUserSafetyRiskRankRequest wxMaUserSafetyRiskRankRequest) throws WxErrorException; + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaSecCheckService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaSecCheckService.java index 8b135adcd..a22061a00 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaSecCheckService.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaSecCheckService.java @@ -1,6 +1,7 @@ package cn.binarywang.wx.miniapp.api; import cn.binarywang.wx.miniapp.bean.WxMaMediaAsyncCheckResult; +import cn.binarywang.wx.miniapp.bean.security.WxMaMediaSecCheckCheckRequest; import cn.binarywang.wx.miniapp.bean.security.WxMaMsgSecCheckCheckRequest; import cn.binarywang.wx.miniapp.bean.security.WxMaMsgSecCheckCheckResponse; import me.chanjar.weixin.common.error.WxErrorException; @@ -64,7 +65,7 @@ public interface WxMaSecCheckService { *
* @param msgRequest * @return WxMaMsgSecCheckCheckResponse - * @throws WxErrorException + * @throws WxErrorException the wx error exception */ WxMaMsgSecCheckCheckResponse checkMessage(WxMaMsgSecCheckCheckRequest msgRequest) throws WxErrorException; @@ -88,4 +89,25 @@ public interface WxMaSecCheckService { */ WxMaMediaAsyncCheckResult mediaCheckAsync(String mediaUrl, int mediaType) throws WxErrorException; + + /** + *
+   * 异步校验图片/音频是否含有违法违规内容。(新版本接口,主要对request和respone做了参数优化)
+   * 应用场景举例:
+   * 语音风险识别:社交类用户发表的语音内容检测;
+   * 图片智能鉴黄:涉及拍照的工具类应用(如美拍,识图类应用)用户拍照上传检测;电商类商品上架图片检测;媒体类用户文章里的图片检测等;
+   * 敏感人脸识别:用户头像;媒体类用户文章里的图片检测;社交类用户上传的图片检测等。
+   * 频率限制:
+   * 单个 appId 调用上限为 2000 次/分钟,200,000 次/天;文件大小限制:单个文件大小不超过10M
+   * 详情请见:
+   * https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/sec-check/security.mediaCheckAsync.html
+   * 
+ * + * @param medisRequest + * @return wx ma media async check result + * @throws WxErrorException the wx error exception + */ + + WxMaMediaAsyncCheckResult mediaCheckAsync(WxMaMediaSecCheckCheckRequest medisRequest) throws WxErrorException; + } diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaService.java index d0b2dcf12..8b357a847 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaService.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaService.java @@ -462,4 +462,34 @@ public interface WxMaService extends WxService { * @return */ WxMaReimburseInvoiceService getReimburseInvoiceService(); + + /** + * 返回设备订阅消息相关接口服务对象 + * + * @return WxMaDeviceSubscribeService plugin service + */ + WxMaDeviceSubscribeService getDeviceSubscribeService(); + + /** + * 返回小程序广告接入相关接口服务对象 + * + * @return WxMaDeviceSubscribeService plugin service + */ + WxMaMarketingService getMarketingService(); + + /** + * 返回微信小程序即时配送服务接口. + * + * @return WxMaImmediateDeliveryService + */ + WxMaImmediateDeliveryService getWxMaImmediateDeliveryService(); + + + /** + * 小程序安全风控相关接口服务 + * + * @return safetyRiskControl service + */ + WxMaSafetyRiskControlService getSafetyRiskControlService(); + } diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaShopImgService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaShopImgService.java index 2cb9334fc..0bb4d3198 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaShopImgService.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaShopImgService.java @@ -29,4 +29,14 @@ public interface WxMaShopImgService { * @throws WxErrorException */ WxMinishopImageUploadCustomizeResult uploadImg(File file, String respType) throws WxErrorException; + + /** + * 上传图片链接,带respType参数 + * + * @param imgUrl + * @param respType + * @return WxMinishopImageUploadCustomizeResult + * @throws WxErrorException + */ + WxMinishopImageUploadCustomizeResult uploadImg(String imgUrl, String respType) throws WxErrorException; } diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaUserService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaUserService.java index 0c513789c..0a0d66f6e 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaUserService.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaUserService.java @@ -54,6 +54,15 @@ public interface WxMaUserService { */ WxMaPhoneNumberInfo getPhoneNoInfo(String sessionKey, String encryptedData, String ivStr); + /** + * 获取手机号信息,基础库:2.21.2及以上 + * + * @param code 动态令牌 + * @return . + * @throws WxErrorException . + */ + WxMaPhoneNumberInfo getNewPhoneNoInfo(String code) throws WxErrorException; + /** * 验证用户信息完整性. * diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/BaseWxMaServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/BaseWxMaServiceImpl.java index ecbce2ee0..62ca188d5 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/BaseWxMaServiceImpl.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/BaseWxMaServiceImpl.java @@ -75,6 +75,10 @@ public abstract class BaseWxMaServiceImpl implements WxMaService, RequestH private final WxMaShopDeliveryService shopDeliveryService = new WxMaShopDeliveryServiceImpl(this); private final WxMaLinkService linkService = new WxMaLinkServiceImpl(this); private final WxMaReimburseInvoiceService reimburseInvoiceService = new WxMaReimburseInvoiceServiceImpl(this); + private final WxMaDeviceSubscribeService deviceSubscribeService = new WxMaDeviceSubscribeServiceImpl(this); + private final WxMaMarketingService marketingService = new WxMaMarketingServiceImpl(this); + private final WxMaImmediateDeliveryService immediateDeliveryService = new WxMaImmediateDeliveryServiceImpl(this); + private final WxMaSafetyRiskControlService safetyRiskControlService = new WxMaSafetyRiskControlServiceImpl(this); private Map configMap; private int retrySleepMillis = 1000; private int maxRetryTimes = 5; @@ -573,4 +577,19 @@ public WxMaLinkService getLinkService() { public WxMaReimburseInvoiceService getReimburseInvoiceService() { return this.reimburseInvoiceService; } + + @Override + public WxMaDeviceSubscribeService getDeviceSubscribeService(){ return this.deviceSubscribeService; } + + @Override + public WxMaMarketingService getMarketingService() {return this.marketingService; } + + @Override + public WxMaImmediateDeliveryService getWxMaImmediateDeliveryService() { + return this.immediateDeliveryService; + } + + @Override + public WxMaSafetyRiskControlService getSafetyRiskControlService(){ return this.safetyRiskControlService; } + } diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaDeviceSubscribeServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaDeviceSubscribeServiceImpl.java new file mode 100644 index 000000000..db12ab9b5 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaDeviceSubscribeServiceImpl.java @@ -0,0 +1,50 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaDeviceSubscribeService; +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.device.WxMaDeviceSubscribeMessageRequest; +import cn.binarywang.wx.miniapp.bean.device.WxMaDeviceTicketRequest; +import cn.binarywang.wx.miniapp.constant.WxMaConstants; +import com.google.gson.JsonObject; +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.json.GsonParser; + +import static cn.binarywang.wx.miniapp.constant.WxMaApiUrlConstants.DeviceSubscribe.GET_SN_TICKET_URL; +import static cn.binarywang.wx.miniapp.constant.WxMaApiUrlConstants.DeviceSubscribe.SEND_DEVICE_SUBSCRIBE_MSG_URL; + +/** + * 小程序设备订阅消息相关 API + * 文档: + * + * @author JCLee + * @since 2021-12-16 17:13:35 + */ + +@RequiredArgsConstructor +public class WxMaDeviceSubscribeServiceImpl implements WxMaDeviceSubscribeService { + + private final WxMaService service; + + @Override + public String getSnTicket(WxMaDeviceTicketRequest deviceTicketRequest) throws WxErrorException { + String responseContent = this.service.post(GET_SN_TICKET_URL, deviceTicketRequest.toJson()); + JsonObject jsonObject = GsonParser.parse(responseContent); + if (jsonObject.get(WxMaConstants.ERRCODE).getAsInt() != 0) { + throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp)); + } + String snTicket = jsonObject.get("sn_ticket").getAsString(); + return snTicket; + } + + @Override + public void sendDeviceSubscribeMsg(WxMaDeviceSubscribeMessageRequest deviceSubscribeMessageRequest) throws WxErrorException { + String responseContent = this.service.post(SEND_DEVICE_SUBSCRIBE_MSG_URL, deviceSubscribeMessageRequest.toJson()); + JsonObject jsonObject = GsonParser.parse(responseContent); + if (jsonObject.get(WxMaConstants.ERRCODE).getAsInt() != 0) { + throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp)); + } + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaImgProcServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaImgProcServiceImpl.java index 1ed94fe4d..03f46fb31 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaImgProcServiceImpl.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaImgProcServiceImpl.java @@ -35,7 +35,7 @@ public WxImgProcQrCodeResult qrCode(String imgUrl) throws WxErrorException { //ignore } - final String result = this.service.get(String.format(QRCODE, imgUrl), null); + final String result = this.service.post(String.format(QRCODE, imgUrl), ""); return WxImgProcQrCodeResult.fromJson(result); } diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaImmediateDeliveryServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaImmediateDeliveryServiceImpl.java new file mode 100644 index 000000000..18f99a860 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaImmediateDeliveryServiceImpl.java @@ -0,0 +1,219 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaImmediateDeliveryService; +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.delivery.AbnormalConfirmRequest; +import cn.binarywang.wx.miniapp.bean.delivery.AbnormalConfirmResponse; +import cn.binarywang.wx.miniapp.bean.delivery.AddOrderRequest; +import cn.binarywang.wx.miniapp.bean.delivery.AddOrderResponse; +import cn.binarywang.wx.miniapp.bean.delivery.BindAccountResponse; +import cn.binarywang.wx.miniapp.bean.delivery.CancelOrderRequest; +import cn.binarywang.wx.miniapp.bean.delivery.CancelOrderResponse; +import cn.binarywang.wx.miniapp.bean.delivery.GetOrderRequest; +import cn.binarywang.wx.miniapp.bean.delivery.GetOrderResponse; +import cn.binarywang.wx.miniapp.bean.delivery.MockUpdateOrderRequest; +import cn.binarywang.wx.miniapp.bean.delivery.MockUpdateOrderResponse; +import cn.binarywang.wx.miniapp.bean.delivery.QueryWaybillTraceRequest; +import cn.binarywang.wx.miniapp.bean.delivery.QueryWaybillTraceResponse; +import cn.binarywang.wx.miniapp.bean.delivery.TraceWaybillRequest; +import cn.binarywang.wx.miniapp.bean.delivery.TraceWaybillResponse; +import cn.binarywang.wx.miniapp.bean.delivery.base.WxMaDeliveryBaseResponse; +import cn.binarywang.wx.miniapp.constant.WxMaApiUrlConstants; +import cn.binarywang.wx.miniapp.constant.WxMaApiUrlConstants.InstantDelivery; +import cn.binarywang.wx.miniapp.constant.WxMaConstants; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import javassist.bytecode.ConstPool; +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.enums.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.json.GsonParser; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; + +/** + * 微信小程序即时配送服务. + *
+ *     文档地址:https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/industry/immediate-delivery/overview.html
+ * 
+ * + * @author Luo + * @version 1.0 + * @date 2021-10-13 16:40 + */ +@RequiredArgsConstructor +public class WxMaImmediateDeliveryServiceImpl implements WxMaImmediateDeliveryService { + + /** + * 微信响应码. + */ + public static final String ERR_CODE = "errcode"; + + /** + * 顺丰同城响应码. + */ + public static final String SF_ERR_CODE = "resultcode"; + + /** + * 顺丰同城响应说明. + */ + public static final String SF_ERR_MSG = "resultmsg"; + + /** + * 成功响应状态码. + */ + public static final int SUCCESS_CODE = 0; + + private final WxMaService wxMaService; + + /** + * 拉取已绑定账号. + *
+   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/immediate-delivery/by-business/immediateDelivery.getBindAccount.html
+   * 
+ * + * @return 响应 + * @throws WxErrorException 异常 + */ + @Override + public BindAccountResponse getBindAccount() throws WxErrorException { + return this.parse(this.wxMaService.post(WxMaApiUrlConstants.InstantDelivery.GET_BIND_ACCOUNT, "{}"), + BindAccountResponse.class); + } + + /** + * 下配送单接口. + *
+   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/immediate-delivery/by-business/immediateDelivery.addOrder.html
+   * 
+ * + * @param request request + * @return 响应 + * @throws WxErrorException 异常 + */ + @Override + public AddOrderResponse addOrder(final AddOrderRequest request) throws WxErrorException { + return this.parse(this.wxMaService.post(WxMaApiUrlConstants.InstantDelivery.PlaceAnOrder.ADD_ORDER, request), + AddOrderResponse.class); + } + + /** + * 拉取配送单信息. + *
+   * 商家可使用本接口查询某一配送单的配送状态,便于商家掌握配送情况。
+   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/immediate-delivery/by-business/immediateDelivery.getOrder.html
+   * 
+ * + * @param request request + * @return 响应 + * @throws WxErrorException 异常 + */ + @Override + public GetOrderResponse getOrder(final GetOrderRequest request) throws WxErrorException { + return this.parse(this.wxMaService.post(WxMaApiUrlConstants.InstantDelivery.GET_ORDER, request), + GetOrderResponse.class); + } + + /** + * 取消配送单接口. + *
+   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/immediate-delivery/by-business/immediateDelivery.cancelOrder.html
+   * 
+ * + * @param request request + * @return 响应 + * @throws WxErrorException 异常 + */ + @Override + public CancelOrderResponse cancelOrder(final CancelOrderRequest request) throws WxErrorException { + return this.parse(this.wxMaService.post(WxMaApiUrlConstants.InstantDelivery.Cancel.CANCEL_ORDER, request), + CancelOrderResponse.class); + } + + /** + * 异常件退回商家商家确认收货接口. + *
+   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/immediate-delivery/by-business/immediateDelivery.abnormalConfirm.html
+   * 
+ * + * @param request request + * @return 响应 + * @throws WxErrorException 异常 + */ + @Override + public AbnormalConfirmResponse abnormalConfirm(final AbnormalConfirmRequest request) throws WxErrorException { + return this.parse(this.wxMaService.post(WxMaApiUrlConstants.InstantDelivery.Cancel.ABNORMAL_CONFIRM, request), + AbnormalConfirmResponse.class); + } + + /** + * 模拟配送公司更新配送单状态, 该接口只用于沙盒环境,即订单并没有真实流转到运力方. + *
+   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/immediate-delivery/by-business/immediateDelivery.mockUpdateOrder.html
+   * 
+ * + * @param request request + * @return 响应 + * @throws WxErrorException 异常 + */ + @Override + public MockUpdateOrderResponse mockUpdateOrder(final MockUpdateOrderRequest request) throws WxErrorException { + return this.parse(this.wxMaService.post(WxMaApiUrlConstants.InstantDelivery.MOCK_UPDATE_ORDER, request), + MockUpdateOrderResponse.class); + } + + @Override + public TraceWaybillResponse traceWaybill( + TraceWaybillRequest request) throws WxErrorException { + String responseContent = this.wxMaService.post(InstantDelivery.TRACE_WAYBILL_URL, request); + TraceWaybillResponse response = TraceWaybillResponse.fromJson(responseContent); + if (response.getErrcode() == -1) { + throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp)); + } + return response; + + } + + @Override + public QueryWaybillTraceResponse queryWaybillTrace( + QueryWaybillTraceRequest request) throws WxErrorException { + String responseContent = this.wxMaService.post(InstantDelivery.QUERY_WAYBILL_TRACE_URL, request); + QueryWaybillTraceResponse response = QueryWaybillTraceResponse.fromJson(responseContent); + if (response.getErrcode() == -1) { + throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp)); + } + return response; + } + + /** + * 解析响应. + * + * @param responseContent 响应内容 + * @param valueType 类型 + * @param 类型 + * @return 结果 + * @throws WxErrorException 异常 + */ + private T parse(final String responseContent, final Class valueType) throws WxErrorException { + if (StringUtils.isBlank(responseContent)) { + throw new RuntimeException("the responseContent cannot be empty"); + } + // 解析成Json对象 + JsonObject jsonObject = GsonParser.parse(responseContent); + // 是否为微信错误响应 当 errcode==0 或者 不存在 还需要看 运力方 resultcode 状态码 + JsonElement element = jsonObject.get(ERR_CODE); + // 正常响应下不会有该字段返回 + if (!ObjectUtils.isEmpty(element) && SUCCESS_CODE != element.getAsInt()) { + throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp)); + } + // 是否为运力方错误响应 + JsonElement delivery = jsonObject.get(SF_ERR_CODE); + if (!ObjectUtils.isEmpty(delivery) && SUCCESS_CODE != delivery.getAsInt()) { + throw new WxErrorException(jsonObject.get(SF_ERR_MSG).getAsString()); + } + // 解析成对应响应对象 + return WxMaDeliveryBaseResponse.fromJson(responseContent, valueType); + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaInternetServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaInternetServiceImpl.java index 8ee023baf..c72382836 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaInternetServiceImpl.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaInternetServiceImpl.java @@ -9,9 +9,12 @@ import me.chanjar.weixin.common.enums.WxType; import me.chanjar.weixin.common.error.WxError; import me.chanjar.weixin.common.error.WxErrorException; +import org.jetbrains.annotations.NotNull; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; /** - * * 服务端网络相关接口 * * @author chutian0124 @@ -21,9 +24,39 @@ public class WxMaInternetServiceImpl implements WxMaInternetService { private final WxMaService wxMaService; + private String sha256(String data, String sessionKey) throws Exception { + Mac sha256_HMAC = Mac.getInstance("HmacSHA256"); + SecretKeySpec secret_key = new SecretKeySpec(sessionKey.getBytes("UTF-8"), "HmacSHA256"); + sha256_HMAC.init(secret_key); + byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8")); + StringBuilder sb = new StringBuilder(); + for (byte item : array) { + sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3)); + } + return sb.toString().toUpperCase(); + } + @Override - public WxMaInternetResponse getUserEncryptKey() throws WxErrorException { - String responseContent = this.wxMaService.post(WxMaApiUrlConstants.Internet.GET_USER_ENCRYPT_KEY, ""); + public WxMaInternetResponse getUserEncryptKey(String openid, String signature, String sigMethod) throws WxErrorException { + String url = WxMaApiUrlConstants.Internet.GET_USER_ENCRYPT_KEY + "?openid=" + openid + "&signature=" + signature + "&sig_method=" + sigMethod; + return getWxMaInternetResponse(url); + } + + @Override + public WxMaInternetResponse getUserEncryptKey(String openid, String sessionKey) throws WxErrorException { + String signature = null; + try { + signature = sha256("", sessionKey); + } catch (Exception e) { + throw new WxErrorException("签名错误"); + } + String url = WxMaApiUrlConstants.Internet.GET_USER_ENCRYPT_KEY + "?sig_method=hmac_sha256&openid=" + openid + "&signature=" + signature; + return getWxMaInternetResponse(url); + } + + @NotNull + private WxMaInternetResponse getWxMaInternetResponse(String url) throws WxErrorException { + String responseContent = this.wxMaService.post(url, ""); WxMaInternetResponse response = WxMaGsonBuilder.create().fromJson(responseContent, WxMaInternetResponse.class); if (response.getErrcode() == -1) { throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp)); diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaLinkServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaLinkServiceImpl.java index e0dd4a915..156795d4c 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaLinkServiceImpl.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaLinkServiceImpl.java @@ -29,7 +29,7 @@ public String generateUrlLink(GenerateUrlLinkRequest request) throws WxErrorExce String linkField = "url_link"; JsonObject jsonObject = GsonParser.parse(result); if (jsonObject.has(linkField)) { - return jsonObject.get(linkField).toString(); + return jsonObject.get(linkField).getAsString(); } throw new WxErrorException("无url_link"); } @@ -40,7 +40,7 @@ public String generateShortLink(GenerateShortLinkRequest request) throws WxError String linkField = "link"; JsonObject jsonObject = GsonParser.parse(result); if (jsonObject.has(linkField)) { - return jsonObject.get(linkField).toString(); + return jsonObject.get(linkField).getAsString(); } throw new WxErrorException("无link"); } diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaLiveGoodsServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaLiveGoodsServiceImpl.java index 99d82fdbf..cfd842867 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaLiveGoodsServiceImpl.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaLiveGoodsServiceImpl.java @@ -9,6 +9,7 @@ import com.google.common.collect.ImmutableMap; import com.google.gson.JsonArray; import com.google.gson.JsonObject; +import com.google.gson.reflect.TypeToken; import lombok.RequiredArgsConstructor; import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.util.json.GsonHelper; @@ -20,6 +21,7 @@ import java.util.Map; import static cn.binarywang.wx.miniapp.constant.WxMaApiUrlConstants.Broadcast.Goods.*; +import static cn.binarywang.wx.miniapp.constant.WxMaApiUrlConstants.Code.GET_PAGE_URL; /** *
@@ -94,4 +96,26 @@ public WxMaLiveResult getApprovedGoods(Integer offset, Integer limit, Integer st
     return WxMaLiveResult.fromJson(jsonObject.toString());
   }
 
+  @Override
+  public boolean setKey(List goodsKey) throws WxErrorException {
+    Map map = new HashMap<>(1);
+    map.put("goodsKey", goodsKey);
+    this.wxMaService.post(SET_KEY, WxMaGsonBuilder.create().toJson(map));
+    return true;
+  }
+
+  @Override
+  public List getKey() throws WxErrorException {
+    String responseContent = this.wxMaService.get(GET_KEY, null);
+    JsonObject jsonObject = GsonParser.parse(responseContent);
+    boolean vendorGoodsKey = jsonObject.has("vendorGoodsKey");
+    if (vendorGoodsKey) {
+      return WxMaGsonBuilder.create().fromJson(jsonObject.getAsJsonArray("vendorGoodsKey"),
+              new TypeToken>() {
+              }.getType());
+    } else {
+      return null;
+    }
+  }
+
 }
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaLiveServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaLiveServiceImpl.java
index b71e58653..56c744f6d 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaLiveServiceImpl.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaLiveServiceImpl.java
@@ -18,7 +18,6 @@
 import java.util.List;
 import java.util.Map;
 
-import static cn.binarywang.wx.miniapp.constant.WxMaApiUrlConstants.Broadcast.GET_LIVE_INFO;
 import static cn.binarywang.wx.miniapp.constant.WxMaApiUrlConstants.Broadcast.Room;
 
 /**
@@ -152,7 +151,7 @@ private JsonObject getLiveInfo(Integer start, Integer limit, Map
     }
     map.put("start", start);
     map.put("limit", limit);
-    String responseContent = wxMaService.post(GET_LIVE_INFO, WxMaGsonBuilder.create().toJson(map));
+    String responseContent = wxMaService.post(Room.GET_LIVE_INFO, WxMaGsonBuilder.create().toJson(map));
     JsonObject jsonObject = GsonParser.parse(responseContent);
     if (jsonObject.get(ERR_CODE).getAsInt() != 0) {
       throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp));
@@ -231,4 +230,172 @@ public List getAssistantList(Integer roomId) thro
     return WxMaAssistantResult.fromJson(responseContent).getList();
   }
 
+  @Override
+  public boolean addSubanchor(Integer roomId, String username) throws WxErrorException {
+    Map map = new HashMap<>(2);
+    map.put(ROOM_ID, roomId);
+    map.put("username", username);
+    String responseContent = this.wxMaService.post(Room.ADD_SUBANCHOR, WxMaGsonBuilder.create().toJson(map));
+    JsonObject jsonObject = GsonParser.parse(responseContent);
+    if (jsonObject.get(ERR_CODE).getAsInt() != 0) {
+      throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp));
+    }
+    return true;
+  }
+
+  @Override
+  public boolean modifySubanchor(Integer roomId, String username) throws WxErrorException {
+    Map map = new HashMap<>(2);
+    map.put(ROOM_ID, roomId);
+    map.put("username", username);
+    String responseContent = this.wxMaService.post(Room.MODIFY_SUBANCHOR, WxMaGsonBuilder.create().toJson(map));
+    JsonObject jsonObject = GsonParser.parse(responseContent);
+    if (jsonObject.get(ERR_CODE).getAsInt() != 0) {
+      throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp));
+    }
+    return true;
+  }
+
+  @Override
+  public boolean deleteSubanchor(Integer roomId) throws WxErrorException {
+    Map map = new HashMap<>(1);
+    map.put(ROOM_ID, roomId);
+    String responseContent = this.wxMaService.post(Room.DELETE_SUBANCHOR, WxMaGsonBuilder.create().toJson(map));
+    JsonObject jsonObject = GsonParser.parse(responseContent);
+    if (jsonObject.get(ERR_CODE).getAsInt() != 0) {
+      throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp));
+    }
+    return true;
+  }
+
+  @Override
+  public String getSubanchor(Integer roomId) throws WxErrorException {
+    Map map = new HashMap<>(1);
+    map.put(ROOM_ID, roomId);
+    String responseContent = this.wxMaService.get(Room.GET_SUBANCHOR, Joiner.on("&").withKeyValueSeparator("=").join(map));
+    JsonObject jsonObject = GsonParser.parse(responseContent);
+    if (jsonObject.get(ERR_CODE).getAsInt() != 0) {
+      throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp));
+    }
+    return jsonObject.get("username").getAsString();
+  }
+
+  @Override
+  public boolean updatefeedpublic(Integer roomId, Integer isFeedsPublic) throws WxErrorException {
+    Map map = new HashMap<>(2);
+    map.put(ROOM_ID, roomId);
+    map.put("isFeedsPublic", isFeedsPublic);
+    String responseContent = this.wxMaService.post(Room.UPDATE_FEED_PUBLIC, WxMaGsonBuilder.create().toJson(map));
+    JsonObject jsonObject = GsonParser.parse(responseContent);
+    if (jsonObject.get(ERR_CODE).getAsInt() != 0) {
+      throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp));
+    }
+    return true;
+  }
+
+  @Override
+  public boolean updatereplay(Integer roomId, Integer closeReplay) throws WxErrorException {
+    Map map = new HashMap<>(2);
+    map.put(ROOM_ID, roomId);
+    map.put("closeReplay", closeReplay);
+    String responseContent = this.wxMaService.post(Room.UPDATE_REPLAY, WxMaGsonBuilder.create().toJson(map));
+    JsonObject jsonObject = GsonParser.parse(responseContent);
+    if (jsonObject.get(ERR_CODE).getAsInt() != 0) {
+      throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp));
+    }
+    return true;
+  }
+
+  @Override
+  public boolean updatekf(Integer roomId, Integer closeKf) throws WxErrorException {
+    Map map = new HashMap<>(2);
+    map.put(ROOM_ID, roomId);
+    map.put("closeKf", closeKf);
+    String responseContent = this.wxMaService.post(Room.UPDATE_KF, WxMaGsonBuilder.create().toJson(map));
+    JsonObject jsonObject = GsonParser.parse(responseContent);
+    if (jsonObject.get(ERR_CODE).getAsInt() != 0) {
+      throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp));
+    }
+    return true;
+  }
+
+  @Override
+  public boolean updatecomment(Integer roomId, Integer banComment) throws WxErrorException {
+    Map map = new HashMap<>(2);
+    map.put(ROOM_ID, roomId);
+    map.put("banComment", banComment);
+    String responseContent = this.wxMaService.post(Room.UPDATE_COMMENT, WxMaGsonBuilder.create().toJson(map));
+    JsonObject jsonObject = GsonParser.parse(responseContent);
+    if (jsonObject.get(ERR_CODE).getAsInt() != 0) {
+      throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp));
+    }
+    return true;
+  }
+
+  @Override
+  public boolean onsale(Integer roomId, Integer goodsId, Integer onSale) throws WxErrorException {
+    Map map = new HashMap<>(3);
+    map.put(ROOM_ID, roomId);
+    map.put("goodsId", goodsId);
+    map.put("onSale", onSale);
+    String responseContent = this.wxMaService.post(Room.ONSALE, WxMaGsonBuilder.create().toJson(map));
+    JsonObject jsonObject = GsonParser.parse(responseContent);
+    if (jsonObject.get(ERR_CODE).getAsInt() != 0) {
+      throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp));
+    }
+    return true;
+  }
+
+  @Override
+  public boolean deleteInRoom(Integer roomId, Integer goodsId) throws WxErrorException {
+    Map map = new HashMap<>(2);
+    map.put(ROOM_ID, roomId);
+    map.put("goodsId", goodsId);
+    String responseContent = this.wxMaService.post(Room.DELETE_IN_ROOM, WxMaGsonBuilder.create().toJson(map));
+    JsonObject jsonObject = GsonParser.parse(responseContent);
+    if (jsonObject.get(ERR_CODE).getAsInt() != 0) {
+      throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp));
+    }
+    return true;
+  }
+
+  @Override
+  public boolean push(Integer roomId, Integer goodsId) throws WxErrorException {
+    Map map = new HashMap<>(2);
+    map.put(ROOM_ID, roomId);
+    map.put("goodsId", goodsId);
+    String responseContent = this.wxMaService.post(Room.PUSH, WxMaGsonBuilder.create().toJson(map));
+    JsonObject jsonObject = GsonParser.parse(responseContent);
+    if (jsonObject.get(ERR_CODE).getAsInt() != 0) {
+      throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp));
+    }
+    return true;
+  }
+
+  @Override
+  public boolean sort(Integer roomId, List> goods) throws WxErrorException {
+    Map map = new HashMap<>(2);
+    map.put(ROOM_ID, roomId);
+    map.put("goods", goods);
+    String responseContent = this.wxMaService.post(Room.SORT, WxMaGsonBuilder.create().toJson(map));
+    JsonObject jsonObject = GsonParser.parse(responseContent);
+    if (jsonObject.get(ERR_CODE).getAsInt() != 0) {
+      throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp));
+    }
+    return true;
+  }
+
+  @Override
+  public String getVideo(Integer roomId, Integer goodsId) throws WxErrorException {
+    Map map = new HashMap<>(2);
+    map.put(ROOM_ID, roomId);
+    map.put("goodsId", goodsId);
+    String responseContent = this.wxMaService.post(Room.GET_VIDEO, WxMaGsonBuilder.create().toJson(map));
+    JsonObject jsonObject = GsonParser.parse(responseContent);
+    if (jsonObject.get(ERR_CODE).getAsInt() != 0) {
+      throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp));
+    }
+    return jsonObject.get("url").getAsString();
+  }
+
 }
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaMarketingServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaMarketingServiceImpl.java
new file mode 100644
index 000000000..001519e56
--- /dev/null
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaMarketingServiceImpl.java
@@ -0,0 +1,42 @@
+package cn.binarywang.wx.miniapp.api.impl;
+
+import cn.binarywang.wx.miniapp.api.WxMaMarketingService;
+import cn.binarywang.wx.miniapp.api.WxMaService;
+import cn.binarywang.wx.miniapp.bean.marketing.WxMaUserAction;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.common.util.json.GsonParser;
+
+import java.util.List;
+
+/**
+ * @author 184759547
+ * @Description :微信营销接口
+ * @since : 2021/12/28
+ */
+@Slf4j
+@RequiredArgsConstructor
+public class WxMaMarketingServiceImpl implements WxMaMarketingService {
+  private final WxMaService wxMaService;
+  private final String USER_ACTION_SETS_ADD = "https://api.weixin.qq.com/marketing/user_action_sets/add?version=v1.0";
+  private final String USER_ACTIONS_ADD = "https://api.weixin.qq.com/marketing/user_actions/add?version=v1.0";
+
+  @Override
+  public long addUserActionSets(String type, String name, String description) throws WxErrorException {
+    JsonObject json = new JsonObject();
+    json.addProperty("type", type);
+    json.addProperty("name", name);
+    json.addProperty("description", description);
+    String responseContent = wxMaService.post(USER_ACTION_SETS_ADD, json.toString());
+    JsonObject tmpJson = GsonParser.parse(responseContent);
+    return tmpJson.get("data").getAsJsonObject().get("user_action_set_id").getAsLong();
+  }
+
+  @Override
+  public String addUserAction(List actions, Long userActionSetId) throws WxErrorException {
+    return wxMaService.post(USER_ACTIONS_ADD, WxMaUserAction.listToJson(actions, userActionSetId));
+  }
+}
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaQrcodeServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaQrcodeServiceImpl.java
index 31bba36a3..cc7fc51fe 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaQrcodeServiceImpl.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaQrcodeServiceImpl.java
@@ -10,10 +10,12 @@
 import cn.binarywang.wx.miniapp.executor.QrcodeRequestExecutor;
 import lombok.RequiredArgsConstructor;
 import me.chanjar.weixin.common.error.WxErrorException;
+import org.apache.commons.lang3.StringUtils;
 
 import java.io.File;
 
 import static cn.binarywang.wx.miniapp.constant.WxMaApiUrlConstants.Qrcode.*;
+import static cn.binarywang.wx.miniapp.constant.WxMaConstants.DEFAULT_ENV_VERSION;
 
 /**
  * @author Binary Wang
@@ -40,11 +42,12 @@ public File createQrcode(String path) throws WxErrorException {
   }
 
   @Override
-  public byte[] createWxaCodeBytes(String path, int width, boolean autoColor, WxMaCodeLineColor lineColor, boolean isHyaline)
-    throws WxErrorException {
+  public byte[] createWxaCodeBytes(String path, String envVersion, int width, boolean autoColor,
+                                   WxMaCodeLineColor lineColor, boolean isHyaline) throws WxErrorException {
     return this.service.execute(QrcodeBytesRequestExecutor.create(this.service.getRequestHttp()), GET_WXACODE_URL,
       WxaCode.builder()
         .path(path)
+        .envVersion(StringUtils.defaultIfEmpty(envVersion, DEFAULT_ENV_VERSION))
         .width(width)
         .autoColor(autoColor)
         .lineColor(lineColor)
@@ -53,11 +56,12 @@ public byte[] createWxaCodeBytes(String path, int width, boolean autoColor, WxMa
   }
 
   @Override
-  public File createWxaCode(String path, int width, boolean autoColor, WxMaCodeLineColor lineColor, boolean isHyaline)
-    throws WxErrorException {
+  public File createWxaCode(String path, String envVersion, int width, boolean autoColor, WxMaCodeLineColor lineColor,
+                            boolean isHyaline) throws WxErrorException {
     return this.service.execute(QrcodeRequestExecutor.create(this.service.getRequestHttp()), GET_WXACODE_URL,
       WxaCode.builder()
         .path(path)
+        .envVersion(StringUtils.defaultIfEmpty(envVersion, DEFAULT_ENV_VERSION))
         .width(width)
         .autoColor(autoColor)
         .lineColor(lineColor)
@@ -67,7 +71,7 @@ public File createWxaCode(String path, int width, boolean autoColor, WxMaCodeLin
 
   @Override
   public File createWxaCode(String path, int width) throws WxErrorException {
-    return this.createWxaCode(path, width, true, null, false);
+    return this.createWxaCode(path, null, width, true, null, false);
   }
 
   @Override
@@ -76,24 +80,27 @@ public File createWxaCode(String path) throws WxErrorException {
   }
 
   @Override
-  public byte[] createWxaCodeUnlimitBytes(String scene, String page, int width, boolean autoColor,
-                                          WxMaCodeLineColor lineColor, boolean isHyaline) throws WxErrorException {
+  public byte[] createWxaCodeUnlimitBytes(String scene, String page, boolean checkPath, String envVersion, int width,
+                                          boolean autoColor, WxMaCodeLineColor lineColor, boolean isHyaline)
+    throws WxErrorException {
     return this.service.execute(QrcodeBytesRequestExecutor.create(this.service.getRequestHttp()),
-      GET_WXACODE_UNLIMIT_URL, this.buildWxaCodeUnlimit(scene, page, width, autoColor, lineColor, isHyaline));
+      GET_WXACODE_UNLIMIT_URL, this.buildWxaCodeUnlimit(scene, page, checkPath, envVersion, width, autoColor, lineColor, isHyaline));
   }
 
   @Override
-  public File createWxaCodeUnlimit(String scene, String page, int width, boolean autoColor,
-                                   WxMaCodeLineColor lineColor, boolean isHyaline) throws WxErrorException {
+  public File createWxaCodeUnlimit(String scene, String page, boolean checkPath, String envVersion, int width,
+                                   boolean autoColor, WxMaCodeLineColor lineColor, boolean isHyaline) throws WxErrorException {
     return this.service.execute(QrcodeRequestExecutor.create(this.service.getRequestHttp()),
-      GET_WXACODE_UNLIMIT_URL, this.buildWxaCodeUnlimit(scene, page, width, autoColor, lineColor, isHyaline));
+      GET_WXACODE_UNLIMIT_URL, this.buildWxaCodeUnlimit(scene, page, checkPath, envVersion, width, autoColor, lineColor, isHyaline));
   }
 
-  private WxaCodeUnlimit buildWxaCodeUnlimit(String scene, String page, int width, boolean autoColor,
-                                             WxMaCodeLineColor lineColor, boolean isHyaline) {
+  private WxaCodeUnlimit buildWxaCodeUnlimit(String scene, String page, boolean checkPath, String envVersion, int width,
+                                             boolean autoColor, WxMaCodeLineColor lineColor, boolean isHyaline) {
     WxaCodeUnlimit wxaCodeUnlimit = new WxaCodeUnlimit();
     wxaCodeUnlimit.setScene(scene);
     wxaCodeUnlimit.setPage(page);
+    wxaCodeUnlimit.setCheckPath(checkPath);
+    wxaCodeUnlimit.setEnvVersion(envVersion);
     wxaCodeUnlimit.setWidth(width);
     wxaCodeUnlimit.setAutoColor(autoColor);
     wxaCodeUnlimit.setLineColor(lineColor);
@@ -104,7 +111,7 @@ private WxaCodeUnlimit buildWxaCodeUnlimit(String scene, String page, int width,
 
   @Override
   public File createWxaCodeUnlimit(String scene, String page) throws WxErrorException {
-    return this.createWxaCodeUnlimit(scene, page, 430, true, null, false);
+    return this.createWxaCodeUnlimit(scene, page, true, DEFAULT_ENV_VERSION, 430, true, null, false);
   }
 
   @Override
@@ -119,12 +126,12 @@ public File createQrcode(String path, String filePath) throws WxErrorException {
   }
 
   @Override
-  public File createWxaCode(String path, int width, String filePath, boolean autoColor, WxMaCodeLineColor lineColor,
-                            boolean isHyaline)
-    throws WxErrorException {
+  public File createWxaCode(String path, String envVersion, int width, String filePath, boolean autoColor,
+                            WxMaCodeLineColor lineColor, boolean isHyaline) throws WxErrorException {
     return this.service.execute(QrcodeRequestExecutor.create(this.service.getRequestHttp(), filePath), GET_WXACODE_URL,
       WxaCode.builder()
         .path(path)
+        .envVersion(StringUtils.defaultIfEmpty(envVersion, DEFAULT_ENV_VERSION))
         .width(width)
         .autoColor(autoColor)
         .lineColor(lineColor)
@@ -134,7 +141,7 @@ public File createWxaCode(String path, int width, String filePath, boolean autoC
 
   @Override
   public File createWxaCode(String path, int width, String filePath) throws WxErrorException {
-    return this.createWxaCode(path, width, filePath, true, null, false);
+    return this.createWxaCode(path, null, width, filePath, true, null, false);
   }
 
   @Override
@@ -143,15 +150,16 @@ public File createWxaCode(String path, String filePath) throws WxErrorException
   }
 
   @Override
-  public File createWxaCodeUnlimit(String scene, String page, String filePath, int width, boolean autoColor,
-                                   WxMaCodeLineColor lineColor, boolean isHyaline) throws WxErrorException {
+  public File createWxaCodeUnlimit(String scene, String page, String filePath, boolean checkPath, String envVersion,
+                                   int width, boolean autoColor, WxMaCodeLineColor lineColor, boolean isHyaline)
+    throws WxErrorException {
     return this.service.execute(QrcodeRequestExecutor.create(this.service.getRequestHttp(), filePath),
-      GET_WXACODE_UNLIMIT_URL, this.buildWxaCodeUnlimit(scene, page, width, autoColor, lineColor, isHyaline));
+      GET_WXACODE_UNLIMIT_URL, this.buildWxaCodeUnlimit(scene, page, checkPath, envVersion, width, autoColor, lineColor, isHyaline));
   }
 
   @Override
   public File createWxaCodeUnlimit(String scene, String page, String filePath) throws WxErrorException {
-    return this.createWxaCodeUnlimit(scene, page, filePath, 430, true, null, false);
+    return this.createWxaCodeUnlimit(scene, page, filePath, true, DEFAULT_ENV_VERSION, 430, true, null, false);
   }
 
 }
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaSafetyRiskControlServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaSafetyRiskControlServiceImpl.java
new file mode 100644
index 000000000..8152c72c9
--- /dev/null
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaSafetyRiskControlServiceImpl.java
@@ -0,0 +1,35 @@
+package cn.binarywang.wx.miniapp.api.impl;
+
+import cn.binarywang.wx.miniapp.api.WxMaSafetyRiskControlService;
+import cn.binarywang.wx.miniapp.api.WxMaService;
+import cn.binarywang.wx.miniapp.bean.safety.request.WxMaUserSafetyRiskRankRequest;
+import cn.binarywang.wx.miniapp.bean.safety.response.WxMaUserSafetyRiskRankResponse;
+import cn.binarywang.wx.miniapp.constant.WxMaConstants;
+import com.google.gson.JsonObject;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.common.enums.WxType;
+import me.chanjar.weixin.common.error.WxError;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.common.util.json.GsonParser;
+
+import static cn.binarywang.wx.miniapp.constant.WxMaApiUrlConstants.InstantDelivery.SafetyRiskControl.GET_USER_RISK_RANK;
+
+/**
+ * @author azouever
+ */
+
+@RequiredArgsConstructor
+public class WxMaSafetyRiskControlServiceImpl implements WxMaSafetyRiskControlService {
+
+  private final WxMaService service;
+
+  @Override
+  public WxMaUserSafetyRiskRankResponse getUserRiskRank(WxMaUserSafetyRiskRankRequest wxMaUserSafetyRiskRankRequest) throws WxErrorException {
+    String responseContent = this.service.post(GET_USER_RISK_RANK, wxMaUserSafetyRiskRankRequest.toJson());
+    JsonObject jsonObject = GsonParser.parse(responseContent);
+    if (jsonObject.get(WxMaConstants.ERRCODE).getAsInt() != 0) {
+      throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp));
+    }
+    return WxMaUserSafetyRiskRankResponse.fromJson(responseContent);
+  }
+}
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaSecCheckServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaSecCheckServiceImpl.java
index 332a47ad7..837674eb6 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaSecCheckServiceImpl.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaSecCheckServiceImpl.java
@@ -3,6 +3,7 @@
 import cn.binarywang.wx.miniapp.api.WxMaSecCheckService;
 import cn.binarywang.wx.miniapp.api.WxMaService;
 import cn.binarywang.wx.miniapp.bean.WxMaMediaAsyncCheckResult;
+import cn.binarywang.wx.miniapp.bean.security.WxMaMediaSecCheckCheckRequest;
 import cn.binarywang.wx.miniapp.bean.security.WxMaMsgSecCheckCheckRequest;
 import cn.binarywang.wx.miniapp.bean.security.WxMaMsgSecCheckCheckResponse;
 import cn.binarywang.wx.miniapp.json.WxMaGsonBuilder;
@@ -68,10 +69,7 @@ public boolean checkMessage(String msgString) throws WxErrorException {
   @Override
   public WxMaMsgSecCheckCheckResponse checkMessage(WxMaMsgSecCheckCheckRequest msgRequest) throws WxErrorException {
     String response = this.service.post(MSG_SEC_CHECK_URL, msgRequest);
-    JsonObject jsonObject = GsonParser.parse(response);
-    if (jsonObject.get(ERRCODE).getAsInt() != 0) {
-      throw new WxErrorException(WxError.fromJson(response, WxType.MiniApp));
-    }
+    parseErrorResponse(response);
     return WxMaGsonBuilder.create().fromJson(response, WxMaMsgSecCheckCheckResponse.class);
   }
 
@@ -86,4 +84,17 @@ public WxMaMediaAsyncCheckResult mediaCheckAsync(String mediaUrl, int mediaType)
       .fromJson(this.service.post(MEDIA_CHECK_ASYNC_URL, jsonObject.toString()));
   }
 
+  @Override
+  public WxMaMediaAsyncCheckResult mediaCheckAsync(WxMaMediaSecCheckCheckRequest medisRequest) throws WxErrorException {
+    String response = this.service.post(MEDIA_CHECK_ASYNC_URL,medisRequest);
+    parseErrorResponse(response);
+    return WxMaGsonBuilder.create().fromJson(response,WxMaMediaAsyncCheckResult.class);
+  }
+
+  private void parseErrorResponse(String response) throws WxErrorException {
+    JsonObject jsonObject = GsonParser.parse(response);
+    if (jsonObject.get(ERRCODE).getAsInt() != 0) {
+      throw new WxErrorException(WxError.fromJson(response, WxType.MiniApp));
+    }
+  }
 }
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaShopImgServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaShopImgServiceImpl.java
index 1c69f8dc8..33e2a5de5 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaShopImgServiceImpl.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaShopImgServiceImpl.java
@@ -23,14 +23,21 @@ public class WxMaShopImgServiceImpl implements WxMaShopImgService {
   @Override
   public WxMinishopImageUploadCustomizeResult uploadImg(File file) throws WxErrorException {
     WxMinishopImageUploadCustomizeResult result = this.service.execute(
-      MinishopUploadRequestCustomizeExecutor.create(this.service.getRequestHttp(), "0"), IMG_UPLOAD, file);
+      MinishopUploadRequestCustomizeExecutor.create(this.service.getRequestHttp(), "0", null), IMG_UPLOAD, file);
     return result;
   }
 
   @Override
   public WxMinishopImageUploadCustomizeResult uploadImg(File file, String respType) throws WxErrorException {
     WxMinishopImageUploadCustomizeResult result = this.service.execute(
-      MinishopUploadRequestCustomizeExecutor.create(this.service.getRequestHttp(), respType), IMG_UPLOAD, file);
+      MinishopUploadRequestCustomizeExecutor.create(this.service.getRequestHttp(), respType, null), IMG_UPLOAD, file);
+    return result;
+  }
+
+  @Override
+  public WxMinishopImageUploadCustomizeResult uploadImg(String imgUrl, String respType) throws WxErrorException {
+    WxMinishopImageUploadCustomizeResult result = this.service.execute(
+            MinishopUploadRequestCustomizeExecutor.create(this.service.getRequestHttp(), respType, imgUrl), IMG_UPLOAD, null);
     return result;
   }
 }
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImpl.java
index 97c2d196a..fe513368c 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImpl.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImpl.java
@@ -6,16 +6,19 @@
 import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
 import cn.binarywang.wx.miniapp.bean.WxMaUserInfo;
 import cn.binarywang.wx.miniapp.config.WxMaConfig;
+import cn.binarywang.wx.miniapp.json.WxMaGsonBuilder;
 import cn.binarywang.wx.miniapp.util.crypt.WxMaCryptUtils;
 import com.google.gson.JsonArray;
 import com.google.gson.JsonObject;
 import lombok.RequiredArgsConstructor;
 import me.chanjar.weixin.common.error.WxErrorException;
 import me.chanjar.weixin.common.util.SignUtils;
+import me.chanjar.weixin.common.util.json.GsonParser;
 import org.apache.commons.codec.digest.DigestUtils;
 
 import java.util.Map;
 
+import static cn.binarywang.wx.miniapp.constant.WxMaApiUrlConstants.User.GET_PHONE_NUMBER_URL;
 import static cn.binarywang.wx.miniapp.constant.WxMaApiUrlConstants.User.SET_USER_STORAGE;
 
 /**
@@ -58,6 +61,20 @@ public WxMaPhoneNumberInfo getPhoneNoInfo(String sessionKey, String encryptedDat
     return WxMaPhoneNumberInfo.fromJson(WxMaCryptUtils.decrypt(sessionKey, encryptedData, ivStr));
   }
 
+  @Override
+  public WxMaPhoneNumberInfo getNewPhoneNoInfo(String code) throws WxErrorException {
+    JsonObject param = new JsonObject();
+    param.addProperty("code", code);
+    String responseContent = this.service.post(GET_PHONE_NUMBER_URL, param.toString());
+    JsonObject response = GsonParser.parse(responseContent);
+    boolean hasPhoneInfo = response.has("phone_info");
+    if (hasPhoneInfo) {
+      return WxMaGsonBuilder.create().fromJson(response.getAsJsonObject("phone_info"), WxMaPhoneNumberInfo.class);
+    } else {
+      return null;
+    }
+  }
+
   @Override
   public boolean checkUserInfo(String sessionKey, String rawData, String signature) {
     final String generatedSignature = DigestUtils.sha1Hex(rawData + sessionKey);
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaMessage.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaMessage.java
index 57d6a5b9b..aa9fd868d 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaMessage.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaMessage.java
@@ -9,13 +9,16 @@
 import com.thoughtworks.xstream.annotations.XStreamConverter;
 import lombok.Data;
 import me.chanjar.weixin.common.error.WxRuntimeException;
+import me.chanjar.weixin.common.util.XmlUtils;
 import me.chanjar.weixin.common.util.xml.XStreamCDataConverter;
 import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
 
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.Serializable;
 import java.nio.charset.StandardCharsets;
+import java.util.Map;
 
 /**
  * @author Binary Wang
@@ -25,6 +28,11 @@
 public class WxMaMessage implements Serializable {
   private static final long serialVersionUID = -3586245291677274914L;
 
+  /**
+   * 使用dom4j解析的存放所有xml属性和值的map.
+   */
+  private Map allFieldsMap;
+
   @SerializedName("Encrypt")
   @XStreamAlias("Encrypt")
   @XStreamConverter(value = XStreamCDataConverter.class)
@@ -145,10 +153,72 @@ public class WxMaMessage implements Serializable {
   @XStreamConverter(value = XStreamCDataConverter.class)
   private String query;
 
+  @SerializedName("AppID")
+  @XStreamAlias("AppID")
+  @XStreamConverter(value = XStreamCDataConverter.class)
+  private String appID;
+
+  @SerializedName("RevokeInfo")
+  @XStreamAlias("RevokeInfo")
+  @XStreamConverter(value = XStreamCDataConverter.class)
+  private String revokeInfo;
+
+  @SerializedName("OpenID")
+  @XStreamAlias("OpenID")
+  @XStreamConverter(value = XStreamCDataConverter.class)
+  private String openId;
+
+  @SerializedName("PluginID")
+  @XStreamAlias("PluginID")
+  @XStreamConverter(value = XStreamCDataConverter.class)
+  private String pluginId;
+
+  @SerializedName("OpenPID")
+  @XStreamAlias("OpenPID")
+  @XStreamConverter(value = XStreamCDataConverter.class)
+  private String openPid;
+
+  @XStreamAlias("SubscribeMsgPopupEvent")
+  private WxMaSubscribeMsgEvent.SubscribeMsgPopupEvent subscribeMsgPopupEvent;
+
+  @XStreamAlias("SubscribeMsgChangeEvent")
+  private WxMaSubscribeMsgEvent.SubscribeMsgChangeEvent subscribeMsgChangeEvent;
+
+  @XStreamAlias("SubscribeMsgSentEvent")
+  private WxMaSubscribeMsgEvent.SubscribeMsgSentEvent subscribeMsgSentEvent;
+
+  /**
+   * 不要直接使用这个字段,
+   * 这个字段只是为了适配 SubscribeMsgPopupEvent SubscribeMsgChangeEvent SubscribeMsgSentEvent
+   * 在json里面名称都是List并且有时候是对象有时候是数组的问题
+   * 当List只有一个对象的时候,微信服务器推送过来的的List是对象而非数组,当有多个对象的时候推送过来的才是数组
+   * 当只有一个对象的时候
+   * "List": {
+   *         "TemplateId": "hD-ixGOhYmUfjOnI8MCzQMPshzGVeux_2vzyvQu7O68",
+   *         "SubscribeStatusString": "accept",
+   *         "PopupScene": "0"
+   *     }
+   * 当有多条数据的时候
+   * "List": [   {
+   *         "TemplateId": "hD-ixGOhYmUfjOnI8MCzQMPshzGVeux_2vzyvQu7O68",
+   *         "SubscribeStatusString": "accept",
+   *         "PopupScene": "0"
+   *     }, {
+   *         "TemplateId": "hD-ixGOhYmUfjOnI8MCzQMPshzGVeux_2vzyvQu7O68",
+   *         "SubscribeStatusString": "accept",
+   *         "PopupScene": "0"
+   *     }]
+   */
+  @SerializedName("List")
+  private WxMaSubscribeMsgEvent.WxMaSubscribeMsgEventJson uselessMsg;
+
   public static WxMaMessage fromXml(String xml) {
-    return XStreamTransformer.fromXml(WxMaMessage.class, xml);
+    WxMaMessage message = XStreamTransformer.fromXml(WxMaMessage.class, xml);
+    message.setAllFieldsMap(XmlUtils.xml2Map(xml));
+    return message;
   }
 
+  @Deprecated
   public static WxMaMessage fromXml(InputStream is) {
     return XStreamTransformer.fromXml(WxMaMessage.class, is);
   }
@@ -165,7 +235,7 @@ public static WxMaMessage fromXml(InputStream is) {
   public static WxMaMessage fromEncryptedXml(String encryptedXml,
                                              WxMaConfig wxMaConfig, String timestamp, String nonce,
                                              String msgSignature) {
-    String plainText = new WxMaCryptUtils(wxMaConfig).decrypt(msgSignature, timestamp, nonce, encryptedXml);
+    String plainText = new WxMaCryptUtils(wxMaConfig).decryptXml(msgSignature, timestamp, nonce, encryptedXml);
     return fromXml(plainText);
   }
 
@@ -180,7 +250,19 @@ public static WxMaMessage fromEncryptedXml(InputStream is, WxMaConfig wxMaConfig
   }
 
   public static WxMaMessage fromJson(String json) {
-    return WxMaGsonBuilder.create().fromJson(json, WxMaMessage.class);
+    WxMaMessage message =  WxMaGsonBuilder.create().fromJson(json, WxMaMessage.class);
+    // 在这里处理 event的json格式时候的 list 问题,让json和xml的程序接口可以保持一致, 详见 uselessMsg 字段的注释
+    if (message.getUselessMsg() != null) {
+      if (StringUtils.equals(message.getEvent(), "subscribe_msg_popup_event")) {
+        message.setSubscribeMsgPopupEvent(message.getUselessMsg().getPopupEvents());
+      } else if (StringUtils.equals(message.getEvent(), "subscribe_msg_change_event")) {
+        message.setSubscribeMsgChangeEvent(message.getUselessMsg().getChangeEvents());
+      } else if (StringUtils.equals(message.getEvent(), "subscribe_msg_sent_event")) {
+        message.setSubscribeMsgSentEvent(message.getUselessMsg().getSentEvent());
+      }
+      message.setUselessMsg(null);
+    }
+    return message;
   }
 
   public static WxMaMessage fromEncryptedJson(String encryptedJson, WxMaConfig config) {
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaSubscribeMsgEvent.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaSubscribeMsgEvent.java
new file mode 100644
index 000000000..2191dd838
--- /dev/null
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaSubscribeMsgEvent.java
@@ -0,0 +1,118 @@
+package cn.binarywang.wx.miniapp.bean;
+
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import com.thoughtworks.xstream.annotations.XStreamConverter;
+import com.thoughtworks.xstream.annotations.XStreamImplicit;
+import lombok.Data;
+import me.chanjar.weixin.common.util.xml.XStreamCDataConverter;
+
+import java.io.Serializable;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * WxMaSubscribeMsgEvent class
+ *  客户端订阅,服务端收到的通知
+ * @author dany
+ * @date 2021/12/31
+ */
+public class WxMaSubscribeMsgEvent {
+  /**
+   * https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/subscribe-message.html
+   */
+  @Data
+  @XStreamAlias("SubscribeMsgPopupEvent")
+  public static class SubscribeMsgPopupEvent implements Serializable {
+    private static final long serialVersionUID = 6319723189257161326L;
+    @XStreamImplicit(itemFieldName = "List")
+    private List list = new LinkedList<>();
+  }
+
+  @Data
+  @XStreamAlias("SubscribeMsgChangeEvent")
+  public static class SubscribeMsgChangeEvent implements Serializable {
+    private static final long serialVersionUID = 7705686111539437751L;
+    @XStreamImplicit(itemFieldName = "List")
+    private List list = new LinkedList<>();
+  }
+
+  @Data
+  @XStreamAlias("SubscribeMsgSentEvent")
+  public static class SubscribeMsgSentEvent implements Serializable {
+    private static final long serialVersionUID = 7705686111539437751L;
+    @XStreamAlias("List")
+    private SentEvent list;
+  }
+
+
+  @Data
+  public static class PopupEvent implements Serializable {
+    private static final long serialVersionUID = 4934029303241387226L;
+    /**
+     * 模板id
+     */
+    @XStreamAlias("TemplateId")
+    @XStreamConverter(value = XStreamCDataConverter.class)
+    private String templateId;
+    /**
+     * 订阅结果(accept接收;reject拒收)
+     */
+    @XStreamAlias("SubscribeStatusString")
+    @XStreamConverter(value = XStreamCDataConverter.class)
+    private String subscribeStatusString;
+    /**
+     * 弹框场景,0代表在小程序页面内
+     */
+    @XStreamAlias("PopupScene")
+    private String popupScene;
+  }
+
+  @Data
+  public static class ChangeEvent implements Serializable {
+    private static final long serialVersionUID = 1523634146232757624L;
+    /**
+     * 模板id
+     */
+    @XStreamAlias("TemplateId")
+    @XStreamConverter(value = XStreamCDataConverter.class)
+    private String templateId;
+    /**
+     * 订阅结果(accept接收;reject拒收)
+     */
+    @XStreamAlias("SubscribeStatusString")
+    @XStreamConverter(value = XStreamCDataConverter.class)
+    private String subscribeStatusString;
+  }
+
+  @Data
+  public static class SentEvent implements Serializable {
+    private static final long serialVersionUID = -8734478345463177940L;
+    /**
+     * 模板id
+     */
+    @XStreamAlias("TemplateId")
+    @XStreamConverter(value = XStreamCDataConverter.class)
+    private String templateId;
+
+    @XStreamAlias("MsgID")
+    private String msgId;
+
+    @XStreamAlias("ErrorCode")
+    private String errorCode;
+
+    @XStreamAlias("ErrorStatus")
+    @XStreamConverter(value = XStreamCDataConverter.class)
+    private String errorStatus;
+  }
+
+  @Data
+  public static class WxMaSubscribeMsgEventJson implements Serializable {
+    private static final long serialVersionUID = -4820758280837190275L;
+
+    private SubscribeMsgPopupEvent popupEvents;
+
+    private SubscribeMsgChangeEvent changeEvents;
+
+    private SubscribeMsgSentEvent sentEvent;
+  }
+}
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxaCode.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxaCode.java
index 2361355ad..2711ccb24 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxaCode.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxaCode.java
@@ -10,6 +10,8 @@
 import lombok.EqualsAndHashCode;
 import lombok.NoArgsConstructor;
 
+import static cn.binarywang.wx.miniapp.constant.WxMaConstants.DEFAULT_ENV_VERSION;
+
 /**
  * 小程序码.
  *
@@ -26,6 +28,9 @@ public class WxaCode extends AbstractWxMaQrcodeWrapper implements Serializable {
 
   private String path;
 
+  @SerializedName("env_version")
+  private String envVersion = DEFAULT_ENV_VERSION;
+
   @Builder.Default
   private int width = 430;
 
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxaCodeUnlimit.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxaCodeUnlimit.java
index ab0dad4e2..351ee5c9e 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxaCodeUnlimit.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxaCodeUnlimit.java
@@ -7,6 +7,8 @@
 
 import java.io.Serializable;
 
+import static cn.binarywang.wx.miniapp.constant.WxMaConstants.DEFAULT_ENV_VERSION;
+
 /**
  * 小程序码接口B.
  *
@@ -20,6 +22,12 @@ public class WxaCodeUnlimit extends AbstractWxMaQrcodeWrapper implements Seriali
   private String scene;
   private String page;
 
+  @SerializedName("check_path")
+  private boolean checkPath = true;
+
+  @SerializedName("env_version")
+  private String envVersion = DEFAULT_ENV_VERSION;
+
   private int width = 430;
 
   @SerializedName("auto_color")
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCategory.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCategory.java
index 5e176cfa6..2311eba47 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCategory.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCategory.java
@@ -5,6 +5,7 @@
 import lombok.Builder;
 import lombok.Data;
 import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
 
 import java.io.Serializable;
 
@@ -18,8 +19,19 @@
 @Builder
 @NoArgsConstructor
 @AllArgsConstructor
+@Accessors(chain = true)
 public class WxMaCategory implements Serializable {
   private static final long serialVersionUID = -7663757440028175135L;
+
+  /**
+   * 小程序的页面,可通过“获取小程序的第三方提交代码的页面配置”接口获得
+   */
+  private String address;
+  /**
+   * 小程序的标签,多个标签用空格分隔,标签不能多于10个,标签长度不超过20
+   */
+  private String tag;
+
   /**
    * 一级类目名称
    */
@@ -51,14 +63,6 @@ public class WxMaCategory implements Serializable {
   @SerializedName("third_id")
   private Long thirdId;
 
-  /**
-   * 小程序的页面,可通过“获取小程序的第三方提交代码的页面配置”接口获得
-   */
-  private String address;
-  /**
-   * 小程序的标签,多个标签用空格分隔,标签不能多于10个,标签长度不超过20
-   */
-  private String tag;
   /**
    * 小程序页面的标题,标题长度不超过32
    */
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeSubmitAuditRequest.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeSubmitAuditRequest.java
index ff245c8e6..bc944353d 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeSubmitAuditRequest.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeSubmitAuditRequest.java
@@ -6,6 +6,7 @@
 import lombok.Builder;
 import lombok.Data;
 import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
 
 import java.io.Serializable;
 import java.util.List;
@@ -20,15 +21,101 @@
 @Builder
 @NoArgsConstructor
 @AllArgsConstructor
+@Accessors(chain = true)
 public class WxMaCodeSubmitAuditRequest implements Serializable {
   private static final long serialVersionUID = 8854979405505241314L;
+
   /**
    * 提交审核项的一个列表(至少填写1项,至多填写5项)
    */
   @SerializedName("item_list")
   private List itemList;
 
+  /**
+   * feedback_info	String	否	反馈内容,至多 200 字
+   */
+  @SerializedName("feedback_info")
+  private String feedbackInfo;
+
+  /**
+   * feedback_stuff	String	否	用 | 分割的 media_id 列表,至多 5 张图片, 可以通过新增临时素材接口上传而得到
+   */
+  @SerializedName("feedback_stuff")
+  private String feedbackStuff;
+
+  /**
+   * preview_info	Object	否	预览信息(小程序页面截图和操作录屏)
+   */
+  @SerializedName("preview_info")
+  private PreviewInfo previewInfo;
+
+  /**
+   * version_desc	String	否	小程序版本说明和功能解释
+   */
+  @SerializedName("version_desc")
+  private String versionDesc;
+
+  /**
+   * ugc_declare	Object	否	用户生成内容场景(UGC)信息安全声明
+   */
+  @SerializedName("ugc_declare")
+  private UgcDeclare ugcDeclare;
+
   public String toJson() {
     return WxMaGsonBuilder.create().toJson(this);
   }
+
+  @Data
+  @Accessors(chain = true)
+  public static class PreviewInfo implements Serializable {
+    private static final long serialVersionUID = -3391652096859063951L;
+
+    /**
+     * video_id_list	String Array	否	录屏mediaid列表,可以通过提审素材上传接口获得
+     */
+    @SerializedName("video_id_list")
+    private List videoIdList;
+
+    /**
+     * pic_id_list	String Array	否	截屏mediaid列表,可以通过提审素材上传接口获得
+     */
+    @SerializedName("pic_id_list")
+    private List picIdList;
+  }
+
+  @Data
+  @Accessors(chain = true)
+  public static class UgcDeclare implements Serializable {
+    private static final long serialVersionUID = 201470564426848261L;
+
+    /**
+     * scene	Number Array	否	UGC场景 0,不涉及用户生成内容, 1.用户资料,2.图片,3.视频,4.文本,5其他, 可多选,当scene填0时无需填写下列字段
+     */
+    @SerializedName("scene")
+    private Integer[] scene;
+
+    /**
+     * other_scene_desc	String	否	当scene选其他时的说明,不超时256字
+     */
+    @SerializedName("other_scene_desc")
+    private String otherSceneDesc;
+
+    /**
+     * method	Number Array	否	内容安全机制 1.使用平台建议的内容安全API,2.使用其他的内容审核产品,3.通过人工审核把关,4.未做内容审核把关
+     */
+    @SerializedName("method")
+    private Integer[] method;
+
+    /**
+     * has_audit_team	Number	否	是否有审核团队, 0.无,1.有,默认0
+     */
+    @SerializedName("has_audit_team")
+    private Integer hasAuditTeam;
+
+    /**
+     * audit_desc	String	否	说明当前对UGC内容的审核机制,不超过256字
+     */
+    @SerializedName("audit_desc")
+    private String auditDesc;
+  }
 }
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/AbnormalConfirmRequest.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/AbnormalConfirmRequest.java
new file mode 100644
index 000000000..af461e47c
--- /dev/null
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/AbnormalConfirmRequest.java
@@ -0,0 +1,43 @@
+package cn.binarywang.wx.miniapp.bean.delivery;
+
+import cn.binarywang.wx.miniapp.bean.delivery.base.WxMaDeliveryBaseRequest;
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+
+/**
+ * 微信小程序即时配送 异常件退回商家商家确认收货接口 请求参数.
+ *
+ * @author Luo
+ * @version 1.0
+ * @date 2021-10-14 10:49
+ */
+@Data
+@Accessors(chain = true)
+@EqualsAndHashCode(callSuper = true)
+public class AbnormalConfirmRequest extends WxMaDeliveryBaseRequest implements Serializable {
+
+    private static final long serialVersionUID = 3773007367000633663L;
+
+    /**
+     * 配送单id.
+     * 
+     * 是否必填:是
+     * 
+ */ + @SerializedName("waybill_id") + private String waybillId; + + /** + * 备注. + *
+     * 是否必填:否
+     * 
+ */ + @SerializedName("remark") + private String remark; + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/AbnormalConfirmResponse.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/AbnormalConfirmResponse.java new file mode 100644 index 000000000..641ab7e38 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/AbnormalConfirmResponse.java @@ -0,0 +1,25 @@ +package cn.binarywang.wx.miniapp.bean.delivery; + +import cn.binarywang.wx.miniapp.bean.delivery.base.WxMaDeliveryBaseResponse; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serializable; + +/** + * 微信小程序即时配送 异常件退回商家商家确认收货接口 响应参数. + * + * @author Luo + * @version 1.0 + * @date 2021-10-14 10:49 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +public class AbnormalConfirmResponse extends WxMaDeliveryBaseResponse implements Serializable { + + private static final long serialVersionUID = 3773007367000633663L; + + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/AddOrderRequest.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/AddOrderRequest.java new file mode 100644 index 000000000..c8a220776 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/AddOrderRequest.java @@ -0,0 +1,639 @@ +package cn.binarywang.wx.miniapp.bean.delivery; + +import cn.binarywang.wx.miniapp.bean.delivery.base.WxMaDeliveryBaseRequest; +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.List; + +/** + * 微信小程序即时配送 下配送单接口 请求参数. + * + * @author Luo + * @version 1.0 + * @date 2021-10-14 10:49 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +public class AddOrderRequest extends WxMaDeliveryBaseRequest implements Serializable { + + private static final long serialVersionUID = 3773007367000633663L; + + /** + * 子商户id,区分小程序内部多个子商户. + *
+     * 是否必填:否
+     * 
+ */ + @SerializedName("sub_biz_id") + private String subBizId; + + /** + * 发件人信息,顺丰同城急送必须填写,美团配送、达达、闪送,若传了shop_no的值可不填该字段. + *
+     * 是否必填:是
+     * 
+ */ + @SerializedName("sender") + private Sender sender; + + /** + * 收件人信息. + *
+     * 是否必填:是
+     * 
+ */ + @SerializedName("receiver") + private Receiver receiver; + + /** + * 货物信息. + *
+     * 是否必填:是
+     * 
+ */ + @SerializedName("cargo") + private Cargo cargo; + + /** + * 订单信息. + *
+     * 是否必填:是
+     * 
+ */ + @SerializedName("order_info") + private OrderInfo orderInfo; + + /** + * 商品信息,会展示到物流通知消息中. + *
+     * 是否必填:是
+     * 
+ */ + @SerializedName("shop") + private Shop shop; + + /** + * 发件人信息. + */ + @Data + @Accessors(chain = true) + public static class Sender implements Serializable { + + private static final long serialVersionUID = -8101805250220380047L; + + /** + * 姓名,最长不超过256个字符. + *
+         * 是否必填:是
+         * 
+ */ + @SerializedName("name") + private String name; + + /** + * 城市名称,如广州市. + *
+         * 是否必填:是
+         * 
+ */ + @SerializedName("city") + private String city; + + /** + * 地址(街道、小区、大厦等,用于定位). + *
+         * 是否必填:是
+         * 
+ */ + @SerializedName("address") + private String address; + + /** + * 地址(街道、小区、大厦等,用于定位). + *
+         * 是否必填:是
+         * 
+ */ + @SerializedName("address_detail") + private String addressDetail; + + /** + * 坐标类型,默认 0:火星坐标(高德,腾讯地图均采用火星坐标) 1:百度坐标. + *
+         * 是否必填:否
+         * 
+ */ + @SerializedName("coordinate_type") + private int coordinateType; + + /** + * 经度(火星坐标或百度坐标,和 coordinate_type 字段配合使用,确到小数点后6位. + *
+         * 是否必填:是
+         * 
+ */ + @SerializedName("lng") + private BigDecimal lng; + + /** + * 纬度(火星坐标或百度坐标,和 coordinate_type 字段配合使用,精确到小数点后6位). + *
+         * 是否必填:是
+         * 
+ */ + @SerializedName("lat") + private BigDecimal lat; + + /** + * 电话/手机号,最长不超过64个字符. + *
+         * 是否必填:是
+         * 
+ */ + @SerializedName("phone") + private String phone; + + } + + /** + * 收件人信息. + */ + @Data + @Accessors(chain = true) + public static class Receiver implements Serializable { + + private static final long serialVersionUID = -8101805250220380047L; + + /** + * 姓名,最长不超过256个字符. + *
+         * 是否必填:是
+         * 
+ */ + @SerializedName("name") + private String name; + + /** + * 城市名称,如广州市. + *
+         * 是否必填:是
+         * 
+ */ + @SerializedName("city") + private String city; + + /** + * 地址(街道、小区、大厦等,用于定位). + *
+         * 是否必填:是
+         * 
+ */ + @SerializedName("address") + private String address; + + /** + * 地址(街道、小区、大厦等,用于定位). + *
+         * 是否必填:是
+         * 
+ */ + @SerializedName("address_detail") + private String addressDetail; + + /** + * 坐标类型,默认 0:火星坐标(高德,腾讯地图均采用火星坐标) 1:百度坐标. + *
+         * 是否必填:否
+         * 
+ */ + @SerializedName("coordinate_type") + private int coordinateType; + + /** + * 经度(火星坐标或百度坐标,和 coordinate_type 字段配合使用,确到小数点后6位. + *
+         * 是否必填:是
+         * 
+ */ + @SerializedName("lng") + private BigDecimal lng; + + /** + * 纬度(火星坐标或百度坐标,和 coordinate_type 字段配合使用,精确到小数点后6位). + *
+         * 是否必填:是
+         * 
+ */ + @SerializedName("lat") + private BigDecimal lat; + + /** + * 电话/手机号,最长不超过64个字符. + *
+         * 是否必填:是
+         * 
+ */ + @SerializedName("phone") + private String phone; + + } + + /** + * 商品信息. + */ + @Data + @Accessors(chain = true) + public static class Shop implements Serializable { + + private static final long serialVersionUID = -8958461649711388689L; + + /** + * 商品数量. + *
+         * 是否必填:是
+         * 
+ */ + @SerializedName("goods_count") + private Integer goodsCount; + + /** + * 商品名称. + *
+         * 是否必填:是
+         * 
+ */ + @SerializedName("goods_name") + private String goodsName; + + /** + * 商品缩略图 url. + *
+         * 是否必填:是
+         * 
+ */ + @SerializedName("img_url") + private String imgUrl; + + /** + * 商家小程序的路径,建议为订单页面. + *
+         * 是否必填:是
+         * 
+ */ + @SerializedName("wxa_path") + private String wxaPath; + + /** + * 若结算方式为:第三方向配送公司统一结算,商户后续和第三方结算,则该参数必填. + * 在该结算模式下,第三方用自己的开发小程序替授权商户发起下单,并将授权小程序的appid给平台,后续配送通知中可回流授权商户小程序. + *
+         * 是否必填:否
+         * 
+ */ + @SerializedName("wxa_appid") + private String wxaAppid; + + } + + /** + * 订单信息. + */ + @Data + @Accessors(chain = true) + public static class OrderInfo implements Serializable { + + private static final long serialVersionUID = 5277759430030747900L; + + /** + * 配送服务代码 不同配送公司自定义, 顺丰和达达不填. + *
+         * 是否必填:否
+         * 
+ */ + @SerializedName("delivery_service_code") + private String deliveryServiceCode; + + /** + * 订单类型, 0: 即时单 1 预约单,如预约单,需要设置expected_delivery_time或expected_finish_time或expected_pick_time. + *
+         * 是否必填:否
+         * 
+ */ + @SerializedName("order_type") + private Integer orderType; + + /** + * 期望派单时间(达达支持,表示达达系统调度时间, 到那个时间才会有状态更新的回调通知),unix-timestamp, 比如1586342180. + *
+         * 是否必填:否
+         * 
+ */ + @SerializedName("expected_delivery_time") + private Long expectedDeliveryTime; + + /** + * 期望送达时间(美团、顺丰同城急送支持),unix-timestamp, 比如1586342180. + *
+         * 是否必填:否
+         * 
+ */ + @SerializedName("expected_finish_time") + private Long expectedFinishTime; + + /** + * 期望取件时间(闪送、顺丰同城急送支持,闪送需要设置两个小时后的时间,顺丰同城急送只需传expected_finish_time或expected_pick_time其中之一即可,同时都传则以expected_finish_time为准),unix-timestamp, 比如1586342180. + *
+         * 是否必填:否
+         * 
+ */ + @SerializedName("expected_pick_time") + private Long expectedPickTime; + + /** + * 备注,最长不超过200个字符. + *
+         * 是否必填:否
+         * 
+ */ + @SerializedName("note") + private String note; + + /** + * 门店订单流水号,建议提供,方便骑手门店取货,最长不超过32个字符. + *
+         * 是否必填:否
+         * 
+ */ + @SerializedName("poi_seq") + private String poiSeq; + + /** + * 用户下单付款时间, 顺丰必填, 比如1555220757. + *
+         * 是否必填:否
+         * 
+ */ + @SerializedName("order_time") + private Long orderTime; + + /** + * 是否保价,0:非保价,1:保价. + *
+         * 是否必填:否
+         * 
+ */ + @SerializedName("is_insured") + private Integer isInsured; + + /** + * 保价金额,单位为元,精确到分. + *
+         * 是否必填:否
+         * 
+ */ + @SerializedName("declared_value") + private BigDecimal declaredValue; + + /** + * 小费,单位为元, 下单一般不加小费. + *
+         * 是否必填:否
+         * 
+ */ + @SerializedName("tips") + private Integer tips; + + /** + * 是否选择直拿直送(0:不需要;1:需要。选择直拿直送后,同一时间骑手只能配送此订单至完成,配送费用也相应高一些,闪送必须选1,达达可选0或1,其余配送公司不支持直拿直送). + *
+         * 是否必填:否
+         * 
+ */ + @SerializedName("is_direct_delivery") + private Integer isDirectDelivery; + + /** + * 骑手应付金额,单位为元,精确到分. + *
+         * 是否必填:否
+         * 
+ */ + @SerializedName("cash_on_delivery") + private BigDecimal cashOnDelivery; + + /** + * 骑手应收金额,单位为元,精确到分. + *
+         * 是否必填:否
+         * 
+ */ + @SerializedName("cash_on_pickup") + private BigDecimal cashOnPickup; + + /** + * 物流流向,1:从门店取件送至用户;2:从用户取件送至门店. + *
+         * 是否必填:否
+         * 
+ */ + @SerializedName("rider_pick_method") + private Integer riderPickMethod; + + /** + * 收货码(0:不需要;1:需要。收货码的作用是:骑手必须输入收货码才能完成订单妥投). + *
+         * 是否必填:否
+         * 
+ */ + @SerializedName("is_finish_code_needed") + private Integer isFinishCodeNeeded; + + /** + * 取货码(0:不需要;1:需要。取货码的作用是:骑手必须输入取货码才能从商家取货). + *
+         * 是否必填:否
+         * 
+ */ + @SerializedName("is_pickup_code_needed") + private Integer isPickupCodeNeeded; + + } + + /** + * 货物信息. + */ + @NoArgsConstructor + @Data + @Accessors(chain = true) + public static class Cargo implements Serializable { + + private static final long serialVersionUID = -8339389045820636620L; + + /** + * 货物价格,单位为元,精确到小数点后两位(如果小数点后位数多于两位,则四舍五入保留两位小数),范围为(0-5000]. + *
+         * 是否必填:是
+         * 
+ */ + @SerializedName("goods_value") + private BigDecimal goodsValue; + + /** + * 货物高度,单位为cm,精确到小数点后两位(如果小数点后位数多于两位,则四舍五入保留两位小数),范围为(0-45]. + *
+         * 是否必填:否
+         * 
+ */ + @SerializedName("goods_height") + private BigDecimal goodsHeight; + + /** + * 货物长度,单位为cm,精确到小数点后两位(如果小数点后位数多于两位,则四舍五入保留两位小数),范围为(0-65]. + *
+         * 是否必填:否
+         * 
+ */ + @SerializedName("goods_length") + private BigDecimal goodsLength; + + /** + * 货物宽度,单位为cm,精确到小数点后两位(如果小数点后位数多于两位,则四舍五入保留两位小数),范围为(0-50]. + *
+         * 是否必填:否
+         * 
+ */ + @SerializedName("goods_width") + private BigDecimal goodsWidth; + + /** + * 货物重量,单位为kg,精确到小数点后两位(如果小数点后位数多于两位,则四舍五入保留两位小数),范围为(0-50]. + *
+         * 是否必填:是
+         * 
+ */ + @SerializedName("goods_weight") + private BigDecimal goodsWeight; + + /** + * 货物详情,最长不超过10240个字符. + *
+         * 是否必填:否
+         * 
+ */ + @SerializedName("goods_detail") + private GoodsDetail goodsDetail; + + /** + * 货物取货信息,用于骑手到店取货,最长不超过100个字符. + *
+         * 是否必填:否
+         * 
+ */ + @SerializedName("goods_pickup_info") + private String goodsPickupInfo; + + /** + * 货物交付信息,最长不超过100个字符. + *
+         * 是否必填:否
+         * 
+ */ + @SerializedName("goods_delivery_info") + private String goodsDeliveryInfo; + + /** + * 品类一级类目, 详见品类表 https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/business-capabilities/union/access-guidelines/promoter/api/product/category.html. + *
+         * 是否必填:是
+         * 
+ */ + @SerializedName("cargo_first_class") + private String cargoFirstClass; + + /** + * 品类二级类目. + *
+         * 是否必填:是
+         * 
+ */ + @SerializedName("cargo_second_class") + private String cargoSecondClass; + + /** + * 货物详情. + */ + @Data + @Accessors(chain = true) + public static class GoodsDetail implements Serializable { + + private static final long serialVersionUID = -8339389045820636620L; + + /** + * 货物列表. + *
+             * 是否必填:是
+             * 
+ */ + @SerializedName("goods") + private List goods; + + /** + * 货物详情. + */ + @NoArgsConstructor + @Data + @Accessors(chain = true) + public static class Goods implements Serializable { + + private static final long serialVersionUID = -8339389045820636620L; + + /** + * 货物数量. + *
+                 * 是否必填:是
+                 * 
+ */ + @SerializedName("good_count") + private Integer goodCount; + + /** + * 货品名称. + *
+                 * 是否必填:是
+                 * 
+ */ + @SerializedName("good_name") + private String goodName; + + /** + * 货品单价,精确到小数点后两位(如果小数点后位数多于两位,则四舍五入保留两位小数). + *
+                 * 是否必填:否
+                 * 
+ */ + @SerializedName("good_price") + private BigDecimal goodPrice; + + /** + * 货品单位,最长不超过20个字符. + *
+                 * 是否必填:否
+                 * 
+ */ + @SerializedName("good_unit") + private String goodUnit; + + } + + } + + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/AddOrderResponse.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/AddOrderResponse.java new file mode 100644 index 000000000..5f56b007b --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/AddOrderResponse.java @@ -0,0 +1,92 @@ +package cn.binarywang.wx.miniapp.bean.delivery; + +import cn.binarywang.wx.miniapp.bean.delivery.base.WxMaDeliveryBaseResponse; +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * 微信小程序即时配送 下配送单接口 响应参数. + * + * @author Luo + * @version 1.0 + * @date 2021-10-14 10:49 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +public class AddOrderResponse extends WxMaDeliveryBaseResponse implements Serializable { + + private static final long serialVersionUID = 3773007367000633663L; + + /** + * 实际运费(单位:元),运费减去优惠券费用. + */ + @SerializedName("fee") + private BigDecimal fee; + + /** + * 运费(单位:元). + */ + @SerializedName("deliverfee") + private BigDecimal deliverFee; + + /** + * 优惠券费用(单位:元). + */ + @SerializedName("couponfee") + private BigDecimal couponFee; + + /** + * 小费(单位:元). + */ + @SerializedName("tips") + private BigDecimal tips; + + /** + * 保价费(单位:元). + */ + @SerializedName("insurancfee") + private BigDecimal insurancFee; + + /** + * 配送距离(整数单位:米). + */ + @SerializedName("distance") + private BigDecimal distance; + + /** + * 配送单号. + */ + @SerializedName("waybill_id") + private String waybillId; + + /** + * 配送状态. + */ + @SerializedName("order_status") + private Integer orderStatus; + + /** + * 收货码. + */ + @SerializedName("finish_code") + private Integer finishCode; + + /** + * 取货码. + */ + @SerializedName("pickup_code") + private Integer pickupCode; + + /** + * 预计骑手接单时间,单位秒,比如5分钟,就填300, 无法预计填0. + */ + @SerializedName("dispatch_duration") + private BigDecimal dispatchDuration; + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/BindAccountResponse.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/BindAccountResponse.java new file mode 100644 index 000000000..0afb174eb --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/BindAccountResponse.java @@ -0,0 +1,66 @@ +package cn.binarywang.wx.miniapp.bean.delivery; + +import cn.binarywang.wx.miniapp.bean.delivery.base.WxMaDeliveryBaseResponse; +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.List; + +/** + * 微信小程序即时配送 拉取已绑定账号 响应参数. + *
+ * 使用场景:
+ *      1.商家可通过本接口查询自己已经在小程序后台绑定的和配送公司签约的账号;
+ *      2.服务商可通过本接口查询代开发的小程序在小程序后台绑定的和配送公司签约的账号,为其完成后续的接口代开发业务;
+ * 
+ * + * @author Luo + * @version 1.0 + * @date 2021-10-14 10:49 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +public class BindAccountResponse extends WxMaDeliveryBaseResponse implements Serializable { + + private static final long serialVersionUID = 3773007367000633663L; + + /** + * 店铺账号信息集合. + */ + @SerializedName("shop_list") + private List shopList; + + /** + * 店铺账号信息. + */ + @Data + @Accessors(chain = true) + public static class Shop implements Serializable { + + private static final long serialVersionUID = -3759074878713856529L; + + /** + * 配送公司Id. + */ + @SerializedName("delivery_id") + private String deliveryId; + + /** + * 商家id. + */ + @SerializedName("shopid") + private String shopId; + + /** + * 审核状态 0:审核通过、1:审核中、2:审核不通过. + */ + @SerializedName("audit_result") + private String auditResult; + + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/CancelOrderRequest.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/CancelOrderRequest.java new file mode 100644 index 000000000..1220e0807 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/CancelOrderRequest.java @@ -0,0 +1,52 @@ +package cn.binarywang.wx.miniapp.bean.delivery; + +import cn.binarywang.wx.miniapp.bean.delivery.base.WxMaDeliveryBaseRequest; +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serializable; + +/** + * 微信小程序即时配送 取消配送单接口 请求参数. + * + * @author Luo + * @version 1.0 + * @date 2021-10-14 10:49 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +public class CancelOrderRequest extends WxMaDeliveryBaseRequest implements Serializable { + + private static final long serialVersionUID = 3773007367000633663L; + + /** + * 配送单id. + *
+     * 是否必填:是
+     * 
+ */ + @SerializedName("waybill_id") + private String waybillId; + + /** + * 取消原因Id. + *
+     * 是否必填:是
+     * 
+ */ + @SerializedName("cancel_reason_id") + private Integer cancelReasonId; + + /** + * 取消原因. + *
+     * 是否必填:否
+     * 
+ */ + @SerializedName("cancel_reason") + private String cancelReason; + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/CancelOrderResponse.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/CancelOrderResponse.java new file mode 100644 index 000000000..a556fba99 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/CancelOrderResponse.java @@ -0,0 +1,38 @@ +package cn.binarywang.wx.miniapp.bean.delivery; + +import cn.binarywang.wx.miniapp.bean.delivery.base.WxMaDeliveryBaseResponse; +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * 微信小程序即时配送 取消配送单接口 响应参数. + * + * @author Luo + * @version 1.0 + * @date 2021-10-14 10:49 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +public class CancelOrderResponse extends WxMaDeliveryBaseResponse implements Serializable { + + private static final long serialVersionUID = 3773007367000633663L; + + /** + * 扣除的违约金(单位:元),精确到分. + */ + @SerializedName("deduct_fee") + private BigDecimal deductFee; + + /** + * 说明. + */ + @SerializedName("desc") + private String desc; + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/GetOrderRequest.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/GetOrderRequest.java new file mode 100644 index 000000000..9927ee06d --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/GetOrderRequest.java @@ -0,0 +1,24 @@ +package cn.binarywang.wx.miniapp.bean.delivery; + +import cn.binarywang.wx.miniapp.bean.delivery.base.WxMaDeliveryBaseRequest; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serializable; + +/** + * 微信小程序即时配送 拉取配送单信息 请求参数. + * + * @author Luo + * @version 1.0 + * @date 2021-10-14 10:49 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +public class GetOrderRequest extends WxMaDeliveryBaseRequest implements Serializable { + + private static final long serialVersionUID = 3773007367000633663L; + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/GetOrderResponse.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/GetOrderResponse.java new file mode 100644 index 000000000..ec06026a0 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/GetOrderResponse.java @@ -0,0 +1,68 @@ +package cn.binarywang.wx.miniapp.bean.delivery; + +import cn.binarywang.wx.miniapp.bean.delivery.base.WxMaDeliveryBaseResponse; +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * 微信小程序即时配送 拉取配送单信息 响应参数. + * + * @author Luo + * @version 1.0 + * @date 2021-10-14 10:49 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +public class GetOrderResponse extends WxMaDeliveryBaseResponse implements Serializable { + + private static final long serialVersionUID = 3773007367000633663L; + + /** + * 配送状态. + */ + @SerializedName("order_status") + private Integer orderStatus; + + /** + * 配送单号. + */ + @SerializedName("waybill_id") + private String waybillId; + + /** + * 骑手姓名. + */ + @SerializedName("rider_name") + private String riderName; + + /** + * 骑手电话. + */ + @SerializedName("rider_phone") + private String riderPhone; + + /** + * 骑手位置经度, 配送中时返回. + */ + @SerializedName("rider_lng") + private BigDecimal riderLng; + + /** + * 骑手位置纬度, 配送中时返回. + */ + @SerializedName("rider_lat") + private BigDecimal riderLat; + + /** + * 预计还剩多久送达时间, 配送中时返回,单位秒, 已取货配送中需返回,比如5分钟后送达,填300. + */ + @SerializedName("reach_time") + private BigDecimal reachTime; + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/MockUpdateOrderRequest.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/MockUpdateOrderRequest.java new file mode 100644 index 000000000..1adf02028 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/MockUpdateOrderRequest.java @@ -0,0 +1,58 @@ +package cn.binarywang.wx.miniapp.bean.delivery; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serializable; + +/** + * 微信小程序即时配送 模拟配送公司更新配送单状态 请求参数. + * + * @author Luo + * @version 1.0 + * @date 2021-10-14 10:49 + */ +@Data +@Accessors(chain = true) +public class MockUpdateOrderRequest implements Serializable { + + private static final long serialVersionUID = 3773007367000633663L; + + /** + * 商家id, 必须是 "test_shop_id". + *
+     * 是否必填:是
+     * 
+ */ + @SerializedName("shopid") + private String shopId = "test_shop_id"; + + /** + * 唯一标识订单的 ID,由商户生成. + *
+     * 是否必填:是
+     * 
+ */ + @SerializedName("shop_order_id") + private String shopOrderId; + + /** + * 状态变更时间点,Unix秒级时间戳. + *
+     * 是否必填:是
+     * 
+ */ + @SerializedName("action_time") + private Long actionTime; + + /** + * 配送状态,枚举值. + *
+     * 是否必填:是
+     * 
+ */ + @SerializedName("order_status") + private Integer orderStatus; + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/MockUpdateOrderResponse.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/MockUpdateOrderResponse.java new file mode 100644 index 000000000..de861493e --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/MockUpdateOrderResponse.java @@ -0,0 +1,25 @@ +package cn.binarywang.wx.miniapp.bean.delivery; + +import cn.binarywang.wx.miniapp.bean.delivery.base.WxMaDeliveryBaseResponse; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serializable; + +/** + * 微信小程序即时配送 模拟配送公司更新配送单状态 响应参数. + * + * @author Luo + * @version 1.0 + * @date 2021-10-14 10:49 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +public class MockUpdateOrderResponse extends WxMaDeliveryBaseResponse implements Serializable { + + private static final long serialVersionUID = 3773007367000633663L; + + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/QueryWaybillTraceRequest.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/QueryWaybillTraceRequest.java new file mode 100644 index 000000000..b7069a873 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/QueryWaybillTraceRequest.java @@ -0,0 +1,46 @@ +package cn.binarywang.wx.miniapp.bean.delivery; + + +import cn.binarywang.wx.miniapp.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import java.io.Serializable; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + *
+ * 查询运单接口 query_trace
+ *
+ * 商户在调用完trace_waybill接口后,可以使用本接口查询到对应运单的详情信息
+ * 
+ * + * @author boris + * @since 2022-04-01 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +public class QueryWaybillTraceRequest implements Serializable { + + private static final long serialVersionUID = -7538739003766268386L; + + + /** + * 查询id + *
+   * 是否必填: 是
+   * 描述: 可以从 传运单接口 trace_waybill 取数据
+   * 
+ */ + @SerializedName("waybill_token") + private String waybillToken; + + public String toJson() { + return WxMaGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/QueryWaybillTraceResponse.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/QueryWaybillTraceResponse.java new file mode 100644 index 000000000..726705cf8 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/QueryWaybillTraceResponse.java @@ -0,0 +1,123 @@ +package cn.binarywang.wx.miniapp.bean.delivery; + +import cn.binarywang.wx.miniapp.bean.WxMaBaseResponse; +import cn.binarywang.wx.miniapp.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import java.io.Serializable; +import lombok.Data; +import lombok.experimental.Accessors; + +/** + *
+ * 查询运单接口 query_trace 响应参数
+ *
+ * 商户在调用完trace_waybill接口后,可以使用本接口查询到对应运单的详情信息
+ * 
+ * + * @author boris + * @since 2022-04-01 + */ +@Data +@Accessors(chain = true) +public class QueryWaybillTraceResponse extends WxMaBaseResponse implements Serializable { + + private static final long serialVersionUID = 3773007367000633663L; + + /** + * 运单信息. + */ + @SerializedName("waybill_info") + private WaybillInfo waybillInfo; + + /** + * 商品信息 + */ + @SerializedName("shop_info") + private ShopInfo shopInfo; + + /** + * 运力信息. + */ + @SerializedName("delivery_info") + private DeliveryInfo deliveryInfo; + + + public static QueryWaybillTraceResponse fromJson(String json) { + return WxMaGsonBuilder.create().fromJson(json, QueryWaybillTraceResponse.class); + } + + /** + * 运单信息. + */ + @Data + @Accessors(chain = true) + public static class WaybillInfo implements Serializable { + + private static final long serialVersionUID = -3759074878713856529L; + + /** + * 运单状态 释义 + *
+     *
+     * 0	运单不存在或者未揽收
+     * 1	已揽件
+     * 2	运输中
+     * 3	派件中
+     * 4	已签收
+     * 5	异常
+     * 6	代签收
+     *
+     * 
+ */ + @SerializedName("status") + private Integer status; + + /** + * 查询id. + */ + @SerializedName("waybill_token") + private String waybillToken; + } + + /** + * 商品信息. + */ + @Data + @Accessors(chain = true) + public static class ShopInfo implements Serializable { + + private static final long serialVersionUID = -3759074878713856529L; + + /** + * 配送公司Id. + */ + @SerializedName("goods_info") + private WaybillGoodsInfo goodsInfo; + + + } + + + /** + * 运力信息. + */ + @Data + @Accessors(chain = true) + public static class DeliveryInfo implements Serializable { + + private static final long serialVersionUID = -3759074878713856529L; + + /** + * 配送公司Id. + */ + @SerializedName("delivery_id") + private String deliveryId; + + /** + * 运力公司名称. + */ + @SerializedName("delivery_name") + private String deliveryName; + + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/TraceWaybillRequest.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/TraceWaybillRequest.java new file mode 100644 index 000000000..0f929956a --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/TraceWaybillRequest.java @@ -0,0 +1,101 @@ +package cn.binarywang.wx.miniapp.bean.delivery; + + +import cn.binarywang.wx.miniapp.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import java.io.Serializable; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + *
+ * 传运单接口 trace_waybill
+ * 
+ * + * @author boris + * @since 2022-04-01 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +public class TraceWaybillRequest implements Serializable { + + private static final long serialVersionUID = -7538739003766268386L; + + + /** + * 用户openid + *
+   * 是否必填: 是
+   * 描述: 用户openid
+   * 
+ */ + @SerializedName("openid") + private String openid; + + /** + * 寄件人手机号 + *
+   * 是否必填: 否
+   * 描述:
+   * 
+ */ + @SerializedName("sender_phone") + private String senderPhone; + + /** + * 收件人手机号 + *
+   * 是否必填: 否
+   * 描述:部分运力需要用户手机号作为查单依据
+   * 
+ */ + @SerializedName("receiver_phone") + private String receiverPhone; + + /** + * 运单ID + *
+   * 是否必填: 是
+   * 
+ */ + @SerializedName("waybill_id") + private String waybillId; + + /** + * 交易单号(微信支付生成的交易单号,一般以420开头) + *
+   * 是否必填: 是
+   * 
+ */ + @SerializedName("trans_id") + private String transId; + + + /** + * 点击落地页商品卡片跳转路径(建议为订单详情页path),不传默认跳转小程序首页。 + *
+   * 是否必填: 否
+   * 
+ */ + @SerializedName("order_detail_path") + private String orderDetailPath; + + /** + * 商品信息 + *
+   * 是否必填: 是
+   * 
+ */ + @SerializedName("goods_info") + private WaybillGoodsInfo goodsInfo; + + public String toJson() { + return WxMaGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/TraceWaybillResponse.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/TraceWaybillResponse.java new file mode 100644 index 000000000..836e51ba0 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/TraceWaybillResponse.java @@ -0,0 +1,34 @@ +package cn.binarywang.wx.miniapp.bean.delivery; + +import cn.binarywang.wx.miniapp.bean.WxMaBaseResponse; +import cn.binarywang.wx.miniapp.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import java.io.Serializable; +import lombok.Data; +import lombok.experimental.Accessors; + +/** + *
+ * 传运单接口 trace_waybill 响应参数
+ * 
+ * + * @author boris + * @since 2022-04-01 + */ +@Data +@Accessors(chain = true) +public class TraceWaybillResponse extends WxMaBaseResponse implements Serializable { + + private static final long serialVersionUID = 3773007367000633663L; + + /** + * 查询id. + */ + @SerializedName("waybill_token") + private String waybillToken; + + + public static TraceWaybillResponse fromJson(String json) { + return WxMaGsonBuilder.create().fromJson(json, TraceWaybillResponse.class); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/WaybillGoodsInfo.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/WaybillGoodsInfo.java new file mode 100644 index 000000000..709d316ec --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/WaybillGoodsInfo.java @@ -0,0 +1,59 @@ +package cn.binarywang.wx.miniapp.bean.delivery; + +import cn.binarywang.wx.miniapp.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import java.io.Serializable; +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 运单商品信息 + * + * @author boris + * @since 2022-04-01 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class WaybillGoodsInfo implements Serializable { + + private static final long serialVersionUID = 5643624677715536605L; + + + + /** + * 商品信息 + */ + @SerializedName("detail_list") + private List goodsItemList; + + public static WaybillGoodsInfo fromJson(String json) { + return WxMaGsonBuilder.create().fromJson(json, WaybillGoodsInfo.class); + } + + @Data + public static class GoodsItem { + + /** + * 商品名称 + *
+     * 是否必填: 是
+     * 
+ */ + @SerializedName("goods_name") + private Long goodsName; + + /** + * 商品图片URL + *
+     * 是否必填: 是
+     * 
+ */ + @SerializedName("goods_img_url") + private Integer goodsImgUrl; + + + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/base/WxMaDeliveryBaseRequest.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/base/WxMaDeliveryBaseRequest.java new file mode 100644 index 000000000..a139ea907 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/base/WxMaDeliveryBaseRequest.java @@ -0,0 +1,119 @@ +package cn.binarywang.wx.miniapp.bean.delivery.base; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; +import lombok.AccessLevel; +import lombok.Data; +import lombok.Setter; +import lombok.experimental.Accessors; +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.lang3.StringUtils; + +import java.io.Serializable; + +/** + * 微信小程序 即时配送 基础请求参数. + * + * @author Luo + * @version 1.0 + * @date 2021-10-14 10:36 + */ +@Data +@Accessors(chain = true) +public abstract class WxMaDeliveryBaseRequest implements Serializable { + + private static final long serialVersionUID = -6811550517417623460L; + + /** + * 配送公司ID. + *
+     * 是否必填:是
+     * 
+ */ + @SerializedName("delivery_id") + private String deliveryId; + + /** + * 唯一标识订单的 ID,由商户生成, 不超过 128 字节. + *
+     * 是否必填:是
+     * 
+ */ + @SerializedName("shop_order_id") + private String shopOrderId; + + /** + * 下单用户的openid. + *
+     * 是否必填:是
+     * 
+ */ + @SerializedName("openid") + private String openid; + + /** + * 商家门店编号,在配送公司登记,如果只有一个门店,美团闪送必填, 值为店铺id. + *
+     * 是否必填:是
+     * 
+ */ + @SerializedName("shop_no") + private String shopNo; + + /** + * 商家id,由配送公司分配的appKey. + *
+     * 是否必填:是
+     * 
+ */ + @SerializedName("shopid") + private String shopId; + + /** + * 用配送公司提供的appSecret加密的校验串. + *
+     * 除了平台本身的加解密和签名,和订单相关的请求还需要带上运力侧签名delivery_sign,签名规则为
+     * 如果接口请求里有字段shop_order_id ,则delivery_sign=SHA1(shopid + shop_order_id + AppSecret),其中shopid对应运力侧的appkey,shop_order_id对应订单id,AppSecret即配送公司帐号对应的秘钥
+     * 如果请求里没有字段shop_order_id ,则delivery_sign=SHA1(shopid + AppSecret),其中shopid对应运力侧的appkey,AppSecret即配送公司帐号对应的秘钥
+     * 示例:shopid=“test_shop_id”,shop_order_id =“test_shop_order_id”, AppSecret=“test_app_secrect”,则delivery_sign=“a93d8d6bae9a9483c1b1d4e8670e7f6226ec94cb”
+     * 是否必填:是
+     * 
+ */ + @Setter(AccessLevel.NONE) + @SerializedName("delivery_sign") + private String deliverySign; + + /** + * 配送公司分配的appSecret. + *
+     * 是否必填:是
+     * 
+ */ + @Expose + private String appSecret; + + /** + * 获取签名. + *
+     * 除了平台本身的加解密和签名,和订单相关的请求还需要带上运力侧签名delivery_sign,签名规则为
+     * 如果接口请求里有字段shop_order_id ,则delivery_sign=SHA1(shopid + shop_order_id + AppSecret),其中shopid对应运力侧的appkey,shop_order_id对应订单id,AppSecret即配送公司帐号对应的秘钥
+     * 如果请求里没有字段shop_order_id ,则delivery_sign=SHA1(shopid + AppSecret),其中shopid对应运力侧的appkey,AppSecret即配送公司帐号对应的秘钥
+     * 示例:shopid=“test_shop_id”,shop_order_id =“test_shop_order_id”, AppSecret=“test_app_secrect”,则delivery_sign=“a93d8d6bae9a9483c1b1d4e8670e7f6226ec94cb”
+     * 是否必填:是
+     * 
+ * + * @return 结果 + */ + public String getDeliverySign() { + if (StringUtils.isBlank(getShopId()) || StringUtils.isBlank(getAppSecret())) { + throw new RuntimeException("shopId or appSecret can not be empty"); + } + String str = getShopId(); + if (StringUtils.isNotBlank(getShopOrderId())) { + str = str.concat(getShopOrderId()); + } + str = str.concat(getAppSecret()); + return DigestUtils.sha1Hex(str); + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/base/WxMaDeliveryBaseResponse.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/base/WxMaDeliveryBaseResponse.java new file mode 100644 index 000000000..38f354bb7 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/base/WxMaDeliveryBaseResponse.java @@ -0,0 +1,66 @@ +package cn.binarywang.wx.miniapp.bean.delivery.base; + +import cn.binarywang.wx.miniapp.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.experimental.Accessors; +import org.apache.commons.lang3.StringUtils; + +import java.io.Serializable; + +/** + * 微信小程序 即时配送 基础响应参数. + * + * @author Luo + * @version 1.0 + * @date 2021-10-14 10:36 + */ +@Data +@Accessors(chain = true) +public abstract class WxMaDeliveryBaseResponse implements Serializable { + + private static final long serialVersionUID = -6811550517417623460L; + + /** + * 成功状态码. + */ + private static final int SUCCESS_CODE = 0; + + /** + * 运力返回的错误码. + */ + @SerializedName("resultcode") + private Integer resultCode; + + /** + * 运力返回的错误描述. + */ + @SerializedName("resultmsg") + private String resultMsg; + + /** + * 是否响应成功. + * + * @return true:成功、false:失败 + */ + public boolean success() { + return SUCCESS_CODE == getResultCode(); + } + + /** + * 解析响应. + * + * @param json 响应内容 + * @param valueType 类型 + * @param 类型 + * @return 结果 + */ + public static T fromJson(final String json, final Class valueType) { + if (StringUtils.isBlank(json)) { + throw new RuntimeException("the json cannot be empty"); + } + // 解析成对应响应对象 + return WxMaGsonBuilder.create().fromJson(json, valueType); + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/device/WxMaDeviceSubscribeMessageRequest.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/device/WxMaDeviceSubscribeMessageRequest.java new file mode 100644 index 000000000..34158391a --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/device/WxMaDeviceSubscribeMessageRequest.java @@ -0,0 +1,72 @@ +package cn.binarywang.wx.miniapp.bean.device; + +import cn.binarywang.wx.miniapp.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +/** + * 小程序设备订阅消息请求参数 + * + * @author JCLee + * @since 2021-12-16 17:13:22 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WxMaDeviceSubscribeMessageRequest implements Serializable { + + private static final long serialVersionUID = -7973228178407991299L; + + /** + * 接收者(用户)的 openid列表. + */ + @SerializedName("to_openid_list") + private List toOpenidList; + + /** + * 下发通知的设备唯⼀序列号。由⼚商⽣成 + */ + @SerializedName("sn") + private String sn; + + /** + * 所需下发的消息模板ID + */ + @SerializedName("template_id") + private String templateId; + + /** + * 点击模板卡片后的跳转页面,仅限本小程序内的页面。支持带参数,(示例index?foo=bar)。该字段不填则模板无跳转. + */ + @SerializedName("page") + private String page; + + /** + * 跳转小程序类型:developer为开发版;trial为体验版;formal为正式版;默认为正式版. + */ + @SerializedName("miniprogram_state") + private String miniprogramState; + + /** + * 进入小程序查看”的语言类型,支持zh_CN(简体中文)、en_US(英文)、zh_HK(繁体中文)、zh_TW(繁体中文),默认为zh_CN. + */ + @SerializedName("lang") + private String lang; + + /** + * 模板内容,格式形如 { "key1": { "value": any }, "key2": { "value": any } }. + */ + @SerializedName("data") + private Object data; + + public String toJson() { + return WxMaGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/device/WxMaDeviceTicketRequest.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/device/WxMaDeviceTicketRequest.java new file mode 100644 index 000000000..c3319eecd --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/device/WxMaDeviceTicketRequest.java @@ -0,0 +1,41 @@ +package cn.binarywang.wx.miniapp.bean.device; + +import cn.binarywang.wx.miniapp.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 小程序设备ticket请求参数 + * + * @author JCLee + * @since 2021-12-16 17:13:28 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WxMaDeviceTicketRequest implements Serializable { + private static final long serialVersionUID = -2152114813101871295L; + + /** + * 设备型号id。通过注册设备获得(必填) + * + */ + @SerializedName("model_id") + private String modelId; + + /** + * 设备唯⼀序列号。由⼚商分配。⻓度不能超过128字节。字符只接受数字与⼤⼩写字⺟(必填) + */ + @SerializedName("sn") + private String sn; + + public String toJson() { + return WxMaGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/live/WxMaLiveGoodInfo.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/live/WxMaLiveGoodInfo.java index 4a63ff7a4..3ef043495 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/live/WxMaLiveGoodInfo.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/live/WxMaLiveGoodInfo.java @@ -4,6 +4,7 @@ import java.io.Serializable; import java.math.BigDecimal; +import java.util.List; /** * 直播商品信息 @@ -22,4 +23,8 @@ public class WxMaLiveGoodInfo implements Serializable { * 1, 2:表示是为api添加商品,否则是在MP添加商品 */ private String thirdPartyTag; + /** + * https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/industry/liveplayer/pendant.html + */ + private List goodsKey; } diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/live/WxMaLiveResult.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/live/WxMaLiveResult.java index 7d880ecc8..dfb7b1e48 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/live/WxMaLiveResult.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/live/WxMaLiveResult.java @@ -20,7 +20,7 @@ public class WxMaLiveResult implements Serializable { private static final long serialVersionUID = 1L; private Integer total; - private Integer auditId; + private Long auditId; private Integer goodsId; private List goods; diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/marketing/WxMaUserAction.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/marketing/WxMaUserAction.java new file mode 100644 index 000000000..2cde905be --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/marketing/WxMaUserAction.java @@ -0,0 +1,73 @@ +package cn.binarywang.wx.miniapp.bean.marketing; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +/** + * @Description :微信营销接口 + * @author 184759547 + * @since : 2021/12/28 + */ + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class WxMaUserAction implements Serializable { + private static final long serialVersionUID = 7482378011709983616L; + private String url; + private Integer actionTime; + private String actionType; + private String leadsType; + private String clickId; + private Integer actionParam; + + private JsonObject toJsonObject() { + JsonObject json = new JsonObject(); + json.addProperty("url", this.url); + json.addProperty("action_time", this.actionTime); + json.addProperty("action_type", this.actionType); + + if (this.clickId != null) { + JsonObject traceJson = new JsonObject(); + traceJson.addProperty("click_id", this.clickId); + json.add("trace", traceJson); + } + + if (this.actionParam != null) { + JsonObject actionParamJson = new JsonObject(); + actionParamJson.addProperty("value", actionParam); + if (this.leadsType != null) { + actionParamJson.addProperty("leads_type", leadsType); + } + json.add("action_param", actionParamJson); + } + + return json; + } + + /** + * list对象转换为json字符串 + * + * @param actions . + * @return . + */ + public static String listToJson(List actions, Long userActionSetId) { + JsonArray array = new JsonArray(); + for (WxMaUserAction action : actions) { + array.add(action.toJsonObject()); + } + + JsonObject result = new JsonObject(); + result.addProperty("user_action_set_id", userActionSetId); + result.add("actions", array); + return result.toString(); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/safety/request/WxMaUserSafetyRiskRankRequest.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/safety/request/WxMaUserSafetyRiskRankRequest.java new file mode 100644 index 000000000..da9384b1a --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/safety/request/WxMaUserSafetyRiskRankRequest.java @@ -0,0 +1,81 @@ +package cn.binarywang.wx.miniapp.bean.safety.request; + +import cn.binarywang.wx.miniapp.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 获取用户的安全等级请求参数 + * + * @author azouever + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WxMaUserSafetyRiskRankRequest implements Serializable { + private static final long serialVersionUID = 1052539797739665816L; + + + /** + * 小程序appid + * 必填 + */ + private String appid; + + /** + * 用户的openid + * 必填 + */ + private String openid; + + /** + * 场景值,0:注册,1:营销作弊 + * 必填 + */ + private Integer scene; + + /** + * 用户手机号 + * 非必填 + */ + @SerializedName("mobile_no") + private String mobileNo; + + /** + * 用户访问源ip + * 必填 + */ + @SerializedName("client_ip") + private String clientIp; + + /** + * 用户邮箱地址 + * 非必填 + */ + @SerializedName("email_address") + private String emailAddress; + + /** + * 额外补充信息 + * 非必填 + */ + @SerializedName("extended_info") + private String extendedInfo; + + /** + * false:正式调用,true:测试调用 + * 非必填 + */ + @SerializedName("is_test") + private boolean isTest; + + public String toJson() { + return WxMaGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/safety/response/WxMaUserSafetyRiskRankResponse.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/safety/response/WxMaUserSafetyRiskRankResponse.java new file mode 100644 index 000000000..1a3a5d1f5 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/safety/response/WxMaUserSafetyRiskRankResponse.java @@ -0,0 +1,42 @@ +package cn.binarywang.wx.miniapp.bean.safety.response; + +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +import java.io.Serializable; + +/** + * 获取用户的安全等级响应参数 + * + * @author azouever + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WxMaUserSafetyRiskRankResponse implements Serializable { + + private static final long serialVersionUID = -2434941857751339150L; + + /** + * 唯一请求标识,标记单次请求 + */ + @SerializedName("unoin_id") + private Integer unoinId; + + /** + * 用户风险等级 + * 合法值 https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/safety-control-capability/riskControl.getUserRiskRank.html + */ + @SerializedName("risk_rank") + private Integer riskRank; + + public static WxMaUserSafetyRiskRankResponse fromJson(String json) { + return WxGsonBuilder.create().fromJson(json, WxMaUserSafetyRiskRankResponse.class); + } +} + diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/scheme/WxMaGenerateSchemeRequest.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/scheme/WxMaGenerateSchemeRequest.java index 80c5f9034..a62c1334a 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/scheme/WxMaGenerateSchemeRequest.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/scheme/WxMaGenerateSchemeRequest.java @@ -40,10 +40,23 @@ public class WxMaGenerateSchemeRequest { private Long expireTime; /** - * 要打开的小程序版本。正式版为"release",体验版为"trial",开发版为"develop"默认值:release + * 到期失效的 scheme 码失效类型,失效时间:0,失效间隔天数:1 + *
+   * 是否必填:否
+   * 
*/ - @SerializedName("env_version") - private String envVersion = "release"; + @SerializedName("expire_type") + private Integer expireType; + + /** + * 到期失效的 scheme 码的失效间隔天数。 + *
+   * 生成的到期失效 scheme 码在该间隔时间到达前有效。最长间隔天数为365天。is_expire 为 true 且 expire_type 为 1 时必填   * 
+   * 是否必填:否
+   * 
+ */ + @SerializedName("expire_interval") + private Integer expireInterval; @Data @Builder(builderMethodName = "newBuilder") @@ -66,6 +79,12 @@ public static class JumpWxa { */ @SerializedName("query") private String query; + + /** + * 要打开的小程序版本。正式版为"release",体验版为"trial",开发版为"develop"默认值:release + */ + @SerializedName("env_version") + private String envVersion = "release"; } public String toJson() { diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/security/WxMaMediaSecCheckCheckRequest.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/security/WxMaMediaSecCheckCheckRequest.java new file mode 100644 index 000000000..16b0d35c7 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/security/WxMaMediaSecCheckCheckRequest.java @@ -0,0 +1,34 @@ +package cn.binarywang.wx.miniapp.bean.security; + +import com.google.gson.annotations.SerializedName; +import lombok.Builder; +import lombok.Data; + +import java.io.Serializable; + +/** + * @author dingxw + * @date 2021/11/18 20:27 + */ +@Data +@Builder +public class WxMaMediaSecCheckCheckRequest implements Serializable { + + private static final long serialVersionUID = -3947838615379224577L; + + @SerializedName("media_url") + private String mediaUrl; + + @SerializedName("media_type") + private Integer mediaType; + + @SerializedName("version") + private Integer version; + + @SerializedName("openid") + private String openid; + + @SerializedName("scene") + private Integer scene; + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/shop/response/WxMaShopAddOrderResponse.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/shop/response/WxMaShopAddOrderResponse.java index 4e724423b..ecd4ff9b7 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/shop/response/WxMaShopAddOrderResponse.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/shop/response/WxMaShopAddOrderResponse.java @@ -16,5 +16,5 @@ public class WxMaShopAddOrderResponse extends WxMaShopBaseResponse implements Se private static final long serialVersionUID = -8923439859095040010L; @SerializedName("data") - private WxMaShopAddOrderResult date; + private WxMaShopAddOrderResult data; } diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/urllink/GenerateUrlLinkRequest.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/urllink/GenerateUrlLinkRequest.java index 207aa3dee..9578e7694 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/urllink/GenerateUrlLinkRequest.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/urllink/GenerateUrlLinkRequest.java @@ -1,9 +1,10 @@ package cn.binarywang.wx.miniapp.bean.urllink; -import cn.binarywang.wx.miniapp.json.WxMaGsonBuilder; import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; +import lombok.NoArgsConstructor; import java.io.Serializable; @@ -16,6 +17,8 @@ */ @Data @Builder +@NoArgsConstructor +@AllArgsConstructor public class GenerateUrlLinkRequest implements Serializable { private static final long serialVersionUID = -2183685760797791910L; @@ -36,6 +39,15 @@ public class GenerateUrlLinkRequest implements Serializable { */ private String query; + /** + * 要打开的小程序版本。正式版为"release",体验版为"trial",开发版为"develop",仅在微信外打开时生效。 + *
+   * 是否必填: 否
+   * 
+ */ + @SerializedName("env_version") + private String envVersion = "release"; + /** * 生成的 URL Link 类型,到期失效:true,永久有效:false *
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/WxMaRedisBetterConfigImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/WxMaRedisBetterConfigImpl.java
index 48c5e8e31..4b38f1d64 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/WxMaRedisBetterConfigImpl.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/WxMaRedisBetterConfigImpl.java
@@ -7,6 +7,8 @@
 
 /**
  * 基于redis存储的微信小程序配置类
+ *
+ * @author Mario Luo, 2020-04-18 23:08
  */
 public class WxMaRedisBetterConfigImpl extends WxMaDefaultConfigImpl {
   private static final String ACCESS_TOKEN_KEY_TPL = "%s:access_token:%s";
@@ -121,4 +123,20 @@ private void doExpireTicket(TicketType type) {
     redisOps.expire(this.getTicketRedisKey(type), 0, TimeUnit.SECONDS);
   }
 
+  @Override
+  public String toString() {
+    return "WxMaRedisBetterConfigImpl{" +
+      "appid='" + appid + '\'' +
+      ", token='" + token + '\'' +
+      ", originalId='" + originalId + '\'' +
+      ", accessTokenLock=" + accessTokenLock +
+      ", tmpDirFile=" + tmpDirFile +
+      ", jsapiTicketLock=" + jsapiTicketLock +
+      ", cardApiTicketLock=" + cardApiTicketLock +
+      ", redisOps=" + redisOps +
+      ", keyPrefix='" + keyPrefix + '\'' +
+      ", accessTokenKey='" + accessTokenKey + '\'' +
+      ", lockKey='" + lockKey + '\'' +
+      '}';
+  }
 }
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaApiUrlConstants.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaApiUrlConstants.java
index 17b72c8db..4377b148b 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaApiUrlConstants.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaApiUrlConstants.java
@@ -158,22 +158,107 @@ public interface Jsapi {
   }
 
   public interface Broadcast {
-    String GET_LIVE_INFO = "https://api.weixin.qq.com/wxa/business/getliveinfo";
-
     /**
      * 直播间管理相关接口
      */
     interface Room {
+      /**
+       * 创建直播间
+       */
       String CREATE_ROOM = "https://api.weixin.qq.com/wxaapi/broadcast/room/create";
+      /**
+       * 获取直播间列表
+       * 获取直播间回放
+       */
+      String GET_LIVE_INFO = "https://api.weixin.qq.com/wxa/business/getliveinfo";
+      /**
+       * 直播间导入商品
+       */
       String ADD_GOODS = "https://api.weixin.qq.com/wxaapi/broadcast/room/addgoods";
+      /**
+       * 删除直播间
+       */
       String DELETE_ROOM = "https://api.weixin.qq.com/wxaapi/broadcast/room/deleteroom";
+      /**
+       * 编辑直播间
+       */
       String EDIT_ROOM = "https://api.weixin.qq.com/wxaapi/broadcast/room/editroom";
+      /**
+       * 获取直播间推流地址
+       */
       String GET_PUSH_URL = "https://api.weixin.qq.com/wxaapi/broadcast/room/getpushurl";
+      /**
+       * 获取直播间分享二维码
+       */
       String GET_SHARED_CODE = "https://api.weixin.qq.com/wxaapi/broadcast/room/getsharedcode";
+      /**
+       * 添加管理直播间小助手
+       */
       String ADD_ASSISTANT = "https://api.weixin.qq.com/wxaapi/broadcast/room/addassistant";
+      /**
+       * 修改管理直播间小助手
+       */
       String MODIFY_ASSISTANT = "https://api.weixin.qq.com/wxaapi/broadcast/room/modifyassistant";
+      /**
+       * 删除管理直播间小助手
+       */
       String REMOVE_ASSISTANT = "https://api.weixin.qq.com/wxaapi/broadcast/room/removeassistant";
+      /**
+       * 查询管理直播间小助手
+       */
       String GET_ASSISTANT_LIST = "https://api.weixin.qq.com/wxaapi/broadcast/room/getassistantlist";
+      /**
+       * 添加主播副号
+       */
+      String ADD_SUBANCHOR = "https://api.weixin.qq.com/wxaapi/broadcast/room/addsubanchor";
+      /**
+       * 修改主播副号
+       */
+      String MODIFY_SUBANCHOR = "https://api.weixin.qq.com/wxaapi/broadcast/room/modifysubanchor";
+      /**
+       * 删除主播副号
+       */
+      String DELETE_SUBANCHOR = "https://api.weixin.qq.com/wxaapi/broadcast/room/deletesubanchor";
+      /**
+       * 获取主播副号
+       */
+      String GET_SUBANCHOR = "https://api.weixin.qq.com/wxaapi/broadcast/room/getsubanchor";
+      /**
+       * 开启/关闭直播间官方收录
+       */
+      String UPDATE_FEED_PUBLIC = "https://api.weixin.qq.com/wxaapi/broadcast/room/updatefeedpublic";
+      /**
+       * 开启/关闭回放功能
+       */
+      String UPDATE_REPLAY = "https://api.weixin.qq.com/wxaapi/broadcast/room/updatereplay";
+      /**
+       * 开启/关闭客服功能
+       */
+      String UPDATE_KF = "https://api.weixin.qq.com/wxaapi/broadcast/room/updatekf";
+      /**
+       * 开启/关闭直播间全局禁言
+       */
+      String UPDATE_COMMENT = "https://api.weixin.qq.com/wxaapi/broadcast/room/updatecomment";
+      /**
+       * 上下架商品
+       */
+      String ONSALE = "https://api.weixin.qq.com/wxaapi/broadcast/goods/onsale";
+      /**
+       * 删除商品
+       */
+      String DELETE_IN_ROOM = "https://api.weixin.qq.com/wxaapi/broadcast/goods/deleteInRoom";
+      /**
+       * 推送商品
+       */
+      String PUSH = "https://api.weixin.qq.com/wxaapi/broadcast/goods/push";
+      /**
+       * 商品排序
+       */
+      String SORT = "https://api.weixin.qq.com/wxaapi/broadcast/goods/sort";
+      /**
+       * 下载商品讲解视频
+       */
+      String GET_VIDEO = "https://api.weixin.qq.com/wxaapi/broadcast/goods/getVideo";
     }
 
     /**
@@ -187,6 +272,14 @@ interface Goods {
       String UPDATE_GOODS = "https://api.weixin.qq.com/wxaapi/broadcast/goods/update";
       String GET_GOODS_WARE_HOUSE = "https://api.weixin.qq.com/wxa/business/getgoodswarehouse";
       String GET_APPROVED_GOODS = "https://api.weixin.qq.com/wxaapi/broadcast/goods/getapproved";
+      /**
+       * 直播挂件设置全局 Key
+       */
+      String SET_KEY = "https://api.weixin.qq.com/wxaapi/broadcast/goods/setkey";
+      /**
+       * 直播挂件获取全局 Key
+       */
+      String GET_KEY = "https://api.weixin.qq.com/wxaapi/broadcast/goods/getkey";
     }
 
     /**
@@ -288,6 +381,7 @@ public interface Subscribe {
 
   public interface User {
     String SET_USER_STORAGE = "https://api.weixin.qq.com/wxa/set_user_storage?appid=%s&signature=%s&openid=%s&sig_method=%s";
+    String GET_PHONE_NUMBER_URL = "https://api.weixin.qq.com/wxa/business/getuserphonenumber";
   }
 
   public interface Ocr {
@@ -357,20 +451,20 @@ interface Audit {
     interface Delivery {
       String GET_COMPANY_LIST = "https://api.weixin.qq.com/shop/delivery/get_company_list";
       String DELIVERY_SEND = "https://api.weixin.qq.com/shop/delivery/send";
-      String DELIVERY_RECEIVE  = "https://api.weixin.qq.com/shop/delivery/recieve";
+      String DELIVERY_RECEIVE = "https://api.weixin.qq.com/shop/delivery/recieve";
     }
 
     interface Aftersale {
       String AFTERSALE_ADD = "https://api.weixin.qq.com/shop/aftersale/add";
       String AFTERSALE_GET = "https://api.weixin.qq.com/shop/aftersale/get";
-      String AFTERSALE_UPDATE  = "https://api.weixin.qq.com/shop/aftersale/update";
+      String AFTERSALE_UPDATE = "https://api.weixin.qq.com/shop/aftersale/update";
     }
   }
 
   /**
    * 电子发票报销方
    */
-  public interface Invoice{
+  public interface Invoice {
 
     /**
      * 报销方查询报销发票信息
@@ -393,7 +487,160 @@ public interface Invoice{
     String UPDATE_STATUS_BATCH = "https://api.weixin.qq.com/card/invoice/reimburse/updatestatusbatch";
   }
 
-  public interface Internet{
+  public interface Internet {
     String GET_USER_ENCRYPT_KEY = "https://api.weixin.qq.com/wxa/business/getuserencryptkey";
   }
+
+  /**
+   * 设备订阅消息
+   */
+  public interface DeviceSubscribe {
+    /**
+     * 获取设备票据
+     */
+    String GET_SN_TICKET_URL = "https://api.weixin.qq.com/wxa/getsnticket";
+    /**
+     * 发送设备订阅消息
+     */
+    String SEND_DEVICE_SUBSCRIBE_MSG_URL = "https://api.weixin.qq.com/cgi-bin/message/device/subscribe/send";
+  }
+
+  /**
+   * 即时配送相关接口.
+   * 
+   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/industry/immediate-delivery/overview.html
+   * 
+ */ + public interface InstantDelivery { + + /** + * 拉取已绑定账号. + *
+     * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/immediate-delivery/by-business/immediateDelivery.getBindAccount.html
+     * 
+ */ + String GET_BIND_ACCOUNT = "https://api.weixin.qq.com/cgi-bin/express/local/business/shop/get"; + + /** + * 拉取配送单信息. + *
+     * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/immediate-delivery/by-business/immediateDelivery.getOrder.html
+     * 
+ */ + String GET_ORDER = "https://api.weixin.qq.com/cgi-bin/express/local/business/order/get"; + + /** + * 模拟配送公司更新配送单状态. + *
+     * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/immediate-delivery/by-business/immediateDelivery.mockUpdateOrder.html
+     * 
+ */ + String MOCK_UPDATE_ORDER = "https://api.weixin.qq.com/cgi-bin/express/local/business/test_update_order"; + + /** + * 物流服务-查询组件-跟踪物流面单 + * 商户使用此接口向微信提供某交易单号对应的运单号。微信后台会跟踪运单的状态变化 + */ + String TRACE_WAYBILL_URL = "https://api.weixin.qq.com/cgi-bin/express/delivery/open_msg/trace_waybill"; + + + /** + * 物流服务-查询组件-查询运单接口 query_trace + * 商户在调用完trace_waybill接口后,可以使用本接口查询到对应运单的详情信息 + */ + String QUERY_WAYBILL_TRACE_URL = "https://api.weixin.qq.com/cgi-bin/express/delivery/open_msg/query_trace"; + + + /** + * 下单接口. + */ + interface PlaceAnOrder { + + /** + * 获取已支持的配送公司列表接口. + *
+       * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/immediate-delivery/by-business/immediateDelivery.getAllImmeDelivery.html
+       * 
+ */ + String GET_ALL_IMME_DELIVERY = "https://api.weixin.qq.com/cgi-bin/express/local/business/delivery/getall"; + + /** + * 预下配送单接口. + *
+       * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/immediate-delivery/by-business/immediateDelivery.preAddOrder.html
+       * 
+ */ + String PRE_ADD_ORDER = "https://api.weixin.qq.com/cgi-bin/express/local/business/order/pre_add"; + + /** + * 下配送单接口. + *
+       * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/immediate-delivery/by-business/immediateDelivery.addOrder.html
+       * 
+ */ + String ADD_ORDER = "https://api.weixin.qq.com/cgi-bin/express/local/business/order/add"; + + /** + * 重新下单. + *
+       * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/immediate-delivery/by-business/immediateDelivery.reOrder.html
+       * 
+ */ + String RE_ORDER = "https://api.weixin.qq.com/cgi-bin/express/local/business/order/readd"; + + /** + * 增加小费. + *
+       * 可以对待接单状态的订单增加小费。需要注意:订单的小费,以最新一次加小费动作的金额为准,故下一次增加小费额必须大于上一次小费额.
+       * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/immediate-delivery/by-business/immediateDelivery.addTip.html
+       * 
+ */ + String ADD_TIP = "https://api.weixin.qq.com/cgi-bin/express/local/business/order/addtips"; + + } + + /** + * 取消接口. + */ + interface Cancel { + + /** + * 预取消配送单接口. + *
+       * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/immediate-delivery/by-business/immediateDelivery.preCancelOrder.html
+       * 
+ */ + String PRE_CANCEL_ORDER = "https://api.weixin.qq.com/cgi-bin/express/local/business/order/precancel"; + + /** + * 取消配送单接口. + *
+       * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/immediate-delivery/by-business/immediateDelivery.cancelOrder.html
+       * 
+ */ + String CANCEL_ORDER = "https://api.weixin.qq.com/cgi-bin/express/local/business/order/cancel"; + + /** + * 异常件退回商家商家确认收货接口. + *
+       * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/immediate-delivery/by-business/immediateDelivery.abnormalConfirm.html
+       * 
+ */ + String ABNORMAL_CONFIRM = "https://api.weixin.qq.com/cgi-bin/express/local/business/order/confirm_return"; + + } + + + /** + * 安全风控 + */ + interface SafetyRiskControl { + /** + * 获取用户的安全等级,无需用户授权 + */ + String GET_USER_RISK_RANK = "https://api.weixin.qq.com/wxa/getuserriskrank"; + } + + } + } diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaConstants.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaConstants.java index 8ac322aa5..646d909fc 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaConstants.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaConstants.java @@ -11,6 +11,11 @@ public abstract class WxMaConstants { private WxMaConstants() { } + /** + * 默认的env_version值 + */ + public static final String DEFAULT_ENV_VERSION = "release"; + /** * 微信接口返回的参数errcode. */ diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/json/WxMaGsonBuilder.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/json/WxMaGsonBuilder.java index e6f6842fa..537982665 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/json/WxMaGsonBuilder.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/json/WxMaGsonBuilder.java @@ -1,6 +1,7 @@ package cn.binarywang.wx.miniapp.json; import cn.binarywang.wx.miniapp.bean.WxMaSubscribeMessage; +import cn.binarywang.wx.miniapp.bean.WxMaSubscribeMsgEvent; import cn.binarywang.wx.miniapp.bean.WxMaUniformMessage; import cn.binarywang.wx.miniapp.bean.analysis.WxMaRetainInfo; import cn.binarywang.wx.miniapp.bean.analysis.WxMaUserPortrait; @@ -10,12 +11,14 @@ import cn.binarywang.wx.miniapp.json.adaptor.*; import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import java.util.Objects; /** * @author Binary Wang */ public class WxMaGsonBuilder { private static final GsonBuilder INSTANCE = new GsonBuilder(); + private static volatile Gson GSON_INSTANCE; static { INSTANCE.disableHtmlEscaping(); @@ -26,10 +29,18 @@ public class WxMaGsonBuilder { INSTANCE.registerTypeAdapter(WxMaVisitDistribution.class, new WxMaVisitDistributionGsonAdapter()); INSTANCE.registerTypeAdapter(WxMaRetainInfo.class, new WxMaRetainInfoGsonAdapter()); INSTANCE.registerTypeAdapter(WxMaUserPortrait.class, new WxMaUserPortraitGsonAdapter()); + INSTANCE.registerTypeAdapter(WxMaSubscribeMsgEvent.WxMaSubscribeMsgEventJson.class, new WxMaSubscribeMsgEventJsonAdapter()); } public static Gson create() { - return INSTANCE.create(); + if (Objects.isNull(GSON_INSTANCE)) { + synchronized (INSTANCE) { + if (Objects.isNull(GSON_INSTANCE)) { + GSON_INSTANCE = INSTANCE.create(); + } + } + } + return GSON_INSTANCE; } } diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/json/adaptor/WxMaSubscribeMsgEventJsonAdapter.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/json/adaptor/WxMaSubscribeMsgEventJsonAdapter.java new file mode 100644 index 000000000..d489f14a7 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/json/adaptor/WxMaSubscribeMsgEventJsonAdapter.java @@ -0,0 +1,104 @@ +package cn.binarywang.wx.miniapp.json.adaptor; + +import cn.binarywang.wx.miniapp.bean.WxMaSubscribeMsgEvent; +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 lombok.extern.slf4j.Slf4j; + +import java.lang.reflect.Type; + +/** + * WxMaSubscribeMsgEventJsonAdapter class + * + * @author dany + * @date 2021/12/31 + */ +@Slf4j +public class WxMaSubscribeMsgEventJsonAdapter implements JsonDeserializer { + @Override + public WxMaSubscribeMsgEvent.WxMaSubscribeMsgEventJson deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + WxMaSubscribeMsgEvent.WxMaSubscribeMsgEventJson result = new WxMaSubscribeMsgEvent.WxMaSubscribeMsgEventJson(); + if (json.isJsonArray()) { + JsonArray array = json.getAsJsonArray(); + if (array.size() > 0) { + JsonObject obj = array.get(0).getAsJsonObject(); + MsgEventTypeEnum eventType = detectMsgEventType(obj); + for (int i = 0; i < array.size(); ++i) { + obj = array.get(i).getAsJsonObject(); + setField(result, eventType, obj); + } + } + } else { + JsonObject obj = json.getAsJsonObject(); + MsgEventTypeEnum eventType = detectMsgEventType(obj); + setField(result, eventType, obj); + } + return result; + } + + public enum MsgEventTypeEnum { + EVENT_POPUP,EVENT_CHANGE,EVENT_SENT; + } + private MsgEventTypeEnum detectMsgEventType(JsonObject obj) { + JsonElement popupScene = obj.get("PopupScene"); + if (popupScene != null) { + return MsgEventTypeEnum.EVENT_POPUP; + } + + JsonElement msgId = obj.get("MsgID"); + if (msgId != null) { + return MsgEventTypeEnum.EVENT_SENT; + } + JsonElement errorCode = obj.get("ErrorCode"); + if (errorCode != null) { + return MsgEventTypeEnum.EVENT_SENT; + } + JsonElement errorStatus = obj.get("ErrorStatus"); + if (errorStatus != null) { + return MsgEventTypeEnum.EVENT_SENT; + } + + return MsgEventTypeEnum.EVENT_CHANGE; + } + + private WxMaSubscribeMsgEvent.WxMaSubscribeMsgEventJson setField(WxMaSubscribeMsgEvent.WxMaSubscribeMsgEventJson target, + MsgEventTypeEnum eventType, JsonObject json) { + switch (eventType) { + case EVENT_POPUP: + if (target.getPopupEvents() == null) { + target.setPopupEvents(new WxMaSubscribeMsgEvent.SubscribeMsgPopupEvent()); + } + WxMaSubscribeMsgEvent.PopupEvent popupEvent = new WxMaSubscribeMsgEvent.PopupEvent(); + popupEvent.setTemplateId(json.get("TemplateId").getAsString()); + popupEvent.setSubscribeStatusString(json.get("SubscribeStatusString").getAsString()); + popupEvent.setPopupScene(json.get("PopupScene").getAsString()); + target.getPopupEvents().getList().add(popupEvent); + break; + case EVENT_CHANGE: + if (target.getChangeEvents() == null) { + target.setChangeEvents(new WxMaSubscribeMsgEvent.SubscribeMsgChangeEvent()); + } + WxMaSubscribeMsgEvent.ChangeEvent changeEvent = new WxMaSubscribeMsgEvent.ChangeEvent(); + changeEvent.setTemplateId(json.get("TemplateId").getAsString()); + changeEvent.setSubscribeStatusString(json.get("SubscribeStatusString").getAsString()); + target.getChangeEvents().getList().add(changeEvent); + break; + case EVENT_SENT: + if (target.getSentEvent() == null) { + target.setSentEvent(new WxMaSubscribeMsgEvent.SubscribeMsgSentEvent()); + } + WxMaSubscribeMsgEvent.SentEvent sentEvent = new WxMaSubscribeMsgEvent.SentEvent(); + sentEvent.setTemplateId(json.get("TemplateId").getAsString()); + sentEvent.setMsgId(json.get("MsgID").getAsString()); + sentEvent.setErrorCode(json.get("ErrorCode").getAsString()); + sentEvent.setErrorStatus(json.get("ErrorStatus").getAsString()); + target.getSentEvent().setList(sentEvent); + break; + } + return target; + } +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaDeviceSubscribeServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaDeviceSubscribeServiceImplTest.java new file mode 100644 index 000000000..e1c439054 --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaDeviceSubscribeServiceImplTest.java @@ -0,0 +1,59 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.device.WxMaDeviceSubscribeMessageRequest; +import cn.binarywang.wx.miniapp.bean.device.WxMaDeviceTicketRequest; +import cn.binarywang.wx.miniapp.test.ApiTestModule; +import com.google.common.collect.Lists; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonObject; +import com.google.inject.Inject; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.json.GsonParser; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +/** + * 小程序设备订阅消息相关 测试类 + * + * @author JCLee + * @since 2021-12-16 17:13:35 + */ +@Test +@Guice(modules = ApiTestModule.class) +public class WxMaDeviceSubscribeServiceImplTest { + + @Inject + protected WxMaService wxService; + + @Test + public void testGetSnTicket() throws WxErrorException{ + WxMaDeviceTicketRequest wxMaDeviceTicketRequest = new WxMaDeviceTicketRequest(); + wxMaDeviceTicketRequest.setModelId("11111"); + wxMaDeviceTicketRequest.setSn("11111"); + String snTicket = this.wxService.getDeviceSubscribeService().getSnTicket(wxMaDeviceTicketRequest); + System.out.println(snTicket); + } + + @Test + public void sendDeviceSubscribeMsg() throws WxErrorException{ + WxMaDeviceSubscribeMessageRequest wxMaDeviceSubscribeMessageRequest = new WxMaDeviceSubscribeMessageRequest(); + wxMaDeviceSubscribeMessageRequest.setToOpenidList(Lists.newArrayList("1", "2")); + wxMaDeviceSubscribeMessageRequest.setPage("pages/index/index"); + wxMaDeviceSubscribeMessageRequest.setTemplateId("11111111"); + wxMaDeviceSubscribeMessageRequest.setSn("111111"); + JsonObject data = GsonParser.parse("{\n" + + "\t\t\"thing2\": {\n" + + "\t\t\t\"value\": \"阳台\"\n" + + "\t\t},\n" + + "\t\t\"time1\": {\n" + + "\t\t\t\"value\": \"2021-09-30 13:32:44\"\n" + + "\t\t},\n" + + "\t\t\"thing3\": {\n" + + "\t\t\t\"value\": \"洗衣已完成\"\n" + + "\t\t}\n" + + "\t}"); + wxMaDeviceSubscribeMessageRequest.setData(data); + this.wxService.getDeviceSubscribeService().sendDeviceSubscribeMsg(wxMaDeviceSubscribeMessageRequest); + } +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaImmediateDeliveryServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaImmediateDeliveryServiceImplTest.java new file mode 100644 index 000000000..739bc998f --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaImmediateDeliveryServiceImplTest.java @@ -0,0 +1,203 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.delivery.AbnormalConfirmRequest; +import cn.binarywang.wx.miniapp.bean.delivery.AbnormalConfirmResponse; +import cn.binarywang.wx.miniapp.bean.delivery.AddOrderRequest; +import cn.binarywang.wx.miniapp.bean.delivery.AddOrderResponse; +import cn.binarywang.wx.miniapp.bean.delivery.BindAccountResponse; +import cn.binarywang.wx.miniapp.bean.delivery.CancelOrderRequest; +import cn.binarywang.wx.miniapp.bean.delivery.CancelOrderResponse; +import cn.binarywang.wx.miniapp.bean.delivery.GetOrderRequest; +import cn.binarywang.wx.miniapp.bean.delivery.GetOrderResponse; +import cn.binarywang.wx.miniapp.bean.delivery.MockUpdateOrderRequest; +import cn.binarywang.wx.miniapp.bean.delivery.MockUpdateOrderResponse; +import cn.binarywang.wx.miniapp.test.ApiTestModule; +import com.google.common.collect.Lists; +import com.google.inject.Inject; +import me.chanjar.weixin.common.error.WxErrorException; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import java.math.BigDecimal; + +/** + * 微信小程序即时配送服务测试. + * + * @author Luo + * @version 1.0 + * @date 2021-10-14 11:48 + */ +@Guice(modules = ApiTestModule.class) +public class WxMaImmediateDeliveryServiceImplTest { + + /** + * 对应配送公司的appKey. + */ + private static final String SHOP_ID = "***"; + + /** + * 对应配送公司appSecret. + */ + private static final String APP_SECRET = "****"; + + /** + * 商家门店编号. + */ + private static final String SHOP_NO = "***"; + + /** + * 快递公司Id. + */ + private static final String DELIVERY_ID = "SFTC"; + + @Inject + private WxMaService wxMaService; + + /** + * 测试拉取已绑定账号接口. + * + * @throws WxErrorException 异常 + */ + @Test + public void testGetBindAccount() throws WxErrorException { + BindAccountResponse response = wxMaService.getWxMaImmediateDeliveryService().getBindAccount(); + System.out.println("response = " + response); + } + + /** + * 测试下配送单接口. + * + * @throws WxErrorException 异常 + */ + @Test + public void testAddOrder() throws WxErrorException { + AddOrderRequest request = new AddOrderRequest(); + // 下单用户的openid + request.setOpenid("*****"); + // 微信平台字段,对应配送公司的appkey + request.setShopId(SHOP_ID); + // 对应配送公司appSecret + request.setAppSecret(APP_SECRET); + // 商家门店编号,在配送公司登记,如果只有一个门店,美团闪送必填, 值为店铺id + // 商家对不同门店进行的编号,需要在配送公司系统有过登记,比如商家自己门店系统中有100个门店,编号是1-100,在顺丰同城的系统中有登记过这100个门店,且在顺丰同城登记的编号也是1-100,那么下单的时候传shop_no + // =1,就是编号为1 的门店下的配送单 + request.setShopNo(SHOP_NO); + // 配送公司Id + request.setDeliveryId(DELIVERY_ID); + // 唯一标识订单的 ID,由商户生成, 不超过 128 字节 + String shopOrderId = String.valueOf(System.currentTimeMillis()); + request.setShopOrderId(shopOrderId); + + // 订单信息 + AddOrderRequest.OrderInfo orderInfo = new AddOrderRequest.OrderInfo(); + orderInfo.setOrderTime(System.currentTimeMillis() / 1000L); + request.setOrderInfo(orderInfo); + + // 发件人信息 + AddOrderRequest.Sender sender = new AddOrderRequest.Sender(); + sender.setCity("上海市").setAddress("***").setAddressDetail("****"); + sender.setName("***").setPhone("166****8829"); + sender.setLng(new BigDecimal("121.281379")).setLat(new BigDecimal("31.049363")); + request.setSender(sender); + + // 收件人信息 + AddOrderRequest.Receiver receiver = new AddOrderRequest.Receiver().setCoordinateType(1); + receiver.setCity("北京市").setAddress("海淀区").setAddressDetail("北京市海淀区学清嘉创大厦A座15层"); + receiver.setName("顺丰同城").setPhone("166****8829"); + receiver.setLng(new BigDecimal("116.359442")).setLat(new BigDecimal("40.020407")); + request.setReceiver(receiver); + + // 商品信息 + AddOrderRequest.Cargo cargo = new AddOrderRequest.Cargo(); + cargo.setCargoFirstClass("电商").setCargoSecondClass("线上商城"); + cargo.setGoodsHeight(BigDecimal.valueOf(1)).setGoodsLength(BigDecimal.valueOf(3)); + cargo.setGoodsValue(BigDecimal.valueOf(5)).setGoodsWeight(BigDecimal.valueOf(1)).setGoodsWidth(BigDecimal.valueOf(2)); + // 商品列表 + AddOrderRequest.Cargo.GoodsDetail goodsDetail = new AddOrderRequest.Cargo.GoodsDetail(); + AddOrderRequest.Cargo.GoodsDetail.Goods goods1 = new AddOrderRequest.Cargo.GoodsDetail.Goods(); + goods1.setGoodCount(1).setGoodName("水果").setGoodPrice(new BigDecimal(10)); + AddOrderRequest.Cargo.GoodsDetail.Goods goods2 = new AddOrderRequest.Cargo.GoodsDetail.Goods(); + goods2.setGoodCount(2).setGoodName("蔬菜").setGoodPrice(new BigDecimal(20)); + goodsDetail.setGoods(Lists.newArrayList(goods1, goods2)); + cargo.setGoodsDetail(goodsDetail); + request.setCargo(cargo); + + // 店铺信息 + AddOrderRequest.Shop shop = new AddOrderRequest.Shop(); + int sum = + request.getCargo().getGoodsDetail().getGoods().stream().mapToInt(AddOrderRequest.Cargo.GoodsDetail.Goods::getGoodCount).sum(); + shop.setGoodsCount(sum).setGoodsName("商品"); + shop.setImgUrl("https://").setWxaPath("pages/index/index"); + request.setShop(shop); + + AddOrderResponse response = wxMaService.getWxMaImmediateDeliveryService().addOrder(request); + System.out.println("response = " + response); + + } + + /** + * 测试拉取配送单信息接口. + * + * @throws WxErrorException 异常 + */ + @Test + public void testGetOrder() throws WxErrorException { + GetOrderRequest request = new GetOrderRequest(); + request.setShopId(SHOP_ID).setShopNo(SHOP_NO).setAppSecret(APP_SECRET); + request.setShopOrderId("1561399675737608193"); + GetOrderResponse response = wxMaService.getWxMaImmediateDeliveryService().getOrder(request); + System.out.println("response = " + response); + } + + /** + * 测试取消配送单信息接口. + * + * @throws WxErrorException 异常 + */ + @Test + public void testCancelOrder() throws WxErrorException { + CancelOrderRequest request = new CancelOrderRequest(); + request.setShopId(SHOP_ID).setShopNo(SHOP_NO).setAppSecret(APP_SECRET); + request.setDeliveryId(DELIVERY_ID); + request.setCancelReasonId(1); + request.setShopOrderId("1560365275348471809"); + request.setWaybillId("3427365636312065025"); + CancelOrderResponse response = wxMaService.getWxMaImmediateDeliveryService().cancelOrder(request); + System.out.println("response = " + response); + } + + /** + * 测试异常件退回商家商家确认收货接口. + * + * @throws WxErrorException 异常 + */ + @Test + public void testAbnormalConfirm() throws WxErrorException { + AbnormalConfirmRequest request = new AbnormalConfirmRequest(); + request.setShopId(SHOP_ID).setShopNo(SHOP_NO).setAppSecret(APP_SECRET); + request.setDeliveryId(DELIVERY_ID); + request.setShopOrderId("1561399675737608193"); + request.setWaybillId("3427882855372591617"); + request.setRemark("测试签收异常订单"); + AbnormalConfirmResponse response = wxMaService.getWxMaImmediateDeliveryService().abnormalConfirm(request); + System.out.println("response = " + response); + } + + /** + * 测试模拟配送公司更新配送单状态接口. + * + * @throws WxErrorException 异常 + */ + @Test + public void testMockUpdateOrder() throws WxErrorException { + // 请求参数 + MockUpdateOrderRequest request = new MockUpdateOrderRequest(); + request.setActionTime(System.currentTimeMillis() / 1000L); + request.setOrderStatus(102); + request.setShopOrderId(""); + MockUpdateOrderResponse response = wxMaService.getWxMaImmediateDeliveryService().mockUpdateOrder(request); + System.out.println("response = " + response); + } + +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaInternetServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaInternetServiceImplTest.java index ccc2f9c93..9b1ffa167 100644 --- a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaInternetServiceImplTest.java +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaInternetServiceImplTest.java @@ -4,12 +4,15 @@ import cn.binarywang.wx.miniapp.bean.internet.WxMaInternetResponse; import cn.binarywang.wx.miniapp.test.ApiTestModule; import com.google.inject.Inject; -import me.chanjar.weixin.common.error.WxErrorException; import org.testng.annotations.Guice; import org.testng.annotations.Test; +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; + +import static org.assertj.core.api.Assertions.assertThat; + /** - * * 服务端网络相关接口测试 * * @author chutian0124 @@ -21,9 +24,32 @@ public class WxMaInternetServiceImplTest { @Inject private WxMaService wxService; + private static String HMACSHA256(String data, String key) throws Exception { + Mac sha256_HMAC = Mac.getInstance("HmacSHA256"); + SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256"); + sha256_HMAC.init(secret_key); + byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8")); + StringBuilder sb = new StringBuilder(); + for (byte item : array) { + sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3)); + } + return sb.toString().toUpperCase(); + } + + @Test + public void testGetUserEncryptKey() throws Exception { + String openid = "ogu-84hVFTbTt-myGisQESoDJ6BM"; + String signature = HMACSHA256("", "9ny8n3t0KULoi0deF7T9pw=="); + String sigMethod = "hmac_sha256"; + WxMaInternetResponse response = this.wxService.getInternetService().getUserEncryptKey(openid, signature, sigMethod); + assertThat(response).isNotNull(); + } + @Test - public void testGetUserEncryptKey() throws WxErrorException { - WxMaInternetResponse response = this.wxService.getInternetService().getUserEncryptKey(); - System.out.println(response); + public void testGetUserEncryptKey2() throws Exception { + String openid = "ogu-84hVFTbTt-myGisQESoDJ6BM"; + String sessionKey = "9ny8n3t0KULoi0deF7T9pw=="; + WxMaInternetResponse response = this.wxService.getInternetService().getUserEncryptKey(openid, sessionKey); + assertThat(response).isNotNull(); } } diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaLinkServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaLinkServiceImplTest.java index c97e11077..8774affc0 100644 --- a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaLinkServiceImplTest.java +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaLinkServiceImplTest.java @@ -5,12 +5,14 @@ import cn.binarywang.wx.miniapp.bean.urllink.GenerateUrlLinkRequest; import cn.binarywang.wx.miniapp.test.ApiTestModule; import com.google.inject.Inject; +import lombok.extern.slf4j.Slf4j; import me.chanjar.weixin.common.error.WxErrorException; import org.testng.annotations.Guice; import org.testng.annotations.Test; @Test @Guice(modules = ApiTestModule.class) +@Slf4j public class WxMaLinkServiceImplTest { @Inject private WxMaService wxMaService; @@ -34,4 +36,17 @@ public void testGenerateShortLink() throws WxErrorException { System.out.println("generate:"); System.out.println(generate); } + + /** + * 多版本链接生成测试 + * 开发时,仅支持IOS设备打开体验版及开发版 + */ + @Test + public void testGenerateMultiEnvUrlLink() throws WxErrorException { + String url = this.wxMaService.getLinkService().generateUrlLink(GenerateUrlLinkRequest.builder() + .path("") + .envVersion("trial") + .build()); + log.info("generate url link = {}", url); + } } diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaQrcodeServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaQrcodeServiceImplTest.java index e6c596944..d45651ba2 100644 --- a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaQrcodeServiceImplTest.java +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaQrcodeServiceImplTest.java @@ -1,13 +1,13 @@ 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 org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import java.io.File; import static org.assertj.core.api.Assertions.assertThat; @@ -46,13 +46,13 @@ public void testCreateQrcodeBytes() throws WxErrorException { @Test public void testCreateWxaCodeBytes() throws WxErrorException { - final byte[] wxCode = this.wxService.getQrcodeService().createWxaCodeBytes("111", 122, true, null, false); + final byte[] wxCode = this.wxService.getQrcodeService().createWxaCodeBytes("111", null, 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); + final byte[] wxCode = this.wxService.getQrcodeService().createWxaCodeUnlimitBytes("111", "pages/unknown", false, "trial", 122, true, null, false); assertThat(wxCode).isNotNull(); } @@ -70,7 +70,7 @@ public void testCreateWxaCodeByFile() throws WxErrorException { @Test public void testCreateQrcodeUnlimitByFile() throws WxErrorException { - final File wxCode = this.wxService.getQrcodeService().createWxaCodeUnlimit("111",null,"/opt/logs"); + final File wxCode = this.wxService.getQrcodeService().createWxaCodeUnlimit("111", null, "/opt/logs"); assertThat(wxCode).isNotNull(); } } diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaSafetyRiskControlServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaSafetyRiskControlServiceImplTest.java new file mode 100644 index 000000000..9a2491fee --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaSafetyRiskControlServiceImplTest.java @@ -0,0 +1,34 @@ +package cn.binarywang.wx.miniapp.api.impl; + + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.safety.request.WxMaUserSafetyRiskRankRequest; +import cn.binarywang.wx.miniapp.bean.safety.response.WxMaUserSafetyRiskRankResponse; +import cn.binarywang.wx.miniapp.test.ApiTestModule; +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.AssertJUnit.assertNotNull; + +@Test +@Guice(modules = ApiTestModule.class) +public class WxMaSafetyRiskControlServiceImplTest { + + @Inject + protected WxMaService wxService; + + @Test + public void testGetUserRiskRank() throws WxErrorException { + WxMaUserSafetyRiskRankRequest wxMaUserSafetyRiskRankRequest = WxMaUserSafetyRiskRankRequest.builder() + .appid("") + .openid("") + .scene(1) + .isTest(true) + .build(); + WxMaUserSafetyRiskRankResponse wxMaUserSafetyRiskRankResponse = this.wxService.getSafetyRiskControlService().getUserRiskRank(wxMaUserSafetyRiskRankRequest); + assertNotNull(wxMaUserSafetyRiskRankResponse); + } + +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaShopImgServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaShopImgServiceImplTest.java index 060896ab0..9999c14ab 100644 --- a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaShopImgServiceImplTest.java +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaShopImgServiceImplTest.java @@ -35,4 +35,11 @@ public void testUploadImg2() throws WxErrorException { WxMinishopImageUploadCustomizeResult result = wxService.getShopImgService().uploadImg(file, "1"); assertThat(result).isNotNull(); } + + @Test + public void testUploadImg3() throws WxErrorException { + String imgUrl = "https://www.example.com/demo.jpg"; + WxMinishopImageUploadCustomizeResult result = wxService.getShopImgService().uploadImg(imgUrl, "1"); + assertThat(result).isNotNull(); + } } diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImplTest.java index fe3fa1675..5b04d05f8 100644 --- a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImplTest.java +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImplTest.java @@ -71,4 +71,14 @@ public void testSetUserStorage() throws WxErrorException { this.wxService.getUserService().setUserStorage(ImmutableMap.of("1","2"), "r7BXXKkLb8qrSNn05n0qiA",((TestConfig)this.wxService.getWxMaConfig()).getOpenid()); } + + @Test + public void testGetNewPhoneNoInfo() throws Exception{ + assertNotNull(wxService.getUserService().getNewPhoneNoInfo("test")); + } + + @Test + public void testGetAccessToken() throws Exception{ + assertNotNull(wxService.getAccessToken(true)); + } } diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/WxMaMessageTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/WxMaMessageTest.java index 26855b36e..098da74e5 100644 --- a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/WxMaMessageTest.java +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/WxMaMessageTest.java @@ -3,7 +3,12 @@ import me.chanjar.weixin.common.api.WxConsts; import org.testng.annotations.Test; +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; /** * @author Binary Wang @@ -32,7 +37,7 @@ public void testFromXml() { WxMaMessage wxMessage = WxMaMessage.fromXml(xml); assertEquals(wxMessage.getToUser(), "toUser"); assertEquals(wxMessage.getFromUser(), "fromUser"); - assertEquals(wxMessage.getCreateTime(),new Integer(1482048670)); + assertEquals(wxMessage.getCreateTime(), new Integer(1482048670)); assertEquals(wxMessage.getMsgType(), WxConsts.XmlMsgType.TEXT); assertEquals(wxMessage.getContent(), "this is a test"); assertEquals(wxMessage.getMsgId(), new Long(1234567890123456L)); @@ -47,4 +52,243 @@ public void testFromXml() { assertEquals(wxMessage.getSessionFrom(), "sessionFrom"); } + public void testSubscribeMsgPopupEvent() { + // xml 格式 + String xml = "" + + "\n" + + "\n" + + "1610969440\n" + + "\n" + + "\n" + + "\n" + + " \n" + + " \n" + + " \n" + + " 0\n" + + " \n" + + "" + + ""; + + WxMaMessage wxMessage = WxMaMessage.fromXml(xml); + checkSubscribeMsgPopupEvent(wxMessage); + + // 订阅单个模板 json格式 (对象) + String json = "{\n" + + " \"ToUserName\": \"gh_123456789abc\",\n" + + " \"FromUserName\": \"otFpruAK8D-E6EfStSYonYSBZ8_4\",\n" + + " \"CreateTime\": \"1610969440\",\n" + + " \"MsgType\": \"event\",\n" + + " \"Event\": \"subscribe_msg_popup_event\",\n" + + " \"List\": {\n" + + " \"TemplateId\": \"VRR0UEO9VJOLs0MHlU0OilqX6MVFDwH3_3gz3Oc0NIc\",\n" + + " \"SubscribeStatusString\": \"accept\",\n" + + " \"PopupScene\": \"0\"\n" + + " }\n" + + " }"; + wxMessage = WxMaMessage.fromJson(json); + checkSubscribeMsgPopupEvent(wxMessage); + // 订阅多条模板的 json格式(数组) + json = "{\n" + + " \"ToUserName\": \"gh_123456789abc\",\n" + + " \"FromUserName\": \"otFpruAK8D-E6EfStSYonYSBZ8_4\",\n" + + " \"CreateTime\": \"1610969440\",\n" + + " \"MsgType\": \"event\",\n" + + " \"Event\": \"subscribe_msg_popup_event\",\n" + + " \"List\": [{\n" + + " \"TemplateId\": \"VRR0UEO9VJOLs0MHlU0OilqX6MVFDwH3_3gz3Oc0NIc\",\n" + + " \"SubscribeStatusString\": \"accept\",\n" + + " \"PopupScene\": \"0\"\n" + + " }]\n" + + " }"; + wxMessage = WxMaMessage.fromJson(json); + checkSubscribeMsgPopupEvent(wxMessage); + } + + private void checkSubscribeMsgPopupEvent(WxMaMessage wxMessage) { + assertEquals(wxMessage.getToUser(), "gh_123456789abc"); + assertEquals(wxMessage.getFromUser(), "otFpruAK8D-E6EfStSYonYSBZ8_4"); + assertEquals(wxMessage.getCreateTime(), new Integer(1610969440)); + assertEquals(wxMessage.getMsgType(), WxConsts.XmlMsgType.EVENT); + assertEquals(wxMessage.getEvent(), "subscribe_msg_popup_event"); + assertEquals(wxMessage.getSubscribeMsgPopupEvent().getList().size(), 1); + WxMaSubscribeMsgEvent.PopupEvent event = wxMessage.getSubscribeMsgPopupEvent().getList().get(0); + assertEquals(event.getTemplateId(), "VRR0UEO9VJOLs0MHlU0OilqX6MVFDwH3_3gz3Oc0NIc"); + assertEquals(event.getSubscribeStatusString(), "accept"); + assertEquals(event.getPopupScene(), "0"); + } + + public void testSubscribeMsgChangeEvent() { + // xml 格式 + String xml = "\n" + + " \n" + + " \n" + + " 1610968440\n" + + " \n" + + " \n" + + " \n" + + " " + + " \n" + + " \n" + + " \n" + + " \n" + + ""; + + WxMaMessage wxMessage = WxMaMessage.fromXml(xml); + checkSubscribeMsgChangeEvent(wxMessage); + + // json格式 (对象) + String json = "{\n" + + " \"ToUserName\": \"gh_123456789abc\",\n" + + " \"FromUserName\": \"o7esq5OI1Uej6Xixw1lA2H7XDVbc\",\n" + + " \"CreateTime\": \"1610968440\",\n" + + " \"MsgType\": \"event\",\n" + + " \"Event\": \"subscribe_msg_change_event\",\n" + + " \"List\": {\n" + + " \"TemplateId\":\"BEwX0BOT3MqK3Uc5oTU3CGBqzjpndk2jzUf7VfExd8\",\n" + + " \"SubscribeStatusString\": \"reject\"\n" + + " }\n" + + "}\n"; + wxMessage = WxMaMessage.fromJson(json); + checkSubscribeMsgChangeEvent(wxMessage); + // json格式(数组) + json = "{\n" + + " \"ToUserName\": \"gh_123456789abc\",\n" + + " \"FromUserName\": \"o7esq5OI1Uej6Xixw1lA2H7XDVbc\",\n" + + " \"CreateTime\": \"1610968440\",\n" + + " \"MsgType\": \"event\",\n" + + " \"Event\": \"subscribe_msg_change_event\",\n" + + " \"List\": [ {\n" + + " \"TemplateId\":\"BEwX0BOT3MqK3Uc5oTU3CGBqzjpndk2jzUf7VfExd8\",\n" + + " \"SubscribeStatusString\": \"reject\"\n" + + " }]" + + "}"; + wxMessage = WxMaMessage.fromJson(json); + checkSubscribeMsgChangeEvent(wxMessage); + } + + private void checkSubscribeMsgChangeEvent(WxMaMessage wxMessage) { + assertEquals(wxMessage.getToUser(), "gh_123456789abc"); + assertEquals(wxMessage.getFromUser(), "o7esq5OI1Uej6Xixw1lA2H7XDVbc"); + assertEquals(wxMessage.getCreateTime(), new Integer(1610968440)); + assertEquals(wxMessage.getMsgType(), WxConsts.XmlMsgType.EVENT); + assertEquals(wxMessage.getEvent(), "subscribe_msg_change_event"); + assertEquals(wxMessage.getSubscribeMsgChangeEvent().getList().size(), 1); + WxMaSubscribeMsgEvent.ChangeEvent event = wxMessage.getSubscribeMsgChangeEvent().getList().get(0); + assertEquals(event.getTemplateId(), "BEwX0BOT3MqK3Uc5oTU3CGBqzjpndk2jzUf7VfExd8"); + assertEquals(event.getSubscribeStatusString(), "reject"); + } + + public void testSubscribeMsgSentEvent() { + // xml 格式 + String xml = "\n" + + " \n" + + " \n" + + " 1620963428\n" + + " \n" + + " \n" + + " \n" + + " " + + " \n" + + " 1864323726461255680\n" + + " 0\n" + + " \n" + + " \n" + + " \n" + + ""; + + WxMaMessage wxMessage = WxMaMessage.fromXml(xml); + checkSubscribeMsgSentEvent(wxMessage); + + // json格式 (对象) + String json = "{\n" + + " \"ToUserName\": \"gh_123456789abc\",\n" + + " \"FromUserName\": \"o7esq5PHRGBQYmeNyfG064wEFVpQ\",\n" + + " \"CreateTime\": \"1620963428\",\n" + + " \"MsgType\": \"event\",\n" + + " \"Event\": \"subscribe_msg_sent_event\",\n" + + " \"List\": {\n" + + " \"TemplateId\": \"BEwX0BO-T3MqK3Uc5oTU3CGBqzjpndk2jzUf7VfExd8\",\n" + + " \"MsgID\": \"1864323726461255680\",\n" + + " \"ErrorCode\": \"0\",\n" + + " \"ErrorStatus\": \"success\"\n" + + " }\n" + + "}"; + wxMessage = WxMaMessage.fromJson(json); + checkSubscribeMsgSentEvent(wxMessage); + } + + private void checkSubscribeMsgSentEvent(WxMaMessage wxMessage) { + assertEquals(wxMessage.getToUser(), "gh_123456789abc"); + assertEquals(wxMessage.getFromUser(), "o7esq5PHRGBQYmeNyfG064wEFVpQ"); + assertEquals(wxMessage.getCreateTime(), new Integer(1620963428)); + assertEquals(wxMessage.getMsgType(), WxConsts.XmlMsgType.EVENT); + assertEquals(wxMessage.getEvent(), "subscribe_msg_sent_event"); + assertNotNull(wxMessage.getSubscribeMsgSentEvent()); + WxMaSubscribeMsgEvent.SentEvent event = wxMessage.getSubscribeMsgSentEvent().getList(); + assertEquals(event.getTemplateId(), "BEwX0BO-T3MqK3Uc5oTU3CGBqzjpndk2jzUf7VfExd8"); + assertEquals(event.getMsgId(), "1864323726461255680"); + assertEquals(event.getErrorCode(), "0"); + assertEquals(event.getErrorStatus(), "success"); + } + + @Test + public void testFromXmlForAllFieldsMap() { + String xml = "\n" + + " \n" + + " \n" + + " 1642658087\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " 16\n" + + " 2\n" + + " \n" + + " 1642605533\n" + + " 300001\n" + + " \n" + + " 0\n" + + " 0\n" + + " \n" + + " \n" + + " 1642605533\n" + + " 100001\n" + + " \n" + + " 0\n" + + " 0\n" + + " \n" + + " \n" + + ""; + + WxMaMessage wxMessage = WxMaMessage.fromXml(xml); + Map allFieldsMap = wxMessage.getAllFieldsMap(); + assertThat(allFieldsMap).isNotEmpty() + .containsEntry("ToUserName", "gh_3953b390c11d") + .containsEntry("FromUserName", "ofYMP5JFT4SD7EX1LQv3IWrciBSo") + .containsEntry("CreateTime", "1642658087") + .containsEntry("MsgType", "event") + .containsEntry("Event", "add_express_path") + .containsEntry("DeliveryID", "TEST") + .containsEntry("WayBillId", "01234567894_waybill_id") + .containsEntry("Version", "16") + .containsEntry("Count", "2") + .containsEntry("OrderId", "01234567894"); + + List> actions = (List>) allFieldsMap.get("Actions"); + assertThat(actions).isNotEmpty().hasSize(2); + + assertThat(actions.get(0)) + .containsEntry("ActionTime", "1642605533") + .containsEntry("ActionType", "300001") + .containsEntry("ActionMsg", "揽件阶段-揽件成功") + .containsEntry("Lat", "0") + .containsEntry("Lng", "0"); + + assertThat(actions.get(1)) + .containsEntry("ActionTime", "1642605533") + .containsEntry("ActionType", "100001") + .containsEntry("ActionMsg", "揽件阶段-揽件成功") + .containsEntry("Lat", "0") + .containsEntry("Lng", "0"); + } } diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeSubmitAuditRequestTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeSubmitAuditRequestTest.java index 1f962087d..4567f4275 100644 --- a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeSubmitAuditRequestTest.java +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeSubmitAuditRequestTest.java @@ -1,9 +1,12 @@ package cn.binarywang.wx.miniapp.bean.code; +import me.chanjar.weixin.common.util.json.GsonParser; import org.testng.annotations.Test; import java.util.Arrays; +import static org.assertj.core.api.Assertions.assertThat; + /** * @author Charming * @since 2018-04-26 19:55 @@ -11,20 +14,84 @@ public class WxMaCodeSubmitAuditRequestTest { @Test public void testToJson() { - WxMaCodeSubmitAuditRequest request = WxMaCodeSubmitAuditRequest - .builder() + WxMaCodeSubmitAuditRequest request = WxMaCodeSubmitAuditRequest.builder() .itemList(Arrays.asList( - WxMaCategory - .builder() - .address("pages/logs/logs") - .tag("工具 效率") - .firstClass("工具") - .firstId(287L) - .secondClass("效率") - .secondId(616L) + WxMaCategory.builder() + .address("index") + .tag("学习 生活") + .firstClass("文娱") + .firstId(1L) + .secondClass("资讯") + .secondId(2L) + .title("首页") + .build(), + WxMaCategory.builder() + .address("page/logs/logs") + .tag("学习 工作") + .firstClass("教育") + .firstId(3L) + .secondClass("学历教育") + .secondId(4L) + .thirdClass("高等") + .thirdId(5L) .title("日志") .build() - )).build(); - System.out.println(request.toJson()); + )) + .feedbackInfo("blablabla") + .feedbackStuff("xx|yy|zz") + .previewInfo(new WxMaCodeSubmitAuditRequest.PreviewInfo().setVideoIdList(Arrays.asList("xxxx")) + .setPicIdList(Arrays.asList("xxxx", "yyyy", "zzzz"))) + .versionDesc("blablabla") + .ugcDeclare(new WxMaCodeSubmitAuditRequest.UgcDeclare() + .setAuditDesc("blablabla") + .setHasAuditTeam(1) + .setMethod(new Integer[]{1}) + .setScene(new Integer[]{1, 2}) + ).build(); + + String expectedJson = "{\n" + + "\t\"item_list\": [\n" + + "\t{\n" + + "\t\t\"address\":\"index\",\n" + + "\t\t\"tag\":\"学习 生活\",\n" + + "\t\t\"first_class\": \"文娱\",\n" + + "\t\t\"second_class\": \"资讯\",\n" + + "\t\t\"first_id\":1,\n" + + "\t\t\"second_id\":2,\n" + + "\t\t\"title\": \"首页\"\n" + + "\t},\n" + + "\t{\n" + + "\t\t\"address\":\"page/logs/logs\",\n" + + "\t\t\"tag\":\"学习 工作\",\n" + + "\t\t\"first_class\": \"教育\",\n" + + "\t\t\"second_class\": \"学历教育\",\n" + + "\t\t\"third_class\": \"高等\",\n" + + "\t\t\"first_id\":3,\n" + + "\t\t\"second_id\":4,\n" + + "\t\t\"third_id\":5,\n" + + "\t\t\"title\": \"日志\"\n" + + "\t}\n" + + "\t],\n" + + "\t\"feedback_info\": \"blablabla\",\n" + + " \"feedback_stuff\": \"xx|yy|zz\",\n" + + " \"preview_info\" : {\n" + + " \"video_id_list\": [\"xxxx\"],\n" + + " \"pic_id_list\": [\"xxxx\", \"yyyy\", \"zzzz\" ]\n" + + " },\n" + + " \"version_desc\":\"blablabla\",\n" + + " \"ugc_declare\": {\n" + + " \"scene\": [\n" + + " 1,\n" + + " 2\n" + + " ],\n" + + " \"method\": [\n" + + " 1\n" + + " ],\n" + + " \"has_audit_team\": 1,\n" + + " \"audit_desc\": \"blablabla\"\n" + + " }\n" + + "}\n" + + ""; + assertThat(request.toJson().replace("\n", "")).isEqualTo(GsonParser.parse(expectedJson).toString()); } } diff --git a/weixin-java-mp/pom.xml b/weixin-java-mp/pom.xml index 86b562160..e8c924c0d 100644 --- a/weixin-java-mp/pom.xml +++ b/weixin-java-mp/pom.xml @@ -7,7 +7,7 @@ com.github.binarywang wx-java - 4.2.0 + 4.3.0 weixin-java-mp @@ -84,6 +84,11 @@ org.redisson redisson + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + true + diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMaterialService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMaterialService.java index afccd004e..1f7e8d3a2 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMaterialService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMaterialService.java @@ -165,7 +165,9 @@ public interface WxMpMaterialService { * @param news 上传的图文消息, 请看{@link WxMpMaterialNews} * @return the wx mp material upload result * @throws WxErrorException the wx error exception + * @deprecated 关于永久图文素材相关接口下线的公告: https://mp.weixin.qq.com/cgi-bin/announce?action=getannouncement&announce_id=11644831863qFQSh&version=&token=2085564289&lang=zh_CN */ + @Deprecated WxMpMaterialUploadResult materialNewsUpload(WxMpMaterialNews news) throws WxErrorException; /** @@ -221,7 +223,9 @@ public interface WxMpMaterialService { * @param wxMpMaterialArticleUpdate 用来更新图文素材的bean, 请看{@link WxMpMaterialArticleUpdate} * @return the boolean * @throws WxErrorException the wx error exception + * @deprecated 关于永久图文素材相关接口下线的公告: https://mp.weixin.qq.com/cgi-bin/announce?action=getannouncement&announce_id=11644831863qFQSh&version=&token=2085564289&lang=zh_CN */ + @Deprecated boolean materialNewsUpdate(WxMpMaterialArticleUpdate wxMpMaterialArticleUpdate) throws WxErrorException; /** diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/BaseWxMpServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/BaseWxMpServiceImpl.java index d11499bd4..e12e30493 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/BaseWxMpServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/BaseWxMpServiceImpl.java @@ -25,11 +25,7 @@ import me.chanjar.weixin.common.util.DataUtils; import me.chanjar.weixin.common.util.RandomUtils; import me.chanjar.weixin.common.util.crypto.SHA1; -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.common.util.http.URIUtil; +import me.chanjar.weixin.common.util.http.*; import me.chanjar.weixin.common.util.json.GsonParser; import me.chanjar.weixin.common.util.json.WxGsonBuilder; import me.chanjar.weixin.mp.api.*; @@ -46,16 +42,7 @@ import java.util.Map; import java.util.concurrent.locks.Lock; -import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Other.CLEAR_QUOTA_URL; -import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Other.FETCH_SHORTEN_URL; -import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Other.GEN_SHORTEN_URL; -import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Other.GET_CALLBACK_IP_URL; -import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Other.GET_CURRENT_AUTOREPLY_INFO_URL; -import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Other.GET_TICKET_URL; -import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Other.NETCHECK_URL; -import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Other.QRCONNECT_URL; -import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Other.SEMANTIC_SEMPROXY_SEARCH_URL; -import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Other.SHORTURL_API_URL; +import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Other.*; /** * 基础实现类. @@ -482,6 +469,10 @@ protected String extractAccessToken(String resultContent) throws WxErrorExceptio @Override public void setWxMpConfigStorage(WxMpConfigStorage wxConfigProvider) { final String defaultMpId = wxConfigProvider.getAppId(); + if (defaultMpId == null) { + throw new WxRuntimeException("appid不能设置为null"); + } + this.setMultiConfigStorages(ImmutableMap.of(defaultMpId, wxConfigProvider), defaultMpId); } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpDraftServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpDraftServiceImpl.java index 96ff9f70b..fb173b1eb 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpDraftServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpDraftServiceImpl.java @@ -43,13 +43,13 @@ public String addDraft(String title, String content, String thumbMediaId) throws @Override public String addDraft(WxMpAddDraft addDraft) throws WxErrorException { String json = this.mpService.post(WxMpApiUrl.Draft.ADD_DRAFT, addDraft); - return GsonParser.parse(json).get(MEDIA_ID).toString(); + return GsonParser.parse(json).get(MEDIA_ID).getAsString(); } @Override public Boolean updateDraft(WxMpUpdateDraft updateDraftInfo) throws WxErrorException { String json = this.mpService.post(WxMpApiUrl.Draft.UPDATE_DRAFT, updateDraftInfo); - return GsonParser.parse(json).get(ERRCODE).toString().equals(ERRCODE_SUCCESS); + return GsonParser.parse(json).get(ERRCODE).getAsString().equals(ERRCODE_SUCCESS); } @Override @@ -62,7 +62,7 @@ public WxMpDraftInfo getDraft(String mediaId) throws WxErrorException { public Boolean delDraft(String mediaId) throws WxErrorException { String json = this.mpService.post(WxMpApiUrl.Draft.DEL_DRAFT, GsonHelper.buildJsonObject(MEDIA_ID, mediaId)); - return GsonParser.parse(json).get(ERRCODE).toString().equals(ERRCODE_SUCCESS); + return GsonParser.parse(json).get(ERRCODE).getAsString().equals(ERRCODE_SUCCESS); } @Override diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/draft/WxMpDraftArticles.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/draft/WxMpDraftArticles.java index fdcff5c29..2be78d6ab 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/draft/WxMpDraftArticles.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/draft/WxMpDraftArticles.java @@ -68,6 +68,16 @@ public class WxMpDraftArticles implements ToJson, Serializable { */ @SerializedName("only_fans_can_comment") private Integer onlyFansCanComment; + /** + * 草稿的临时链接,点击图文消息跳转链接 + */ + @SerializedName("url") + private String url; + /** + * 图文消息的封面url + */ + @SerializedName("thumb_url") + private String thumbUrl; public static WxMpDraftArticles fromJson(String json) { return WxGsonBuilder.create().fromJson(json, WxMpDraftArticles.class); diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/draft/WxMpDraftItem.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/draft/WxMpDraftItem.java index 05294cade..0ae42b17f 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/draft/WxMpDraftItem.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/draft/WxMpDraftItem.java @@ -27,6 +27,12 @@ public class WxMpDraftItem implements Serializable { @SerializedName("content") private WxMpDraftInfo content; + /** + * 本草稿的图文消息素材的最后更新时间 + */ + @SerializedName("update_time") + private Long updateTime; + public static WxMpDraftItem fromJson(String json) { return WxGsonBuilder.create().fromJson(json, WxMpDraftItem.class); } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/freepublish/WxMpFreePublishArticles.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/freepublish/WxMpFreePublishArticles.java index 20f915f19..3c378934e 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/freepublish/WxMpFreePublishArticles.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/freepublish/WxMpFreePublishArticles.java @@ -64,6 +64,12 @@ public class WxMpFreePublishArticles implements Serializable { @SerializedName("only_fans_can_comment") private Integer onlyFansCanComment; + /** + * 图文消息的封面url + */ + @SerializedName("thumb_url") + private String thumbUrl; + /* * ===== 上面的参数,就是草稿箱的内容的字段,为了后续扩展,单独写一份==== */ diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/freepublish/WxMpFreePublishItem.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/freepublish/WxMpFreePublishItem.java index f32b34a9f..dfe953e5b 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/freepublish/WxMpFreePublishItem.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/freepublish/WxMpFreePublishItem.java @@ -23,12 +23,19 @@ public class WxMpFreePublishItem implements Serializable { */ @SerializedName("article_id") private String articleId; + /** * 图文消息的具体内容,支持HTML标签,必须少于2万字符,小于1M,且此处会去除JS。 */ @SerializedName("content") private WxMpFreePublishInfo content; + /** + * 这篇图文消息素材的最后更新时间 + */ + @SerializedName("update_time") + private String updateTime; + public static WxMpFreePublishItem fromJson(String json) { return WxGsonBuilder.create().fromJson(json, WxMpFreePublishItem.class); } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/freepublish/WxMpFreePublishStatus.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/freepublish/WxMpFreePublishStatus.java index 573412396..033d300cb 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/freepublish/WxMpFreePublishStatus.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/freepublish/WxMpFreePublishStatus.java @@ -42,13 +42,15 @@ public String toJson() { @NoArgsConstructor @Data - public static class ArticleDetail { + public static class ArticleDetail implements Serializable{ + private static final long serialVersionUID = 2802949203075628412L; private Integer count; private List item; @NoArgsConstructor @Data - public static class Item { + public static class Item implements Serializable{ + private static final long serialVersionUID = -6496102084844816489L; private Integer idx; private String article_url; } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/ClearOutInvoiceRequest.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/ClearOutInvoiceRequest.java index 108e76ca2..d0d0f6a9a 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/ClearOutInvoiceRequest.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/ClearOutInvoiceRequest.java @@ -9,8 +9,6 @@ */ @Data public class ClearOutInvoiceRequest implements Serializable { - - private ClearOutInvoiceInfo invoiceinfo; @Data diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/InvoiceRejectRequest.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/InvoiceRejectRequest.java index 9048ceb05..0d48cd79f 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/InvoiceRejectRequest.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/InvoiceRejectRequest.java @@ -1,10 +1,13 @@ package me.chanjar.weixin.mp.bean.invoice.merchant; +import lombok.Data; + import java.io.Serializable; /** * 拒绝开票请求参数 */ +@Data public class InvoiceRejectRequest implements Serializable { /** diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/MerchantInvoicePlatformInfo.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/MerchantInvoicePlatformInfo.java index f7a64edfc..9c54b82c1 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/MerchantInvoicePlatformInfo.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/invoice/merchant/MerchantInvoicePlatformInfo.java @@ -1,10 +1,13 @@ package me.chanjar.weixin.mp.bean.invoice.merchant; +import lombok.Data; + import java.io.Serializable; /** * 商户的开票平台信息 */ +@Data public class MerchantInvoicePlatformInfo implements Serializable { /** diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/WxMpKefuMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/WxMpKefuMessage.java index 37beb77e7..7ac8bee4f 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/WxMpKefuMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/WxMpKefuMessage.java @@ -37,6 +37,7 @@ public class WxMpKefuMessage implements Serializable { private String headContent; private String tailContent; private List articles = new ArrayList<>(); + private String mpNewsArticleId; /** * 菜单消息里的菜单内容. @@ -113,6 +114,13 @@ public static MiniProgramPageBuilder MINIPROGRAMPAGE() { return new MiniProgramPageBuilder(); } + /** + * 发送图文消息(点击跳转到图文消息页面)使用通过 “发布” 系列接口得到的 article_id(草稿箱功能上线后不再支持客服接口中带 media_id 的 mpnews 类型的图文消息) + */ + public static MpNewsArticleBuilder MPNEWSARTICLE() { + return new MpNewsArticleBuilder(); + } + /** *
    * 请使用
@@ -127,6 +135,7 @@ public static MiniProgramPageBuilder MINIPROGRAMPAGE() {
    * {@link WxConsts.KefuMsgType#MINIPROGRAMPAGE}
    * {@link WxConsts.KefuMsgType#TASKCARD}
    * {@link WxConsts.KefuMsgType#MSGMENU}
+   * {@link WxConsts.KefuMsgType#MP_NEWS_ARTICLE}
    * 
*/ public void setMsgType(String msgType) { diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/ArticleUrlResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/ArticleUrlResult.java index e635e7e50..601c18743 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/ArticleUrlResult.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/ArticleUrlResult.java @@ -1,5 +1,8 @@ package me.chanjar.weixin.mp.bean.message; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlCData; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.annotations.XStreamConverter; import lombok.Data; @@ -13,14 +16,17 @@ * @author plw on 2021/9/7 10:39 AM. * @version 1.0 */ -@XStreamAlias("ArticleUrlResult") @Data +@XStreamAlias("ArticleUrlResult") +@JacksonXmlRootElement(localName = "ArticleUrlResult") public class ArticleUrlResult implements Serializable { @XStreamAlias("ResultList") + @JacksonXmlProperty(localName = "ResultList") private List resultList; @XStreamAlias("Count") + @JacksonXmlProperty(localName = "Count") private Long count; @Override @@ -28,15 +34,19 @@ public String toString() { return WxMpGsonBuilder.create().toJson(this); } - @XStreamAlias("item") @Data + @XStreamAlias("item") + @JacksonXmlRootElement(localName = "item") public static class Item implements Serializable { @XStreamAlias("ArticleIdx") + @JacksonXmlProperty(localName = "ArticleIdx") private String articleIdx; @XStreamAlias("ArticleUrl") @XStreamConverter(value = XStreamCDataConverter.class) + @JacksonXmlProperty(localName = "ArticleUrl") + @JacksonXmlCData private String articleUrl; @Override diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/HardWare.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/HardWare.java index a75c98e02..2dbd70140 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/HardWare.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/HardWare.java @@ -2,6 +2,9 @@ import java.io.Serializable; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlCData; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.annotations.XStreamConverter; import lombok.Data; @@ -15,8 +18,9 @@ * * @author Binary Wang */ -@XStreamAlias("HardWare") @Data +@XStreamAlias("HardWare") +@JacksonXmlRootElement(localName = "HardWare") public class HardWare implements Serializable { private static final long serialVersionUID = -1295785297354896461L; @@ -25,12 +29,17 @@ public class HardWare implements Serializable { */ @XStreamAlias("MessageView") @XStreamConverter(value = XStreamCDataConverter.class) + @JacksonXmlProperty(localName = "MessageView") + @JacksonXmlCData private String messageView; + /** * 消息点击动作,目前支持ranklist(点击跳转排行榜) */ @XStreamAlias("MessageAction") @XStreamConverter(value = XStreamCDataConverter.class) + @JacksonXmlProperty(localName = "MessageAction") + @JacksonXmlCData private String messageAction; @Override diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/ScanCodeInfo.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/ScanCodeInfo.java index f53b44c41..685290bf3 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/ScanCodeInfo.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/ScanCodeInfo.java @@ -2,6 +2,9 @@ import java.io.Serializable; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlCData; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.annotations.XStreamConverter; import lombok.Data; @@ -15,8 +18,9 @@ * * @author Binary Wang */ -@XStreamAlias("ScanCodeInfo") @Data +@XStreamAlias("ScanCodeInfo") +@JacksonXmlRootElement(localName = "ScanCodeInfo") public class ScanCodeInfo implements Serializable { private static final long serialVersionUID = 4745181270645050122L; @@ -25,6 +29,8 @@ public class ScanCodeInfo implements Serializable { */ @XStreamAlias("ScanType") @XStreamConverter(value = XStreamCDataConverter.class) + @JacksonXmlProperty(localName = "ScanType") + @JacksonXmlCData private String scanType; /** @@ -32,6 +38,8 @@ public class ScanCodeInfo implements Serializable { */ @XStreamAlias("ScanResult") @XStreamConverter(value = XStreamCDataConverter.class) + @JacksonXmlProperty(localName = "ScanResult") + @JacksonXmlCData private String scanResult; @Override diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/SendLocationInfo.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/SendLocationInfo.java index 09f1776bb..6fb469554 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/SendLocationInfo.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/SendLocationInfo.java @@ -2,6 +2,9 @@ import java.io.Serializable; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlCData; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.annotations.XStreamConverter; import lombok.Data; @@ -15,29 +18,40 @@ * * @author Binary Wang */ -@XStreamAlias("SendLocationInfo") @Data +@XStreamAlias("SendLocationInfo") +@JacksonXmlRootElement(localName = "SendLocationInfo") public class SendLocationInfo implements Serializable { private static final long serialVersionUID = 6633214140499161130L; @XStreamAlias("Location_X") @XStreamConverter(value = XStreamCDataConverter.class) + @JacksonXmlProperty(localName = "Location_X") + @JacksonXmlCData private String locationX; @XStreamAlias("Location_Y") @XStreamConverter(value = XStreamCDataConverter.class) + @JacksonXmlProperty(localName = "Location_Y") + @JacksonXmlCData private String locationY; @XStreamAlias("Scale") @XStreamConverter(value = XStreamCDataConverter.class) + @JacksonXmlProperty(localName = "Scale") + @JacksonXmlCData private String scale; @XStreamAlias("Label") @XStreamConverter(value = XStreamCDataConverter.class) + @JacksonXmlProperty(localName = "Label") + @JacksonXmlCData private String label; @XStreamAlias("Poiname") @XStreamConverter(value = XStreamCDataConverter.class) + @JacksonXmlProperty(localName = "Poiname") + @JacksonXmlCData private String poiName; @Override diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/SendPicsInfo.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/SendPicsInfo.java index a01e66596..c5533328c 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/SendPicsInfo.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/SendPicsInfo.java @@ -4,6 +4,9 @@ import java.util.ArrayList; import java.util.List; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlCData; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.annotations.XStreamConverter; import lombok.Data; @@ -17,15 +20,18 @@ * * @author Binary Wang */ -@XStreamAlias("SendPicsInfo") @Data +@XStreamAlias("SendPicsInfo") +@JacksonXmlRootElement(localName = "SendPicsInfo") public class SendPicsInfo implements Serializable { private static final long serialVersionUID = -4572837013294199227L; @XStreamAlias("PicList") + @JacksonXmlProperty(localName = "PicList") protected final List picList = new ArrayList<>(); @XStreamAlias("Count") + @JacksonXmlProperty(localName = "Count") private Long count; @Override @@ -33,13 +39,16 @@ public String toString() { return WxMpGsonBuilder.create().toJson(this); } - @XStreamAlias("item") @Data + @XStreamAlias("item") + @JacksonXmlRootElement(localName = "item") public static class Item implements Serializable { private static final long serialVersionUID = 7706235740094081194L; @XStreamAlias("PicMd5Sum") @XStreamConverter(value = XStreamCDataConverter.class) + @JacksonXmlProperty(localName = "PicMd5Sum") + @JacksonXmlCData private String picMd5Sum; @Override diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java index 56af43a64..6a1d6c169 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java @@ -1,5 +1,8 @@ package me.chanjar.weixin.mp.bean.message; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlCData; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.annotations.XStreamConverter; import lombok.Data; @@ -33,6 +36,7 @@ @Data @Slf4j @XStreamAlias("xml") +@JacksonXmlRootElement(localName = "xml") public class WxMpXmlMessage implements Serializable { private static final long serialVersionUID = -3586245291677274914L; @@ -47,97 +51,139 @@ public class WxMpXmlMessage implements Serializable { @XStreamAlias("ToUserName") @XStreamConverter(value = XStreamCDataConverter.class) + @JacksonXmlProperty(localName = "ToUserName") + @JacksonXmlCData private String toUser; @XStreamAlias("FromUserName") @XStreamConverter(value = XStreamCDataConverter.class) + @JacksonXmlProperty(localName = "FromUserName") + @JacksonXmlCData private String fromUser; @XStreamAlias("CreateTime") + @JacksonXmlProperty(localName = "CreateTime") private Long createTime; @XStreamAlias("MsgType") @XStreamConverter(value = XStreamCDataConverter.class) + @JacksonXmlProperty(localName = "MsgType") + @JacksonXmlCData private String msgType; @XStreamAlias("Content") @XStreamConverter(value = XStreamCDataConverter.class) + @JacksonXmlProperty(localName = "Content") + @JacksonXmlCData private String content; @XStreamAlias("MenuId") + @JacksonXmlProperty(localName = "MenuId") private Long menuId; @XStreamAlias("MsgId") + @JacksonXmlProperty(localName = "MsgId") private Long msgId; @XStreamAlias("PicUrl") @XStreamConverter(value = XStreamCDataConverter.class) + @JacksonXmlProperty(localName = "PicUrl") + @JacksonXmlCData private String picUrl; @XStreamAlias("MediaId") @XStreamConverter(value = XStreamCDataConverter.class) + @JacksonXmlProperty(localName = "MediaId") + @JacksonXmlCData private String mediaId; @XStreamAlias("Format") @XStreamConverter(value = XStreamCDataConverter.class) + @JacksonXmlProperty(localName = "Format") + @JacksonXmlCData private String format; @XStreamAlias("ThumbMediaId") @XStreamConverter(value = XStreamCDataConverter.class) + @JacksonXmlProperty(localName = "ThumbMediaId") + @JacksonXmlCData private String thumbMediaId; @XStreamAlias("Location_X") + @JacksonXmlProperty(localName = "Location_X") private Double locationX; @XStreamAlias("Location_Y") + @JacksonXmlProperty(localName = "Location_Y") private Double locationY; @XStreamAlias("Scale") + @JacksonXmlProperty(localName = "Scale") private Double scale; @XStreamAlias("Label") @XStreamConverter(value = XStreamCDataConverter.class) + @JacksonXmlProperty(localName = "Label") + @JacksonXmlCData private String label; @XStreamAlias("Title") @XStreamConverter(value = XStreamCDataConverter.class) + @JacksonXmlProperty(localName = "Title") + @JacksonXmlCData private String title; @XStreamAlias("Description") @XStreamConverter(value = XStreamCDataConverter.class) + @JacksonXmlProperty(localName = "Description") + @JacksonXmlCData private String description; @XStreamAlias("Url") @XStreamConverter(value = XStreamCDataConverter.class) + @JacksonXmlProperty(localName = "Url") + @JacksonXmlCData private String url; @XStreamAlias("Event") @XStreamConverter(value = XStreamCDataConverter.class) + @JacksonXmlProperty(localName = "Event") + @JacksonXmlCData private String event; @XStreamAlias("EventKey") @XStreamConverter(value = XStreamCDataConverter.class) + @JacksonXmlProperty(localName = "EventKey") + @JacksonXmlCData private String eventKey; @XStreamAlias("Ticket") @XStreamConverter(value = XStreamCDataConverter.class) + @JacksonXmlProperty(localName = "Ticket") + @JacksonXmlCData private String ticket; @XStreamAlias("Latitude") + @JacksonXmlProperty(localName = "Latitude") private Double latitude; @XStreamAlias("Longitude") + @JacksonXmlProperty(localName = "Longitude") private Double longitude; @XStreamAlias("Precision") + @JacksonXmlProperty(localName = "Precision") private Double precision; @XStreamAlias("Recognition") + @JacksonXmlProperty(localName = "Recognition") @XStreamConverter(value = XStreamCDataConverter.class) private String recognition; @XStreamAlias("UnionId") @XStreamConverter(value = XStreamCDataConverter.class) + @JacksonXmlProperty(localName = "UnionId") + @JacksonXmlCData private String unionId; /////////////////////////////////////// @@ -148,27 +194,33 @@ public class WxMpXmlMessage implements Serializable { */ @XStreamAlias("Status") @XStreamConverter(value = XStreamCDataConverter.class) + @JacksonXmlProperty(localName = "Status") + @JacksonXmlCData private String status; /** * group_id下粉丝数;或者openid_list中的粉丝数. */ @XStreamAlias("TotalCount") + @JacksonXmlProperty(localName = "TotalCount") private Integer totalCount; /** * 过滤(过滤是指特定地区、性别的过滤、用户设置拒收的过滤,用户接收已超4条的过滤)后,准备发送的粉丝数. * 原则上,filterCount = sentCount + errorCount */ @XStreamAlias("FilterCount") + @JacksonXmlProperty(localName = "FilterCount") private Integer filterCount; /** * 发送成功的粉丝数. */ @XStreamAlias("SentCount") + @JacksonXmlProperty(localName = "SentCount") private Integer sentCount; /** * 发送失败的粉丝数. */ @XStreamAlias("ErrorCount") + @JacksonXmlProperty(localName = "ErrorCount") private Integer errorCount; /////////////////////////////////////// @@ -178,16 +230,19 @@ public class WxMpXmlMessage implements Serializable { * 创建或关闭客服会话时的客服帐号. */ @XStreamAlias("KfAccount") + @JacksonXmlProperty(localName = "KfAccount") private String kfAccount; /** * 转接客服会话时的转入客服帐号. */ @XStreamAlias("ToKfAccount") + @JacksonXmlProperty(localName = "ToKfAccount") private String toKfAccount; /** * 转接客服会话时的转出客服帐号. */ @XStreamAlias("FromKfAccount") + @JacksonXmlProperty(localName = "FromKfAccount") private String fromKfAccount; /////////////////////////////////////// @@ -196,33 +251,44 @@ public class WxMpXmlMessage implements Serializable { @XStreamAlias("CardId") @XStreamConverter(value = XStreamCDataConverter.class) + @JacksonXmlProperty(localName = "CardId") + @JacksonXmlCData private String cardId; @XStreamAlias("FriendUserName") @XStreamConverter(value = XStreamCDataConverter.class) + @JacksonXmlProperty(localName = "FriendUserName") + @JacksonXmlCData private String friendUserName; /** * 是否为转赠,1代表是,0代表否. */ @XStreamAlias("IsGiveByFriend") + @JacksonXmlProperty(localName = "IsGiveByFriend") private Integer isGiveByFriend; @XStreamAlias("UserCardCode") @XStreamConverter(value = XStreamCDataConverter.class) + @JacksonXmlProperty(localName = "UserCardCode") + @JacksonXmlCData private String userCardCode; @XStreamAlias("OldUserCardCode") @XStreamConverter(value = XStreamCDataConverter.class) + @JacksonXmlProperty(localName = "OldUserCardCode") + @JacksonXmlCData private String oldUserCardCode; @XStreamAlias("OuterId") + @JacksonXmlProperty(localName = "OuterId") private Integer outerId; /** * 用户删除会员卡后可重新找回,当用户本次操作为找回时,该值为1,否则为0. */ @XStreamAlias("IsRestoreMemberCard") + @JacksonXmlProperty(localName = "IsRestoreMemberCard") private String isRestoreMemberCard; /** @@ -235,18 +301,21 @@ public class WxMpXmlMessage implements Serializable { *
*/ @XStreamAlias("OuterStr") + @JacksonXmlProperty(localName = "OuterStr") private String outerStr; /** * 是否转赠退回,0代表不是,1代表是. */ @XStreamAlias("IsReturnBack") + @JacksonXmlProperty(localName = "IsReturnBack") private String isReturnBack; /** * 是否是群转赠,0代表不是,1代表是. */ @XStreamAlias("IsChatRoom") + @JacksonXmlProperty(localName = "IsChatRoom") private String isChatRoom; /** @@ -254,6 +323,7 @@ public class WxMpXmlMessage implements Serializable { * 支持开发者统计API核销(FROM_API)、公众平台核销(FROM_MP)、卡券商户助手核销(FROM_MOBILE_HELPER)(核销员微信号) */ @XStreamAlias("ConsumeSource") + @JacksonXmlProperty(localName = "ConsumeSource") private String consumeSource; /** @@ -261,24 +331,28 @@ public class WxMpXmlMessage implements Serializable { * 当前卡券核销的门店名称(只有通过自助核销和买单核销时才会出现该字段) */ @XStreamAlias("LocationName") + @JacksonXmlProperty(localName = "LocationName") private String locationName; /** * 核销该卡券核销员的openid(只有通过卡券商户助手核销时才会出现). */ @XStreamAlias("StaffOpenId") + @JacksonXmlProperty(localName = "StaffOpenId") private String staffOpenId; /** * 自助核销时,用户输入的验证码. */ @XStreamAlias("VerifyCode") + @JacksonXmlProperty(localName = "VerifyCode") private String verifyCode; /** * 自助核销时,用户输入的备注金额. */ @XStreamAlias("RemarkAmount") + @JacksonXmlProperty(localName = "RemarkAmount") private String remarkAmount; /** @@ -288,6 +362,7 @@ public class WxMpXmlMessage implements Serializable { *
*/ @XStreamAlias("Detail") + @JacksonXmlProperty(localName = "Detail") private String detail; /** @@ -297,6 +372,7 @@ public class WxMpXmlMessage implements Serializable { *
*/ @XStreamAlias("ModifyBonus") + @JacksonXmlProperty(localName = "ModifyBonus") private String modifyBonus; /** @@ -306,6 +382,7 @@ public class WxMpXmlMessage implements Serializable { *
*/ @XStreamAlias("ModifyBalance") + @JacksonXmlProperty(localName = "ModifyBalance") private String modifyBalance; /** @@ -315,6 +392,7 @@ public class WxMpXmlMessage implements Serializable { *
*/ @XStreamAlias("TransId") + @JacksonXmlProperty(localName = "TransId") private String transId; /** @@ -324,6 +402,7 @@ public class WxMpXmlMessage implements Serializable { *
*/ @XStreamAlias("LocationId") + @JacksonXmlProperty(localName = "LocationId") private String locationId; /** @@ -333,6 +412,7 @@ public class WxMpXmlMessage implements Serializable { *
*/ @XStreamAlias("Fee") + @JacksonXmlProperty(localName = "Fee") private String fee; /** @@ -342,72 +422,86 @@ public class WxMpXmlMessage implements Serializable { *
*/ @XStreamAlias("OriginalFee") + @JacksonXmlProperty(localName = "OriginalFee") private String originalFee; @XStreamAlias("ScanCodeInfo") + @JacksonXmlProperty(localName = "ScanCodeInfo") private ScanCodeInfo scanCodeInfo = new ScanCodeInfo(); @XStreamAlias("SendPicsInfo") + @JacksonXmlProperty(localName = "SendPicsInfo") private SendPicsInfo sendPicsInfo = new SendPicsInfo(); @XStreamAlias("SendLocationInfo") + @JacksonXmlProperty(localName = "SendLocationInfo") private SendLocationInfo sendLocationInfo = new SendLocationInfo(); @XStreamAlias("ArticleUrlResult") + @JacksonXmlProperty(localName = "ArticleUrlResult") private ArticleUrlResult articleUrlResult = new ArticleUrlResult(); /** * 审核不通过原因 */ @XStreamAlias("RefuseReason") + @JacksonXmlProperty(localName = "RefuseReason") private String refuseReason; /** * 是否为朋友推荐,0代表否,1代表是 */ @XStreamAlias("IsRecommendByFriend") + @JacksonXmlProperty(localName = "IsRecommendByFriend") private String isRecommendByFriend; /** * 购买券点时,实际支付成功的时间 */ @XStreamAlias("PayFinishTime") + @JacksonXmlProperty(localName = "PayFinishTime") private String payFinishTime; /** * 购买券点时,支付二维码的生成时间 */ @XStreamAlias("CreateOrderTime") + @JacksonXmlProperty(localName = "CreateOrderTime") private String createOrderTime; /** * 购买券点时,支付二维码的生成时间 */ @XStreamAlias("Desc") + @JacksonXmlProperty(localName = "Desc") private String desc; /** * 剩余免费券点数量 */ @XStreamAlias("FreeCoinCount") + @JacksonXmlProperty(localName = "FreeCoinCount") private String freeCoinCount; /** * 剩余付费券点数量 */ @XStreamAlias("PayCoinCount") + @JacksonXmlProperty(localName = "PayCoinCount") private String payCoinCount; /** * 本次变动的免费券点数量 */ @XStreamAlias("RefundFreeCoinCount") + @JacksonXmlProperty(localName = "RefundFreeCoinCount") private String refundFreeCoinCount; /** * 本次变动的付费券点数量 */ @XStreamAlias("RefundPayCoinCount") + @JacksonXmlProperty(localName = "RefundPayCoinCount") private String refundPayCoinCount; /** @@ -417,21 +511,23 @@ public class WxMpXmlMessage implements Serializable { *
*/ @XStreamAlias("OrderType") + @JacksonXmlProperty(localName = "OrderType") private String orderType; /** * 系统备注,说明此次变动的缘由,如开通账户奖励、门店奖励、核销奖励以及充值、扣减。 */ @XStreamAlias("Memo") + @JacksonXmlProperty(localName = "Memo") private String memo; /** * 所开发票的详情 */ @XStreamAlias("ReceiptInfo") + @JacksonXmlProperty(localName = "ReceiptInfo") private String receiptInfo; - /////////////////////////////////////// // 门店审核事件推送 /////////////////////////////////////// @@ -439,12 +535,14 @@ public class WxMpXmlMessage implements Serializable { * 商户自己内部ID,即字段中的sid. */ @XStreamAlias("UniqId") + @JacksonXmlProperty(localName = "UniqId") private String storeUniqId; /** * 微信的门店ID,微信内门店唯一标示ID. */ @XStreamAlias("PoiId") + @JacksonXmlProperty(localName = "PoiId") private String poiId; /** @@ -453,12 +551,14 @@ public class WxMpXmlMessage implements Serializable { * 在商品审核结果推送时,verify_ok表示审核通过,verify_not_pass表示审核未通过。 */ @XStreamAlias("Result") + @JacksonXmlProperty(localName = "Result") private String result; /** * 成功的通知信息,或审核失败的驳回理由. */ @XStreamAlias("msg") + @JacksonXmlProperty(localName = "msg") private String msg; /////////////////////////////////////// @@ -470,16 +570,19 @@ public class WxMpXmlMessage implements Serializable { * 认证过期失效通知: 有效期 (整形),指的是时间戳,表示已于该时间戳认证过期,需要重新发起微信认证 */ @XStreamAlias("ExpiredTime") + @JacksonXmlProperty(localName = "ExpiredTime") private Long expiredTime; /** * 失败发生时间 (整形),时间戳. */ @XStreamAlias("FailTime") + @JacksonXmlProperty(localName = "FailTime") private Long failTime; /** * 认证失败的原因. */ @XStreamAlias("FailReason") + @JacksonXmlProperty(localName = "FailReason") private String failReason; /////////////////////////////////////// @@ -490,12 +593,15 @@ public class WxMpXmlMessage implements Serializable { */ @XStreamAlias("OrderId") @XStreamConverter(value = XStreamCDataConverter.class) + @JacksonXmlProperty(localName = "OrderId") + @JacksonXmlCData private String orderId; /** * 订单状态. */ @XStreamAlias("OrderStatus") + @JacksonXmlProperty(localName = "OrderStatus") private String orderStatus; /** @@ -503,6 +609,8 @@ public class WxMpXmlMessage implements Serializable { */ @XStreamAlias("ProductId") @XStreamConverter(value = XStreamCDataConverter.class) + @JacksonXmlProperty(localName = "ProductId") + @JacksonXmlCData private String productId; /** @@ -510,6 +618,8 @@ public class WxMpXmlMessage implements Serializable { */ @XStreamAlias("SkuInfo") @XStreamConverter(value = XStreamCDataConverter.class) + @JacksonXmlProperty(localName = "SkuInfo") + @JacksonXmlCData private String skuInfo; /////////////////////////////////////// @@ -521,6 +631,8 @@ public class WxMpXmlMessage implements Serializable { */ @XStreamAlias("DeviceType") @XStreamConverter(value = XStreamCDataConverter.class) + @JacksonXmlProperty(localName = "DeviceType") + @JacksonXmlCData private String deviceType; /** @@ -529,6 +641,8 @@ public class WxMpXmlMessage implements Serializable { */ @XStreamAlias("DeviceID") @XStreamConverter(value = XStreamCDataConverter.class) + @JacksonXmlProperty(localName = "DeviceID") + @JacksonXmlCData private String deviceId; /** @@ -537,6 +651,8 @@ public class WxMpXmlMessage implements Serializable { */ @XStreamAlias("SessionID") @XStreamConverter(value = XStreamCDataConverter.class) + @JacksonXmlProperty(localName = "SessionID") + @JacksonXmlCData private String sessionId; /** @@ -544,9 +660,12 @@ public class WxMpXmlMessage implements Serializable { */ @XStreamAlias("OpenID") @XStreamConverter(value = XStreamCDataConverter.class) + @JacksonXmlProperty(localName = "OpenID") + @JacksonXmlCData private String openId; @XStreamAlias("HardWare") + @JacksonXmlProperty(localName = "HardWare") private HardWare hardWare = new HardWare(); /** @@ -556,6 +675,7 @@ public class WxMpXmlMessage implements Serializable { * 2:订阅设备状态 */ @XStreamAlias("OpType") + @JacksonXmlProperty(localName = "OpType") private Integer opType; /** @@ -563,6 +683,7 @@ public class WxMpXmlMessage implements Serializable { * 0:未连接;1:已连接 */ @XStreamAlias("DeviceStatus") + @JacksonXmlProperty(localName = "DeviceStatus") private Integer deviceStatus; /////////////////////////////////////// @@ -572,12 +693,14 @@ public class WxMpXmlMessage implements Serializable { * 审核成功时的时间(整形),时间戳 */ @XStreamAlias("SuccTime") + @JacksonXmlProperty(localName = "SuccTime") private Long successTime; /** * 审核失败的原因 */ @XStreamAlias("Reason") + @JacksonXmlProperty(localName = "Reason") private String reason; /////////////////////////////////////// @@ -587,65 +710,76 @@ public class WxMpXmlMessage implements Serializable { * 商品编码标准 */ @XStreamAlias("KeyStandard") + @JacksonXmlProperty(localName = "KeyStandard") private String keyStandard; /** * 商品编码内容 */ @XStreamAlias("KeyStr") + @JacksonXmlProperty(localName = "KeyStr") private String keyStr; /** * 用户在微信内设置的国家 */ @XStreamAlias("Country") + @JacksonXmlProperty(localName = "Country") private String country; /** * 用户在微信内设置的省份 */ @XStreamAlias("Province") + @JacksonXmlProperty(localName = "Province") private String province; /** * 用户在微信内设置的城市 */ @XStreamAlias("City") + @JacksonXmlProperty(localName = "City") private String city; /** * 用户的性别,1为男性,2为女性,0代表未知 */ @XStreamAlias("Sex") + @JacksonXmlProperty(localName = "Sex") private String sex; /** * 打开商品主页的场景,1为扫码,2为其他打开场景(如会话、收藏或朋友圈) */ @XStreamAlias("Scene") + @JacksonXmlProperty(localName = "Scene") private String scene; /** * 调用“获取商品二维码接口”时传入的extinfo,为标识参数 */ @XStreamAlias("ExtInfo") + @JacksonXmlProperty(localName = "ExtInfo") private String extInfo; /** * 用户的实时地理位置信息(目前只精确到省一级),可在国家统计局网站查到对应明细: http://www.stats.gov.cn/tjsj/tjbz/xzqhdm/201504/t20150415_712722.html */ @XStreamAlias("RegionCode") + @JacksonXmlProperty(localName = "RegionCode") private String regionCode; /** * 审核未通过的原因. */ @XStreamAlias("ReasonMsg") + @JacksonXmlProperty(localName = "ReasonMsg") private String reasonMsg; /** * 给用户发菜单消息类型的客服消息后,用户所点击的菜单ID. */ @XStreamAlias("bizmsgmenuid") + @JacksonXmlProperty(localName = "bizmsgmenuid") private String bizMsgMenuId; /*------------------ 电子发票 ------------------*/ @@ -653,38 +787,50 @@ public class WxMpXmlMessage implements Serializable { * 授权成功的订单号,与失败订单号两者必显示其一 */ @XStreamAlias("SuccOrderId") + @JacksonXmlProperty(localName = "SuccOrderId") private String succOrderId; /** * 授权失败的订单号,与成功订单号两者必显示其一 */ @XStreamAlias("FailOrderId") + @JacksonXmlProperty(localName = "FailOrderId") private String failOrderId; /** * 获取授权页链接的AppId */ @XStreamAlias("AuthorizeAppId") + @JacksonXmlProperty(localName = "AuthorizeAppId") private String authorizeAppId; /** * 授权来源,web:公众号开票,app:app开票,wxa:小程序开票,wap:h5开票 */ @XStreamAlias("source") + @JacksonXmlProperty(localName = "source") private String source; /** * 发票请求流水号,唯一识别发票请求的流水号 */ @XStreamAlias("fpqqlsh") + @JacksonXmlProperty(localName = "fpqqlsh") private String fpqqlsh; /** * 纳税人识别码 */ @XStreamAlias("nsrsbh") + @JacksonXmlProperty(localName = "nsrsbh") private String nsrsbh; + /** + * 加密消息 + */ + @XStreamAlias("Encrypt") + @JacksonXmlProperty(localName = "Encrypt") + private String encrypt; public static WxMpXmlMessage fromXml(String xml) { //修改微信变态的消息内容格式,方便解析 @@ -710,7 +856,7 @@ public static WxMpXmlMessage fromXml(InputStream is) { public static WxMpXmlMessage fromEncryptedXml(String encryptedXml, WxMpConfigStorage wxMpConfigStorage, String timestamp, String nonce, String msgSignature) { WxMpCryptUtil cryptUtil = new WxMpCryptUtil(wxMpConfigStorage); - String plainText = cryptUtil.decrypt(msgSignature, timestamp, nonce, encryptedXml); + String plainText = cryptUtil.decryptXml(msgSignature, timestamp, nonce, encryptedXml); log.debug("解密后的原始xml消息内容:{}", plainText); return fromXml(plainText); } @@ -724,6 +870,14 @@ public static WxMpXmlMessage fromEncryptedXml(InputStream is, WxMpConfigStorage } } + public WxMpXmlMessage decryptField(WxMpConfigStorage wxMpConfigStorage, + String timestamp, String nonce, String msgSignature) { + WxMpCryptUtil cryptUtil = new WxMpCryptUtil(wxMpConfigStorage); + String plainText = cryptUtil.decryptContent(msgSignature, timestamp, nonce, this.encrypt); + log.debug("解密后的原始xml消息内容:{}", plainText); + return fromXml(plainText); + } + /** *
    * 当接受用户消息时,可能会获得以下值:
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutDeviceMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutDeviceMessage.java
index b32935a10..d8b41bb24 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutDeviceMessage.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutDeviceMessage.java
@@ -1,36 +1,48 @@
-package me.chanjar.weixin.mp.bean.message;
-
-import com.thoughtworks.xstream.annotations.XStreamAlias;
-import com.thoughtworks.xstream.annotations.XStreamConverter;
-
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import me.chanjar.weixin.common.api.WxConsts;
-import me.chanjar.weixin.common.util.xml.XStreamCDataConverter;
-
-@XStreamAlias("xml")
-@Data
-@EqualsAndHashCode(callSuper = true)
-public class WxMpXmlOutDeviceMessage extends WxMpXmlOutMessage {
-  private static final long serialVersionUID = -3093843149649157587L;
-
-  @XStreamAlias("DeviceType")
-  @XStreamConverter(value = XStreamCDataConverter.class)
-  private String deviceType;
-
-  @XStreamAlias("DeviceID")
-  @XStreamConverter(value = XStreamCDataConverter.class)
-  private String deviceId;
-
-  @XStreamAlias("Content")
-  @XStreamConverter(value = XStreamCDataConverter.class)
-  private String content;
-
-  @XStreamAlias("SessionID")
-  @XStreamConverter(value = XStreamCDataConverter.class)
-  private String sessionId;
-
-  public WxMpXmlOutDeviceMessage() {
-    this.msgType = WxConsts.XmlMsgType.DEVICE_TEXT;
-  }
-}
+package me.chanjar.weixin.mp.bean.message;
+
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlCData;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import com.thoughtworks.xstream.annotations.XStreamConverter;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import me.chanjar.weixin.common.api.WxConsts;
+import me.chanjar.weixin.common.util.xml.XStreamCDataConverter;
+
+@Data
+@XStreamAlias("xml")
+@JacksonXmlRootElement(localName = "xml")
+@EqualsAndHashCode(callSuper = true)
+public class WxMpXmlOutDeviceMessage extends WxMpXmlOutMessage {
+  private static final long serialVersionUID = -3093843149649157587L;
+
+  @XStreamAlias("DeviceType")
+  @XStreamConverter(value = XStreamCDataConverter.class)
+  @JacksonXmlProperty(localName = "DeviceType")
+  @JacksonXmlCData
+  private String deviceType;
+
+  @XStreamAlias("DeviceID")
+  @XStreamConverter(value = XStreamCDataConverter.class)
+  @JacksonXmlProperty(localName = "DeviceID")
+  @JacksonXmlCData
+  private String deviceId;
+
+  @XStreamAlias("Content")
+  @XStreamConverter(value = XStreamCDataConverter.class)
+  @JacksonXmlProperty(localName = "Content")
+  @JacksonXmlCData
+  private String content;
+
+  @XStreamAlias("SessionID")
+  @XStreamConverter(value = XStreamCDataConverter.class)
+  @JacksonXmlProperty(localName = "SessionID")
+  @JacksonXmlCData
+  private String sessionId;
+
+  public WxMpXmlOutDeviceMessage() {
+    this.msgType = WxConsts.XmlMsgType.DEVICE_TEXT;
+  }
+}
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutImageMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutImageMessage.java
index dbb0ab90f..d2328890e 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutImageMessage.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutImageMessage.java
@@ -1,5 +1,8 @@
 package me.chanjar.weixin.mp.bean.message;
 
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlCData;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
 import com.thoughtworks.xstream.annotations.XStreamAlias;
 import com.thoughtworks.xstream.annotations.XStreamConverter;
 import lombok.Data;
@@ -7,14 +10,17 @@
 import me.chanjar.weixin.common.api.WxConsts;
 import me.chanjar.weixin.common.util.xml.XStreamMediaIdConverter;
 
-@XStreamAlias("xml")
 @Data
+@XStreamAlias("xml")
+@JacksonXmlRootElement(localName = "xml")
 @EqualsAndHashCode(callSuper = true)
 public class WxMpXmlOutImageMessage extends WxMpXmlOutMessage {
   private static final long serialVersionUID = -2684778597067990723L;
 
   @XStreamAlias("Image")
   @XStreamConverter(value = XStreamMediaIdConverter.class)
+  @JacksonXmlProperty(localName = "Image")
+  @JacksonXmlCData
   private String mediaId;
 
   public WxMpXmlOutImageMessage() {
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutMessage.java
index 40e0b41a2..a44aea740 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutMessage.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutMessage.java
@@ -1,8 +1,12 @@
 package me.chanjar.weixin.mp.bean.message;
 
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlCData;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
 import com.thoughtworks.xstream.annotations.XStreamAlias;
 import com.thoughtworks.xstream.annotations.XStreamConverter;
 import lombok.Data;
+import me.chanjar.weixin.common.util.crypto.WxCryptUtil;
 import me.chanjar.weixin.common.util.xml.XStreamCDataConverter;
 import me.chanjar.weixin.mp.config.WxMpConfigStorage;
 import me.chanjar.weixin.mp.builder.outxml.*;
@@ -11,26 +15,59 @@
 
 import java.io.Serializable;
 
-@XStreamAlias("xml")
 @Data
+@XStreamAlias("xml")
+@JacksonXmlRootElement(localName = "xml")
 public abstract class WxMpXmlOutMessage implements Serializable {
   private static final long serialVersionUID = -381382011286216263L;
 
   @XStreamAlias("ToUserName")
   @XStreamConverter(value = XStreamCDataConverter.class)
+  @JacksonXmlProperty(localName = "ToUserName")
+  @JacksonXmlCData
   protected String toUserName;
 
   @XStreamAlias("FromUserName")
   @XStreamConverter(value = XStreamCDataConverter.class)
+  @JacksonXmlProperty(localName = "FromUserName")
+  @JacksonXmlCData
   protected String fromUserName;
 
   @XStreamAlias("CreateTime")
+  @JacksonXmlProperty(localName = "CreateTime")
   protected Long createTime;
 
   @XStreamAlias("MsgType")
   @XStreamConverter(value = XStreamCDataConverter.class)
+  @JacksonXmlProperty(localName = "MsgType")
+  @JacksonXmlCData
   protected String msgType;
 
+
+  @XStreamAlias("Encrypt")
+  @XStreamConverter(value = XStreamCDataConverter.class)
+  @JacksonXmlProperty(localName = "Encrypt")
+  @JacksonXmlCData
+  private String encrypt;
+
+  @XStreamAlias("MsgSignature")
+  @XStreamConverter(value = XStreamCDataConverter.class)
+  @JacksonXmlProperty(localName = "MsgSignature")
+  @JacksonXmlCData
+  private String msgSignature;
+
+  @XStreamAlias("TimeStamp")
+  @XStreamConverter(value = XStreamCDataConverter.class)
+  @JacksonXmlProperty(localName = "TimeStamp")
+  @JacksonXmlCData
+  private String timeStamp;
+
+  @XStreamAlias("Nonce")
+  @XStreamConverter(value = XStreamCDataConverter.class)
+  @JacksonXmlProperty(localName = "Nonce")
+  @JacksonXmlCData
+  private String nonce;
+
   /**
    * 获得文本消息builder
    */
@@ -92,6 +129,21 @@ public String toXml() {
     return XStreamTransformer.toXml((Class) this.getClass(), this);
   }
 
+  /**
+   * 转换成加密的结果
+   */
+  public WxMpXmlOutMessage toEncrypted(WxMpConfigStorage wxMpConfigStorage) {
+    String plainXml = toXml();
+    WxMpCryptUtil pc = new WxMpCryptUtil(wxMpConfigStorage);
+    WxCryptUtil.EncryptContext context = pc.encryptContext(plainXml);
+    WxMpXmlOutMessage res = new WxMpXmlOutMessage() {};
+    res.setNonce(context.getNonce());
+    res.setEncrypt(context.getEncrypt());
+    res.setTimeStamp(context.getTimeStamp());
+    res.setMsgSignature(context.getSignature());
+    return res;
+  }
+
   /**
    * 转换成加密的xml格式
    */
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutMusicMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutMusicMessage.java
index 1124f4585..240b979fa 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutMusicMessage.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutMusicMessage.java
@@ -1,5 +1,8 @@
 package me.chanjar.weixin.mp.bean.message;
 
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlCData;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
 import com.thoughtworks.xstream.annotations.XStreamAlias;
 import com.thoughtworks.xstream.annotations.XStreamConverter;
 import lombok.Data;
@@ -9,42 +12,55 @@
 
 import java.io.Serializable;
 
-@XStreamAlias("xml")
 @Data
+@XStreamAlias("xml")
+@JacksonXmlRootElement(localName = "xml")
 @EqualsAndHashCode(callSuper = true)
 public class WxMpXmlOutMusicMessage extends WxMpXmlOutMessage {
   private static final long serialVersionUID = -4159937804975448945L;
 
   @XStreamAlias("Music")
+  @JacksonXmlProperty(localName = "Music")
   protected final Music music = new Music();
 
   public WxMpXmlOutMusicMessage() {
     this.msgType = WxConsts.XmlMsgType.MUSIC;
   }
 
-  @XStreamAlias("Music")
   @Data
+  @XStreamAlias("Music")
+  @JacksonXmlRootElement(localName = "Music")
   public static class Music implements Serializable {
     private static final long serialVersionUID = -5492592401691895334L;
 
     @XStreamAlias("Title")
     @XStreamConverter(value = XStreamCDataConverter.class)
+    @JacksonXmlProperty(localName = "Title")
+    @JacksonXmlCData
     private String title;
 
     @XStreamAlias("Description")
     @XStreamConverter(value = XStreamCDataConverter.class)
+    @JacksonXmlProperty(localName = "Description")
+    @JacksonXmlCData
     private String description;
 
     @XStreamAlias("ThumbMediaId")
     @XStreamConverter(value = XStreamCDataConverter.class)
+    @JacksonXmlProperty(localName = "ThumbMediaId")
+    @JacksonXmlCData
     private String thumbMediaId;
 
     @XStreamAlias("MusicUrl")
     @XStreamConverter(value = XStreamCDataConverter.class)
+    @JacksonXmlProperty(localName = "MusicUrl")
+    @JacksonXmlCData
     private String musicUrl;
 
     @XStreamAlias("HQMusicUrl")
     @XStreamConverter(value = XStreamCDataConverter.class)
+    @JacksonXmlProperty(localName = "HQMusicUrl")
+    @JacksonXmlCData
     private String hqMusicUrl;
   }
 
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutNewsMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutNewsMessage.java
index 00f8d70c8..192b24a35 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutNewsMessage.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutNewsMessage.java
@@ -4,6 +4,9 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlCData;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
 import com.thoughtworks.xstream.annotations.XStreamAlias;
 import com.thoughtworks.xstream.annotations.XStreamConverter;
 import lombok.Data;
@@ -15,8 +18,9 @@
  * 被动回复的图文消息xml.
  * @author chanjarster
  */
-@XStreamAlias("xml")
 @Data
+@XStreamAlias("xml")
+@JacksonXmlRootElement(localName = "xml")
 @EqualsAndHashCode(callSuper = true)
 public class WxMpXmlOutNewsMessage extends WxMpXmlOutMessage {
   private static final long serialVersionUID = -4604402850905714772L;
@@ -26,12 +30,14 @@ public class WxMpXmlOutNewsMessage extends WxMpXmlOutMessage {
    * 注意,如果图文数超过限制,则将只发限制内的条数
    */
   @XStreamAlias("Articles")
+  @JacksonXmlProperty(localName = "Articles")
   protected final List articles = new ArrayList<>();
   /**
    * 图文消息个数.
    * 当用户发送文本、图片、视频、图文、地理位置这五种消息时,开发者只能回复1条图文消息;其余场景最多可回复8条图文消息
    */
   @XStreamAlias("ArticleCount")
+  @JacksonXmlProperty(localName = "ArticleCount")
   protected int articleCount;
 
   public WxMpXmlOutNewsMessage() {
@@ -43,8 +49,9 @@ public void addArticle(Item item) {
     this.articleCount = this.articles.size();
   }
 
-  @XStreamAlias("item")
   @Data
+  @XStreamAlias("item")
+  @JacksonXmlRootElement(localName = "item")
   public static class Item implements Serializable {
     private static final long serialVersionUID = -4971456355028904754L;
 
@@ -53,6 +60,8 @@ public static class Item implements Serializable {
      */
     @XStreamAlias("Title")
     @XStreamConverter(value = XStreamCDataConverter.class)
+    @JacksonXmlProperty(localName = "Title")
+    @JacksonXmlCData
     private String title;
 
     /**
@@ -60,6 +69,8 @@ public static class Item implements Serializable {
      */
     @XStreamAlias("Description")
     @XStreamConverter(value = XStreamCDataConverter.class)
+    @JacksonXmlProperty(localName = "Description")
+    @JacksonXmlCData
     private String description;
 
     /**
@@ -68,6 +79,8 @@ public static class Item implements Serializable {
      */
     @XStreamAlias("PicUrl")
     @XStreamConverter(value = XStreamCDataConverter.class)
+    @JacksonXmlProperty(localName = "PicUrl")
+    @JacksonXmlCData
     private String picUrl;
 
     /**
@@ -75,6 +88,8 @@ public static class Item implements Serializable {
      */
     @XStreamAlias("Url")
     @XStreamConverter(value = XStreamCDataConverter.class)
+    @JacksonXmlProperty(localName = "Url")
+    @JacksonXmlCData
     private String url;
 
   }
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutTextMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutTextMessage.java
index cbaa05abc..b9c7747b0 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutTextMessage.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutTextMessage.java
@@ -1,5 +1,8 @@
 package me.chanjar.weixin.mp.bean.message;
 
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlCData;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
 import com.thoughtworks.xstream.annotations.XStreamAlias;
 import com.thoughtworks.xstream.annotations.XStreamConverter;
 import lombok.Data;
@@ -7,14 +10,17 @@
 import me.chanjar.weixin.common.api.WxConsts;
 import me.chanjar.weixin.common.util.xml.XStreamCDataConverter;
 
-@XStreamAlias("xml")
 @Data
+@XStreamAlias("xml")
+@JacksonXmlRootElement(localName = "xml")
 @EqualsAndHashCode(callSuper = true)
 public class WxMpXmlOutTextMessage extends WxMpXmlOutMessage {
   private static final long serialVersionUID = -3972786455288763361L;
 
   @XStreamAlias("Content")
   @XStreamConverter(value = XStreamCDataConverter.class)
+  @JacksonXmlProperty(localName = "Content")
+  @JacksonXmlCData
   private String content;
 
   public WxMpXmlOutTextMessage() {
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutTransferKefuMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutTransferKefuMessage.java
index 5b0857830..1d7073861 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutTransferKefuMessage.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutTransferKefuMessage.java
@@ -1,5 +1,8 @@
 package me.chanjar.weixin.mp.bean.message;
 
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlCData;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
 import com.thoughtworks.xstream.annotations.XStreamAlias;
 import com.thoughtworks.xstream.annotations.XStreamConverter;
 import lombok.Data;
@@ -9,26 +12,31 @@
 
 import java.io.Serializable;
 
-@XStreamAlias("xml")
 @Data
+@XStreamAlias("xml")
+@JacksonXmlRootElement(localName = "xml")
 @EqualsAndHashCode(callSuper = true)
 public class WxMpXmlOutTransferKefuMessage extends WxMpXmlOutMessage {
   private static final long serialVersionUID = 1850903037285841322L;
 
   @XStreamAlias("TransInfo")
+  @JacksonXmlProperty(localName = "TransInfo")
   protected TransInfo transInfo;
 
   public WxMpXmlOutTransferKefuMessage() {
     this.msgType = WxConsts.KefuMsgType.TRANSFER_CUSTOMER_SERVICE;
   }
 
-  @XStreamAlias("TransInfo")
   @Data
+  @XStreamAlias("TransInfo")
+  @JacksonXmlRootElement(localName = "TransInfo")
   public static class TransInfo implements Serializable {
     private static final long serialVersionUID = -6317885617135706056L;
 
     @XStreamAlias("KfAccount")
     @XStreamConverter(value = XStreamCDataConverter.class)
+    @JacksonXmlProperty(localName = "KfAccount")
+    @JacksonXmlCData
     private String kfAccount;
 
   }
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutVideoMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutVideoMessage.java
index 7f43a5680..101fa9605 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutVideoMessage.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutVideoMessage.java
@@ -1,5 +1,8 @@
 package me.chanjar.weixin.mp.bean.message;
 
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlCData;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
 import com.thoughtworks.xstream.annotations.XStreamAlias;
 import com.thoughtworks.xstream.annotations.XStreamConverter;
 import lombok.Data;
@@ -9,34 +12,43 @@
 
 import java.io.Serializable;
 
-@XStreamAlias("xml")
 @Data
+@XStreamAlias("xml")
+@JacksonXmlRootElement(localName = "xml")
 @EqualsAndHashCode(callSuper = true)
 public class WxMpXmlOutVideoMessage extends WxMpXmlOutMessage {
   private static final long serialVersionUID = 1745902309380113978L;
 
   @XStreamAlias("Video")
+  @JacksonXmlProperty(localName = "Video")
   protected final Video video = new Video();
 
   public WxMpXmlOutVideoMessage() {
     this.msgType = WxConsts.XmlMsgType.VIDEO;
   }
 
-  @XStreamAlias("Video")
   @Data
+  @XStreamAlias("Video")
+  @JacksonXmlRootElement(localName = "Video")
   public static class Video implements Serializable {
     private static final long serialVersionUID = -6445448977569651183L;
 
     @XStreamAlias("MediaId")
     @XStreamConverter(value = XStreamCDataConverter.class)
+    @JacksonXmlProperty(localName = "MediaId")
+    @JacksonXmlCData
     private String mediaId;
 
     @XStreamAlias("Title")
     @XStreamConverter(value = XStreamCDataConverter.class)
+    @JacksonXmlProperty(localName = "Title")
+    @JacksonXmlCData
     private String title;
 
     @XStreamAlias("Description")
     @XStreamConverter(value = XStreamCDataConverter.class)
+    @JacksonXmlProperty(localName = "Description")
+    @JacksonXmlCData
     private String description;
 
   }
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutVoiceMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutVoiceMessage.java
index bd91b861e..f8330efd7 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutVoiceMessage.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutVoiceMessage.java
@@ -1,5 +1,8 @@
 package me.chanjar.weixin.mp.bean.message;
 
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlCData;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
 import com.thoughtworks.xstream.annotations.XStreamAlias;
 import com.thoughtworks.xstream.annotations.XStreamConverter;
 import lombok.Data;
@@ -7,14 +10,17 @@
 import me.chanjar.weixin.common.api.WxConsts;
 import me.chanjar.weixin.common.util.xml.XStreamMediaIdConverter;
 
-@XStreamAlias("xml")
 @Data
+@XStreamAlias("xml")
+@JacksonXmlRootElement(localName = "xml")
 @EqualsAndHashCode(callSuper = true)
 public class WxMpXmlOutVoiceMessage extends WxMpXmlOutMessage {
   private static final long serialVersionUID = 240367390249860551L;
 
   @XStreamAlias("Voice")
   @XStreamConverter(value = XStreamMediaIdConverter.class)
+  @JacksonXmlProperty(localName = "Voice")
+  @JacksonXmlCData
   private String mediaId;
 
   public WxMpXmlOutVoiceMessage() {
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpUser.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpUser.java
index 6edf351d4..bfd8b6d8d 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpUser.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpUser.java
@@ -21,8 +21,16 @@ public class WxMpUser implements Serializable {
 
   private Boolean subscribe;
   private String openId;
+  /**
+   * @deprecated 2021年12月27日之后不再输出
+   */
+  @Deprecated
   private String nickname;
   private String language;
+  /**
+   * @deprecated 2021年12月27日之后不再输出
+   */
+  @Deprecated
   private String headImgUrl;
   private Long subscribeTime;
   /**
@@ -45,8 +53,7 @@ public class WxMpUser implements Serializable {
   private String[] privileges;
 
   /**
-   * subscribe_scene 返回用户关注的渠道来源.
-   * ADD_SCENE_SEARCH 公众号搜索,ADD_SCENE_ACCOUNT_MIGRATION 公众号迁移,ADD_SCENE_PROFILE_CARD 名片分享,ADD_SCENE_QR_CODE 扫描二维码,ADD_SCENEPROFILE LINK 图文页内名称点击,ADD_SCENE_PROFILE_ITEM 图文页右上角菜单,ADD_SCENE_PAID 支付后关注,ADD_SCENE_OTHERS 其他
+   * subscribe_scene 返回用户关注的渠道来源,ADD_SCENE_SEARCH 公众号搜索,ADD_SCENE_ACCOUNT_MIGRATION 公众号迁移,ADD_SCENE_PROFILE_CARD 名片分享,ADD_SCENE_QR_CODE 扫描二维码,ADD_SCENE_PROFILE_LINK 图文页内名称点击,ADD_SCENE_PROFILE_ITEM 图文页右上角菜单,ADD_SCENE_PAID 支付后关注,ADD_SCENE_WECHAT_ADVERTISEMENT 微信广告,ADD_SCENE_OTHERS 其他
    */
   private String subscribeScene;
 
@@ -60,7 +67,6 @@ public class WxMpUser implements Serializable {
    */
   private String qrSceneStr;
 
-
   public static WxMpUser fromJson(String json) {
     return WxMpGsonBuilder.create().fromJson(json, WxMpUser.class);
   }
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/MpNewsArticleBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/MpNewsArticleBuilder.java
new file mode 100644
index 000000000..2d6babcf8
--- /dev/null
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/MpNewsArticleBuilder.java
@@ -0,0 +1,33 @@
+package me.chanjar.weixin.mp.builder.kefu;
+
+import me.chanjar.weixin.common.api.WxConsts;
+import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage;
+
+/**
+ * 图文消息builder
+ * 
+ * 用法:
+ * WxMpKefuMessage m = WxMpKefuMessage.MPNEWSARTICLE().articleId("xxxxx").toUser(...).build();
+ * 
+ * + * @author JCLee + */ +public final class MpNewsArticleBuilder extends BaseBuilder{ + private String articleId; + + public MpNewsArticleBuilder() { + this.msgType = WxConsts.KefuMsgType.MP_NEWS_ARTICLE; + } + + public MpNewsArticleBuilder articleId(String articleId) { + this.articleId = articleId; + return this; + } + + @Override + public WxMpKefuMessage build() { + WxMpKefuMessage m = super.build(); + m.setMpNewsArticleId(this.articleId); + return m; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpGsonBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpGsonBuilder.java index 5f762cc6d..53c39a0c4 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpGsonBuilder.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpGsonBuilder.java @@ -16,6 +16,7 @@ import me.chanjar.weixin.mp.bean.subscribe.WxMpSubscribeMessage; import me.chanjar.weixin.mp.bean.template.WxMpTemplateIndustry; import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage; +import java.util.Objects; /** * @author someone @@ -23,6 +24,7 @@ public class WxMpGsonBuilder { private static final GsonBuilder INSTANCE = new GsonBuilder(); + private static volatile Gson GSON_INSTANCE; static { INSTANCE.disableHtmlEscaping(); @@ -64,7 +66,14 @@ public class WxMpGsonBuilder { } public static Gson create() { - return INSTANCE.create(); + if (Objects.isNull(GSON_INSTANCE)) { + synchronized (INSTANCE) { + if (Objects.isNull(GSON_INSTANCE)) { + GSON_INSTANCE = INSTANCE.create(); + } + } + } + return GSON_INSTANCE; } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpKefuMessageGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpKefuMessageGsonAdapter.java index 679f8db1a..c4b124452 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpKefuMessageGsonAdapter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpKefuMessageGsonAdapter.java @@ -96,6 +96,11 @@ public JsonElement serialize(WxMpKefuMessage message, Type typeOfSrc, JsonSerial messageJson.add("msgmenu", msgmenuJsonObject); break; } + case KefuMsgType.MP_NEWS_ARTICLE: + JsonObject mpNewsArticleJson = new JsonObject(); + mpNewsArticleJson.addProperty("article_id", message.getMpNewsArticleId()); + messageJson.add("mpnewsarticle", mpNewsArticleJson); + break; default: { throw new WxRuntimeException("非法消息类型,暂不支持"); } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassTagMessageGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassTagMessageGsonAdapter.java index b73540d1f..b75f11f68 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassTagMessageGsonAdapter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassTagMessageGsonAdapter.java @@ -56,7 +56,7 @@ public JsonElement serialize(WxMpMassTagMessage message, Type typeOfSrc, JsonSer messageJson.add(WxConsts.MassMsgType.MPVIDEO, sub); } messageJson.addProperty("msgtype", message.getMsgType()); - messageJson.addProperty("send_ignore_reprint", message.isSendIgnoreReprint() ? 0 : 1); + messageJson.addProperty("send_ignore_reprint", message.isSendIgnoreReprint() ? 1 : 0); if (StringUtils.isNotEmpty(message.getClientMsgId())) { messageJson.addProperty("clientmsgid", message.getClientMsgId()); diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/kefu/WxMpKefuMessageTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/kefu/WxMpKefuMessageTest.java index ec754875d..e74f200b4 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/kefu/WxMpKefuMessageTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/kefu/WxMpKefuMessageTest.java @@ -166,4 +166,13 @@ public void testMsgMenuBuild() { "{\"touser\":\"OPENID\",\"msgtype\":\"msgmenu\",\"msgmenu\":{\"head_content\":\"head_content\",\"list\":[{\"id\":\"101\",\"content\":\"msgmenu1\"},{\"id\":\"102\",\"content\":\"msgmenu2\"}],\"tail_content\":\"tail_content\"}}"); } + public void testMpNewsArticleBuilder() { + WxMpKefuMessage reply = WxMpKefuMessage.MPNEWSARTICLE() + .toUser("OPENID") + .articleId("ARTICLE_ID") + .build(); + Assert.assertEquals(reply.toJson(), + "{\"touser\":\"OPENID\",\"msgtype\":\"mpnewsarticle\",\"mpnewsarticle\":{\"article_id\":\"ARTICLE_ID\"}}"); + } + } diff --git a/weixin-java-open/pom.xml b/weixin-java-open/pom.xml index 1676d1c01..5d77bf3b9 100644 --- a/weixin-java-open/pom.xml +++ b/weixin-java-open/pom.xml @@ -7,7 +7,7 @@ com.github.binarywang wx-java - 4.2.0 + 4.3.0 weixin-java-open diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenComponentService.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenComponentService.java index c25acdf1a..d56d8d432 100644 --- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenComponentService.java +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenComponentService.java @@ -120,6 +120,21 @@ public interface WxOpenComponentService { */ String FAST_REGISTER_WEAPP_SEARCH_URL = "https://api.weixin.qq.com/cgi-bin/component/fastregisterweapp?action=search"; + /** + * 快速创建个人小程序接口. + */ + String FAST_REGISTER_PERSONAL_WEAPP_URL = "https://api.weixin.qq.com/wxa/component/fastregisterpersonalweapp?action=create"; + + /** + * 查询快速创建个人小程序任务状态接口. + */ + String FAST_REGISTER_PERSONAL_WEAPP_SEARCH_URL = "https://api.weixin.qq.com/wxa/component/fastregisterpersonalweapp?action=query"; + + /** + * 快速创建试用小程序接口. + */ + String FAST_REGISTER_BETA_WEAPP_URL = "https://api.weixin.qq.com/wxa/component/fastregisterbetaweapp"; + /** * 代小程序实现业务. * 小程序代码模版库管理:https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1506504150_nMMh6&token=&lang=zh_CN @@ -581,6 +596,39 @@ public interface WxOpenComponentService { WxOpenResult fastRegisterWeappSearch(String name, String legalPersonaWechat, String legalPersonaName) throws WxErrorException; + /** + * https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/Register_Mini_Programs/fastregisterpersonalweapp.html + * 快速创建个人小程序 + * + * @param idname 个人用户名字 + * @param wxuser 个人用户微信号 + * @param componentPhone 第三方联系电话 + * @return the wx open result + * @throws WxErrorException + */ + WxOpenRegisterPersonalWeappResult fastRegisterPersonalWeapp(String idname, String wxuser, String componentPhone) throws WxErrorException; + + /** + * https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/Register_Mini_Programs/fastregisterpersonalweapp.html + * 查询个人小程序注册任务状态 + * + * @param taskid 任务ID + * @return the wx open result + * @throws WxErrorException + */ + WxOpenRegisterPersonalWeappResult fastRegisterPersonalWeappSearch(String taskid) throws WxErrorException; + + /** + * https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/beta_Mini_Programs/fastregister.html + * 注册试用小程序 + * + * @param name 小程序名称 + * @param openid 微信用户的openid(不是微信号) + * @return the wx open result + * @throws WxErrorException + */ + WxOpenRegisterBetaWeappResult fastRegisterBetaWeapp(String name, String openid) throws WxErrorException; + /** * https://api.weixin.qq.com/product/register/register_shop?component_access_token=xxxxxxxxx * 注册小商店账号 diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenMaPrivacyService.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenMaPrivacyService.java new file mode 100644 index 000000000..4bf78f53b --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenMaPrivacyService.java @@ -0,0 +1,65 @@ +package me.chanjar.weixin.open.api; + +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.open.bean.ma.privacy.GetPrivacySettingResult; +import me.chanjar.weixin.open.bean.ma.privacy.SetPrivacySetting; +import me.chanjar.weixin.open.bean.ma.privacy.UploadPrivacyFileResult; +import org.jetbrains.annotations.Nullable; + +/** + * 微信第三方平台 小程序用户隐私保护指引接口 + * https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/privacy_config/set_privacy_setting.html + * + * @author 广州跨界 + */ +public interface WxOpenMaPrivacyService { + + /** + * 1 设置小程序用户隐私保护指引 + */ + String OPEN_SET_PRIVACY_SETTING = "https://api.weixin.qq.com/cgi-bin/component/setprivacysetting"; + + /** + * 2 查询小程序用户隐私保护指引 + */ + String OPEN_GET_PRIVACY_SETTING = "https://api.weixin.qq.com/cgi-bin/component/getprivacysetting"; + + /** + * 3 上传小程序用户隐私保护指引文件 + */ + String OPEN_UPLOAD_PRIVACY_FILE = "https://api.weixin.qq.com/cgi-bin/component/uploadprivacyextfile"; + + + /** + * 查询小程序用户隐私保护指引 + * 文档地址:https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/privacy_config/get_privacy_setting.html + * + * @param privacyVer 1表示现网版本,即,传1则该接口返回的内容是现网版本的;2表示开发版,即,传2则该接口返回的内容是开发版本的。默认是2。 + * @return 查询结果 + * @throws WxErrorException 如果出错,抛出此异常 + */ + GetPrivacySettingResult getPrivacySetting(@Nullable Integer privacyVer) throws WxErrorException; + + + /** + * 设置小程序用户隐私保护指引 + * 文档地址:https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/privacy_config/set_privacy_setting.html + * + * @param dto 参数对象 + * @throws WxErrorException 如果出错,抛出此异常 + */ + void setPrivacySetting(SetPrivacySetting dto) throws WxErrorException; + + + /** + * 上传小程序用户隐私保护指引文件 + * 本接口用于上传自定义的小程序的用户隐私保护指引 + * 仅限文本文件, 限制文件大小为不超过100kb,否则会报错 + * 文档地址:https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/privacy_config/upload_privacy_exfile.html + * + * @param content 文本文件内容 + * @return 上传结果 + * @throws WxErrorException 如果出错,抛出此异常 + */ + UploadPrivacyFileResult uploadPrivacyFile(String content) throws WxErrorException; +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenMaService.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenMaService.java index cbb4b2378..f907ff9be 100644 --- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenMaService.java +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenMaService.java @@ -632,6 +632,13 @@ WxOpenMaDomainResult modifyDomain(String action, List requestDomains, Li */ WxOpenMaBasicService getBasicService(); + /** + * 小程序用户隐私保护指引服务 + * + * @return 小程序用户隐私保护指引服务 + */ + WxOpenMaPrivacyService getPrivacyService(); + /** * 小程序审核 提审素材上传接口 * diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenComponentServiceImpl.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenComponentServiceImpl.java index ac71523a7..28bd71cb5 100644 --- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenComponentServiceImpl.java +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenComponentServiceImpl.java @@ -626,6 +626,34 @@ public WxOpenResult fastRegisterWeappSearch(String name, String legalPersonaWech return WxOpenGsonBuilder.create().fromJson(response, WxOpenResult.class); } + @Override + public WxOpenRegisterPersonalWeappResult fastRegisterPersonalWeapp(String idname, String wxuser, String componentPhone) throws WxErrorException { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("idname", idname); + jsonObject.addProperty("wxuser", wxuser); + jsonObject.addProperty("component_phone", componentPhone); + String response = post(FAST_REGISTER_PERSONAL_WEAPP_URL, jsonObject.toString(), "component_access_token"); + return WxOpenGsonBuilder.create().fromJson(response, WxOpenRegisterPersonalWeappResult.class); + } + + @Override + public WxOpenRegisterPersonalWeappResult fastRegisterPersonalWeappSearch(String taskid) throws WxErrorException { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("taskid", taskid); + String response = post(FAST_REGISTER_PERSONAL_WEAPP_SEARCH_URL, jsonObject.toString(), "component_access_token"); + return WxOpenGsonBuilder.create().fromJson(response, WxOpenRegisterPersonalWeappResult.class); + } + + @Override + public WxOpenRegisterBetaWeappResult fastRegisterBetaWeapp(String name, String openid) throws WxErrorException { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("name", name); + jsonObject.addProperty("openid", openid); + String response = wxOpenService.getWxOpenComponentService() + .post(FAST_REGISTER_BETA_WEAPP_URL, jsonObject.toString(), "access_token"); + return WxOpenGsonBuilder.create().fromJson(response, WxOpenRegisterBetaWeappResult.class); + } + @Override public WxOpenResult registerShop(String wxName, String idCardName, String idCardNumber, String channelId, Integer apiOpenstoreType, String authPageUrl) throws WxErrorException { JsonObject jsonObject = new JsonObject(); diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInRedisTemplateConfigStorage.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInRedisTemplateConfigStorage.java index da04b176b..f7d8fdd45 100644 --- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInRedisTemplateConfigStorage.java +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInRedisTemplateConfigStorage.java @@ -10,8 +10,6 @@ import me.chanjar.weixin.common.redis.JedisWxRedisOps; import me.chanjar.weixin.common.redis.RedisTemplateWxRedisOps; import me.chanjar.weixin.common.redis.WxRedisOps; -import redis.clients.jedis.Jedis; -import redis.clients.jedis.util.Pool; /** *
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMaPrivacyServiceImpl.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMaPrivacyServiceImpl.java
new file mode 100644
index 000000000..f7deb523c
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMaPrivacyServiceImpl.java
@@ -0,0 +1,55 @@
+package me.chanjar.weixin.open.api.impl;
+
+import cn.binarywang.wx.miniapp.api.WxMaService;
+import lombok.AllArgsConstructor;
+import lombok.SneakyThrows;
+import me.chanjar.weixin.common.error.WxError;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.open.api.WxOpenMaPrivacyService;
+import me.chanjar.weixin.open.bean.ma.privacy.GetPrivacySettingResult;
+import me.chanjar.weixin.open.bean.ma.privacy.SetPrivacySetting;
+import me.chanjar.weixin.open.bean.ma.privacy.UploadPrivacyFileResult;
+import me.chanjar.weixin.open.util.json.WxOpenGsonBuilder;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 微信第三方平台 小程序用户隐私保护指引接口
+ * https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/privacy_config/set_privacy_setting.html
+ *
+ * @author 广州跨界
+ */
+@AllArgsConstructor
+public class WxOpenMaPrivacyServiceImpl implements WxOpenMaPrivacyService {
+
+  private final WxMaService wxMaService;
+
+
+  @Override
+  public GetPrivacySettingResult getPrivacySetting(@Nullable Integer privacyVer) throws WxErrorException {
+    Map params = new HashMap<>();
+    if (privacyVer != null) {
+      params.put("privacy_ver", privacyVer);
+    }
+    String json = wxMaService.post(OPEN_GET_PRIVACY_SETTING, params);
+    return WxOpenGsonBuilder.create().fromJson(json, GetPrivacySettingResult.class);
+  }
+
+  @Override
+  public void setPrivacySetting(SetPrivacySetting dto) throws WxErrorException {
+    wxMaService.post(OPEN_SET_PRIVACY_SETTING, dto);
+  }
+
+  @SneakyThrows
+  @Override
+  public UploadPrivacyFileResult uploadPrivacyFile(String content) throws WxErrorException {
+    // TODO 应实现通过InputStream上传的功能,一下代码暂时无法正常运行
+//    ByteArrayInputStream data = new ByteArrayInputStream(content.getBytes("GBK"));
+//    GenericUploadRequestExecutor executor = new GenericUploadRequestExecutor(wxMaService.getRequestHttp(), "POST", "file", "/temp.txt");
+//    String json = wxMaService.execute(executor, OPEN_UPLOAD_PRIVACY_FILE, data);
+//    return WxOpenGsonBuilder.create().fromJson(json, UploadPrivacyFileResult.class);
+    throw new WxErrorException(new WxError(5003, "暂未实现用户隐私指引内容上传"));
+  }
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMaServiceImpl.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMaServiceImpl.java
index 4fb637b39..7188a669c 100644
--- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMaServiceImpl.java
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMaServiceImpl.java
@@ -15,6 +15,7 @@
 import me.chanjar.weixin.common.error.WxErrorException;
 import me.chanjar.weixin.open.api.WxOpenComponentService;
 import me.chanjar.weixin.open.api.WxOpenMaBasicService;
+import me.chanjar.weixin.open.api.WxOpenMaPrivacyService;
 import me.chanjar.weixin.open.api.WxOpenMaService;
 import me.chanjar.weixin.open.bean.ma.WxMaQrcodeParam;
 import me.chanjar.weixin.open.bean.ma.WxMaScheme;
@@ -42,12 +43,15 @@ public class WxOpenMaServiceImpl extends WxMaServiceImpl implements WxOpenMaServ
   private final String appId;
   @Getter
   private final WxOpenMaBasicService basicService;
+  @Getter
+  private final WxOpenMaPrivacyService privacyService;
 
   public WxOpenMaServiceImpl(WxOpenComponentService wxOpenComponentService, String appId, WxMaConfig wxMaConfig) {
     this.wxOpenComponentService = wxOpenComponentService;
     this.appId = appId;
     this.wxMaConfig = wxMaConfig;
     this.basicService = new WxOpenMaBasicServiceImpl(this);
+    this.privacyService = new WxOpenMaPrivacyServiceImpl(this);
     initHttp();
   }
 
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/WxOpenMaCodeTemplate.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/WxOpenMaCodeTemplate.java
index d441906c8..741a6f607 100644
--- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/WxOpenMaCodeTemplate.java
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/WxOpenMaCodeTemplate.java
@@ -46,4 +46,16 @@ public class WxOpenMaCodeTemplate implements Serializable {
    */
   @SerializedName(value = "createTime", alternate = "create_time")
   private Long createTime;
+
+  /**
+   * 开发小程序的appid
+   */
+  @SerializedName(value = "sourceMiniProgramAppid", alternate = "source_miniprogram_appid")
+  private String sourceMiniProgramAppid;
+
+  /**
+   * 开发小程序的名称
+   */
+  @SerializedName(value = "sourceMiniProgram", alternate = "source_miniprogram")
+  private String sourceMiniProgram;
 }
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/privacy/GetPrivacySettingResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/privacy/GetPrivacySettingResult.java
new file mode 100644
index 000000000..a3b0a6ef1
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/privacy/GetPrivacySettingResult.java
@@ -0,0 +1,118 @@
+package me.chanjar.weixin.open.bean.ma.privacy;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.Getter;
+import lombok.Setter;
+import me.chanjar.weixin.open.bean.result.WxOpenResult;
+
+import java.util.List;
+
+/**
+ * 查询小程序用户隐私保护指引 响应
+ *
+ * @author 广州跨界
+ */
+@Getter
+@Setter
+public class GetPrivacySettingResult extends WxOpenResult {
+
+  /**
+   * 代码是否存在, 0 不存在, 1 存在 。如果最近没有通过commit接口上传代码,则会出现 code_exist=0的情况。
+   */
+  @SerializedName("code_exist")
+  private Integer codeExist;
+
+  /**
+   * 代码检测出来的用户信息类型(privacy_key)
+   */
+  @SerializedName("privacy_list")
+  private List privacyList;
+
+  /**
+   * 要收集的用户信息配置
+   */
+  @SerializedName("setting_list")
+  private List settingList;
+
+  /**
+   * 更新时间
+   */
+  @SerializedName("update_time")
+  private Long updateTime;
+
+  /**
+   * 收集方(开发者)信息配置
+   */
+  @SerializedName("owner_setting")
+  private PrivacyOwnerSetting ownerSetting;
+
+  /**
+   * 收集方(开发者)信息配置
+   */
+  @SerializedName("privacy_desc")
+  private PrivacyDesc privacyDesc;
+
+
+  @Data
+  public static class Setting {
+
+    /**
+     * 官方的可选值参考下方说明;该字段也支持自定义
+     *
+     * @see PrivacyKeyEnum
+     * @see PrivacyKeyEnum#getKey()
+     */
+    @SerializedName("privacy_key")
+    private String privacyKey;
+
+    /**
+     * 请填写收集该信息的用途。例如privacy_key=Location(位置信息),那么privacy_text则填写收集位置信息的用途。
+     * 无需再带上“为了”或者“用于”这些字眼,小程序端的显示格式是为了xxx,因此开发者只需要直接填写用途即可。
+     */
+    @SerializedName("privacy_text")
+    private String privacyText;
+
+    /**
+     * 用户信息类型的中文名称
+     *
+     * @see PrivacyKeyEnum#getDesc() ()
+     */
+    @SerializedName("privacy_label")
+    private String privacyLabel;
+  }
+
+
+  @Data
+  public static class PrivacyDesc {
+
+    /**
+     * 用户信息类型
+     */
+    @SerializedName("privacy_desc_list")
+    private List privacyDescList;
+  }
+
+  @Data
+  public static class PrivacyDescItem {
+
+    /**
+     * 用户信息类型的英文key
+     *
+     * @see PrivacyKeyEnum
+     * @see PrivacyKeyEnum#getKey()
+     */
+    @SerializedName("privacy_key")
+    private String privacyKey;
+
+    /**
+     * 用户信息类型的中文描述
+     *
+     * @see PrivacyKeyEnum#getDesc()
+     */
+    @SerializedName("privacy_desc")
+    private String privacyDesc;
+  }
+
+
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/privacy/PrivacyKeyEnum.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/privacy/PrivacyKeyEnum.java
new file mode 100644
index 000000000..c3d6af281
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/privacy/PrivacyKeyEnum.java
@@ -0,0 +1,62 @@
+package me.chanjar.weixin.open.bean.ma.privacy;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 隐私key枚举
+ *
+ * @author 广州跨界
+ */
+@Getter
+@AllArgsConstructor
+public enum PrivacyKeyEnum {
+
+  USER_INFO("UserInfo", "用户信息(微信昵称、头像)"),
+
+  LOCATION("Location", "位置信息"),
+
+  ADDRESS("Address", "地址"),
+
+  INVOICE("Invoice", "发票信息"),
+
+  RUN_DATA("RunData", "微信运动数据"),
+
+  RECORD("Record", "麦克风"),
+
+  ALBUM("Album", "选中的照片或视频信息"),
+
+  CAMERA("Camera", "摄像头"),
+
+  PHONE_NUMBER("PhoneNumber", "手机号码"),
+
+  CONTACT("Contact", "通讯录(仅写入)权限"),
+
+  DEVICE_INFO("DeviceInfo", "设备信息"),
+
+  EXID_NUMBER("EXIDNumber", "身份证号码"),
+
+  EX_ORDER_INFO("EXOrderInfo", "订单信息"),
+
+  EX_USER_PUBLISH_CONTENT("EXUserPublishContent", "发布内容"),
+
+  EX_USER_FOLLOW_ACCT("EXUserFollowAcct", "所关注账号"),
+
+  EX_USER_OP_LOG("EXUserOpLog", "操作日志"),
+
+  ALBUM_WRITE_ONLY("AlbumWriteOnly", "相册(仅写入)权限"),
+
+  LICENSE_PLATE("LicensePlate", "车牌号"),
+
+  BLUE_TOOTH("BlueTooth", "蓝牙"),
+
+  CALENDAR_WRITE_ONLY("CalendarWriteOnly", "日历(仅写入)权限"),
+
+  EMAIL("Email", "邮箱"),
+
+  MESSAGE_FILE("MessageFile", "选中的文件"),
+  ;
+
+  private final String key;
+  private final String desc;
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/privacy/PrivacyOwnerSetting.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/privacy/PrivacyOwnerSetting.java
new file mode 100644
index 000000000..a52d0588a
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/privacy/PrivacyOwnerSetting.java
@@ -0,0 +1,65 @@
+package me.chanjar.weixin.open.bean.ma.privacy;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * 小程序用户隐私保护指引 收集方(开发者)信息配置
+ *
+ * @author 广州跨界
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class PrivacyOwnerSetting {
+
+  /**
+   * 信息收集方(开发者)的邮箱地址,4种联系方式至少要填一种
+   */
+  @SerializedName("contact_email")
+  private String contactEmail;
+
+  /**
+   * 信息收集方(开发者)的手机号,4种联系方式至少要填一种
+   */
+  @SerializedName("contact_phone")
+  private String contactPhone;
+
+  /**
+   * 信息收集方(开发者)的qq号,4种联系方式至少要填一种
+   */
+  @SerializedName("contact_qq")
+  private String contactQq;
+
+  /**
+   * 信息收集方(开发者)的微信号,4种联系方式至少要填一种
+   */
+  @SerializedName("contact_weixin")
+  private String contactWeixin;
+
+  /**
+   * 如果开发者不使用微信提供的标准化用户隐私保护指引模板,也可以上传自定义的用户隐私保护指引,通过上传接口上传后可获取media_id
+   */
+  @SerializedName("ext_file_media_id")
+  private String extFileMediaId;
+
+  /**
+   * 通知方式,指的是当开发者收集信息有变动时,通过该方式通知用户。这里服务商需要按照实际情况填写,例如通过弹窗或者公告或者其他方式。
+   */
+  @NotNull
+  @SerializedName("notice_method")
+  private String noticeMethod;
+
+  /**
+   * 存储期限,指的是开发者收集用户信息存储多久。如果不填则展示为【开发者承诺,除法律法规另有规定,开发者对你的信息保存期限应当为实现处理目的所必要的最短时间】,
+   * 如果填请填数字+天,例如“30天”,否则会出现87072的报错。
+   */
+  @SerializedName("store_expire_timestamp")
+  private String storeExpireTimestamp;
+
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/privacy/SetPrivacySetting.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/privacy/SetPrivacySetting.java
new file mode 100644
index 000000000..d74b86d45
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/privacy/SetPrivacySetting.java
@@ -0,0 +1,68 @@
+package me.chanjar.weixin.open.bean.ma.privacy;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+/**
+ * 设置小程序用户隐私保护指引参数
+ *
+ * @author 广州跨界
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class SetPrivacySetting {
+
+  /**
+   * 用户隐私保护指引的版本,1表示现网版本;2表示开发版。默认是2开发版。
+   */
+  @SerializedName("privacy_ver")
+  private Integer privacyVer;
+
+  /**
+   * 收集方(开发者)信息配置
+   */
+  @NotNull
+  @SerializedName("owner_setting")
+  private PrivacyOwnerSetting ownerSetting;
+
+  /**
+   * 要收集的用户信息配置
+   */
+  @NotNull
+  @SerializedName("setting_list")
+  private List settingList;
+
+
+  @Data
+  @Builder
+  @NoArgsConstructor
+  @AllArgsConstructor
+  public static class Setting {
+
+    /**
+     * 官方的可选值参考下方说明;该字段也支持自定义
+     *
+     * @see PrivacyKeyEnum
+     * @see PrivacyKeyEnum#getKey()
+     */
+    @NotNull
+    @SerializedName("privacy_key")
+    private String privacyKey;
+
+    /**
+     * 请填写收集该信息的用途。例如privacy_key=Location(位置信息),那么privacy_text则填写收集位置信息的用途。
+     * 无需再带上“为了”或者“用于”这些字眼,小程序端的显示格式是为了xxx,因此开发者只需要直接填写用途即可。
+     */
+    @NotNull
+    @SerializedName("privacy_text")
+    private String privacyText;
+  }
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/privacy/UploadPrivacyFileResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/privacy/UploadPrivacyFileResult.java
new file mode 100644
index 000000000..d2fcfecc5
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/privacy/UploadPrivacyFileResult.java
@@ -0,0 +1,22 @@
+package me.chanjar.weixin.open.bean.ma.privacy;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Getter;
+import lombok.Setter;
+import me.chanjar.weixin.open.bean.result.WxOpenResult;
+
+/**
+ * 上传小程序用户隐私保护指引文件 响应
+ *
+ * @author 广州跨界
+ */
+@Getter
+@Setter
+public class UploadPrivacyFileResult extends WxOpenResult {
+
+  /**
+   * 文件的media_id
+   */
+  @SerializedName("ext_file_media_id")
+  private String extFileMediaId;
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/message/WxOpenXmlMessage.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/message/WxOpenXmlMessage.java
index 7a4135592..dc99fcc18 100644
--- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/message/WxOpenXmlMessage.java
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/message/WxOpenXmlMessage.java
@@ -102,6 +102,21 @@ public static class Info implements Serializable {
     @XStreamAlias("component_phone")
     @XStreamConverter(value = XStreamCDataConverter.class)
     private String componentPhone;
+
+    // 创建个人小程序审核通知数据
+    @XStreamAlias("wxuser")
+    @XStreamConverter(value = XStreamCDataConverter.class)
+    private String wxuser;
+
+    @XStreamAlias("idname")
+    @XStreamConverter(value = XStreamCDataConverter.class)
+    private String idname;
+
+    // 创建试用小程序成功/失败的通知数据
+    @XStreamAlias("unique_id")
+    @XStreamConverter(value = XStreamCDataConverter.class)
+    private String uniqueId;
+
   }
 
   public static String wxMpOutXmlMessageToEncryptedXml(WxMpXmlOutMessage message, WxOpenConfigStorage wxOpenConfigStorage) {
@@ -132,7 +147,7 @@ public static WxOpenXmlMessage fromXml(InputStream is) {
   public static WxOpenXmlMessage fromEncryptedXml(String encryptedXml, WxOpenConfigStorage wxOpenConfigStorage,
                                                   String timestamp, String nonce, String msgSignature) {
     WxOpenCryptUtil cryptUtil = new WxOpenCryptUtil(wxOpenConfigStorage);
-    String plainText = cryptUtil.decrypt(msgSignature, timestamp, nonce, encryptedXml);
+    String plainText = cryptUtil.decryptXml(msgSignature, timestamp, nonce, encryptedXml);
     log.debug("解密后的原始xml消息内容:{}", plainText);
     return fromXml(plainText);
   }
@@ -140,7 +155,7 @@ public static WxOpenXmlMessage fromEncryptedXml(String encryptedXml, WxOpenConfi
   public static WxMpXmlMessage fromEncryptedMpXml(String encryptedXml, WxOpenConfigStorage wxOpenConfigStorage,
                                                   String timestamp, String nonce, String msgSignature) {
     WxOpenCryptUtil cryptUtil = new WxOpenCryptUtil(wxOpenConfigStorage);
-    String plainText = cryptUtil.decrypt(msgSignature, timestamp, nonce, encryptedXml);
+    String plainText = cryptUtil.decryptXml(msgSignature, timestamp, nonce, encryptedXml);
     return WxMpXmlMessage.fromXml(plainText);
   }
 
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaHistoryVersionResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaHistoryVersionResult.java
index 61b9f5ddd..d1e7ede13 100644
--- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaHistoryVersionResult.java
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaHistoryVersionResult.java
@@ -18,8 +18,8 @@
 public class WxOpenMaHistoryVersionResult extends WxOpenResult {
   private static final long serialVersionUID = 4102311851687901079L;
 
-  @SerializedName("template_list")
-  List templateList;
+  @SerializedName("version_list")
+  List versions;
 
   @Override
   public String toString() {
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenRegisterBetaWeappResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenRegisterBetaWeappResult.java
new file mode 100644
index 000000000..735a26eca
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenRegisterBetaWeappResult.java
@@ -0,0 +1,14 @@
+package me.chanjar.weixin.open.bean.result;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class WxOpenRegisterBetaWeappResult extends WxOpenResult {
+  @SerializedName("authorize_url")
+  private String authorizeUrl;
+  @SerializedName("unique_id")
+  protected String uniqueId;
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenRegisterPersonalWeappResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenRegisterPersonalWeappResult.java
new file mode 100644
index 000000000..18ef23e39
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenRegisterPersonalWeappResult.java
@@ -0,0 +1,15 @@
+package me.chanjar.weixin.open.bean.result;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class WxOpenRegisterPersonalWeappResult extends WxOpenResult {
+  private String taskid;
+  @SerializedName("authorize_url")
+  private String authorizeUrl;
+  @SerializedName("status")
+  private Integer status;
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/executor/GenericUploadRequestExecutor.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/executor/GenericUploadRequestExecutor.java
new file mode 100644
index 000000000..e2d43a96a
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/executor/GenericUploadRequestExecutor.java
@@ -0,0 +1,184 @@
+package me.chanjar.weixin.open.executor;
+
+import com.google.common.io.Files;
+import jodd.http.HttpConnectionProvider;
+import jodd.http.HttpRequest;
+import jodd.http.HttpResponse;
+import jodd.http.ProxyInfo;
+import lombok.Data;
+import me.chanjar.weixin.common.enums.WxType;
+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.Utf8ResponseHandler;
+import okhttp3.*;
+import org.apache.commons.io.IOUtils;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpHost;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.*;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.mime.HttpMultipartMode;
+import org.apache.http.entity.mime.MultipartEntityBuilder;
+import org.apache.http.impl.client.CloseableHttpClient;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Objects;
+
+/**
+ * 通用的上传请求执行器
+ */
+public class GenericUploadRequestExecutor implements RequestExecutor {
+
+  private final Executor executor;
+
+  /**
+   * 构造通用执行器
+   *
+   * @param requestHttp http请求
+   * @param httpMethod  http方法(POST PUT PATCH)
+   * @param paramName   参数名
+   * @param fileName    文件名
+   */
+  @SuppressWarnings("all")
+  public GenericUploadRequestExecutor(RequestHttp requestHttp, String httpMethod, String paramName, String fileName) {
+    switch (requestHttp.getRequestType()) {
+      case APACHE_HTTP:
+        executor = new ApacheExecutor();
+        break;
+      case OK_HTTP:
+        executor = new OkExecutor();
+        break;
+      case JODD_HTTP:
+        executor = new JoddExecutor();
+        break;
+      default:
+        throw new UnsupportedOperationException("使用了暂不支持的HTTP客户端:" + requestHttp.getRequestType());
+    }
+    executor.setRequestHttp((RequestHttp) requestHttp);
+    executor.setHttpMethod(httpMethod);
+    executor.setParamName(paramName);
+    executor.setFileName(fileName);
+  }
+
+  @Override
+  public String execute(String uri, InputStream data, WxType wxType) throws WxErrorException, IOException {
+    String json = executor.execute(uri, data, wxType);
+    WxError error = WxError.fromJson(json, wxType);
+    if (error.getErrorCode() != 0) {
+      throw new WxErrorException(error);
+    }
+    return json;
+  }
+
+  @Override
+  public void execute(String uri, InputStream data, ResponseHandler handler, WxType wxType) throws WxErrorException, IOException {
+    handler.handle(this.execute(uri, data, wxType));
+  }
+
+  /**
+   * 内部请求执行器
+   *
+   * @param  http客户端
+   * @param   http代理
+   */
+  @Data
+  public static abstract class Executor {
+
+    private RequestHttp requestHttp;
+    private String httpMethod;
+    private String paramName;
+    private String fileName;
+
+    public abstract String execute(String uri, InputStream data, WxType wxType) throws WxErrorException, IOException;
+  }
+
+  /**
+   * 阿帕奇执行器
+   */
+  public static class ApacheExecutor extends Executor {
+
+    @Override
+    public String execute(String uri, InputStream data, WxType wxType) throws WxErrorException, IOException {
+      HttpEntityEnclosingRequestBase bodyRequest;
+      switch (getHttpMethod()) {
+        case "POST":
+          bodyRequest = new HttpPost(uri);
+          break;
+        case "PUT":
+          bodyRequest = new HttpPut(uri);
+          break;
+        case "PATCH":
+          bodyRequest = new HttpPatch(uri);
+          break;
+        default:
+          throw new IllegalAccessError("不支持的请求方式:" + getHttpMethod());
+      }
+      if (getRequestHttp().getRequestHttpProxy() != null) {
+        RequestConfig config = RequestConfig.custom().setProxy(getRequestHttp().getRequestHttpProxy()).build();
+        bodyRequest.setConfig(config);
+      }
+
+      HttpEntity entity = MultipartEntityBuilder
+        .create()
+        .addBinaryBody(getParamName(), data, ContentType.create("multipart/form-data", StandardCharsets.UTF_8),
+          getFileName())
+        .setMode(HttpMultipartMode.RFC6532)
+        .build();
+      bodyRequest.setEntity(entity);
+      bodyRequest.setHeader("Content-Type", ContentType.MULTIPART_FORM_DATA.toString());
+
+      try (CloseableHttpResponse response = getRequestHttp().getRequestHttpClient().execute(bodyRequest)) {
+        return Utf8ResponseHandler.INSTANCE.handleResponse(response);
+      } finally {
+        bodyRequest.releaseConnection();
+      }
+    }
+  }
+
+  /**
+   * ok执行器
+   */
+  public static class OkExecutor extends Executor {
+
+    @Override
+    public String execute(String uri, InputStream data, WxType wxType) throws WxErrorException, IOException {
+      OkHttpClient client = getRequestHttp().getRequestHttpClient();
+      byte[] bytes = IOUtils.toByteArray(data);
+      RequestBody body = new MultipartBody.Builder()
+        .setType(Objects.requireNonNull(MediaType.parse("multipart/form-data")))
+        .addFormDataPart("media", getFileName(), RequestBody.create(bytes, MediaType.parse("application/octet-stream")))
+        .build();
+
+      Request request = new Request.Builder().url(uri).method(getHttpMethod(), body).build();
+      Response response = client.newCall(request).execute();
+      return response.body().string();
+    }
+  }
+
+  /**
+   * jodd执行器
+   */
+  public static class JoddExecutor extends Executor {
+
+    @Override
+    public String execute(String uri, InputStream data, WxType wxType) throws WxErrorException, IOException {
+      HttpRequest request = HttpRequest.post(uri);
+      if (getRequestHttp().getRequestHttpProxy() != null) {
+        getRequestHttp().getRequestHttpClient().useProxy(getRequestHttp().getRequestHttpProxy());
+      }
+      request.withConnectionProvider(getRequestHttp().getRequestHttpClient());
+
+      request.form(getParamName(), data);
+
+      HttpResponse response = request.send();
+      response.charset(StandardCharsets.UTF_8.name());
+      return response.bodyText();
+    }
+  }
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenGsonBuilder.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenGsonBuilder.java
index 325383ebf..5dbae037a 100644
--- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenGsonBuilder.java
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenGsonBuilder.java
@@ -7,6 +7,7 @@
 import me.chanjar.weixin.open.bean.auth.WxOpenAuthorizationInfo;
 import me.chanjar.weixin.open.bean.auth.WxOpenAuthorizerInfo;
 import me.chanjar.weixin.open.bean.result.*;
+import java.util.Objects;
 
 /**
  * @author 007
@@ -14,6 +15,7 @@
 public class WxOpenGsonBuilder {
 
   private static final GsonBuilder INSTANCE = new GsonBuilder();
+  private static volatile Gson GSON_INSTANCE;
 
   static {
     INSTANCE.disableHtmlEscaping();
@@ -26,11 +28,17 @@ public class WxOpenGsonBuilder {
     INSTANCE.registerTypeAdapter(WxOpenAuthorizerOptionResult.class, new WxOpenAuthorizerOptionResultGsonAdapter());
     INSTANCE.registerTypeAdapter(WxFastMaAccountBasicInfoResult.class, new WxFastMaAccountBasicInfoGsonAdapter());
     INSTANCE.registerTypeAdapter(WxOpenAuthorizerListResult.class, new WxOpenAuthorizerListResultGsonAdapter());
-
   }
 
   public static Gson create() {
-    return INSTANCE.create();
+    if (Objects.isNull(GSON_INSTANCE)) {
+      synchronized (INSTANCE) {
+        if (Objects.isNull(GSON_INSTANCE)) {
+          GSON_INSTANCE = INSTANCE.create();
+        }
+      }
+    }
+    return GSON_INSTANCE;
   }
 
 }
diff --git a/weixin-java-pay/pom.xml b/weixin-java-pay/pom.xml
index b6f9dfb24..6ecd99596 100644
--- a/weixin-java-pay/pom.xml
+++ b/weixin-java-pay/pom.xml
@@ -5,7 +5,7 @@
   
     com.github.binarywang
     wx-java
-    4.2.0
+    4.3.0
   
   4.0.0
 
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/ComplaintDetailRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/ComplaintDetailRequest.java
new file mode 100644
index 000000000..2e8f23db1
--- /dev/null
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/ComplaintDetailRequest.java
@@ -0,0 +1,36 @@
+package com.github.binarywang.wxpay.bean.complaint;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 微信消费者投诉2.0
+ * 查询投诉单详情请求实体
+ *
+ * @author jmdhappy
+ * @date 2022-3-19
+ */
+@Data
+@Builder(builderMethodName = "newBuilder")
+@NoArgsConstructor
+@AllArgsConstructor
+public class ComplaintDetailRequest implements Serializable {
+
+  private static final long serialVersionUID = 3244929701614280801L;
+
+  /**
+   * 
+   * 字段名:投诉单号
+   * 是否必填:是
+   * 描述:投诉单对应的投诉单号
+   * 
+ */ + @SerializedName("complaint_id") + private String complaintId; + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/ComplaintDetailResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/ComplaintDetailResult.java new file mode 100644 index 000000000..f62c9c1dd --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/ComplaintDetailResult.java @@ -0,0 +1,236 @@ +package com.github.binarywang.wxpay.bean.complaint; + + +import com.github.binarywang.wxpay.v3.SpecEncrypt; +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 微信消费者投诉2.0 + * 查询投诉单列表返回的实体 + * + * @author jmdhappy + * @date 2022-3-19 + */ +@Data +public class ComplaintDetailResult implements Serializable { + + private static final long serialVersionUID = -6201692411535927503L; + + /** + *
+   * 字段名:投诉单号
+   * 是否必填:是
+   * 描述:投诉单对应的投诉单号
+   * 
+ */ + @SerializedName("complaint_id") + private String complaintId; + + /** + *
+   * 字段名:投诉时间
+   * 是否必填:是
+   * 描述:投诉时间,遵循rfc3339标准格式,格式为yyyy-MM-DDTHH:mm:ss.sss+TIMEZONE,yyyy-MM-DD表示年月日,
+   * T出现在字符串中,表示time元素的开头,HH:mm:ss.sss表示时分秒毫秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。
+   * 例如:2015-05-20T13:29:35.120+08:00表示北京时间2015年05月20日13点29分35秒
+   * 示例值:2015-05-20T13:29:35.120+08:00
+   * 
+ */ + @SerializedName("complaint_time") + private String complaintTime; + + /** + *
+   * 字段名:投诉详情
+   * 是否必填:是
+   * 投诉的具体描述
+   * 
+ */ + @SerializedName("complaint_detail") + private String complaintDetail; + + /** + *
+   * 字段名:被诉商户号
+   * 是否必填:是
+   * 投诉单对应的被诉商户号。
+   * 
+ */ + @SerializedName("complainted_mchid") + private String complaintedMchid; + + /** + *
+   * 字段名:投诉单状态
+   * 是否必填:是
+   * 标识当前投诉单所处的处理阶段,具体状态如下所示:
+   * PENDING:待处理
+   * PROCESSING:处理中
+   * PROCESSED:已处理完成
+   * 
+ */ + @SerializedName("complaint_state") + private String complaintState; + + /** + *
+   * 字段名:投诉人联系方式
+   * 是否必填:否
+   * 投诉人联系方式。该字段已做加密处理,具体解密方法详见敏感信息加密说明。
+   * 
+ */ + @SerializedName("payer_phone") + @SpecEncrypt + private String payerPhone; + + /** + *
+   * 字段名:投诉人openid
+   * 是否必填:是
+   * 投诉人在商户appid下的唯一标识
+   * 
+ */ + @SerializedName("payer_openid") + private String payerOpenid; + + + /** + *
+   * 字段名:投诉资料列表
+   * 是否必填:是
+   * 用户上传的投诉相关资料,包括图片凭证等
+   * 
+ */ + @SerializedName("complaint_media_list") + private List complaintMediaList; + + @Data + public static class ComplaintMedia implements Serializable { + private static final long serialVersionUID = 4240983048700956803L; + + /** + *
+     * 字段名:媒体文件业务类型
+     * 是否必填:是
+     * 描述:
+     * 媒体文件对应的业务类型
+     * USER_COMPLAINT_IMAGE:用户投诉图片,用户提交投诉时上传的图片凭证
+     * OPERATION_IMAGE:操作流水图片,用户、商户、微信支付客服在协商解决投诉时,上传的图片凭证
+     * 注:用户上传的图片凭证会以白名单的形式提供给商户,若希望查看用户图片,联系微信支付客服
+     * 示例值:USER_COMPLAINT_IMAGE
+     * 
+ */ + @SerializedName("media_type") + private String mediaType; + + /** + *
+     * 字段名:媒体文件请求url
+     * 是否必填:是
+     * 描述:
+     * 微信返回的媒体文件请求url
+     * 
+ */ + @SerializedName("media_url") + private String mediaUrl; + + } + + /** + *
+   * 字段名:投诉单关联订单信息
+   * 是否必填:是
+   * 投诉单关联订单信息
+   * 注:投诉单和订单目前是一对一关系,array是预留未来一对多的扩展
+   * 
+ */ + @SerializedName("complaint_order_info") + private List complaintOrderInfo; + + @Data + public static class ComplaintOrder implements Serializable { + private static final long serialVersionUID = 4240983048700956804L; + + /** + *
+     * 字段名:微信订单号
+     * 是否必填:是
+     * 描述:
+     * 投诉单关联的微信订单号
+     * 
+ */ + @SerializedName("transaction_id") + private String transactionId; + + /** + *
+     * 字段名:商户订单号
+     * 是否必填:是
+     * 描述:
+     * 投诉单关联的商户订单号
+     * 
+ */ + @SerializedName("out_trade_no") + private String outTradeNo; + + /** + *
+     * 字段名:订单金额
+     * 是否必填:是
+     * 描述:
+     * 订单金额,单位(分)
+     * 
+ */ + @SerializedName("amount") + private Integer amount; + + } + + /** + *
+   * 字段名:投诉单是否已全额退款
+   * 是否必填:是
+   * 描述:
+   * 投诉单下所有订单是否已全部全额退款
+   * 
+ */ + @SerializedName("complaint_full_refunded") + private Boolean complaintFullRefunded; + + /** + *
+   * 字段名:是否有待回复的用户留言
+   * 是否必填:是
+   * 描述:
+   * 投诉单是否有待回复的用户留言
+   * 
+ */ + @SerializedName("incoming_user_response") + private Boolean incomingUserResponse; + + /** + *
+   * 字段名:问题描述
+   * 是否必填:是
+   * 描述:
+   * 用户发起投诉前选择的faq标题(2021年7月15日之后的投诉单均包含此信息)
+   * 
+ */ + @SerializedName("problem_description") + private String problemDescription; + + /** + *
+   * 字段名:用户投诉次数
+   * 是否必填:是
+   * 描述:
+   * 用户投诉次数。用户首次发起投诉记为1次,用户每有一次继续投诉就加1
+   * 
+ */ + @SerializedName("user_complaint_times") + private Integer userComplaintTimes; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/ComplaintNotifyUrlRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/ComplaintNotifyUrlRequest.java new file mode 100644 index 000000000..28a51bd02 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/ComplaintNotifyUrlRequest.java @@ -0,0 +1,36 @@ +package com.github.binarywang.wxpay.bean.complaint; + +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 微信消费者投诉2.0 + * 投诉通知请求实体 + * + * @author jmdhappy + * @date 2022-3-19 + */ +@Data +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor +public class ComplaintNotifyUrlRequest implements Serializable { + + private static final long serialVersionUID = -1L; + + /** + *
+   * 字段名:通知地址
+   * 是否必填:是
+   * 描述:通知地址,仅支持https。
+   * 
+ */ + @SerializedName("url") + private String url; + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/ComplaintNotifyUrlResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/ComplaintNotifyUrlResult.java new file mode 100644 index 000000000..5254201e6 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/ComplaintNotifyUrlResult.java @@ -0,0 +1,44 @@ +package com.github.binarywang.wxpay.bean.complaint; + + +import com.github.binarywang.wxpay.bean.media.MarketingImageUploadResult; +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +import java.io.Serializable; +import java.util.List; + +/** + * 微信消费者投诉2.0 + * 投诉通知地址返回的实体 + * + * @author jmdhappy + * @date 2022-3-19 + */ +@Data +public class ComplaintNotifyUrlResult implements Serializable { + + private static final long serialVersionUID = -6201692411535927502L; + + /** + *
+   * 字段名:商户号
+   * 是否必填:是
+   * 描述:返回创建回调地址的商户号,由微信支付生成并下发。
+   * 
+ */ + @SerializedName("mchid") + private String mchid; + + /** + *
+   * 字段名:通知地址
+   * 是否必填:是
+   * 描述:通知地址,仅支持https。
+   * 
+ */ + @SerializedName("url") + private String url; + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/ComplaintRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/ComplaintRequest.java new file mode 100644 index 000000000..b53a1b590 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/ComplaintRequest.java @@ -0,0 +1,77 @@ +package com.github.binarywang.wxpay.bean.complaint; + +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 微信消费者投诉2.0 + * 查询投诉单列表请求实体 + * + * @author jmdhappy + * @date 2022-3-19 + */ +@Data +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor +public class ComplaintRequest implements Serializable { + + private static final long serialVersionUID = 3244929701614280800L; + + /** + *
+   * 字段名:分页大小
+   * 是否必填:否
+   * 描述:设置该次请求返回的最大投诉条数,范围【1,50】,商户自定义字段,不传默认为10。
+   * 注:如遇到提示“当前查询结果数据量过大”,是回包触发微信支付下行数据包大小限制,请缩小入参limit并重试。
+   * 
+ */ + @SerializedName("limit") + private Integer limit = 10; + + /** + *
+   * 字段名:分页开始位置
+   * 是否必填:否
+   * 描述:该次请求的分页开始位置,从0开始计数,例如offset=10,表示从第11条记录开始返回,不传默认为0 。
+   * 
+ */ + @SerializedName("offset") + private Integer offset = 0; + + /** + *
+   * 字段名:开始日期
+   * 是否必填:是
+   * 描述:投诉发生的开始日期,格式为yyyy-MM-DD。注意,查询日期跨度不超过30天,当前查询为实时查询
+   * 
+ */ + @SerializedName("begin_date") + private String beginDate; + + /** + *
+   * 字段名:结束日期
+   * 是否必填:是
+   * 描述:投诉发生的结束日期,格式为yyyy-MM-DD。注意,查询日期跨度不超过30天,当前查询为实时查询
+   * 
+ */ + @SerializedName("end_date") + private String endDate; + + /** + *
+   * 字段名:被诉商户号
+   * 是否必填:否
+   * 描述:投诉单对应的被诉商户号。
+   * 
+ */ + @SerializedName("complainted_mchid") + private String complaintedMchid; + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/ComplaintResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/ComplaintResult.java new file mode 100644 index 000000000..1ee346d53 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/ComplaintResult.java @@ -0,0 +1,58 @@ +package com.github.binarywang.wxpay.bean.complaint; + + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 微信消费者投诉2.0 + * 查询投诉单列表返回的实体 + * + * @author jmdhappy + * @date 2022-3-19 + */ +@Data +public class ComplaintResult implements Serializable { + + private static final long serialVersionUID = -6201692411535927502L; + + /** + *
+   * 字段名:分页大小
+   * 是否必填:是
+   * 描述:设置该次请求返回的最大投诉条数,范围【1,50】
+   * 
+ */ + @SerializedName("limit") + private Integer limit; + + /** + *
+   * 字段名:分页开始位置
+   * 是否必填:是
+   * 描述:该次请求的分页开始位置,从0开始计数,例如offset=10,表示从第11条记录开始返回。
+   * 
+ */ + @SerializedName("offset") + private Integer offset; + + /** + *
+   * 字段名:投诉总条数
+   * 是否必填:否
+   * 描述:投诉总条数,当offset=0时返回
+   * 
+ */ + @SerializedName("total_count") + private Integer totalCount; + + /** + * 用户投诉信息详情 + */ + @SerializedName("data") + private List data; + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/CompleteRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/CompleteRequest.java new file mode 100644 index 000000000..a4d066df9 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/CompleteRequest.java @@ -0,0 +1,48 @@ +package com.github.binarywang.wxpay.bean.complaint; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 微信消费者投诉2.0 + * 反馈处理完成请求实体 + * + * @author jmdhappy + * @date 2022-3-19 + */ +@Data +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor +public class CompleteRequest implements Serializable { + + private static final long serialVersionUID = 3243229701614220801L; + + /** + *
+   * 字段名:投诉单号
+   * 是否必填:是
+   * 描述:投诉单对应的投诉单号
+   * 
+ */ + @SerializedName("complaint_id") + @Expose + private String complaintId; + + /** + *
+   * 字段名:被诉商户号
+   * 是否必填:是
+   * 描述:投诉单对应的被诉商户号
+   * 
+ */ + @SerializedName("complainted_mchid") + private String complaintedMchid; + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/NegotiationHistoryRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/NegotiationHistoryRequest.java new file mode 100644 index 000000000..3362e4a92 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/NegotiationHistoryRequest.java @@ -0,0 +1,57 @@ +package com.github.binarywang.wxpay.bean.complaint; + +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 微信消费者投诉2.0 + * 查询投诉协商历史请求实体 + * + * @author jmdhappy + * @date 2022-3-19 + */ +@Data +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor +public class NegotiationHistoryRequest implements Serializable { + + private static final long serialVersionUID = 3244929701614280806L; + + /** + *
+   * 字段名:投诉单号
+   * 是否必填:是
+   * 描述:投诉单对应的投诉单号
+   * 
+ */ + @SerializedName("complaint_id") + private String complaintId; + + /** + *
+   * 字段名:分页大小
+   * 是否必填:否
+   * 描述:设置该次请求返回的最大投诉条数,范围【1,50】,商户自定义字段,不传默认为10。
+   * 注:如遇到提示“当前查询结果数据量过大”,是回包触发微信支付下行数据包大小限制,请缩小入参limit并重试。
+   * 
+ */ + @SerializedName("limit") + private Integer limit = 10; + + /** + *
+   * 字段名:分页开始位置
+   * 是否必填:否
+   * 描述:该次请求的分页开始位置,从0开始计数,例如offset=10,表示从第11条记录开始返回,不传默认为0 。
+   * 
+ */ + @SerializedName("offset") + private Integer offset = 0; + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/NegotiationHistoryResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/NegotiationHistoryResult.java new file mode 100644 index 000000000..4e5ab4197 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/NegotiationHistoryResult.java @@ -0,0 +1,190 @@ +package com.github.binarywang.wxpay.bean.complaint; + + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 微信消费者投诉2.0 + * 查询投诉单协商历史返回的实体 + * + * @author jmdhappy + * @date 2022-3-19 + */ +@Data +public class NegotiationHistoryResult implements Serializable { + + private static final long serialVersionUID = -6201692411535927502L; + + /** + *
+   * 字段名:分页大小
+   * 是否必填:是
+   * 描述:设置该次请求返回的最大投诉条数,范围【1,50】
+   * 
+ */ + @SerializedName("limit") + private Integer limit; + + /** + *
+   * 字段名:分页开始位置
+   * 是否必填:是
+   * 描述:该次请求的分页开始位置,从0开始计数,例如offset=10,表示从第11条记录开始返回。
+   * 
+ */ + @SerializedName("offset") + private Integer offset; + + /** + *
+   * 字段名:投诉协商历史总条数
+   * 是否必填:否
+   * 描述:投诉协商历史总条数,当offset=0时返回
+   * 
+ */ + @SerializedName("total_count") + private Integer totalCount; + + /** + * 投诉协商历史 + */ + @SerializedName("data") + private List data; + + @Data + public static class NegotiationHistory implements Serializable { + private static final long serialVersionUID = 4240983048700956824L; + + /** + *
+     * 字段名:投诉资料列表
+     * 是否必填:是
+     * 用户上传的投诉相关资料,包括图片凭证等
+     * 
+ */ + @SerializedName("complaint_media_list") + private List complaintMediaList; + + @Data + public static class ComplaintMedia implements Serializable { + private static final long serialVersionUID = 4240983048700956803L; + + /** + *
+       * 字段名:媒体文件业务类型
+       * 是否必填:是
+       * 描述:
+       * 媒体文件对应的业务类型
+       * USER_COMPLAINT_IMAGE:用户投诉图片,用户提交投诉时上传的图片凭证
+       * OPERATION_IMAGE:操作流水图片,用户、商户、微信支付客服在协商解决投诉时,上传的图片凭证
+       * 注:用户上传的图片凭证会以白名单的形式提供给商户,若希望查看用户图片,联系微信支付客服
+       * 示例值:USER_COMPLAINT_IMAGE
+       * 
+ */ + @SerializedName("media_type") + private String mediaType; + + /** + *
+       * 字段名:媒体文件请求url
+       * 是否必填:是
+       * 描述:
+       * 微信返回的媒体文件请求url
+       * 
+ */ + @SerializedName("media_url") + private String mediaUrl; + + } + + /** + *
+     * 字段名:操作流水号
+     * 是否必填:是
+     * 描述:
+     * 操作流水号
+     * 
+ */ + @SerializedName("log_id") + private String logId; + + /** + *
+     * 字段名:操作人
+     * 是否必填:是
+     * 描述:
+     * 当前投诉协商记录的操作人
+     * 
+ */ + @SerializedName("operator") + private String operator; + + /** + *
+     * 字段名:操作时间
+     * 是否必填:是
+     * 描述:
+     * 当前投诉协商记录的操作时间,遵循rfc3339标准格式,格式为yyyy-MM-DDTHH:mm:ss.sss+TIMEZONE,yyyy-MM-DD表示年月日,
+     * T出现在字符串中,表示time元素的开头,HH:mm:ss.sss表示时分秒毫秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。
+     * 例如:2015-05-20T13:29:35.120+08:00表示北京时间2015年05月20日13点29分35秒。
+     * 示例值:2015-05-20T13:29:35.120+08:00
+     * 
+ */ + @SerializedName("operate_time") + private String operateTime; + + /** + *
+     * 字段名:操作类型
+     * 是否必填:是
+     * 描述:
+     * 当前投诉协商记录的操作类型,对应枚举:
+     * USER_CREATE_COMPLAINT:用户提交投诉
+     * USER_CONTINUE_COMPLAINT:用户继续投诉
+     * USER_RESPONSE:用户留言
+     * PLATFORM_RESPONSE:平台留言
+     * MERCHANT_RESPONSE:商户留言
+     * MERCHANT_CONFIRM_COMPLETE:商户申请结单
+     * COMPLAINT_FULL_REFUNDED:投诉单全额退款
+     * USER_CREATE_COMPLAINT_SYSTEM_MESSAGE:用户提交投诉系统通知
+     * COMPLAINT_FULL_REFUNDED_SYSTEM_MESSAGE:投诉单全额退款系统通知
+     * USER_CONTINUE_COMPLAINT_SYSTEM_MESSAGE:用户继续投诉系统通知
+     * MERCHANT_CONFIRM_COMPLETE_SYSTEM_MESSAGE:商户申请结单系统通知
+     * USER_REVOKE_COMPLAINT:用户主动撤诉(只存在于历史投诉单的协商历史中)
+     * PLATFORM_HELP_APPLICATION:平台问询
+     * USER_APPLY_PLATFORM_HELP:申请协助
+     * 
+ */ + @SerializedName("operate_type") + private String operateType; + + /** + *
+     * 字段名:操作内容
+     * 是否必填:否
+     * 描述:
+     * 当前投诉协商记录的具体内容
+     * 
+ */ + @SerializedName("operate_details") + private String operateDetails; + + /** + *
+     * 字段名:图片凭证
+     * 是否必填:是
+     * 描述:
+     * 当前投诉协商记录提交的图片凭证(url格式),最多返回4张图片,url有效时间为1小时。如未查询到协商历史图片凭证,则返回空数组。
+     * 注:本字段包含商户、微信支付客服在协商解决投诉时上传的图片凭证,若希望查看用户图片,请使用complaint_media_list字段并联系微信支付客服
+     * 
+ */ + @SerializedName("image_list") + private List imageList; + + } + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/ResponseRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/ResponseRequest.java new file mode 100644 index 000000000..24e287773 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/ResponseRequest.java @@ -0,0 +1,96 @@ +package com.github.binarywang.wxpay.bean.complaint; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 微信消费者投诉2.0 + * 提交回复请求实体 + * + * @author jmdhappy + * @date 2022-3-19 + */ +@Data +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor +public class ResponseRequest implements Serializable { + + private static final long serialVersionUID = 3244929701614220801L; + + /** + *
+   * 字段名:投诉单号
+   * 是否必填:是
+   * 描述:投诉单对应的投诉单号
+   * 
+ */ + @SerializedName("complaint_id") + @Expose + private String complaintId; + + /** + *
+   * 字段名:被诉商户号
+   * 是否必填:是
+   * 描述:投诉单对应的被诉商户号
+   * 
+ */ + @SerializedName("complainted_mchid") + private String complaintedMchid; + + /** + *
+   * 字段名:回复内容
+   * 是否必填:是
+   * 描述:具体的投诉处理方案,限制200个字符以内。
+   * 
+ */ + @SerializedName("response_content") + private String responseContent; + + /** + *
+   * 字段名:回复图片
+   * 是否必填:否
+   * 描述:
+   * 传入调用商户上传反馈图片接口返回的media_id,最多上传4张图片凭证
+   * 示例值:file23578_21798531.jpg
+   * 
+ */ + @SerializedName("response_images") + private String responseImages; + + /** + *
+   * 字段名:跳转链接
+   * 是否必填:是
+   * 描述:
+   * 商户可在回复中附加跳转链接,引导用户跳转至商户客诉处理页面,链接需满足https格式
+   * 注:配置文字链属于灰度功能, 若有需要请使用超管邮箱,按照要求发送邮件申请。邮件要求详情见:
+   * 商户申请开通留言链接白名单指南。
+   * 示例值:https://www.xxx.com/notify
+   * 
+ */ + @SerializedName("jump_url") + private String jumpUrl; + + /** + *
+   * 字段名:跳转链接文案
+   * 是否必填:否
+   * 描述:
+   * 实际展示给用户的文案,附在回复内容之后。用户点击文案,即可进行跳转。
+   * 注:若传入跳转链接,则跳转链接文案为必传项,二者缺一不可。
+   * 
+ */ + @SerializedName("jump_url_text") + private String jumpUrlText; + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/ApplymentsRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/ApplymentsRequest.java index 41f222ca9..ac17e18cd 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/ApplymentsRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/ApplymentsRequest.java @@ -131,6 +131,19 @@ public class ApplymentsRequest implements Serializable { @SpecEncrypt private IdDocInfo idDocInfo; + /** + *
+   * 字段名:经营者/法人是否为受益人
+   * 变量名:owner
+   * 是否必填:条件选填
+   * 类型:bool
+   * 描述:主体类型为企业时,需要填写:1、若经营者/法人是最终受益人,则填写:true。2、若经营者/法人不是最终受益人,则填写:false。
+   * 示例值:true
+   * 
+ */ + @SerializedName(value = "owner") + private Boolean owner; + /** *
    * 字段名:是否填写结算账户信息
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entwxpay/EntWxEmpPayRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entwxpay/EntWxEmpPayRequest.java
index 193c5293f..e093a818d 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entwxpay/EntWxEmpPayRequest.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entwxpay/EntWxEmpPayRequest.java
@@ -227,4 +227,9 @@ protected void storeMap(Map map) {
     map.put("approval_type", approvalType.toString());
     map.put("agentid", agentId.toString());
   }
+
+  @Override
+  protected String[] getIgnoredParamsForSign() {
+    return new String[]{"sign_type"};
+  }
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/AuthRecordRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/AuthRecordRequest.java
new file mode 100644
index 000000000..2de216850
--- /dev/null
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/AuthRecordRequest.java
@@ -0,0 +1,135 @@
+package com.github.binarywang.wxpay.bean.marketing.payroll;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 
+ * 查询核身记录
+ * 文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/Offline/apis/chapter4_1_5.shtml
+ *
+ * 适用对象:服务商
+ * 请求URL:https://api.mch.weixin.qq.com/v3/payroll-card/authentications
+ * 请求方式:GET
+ * 
+ * + * @author xiaoqiang + * @date 2021/12/2 + */ +@Data +@NoArgsConstructor +public class AuthRecordRequest implements Serializable { + private static final long serialVersionUID = 1L; + + /** + *
+   * 字段名:用户标识
+   * 变量名:openid
+   * 是否必填:是
+   * 类型:string[1, 64]
+   * 描述:
+   *  用户在商户对应appid下的唯一标识
+   *  示例值:9x111111
+   * 
+ */ + @SerializedName(value = "openid") + private String openid; + + /** + *
+   * 字段名:应用ID
+   * 变量名:appid
+   * 是否必填:二选一
+   * 类型:string[1, 32]
+   * 描述:
+   *   是服务商在微信申请公众号/小程序或移动应用成功后分配的账号ID(与服务商主体一致),登录平台为mp.weixin.qq.com或open.weixin.qq.com。
+   *  当输入应用ID时,会校验其与服务商商户号的绑定关系。服务商应用ID和与子商户应用ID至少输入一个,且必须要有拉起微工卡时使用的APPID。
+   *  示例值:wxa1111111
+   * 
+ */ + @SerializedName(value = "appid") + private String appid; + + /** + *
+   * 字段名:子商户应用ID
+   * 变量名:sub_appid
+   * 是否必填:是
+   * 类型:string[1, 32]
+   * 描述:
+   *  是特约商户在微信申请公众号/小程序或移动应用成功后分配的账号ID(与特约商户主体一致),登录平台为mp.weixin.qq.com或open.weixin.qq.com。当输入子商户应用ID时,会校验其与特约商户号的绑定关系。 服务商应用ID和与子商户应用ID至少输入一个,且必须要有拉起微工卡时使用的APPID。
+   *  示例值:wxa1111111
+   * 
+ */ + @SerializedName(value = "sub_appid") + private String subAppid; + + /** + *
+   * 字段名:子商户号
+   * 变量名:sub_mchid
+   * 是否必填:是
+   * 类型:string[1, 32]
+   * 描述:
+   *  微信服务商下特约商户的商户号,由微信支付生成并下发
+   * 示例值:1111111
+   * 
+ */ + @SerializedName(value = "sub_mchid") + private String subMchid; + + /** + *
+   * 字段名:核身日期
+   * 变量名:authenticate_date
+   * 是否必填:是
+   * 类型:string[8, 8]
+   * 描述:
+   *  query核身日期,一次只能查询一天,最久可查询90天内的记录,格式为“YYYY-MM-DD”
+   *     示例值:2020-12-25
+   * 
+ */ + @SerializedName(value = "authenticate_date") + private String authenticateDate; + /** + *
+   * 字段名:核身状态
+   * 变量名:authenticate_state
+   * 是否必填:否
+   * 类型:string[1, 32]
+   * 描述:
+   * query核身状态,列表查询仅提供成功状态的核身记录查询,故此字段固定默认值即可
+   *     示例值:AUTHENTICATE_SUCCESS
+   * 
+ */ + @SerializedName(value = "authenticate_state") + private String authenticateState; + /** + *
+   * 字段名:本次查询偏移量
+   * 变量名:offset
+   * 是否必填:否
+   * 类型:int
+   * 描述:
+   * query非负整数,表示该次请求资源的起始位置,从0开始计数。调用方选填,默认为0。offset为10,limit为10时,查询第10-19条数据
+   * 
+ */ + @SerializedName(value = "offset") + private Integer offset; + /** + *
+   * 字段名:本次请求最大查询条数
+   * 变量名:limit
+   * 是否必填:否
+   * 类型:int
+   * 描述:
+   * query非0非负的整数,该次请求可返回的最大资源条数,默认值为10,最大支持10条。
+   *     示例值:10
+   * 
+ */ + @SerializedName(value = "limit") + private Integer limit; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/AuthRecordResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/AuthRecordResult.java new file mode 100644 index 000000000..5318e5315 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/AuthRecordResult.java @@ -0,0 +1,213 @@ +package com.github.binarywang.wxpay.bean.marketing.payroll; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +/** + *
+ * 查询核身记录
+ * 文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/Offline/apis/chapter4_1_5.shtml
+ *
+ * 适用对象:服务商
+ * 请求URL:https://api.mch.weixin.qq.com/v3/payroll-card/authentications
+ * 请求方式:GET
+ * 
+ * + * @author xiaoqiang + * @date 2021/12/2 + */ +@Data +@NoArgsConstructor +public class AuthRecordResult implements Serializable { + private static final long serialVersionUID = 1L; + + @SerializedName(value = "data") + private List dataList; + + @Data + @NoArgsConstructor + public static class RecordData implements Serializable { + private static final long serialVersionUID = 1L; + /** + *
+     * 字段名:商户号
+     * 变量名:mchid
+     * 是否必填:是
+     * 类型:string[1, 32]
+     * 描述:
+     *  微信服务商商户的商户号,由微信支付生成并下发。
+     *  示例值:1111111
+     * 
+ */ + @SerializedName(value = "mchid") + private String mchid; + /** + *
+     * 字段名:子商户号
+     * 变量名:sub_mchid
+     * 是否必填:是
+     * 类型:string[1, 32]
+     * 描述:
+     *  微信服务商下特约商户的商户号,由微信支付生成并下发
+     *  示例值:111111
+     * 
+ */ + @SerializedName(value = "sub_mchid") + private String subMchid; + /** + *
+     * 字段名:用户标识
+     * 变量名:openid
+     * 是否必填:是
+     * 类型:string[1, 64]
+     * 描述:
+     *  用户在商户对应appid下的唯一标识
+     *  示例值:onqOjjmo8wmTOOtSKwXtGjg9Gb58
+     * 
+ */ + @SerializedName(value = "openid") + private String openid; + /** + *
+     * 字段名:核身渠道
+     * 变量名:authenticate_scene
+     * 是否必填:是
+     * 类型:string[1, 16]
+     * 描述:
+     *  核身渠道,发起核身时的来源渠道,如通过小程序,硬件设备等
+     *         FROM_MINI_APP:来自小程序方式核身
+     *         FROM_HARDWARE:来自硬件设备方式核身
+     *  示例值:FROM_HARDWARE
+     * 
+ */ + @SerializedName(value = "authenticate_scene") + private String authenticateScene; + /** + *
+     * 字段名:核身渠道标识
+     * 变量名:authenticate_source
+     * 是否必填:是
+     * 类型:string[1, 64]
+     * 描述:
+     *  核身渠道标识,用于定位渠道具体来源,如果是扫码打卡渠道标识就是具体的小程序appid,若是硬件设备,则是设备的序列号等
+     *  示例值:wdiooewl7587443649000
+     * 
+ */ + @SerializedName(value = "authenticate_source") + private String authenticateSource; + /** + *
+     * 字段名:项目名称
+     * 变量名:project_name
+     * 是否必填:是
+     * 类型:string[1, 12]
+     * 描述:
+     *  该项目的名称
+     *  示例值:某项目
+     * 
+ */ + @SerializedName(value = "project_name") + private String projectName; + /** + *
+     * 字段名:单位名称
+     * 变量名:employer_name
+     * 是否必填:是
+     * 类型:string[1, 12]
+     * 描述:
+     *    该用户所属的单位名称。
+     *     示例值:某单位名称
+     * 
+ */ + @SerializedName(value = "employer_name") + private String employerName; + /** + *
+     * 字段名:核身状态
+     * 变量名:authenticate_state
+     * 是否必填:是
+     * 类型:string[1, 32]
+     * 描述:
+     *    核身状态
+     *     AUTHENTICATE_PROCESSING:核身中
+     *     AUTHENTICATE_SUCCESS:核身成功
+     *     AUTHENTICATE_FAILED:核身失败
+     *  示例值:AUTHENTICATE_PROCESSING
+     * 
+ */ + @SerializedName(value = "authenticate_state") + private String authenticateState; + + /** + *
+     * 字段名:核身时间
+     * 变量名:authenticate_time
+     * 是否必填:是
+     * 类型:string[1, 32]
+     * 描述:
+     *   核身时间,遵循RFC3339标准格式,格式为YYYY-MM-DDTHH:mm:ss+TIMEZONE,YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss表示时分秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。
+     *   示例值:2015-05-20T13:29:35+08:00
+     * 
+ */ + @SerializedName(value = "authenticate_time") + private String authenticateTime; + /** + *
+     * 字段名:商家核身单号
+     * 变量名:authenticate_number
+     * 是否必填:是
+     * 类型:string[1, 64]
+     * 描述:
+     *  商户系统内部的商家核身单号,要求此参数只能由数字、大小写字母组成,在服务商内部唯一
+     *  示例值:mcdhehfgisdhfjghed39384564i83
+     * 
+ */ + @SerializedName(value = "authenticate_number") + private String authenticateNumber; + } + + /** + *
+   * 字段名:总记录条数
+   * 变量名:total_count
+   * 是否必填:是
+   * 类型:int
+   * 描述:
+   *  经过条件筛选,查询到的记录总数
+   *  示例值:9
+   * 
+ */ + @SerializedName(value = "total_count") + private Integer totalCount; + + /** + *
+   * 字段名:记录起始位置
+   * 变量名:offset
+   * 是否必填:是
+   * 类型:int
+   * 描述:
+   *  该次请求资源的起始位置,请求中包含偏移量时应答消息返回相同偏移量,否则返回默认值0
+   * 
+ */ + @SerializedName(value = "offset") + private Integer offset; + + /** + *
+   * 字段名:本次返回条数
+   * 变量名:limit
+   * 是否必填:是
+   * 类型:int
+   * 描述:
+   *  经过条件筛选,本次查询到的记录条数
+   *  示例值:10
+   * 
+ */ + @SerializedName(value = "limit") + private Integer limit; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/AuthenticationsResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/AuthenticationsResult.java new file mode 100644 index 000000000..755724869 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/AuthenticationsResult.java @@ -0,0 +1,182 @@ +package com.github.binarywang.wxpay.bean.marketing.payroll; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + *
+ * 获取核身结果
+ * 文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/Offline/apis/chapter4_1_4.shtml
+ *
+ * 适用对象:服务商
+ * 请求URL:https://api.mch.weixin.qq.com/v3/payroll-card/authentications/{authenticate_number}
+ * 请求方式:GET
+ * 
+ * + * @author xiaoqiang + * @date 2021/12/2 + */ +@Data +@NoArgsConstructor +public class AuthenticationsResult implements Serializable { + private static final long serialVersionUID = 1L; + + /** + *
+   * 字段名:商户号
+   * 变量名:mchid
+   * 是否必填:是
+   * 类型:string[1, 32]
+   * 描述:
+   *  query微信服务商下特约商户的商户号,由微信支付生成并下发
+   *     示例值:1111111
+   * 
+ */ + @SerializedName(value = "mchid") + private String mchid; + + /** + *
+   * 字段名:子商户号
+   * 变量名:sub_mchid
+   * 是否必填:是
+   * 类型:string[1, 32]
+   * 描述:
+   *  query微信服务商下特约商户的商户号,由微信支付生成并下发
+   *  示例值:1111111
+   * 
+ */ + @SerializedName(value = "sub_mchid") + private String subMchid; + /** + *
+   * 字段名:用户标识
+   * 变量名:authenticate_number
+   * 是否必填:是
+   * 类型:string[1, 64]
+   * 描述:
+   *  用户在商户对应appid下的唯一标识
+   * 示例值:onqOjjmo8wmTOOtSKwXtGjg9Gb58
+   * 
+ */ + @SerializedName(value = "openid") + private String openid; + + /** + *
+   * 字段名:核身渠道
+   * 变量名:authenticate_scene
+   * 是否必填:是
+   * 类型:string[1, 16]
+   * 描述:
+   *   核身渠道,发起核身时的来源渠道,如通过小程序,硬件设备等
+   *     FROM_MINI_APP:来自小程序方式核身
+   *     FROM_HARDWARE:来自硬件设备方式核身
+   *  示例值:onqOjjmo8wmTOOtSKwXtGjg9Gb58
+   * 
+ */ + @SerializedName(value = "authenticate_scene") + private String authenticateScene; + + /** + *
+   * 字段名:核身渠道标识
+   * 变量名:authenticate_source
+   * 是否必填:是
+   * 类型:string[1, 64]
+   * 描述:
+   *    核身渠道标识,用于定位渠道具体来源,如果是扫码打卡渠道标识就是具体的小程序appid,若是硬件设备,则是设备的序列号等
+   * 示例值:wdiooewl7587443649000
+   * 
+ */ + @SerializedName(value = "authenticate_source") + private String authenticateSource; + + /** + *
+   * 字段名:项目名称
+   * 变量名:project_name
+   * 是否必填:是
+   * 类型:string[1, 12]
+   * 描述:
+   *    该项目的名称
+   *  示例值:某项目
+   * 
+ */ + @SerializedName(value = "project_name") + private String projectName; + + /** + *
+   * 字段名:单位名称
+   * 变量名:employer_name
+   * 是否必填:是
+   * 类型:string[1, 12]
+   * 描述:
+   *    该用户所属的单位名称。
+   *  示例值:某单位名称
+   * 
+ */ + @SerializedName(value = "employer_name") + private String employerName; + + /** + *
+   * 字段名:核身状态
+   * 变量名:authenticate_state
+   * 是否必填:是
+   * 类型:string[1, 32]
+   * 描述:
+   *    核身状态
+   *     AUTHENTICATE_PROCESSING:核身中
+   *     AUTHENTICATE_SUCCESS:核身成功
+   *     AUTHENTICATE_FAILED:核身失败
+   *  示例值:AUTHENTICATE_PROCESSING
+   * 
+ */ + @SerializedName(value = "authenticate_state") + private String authenticateState; + /** + *
+   * 字段名:核身时间
+   * 变量名:authenticate_time
+   * 是否必填:是
+   * 类型:string[1, 32]
+   * 描述:
+   *   核身时间,遵循RFC3339标准格式,格式为YYYY-MM-DDTHH:mm:ss+TIMEZONE,YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss表示时分秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。
+   *   示例值:2015-05-20T13:29:35+08:00
+   * 
+ */ + @SerializedName(value = "authenticate_time") + private String authenticateTime; + /** + *
+   * 字段名:商家核身单号
+   * 变量名:authenticate_number
+   * 是否必填:是
+   * 类型:string[1, 64]
+   * 描述:
+   *  商户系统内部的商家核身单号,要求此参数只能由数字、大小写字母组成,在服务商内部唯一
+   *  示例值:mcdhehfgisdhfjghed39384564i83
+   * 
+ */ + @SerializedName(value = "authenticate_number") + private String authenticateNumber; + + /** + *
+   * 字段名:核身失败原因
+   * 变量名:authenticate_failed_reason
+   * 是否必填:否
+   * 类型:string[1, 128]
+   * 描述:
+   *  结果为核身失败时的原因描述,仅在失败记录返回
+   *  示例值:人脸验证未通过
+   * 
+ */ + @SerializedName(value = "authenticate_failed_reason") + private String authenticateFailedReason; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/MerchantIncomeRecordsRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/MerchantIncomeRecordsRequest.java new file mode 100644 index 000000000..04fe70964 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/MerchantIncomeRecordsRequest.java @@ -0,0 +1,83 @@ +package com.github.binarywang.wxpay.bean.marketing.payroll; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + *
+ * 服务商银行来账查询
+ * 文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/Offline/apis/chapter4_1_28.shtml
+ *
+ * 适用对象:服务商
+ * 请求URL:https://api.mch.weixin.qq.com/v3/merchantfund/merchant/income-records
+ * 请求方式:GET
+ * 
+ * + * @author xiaoqiang + * @date 2021/12/7 + */ +@Data +@NoArgsConstructor +public class MerchantIncomeRecordsRequest implements Serializable { + private static final long serialVersionUID = 1L; + + /** + *
+   * 字段名:账户类型
+   * 变量名:account_type
+   * 是否必填:是
+   * 类型:string[1, 32]
+   * 描述:
+   * query需查询银行来账记录商户的账户类型。
+   *     枚举值:
+   *     BASIC:基本账户
+   *     OPERATION:运营账户
+   *     FEES:手续费账户
+   * 示例值:BASIC
+   * 
+ */ + @SerializedName(value = "account_type") + private String accountType; + /** + *
+   * 字段名:日期
+   * 变量名:date
+   * 是否必填:是
+   * 类型:string[10, 10]
+   * 描述:
+   * query查询的日期,一次只能查询一天,最久可查询90天内的记录,格式为“YYYY-MM-DD”。
+   * 示例值:2019-06-11
+   * 
+ */ + @SerializedName(value = "date") + private String date; + /** + *
+   * 字段名:本次查询偏移量
+   * 变量名:offset
+   * 是否必填:否
+   * 类型:int
+   * 描述:
+   * query非负整数,表示该次请求资源的起始位置,从0开始计数。调用方选填,默认为0。offset为20,limit为100时,查询第20-119条数据。
+   * 示例值:0
+   * 
+ */ + @SerializedName(value = "offset") + private int offset; + /** + *
+   * 字段名:本次请求最大查询条数
+   * 变量名:limit
+   * 是否必填:是
+   * 类型:int
+   * 描述:
+   * query非0非负的整数,该次请求可返回的最大资源条数,最大支持100条。
+   * 示例值:100
+   * 
+ */ + @SerializedName(value = "limit") + private int limit; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/MerchantIncomeRecordsResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/MerchantIncomeRecordsResult.java new file mode 100644 index 000000000..a587d27f0 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/MerchantIncomeRecordsResult.java @@ -0,0 +1,286 @@ +package com.github.binarywang.wxpay.bean.marketing.payroll; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +/** + *
+ * 服务商银行来账查询
+ * 文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/Offline/apis/chapter4_1_28.shtml
+ *
+ * 适用对象:服务商
+ * 请求URL:https://api.mch.weixin.qq.com/v3/merchantfund/merchant/income-records
+ * 请求方式:GET
+ * 
+ * + * @author xiaoqiang + * @date 2021/12/7 + */ +@Data +@NoArgsConstructor +public class MerchantIncomeRecordsResult implements Serializable { + private static final long serialVersionUID = 1L; + + /** + *
+   * 字段名:查询数据总条数
+   * 变量名:total_count
+   * 是否必填:是
+   * 类型:int
+   * 描述:
+   *  经过条件筛选,查询到的银行来账记录总数 。
+   * 示例值:20
+   * 
+ */ + @SerializedName(value = "total_count") + private int totalCount; + + /** + *
+   * 字段名:本次查询偏移量
+   * 变量名:offset
+   * 是否必填:否
+   * 类型:int
+   * 描述:
+   * 该次请求资源的起始位置,请求中包含偏移量时应答消息返回相同偏移量,否则返回默认值0。
+   * 示例值:0
+   * 
+ */ + @SerializedName(value = "offset") + private int offset; + /** + *
+   * 字段名:本次请求最大查询条数
+   * 变量名:limit
+   * 是否必填:是
+   * 类型:int
+   * 描述:
+   * 经过条件筛选,本次查询到的银行来账记录条数。
+   * 示例值:100
+   * 
+ */ + @SerializedName(value = "limit") + private int limit; + /** + *
+   * 字段名:银行来账记录列表
+   * 变量名:data
+   * 是否必填:否
+   * 类型:array
+   * 描述:
+   *  单次查询返回的银行来账记录列表结果,如果查询结果为空时,则为空数组。
+   * 
+ */ + @SerializedName(value = "data") + private List incomeRecordDataList; + + + @Data + @NoArgsConstructor + public static class IncomeRecordData implements Serializable { + private static final long serialVersionUID = 1L; + + /** + *
+     * 字段名:商户号
+     * 变量名:sub_mchid
+     * 是否必填:是
+     * 类型:string[1, 32]
+     * 描述:
+     *  需查询银行来账记录列表的商户号
+     * 示例值:2480253391
+     * 
+ */ + @SerializedName(value = "mchid") + private String mchid; + + /** + *
+     * 字段名:账户类型
+     * 变量名:account_type
+     * 是否必填:是
+     * 类型:string[1, 32]
+     * 描述:
+     *  需查询银行来账记录商户的账户类型。
+     *     枚举值:
+     *     BASIC:基本账户
+     *     OPERATION:运营账户
+     *     FEES:手续费账户
+     * 示例值:BASIC
+     * 
+ */ + @SerializedName(value = "account_type") + private String accountType; + /** + *
+     * 字段名:银行来账类型
+     * 变量名:income_record_type
+     * 是否必填:是
+     * 类型:string[1, 64]
+     * 描述:
+     *  银行来账类型,后续会有所扩展。
+     *     枚举值:
+     *     OFFLINERECHARGE:转账充值
+     *     ENTERPRISEDIRECTREVENUE:企业直收
+     * 示例值:OFFLINERECHARGE
+     * 
+ */ + @SerializedName(value = "income_record_type") + private String incomeRecordType; + /** + *
+     * 字段名:银行来账微信单号
+     * 变量名:income_record_id
+     * 是否必填:是
+     * 类型:string[1, 64]
+     * 描述:
+     *  银行来账的微信单号
+     * 示例值:4200000811202011056138519459
+     * 
+ */ + @SerializedName(value = "income_record_id") + private String incomeRecordId; + /** + *
+     * 字段名:银行来账金额
+     * 变量名:amount
+     * 是否必填:是
+     * 类型:int
+     * 描述:
+     *  银行来账金额,单位为分,只能为整数。
+     * 示例值:2734921
+     * 
+ */ + @SerializedName(value = "amount") + private String amount; + /** + *
+     * 字段名:银行来账完成时间
+     * 变量名:success_time
+     * 是否必填:是
+     * 类型:string[1, 32]
+     * 描述:
+     *  银行来账完成时间,遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss.sss+TIMEZONE,YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss.sss表示时分秒毫秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。例如:2015-05-20T13:29:35.120+08:00表示,北京时间2015年5月20日 13点29分35秒。
+     * 示例值:2017-12-08T00:08:00.00+08:00
+     * 
+ */ + @SerializedName(value = "success_time") + private String successTime; + /** + *
+     * 字段名:付款方银行名称
+     * 变量名:bank_name
+     * 是否必填:是
+     * 类型:string[1, 256]
+     * 描述:
+     *  银行来账的付款方银行名称,由于部分银行的数据获取限制,该字段有可能为空。
+     * 示例值:招商银行
+     * 
+ */ + @SerializedName(value = "bank_name") + private String bankName; + /** + *
+     * 字段名:付款方银行户名
+     * 变量名:bank_account_name
+     * 是否必填:是
+     * 类型:string[1, 256]
+     * 描述:
+     *  银行来账的付款方银行账户信息,户名为全称、明文,由于部分银行的数据获取限制,该字段有可能为空。
+     * 示例值:北京三快科技有限公司
+     * 
+ */ + @SerializedName(value = "bank_account_name") + private String bankAccountName; + /** + *
+     * 字段名:付款方银行卡号
+     * 变量名:bank_account_number
+     * 是否必填:是
+     * 类型:string[1, 64]
+     * 描述:
+     *  四位掩码+付款方银行卡尾号后四位。
+     * 示例值:****6473
+     * 
+ */ + @SerializedName(value = "bank_account_number") + private String bankAccountNumber; + /** + *
+     * 字段名:银行备注
+     * 变量名:recharge_remark
+     * 是否必填:是
+     * 类型:string[1, 256]
+     * 描述:
+     *  随银行转账时,商户填入的附言、摘要等信息,目前支持的银行及填写指引请查看各银行对账详情
+     * 示例值:单号:202106010001
+     * 
+ */ + @SerializedName(value = "recharge_remark") + private String rechargeRemark; + } + + /** + *
+   * 字段名:分页链接
+   * 变量名:links
+   * 是否必填:是
+   * 类型:object
+   * 描述:
+   *  返回前后页和当前页面的访问链接
+   * 
+ */ + @SerializedName(value = "links") + private List linksDataList; + + + @Data + @NoArgsConstructor + public static class LinksData implements Serializable { + private static final long serialVersionUID = 109053454401492L; + + /** + *
+     * 字段名:下一页链接
+     * 变量名:next
+     * 是否必填:是
+     * 类型:string[1, 2048]
+     * 描述:
+     *  使用同样的limit进行下一页查询时的相对请求链接,使用方需要自行根据当前域名进行拼接。如果已经到最后时,为空 。
+     * 示例值:/v3/merchantfund/partner/income-records?offset=10&limit=5
+     * 
+ */ + @SerializedName(value = "next") + private String next; + /** + *
+     * 字段名:上一页链接
+     * 变量名:prev
+     * 是否必填:是
+     * 类型:string[1, 2048]
+     * 描述:
+     *  使用同样的limit进行上一页查询时的相对请求链接,使用方需要自行根据当前域名进行拼接。如果是第一页,为空。
+     * 示例值:/v3/merchantfund/partner/income-records?offset=0&limit=5
+     * 
+ */ + @SerializedName(value = "prev") + private String prev; + /** + *
+     * 字段名:当前链接
+     * 变量名:self
+     * 是否必填:是
+     * 类型:string[1, 2048]
+     * 描述:
+     *  当前的相对请求链接,使用方需要自行根据当前域名进行拼接。
+     * 示例值:/v3/merchantfund/partner/income-records?offset=5&limit=5
+     * 
+ */ + @SerializedName(value = "self") + private String self; + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/PartnerIncomeRecordsRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/PartnerIncomeRecordsRequest.java new file mode 100644 index 000000000..f266bdb17 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/PartnerIncomeRecordsRequest.java @@ -0,0 +1,97 @@ +package com.github.binarywang.wxpay.bean.marketing.payroll; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + *
+ * 特约商户银行来账查询
+ * 文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/Offline/apis/chapter4_1_27.shtml
+ *
+ * 适用对象:服务商
+ * 请求URL:https://api.mch.weixin.qq.com/v3/merchantfund/partner/income-records
+ * 请求方式:GET
+ * 
+ * + * @author xiaoqiang + * @date 2021/12/7 + */ +@Data +@NoArgsConstructor +public class PartnerIncomeRecordsRequest implements Serializable { + private static final long serialVersionUID = 1L; + + /** + *
+   * 字段名:特约商户号
+   * 变量名:sub_mchid
+   * 是否必填:是
+   * 类型:string[1, 32]
+   * 描述:
+   *  query需查询银行来账记录列表的特约商户的商户号,该商户号须为服务商的特约商户号。
+   *  示例值:2480253391
+   * 
+ */ + @SerializedName(value = "sub_mchid") + private String subMchid; + + /** + *
+   * 字段名:账户类型
+   * 变量名:account_type
+   * 是否必填:是
+   * 类型:string[1, 32]
+   * 描述:
+   * query需查询银行来账记录商户的账户类型。
+   *     枚举值:
+   *     BASIC:基本账户
+   *     OPERATION:运营账户
+   *     FEES:手续费账户
+   * 示例值:BASIC
+   * 
+ */ + @SerializedName(value = "account_type") + private String accountType; + /** + *
+   * 字段名:日期
+   * 变量名:date
+   * 是否必填:是
+   * 类型:string[10, 10]
+   * 描述:
+   * query查询的日期,一次只能查询一天,最久可查询90天内的记录,格式为“YYYY-MM-DD”。
+   * 示例值:2019-06-11
+   * 
+ */ + @SerializedName(value = "date") + private String date; + /** + *
+   * 字段名:本次查询偏移量
+   * 变量名:offset
+   * 是否必填:否
+   * 类型:int
+   * 描述:
+   * query非负整数,表示该次请求资源的起始位置,从0开始计数。调用方选填,默认为0。offset为20,limit为100时,查询第20-119条数据。
+   * 示例值:0
+   * 
+ */ + @SerializedName(value = "offset") + private int offset; + /** + *
+   * 字段名:本次请求最大查询条数
+   * 变量名:limit
+   * 是否必填:是
+   * 类型:int
+   * 描述:
+   * query非0非负的整数,该次请求可返回的最大资源条数,最大支持100条。
+   * 示例值:100
+   * 
+ */ + @SerializedName(value = "limit") + private int limit; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/PartnerIncomeRecordsResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/PartnerIncomeRecordsResult.java new file mode 100644 index 000000000..90d7d6cc1 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/PartnerIncomeRecordsResult.java @@ -0,0 +1,286 @@ +package com.github.binarywang.wxpay.bean.marketing.payroll; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +/** + *
+ * 特约商户银行来账查询
+ * 文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/Offline/apis/chapter4_1_27.shtml
+ *
+ * 适用对象:服务商
+ * 请求URL:https://api.mch.weixin.qq.com/v3/merchantfund/partner/income-records
+ * 请求方式:GET
+ * 
+ * + * @author xiaoqiang + * @date 2021/12/7 + */ +@Data +@NoArgsConstructor +public class PartnerIncomeRecordsResult implements Serializable { + private static final long serialVersionUID = 1L; + + /** + *
+   * 字段名:查询数据总条数
+   * 变量名:total_count
+   * 是否必填:是
+   * 类型:int
+   * 描述:
+   *  经过条件筛选,查询到的银行来账记录总数 。
+   * 示例值:20
+   * 
+ */ + @SerializedName(value = "total_count") + private int totalCount; + + /** + *
+   * 字段名:本次查询偏移量
+   * 变量名:offset
+   * 是否必填:否
+   * 类型:int
+   * 描述:
+   * 该次请求资源的起始位置,请求中包含偏移量时应答消息返回相同偏移量,否则返回默认值0。
+   * 示例值:0
+   * 
+ */ + @SerializedName(value = "offset") + private int offset; + /** + *
+   * 字段名:本次请求最大查询条数
+   * 变量名:limit
+   * 是否必填:是
+   * 类型:int
+   * 描述:
+   * 经过条件筛选,本次查询到的银行来账记录条数。
+   * 示例值:100
+   * 
+ */ + @SerializedName(value = "limit") + private int limit; + /** + *
+   * 字段名:银行来账记录列表
+   * 变量名:data
+   * 是否必填:否
+   * 类型:array
+   * 描述:
+   *  单次查询返回的银行来账记录列表结果,如果查询结果为空时,则为空数组。
+   * 
+ */ + @SerializedName(value = "data") + private List incomeRecordDataList; + + + @Data + @NoArgsConstructor + public static class IncomeRecordData implements Serializable { + private static final long serialVersionUID = 1L; + + /** + *
+     * 字段名:特约商户号
+     * 变量名:sub_mchid
+     * 是否必填:是
+     * 类型:string[1, 32]
+     * 描述:
+     *  需查询银行来账记录列表的特约商户的商户号,该商户号须为服务商的特约商户号。
+     * 示例值:2480253391
+     * 
+ */ + @SerializedName(value = "sub_mchid") + private String subMchid; + + /** + *
+     * 字段名:账户类型
+     * 变量名:account_type
+     * 是否必填:是
+     * 类型:string[1, 32]
+     * 描述:
+     *  需查询银行来账记录商户的账户类型。
+     *     枚举值:
+     *     BASIC:基本账户
+     *     OPERATION:运营账户
+     *     FEES:手续费账户
+     * 示例值:BASIC
+     * 
+ */ + @SerializedName(value = "account_type") + private String accountType; + /** + *
+     * 字段名:银行来账类型
+     * 变量名:income_record_type
+     * 是否必填:是
+     * 类型:string[1, 64]
+     * 描述:
+     *  银行来账类型,后续会有所扩展。
+     *     枚举值:
+     *     OFFLINERECHARGE:转账充值
+     *     ENTERPRISEDIRECTREVENUE:企业直收
+     * 示例值:OFFLINERECHARGE
+     * 
+ */ + @SerializedName(value = "income_record_type") + private String incomeRecordType; + /** + *
+     * 字段名:银行来账微信单号
+     * 变量名:income_record_id
+     * 是否必填:是
+     * 类型:string[1, 64]
+     * 描述:
+     *  银行来账的微信单号
+     * 示例值:4200000811202011056138519459
+     * 
+ */ + @SerializedName(value = "income_record_id") + private String incomeRecordId; + /** + *
+     * 字段名:银行来账金额
+     * 变量名:amount
+     * 是否必填:是
+     * 类型:int
+     * 描述:
+     *  银行来账金额,单位为分,只能为整数。
+     * 示例值:2734921
+     * 
+ */ + @SerializedName(value = "amount") + private String amount; + /** + *
+     * 字段名:银行来账完成时间
+     * 变量名:success_time
+     * 是否必填:是
+     * 类型:string[1, 32]
+     * 描述:
+     *  银行来账完成时间,遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss.sss+TIMEZONE,YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss.sss表示时分秒毫秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。例如:2015-05-20T13:29:35.120+08:00表示,北京时间2015年5月20日 13点29分35秒。
+     * 示例值:2017-12-08T00:08:00.00+08:00
+     * 
+ */ + @SerializedName(value = "success_time") + private String successTime; + /** + *
+     * 字段名:付款方银行名称
+     * 变量名:bank_name
+     * 是否必填:是
+     * 类型:string[1, 256]
+     * 描述:
+     *  银行来账的付款方银行名称,由于部分银行的数据获取限制,该字段有可能为空。
+     * 示例值:招商银行
+     * 
+ */ + @SerializedName(value = "bank_name") + private String bankName; + /** + *
+     * 字段名:付款方银行户名
+     * 变量名:bank_account_name
+     * 是否必填:是
+     * 类型:string[1, 256]
+     * 描述:
+     *  银行来账的付款方银行账户信息,户名为全称、明文,由于部分银行的数据获取限制,该字段有可能为空。
+     * 示例值:北京三快科技有限公司
+     * 
+ */ + @SerializedName(value = "bank_account_name") + private String bankAccountName; + /** + *
+     * 字段名:付款方银行卡号
+     * 变量名:bank_account_number
+     * 是否必填:是
+     * 类型:string[1, 64]
+     * 描述:
+     *  四位掩码+付款方银行卡尾号后四位。
+     * 示例值:****6473
+     * 
+ */ + @SerializedName(value = "bank_account_number") + private String bankAccountNumber; + /** + *
+     * 字段名:银行备注
+     * 变量名:recharge_remark
+     * 是否必填:是
+     * 类型:string[1, 256]
+     * 描述:
+     *  随银行转账时,商户填入的附言、摘要等信息,目前支持的银行及填写指引请查看各银行对账详情
+     * 示例值:单号:202106010001
+     * 
+ */ + @SerializedName(value = "recharge_remark") + private String rechargeRemark; + } + + /** + *
+   * 字段名:分页链接
+   * 变量名:links
+   * 是否必填:是
+   * 类型:object
+   * 描述:
+   *  返回前后页和当前页面的访问链接
+   * 
+ */ + @SerializedName(value = "links") + private List linksDataList; + + + @Data + @NoArgsConstructor + public static class LinksData implements Serializable { + private static final long serialVersionUID = 109053454401492L; + + /** + *
+     * 字段名:下一页链接
+     * 变量名:next
+     * 是否必填:是
+     * 类型:string[1, 2048]
+     * 描述:
+     *  使用同样的limit进行下一页查询时的相对请求链接,使用方需要自行根据当前域名进行拼接。如果已经到最后时,为空 。
+     * 示例值:/v3/merchantfund/partner/income-records?offset=10&limit=5
+     * 
+ */ + @SerializedName(value = "next") + private String next; + /** + *
+     * 字段名:上一页链接
+     * 变量名:prev
+     * 是否必填:是
+     * 类型:string[1, 2048]
+     * 描述:
+     *  使用同样的limit进行上一页查询时的相对请求链接,使用方需要自行根据当前域名进行拼接。如果是第一页,为空。
+     * 示例值:/v3/merchantfund/partner/income-records?offset=0&limit=5
+     * 
+ */ + @SerializedName(value = "prev") + private String prev; + /** + *
+     * 字段名:当前链接
+     * 变量名:self
+     * 是否必填:是
+     * 类型:string[1, 2048]
+     * 描述:
+     *  当前的相对请求链接,使用方需要自行根据当前域名进行拼接。
+     * 示例值:/v3/merchantfund/partner/income-records?offset=5&limit=5
+     * 
+ */ + @SerializedName(value = "self") + private String self; + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/PreOrderRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/PreOrderRequest.java new file mode 100644 index 000000000..80d0ed9f4 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/PreOrderRequest.java @@ -0,0 +1,125 @@ +package com.github.binarywang.wxpay.bean.marketing.payroll; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + *
+ * 微工卡核身预下单
+ * 文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/Offline/apis/chapter4_1_3.shtml
+ *
+ * 适用对象:服务商
+ * 请求URL:https://api.mch.weixin.qq.com/v3/payroll-card/authentications/pre-order
+ * 请求方式:POST
+ * 
+ * + * @author xiaoqiang + * @date 2021/12/2 + */ +@Data +@NoArgsConstructor +public class PreOrderRequest implements Serializable { + private static final long serialVersionUID = 1L; + + /** + *
+   * 字段名:用户标识
+   * 变量名:openid
+   * 是否必填:是
+   * 类型:string[1, 64]
+   * 描述:
+   *  用户在商户对应appid下的唯一标识
+   *  示例值:9x111111
+   * 
+ */ + @SerializedName(value = "openid") + private String openid; + + /** + *
+   * 字段名:应用ID
+   * 变量名:appid
+   * 是否必填:二选一
+   * 类型:string[1, 32]
+   * 描述:
+   *   是服务商在微信申请公众号/小程序或移动应用成功后分配的账号ID(与服务商主体一致),登录平台为mp.weixin.qq.com或open.weixin.qq.com。
+   *  当输入应用ID时,会校验其与服务商商户号的绑定关系。服务商应用ID和与子商户应用ID至少输入一个,且必须要有拉起微工卡时使用的APPID。
+   *  示例值:wxa1111111
+   * 
+ */ + @SerializedName(value = "appid") + private String appid; + + /** + *
+   * 字段名:子商户号
+   * 变量名:sub_mchid
+   * 是否必填:是
+   * 类型:string[1, 32]
+   * 描述:
+   *  微信服务商下特约商户的商户号,由微信支付生成并下发
+   * 示例值:1111111
+   * 
+ */ + @SerializedName(value = "sub_mchid") + private String subMchid; + + /** + *
+   * 字段名:子商户应用ID
+   * 变量名:sub_appid
+   * 是否必填:是
+   * 类型:string[1, 32]
+   * 描述:
+   *  是特约商户在微信申请公众号/小程序或移动应用成功后分配的账号ID(与特约商户主体一致),登录平台为mp.weixin.qq.com或open.weixin.qq.com。当输入子商户应用ID时,会校验其与特约商户号的绑定关系。 服务商应用ID和与子商户应用ID至少输入一个,且必须要有拉起微工卡时使用的APPID。
+   *  示例值:wxa1111111
+   * 
+ */ + @SerializedName(value = "sub_appid") + private String subAppid; + + /** + *
+   * 字段名:商家核身单号
+   * 变量名:authenticate_number
+   * 是否必填:是
+   * 类型:string[1, 64]
+   * 描述:
+   *  body商户系统内部的商家核身单号,要求此参数只能由数字、大小写字母组成,在服务商内部唯一
+   *  示例值:mcdhehfgisdhfjghed39384564i83
+   * 
+ */ + @SerializedName(value = "authenticate_number") + private String authenticateNumber; + + /** + *
+   * 字段名:项目名称
+   * 变量名:project_name
+   * 是否必填:是
+   * 类型:string[1, 12]
+   * 描述:
+   * body该项目的名称
+   * 示例值:某项目
+   * 
+ */ + @SerializedName(value = "project_name") + private String projectName; + /** + *
+   * 字段名:单位名称
+   * 变量名:employer_name
+   * 是否必填:是
+   * 类型:string[1, 12]
+   * 描述:
+   * body该用户所属的单位名称
+   * 示例值:某单位名称
+   * 
+ */ + @SerializedName(value = "employer_name") + private String employerName; + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/PreOrderResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/PreOrderResult.java new file mode 100644 index 000000000..dbe909ac6 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/PreOrderResult.java @@ -0,0 +1,111 @@ +package com.github.binarywang.wxpay.bean.marketing.payroll; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + *
+ * 微工卡核身预下单
+ * 文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/Offline/apis/chapter4_1_3.shtml
+ *
+ * 适用对象:服务商
+ * 请求URL:https://api.mch.weixin.qq.com/v3/payroll-card/authentications/pre-order
+ * 请求方式:POST
+ * 
+ * + * @author xiaoqiang + * @date 2021/12/2 + */ +@Data +@NoArgsConstructor +public class PreOrderResult implements Serializable { + private static final long serialVersionUID = 1L; + + /** + *
+   * 字段名:商家核身单号
+   * 变量名:authenticate_number
+   * 是否必填:是
+   * 类型:string[1, 64]
+   * 描述:
+   *  商户系统内部的商家核身单号,要求此参数只能由数字、大小写字母组成,在服务商内部唯一
+   *  示例值:mcdhehfgisdhfjghed39384564i83
+   * 
+ */ + @SerializedName(value = "authenticate_number") + private String authenticateNumber; + + /** + *
+   * 字段名:用户标识
+   * 变量名:openid
+   * 是否必填:是
+   * 类型:string[1, 64]
+   * 描述:
+   *  用户在商户对应appid下的唯一标识
+   *  示例值:9x111111
+   * 
+ */ + @SerializedName(value = "openid") + private String openid; + + /** + *
+   * 字段名:服务商商户号
+   * 变量名:mchid
+   * 是否必填:是
+   * 类型:string[1, 32]
+   * 描述:
+   *  微信服务商商户的商户号,由微信支付生成并下发。
+   *  示例值:1111111
+   * 
+ */ + @SerializedName(value = "mchid") + private String mchid; + + /** + *
+   * 字段名:子商户号
+   * 变量名:sub_mchid
+   * 是否必填:是
+   * 类型:string[1, 32]
+   * 描述:
+   *  微信服务商下特约商户的商户号,由微信支付生成并下发
+   *  示例值:1111111
+   * 
+ */ + @SerializedName(value = "sub_mchid") + private String subMchid; + + /** + *
+   * 字段名:授权token
+   * 变量名:token
+   * 是否必填:是
+   * 类型:string[1, 1024]
+   * 描述:
+   *  授权token
+   *  示例值:abcdefghijklmn
+   * 
+ */ + @SerializedName(value = "token") + private String token; + + /** + *
+   * 字段名:token有效时间
+   * 变量名:expires_in
+   * 是否必填:是
+   * 类型:int
+   * 描述:
+   * token有效时间,单位秒
+   * 示例值:1800
+   * 
+ */ + @SerializedName(value = "expires_in") + private Integer expiresIn; + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/PreOrderWithAuthRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/PreOrderWithAuthRequest.java new file mode 100644 index 000000000..5f4e8ae57 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/PreOrderWithAuthRequest.java @@ -0,0 +1,169 @@ +package com.github.binarywang.wxpay.bean.marketing.payroll; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + *
+ * 微工卡核身预下单(流程中完成授权)
+ * 文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/Offline/apis/chapter4_1_1.shtml
+ *
+ * 适用对象:服务商
+ * 请求URL:https://api.mch.weixin.qq.com/v3/payroll-card/authentications/pre-order-with-auth
+ * 请求方式:POST
+ * 
+ * + * @author xiaoqiang + * @date 2021/12/2 + */ +@Data +@NoArgsConstructor +public class PreOrderWithAuthRequest implements Serializable { + private static final long serialVersionUID = 1L; + + /** + *
+   * 字段名:用户标识
+   * 变量名:openid
+   * 是否必填:是
+   * 类型:string[1, 64]
+   * 描述:
+   *  用户在商户对应appid下的唯一标识
+   *  示例值:9x111111
+   * 
+ */ + @SerializedName(value = "openid") + private String openid; + + /** + *
+   * 字段名:应用ID
+   * 变量名:appid
+   * 是否必填:二选一
+   * 类型:string[1, 32]
+   * 描述:
+   *   是服务商在微信申请公众号/小程序或移动应用成功后分配的账号ID(与服务商主体一致),登录平台为mp.weixin.qq.com或open.weixin.qq.com。
+   *  当输入应用ID时,会校验其与服务商商户号的绑定关系。服务商应用ID和与子商户应用ID至少输入一个,且必须要有拉起微工卡时使用的APPID。
+   *  示例值:wxa1111111
+   * 
+ */ + @SerializedName(value = "appid") + private String appid; + + /** + *
+   * 字段名:子商户号
+   * 变量名:sub_mchid
+   * 是否必填:是
+   * 类型:string[1, 32]
+   * 描述:
+   *  微信服务商下特约商户的商户号,由微信支付生成并下发
+   * 示例值:1111111
+   * 
+ */ + @SerializedName(value = "sub_mchid") + private String subMchid; + + /** + *
+   * 字段名:子商户应用ID
+   * 变量名:sub_appid
+   * 是否必填:是
+   * 类型:string[1, 32]
+   * 描述:
+   *  是特约商户在微信申请公众号/小程序或移动应用成功后分配的账号ID(与特约商户主体一致),登录平台为mp.weixin.qq.com或open.weixin.qq.com。当输入子商户应用ID时,会校验其与特约商户号的绑定关系。 服务商应用ID和与子商户应用ID至少输入一个,且必须要有拉起微工卡时使用的APPID。
+   *  示例值:wxa1111111
+   * 
+ */ + @SerializedName(value = "sub_appid") + private String subAppid; + + /** + *
+   * 字段名:商家核身单号
+   * 变量名:authenticate_number
+   * 是否必填:是
+   * 类型:string[1, 64]
+   * 描述:
+   *  商户系统内部的商家核身单号,要求此参数只能由数字、大小写字母组成,在服务商内部唯一
+   *  示例值:mcdhehfgisdhfjghed39384564i83
+   * 
+ */ + @SerializedName(value = "authenticate_number") + private String authenticateNumber; + + /** + *
+   * 字段名:项目名称
+   * 变量名:project_name
+   * 是否必填:是
+   * 类型:string[1, 12]
+   * 描述:
+   *  该项目的名称
+   *  示例值:某项目
+   * 
+ */ + @SerializedName(value = "project_name") + private String projectName; + /** + *
+   * 字段名:用工单位名称
+   * 变量名:employer_name
+   * 是否必填:是
+   * 类型:string[1, 12]
+   * 描述:
+   *    该用户所属的单位名称。
+   *     示例值:某单位名称
+   * 
+ */ + @SerializedName(value = "employer_name") + private String employerName; + + /** + *
+   * 字段名:用户实名
+   * 变量名:user_name
+   * 是否必填:是
+   * 类型:string[1, 1024]
+   * 描述:
+   *  用户证件号,该字段需进行加密处理,加密方法详见敏感信息加密说明。(提醒:必须在HTTP头中上送Wechatpay-Serial)
+   * 示例值:7FzH5XksJG3a8HLLsaaUV6K54y1OnPMY5
+   * 
+ */ + @SerializedName(value = "user_name") + private String userName; + + /** + *
+   * 字段名:用户证件号
+   * 变量名:id_card_number
+   * 是否必填:是
+   * 类型:string[1, 1024]
+   * 描述:
+   *  用户证件号,该字段需进行加密处理,加密方法详见敏感信息加密说明。(提醒:必须在HTTP头中上送Wechatpay-Serial)
+   * 示例值:7FzH5XksJG3a8HLLsaaUV6K54y1OnPMY5
+   * 
+ */ + @SerializedName(value = "id_card_number") + private String idCardNumber; + + /** + *
+   * 字段名:用工类型
+   * 变量名:employment_type
+   * 是否必填:是
+   * 类型:string[1,32]
+   * 描述:
+   *  微工卡服务仅支持用于与商户有用工关系的用户,需明确用工类型;参考值:
+   * LONG_TERM_EMPLOYMENT:长期用工,
+   * SHORT_TERM_EMPLOYMENT: 短期用工,
+   * COOPERATION_EMPLOYMENT:合作关系
+   * 示例值:LONG_TERM_EMPLOYMENT
+   * 
+ */ + @SerializedName(value = "employment_type") + private String employmentType; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/PreOrderWithAuthResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/PreOrderWithAuthResult.java new file mode 100644 index 000000000..235ac056d --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/PreOrderWithAuthResult.java @@ -0,0 +1,110 @@ +package com.github.binarywang.wxpay.bean.marketing.payroll; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + *
+ * 微工卡核身预下单(流程中完成授权)
+ * 文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/Offline/apis/chapter4_1_1.shtml
+ *
+ * 适用对象:服务商
+ * 请求URL:https://api.mch.weixin.qq.com/v3/payroll-card/authentications/pre-order-with-auth
+ * 请求方式:POST
+ * 
+ * + * @author xiaoqiang + * @date 2021/12/2 + */ +@Data +@NoArgsConstructor +public class PreOrderWithAuthResult implements Serializable { + private static final long serialVersionUID = 1L; + + /** + *
+   * 字段名:商家核身单号
+   * 变量名:authenticate_number
+   * 是否必填:是
+   * 类型:string[1, 64]
+   * 描述:
+   *  商户系统内部的商家核身单号,要求此参数只能由数字、大小写字母组成,在服务商内部唯一
+   *  示例值:mcdhehfgisdhfjghed39384564i83
+   * 
+ */ + @SerializedName(value = "authenticate_number") + private String authenticateNumber; + + /** + *
+   * 字段名:用户标识
+   * 变量名:authenticate_number
+   * 是否必填:是
+   * 类型:string[1, 64]
+   * 描述:
+   *  用户在商户对应appid下的唯一标识
+   * 示例值:onqOjjmo8wmTOOtSKwXtGjg9Gb58
+   * 
+ */ + @SerializedName(value = "openid") + private String openid; + + /** + *
+   * 字段名:商户号
+   * 变量名:mchid
+   * 是否必填:是
+   * 类型:string[1, 32]
+   * 描述:
+   *  微信服务商商户的商户号,由微信支付生成并下发
+   *  示例值:1111111
+   * 
+ */ + @SerializedName(value = "mchid") + private Integer mchid; + + /** + *
+   * 字段名:特约商户号
+   * 变量名:sub_mchid
+   * 是否必填:是
+   * 类型:string[1, 32]
+   * 描述:
+   *  微信服务商下特约商户的商户号,由微信支付生成并下发
+   *  示例值:1900000109
+   * 
+ */ + @SerializedName(value = "sub_mchid") + private String subMchid; + + /** + *
+   * 字段名:授权token
+   * 变量名:token
+   * 是否必填:是
+   * 类型:string[1, 1024]
+   * 描述:
+   *  授权token
+   *  示例值:abcdefghijklmn
+   * 
+ */ + @SerializedName(value = "token") + private String token; + + /** + *
+   * 字段名:token有效时间
+   * 变量名:expires_in
+   * 是否必填:是
+   * 类型:int
+   * 描述:
+   * token有效时间,单位秒
+   * 示例值:300
+   * 
+ */ + @SerializedName(value = "expires_in") + private Integer expiresIn; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/RelationsRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/RelationsRequest.java new file mode 100644 index 000000000..e2d77abb2 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/RelationsRequest.java @@ -0,0 +1,83 @@ +package com.github.binarywang.wxpay.bean.marketing.payroll; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + *
+ * 查询微工卡授权关系
+ * 文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/Offline/apis/chapter4_1_2.shtml
+ *
+ * 适用对象:服务商
+ * 请求URL:https://api.mch.weixin.qq.com/v3/payroll-card/relations/{openid}
+ * 请求方式:GET
+ * 
+ * + * @author xiaoqiang + * @date 2021/12/2 + */ +@Data +@NoArgsConstructor +public class RelationsRequest implements Serializable { + private static final long serialVersionUID = 1L; + + /** + *
+   * 字段名:用户标识
+   * 变量名:openid
+   * 是否必填:是
+   * 类型:string[1, 64]
+   * 描述:
+   *  用户在商户对应appid下的唯一标识
+   *  示例值:9x111111
+   * 
+ */ + @SerializedName(value = "openid") + private String openid; + + /** + *
+   * 字段名:子商户号
+   * 变量名:sub_mchid
+   * 是否必填:是
+   * 类型:string[1, 32]
+   * 描述:
+   *  微信服务商下特约商户的商户号,由微信支付生成并下发
+   * 示例值:1111111
+   * 
+ */ + @SerializedName(value = "sub_mchid") + private String subMchid; + + /** + *
+   * 字段名:应用ID
+   * 变量名:appid
+   * 是否必填:二选一
+   * 类型:string[1, 32]
+   * 描述:
+   *   是服务商在微信申请公众号/小程序或移动应用成功后分配的账号ID(与服务商主体一致),登录平台为mp.weixin.qq.com或open.weixin.qq.com。
+   *  当输入应用ID时,会校验其与服务商商户号的绑定关系。服务商应用ID和与子商户应用ID至少输入一个,且必须要有拉起微工卡时使用的APPID。
+   *  示例值:wxa1111111
+   * 
+ */ + @SerializedName(value = "appid") + private String appid; + + /** + *
+   * 字段名:子商户应用ID
+   * 变量名:sub_appid
+   * 是否必填:是
+   * 类型:string[1, 32]
+   * 描述:
+   *  是特约商户在微信申请公众号/小程序或移动应用成功后分配的账号ID(与特约商户主体一致),登录平台为mp.weixin.qq.com或open.weixin.qq.com。当输入子商户应用ID时,会校验其与特约商户号的绑定关系。 服务商应用ID和与子商户应用ID至少输入一个,且必须要有拉起微工卡时使用的APPID。
+   *  示例值:wxa1111111
+   * 
+ */ + @SerializedName(value = "sub_appid") + private String subAppid; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/RelationsResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/RelationsResult.java new file mode 100644 index 000000000..70225850c --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/RelationsResult.java @@ -0,0 +1,114 @@ +package com.github.binarywang.wxpay.bean.marketing.payroll; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + *
+ * 查询微工卡授权关系
+ * 文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/Offline/apis/chapter4_1_2.shtml
+ *
+ * 适用对象:服务商
+ * 请求URL:https://api.mch.weixin.qq.com/v3/payroll-card/relations/{openid}
+ * 请求方式:GET
+ * 
+ * + * @author xiaoqiang + * @date 2021/12/2 + */ +@Data +@NoArgsConstructor +public class RelationsResult implements Serializable { + private static final long serialVersionUID = 1L; + + /** + *
+   * 字段名:用户标识
+   * 变量名:openid
+   * 是否必填:是
+   * 类型:string[1, 64]
+   * 描述:
+   *  用户在商户对应appid下的唯一标识
+   *  示例值:9x111111
+   * 
+ */ + @SerializedName(value = "openid") + private String openid; + + /** + *
+   * 字段名:服务商商户号
+   * 变量名:mchid
+   * 是否必填:是
+   * 类型:string[1, 32]
+   * 描述:
+   *  微信服务商商户的商户号,由微信支付生成并下发。
+   *  示例值:1111111
+   * 
+ */ + @SerializedName(value = "mchid") + private String mchid; + + /** + *
+   * 字段名:子商户号
+   * 变量名:sub_mchid
+   * 是否必填:是
+   * 类型:string[1, 32]
+   * 描述:
+   *  微信服务商下特约商户的商户号,由微信支付生成并下发
+   *  示例值:1111111
+   * 
+ */ + @SerializedName(value = "sub_mchid") + private String subMchid; + + /** + *
+   * 字段名:授权状态
+   * 变量名:authorize_state
+   * 是否必填:是
+   * 类型:string[1, 32]
+   * 描述:
+   *  授权状态:
+   *      UNAUTHORIZED:未授权
+   *      AUTHORIZED:已授权
+   *      DEAUTHORIZED:已取消授权
+   * 示例值:UNAUTHORIZED
+   * 
+ */ + @SerializedName(value = "authorize_state") + private String authorizeState; + + /** + *
+   * 字段名:授权时间
+   * 变量名:authorize_time
+   * 是否必填:否
+   * 类型:string[1, 32]
+   * 描述:
+   *  授权时间,遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss.sss+TIMEZONE,YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss.sss表示时分秒毫秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。例如:2015-05-20T13:29:35.120+08:00表示北京时间2015年05月20日13点29分35秒。
+   *  示例值:2015-05-20T13:29:35.120+08:00
+   * 
+ */ + @SerializedName(value = "authorize_time") + private String authorizeTime; + + /** + *
+   * 字段名:取消授权时间
+   * 变量名:deauthorize_time
+   * 是否必填:否
+   * 类型:string[1, 32]
+   * 描述:
+   *  取消授权时间,遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss.sss+TIMEZONE,YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss.sss表示时分秒毫秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。例如:2015-05-20T13:29:35.120+08:00表示北京时间2015年05月20日13点29分35秒。
+   *  示例值:2015-05-20T13:29:35.120+08:00
+   * 
+ */ + @SerializedName(value = "deauthorize_time") + private String deauthorizeTime; + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/SubFundFlowBillResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/SubFundFlowBillResult.java new file mode 100644 index 000000000..f1c9d3abd --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/SubFundFlowBillResult.java @@ -0,0 +1,100 @@ +package com.github.binarywang.wxpay.bean.marketing.payroll; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + *
+ * 申请单个子商户资金账单
+ * 文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/Offline/apis/chapter4_1_25.shtml
+ *
+ * 适用对象:服务商
+ * 请求URL:https://api.mch.weixin.qq.com/v3/bill/sub-merchant-fundflowbill
+ * 请求方式:GET
+ * 
+ * + * @author xiaoqiang + * @date 2021/12/7 + */ +@Data +@NoArgsConstructor +public class SubFundFlowBillResult implements Serializable { + private static final long serialVersionUID = 1L; + + /** + *
+   * 字段名:子商户号
+   * 变量名:sub_mchid
+   * 是否必填:是
+   * 类型:string[1, 32]
+   * 描述:
+   *  query下载指定子商户的账单。
+   *  示例值:19000000001
+   * 
+ */ + @SerializedName(value = "sub_mchid") + private String subMchid; + + /** + *
+   * 字段名:账单日期
+   * 变量名:bill_date
+   * 是否必填:是
+   * 类型:string[10, 10]
+   * 描述:
+   *  query格式YYYY-MM-DD
+   *  示例值:2019-06-11
+   * 
+ */ + @SerializedName(value = "bill_date") + private String billDate; + + /** + *
+   * 字段名:资金账户类型
+   * 变量名:account_type
+   * 是否必填:是
+   * 类型:string[1, 16]
+   * 描述:
+   * query枚举值:
+   *     BASIC:基本账户
+   *     OPERATION:运营账户
+   *     FEES:手续费账户
+   * 示例值:BASIC
+   * 
+ */ + @SerializedName(value = "account_type") + private String accountType; + /** + *
+   * 字段名:加密算法
+   * 变量名:algorithm
+   * 是否必填:是
+   * 类型:string[1, 31]
+   * 描述:
+   * query枚举值:
+   *     AEAD_AES_256_GCM:AEAD_AES_256_GCM加密算法
+   * 示例值:AEAD_AES_256_GCM
+   * 
+ */ + @SerializedName(value = "algorithm") + private String algorithm; + /** + *
+   * 字段名:压缩格式
+   * 变量名:tar_type
+   * 是否必填:否
+   * 类型:string[1, 8]
+   * 描述:
+   * query不填则以不压缩的方式返回数据流
+   *     枚举值:
+   *     GZIP:返回格式为.gzip的压缩包账单
+   * 示例值:GZIP
+   * 
+ */ + @SerializedName(value = "tar_type") + private String tarType; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/TokensRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/TokensRequest.java new file mode 100644 index 000000000..f760a10f9 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/TokensRequest.java @@ -0,0 +1,128 @@ +package com.github.binarywang.wxpay.bean.marketing.payroll; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + *
+ * 生成授权token
+ * 文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/Offline/apis/chapter4_1_1.shtml
+ *
+ * 适用对象:服务商
+ * 请求URL:https://api.mch.weixin.qq.com/v3/payroll-card/tokens
+ * 请求方式:POST
+ * 
+ * + * @author xiaoqiang + * @date 2021/12/2 + */ +@Data +@NoArgsConstructor +public class TokensRequest implements Serializable { + private static final long serialVersionUID = 1L; + + /** + *
+   * 字段名:用户标识
+   * 变量名:openid
+   * 是否必填:是
+   * 类型:string[1, 64]
+   * 描述:
+   *  用户在商户对应appid下的唯一标识
+   *  示例值:9x111111
+   * 
+ */ + @SerializedName(value = "openid") + private String openid; + + /** + *
+   * 字段名:应用ID
+   * 变量名:appid
+   * 是否必填:二选一
+   * 类型:string[1, 32]
+   * 描述:
+   *   是服务商在微信申请公众号/小程序或移动应用成功后分配的账号ID(与服务商主体一致),登录平台为mp.weixin.qq.com或open.weixin.qq.com。
+   *  当输入应用ID时,会校验其与服务商商户号的绑定关系。服务商应用ID和与子商户应用ID至少输入一个,且必须要有拉起微工卡时使用的APPID。
+   *  示例值:wxa1111111
+   * 
+ */ + @SerializedName(value = "appid") + private String appid; + + /** + *
+   * 字段名:子商户应用ID
+   * 变量名:sub_appid
+   * 是否必填:是
+   * 类型:string[1, 32]
+   * 描述:
+   *  是特约商户在微信申请公众号/小程序或移动应用成功后分配的账号ID(与特约商户主体一致),登录平台为mp.weixin.qq.com或open.weixin.qq.com。当输入子商户应用ID时,会校验其与特约商户号的绑定关系。 服务商应用ID和与子商户应用ID至少输入一个,且必须要有拉起微工卡时使用的APPID。
+   *  示例值:wxa1111111
+   * 
+ */ + @SerializedName(value = "sub_appid") + private String subAppid; + + /** + *
+   * 字段名:子商户号
+   * 变量名:sub_mchid
+   * 是否必填:是
+   * 类型:string[1, 32]
+   * 描述:
+   *  微信服务商下特约商户的商户号,由微信支付生成并下发
+   * 示例值:1111111
+   * 
+ */ + @SerializedName(value = "sub_mchid") + private String subMchid; + + /** + *
+   * 字段名:用户实名
+   * 变量名:user_name
+   * 是否必填:是
+   * 类型:string[1, 1024]
+   * 描述:
+   *  用户证件号,该字段需进行加密处理,加密方法详见敏感信息加密说明。(提醒:必须在HTTP头中上送Wechatpay-Serial)
+   * 示例值:7FzH5XksJG3a8HLLsaaUV6K54y1OnPMY5
+   * 
+ */ + @SerializedName(value = "user_name") + private String userName; + + /** + *
+   * 字段名:用户证件号
+   * 变量名:id_card_number
+   * 是否必填:是
+   * 类型:string[1, 1024]
+   * 描述:
+   *  用户证件号,该字段需进行加密处理,加密方法详见敏感信息加密说明。(提醒:必须在HTTP头中上送Wechatpay-Serial)
+   * 示例值:7FzH5XksJG3a8HLLsaaUV6K54y1OnPMY5
+   * 
+ */ + @SerializedName(value = "id_card_number") + private String idCardNumber; + + /** + *
+   * 字段名:用工类型
+   * 变量名:employment_type
+   * 是否必填:是
+   * 类型:string[1,32]
+   * 描述:
+   *  微工卡服务仅支持用于与商户有用工关系的用户,需明确用工类型;参考值:
+   * LONG_TERM_EMPLOYMENT:长期用工,
+   * SHORT_TERM_EMPLOYMENT: 短期用工,
+   * COOPERATION_EMPLOYMENT:合作关系
+   * 示例值:LONG_TERM_EMPLOYMENT
+   * 
+ */ + @SerializedName(value = "employment_type") + private String employmentType; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/TokensResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/TokensResult.java new file mode 100644 index 000000000..c49286436 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/payroll/TokensResult.java @@ -0,0 +1,97 @@ +package com.github.binarywang.wxpay.bean.marketing.payroll; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + *
+ * 生成授权token
+ * 文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/Offline/apis/chapter4_1_1.shtml
+ *
+ * 适用对象:服务商
+ * 请求URL:https://api.mch.weixin.qq.com/v3/payroll-card/tokens
+ * 请求方式:POST
+ * 
+ * + * @author xiaoqiang + * @date 2021/12/2 + */ +@Data +@NoArgsConstructor +public class TokensResult implements Serializable { + private static final long serialVersionUID = 1L; + + /** + *
+   * 字段名:用户标识
+   * 变量名:openid
+   * 是否必填:是
+   * 类型:string[1, 64]
+   * 描述:
+   *  用户在商户对应appid下的唯一标识
+   *  示例值:9x111111
+   * 
+ */ + @SerializedName(value = "openid") + private String openid; + + /** + *
+   * 字段名:服务商商户号
+   * 变量名:mchid
+   * 是否必填:是
+   * 类型:string[1, 32]
+   * 描述:
+   *  微信服务商商户的商户号,由微信支付生成并下发。
+   *  示例值:1111111
+   * 
+ */ + @SerializedName(value = "mchid") + private String mchid; + + /** + *
+   * 字段名:子商户号
+   * 变量名:sub_mchid
+   * 是否必填:是
+   * 类型:string[1, 32]
+   * 描述:
+   *  微信服务商下特约商户的商户号,由微信支付生成并下发
+   *  示例值:1111111
+   * 
+ */ + @SerializedName(value = "sub_mchid") + private String subMchid; + + /** + *
+   * 字段名:授权token
+   * 变量名:token
+   * 是否必填:是
+   * 类型:string[1, 1024]
+   * 描述:
+   *  授权token
+   *  示例值:abcdefghijklmn
+   * 
+ */ + @SerializedName(value = "token") + private String token; + + /** + *
+   * 字段名:token有效时间
+   * 变量名:expires_in
+   * 是否必填:是
+   * 类型:int
+   * 描述:
+   * token有效时间,单位秒
+   * 示例值:1800
+   * 
+ */ + @SerializedName(value = "expires_in") + private Integer expiresIn; + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/transfer/BatchDetailsRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/transfer/BatchDetailsRequest.java new file mode 100644 index 000000000..aeb778f69 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/transfer/BatchDetailsRequest.java @@ -0,0 +1,53 @@ +package com.github.binarywang.wxpay.bean.marketing.transfer; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + *
+ * 微信支付明细单号查询明细单API
+ * 文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer_partner/chapter3_3.shtml
+ *
+ * 适用对象:服务商
+ * 请求URL:https://api.mch.weixin.qq.com/v3/partner-transfer/batches/batch-id/{batch_id}/details/detail-id/{detail_id}
+ * 请求方式:GET
+ * 接口限频:单个服务商 50QPS,如果超过频率限制,会报错FREQUENCY_LIMITED,请降低频率请求。
+ * 
+ * + * @author xiaoqiang + * @date 2021-12-06 + */ +@Data +@NoArgsConstructor +public class BatchDetailsRequest implements Serializable { + public static final float serialVersionUID = 1L; + /** + *
+   * 字段名:微信支付批次单号
+   * 变量名:need_query_detail
+   * 是否必填:是
+   * 类型:string[32, 64]
+   * 描述:
+   *  path微信支付批次单号,微信商家转账系统返回的唯一标识
+   *  示例值:1030000071100999991182020050700019480001
+   * 
+ */ + @SerializedName(value = "batch_id") + private String batchId; + /** + *
+   * 字段名:微信明细单号
+   * 变量名:need_query_detail
+   * 是否必填:是
+   * 类型:string[32, 64]
+   * 描述:
+   *  path微信支付系统内部区分转账批次单下不同转账明细单的唯一标识
+   *  示例值:1040000071100999991182020050700019500100
+   * 
+ */ + @SerializedName(value = "detail_id") + private String detailId; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/transfer/BatchDetailsResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/transfer/BatchDetailsResult.java new file mode 100644 index 000000000..5c77281e7 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/transfer/BatchDetailsResult.java @@ -0,0 +1,239 @@ +package com.github.binarywang.wxpay.bean.marketing.transfer; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +import java.io.Serializable; +import java.util.Date; + +/** + *
+ * 微信支付明细单号查询明细单API
+ * 文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer_partner/chapter3_3.shtml
+ *
+ * 适用对象:服务商
+ * 请求URL:https://api.mch.weixin.qq.com/v3/partner-transfer/batches/batch-id/{batch_id}/details/detail-id/{detail_id}
+ * 请求方式:GET
+ * 接口限频:单个服务商 50QPS,如果超过频率限制,会报错FREQUENCY_LIMITED,请降低频率请求。
+ * 
+ * + * @author xiaoqiang + * @date 2021-12-06 + */ +@Data +@NoArgsConstructor +public class BatchDetailsResult implements Serializable { + public static final float serialVersionUID = 1L; + + @Override + public String toString() { + return WxGsonBuilder.create().toJson(this); + } + + /** + *
+   * 字段名:服务商商户号
+   * 变量名:sp_mchid
+   * 是否必填:是
+   * 类型:string[1, 32]
+   * 描述:
+   *  微信支付分配的商户号,此处为服务商商户号
+   *  示例值:1900001109
+   * 
+ */ + @SerializedName(value = "sp_mchid") + private String spMchid; + /** + *
+   * 字段名:商家批次单号
+   * 变量名:out_batch_no
+   * 是否必填:是
+   * 类型:string[5, 32]
+   * 描述:
+   *  商户系统内部的商家批次单号,在商户系统内部唯一
+   *     示例值:plfk2020042013
+   * 
+ */ + @SerializedName(value = "out_batch_no") + private String outBatchNo; + /** + *
+   * 字段名:微信支付批次单号
+   * 变量名:batch_id
+   * 是否必填:是
+   * 类型:string[5, 32]
+   * 描述:
+   *  微信支付批次单号,微信商家转账系统返回的唯一标识
+   *     示例值:1030000071100999991182020050700019480001
+   * 
+ */ + @SerializedName(value = "batch_id") + private String batchId; + /** + *
+   * 字段名:商户的appid
+   * 变量名:
+   * 是否必填:否
+   * 类型:string[1, 32]
+   * 描述:
+   * 微信分配的特约商户公众账号ID。特约商户授权类型为 INFORMATION_AUTHORIZATION_TYPE和
+   *     INFORMATION_AND_FUND_AUTHORIZATION_TYPE时对应的是特约商户的appid,
+   *     特约商户授权类型为FUND_AUTHORIZATION_TYPE时为服务商的appid
+   * 例值:wxf636efh567hg4356
+   * 
+ */ + @Expose + @SerializedName(value = "appid") + private String appId; + /** + *
+   * 字段名:商家明细单号
+   * 变量名:out_detail_no
+   * 是否必填:是
+   * 类型:string[5, 32]
+   * 描述:
+   * 商户系统内部区分转账批次单下不同转账明细单的唯一标识
+   *     示例值:x23zy545Bd5436
+   * 
+ */ + @SerializedName(value = "out_detail_no") + private String outDetailNo; + /** + *
+   * 字段名:微信支付明细单号
+   * 变量名:detail_id
+   * 是否必填:是
+   * 类型:string[32, 64]
+   * 描述:
+   * 微信支付系统内部区分转账批次单下不同转账明细单的唯一标识
+   *     示例值:1040000071100999991182020050700019500100
+   * 
+ */ + @SerializedName(value = "detail_id") + private String detailId; + /** + *
+   * 字段名:明细状态
+   * 变量名:detail_status
+   * 是否必填:是
+   * 类型:string[1, 32]
+   * 描述:
+   * 枚举值:
+   *     PROCESSING:转账中。正在处理中,转账结果尚未明确
+   *     SUCCESS:转账成功
+   *     FAIL:转账失败。需要确认失败原因后,再决定是否重新发起对该笔明细单的转账(并非整个转账批次单)
+   * 示例值:SUCCESS
+   * 
+ */ + @SerializedName(value = "detail_status") + private String detailStatus; + /** + *
+   * 字段名:转账金额
+   * 变量名:transfer_amount
+   * 是否必填:是
+   * 类型:int
+   * 描述:
+   * 转账金额单位为“分”
+   *     示例值:200000
+   * 
+ */ + @SerializedName(value = "transfer_amount") + private Integer transferAmount; + /** + *
+   * 字段名:转账备注
+   * 变量名:transfer_remark
+   * 是否必填:是
+   * 类型:string[1, 32]
+   * 描述:
+   * 单条转账备注(微信用户会收到该备注),UTF8编码,最多允许32个字符
+   *     示例值:2020年4月报销
+   * 
+ */ + @SerializedName(value = "transfer_remark") + private String transferRemark; + /** + *
+   * 字段名:明细失败原因
+   * 变量名:fail_reason
+   * 是否必填:否
+   * 类型:string[1, 64]
+   * 描述:
+   * 如果转账失败则有失败原因:
+   *     ACCOUNT_FROZEN:账户冻结
+   *     REAL_NAME_CHECK_FAIL:用户未实名
+   *     NAME_NOT_CORRECT:用户姓名校验失败
+   *     OPENID_INVALID:Openid校验失败
+   *     TRANSFER_QUOTA_EXCEED:超过用户单笔收款额度
+   *     DAY_RECEIVED_QUOTA_EXCEED:超过用户单日收款额度
+   *     MONTH_RECEIVED_QUOTA_EXCEED:超过用户单月收款额度
+   *     DAY_RECEIVED_COUNT_EXCEED:超过用户单日收款次数
+   *     PRODUCT_AUTH_CHECK_FAIL:产品权限校验失败
+   *     OVERDUE_CLOSE:转账关闭
+   *     ID_CARD_NOT_CORRECT:用户身份证校验失败
+   *     ACCOUNT_NOT_EXIST:用户账户不存在
+   *     TRANSFER_RISK:转账存在风险
+   * 示例值:ACCOUNT_FROZEN
+   * 
+ */ + @SerializedName(value = "fail_reason") + private String failReason; + /** + *
+   * 字段名:收款用户openid
+   * 变量名:openid
+   * 是否必填:是
+   * 类型:string[1, 64]
+   * 描述:
+   * 收款用户openid。如果转账特约商户授权类型是INFORMATION_AUTHORIZATION_TYPE,对应的是特约商户公众号下的openid;
+   *     如果转账特约商户授权类型是FUND_AUTHORIZATION_TYPE,对应的是服务商商户公众号下的openid。
+   * 示例值:o-MYE42l80oelYMDE34nYD456Xoy
+   * 
+ */ + @SerializedName(value = "openid") + private String openid; + /** + *
+   * 字段名:收款用户姓名
+   * 变量名:username
+   * 是否必填:是
+   * 类型:string[1, 1024]
+   * 描述:
+   * 1、收款方姓名。采用标准RSA算法,公钥由微信侧提供
+   * 2、该字段需进行加密处理,加密方法详见敏感信息加密说明。(提醒:必须在HTTP头中上送Wechatpay-Serial)
+   *     示例值:757b340b45ebef5467rter35gf464344v3542sdf4t6re4tb4f54ty45t4yyry45
+   * 
+ */ + @SerializedName(value = "username") + private String userName; + /** + *
+   * 字段名:转账发起时间
+   * 变量名:initiate_time
+   * 是否必填:是
+   * 类型:string[1, 32]
+   * 描述:
+   * 转账发起的时间,遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss.sss+TIMEZONE,YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss.sss表示时分秒毫秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。例如:2015-05-20T13:29:35.120+08:00表示北京时间2015年05月20日13点29分35秒
+   *     示例值:2015-05-20T13:29:35.120+08:00
+   * 
+ */ + @SerializedName(value = "initiate_time") + private String initiateTime; + /** + *
+   * 字段名:明细更新时间
+   * 变量名:initiate_time
+   * 是否必填:是
+   * 类型:string[1, 32]
+   * 描述:
+   * 明细最后一次状态变更的时间,遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss.sss+TIMEZONE,YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss.sss表示时分秒毫秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。例如:2015-05-20T13:29:35.120+08:00表示北京时间2015年05月20日13点29分35秒
+   *     示例值:2015-05-20T13:29:35.120+08:00
+   * 
+ */ + @SerializedName(value = "update_time") + private String updateTime; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/transfer/BatchNumberRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/transfer/BatchNumberRequest.java new file mode 100644 index 000000000..904fc2d03 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/transfer/BatchNumberRequest.java @@ -0,0 +1,96 @@ +package com.github.binarywang.wxpay.bean.marketing.transfer; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + *
+ * 微信支付批次单号查询批次单API
+ * 文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer_partner/chapter3_2.shtml
+ * 
+ * + * @author xiaoqiang + * @date 2021-12-06 + */ +@Data +@NoArgsConstructor +public class BatchNumberRequest implements Serializable { + public static final float serialVersionUID = 1L; + + /** + *
+   * 字段名:微信支付批次单号
+   * 变量名:batch_id
+   * 是否必填:是
+   * 类型:string[32, 64]
+   * 描述:
+   *  path微信支付批次单号,微信商家转账系统返回的唯一标识
+   *  示例值:1030000071100999991182020050700019480001
+   * 
+ */ + @SerializedName(value = "batch_id") + private String batchId; + + /** + *
+   * 字段名:是否查询转账明细单
+   * 变量名:need_query_detail
+   * 是否必填:是
+   * 类型:boolean 默认否
+   * 描述:
+   *  商户可选择是否查询指定状态的转账明细单,当转账批次单状态为“FINISHED”(已完成)时,才会返回满足条件的转账明细单
+   *  示例值:true
+   * 
+ */ + @SerializedName(value = "need_query_detail") + private Boolean needQueryDetail; + + /** + *
+   * 字段名:请求资源起始位置
+   * 变量名:offset
+   * 是否必填:否
+   * 类型:int
+   * 描述:
+   *  query该次请求资源的起始位置。返回的明细是按照设置的明细条数进行分页展示的,一次查询可能无法返回所有明细,我们使用该参数标识查询开始位置,默认值为0
+   *  示例值:0
+   * 
+ */ + @SerializedName(value = "offset") + private Integer offset; + + /** + *
+   * 字段名:最大资源条数
+   * 变量名:limit
+   * 是否必填:否
+   * 类型:int
+   * 描述:
+   *  query该次请求可返回的最大明细条数,最小20条,最大100条,不传则默认20条。不足20条按实际条数返回
+   * 示例值:20
+   * 
+ */ + @SerializedName(value = "limit") + private Integer limit; + + /** + *
+   * 字段名:明细状态
+   * 变量名:detail_status
+   * 是否必填:否
+   * 类型:string[1, 32]
+   * 描述:
+   *  query查询指定状态的转账明细单,不传没有明细状态信息返回。当need_query_detail为true时,该字段必填
+   *  枚举值:
+   *     ALL:全部。需要同时查询转账成功和转账失败的明细单
+   *     SUCCESS:转账成功。只查询转账成功的明细单
+   *     FAIL:转账失败。只查询转账失败的明细单
+   *  示例值:FAIL
+   * 
+ */ + @SerializedName(value = "detail_status") + private String detailStatus; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/transfer/BatchNumberResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/transfer/BatchNumberResult.java new file mode 100644 index 000000000..9f1036d22 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/transfer/BatchNumberResult.java @@ -0,0 +1,393 @@ +package com.github.binarywang.wxpay.bean.marketing.transfer; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +import java.io.Serializable; +import java.util.List; + +/** + *
+ * 微信支付批次单号查询批次单API
+ * 文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer_partner/chapter3_2.shtml
+ * 
+ * + * @author xiaoqiang + * @date 2021-12-06 + */ +@Data +@NoArgsConstructor +public class BatchNumberResult implements Serializable { + public static final float serialVersionUID = 1L; + + @Override + public String toString() { + return WxGsonBuilder.create().toJson(this); + } + + /** + *
+   * 字段名:服务商商户号
+   * 变量名:sp_mchid
+   * 是否必填:是
+   * 类型:string[1, 32]
+   * 描述:
+   *  微信支付分配的服务商商户号
+   *  示例值:1900001109
+   * 
+ */ + @SerializedName(value = "sp_mchid") + private String spMchid; + + /** + *
+   * 字段名:特约商户号
+   * 变量名:sub_mchid
+   * 是否必填:是
+   * 类型:string[1, 32]
+   * 描述:
+   *  微信支付分配的特约商户号
+   *  示例值:1900000109
+   * 
+ */ + @SerializedName(value = "sub_mchid") + private String subMchid; + + /** + *
+   * 字段名:商家批次单号
+   * 变量名:out_batch_no
+   * 是否必填:是
+   * 类型:string[5, 32]
+   * 描述:
+   *  商户系统内部的商家批次单号,在商户系统内部唯一
+   *  示例值:plfk2020042013
+   * 
+ */ + @SerializedName(value = "out_batch_no") + private String outBatchNo; + + /** + *
+   * 字段名:微信支付批次单号
+   * 变量名:batch_id
+   * 是否必填:是
+   * 类型:string[32, 64]
+   * 描述:
+   *  微信支付批次单号,微信商家转账系统返回的唯一标识
+   *  示例值:1030000071100999991182020050700019480001
+   * 
+ */ + @SerializedName(value = "batch_id") + private String batchId; + /** + *
+   * 字段名:特约商户appid
+   * 变量名:sub_appid
+   * 是否必填:否
+   * 类型:string[1, 32]
+   * 描述:
+   *  微信分配的特约商户公众账号ID。特约商户appid
+   *   示例值:wxf636efh567hg4356
+   * 
+ */ + @SerializedName(value = "sub_appid") + private String subAppid; + /** + *
+   * 字段名:批次状态
+   * 变量名:batch_status
+   * 是否必填:是
+   * 类型:string[1, 32]
+   * 描述:
+   *  枚举值:
+   *     WAIT_PAY:待付款,商户员工确认付款阶段。
+   *     ACCEPTED:已受理。批次已受理成功,若发起批量转账的30分钟后,转账批次单仍处于该状态,可能原因是商户账户余额不足等。商户可查询账户资金流水,若该笔转账批次单的扣款已经发生,则表示批次已经进入转账中,请再次查单确认
+   *     PROCESSING:转账中。已开始处理批次内的转账明细单
+   *     FINISHED:已完成。批次内的所有转账明细单都已处理完成
+   *     CLOSED:已关闭。可查询具体的批次关闭原因确认
+   *  示例值:ACCEPTED
+   * 
+ */ + @SerializedName(value = "batch_status") + private String batchStatus; + + /** + *
+   * 字段名:批次类型
+   * 变量名:batch_type
+   * 是否必填:是
+   * 类型:string[1, 32]
+   * 描述:
+   *  枚举值:
+   *     API:API方式发起
+   *     WEB:页面方式发起
+   * 示例值:API
+   * 
+ */ + @SerializedName(value = "batch_type") + private String batchType; + + /** + *
+   * 字段名:特约商户授权类型
+   * 变量名:authorization_type
+   * 是否必填:是
+   * 类型:string[1, 32]
+   * 描述:
+   *  特约商户授权类型:
+   *     INFORMATION_AUTHORIZATION_TYPE:信息授权类型
+   *     FUND_AUTHORIZATION_TYPE:资金授权类型
+   *     INFORMATION_AND_FUND_AUTHORIZATION_TYPE:信息和资金授权类型
+   * 示例值:INFORMATION_AUTHORIZATION_TYPE
+   * 
+ */ + @SerializedName(value = "authorization_type") + private String authorizationType; + + /** + *
+   * 字段名:批次名称
+   * 变量名:batch_name
+   * 是否必填:是
+   * 类型:string[1, 32]
+   * 描述:
+   * 示例值:2019年1月深圳分部报销单
+   * 
+ */ + @SerializedName(value = "batch_name") + private String batchName; + + /** + *
+   * 字段名:批次备注
+   * 变量名:batch_remark
+   * 是否必填:是
+   * 类型:string[1, 32]
+   * 描述:
+   *      转账说明,UTF8编码,最多允许32个字符
+   *  示例值:2019年1月深圳分部报销单
+   * 
+ */ + @SerializedName(value = "batch_remark") + private String batchRemark; + + /** + *
+   * 字段名:批次关闭原因
+   * 变量名:close_reason
+   * 是否必填:否
+   * 类型:string[1, 64]
+   * 描述:
+   *    如果批次单状态为“CLOSED”(已关闭),则有关闭原因:
+   *     MERCHANT_REVOCATION:商户主动撤销
+   *     OVERDUE_CLOSE:系统超时关闭
+   * 示例值:OVERDUE_CLOSE
+   * 
+ */ + @SerializedName(value = "close_reason") + private String closeReason; + + /** + *
+   * 字段名:转账总金额
+   * 变量名:total_amount
+   * 是否必填:是
+   * 类型:int
+   * 描述:
+   *   转账金额单位为“分”
+   * 示例值:4000000
+   * 
+ */ + @SerializedName(value = "total_amount") + private Integer totalAmount; + + /** + *
+   * 字段名:转账总笔数
+   * 变量名:total_num
+   * 是否必填:是
+   * 类型:int
+   * 描述:
+   *   一个转账批次单最多发起三千笔转账
+   * 示例值:200
+   * 
+ */ + @SerializedName(value = "total_num") + private Integer totalNum; + /** + *
+   * 字段名:批次创建时间
+   * 变量名:create_time
+   * 是否必填:否
+   * 类型:string[1, 32]
+   * 描述:
+   *  批次受理成功时返回,遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss.sss+TIMEZONE,YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss.sss表示时分秒毫秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。例如:2015-05-20T13:29:35.120+08:00表示北京时间2015年05月20日13点29分35秒
+   * 示例值:2015-05-20T13:29:35.120+08:00
+   * 
+ */ + @SerializedName(value = "create_time") + private String createTime; + /** + *
+   * 字段名:批次更新时间
+   * 变量名:update_time
+   * 是否必填:否
+   * 类型:string[1, 32]
+   * 描述:
+   *  批次最近一次状态变更的时间,遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss.sss+TIMEZONE,YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss.sss表示时分秒毫秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。例如:2015-05-20T13:29:35.120+08:00表示北京时间2015年05月20日13点29分35秒
+   *  示例值:2015-05-20T13:29:35.120+08:00
+   * 
+ */ + @SerializedName(value = "update_time") + private String updateTime; + /** + *
+   * 字段名:转账成功金额
+   * 变量名:success_amount
+   * 是否必填:否
+   * 类型:int
+   * 描述:
+   *  转账成功的金额,单位为“分”。当批次状态为“PROCESSING”(转账中)时,转账成功金额随时可能变化
+   *  示例值:3900000
+   * 
+ */ + @SerializedName(value = "success_amount") + private Integer successAmount; + /** + *
+   * 字段名:转账成功笔数
+   * 变量名:success_num
+   * 是否必填:否
+   * 类型:int
+   * 描述:
+   *  转账成功的笔数。当批次状态为“PROCESSING”(转账中)时,转账成功笔数随时可能变化
+   * 示例值:199
+   * 
+ */ + @SerializedName(value = "success_num") + private Integer successNum; + /** + *
+   * 字段名:转账失败金额
+   * 变量名:fail_amount
+   * 是否必填:否
+   * 类型:int
+   * 描述:
+   *  转账失败的金额,单位为“分”
+   *     示例值:100000
+   * 
+ */ + @SerializedName(value = "fail_amount") + private Integer failAmount; + /** + *
+   * 字段名:转账失败笔数
+   * 变量名:fail_amount
+   * 是否必填:否
+   * 类型:int
+   * 描述:
+   *  转账失败的笔数
+   *     示例值:1
+   * 
+ */ + @SerializedName(value = "fail_num") + private Integer failNum; + /** + *
+   * 字段名:转账明细单列表
+   * 变量名:transfer_detail_list
+   * 是否必填:否
+   * 类型:int
+   * 描述:
+   *  当批次状态为“FINISHED”(已完成),且成功查询到转账明细单时返回。包括微信明细单号、明细状态信息
+   * 
+ */ + @SerializedName(value = "transfer_detail_list") + private List transferDetailList; + + @Data + @NoArgsConstructor + public static class TransferDetail implements Serializable { + private static final long serialVersionUID = 10941148801492L; + /** + *
+     * 字段名:微信支付明细单号
+     * 变量名:detail_id
+     * 是否必填:是
+     * 类型:string[32, 64]
+     * 描述:
+     *  微信支付系统内部区分转账批次单下不同转账明细单的唯一标识
+     *  示例值:1040000071100999991182020050700019500100
+     * 
+ */ + @SerializedName(value = "detail_id") + private String detailId; + + /** + *
+     * 字段名:商家明细单号
+     * 变量名:out_detail_no
+     * 是否必填:是
+     * 类型:string[5, 32]
+     * 描述:
+     *  商户系统内部区分转账批次单下不同转账明细单的唯一标识
+     *  示例值:x23zy545Bd5436
+     * 
+ */ + @SerializedName(value = "out_detail_no") + private String outDetailNo; + + /** + *
+     * 字段名:明细状态
+     * 变量名:detail_status
+     * 是否必填:是
+     * 类型:string[1, 32]
+     * 描述:
+     *     枚举值:
+     *         PROCESSING:转账中。正在处理中,转账结果尚未明确
+     *         SUCCESS:转账成功
+     *         FAIL:转账失败。需要确认失败原因后,再决定是否重新发起对该笔明细单的转账(并非整个转账批次单)
+     * 示例值:SUCCESS
+     * 
+ */ + @SerializedName(value = "detail_status") + private String detailStatus; + } + + /** + *
+   * 字段名:服务商的appid
+   * 变量名:sp_appid
+   * 是否必填:否
+   * 类型:string[1, 32]
+   * 描述:
+   *    微信分配的服务商商户公众账号ID,特约商户授权类型为FUND_AUTHORIZATION_TYPE时才有该字段
+   *     示例值:wxf636efh567hg4388
+   * 
+ */ + @SerializedName(value = "sp_appid") + private String spAppid; + /** + *
+   * 字段名:批量转账用途
+   * 变量名:transfer_purpose
+   * 是否必填:否
+   * 类型:string[1,16]
+   * 描述:
+   *   批量转账用途:
+   *     GOODSPAYMENT:货款
+   *     COMMISSION:佣金
+   *     REFUND:退款
+   *     REIMBURSEMENT:报销
+   *     FREIGHT:运费
+   *     OTHERS:其他
+   * 示例值:COMMISSION
+   * 
+ */ + @SerializedName(value = "transfer_purpose") + private String transferPurpose; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/transfer/BillReceiptResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/transfer/BillReceiptResult.java new file mode 100644 index 000000000..a6f98e6aa --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/transfer/BillReceiptResult.java @@ -0,0 +1,143 @@ +package com.github.binarywang.wxpay.bean.marketing.transfer; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +import java.io.Serializable; + +/** + * 转账电子回单申请受理API + * 接口说明 + * 适用对象:直连商户 服务商 + * 请求URL:https://api.mch.weixin.qq.com/v3/transfer/bill-receipt + * 请求方式:POST + * + * @author xiaoqiang + * @date 2021-12-06 + */ +@Data +@NoArgsConstructor +public class BillReceiptResult implements Serializable { + public static final float serialVersionUID = 1L; + + @Override + public String toString() { + return WxGsonBuilder.create().toJson(this); + } + + /** + *
+   * 字段名:商家批次单号
+   * 变量名:out_batch_no
+   * 是否必填:是
+   * 类型:string[5,32]
+   * 描述:
+   *  商户系统内部的商家批次单号,在商户系统内部唯一。需要电子回单的批次单号
+   *  示例值:plfk2020042013
+   * 
+ */ + @SerializedName(value = "out_batch_no") + private String outBatchNo; + + /** + *
+   * 字段名:电子回单申请单号
+   * 变量名:signature_no
+   * 是否必填:是
+   * 类型:string[3.45]
+   * 描述:
+   *  电子回单申请单号,申请单据的唯一标识
+   *     示例值:1050000010509999485212020110200058820001
+   * 
+ */ + @SerializedName(value = "signature_no") + private String signatureNo; + + /** + *
+   * 字段名:电子回单状态
+   * 变量名:signature_status
+   * 是否必填:否
+   * 类型:string[1,10]
+   * 描述:
+   *  枚举值:
+   *     ACCEPTED:已受理,电子签章已受理成功
+   *     FINISHED:已完成。电子签章已处理完成
+   *     示例值:ACCEPTED
+   * 
+ */ + @SerializedName(value = "signature_status") + private String signatureStatus; + + /** + *
+   * 字段名:电子回单文件的hash方法
+   * 变量名:hash_type
+   * 是否必填:否
+   * 类型:string[1,20]
+   * 描述:
+   *  电子回单文件的hash方法,回单状态为:FINISHED时返回。
+   *     示例值:SHA256
+   * 
+ */ + @SerializedName(value = "hash_type") + private String hashType; + + /** + *
+   * 字段名:电子回单文件的hash值
+   * 变量名:hash_value
+   * 是否必填:否
+   * 类型:string[3,1000]
+   * 描述:
+   *  电子回单文件的hash值,用于下载之后验证文件的完整、正确性,回单状态为:FINISHED时返回。
+   *     示例值:DE731F35146A0BEFADE5DB9D1E468D96C01CA8898119C674FEE9F11F4DBE5529
+   * 
+ */ + @SerializedName(value = "hash_value") + private String hashValue; + + + /** + *
+   * 字段名:电子回单文件的下载地址
+   * 变量名:hash_value
+   * 是否必填:否
+   * 类型:string[10,3000]
+   * 描述:
+   *  电子回单文件的下载地址,回单状态为:FINISHED时返回。URL有效时长为10分钟,10分钟后需要重新去获取下载地址
+   *     示例值:https://api.mch.weixin.qq.com/v3/billdownload/file?token=xxx
+   * 
+ */ + @SerializedName(value = "download_url") + private String downloadUrl; + + /** + *
+   * 字段名:创建时间
+   * 变量名:create_time
+   * 是否必填:否
+   * 类型:string[1, 32]
+   * 描述:
+   * 	电子签章单创建时间,遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss.sss+TIMEZONE,YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss.sss表示时分秒毫秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。例如:2015-05-20T13:29:35.120+08:00表示北京时间2015年05月20日13点29分35秒
+   *     示例值:2020-05-20T13:29:35.120+08:00
+   * 
+ */ + @SerializedName(value = "create_time") + private String createTime; + /** + *
+   * 字段名:更新时间
+   * 变量名:update_time
+   * 是否必填:否
+   * 类型:string[1, 32]
+   * 描述:
+   * 	电子签章单最近一次状态变更的时间,遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss.sss+TIMEZONE,YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss.sss表示时分秒毫秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。例如:2015-05-20T13:29:35.120+08:00表示北京时间2015年05月20日13点29分35秒
+   *     示例值:2020-05-21T13:29:35.120+08:00
+   * 
+ */ + @SerializedName(value = "update_time") + private String updateTime; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/transfer/DownloadRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/transfer/DownloadRequest.java new file mode 100644 index 000000000..639b2cd57 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/transfer/DownloadRequest.java @@ -0,0 +1,65 @@ +package com.github.binarywang.wxpay.bean.marketing.transfer; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 下载电子回单API + * 接口说明 + * 适用对象:直连商户 服务商 + * 请求URL:通过申请账单接口获取到“download_url”,URL有效期10min + * 请求方式:GET + * 前置条件:调用申请账单接口并获取到“download_url” + * 接口规则:https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay-1.shtml + * + * @author xiaoqiang + * @date 2021-12-06 + */ +@Data +@NoArgsConstructor +public class DownloadRequest implements Serializable { + public static final float serialVersionUID = 1L; + + /** + *
+   * 字段名:电子回单文件的hash方法
+   * 变量名:hash_type
+   * 是否必填:否
+   * 类型:string[1, 20]
+   * 描述:
+   *  电子回单文件的hash方法,回单状态为:FINISHED时返回
+   *  例值:SHA256
+   * 
+ */ + @SerializedName(value = "hash_type") + private String hashType; + /** + *
+   * 字段名:电子回单文件的hash值
+   * 变量名:hash_value
+   * 是否必填:否
+   * 类型:string[3, 1000]
+   * 描述:
+   *  电子回单文件的hash值,用于下载之后验证文件的完整、正确性,回单状态为:FINISHED时返回
+   *  示例值:DE731F35146A0BEFADE5DB9D1E468D96C01CA8898119C674FEE9F11F4DBE5529
+   * 
+ */ + @SerializedName(value = "hash_value") + private String hashValue; + /** + *
+   * 字段名:电子回单文件的下载地址
+   * 变量名:download_url
+   * 是否必填:否
+   * 类型:string[10, 3000]
+   * 描述:
+   *  电子回单文件的下载地址,回单状态为:FINISHED时返回。URL有效时长为10分钟,10分钟后需要重新去获取下载地址(但不需要走受理)
+   * 示例值:https://api.mch.weixin.qq.com/v3/billdownload/file?token=xxx
+   * 
+ */ + @SerializedName(value = "download_url") + private String downloadUrl; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/transfer/ElectronicReceiptsRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/transfer/ElectronicReceiptsRequest.java new file mode 100644 index 000000000..6c54cbc9c --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/transfer/ElectronicReceiptsRequest.java @@ -0,0 +1,70 @@ +package com.github.binarywang.wxpay.bean.marketing.transfer; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 转账明细电子回单受理API + * 适用对象:服务商 + * 请求URL:https://api.mch.weixin.qq.com/v3/transfer-detail/electronic-receipts + * 请求方式:POST + * 前置条件:只支持受理最近90天内的转账明细单 + * 接口规则:https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay-1.shtml + * + * @author xiaoqiang + * @date 2021-12-06 + */ +@Data +@NoArgsConstructor +public class ElectronicReceiptsRequest implements Serializable { + public static final float serialVersionUID = 1L; + /** + *
+   * 字段名:受理类型
+   * 变量名:accept_type
+   * 是否必填:是
+   * 类型:string[1,32]
+   * 描述:
+   *  body电子回单受理类型:
+   *     BATCH_TRANSFER:批量转账明细电子回单
+   *     TRANSFER_TO_POCKET:企业付款至零钱电子回单
+   *     TRANSFER_TO_BANK:企业付款至银行卡电子回单
+   * 示例值:BATCH_TRANSFER
+   * 
+ */ + @SerializedName(value = "accept_type") + private String acceptType; + + /** + *
+   * 字段名:商家转账批次单号
+   * 变量名:out_batch_no
+   * 是否必填:否
+   * 类型:string[5, 32]
+   * 描述:
+   *  body需要电子回单的批量转账明细单所在的转账批次单号,该单号为商户申请转账时生成的商户单号。受理类型为BATCH_TRANSFER时该单号必填,否则该单号留空。
+   *  示例值:GD2021011610162610BBdkkIwcu3
+   * 
+ */ + @SerializedName(value = "out_batch_no") + private String outBatchNo; + + /** + *
+   * 字段名:商家转账明细单号
+   * 变量名:out_batch_no
+   * 是否必填:是
+   * 类型:string[5, 32]
+   * 描述:
+   *  body该单号为商户申请转账时生成的商家转账明细单号。
+   *             1.受理类型为BATCH_TRANSFER时填写商家批量转账明细单号。
+   *             2. 受理类型为TRANSFER_TO_POCKET或TRANSFER_TO_BANK时填写商家转账单号。
+   *  示例值:mx0911231610162610v4CNkO4HAf
+   * 
+ */ + @SerializedName(value = "out_detail_no") + private String outDetailNo; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/transfer/ElectronicReceiptsResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/transfer/ElectronicReceiptsResult.java new file mode 100644 index 000000000..75d1243e4 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/transfer/ElectronicReceiptsResult.java @@ -0,0 +1,137 @@ +package com.github.binarywang.wxpay.bean.marketing.transfer; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 转账明细电子回单受理API + * 适用对象:服务商 + * 请求URL:https://api.mch.weixin.qq.com/v3/transfer-detail/electronic-receipts + * 请求方式:POST + * 前置条件:只支持受理最近90天内的转账明细单 + * 接口规则:https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay-1.shtml + * + * @author xiaoqiang + * @date 2021-12-06 + */ +@Data +@NoArgsConstructor +public class ElectronicReceiptsResult implements Serializable { + public static final float serialVersionUID = 1L; + /** + *
+   * 字段名:受理类型
+   * 变量名:accept_type
+   * 是否必填:是
+   * 类型:string[1,32]
+   * 描述:
+   *  电子回单受理类型:
+   *     BATCH_TRANSFER:批量转账明细电子回单
+   *     TRANSFER_TO_POCKET:企业付款至零钱电子回单
+   *     TRANSFER_TO_BANK:企业付款至银行卡电子回单
+   *  示例值:BATCH_TRANSFER
+   * 
+ */ + @SerializedName(value = "accept_type") + private String acceptType; + + /** + *
+   * 字段名:商家转账批次单号
+   * 变量名:out_batch_no
+   * 是否必填:否
+   * 类型:string[5, 32]
+   * 描述:
+   *  需要电子回单的批量转账明细单所在的转账批次单号,该单号为商户申请转账时生成的商户单号。受理类型为BATCH_TRANSFER时该单号必填,否则该单号留空。
+   *  示例值:GD2021011610162610BBdkkIwcu3
+   * 
+ */ + @SerializedName(value = "out_batch_no") + private String outBatchNo; + + /** + *
+   * 字段名:商家转账明细单号
+   * 变量名:out_detail_no
+   * 是否必填:是
+   * 类型:string[5, 32]
+   * 描述:
+   *  该单号为商户申请转账时生成的商家转账明细单号。
+   *     1.受理类型为BATCH_TRANSFER时填写商家批量转账明细单号。
+   *     2. 受理类型为TRANSFER_TO_POCKET或TRANSFER_TO_BANK时填写商家转账单号。
+   *  示例值:mx0911231610162610v4CNkO4HAf
+   * 
+ */ + @SerializedName(value = "out_detail_no") + private String outDetailNo; + /** + *
+   * 字段名:电子回单受理单号
+   * 变量名:signature_no
+   * 是否必填:是
+   * 类型:string[3, 256]
+   * 描述:
+   *  电子回单受理单号,受理单据的唯一标识
+   *  示例值:1050000010509999485212020110200058820001
+   * 
+ */ + @SerializedName(value = "signature_no") + private String signatureNo; + /** + *
+   * 字段名:电子回单状态
+   * 变量名:signature_status
+   * 是否必填:否
+   * 类型:string[1, 10]
+   * 描述:
+   *  枚举值:
+   *     ACCEPTED:已受理,电子签章已受理成功
+   *     FINISHED:已完成。电子签章已处理完成
+   *  示例值:ACCEPTED
+   * 
+ */ + @SerializedName(value = "signature_status") + private String signatureStatus; + /** + *
+   * 字段名:电子回单文件的hash方法
+   * 变量名:hash_type
+   * 是否必填:否
+   * 类型:string[1, 20]
+   * 描述:
+   *  电子回单文件的hash方法,回单状态为:FINISHED时返回
+   *  例值:SHA256
+   * 
+ */ + @SerializedName(value = "hash_type") + private String hashType; + /** + *
+   * 字段名:电子回单文件的hash值
+   * 变量名:hash_value
+   * 是否必填:否
+   * 类型:string[3, 1000]
+   * 描述:
+   *  电子回单文件的hash值,用于下载之后验证文件的完整、正确性,回单状态为:FINISHED时返回
+   *  示例值:DE731F35146A0BEFADE5DB9D1E468D96C01CA8898119C674FEE9F11F4DBE5529
+   * 
+ */ + @SerializedName(value = "hash_value") + private String hashValue; + /** + *
+   * 字段名:电子回单文件的下载地址
+   * 变量名:download_url
+   * 是否必填:否
+   * 类型:string[10, 3000]
+   * 描述:
+   *  电子回单文件的下载地址,回单状态为:FINISHED时返回。URL有效时长为10分钟,10分钟后需要重新去获取下载地址(但不需要走受理)
+   * 示例值:https://api.mch.weixin.qq.com/v3/billdownload/file?token=xxx
+   * 
+ */ + @SerializedName(value = "download_url") + private String downloadUrl; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/transfer/MerchantBatchRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/transfer/MerchantBatchRequest.java new file mode 100644 index 000000000..3a1e3ed80 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/transfer/MerchantBatchRequest.java @@ -0,0 +1,97 @@ +package com.github.binarywang.wxpay.bean.marketing.transfer; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 商家批次单号查询批次单API + * 适用对象:服务商 + * 请求URL:https://api.mch.weixin.qq.com/v3/partner-transfer/batches/out-batch-no/{out_batch_no} + * 请求方式:GET + * 接口限频:单个服务商 50QPS,如果超过频率限制,会报错FREQUENCY_LIMITED,请降低频率请求。 + * 接口规则:https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay-1.shtml + * + * @author xiaoqiang + * @date 2021-12-06 + */ +@Data +@NoArgsConstructor +public class MerchantBatchRequest implements Serializable { + public static final float serialVersionUID = 1L; + /** + *
+   * 字段名:商家批次单号
+   * 变量名:out_batch_no
+   * 是否必填:是
+   * 类型:string[5, 32]
+   * 描述:
+   *  path商户系统内部的商家批次单号,要求此参数只能由数字、大小写字母组成,在商户系统内部唯一
+   *     示例值:plfk2020042013
+   * 
+ */ + @SerializedName(value = "out_batch_no") + private String outBatchNo; + + /** + *
+   * 字段名:是否查询转账明细单
+   * 变量名:need_query_detail
+   * 是否必填:是
+   * 类型:boolean 默认否
+   * 描述:
+   *  商户可选择是否查询指定状态的转账明细单,当转账批次单状态为“FINISHED”(已完成)时,才会返回满足条件的转账明细单
+   *  示例值:true
+   * 
+ */ + @SerializedName(value = "need_query_detail") + private Boolean needQueryDetail; + + /** + *
+   * 字段名:请求资源起始位置
+   * 变量名:offset
+   * 是否必填:否
+   * 类型:int
+   * 描述:
+   *  query该次请求资源的起始位置。返回的明细是按照设置的明细条数进行分页展示的,一次查询可能无法返回所有明细,我们使用该参数标识查询开始位置,默认值为0
+   *  示例值:0
+   * 
+ */ + @SerializedName(value = "offset") + private Integer offset; + + /** + *
+   * 字段名:最大资源条数
+   * 变量名:limit
+   * 是否必填:否
+   * 类型:int
+   * 描述:
+   *  query该次请求可返回的最大明细条数,最小20条,最大100条,不传则默认20条。不足20条按实际条数返回
+   * 示例值:20
+   * 
+ */ + @SerializedName(value = "limit") + private Integer limit; + + /** + *
+   * 字段名:明细状态
+   * 变量名:detail_status
+   * 是否必填:否
+   * 类型:string[1, 32]
+   * 描述:
+   *  query查询指定状态的转账明细单,不传没有明细状态信息返回。当need_query_detail为true时,该字段必填
+   *  枚举值:
+   *     ALL:全部。需要同时查询转账成功和转账失败的明细单
+   *     SUCCESS:转账成功。只查询转账成功的明细单
+   *     FAIL:转账失败。只查询转账失败的明细单
+   *  示例值:FAIL
+   * 
+ */ + @SerializedName(value = "detail_status") + private String detailStatus; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/transfer/PartnerTransferRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/transfer/PartnerTransferRequest.java new file mode 100644 index 000000000..ce4738ae1 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/transfer/PartnerTransferRequest.java @@ -0,0 +1,289 @@ +package com.github.binarywang.wxpay.bean.marketing.transfer; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +/** + * 发起批量转账API + *
+ * 文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer_partner/chapter3_1.shtml
+ * 
+ * + * @author xiaoqiang + * @date 2021-12-06 + */ +@Data +@NoArgsConstructor +public class PartnerTransferRequest implements Serializable { + public static final float serialVersionUID = 1L; + + /** + *
+   * 字段名:特约商户号
+   * 变量名:sub_mchid
+   * 是否必填:是
+   * 类型:string(32)
+   * 描述:
+   *  特约商户号
+   *  示例值:1900000109
+   * 
+ */ + @SerializedName(value = "sub_mchid") + private String subMchid; + + /** + *
+   * 字段名:特约商户appid
+   * 变量名:sub_appid
+   * 是否必填:否
+   * 类型:string(32)
+   * 描述:
+   *  示例值:wxf636efh567hg4356
+   * 
+ */ + @SerializedName(value = "sub_appid") + private String subAppid; + + /** + *
+   * 字段名:特约商户授权类型
+   * 变量名:authorization_type
+   * 是否必填:是
+   * 类型:string(32)
+   * 描述:
+   *  特约商户授权类型:
+   *    INFORMATION_AUTHORIZATION_TYPE:特约商户信息授权类型
+   *    FUND_AUTHORIZATION_TYPE:特约商户资金授权类型
+   *    INFORMATION_AND_FUND_AUTHORIZATION_TYPE:特约商户信息和资金授权类型
+   * 示例值:INFORMATION_AUTHORIZATION_TYPE
+   * 
+ */ + @SerializedName(value = "authorization_type") + private String authorizationType; + /** + *
+   * 字段名:商家批次单号
+   * 变量名:out_batch_no
+   * 是否必填:是
+   * 类型:string(5-32)
+   * 描述:
+   *    商户系统内部的商家批次单号,要求此参数只能由数字、大小写字母组成,在商户系统内部唯一
+   *    示例值:plfk2020042013
+   * 
+ */ + @SerializedName(value = "out_batch_no") + private String outBatchNo; + + /** + *
+   * 字段名:批次名称
+   * 变量名:batch_name
+   * 是否必填:是
+   * 类型:string(32)
+   * 描述:
+   *  body该笔批量转账的名称
+   *  示例值:2019年1月深圳分部报销单
+   * 
+ */ + @SerializedName(value = "batch_name") + private String batchName; + + /** + *
+   * 字段名:批次备注
+   * 变量名:out_trade_no
+   * 是否必填:是
+   * 类型:string(32)
+   * 描述:
+   *  body转账说明,UTF8编码,最多允许32个字符
+   *   示例值:2019年1月深圳分部报销单
+   * 
+ */ + @SerializedName(value = "batch_remark") + private String batchRemark; + + /** + *
+   * 字段名:转账总金额
+   * 变量名:time_expire
+   * 是否必填:是
+   * 类型:int
+   * 描述:
+   *  body转账金额单位为“分”。转账总金额必须与批次内所有明细转账金额之和保持一致,否则无法发起转账操作
+   *  示例值:4000000
+   * 
+ */ + @SerializedName(value = "total_amount") + private Integer totalAmount; + + /** + *
+   * 字段名:转账总笔数
+   * 变量名:total_num
+   * 是否必填:是
+   * 类型:int
+   * 描述:
+   *  body一个转账批次单最多发起三千笔转账。转账总笔数必须与批次内所有明细之和保持一致,否则无法发起转账操作
+   *  示例值:200
+   * 
+ */ + @SerializedName(value = "total_num") + private Integer totalNum; + + /** + *
+   * 字段名:转账明细列表
+   * 变量名:transfer_detail_list
+   * 是否必填:是
+   * 类型:array
+   * 描述:
+   *  body发起批量转账的明细列表,最多三千笔
+   * 
+ */ + @SerializedName(value = "transfer_detail_list") + private List transferDetailList; + + @Data + @NoArgsConstructor + public static class TransferDetail implements Serializable { + private static final long serialVersionUID = 109053454401492L; + + /** + *
+     * 字段名:商家明细单号
+     * 变量名:out_detail_no
+     * 是否必填:是
+     * 类型:string[5, 32]
+     * 描述:
+     *  商户系统内部区分转账批次单下不同转账明细单的唯一标识,要求此参数只能由数字、大小写字母组成
+     *  示例值:x23zy545Bd5436
+     * 
+ */ + @SerializedName(value = "out_detail_no") + private String outDetailNo; + + + /** + *
+     * 字段名:转账金额
+     * 变量名:transfer_amount
+     * 是否必填:是
+     * 类型:int
+     * 描述:
+     *  转账金额单位为“分”
+     *  示例值:200000
+     * 
+ */ + @SerializedName(value = "transfer_amount") + private Integer transferAmount; + + /** + *
+     * 字段名:转账备注
+     * 变量名:transfer_amount
+     * 是否必填:是
+     * 类型:string[1, 32]
+     * 描述:
+     *  单条转账备注(微信用户会收到该备注),UTF8编码,最多允许32个字符
+     *  示例值:2020年4月报销
+     * 
+ */ + @SerializedName(value = "transfer_remark") + private String transferRemark; + + /** + *
+     * 字段名:收款用户openid
+     * 变量名:openid
+     * 是否必填:是
+     * 类型:string[1, 64]
+     * 描述:
+     *  收款用户openid。如果转账特约商户授权类型是INFORMATION_AUTHORIZATION_TYPE,对应的是特约商户公众号下的openid。
+     *  示例值:o-MYE42l80oelYMDE34nYD456Xoy
+     * 
+ */ + @SerializedName(value = "openid") + private String openid; + + /** + *
+     * 字段名:收款用户姓名
+     * 变量名:user_name
+     * 是否必填:是
+     * 类型:string[1, 1024]
+     * 描述:
+     *  1、收款用户姓名。采用标准RSA算法,公钥由微信侧提供
+     *  2、该字段需进行加密处理,加密方法详见敏感信息加密说明。(提醒:必须在HTTP头中上送Wechatpay-Serial)
+     *  示例值:757b340b45ebef5467rter35gf464344v3542sdf4t6re4tb4f54ty45t4yyry45
+     * 
+ */ + @SerializedName(value = "user_name") + private String userName; + /** + *
+     * 字段名:收款用户身份证
+     * 变量名:user_id_card
+     * 是否必填:否
+     * 类型:string[1, 1024]
+     * 描述:
+     *  1、收款方身份证号,可不用填(采用标准RSA算法,公钥由微信侧提供)
+     *  2、该字段需进行加密处理,加密方法详见敏感信息加密说明。(提醒:必须在HTTP头中上送Wechatpay-Serial)
+     *  示例值:8609cb22e1774a50a930e414cc71eca06121bcd266335cda230d24a7886a8d9f
+     * 
+ */ + @SerializedName(value = "user_id_card") + private String userIdCard; + } + + /** + *
+   * 字段名:服务商的appid
+   * 变量名:sp_appid
+   * 是否必填:否
+   * 类型:string[1, 32]
+   * 描述:
+   *  特约商户授权类型为FUND_AUTHORIZATION_TYPE时需要填写
+   *  示例值:wxf636efh567hg4388
+   * 
+ */ + @SerializedName(value = "sp_appid") + private String spAppid; + + + /** + *
+   * 字段名:批量转账用途
+   * 变量名:transfer_purpose
+   * 是否必填:否
+   * 类型:string[1, 32]
+   * 描述:
+   *  body批量转账用途,枚举值:
+   *        GOODSPAYMENT:货款、COMMISSION:佣金、REFUND:退款、REIMBURSEMENT:报销
+   *        FREIGHT:运费、OTHERS:其他
+   *  示例值:COMMISSION
+   * 
+ */ + @SerializedName(value = "transfer_purpose") + private String transferPurpose; + + /** + *
+   * 字段名:转账场景【微工卡】
+   * 变量名:transfer_scene
+   * 是否必填:否
+   * 类型:string[1, 32]
+   * 描述:
+   *  body商户的转账场景
+   *     ORDINARY_TRANSFER:普通转账,可转入用户的零钱账户
+   *     PAYROLL_CARD_TRANSFER:微工卡转账,可转入用户在微工卡选择的收款账户(零钱/银行卡)
+   *  示例值:ORDINARY_TRANSFER
+   * 
+ */ + @SerializedName(value = "transfer_scene") + private String transferScene; + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/transfer/PartnerTransferResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/transfer/PartnerTransferResult.java new file mode 100644 index 000000000..9ecc6a7a5 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/transfer/PartnerTransferResult.java @@ -0,0 +1,67 @@ +package com.github.binarywang.wxpay.bean.marketing.transfer; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 发起批量转账API + *
+ * 文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer_partner/chapter3_1.shtml
+ * 
+ * + * @author xiaoqiang + * @date 2021-12-06 + */ +@Data +@NoArgsConstructor +public class PartnerTransferResult implements Serializable { + public static final float serialVersionUID = 1L; + /** + *
+   * 字段名:商家批次单号
+   * 变量名:out_batch_no
+   * 是否必填:是
+   * 类型:string[5, 32]
+   * 描述:
+   *  商户系统内部的商家批次单号,在商户系统内部唯一
+   *  示例值:plfk2020042013
+   * 
+ */ + @SerializedName(value = "out_batch_no") + private String outBatchNo; + + /** + *
+   * 字段名:微信支付批次单号
+   * 变量名:batch_id
+   * 是否必填:是
+   * 类型:string[32, 64]
+   * 描述:
+   *  微信支付批次单号,微信商家转账系统返回的唯一标识
+   *  示例值:1030000071100999991182020050700019480001
+   * 
+ */ + @SerializedName(value = "batch_id") + private String batchId; + + /** + *
+   * 字段名:批次创建时间
+   * 变量名:create_time
+   * 是否必填:是
+   * 类型:string[1, 32]
+   * 描述:
+   *  批次受理成功时返回,遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss.sss+TIMEZONE,YYYY-MM-DD表示年月日,
+   *  T出现在字符串中,表示time元素的开头,HH:mm:ss.sss表示时分秒毫秒,TIMEZONE表示时区(+08:00表示东八区时间,
+   *  领先UTC 8小时,即北京时间)。例如:2015-05-20T13:29:35.120+08:00表示北京时间2015年05月20日13点29分35秒
+   *
+   *  示例值:2015-05-20T13:29:35.120+08:00
+   * 
+ */ + @SerializedName(value = "create_time") + private String createTime; + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/transfer/ReceiptBillRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/transfer/ReceiptBillRequest.java new file mode 100644 index 000000000..967ba4f15 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/transfer/ReceiptBillRequest.java @@ -0,0 +1,35 @@ +package com.github.binarywang.wxpay.bean.marketing.transfer; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 转账电子回单申请受理API + *
+ * 文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter4_1.shtml
+ * 
+ * + * @author xiaoqiang + * @date 2021-12-06 + */ +@Data +@NoArgsConstructor +public class ReceiptBillRequest implements Serializable { + public static final float serialVersionUID = 1L; + /** + *
+   * 字段名:商家批次单号
+   * 变量名:out_batch_no
+   * 是否必填:是
+   * 类型:string[5, 32]
+   * 描述:
+   *  body商户系统内部的商家批次单号,在商户系统内部唯一。需要电子回单的批次单号
+   *  示例值:plfk2020042013
+   * 
+ */ + @SerializedName(value = "out_batch_no") + private String outBatchNo; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/ComplaintNotifyResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/ComplaintNotifyResult.java new file mode 100644 index 000000000..a5d18df6d --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/ComplaintNotifyResult.java @@ -0,0 +1,64 @@ +package com.github.binarywang.wxpay.bean.notify; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 投诉通知. + * 文档见:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter10_2_16.shtml + * + * @author jmdhappy + */ +@Data +@NoArgsConstructor +public class ComplaintNotifyResult implements Serializable { + private static final long serialVersionUID = -1L; + /** + * 源数据 + */ + private OriginNotifyResponse rawData; + /** + * 解密后的数据 + */ + private DecryptNotifyResult result; + + @Data + @NoArgsConstructor + public static class DecryptNotifyResult implements Serializable { + private static final long serialVersionUID = -1L; + + /** + *
+     * 字段名:投诉单号
+     * 是否必填:是
+     * 描述:
+     *  投诉单对应的投诉单号
+     * 
+ */ + @SerializedName(value = "complaint_id") + private String complaintId; + + /** + *
+     * 字段名:动作类型
+     * 是否必填:是
+     * 描述:
+     * 触发本次投诉通知回调的具体动作类型,枚举如下:
+     * CREATE_COMPLAINT:用户提交投诉
+     * CONTINUE_COMPLAINT:用户继续投诉
+     * USER_RESPONSE:用户新留言
+     * RESPONSE_BY_PLATFORM:平台新留言
+     * SELLER_REFUND:收款方全额退款
+     * MERCHANT_RESPONSE:商户新回复
+     * MERCHANT_CONFIRM_COMPLETE:商户反馈处理完成
+     * 
+ */ + @SerializedName(value = "action_type") + private String actionType; + + } + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharingV3/ProfitSharingResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharingV3/ProfitSharingResult.java index 6e0626f65..892d31773 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharingV3/ProfitSharingResult.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharingV3/ProfitSharingResult.java @@ -168,5 +168,16 @@ public static class Receiver implements Serializable { */ @SerializedName("finish_time") private String finishTime; + + /** + *
+     * 字段名:微信分账明细单号
+     * 是否必填:是
+     * 每笔分账业务执行的明细单号,可与资金账单对账使用,
+     * 例如:36011111111111111111111
+     * 
+ */ + @SerializedName("detail_id") + private String detailId; } } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundV3Request.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundV3Request.java index da90306de..31a41d922 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundV3Request.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundV3Request.java @@ -143,7 +143,7 @@ public static class Amount implements Serializable { *
      * 字段名:币类型
      * 变量名:currency
-     * 是否必填:否
+     * 是否必填:是
      * 类型:string[1, 16]
      * 描述:
      *  符合ISO 4217标准的三位字母代码,目前只支持人民币:CNY。
@@ -237,4 +237,7 @@ public static class GoodsDetail implements Serializable {
     @SerializedName(value = "refund_quantity")
     private Integer refundQuantity;
   }
+
+  @SerializedName(value = "sub_mchid")
+  private String subMchid;
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayUnifiedOrderV3Request.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayUnifiedOrderV3Request.java
index c0bf41782..98b46e115 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayUnifiedOrderV3Request.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayUnifiedOrderV3Request.java
@@ -80,7 +80,7 @@ public class WxPayUnifiedOrderV3Request implements Serializable {
   /**
    * 
    * 字段名:交易结束时间
-   * 变量名:out_trade_no
+   * 变量名:time_expire
    * 是否必填:是
    * 类型:string[1,64]
    * 描述:
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/BaseWxPayResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/BaseWxPayResult.java
index 6f66dfdd8..a48617c4c 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/BaseWxPayResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/BaseWxPayResult.java
@@ -230,6 +230,15 @@ protected static Integer readXmlInteger(Document d, String tagName) {
     return Integer.parseInt(content);
   }
 
+  protected static Long readXmlLong(Document d, String tagName) {
+    String content = readXmlString(d, tagName);
+    if (content == null || content.trim().length() == 0) {
+      return null;
+    }
+
+    return Long.parseLong(content);
+  }
+
   /**
    * Gets logger.
    *
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayApplyBillV3Result.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayApplyBillV3Result.java
index f5fbf764c..11915538f 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayApplyBillV3Result.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayApplyBillV3Result.java
@@ -41,8 +41,8 @@ public class WxPayApplyBillV3Result implements Serializable {
    *  示例值:79bb0f45fc4c42234a918000b2668d689e2bde04
    * 
*/ - @SerializedName(value = "out_refund_no") - private String outRefundNo; + @SerializedName(value = "hash_value") + private String hashValue; /** *
    * 字段名:账单下载地址
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayEntrustResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayEntrustResult.java
index 4dc6f19ae..2cd0e3588 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayEntrustResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayEntrustResult.java
@@ -81,7 +81,7 @@ public class WxPayEntrustResult extends BaseWxPayResult implements Serializable
    * 非必传
    */
   @XStreamAlias("request_serial")
-  private Integer requestSerial;
+  private Long requestSerial;
 
   /**
    * 签约协议号
@@ -120,7 +120,7 @@ protected void loadXml(Document d) {
     tradeType = readXmlString(d, "trade_type");
     codeUrl = readXmlString(d, "code_url");
     planId = readXmlInteger(d, "plan_id");
-    requestSerial = readXmlInteger(d, "request_serial");
+    requestSerial = readXmlLong(d, "request_serial");
     contractCode = readXmlString(d, "contract_code");
     contractDisplayAccount = readXmlString(d, "contract_display_account");
     mwebUrl = readXmlString(d, "mweb_url");
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxSignQueryResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxSignQueryResult.java
index abb72a5b7..d04f47a9d 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxSignQueryResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxSignQueryResult.java
@@ -42,7 +42,7 @@ public class WxSignQueryResult extends BaseWxPayResult implements Serializable {
    * 请求序列号
    */
   @XStreamAlias("request_serial")
-  private Integer requestSerial;
+  private Long requestSerial;
 
   /**
    * 签约协议号
@@ -106,7 +106,7 @@ public class WxSignQueryResult extends BaseWxPayResult implements Serializable {
   protected void loadXml(Document d) {
     contractId = readXmlString(d, "contract_id");
     planId = readXmlString(d, "plan_id");
-    requestSerial = readXmlInteger(d, "request_serial");
+    requestSerial = readXmlLong(d, "request_serial");
     contractCode = readXmlString(d, "contract_code");
     contractDisplayAccount = readXmlString(d, "contract_display_account");
     contractState = readXmlInteger(d, "contract_state");
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxSignStatusNotifyResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxSignStatusNotifyResult.java
index f55b576e3..0c0b48ecd 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxSignStatusNotifyResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxSignStatusNotifyResult.java
@@ -81,7 +81,7 @@ public class WxSignStatusNotifyResult extends BaseWxPayResult {
    * 请求序列号
    */
   @XStreamAlias("request_serial")
-  private Integer requestSerial;
+  private Long requestSerial;
 
   @Override
   public void checkResult(WxPayService wxPayService, String signType, boolean checkSuccess) throws WxPayException {
@@ -117,7 +117,7 @@ protected void loadXml(Document d) {
     contractId = readXmlString(d, "contract_id");
     contractExpiredTime = readXmlString(d, "contract_expired_time");
     contractTerminationMode = readXmlInteger(d, "contract_termination_mode");
-    requestSerial = readXmlInteger(d, "request_serial");
+    requestSerial = readXmlLong(d, "request_serial");
   }
 
   @Override
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java
index a9092e2fd..59733944a 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java
@@ -1,6 +1,7 @@
 package com.github.binarywang.wxpay.config;
 
 import com.github.binarywang.wxpay.exception.WxPayException;
+import com.github.binarywang.wxpay.util.HttpProxyUtils;
 import com.github.binarywang.wxpay.util.ResourcesUtils;
 import com.github.binarywang.wxpay.v3.WxPayV3HttpClientBuilder;
 import com.github.binarywang.wxpay.v3.auth.*;
@@ -9,7 +10,13 @@
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.RegExUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.http.HttpHost;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.impl.client.BasicCredentialsProvider;
 import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
 import org.apache.http.ssl.SSLContexts;
 
 import javax.net.ssl.SSLContext;
@@ -108,6 +115,16 @@ public class WxPayConfig {
    */
   private String privateCertPath;
 
+  /**
+   * apiclient_key.pem证书文件内容的字节数组.
+   */
+  private byte[] privateKeyContent;
+
+  /**
+   * apiclient_cert.pem证书文件内容的字节数组.
+   */
+  private byte[] privateCertContent;
+
   /**
    * apiV3 秘钥值.
    */
@@ -205,15 +222,7 @@ public SSLContext initSSLContext() throws WxPayException {
       throw new WxPayException("请确保商户号mchId已设置");
     }
 
-    InputStream inputStream;
-    if (this.keyContent != null) {
-      inputStream = new ByteArrayInputStream(this.keyContent);
-    } else {
-      if (StringUtils.isBlank(this.getKeyPath())) {
-        throw new WxPayException("请确保证书文件地址keyPath已配置");
-      }
-      inputStream = this.loadConfigInputStream(this.getKeyPath());
-    }
+    InputStream inputStream = this.loadConfigInputStream(this.getKeyPath(), this.keyContent, "p12证书");
 
     try {
       KeyStore keystore = KeyStore.getInstance("PKCS12");
@@ -240,37 +249,34 @@ public CloseableHttpClient initApiV3HttpClient() throws WxPayException {
     val privateCertPath = this.getPrivateCertPath();
     val serialNo = this.getCertSerialNo();
     val apiV3Key = this.getApiV3Key();
-    if (StringUtils.isBlank(privateKeyPath)) {
-      throw new WxPayException("请确保privateKeyPath已设置");
-    }
-    if (StringUtils.isBlank(privateCertPath)) {
-      throw new WxPayException("请确保privateCertPath已设置");
-    }
-//    if (StringUtils.isBlank(certSerialNo)) {
-//      throw new WxPayException("请确保certSerialNo证书序列号已设置");
-//    }
     if (StringUtils.isBlank(apiV3Key)) {
       throw new WxPayException("请确保apiV3Key值已设置");
     }
 
-    InputStream keyInputStream = this.loadConfigInputStream(privateKeyPath);
-    InputStream certInputStream = this.loadConfigInputStream(privateCertPath);
+    InputStream keyInputStream = this.loadConfigInputStream(privateKeyPath, this.privateKeyContent, "privateKeyPath");
+    InputStream certInputStream = this.loadConfigInputStream(privateCertPath, this.privateCertContent, "privateCertPath");
     try {
       PrivateKey merchantPrivateKey = PemUtils.loadPrivateKey(keyInputStream);
       X509Certificate certificate = PemUtils.loadCertificate(certInputStream);
       if(StringUtils.isBlank(serialNo)){
         this.certSerialNo = certificate.getSerialNumber().toString(16).toUpperCase();
       }
+      //构造Http Proxy正向代理
+      WxPayHttpProxy wxPayHttpProxy = getWxPayHttpProxy();
 
       AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
         new WxPayCredentials(mchId, new PrivateKeySigner(certSerialNo, merchantPrivateKey)),
-        apiV3Key.getBytes(StandardCharsets.UTF_8), this.getCertAutoUpdateTime());
+        apiV3Key.getBytes(StandardCharsets.UTF_8), this.getCertAutoUpdateTime(),wxPayHttpProxy);
 
-      CloseableHttpClient httpClient = WxPayV3HttpClientBuilder.create()
+      WxPayV3HttpClientBuilder wxPayV3HttpClientBuilder = WxPayV3HttpClientBuilder.create()
         .withMerchant(mchId, certSerialNo, merchantPrivateKey)
         .withWechatpay(Collections.singletonList(certificate))
-        .withValidator(new WxPayValidator(verifier))
-        .build();
+        .withValidator(new WxPayValidator(verifier));
+      //初始化V3接口正向代理设置
+      HttpProxyUtils.initHttpProxy(wxPayV3HttpClientBuilder,wxPayHttpProxy);
+
+      CloseableHttpClient httpClient = wxPayV3HttpClientBuilder.build();
+
       this.apiV3HttpClient = httpClient;
       this.verifier=verifier;
       this.privateKey = merchantPrivateKey;
@@ -281,6 +287,31 @@ public CloseableHttpClient initApiV3HttpClient() throws WxPayException {
     }
   }
 
+  /**
+   * 初始化一个WxPayHttpProxy对象
+   * @return 返回封装的WxPayHttpProxy对象。如未指定代理主机和端口,则默认返回null
+   */
+  private WxPayHttpProxy getWxPayHttpProxy() {
+    if (StringUtils.isNotBlank(this.getHttpProxyHost()) && this.getHttpProxyPort() > 0) {
+      return new WxPayHttpProxy(getHttpProxyHost(), getHttpProxyPort(), getHttpProxyUsername(), getHttpProxyPassword());
+    }
+    return null;
+  }
+
+  private InputStream loadConfigInputStream(String configPath, byte[] configContent, String fileName) throws WxPayException {
+    InputStream inputStream;
+    if (configContent != null) {
+      inputStream = new ByteArrayInputStream(configContent);
+    } else {
+      if (StringUtils.isBlank(configPath)) {
+        throw new WxPayException("请确保证书文件地址【" + fileName + "】或者内容已配置");
+      }
+      inputStream = this.loadConfigInputStream(configPath);
+    }
+    return inputStream;
+  }
+
+
   /**
    * 从配置路径 加载配置 信息(支持 classpath、本地路径、网络url)
    * @param configPath 配置路径
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayHttpProxy.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayHttpProxy.java
new file mode 100755
index 000000000..2b3b1c937
--- /dev/null
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayHttpProxy.java
@@ -0,0 +1,75 @@
+package com.github.binarywang.wxpay.config;
+
+
+import java.io.Serializable;
+
+/**
+ * 微信支付 HTTP Proxy 正向代理配置
+ *
+ * @author Long Yu
+ * @date 2021-12-28 15:49:03
+ */
+public class WxPayHttpProxy implements Serializable {
+
+  /**
+   * 代理主机
+   */
+  private String httpProxyHost;
+  /**
+   * 代理端口
+   */
+  private Integer httpProxyPort;
+  /**
+   * 代理用户名称
+   */
+  private String httpProxyUsername;
+  /**
+   * 代理密码
+   */
+  private String httpProxyPassword;
+
+  public WxPayHttpProxy() {
+  }
+
+  public WxPayHttpProxy(String httpProxyHost, Integer httpProxyPort, String httpProxyUsername, String httpProxyPassword) {
+    this.httpProxyHost = httpProxyHost;
+    this.httpProxyPort = httpProxyPort;
+    this.httpProxyUsername = httpProxyUsername;
+    this.httpProxyPassword = httpProxyPassword;
+  }
+
+  public String getHttpProxyHost() {
+    return httpProxyHost;
+  }
+
+  public void setHttpProxyHost(String httpProxyHost) {
+    this.httpProxyHost = httpProxyHost;
+  }
+
+  public Integer getHttpProxyPort() {
+    return httpProxyPort;
+  }
+
+  public void setHttpProxyPort(Integer httpProxyPort) {
+    this.httpProxyPort = httpProxyPort;
+  }
+
+  public String getHttpProxyUsername() {
+    return httpProxyUsername;
+  }
+
+  public void setHttpProxyUsername(String httpProxyUsername) {
+    this.httpProxyUsername = httpProxyUsername;
+  }
+
+  public String getHttpProxyPassword() {
+    return httpProxyPassword;
+  }
+
+  public void setHttpProxyPassword(String httpProxyPassword) {
+    this.httpProxyPassword = httpProxyPassword;
+  }
+
+
+
+}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/ComplaintService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/ComplaintService.java
new file mode 100644
index 000000000..bd6a2e346
--- /dev/null
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/ComplaintService.java
@@ -0,0 +1,132 @@
+package com.github.binarywang.wxpay.service;
+
+import com.github.binarywang.wxpay.bean.complaint.*;
+import com.github.binarywang.wxpay.exception.WxPayException;
+
+import javax.crypto.BadPaddingException;
+
+/**
+ * 
+ * 微信支付 消费者投诉2.0 API.
+ * Created by jmdhappy on 2022/3/19.
+ * 
+ * + * @author jmdhappy + */ +public interface ComplaintService { + + /** + *
+   * 查询投诉单列表API
+   * 商户可通过调用此接口,查询指定时间段的所有用户投诉信息,以分页输出查询结果。
+   * 对于服务商、渠道商,可通过调用此接口,查询指定子商户号对应子商户的投诉信息,若不指定则查询所有子商户投诉信息。
+   * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter10_2_11.shtml
+   * 
+ * + * @param request {@link ComplaintRequest} 查询投诉单列表请求数据 + * @return {@link ComplaintResult} 微信返回的投诉单列表 + * @throws WxPayException the wx pay exception + */ + ComplaintResult queryComplaints(ComplaintRequest request) throws WxPayException, BadPaddingException; + + /** + *
+   * 查询投诉单详情API
+   * 商户可通过调用此接口,查询指定投诉单的用户投诉详情,包含投诉内容、投诉关联订单、投诉人联系方式等信息,方便商户处理投诉。
+   * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter10_2_13.shtml
+   * 
+ * + * @param request {@link ComplaintDetailRequest} 投诉单详情请求数据 + * @return {@link ComplaintDetailResult} 微信返回的投诉单详情 + * @throws WxPayException the wx pay exception + */ + ComplaintDetailResult getComplaint(ComplaintDetailRequest request) throws WxPayException, BadPaddingException; + + /** + *
+   * 查询投诉协商历史API
+   * 商户可通过调用此接口,查询指定投诉的用户商户协商历史,以分页输出查询结果,方便商户根据处理历史来制定后续处理方案。
+   * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter10_2_12.shtml
+   * 
+ * + * @param request {@link NegotiationHistoryRequest} 请求数据 + * @return {@link NegotiationHistoryResult} 微信返回结果 + * @throws WxPayException the wx pay exception + */ + NegotiationHistoryResult queryNegotiationHistorys(NegotiationHistoryRequest request) throws WxPayException; + + /** + *
+   * 创建投诉通知回调地址API
+   * 商户通过调用此接口创建投诉通知回调URL,当用户产生新投诉且投诉状态已变更时,微信支付会通过回 调URL通知商户。对于服务商、渠道商,会收到所有子商户的投诉信息推送。
+   * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter10_2_2.shtml
+   * 
+ * + * @param request {@link ComplaintDetailRequest} 请求数据 + * @return {@link ComplaintNotifyUrlResult} 微信返回结果 + * @throws WxPayException the wx pay exception + */ + ComplaintNotifyUrlResult addComplaintNotifyUrl(ComplaintNotifyUrlRequest request) throws WxPayException; + + /** + *
+   * 查询投诉通知回调地址API
+   * 商户通过调用此接口查询投诉通知的回调URL。
+   * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter10_2_3.shtml
+   * 
+ * + * @return {@link ComplaintNotifyUrlResult} 微信返回结果 + * @throws WxPayException the wx pay exception + */ + ComplaintNotifyUrlResult getComplaintNotifyUrl() throws WxPayException; + + /** + *
+   * 更新投诉通知回调地址API
+   * 商户通过调用此接口更新投诉通知的回调URL。
+   * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter10_2_4.shtml
+   * 
+ * + * @param request {@link ComplaintDetailRequest} 请求数据 + * @return {@link ComplaintNotifyUrlResult} 微信返回结果 + * @throws WxPayException the wx pay exception + */ + ComplaintNotifyUrlResult updateComplaintNotifyUrl(ComplaintNotifyUrlRequest request) throws WxPayException; + + /** + *
+   * 删除投诉通知回调地址API
+   * 当商户不再需要推送通知时,可通过调用此接口删除投诉通知的回调URL,取消通知回调。
+   * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter10_2_5.shtml
+   * 
+ * + * @throws WxPayException the wx pay exception + */ + void deleteComplaintNotifyUrl() throws WxPayException; + + /** + *
+   * 提交回复API
+   * 商户可通过调用此接口,提交回复内容。其中上传图片凭证需首先调用商户上传反馈图片接口,得到图片id,再将id填入请求。
+   * 回复可配置文字链,传入跳转链接文案和跳转链接字段,用户点击即可跳转对应页面
+   * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter10_2_14.shtml
+   * 
+ * + * @param request {@link ResponseRequest} 请求数据 + * @throws WxPayException the wx pay exception + */ + void submitResponse(ResponseRequest request) throws WxPayException; + + /** + *
+   * 反馈处理完成API
+   * 商户可通过调用此接口,反馈投诉单已处理完成。
+   * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter10_2_15.shtml
+   * 
+ * + * @param request {@link CompleteRequest} 请求数据 + * @throws WxPayException the wx pay exception + */ + void complete(CompleteRequest request) throws WxPayException; + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/PartnerTransferService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/PartnerTransferService.java new file mode 100644 index 000000000..0bfc96cf3 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/PartnerTransferService.java @@ -0,0 +1,201 @@ +package com.github.binarywang.wxpay.service; + +import com.github.binarywang.wxpay.bean.ecommerce.FundBalanceResult; +import com.github.binarywang.wxpay.bean.ecommerce.enums.SpAccountTypeEnum; +import com.github.binarywang.wxpay.bean.marketing.transfer.*; +import com.github.binarywang.wxpay.exception.WxPayException; + +import javax.crypto.BadPaddingException; +import java.io.InputStream; + +/** + * 微信批量转账到零钱【V3接口】服务商API + * + * @author xiaoqiang + * @date 2021-12-06 + */ +public interface PartnerTransferService { + + /** + * 发起批量转账API + * 适用对象:服务商 + * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter3_1.shtml + * 请求URL:https://api.mch.weixin.qq.com/v3/partner-transfer/batches + * 请求方式:POST + * 接口限频:单个服务商 50QPS,如果超过频率限制,会报错FREQUENCY_LIMITED,请降低频率请求。 + * + * @param request 请求对象 + * @return 返回数据 fund balance result + * @throws WxPayException the wx pay exception + */ + PartnerTransferResult batchTransfer(PartnerTransferRequest request) throws WxPayException; + + /** + * 微信支付批次单号查询批次单API + * 接口说明 + * 适用对象:服务商 + * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter3_2.shtml + * 请求URL:https://api.mch.weixin.qq.com/v3/partner-transfer/batches/batch-id/{batch_id} + * 请求方式:GET + * 接口限频:单个服务商 50QPS,如果超过频率限制,会报错FREQUENCY_LIMITED,请降低频率请求。 + * + * @param request 请求对象 + * @return 返回数据 fund balance result + * @throws WxPayException the wx pay exception + */ + BatchNumberResult queryBatchByBatchId(BatchNumberRequest request) throws WxPayException; + + /** + * 微信支付明细单号查询明细单API + * 接口说明 + * 适用对象:服务商 + * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter3_3.shtml + * 请求URL:https://api.mch.weixin.qq.com/v3/partner-transfer/batches/batch-id/{batch_id}/details/detail-id/{detail_id} + * 请求方式:GET + * 接口限频:单个服务商 50QPS,如果超过频率限制,会报错FREQUENCY_LIMITED,请降低频率请求。 + * + * @param batchId 微信批次单号 + * @param detailId 微信明细单号 + * @return 返回数据 fund balance result + * @throws WxPayException the wx pay exception + * @throws BadPaddingException the wx decrypt exception + */ + BatchDetailsResult queryBatchDetailByWeChat(String batchId, String detailId) throws WxPayException, BadPaddingException; + + /** + * 商家批次单号查询批次单API + * 接口说明 + * 适用对象:服务商 + * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter3_4.shtml + * 请求URL:https://api.mch.weixin.qq.com/v3/partner-transfer/batches/out-batch-no/{out_batch_no} + * 请求方式:GET + * 接口限频:单个服务商 50QPS,如果超过频率限制,会报错FREQUENCY_LIMITED,请降低频率请求。 + * + * @param request 请求对象 + * @return 返回数据 fund balance result + * @throws WxPayException the wx pay exception + */ + BatchNumberResult queryBatchByOutBatchNo(MerchantBatchRequest request) throws WxPayException; + + /** + * 商家明细单号查询明细单API + * 接口说明 + * 适用对象:服务商 + * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter3_5.shtml + * 请求URL:https://api.mch.weixin.qq.com/v3/partner-transfer/batches/out-batch-no/{out_batch_no}/details/out-detail-no/{out_detail_no} + * 请求方式:GET + * 接口限频:单个服务商 50QPS,如果超过频率限制,会报错FREQUENCY_LIMITED,请降低频率请求。 + * + * @param outBatchNo 商家明细单号 + * @param outDetailNo 商家批次单号 + * @return 返回数据 fund balance result + * @throws WxPayException the wx pay exception + * @throws BadPaddingException the wx decrypt exception + */ + BatchDetailsResult queryBatchDetailByMch(String outBatchNo, String outDetailNo) throws WxPayException, BadPaddingException; + + + /** + * 转账电子回单申请受理API + * 接口说明 + * 适用对象:直连商户 服务商 + * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter4_1.shtml + * 请求URL:https://api.mch.weixin.qq.com/v3/transfer/bill-receipt + * 请求方式:POST + * + * @param request 商家批次单号 + * @return 返回数据 fund balance result + * @throws WxPayException the wx pay exception + */ + BillReceiptResult receiptBill(ReceiptBillRequest request) throws WxPayException; + + + /** + * 查询转账电子回单API + * 接口说明 + * 适用对象:直连商户 服务商 + * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter4_2.shtml + * 请求URL:https://api.mch.weixin.qq.com/v3/transfer/bill-receipt/{out_batch_no} + * 请求方式:GET + * + * @param outBatchNo 商家批次单号 + * @return 返回数据 fund balance result + * @throws WxPayException the wx pay exception + */ + BillReceiptResult queryBillReceipt(String outBatchNo) throws WxPayException; + + /** + * 转账明细电子回单受理API + * 接口说明 + * 适用对象:直连商户 服务商 + * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter4_4.shtml + * 请求URL:https://api.mch.weixin.qq.com/v3/transfer-detail/electronic-receipts + * 请求方式:POST + * 前置条件:只支持受理最近90天内的转账明细单 + * + * @param request 请求对象 + * @return 返回数据 fund balance result + * @throws WxPayException the wx pay exception + */ + ElectronicReceiptsResult transferElectronic(ElectronicReceiptsRequest request) throws WxPayException; + + /** + * 查询转账明细电子回单受理结果API + * 接口说明 + * 适用对象:直连商户 服务商 + * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter4_5.shtml + * 请求URL:https://api.mch.weixin.qq.com/v3/transfer-detail/electronic-receipts + * 请求方式:GET + * 前置条件:只支持查询最近90天内的转账明细单 + * + * @param request 请求对象 + * @return 返回数据 fund balance result + * @throws WxPayException the wx pay exception + */ + ElectronicReceiptsResult queryTransferElectronicResult(ElectronicReceiptsRequest request) throws WxPayException; + + /** + * 下载电子回单API + * 接口说明 + * 适用对象:直连商户 服务商 + * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter4_3.shtml + * 请求URL:通过申请账单接口获取到“download_url”,URL有效期10min + * 请求方式:GET + * 前置条件:调用申请账单接口并获取到“download_url” + * + * @param url 微信返回的电子回单地址。 + * @return 返回数据 fund balance result + * @throws WxPayException the wx pay exception + */ + InputStream transferDownload(String url) throws WxPayException; + + /** + *
+   * 查询账户实时余额API
+   * 接口说明
+   * 适用对象:直连商户 服务商
+   * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter5_1.shtml
+   * 请求URL:https://api.mch.weixin.qq.com/v3/merchant/fund/balance/{account_type}
+   * 请求方式:GET
+   * 
+ * + * @param accountType 服务商账户类型 {@link SpAccountTypeEnum} + * @return 返回数据 fund balance result + * @throws WxPayException the wx pay exception + */ + FundBalanceResult fundBalance(SpAccountTypeEnum accountType) throws WxPayException; + + /** + *
+   * 服务商账户日终余额
+   * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter5_2.shtml
+   * 文档地址: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pages/amount.shtml
+   * 
+ * + * @param accountType 服务商账户类型 + * @param date 查询日期 2020-09-11 + * @return 返回数据 fund balance result + * @throws WxPayException the wx pay exception + */ + FundBalanceResult spDayEndBalance(SpAccountTypeEnum accountType, String date); +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/PayrollService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/PayrollService.java new file mode 100644 index 000000000..be9c64f2e --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/PayrollService.java @@ -0,0 +1,103 @@ +package com.github.binarywang.wxpay.service; + +import com.github.binarywang.wxpay.bean.marketing.payroll.*; +import com.github.binarywang.wxpay.exception.WxPayException; + +/** + * 微工卡-对接微信api + * + * @author xiaoqiang + * @date 2021/12/7 14:26 + */ +public interface PayrollService { + /** + * 生成授权token + * 适用对象:服务商 + * 请求URL:https://api.mch.weixin.qq.com/v3/payroll-card/tokens + * 请求方式:POST + * + * @param request 请求参数 + * @return 返回数据 + * @throws WxPayException the wx pay exception + */ + TokensResult payrollCardTokens(TokensRequest request) throws WxPayException; + + /** + * 查询微工卡授权关系API + * 适用对象:服务商 + * 请求URL:https://api.mch.weixin.qq.com/v3/payroll-card/relations/{openid} + * 请求方式:GET + * + * @param request 请求参数 + * @return 返回数据 + * @throws WxPayException the wx pay exception + */ + RelationsResult payrollCardRelations(RelationsRequest request) throws WxPayException; + + /** + * 微工卡核身预下单API + * 适用对象:服务商 + * 请求URL:https://api.mch.weixin.qq.com/v3/payroll-card/authentications/pre-order + * 请求方式:POST + * + * @param request 请求参数 + * @return 返回数据 + * @throws WxPayException the wx pay exception + */ + PreOrderResult payrollCardPreOrder(PreOrderRequest request) throws WxPayException; + + /** + * 获取核身结果API + * 适用对象:服务商 + * 请求URL:https://api.mch.weixin.qq.com/v3/payroll-card/authentications/{authenticate_number} + * 请求方式:GET + * + * @param subMchid 子商户号 + * @param authenticateNumber 商家核身单号 + * @return 返回数据 + * @throws WxPayException the wx pay exception + */ + AuthenticationsResult payrollCardAuthenticationsNumber(String subMchid, String authenticateNumber) throws WxPayException; + + /** + * 查询核身记录API + * 适用对象:服务商 + * 请求URL:https://api.mch.weixin.qq.com/v3/payroll-card/authentications + * 请求方式:GET + * + * @param request 请求参数 + * @return 返回数据 + * @throws WxPayException the wx pay exception + */ + AuthRecordResult payrollCardAuthentications(AuthRecordRequest request) throws WxPayException; + + /** + * 微工卡核身预下单(流程中完成授权) + * 适用对象:服务商 + * 请求URL:https://api.mch.weixin.qq.com/v3/payroll-card/authentications/pre-order-with-auth + * 请求方式:POST + * + * @param request 请求参数 + * @return 返回数据 + * @throws WxPayException the wx pay exception + */ + PreOrderWithAuthResult payrollCardPreOrderWithAuth(PreOrderWithAuthRequest request) throws WxPayException; + + /** + * 按日下载提现异常文件API + * 适用对象:服务商 + * 请求URL:https://api.mch.weixin.qq.com/v3/merchant/fund/withdraw/bill-type/{bill_type} + * 请求方式:GET + * + * @param billType 账单类型 + * NO_SUCC:提现异常账单,包括提现失败和提现退票账单。 + * 示例值:NO_SUCC + * @param billDate 账单日期 表示所在日期的提现账单,格式为YYYY-MM-DD。 + * 例如:2008-01-01日发起的提现,2008-01-03日银行返回提现失败,则该提现数据将出现在bill_date为2008-01-03日的账单中。 + * 示例值:2019-08-17 + * @return 返回数据 + * @throws WxPayException the wx pay exception + */ + PreOrderWithAuthResult merchantFundWithdrawBillType(String billType, String billDate) throws WxPayException; + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java index 73dad0c9d..3f98c3d2c 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java @@ -173,12 +173,45 @@ public interface WxPayService { */ InputStream downloadV3(String url) throws WxPayException; + /** + * 发送put V3请求,得到响应字符串. + * + * @param url 请求地址 + * @param url 请求数据 + * @return 返回请求结果字符串 string + * @throws WxPayException the wx pay exception + */ + String putV3(String url, String requestStr) throws WxPayException; + + /** + * 发送delete V3请求,得到响应字符串. + * + * @param url 请求地址 + * @return 返回请求结果字符串 string + * @throws WxPayException the wx pay exception + */ + String deleteV3(String url) throws WxPayException; + /** * 获取微信签约代扣服务类 * @return entrust service */ WxEntrustPapService getWxEntrustPapService(); + /** + * 获取批量转账到零钱服务类. + * + * @return the Batch transfer to change service + */ + PartnerTransferService getPartnerTransferService(); + + /** + * 微工卡 + * + * @return the micro card + */ + PayrollService getPayrollService(); + /** * 获取企业付款服务类. * @@ -1282,4 +1315,22 @@ WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, Stri * @throws WxPayException . */ WxPayQueryExchangeRateResult queryExchangeRate(String feeType, String date) throws WxPayException; + + /** + * 解析投诉通知 + * 详见https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter10_2_16.shtml + * + * @param notifyData 通知数据 + * @param header 通知头部数据,不传则表示不校验头 + * @return the wx pay refund notify result + * @throws WxPayException the wx pay exception + */ + ComplaintNotifyResult parseComplaintNotifyResult(String notifyData, SignatureHeader header) throws WxPayException; + + /** + * 获取消费者投诉服务类. + * + * @return the complaints service + */ + ComplaintService getComplaintsService(); } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java index e5ef0be8b..128a7362d 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java @@ -20,13 +20,13 @@ import com.github.binarywang.wxpay.service.*; import com.github.binarywang.wxpay.util.SignUtils; import com.github.binarywang.wxpay.util.XmlConfig; +import com.github.binarywang.wxpay.util.ZipUtils; import com.github.binarywang.wxpay.v3.util.AesUtils; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import jodd.io.ZipUtil; import me.chanjar.weixin.common.error.WxRuntimeException; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; @@ -75,7 +75,9 @@ public abstract class BaseWxPayServiceImpl implements WxPayService { private final MarketingFavorService marketingFavorService = new MarketingFavorServiceImpl(this); private final MarketingBusiFavorService marketingBusiFavorService = new MarketingBusiFavorServiceImpl(this); private final WxEntrustPapService wxEntrustPapService = new WxEntrustPapServiceImpl(this); - + private final PartnerTransferService partnerTransferService = new PartnerTransferServiceImpl(this); + private final PayrollService payrollService = new PayrollServiceImpl(this); + private final ComplaintService complaintsService = new ComplaintServiceImpl(this); protected Map configMap; @@ -144,6 +146,16 @@ public WxEntrustPapService getWxEntrustPapService() { return wxEntrustPapService; } + @Override + public PartnerTransferService getPartnerTransferService() { + return partnerTransferService; + } + + @Override + public PayrollService getPayrollService() { + return payrollService; + } + @Override public WxPayConfig getConfig() { if (this.configMap.size() == 1) { @@ -350,8 +362,9 @@ public WxPayOrderNotifyResult parseOrderNotifyResult(String xmlData, String sign /** * 校验通知签名 + * * @param header 通知头信息 - * @param data 通知数据 + * @param data 通知数据 * @return true:校验通过 false:校验不通过 */ private boolean verifyNotifySign(SignatureHeader header, String data) { @@ -692,6 +705,10 @@ public WxPayUnifiedOrderV3Result unifiedOrderV3(TradeTypeEnum tradeType, WxPayUn if (StringUtils.isBlank(request.getMchid())) { request.setMchid(this.getConfig().getMchId()); } + if (StringUtils.isBlank(request.getNotifyUrl())) { + request.setNotifyUrl(this.getConfig().getNotifyUrl()); + } + String url = this.getPayBaseUrl() + tradeType.getPartnerUrl(); String response = this.postV3(url, GSON.toJson(request)); return GSON.fromJson(response, WxPayUnifiedOrderV3Result.class); @@ -880,7 +897,7 @@ private String handleGzipBill(String url, String requestStr) throws WxPayExcepti Path path = Paths.get(tempDirectory.toString(), System.currentTimeMillis() + ".gzip"); Files.write(path, responseBytes); try { - List allLines = Files.readAllLines(ZipUtil.ungzip(path.toFile()).toPath(), StandardCharsets.UTF_8); + List allLines = Files.readAllLines(ZipUtils.unGzip(path.toFile()).toPath(), StandardCharsets.UTF_8); return Joiner.on("\n").join(allLines); } catch (ZipException e) { if (e.getMessage().contains("Not in GZIP format")) { @@ -933,7 +950,7 @@ private String handleGzipFundFlow(String url, String requestStr) throws WxPayExc Files.write(path, responseBytes); try { - List allLines = Files.readAllLines(ZipUtil.ungzip(path.toFile()).toPath(), StandardCharsets.UTF_8); + List allLines = Files.readAllLines(ZipUtils.unGzip(path.toFile()).toPath(), StandardCharsets.UTF_8); return Joiner.on("\n").join(allLines); } catch (ZipException e) { if (e.getMessage().contains("Not in GZIP format")) { @@ -1215,4 +1232,33 @@ public WxPayQueryExchangeRateResult queryExchangeRate(String feeType, String dat result.checkResult(this, request.getSignType(), true); return result; } + + @Override + public ComplaintNotifyResult parseComplaintNotifyResult(String notifyData, SignatureHeader header) throws WxPayException { + if (Objects.nonNull(header) && !this.verifyNotifySign(header, notifyData)) { + throw new WxPayException("非法请求,头部信息验证失败"); + } + OriginNotifyResponse response = GSON.fromJson(notifyData, OriginNotifyResponse.class); + OriginNotifyResponse.Resource resource = response.getResource(); + String cipherText = resource.getCiphertext(); + String associatedData = resource.getAssociatedData(); + String nonce = resource.getNonce(); + String apiV3Key = this.getConfig().getApiV3Key(); + try { + String result = AesUtils.decryptToString(associatedData, nonce, cipherText, apiV3Key); + ComplaintNotifyResult.DecryptNotifyResult decryptNotifyResult = GSON.fromJson(result, ComplaintNotifyResult.DecryptNotifyResult.class); + ComplaintNotifyResult notifyResult = new ComplaintNotifyResult(); + notifyResult.setRawData(response); + notifyResult.setResult(decryptNotifyResult); + return notifyResult; + } catch (GeneralSecurityException | IOException e) { + throw new WxPayException("解析报文异常!", e); + } + } + + @Override + public ComplaintService getComplaintsService() { + return complaintsService; + } + } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/ComplaintServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/ComplaintServiceImpl.java new file mode 100644 index 000000000..d269a8f90 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/ComplaintServiceImpl.java @@ -0,0 +1,110 @@ +package com.github.binarywang.wxpay.service.impl; + +import com.github.binarywang.wxpay.bean.complaint.*; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.ComplaintService; +import com.github.binarywang.wxpay.service.WxPayService; +import com.github.binarywang.wxpay.v3.util.RsaCryptoUtil; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import lombok.RequiredArgsConstructor; + +import javax.crypto.BadPaddingException; +import java.util.List; + +/** + *
+ * 消费者投诉2.0 实现.
+ * Created by jmdhappy on 2022/3/19.
+ * 
+ * + * @author jmdhappy + */ +@RequiredArgsConstructor +public class ComplaintServiceImpl implements ComplaintService { + private static final Gson GSON = new GsonBuilder().create(); + private final WxPayService payService; + + @Override + public ComplaintResult queryComplaints(ComplaintRequest request) throws WxPayException, BadPaddingException { + String url = String.format("%s/v3/merchant-service/complaints-v2?limit=%d&offset=%d&begin_date=%s&end_date=%s&complainted_mchid=%s", + this.payService.getPayBaseUrl(), request.getLimit(), request.getOffset(), request.getBeginDate(), request.getEndDate(), request.getComplaintedMchid()); + String response = this.payService.getV3(url); + ComplaintResult complaintResult = GSON.fromJson(response, ComplaintResult.class); + List data = complaintResult.getData(); + for (ComplaintDetailResult complaintDetailResult : data) { + // 对手机号进行解密操作 + if(complaintDetailResult.getPayerPhone() != null) { + String payerPhone = RsaCryptoUtil.decryptOAEP(complaintDetailResult.getPayerPhone(), this.payService.getConfig().getPrivateKey()); + complaintDetailResult.setPayerPhone(payerPhone); + } + } + return complaintResult; + } + + @Override + public ComplaintDetailResult getComplaint(ComplaintDetailRequest request) throws WxPayException, BadPaddingException { + String url = String.format("%s/v3/merchant-service/complaints-v2/%s", + this.payService.getPayBaseUrl(), request.getComplaintId()); + String response = this.payService.getV3(url); + ComplaintDetailResult result = GSON.fromJson(response, ComplaintDetailResult.class); + // 对手机号进行解密操作 + if(result.getPayerPhone() != null) { + String payerPhone = RsaCryptoUtil.decryptOAEP(result.getPayerPhone(), this.payService.getConfig().getPrivateKey()); + result.setPayerPhone(payerPhone); + } + return result; + } + + @Override + public NegotiationHistoryResult queryNegotiationHistorys(NegotiationHistoryRequest request) throws WxPayException { + String url = String.format("%s/v3/merchant-service/complaints-v2/%s/negotiation-historys?limit=%d&offset=%d", + this.payService.getPayBaseUrl(), request.getComplaintId(), request.getLimit(), request.getOffset()); + String response = this.payService.getV3(url); + return GSON.fromJson(response, NegotiationHistoryResult.class); + } + + @Override + public ComplaintNotifyUrlResult addComplaintNotifyUrl(ComplaintNotifyUrlRequest request) throws WxPayException { + String url = String.format("%s/v3/merchant-service/complaint-notifications", this.payService.getPayBaseUrl()); + String response = this.payService.postV3(url, GSON.toJson(request)); + return GSON.fromJson(response, ComplaintNotifyUrlResult.class); + } + + @Override + public ComplaintNotifyUrlResult getComplaintNotifyUrl() throws WxPayException { + String url = String.format("%s/v3/merchant-service/complaint-notifications", this.payService.getPayBaseUrl()); + String response = this.payService.getV3(url); + return GSON.fromJson(response, ComplaintNotifyUrlResult.class); + } + + @Override + public ComplaintNotifyUrlResult updateComplaintNotifyUrl(ComplaintNotifyUrlRequest request) throws WxPayException { + String url = String.format("%s/v3/merchant-service/complaint-notifications", this.payService.getPayBaseUrl()); + String response = this.payService.putV3(url, GSON.toJson(request)); + return GSON.fromJson(response, ComplaintNotifyUrlResult.class); + } + + @Override + public void deleteComplaintNotifyUrl() throws WxPayException { + String url = String.format("%s/v3/merchant-service/complaint-notifications", this.payService.getPayBaseUrl()); + this.payService.deleteV3(url); + } + + @Override + public void submitResponse(ResponseRequest request) throws WxPayException { + String url = String.format("%s/v3/merchant-service/complaints-v2/%s/response", this.payService.getPayBaseUrl(), request.getComplaintId()); + // 上面url已经含有complaintId,这里设置为空,避免在body中再次传递,否则微信会报错 + request.setComplaintId(null); + this.payService.postV3(url, GSON.toJson(request)); + } + + @Override + public void complete(CompleteRequest request) throws WxPayException { + String url = String.format("%s/v3/merchant-service/complaints-v2/%s/complete", this.payService.getPayBaseUrl(), request.getComplaintId()); + // 上面url已经含有complaintId,这里设置为空,避免在body中再次传递,否则微信会报错 + request.setComplaintId(null); + this.payService.postV3(url, GSON.toJson(request)); + } + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/PartnerTransferServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/PartnerTransferServiceImpl.java new file mode 100644 index 000000000..1a8a28f84 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/PartnerTransferServiceImpl.java @@ -0,0 +1,311 @@ +package com.github.binarywang.wxpay.service.impl; + +import com.github.binarywang.wxpay.bean.ecommerce.FundBalanceResult; +import com.github.binarywang.wxpay.bean.ecommerce.enums.SpAccountTypeEnum; +import com.github.binarywang.wxpay.bean.marketing.transfer.*; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.PartnerTransferService; +import com.github.binarywang.wxpay.service.WxPayService; +import com.github.binarywang.wxpay.v3.util.RsaCryptoUtil; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import jodd.util.StringUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import java.io.InputStream; + +/** + * 批量转账到零钱(服务商) + * + * @author xiaoqiang + * @date 2021-12-06 + */ +@Slf4j +@RequiredArgsConstructor +public class PartnerTransferServiceImpl implements PartnerTransferService { + + private static final Gson GSON = new GsonBuilder().create(); + private final WxPayService payService; + + /** + * 发起批量转账API + * 适用对象:服务商 + * 请求URL:https://api.mch.weixin.qq.com/v3/partner-transfer/batches + * 请求方式:POST + * 接口限频:单个服务商 50QPS,如果超过频率限制,会报错FREQUENCY_LIMITED,请降低频率请求。 + * + * @param request 请求对象 + * @return 返回数据 {@link PartnerTransferResult} + * @throws WxPayException the wx pay exception + */ + @Override + public PartnerTransferResult batchTransfer(PartnerTransferRequest request) throws WxPayException { + request.getTransferDetailList().stream().forEach(p -> { + try { + String userName = RsaCryptoUtil.encryptOAEP(p.getUserName(), this.payService.getConfig().getVerifier().getValidCertificate()); + p.setUserName(userName); + } catch (IllegalBlockSizeException e) { + throw new RuntimeException("姓名转换异常!", e); + } + }); + String url = String.format("%s/v3/partner-transfer/batches", this.payService.getPayBaseUrl()); + String response = this.payService.postV3WithWechatpaySerial(url, GSON.toJson(request)); + return GSON.fromJson(response, PartnerTransferResult.class); + } + + /** + * 微信支付批次单号查询批次单API + * 接口说明 + * 适用对象:服务商 + * 请求URL:https://api.mch.weixin.qq.com/v3/partner-transfer/batches/batch-id/{batch_id} + * https://api.mch.weixin.qq.com/v3/partner-transfer/batches/batch-id/1030000071100999991182020050700019480001?need_query_detail=true&offset=1 + * 请求方式:GET + * 接口限频:单个服务商 50QPS,如果超过频率限制,会报错FREQUENCY_LIMITED,请降低频率请求。 + * + * @param request 请求对象 + * @return 返回数据 {@link BatchNumberResult} + * @throws WxPayException the wx pay exception + */ + @Override + public BatchNumberResult queryBatchByBatchId(BatchNumberRequest request) throws WxPayException { + String url = String.format("%s/v3/partner-transfer/batches/batch-id/%s", this.payService.getPayBaseUrl(), request.getBatchId()); + if (request.getOffset() == null) { + request.setOffset(0); + } + if (request.getLimit() == null || request.getLimit() <= 0) { + request.setLimit(20); + } + String query = String.format("?need_query_detail=true&detail_status=ALL&offset=%s&limit=%s", request.getNeedQueryDetail(), request.getOffset(), request.getLimit()); + if (StringUtil.isNotBlank(request.getDetailStatus())){ + query += "&detail_status="+request.getDetailStatus(); + } + String response = this.payService.getV3(url + query); + return GSON.fromJson(response, BatchNumberResult.class); + } + + /** + * 商家批次单号查询批次单API + * 接口说明 + * 适用对象:服务商 + * 请求URL:https://api.mch.weixin.qq.com/v3/partner-transfer/batches/out-batch-no/{out_batch_no} + * 请求方式:GET + * 接口限频:单个服务商 50QPS,如果超过频率限制,会报错FREQUENCY_LIMITED,请降低频率请求。 + * + * @param request 请求对象 + * @return 返回数据 {@link BatchNumberResult} + * @throws WxPayException the wx pay exception + */ + @Override + public BatchNumberResult queryBatchByOutBatchNo(MerchantBatchRequest request) throws WxPayException { + String url = String.format("%s/v3/partner-transfer/batches/out-batch-no/%s", this.payService.getPayBaseUrl(), request.getOutBatchNo()); + if (request.getOffset() == null) { + request.setOffset(0); + } + if (request.getLimit() == null || request.getLimit() <= 0) { + request.setLimit(20); + } + String query = String.format("?need_query_detail=true&offset=%s&limit=%s", request.getNeedQueryDetail(), request.getOffset(), request.getLimit()); + if (StringUtil.isNotBlank(request.getDetailStatus())){ + query += "&detail_status="+request.getDetailStatus(); + } + String response = this.payService.getV3(url + query); + return GSON.fromJson(response, BatchNumberResult.class); + } + + /** + * 微信支付明细单号查询明细单API + * 接口说明 + * 适用对象:服务商 + * 请求URL:https://api.mch.weixin.qq.com/v3/partner-transfer/batches/batch-id/{batch_id}/details/detail-id/{detail_id} + * 请求方式:GET + * 接口限频:单个服务商 50QPS,如果超过频率限制,会报错FREQUENCY_LIMITED,请降低频率请求。 + * + * @param batchId 微信批次单号 + * @param detailId 微信明细单号 + * @return 返回数据 {@link BatchDetailsResult} + * @throws WxPayException the wx pay exception + * @throws BadPaddingException the wx decrypt exception + */ + @Override + public BatchDetailsResult queryBatchDetailByWeChat(String batchId, String detailId) throws WxPayException, BadPaddingException { + String url = String.format("%s/v3/partner-transfer/batches/batch-id/%s/details/detail-id/%s", this.payService.getPayBaseUrl(), batchId, detailId); + String response = this.payService.getV3(url); + BatchDetailsResult batchDetailsResult = GSON.fromJson(response, BatchDetailsResult.class); + String userName = RsaCryptoUtil.decryptOAEP(batchDetailsResult.getUserName(), this.payService.getConfig().getPrivateKey()); + batchDetailsResult.setUserName(userName); + return batchDetailsResult; + } + + /** + * 商家明细单号查询明细单API + * 接口说明 + * 适用对象:服务商 + * 请求URL:https://api.mch.weixin.qq.com/v3/partner-transfer/batches/out-batch-no/{out_batch_no}/details/out-detail-no/{out_detail_no} + * 请求方式:GET + * 接口限频:单个服务商 50QPS,如果超过频率限制,会报错FREQUENCY_LIMITED,请降低频率请求。 + * + * @param outBatchNo 商家明细单号 + * @param outDetailNo 商家批次单号 + * @return 返回数据 {@link BatchDetailsResult} + * @throws WxPayException the wx pay exception + * @throws BadPaddingException the wx decrypt exception + */ + @Override + public BatchDetailsResult queryBatchDetailByMch(String outBatchNo, String outDetailNo) throws WxPayException, BadPaddingException { + String url = String.format("%s/v3/partner-transfer/batches/out-batch-no/%s/details/out-detail-no/%s", this.payService.getPayBaseUrl(), outBatchNo, outDetailNo); + String response = this.payService.getV3(url); + BatchDetailsResult batchDetailsResult = GSON.fromJson(response, BatchDetailsResult.class); + String userName = RsaCryptoUtil.decryptOAEP(batchDetailsResult.getUserName(), this.payService.getConfig().getPrivateKey()); + batchDetailsResult.setUserName(userName); + return batchDetailsResult; + } + + + /** + * 转账电子回单申请受理API + * 接口说明 + * 适用对象:直连商户 服务商 + * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter4_1.shtml + * 请求URL:https://api.mch.weixin.qq.com/v3/transfer/bill-receipt + * 请求方式:POST + * + * @param request 商家批次单号 + * @return 返回数据 fund balance result + * @throws WxPayException the wx pay exception + */ + @Override + public BillReceiptResult receiptBill(ReceiptBillRequest request) throws WxPayException { + String url = String.format("%s/v3/transfer/bill-receipt", this.payService.getPayBaseUrl()); + String response = this.payService.postV3(url, GSON.toJson(request)); + return GSON.fromJson(response, BillReceiptResult.class); + } + + + /** + * 查询转账电子回单API + * 接口说明 + * 适用对象:直连商户 服务商 + * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter4_2.shtml + * 请求URL:https://api.mch.weixin.qq.com/v3/transfer/bill-receipt/{out_batch_no} + * 请求方式:GET + * + * @param outBatchNo 商家批次单号 + * @return 返回数据 fund balance result + * @throws WxPayException the wx pay exception + */ + @Override + public BillReceiptResult queryBillReceipt(String outBatchNo) throws WxPayException { + String url = String.format("%s/v3/transfer/bill-receipt/%s", this.payService.getPayBaseUrl(), outBatchNo); + String response = this.payService.getV3(url); + return GSON.fromJson(response, BillReceiptResult.class); + } + + /** + * 转账明细电子回单受理API + * 接口说明 + * 适用对象:直连商户 服务商 + * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter4_4.shtml + * 请求URL:https://api.mch.weixin.qq.com/v3/transfer-detail/electronic-receipts + * 请求方式:POST + * 前置条件:只支持受理最近90天内的转账明细单 + * + * @param request 请求对象 + * @return 返回数据 fund balance result + * @throws WxPayException the wx pay exception + */ + @Override + public ElectronicReceiptsResult transferElectronic(ElectronicReceiptsRequest request) throws WxPayException { + String url = String.format("%s/v3/transfer-detail/electronic-receipts", this.payService.getPayBaseUrl()); + String response = this.payService.postV3(url, GSON.toJson(request)); + return GSON.fromJson(response, ElectronicReceiptsResult.class); + } + + + /** + * 查询转账明细电子回单受理结果API + * 接口说明 + * 适用对象:直连商户 服务商 + * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter4_5.shtml + * 请求URL:https://api.mch.weixin.qq.com/v3/transfer-detail/electronic-receipts + * 请求方式:GET + * 前置条件:只支持查询最近90天内的转账明细单 + * + * @param request 请求对象 + * @return 返回数据 fund balance result + * @throws WxPayException the wx pay exception + */ + @Override + public ElectronicReceiptsResult queryTransferElectronicResult(ElectronicReceiptsRequest request) throws WxPayException { + String url = String.format("%s/v3/transfer-detail/electronic-receipts", this.payService.getPayBaseUrl()); + String query = String.format("?accept_type=%s&out_batch_no=%s&out_detail_no=%s", request.getAcceptType(), request.getOutBatchNo(), request.getOutDetailNo()); + String response = this.payService.getV3(url + query); + return GSON.fromJson(response, ElectronicReceiptsResult.class); + } + + /** + * 下载电子回单API + * 接口说明 + * 适用对象:直连商户 服务商 + * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter4_3.shtml + * 请求URL:通过申请账单接口获取到“download_url”,URL有效期10min + * 请求方式:GET + * 前置条件:调用申请账单接口并获取到“download_url” + * + * @param url 微信返回的电子回单地址。 + * @return 返回数据 fund balance result + * @throws WxPayException the wx pay exception + */ + @Override + public InputStream transferDownload(String url) throws WxPayException { + InputStream response = this.payService.downloadV3(url); + return response; + } + + /** + *
+   * 查询账户实时余额API
+   * 接口说明
+   * 适用对象:直连商户 服务商
+   * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter5_1.shtml
+   * 请求URL:https://api.mch.weixin.qq.com/v3/merchant/fund/balance/{account_type}
+   * 请求方式:GET
+   * 
+ * + * @param accountType 服务商账户类型 {@link SpAccountTypeEnum} + * @return 返回数据 fund balance result + * @throws WxPayException the wx pay exception + */ + @Override + public FundBalanceResult fundBalance(SpAccountTypeEnum accountType) throws WxPayException { + String url = String.format("%s/v3/merchant/fund/balance/%s", this.payService.getPayBaseUrl(), accountType); + String response = this.payService.getV3(url); + return GSON.fromJson(response, FundBalanceResult.class); + } + + /** + *
+   * 服务商账户日终余额
+   * 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter5_2.shtml
+   * 文档地址: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pages/amount.shtml
+   * 
+ * + * @param accountType 服务商账户类型 + * @param date 查询日期 2020-09-11 + * @return 返回数据 fund balance result + * @throws WxPayException the wx pay exception + */ + @Override + public FundBalanceResult spDayEndBalance(SpAccountTypeEnum accountType, String date) { + try { + return this.payService.getEcommerceService().spDayEndBalance(accountType, date); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/PayScoreServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/PayScoreServiceImpl.java index 0b237fda3..4181e74f2 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/PayScoreServiceImpl.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/PayScoreServiceImpl.java @@ -10,6 +10,7 @@ import com.github.binarywang.wxpay.service.PayScoreService; import com.github.binarywang.wxpay.service.WxPayService; import com.github.binarywang.wxpay.v3.util.AesUtils; +import com.google.common.base.Strings; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import lombok.RequiredArgsConstructor; @@ -38,17 +39,23 @@ public class PayScoreServiceImpl implements PayScoreService { public WxPayScoreResult permissions(WxPayScoreRequest request) throws WxPayException { WxPayConfig config = this.payService.getConfig(); String url = this.payService.getPayBaseUrl() + "/v3/payscore/permissions"; - request.setAppid(config.getAppId()); - request.setServiceId(config.getServiceId()); - String permissionNotifyUrl = config.getPayScorePermissionNotifyUrl(); - if (StringUtils.isBlank(permissionNotifyUrl)) { - throw new WxPayException("授权回调地址未配置"); + if(Strings.isNullOrEmpty(request.getAppid())){ + request.setAppid(config.getAppId()); + } + if(Strings.isNullOrEmpty(request.getServiceId())){ + request.setServiceId(config.getServiceId()); + } + if(Strings.isNullOrEmpty(request.getNotifyUrl())){ + String permissionNotifyUrl = config.getPayScorePermissionNotifyUrl(); + if (StringUtils.isBlank(permissionNotifyUrl)) { + throw new WxPayException("授权回调地址未配置"); + } + request.setNotifyUrl(permissionNotifyUrl); } String authorizationCode = request.getAuthorizationCode(); if (StringUtils.isBlank(authorizationCode)) { throw new WxPayException("authorizationCode不允许为空"); } - request.setNotifyUrl(permissionNotifyUrl); String result = this.payService.postV3(url, request.toJson()); return WxPayScoreResult.fromJson(result); @@ -139,9 +146,15 @@ public WxPayScoreResult createServiceOrder(WxPayScoreRequest request) throws WxP boolean needUserConfirm = request.getNeedUserConfirm(); WxPayConfig config = this.payService.getConfig(); String url = this.payService.getPayBaseUrl() + "/v3/payscore/serviceorder"; - request.setAppid(config.getAppId()); - request.setServiceId(config.getServiceId()); - request.setNotifyUrl(config.getPayScoreNotifyUrl()); + if(Strings.isNullOrEmpty(request.getAppid())){ + request.setAppid(config.getAppId()); + } + if(Strings.isNullOrEmpty(request.getServiceId())){ + request.setServiceId(config.getServiceId()); + } + if(Strings.isNullOrEmpty(request.getNotifyUrl())){ + request.setNotifyUrl(config.getPayScoreNotifyUrl()); + } String result = this.payService.postV3(url, request.toJson()); WxPayScoreResult wxPayScoreCreateResult = WxPayScoreResult.fromJson(result); @@ -213,8 +226,12 @@ public WxPayScoreResult modifyServiceOrder(WxPayScoreRequest request) throws WxP WxPayConfig config = this.payService.getConfig(); String outOrderNo = request.getOutOrderNo(); String url = String.format("%s/v3/payscore/serviceorder/%s/modify", this.payService.getPayBaseUrl(), outOrderNo); - request.setAppid(config.getAppId()); - request.setServiceId(config.getServiceId()); + if(Strings.isNullOrEmpty(request.getAppid())){ + request.setAppid(config.getAppId()); + } + if(Strings.isNullOrEmpty(config.getServiceId())){ + request.setServiceId(config.getServiceId()); + } request.setOutOrderNo(null); String result = payService.postV3(url, request.toJson()); return WxPayScoreResult.fromJson(result); @@ -225,8 +242,12 @@ public WxPayScoreResult completeServiceOrder(WxPayScoreRequest request) throws W WxPayConfig config = this.payService.getConfig(); String outOrderNo = request.getOutOrderNo(); String url = String.format("%s/v3/payscore/serviceorder/%s/complete", this.payService.getPayBaseUrl(), outOrderNo); - request.setAppid(config.getAppId()); - request.setServiceId(config.getServiceId()); + if(Strings.isNullOrEmpty(request.getAppid())){ + request.setAppid(config.getAppId()); + } + if(Strings.isNullOrEmpty(request.getServiceId())){ + request.setServiceId(config.getServiceId()); + } request.setOutOrderNo(null); String result = payService.postV3(url, request.toJson()); return WxPayScoreResult.fromJson(result); @@ -248,8 +269,12 @@ public WxPayScoreResult syncServiceOrder(WxPayScoreRequest request) throws WxPay WxPayConfig config = this.payService.getConfig(); String outOrderNo = request.getOutOrderNo(); String url = String.format("%s/v3/payscore/serviceorder/%s/sync", this.payService.getPayBaseUrl(), outOrderNo); - request.setAppid(config.getAppId()); - request.setServiceId(config.getServiceId()); + if(Strings.isNullOrEmpty(request.getAppid())){ + request.setAppid(config.getAppId()); + } + if(Strings.isNullOrEmpty(request.getServiceId())){ + request.setServiceId(config.getServiceId()); + } request.setOutOrderNo(null); String result = payService.postV3(url, request.toJson()); return WxPayScoreResult.fromJson(result); diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/PayrollServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/PayrollServiceImpl.java new file mode 100644 index 000000000..51947a274 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/PayrollServiceImpl.java @@ -0,0 +1,192 @@ +package com.github.binarywang.wxpay.service.impl; + +import com.github.binarywang.wxpay.bean.marketing.payroll.*; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.PayrollService; +import com.github.binarywang.wxpay.service.WxPayService; +import com.github.binarywang.wxpay.v3.util.RsaCryptoUtil; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; + +import javax.crypto.IllegalBlockSizeException; + +/** + * 微信支付-微工卡 + * + * @author xiaoqiang + * @date 2021/12/2 + */ +@Slf4j +@RequiredArgsConstructor +public class PayrollServiceImpl implements PayrollService { + + private static final Gson GSON = new GsonBuilder().create(); + + private final WxPayService payService; + + /** + * 生成授权token + * 适用对象:服务商 + * 请求URL:https://api.mch.weixin.qq.com/v3/payroll-card/tokens + * 请求方式:POST + * + * @param request 请求参数 + * @return 返回数据 + * @throws WxPayException the wx pay exception + */ + @Override + public TokensResult payrollCardTokens(TokensRequest request) throws WxPayException { + String url = String.format("%s/v3/payroll-card/tokens", payService.getPayBaseUrl()); + try { + String userName = RsaCryptoUtil.encryptOAEP(request.getUserName(), payService.getConfig().getVerifier().getValidCertificate()); + request.setUserName(userName); + String idCardNumber = RsaCryptoUtil.encryptOAEP(request.getIdCardNumber(), payService.getConfig().getVerifier().getValidCertificate()); + request.setIdCardNumber(idCardNumber); + } catch (IllegalBlockSizeException e) { + throw new RuntimeException("加密异常!", e); + } + String response = payService.postV3(url, GSON.toJson(request)); + return GSON.fromJson(response, TokensResult.class); + } + + /** + * 查询微工卡授权关系API + * 适用对象:服务商 + * 请求URL:https://api.mch.weixin.qq.com/v3/payroll-card/relations/{openid} + * 请求方式:GET + * + * @param request 请求参数 + * @return 返回数据 + * @throws WxPayException the wx pay exception + */ + @Override + public RelationsResult payrollCardRelations(RelationsRequest request) throws WxPayException { + String url = String.format("%s/v3/payroll-card/relations/%s", + payService.getPayBaseUrl(), request.getOpenid()); + String query = String.format("?sub_mchid=%s", request.getSubMchid()); + if (StringUtils.isNotEmpty(request.getAppid())) { + query += "&appid=" + request.getAppid(); + } + if (StringUtils.isNotEmpty(request.getSubAppid())) { + query += "&sub_appid=" + request.getSubAppid(); + } + String response = payService.getV3(url + query); + return GSON.fromJson(response, RelationsResult.class); + } + + /** + * 微工卡核身预下单API + * 适用对象:服务商 + * 请求URL:https://api.mch.weixin.qq.com/v3/payroll-card/authentications/pre-order + * 请求方式:POST + * + * @param request 请求参数 + * @return 返回数据 + * @throws WxPayException the wx pay exception + */ + @Override + public PreOrderResult payrollCardPreOrder(PreOrderRequest request) throws WxPayException { + String url = String.format("%s/v3/payroll-card/authentications/pre-order", payService.getPayBaseUrl()); + String response = payService.postV3(url, GSON.toJson(request)); + return GSON.fromJson(response, PreOrderResult.class); + } + + /** + * 获取核身结果API + * 适用对象:服务商 + * 请求URL:https://api.mch.weixin.qq.com/v3/payroll-card/authentications/{authenticate_number} + * 请求方式:GET + * + * @param subMchid 子商户号 + * @param authenticateNumber 商家核身单号 + * @return 返回数据 + * @throws WxPayException the wx pay exception + */ + @Override + public AuthenticationsResult payrollCardAuthenticationsNumber(String subMchid, String authenticateNumber) throws WxPayException { + String url = String.format("%s/v3/payroll-card/authentications/%s", payService.getPayBaseUrl(), authenticateNumber); + String query = String.format("?sub_mchid=%s", subMchid); + String response = payService.getV3(url + query); + return GSON.fromJson(response, AuthenticationsResult.class); + } + + /** + * 查询核身记录API + * 适用对象:服务商 + * 请求URL:https://api.mch.weixin.qq.com/v3/payroll-card/authentications + * 请求方式:GET + * + * @param request 请求参数 + * @return 返回数据 + * @throws WxPayException the wx pay exception + */ + @Override + public AuthRecordResult payrollCardAuthentications(AuthRecordRequest request) throws WxPayException { + String url = String.format("%s/v3/payroll-card/authentications", payService.getPayBaseUrl()); + String query = String.format("?openid=%s&sub_mchid=%s&authenticate_date=%s", + request.getOpenid(), request.getAppid(), request.getSubMchid(), request.getAuthenticateDate()); + if (StringUtils.isNotEmpty(request.getAppid())) { + query += "&appid=" + request.getAppid(); + } + if (StringUtils.isNotEmpty(request.getAppid())) { + query += "&sub_appid=" + request.getSubAppid(); + } + if (StringUtils.isNotEmpty(request.getAuthenticateState())) { + query += "&authenticate_state=" + request.getAuthenticateState(); + } + String response = payService.getV3(url + query); + return GSON.fromJson(response, AuthRecordResult.class); + } + + /** + * 微工卡核身预下单(流程中完成授权) + * 适用对象:服务商 + * 请求URL:https://api.mch.weixin.qq.com/v3/payroll-card/authentications/pre-order-with-auth + * 请求方式:POST + * + * @param request 请求参数 + * @return 返回数据 + * @throws WxPayException the wx pay exception + */ + @Override + public PreOrderWithAuthResult payrollCardPreOrderWithAuth(PreOrderWithAuthRequest request) throws WxPayException { + String url = String.format("%s/v3/payroll-card/authentications/pre-order-with-auth", payService.getPayBaseUrl()); + try { + String userName = RsaCryptoUtil.encryptOAEP(request.getUserName(), payService.getConfig().getVerifier().getValidCertificate()); + request.setUserName(userName); + String idCardNumber = RsaCryptoUtil.encryptOAEP(request.getIdCardNumber(), payService.getConfig().getVerifier().getValidCertificate()); + request.setIdCardNumber(idCardNumber); + } catch (IllegalBlockSizeException e) { + throw new RuntimeException("敏感信息加密异常!", e); + } + String response = payService.postV3(url, GSON.toJson(request)); + return GSON.fromJson(response, PreOrderWithAuthResult.class); + } + + /** + * 按日下载提现异常文件API + * 适用对象:服务商 + * 请求URL:https://api.mch.weixin.qq.com/v3/merchant/fund/withdraw/bill-type/{bill_type} + * 请求方式:GET + * + * @param billType 账单类型 + * NO_SUCC:提现异常账单,包括提现失败和提现退票账单。 + * 示例值:NO_SUCC + * @param billDate 账单日期 表示所在日期的提现账单,格式为YYYY-MM-DD。 + * 例如:2008-01-01日发起的提现,2008-01-03日银行返回提现失败,则该提现数据将出现在bill_date为2008-01-03日的账单中。 + * 示例值:2019-08-17 + * @return 返回数据 + * @throws WxPayException the wx pay exception + */ + @Override + public PreOrderWithAuthResult merchantFundWithdrawBillType(String billType, String billDate) throws WxPayException { + String url = String.format("%s/v3/merchant/fund/withdraw/bill-type/%s", payService.getPayBaseUrl(), billType); + String query = String.format("?bill_date=%s", billDate); + String response = payService.getV3(url + query); + return GSON.fromJson(response, PreOrderWithAuthResult.class); + } + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceApacheHttpImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceApacheHttpImpl.java index 87d5014ac..e70813fc3 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceApacheHttpImpl.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceApacheHttpImpl.java @@ -269,6 +269,24 @@ public InputStream downloadV3(String url) throws WxPayException { } } + @Override + public String putV3(String url, String requestStr) throws WxPayException { + HttpPut httpPut = new HttpPut(url); + StringEntity entity = this.createEntry(requestStr); + httpPut.setEntity(entity); + httpPut.addHeader("Accept", "application/json"); + httpPut.addHeader("Content-Type", "application/json"); + return requestV3(url, httpPut); + } + + @Override + public String deleteV3(String url) throws WxPayException { + HttpDelete httpDelete = new HttpDelete(url); + httpDelete.addHeader("Accept", "application/json"); + httpDelete.addHeader("Content-Type", "application/json"); + return requestV3(url, httpDelete); + } + private CloseableHttpClient createApiV3HttpClient() throws WxPayException { CloseableHttpClient apiV3HttpClient = this.getConfig().getApiV3HttpClient(); if (null == apiV3HttpClient) { diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceJoddHttpImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceJoddHttpImpl.java index 374528454..29521d493 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceJoddHttpImpl.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceJoddHttpImpl.java @@ -96,6 +96,16 @@ public InputStream downloadV3(String url) throws WxPayException { return null; } + @Override + public String putV3(String url, String requestStr) throws WxPayException { + return null; + } + + @Override + public String deleteV3(String url) throws WxPayException { + return null; + } + private HttpRequest buildHttpRequest(String url, String requestStr, boolean useKey) throws WxPayException { HttpRequest request = HttpRequest .post(url) diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/HttpProxyUtils.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/HttpProxyUtils.java new file mode 100755 index 000000000..7e24cde80 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/HttpProxyUtils.java @@ -0,0 +1,49 @@ +package com.github.binarywang.wxpay.util; + +import com.github.binarywang.wxpay.config.WxPayHttpProxy; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpHost; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.apache.http.impl.client.HttpClientBuilder; + +/** + * 微信支付 HTTP Proxy 工具类 + * + * @author Long Yu + * @date 2021-12-28 15:58:03 + */ +public class HttpProxyUtils { + + /** + * 配置 http 正向代理 可以实现内网服务通过代理调用接口 + * 参考代码: WxPayServiceApacheHttpImpl 中的方法 createHttpClientBuilder + * + * @param wxPayHttpProxy 代理配置 + * @param httpClientBuilder http构造参数 + */ + public static void initHttpProxy(HttpClientBuilder httpClientBuilder, WxPayHttpProxy wxPayHttpProxy) { + if(wxPayHttpProxy == null){ + return; + } + if (StringUtils.isNotBlank(wxPayHttpProxy.getHttpProxyHost()) && wxPayHttpProxy.getHttpProxyPort() > 0) { + if (StringUtils.isEmpty(wxPayHttpProxy.getHttpProxyUsername())) { + wxPayHttpProxy.setHttpProxyUsername("whatever"); + } + + // 使用代理服务器 需要用户认证的代理服务器 + CredentialsProvider provider = new BasicCredentialsProvider(); + provider.setCredentials(new AuthScope(wxPayHttpProxy.getHttpProxyHost(), wxPayHttpProxy.getHttpProxyPort()), + new UsernamePasswordCredentials(wxPayHttpProxy.getHttpProxyUsername(), wxPayHttpProxy.getHttpProxyPassword())); + httpClientBuilder.setDefaultCredentialsProvider(provider); + httpClientBuilder.setProxy(new HttpHost(wxPayHttpProxy.getHttpProxyHost(), wxPayHttpProxy.getHttpProxyPort())); + } + } + + + + + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/ZipUtils.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/ZipUtils.java new file mode 100644 index 000000000..f9c434196 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/ZipUtils.java @@ -0,0 +1,33 @@ +package com.github.binarywang.wxpay.util; + +import lombok.experimental.UtilityClass; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.io.IOUtils; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.zip.GZIPInputStream; + +/** + * @author Binary Wang + */ +@UtilityClass +public class ZipUtils { + + /** + * 解压gzip文件 + */ + public static File unGzip(final File file) throws IOException { + File resultFile = new File(FilenameUtils.removeExtension(file.getAbsolutePath())); + resultFile.createNewFile(); + + try (FileOutputStream fos = new FileOutputStream(resultFile); + GZIPInputStream gzis = new GZIPInputStream(new FileInputStream(file));) { + IOUtils.copy(gzis, fos); + } + + return resultFile; + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/auth/AutoUpdateCertificatesVerifier.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/auth/AutoUpdateCertificatesVerifier.java old mode 100644 new mode 100755 index d365ccaf4..ca8b30bc8 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/auth/AutoUpdateCertificatesVerifier.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/auth/AutoUpdateCertificatesVerifier.java @@ -1,5 +1,7 @@ package com.github.binarywang.wxpay.v3.auth; +import com.github.binarywang.wxpay.config.WxPayHttpProxy; +import com.github.binarywang.wxpay.util.HttpProxyUtils; import com.github.binarywang.wxpay.v3.Credentials; import com.github.binarywang.wxpay.v3.Validator; import com.github.binarywang.wxpay.v3.WxPayV3HttpClientBuilder; @@ -35,6 +37,7 @@ * 在原有CertificatesVerifier基础上,增加自动更新证书功能 * * @author doger.wang + * @author Long Yu */ @Slf4j public class AutoUpdateCertificatesVerifier implements Verifier { @@ -61,6 +64,11 @@ public class AutoUpdateCertificatesVerifier implements Verifier { private final ReentrantLock lock = new ReentrantLock(); + /** + * 微信支付代理对象 + */ + private WxPayHttpProxy wxPayHttpProxy; + /** * 时间间隔枚举,支持一小时、六小时以及十二小时 */ @@ -88,9 +96,14 @@ public AutoUpdateCertificatesVerifier(Credentials credentials, byte[] apiV3Key) } public AutoUpdateCertificatesVerifier(Credentials credentials, byte[] apiV3Key, int minutesInterval) { + this(credentials,apiV3Key,minutesInterval,null); + } + + public AutoUpdateCertificatesVerifier(Credentials credentials, byte[] apiV3Key, int minutesInterval,WxPayHttpProxy wxPayHttpProxy) { this.credentials = credentials; this.apiV3Key = apiV3Key; this.minutesInterval = minutesInterval; + this.wxPayHttpProxy = wxPayHttpProxy; //构造时更新证书 try { autoUpdateCert(); @@ -126,15 +139,22 @@ private void checkAndAutoUpdateCert() { } private void autoUpdateCert() throws IOException, GeneralSecurityException { - CloseableHttpClient httpClient = WxPayV3HttpClientBuilder.create() + WxPayV3HttpClientBuilder wxPayV3HttpClientBuilder = WxPayV3HttpClientBuilder.create() .withCredentials(credentials) .withValidator(verifier == null ? new Validator() { @Override public boolean validate(CloseableHttpResponse response) throws IOException { return true; } - } : new WxPayValidator(verifier)) - .build(); + } : new WxPayValidator(verifier)); + + //调用自定义扩展设置设置HTTP PROXY对象 + HttpProxyUtils.initHttpProxy(wxPayV3HttpClientBuilder,this.wxPayHttpProxy); + + //增加自定义扩展点,子类可以设置其他构造参数 + this.customHttpClientBuilder(wxPayV3HttpClientBuilder); + + CloseableHttpClient httpClient = wxPayV3HttpClientBuilder.build(); HttpGet httpGet = new HttpGet(CERT_DOWNLOAD_PATH); httpGet.addHeader("Accept", "application/json"); @@ -154,6 +174,14 @@ public boolean validate(CloseableHttpResponse response) throws IOException { } } + + /** + * 子类可以自定义添加Http client builder的信息 + * @param builder httpclient构造器 + */ + public void customHttpClientBuilder(WxPayV3HttpClientBuilder builder) { + } + /** * 反序列化证书并解密 */ diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImplTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImplTest.java index 920d917ff..e25efe5cb 100644 --- a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImplTest.java +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImplTest.java @@ -10,6 +10,7 @@ import com.github.binarywang.wxpay.bean.request.*; import com.github.binarywang.wxpay.bean.result.*; import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum; +import com.github.binarywang.wxpay.config.WxPayConfig; import com.github.binarywang.wxpay.constant.WxPayConstants.AccountType; import com.github.binarywang.wxpay.constant.WxPayConstants.BillType; import com.github.binarywang.wxpay.constant.WxPayConstants.SignType; @@ -775,4 +776,24 @@ public void testRefundQueryV3() throws WxPayException { System.out.println(GSON.toJson(result)); } + /** + * 测试包含正向代理的测试 + * @throws WxPayException + */ + @Test + public void testQueryOrderV3WithProxy() { + try { + WxPayOrderQueryV3Request request = new WxPayOrderQueryV3Request(); + request.setOutTradeNo("n1ZvYqjAg3D3LUBa"); + WxPayConfig config = this.payService.getConfig(); + config.setPayBaseUrl("http://api.mch.weixin.qq.com"); + config.setHttpProxyHost("12.11.1.113"); + config.setHttpProxyPort(8015); + WxPayOrderQueryV3Result result = this.payService.queryOrderV3(request); + System.out.println(GSON.toJson(result)); + } catch (WxPayException e) { + } + + } + } diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/ComplaintServiceImplTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/ComplaintServiceImplTest.java new file mode 100644 index 000000000..6014924fd --- /dev/null +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/ComplaintServiceImplTest.java @@ -0,0 +1,155 @@ +package com.github.binarywang.wxpay.service.impl; + +import com.github.binarywang.wxpay.bean.complaint.*; +import com.github.binarywang.wxpay.bean.profitsharing.*; +import com.github.binarywang.wxpay.constant.WxPayConstants; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.WxPayService; +import com.github.binarywang.wxpay.testbase.ApiTestModule; +import com.google.inject.Inject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import javax.crypto.BadPaddingException; + +/** + *
+ *  消费者投诉2.0 测试类
+ * 
+ * + * @author jmdhappy + */ +@Test +@Guice(modules = ApiTestModule.class) +public class ComplaintServiceImplTest { + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + @Inject + private WxPayService payService; + + private static final String complaintId = "200231020220320120496109901"; + + /** + * 查询投诉单列表API + * @throws WxPayException + */ + @Test + public void testQueryComplaints() throws WxPayException, BadPaddingException { + ComplaintRequest request = ComplaintRequest + .newBuilder() + .offset(0) + .limit(10) + .beginDate("2022-03-01") + .endDate("2022-03-20") + .complaintedMchid(this.payService.getConfig().getMchId()) + .build(); + this.logger.info(this.payService.getComplaintsService().queryComplaints(request).toString()); + } + + /** + * 查询投诉单详情API + * @throws WxPayException + */ + @Test + public void testGetComplaint() throws WxPayException, BadPaddingException { + ComplaintDetailRequest request = ComplaintDetailRequest + .newBuilder() + .complaintId(complaintId) + .build(); + this.logger.info(this.payService.getComplaintsService().getComplaint(request).toString()); + } + + /** + * 查询投诉协商历史API + * @throws WxPayException + */ + @Test + public void testQueryNegotiationHistorys() throws WxPayException { + NegotiationHistoryRequest request = NegotiationHistoryRequest + .newBuilder() + .complaintId(complaintId) + .offset(0) + .limit(20) + .build(); + this.logger.info(this.payService.getComplaintsService().queryNegotiationHistorys(request).toString()); + } + + /** + * 创建投诉通知回调地址API + * @throws WxPayException + */ + @Test + public void testAddComplaintNotifyUrl() throws WxPayException { + ComplaintNotifyUrlRequest request = ComplaintNotifyUrlRequest + .newBuilder() + .url("https://jeepay.natapp4.cc") + .build(); + this.logger.info(this.payService.getComplaintsService().addComplaintNotifyUrl(request).toString()); + } + + /** + * 查询投诉通知回调地址API + * @throws WxPayException + */ + @Test + public void testGetComplaintNotifyUrl() throws WxPayException { + this.logger.info(this.payService.getComplaintsService().getComplaintNotifyUrl().toString()); + } + + /** + * 更新投诉通知回调地址API + * @throws WxPayException + */ + @Test + public void testUpdateComplaintNotifyUrl() throws WxPayException { + ComplaintNotifyUrlRequest request = ComplaintNotifyUrlRequest + .newBuilder() + .url("https://jeepay1.natapp4.cc") + .build(); + this.logger.info(this.payService.getComplaintsService().updateComplaintNotifyUrl(request).toString()); + } + + /** + * 删除投诉通知回调地址API + * @throws WxPayException + */ + @Test + public void testDeleteComplaintNotifyUrl() throws WxPayException { + this.payService.getComplaintsService().deleteComplaintNotifyUrl(); + } + + /** + * 提交回复API + * @throws WxPayException + */ + @Test + public void testSubmitResponse() throws WxPayException { + ResponseRequest request = ResponseRequest + .newBuilder() + .complaintId(complaintId) + .complaintedMchid(this.payService.getConfig().getMchId()) + .responseContent("测试投诉接口1233,正在处理,不要炸鸡") + //.jumpUrl("https://www.baidu.com") + //.jumpUrlText("问题解决方案") + .build(); + this.payService.getComplaintsService().submitResponse(request); + } + + /** + * 反馈处理完成API + * @throws WxPayException + */ + @Test + public void testComplete() throws WxPayException { + CompleteRequest request = CompleteRequest + .newBuilder() + .complaintId(complaintId) + .complaintedMchid(this.payService.getConfig().getMchId()) + .build(); + this.payService.getComplaintsService().complete(request); + } + +} diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/PartnerTransferServiceImplTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/PartnerTransferServiceImplTest.java new file mode 100644 index 000000000..9c7b1cb54 --- /dev/null +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/PartnerTransferServiceImplTest.java @@ -0,0 +1,139 @@ +package com.github.binarywang.wxpay.service.impl; + +import com.github.binarywang.wxpay.bean.ecommerce.FundBalanceResult; +import com.github.binarywang.wxpay.bean.ecommerce.enums.SpAccountTypeEnum; +import com.github.binarywang.wxpay.bean.marketing.transfer.*; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.WxPayService; +import com.github.binarywang.wxpay.testbase.ApiTestModule; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.inject.Inject; +import lombok.extern.slf4j.Slf4j; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import javax.crypto.BadPaddingException; +import java.io.InputStream; + +/** + * 批量转账到零钱(服务商) + * + * @author xiaoqiang + * @date 2021/12/9 + */ +@Slf4j +@Test +@Guice(modules = ApiTestModule.class) +public class PartnerTransferServiceImplTest { + + @Inject + private WxPayService wxPayService; + + private static final Gson GSON = new GsonBuilder().create(); + + @Test + public void batchTransfer() throws WxPayException { + String requestParamStr = "{\"sub_mchid\":\"1608*****1\",\"authorization_type\":\"FUND_AUTHORIZATION_TYPE\",\"out_batch_no\":\"202108241345*****4618387\",\"batch_name\":\"用户提现批次单2021-12-13_13_45_50\",\"batch_remark\":\"用户>提现\",\"total_amount\":30,\"total_num\":1,\"transfer_detail_list\":[{\"out_detail_no\":\"DN202112131345506240631640467671\",\"transfer_amount\":30,\"transfer_remark\":\"钱包提现\",\"openid\":\"oUaIE6PdSuBpsHAOtvf_jsgtqu5I\",\"user_name\":\"Q2FoOMuf1Ulsab+j0nObLjkIZAUZan8Z7RaEU5qOjv1RUq5ImuqqAqoQZ4f/zD5CMxuLD7lM1TIdGIvrvO8pe2YOwoUdRxiRzDX+Z0Rsy5Y9QqEiuHHK1JTR7vC18eKp0a4PlY7K4jUl49jG0QE+6gOG83Cqj3Z9dupPor94fPRUM/ZIzF293ONgSJW1iuHkd6g7EHTpizHZ/r5XcT+qh*************************kqjtVkT3GiuDXmMA8d/hO85uY50ItNNa5Ov8kmJbLCgFreoS49LUEwj/yuDap6F4g\\u003d\\u003d\"}],\"sp_appid\":\"wx6aa************ef\",\"transfer_purpose\":\"OTHERS\"}"; + + PartnerTransferRequest request = GSON.fromJson(requestParamStr, PartnerTransferRequest.class); + + PartnerTransferResult partnerTransferResult = wxPayService.getPartnerTransferService().batchTransfer(request); + log.info(partnerTransferResult.toString()); + } + + + @Test + public void queryBatchByBatchId() throws WxPayException { + BatchNumberRequest request = new BatchNumberRequest(); + request.setBatchId("1030000071100999991182020050700019480001"); + request.setNeedQueryDetail(true); + request.setDetailStatus("ALL"); + BatchNumberResult batchResult = wxPayService.getPartnerTransferService().queryBatchByBatchId(request); + log.info(batchResult.toString()); + } + + + @Test + public void queryBatchDetailByWeChat() throws WxPayException, BadPaddingException { + String batchId = "1030000071100999991182020050700019480001"; + String detailId = "1040000071100999991182020050700019500100"; + BatchDetailsResult batchResult = wxPayService.getPartnerTransferService().queryBatchDetailByWeChat(batchId, detailId); + log.info(batchResult.toString()); + } + + @Test + public void queryBatchByOutBatchNo() throws WxPayException { + MerchantBatchRequest request = new MerchantBatchRequest(); + request.setOutBatchNo("10300000************0019480001"); + request.setDetailStatus("ALL"); + request.setNeedQueryDetail(true); + BatchNumberResult batchResult = wxPayService.getPartnerTransferService().queryBatchByOutBatchNo(request); + log.info(batchResult.toString()); + } + + @Test + public void queryBatchDetailByMch() throws WxPayException, BadPaddingException { + String outBatchNo = "10300000************0019480001"; + String outDetailNo = "10***********0019480001"; + BatchDetailsResult batchResult = wxPayService.getPartnerTransferService().queryBatchDetailByMch(outBatchNo, outDetailNo); + log.info(batchResult.toString()); + } + + @Test + public void receiptBill() throws WxPayException { + ReceiptBillRequest request = new ReceiptBillRequest(); + request.setOutBatchNo("10300000************0019480001"); + BillReceiptResult batchResult = wxPayService.getPartnerTransferService().receiptBill(request); + log.info(batchResult.toString()); + } + + @Test + public void queryBillReceipt() throws WxPayException { + String outBatchNo = "10300000************0019480001"; + BillReceiptResult batchResult = wxPayService.getPartnerTransferService().queryBillReceipt(outBatchNo); + log.info(batchResult.toString()); + } + + @Test + public void transferElectronic() throws WxPayException { + ElectronicReceiptsRequest request = new ElectronicReceiptsRequest(); + request.setAcceptType("BATCH_TRANSFER"); + request.setOutBatchNo("GD2021011610162610BBdkkIwcu3"); + request.setOutDetailNo("mx0911231610162610v4CNkO4HAf"); + ElectronicReceiptsResult batchResult = wxPayService.getPartnerTransferService().transferElectronic(request); + log.info(batchResult.toString()); + } + + @Test + public void queryTransferElectronicResult() throws WxPayException { + ElectronicReceiptsRequest request = new ElectronicReceiptsRequest(); + request.setAcceptType("BATCH_TRANSFER"); + request.setOutBatchNo("GD2021011610162610BBdkkIwcu3"); + request.setOutDetailNo("mx0911231610162610v4CNkO4HAf"); + ElectronicReceiptsResult batchResult = wxPayService.getPartnerTransferService().queryTransferElectronicResult(request); + log.info(batchResult.toString()); + } + + + @Test + public void transferDownload() throws WxPayException { + String url = "https://api.mch.weixin.qq.com/v3/billdownload/file?token=xxx"; + InputStream batchResult = wxPayService.getPartnerTransferService().transferDownload(url); + log.info(batchResult.toString()); + } + + @Test + public void fundBalance() throws WxPayException { + FundBalanceResult batchResult = wxPayService.getPartnerTransferService().fundBalance(SpAccountTypeEnum.BASIC); + log.info(batchResult.toString()); + } + + @Test + public void spDayEndBalance() { + String date = "2020-09-11"; + FundBalanceResult batchResult = wxPayService.getPartnerTransferService().spDayEndBalance(SpAccountTypeEnum.BASIC, date); + log.info(batchResult.toString()); + } + +} diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/PayrollServiceImplTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/PayrollServiceImplTest.java new file mode 100644 index 000000000..406d1fdf1 --- /dev/null +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/PayrollServiceImplTest.java @@ -0,0 +1,128 @@ +package com.github.binarywang.wxpay.service.impl; + +import com.github.binarywang.wxpay.bean.marketing.payroll.*; +import com.github.binarywang.wxpay.bean.marketing.transfer.PartnerTransferRequest; +import com.github.binarywang.wxpay.bean.marketing.transfer.PartnerTransferResult; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.WxPayService; +import com.github.binarywang.wxpay.testbase.ApiTestModule; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.inject.Inject; +import lombok.extern.slf4j.Slf4j; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +/** + * 微工卡(服务商) + * + * @author xiaoqiang + * @date 2021/12/9 + */ +@Slf4j +@Test +@Guice(modules = ApiTestModule.class) +public class PayrollServiceImplTest { + + + @Inject + private WxPayService wxPayService; + + private static final Gson GSON = new GsonBuilder().create(); + + @Test + public void payrollCardTokens() throws WxPayException { + TokensRequest request = new TokensRequest(); + request.setOpenid("onqOjjmo8wmTOOtSKwXtGjg9Gb58"); + request.setAppid("wxa1111111"); + request.setSubMchid("1111111"); + request.setSubAppid("wxa1111111"); + request.setUserName("LP7bT4hQXUsOZCEvK2YrSiqFsnP0oRMfeoLN0vBg"); + request.setIdCardNumber("7FzH5XksJG3a8HLLsaaUV6K54y1OnPMY5"); + request.setEmploymentType("LONG_TERM_EMPLOYMENT"); + TokensResult tokensResult = wxPayService.getPayrollService().payrollCardTokens(request); + log.info(tokensResult.toString()); + + } + + @Test + public void payrollCardRelations() throws WxPayException { + RelationsRequest request = new RelationsRequest(); + request.setOpenid("onqOjjmo8wmTOOtSKwXtGjg9Gb58"); + request.setSubMchid("1111111"); + request.setAppid("wxa1111111"); + request.setSubAppid("wxa1111111"); + RelationsResult relationsResult = wxPayService.getPayrollService().payrollCardRelations(request); + log.info(relationsResult.toString()); + + } + + + @Test + public void payrollCardPreOrder() throws WxPayException { + PreOrderRequest request = new PreOrderRequest(); + request.setOpenid("onqOjjmo8wmTOOtSKwXtGjg9Gb58"); + request.setSubMchid("1111111"); + request.setAppid("wxa1111111"); + request.setSubAppid("wxa1111111"); + request.setAuthenticateNumber("mcdhehfgisdhfjghed39384564i83"); + request.setProjectName("某项目"); + request.setEmployerName("某单位名称"); + PreOrderResult preOrderResult = wxPayService.getPayrollService().payrollCardPreOrder(request); + log.info(preOrderResult.toString()); + + } + + @Test + public void payrollCardAuthenticationsNumber() throws WxPayException { + String subMchid = "1111111"; + String authenticateNumber = "mcdhehfgisdhfjghed39384564i83"; + AuthenticationsResult authenticationsResult = wxPayService.getPayrollService().payrollCardAuthenticationsNumber(subMchid, authenticateNumber); + log.info(authenticationsResult.toString()); + + } + + @Test + public void payrollCardAuthentications() throws WxPayException { + AuthRecordRequest request = new AuthRecordRequest(); + request.setOpenid("onqOjjmo8wmTOOtSKwXtGjg9Gb58"); + request.setSubMchid("1111111"); + request.setAppid("wxa1111111"); + request.setSubAppid("wxa1111111"); + request.setAuthenticateDate("2020-12-25"); + request.setAuthenticateState("AUTHENTICATE_SUCCESS"); + request.setOffset(0); + request.setLimit(10); + AuthRecordResult authRecordResult = wxPayService.getPayrollService().payrollCardAuthentications(request); + log.info(authRecordResult.toString()); + + } + + @Test + public void payrollCardPreOrderWithAuth() throws WxPayException { + PreOrderWithAuthRequest request = new PreOrderWithAuthRequest(); + request.setOpenid("onqOjjmo8wmTOOtSKwXtGjg9Gb58"); + request.setSubMchid("1111111"); + request.setAppid("wxa1111111"); + request.setSubAppid("wxa1111111"); + request.setAuthenticateNumber("mcdhehfgisdhfjghed39384564i83"); + request.setEmployerName("某用工企业"); + request.setEmploymentType("LONG_TERM_EMPLOYMENT"); + request.setIdCardNumber("7FzH5XksJG3a8HLLsaaUV6K54y1OnPMY5"); + request.setProjectName("某项目"); + request.setUserName("LP7bT4hQXUsOZCEvK2YrSiqFsnP0oRMfeoLN0vBg"); + PreOrderWithAuthResult preOrderWithAuthResult = wxPayService.getPayrollService().payrollCardPreOrderWithAuth(request); + log.info(preOrderWithAuthResult.toString()); + + } + + @Test + public void merchantFundWithdrawBillType() throws WxPayException { + String billType = "NO_SUCC"; + String billDate = "2019-08-17"; + PreOrderWithAuthResult preOrderWithAuthResult = wxPayService.getPayrollService().merchantFundWithdrawBillType(billType, billDate); + log.info(preOrderWithAuthResult.toString()); + + } + +} diff --git a/weixin-java-qidian/pom.xml b/weixin-java-qidian/pom.xml index 4576f6918..f0f2d05b1 100644 --- a/weixin-java-qidian/pom.xml +++ b/weixin-java-qidian/pom.xml @@ -7,7 +7,7 @@ com.github.binarywang wx-java - 4.2.0 + 4.3.0 weixin-java-qidian