diff --git a/.gitignore b/.gitignore index d97b22b768..a231e6c659 100644 --- a/.gitignore +++ b/.gitignore @@ -42,6 +42,7 @@ Icon .Spotlight-V100 .TemporaryItems .Trashes +.vscode .VolumeIcon.icns .AppleDB .AppleDesktop diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000..c5f3f6b9c7 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "java.configuration.updateBuildConfiguration": "interactive" +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index f0fb113cf5..2e9a70c4fd 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ 4.0.0 com.github.binarywang wx-java - 3.3.0 + 3.4.0 pom WxJava - Weixin/Wechat Java SDK 微信开发Java SDK @@ -105,6 +105,8 @@ weixin-java-pay weixin-java-miniapp weixin-java-open + starters/wx-java-pay-starter + starters/wx-java-mp-starter diff --git a/starters/wx-java-mp-starter/README.md b/starters/wx-java-mp-starter/README.md new file mode 100644 index 0000000000..d8e1010322 --- /dev/null +++ b/starters/wx-java-mp-starter/README.md @@ -0,0 +1,32 @@ +# wx-java-mp-starter +## 快速开始 +1. 引入依赖 + ```xml + + com.github.binarywang + wx-java-mp-starter + ${version} + + ``` +2. 添加配置(application.properties) + ```properties + # 公众号配置(必填) + wx.mp.appId = @appId + wx.mp.secret = @secret + wx.mp.token = @token + wx.mp.aesKey = @aesKey + # 存储配置redis(可选) + wx.mp.config-storage.type = redis + wx.mp.config-storage.redis.host = 127.0.0.1 + wx.mp.config-storage.redis.port = 6379 + ``` +3. 支持自动注入的类型 + +`WxMpService`以及相关的服务类, 比如: `wxMpService.getXxxService`。 + + + + + + + diff --git a/starters/wx-java-mp-starter/pom.xml b/starters/wx-java-mp-starter/pom.xml new file mode 100644 index 0000000000..639662b8fe --- /dev/null +++ b/starters/wx-java-mp-starter/pom.xml @@ -0,0 +1,72 @@ + + + 4.0.0 + + com.github.binarywang + wx-java + 3.4.0 + ../../ + + + wx-java-mp-starter + WxJava - Spring Boot Starter for MP + 微信公众号开发的Spring Boot Starter + + + 2.1.4.RELEASE + + + + + org.springframework.boot + spring-boot-autoconfigure + ${spring.boot.version} + + + org.springframework.boot + spring-boot-configuration-processor + ${spring.boot.version} + true + + + com.github.binarywang + weixin-java-mp + ${project.version} + + + redis.clients + jedis + + + org.projectlombok + lombok + provided + + + + + + + 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/starters/wx-java-mp-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/RedisProperties.java b/starters/wx-java-mp-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/RedisProperties.java new file mode 100644 index 0000000000..d609cc88c1 --- /dev/null +++ b/starters/wx-java-mp-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/RedisProperties.java @@ -0,0 +1,42 @@ +package com.binarywang.spring.starter.wxjava.mp; + +import lombok.Data; + +import java.io.Serializable; + +/** + * Redis配置 + */ +@Data +public class RedisProperties implements Serializable { + + /** + * 主机地址 + */ + private String host = "127.0.0.1"; + + /** + * 端口号 + */ + private int port = 6379; + + /** + * 密码 + */ + private String password; + + /** + * 超时 + */ + private int timeout = 2000; + + /** + * 数据库 + */ + private int database = 0; + + private Integer maxActive; + private Integer maxIdle; + private Integer maxWaitMillis; + private Integer minIdle; +} \ No newline at end of file diff --git a/starters/wx-java-mp-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/WxMpAutoConfiguration.java b/starters/wx-java-mp-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/WxMpAutoConfiguration.java new file mode 100644 index 0000000000..ba85d04482 --- /dev/null +++ b/starters/wx-java-mp-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/WxMpAutoConfiguration.java @@ -0,0 +1,11 @@ +package com.binarywang.spring.starter.wxjava.mp; + +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +@Configuration +@EnableConfigurationProperties(WxMpProperties.class) +@Import({WxMpStorageAutoConfiguration.class, WxMpServiceAutoConfiguration.class}) +public class WxMpAutoConfiguration { +} diff --git a/starters/wx-java-mp-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/WxMpProperties.java b/starters/wx-java-mp-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/WxMpProperties.java new file mode 100644 index 0000000000..af74f430ed --- /dev/null +++ b/starters/wx-java-mp-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/WxMpProperties.java @@ -0,0 +1,58 @@ +package com.binarywang.spring.starter.wxjava.mp; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +import java.io.Serializable; + +import static com.binarywang.spring.starter.wxjava.mp.WxMpProperties.PREFIX; +import static com.binarywang.spring.starter.wxjava.mp.WxMpProperties.StorageType.memory; + + +/** + * 微信接入相关配置属性 + */ +@Data +@ConfigurationProperties(PREFIX) +public class WxMpProperties { + public static final String PREFIX = "wx.mp"; + + /** + * 设置微信公众号的appid + */ + private String appId; + + /** + * 设置微信公众号的app secret + */ + private String secret; + + /** + * 设置微信公众号的token + */ + private String token; + + /** + * 设置微信公众号的EncodingAESKey + */ + private String aesKey; + + /** + * 存储策略, memory, redis + */ + private ConfigStorage configStorage = new ConfigStorage(); + + + @Data + public static class ConfigStorage implements Serializable { + + private StorageType type = memory; + + private RedisProperties redis = new RedisProperties(); + + } + + public enum StorageType { + memory, redis + } +} diff --git a/starters/wx-java-mp-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/WxMpServiceAutoConfiguration.java b/starters/wx-java-mp-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/WxMpServiceAutoConfiguration.java new file mode 100644 index 0000000000..7a6cf920c4 --- /dev/null +++ b/starters/wx-java-mp-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/WxMpServiceAutoConfiguration.java @@ -0,0 +1,53 @@ +package com.binarywang.spring.starter.wxjava.mp; + +import me.chanjar.weixin.mp.api.WxMpConfigStorage; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * 微信公众号相关服务自动注册 + */ +@Configuration +public class WxMpServiceAutoConfiguration { + @Autowired + private ApplicationContext ctx; + + @Bean + @ConditionalOnMissingBean + public WxMpService wxMpService(WxMpConfigStorage configStorage) { + WxMpService wxMpService = new WxMpServiceImpl(); + wxMpService.setWxMpConfigStorage(configStorage); + registerWxMpSubService(wxMpService); + return wxMpService; + } + + @ConditionalOnBean(WxMpService.class) + public Object registerWxMpSubService(WxMpService wxMpService) { + ConfigurableListableBeanFactory factory = (ConfigurableListableBeanFactory) ctx.getAutowireCapableBeanFactory(); + factory.registerSingleton("wxMpKefuService", wxMpService.getKefuService()); + factory.registerSingleton("wxMpMaterialService", wxMpService.getMaterialService()); + factory.registerSingleton("wxMpMenuService", wxMpService.getMenuService()); + factory.registerSingleton("wxMpUserService", wxMpService.getUserService()); + factory.registerSingleton("wxMpUserTagService", wxMpService.getUserTagService()); + factory.registerSingleton("wxMpQrcodeService", wxMpService.getQrcodeService()); + factory.registerSingleton("wxMpCardService", wxMpService.getCardService()); + factory.registerSingleton("wxMpDataCubeService", wxMpService.getDataCubeService()); + factory.registerSingleton("wxMpUserBlacklistService", wxMpService.getBlackListService()); + factory.registerSingleton("wxMpStoreService", wxMpService.getStoreService()); + factory.registerSingleton("wxMpTemplateMsgService", wxMpService.getTemplateMsgService()); + factory.registerSingleton("wxMpSubscribeMsgService", wxMpService.getSubscribeMsgService()); + factory.registerSingleton("wxMpDeviceService", wxMpService.getDeviceService()); + factory.registerSingleton("wxMpShakeService", wxMpService.getShakeService()); + factory.registerSingleton("wxMpMemberCardService", wxMpService.getMemberCardService()); + factory.registerSingleton("wxMpMassMessageService", wxMpService.getMassMessageService()); + return Boolean.TRUE; + } + +} diff --git a/starters/wx-java-mp-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/WxMpStorageAutoConfiguration.java b/starters/wx-java-mp-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/WxMpStorageAutoConfiguration.java new file mode 100644 index 0000000000..a0fb7eb3d5 --- /dev/null +++ b/starters/wx-java-mp-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/WxMpStorageAutoConfiguration.java @@ -0,0 +1,84 @@ +package com.binarywang.spring.starter.wxjava.mp; + +import me.chanjar.weixin.mp.api.WxMpConfigStorage; +import me.chanjar.weixin.mp.api.WxMpInMemoryConfigStorage; +import me.chanjar.weixin.mp.api.WxMpInRedisConfigStorage; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import redis.clients.jedis.JedisPool; +import redis.clients.jedis.JedisPoolConfig; + +/** + * 微信公众号存储策略自动配置 + */ +@Configuration +public class WxMpStorageAutoConfiguration { + + @Autowired + private WxMpProperties properties; + + @Autowired(required = false) + private JedisPool jedisPool; + + @Bean + @ConditionalOnMissingBean(WxMpConfigStorage.class) + public WxMpConfigStorage wxMpInMemoryConfigStorage() { + WxMpProperties.ConfigStorage storage = properties.getConfigStorage(); + WxMpProperties.StorageType type = storage.getType(); + + if (type == WxMpProperties.StorageType.redis) { + return getWxMpInRedisConfigStorage(); + } + return getWxMpInMemoryConfigStorage(); + } + + private WxMpInMemoryConfigStorage getWxMpInMemoryConfigStorage() { + WxMpInMemoryConfigStorage config = new WxMpInMemoryConfigStorage(); + setWxMpInfo(config); + return config; + } + + private WxMpInRedisConfigStorage getWxMpInRedisConfigStorage() { + JedisPool poolToUse = jedisPool; + if (poolToUse == null) { + poolToUse = getJedisPool(); + } + WxMpInRedisConfigStorage config = new WxMpInRedisConfigStorage(poolToUse); + setWxMpInfo(config); + return config; + } + + private void setWxMpInfo(WxMpInMemoryConfigStorage config) { + config.setAppId(properties.getAppId()); + config.setSecret(properties.getSecret()); + config.setToken(properties.getToken()); + config.setAesKey(properties.getAesKey()); + } + + private JedisPool getJedisPool() { + WxMpProperties.ConfigStorage storage = properties.getConfigStorage(); + RedisProperties redis = storage.getRedis(); + + JedisPoolConfig config = new JedisPoolConfig(); + if (redis.getMaxActive() != null) { + config.setMaxTotal(redis.getMaxActive()); + } + if (redis.getMaxIdle() != null) { + config.setMaxIdle(redis.getMaxIdle()); + } + if (redis.getMaxWaitMillis() != null) { + config.setMaxWaitMillis(redis.getMaxWaitMillis()); + } + if (redis.getMinIdle() != null) { + config.setMinIdle(redis.getMinIdle()); + } + config.setTestOnBorrow(true); + config.setTestWhileIdle(true); + + JedisPool pool = new JedisPool(config, redis.getHost(), redis.getPort(), + redis.getTimeout(), redis.getPassword(), redis.getDatabase()); + return pool; + } +} diff --git a/starters/wx-java-mp-starter/src/main/resources/META-INF/spring.factories b/starters/wx-java-mp-starter/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000000..aaa17f7676 --- /dev/null +++ b/starters/wx-java-mp-starter/src/main/resources/META-INF/spring.factories @@ -0,0 +1 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.binarywang.spring.starter.wxjava.mp.WxMpAutoConfiguration \ No newline at end of file diff --git a/starters/wx-java-pay-starter/README.md b/starters/wx-java-pay-starter/README.md new file mode 100644 index 0000000000..c987216147 --- /dev/null +++ b/starters/wx-java-pay-starter/README.md @@ -0,0 +1,27 @@ +# 使用说明 +1. 在自己的Spring Boot项目里,引入maven依赖 +```xml + + com.github.binarywang + wx-java-pay-starter + ${version} + + ``` +2. 添加配置(application.yml) +```yml +wx: + pay: + appId: wx5b69c56ac01ed858 + mchId: 1462547202 + mchKey: OGL9fvig9y2HrXrQ86tM4jTwyv4ja6G5 + subAppId: + subMchId: + keyPath: +``` + + + + + + + diff --git a/starters/wx-java-pay-starter/pom.xml b/starters/wx-java-pay-starter/pom.xml new file mode 100644 index 0000000000..eba398b454 --- /dev/null +++ b/starters/wx-java-pay-starter/pom.xml @@ -0,0 +1,68 @@ + + + + wx-java + com.github.binarywang + 3.4.0 + ../../ + + 4.0.0 + + wx-java-pay-starter + WxJava - Spring Boot Starter for WxPay + 微信支付开发的Spring Boot Starter + + + 2.1.4.RELEASE + + + + + org.springframework.boot + spring-boot-autoconfigure + ${spring.boot.version} + + + org.springframework.boot + spring-boot-configuration-processor + ${spring.boot.version} + true + + + org.projectlombok + lombok + provided + + + com.github.binarywang + weixin-java-pay + ${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/starters/wx-java-pay-starter/src/main/java/com/binarywang/spring/starter/wxjava/pay/config/WxPayAutoConfiguration.java b/starters/wx-java-pay-starter/src/main/java/com/binarywang/spring/starter/wxjava/pay/config/WxPayAutoConfiguration.java new file mode 100644 index 0000000000..43b2114e6e --- /dev/null +++ b/starters/wx-java-pay-starter/src/main/java/com/binarywang/spring/starter/wxjava/pay/config/WxPayAutoConfiguration.java @@ -0,0 +1,57 @@ +package com.binarywang.spring.starter.wxjava.pay.config; + +import com.binarywang.spring.starter.wxjava.pay.properties.WxPayProperties; +import com.github.binarywang.wxpay.config.WxPayConfig; +import com.github.binarywang.wxpay.service.WxPayService; +import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + *
+ *  微信支付自动配置
+ *  Created by BinaryWang on 2019/4/17.
+ * 
+ * + * @author Binary Wang + */ +@Configuration +@EnableConfigurationProperties(WxPayProperties.class) +@ConditionalOnClass(WxPayService.class) +@ConditionalOnProperty(prefix = "wx.pay", value = "enabled", matchIfMissing = true) +public class WxPayAutoConfiguration { + private WxPayProperties properties; + + @Autowired + public WxPayAutoConfiguration(WxPayProperties properties) { + this.properties = properties; + } + + /** + * 构造微信支付服务对象. + * + * @return 微信支付service + */ + @Bean + @ConditionalOnMissingBean(WxPayService.class) + public WxPayService wxPayService() { + final WxPayServiceImpl wxPayService = new WxPayServiceImpl(); + WxPayConfig payConfig = new WxPayConfig(); + payConfig.setAppId(StringUtils.trimToNull(this.properties.getAppId())); + payConfig.setMchId(StringUtils.trimToNull(this.properties.getMchId())); + payConfig.setMchKey(StringUtils.trimToNull(this.properties.getMchKey())); + payConfig.setSubAppId(StringUtils.trimToNull(this.properties.getSubAppId())); + payConfig.setSubMchId(StringUtils.trimToNull(this.properties.getSubMchId())); + payConfig.setKeyPath(StringUtils.trimToNull(this.properties.getKeyPath())); + + wxPayService.setConfig(payConfig); + return wxPayService; + } + +} diff --git a/starters/wx-java-pay-starter/src/main/java/com/binarywang/spring/starter/wxjava/pay/properties/WxPayProperties.java b/starters/wx-java-pay-starter/src/main/java/com/binarywang/spring/starter/wxjava/pay/properties/WxPayProperties.java new file mode 100644 index 0000000000..fe8a215650 --- /dev/null +++ b/starters/wx-java-pay-starter/src/main/java/com/binarywang/spring/starter/wxjava/pay/properties/WxPayProperties.java @@ -0,0 +1,46 @@ +package com.binarywang.spring.starter.wxjava.pay.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + *
+ *  微信支付属性配置类
+ * Created by Binary Wang on 2019/4/17.
+ * 
+ * + * @author Binary Wang + */ +@Data +@ConfigurationProperties(prefix = "wx.pay") +public class WxPayProperties { + /** + * 设置微信公众号或者小程序等的appid. + */ + private String appId; + + /** + * 微信支付商户号. + */ + private String mchId; + + /** + * 微信支付商户密钥. + */ + private String mchKey; + + /** + * 服务商模式下的子商户公众账号ID,普通模式请不要配置,请在配置文件中将对应项删除. + */ + private String subAppId; + + /** + * 服务商模式下的子商户号,普通模式请不要配置,最好是请在配置文件中将对应项删除. + */ + private String subMchId; + + /** + * apiclient_cert.p12文件的绝对路径,或者如果放在项目中,请以classpath:开头指定. + */ + private String keyPath; +} diff --git a/starters/wx-java-pay-starter/src/main/resources/META-INF/spring.factories b/starters/wx-java-pay-starter/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000000..37fe6c20e4 --- /dev/null +++ b/starters/wx-java-pay-starter/src/main/resources/META-INF/spring.factories @@ -0,0 +1 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.binarywang.spring.starter.wxjava.pay.config.WxPayAutoConfiguration diff --git a/weixin-java-common/pom.xml b/weixin-java-common/pom.xml index 152c7dabfe..7e5f54fefc 100644 --- a/weixin-java-common/pom.xml +++ b/weixin-java-common/pom.xml @@ -1,17 +1,16 @@ - 4.0.0 com.github.binarywang wx-java - 3.3.0 + 3.4.0 weixin-java-common - WxJava - Common + WxJava - Common Java SDK 微信开发Java SDK公共模块 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 c4bac2a83c..9af7811c82 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 @@ -62,6 +62,11 @@ public static class KefuMsgType { * 图文消息(点击跳转到图文消息页面). */ public static final String MPNEWS = "mpnews"; + /** + * markdown消息. + * (目前仅支持markdown语法的子集,微工作台(原企业号)不支持展示markdown消息) + */ + public static final String MARKDOWN = "markdown"; /** * 发送文件(CP专用). */ @@ -83,6 +88,11 @@ public static class KefuMsgType { * 小程序卡片(要求小程序与公众号已关联) */ public static final String MINIPROGRAMPAGE = "miniprogrampage"; + + /** + * 任务卡片消息 + */ + public static final String TASKCARD = "taskcard"; } /** 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 cded0c9846..8124148110 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 @@ -39,6 +39,7 @@ protected DocumentBuilder initialValue() { try { final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setExpandEntityReferences(false); + factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); return factory.newDocumentBuilder(); } catch (ParserConfigurationException exc) { throw new IllegalArgumentException(exc); diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheMediaDownloadRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheMediaDownloadRequestExecutor.java index 779a844aa9..da8f435c87 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheMediaDownloadRequestExecutor.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheMediaDownloadRequestExecutor.java @@ -57,7 +57,7 @@ public File execute(String uri, String queryParam) throws WxErrorException, IOEx String fileName = new HttpResponseProxy(response).getFileName(); if (StringUtils.isBlank(fileName)) { - return null; + fileName = String.valueOf(System.currentTimeMillis()); } return FileUtils.createTmpFile(inputStream, FilenameUtils.getBaseName(fileName), FilenameUtils.getExtension(fileName), diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/res/StringManager.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/res/StringManager.java index e3f4aa05e9..e5bdb38804 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/res/StringManager.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/res/StringManager.java @@ -19,6 +19,7 @@ import java.text.MessageFormat; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; /** * An internationalization / localization helper class which reduces @@ -46,7 +47,7 @@ */ public class StringManager { - private static final Map> MANAGERS = new Hashtable<>(); + private static final Map> MANAGERS = new ConcurrentHashMap<>(); private static int LOCALE_CACHE_SIZE = 10; /** * The ResourceBundle for this StringManager. 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 06f9d7ba28..82cfa9d2d6 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 @@ -40,6 +40,7 @@ public void testNormal() throws ParserConfigurationException, SAXException, IOEx DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); documentBuilderFactory.setExpandEntityReferences(false); + documentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); Document document = documentBuilder.parse(new InputSource(new StringReader(encryptedXml))); @@ -83,6 +84,8 @@ public void testValidateSignatureError() throws ParserConfigurationException, SA String afterEncrpt = pc.encrypt(this.replyMsg); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setExpandEntityReferences(false); + dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + DocumentBuilder db = dbf.newDocumentBuilder(); StringReader sr = new StringReader(afterEncrpt); InputSource is = new InputSource(sr); diff --git a/weixin-java-cp/pom.xml b/weixin-java-cp/pom.xml index a1808f2d50..09208eb329 100644 --- a/weixin-java-cp/pom.xml +++ b/weixin-java-cp/pom.xml @@ -7,11 +7,11 @@ com.github.binarywang wx-java - 3.3.0 + 3.4.0 weixin-java-cp - WxJava - CP + WxJava - CP Java SDK 微信企业号/企业微信 Java SDK diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/WxCpConsts.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/WxCpConsts.java index 4eb52a903d..87892eccdd 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/WxCpConsts.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/WxCpConsts.java @@ -84,6 +84,11 @@ public static class EventType { */ public static final String LOCATION_SELECT = "location_select"; + /** + * 任务卡片事件推送. + */ + public static final String TASKCARD_CLICK = "taskcard_click"; + } /** @@ -126,4 +131,46 @@ public static class ContactChangeType { public static final String UPDATE_TAG = "update_tag"; } + + /** + * 应用推送消息的消息类型. + */ + public static class AppChatMsgType { + /** + * 文本消息. + */ + public static final String TEXT = "text"; + /** + * 图片消息. + */ + public static final String IMAGE = "image"; + /** + * 语音消息. + */ + public static final String VOICE = "voice"; + /** + * 视频消息. + */ + public static final String VIDEO = "video"; + /** + * 发送文件(CP专用). + */ + public static final String FILE = "file"; + /** + * 文本卡片消息(CP专用). + */ + public static final String TEXTCARD = "textcard"; + /** + * 图文消息(点击跳转到外链). + */ + public static final String NEWS = "news"; + /** + * 图文消息(点击跳转到图文消息页面). + */ + public static final String MPNEWS = "mpnews"; + /** + * markdown消息. + */ + public static final String MARKDOWN = "markdown"; + } } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpChatService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpChatService.java index 2c24701dda..95f747e356 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpChatService.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpChatService.java @@ -3,46 +3,75 @@ import java.util.List; import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.cp.bean.WxCpAppChatMessage; import me.chanjar.weixin.cp.bean.WxCpChat; /** - * 群聊服务 + * 群聊服务. * * @author gaigeshen */ public interface WxCpChatService { + String APPCHAT_CREATE = "https://qyapi.weixin.qq.com/cgi-bin/appchat/create"; + String APPCHAT_UPDATE = "https://qyapi.weixin.qq.com/cgi-bin/appchat/update"; + String APPCHAT_GET_CHATID = "https://qyapi.weixin.qq.com/cgi-bin/appchat/get?chatid="; /** - * 创建群聊会话,注意:刚创建的群,如果没有下发消息,在企业微信不会出现该群。 + * 创建群聊会话,注意:刚创建的群,如果没有下发消息,在企业微信不会出现该群. * - * @param name 群聊名,最多50个utf8字符,超过将截断 - * @param owner 指定群主的id。如果不指定,系统会随机从userlist中选一人作为群主 - * @param users 群成员id列表。至少2人,至多500人 + * @param name 群聊名,最多50个utf8字符,超过将截断 + * @param owner 指定群主的id。如果不指定,系统会随机从userlist中选一人作为群主 + * @param users 群成员id列表。至少2人,至多500人 * @param chatId 群聊的唯一标志,不能与已有的群重复;字符串类型,最长32个字符。只允许字符0-9及字母a-zA-Z。如果不填,系统会随机生成群id - * @return 创建群聊会话的结果,群聊的唯一标志 + * @return 创建的群聊会话chatId * @throws WxErrorException 发生异常 */ String chatCreate(String name, String owner, List users, String chatId) throws WxErrorException; - + /** - * 修改群聊会话 - * - * @param chatId 群聊id - * @param name 新的群聊名。若不需更新,请忽略此参数(null or empty)。最多50个utf8字符,超过将截断 - * @param owner 新群主的id。若不需更新,请忽略此参数(null or empty) - * @param usersToAdd 添加成员的id列表,若不需要更新,则传递空对象或者空集合 + * chatCreate 同名方法 + */ + String create(String name, String owner, List users, String chatId) throws WxErrorException; + + /** + * 修改群聊会话. + * + * @param chatId 群聊id + * @param name 新的群聊名。若不需更新,请忽略此参数(null or empty)。最多50个utf8字符,超过将截断 + * @param owner 新群主的id。若不需更新,请忽略此参数(null or empty) + * @param usersToAdd 添加成员的id列表,若不需要更新,则传递空对象或者空集合 * @param usersToDelete 踢出成员的id列表,若不需要更新,则传递空对象或者空集合 * @throws WxErrorException 发生异常 */ void chatUpdate(String chatId, String name, String owner, List usersToAdd, List usersToDelete) throws WxErrorException; /** - * 获取群聊会话 - * + * chatUpdate 同名方法 + */ + void update(String chatId, String name, String owner, List usersToAdd, List usersToDelete) throws WxErrorException; + + /** + * 获取群聊会话. + * * @param chatId 群聊编号 * @return 群聊会话 * @throws WxErrorException 发生异常 */ WxCpChat chatGet(String chatId) throws WxErrorException; - + + /** + * chatGet 同名方法 + */ + WxCpChat get(String chatId) throws WxErrorException; + + /** + * 应用支持推送文本、图片、视频、文件、图文等类型. + * 请求方式: POST(HTTPS) + * 请求地址: https://qyapi.weixin.qq.com/cgi-bin/appchat/send?access_token=ACCESS_TOKEN + * 文档地址:https://work.weixin.qq.com/api/doc#90000/90135/90248 + * + * @param message 要发送的消息内容对象 + */ + void sendMsg(WxCpAppChatMessage message) throws WxErrorException; + } 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 8a8ca054d3..82e571ac5b 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 @@ -26,7 +26,7 @@ public interface WxCpDepartmentService { * @return 部门id * @throws WxErrorException 异常 */ - Integer create(WxCpDepart depart) throws WxErrorException; + Long create(WxCpDepart depart) 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
new file mode 100644
index 0000000000..e8cf874eb6
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOAService.java
@@ -0,0 +1,66 @@
+package me.chanjar.weixin.cp.api;
+
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.cp.bean.WxCpApprovalDataResult;
+import me.chanjar.weixin.cp.bean.WxCpCheckinData;
+import me.chanjar.weixin.cp.bean.WxCpCheckinOption;
+import me.chanjar.weixin.cp.bean.WxCpDialRecord;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author Element
+ * @Package me.chanjar.weixin.cp.api
+ * @date 2019-04-06 10:52
+ * @Description: 
+ *     企业微信OA相关接口
+ *
+ * 
+ */ +public interface WxCpOAService { + + /** + *
+   *     获取打卡数据
+   *     API doc : https://work.weixin.qq.com/api/doc#90000/90135/90262
+   * 
+ * + * @param openCheckinDataType 打卡类型。1:上下班打卡;2:外出打卡;3:全部打卡 + * @param starttime 获取打卡记录的开始时间 + * @param endtime 获取打卡记录的结束时间 + * @param userIdList 需要获取打卡记录的用户列表 + */ + List getCheckinData(Integer openCheckinDataType, Date starttime, Date endtime, List userIdList) throws WxErrorException; + + /** + *
+   *     获取打卡规则
+   *     API doc : https://work.weixin.qq.com/api/doc#90000/90135/90263
+   * 
+ * + * @param datetime 需要获取规则的当天日期 + * @param userIdList 需要获取打卡规则的用户列表 + * @return + * @throws WxErrorException + */ + List getCheckinOption(Date datetime, List userIdList) throws WxErrorException; + + /** + *
+   *     获取审批数据
+   *     通过本接口来获取公司一段时间内的审批记录。一次拉取调用最多拉取10000个审批记录,可以通过多次拉取的方式来满足需求,但调用频率不可超过600次/分。
+   *     API doc : https://work.weixin.qq.com/api/doc#90000/90135/91530
+   * 
+ * + * @param starttime 获取审批记录的开始时间 + * @param endtime 获取审批记录的结束时间 + * @param nextSpnum 第一个拉取的审批单号,不填从该时间段的第一个审批单拉取 + * @return + * @throws WxErrorException + */ + WxCpApprovalDataResult getApprovalData(Date starttime, Date endtime, Long nextSpnum) throws WxErrorException; + + 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/api/WxCpService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java index afda991b55..364723db87 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 @@ -7,6 +7,7 @@ import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; import me.chanjar.weixin.common.util.http.RequestExecutor; import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.cp.bean.WxCpMaJsCode2SessionResult; import me.chanjar.weixin.cp.bean.WxCpMessage; import me.chanjar.weixin.cp.bean.WxCpMessageSendResult; import me.chanjar.weixin.cp.config.WxCpConfigStorage; @@ -16,6 +17,15 @@ * @author chanjaster */ public interface WxCpService { + String GET_JSAPI_TICKET = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket"; + String GET_AGENT_CONFIG_TICKET = "https://qyapi.weixin.qq.com/cgi-bin/ticket/get?&type=agent_config"; + String MESSAGE_SEND = "https://qyapi.weixin.qq.com/cgi-bin/message/send"; + String GET_CALLBACK_IP = "https://qyapi.weixin.qq.com/cgi-bin/getcallbackip"; + String BATCH_REPLACE_PARTY = "https://qyapi.weixin.qq.com/cgi-bin/batch/replaceparty"; + String BATCH_REPLACE_USER = "https://qyapi.weixin.qq.com/cgi-bin/batch/replaceuser"; + String BATCH_GET_RESULT = "https://qyapi.weixin.qq.com/cgi-bin/batch/getresult?jobid="; + String JSCODE_TO_SESSION_URL = "https://qyapi.weixin.qq.com/cgi-bin/miniprogram/jscode2session"; + /** *
    * 验证推送过来的消息的正确性
@@ -68,6 +78,33 @@ public interface WxCpService {
    */
   String getJsapiTicket(boolean forceRefresh) throws WxErrorException;
 
+  /**
+   * 获得jsapi_ticket,不强制刷新jsapi_ticket
+   * 应用的jsapi_ticket用于计算agentConfig(参见“通过agentConfig注入应用的权限”)的签名,签名计算方法与上述介绍的config的签名算法完全相同,但需要注意以下区别:
+   *
+   * 签名的jsapi_ticket必须使用以下接口获取。且必须用wx.agentConfig中的agentid对应的应用secret去获取access_token。
+   * 签名用的noncestr和timestamp必须与wx.agentConfig中的nonceStr和timestamp相同。
+   * @see #getJsapiTicket(boolean)
+   */
+  String getAgentJsapiTicket() throws WxErrorException;
+
+  /**
+   * 
+   * 获取应用的jsapi_ticket
+   * 应用的jsapi_ticket用于计算agentConfig(参见“通过agentConfig注入应用的权限”)的签名,签名计算方法与上述介绍的config的签名算法完全相同,但需要注意以下区别:
+   *
+   * 签名的jsapi_ticket必须使用以下接口获取。且必须用wx.agentConfig中的agentid对应的应用secret去获取access_token。
+   * 签名用的noncestr和timestamp必须与wx.agentConfig中的nonceStr和timestamp相同。
+   *
+   * 获得时会检查jsapiToken是否过期,如果过期了,那么就刷新一下,否则就什么都不干
+   *
+   * 详情请见:https://work.weixin.qq.com/api/doc#10029/%E8%8E%B7%E5%8F%96%E5%BA%94%E7%94%A8%E7%9A%84jsapi_ticket
+   * 
+ * + * @param forceRefresh 强制刷新 + */ + String getAgentJsapiTicket(boolean forceRefresh) throws WxErrorException; + /** *
    * 创建调用jsapi时所需要的签名
@@ -89,6 +126,13 @@ public interface WxCpService {
    */
   WxCpMessageSendResult messageSend(WxCpMessage message) throws WxErrorException;
 
+  /**
+   * 小程序登录凭证校验
+   *
+   * @param jsCode 登录时获取的 code
+   */
+  WxCpMaJsCode2SessionResult jsCode2Session(String jsCode) throws WxErrorException;
+
   /**
    * 
    * 获取微信服务器的ip段
@@ -165,6 +209,13 @@ public interface WxCpService {
    */
   WxSession getSession(String id, boolean create);
 
+  /**
+   * 获取WxSessionManager 对象
+   *
+   * @return WxSessionManager
+   */
+  WxSessionManager getSessionManager();
+  
   /**
    * 
    * 设置WxSessionManager,只有当需要使用个性化的WxSessionManager的时候才需要调用此方法,
@@ -250,8 +301,17 @@ public interface WxCpService {
    */
   WxCpChatService getChatService();
 
+  /**
+   * 获取任务卡片服务
+   *
+   * @return 任务卡片服务
+   */
+  WxCpTaskCardService getTaskCardService();
+
   WxCpAgentService getAgentService();
 
+  WxCpOAService getOAService();
+
   /**
    * http请求对象
    */
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpTaskCardService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpTaskCardService.java
new file mode 100644
index 0000000000..038c2dd3bf
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpTaskCardService.java
@@ -0,0 +1,30 @@
+package me.chanjar.weixin.cp.api;
+
+import me.chanjar.weixin.common.error.WxErrorException;
+
+import java.util.List;
+
+/**
+ * 
+ *  任务卡片管理接口.
+ *  Created by Jeff on 2019-05-16.
+ * 
+ * + * @author Jeff + * @date 2019-05-16 + */ +public interface WxCpTaskCardService { + /** + *
+   * 更新任务卡片消息状态
+   * 详情请见: https://work.weixin.qq.com/api/doc#90000/90135/91579
+   *
+   * 注意: 这个方法使用WxCpConfigStorage里的agentId
+   * 
+ * + * @param userIds 企业的成员ID列表 + * @param taskId 任务卡片ID + * @param clickedKey 已点击按钮的Key + */ + void update(List userIds, String taskId, String clickedKey) throws WxErrorException; +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceAbstractImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImpl.java similarity index 79% rename from weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceAbstractImpl.java rename to weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImpl.java index f0c15109b9..3eb99b79d2 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceAbstractImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImpl.java @@ -1,16 +1,10 @@ package me.chanjar.weixin.cp.api.impl; -import java.io.File; -import java.io.IOException; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - +import com.google.common.base.Joiner; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; - import me.chanjar.weixin.common.bean.WxJsapiSignature; import me.chanjar.weixin.common.error.WxError; import me.chanjar.weixin.common.error.WxErrorException; @@ -24,20 +18,23 @@ import me.chanjar.weixin.common.util.http.RequestHttp; import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor; -import me.chanjar.weixin.cp.api.WxCpAgentService; -import me.chanjar.weixin.cp.api.WxCpChatService; -import me.chanjar.weixin.cp.api.WxCpDepartmentService; -import me.chanjar.weixin.cp.api.WxCpMediaService; -import me.chanjar.weixin.cp.api.WxCpMenuService; -import me.chanjar.weixin.cp.api.WxCpOAuth2Service; -import me.chanjar.weixin.cp.api.WxCpService; -import me.chanjar.weixin.cp.api.WxCpTagService; -import me.chanjar.weixin.cp.api.WxCpUserService; +import me.chanjar.weixin.cp.api.*; +import me.chanjar.weixin.cp.bean.WxCpMaJsCode2SessionResult; import me.chanjar.weixin.cp.bean.WxCpMessage; import me.chanjar.weixin.cp.bean.WxCpMessageSendResult; import me.chanjar.weixin.cp.config.WxCpConfigStorage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; -public abstract class WxCpServiceAbstractImpl implements WxCpService, RequestHttp { +/** + * @author chanjarster + */ +public abstract class BaseWxCpServiceImpl implements WxCpService, RequestHttp { protected final Logger log = LoggerFactory.getLogger(this.getClass()); private WxCpUserService userService = new WxCpUserServiceImpl(this); @@ -48,6 +45,8 @@ public abstract class WxCpServiceAbstractImpl implements WxCpService, Requ private WxCpOAuth2Service oauth2Service = new WxCpOAuth2ServiceImpl(this); private WxCpTagService tagService = new WxCpTagServiceImpl(this); private WxCpAgentService agentService = new WxCpAgentServiceImpl(this); + private WxCpOAService oaService = new WxCpOAServiceImpl(this); + private WxCpTaskCardService taskCardService = new WxCpTaskCardServiceImpl(this); /** * 全局的是否正在刷新access token的锁 @@ -59,14 +58,19 @@ public abstract class WxCpServiceAbstractImpl implements WxCpService, Requ */ protected final Object globalJsapiTicketRefreshLock = new Object(); + /** + * 全局的是否正在刷新agent的jsapi_ticket的锁 + */ + protected final Object globalAgentJsapiTicketRefreshLock = new Object(); + protected WxCpConfigStorage configStorage; + private WxSessionManager sessionManager = new StandardSessionManager(); - protected WxSessionManager sessionManager = new StandardSessionManager(); /** * 临时文件目录 */ - protected File tmpDirFile; + private File tmpDirFile; private int retrySleepMillis = 1000; private int maxRetryTimes = 5; @@ -86,6 +90,30 @@ public String getAccessToken() throws WxErrorException { return getAccessToken(false); } + @Override + public String getAgentJsapiTicket() throws WxErrorException { + return this.getAgentJsapiTicket(false); + } + + @Override + public String getAgentJsapiTicket(boolean forceRefresh) throws WxErrorException { + if (forceRefresh) { + this.configStorage.expireAgentJsapiTicket(); + } + + if (this.configStorage.isAgentJsapiTicketExpired()) { + synchronized (this.globalAgentJsapiTicketRefreshLock) { + if (this.configStorage.isAgentJsapiTicketExpired()) { + String responseContent = this.get(WxCpService.GET_AGENT_CONFIG_TICKET, null); + JsonObject jsonObject = new JsonParser().parse(responseContent).getAsJsonObject(); + this.configStorage.updateAgentJsapiTicket(jsonObject.get("ticket").getAsString(), + jsonObject.get("expires_in").getAsInt()); + } + } + } + + return this.configStorage.getAgentJsapiTicket(); + } @Override public String getJsapiTicket() throws WxErrorException { @@ -97,20 +125,18 @@ public String getJsapiTicket(boolean forceRefresh) throws WxErrorException { if (forceRefresh) { this.configStorage.expireJsapiTicket(); } + if (this.configStorage.isJsapiTicketExpired()) { synchronized (this.globalJsapiTicketRefreshLock) { if (this.configStorage.isJsapiTicketExpired()) { - String url = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket"; - String responseContent = execute(SimpleGetRequestExecutor.create(this), url, null); - JsonElement tmpJsonElement = new JsonParser().parse(responseContent); - JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject(); - String jsapiTicket = tmpJsonObject.get("ticket").getAsString(); - int expiresInSeconds = tmpJsonObject.get("expires_in").getAsInt(); - this.configStorage.updateJsapiTicket(jsapiTicket, - expiresInSeconds); + String responseContent = this.get(WxCpService.GET_JSAPI_TICKET, null); + JsonObject tmpJsonObject = new JsonParser().parse(responseContent).getAsJsonObject(); + this.configStorage.updateJsapiTicket(tmpJsonObject.get("ticket").getAsString(), + tmpJsonObject.get("expires_in").getAsInt()); } } } + return this.configStorage.getJsapiTicket(); } @@ -139,18 +165,27 @@ public WxJsapiSignature createJsapiSignature(String url) throws WxErrorException @Override public WxCpMessageSendResult messageSend(WxCpMessage message) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/message/send"; Integer agentId = message.getAgentId(); - if(null == agentId){ + if (null == agentId) { message.setAgentId(this.getWxCpConfigStorage().getAgentId()); } - return WxCpMessageSendResult.fromJson(this.post(url, message.toJson())); + + return WxCpMessageSendResult.fromJson(this.post(WxCpService.MESSAGE_SEND, message.toJson())); + } + + @Override + public WxCpMaJsCode2SessionResult jsCode2Session(String jsCode) throws WxErrorException { + Map params = new HashMap<>(2); + params.put("js_code", jsCode); + params.put("grant_type", "authorization_code"); + + String result = this.get(JSCODE_TO_SESSION_URL, Joiner.on("&").withKeyValueSeparator("=").join(params)); + return WxCpMaJsCode2SessionResult.fromJson(result); } @Override public String[] getCallbackIp() throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/getcallbackip"; - String responseContent = get(url, null); + String responseContent = get(WxCpService.GET_CALLBACK_IP, null); JsonElement tmpJsonElement = new JsonParser().parse(responseContent); JsonArray jsonArray = tmpJsonElement.getAsJsonObject().get("ip_list").getAsJsonArray(); String[] ips = new String[jsonArray.size()]; @@ -171,7 +206,7 @@ public String post(String url, String postData) throws WxErrorException { } /** - * 向微信端发送请求,在这里执行的策略是当发生access_token过期时才去刷新,然后重新执行请求,而不是全局定时请求 + * 向微信端发送请求,在这里执行的策略是当发生access_token过期时才去刷新,然后重新执行请求,而不是全局定时请求. */ @Override public T execute(RequestExecutor executor, String uri, E data) throws WxErrorException { @@ -285,25 +320,28 @@ public void setSessionManager(WxSessionManager sessionManager) { this.sessionManager = sessionManager; } + @Override + public WxSessionManager getSessionManager() { + return this.sessionManager; + } + @Override public String replaceParty(String mediaId) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/batch/replaceparty"; JsonObject jsonObject = new JsonObject(); jsonObject.addProperty("media_id", mediaId); - return post(url, jsonObject.toString()); + return post(WxCpService.BATCH_REPLACE_PARTY, jsonObject.toString()); } @Override public String replaceUser(String mediaId) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/batch/replaceuser"; JsonObject jsonObject = new JsonObject(); jsonObject.addProperty("media_id", mediaId); - return post(url, jsonObject.toString()); + return post(WxCpService.BATCH_REPLACE_USER, jsonObject.toString()); } @Override public String getTaskResult(String joinId) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/batch/getresult?jobid=" + joinId; + String url = WxCpService.BATCH_GET_RESULT + joinId; return get(url, null); } @@ -350,6 +388,16 @@ public WxCpChatService getChatService() { return chatService; } + @Override + public WxCpOAService getOAService() { + return oaService; + } + + @Override + public WxCpTaskCardService getTaskCardService() { + return taskCardService; + } + @Override public RequestHttp getRequestHttp() { return this; diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpChatServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpChatServiceImpl.java index b8e894fb9b..05d298c9ad 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpChatServiceImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpChatServiceImpl.java @@ -1,36 +1,35 @@ package me.chanjar.weixin.cp.api.impl; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.apache.commons.lang3.StringUtils; - import com.google.gson.JsonParser; - import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.util.json.WxGsonBuilder; import me.chanjar.weixin.cp.api.WxCpChatService; import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.bean.WxCpAppChatMessage; import me.chanjar.weixin.cp.bean.WxCpChat; import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; +import org.apache.commons.lang3.StringUtils; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; /** - * 群聊服务实现 + * 群聊服务实现. * * @author gaigeshen */ -public class WxCpChatServiceImpl implements WxCpChatService { +public class WxCpChatServiceImpl implements WxCpChatService { + private static final JsonParser JSON_PARSER = new JsonParser(); + private final WxCpService cpService; - private final WxCpService internalService; - /** - * 创建群聊服务实现的实例 - * - * @param internalService 企业微信的服务 + * 创建群聊服务实现的实例. + * + * @param cpService 企业微信的服务 */ - public WxCpChatServiceImpl(WxCpService internalService) { - this.internalService = internalService; + WxCpChatServiceImpl(WxCpService cpService) { + this.cpService = cpService; } @Override @@ -48,12 +47,18 @@ public String chatCreate(String name, String owner, List users, String c if (StringUtils.isNotBlank(chatId)) { data.put("chatid", chatId); } - String result = internalService.post("https://qyapi.weixin.qq.com/cgi-bin/appchat/create", WxGsonBuilder.create().toJson(data)); + String result = this.cpService.post(APPCHAT_CREATE, WxGsonBuilder.create().toJson(data)); return new JsonParser().parse(result).getAsJsonObject().get("chatid").getAsString(); } @Override - public void chatUpdate(String chatId, String name, String owner, List usersToAdd, List usersToDelete) throws WxErrorException { + public String create(String name, String owner, List users, String chatId) throws WxErrorException { + return chatCreate(name, owner, users, chatId); + } + + @Override + public void chatUpdate(String chatId, String name, String owner, List usersToAdd, List usersToDelete) + throws WxErrorException { Map data = new HashMap<>(5); if (StringUtils.isNotBlank(chatId)) { data.put("chatid", chatId); @@ -70,14 +75,30 @@ public void chatUpdate(String chatId, String name, String owner, List us if (usersToDelete != null && !usersToDelete.isEmpty()) { data.put("del_user_list", usersToDelete); } - internalService.post("https://qyapi.weixin.qq.com/cgi-bin/appchat/update", WxGsonBuilder.create().toJson(data)); + + this.cpService.post(APPCHAT_UPDATE, WxGsonBuilder.create().toJson(data)); + } + + @Override + public void update(String chatId, String name, String owner, List usersToAdd, List usersToDelete) throws WxErrorException { + chatUpdate(chatId, name, owner, usersToAdd, usersToDelete); } @Override public WxCpChat chatGet(String chatId) throws WxErrorException { - String result = internalService.get("https://qyapi.weixin.qq.com/cgi-bin/appchat/get?chatid=" + chatId, null); - return WxCpGsonBuilder.create().fromJson( - new JsonParser().parse(result).getAsJsonObject().getAsJsonObject("chat_info").toString(), WxCpChat.class); + String result = this.cpService.get(APPCHAT_GET_CHATID + chatId, null); + return WxCpGsonBuilder.create() + .fromJson(JSON_PARSER.parse(result).getAsJsonObject().getAsJsonObject("chat_info").toString(), WxCpChat.class); + } + + @Override + public WxCpChat get(String chatId) throws WxErrorException { + return chatGet(chatId); + } + + @Override + public void sendMsg(WxCpAppChatMessage message) throws WxErrorException { + this.cpService.post("https://qyapi.weixin.qq.com/cgi-bin/appchat/send", message.toJson()); } } 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 5d623b2ad4..481115fa51 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 @@ -28,11 +28,11 @@ public WxCpDepartmentServiceImpl(WxCpService mainService) { } @Override - public Integer create(WxCpDepart depart) throws WxErrorException { + public Long create(WxCpDepart depart) throws WxErrorException { String url = "https://qyapi.weixin.qq.com/cgi-bin/department/create"; String responseContent = this.mainService.post(url, depart.toJson()); JsonElement tmpJsonElement = new JsonParser().parse(responseContent); - return GsonHelper.getAsInteger(tmpJsonElement.getAsJsonObject().get("id")); + return GsonHelper.getAsLong(tmpJsonElement.getAsJsonObject().get("id")); } @Override 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 new file mode 100644 index 0000000000..2d1ca6ab0b --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpOAServiceImpl.java @@ -0,0 +1,165 @@ +package me.chanjar.weixin.cp.api.impl; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.reflect.TypeToken; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.cp.api.WxCpOAService; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.bean.WxCpApprovalDataResult; +import me.chanjar.weixin.cp.bean.WxCpCheckinData; +import me.chanjar.weixin.cp.bean.WxCpCheckinOption; +import me.chanjar.weixin.cp.bean.WxCpDialRecord; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.util.Date; +import java.util.List; + +/** + * @author Element + * @Package me.chanjar.weixin.cp.api.impl + * @date 2019-04-06 11:20 + * @Description: TODO + */ +public class WxCpOAServiceImpl implements WxCpOAService { + + private WxCpService mainService; + + public WxCpOAServiceImpl(WxCpService mainService) { + this.mainService = mainService; + } + + @Override + public List getCheckinData(Integer openCheckinDataType, Date starttime, Date endtime, List userIdList) throws WxErrorException { + + if (starttime == null || endtime == null) { + throw new RuntimeException("starttime and endtime can't be null"); + } + + if (userIdList == null || userIdList.size() > 100) { + throw new RuntimeException("用户列表不能为空,不超过100个,若用户超过100个,请分批获取"); + } + + long endtimestamp = endtime.getTime() / 1000L; + long starttimestamp = starttime.getTime() / 1000L; + + if (endtimestamp - starttimestamp < 0 || endtimestamp - starttimestamp >= 30 * 24 * 60 * 60) { + throw new RuntimeException("获取记录时间跨度不超过一个月"); + } + + String url = "https://qyapi.weixin.qq.com/cgi-bin/checkin/getcheckindata"; + + JsonObject jsonObject = new JsonObject(); + JsonArray jsonArray = new JsonArray(); + + jsonObject.addProperty("opencheckindatatype", openCheckinDataType); + jsonObject.addProperty("starttime", starttimestamp); + jsonObject.addProperty("endtime", endtimestamp); + + for (String userid : userIdList) { + jsonArray.add(userid); + } + + jsonObject.add("useridlist", jsonArray); + + String responseContent = this.mainService.post(url, jsonObject.toString()); + JsonElement tmpJsonElement = new JsonParser().parse(responseContent); + return WxCpGsonBuilder.create() + .fromJson( + tmpJsonElement.getAsJsonObject().get("checkindata"), + new TypeToken>() { + }.getType() + ); + } + + @Override + public List getCheckinOption(Date datetime, List userIdList) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/checkin/getcheckinoption"; + if (datetime == null) { + throw new RuntimeException("datetime can't be null"); + } + + if (userIdList == null || userIdList.size() > 100) { + throw new RuntimeException("用户列表不能为空,不超过100个,若用户超过100个,请分批获取"); + } + + JsonArray jsonArray = new JsonArray(); + for (String userid : userIdList) { + jsonArray.add(userid); + } + + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("datetime", datetime.getTime() / 1000L); + jsonObject.add("useridlist", jsonArray); + + String responseContent = this.mainService.post(url, jsonObject.toString()); + JsonElement tmpJsonElement = new JsonParser().parse(responseContent); + + return WxCpGsonBuilder.create() + .fromJson( + tmpJsonElement.getAsJsonObject().get("info"), + new TypeToken>() { + }.getType() + ); + } + + @Override + public WxCpApprovalDataResult getApprovalData(Date starttime, Date endtime, Long nextSpnum) throws WxErrorException { + + String url = "https://qyapi.weixin.qq.com/cgi-bin/corp/getapprovaldata"; + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("starttime", starttime.getTime() / 1000L); + jsonObject.addProperty("endtime", endtime.getTime() / 1000L); + if (nextSpnum != null) { + jsonObject.addProperty("next_spnum", nextSpnum); + } + + String responseContent = this.mainService.post(url, jsonObject.toString()); + return WxCpGsonBuilder.create().fromJson(responseContent, WxCpApprovalDataResult.class); + } + + @Override + public List getDialRecord(Date starttime, Date endtime, Integer offset, Integer limit) throws WxErrorException { + + String url = "https://qyapi.weixin.qq.com/cgi-bin/dial/get_dial_record"; + JsonObject jsonObject = new JsonObject(); + + if (offset == null) { + offset = 0; + } + + if (limit == null || limit <= 0) { + limit = 100; + } + + jsonObject.addProperty("offset", offset); + jsonObject.addProperty("limit", limit); + + if (starttime != null && endtime != null) { + + long endtimestamp = endtime.getTime() / 1000L; + long starttimestamp = starttime.getTime() / 1000L; + + if (endtimestamp - starttimestamp < 0 || endtimestamp - starttimestamp >= 30 * 24 * 60 * 60) { + throw new RuntimeException("受限于网络传输,起止时间的最大跨度为30天,如超过30天,则以结束时间为基准向前取30天进行查询"); + } + + jsonObject.addProperty("start_time", starttimestamp); + jsonObject.addProperty("end_time", endtimestamp); + + + } + + String responseContent = this.mainService.post(url, jsonObject.toString()); + JsonElement tmpJsonElement = new JsonParser().parse(responseContent); + + return WxCpGsonBuilder.create() + .fromJson( + tmpJsonElement.getAsJsonObject().get("record"), + new TypeToken>() { + }.getType() + ); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceApacheHttpClientImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceApacheHttpClientImpl.java index 7081272793..86e237168d 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceApacheHttpClientImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceApacheHttpClientImpl.java @@ -8,6 +8,7 @@ import me.chanjar.weixin.common.util.http.HttpType; import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder; +import me.chanjar.weixin.cp.api.WxCpOAService; import me.chanjar.weixin.cp.config.WxCpConfigStorage; import org.apache.http.HttpHost; import org.apache.http.client.config.RequestConfig; @@ -18,7 +19,7 @@ import java.io.IOException; -public class WxCpServiceApacheHttpClientImpl extends WxCpServiceAbstractImpl { +public class WxCpServiceApacheHttpClientImpl extends BaseWxCpServiceImpl { protected CloseableHttpClient httpClient; protected HttpHost httpProxy; @@ -39,37 +40,37 @@ public HttpType getRequestType() { @Override public String getAccessToken(boolean forceRefresh) throws WxErrorException { - if (this.configStorage.isAccessTokenExpired() || forceRefresh) { - synchronized (this.globalAccessTokenRefreshLock) { - if (this.configStorage.isAccessTokenExpired()) { - String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?" - + "&corpid=" + this.configStorage.getCorpId() - + "&corpsecret=" + this.configStorage.getCorpSecret(); - try { - HttpGet httpGet = new HttpGet(url); - if (this.httpProxy != null) { - RequestConfig config = RequestConfig.custom() - .setProxy(this.httpProxy).build(); - httpGet.setConfig(config); - } - String resultContent = null; - try (CloseableHttpClient httpclient = getRequestHttpClient(); - CloseableHttpResponse response = httpclient.execute(httpGet)) { - resultContent = new BasicResponseHandler().handleResponse(response); - } finally { - httpGet.releaseConnection(); - } - WxError error = WxError.fromJson(resultContent, WxType.CP); - if (error.getErrorCode() != 0) { - throw new WxErrorException(error); - } - WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); - this.configStorage.updateAccessToken( - accessToken.getAccessToken(), accessToken.getExpiresIn()); - } catch (IOException e) { - throw new RuntimeException(e); - } + if (!this.configStorage.isAccessTokenExpired() && !forceRefresh) { + return this.configStorage.getAccessToken(); + } + + synchronized (this.globalAccessTokenRefreshLock) { + String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?" + + "&corpid=" + this.configStorage.getCorpId() + + "&corpsecret=" + this.configStorage.getCorpSecret(); + try { + HttpGet httpGet = new HttpGet(url); + if (this.httpProxy != null) { + RequestConfig config = RequestConfig.custom() + .setProxy(this.httpProxy).build(); + httpGet.setConfig(config); + } + String resultContent; + try (CloseableHttpClient httpclient = getRequestHttpClient(); + CloseableHttpResponse response = httpclient.execute(httpGet)) { + resultContent = new BasicResponseHandler().handleResponse(response); + } finally { + httpGet.releaseConnection(); } + WxError error = WxError.fromJson(resultContent, WxType.CP); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + + WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); + this.configStorage.updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); + } catch (IOException e) { + throw new RuntimeException(e); } } return this.configStorage.getAccessToken(); diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceJoddHttpImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceJoddHttpImpl.java index 2d7e1b1c29..3603a59b17 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceJoddHttpImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceJoddHttpImpl.java @@ -8,7 +8,7 @@ import me.chanjar.weixin.common.util.http.HttpType; import me.chanjar.weixin.cp.config.WxCpConfigStorage; -public class WxCpServiceJoddHttpImpl extends WxCpServiceAbstractImpl { +public class WxCpServiceJoddHttpImpl extends BaseWxCpServiceImpl { protected HttpConnectionProvider httpClient; protected ProxyInfo httpProxy; @@ -30,30 +30,29 @@ public HttpType getRequestType() { @Override public String getAccessToken(boolean forceRefresh) throws WxErrorException { - if (this.configStorage.isAccessTokenExpired() || forceRefresh) { - synchronized (this.globalAccessTokenRefreshLock) { - if (this.configStorage.isAccessTokenExpired()) { - String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?" - + "&corpid=" + this.configStorage.getCorpId() - + "&corpsecret=" + this.configStorage.getCorpSecret(); + if (!this.configStorage.isAccessTokenExpired() && !forceRefresh) { + return this.configStorage.getAccessToken(); + } + + synchronized (this.globalAccessTokenRefreshLock) { + String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?" + + "&corpid=" + this.configStorage.getCorpId() + + "&corpsecret=" + this.configStorage.getCorpSecret(); - HttpRequest request = HttpRequest.get(url); - if (this.httpProxy != null) { - httpClient.useProxy(this.httpProxy); - } - request.withConnectionProvider(httpClient); - HttpResponse response = request.send(); + HttpRequest request = HttpRequest.get(url); + if (this.httpProxy != null) { + httpClient.useProxy(this.httpProxy); + } + request.withConnectionProvider(httpClient); + HttpResponse response = request.send(); - String resultContent = response.bodyText(); - WxError error = WxError.fromJson(resultContent, WxType.CP); - if (error.getErrorCode() != 0) { - throw new WxErrorException(error); - } - WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); - this.configStorage.updateAccessToken( - accessToken.getAccessToken(), accessToken.getExpiresIn()); - } + String resultContent = response.bodyText(); + WxError error = WxError.fromJson(resultContent, WxType.CP); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); } + WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); + this.configStorage.updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); } return this.configStorage.getAccessToken(); } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceOkHttpImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceOkHttpImpl.java index 34c978cd4f..9163ce03a6 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceOkHttpImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceOkHttpImpl.java @@ -11,7 +11,7 @@ import java.io.IOException; -public class WxCpServiceOkHttpImpl extends WxCpServiceAbstractImpl { +public class WxCpServiceOkHttpImpl extends BaseWxCpServiceImpl { protected OkHttpClient httpClient; protected OkHttpProxyInfo httpProxy; @@ -33,34 +33,33 @@ public HttpType getRequestType() { @Override public String getAccessToken(boolean forceRefresh) throws WxErrorException { - this.log.debug("WxCpServiceOkHttpImpl is running"); - if (this.configStorage.isAccessTokenExpired() || forceRefresh) { - synchronized (this.globalAccessTokenRefreshLock) { - if (this.configStorage.isAccessTokenExpired()) { - String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?" - + "&corpid=" + this.configStorage.getCorpId() - + "&corpsecret=" + this.configStorage.getCorpSecret(); - //得到httpClient - OkHttpClient client = getRequestHttpClient(); - //请求的request - Request request = new Request.Builder().url(url).get().build(); - String resultContent = null; - try { - Response response = client.newCall(request).execute(); - resultContent = response.body().string(); - } catch (IOException e) { - this.log.error(e.getMessage(), e); - } + if (!this.configStorage.isAccessTokenExpired() && !forceRefresh) { + return this.configStorage.getAccessToken(); + } + + synchronized (this.globalAccessTokenRefreshLock) { + String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?" + + "&corpid=" + this.configStorage.getCorpId() + + "&corpsecret=" + this.configStorage.getCorpSecret(); + //得到httpClient + OkHttpClient client = getRequestHttpClient(); + //请求的request + Request request = new Request.Builder().url(url).get().build(); + String resultContent = null; + try { + Response response = client.newCall(request).execute(); + resultContent = response.body().string(); + } catch (IOException e) { + this.log.error(e.getMessage(), e); + } - WxError error = WxError.fromJson(resultContent, WxType.CP); - if (error.getErrorCode() != 0) { - throw new WxErrorException(error); - } - WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); - this.configStorage.updateAccessToken(accessToken.getAccessToken(), - accessToken.getExpiresIn()); + WxError error = WxError.fromJson(resultContent, WxType.CP); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); } - } + WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); + this.configStorage.updateAccessToken(accessToken.getAccessToken(), + accessToken.getExpiresIn()); } return this.configStorage.getAccessToken(); } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpTaskCardServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpTaskCardServiceImpl.java new file mode 100644 index 0000000000..3011320d50 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpTaskCardServiceImpl.java @@ -0,0 +1,39 @@ +package me.chanjar.weixin.cp.api.impl; + +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.api.WxCpTaskCardService; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + *
+ *  任务卡片管理接口.
+ *  Created by Jeff on 2019-05-16.
+ * 
+ * + * @author Jeff + * @date 2019-05-16 + */ +@RequiredArgsConstructor +public class WxCpTaskCardServiceImpl implements WxCpTaskCardService { + private final WxCpService mainService; + + @Override + public void update(List userIds, String taskId, String clickedKey) throws WxErrorException { + Integer agentId = this.mainService.getWxCpConfigStorage().getAgentId(); + + Map data = new HashMap<>(4); + data.put("userids", userIds); + data.put("agentid", agentId); + data.put("task_id", taskId); + data.put("clicked_key", clickedKey); + + String url = "https://qyapi.weixin.qq.com/cgi-bin/message/update_taskcard"; + this.mainService.post(url, WxGsonBuilder.create().toJson(data)); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpAppChatMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpAppChatMessage.java new file mode 100644 index 0000000000..96639f6c51 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpAppChatMessage.java @@ -0,0 +1,163 @@ +package me.chanjar.weixin.cp.bean; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.cp.WxCpConsts.AppChatMsgType; +import me.chanjar.weixin.cp.bean.article.MpnewsArticle; +import me.chanjar.weixin.cp.bean.article.NewArticle; + +import java.io.Serializable; +import java.util.List; + +/** + *
+ * 应用推送消息
+ * Created by Binary Wang on 2019/1/26.
+ * 
+ * + * @author Binary Wang + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WxCpAppChatMessage implements Serializable { + private static final long serialVersionUID = -5469013416372240229L; + + private String msgType; + private String content; + private String chatId; + private String mediaId; + private String title; + private String description; + private Boolean safe; + private String url; + private String btnTxt; + private List articles; + private List mpnewsArticles; + + /** + * 构建文本消息. + */ + public static WxCpAppChatMessage buildTextMsg(String chatId, String content, boolean safe) { + final WxCpAppChatMessage message = new WxCpAppChatMessage(); + message.setMsgType(AppChatMsgType.TEXT); + message.setContent(content); + message.setChatId(chatId); + message.setSafe(safe); + return message; + } + + /** + * 生成json字符串. + */ + public String toJson() { + JsonObject messageJson = new JsonObject(); + messageJson.addProperty("msgtype", this.getMsgType()); + messageJson.addProperty("chatid", this.getChatId()); + + if (this.getSafe() != null && this.getSafe()) { + messageJson.addProperty("safe", 1); + } + + this.handleMsgType(messageJson); + + return messageJson.toString(); + } + + private void handleMsgType(JsonObject messageJson) { + switch (this.getMsgType()) { + case AppChatMsgType.TEXT: { + JsonObject text = new JsonObject(); + text.addProperty("content", this.getContent()); + messageJson.add("text", text); + break; + } + case AppChatMsgType.MARKDOWN: { + JsonObject text = new JsonObject(); + text.addProperty("content", this.getContent()); + messageJson.add("markdown", text); + break; + } + case AppChatMsgType.TEXTCARD: { + JsonObject text = new JsonObject(); + text.addProperty("title", this.getTitle()); + text.addProperty("description", this.getDescription()); + text.addProperty("url", this.getUrl()); + text.addProperty("btntxt", this.getBtnTxt()); + messageJson.add("textcard", text); + break; + } + case AppChatMsgType.IMAGE: { + JsonObject image = new JsonObject(); + image.addProperty("media_id", this.getMediaId()); + messageJson.add("image", image); + break; + } + case AppChatMsgType.FILE: { + JsonObject image = new JsonObject(); + image.addProperty("media_id", this.getMediaId()); + messageJson.add("file", image); + break; + } + case AppChatMsgType.VOICE: { + JsonObject voice = new JsonObject(); + voice.addProperty("media_id", this.getMediaId()); + messageJson.add("voice", voice); + break; + } + case AppChatMsgType.VIDEO: { + JsonObject video = new JsonObject(); + video.addProperty("media_id", this.getMediaId()); + video.addProperty("title", this.getTitle()); + video.addProperty("description", this.getDescription()); + messageJson.add("video", video); + break; + } + case AppChatMsgType.NEWS: { + JsonObject newsJsonObject = new JsonObject(); + JsonArray articleJsonArray = new JsonArray(); + for (NewArticle article : this.getArticles()) { + JsonObject articleJson = new JsonObject(); + articleJson.addProperty("title", article.getTitle()); + articleJson.addProperty("description", article.getDescription()); + articleJson.addProperty("url", article.getUrl()); + articleJson.addProperty("picurl", article.getPicUrl()); + articleJsonArray.add(articleJson); + } + newsJsonObject.add("articles", articleJsonArray); + messageJson.add("news", newsJsonObject); + break; + } + case AppChatMsgType.MPNEWS: { + JsonObject newsJsonObject = new JsonObject(); + if (this.getMediaId() != null) { + newsJsonObject.addProperty("media_id", this.getMediaId()); + } else { + JsonArray articleJsonArray = new JsonArray(); + for (MpnewsArticle article : this.getMpnewsArticles()) { + JsonObject articleJson = new JsonObject(); + articleJson.addProperty("title", article.getTitle()); + articleJson.addProperty("thumb_media_id", article.getThumbMediaId()); + articleJson.addProperty("author", article.getAuthor()); + articleJson.addProperty("content_source_url", article.getContentSourceUrl()); + articleJson.addProperty("content", article.getContent()); + articleJson.addProperty("digest", article.getDigest()); + articleJsonArray.add(articleJson); + } + + newsJsonObject.add("articles", articleJsonArray); + } + messageJson.add("mpnews", newsJsonObject); + break; + } + default: { + //do nothing + } + } + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpApprovalDataResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpApprovalDataResult.java new file mode 100644 index 0000000000..3e1299e92b --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpApprovalDataResult.java @@ -0,0 +1,69 @@ +package me.chanjar.weixin.cp.bean; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; +import java.util.Map; + +/** + * @author Element + * @Package me.chanjar.weixin.cp.bean + * @date 2019-04-06 14:36 + * @Description: 企业微信 OA 审批数据 + */ +@Data +public class WxCpApprovalDataResult implements Serializable { + private static final long serialVersionUID = -1046940445840716590L; + + @SerializedName("errcode") + private Integer errCode; + + @SerializedName("errmsg") + private String errMsg; + + private Integer count; + + private Integer total; + + @SerializedName("next_spnum") + private Long nextSpnum; + + private WxCpApprovalData[] data; + + + @Data + public static class WxCpApprovalData implements Serializable{ + + private static final long serialVersionUID = -3051785319608491640L; + + private String spname; + + @SerializedName("apply_name") + private String applyName; + + @SerializedName("apply_org") + private String applyOrg; + + @SerializedName("approval_name") + private String[] approvalName; + + @SerializedName("notify_name") + private String[] notifyName; + + @SerializedName("sp_status") + private Integer spStatus; + + @SerializedName("sp_num") + private Long spNum; + + @SerializedName("apply_time") + private Long applyTime; + + @SerializedName("apply_user_id") + private String applyUserId; + + @SerializedName("comm") + private Map comm; + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpCheckinData.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpCheckinData.java new file mode 100644 index 0000000000..369c771f98 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpCheckinData.java @@ -0,0 +1,51 @@ +package me.chanjar.weixin.cp.bean; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * @author Element + * @Package me.chanjar.weixin.cp.bean + * @date 2019-04-06 11:01 + * @Description: 企业微信打卡数据 + */ +@Data +public class WxCpCheckinData implements Serializable { + + private static final long serialVersionUID = 1915820330847799605L; + + @SerializedName("userid") + private String userId; + + @SerializedName("groupname") + private String groupName; + + @SerializedName("checkin_type") + private String checkinType; + + @SerializedName("exception_type") + private String exceptionType; + + @SerializedName("checkin_time") + private Long checkinTime; + + @SerializedName("location_title") + private String locationTitle; + + @SerializedName("location_detail") + private String locationDetail; + + @SerializedName("wifiname") + private String wifiName; + + @SerializedName("wifimac") + private String wifiMAC; + + private String notes; + + @SerializedName("mediaids") + private List mediaIds; +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpCheckinOption.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpCheckinOption.java new file mode 100644 index 0000000000..9cc3c389c7 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpCheckinOption.java @@ -0,0 +1,151 @@ +package me.chanjar.weixin.cp.bean; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * @author Element + * @Package me.chanjar.weixin.cp.bean + * @date 2019-04-06 13:22 + * @Description: 企业微信打卡规则 + */ +@Data +public class WxCpCheckinOption implements Serializable { + private static final long serialVersionUID = -1964233697990417482L; + + @SerializedName("userid") + private String userId; + + private Group group; + + + @Data + public static class CheckinDate implements Serializable { + + private static final long serialVersionUID = -5601722383347110974L; + + private List workdays; + + @SerializedName("checkintime") + private CheckinTime[] checkinTime; + + @SerializedName("flex_time") + private Long flexTime; + + @SerializedName("noneed_offwork") + private Boolean noneedOffwork; + + @SerializedName("limit_aheadtime") + private Long limitAheadtime; + } + + @Data + public static class CheckinTime implements Serializable { + + private static final long serialVersionUID = -8579954143265336276L; + + @SerializedName("work_sec") + private Long workSec; + + @SerializedName("off_work_sec") + private Long offWorkSec; + + @SerializedName("remind_work_sec") + private Long remindWorkSec; + + @SerializedName("remind_off_work_sec") + private Long remindOffWorkSec; + } + + @Data + public static class Group implements Serializable { + + private static final long serialVersionUID = -5888406969613403044L; + + @SerializedName("groupid") + private Long id; + + @SerializedName("groupname") + private String name; + + @SerializedName("grouptype") + private Integer type; + + @SerializedName("checkindate") + private List checkinDate; + + @SerializedName("spe_workdays") + private List speWorkdays; + + @SerializedName("spe_offdays") + private List speOffdays; + + @SerializedName("sync_holidays") + private Boolean syncHolidays; + + @SerializedName("need_photo") + private Boolean needPhoto; + + @SerializedName("note_can_use_local_pic") + private Boolean note_can_use_local_pic; + + @SerializedName("allow_checkin_offworkday") + private Boolean allow_checkin_offworkday; + + @SerializedName("allow_apply_offworkday") + private Boolean allow_apply_offworkday; + + @SerializedName("wifimac_infos") + private List wifiMACInfos; + + @SerializedName("loc_infos") + private List locInfos; + + } + + @Data + public static class WifiMACInfo implements Serializable{ + + private static final long serialVersionUID = -4657809185716627368L; + + @SerializedName("wifiname") + private String name; + + @SerializedName("wifimac") + private String mac; + } + + @Data + public static class LocInfo implements Serializable{ + + private static final long serialVersionUID = -618965280668099608L; + + private Long lat; + private Long lng; + + @SerializedName("loc_title") + private String title; + + @SerializedName("loc_detail") + private String detail; + + private Long distance; + } + + @Data + public static class SpeDay implements Serializable{ + + private static final long serialVersionUID = -3538818921359212748L; + + private Long timestamp; + private String notes; + + @SerializedName("checkintime") + private List checkinTime; + + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpDialRecord.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpDialRecord.java new file mode 100644 index 0000000000..d8e40c79bd --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpDialRecord.java @@ -0,0 +1,73 @@ +package me.chanjar.weixin.cp.bean; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * @author Element + * @Package me.chanjar.weixin.cp.bean + * @date 2019-04-06 15:38 + * @Description: 公费电话拨打记录 + */ +@Data +public class WxCpDialRecord implements Serializable { + + private static final long serialVersionUID = 4178886812949929116L; + @SerializedName("call_time") + private Long callTime; + + /** + * 总通话时长,单位为分钟 + */ + @SerializedName("total_duration") + private Integer totalDuration; + + /** + * 通话类型,1-单人通话 2-多人通话 + */ + @SerializedName("call_type") + private Integer callType; + + private Caller caller; + + private List callee; + + /** + * 主叫信息 + */ + @Data + public static class Caller implements Serializable{ + + private static final long serialVersionUID = 4792200404338145607L; + + @SerializedName("userid") + private String userId; + + private Integer duration; + } + + /** + * 被叫信息 + */ + @Data + public static class Callee implements Serializable{ + + private static final long serialVersionUID = 2390963671336179550L; + + /** + * 被叫用户的userid,当被叫用户为企业内用户时返回 + */ + @SerializedName("userid") + private String userId; + + /** + * 被叫用户的号码,当被叫用户为外部用户时返回 + */ + private String phone; + + private Integer duration; + } +} 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 871e48394e..ccf6fc94db 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 @@ -37,7 +37,7 @@ public static WxCpInviteResult fromJson(String json) { private String errMsg; @SerializedName("invaliduser") - private String invalidUsers; + private String[] invalidUsers; @SerializedName("invalidparty") private String[] invalidParties; @@ -45,16 +45,4 @@ public static WxCpInviteResult fromJson(String json) { @SerializedName("invalidtag") private String[] invalidTags; - public List getInvalidUserList() { - return this.content2List(this.invalidUsers); - } - - private List content2List(String content) { - if (StringUtils.isBlank(content)) { - return Collections.emptyList(); - } - - return Splitter.on("|").splitToList(content); - } - } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMaJsCode2SessionResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMaJsCode2SessionResult.java new file mode 100644 index 0000000000..90f1ae840c --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMaJsCode2SessionResult.java @@ -0,0 +1,33 @@ +package me.chanjar.weixin.cp.bean; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.io.Serializable; + +/** + *
+ * 小程序登录凭证校验
+ * 文档地址:https://work.weixin.qq.com/api/doc#90000/90136/90289/wx.qy.login
+ * 
+ * @author Binary Wang + */ +@Data +public class WxCpMaJsCode2SessionResult implements Serializable { + private static final long serialVersionUID = 6229609023682814765L; + + @SerializedName("session_key") + private String sessionKey; + + @SerializedName("userid") + private String userId; + + @SerializedName("corpid") + private String corpId; + + public static WxCpMaJsCode2SessionResult fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpMaJsCode2SessionResult.class); + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMessage.java index d28c178c35..a22efd0e45 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMessage.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMessage.java @@ -1,22 +1,18 @@ package me.chanjar.weixin.cp.bean; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; - +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; import lombok.Data; -import me.chanjar.weixin.common.api.WxConsts; +import me.chanjar.weixin.common.api.WxConsts.KefuMsgType; import me.chanjar.weixin.cp.bean.article.MpnewsArticle; import me.chanjar.weixin.cp.bean.article.NewArticle; -import me.chanjar.weixin.cp.bean.messagebuilder.FileBuilder; -import me.chanjar.weixin.cp.bean.messagebuilder.ImageBuilder; -import me.chanjar.weixin.cp.bean.messagebuilder.MpnewsBuilder; -import me.chanjar.weixin.cp.bean.messagebuilder.NewsBuilder; -import me.chanjar.weixin.cp.bean.messagebuilder.TextBuilder; -import me.chanjar.weixin.cp.bean.messagebuilder.TextCardBuilder; -import me.chanjar.weixin.cp.bean.messagebuilder.VideoBuilder; -import me.chanjar.weixin.cp.bean.messagebuilder.VoiceBuilder; -import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; +import me.chanjar.weixin.cp.bean.messagebuilder.*; +import me.chanjar.weixin.cp.bean.taskcard.TaskCardButton; +import org.apache.commons.lang3.StringUtils; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; /** * 消息. @@ -45,6 +41,12 @@ public class WxCpMessage implements Serializable { private List articles = new ArrayList<>(); private List mpnewsArticles = new ArrayList<>(); + /** + * 任务卡片特有的属性 + */ + private String taskId; + private List taskButtons = new ArrayList<>(); + /** * 获得文本消息builder. */ @@ -94,6 +96,13 @@ public static MpnewsBuilder MPNEWS() { return new MpnewsBuilder(); } + /** + * 获得markdown消息builder. + */ + public static MarkdownMsgBuilder MARKDOWN() { + return new MarkdownMsgBuilder(); + } + /** * 获得文件消息builder. */ @@ -101,17 +110,26 @@ public static FileBuilder FILE() { return new FileBuilder(); } + /** + * 获得任务卡片消息builder. + */ + public static TaskCardBuilder TASKCARD() { + return new TaskCardBuilder(); + } + /** *
    * 请使用
-   * {@link WxConsts.KefuMsgType#TEXT}
-   * {@link WxConsts.KefuMsgType#IMAGE}
-   * {@link WxConsts.KefuMsgType#VOICE}
-   * {@link WxConsts.KefuMsgType#MUSIC}
-   * {@link WxConsts.KefuMsgType#VIDEO}
-   * {@link WxConsts.KefuMsgType#NEWS}
-   * {@link WxConsts.KefuMsgType#MPNEWS}
+   * {@link KefuMsgType#TEXT}
+   * {@link KefuMsgType#IMAGE}
+   * {@link KefuMsgType#VOICE}
+   * {@link KefuMsgType#MUSIC}
+   * {@link KefuMsgType#VIDEO}
+   * {@link KefuMsgType#NEWS}
+   * {@link KefuMsgType#MPNEWS}
+   * {@link KefuMsgType#MARKDOWN}
+   * {@link KefuMsgType#TASKCARD}
    * 
* * @param msgType 消息类型 @@ -121,7 +139,162 @@ public void setMsgType(String msgType) { } public String toJson() { - return WxCpGsonBuilder.create().toJson(this); + JsonObject messageJson = new JsonObject(); + if (this.getAgentId() != null) { + messageJson.addProperty("agentid", this.getAgentId()); + } + + if (StringUtils.isNotBlank(this.getToUser())) { + messageJson.addProperty("touser", this.getToUser()); + } + + messageJson.addProperty("msgtype", this.getMsgType()); + + if (StringUtils.isNotBlank(this.getToParty())) { + messageJson.addProperty("toparty", this.getToParty()); + } + + if (StringUtils.isNotBlank(this.getToTag())) { + messageJson.addProperty("totag", this.getToTag()); + } + + this.handleMsgType(messageJson); + + if (StringUtils.isNotBlank(this.getSafe())) { + messageJson.addProperty("safe", this.getSafe()); + } + + return messageJson.toString(); + } + + private void handleMsgType(JsonObject messageJson) { + switch (this.getMsgType()) { + case KefuMsgType.TEXT: { + JsonObject text = new JsonObject(); + text.addProperty("content", this.getContent()); + messageJson.add("text", text); + break; + } + case KefuMsgType.MARKDOWN: { + JsonObject text = new JsonObject(); + text.addProperty("content", this.getContent()); + messageJson.add("markdown", text); + break; + } + case KefuMsgType.TEXTCARD: { + JsonObject text = new JsonObject(); + text.addProperty("title", this.getTitle()); + text.addProperty("description", this.getDescription()); + text.addProperty("url", this.getUrl()); + text.addProperty("btntxt", this.getBtnTxt()); + messageJson.add("textcard", text); + break; + } + case KefuMsgType.IMAGE: { + JsonObject image = new JsonObject(); + image.addProperty("media_id", this.getMediaId()); + messageJson.add("image", image); + break; + } + case KefuMsgType.FILE: { + JsonObject image = new JsonObject(); + image.addProperty("media_id", this.getMediaId()); + messageJson.add("file", image); + break; + } + case KefuMsgType.VOICE: { + JsonObject voice = new JsonObject(); + voice.addProperty("media_id", this.getMediaId()); + messageJson.add("voice", voice); + break; + } + case KefuMsgType.VIDEO: { + JsonObject video = new JsonObject(); + video.addProperty("media_id", this.getMediaId()); + video.addProperty("thumb_media_id", this.getThumbMediaId()); + video.addProperty("title", this.getTitle()); + video.addProperty("description", this.getDescription()); + messageJson.add("video", video); + break; + } + case KefuMsgType.NEWS: { + JsonObject newsJsonObject = new JsonObject(); + JsonArray articleJsonArray = new JsonArray(); + for (NewArticle article : this.getArticles()) { + JsonObject articleJson = new JsonObject(); + articleJson.addProperty("title", article.getTitle()); + articleJson.addProperty("description", article.getDescription()); + articleJson.addProperty("url", article.getUrl()); + articleJson.addProperty("picurl", article.getPicUrl()); + articleJsonArray.add(articleJson); + } + newsJsonObject.add("articles", articleJsonArray); + messageJson.add("news", newsJsonObject); + break; + } + case KefuMsgType.MPNEWS: { + JsonObject newsJsonObject = new JsonObject(); + if (this.getMediaId() != null) { + newsJsonObject.addProperty("media_id", this.getMediaId()); + } else { + JsonArray articleJsonArray = new JsonArray(); + for (MpnewsArticle article : this.getMpnewsArticles()) { + JsonObject articleJson = new JsonObject(); + articleJson.addProperty("title", article.getTitle()); + articleJson.addProperty("thumb_media_id", article.getThumbMediaId()); + articleJson.addProperty("author", article.getAuthor()); + articleJson.addProperty("content_source_url", article.getContentSourceUrl()); + articleJson.addProperty("content", article.getContent()); + articleJson.addProperty("digest", article.getDigest()); + articleJson.addProperty("show_cover_pic", article.getShowCoverPic()); + articleJsonArray.add(articleJson); + } + + newsJsonObject.add("articles", articleJsonArray); + } + messageJson.add("mpnews", newsJsonObject); + break; + } + case KefuMsgType.TASKCARD: { + JsonObject text = new JsonObject(); + text.addProperty("title", this.getTitle()); + text.addProperty("description", this.getDescription()); + + if (StringUtils.isNotBlank(this.getUrl())) { + text.addProperty("url", this.getUrl()); + } + + text.addProperty("task_id", this.getTaskId()); + + JsonArray buttonJsonArray = new JsonArray(); + for (TaskCardButton button : this.getTaskButtons()) { + JsonObject buttonJson = new JsonObject(); + buttonJson.addProperty("key", button.getKey()); + buttonJson.addProperty("name", button.getName()); + + if (StringUtils.isNotBlank(button.getReplaceName())) { + buttonJson.addProperty("replace_name", button.getReplaceName()); + } + + if (StringUtils.isNotBlank(button.getColor())) { + buttonJson.addProperty("color", button.getColor()); + } + + if (button.getBold() != null) { + buttonJson.addProperty("is_bold", button.getBold()); + } + + buttonJsonArray.add(buttonJson); + } + text.add("btn", buttonJsonArray); + + messageJson.add("taskcard", text); + break; + } + default: { + // do nothing + } + } } } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTaskCardUpdateResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTaskCardUpdateResult.java new file mode 100644 index 0000000000..c86b255b44 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTaskCardUpdateResult.java @@ -0,0 +1,42 @@ +package me.chanjar.weixin.cp.bean; + +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +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/91579
+ *  Created by Jeff on 2019-05-16.
+ * 
+ * + * @author Jeff + * @date 2019-05-16 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class WxCpTaskCardUpdateResult implements Serializable { + + @SerializedName("errcode") + private Integer errcode; + + @SerializedName("errmsg") + private String errmsg; + + /** + * 用户列表 + */ + @SerializedName("invaliduser") + private List invalidUsers; + + public static WxCpTaskCardUpdateResult fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpTaskCardUpdateResult.class); + } +} 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 233d4576e1..dcf4789eba 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 @@ -20,7 +20,8 @@ public class WxCpUser implements Serializable { private static final long serialVersionUID = -5696099236344075582L; private String userId; private String name; - private Integer[] departIds; + private Long[] departIds; + private Integer[] orders; private String position; private String mobile; private Gender gender; diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlMessage.java index 3fc2137f3e..e6b2be197a 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlMessage.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlMessage.java @@ -1,24 +1,26 @@ package me.chanjar.weixin.cp.bean; -import java.io.IOException; -import java.io.InputStream; -import java.io.Serializable; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; - -import org.apache.commons.io.IOUtils; - import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.annotations.XStreamConverter; +import com.thoughtworks.xstream.annotations.XStreamImplicit; import lombok.Data; import lombok.extern.slf4j.Slf4j; import me.chanjar.weixin.common.api.WxConsts; +import me.chanjar.weixin.common.util.XmlUtils; import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; import me.chanjar.weixin.cp.config.WxCpConfigStorage; import me.chanjar.weixin.cp.util.crypto.WxCpCryptUtil; import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; import me.chanjar.weixin.cp.util.xml.XStreamTransformer; +import org.apache.commons.io.IOUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Serializable; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; /** *
@@ -36,6 +38,11 @@
 public class WxCpXmlMessage implements Serializable {
   private static final long serialVersionUID = -1042994982179476410L;
 
+  /**
+   * 使用dom4j解析的存放所有xml属性和值的map.
+   */
+  private Map allFieldsMap;
+
   ///////////////////////
   // 以下都是微信推送过来的消息的xml的element所对应的属性
   ///////////////////////
@@ -149,6 +156,10 @@ public class WxCpXmlMessage implements Serializable {
   @XStreamConverter(value = XStreamCDataConverter.class)
   private String recognition;
 
+  @XStreamAlias("TaskId")
+  @XStreamConverter(value = XStreamCDataConverter.class)
+  private String taskId;
+
   /**
    * 通讯录变更事件.
    * 请参考常量 me.chanjar.weixin.cp.WxCpConsts.ContactChangeType
@@ -240,6 +251,13 @@ public class WxCpXmlMessage implements Serializable {
   @XStreamConverter(value = XStreamCDataConverter.class)
   private String telephone;
 
+  /**
+   * 地址.
+   */
+  @XStreamAlias("Address")
+  @XStreamConverter(value = XStreamCDataConverter.class)
+  private String address;
+
   /**
    * 扩展属性.
    */
@@ -320,17 +338,20 @@ public class WxCpXmlMessage implements Serializable {
    */
   @XStreamAlias("TotalCount")
   private Integer totalCount;
+
   /**
    * 过滤.
    * (过滤是指特定地区、性别的过滤、用户设置拒收的过滤,用户接收已超4条的过滤)后,准备发送的粉丝数,原则上,filterCount = sentCount + errorCount
    */
   @XStreamAlias("FilterCount")
   private Integer filterCount;
+
   /**
    * 发送成功的粉丝数.
    */
   @XStreamAlias("SentCount")
   private Integer sentCount;
+
   /**
    * 发送失败的粉丝数.
    */
@@ -349,7 +370,9 @@ public class WxCpXmlMessage implements Serializable {
   protected static WxCpXmlMessage fromXml(String xml) {
     //修改微信变态的消息内容格式,方便解析
     xml = xml.replace("", "");
-    return XStreamTransformer.fromXml(WxCpXmlMessage.class, xml);
+    final WxCpXmlMessage xmlMessage = XStreamTransformer.fromXml(WxCpXmlMessage.class, xml);
+    xmlMessage.setAllFieldsMap(XmlUtils.xml2Map(xml));
+    return xmlMessage;
   }
 
   protected static WxCpXmlMessage fromXml(InputStream is) {
@@ -402,9 +425,11 @@ public static class ScanCodeInfo {
 
   @Data
   public static class ExtAttr {
-    @XStreamAlias("Item")
+
+    @XStreamImplicit(itemFieldName = "Item")
     protected final List items = new ArrayList<>();
 
+    @XStreamAlias("Item")
     @Data
     public static class Item {
       @XStreamAlias("Name")
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/article/NewArticle.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/article/NewArticle.java
index 7f10d363b4..d5e74c44ff 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/article/NewArticle.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/article/NewArticle.java
@@ -1,9 +1,12 @@
 package me.chanjar.weixin.cp.bean.article;
 
-import lombok.Data;
-
 import java.io.Serializable;
 
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
 /**
  * 
  *  Created by BinaryWang on 2017/3/27.
@@ -12,6 +15,9 @@
  * @author Binary Wang
  */
 @Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
 public class NewArticle implements Serializable {
   private static final long serialVersionUID = 4087852055781140659L;
 
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/MarkdownMsgBuilder.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/MarkdownMsgBuilder.java
new file mode 100644
index 0000000000..6e0a4a3302
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/MarkdownMsgBuilder.java
@@ -0,0 +1,32 @@
+package me.chanjar.weixin.cp.bean.messagebuilder;
+
+import me.chanjar.weixin.common.api.WxConsts;
+import me.chanjar.weixin.cp.bean.WxCpMessage;
+
+/**
+ * 
+ * markdown类型的消息builder
+ * Created by Binary Wang on 2019/1/20.
+ * 
+ * + * @author Binary Wang + */ +public class MarkdownMsgBuilder extends BaseBuilder { + private String content; + + public MarkdownMsgBuilder() { + this.msgType = WxConsts.KefuMsgType.MARKDOWN; + } + + public MarkdownMsgBuilder content(String content) { + this.content = content; + return this; + } + + @Override + public WxCpMessage build() { + WxCpMessage m = super.build(); + m.setContent(this.content); + return m; + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/TaskCardBuilder.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/TaskCardBuilder.java new file mode 100644 index 0000000000..3c2d77924d --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/TaskCardBuilder.java @@ -0,0 +1,68 @@ +package me.chanjar.weixin.cp.bean.messagebuilder; + +import me.chanjar.weixin.common.api.WxConsts; +import me.chanjar.weixin.cp.bean.WxCpMessage; +import me.chanjar.weixin.cp.bean.taskcard.TaskCardButton; + +import java.util.List; + +/** + *
+ * 任务卡片消息Builder
+ * 用法: WxCustomMessage m = WxCustomMessage.TASKCARD().title(...)....toUser(...).build();
+ * 
+ * + * @author Jeff + * @date 2019-05-16 + */ +public class TaskCardBuilder extends BaseBuilder { + private String title; + private String description; + private String url; + private String taskId; + /** + * 按钮个数为1~2个 + */ + private List buttons; + + public TaskCardBuilder() { + this.msgType = WxConsts.KefuMsgType.TASKCARD; + } + + public TaskCardBuilder title(String title) { + this.title = title; + return this; + } + + public TaskCardBuilder description(String description) { + this.description = description; + return this; + } + + public TaskCardBuilder url(String url) { + this.url = url; + return this; + } + + public TaskCardBuilder taskId(String taskId) { + this.taskId = taskId; + return this; + } + + public TaskCardBuilder buttons(List buttons) { + this.buttons = buttons; + return this; + } + + @Override + public WxCpMessage build() { + WxCpMessage m = super.build(); + m.setSafe(null); + m.setTitle(this.title); + m.setDescription(this.description); + m.setUrl(this.url); + m.setTaskId(this.taskId); + m.setTaskButtons(this.buttons); + return m; + } +} 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 new file mode 100644 index 0000000000..0ffe49ce58 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/taskcard/TaskCardButton.java @@ -0,0 +1,23 @@ +package me.chanjar.weixin.cp.bean.taskcard; + +import lombok.Builder; +import lombok.Data; + +/** + *
+ *  任务卡片按钮
+ *  Created by Jeff on 2019-05-16.
+ * 
+ * + * @author Jeff + * @date 2019-05-16 + */ +@Data +@Builder +public class TaskCardButton { + private String key; + private String name; + private String replaceName; + private String color; + private Boolean bold; +} 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 65dd3affff..e13738142f 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 @@ -36,11 +36,23 @@ public interface WxCpConfigStorage { /** * 应该是线程安全的 - * - * @param jsapiTicket */ void updateJsapiTicket(String jsapiTicket, int expiresInSeconds); + String getAgentJsapiTicket(); + + boolean isAgentJsapiTicketExpired(); + + /** + * 强制将jsapi ticket过期掉 + */ + void expireAgentJsapiTicket(); + + /** + * 应该是线程安全的 + */ + void updateAgentJsapiTicket(String jsapiTicket, int expiresInSeconds); + String getCorpId(); String getCorpSecret(); diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpInMemoryConfigStorage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpInMemoryConfigStorage.java index 1b9f8a61b5..a501edeb6e 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpInMemoryConfigStorage.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpInMemoryConfigStorage.java @@ -1,11 +1,11 @@ package me.chanjar.weixin.cp.config; -import java.io.File; - import me.chanjar.weixin.common.bean.WxAccessToken; import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; +import java.io.File; + /** * 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化 * @@ -32,6 +32,9 @@ public class WxCpInMemoryConfigStorage implements WxCpConfigStorage { protected volatile String jsapiTicket; protected volatile long jsapiTicketExpiresTime; + protected volatile String agentJsapiTicket; + protected volatile long agentJsapiTicketExpiresTime; + protected volatile File tmpDirFile; private volatile ApacheHttpClientBuilder apacheHttpClientBuilder; @@ -95,6 +98,28 @@ public synchronized void updateJsapiTicket(String jsapiTicket, int expiresInSeco this.jsapiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L; } + @Override + public String getAgentJsapiTicket() { + return this.agentJsapiTicket; + } + + @Override + public boolean isAgentJsapiTicketExpired() { + return System.currentTimeMillis() > this.agentJsapiTicketExpiresTime; + } + + @Override + public void expireAgentJsapiTicket() { + this.agentJsapiTicketExpiresTime = 0; + } + + @Override + public void updateAgentJsapiTicket(String jsapiTicket, int expiresInSeconds) { + this.agentJsapiTicket = jsapiTicket; + // 预留200秒的时间 + this.agentJsapiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L; + } + @Override public void expireJsapiTicket() { this.jsapiTicketExpiresTime = 0; diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpJedisConfigStorage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpJedisConfigStorage.java index 6ae48f0e64..7c72de0e1b 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpJedisConfigStorage.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpJedisConfigStorage.java @@ -9,26 +9,21 @@ import java.io.File; /** - * Jedis client implementor for wechat config storage. *
- *    使用说明:本实现仅供参考,并不完整,
+ *    使用说明:本实现仅供参考,并不完整.
  *    比如为减少项目依赖,未加入redis分布式锁的实现,如有需要请自行实现。
  * 
* * @author gaigeshen */ public class WxCpJedisConfigStorage implements WxCpConfigStorage { - - /** - * Redis keys here - */ private static final String ACCESS_TOKEN_KEY = "WX_CP_ACCESS_TOKEN"; private static final String ACCESS_TOKEN_EXPIRES_TIME_KEY = "WX_CP_ACCESS_TOKEN_EXPIRES_TIME"; private static final String JS_API_TICKET_KEY = "WX_CP_JS_API_TICKET"; private static final String JS_API_TICKET_EXPIRES_TIME_KEY = "WX_CP_JS_API_TICKET_EXPIRES_TIME"; - /** - * Redis clients pool - */ + private static final String AGENT_JSAPI_TICKET_KEY = "WX_CP_AGENT_%s_JSAPI_TICKET"; + private static final String AGENT_JSAPI_TICKET_EXPIRES_TIME_KEY = "WX_CP_AGENT_%s_JSAPI_TICKET_EXPIRES_TIME"; + private final JedisPool jedisPool; private volatile String corpId; private volatile String corpSecret; @@ -46,7 +41,7 @@ public class WxCpJedisConfigStorage implements WxCpConfigStorage { public WxCpJedisConfigStorage(JedisPool jedisPool) { this.jedisPool = jedisPool; } - + public WxCpJedisConfigStorage(String host, int port) { jedisPool = new JedisPool(host, port); } @@ -83,8 +78,7 @@ public boolean isAccessTokenExpired() { String expiresTimeStr = jedis.get(ACCESS_TOKEN_EXPIRES_TIME_KEY); if (expiresTimeStr != null) { - Long expiresTime = Long.parseLong(expiresTimeStr); - return System.currentTimeMillis() > expiresTime; + return System.currentTimeMillis() > Long.parseLong(expiresTimeStr); } return true; @@ -123,17 +117,15 @@ public String getJsapiTicket() { @Override public boolean isJsapiTicketExpired() { - try (Jedis jedis = this.jedisPool.getResource()) { String expiresTimeStr = jedis.get(JS_API_TICKET_EXPIRES_TIME_KEY); if (expiresTimeStr != null) { - Long expiresTime = Long.parseLong(expiresTimeStr); + long expiresTime = Long.parseLong(expiresTimeStr); return System.currentTimeMillis() > expiresTime; } return true; - } } @@ -146,16 +138,51 @@ public void expireJsapiTicket() { @Override public synchronized void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) { - try (Jedis jedis = this.jedisPool.getResource()) { jedis.set(JS_API_TICKET_KEY, jsapiTicket); - jedis.set(JS_API_TICKET_EXPIRES_TIME_KEY, (System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L + "")); } } + @Override + public String getAgentJsapiTicket() { + try (Jedis jedis = this.jedisPool.getResource()) { + return jedis.get(String.format(AGENT_JSAPI_TICKET_KEY, agentId)); + } + } + + @Override + public boolean isAgentJsapiTicketExpired() { + try (Jedis jedis = this.jedisPool.getResource()) { + String expiresTimeStr = jedis.get(String.format(AGENT_JSAPI_TICKET_EXPIRES_TIME_KEY, agentId)); + + if (expiresTimeStr != null) { + return System.currentTimeMillis() > Long.parseLong(expiresTimeStr); + } + + return true; + } + } + + @Override + public void expireAgentJsapiTicket() { + try (Jedis jedis = this.jedisPool.getResource()) { + jedis.set(String.format(AGENT_JSAPI_TICKET_EXPIRES_TIME_KEY, agentId), "0"); + } + } + + @Override + public void updateAgentJsapiTicket(String jsapiTicket, int expiresInSeconds) { + try (Jedis jedis = this.jedisPool.getResource()) { + jedis.set(String.format(AGENT_JSAPI_TICKET_KEY, agentId), jsapiTicket); + jedis.set(String.format(AGENT_JSAPI_TICKET_EXPIRES_TIME_KEY, agentId), + (System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L + "")); + } + + } + @Override public String getCorpId() { return this.corpId; diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/message/WxCpMessageRouter.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/message/WxCpMessageRouter.java index 6b778be66c..631abdff8b 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/message/WxCpMessageRouter.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/message/WxCpMessageRouter.java @@ -73,7 +73,7 @@ public WxCpMessageRouter(WxCpService wxCpService) { this.wxCpService = wxCpService; this.executorService = Executors.newFixedThreadPool(DEFAULT_THREAD_POOL_SIZE); this.messageDuplicateChecker = new WxMessageInMemoryDuplicateChecker(); - this.sessionManager = new StandardSessionManager(); + this.sessionManager = wxCpService.getSessionManager(); this.exceptionHandler = new LogExceptionHandler(); } 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 654d55111b..16f0108e09 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 @@ -7,7 +7,6 @@ import me.chanjar.weixin.common.util.json.WxErrorAdapter; import me.chanjar.weixin.cp.bean.WxCpChat; import me.chanjar.weixin.cp.bean.WxCpDepart; -import me.chanjar.weixin.cp.bean.WxCpMessage; import me.chanjar.weixin.cp.bean.WxCpTag; import me.chanjar.weixin.cp.bean.WxCpUser; @@ -20,7 +19,6 @@ public class WxCpGsonBuilder { static { INSTANCE.disableHtmlEscaping(); - INSTANCE.registerTypeAdapter(WxCpMessage.class, new WxCpMessageGsonAdapter()); INSTANCE.registerTypeAdapter(WxCpChat.class, new WxCpChatGsonAdapter()); INSTANCE.registerTypeAdapter(WxCpDepart.class, new WxCpDepartGsonAdapter()); INSTANCE.registerTypeAdapter(WxCpUser.class, new WxCpUserGsonAdapter()); diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpMessageGsonAdapter.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpMessageGsonAdapter.java deleted file mode 100644 index 7bed435d30..0000000000 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpMessageGsonAdapter.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. - * - * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended - * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction - * arose from modification of the original source, or other redistribution of this source - * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. - */ -package me.chanjar.weixin.cp.util.json; - -import com.google.gson.*; -import me.chanjar.weixin.common.api.WxConsts; -import me.chanjar.weixin.cp.bean.WxCpMessage; -import me.chanjar.weixin.cp.bean.article.MpnewsArticle; -import me.chanjar.weixin.cp.bean.article.NewArticle; -import org.apache.commons.lang3.StringUtils; - -import java.lang.reflect.Type; - -/** - * @author Daniel Qian - */ -public class WxCpMessageGsonAdapter implements JsonSerializer { - - @Override - public JsonElement serialize(WxCpMessage message, Type typeOfSrc, JsonSerializationContext context) { - JsonObject messageJson = new JsonObject(); - messageJson.addProperty("agentid", message.getAgentId()); - if (StringUtils.isNotBlank(message.getToUser())) { - messageJson.addProperty("touser", message.getToUser()); - } - messageJson.addProperty("msgtype", message.getMsgType()); - - if (StringUtils.isNotBlank(message.getToParty())) { - messageJson.addProperty("toparty", message.getToParty()); - } - if (StringUtils.isNotBlank(message.getToTag())) { - messageJson.addProperty("totag", message.getToTag()); - } - if (WxConsts.KefuMsgType.TEXT.equals(message.getMsgType())) { - JsonObject text = new JsonObject(); - text.addProperty("content", message.getContent()); - messageJson.add("text", text); - } - - if (WxConsts.KefuMsgType.TEXTCARD.equals(message.getMsgType())) { - JsonObject text = new JsonObject(); - text.addProperty("title", message.getTitle()); - text.addProperty("description", message.getDescription()); - text.addProperty("url", message.getUrl()); - text.addProperty("btntxt", message.getBtnTxt()); - messageJson.add("textcard", text); - } - - if (WxConsts.KefuMsgType.IMAGE.equals(message.getMsgType())) { - JsonObject image = new JsonObject(); - image.addProperty("media_id", message.getMediaId()); - messageJson.add("image", image); - } - - if (WxConsts.KefuMsgType.FILE.equals(message.getMsgType())) { - JsonObject image = new JsonObject(); - image.addProperty("media_id", message.getMediaId()); - messageJson.add("file", image); - } - - if (WxConsts.KefuMsgType.VOICE.equals(message.getMsgType())) { - JsonObject voice = new JsonObject(); - voice.addProperty("media_id", message.getMediaId()); - messageJson.add("voice", voice); - } - - if (StringUtils.isNotBlank(message.getSafe())) { - messageJson.addProperty("safe", message.getSafe()); - } - - if (WxConsts.KefuMsgType.VIDEO.equals(message.getMsgType())) { - JsonObject video = new JsonObject(); - video.addProperty("media_id", message.getMediaId()); - video.addProperty("thumb_media_id", message.getThumbMediaId()); - video.addProperty("title", message.getTitle()); - video.addProperty("description", message.getDescription()); - messageJson.add("video", video); - } - - if (WxConsts.KefuMsgType.NEWS.equals(message.getMsgType())) { - JsonObject newsJsonObject = new JsonObject(); - JsonArray articleJsonArray = new JsonArray(); - for (NewArticle article : message.getArticles()) { - JsonObject articleJson = new JsonObject(); - articleJson.addProperty("title", article.getTitle()); - articleJson.addProperty("description", article.getDescription()); - articleJson.addProperty("url", article.getUrl()); - articleJson.addProperty("picurl", article.getPicUrl()); - articleJsonArray.add(articleJson); - } - newsJsonObject.add("articles", articleJsonArray); - messageJson.add("news", newsJsonObject); - } - - if (WxConsts.KefuMsgType.MPNEWS.equals(message.getMsgType())) { - JsonObject newsJsonObject = new JsonObject(); - if (message.getMediaId() != null) { - newsJsonObject.addProperty("media_id", message.getMediaId()); - } else { - JsonArray articleJsonArray = new JsonArray(); - for (MpnewsArticle article : message.getMpnewsArticles()) { - JsonObject articleJson = new JsonObject(); - articleJson.addProperty("title", article.getTitle()); - articleJson.addProperty("thumb_media_id", article.getThumbMediaId()); - articleJson.addProperty("author", article.getAuthor()); - articleJson.addProperty("content_source_url", article.getContentSourceUrl()); - articleJson.addProperty("content", article.getContent()); - articleJson.addProperty("digest", article.getDigest()); - articleJson.addProperty("show_cover_pic", article.getShowCoverPic()); - articleJsonArray.add(articleJson); - } - - newsJsonObject.add("articles", articleJsonArray); - } - messageJson.add("mpnews", newsJsonObject); - } - - return messageJson; - } - -} 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 1dc3f687d5..84ee7c0f2f 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 @@ -6,6 +6,7 @@ * arose from modification of the original source, or other redistribution of this source * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. */ + package me.chanjar.weixin.cp.util.json; import java.lang.reflect.Type; @@ -24,11 +25,14 @@ import me.chanjar.weixin.cp.bean.WxCpUser; /** + * cp user gson adapter. + * * @author Daniel Qian */ public class WxCpUserGsonAdapter implements JsonDeserializer, JsonSerializer { private static final String EXTERNAL_PROFILE = "external_profile"; private static final String EXTERNAL_ATTR = "external_attr"; + private static final String EXTATTR = "extattr"; @Override public WxCpUser deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { @@ -37,14 +41,24 @@ public WxCpUser deserialize(JsonElement json, Type typeOfT, JsonDeserializationC if (o.get("department") != null) { JsonArray departJsonArray = o.get("department").getAsJsonArray(); - Integer[] departIds = new Integer[departJsonArray.size()]; + Long[] departIds = new Long[departJsonArray.size()]; int i = 0; for (JsonElement jsonElement : departJsonArray) { - departIds[i++] = jsonElement.getAsInt(); + departIds[i++] = jsonElement.getAsLong(); } user.setDepartIds(departIds); } + if (o.get("order") != null) { + JsonArray departJsonArray = o.get("order").getAsJsonArray(); + Integer[] orders = new Integer[departJsonArray.size()]; + int i = 0; + for (JsonElement jsonElement : departJsonArray) { + orders[i++] = jsonElement.getAsInt(); + } + user.setOrders(orders); + } + user.setUserId(GsonHelper.getString(o, "userid")); user.setName(GsonHelper.getString(o, "name")); user.setPosition(GsonHelper.getString(o, "position")); @@ -62,64 +76,73 @@ public WxCpUser deserialize(JsonElement json, Type typeOfT, JsonDeserializationC user.setQrCode(GsonHelper.getString(o, "qr_code")); user.setToInvite(GsonHelper.getBoolean(o, "to_invite")); - if (GsonHelper.isNotNull(o.get("extattr"))) { - JsonArray attrJsonElements = o.get("extattr").getAsJsonObject().get("attrs").getAsJsonArray(); - for (JsonElement attrJsonElement : attrJsonElements) { - WxCpUser.Attr attr = new WxCpUser.Attr( - GsonHelper.getString(attrJsonElement.getAsJsonObject(), "name"), - GsonHelper.getString(attrJsonElement.getAsJsonObject(), "value") - ); - user.getExtAttrs().add(attr); - } + if (GsonHelper.isNotNull(o.get(EXTATTR))) { + this.buildExtraAttrs(o, user); } if (GsonHelper.isNotNull(o.get(EXTERNAL_PROFILE))) { - JsonArray attrJsonElements = o.get(EXTERNAL_PROFILE).getAsJsonObject().get(EXTERNAL_ATTR).getAsJsonArray(); - for (JsonElement element : attrJsonElements) { - final Integer type = GsonHelper.getInteger(element.getAsJsonObject(), "type"); - final String name = GsonHelper.getString(element.getAsJsonObject(), "name"); + this.buildExternalAttrs(o, user); + } - switch (type) { - case 0: { - user.getExternalAttrs() - .add(WxCpUser.ExternalAttribute.builder() - .type(type) - .name(name) - .value(GsonHelper.getString(element.getAsJsonObject().get("text").getAsJsonObject(), "value")) - .build() - ); - break; - } - case 1: { - final JsonObject web = element.getAsJsonObject().get("web").getAsJsonObject(); - user.getExternalAttrs() - .add(WxCpUser.ExternalAttribute.builder() - .type(type) - .name(name) - .url(GsonHelper.getString(web, "url")) - .title(GsonHelper.getString(web, "title")) - .build() - ); - break; - } - case 2: { - final JsonObject miniprogram = element.getAsJsonObject().get("miniprogram").getAsJsonObject(); - user.getExternalAttrs() - .add(WxCpUser.ExternalAttribute.builder() - .type(type) - .name(name) - .appid(GsonHelper.getString(miniprogram, "appid")) - .pagePath(GsonHelper.getString(miniprogram, "pagepath")) - .title(GsonHelper.getString(miniprogram, "title")) - .build() - ); - break; - } - default://ignored + return user; + } + + private void buildExtraAttrs(JsonObject o, WxCpUser user) { + JsonArray attrJsonElements = o.get(EXTATTR).getAsJsonObject().get("attrs").getAsJsonArray(); + for (JsonElement attrJsonElement : attrJsonElements) { + WxCpUser.Attr attr = new WxCpUser.Attr( + GsonHelper.getString(attrJsonElement.getAsJsonObject(), "name"), + GsonHelper.getString(attrJsonElement.getAsJsonObject(), "value") + ); + user.getExtAttrs().add(attr); + } + } + + private void buildExternalAttrs(JsonObject o, WxCpUser user) { + JsonArray attrJsonElements = o.get(EXTERNAL_PROFILE).getAsJsonObject().get(EXTERNAL_ATTR).getAsJsonArray(); + for (JsonElement element : attrJsonElements) { + final Integer type = GsonHelper.getInteger(element.getAsJsonObject(), "type"); + final String name = GsonHelper.getString(element.getAsJsonObject(), "name"); + + switch (type) { + case 0: { + user.getExternalAttrs() + .add(WxCpUser.ExternalAttribute.builder() + .type(type) + .name(name) + .value(GsonHelper.getString(element.getAsJsonObject().get("text").getAsJsonObject(), "value")) + .build() + ); + break; } + case 1: { + final JsonObject web = element.getAsJsonObject().get("web").getAsJsonObject(); + user.getExternalAttrs() + .add(WxCpUser.ExternalAttribute.builder() + .type(type) + .name(name) + .url(GsonHelper.getString(web, "url")) + .title(GsonHelper.getString(web, "title")) + .build() + ); + break; + } + case 2: { + final JsonObject miniprogram = element.getAsJsonObject().get("miniprogram").getAsJsonObject(); + user.getExternalAttrs() + .add(WxCpUser.ExternalAttribute.builder() + .type(type) + .name(name) + .appid(GsonHelper.getString(miniprogram, "appid")) + .pagePath(GsonHelper.getString(miniprogram, "pagepath")) + .title(GsonHelper.getString(miniprogram, "title")) + .build() + ); + break; + } + default://ignored } } - return user; } @Override @@ -133,11 +156,20 @@ public JsonElement serialize(WxCpUser user, Type typeOfSrc, JsonSerializationCon } if (user.getDepartIds() != null) { JsonArray jsonArray = new JsonArray(); - for (Integer departId : user.getDepartIds()) { + for (Long departId : user.getDepartIds()) { jsonArray.add(new JsonPrimitive(departId)); } o.add("department", jsonArray); } + + if (user.getOrders() != null) { + JsonArray jsonArray = new JsonArray(); + for (Integer order : user.getOrders()) { + jsonArray.add(new JsonPrimitive(order)); + } + o.add("order", jsonArray); + } + if (user.getPosition() != null) { o.addProperty("position", user.getPosition()); } @@ -191,14 +223,14 @@ public JsonElement serialize(WxCpUser user, Type typeOfSrc, JsonSerializationCon } JsonObject attrsJson = new JsonObject(); attrsJson.add("attrs", attrsJsonArray); - o.add("extattr", attrsJson); + o.add(EXTATTR, attrsJson); } if (user.getExternalAttrs().size() > 0) { JsonArray attrsJsonArray = new JsonArray(); for (WxCpUser.ExternalAttribute attr : user.getExternalAttrs()) { JsonObject attrJson = new JsonObject(); - attrJson.addProperty("type",attr.getType()); + attrJson.addProperty("type", attr.getType()); attrJson.addProperty("name", attr.getName()); switch (attr.getType()) { case 0: { diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMessageAPITest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMessageAPITest.java index 640bbad17d..5c93f38fb1 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMessageAPITest.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMessageAPITest.java @@ -1,11 +1,12 @@ package me.chanjar.weixin.cp.api; +import org.testng.annotations.*; + import com.google.inject.Inject; import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.cp.bean.WxCpMessage; import me.chanjar.weixin.cp.bean.WxCpMessageSendResult; -import org.testng.annotations.*; import static org.testng.Assert.*; @@ -14,7 +15,7 @@ * @author Daniel Qian * */ -@Test(groups = "customMessageAPI") +@Test @Guice(modules = ApiTestModule.class) public class WxCpMessageAPITest { @@ -59,4 +60,51 @@ public void testSendMessage1() throws WxErrorException { System.out.println(messageSendResult.getInvalidUserList()); System.out.println(messageSendResult.getInvalidTagList()); } + + @Test + public void testSendMessage_markdown() throws WxErrorException { + WxCpMessage message = WxCpMessage + .MARKDOWN() + .toUser(configStorage.getUserId()) + .content("您的会议室已经预定,稍后会同步到`邮箱` \n" + + " >**事项详情** \n" + + " >事 项:开会 \n" + + " >组织者:@miglioguan \n" + + " >参与者:@miglioguan、@kunliu、@jamdeezhou、@kanexiong、@kisonwang \n" + + " > \n" + + " >会议室:广州TIT 1楼 301 \n" + + " >日 期:2018年5月18日 \n" + + " >时 间:上午9:00-11:00 \n" + + " > \n" + + " >请准时参加会议。 \n" + + " > \n" + + " >如需修改会议信息,请点击:[修改会议信息](https://work.weixin.qq.com)") + .build(); + + WxCpMessageSendResult messageSendResult = this.wxService.messageSend(message); + assertNotNull(messageSendResult); + System.out.println(messageSendResult); + System.out.println(messageSendResult.getInvalidPartyList()); + System.out.println(messageSendResult.getInvalidUserList()); + System.out.println(messageSendResult.getInvalidTagList()); + } + + @Test + public void testSendMessage_textCard() throws WxErrorException { + WxCpMessage message = WxCpMessage + .TEXTCARD() + .toUser(configStorage.getUserId()) + .btnTxt("更多") + .description( "
2016年9月26日
恭喜你抽中iPhone 7一台,领奖码:xxxx
请于2016年10月10日前联系行政同事领取
") + .url("URL") + .title("领奖通知") + .build(); + + WxCpMessageSendResult messageSendResult = this.wxService.messageSend(message); + assertNotNull(messageSendResult); + System.out.println(messageSendResult); + System.out.println(messageSendResult.getInvalidPartyList()); + System.out.println(messageSendResult.getInvalidUserList()); + System.out.println(messageSendResult.getInvalidTagList()); + } } diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImplTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImplTest.java new file mode 100644 index 0000000000..69a6aa43d8 --- /dev/null +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImplTest.java @@ -0,0 +1,36 @@ +package me.chanjar.weixin.cp.api.impl; + +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 org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.testng.Assert.*; + +/** + *
+ *  Created by BinaryWang on 2019/3/31.
+ * 
+ * + * @author Binary Wang + */ +@Test +@Guice(modules = ApiTestModule.class) +public class BaseWxCpServiceImplTest { + @Inject + protected WxCpService wxService; + + @Test + public void testGetAgentJsapiTicket() throws WxErrorException { + assertThat(this.wxService.getAgentJsapiTicket()).isNotEmpty(); + assertThat(this.wxService.getAgentJsapiTicket(true)).isNotEmpty(); + } + + @Test + public void testJsCode2Session() throws WxErrorException { + assertThat(this.wxService.jsCode2Session("111")).isNotNull(); + } +} diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpChatServiceImplTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpChatServiceImplTest.java index 8317a45ebb..4b25985f11 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpChatServiceImplTest.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpChatServiceImplTest.java @@ -2,15 +2,21 @@ import java.util.Arrays; -import me.chanjar.weixin.cp.bean.WxCpChat; -import org.testng.Assert; -import org.testng.annotations.Guice; -import org.testng.annotations.Test; +import org.testng.*; +import org.testng.annotations.*; +import com.google.common.collect.Lists; import com.google.inject.Inject; - +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.cp.WxCpConsts.AppChatMsgType; import me.chanjar.weixin.cp.api.ApiTestModule; import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.bean.WxCpAppChatMessage; +import me.chanjar.weixin.cp.bean.WxCpChat; +import me.chanjar.weixin.cp.bean.article.MpnewsArticle; +import me.chanjar.weixin.cp.bean.article.NewArticle; + +import static org.assertj.core.api.Assertions.assertThat; /** * 测试群聊服务 @@ -19,28 +25,134 @@ */ @Guice(modules = ApiTestModule.class) public class WxCpChatServiceImplTest { + private String chatId; + private String userId; @Inject - private WxCpService wxCpService; - + private WxCpService cpService; + + @BeforeTest + public void init() { + this.chatId = "mychatid"; + this.userId = ((ApiTestModule.WxXmlCpInMemoryConfigStorage) this.cpService.getWxCpConfigStorage()).getUserId(); + } + @Test - public void create() throws Exception { - wxCpService.getChatService().chatCreate("测试群聊", "gaige_shen", Arrays.asList("gaige_shen", "ZhangXiaoMing"), "mychatid"); + public void testChatCreate() throws Exception { + final String result = cpService.getChatService().chatCreate("测试群聊", userId, + Arrays.asList(userId, userId), chatId); + assertThat(result).isNotEmpty(); + assertThat(result).isEqualTo(chatId); } @Test - public void get() throws Exception { - WxCpChat chat = wxCpService.getChatService().chatGet("mychatid"); + public void testChatGet() throws Exception { + WxCpChat chat = this.cpService.getChatService().chatGet(chatId); System.out.println(chat); Assert.assertEquals(chat.getName(), "测试群聊"); } @Test - public void update() throws Exception { - wxCpService.getChatService().chatUpdate("mychatid", "", "", Arrays.asList("ZhengWuYao"), null); - WxCpChat chat = wxCpService.getChatService().chatGet("mychatid"); + public void testChatUpdate() throws Exception { + this.cpService.getChatService().chatUpdate(chatId, "", "", Arrays.asList("ZhengWuYao"), null); + WxCpChat chat = this.cpService.getChatService().chatGet(chatId); System.out.println(chat); Assert.assertEquals(chat.getUsers().size(), 3); } + @DataProvider + public Object[][] messages() { + return new Object[][]{ + {WxCpAppChatMessage.builder() + .msgType(AppChatMsgType.TEXT) + .chatId(chatId) + .content("你的快递已到\n请携带工卡前往邮件中心领取") + .build() + }, + {WxCpAppChatMessage.builder() + .msgType(AppChatMsgType.IMAGE) + .chatId(chatId) + .mediaId("3_xWGPXZhpOKZrlRISWrjhPrDUZqZ-jIEVzxd56jLuqM") + .build() + }, + {WxCpAppChatMessage.builder() + .msgType(AppChatMsgType.VOICE) + .chatId(chatId) + .mediaId("3X5t6HkdN1hUgB7OzrdRnc8v0yI0CqlAxFxnCkS3msTnTLanpYrV4esLv4foZVnlf") + .build() + }, + {WxCpAppChatMessage.builder() + .msgType(AppChatMsgType.VIDEO) + .chatId(chatId) + .mediaId("3otWyy_acbID8fyltmCOW5hGVD8oa0_p0za5jhukxKTUDoGT71lqTvtQAWoycXpQf") + .title("aaaa") + .description("ddddd") + .build() + }, + {WxCpAppChatMessage.builder() + .msgType(AppChatMsgType.FILE) + .chatId(chatId) + .mediaId("34AyVyDdndVhB4Z2tT-_FYKZ7Xqrr47LPC11GHH4oy7o") + .build() + }, + {WxCpAppChatMessage.builder() + .msgType(AppChatMsgType.TEXTCARD) + .chatId(chatId) + .btnTxt("更多") + .title("领奖通知") + .url("https://zhidao.baidu.com/question/2073647112026042748.html") + .description("
2016年9月26日
恭喜你抽中iPhone 7一台,领奖码:520258
请于2016年10月10日前联系行 政同事领取
") + .build() + }, + {WxCpAppChatMessage.builder() + .msgType(AppChatMsgType.NEWS) + .chatId(chatId) + .articles(Lists.newArrayList(NewArticle.builder() + .title("领奖通知") + .url("https://zhidao.baidu.com/question/2073647112026042748.html") + .description("今年中秋节公司有豪礼相送") + .picUrl("http://res.mail.qq.com/node/ww/wwopenmng/images/independent/doc/test_pic_msg1.png") + .build() + )) + .build() + }, + {WxCpAppChatMessage.builder() + .msgType(AppChatMsgType.MPNEWS) + .chatId(chatId) + .mpnewsArticles(Lists.newArrayList(MpnewsArticle.newBuilder() + .title("地球一小时") + .thumbMediaId("3_xWGPXZhpOKZrlRISWrjhPrDUZqZ-jIEVzxd56jLuqM") + .author("Author") + .contentSourceUrl("https://work.weixin.qq.com") + .content("3月24日20:30-21:30 \n办公区将关闭照明一小时,请各部门同事相互转告") + .digest("3月24日20:30-21:30 \n办公区将关闭照明一小时") + .build() + )) + .build() + }, + {WxCpAppChatMessage.builder() + .msgType(AppChatMsgType.MARKDOWN) + .chatId(chatId) + .content("您的会议室已经预定,稍后会同步到`邮箱` \n" + + " >**事项详情** \n" + + " >事 项:开会 \n" + + " >组织者:@miglioguan \n" + + " >参与者:@miglioguan、@kunliu、@jamdeezhou、@kanexiong、@kisonwang \n" + + " > \n" + + " >会议室:广州TIT 1楼 301 \n" + + " >日 期:2018年5月18日 \n" + + " >时 间:上午9:00-11:00 \n" + + " > \n" + + " >请准时参加会议。 \n" + + " > \n" + + " >如需修改会议信息,请点击:[修改会议信息](https://work.weixin.qq.com)") + .build() + }, + }; + } + + @Test(dataProvider = "messages") + public void testSendMsg(WxCpAppChatMessage message) throws WxErrorException { + this.cpService.getChatService().sendMsg(message); + } } 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 097a6ee71f..57957d3fb6 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 @@ -31,7 +31,7 @@ public void testCreate() throws Exception { cpDepart.setName("子部门" + System.currentTimeMillis()); cpDepart.setParentId(1L); cpDepart.setOrder(1L); - Integer departId = this.wxCpService.getDepartmentService().create(cpDepart); + Long departId = this.wxCpService.getDepartmentService().create(cpDepart); System.out.println(departId); } 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 new file mode 100644 index 0000000000..9497a5626b --- /dev/null +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpOAServiceImplTest.java @@ -0,0 +1,49 @@ +package me.chanjar.weixin.cp.api.impl; + +import com.google.common.collect.Lists; +import com.google.gson.Gson; +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.WxCpCheckinData; +import me.chanjar.weixin.cp.bean.WxCpCheckinOption; +import org.apache.commons.lang3.time.DateFormatUtils; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Element + * @date 2019-04-20 13:46 + */ +@Guice(modules = ApiTestModule.class) +public class WxCpOAServiceImplTest { + @Inject + protected WxCpService wxService; + + @Test + public void testGetCheckinData() throws ParseException, WxErrorException { + Date startTime = DateFormatUtils.ISO_8601_EXTENDED_DATE_FORMAT.parse("2019-04-11"); + Date endTime = DateFormatUtils.ISO_8601_EXTENDED_DATE_FORMAT.parse("2019-05-10"); + + List results = wxService.getOAService() + .getCheckinData(1, startTime, endTime, Lists.newArrayList("binary")); + + assertThat(results).isNotNull(); + } + + @Test + public void testGetCheckinOption() throws WxErrorException { + Date now = new Date(); + List results = wxService.getOAService() + .getCheckinOption(now, Lists.newArrayList("binary")); + assertThat(results).isNotNull(); + } +} diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpTaskCardServiceImplTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpTaskCardServiceImplTest.java new file mode 100644 index 0000000000..be387548b9 --- /dev/null +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpTaskCardServiceImplTest.java @@ -0,0 +1,65 @@ +package me.chanjar.weixin.cp.api.impl; + +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.WxCpMessage; +import me.chanjar.weixin.cp.bean.WxCpMessageSendResult; +import me.chanjar.weixin.cp.bean.taskcard.TaskCardButton; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import java.util.Arrays; + +import static org.testng.Assert.assertNotNull; + +/** + * 测试任务卡片服务 + * + * @author Jeff + * @date 2019-05-16 + */ +@Guice(modules = ApiTestModule.class) +public class WxCpTaskCardServiceImplTest { + + @Inject + private WxCpService wxCpService; + + @Test + public void testSendTaskCard() throws WxErrorException { + TaskCardButton btn1 = TaskCardButton.builder() + .key("key1") + .name("同意") + .replaceName("已同意") + .bold(true) + .build(); + TaskCardButton btn2 = TaskCardButton.builder() + .key("key2") + .name("拒绝") + .replaceName("已拒绝") + .color("red") + .build(); + WxCpMessage message = WxCpMessage.TASKCARD() + .toUser("jeff|mr.t") + .title("有一个待审批的请求") + .description("申请:购买图书\n金额:100 元") + .taskId("task_1") + .url("http://www.qq.com") + .buttons(Arrays.asList(btn1, btn2)) + .build(); + + WxCpMessageSendResult messageSendResult = this.wxCpService.messageSend(message); + assertNotNull(messageSendResult); + System.out.println(messageSendResult); + System.out.println(messageSendResult.getInvalidPartyList()); + System.out.println(messageSendResult.getInvalidUserList()); + System.out.println(messageSendResult.getInvalidTagList()); + } + + @Test + public void testUpdate() throws Exception { + wxCpService.getTaskCardService().update(Arrays.asList("jeff", "mr.t"), "task_1", "key1"); + } + +} diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpUserServiceImplTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpUserServiceImplTest.java index f473fe6be7..2ec33e7ba7 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpUserServiceImplTest.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpUserServiceImplTest.java @@ -42,7 +42,7 @@ public void testCreate() throws Exception { WxCpUser user = new WxCpUser(); user.setUserId(userId); user.setName("Some Woman"); - user.setDepartIds(new Integer[]{2}); + user.setDepartIds(new Long[]{2L}); user.setEmail("none@none.com"); user.setGender(Gender.FEMALE); user.setMobile("13560084979"); diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpAgentTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpAgentTest.java index 74b8269d3d..9f187e43aa 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpAgentTest.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpAgentTest.java @@ -1,7 +1,7 @@ package me.chanjar.weixin.cp.bean; -import org.testng.Assert; -import org.testng.annotations.Test; +import org.testng.*; +import org.testng.annotations.*; /** * Created by huansinho on 2018/4/13. @@ -10,7 +10,13 @@ public class WxCpAgentTest { public void testDeserialize() { - String json = "{\"errcode\": 0,\"errmsg\": \"ok\",\"agentid\": 9,\"name\": \"测试应用\",\"square_logo_url\": \"http://wx.qlogo.cn/mmhead/alksjf;lasdjf;lasjfuodiuj3rj2o34j/0\",\"description\": \"这是一个企业号应用\",\"allow_userinfos\": {\"user\": [{\"userid\": \"0009854\"}, {\"userid\": \"1723\"}, {\"userid\": \"5625\"}]},\"allow_partys\": {\"partyid\": [42762742]},\"allow_tags\": {\"tagid\": [23, 22, 35, 19, 32, 125, 133, 46, 150, 38, 183, 9, 7]},\"close\": 0,\"redirect_domain\": \"weixin.com.cn\",\"report_location_flag\": 0,\"isreportenter\": 0,\"home_url\": \"\"}"; + String json = "{\"errcode\": 0,\"errmsg\": \"ok\",\"agentid\": 9,\"name\": \"测试应用\"," + + "\"square_logo_url\": \"http://wx.qlogo.cn/mmhead/alksjf;lasdjf;lasjfuodiuj3rj2o34j/0\"," + + "\"description\": \"这是一个企业号应用\",\"allow_userinfos\": {\"user\": [{\"userid\": \"0009854\"}," + + " {\"userid\": \"1723\"}, {\"userid\": \"5625\"}]},\"allow_partys\": {\"partyid\": [42762742]}," + + "\"allow_tags\": {\"tagid\": [23, 22, 35, 19, 32, 125, 133, 46, 150, 38, 183, 9, 7]}," + + "\"close\": 0,\"redirect_domain\": \"weixin.com.cn\",\"report_location_flag\": 0," + + "\"isreportenter\": 0,\"home_url\": \"\"}"; WxCpAgent wxCpAgent = WxCpAgent.fromJson(json); @@ -18,7 +24,8 @@ public void testDeserialize() { Assert.assertEquals(new Integer[]{42762742}, wxCpAgent.getAllowParties().getPartyIds().toArray()); - Assert.assertEquals(new Integer[]{23, 22, 35, 19, 32, 125, 133, 46, 150, 38, 183, 9, 7}, wxCpAgent.getAllowTags().getTagIds().toArray()); + Assert.assertEquals(new Integer[]{23, 22, 35, 19, 32, 125, 133, 46, 150, 38, 183, 9, 7}, + wxCpAgent.getAllowTags().getTagIds().toArray()); } diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpMessageTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpMessageTest.java index 0af121a835..c54211758b 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpMessageTest.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpMessageTest.java @@ -2,10 +2,12 @@ import me.chanjar.weixin.cp.bean.article.MpnewsArticle; import me.chanjar.weixin.cp.bean.article.NewArticle; +import me.chanjar.weixin.cp.bean.taskcard.TaskCardButton; import org.testng.annotations.Test; +import java.util.Arrays; + import static org.assertj.core.api.Assertions.assertThat; -import static org.testng.Assert.assertEquals; @Test public class WxCpMessageTest { @@ -19,12 +21,16 @@ public void testTextBuild() { public void testTextCardBuild() { WxCpMessage reply = WxCpMessage.TEXTCARD().toUser("OPENID") .title("领奖通知") - .description("
2016年9月26日
恭喜你抽中iPhone 7一台,领奖码:xxxx
请于2016年10月10日前联系行政同事领取
") + .description("
2016年9月26日
恭喜你抽中iPhone 7一台," + + "领奖码:xxxx
请于2016年10月10日前联系行政同事领取
") .url("http://www.qq.com") .btnTxt("更多") .build(); assertThat(reply.toJson()) - .isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"textcard\",\"textcard\":{\"title\":\"领奖通知\",\"description\":\"
2016年9月26日
恭喜你抽中iPhone 7一台,领奖码:xxxx
请于2016年10月10日前联系行政同事领取
\",\"url\":\"http://www.qq.com\",\"btntxt\":\"更多\"},\"safe\":\"0\"}"); + .isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"textcard\",\"textcard\":{\"title\":\"领奖通知\"," + + "\"description\":\"
2016年9月26日
" + + "恭喜你抽中iPhone 7一台,领奖码:xxxx
请于2016年10月10日前联系行政同事领取
\"," + + "\"url\":\"http://www.qq.com\",\"btntxt\":\"更多\"},\"safe\":\"0\"}"); } public void testImageBuild() { @@ -40,9 +46,11 @@ public void testVoiceBuild() { } public void testVideoBuild() { - WxCpMessage reply = WxCpMessage.VIDEO().toUser("OPENID").title("TITLE").mediaId("MEDIA_ID").thumbMediaId("MEDIA_ID").description("DESCRIPTION").build(); + WxCpMessage reply = WxCpMessage.VIDEO().toUser("OPENID").title("TITLE").mediaId("MEDIA_ID").thumbMediaId("MEDIA_ID") + .description("DESCRIPTION").build(); assertThat(reply.toJson()) - .isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"video\",\"safe\":\"0\",\"video\":{\"media_id\":\"MEDIA_ID\",\"thumb_media_id\":\"MEDIA_ID\",\"title\":\"TITLE\",\"description\":\"DESCRIPTION\"}}"); + .isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"video\",\"video\":{\"media_id\":\"MEDIA_ID\"," + + "\"thumb_media_id\":\"MEDIA_ID\",\"title\":\"TITLE\",\"description\":\"DESCRIPTION\"},\"safe\":\"0\"}"); } public void testNewsBuild() { @@ -61,7 +69,10 @@ public void testNewsBuild() { WxCpMessage reply = WxCpMessage.NEWS().toUser("OPENID").addArticle(article1).addArticle(article2).build(); assertThat(reply.toJson()) - .isEqualTo( "{\"touser\":\"OPENID\",\"msgtype\":\"news\",\"safe\":\"0\",\"news\":{\"articles\":[{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"},{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}]}}"); + .isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"news\",\"news\":{\"articles\":" + + "[{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}," + + "{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}]}," + + "\"safe\":\"0\"}"); } public void testMpnewsBuild_with_articles() { @@ -88,14 +99,45 @@ public void testMpnewsBuild_with_articles() { WxCpMessage reply = WxCpMessage.MPNEWS().toUser("OPENID").addArticle(article1, article2).build(); assertThat(reply.toJson()) - .isEqualTo( "{\"touser\":\"OPENID\",\"msgtype\":\"mpnews\",\"safe\":\"0\",\"mpnews\":{\"articles\":[{\"title\":\"Happy Day\",\"thumb_media_id\":\"thumb\",\"author\":\"aaaaaa\",\"content_source_url\":\"nice url\",\"content\":\"hahaha\",\"digest\":\"digest\",\"show_cover_pic\":\"heihei\"},{\"title\":\"Happy Day\",\"thumb_media_id\":\"thumb\",\"author\":\"aaaaaa\",\"content_source_url\":\"nice url\",\"content\":\"hahaha\",\"digest\":\"digest\",\"show_cover_pic\":\"heihei\"}]}}"); + .isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"mpnews\",\"mpnews\":{\"articles\":" + + "[{\"title\":\"Happy Day\",\"thumb_media_id\":\"thumb\",\"author\":\"aaaaaa\"," + + "\"content_source_url\":\"nice url\",\"content\":\"hahaha\",\"digest\":\"digest\",\"show_cover_pic\":\"heihei\"}" + + ",{\"title\":\"Happy Day\",\"thumb_media_id\":\"thumb\",\"author\":\"aaaaaa\"," + + "\"content_source_url\":\"nice url\",\"content\":\"hahaha\",\"digest\":\"digest\",\"show_cover_pic\":\"heihei\"}]}," + + "\"safe\":\"0\"}"); } public void testMpnewsBuild_with_media_id() { WxCpMessage reply = WxCpMessage.MPNEWS().toUser("OPENID").mediaId("mmm").build(); assertThat(reply.toJson()) - .isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"mpnews\",\"safe\":\"0\",\"mpnews\":{\"media_id\":\"mmm\"}}"); + .isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"mpnews\",\"mpnews\":{\"media_id\":\"mmm\"},\"safe\":\"0\"}"); + } + + public void testTaskCardBuilder() { + TaskCardButton button1 = TaskCardButton.builder() + .key("yes") + .name("批准") + .replaceName("已批准") + .color("blue") + .bold(true) + .build(); + TaskCardButton button2 = TaskCardButton.builder() + .key("yes") + .name("拒绝") + .replaceName("已拒绝") + .color("red") + .bold(false) + .build(); + WxCpMessage reply = WxCpMessage.TASKCARD().toUser("OPENID") + .title("任务卡片") + .description("有一条待处理任务") + .url("http://www.qq.com") + .taskId("task_123") + .buttons(Arrays.asList(button1, button2)) + .build(); + assertThat(reply.toJson()) + .isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"taskcard\",\"taskcard\":{\"title\":\"任务卡片\",\"description\":\"有一条待处理任务\",\"url\":\"http://www.qq.com\",\"task_id\":\"task_123\",\"btn\":[{\"key\":\"yes\",\"name\":\"批准\",\"replace_name\":\"已批准\",\"color\":\"blue\",\"is_bold\":true},{\"key\":\"yes\",\"name\":\"拒绝\",\"replace_name\":\"已拒绝\",\"color\":\"red\",\"is_bold\":false}]}}"); } } diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlMessageTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlMessageTest.java index 68c57c3d89..89c0396e08 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlMessageTest.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlMessageTest.java @@ -1,9 +1,11 @@ package me.chanjar.weixin.cp.bean; import me.chanjar.weixin.common.api.WxConsts; -import org.testng.annotations.*; +import org.testng.annotations.Test; -import static org.testng.Assert.*; +import static me.chanjar.weixin.cp.WxCpConsts.EventType.TASKCARD_CLICK; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; @Test public class WxCpXmlMessageTest { @@ -117,4 +119,58 @@ public void testSendPicsInfo() { assertEquals(wxMessage.getSendPicsInfo().getPicList().get(0).getPicMd5Sum(), "aef52ae501537e552725c5d7f99c1741"); assertEquals(wxMessage.getSendPicsInfo().getPicList().get(1).getPicMd5Sum(), "c4564632a4fab91378c39bea6aad6f9e"); } + + public void testExtAttr() { + + String xml = "" + + " " + + " " + + " 1557241961" + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + "
" + + "
"; + WxCpXmlMessage wxMessage = WxCpXmlMessage.fromXml(xml); + assertEquals(wxMessage.getToUserName(), "w56c9fe3d50ad1ea2"); + assertEquals(wxMessage.getFromUserName(), "sys"); + assertEquals(wxMessage.getCreateTime(), new Long(1557241961)); + assertEquals(wxMessage.getMsgType(), WxConsts.XmlMsgType.EVENT); + assertEquals(wxMessage.getEvent(), "change_contact"); + assertEquals(wxMessage.getChangeType(), "update_user"); + assertEquals(wxMessage.getUserId(), "zhangsan"); + assertNotNull(wxMessage.getExtAttrs()); + assertNotNull(wxMessage.getExtAttrs().getItems()); + assertEquals(wxMessage.getExtAttrs().getItems().size(), 3); + assertEquals(wxMessage.getExtAttrs().getItems().get(0).getName(), "爱好"); + + } + + public void testTaskCardEvent() { + String xml = "" + + "" + + "" + + "123456789" + + "" + + "" + + "" + + "" + + "1" + + ""; + WxCpXmlMessage wxMessage = WxCpXmlMessage.fromXml(xml); + assertEquals(wxMessage.getToUserName(), "toUser"); + assertEquals(wxMessage.getFromUserName(), "FromUser"); + assertEquals(wxMessage.getCreateTime(), Long.valueOf(123456789L)); + assertEquals(wxMessage.getMsgType(), WxConsts.XmlMsgType.EVENT); + assertEquals(wxMessage.getAgentId(), Integer.valueOf(1)); + assertEquals(wxMessage.getEvent(), TASKCARD_CLICK); + assertEquals(wxMessage.getEventKey(), "key111"); + assertEquals(wxMessage.getTaskId(), "taskid111"); + } } diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpDemoInMemoryConfigStorage.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpDemoInMemoryConfigStorage.java index 93ff1bbc0a..cbb3503760 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpDemoInMemoryConfigStorage.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpDemoInMemoryConfigStorage.java @@ -1,28 +1,23 @@ package me.chanjar.weixin.cp.demo; +import java.io.InputStream; + import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.ToString; import me.chanjar.weixin.common.util.xml.XStreamInitializer; import me.chanjar.weixin.cp.config.WxCpInMemoryConfigStorage; -import java.io.InputStream; - /** * @author Daniel Qian */ @XStreamAlias("xml") -class WxCpDemoInMemoryConfigStorage extends WxCpInMemoryConfigStorage { - +@ToString +public class WxCpDemoInMemoryConfigStorage extends WxCpInMemoryConfigStorage { public static WxCpDemoInMemoryConfigStorage fromXml(InputStream is) { XStream xstream = XStreamInitializer.getInstance(); xstream.processAnnotations(WxCpDemoInMemoryConfigStorage.class); return (WxCpDemoInMemoryConfigStorage) xstream.fromXML(is); } - @Override - public String toString() { - return "SimpleWxConfigProvider [appidOrCorpid=" + this.corpId + ", corpSecret=" + this.corpSecret + ", accessToken=" + this.accessToken - + ", expiresTime=" + this.expiresTime + ", token=" + this.token + ", aesKey=" + this.aesKey + "]"; - } - } diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/util/json/WxCpUserGsonAdapterTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/util/json/WxCpUserGsonAdapterTest.java index ec553f7521..9cdc51f885 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/util/json/WxCpUserGsonAdapterTest.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/util/json/WxCpUserGsonAdapterTest.java @@ -76,6 +76,13 @@ public void testDeserialize() { final WxCpUser user = WxCpUser.fromJson(userJson); assertThat(user).isNotNull(); + + assertThat(user.getOrders()).isNotEmpty(); + assertThat(user.getOrders().length).isEqualTo(2); + assertThat(user.getOrders()[0]).isEqualTo(1); + assertThat(user.getOrders()[1]).isEqualTo(2); + + assertThat(user.getExternalAttrs()).isNotEmpty(); final WxCpUser.ExternalAttribute externalAttr1 = user.getExternalAttrs().get(0); @@ -100,6 +107,7 @@ public void testDeserialize() { @Test public void testSerialize() { WxCpUser user = new WxCpUser(); + user.setOrders(new Integer[]{1, 2}); user.addExternalAttr(WxCpUser.ExternalAttribute.builder() .type(0) .name("文本名称") @@ -119,6 +127,10 @@ public void testSerialize() { .title("my miniprogram") .build()); - assertThat(user.toJson()).isEqualTo("{\"external_profile\":{\"external_attr\":[{\"type\":0,\"name\":\"文本名称\",\"text\":{\"value\":\"文本\"}},{\"type\":1,\"name\":\"网页名称\",\"web\":{\"url\":\"http://www.test.com\",\"title\":\"标题\"}},{\"type\":2,\"name\":\"测试app\",\"miniprogram\":{\"appid\":\"wx8bd80126147df384\",\"pagepath\":\"/index\",\"title\":\"my miniprogram\"}}]}}"); + assertThat(user.toJson()).isEqualTo("{\"order\":[1,2],\"external_profile\":{\"external_attr\":" + + "[{\"type\":0,\"name\":\"文本名称\",\"text\":{\"value\":\"文本\"}}," + + "{\"type\":1,\"name\":\"网页名称\",\"web\":{\"url\":\"http://www.test.com\",\"title\":\"标题\"}}," + + "{\"type\":2,\"name\":\"测试app\"," + + "\"miniprogram\":{\"appid\":\"wx8bd80126147df384\",\"pagepath\":\"/index\",\"title\":\"my miniprogram\"}}]}}"); } } diff --git a/weixin-java-miniapp/pom.xml b/weixin-java-miniapp/pom.xml index 0e0c45e2a6..de3c9f50df 100644 --- a/weixin-java-miniapp/pom.xml +++ b/weixin-java-miniapp/pom.xml @@ -7,11 +7,12 @@ com.github.binarywang wx-java - 3.3.0 + 3.4.0 + weixin-java-miniapp - WxJava - MiniApp - 微信小程序Java SDK + WxJava - MiniApp Java SDK + 微信小程序 Java SDK @@ -70,6 +71,11 @@ redis.clients jedis + + org.bouncycastle + bcpkix-jdk15on + 1.59 + org.projectlombok lombok diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaCodeService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaCodeService.java index fbe0818493..ad102805da 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaCodeService.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaCodeService.java @@ -1,14 +1,10 @@ package cn.binarywang.wx.miniapp.api; -import java.util.List; - -import cn.binarywang.wx.miniapp.bean.code.WxMaCategory; -import cn.binarywang.wx.miniapp.bean.code.WxMaCodeAuditStatus; -import cn.binarywang.wx.miniapp.bean.code.WxMaCodeCommitRequest; -import cn.binarywang.wx.miniapp.bean.code.WxMaCodeSubmitAuditRequest; -import cn.binarywang.wx.miniapp.bean.code.WxMaCodeVersionDistribution; +import cn.binarywang.wx.miniapp.bean.code.*; import me.chanjar.weixin.common.error.WxErrorException; +import java.util.List; + /** * 小程序代码管理相关 API(大部分只能是第三方平台调用) * 文档:https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1489140610_Uavc4&token=&lang=zh_CN @@ -18,7 +14,7 @@ */ public interface WxMaCodeService { /** - * 为授权的小程序帐号上传小程序代码 + * 为授权的小程序帐号上传小程序代码. */ String COMMIT_URL = "https://api.weixin.qq.com/wxa/commit"; String GET_QRCODE_URL = "https://api.weixin.qq.com/wxa/get_qrcode"; @@ -35,7 +31,7 @@ public interface WxMaCodeService { String UNDO_CODE_AUDIT_URL = "https://api.weixin.qq.com/wxa/undocodeaudit"; /** - * 为授权的小程序帐号上传小程序代码(仅仅支持第三方开放平台) + * 为授权的小程序帐号上传小程序代码(仅仅支持第三方开放平台). * * @param commitRequest 参数 * @throws WxErrorException 上传失败时抛出,具体错误码请看类注释文档 @@ -43,19 +39,19 @@ public interface WxMaCodeService { void commit(WxMaCodeCommitRequest commitRequest) throws WxErrorException; /** - * 获取体验小程序的体验二维码 + * 获取体验小程序的体验二维码. * 文档地址: * https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1489140610_Uavc4&token=&lang=zh_CN * * @param path 指定体验版二维码跳转到某个具体页面(如果不需要的话,则不需要填path参数,可在路径后以“?参数”方式传入参数) - * 具体的路径加参数需要urlencode(方法内部处理),比如page/index?action=1编码后得到page%2Findex%3Faction%3D1 + * 具体的路径加参数需要urlencode(方法内部处理),比如page/index?action=1编码后得到page%2Findex%3Faction%3D1 * @return 二维码 bytes * @throws WxErrorException 上传失败时抛出,具体错误码请看类注释文档 */ byte[] getQrCode(String path) throws WxErrorException; /** - * 获取授权小程序帐号的可选类目 + * 获取授权小程序帐号的可选类目. * * @return List * @throws WxErrorException 获取失败时返回,具体错误码请看此接口的注释文档 @@ -63,7 +59,7 @@ public interface WxMaCodeService { List getCategory() throws WxErrorException; /** - * 获取小程序的第三方提交代码的页面配置(仅供第三方开发者代小程序调用) + * 获取小程序的第三方提交代码的页面配置(仅供第三方开发者代小程序调用). * * @return page_list 页面配置列表 * @throws WxErrorException 获取失败时返回,具体错误码请看此接口的注释文档 @@ -71,7 +67,7 @@ public interface WxMaCodeService { List getPage() throws WxErrorException; /** - * 将第三方提交的代码包提交审核(仅供第三方开发者代小程序调用) + * 将第三方提交的代码包提交审核(仅供第三方开发者代小程序调用). * * @param auditRequest 提交审核参数 * @return 审核编号 @@ -80,7 +76,7 @@ public interface WxMaCodeService { long submitAudit(WxMaCodeSubmitAuditRequest auditRequest) throws WxErrorException; /** - * 查询某个指定版本的审核状态(仅供第三方代小程序调用) + * 查询某个指定版本的审核状态(仅供第三方代小程序调用). * * @param auditId 提交审核时获得的审核id * @return 审核状态 @@ -89,7 +85,7 @@ public interface WxMaCodeService { WxMaCodeAuditStatus getAuditStatus(long auditId) throws WxErrorException; /** - * 查询最新一次提交的审核状态(仅供第三方代小程序调用) + * 查询最新一次提交的审核状态(仅供第三方代小程序调用). * * @return 审核状态 * @throws WxErrorException 查询失败时返回,具体错误码请看此接口的注释文档 @@ -97,14 +93,14 @@ public interface WxMaCodeService { WxMaCodeAuditStatus getLatestAuditStatus() throws WxErrorException; /** - * 发布已通过审核的小程序(仅供第三方代小程序调用) + * 发布已通过审核的小程序(仅供第三方代小程序调用). * * @throws WxErrorException 发布失败时抛出,具体错误码请看此接口的注释文档 */ void release() throws WxErrorException; /** - * 修改小程序线上代码的可见状态(仅供第三方代小程序调用) + * 修改小程序线上代码的可见状态(仅供第三方代小程序调用). * * @param action 设置可访问状态,发布后默认可访问,close为不可见,open为可见 * @throws WxErrorException 发布失败时抛出,具体错误码请看此接口的注释文档 @@ -112,14 +108,14 @@ public interface WxMaCodeService { void changeVisitStatus(String action) throws WxErrorException; /** - * 小程序版本回退(仅供第三方代小程序调用) + * 小程序版本回退(仅供第三方代小程序调用). * * @throws WxErrorException 失败时抛出,具体错误码请看此接口的注释文档 */ void revertCodeRelease() throws WxErrorException; /** - * 查询当前设置的最低基础库版本及各版本用户占比 (仅供第三方代小程序调用) + * 查询当前设置的最低基础库版本及各版本用户占比 (仅供第三方代小程序调用). * * @return 小程序版本分布信息 * @throws WxErrorException 失败时抛出,具体错误码请看此接口的注释文档 @@ -127,7 +123,7 @@ public interface WxMaCodeService { WxMaCodeVersionDistribution getSupportVersion() throws WxErrorException; /** - * 设置最低基础库版本(仅供第三方代小程序调用) + * 设置最低基础库版本(仅供第三方代小程序调用). * * @param version 版本 * @throws WxErrorException 失败时抛出,具体错误码请看此接口的注释文档 @@ -135,7 +131,7 @@ public interface WxMaCodeService { void setSupportVersion(String version) throws WxErrorException; /** - * 小程序审核撤回 + * 小程序审核撤回. * 单个帐号每天审核撤回次数最多不超过1次,一个月不超过10次 * * @throws WxErrorException 失败时抛出,具体错误码请看此接口的注释文档 diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaMsgService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaMsgService.java index a838417e23..f0dbccd81a 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaMsgService.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaMsgService.java @@ -20,7 +20,7 @@ public interface WxMaMsgService { /** *
    * 发送客服消息
-   * 详情请见: 发送客服消息
+   * 详情请见: 发送客服消息
    * 接口url格式:https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN
    * 
*/ @@ -29,7 +29,7 @@ public interface WxMaMsgService { /** *
    * 发送模板消息
-   * 详情请见: 发送模板消息
+   * 详情请见: 发送模板消息
    * 接口url格式:https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=ACCESS_TOKEN
    * 
*/ 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 efa53939a8..578acc56af 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 @@ -17,6 +17,10 @@ public interface WxMaService { String GET_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s"; String JSCODE_TO_SESSION_URL = "https://api.weixin.qq.com/sns/jscode2session"; + /** + * getPaidUnionId + */ + String GET_PAID_UNION_ID_URL = "https://api.weixin.qq.com/wxa/getpaidunionid"; /** * 获取登录后的session信息. @@ -56,6 +60,22 @@ public interface WxMaService { */ String getAccessToken(boolean forceRefresh) throws WxErrorException; + /** + *
+   * 用户支付完成后,获取该用户的 UnionId,无需用户授权。本接口支持第三方平台代理查询。
+   *
+   * 注意:调用前需要用户完成支付,且在支付后的五分钟内有效。
+   * 请求地址: GET https://api.weixin.qq.com/wxa/getpaidunionid?access_token=ACCESS_TOKEN&openid=OPENID
+   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/api/getPaidUnionId.html
+   * 
+ * + * @param openid 必填 支付用户唯一标识 + * @param transactionId 非必填 微信支付订单号 + * @param mchId 非必填 微信支付分配的商户号,和商户订单号配合使用 + * @param outTradeNo 非必填 微信支付商户订单号,和商户号配合使用 + */ + String getPaidUnionId(String openid, String transactionId, String mchId, String outTradeNo) throws WxErrorException; + /** * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求. */ @@ -168,18 +188,21 @@ public interface WxMaService { /** * 返回分享相关查询服务. + * * @return WxMaShareService */ WxMaShareService getShareService(); /** * 返回微信运动相关接口服务对象. + * * @return WxMaShareService */ WxMaRunService getRunService(); /** * 返回内容安全相关接口服务对象. + * * @return WxMaShareService */ WxMaSecCheckService getSecCheckService(); diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImpl.java index 280a052e4a..d4f9721805 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImpl.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImpl.java @@ -1,47 +1,33 @@ package cn.binarywang.wx.miniapp.api.impl; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.locks.Lock; - -import org.apache.http.HttpHost; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.BasicResponseHandler; -import org.apache.http.impl.client.CloseableHttpClient; - -import cn.binarywang.wx.miniapp.api.WxMaAnalysisService; -import cn.binarywang.wx.miniapp.api.WxMaCodeService; -import cn.binarywang.wx.miniapp.api.WxMaJsapiService; -import cn.binarywang.wx.miniapp.api.WxMaMediaService; -import cn.binarywang.wx.miniapp.api.WxMaMsgService; -import cn.binarywang.wx.miniapp.api.WxMaQrcodeService; -import cn.binarywang.wx.miniapp.api.WxMaRunService; -import cn.binarywang.wx.miniapp.api.WxMaSecCheckService; -import cn.binarywang.wx.miniapp.api.WxMaService; -import cn.binarywang.wx.miniapp.api.WxMaSettingService; -import cn.binarywang.wx.miniapp.api.WxMaShareService; -import cn.binarywang.wx.miniapp.api.WxMaTemplateService; -import cn.binarywang.wx.miniapp.api.WxMaUserService; +import cn.binarywang.wx.miniapp.api.*; import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; import cn.binarywang.wx.miniapp.config.WxMaConfig; import com.google.common.base.Joiner; import com.google.gson.Gson; +import com.google.gson.JsonParser; import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.WxType; import me.chanjar.weixin.common.bean.WxAccessToken; import me.chanjar.weixin.common.error.WxError; import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.util.DataUtils; import me.chanjar.weixin.common.util.crypto.SHA1; -import me.chanjar.weixin.common.util.http.HttpType; -import me.chanjar.weixin.common.util.http.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.*; import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpHost; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.BasicResponseHandler; +import org.apache.http.impl.client.CloseableHttpClient; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.locks.Lock; import static cn.binarywang.wx.miniapp.constant.WxMaConstants.ErrorCode.*; @@ -50,6 +36,7 @@ */ @Slf4j public class WxMaServiceImpl implements WxMaService, RequestHttp { + private static final JsonParser JSON_PARSER = new JsonParser(); private CloseableHttpClient httpClient; private HttpHost httpProxy; private WxMaConfig wxMaConfig; @@ -114,40 +101,68 @@ public RequestHttp getRequestHttp() { @Override public String getAccessToken(boolean forceRefresh) throws WxErrorException { + if (!this.getWxMaConfig().isAccessTokenExpired() && !forceRefresh) { + return this.getWxMaConfig().getAccessToken(); + } + Lock lock = this.getWxMaConfig().getAccessTokenLock(); + lock.lock(); try { - lock.lock(); - - if (this.getWxMaConfig().isAccessTokenExpired() || forceRefresh) { - String url = String.format(WxMaService.GET_ACCESS_TOKEN_URL, this.getWxMaConfig().getAppid(), - this.getWxMaConfig().getSecret()); - try { - HttpGet httpGet = new HttpGet(url); - if (this.getRequestHttpProxy() != null) { - RequestConfig config = RequestConfig.custom().setProxy(this.getRequestHttpProxy()).build(); - httpGet.setConfig(config); - } - try (CloseableHttpResponse response = getRequestHttpClient().execute(httpGet)) { - String resultContent = new BasicResponseHandler().handleResponse(response); - WxError error = WxError.fromJson(resultContent); - if (error.getErrorCode() != 0) { - throw new WxErrorException(error); - } - WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); - this.getWxMaConfig().updateAccessToken(accessToken.getAccessToken(), - accessToken.getExpiresIn()); - } finally { - httpGet.releaseConnection(); + String url = String.format(WxMaService.GET_ACCESS_TOKEN_URL, this.getWxMaConfig().getAppid(), + this.getWxMaConfig().getSecret()); + try { + HttpGet httpGet = new HttpGet(url); + if (this.getRequestHttpProxy() != null) { + RequestConfig config = RequestConfig.custom().setProxy(this.getRequestHttpProxy()).build(); + httpGet.setConfig(config); + } + try (CloseableHttpResponse response = getRequestHttpClient().execute(httpGet)) { + String resultContent = new BasicResponseHandler().handleResponse(response); + WxError error = WxError.fromJson(resultContent); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); } - } catch (IOException e) { - throw new RuntimeException(e); + WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); + this.getWxMaConfig().updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); + + return this.getWxMaConfig().getAccessToken(); + } finally { + httpGet.releaseConnection(); } + } catch (IOException e) { + throw new RuntimeException(e); } } finally { lock.unlock(); } - return this.getWxMaConfig().getAccessToken(); + } + + @Override + public String getPaidUnionId(String openid, String transactionId, String mchId, String outTradeNo) + throws WxErrorException { + Map params = new HashMap<>(8); + params.put("openid", openid); + + if (StringUtils.isNotEmpty(transactionId)) { + params.put("transaction_id", transactionId); + } + + if (StringUtils.isNotEmpty(mchId)) { + params.put("mch_id", mchId); + } + + if (StringUtils.isNotEmpty(outTradeNo)) { + params.put("out_trade_no", outTradeNo); + } + + String responseContent = this.get(GET_PAID_UNION_ID_URL, Joiner.on("&").withKeyValueSeparator("=").join(params)); + WxError error = WxError.fromJson(responseContent, WxType.MiniApp); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + + return JSON_PARSER.parse(responseContent).getAsJsonObject().get("unionid").getAsString(); } @Override @@ -168,7 +183,7 @@ public boolean checkSignature(String timestamp, String nonce, String signature) try { return SHA1.gen(this.getWxMaConfig().getToken(), timestamp, nonce).equals(signature); } catch (Exception e) { - this.log.error("Checking signature failed, and the reason is :" + e.getMessage()); + log.error("Checking signature failed, and the reason is :" + e.getMessage()); return false; } } @@ -246,7 +261,7 @@ private T executeInternal(RequestExecutor executor, String uri, E d if (error.getErrorCode() == ERR_40001 || error.getErrorCode() == ERR_42001 || error.getErrorCode() == ERR_40014) { - // 强制设置wxMpConfigStorage它的access token过期了,这样在下一次请求里就会刷新access token + // 强制设置WxMaConfig的access token过期了,这样在下一次请求里就会刷新access token this.getWxMaConfig().expireAccessToken(); if (this.getWxMaConfig().autoRefreshToken()) { return this.execute(executor, uri, data); diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaKefuMessage.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaKefuMessage.java index ee11476878..73ca435d62 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaKefuMessage.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaKefuMessage.java @@ -41,20 +41,26 @@ public class WxMaKefuMessage implements Serializable { @Data @AllArgsConstructor - public static class KfText { + public static class KfText implements Serializable { + private static final long serialVersionUID = 151122958720941270L; + private String content; } @Data @AllArgsConstructor - public static class KfImage { + public static class KfImage implements Serializable { + private static final long serialVersionUID = -5409342945117300782L; + @SerializedName("media_id") private String mediaId; } @Data @Builder - public static class KfLink { + public static class KfLink implements Serializable { + private static final long serialVersionUID = -6728776817556127413L; + private String title; private String description; private String url; @@ -65,7 +71,9 @@ public static class KfLink { @Data @Builder - public static class KfMaPage { + public static class KfMaPage implements Serializable { + private static final long serialVersionUID = -5633492281871634466L; + private String title; @SerializedName("pagepath") diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaTemplateData.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaTemplateData.java index 5eae34b115..040edda4d0 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaTemplateData.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaTemplateData.java @@ -5,7 +5,7 @@ /** *
- *
+ * 参考文档 https://developers.weixin.qq.com/miniprogram/dev/api-backend/templateMessage.send.html
  * Created by Binary Wang on 2018/9/23.
  * 
* @@ -29,4 +29,5 @@ public WxMaTemplateData(String name, String value, String color) { this.color = color; } + } diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaTemplateMessage.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaTemplateMessage.java index 5a67439478..00c7e52f46 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaTemplateMessage.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaTemplateMessage.java @@ -13,7 +13,7 @@ /** * 模板消息. - * 参考 https://mp.weixin.qq.com/debug/wxadoc/dev/api/notice.html#接口说明 模板消息部分 + * 参考 https://developers.weixin.qq.com/miniprogram/dev/api-backend/templateMessage.send.html * * @author Binary Wang */ @@ -75,16 +75,6 @@ public class WxMaTemplateMessage implements Serializable { */ private List data; - /** - * 模板内容字体的颜色,不填默认黑色. - *
-   * 参数:color
-   * 是否必填: 否
-   * 描述: 模板内容字体的颜色,不填默认黑色
-   * 
- */ - private String color; - /** * 模板需要放大的关键词,不填则默认无放大. *
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaUniformMessage.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaUniformMessage.java
index e58ad58a98..7515bdbe25 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaUniformMessage.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaUniformMessage.java
@@ -104,5 +104,11 @@ public static class MiniProgram implements Serializable {
      * 加入此字段是基于微信官方接口变化多端的考虑
      */
     private boolean usePath = false;
+
+    /**
+     * 是否使用pagePath,否则使用pagepath.
+     * 加入此字段是基于微信官方接口变化多端的考虑
+     */
+    private boolean usePagePath = false;
   }
 }
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeAuditStatus.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeAuditStatus.java
index 36c33eaf1a..6487485060 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeAuditStatus.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeAuditStatus.java
@@ -18,18 +18,24 @@
 public class WxMaCodeAuditStatus implements Serializable {
   private static final long serialVersionUID = 4655119308692217268L;
   /**
-   * 审核 ID
+   * 审核 ID.
    */
   @SerializedName(value = "auditId", alternate = {"auditid"})
   private Long auditId;
   /**
-   * 审核状态,其中0为审核成功,1为审核失败,2为审核中
+   * 审核状态.
+   * 其中0为审核成功,1为审核失败,2为审核中
    */
   private Integer status;
   /**
-   * 当status=1,审核被拒绝时,返回的拒绝原因
+   * 当status=1,审核被拒绝时,返回的拒绝原因.
    */
   private String reason;
+  /**
+   * 当status=1,审核被拒绝时,会返回审核失败的小程序截图示例。 xxx丨yyy丨zzz是media_id可通过获取永久素材接口 拉取截图内容).
+   */
+  @SerializedName(value = "screenshot")
+  private String screenShot;
 
   public static WxMaCodeAuditStatus fromJson(String json) {
     return WxMaGsonBuilder.create().fromJson(json, WxMaCodeAuditStatus.class);
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/crypt/WxMaCryptUtils.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/crypt/WxMaCryptUtils.java
index e05bac5fb7..23ea851b3f 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/crypt/WxMaCryptUtils.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/crypt/WxMaCryptUtils.java
@@ -1,19 +1,27 @@
 package cn.binarywang.wx.miniapp.util.crypt;
 
-import cn.binarywang.wx.miniapp.config.WxMaConfig;
-import me.chanjar.weixin.common.util.crypto.PKCS7Encoder;
-import org.apache.commons.codec.binary.Base64;
-
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.security.AlgorithmParameters;
+import java.security.Key;
+import java.security.Security;
+import java.util.Arrays;
 import javax.crypto.Cipher;
 import javax.crypto.spec.IvParameterSpec;
 import javax.crypto.spec.SecretKeySpec;
-import java.nio.charset.StandardCharsets;
-import java.security.AlgorithmParameters;
+
+import org.apache.commons.codec.binary.Base64;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+import cn.binarywang.wx.miniapp.config.WxMaConfig;
+import me.chanjar.weixin.common.util.crypto.PKCS7Encoder;
 
 /**
  * @author Binary Wang
  */
 public class WxMaCryptUtils extends me.chanjar.weixin.common.util.crypto.WxCryptUtil {
+  private static final Charset UTF_8 = StandardCharsets.UTF_8;
+
   public WxMaCryptUtils(WxMaConfig config) {
     this.appidOrCorpid = config.getAppid();
     this.token = config.getToken();
@@ -21,8 +29,9 @@ public WxMaCryptUtils(WxMaConfig config) {
   }
 
   /**
-   * AES解密
+   * AES解密.
    *
+   * @param sessionKey    session_key
    * @param encryptedData 消息密文
    * @param ivStr         iv字符串
    */
@@ -34,9 +43,40 @@ public static String decrypt(String sessionKey, String encryptedData, String ivS
       Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
       cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(Base64.decodeBase64(sessionKey), "AES"), params);
 
-      return new String(PKCS7Encoder.decode(cipher.doFinal(Base64.decodeBase64(encryptedData))), StandardCharsets.UTF_8);
+      return new String(PKCS7Encoder.decode(cipher.doFinal(Base64.decodeBase64(encryptedData))), UTF_8);
+    } catch (Exception e) {
+      throw new RuntimeException("AES解密失败!", e);
+    }
+  }
+
+
+  /**
+   * AES解密.
+   *
+   * @param sessionKey    session_key
+   * @param encryptedData 消息密文
+   * @param ivStr         iv字符串
+   */
+  public static String decryptAnotherWay(String sessionKey, String encryptedData, String ivStr) {
+    byte[] keyBytes = Base64.decodeBase64(sessionKey.getBytes(UTF_8));
+
+    int base = 16;
+    if (keyBytes.length % base != 0) {
+      int groups = keyBytes.length / base + (keyBytes.length % base != 0 ? 1 : 0);
+      byte[] temp = new byte[groups * base];
+      Arrays.fill(temp, (byte) 0);
+      System.arraycopy(keyBytes, 0, temp, 0, keyBytes.length);
+      keyBytes = temp;
+    }
+
+    Security.addProvider(new BouncyCastleProvider());
+    Key key = new SecretKeySpec(keyBytes, "AES");
+    try {
+      Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
+      cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(Base64.decodeBase64(ivStr.getBytes(UTF_8))));
+      return new String(cipher.doFinal(Base64.decodeBase64(encryptedData.getBytes(UTF_8))), UTF_8);
     } catch (Exception e) {
-      throw new RuntimeException("AES解密失败", e);
+      throw new RuntimeException("AES解密失败!", e);
     }
   }
 
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaTemplateMessageGsonAdapter.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaTemplateMessageGsonAdapter.java
index 800a7e5a8e..15eff1ff4d 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaTemplateMessageGsonAdapter.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaTemplateMessageGsonAdapter.java
@@ -27,10 +27,6 @@ public JsonElement serialize(WxMaTemplateMessage message, Type typeOfSrc, JsonSe
       messageJson.addProperty("form_id", message.getFormId());
     }
 
-    if (message.getColor() != null) {
-      messageJson.addProperty("color", message.getColor());
-    }
-
     if (message.getEmphasisKeyword() != null) {
       messageJson.addProperty("emphasis_keyword", message.getEmphasisKeyword());
     }
@@ -45,9 +41,6 @@ public JsonElement serialize(WxMaTemplateMessage message, Type typeOfSrc, JsonSe
     for (WxMaTemplateData datum : message.getData()) {
       JsonObject dataJson = new JsonObject();
       dataJson.addProperty("value", datum.getValue());
-      if (datum.getColor() != null) {
-        dataJson.addProperty("color", datum.getColor());
-      }
       data.add(datum.getName(), dataJson);
     }
 
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaUniformMessageGsonAdapter.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaUniformMessageGsonAdapter.java
index c847bf375b..75ccd68aaa 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaUniformMessageGsonAdapter.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaUniformMessageGsonAdapter.java
@@ -36,6 +36,8 @@ public JsonElement serialize(WxMaUniformMessage message, Type typeOfSrc, JsonSer
         miniProgramJson.addProperty("appid", miniProgram.getAppid());
         if (miniProgram.isUsePath()) {
           miniProgramJson.addProperty("path", miniProgram.getPagePath());
+        } else if (miniProgram.isUsePagePath()) {
+          miniProgramJson.addProperty("pagePath", miniProgram.getPagePath());
         } else {
           miniProgramJson.addProperty("pagepath", miniProgram.getPagePath());
         }
@@ -79,9 +81,6 @@ public JsonElement serialize(WxMaUniformMessage message, Type typeOfSrc, JsonSer
       for (WxMaTemplateData templateData : message.getData()) {
         JsonObject dataJson = new JsonObject();
         dataJson.addProperty("value", templateData.getValue());
-        if (templateData.getColor() != null) {
-          dataJson.addProperty("color", templateData.getColor());
-        }
         data.add(templateData.getName(), dataJson);
       }
     }
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaVisitDistributionGsonAdapter.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaVisitDistributionGsonAdapter.java
index 45bdbac0ff..0fc79d44bd 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaVisitDistributionGsonAdapter.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaVisitDistributionGsonAdapter.java
@@ -10,9 +10,9 @@
 import me.chanjar.weixin.common.util.json.GsonHelper;
 
 import java.lang.reflect.Type;
-import java.util.Hashtable;
 import java.util.LinkedHashMap;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * @author Charming
@@ -36,7 +36,7 @@ public WxMaVisitDistribution deserialize(JsonElement json, Type type, JsonDeseri
     }
 
     JsonArray listArray = object.getAsJsonArray("list");
-    Map> list = new Hashtable<>(listArray.size());
+    Map> list = new ConcurrentHashMap<>(listArray.size());
     for (JsonElement indexElement : listArray) {
       JsonObject indexObject = indexElement.getAsJsonObject();
       String index = GsonHelper.getString(indexObject, "index");
diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaMsgServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaMsgServiceImplTest.java
index eb671cda61..298a9c9346 100644
--- a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaMsgServiceImplTest.java
+++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaMsgServiceImplTest.java
@@ -48,10 +48,10 @@ public void testSendTemplateMsg() throws WxErrorException {
       .formId("FORMID")
       .page("index")
       .data(Lists.newArrayList(
-        new WxMaTemplateData("keyword1", "339208499", "#173177"),
-        new WxMaTemplateData("keyword2", dateFormat.format(new Date()), "#173177"),
-        new WxMaTemplateData("keyword3", "粤海喜来登酒店", "#173177"),
-        new WxMaTemplateData("keyword4", "广州市天河区天河路208号", "#173177")))
+        new WxMaTemplateData("keyword1", "339208499"),
+        new WxMaTemplateData("keyword2", dateFormat.format(new Date())),
+        new WxMaTemplateData("keyword3", "粤海喜来登酒店"),
+        new WxMaTemplateData("keyword4", "广州市天河区天河路208号")))
       .templateId(config.getTemplateId())
       .emphasisKeyword("keyword1.DATA")
       .build();
diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImplTest.java
index 1f87c7e4ee..85fb2f9850 100644
--- a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImplTest.java
+++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImplTest.java
@@ -1,7 +1,5 @@
 package cn.binarywang.wx.miniapp.api.impl;
 
-import java.io.File;
-
 import org.apache.commons.lang3.StringUtils;
 import org.testng.annotations.*;
 
@@ -11,6 +9,7 @@
 import com.google.inject.Inject;
 import me.chanjar.weixin.common.error.WxErrorException;
 
+import static org.assertj.core.api.Assertions.assertThat;
 import static org.testng.Assert.*;
 
 /**
@@ -33,4 +32,9 @@ public void testRefreshAccessToken() throws WxErrorException {
     assertTrue(StringUtils.isNotBlank(after));
   }
 
+  @Test(expectedExceptions = {WxErrorException.class})
+  public void testGetPaidUnionId() throws WxErrorException {
+    final String unionId = this.wxService.getPaidUnionId("1", null, "3", "4");
+    assertThat(unionId).isNotEmpty();
+  }
 }
diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/util/crypt/WxMaCryptUtilsTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/util/crypt/WxMaCryptUtilsTest.java
new file mode 100644
index 0000000000..76b4e96743
--- /dev/null
+++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/util/crypt/WxMaCryptUtilsTest.java
@@ -0,0 +1,35 @@
+package cn.binarywang.wx.miniapp.util.crypt;
+
+
+import org.testng.annotations.*;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * 
+ *
+ * Created by Binary Wang on 2018/12/25.
+ * 
+ * + * @author Binary Wang + */ +public class WxMaCryptUtilsTest { + @Test + public void testDecrypt() { + String sessionKey = "7MG7jbTToVVRWRXVA885rg=="; + String encryptedData = "BY6VOgcWbwGcyrunK0ECWI8rnDsT69DucZ+M78tc1aL9aM/3bEAHFYd4fu7kRjWhD4YfjObw44T9vUqKyHIjbKs6hvtEasZZEIW35x4a91xVgN48ZqZ7MTQqUlP13kDUlkuwYh+/8g8yceu4kNbjowYrhihx+SV7CfjKCveJ7TSepr5Z7aLv1o+rfeelfOwn++WN/YoQsuZ6S3L4fWlWe5DAAUnFUI6cJvxxCohVzbrVXhyH2AqQdSjH2WnMYFeaGFIbcoxMznlk7oEwFn+hBj63dyT/swdYQfEdzuyCBmKXy8d6l1RKVX6Y65coTD8kIlbr+FKsqYrXVUIUBSwehqYuOdhYWZ9Bntl5DWU1oqzAPCnMn2cAIoQpQPKP7IGSxMOvCNAMhVXbE7BvnWuVuGF+AM5tXAa9IVUhcMImGwLQqm4iV5uBd+5OcFObh3A4VJk9iBCBWSkBHa/rV9CVoY0bFv2F9/2Hv82++Ybl274="; + String ivStr = "TarMFjnzHVxy8pdS93wQbw=="; + System.out.println(WxMaCryptUtils.decrypt(sessionKey, encryptedData, ivStr)); +// System.out.println(WxMaCryptUtils.decryptAnotherWay(sessionKey, encryptedData, ivStr)); + } + + @Test + public void testDecryptAnotherWay() { + String encryptedData = "CiyLU1Aw2KjvrjMdj8YKliAjtP4gsMZMQmRzooG2xrDcvSnxIMXFufNstNGTyaGS9uT5geRa0W4oTOb1WT7fJlAC+oNPdbB+3hVbJSRgv+4lGOETKUQz6OYStslQ142dNCuabNPGBzlooOmB231qMM85d2/fV6ChevvXvQP8Hkue1poOFtnEtpyxVLW1zAo6/1Xx1COxFvrc2d7UL/lmHInNlxuacJXwu0fjpXfz/YqYzBIBzD6WUfTIF9GRHpOn/Hz7saL8xz+W//FRAUid1OksQaQx4CMs8LOddcQhULW4ucetDf96JcR3g0gfRK4PC7E/r7Z6xNrXd2UIeorGj5Ef7b1pJAYB6Y5anaHqZ9J6nKEBvB4DnNLIVWSgARns/8wR2SiRS7MNACwTyrGvt9ts8p12PKFdlqYTopNHR1Vf7XjfhQlVsAJdNiKdYmYVoKlaRv85IfVunYzO0IKXsyl7JCUjCpoG20f0a04COwfneQAGGwd5oa+T8yO5hzuyDb/XcxxmK01EpqOyuxINew=="; + String ivStr = "r7BXXKkLb8qrSNn05n0qiA=="; + String sessionKey = "tiihtNczf5v6AKRyjwEUhQ=="; + + assertThat(WxMaCryptUtils.decrypt(sessionKey, encryptedData, ivStr)) + .isEqualTo(WxMaCryptUtils.decryptAnotherWay(sessionKey, encryptedData, ivStr)); + } +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/util/json/WxMaUniformMessageGsonAdapterTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/util/json/WxMaUniformMessageGsonAdapterTest.java index 1541879c33..c28711ab81 100644 --- a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/util/json/WxMaUniformMessageGsonAdapterTest.java +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/util/json/WxMaUniformMessageGsonAdapterTest.java @@ -1,10 +1,9 @@ package cn.binarywang.wx.miniapp.util.json; -import org.testng.annotations.*; - import cn.binarywang.wx.miniapp.bean.WxMaTemplateData; import cn.binarywang.wx.miniapp.bean.WxMaUniformMessage; import com.google.gson.JsonParser; +import org.testng.annotations.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -26,7 +25,7 @@ public void testSerialize_mp() { .appid("APPID") .templateId("TEMPLATE_ID") .url("http://weixin.qq.com/download") - .miniProgram(new WxMaUniformMessage.MiniProgram("xiaochengxuappid12345", "index?foo=bar", false)) + .miniProgram(new WxMaUniformMessage.MiniProgram("xiaochengxuappid12345", "index?foo=bar", false, false)) .build(); message.addData(new WxMaTemplateData("first", "恭喜你购买成功!", "#173177")) .addData(new WxMaTemplateData("keyword1", "巧克力", "#173177")) @@ -72,7 +71,7 @@ public void testSerialize_mp() { @Test public void testSerialize_ma() { - WxMaUniformMessage message = WxMaUniformMessage.builder() + WxMaUniformMessage message = WxMaUniformMessage.builder() .isMpTemplateMsg(false) .toUser("OPENID") .page("page/page/index") diff --git a/weixin-java-mp/pom.xml b/weixin-java-mp/pom.xml index 90cb71c8b3..ed8c7e75de 100644 --- a/weixin-java-mp/pom.xml +++ b/weixin-java-mp/pom.xml @@ -7,10 +7,11 @@ com.github.binarywang wx-java - 3.3.0 + 3.4.0 + weixin-java-mp - WxJava - MP + WxJava - MP Java SDK 微信公众号Java SDK diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpAiOpenService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpAiOpenService.java index 74b905d683..c57ad9d0f5 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpAiOpenService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpAiOpenService.java @@ -1,10 +1,10 @@ package me.chanjar.weixin.mp.api; +import java.io.File; + import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.mp.enums.AiLangType; -import java.io.File; - /** *
  * 微信AI开放接口(语音识别,微信翻译).
@@ -15,24 +15,15 @@
  * @author Binary Wang
  */
 public interface WxMpAiOpenService {
+  String TRANSLATE_URL = "http://api.weixin.qq.com/cgi-bin/media/voice/translatecontent?lfrom=%s<o=%s";
   String VOICE_UPLOAD_URL = "http://api.weixin.qq.com/cgi-bin/media/voice/addvoicetorecofortext?format=%s&voice_id=%s&lang=%s";
   String VOICE_QUERY_RESULT_URL = "http://api.weixin.qq.com/cgi-bin/media/voice/queryrecoresultfortext";
 
   /**
    * 
    * 提交语音.
-   * 接口调用请求说明
-   *
    * http请求方式: POST
    * http://api.weixin.qq.com/cgi-bin/media/voice/addvoicetorecofortext?access_token=ACCESS_TOKEN&format=&voice_id=xxxxxx&lang=zh_CN
-   * 参数说明
-   *
-   * 参数	是否必须	说明
-   * access_token	是	接口调用凭证
-   * format	是	文件格式 (只支持mp3,16k,单声道,最大1M)
-   * voice_id	是	语音唯一标识
-   * lang	否	语言,zh_CN 或 en_US,默认中文
-   * 语音内容放body里或者上传文件的形式
    * 
* * @param lang 语言,zh_CN 或 en_US,默认中文 @@ -46,16 +37,9 @@ public interface WxMpAiOpenService { * 获取语音识别结果. * 接口调用请求说明 * - * http请求方式: POST * http://api.weixin.qq.com/cgi-bin/media/voice/queryrecoresultfortext?access_token=ACCESS_TOKEN&voice_id=xxxxxx&lang=zh_CN * 请注意,添加完文件之后10s内调用这个接口 * - * 参数说明 - * - * 参数 是否必须 说明 - * access_token 是 接口调用凭证 - * voice_id 是 语音唯一标识 - * lang 否 语言,zh_CN 或 en_US,默认中文 *
* * @param lang 语言,zh_CN 或 en_US,默认中文 @@ -80,18 +64,12 @@ public interface WxMpAiOpenService { * * http请求方式: POST * http://api.weixin.qq.com/cgi-bin/media/voice/translatecontent?access_token=ACCESS_TOKEN&lfrom=xxx<o=xxx - * 参数说明 * - * 参数 是否必须 说明 - * access_token 是 接口调用凭证 - * lfrom 是 源语言,zh_CN 或 en_US - * lto 是 目标语言,zh_CN 或 en_US - * 源内容放body里或者上传文件的形式(utf8格式,最大600Byte) *
* * @param langFrom 源语言,zh_CN 或 en_US - * @param langTo 目标语言,zh_CN 或 en_US - * @param content 要翻译的文本内容 + * @param langTo 目标语言,zh_CN 或 en_US + * @param content 要翻译的文本内容 */ String translate(AiLangType langFrom, AiLangType langTo, String content) throws WxErrorException; } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpCardService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpCardService.java index af6cefcff8..66aeccade7 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpCardService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpCardService.java @@ -2,10 +2,8 @@ import me.chanjar.weixin.common.bean.WxCardApiSignature; import me.chanjar.weixin.common.error.WxErrorException; -import me.chanjar.weixin.mp.bean.card.WxMpCardLandingPageCreateRequest; -import me.chanjar.weixin.mp.bean.card.WxMpCardLandingPageCreateResult; -import me.chanjar.weixin.mp.bean.card.WxMpCardQrcodeCreateResult; -import me.chanjar.weixin.mp.bean.result.WxMpCardResult; +import me.chanjar.weixin.mp.bean.card.*; +import me.chanjar.weixin.mp.bean.card.WxMpCardResult; /** * 卡券相关接口 @@ -14,6 +12,7 @@ * @author yuanqixun 2018-08-29 */ public interface WxMpCardService { + String CARD_CREATE = "https://api.weixin.qq.com/card/create"; String CARD_GET = "https://api.weixin.qq.com/card/get"; String CARD_GET_TICKET = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=wx_card"; String CARD_CODE_DECRYPT = "https://api.weixin.qq.com/card/code/decrypt"; @@ -28,6 +27,11 @@ public interface WxMpCardService { */ String CARD_CODE_UNAVAILABLE = "https://api.weixin.qq.com/card/code/unavailable"; + /** + * 卡券删除 + */ + String CARD_DELETE = "https://api.weixin.qq.com/card/delete"; + /** * 得到WxMpService */ @@ -81,8 +85,8 @@ WxCardApiSignature createCardApiSignature(String... optionalSignParam) throws String decryptCardCode(String encryptCode) throws WxErrorException; /** - * 卡券Code查询 - * + * 卡券Code查询. + * 文档地址: https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1451025272&anchor=1 * @param cardId 卡券ID代表一类卡券 * @param code 单张卡券的唯一标准 * @param checkConsume 是否校验code核销状态,填入true和false时的code异常状态返回数据不同 @@ -142,6 +146,14 @@ void markCardCode(String code, String cardId, String openId, boolean isMark) thr */ String addTestWhiteList(String openid) throws WxErrorException; + /** + * + * @param cardCreateMessage + * @return + * @throws WxErrorException + */ + WxMpCardCreateResult createCard(WxMpCardCreateMessage cardCreateMessage) throws WxErrorException; + /** * 创建卡券二维码 * @@ -182,4 +194,12 @@ void markCardCode(String code, String cardId, String openId, boolean isMark) thr */ String unavailableCardCode(String cardId, String code, String reason) throws WxErrorException; + /** + * 删除卡券接口 + * @param cardId + * @return + * @throws WxErrorException + */ + WxMpCardDeleteResult deleteCard(String cardId) throws WxErrorException; + } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMarketingService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMarketingService.java new file mode 100644 index 0000000000..f59158b2bd --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMarketingService.java @@ -0,0 +1,71 @@ +package me.chanjar.weixin.mp.api; + +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.bean.marketing.WxMpAdLeadFilter; +import me.chanjar.weixin.mp.bean.marketing.WxMpAdLeadResult; +import me.chanjar.weixin.mp.bean.marketing.WxMpUserAction; +import me.chanjar.weixin.mp.bean.marketing.WxMpUserActionSet; + +import java.io.IOException; +import java.util.Date; +import java.util.List; + +/** + *
+ * 微信营销接口
+ * 
+ * + * @author 007 + */ +public interface WxMpMarketingService { + String API_URL_PREFIX = "https://api.weixin.qq.com/marketing/"; + + /** + *
+   * 创建数据源
+   * 接口调用请求说明
+   * https://wximg.qq.com/wxp/pdftool/get.html?id=rkalQXDBM&pa=39
+   * 
+ * + * @param type 用户行为源类型 + * @param name 用户行为源名称 必填 + * @param description 用户行为源描述,字段长度最小 1 字节,长度最大 128 字节 + */ + long addUserActionSets(String type, String name, String description) throws WxErrorException; + + /** + *
+   * 获取数据源信息
+   * 
+ * + * @param userActionSetId 数据源唯一ID + */ + List getUserActionSets(Long userActionSetId) throws WxErrorException; + + /** + * 回传数据 + * 接口调用请求说明 + * https://wximg.qq.com/wxp/pdftool/get.html?id=rkalQXDBM&pa=39 + * + * @param actions 用户行为源类型 + */ + void addUserAction(List actions) throws WxErrorException; + + /** + *
+   * 获取朋友圈销售线索数据接口
+   * 接口调用请求说明
+   *
+   * http请求方式: POST
+   * http://api.weixin.qq.com/cgi-bin/media/voice/translatecontent?access_token=ACCESS_TOKEN&lfrom=xxx<o=xxx
+   *
+   * 
+ * + * @param beginDate 开始日期 + * @param endDate 结束日期 + * @param filtering 过滤条件 + * @param page 页码,获取指定页数据 + * @param page_size 一页获取的数据条数(1-100) + */ + WxMpAdLeadResult getAdLeads(Date beginDate, Date endDate, List filtering, Integer page, Integer page_size) throws WxErrorException, IOException; +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMemberCardService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMemberCardService.java index b071980480..37785365c1 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMemberCardService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMemberCardService.java @@ -6,12 +6,7 @@ import me.chanjar.weixin.mp.bean.card.MemberCardActivateUserFormResult; import me.chanjar.weixin.mp.bean.card.MemberCardUpdateRequest; import me.chanjar.weixin.mp.bean.card.WxMpCardCreateResult; -import me.chanjar.weixin.mp.bean.membercard.ActivatePluginParam; -import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardActivatedMessage; -import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardCreateMessage; -import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardUpdateMessage; -import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardUpdateResult; -import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardUserInfoResult; +import me.chanjar.weixin.mp.bean.membercard.*; /** * 会员卡相关接口. @@ -22,14 +17,14 @@ * @date 2018-08-30 */ public interface WxMpMemberCardService { - String MEMBER_CARD_CREAET = "https://api.weixin.qq.com/card/create"; + String MEMBER_CARD_CREATE = "https://api.weixin.qq.com/card/create"; String MEMBER_CARD_ACTIVATE = "https://api.weixin.qq.com/card/membercard/activate"; String MEMBER_CARD_USER_INFO_GET = "https://api.weixin.qq.com/card/membercard/userinfo/get"; String MEMBER_CARD_UPDATE_USER = "https://api.weixin.qq.com/card/membercard/updateuser"; /** * 会员卡激活之微信开卡接口(wx_activate=true情况调用). */ - String MEMBER_CARD_ACTIVATEUSERFORM = "https://api.weixin.qq.com/card/membercard/activateuserform/set"; + String MEMBER_CARD_ACTIVATE_USER_FORM = "https://api.weixin.qq.com/card/membercard/activateuserform/set"; /** * 获取会员卡开卡插件参数. @@ -41,19 +36,34 @@ public interface WxMpMemberCardService { */ String MEMBER_CARD_UPDATE = "https://api.weixin.qq.com/card/update"; + /** + * 跳转型会员卡开卡字段. + * 获取用户提交资料(wx_activate=true情况调用),开发者根据activate_ticket获取到用户填写的信息 + */ + String MEMBER_CARD_ACTIVATE_TEMP_INFO = "https://api.weixin.qq.com/card/membercard/activatetempinfo/get"; /** * 得到WxMpService. + * + * @return WxMpService */ WxMpService getWxMpService(); /** * 会员卡创建接口. + * + * @param createJson 会员卡json字符串 + * @return 返回json字符串 + * @throws WxErrorException 接口调用失败抛出的异常 */ WxMpCardCreateResult createMemberCard(String createJson) throws WxErrorException; /** - * 会员卡创建接口. + * 会员卡创建接口 + * + * @param createMessageMessage 会员卡创建对象 + * @return 会员卡信息的结果对象 + * @throws WxErrorException 接口调用失败抛出的异常 */ WxMpCardCreateResult createMemberCard(WxMpMemberCardCreateMessage createMessageMessage) throws WxErrorException; @@ -61,7 +71,7 @@ public interface WxMpMemberCardService { * 会员卡激活接口. * * @param activatedMessage 激活所需参数 - * @return 返回json字符串 + * @return 会员卡激活后的json字符串 * @throws WxErrorException 接口调用失败抛出的异常 */ String activateMemberCard(WxMpMemberCardActivatedMessage activatedMessage) throws WxErrorException; @@ -91,16 +101,40 @@ public interface WxMpMemberCardService { /** * 设置会员卡激活的字段(会员卡设置:wx_activate=true 时需要). + * + * @param userFormRequest 会员卡激活字段对象 + * @return 会员卡激活后结果对象 + * @throws WxErrorException 接口调用失败抛出的异常 */ MemberCardActivateUserFormResult setActivateUserForm(MemberCardActivateUserFormRequest userFormRequest) throws WxErrorException; /** * 获取会员卡开卡插件参数(跳转型开卡组件需要参数). + * + * @param cardId 会员卡的CardId,微信分配 + * @param outStr 会员卡设置商户的渠道 + * @return 会员卡开卡插件参数结果对象 + * @throws WxErrorException 接口调用失败抛出的异常 */ ActivatePluginParam getActivatePluginParam(String cardId, String outStr) throws WxErrorException; /** * 更新会员卡信息. + * + * @param memberCardUpdateRequest 会员卡更新对象 + * @return 会员卡更新后结果对象 + * @throws WxErrorException 接口调用失败抛出的异常 */ CardUpdateResult updateCardInfo(MemberCardUpdateRequest memberCardUpdateRequest) throws WxErrorException; + + /** + * 解析跳转型开卡字段用户提交的资料. + * 开发者在URL上截取ticket后须先进行urldecode + * + * @param activateTicket 用户提交的资料 + * @return 开卡字段的会员信息对象 + * @throws WxErrorException 接口调用失败抛出的异常 + */ + WxMpMemberCardActivateTempInfoResult getActivateTempInfo(String activateTicket) throws WxErrorException; + } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java index fead289a36..a0452ebad7 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java @@ -5,6 +5,7 @@ import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; import me.chanjar.weixin.common.util.http.RequestExecutor; import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.mp.api.impl.BaseWxMpServiceImpl; import me.chanjar.weixin.mp.bean.WxMpSemanticQuery; import me.chanjar.weixin.mp.bean.result.WxMpCurrentAutoReplyInfo; import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken; @@ -12,6 +13,8 @@ import me.chanjar.weixin.mp.bean.result.WxMpUser; import me.chanjar.weixin.mp.enums.TicketType; +import java.util.Map; + /** * 微信公众号API的Service. * @@ -302,10 +305,50 @@ public interface WxMpService { WxMpConfigStorage getWxMpConfigStorage(); /** - * 注入 {@link WxMpConfigStorage} 的实现. + * 设置 {@link WxMpConfigStorage} 的实现. 兼容老版本 */ void setWxMpConfigStorage(WxMpConfigStorage wxConfigProvider); + /** + * {@link Map} 加入新的 {@link WxMpConfigStorage},适用于动态添加新的微信公众号配置 + * @param configStorage 新的微信配置 + */ + void addConfigStorage(String mpId, WxMpConfigStorage configStorage); + + /** + * 从{@link Map} 移除 {@link String mpId} 所对应的 {@link WxMpConfigStorage},适用于动态移除微信公众号配置 + * @param mpId 对应公众号的标识 + */ + void removeConfigStorage(String mpId); + + /** + * 注入多个 {@link WxMpConfigStorage} 的实现. 并为每个 {@link WxMpConfigStorage} 赋予不同的 {@link String mpId} 值 + * 随机采用一个{@link String mpId}进行Http初始化操作 + * @param configStorages WxMpConfigStorage map + */ + void setMultiConfigStorages(Map configStorages); + + /** + * 注入多个 {@link WxMpConfigStorage} 的实现. 并为每个 {@link WxMpConfigStorage} 赋予不同的 {@link String label} 值 + * @param configStorages WxMpConfigStorage map + * @param defaultMpId 设置一个{@link WxMpConfigStorage} 所对应的{@link String mpId}进行Http初始化 + */ + void setMultiConfigStorages(Map configStorages, String defaultMpId); + + /** + * 进行相应的公众号切换 + * @param mpId 公众号标识 + * @return 切换是否成功 + */ + boolean switchover(String mpId); + + /** + * 进行相应的公众号切换 + * @param mpId 公众号标识 + * @return 切换成功,则返回当前对象,方便链式调用,否则抛出异常 + */ + WxMpService switchoverTo(String mpId); + /** * 返回客服接口方法实现类,以方便调用其各个接口. * @@ -411,6 +454,13 @@ public interface WxMpService { */ WxMpMemberCardService getMemberCardService(); + /** + * 返回营销相关接口方法的实现类对象,以方便调用其各个接口. + * + * @return WxMpMarketingService + */ + WxMpMarketingService getMarketingService(); + /** * 初始化http请求对象. */ @@ -473,4 +523,6 @@ public interface WxMpService { void setMassMessageService(WxMpMassMessageService massMessageService); void setAiOpenService(WxMpAiOpenService aiOpenService); + + void setMarketingService(WxMpMarketingService marketingService); } 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 f720461652..534ea1390f 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 @@ -1,12 +1,7 @@ package me.chanjar.weixin.mp.api.impl; -import java.io.IOException; -import java.util.concurrent.locks.Lock; - -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; @@ -19,37 +14,22 @@ 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.mp.api.WxMpAiOpenService; -import me.chanjar.weixin.mp.api.WxMpCardService; -import me.chanjar.weixin.mp.api.WxMpConfigStorage; -import me.chanjar.weixin.mp.api.WxMpDataCubeService; -import me.chanjar.weixin.mp.api.WxMpDeviceService; -import me.chanjar.weixin.mp.api.WxMpKefuService; -import me.chanjar.weixin.mp.api.WxMpMassMessageService; -import me.chanjar.weixin.mp.api.WxMpMaterialService; -import me.chanjar.weixin.mp.api.WxMpMemberCardService; -import me.chanjar.weixin.mp.api.WxMpMenuService; -import me.chanjar.weixin.mp.api.WxMpQrcodeService; -import me.chanjar.weixin.mp.api.WxMpService; -import me.chanjar.weixin.mp.api.WxMpShakeService; -import me.chanjar.weixin.mp.api.WxMpStoreService; -import me.chanjar.weixin.mp.api.WxMpSubscribeMsgService; -import me.chanjar.weixin.mp.api.WxMpTemplateMsgService; -import me.chanjar.weixin.mp.api.WxMpUserBlacklistService; -import me.chanjar.weixin.mp.api.WxMpUserService; -import me.chanjar.weixin.mp.api.WxMpUserTagService; -import me.chanjar.weixin.mp.api.WxMpWifiService; +import me.chanjar.weixin.common.util.http.*; +import me.chanjar.weixin.mp.api.*; import me.chanjar.weixin.mp.bean.WxMpSemanticQuery; import me.chanjar.weixin.mp.bean.result.WxMpCurrentAutoReplyInfo; import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken; import me.chanjar.weixin.mp.bean.result.WxMpSemanticQueryResult; import me.chanjar.weixin.mp.bean.result.WxMpUser; import me.chanjar.weixin.mp.enums.TicketType; +import me.chanjar.weixin.mp.util.WxMpConfigStorageHolder; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.locks.Lock; /** * 基础实现类. @@ -62,7 +42,6 @@ public abstract class BaseWxMpServiceImpl implements WxMpService, RequestH protected final Logger log = LoggerFactory.getLogger(this.getClass()); protected WxSessionManager sessionManager = new StandardSessionManager(); - protected WxMpConfigStorage wxMpConfigStorage; private WxMpKefuService kefuService = new WxMpKefuServiceImpl(this); private WxMpMaterialService materialService = new WxMpMaterialServiceImpl(this); private WxMpMenuService menuService = new WxMpMenuServiceImpl(this); @@ -81,11 +60,13 @@ public abstract class BaseWxMpServiceImpl implements WxMpService, RequestH private WxMpMassMessageService massMessageService = new WxMpMassMessageServiceImpl(this); private WxMpAiOpenService aiOpenService = new WxMpAiOpenServiceImpl(this); private WxMpWifiService wifiService = new WxMpWifiServiceImpl(this); + private WxMpMarketingService marketingService = new WxMpMarketingServiceImpl(this); + + private Map configStorageMap; private int retrySleepMillis = 1000; private int maxRetryTimes = 5; - @Override public boolean checkSignature(String timestamp, String nonce, String signature) { try { @@ -352,15 +333,70 @@ public T executeInternal(RequestExecutor executor, String uri, E da @Override public WxMpConfigStorage getWxMpConfigStorage() { - return this.wxMpConfigStorage; + if (this.configStorageMap.size() == 1) { + // 只有一个公众号,直接返回其配置即可 + return this.configStorageMap.values().iterator().next(); + } + + return this.configStorageMap.get(WxMpConfigStorageHolder.get()); } @Override public void setWxMpConfigStorage(WxMpConfigStorage wxConfigProvider) { - this.wxMpConfigStorage = wxConfigProvider; + final String defaultMpId = WxMpConfigStorageHolder.get(); + this.setMultiConfigStorages(ImmutableMap.of(defaultMpId, wxConfigProvider), defaultMpId); + } + + @Override + public void setMultiConfigStorages(Map configStorages) { + this.setMultiConfigStorages(configStorages, configStorages.keySet().iterator().next()); + } + + @Override + public void setMultiConfigStorages(Map configStorages, String defaultMpId) { + this.configStorageMap = Maps.newHashMap(configStorages); + WxMpConfigStorageHolder.set(defaultMpId); this.initHttp(); } + @Override + public void addConfigStorage(String mpId, WxMpConfigStorage configStorages) { + synchronized (this) { + if (this.configStorageMap.containsKey(mpId)) { + throw new RuntimeException("该公众号标识已存在,请更换其他标识!"); + } + this.configStorageMap.put(mpId, configStorages); + } + } + + @Override + public void removeConfigStorage(String mpId) { + synchronized (this) { + this.configStorageMap.remove(mpId); + } + } + + @Override + public WxMpService switchoverTo(String mpId) { + if (this.configStorageMap.containsKey(mpId)) { + WxMpConfigStorageHolder.set(mpId); + return this; + } + + throw new RuntimeException(String.format("无法找到对应【%s】的公众号配置信息,请核实!", mpId)); + } + + @Override + public boolean switchover(String mpId) { + if (this.configStorageMap.containsKey(mpId)) { + WxMpConfigStorageHolder.set(mpId); + return true; + } + + log.error("无法找到对应【{}】的公众号配置信息,请核实!", mpId); + return false; + } + @Override public void setRetrySleepMillis(int retrySleepMillis) { this.retrySleepMillis = retrySleepMillis; @@ -545,4 +581,14 @@ public void setAiOpenService(WxMpAiOpenService aiOpenService) { public WxMpWifiService getWifiService() { return this.wifiService; } + + @Override + public WxMpMarketingService getMarketingService() { + return this.marketingService; + } + + @Override + public void setMarketingService(WxMpMarketingService marketingService) { + this.marketingService = marketingService; + } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpAiOpenServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpAiOpenServiceImpl.java index b97da0a8a2..628b6c55f8 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpAiOpenServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpAiOpenServiceImpl.java @@ -1,17 +1,16 @@ package me.chanjar.weixin.mp.api.impl; -import com.google.gson.JsonObject; +import java.io.File; + import com.google.gson.JsonParser; import me.chanjar.weixin.common.WxType; import me.chanjar.weixin.common.error.WxError; import me.chanjar.weixin.common.error.WxErrorException; -import me.chanjar.weixin.mp.enums.AiLangType; import me.chanjar.weixin.mp.api.WxMpAiOpenService; import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.enums.AiLangType; import me.chanjar.weixin.mp.util.requestexecuter.voice.VoiceUploadRequestExecutor; -import java.io.File; - /** *
  *  Created by BinaryWang on 2018/6/9.
@@ -20,9 +19,7 @@
  * @author Binary Wang
  */
 public class WxMpAiOpenServiceImpl implements WxMpAiOpenService {
-
   private static final JsonParser JSON_PARSER = new JsonParser();
-  public static final String TRANSLATE_URL = "http://api.weixin.qq.com/cgi-bin/media/voice/translatecontent?lfrom=%s<o=%s";
   private WxMpService wxMpService;
 
   public WxMpAiOpenServiceImpl(WxMpService wxMpService) {
@@ -48,14 +45,14 @@ public String recogniseVoice(String voiceId, AiLangType lang, File voiceFile) th
 
   @Override
   public String translate(AiLangType langFrom, AiLangType langTo, String content) throws WxErrorException {
-    final String responseContent = this.wxMpService.post(String.format(TRANSLATE_URL, langFrom.getCode(), langTo.getCode()),
-      content);
-    final JsonObject jsonObject = new JsonParser().parse(responseContent).getAsJsonObject();
-    if (jsonObject.get("errcode") == null || jsonObject.get("errcode").getAsInt() == 0) {
-      return jsonObject.get("to_content").getAsString();
+    String response = this.wxMpService.post(String.format(TRANSLATE_URL, langFrom.getCode(), langTo.getCode()), content);
+
+    WxError error = WxError.fromJson(response, WxType.MP);
+    if (error.getErrorCode() != 0) {
+      throw new WxErrorException(error);
     }
 
-    throw new WxErrorException(WxError.fromJson(responseContent, WxType.MP));
+    return JSON_PARSER.parse(response).getAsJsonObject().get("to_content").getAsString();
   }
 
   @Override
@@ -64,13 +61,13 @@ public String queryRecognitionResult(String voiceId, AiLangType lang) throws WxE
       lang = AiLangType.zh_CN;
     }
 
-    final String responseContent = this.wxMpService.get(VOICE_QUERY_RESULT_URL,
+    final String response = this.wxMpService.get(VOICE_QUERY_RESULT_URL,
       String.format("voice_id=%s&lang=%s", voiceId, lang.getCode()));
-    final JsonObject jsonObject = JSON_PARSER.parse(responseContent).getAsJsonObject();
-    if (jsonObject.get("errcode") == null || jsonObject.get("errcode").getAsInt() == 0) {
-      return jsonObject.get("result").getAsString();
+    WxError error = WxError.fromJson(response, WxType.MP);
+    if (error.getErrorCode() != 0) {
+      throw new WxErrorException(error);
     }
 
-    throw new WxErrorException(WxError.fromJson(responseContent, WxType.MP));
+    return JSON_PARSER.parse(response).getAsJsonObject().get("result").getAsString();
   }
 }
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpCardServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpCardServiceImpl.java
index bfd58856d0..bdd1990440 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpCardServiceImpl.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpCardServiceImpl.java
@@ -1,18 +1,6 @@
 package me.chanjar.weixin.mp.api.impl;
 
-import java.util.Arrays;
-import java.util.concurrent.locks.Lock;
-
-import org.apache.commons.lang3.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.gson.Gson;
-import com.google.gson.JsonArray;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParser;
-import com.google.gson.JsonPrimitive;
+import com.google.gson.*;
 import com.google.gson.reflect.TypeToken;
 import me.chanjar.weixin.common.bean.WxCardApiSignature;
 import me.chanjar.weixin.common.error.WxError;
@@ -22,12 +10,15 @@
 import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor;
 import me.chanjar.weixin.mp.api.WxMpCardService;
 import me.chanjar.weixin.mp.api.WxMpService;
-import me.chanjar.weixin.mp.bean.card.WxMpCardLandingPageCreateRequest;
-import me.chanjar.weixin.mp.bean.card.WxMpCardLandingPageCreateResult;
-import me.chanjar.weixin.mp.bean.card.WxMpCardQrcodeCreateResult;
-import me.chanjar.weixin.mp.bean.result.WxMpCardResult;
+import me.chanjar.weixin.mp.bean.card.*;
 import me.chanjar.weixin.mp.enums.TicketType;
 import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Arrays;
+import java.util.concurrent.locks.Lock;
 
 /**
  * Created by Binary Wang on 2016/7/27.
@@ -150,14 +141,6 @@ public String decryptCardCode(String encryptCode) throws WxErrorException {
     return jsonPrimitive.getAsString();
   }
 
-  /**
-   * 卡券Code查询.
-   *
-   * @param cardId       卡券ID代表一类卡券
-   * @param code         单张卡券的唯一标准
-   * @param checkConsume 是否校验code核销状态,填入true和false时的code异常状态返回数据不同
-   * @return WxMpCardResult对象
-   */
   @Override
   public WxMpCardResult queryCardCode(String cardId, String code, boolean checkConsume) throws WxErrorException {
     JsonObject param = new JsonObject();
@@ -265,6 +248,13 @@ public String addTestWhiteList(String openid) throws WxErrorException {
     return respone;
   }
 
+  @Override
+  public WxMpCardCreateResult createCard(WxMpCardCreateMessage cardCreateMessage) throws WxErrorException {
+
+    String response = this.wxMpService.post(CARD_CREATE, GSON.toJson(cardCreateMessage));
+    return WxMpCardCreateResult.fromJson(response);
+  }
+
   /**
    * 创建卡券二维码.
    */
@@ -324,4 +314,15 @@ public String unavailableCardCode(String cardId, String code, String reason) thr
     jsonRequest.addProperty("reason", reason);
     return this.wxMpService.post(CARD_CODE_UNAVAILABLE, GSON.toJson(jsonRequest));
   }
+
+  @Override
+  public WxMpCardDeleteResult deleteCard(String cardId) throws WxErrorException {
+    if (StringUtils.isEmpty(cardId)) {
+      throw new WxErrorException(WxError.builder().errorCode(41012).errorMsg("cardId不能为空").build());
+    }
+    JsonObject param = new JsonObject();
+    param.addProperty("card_id", cardId);
+    String response = this.wxMpService.post(CARD_DELETE, param.toString());
+    return WxMpCardDeleteResult.fromJson(response);
+  }
 }
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMarketingServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMarketingServiceImpl.java
new file mode 100644
index 0000000000..91e7d4c1ba
--- /dev/null
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMarketingServiceImpl.java
@@ -0,0 +1,92 @@
+package me.chanjar.weixin.mp.api.impl;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.mp.api.WxMpMarketingService;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.bean.marketing.WxMpAdLeadFilter;
+import me.chanjar.weixin.mp.bean.marketing.WxMpAdLeadResult;
+import me.chanjar.weixin.mp.bean.marketing.WxMpUserAction;
+import me.chanjar.weixin.mp.bean.marketing.WxMpUserActionSet;
+import org.apache.commons.lang3.time.DateFormatUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author 007
+ */
+public class WxMpMarketingServiceImpl implements WxMpMarketingService {
+  protected final Logger log = LoggerFactory.getLogger(this.getClass());
+  private WxMpService wxMpService;
+
+  public WxMpMarketingServiceImpl(WxMpService wxMpService) {
+    this.wxMpService = wxMpService;
+  }
+
+  @Override
+  public long addUserActionSets(String type, String name, String description) throws WxErrorException {
+    String url = API_URL_PREFIX + "user_action_sets/add?version=v1.0";
+    JsonObject json = new JsonObject();
+    json.addProperty("type", type);
+    json.addProperty("name", name);
+    json.addProperty("description", description);
+    String responseContent = wxMpService.post(url, json.toString());
+    JsonElement tmpJsonElement = new JsonParser().parse(responseContent);
+    return tmpJsonElement.getAsJsonObject().get("data").getAsJsonObject().get("user_action_set_id").getAsLong();
+  }
+
+  @Override
+  public List getUserActionSets(Long userActionSetId) throws WxErrorException {
+    String url = API_URL_PREFIX + "user_action_sets/get";
+    String responseContent = wxMpService.get(url, "version=v1.0&user_action_set_id=" + userActionSetId);
+    return WxMpUserActionSet.fromJson(responseContent);
+  }
+
+  @Override
+  public void addUserAction(List actions) throws WxErrorException {
+    String url = API_URL_PREFIX + "user_actions/add?version=v1.0";
+    JsonArray json = new JsonArray();
+    for (WxMpUserAction action : actions) {
+      json.add(action.toJsonObject());
+    }
+    wxMpService.post(url, json.toString());
+  }
+
+  @Override
+  public WxMpAdLeadResult getAdLeads(Date beginDate, Date endDate, List filtering, Integer page, Integer page_size) throws WxErrorException, IOException {
+    Date today = new Date();
+    if (beginDate == null) {
+      beginDate = today;
+    }
+    if (endDate == null) {
+      endDate = today;
+    }
+    String url = API_URL_PREFIX + "wechat_ad_leads/get";
+    String params = "version=v1.0";
+    JsonObject dateRange = new JsonObject();
+    dateRange.addProperty("begin_date", DateFormatUtils.format(beginDate, "yyyy-MM-dd"));
+    dateRange.addProperty("end_date", DateFormatUtils.format(endDate, "yyyy-MM-dd"));
+    params += "&date_range=" + URLEncoder.encode(dateRange.toString(), StandardCharsets.UTF_8.name());
+    params += "&page=" + page;
+    params += "&page_size=" + page_size;
+    if (filtering != null) {
+      JsonArray filterJson = new JsonArray();
+      for (WxMpAdLeadFilter filter : filtering) {
+        filterJson.add(filter.toJsonObject());
+      }
+      params += "&filtering=" + URLEncoder.encode(filterJson.toString(), StandardCharsets.UTF_8.name());
+      ;
+    }
+    String responseContent = wxMpService.get(url, params);
+    return WxMpAdLeadResult.fromJson(responseContent);
+  }
+}
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMemberCardServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMemberCardServiceImpl.java
index 9bdc11b365..3eaf61417c 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMemberCardServiceImpl.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMemberCardServiceImpl.java
@@ -5,6 +5,7 @@
 import java.util.HashMap;
 import java.util.Map;
 
+import me.chanjar.weixin.mp.bean.membercard.*;
 import org.apache.commons.lang3.StringUtils;
 
 import com.google.gson.Gson;
@@ -30,13 +31,6 @@
 import me.chanjar.weixin.mp.bean.card.enums.BusinessServiceType;
 import me.chanjar.weixin.mp.bean.card.enums.CardColor;
 import me.chanjar.weixin.mp.bean.card.enums.DateInfoType;
-import me.chanjar.weixin.mp.bean.membercard.ActivatePluginParam;
-import me.chanjar.weixin.mp.bean.membercard.ActivatePluginParamResult;
-import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardActivatedMessage;
-import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardCreateMessage;
-import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardUpdateMessage;
-import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardUpdateResult;
-import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardUserInfoResult;
 import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder;
 
 /**
@@ -51,7 +45,7 @@ public class WxMpMemberCardServiceImpl implements WxMpMemberCardService {
 
   private static final Gson GSON = WxMpGsonBuilder.create();
 
-  WxMpMemberCardServiceImpl(WxMpService wxMpService) {
+  public WxMpMemberCardServiceImpl(WxMpService wxMpService) {
     this.wxMpService = wxMpService;
   }
 
@@ -76,7 +70,7 @@ public WxMpCardCreateResult createMemberCard(WxMpMemberCardCreateMessage createM
       return validResult;
     }
 
-    String response = this.wxMpService.post(MEMBER_CARD_CREAET, GSON.toJson(createMessageMessage));
+    String response = this.wxMpService.post(MEMBER_CARD_CREATE, GSON.toJson(createMessageMessage));
     return WxMpCardCreateResult.fromJson(response);
   }
 
@@ -249,7 +243,7 @@ public WxMpMemberCardUpdateResult updateUserMemberCard(WxMpMemberCardUpdateMessa
 
   @Override
   public MemberCardActivateUserFormResult setActivateUserForm(MemberCardActivateUserFormRequest userFormRequest) throws WxErrorException {
-    String responseContent = this.getWxMpService().post(MEMBER_CARD_ACTIVATEUSERFORM, GSON.toJson(userFormRequest));
+    String responseContent = this.getWxMpService().post(MEMBER_CARD_ACTIVATE_USER_FORM, GSON.toJson(userFormRequest));
     return MemberCardActivateUserFormResult.fromJson(responseContent);
   }
 
@@ -284,6 +278,15 @@ public CardUpdateResult updateCardInfo(MemberCardUpdateRequest memberCardUpdateR
     return result;
   }
 
+  @Override
+  public WxMpMemberCardActivateTempInfoResult getActivateTempInfo(String activateTicket) throws WxErrorException {
+    JsonObject params = new JsonObject();
+    params.addProperty("activate_ticket", activateTicket);
+    String response = this.wxMpService.post(MEMBER_CARD_ACTIVATE_TEMP_INFO, GSON.toJson(params));
+    WxMpMemberCardActivateTempInfoResult result = GSON.fromJson(response, WxMpMemberCardActivateTempInfoResult.class);
+    return result;
+  }
+
   private static String truncateUrlPage(String strURL) {
     String strAllParam = null;
     String[] arrSplit;
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceHttpClientImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceHttpClientImpl.java
index 3db9e9b149..10e2a5b78f 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceHttpClientImpl.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceHttpClientImpl.java
@@ -63,37 +63,38 @@ public void initHttp() {
 
   @Override
   public String getAccessToken(boolean forceRefresh) throws WxErrorException {
+    if (!this.getWxMpConfigStorage().isAccessTokenExpired() && !forceRefresh) {
+      return this.getWxMpConfigStorage().getAccessToken();
+    }
+
     Lock lock = this.getWxMpConfigStorage().getAccessTokenLock();
+    lock.lock();
     try {
-      lock.lock();
-      if (this.getWxMpConfigStorage().isAccessTokenExpired() || forceRefresh) {
-        String url = String.format(WxMpService.GET_ACCESS_TOKEN_URL,
-          this.getWxMpConfigStorage().getAppId(), this.getWxMpConfigStorage().getSecret());
-        try {
-          HttpGet httpGet = new HttpGet(url);
-          if (this.getRequestHttpProxy() != null) {
-            RequestConfig config = RequestConfig.custom().setProxy(this.getRequestHttpProxy()).build();
-            httpGet.setConfig(config);
-          }
-          try (CloseableHttpResponse response = getRequestHttpClient().execute(httpGet)) {
-            String resultContent = new BasicResponseHandler().handleResponse(response);
-            WxError error = WxError.fromJson(resultContent, WxType.MP);
-            if (error.getErrorCode() != 0) {
-              throw new WxErrorException(error);
-            }
-            WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
-            this.getWxMpConfigStorage().updateAccessToken(accessToken.getAccessToken(),
-              accessToken.getExpiresIn());
-          } finally {
-            httpGet.releaseConnection();
+      String url = String.format(WxMpService.GET_ACCESS_TOKEN_URL,
+        this.getWxMpConfigStorage().getAppId(), this.getWxMpConfigStorage().getSecret());
+      try {
+        HttpGet httpGet = new HttpGet(url);
+        if (this.getRequestHttpProxy() != null) {
+          RequestConfig config = RequestConfig.custom().setProxy(this.getRequestHttpProxy()).build();
+          httpGet.setConfig(config);
+        }
+        try (CloseableHttpResponse response = getRequestHttpClient().execute(httpGet)) {
+          String resultContent = new BasicResponseHandler().handleResponse(response);
+          WxError error = WxError.fromJson(resultContent, WxType.MP);
+          if (error.getErrorCode() != 0) {
+            throw new WxErrorException(error);
           }
-        } catch (IOException e) {
-          throw new RuntimeException(e);
+          WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
+          this.getWxMpConfigStorage().updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
+          return this.getWxMpConfigStorage().getAccessToken();
+        } finally {
+          httpGet.releaseConnection();
         }
+      } catch (IOException e) {
+        throw new RuntimeException(e);
       }
     } finally {
       lock.unlock();
     }
-    return this.getWxMpConfigStorage().getAccessToken();
   }
 }
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceJoddHttpImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceJoddHttpImpl.java
index ee8411ab73..c00cd43234 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceJoddHttpImpl.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceJoddHttpImpl.java
@@ -48,36 +48,37 @@ public void initHttp() {
 
   @Override
   public String getAccessToken(boolean forceRefresh) throws WxErrorException {
+    if (!this.getWxMpConfigStorage().isAccessTokenExpired() && !forceRefresh) {
+      return this.getWxMpConfigStorage().getAccessToken();
+    }
+
     Lock lock = this.getWxMpConfigStorage().getAccessTokenLock();
+    lock.lock();
     try {
-      lock.lock();
-
-      if (this.getWxMpConfigStorage().isAccessTokenExpired() || forceRefresh) {
-        String url = String.format(WxMpService.GET_ACCESS_TOKEN_URL,
-          this.getWxMpConfigStorage().getAppId(), this.getWxMpConfigStorage().getSecret());
-
-        HttpRequest request = HttpRequest.get(url);
-
-        if (this.getRequestHttpProxy() != null) {
-          SocketHttpConnectionProvider provider = new SocketHttpConnectionProvider();
-          provider.useProxy(getRequestHttpProxy());
-
-          request.withConnectionProvider(provider);
-        }
-        HttpResponse response = request.send();
-        String resultContent = response.bodyText();
-        WxError error = WxError.fromJson(resultContent, WxType.MP);
-        if (error.getErrorCode() != 0) {
-          throw new WxErrorException(error);
-        }
-        WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
-        this.getWxMpConfigStorage().updateAccessToken(accessToken.getAccessToken(),
-          accessToken.getExpiresIn());
+      String url = String.format(WxMpService.GET_ACCESS_TOKEN_URL,
+        this.getWxMpConfigStorage().getAppId(), this.getWxMpConfigStorage().getSecret());
+
+      HttpRequest request = HttpRequest.get(url);
+
+      if (this.getRequestHttpProxy() != null) {
+        SocketHttpConnectionProvider provider = new SocketHttpConnectionProvider();
+        provider.useProxy(getRequestHttpProxy());
+
+        request.withConnectionProvider(provider);
+      }
+      HttpResponse response = request.send();
+      String resultContent = response.bodyText();
+      WxError error = WxError.fromJson(resultContent, WxType.MP);
+      if (error.getErrorCode() != 0) {
+        throw new WxErrorException(error);
       }
+      WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
+      this.getWxMpConfigStorage().updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
+
+      return this.getWxMpConfigStorage().getAccessToken();
     } finally {
       lock.unlock();
     }
-    return this.getWxMpConfigStorage().getAccessToken();
   }
 
 }
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceOkHttpImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceOkHttpImpl.java
index 6d3f5bf29a..89771250ef 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceOkHttpImpl.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceOkHttpImpl.java
@@ -6,6 +6,7 @@
 import me.chanjar.weixin.common.error.WxErrorException;
 import me.chanjar.weixin.common.util.http.HttpType;
 import me.chanjar.weixin.common.util.http.okhttp.OkHttpProxyInfo;
+import me.chanjar.weixin.mp.api.WxMpConfigStorage;
 import me.chanjar.weixin.mp.api.WxMpService;
 import okhttp3.*;
 
@@ -36,38 +37,37 @@ public HttpType getRequestType() {
 
   @Override
   public String getAccessToken(boolean forceRefresh) throws WxErrorException {
-    this.log.debug("WxMpServiceOkHttpImpl is running");
+    if (!this.getWxMpConfigStorage().isAccessTokenExpired() && !forceRefresh) {
+      return this.getWxMpConfigStorage().getAccessToken();
+    }
+
     Lock lock = this.getWxMpConfigStorage().getAccessTokenLock();
+    lock.lock();
     try {
-      lock.lock();
-
-      if (this.getWxMpConfigStorage().isAccessTokenExpired() || forceRefresh) {
-        String url = String.format(WxMpService.GET_ACCESS_TOKEN_URL,
-          this.getWxMpConfigStorage().getAppId(), this.getWxMpConfigStorage().getSecret());
+      String url = String.format(WxMpService.GET_ACCESS_TOKEN_URL,
+        this.getWxMpConfigStorage().getAppId(), this.getWxMpConfigStorage().getSecret());
 
-        Request request = new Request.Builder().url(url).get().build();
-        Response response = getRequestHttpClient().newCall(request).execute();
-        String resultContent = response.body().string();
-        WxError error = WxError.fromJson(resultContent, WxType.MP);
-        if (error.getErrorCode() != 0) {
-          throw new WxErrorException(error);
-        }
-        WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
-        this.getWxMpConfigStorage().updateAccessToken(accessToken.getAccessToken(),
-          accessToken.getExpiresIn());
+      Request request = new Request.Builder().url(url).get().build();
+      Response response = getRequestHttpClient().newCall(request).execute();
+      String resultContent = response.body().string();
+      WxError error = WxError.fromJson(resultContent, WxType.MP);
+      if (error.getErrorCode() != 0) {
+        throw new WxErrorException(error);
       }
+      WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
+      this.getWxMpConfigStorage().updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
+
+      return this.getWxMpConfigStorage().getAccessToken();
     } catch (IOException e) {
-      this.log.error(e.getMessage(), e);
+      throw new RuntimeException(e);
     } finally {
       lock.unlock();
     }
-    return this.getWxMpConfigStorage().getAccessToken();
   }
 
   @Override
   public void initHttp() {
-    this.log.debug("WxMpServiceOkHttpImpl initHttp");
-
+    WxMpConfigStorage wxMpConfigStorage = getWxMpConfigStorage();
     //设置代理
     if (wxMpConfigStorage.getHttpProxyHost() != null && wxMpConfigStorage.getHttpProxyPort() > 0) {
       httpProxy = OkHttpProxyInfo.httpProxy(wxMpConfigStorage.getHttpProxyHost(),
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/AdvancedInfo.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/AdvancedInfo.java
index f127c8ecfd..7ab5786dec 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/AdvancedInfo.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/AdvancedInfo.java
@@ -11,6 +11,7 @@
 
 /**
  * 微信会员卡高级字段信息.
+ *
  * @author yuanqixun
  * date:2018-08-25 00:36
  */
@@ -18,13 +19,6 @@
 public class AdvancedInfo implements Serializable {
   private static final long serialVersionUID = -8470424140133771841L;
 
-//    public AdvancedInfo(){
-//        useCondition = new UseCondition();
-//        abstractInfo = new Abstract();
-//        textImageList = new ArrayList<>();
-//        timeLimit = new TimeLimit();
-//    }
-
   /**
    * 使用门槛(条件).
    * 若不填写使用条件则在券面拼写 :无最低消费限制,全场通用,不限品类;并在使用说明显示: 可与其他优惠共享
@@ -56,7 +50,7 @@ public class AdvancedInfo implements Serializable {
    * 使用时段限制.
    */
   @SerializedName("time_limit")
-  private TimeLimit timeLimit;
+  private List timeLimits;
 
   /**
    * 是否可以分享朋友.
@@ -66,7 +60,7 @@ public class AdvancedInfo implements Serializable {
 
   public void addBusinessService(BusinessServiceType businessServiceType) {
     if (businessServiceType != null) {
-      if (businessServiceList == null){
+      if (businessServiceList == null) {
         businessServiceList = new ArrayList<>();
       }
 
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/BaseWxMpCardResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/BaseWxMpCardResult.java
new file mode 100644
index 0000000000..3988ee0c24
--- /dev/null
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/BaseWxMpCardResult.java
@@ -0,0 +1,25 @@
+package me.chanjar.weixin.mp.bean.card;
+
+import java.io.Serializable;
+
+/**
+ * @description 卡券返回结果基础类
+ * @author: fanxl
+ * @date: 2019/1/22 0022 10:08
+ */
+public class BaseWxMpCardResult implements Serializable {
+
+  /**
+   * 错误码
+   */
+  private Integer errcode;
+
+  /**
+   * 错误信息
+   */
+  private String errmsg;
+
+  public boolean isSuccess() {
+    return 0 == errcode;
+  }
+}
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/Card.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/Card.java
new file mode 100644
index 0000000000..c97f88f2fc
--- /dev/null
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/Card.java
@@ -0,0 +1,31 @@
+package me.chanjar.weixin.mp.bean.card;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder;
+
+import java.io.Serializable;
+
+/**
+ * .
+ * @author leeis
+ * @Date 2018/12/29
+ */
+@Data
+public class Card implements Serializable {
+
+  private static final long serialVersionUID = -3697110761983756780L;
+
+  /**
+   * 基本信息.
+   */
+  @SerializedName("base_info")
+  private BaseInfo baseInfo;
+
+  /**
+   * 创建优惠券特有的高级字段.
+   */
+  @SerializedName("advanced_info")
+  private AdvancedInfo advancedInfo;
+
+}
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/CardCreateRequest.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/CardCreateRequest.java
new file mode 100644
index 0000000000..8cb16bc7b2
--- /dev/null
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/CardCreateRequest.java
@@ -0,0 +1,13 @@
+package me.chanjar.weixin.mp.bean.card;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * @Author leeis
+ * @Date 2018/12/29
+ */
+@Data
+public class CardCreateRequest implements Serializable {
+}
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/CashCard.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/CashCard.java
new file mode 100644
index 0000000000..df5290b218
--- /dev/null
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/CashCard.java
@@ -0,0 +1,40 @@
+package me.chanjar.weixin.mp.bean.card;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder;
+
+import java.io.Serializable;
+
+/**
+ * .
+ * @author leeis
+ * @Date 2018/12/29
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public final class CashCard extends Card implements Serializable {
+  private static final long serialVersionUID = 6965491956462769745L;
+
+  /**
+   * 代金券专用,表示起用金额(单位为分),如果无起用门槛则填0
+   */
+  @SerializedName("least_cost")
+  private int leastCost;
+
+  /**
+   * 代金券专用,表示减免金额。(单位为分)
+   */
+  @SerializedName("reduce_cost")
+  private int reduceCost;
+
+  @Override
+  public String toString() {
+    return WxMpGsonBuilder.create().toJson(this);
+  }
+
+  public static CashCard fromJson(String json) {
+    return WxMpGsonBuilder.create().fromJson(json, CashCard.class);
+  }
+}
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/CashCardCreateRequest.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/CashCardCreateRequest.java
new file mode 100644
index 0000000000..dcefab0e29
--- /dev/null
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/CashCardCreateRequest.java
@@ -0,0 +1,25 @@
+package me.chanjar.weixin.mp.bean.card;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder;
+
+import java.io.Serializable;
+
+/**
+ * .
+ * @author leeis
+ * @Date 2018/12/29
+ */
+@Data
+public class CashCardCreateRequest extends CardCreateRequest implements Serializable {
+  @SerializedName("card_type")
+  private String cardType = "CASH";
+
+  private CashCard cash;
+
+  @Override
+  public String toString() {
+    return WxMpGsonBuilder.create().toJson(this);
+  }
+}
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/DiscountCard.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/DiscountCard.java
new file mode 100644
index 0000000000..b1becbd642
--- /dev/null
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/DiscountCard.java
@@ -0,0 +1,33 @@
+package me.chanjar.weixin.mp.bean.card;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder;
+
+import java.io.Serializable;
+
+/**
+ * .
+ * @author leeis
+ * @Date 2018/12/29
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public final class DiscountCard extends Card implements Serializable {
+  private static final long serialVersionUID = 1704610082472315077L;
+
+  /**
+   * 折扣券专用,表示打折额度(百分比)。填30就是七折。
+   */
+  private int discount;
+
+  @Override
+  public String toString() {
+    return WxMpGsonBuilder.create().toJson(this);
+  }
+
+  public static DiscountCard fromJson(String json) {
+    return WxMpGsonBuilder.create().fromJson(json, DiscountCard.class);
+  }
+}
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/DiscountCardCreateRequest.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/DiscountCardCreateRequest.java
new file mode 100644
index 0000000000..b527ec12fc
--- /dev/null
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/DiscountCardCreateRequest.java
@@ -0,0 +1,29 @@
+package me.chanjar.weixin.mp.bean.card;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder;
+
+import java.io.Serializable;
+
+/**
+ * .
+ * @author leeis
+ * @Date 2018/12/29
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class DiscountCardCreateRequest extends CardCreateRequest implements Serializable {
+  private static final long serialVersionUID = 1190518086576489692L;
+
+  @SerializedName("card_type")
+  private String cardType = "DISCOUNT";
+
+  private DiscountCard discount;
+
+  @Override
+  public String toString() {
+    return WxMpGsonBuilder.create().toJson(this);
+  }
+}
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GeneralCard.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GeneralCard.java
new file mode 100644
index 0000000000..2264d8ec0a
--- /dev/null
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GeneralCard.java
@@ -0,0 +1,34 @@
+package me.chanjar.weixin.mp.bean.card;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder;
+
+import java.io.Serializable;
+
+/**
+ * .
+ * @author leeis
+ * @Date 2018/12/29
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public final class GeneralCard extends Card implements Serializable {
+  private static final long serialVersionUID = -1577656733441132585L;
+
+  /**
+   * 兑换券专用,填写兑换内容的名称。
+   */
+  @SerializedName("default_detail")
+  private String defaultDetail;
+
+  @Override
+  public String toString() {
+    return WxMpGsonBuilder.create().toJson(this);
+  }
+
+  public static GeneralCard fromJson(String json) {
+    return WxMpGsonBuilder.create().fromJson(json, GeneralCard.class);
+  }
+}
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GeneralCardCreateRequest.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GeneralCardCreateRequest.java
new file mode 100644
index 0000000000..f1ac56987b
--- /dev/null
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GeneralCardCreateRequest.java
@@ -0,0 +1,30 @@
+package me.chanjar.weixin.mp.bean.card;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder;
+
+import java.io.Serializable;
+
+/**
+ * .
+ * @author leeis
+ * @Date 2018/12/29
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class GeneralCardCreateRequest extends CardCreateRequest implements Serializable {
+  private static final long serialVersionUID = 1771355872211267723L;
+
+  @SerializedName("card_type")
+  private String cardType = "GENERAL_COUPON";
+
+  @SerializedName("general_coupon")
+  private GeneralCard generalCoupon;
+
+  @Override
+  public String toString() {
+    return WxMpGsonBuilder.create().toJson(this);
+  }
+}
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GiftCard.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GiftCard.java
new file mode 100644
index 0000000000..0bfb5359ba
--- /dev/null
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GiftCard.java
@@ -0,0 +1,32 @@
+package me.chanjar.weixin.mp.bean.card;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder;
+
+import java.io.Serializable;
+
+/**
+ * .
+ * @author leeis
+ * @Date 2018/12/29
+ */
+@Data
+public final class GiftCard extends Card implements Serializable {
+
+  private static final long serialVersionUID = -6168739707511792266L;
+
+  /**
+   * 兑换券专用,填写兑换内容的名称。
+   */
+  private String gift;
+
+  @Override
+  public String toString() {
+    return WxMpGsonBuilder.create().toJson(this);
+  }
+
+  public static GiftCard fromJson(String json) {
+    return WxMpGsonBuilder.create().fromJson(json, GiftCard.class);
+  }
+}
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GiftCardCreateRequest.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GiftCardCreateRequest.java
new file mode 100644
index 0000000000..5271763f8e
--- /dev/null
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GiftCardCreateRequest.java
@@ -0,0 +1,29 @@
+package me.chanjar.weixin.mp.bean.card;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder;
+
+import java.io.Serializable;
+
+/**
+ * .
+ * @author leeis
+ * @Date 2018/12/29
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class GiftCardCreateRequest extends CardCreateRequest implements Serializable {
+  private static final long serialVersionUID = 1283655452584811858L;
+
+  @SerializedName("card_type")
+  private String cardType = "GIFT";
+
+  private GiftCard gift;
+
+  @Override
+  public String toString() {
+    return WxMpGsonBuilder.create().toJson(this);
+  }
+}
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GrouponCard.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GrouponCard.java
new file mode 100644
index 0000000000..ba343a435b
--- /dev/null
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GrouponCard.java
@@ -0,0 +1,35 @@
+package me.chanjar.weixin.mp.bean.card;
+
+import java.io.Serializable;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder;
+
+/**
+ * .
+ * @author leeis
+ * @Date 2018/12/29
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+public final class GrouponCard extends Card implements Serializable {
+
+  private static final long serialVersionUID = 3221312561666697005L;
+
+  /**
+   * 团购券专用,团购详情
+   */
+  @SerializedName("deal_detail")
+  private String dealDetail;
+
+  @Override
+  public String toString() {
+    return WxMpGsonBuilder.create().toJson(this);
+  }
+
+  public static GrouponCard fromJson(String json) {
+    return WxMpGsonBuilder.create().fromJson(json, GrouponCard.class);
+  }
+}
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GrouponCardCreateRequest.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GrouponCardCreateRequest.java
new file mode 100644
index 0000000000..aeab1fd3e8
--- /dev/null
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GrouponCardCreateRequest.java
@@ -0,0 +1,29 @@
+package me.chanjar.weixin.mp.bean.card;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder;
+
+import java.io.Serializable;
+
+/**
+ * .
+ * @author leeis
+ * @Date 2018/12/29
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class GrouponCardCreateRequest extends CardCreateRequest implements Serializable {
+  private static final long serialVersionUID = 7551441058859934512L;
+
+  @SerializedName("card_type")
+  private String cardType = "GROUPON";
+
+  private GrouponCard groupon;
+
+  @Override
+  public String toString() {
+    return WxMpGsonBuilder.create().toJson(this);
+  }
+}
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/MemberCard.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/MemberCard.java
index b8b81399d2..a6caf1bbd4 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/MemberCard.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/MemberCard.java
@@ -38,12 +38,6 @@ public final class MemberCard implements Serializable {
   @SerializedName("auto_activate")
   private boolean autoActivate;
 
-  /**
-   * 是否一键开卡.
-   */
-  @SerializedName("wx_activate")
-  private boolean wxActivate;
-
   /**
    * 显示积分.
    */
@@ -145,6 +139,24 @@ public final class MemberCard implements Serializable {
   @SerializedName("advanced_info")
   private AdvancedInfo advancedInfo;
 
+  /**
+   * 是否支持一键激活 ,填true或false.
+   */
+  @SerializedName("wx_activate")
+  private boolean wxActivate;
+
+  /**
+   * 是否支持跳转型一键激活,填true或false.
+   */
+  @SerializedName("wx_activate_after_submit")
+  private boolean wxActivateAfterSubmit;
+
+  /**
+   * 跳转型一键激活跳转的地址链接,请填写http:// 或者https://开头的链接.
+   */
+  @SerializedName("wx_activate_after_submit_url")
+  private String wxActivateAfterSubmitUrl;
+
   @Override
   public String toString() {
     return WxMpGsonBuilder.create().toJson(this);
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/MemberCardCreateRequest.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/MemberCardCreateRequest.java
index b15cf22546..5abdf73310 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/MemberCardCreateRequest.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/MemberCardCreateRequest.java
@@ -1,13 +1,20 @@
 package me.chanjar.weixin.mp.bean.card;
 
-import java.io.Serializable;
-
 import com.google.gson.annotations.SerializedName;
 import lombok.Data;
 import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder;
 
+import java.io.Serializable;
+
+/**
+ * 创建会员卡请求对象.
+ *
+ * @author yuanqixun
+ */
 @Data
 public class MemberCardCreateRequest implements Serializable {
+  private static final long serialVersionUID = -1044836608401698097L;
+
   @SerializedName("card_type")
   private String cardType = "MEMBER_CARD";
 
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/MemberCardUpdateRequest.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/MemberCardUpdateRequest.java
index 37807a068f..8c1f85351e 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/MemberCardUpdateRequest.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/MemberCardUpdateRequest.java
@@ -6,8 +6,15 @@
 
 import java.io.Serializable;
 
+/**
+ * 更新会员卡请求对象.
+ *
+ * @author yuanqixun
+ */
 @Data
 public class MemberCardUpdateRequest implements Serializable {
+  private static final long serialVersionUID = -1025759626161614466L;
+
   @SerializedName("card_id")
   private String cardId;
 
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpCard.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCard.java
similarity index 80%
rename from weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpCard.java
rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCard.java
index 71e419a764..17314c90f6 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpCard.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCard.java
@@ -1,4 +1,4 @@
-package me.chanjar.weixin.mp.bean;
+package me.chanjar.weixin.mp.bean.card;
 
 
 import java.io.Serializable;
@@ -24,7 +24,11 @@ public class WxMpCard implements Serializable {
 
   private String userCardStatus;
 
-  private Boolean canConsume;
+  private String membershipNumber;
+
+  private String code;
+
+  private Integer bonus;
 
   @Override
   public String toString() {
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardCreateMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardCreateMessage.java
new file mode 100644
index 0000000000..760002cb52
--- /dev/null
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardCreateMessage.java
@@ -0,0 +1,23 @@
+package me.chanjar.weixin.mp.bean.card;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder;
+
+import java.io.Serializable;
+
+@Data
+public final class WxMpCardCreateMessage implements Serializable {
+
+  @SerializedName("card")
+  private CardCreateRequest cardCreateRequest;
+
+  @Override
+  public String toString() {
+    return WxMpGsonBuilder.create().toJson(this);
+  }
+
+  public static WxMpCardCreateMessage fromJson(String json) {
+    return WxMpGsonBuilder.create().fromJson(json, WxMpCardCreateMessage.class);
+  }
+}
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardDeleteResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardDeleteResult.java
new file mode 100644
index 0000000000..3dcbc3534c
--- /dev/null
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardDeleteResult.java
@@ -0,0 +1,21 @@
+package me.chanjar.weixin.mp.bean.card;
+
+import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder;
+
+/**
+ * @description 删除卡券结果
+ * @author: fanxl
+ * @date: 2019/1/22 0022 10:24
+ */
+public class WxMpCardDeleteResult extends BaseWxMpCardResult {
+
+  public static WxMpCardDeleteResult fromJson(String json) {
+    return WxMpGsonBuilder.create().fromJson(json, WxMpCardDeleteResult.class);
+  }
+
+  @Override
+  public String toString() {
+    return WxMpGsonBuilder.create().toJson(this);
+  }
+
+}
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpCardResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardResult.java
similarity index 82%
rename from weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpCardResult.java
rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardResult.java
index c6acc08007..9343cb40b5 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpCardResult.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardResult.java
@@ -1,9 +1,8 @@
-package me.chanjar.weixin.mp.bean.result;
+package me.chanjar.weixin.mp.bean.card;
 
 import java.io.Serializable;
 
 import lombok.Data;
-import me.chanjar.weixin.mp.bean.WxMpCard;
 import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder;
 
 /**
@@ -28,6 +27,12 @@ public class WxMpCardResult implements Serializable {
 
   private Boolean canConsume;
 
+  private String outStr;
+
+  private String backgroundPicUrl;
+
+  private String unionid;
+
   @Override
   public String toString() {
     return WxMpGsonBuilder.create().toJson(this);
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 92c298a647..560add6042 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
@@ -109,6 +109,7 @@ public static MiniProgramPageBuilder MINIPROGRAMPAGE() {
    * {@link WxConsts.KefuMsgType#MPNEWS}
    * {@link WxConsts.KefuMsgType#WXCARD}
    * {@link WxConsts.KefuMsgType#MINIPROGRAMPAGE}
+   * {@link WxConsts.KefuMsgType#TASKCARD}
    * 
* */ diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/marketing/WxMpAdLead.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/marketing/WxMpAdLead.java new file mode 100644 index 0000000000..868d22a96b --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/marketing/WxMpAdLead.java @@ -0,0 +1,55 @@ +package me.chanjar.weixin.mp.bean.marketing; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * @author 007 + */ +@Data +public class WxMpAdLead implements Serializable { + private static final long serialVersionUID = -8889087268596440407L; + /** + * 点击ID + */ + @SerializedName("click_id") + private String click_id; + /** + * 广告组ID + */ + @SerializedName("adgroup_id") + private Long adgroup_id; + /** + * 广告组名称 + */ + @SerializedName("adgroup_name") + private String adgroup_name; + /** + * 推广计划ID + */ + @SerializedName("campaign_id") + private Long campaign_id; + /** + * 推广计划名称 + */ + @SerializedName("campaign_name") + private String campaign_name; + /** + * 代理ID + */ + @SerializedName("agency_id") + private String agency_id; + /** + * 代理名称 + */ + @SerializedName("agency_name") + private String agency_name; + /** + * 销售线索信息 + */ + @SerializedName("leads_info") + private List leads_info; +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/marketing/WxMpAdLeadFilter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/marketing/WxMpAdLeadFilter.java new file mode 100644 index 0000000000..3923025336 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/marketing/WxMpAdLeadFilter.java @@ -0,0 +1,32 @@ +package me.chanjar.weixin.mp.bean.marketing; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * @author 007 + */ +@Data +public class WxMpAdLeadFilter implements Serializable { + private static final long serialVersionUID = -1469998986497327439L; + private String field; + private String operator; + private List values; + + public JsonObject toJsonObject() { + JsonObject json = new JsonObject(); + json.addProperty("field", field); + json.addProperty("operator", operator); + if (values != null) { + JsonArray vs = new JsonArray(); + for (String value : values) { + vs.add(value); + } + } + return json; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/marketing/WxMpAdLeadInfo.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/marketing/WxMpAdLeadInfo.java new file mode 100644 index 0000000000..859a0dca5e --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/marketing/WxMpAdLeadInfo.java @@ -0,0 +1,18 @@ +package me.chanjar.weixin.mp.bean.marketing; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; + +/** + * @author 007 + */ +@Data +public class WxMpAdLeadInfo implements Serializable { + private static final long serialVersionUID = -6462312242780350479L; + @SerializedName("key") + private String key; + @SerializedName("value") + private String value; +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/marketing/WxMpAdLeadPageInfo.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/marketing/WxMpAdLeadPageInfo.java new file mode 100644 index 0000000000..296a3fef82 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/marketing/WxMpAdLeadPageInfo.java @@ -0,0 +1,23 @@ +package me.chanjar.weixin.mp.bean.marketing; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; + +/** + * @author 007 + */ +@Data +public class WxMpAdLeadPageInfo implements Serializable { + private static final long serialVersionUID = -896765006445604780L; + @SerializedName("page") + private Integer page; + @SerializedName("page_size") + private Integer pageSize; + @SerializedName("total_page") + private Integer totalPage; + @SerializedName("total_number") + private Integer totalNumber; + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/marketing/WxMpAdLeadResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/marketing/WxMpAdLeadResult.java new file mode 100644 index 0000000000..61805018b0 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/marketing/WxMpAdLeadResult.java @@ -0,0 +1,32 @@ +package me.chanjar.weixin.mp.bean.marketing; + +import com.google.gson.JsonParser; +import com.google.gson.annotations.SerializedName; +import com.google.gson.reflect.TypeToken; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; +import java.util.List; + +/** + * @author 007 + */ +@Data +public class WxMpAdLeadResult implements Serializable { + private static final long serialVersionUID = -1526796632563660821L; + protected static final JsonParser JSON_PARSER = new JsonParser(); + + @SerializedName("page_info") + private WxMpAdLeadPageInfo pageInfo; + @SerializedName("list") + private List adLeads; + + public static WxMpAdLeadResult fromJson(String json) { + + return WxMpGsonBuilder.create().fromJson( + JSON_PARSER.parse(json).getAsJsonObject().get("data"), + new TypeToken() { + }.getType()); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/marketing/WxMpUserAction.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/marketing/WxMpUserAction.java new file mode 100644 index 0000000000..69fced907a --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/marketing/WxMpUserAction.java @@ -0,0 +1,39 @@ +package me.chanjar.weixin.mp.bean.marketing; + +import com.google.gson.JsonObject; +import lombok.Data; + +import java.io.Serializable; + +/** + * @author 007 + */ + +@Data +public class WxMpUserAction implements Serializable { + private static final long serialVersionUID = 7042393762652152209L; + private Long userActionSetId; + private String url; + private Boolean actionTime; + private String actionType; + private String clickId; + private Integer actionParam; + + public JsonObject toJsonObject() { + JsonObject json = new JsonObject(); + json.addProperty("user_action_set_id", this.userActionSetId); + json.addProperty("url", this.url); + json.addProperty("action_time", this.actionTime); + 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); + json.add("action_param", actionParamJson); + } + return json; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/marketing/WxMpUserActionSet.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/marketing/WxMpUserActionSet.java new file mode 100644 index 0000000000..84b0f4bba0 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/marketing/WxMpUserActionSet.java @@ -0,0 +1,56 @@ +package me.chanjar.weixin.mp.bean.marketing; + +import com.google.gson.JsonParser; +import com.google.gson.annotations.SerializedName; +import com.google.gson.reflect.TypeToken; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; +import java.util.List; + +/** + * @author 007 + */ + +@Data +public class WxMpUserActionSet implements Serializable { + private static final long serialVersionUID = 1979861770645159905L; + protected static final JsonParser JSON_PARSER = new JsonParser(); + + /** + * user_action_set_id + * 用户行为源名称 + */ + @SerializedName("user_action_set_id") + private Long userActionSetId; + + /** + * title. + * 用户行为源描述 + */ + @SerializedName("description") + private String description; + + /** + * activate_status. + * 数据接入状态, true 表示已接入, false 表示未接入 + */ + @SerializedName("activate_status") + private Boolean activate_status; + + /** + * created_time. + * 创建时间 + */ + @SerializedName("created_time") + private String createdTime; + + public static List fromJson(String json) { + return WxMpGsonBuilder.create().fromJson( + JSON_PARSER.parse(json).getAsJsonObject().get("data").getAsJsonObject().get("list"), + new TypeToken>() { + }.getType()); + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/membercard/WxMpMemberCardActivateTempInfoResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/membercard/WxMpMemberCardActivateTempInfoResult.java new file mode 100644 index 0000000000..120e326701 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/membercard/WxMpMemberCardActivateTempInfoResult.java @@ -0,0 +1,24 @@ +package me.chanjar.weixin.mp.bean.membercard; + +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + + +/** + * @author thomas + * @date 2019/4/26 + */ +@Data +public class WxMpMemberCardActivateTempInfoResult { + + private String errorCode; + + private String errorMsg; + + private MemberCardUserInfo userInfo; + + public static WxMpMemberCardActivateTempInfoResult fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, WxMpMemberCardActivateTempInfoResult.class); + } + +} 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 f7405600ef..00f8d70c88 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 @@ -1,5 +1,9 @@ package me.chanjar.weixin.mp.bean.message; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.annotations.XStreamConverter; import lombok.Data; @@ -7,18 +11,26 @@ import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; - +/** + * 被动回复的图文消息xml. + * @author chanjarster + */ @XStreamAlias("xml") @Data @EqualsAndHashCode(callSuper = true) public class WxMpXmlOutNewsMessage extends WxMpXmlOutMessage { private static final long serialVersionUID = -4604402850905714772L; + /** + * 图文消息信息. + * 注意,如果图文数超过限制,则将只发限制内的条数 + */ @XStreamAlias("Articles") protected final List articles = new ArrayList<>(); + /** + * 图文消息个数. + * 当用户发送文本、图片、视频、图文、地理位置这五种消息时,开发者只能回复1条图文消息;其余场景最多可回复8条图文消息 + */ @XStreamAlias("ArticleCount") protected int articleCount; @@ -36,23 +48,35 @@ public void addArticle(Item item) { public static class Item implements Serializable { private static final long serialVersionUID = -4971456355028904754L; + /** + * 图文消息标题. + */ @XStreamAlias("Title") @XStreamConverter(value = XStreamCDataConverter.class) private String title; + /** + * 图文消息描述. + */ @XStreamAlias("Description") @XStreamConverter(value = XStreamCDataConverter.class) private String description; + /** + * 图片链接. + * 支持JPG、PNG格式,较好的效果为大图360*200,小图200*200 + */ @XStreamAlias("PicUrl") @XStreamConverter(value = XStreamCDataConverter.class) private String picUrl; + /** + * 点击图文消息跳转链接. + */ @XStreamAlias("Url") @XStreamConverter(value = XStreamCDataConverter.class) private String url; } - } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/constant/WxMpEventConstants.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/constant/WxMpEventConstants.java index df1790e3da..0019816a20 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/constant/WxMpEventConstants.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/constant/WxMpEventConstants.java @@ -10,23 +10,23 @@ */ public class WxMpEventConstants { /** - * 门店审核事件 + * 门店审核事件. */ public static final String POI_CHECK_NOTIFY = "poi_check_notify"; /** - * 接收会员信息事件 + * 接收会员信息事件. */ public static final String SUBMIT_MEMBERCARD_USER_INFO = "submit_membercard_user_info"; /** - * 微信摇一摇周边>>摇一摇事件通知 + * 微信摇一摇周边>>摇一摇事件通知. */ public static final String SHAKEAROUND_USER_SHAKE = "ShakearoundUserShake"; /** - * 卡券相关事件 + * 卡券相关事件. */ public static class Card { public static final String CARD_PASS_CHECK = "card_pass_check"; @@ -39,74 +39,89 @@ public static class Card { public static final String USER_ENTER_SESSION_FROM_CARD = "user_enter_session_from_card"; /** - * 卡券转赠事件 + * 卡券转赠事件. */ public static final String USER_GIFTING_CARD = "user_gifting_card"; /** - * 库存报警 + * 库存报警. */ public static final String CARD_SKU_REMIND = "card_sku_remind"; /** - * 会员卡内容更新事件 + * 会员卡内容更新事件. */ public static final String UPDATE_MEMBER_CARD = "update_member_card"; /** - * 券点流水详情事件 + * 券点流水详情事件. */ public static final String CARD_PAY_ORDER = "card_pay_order"; + + /** + * 用户购买礼品卡付款成功事件. + */ + public static final String GIFTCARD_PAY_DONE = "giftcard_pay_done"; + + /** + * 用户购买后赠送事件. + */ + public static final String GIFTCARD_SEND_TO_FRIEND = "giftcard_send_to_friend"; + + /** + * 用户领取礼品卡成功事件. + */ + public static final String GIFTCARD_USER_ACCEPT = "giftcard_user_accept"; } /** - * 客服相关事件 + * 客服相关事件. */ public static class CustomerService { /** - * 客服接入会话 + * 客服接入会话. */ public static final String KF_CREATE_SESSION = "kf_create_session"; /** - * 客服关闭会话 + * 客服关闭会话. */ public static final String KF_CLOSE_SESSION = "kf_close_session"; /** - * 客服转接会话 + * 客服转接会话. */ public static final String KF_SWITCH_SESSION = "kf_switch_session"; } /** - * 微信认证事件 + * 微信认证事件. */ public static class Qualification { /** - * 资质认证成功 + * 资质认证成功. */ public static final String QUALIFICATION_VERIFY_SUCCESS = "qualification_verify_success"; /** - * 资质认证失败 + * 资质认证失败. */ public static final String QUALIFICATION_VERIFY_FAIL = "qualification_verify_fail"; /** - * 名称认证成功 + * 名称认证成功. */ public static final String NAMING_VERIFY_SUCCESS = "naming_verify_success"; /** - * 名称认证失败 + * 名称认证失败. */ public static final String NAMING_VERIFY_FAIL = "naming_verify_fail"; /** - * 年审通知 + * 年审通知. */ public static final String ANNUAL_RENEW = "annual_renew"; /** - * 认证过期失效通知 + * 认证过期失效通知. */ public static final String VERIFY_EXPIRED = "verify_expired"; } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/WxMpConfigStorageHolder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/WxMpConfigStorageHolder.java new file mode 100644 index 0000000000..af2f9226db --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/WxMpConfigStorageHolder.java @@ -0,0 +1,25 @@ +package me.chanjar.weixin.mp.util; + + +/** + * @Author: yd + * @Date: 2019-03-20 22:06 + */ +public class WxMpConfigStorageHolder { + + private final static ThreadLocal WX_MP_CONFIG_STORAGE_CHOSE = new ThreadLocal() { + @Override + protected String initialValue() { + return "default"; + } + }; + + public static String get() { + return WX_MP_CONFIG_STORAGE_CHOSE.get(); + } + + public static void set(String label) { + WX_MP_CONFIG_STORAGE_CHOSE.set(label); + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpCardGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpCardGsonAdapter.java index 022a59f111..872bf8c757 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpCardGsonAdapter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpCardGsonAdapter.java @@ -1,11 +1,15 @@ package me.chanjar.weixin.mp.util.json; -import com.google.gson.*; -import me.chanjar.weixin.common.util.json.GsonHelper; -import me.chanjar.weixin.mp.bean.WxMpCard; - 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 me.chanjar.weixin.common.util.json.GsonHelper; +import me.chanjar.weixin.mp.bean.card.WxMpCard; + /** * Created by YuJian on 15/11/11. * @@ -18,11 +22,16 @@ public class WxMpCardGsonAdapter implements JsonDeserializer { public WxMpCard deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { WxMpCard card = new WxMpCard(); + JsonObject jsonObject = jsonElement.getAsJsonObject(); card.setCardId(GsonHelper.getString(jsonObject, "card_id")); card.setBeginTime(GsonHelper.getLong(jsonObject, "begin_time")); card.setEndTime(GsonHelper.getLong(jsonObject, "end_time")); + card.setUserCardStatus(GsonHelper.getString(jsonObject, "user_card_status")); + card.setMembershipNumber(GsonHelper.getString(jsonObject, "membership_number")); + card.setCode(GsonHelper.getString(jsonObject, "code")); + card.setBonus(GsonHelper.getInteger(jsonObject, "bonus")); return card; } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpCardResultGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpCardResultGsonAdapter.java index 7e476acbe0..defe8822bb 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpCardResultGsonAdapter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpCardResultGsonAdapter.java @@ -1,12 +1,16 @@ package me.chanjar.weixin.mp.util.json; -import com.google.gson.*; +import java.lang.reflect.Type; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; import com.google.gson.reflect.TypeToken; import me.chanjar.weixin.common.util.json.GsonHelper; -import me.chanjar.weixin.mp.bean.WxMpCard; -import me.chanjar.weixin.mp.bean.result.WxMpCardResult; - -import java.lang.reflect.Type; +import me.chanjar.weixin.mp.bean.card.WxMpCard; +import me.chanjar.weixin.mp.bean.card.WxMpCardResult; /** * Created by YuJian on 15/11/11. @@ -18,6 +22,7 @@ public class WxMpCardResultGsonAdapter implements JsonDeserializer() { 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 a4357e9117..b16775b40b 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 @@ -3,9 +3,12 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import me.chanjar.weixin.mp.bean.*; +import me.chanjar.weixin.mp.bean.card.WxMpCard; +import me.chanjar.weixin.mp.bean.card.WxMpCardResult; import me.chanjar.weixin.mp.bean.datacube.WxDataCubeUserCumulate; import me.chanjar.weixin.mp.bean.datacube.WxDataCubeUserSummary; import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; +import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardActivateTempInfoResult; import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardUpdateResult; import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardUserInfoResult; import me.chanjar.weixin.mp.bean.material.*; @@ -56,6 +59,7 @@ public class WxMpGsonBuilder { INSTANCE.registerTypeAdapter(WxMpUserBlacklistGetResult.class, new WxUserBlacklistGetResultGsonAdapter()); INSTANCE.registerTypeAdapter(WxMpMemberCardUserInfoResult.class, new WxMpMemberCardUserInfoResultGsonAdapter()); INSTANCE.registerTypeAdapter(WxMpMemberCardUpdateResult.class, new WxMpMemberCardUpdateResultGsonAdapter()); + INSTANCE.registerTypeAdapter(WxMpMemberCardActivateTempInfoResult.class, new WxMpMemberCardActivateTempInfoResultGsonAdapter()); } public static Gson create() { diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMemberCardActivateTempInfoResultGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMemberCardActivateTempInfoResultGsonAdapter.java new file mode 100644 index 0000000000..a38a27aa01 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMemberCardActivateTempInfoResultGsonAdapter.java @@ -0,0 +1,65 @@ +package me.chanjar.weixin.mp.util.json; + +import com.google.gson.*; +import me.chanjar.weixin.common.util.json.GsonHelper; +import me.chanjar.weixin.mp.bean.membercard.MemberCardUserInfo; +import me.chanjar.weixin.mp.bean.membercard.NameValues; +import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardActivateTempInfoResult; + +import java.lang.reflect.Type; + +/** + * Json to WxMpMemberCardActivateTempInfoResultGsonAdapter 的转换适配器 + * + * @author thomas(351402401 @ qq.com) + * @version 2019/4/26 + */ +public class WxMpMemberCardActivateTempInfoResultGsonAdapter implements JsonDeserializer { + + @Override + public WxMpMemberCardActivateTempInfoResult deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { + WxMpMemberCardActivateTempInfoResult result = new WxMpMemberCardActivateTempInfoResult(); + + JsonObject jsonObject = jsonElement.getAsJsonObject(); + + result.setErrorCode(GsonHelper.getString(jsonObject, "errcode")); + result.setErrorMsg(GsonHelper.getString(jsonObject, "errmsg")); + + JsonObject userInfoJsonObject = jsonObject.getAsJsonObject("info"); + MemberCardUserInfo cardUserInfo = new MemberCardUserInfo(); + + JsonArray commonFieldListObj = userInfoJsonObject.getAsJsonArray("common_field_list"); + NameValues[] commonFieldListValues = new NameValues[commonFieldListObj.size()]; + for (int i = 0; i < commonFieldListObj.size(); i++) { + JsonObject commonField = commonFieldListObj.get(i).getAsJsonObject(); + NameValues commonNameValues = new NameValues(); + commonNameValues.setName(GsonHelper.getString(commonField, "name")); + commonNameValues.setValue(GsonHelper.getString(commonField, "value")); + commonFieldListValues[i] = commonNameValues; + } + cardUserInfo.setCommonFieldList(commonFieldListValues); + + JsonArray customFieldListObj = userInfoJsonObject.getAsJsonArray("custom_field_list"); + NameValues[] customFieldListValues = new NameValues[customFieldListObj.size()]; + for (int i = 0; i < customFieldListObj.size(); i++) { + JsonObject customField = customFieldListObj.get(i).getAsJsonObject(); + NameValues customNameValues = new NameValues(); + customNameValues.setName(GsonHelper.getString(customField, "name")); + customNameValues.setValue(GsonHelper.getString(customField, "value")); + + JsonArray valueListArray = customField.getAsJsonArray("value_list"); + String[] valueList = new String[valueListArray.size()]; + for (int j = 0; j < valueListArray.size(); j++) { + valueList[j] = valueListArray.get(j).getAsString(); + } + customNameValues.setValueList(valueList); + customFieldListValues[i] = customNameValues; + } + cardUserInfo.setCustomFieldList(customFieldListValues); + + result.setUserInfo(cardUserInfo); + + return result; + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMemberCardUserInfoResultGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMemberCardUserInfoResultGsonAdapter.java index 3b14e0dc10..82790a0b0d 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMemberCardUserInfoResultGsonAdapter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMemberCardUserInfoResultGsonAdapter.java @@ -1,50 +1,62 @@ package me.chanjar.weixin.mp.util.json; -import com.google.gson.*; +import java.lang.reflect.Type; + +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 me.chanjar.weixin.common.util.json.GsonHelper; import me.chanjar.weixin.mp.bean.membercard.MemberCardUserInfo; import me.chanjar.weixin.mp.bean.membercard.NameValues; import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardUserInfoResult; -import java.lang.reflect.Type; +import static me.chanjar.weixin.common.util.json.GsonHelper.getString; /** * Json to WxMpMemberCardUserInfoResult 的转换适配器 * - * @author YuJian(mgcnrx11@gmail.com) + * @author YuJian(mgcnrx11 @ gmail.com) * @version 2017/7/11 */ public class WxMpMemberCardUserInfoResultGsonAdapter implements JsonDeserializer { @Override - public WxMpMemberCardUserInfoResult deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { + public WxMpMemberCardUserInfoResult deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext context) + throws JsonParseException { WxMpMemberCardUserInfoResult result = new WxMpMemberCardUserInfoResult(); JsonObject jsonObject = jsonElement.getAsJsonObject(); - result.setOpenId(GsonHelper.getString(jsonObject, "openid")); - result.setErrorCode(GsonHelper.getString(jsonObject, "errcode")); - result.setErrorMsg(GsonHelper.getString(jsonObject, "errmsg")); - result.setNickname(GsonHelper.getString(jsonObject, "nickname")); - result.setMembershipNumber(GsonHelper.getString(jsonObject, "membership_number")); + result.setOpenId(getString(jsonObject, "openid")); + result.setErrorCode(getString(jsonObject, "errcode")); + result.setErrorMsg(getString(jsonObject, "errmsg")); + result.setNickname(getString(jsonObject, "nickname")); + result.setMembershipNumber(getString(jsonObject, "membership_number")); result.setBonus(GsonHelper.getInteger(jsonObject, "bonus")); result.setBalance(GsonHelper.getDouble(jsonObject, "balance")); - result.setSex(GsonHelper.getString(jsonObject, "sex")); - result.setUserCardStatus(GsonHelper.getString(jsonObject, "user_card_status")); + result.setSex(getString(jsonObject, "sex")); + result.setUserCardStatus(getString(jsonObject, "user_card_status")); result.setHasActive(GsonHelper.getBoolean(jsonObject, "has_active")); JsonObject userInfoJsonObject = jsonObject.getAsJsonObject("user_info"); - MemberCardUserInfo cardUserInfo = new MemberCardUserInfo(); + if (userInfoJsonObject == null) { + return result; + } JsonArray commonFieldListObj = userInfoJsonObject.getAsJsonArray("common_field_list"); NameValues[] commonFieldListValues = new NameValues[commonFieldListObj.size()]; for (int i = 0; i < commonFieldListObj.size(); i++) { JsonObject commonField = commonFieldListObj.get(i).getAsJsonObject(); NameValues commonNameValues = new NameValues(); - commonNameValues.setName(GsonHelper.getString(commonField, "name")); - commonNameValues.setValue(GsonHelper.getString(commonField, "value")); + commonNameValues.setName(getString(commonField, "name")); + commonNameValues.setValue(getString(commonField, "value")); commonFieldListValues[i] = commonNameValues; } + + MemberCardUserInfo cardUserInfo = new MemberCardUserInfo(); cardUserInfo.setCommonFieldList(commonFieldListValues); JsonArray customFieldListObj = userInfoJsonObject.getAsJsonArray("custom_field_list"); @@ -52,8 +64,8 @@ public WxMpMemberCardUserInfoResult deserialize(JsonElement jsonElement, Type ty for (int i = 0; i < customFieldListObj.size(); i++) { JsonObject customField = customFieldListObj.get(i).getAsJsonObject(); NameValues customNameValues = new NameValues(); - customNameValues.setName(GsonHelper.getString(customField, "name")); - customNameValues.setValue(GsonHelper.getString(customField, "value")); + customNameValues.setName(getString(customField, "name")); + customNameValues.setValue(getString(customField, "value")); JsonArray valueListArray = customField.getAsJsonArray("value_list"); String[] valueList = new String[valueListArray.size()]; @@ -63,6 +75,7 @@ public WxMpMemberCardUserInfoResult deserialize(JsonElement jsonElement, Type ty customNameValues.setValueList(valueList); customFieldListValues[i] = customNameValues; } + cardUserInfo.setCustomFieldList(customFieldListValues); result.setUserInfo(cardUserInfo); diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/BaseWxMpServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/BaseWxMpServiceImplTest.java new file mode 100644 index 0000000000..ff0d7537a7 --- /dev/null +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/BaseWxMpServiceImplTest.java @@ -0,0 +1,40 @@ +package me.chanjar.weixin.mp.api.impl; + +import com.google.inject.Inject; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.api.test.ApiTestModule; +import me.chanjar.weixin.mp.util.WxMpConfigStorageHolder; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +/** + *
+ *  Created by BinaryWang on 2019/3/29.
+ * 
+ * + * @author Binary Wang + */ +@Test +@Guice(modules = ApiTestModule.class) +public class BaseWxMpServiceImplTest { + @Inject + private WxMpService wxService; + + @Test + public void testSwitchover() { + assertTrue(this.wxService.switchover("another")); + assertThat(WxMpConfigStorageHolder.get()).isEqualTo("another"); + assertFalse(this.wxService.switchover("whatever")); + } + + @Test + public void testSwitchoverTo() throws WxErrorException { + assertThat(this.wxService.switchoverTo("another").getAccessToken()).isNotEmpty(); + assertThat(WxMpConfigStorageHolder.get()).isEqualTo("another"); + } +} diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpAiOpenServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpAiOpenServiceImplTest.java index 1e61c1faa8..9cf770ac5c 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpAiOpenServiceImplTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpAiOpenServiceImplTest.java @@ -1,14 +1,16 @@ package me.chanjar.weixin.mp.api.impl; +import java.io.File; + +import org.testng.annotations.*; + import com.google.inject.Inject; import me.chanjar.weixin.common.error.WxErrorException; -import me.chanjar.weixin.mp.enums.AiLangType; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.api.test.ApiTestModule; -import org.testng.annotations.Guice; -import org.testng.annotations.Test; +import me.chanjar.weixin.mp.enums.AiLangType; -import java.io.File; +import static org.assertj.core.api.Assertions.assertThat; /** *
@@ -35,13 +37,12 @@ public void testRecogniseVoice() throws WxErrorException {
     String voiceId = System.currentTimeMillis() + "a";
     AiLangType lang = AiLangType.zh_CN;
     final String result = this.wxService.getAiOpenService().recogniseVoice(voiceId, lang, new File("d:\\t.mp3"));
-    System.out.println(result);
+    assertThat(result).isNotEmpty();
   }
 
   @Test
   public void testTranslate() throws WxErrorException {
-    final String responseContent = this.wxService.getAiOpenService()
-      .translate(AiLangType.zh_CN, AiLangType.en_US, "微信文档很坑爹");
-    System.out.println(responseContent);
+    final String result = this.wxService.getAiOpenService().translate(AiLangType.zh_CN, AiLangType.en_US, "微信文档很坑爹");
+    assertThat(result).isNotEmpty();
   }
 }
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpCardServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpCardServiceImplTest.java
index 06945d9a08..37147e535a 100644
--- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpCardServiceImplTest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpCardServiceImplTest.java
@@ -2,12 +2,15 @@
 
 import com.google.inject.Inject;
 import me.chanjar.weixin.common.bean.WxCardApiSignature;
+import me.chanjar.weixin.common.error.WxErrorException;
 import me.chanjar.weixin.mp.api.WxMpService;
 import me.chanjar.weixin.mp.api.test.ApiTestModule;
-import me.chanjar.weixin.mp.bean.result.WxMpCardResult;
-import org.testng.annotations.*;
+import me.chanjar.weixin.mp.bean.card.*;
+import org.testng.annotations.Guice;
+import org.testng.annotations.Test;
 
-import static org.testng.AssertJUnit.*;
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertNotNull;
 
 /**
  * 测试代码仅供参考,未做严格测试,因原接口作者并未提供单元测试代码
@@ -100,4 +103,107 @@ public void testUnavailableCardCode() throws Exception {
     assertNotNull(result);
     System.out.println(result);
   }
+
+  @Test
+  public void testCreateGrouponCard() throws WxErrorException {
+
+    BaseInfo base = new BaseInfo();
+    base.setLogoUrl("http://mmbiz.qpic.cn/mmbiz/iaL1LJM1mF9aRKPZJkmG8xXhiaHqkKSVMMWeN3hLut7X7hicFNjakmxibMLGWpXrEXB33367o7zHN0CwngnQY7zb7g/0");
+    base.setBrandName("测试优惠券");
+    base.setCodeType("CODE_TYPE_QRCODE");
+    base.setTitle("测试标题");
+    base.setColor("Color010");
+    base.setNotice("测试Notice");
+    base.setServicePhone("020-88888888");
+    base.setDescription("不可与其他优惠同享\\n如需团购券发票,请在消费时向商户提出\\n店内均可使用,仅限堂食");
+    DateInfo info = new DateInfo();
+    info.setType("DATE_TYPE_FIX_TERM");
+    info.setFixedBeginTerm(0);
+    info.setFixedTerm(30);
+    base.setDateInfo(info);
+    Sku sku = new Sku();
+    sku.setQuantity(100);
+    base.setSku(sku);
+    base.setGetLimit(1);
+    base.setCanShare(true);
+    base.setCanGiveFriend(true);
+    base.setUseAllLocations(true);
+    base.setCenterTitle("顶部居中按钮");
+    base.setCenterSubTitle("按钮下方的wording");
+    base.setCenterUrl("www.qq.com");
+    base.setCustomUrl("http://www.qq.com");
+    base.setCustomUrlName("立即使用");
+    base.setCustomUrlSubTitle("副标题tip");
+    base.setPromotionUrlName("更多优惠");
+    base.setPromotionUrl("http://www.qq.com");
+    base.setLocationIdList("1234");
+
+    //团购券
+    WxMpCardCreateMessage grouponMessage = new WxMpCardCreateMessage();
+    GrouponCardCreateRequest grouponCardCreateRequest = new GrouponCardCreateRequest();
+    GrouponCard grouponCard = new GrouponCard();
+    grouponCard.setBaseInfo(base);
+    grouponCard.setDealDetail("deal detail");
+
+    grouponCardCreateRequest.setGroupon(grouponCard);
+    grouponMessage.setCardCreateRequest(grouponCardCreateRequest);
+
+    System.out.println(this.wxService.getCardService().createCard(grouponMessage));
+
+    //现金券
+    WxMpCardCreateMessage cashMessage = new WxMpCardCreateMessage();
+    CashCardCreateRequest cashCardCreateRequest = new CashCardCreateRequest();
+    CashCard cashCard = new CashCard();
+    cashCard.setBaseInfo(base);
+    cashCard.setLeastCost(1000);
+    cashCard.setReduceCost(100);
+
+    cashCardCreateRequest.setCash(cashCard);
+    cashMessage.setCardCreateRequest(cashCardCreateRequest);
+
+    System.out.println(this.wxService.getCardService().createCard(cashMessage));
+
+    //折扣券
+    WxMpCardCreateMessage discountMessage = new WxMpCardCreateMessage();
+    DiscountCardCreateRequest discountCardCreateRequest = new DiscountCardCreateRequest();
+    DiscountCard discountCard = new DiscountCard();
+    discountCard.setBaseInfo(base);
+    discountCard.setDiscount(30);
+
+    discountCardCreateRequest.setDiscount(discountCard);
+    discountMessage.setCardCreateRequest(discountCardCreateRequest);
+
+    System.out.println(this.wxService.getCardService().createCard(discountMessage));
+
+    //兑换券
+    WxMpCardCreateMessage giftMessage = new WxMpCardCreateMessage();
+    GiftCardCreateRequest giftCardCreateRequest = new GiftCardCreateRequest();
+    GiftCard giftCard = new GiftCard();
+    giftCard.setBaseInfo(base);
+    giftCard.setGift("星巴克雪瑞纳咖啡大杯");
+
+    giftCardCreateRequest.setGift(giftCard);
+    giftMessage.setCardCreateRequest(giftCardCreateRequest);
+    System.out.println(this.wxService.getCardService().createCard(giftMessage));
+
+    //普通兑换券
+    WxMpCardCreateMessage generalMessage = new WxMpCardCreateMessage();
+    GeneralCardCreateRequest generalCardCreateRequest = new GeneralCardCreateRequest();
+    GeneralCard generalCard = new GeneralCard();
+    generalCard.setBaseInfo(base);
+    generalCard.setDefaultDetail("音乐木盒");
+
+    generalCardCreateRequest.setGeneralCoupon(generalCard);
+    generalMessage.setCardCreateRequest(generalCardCreateRequest);
+    System.out.println(this.wxService.getCardService().createCard(generalMessage));
+  }
+
+  @Test
+  public void testDeleteCard() throws Exception {
+    String cardId = "pwkrWjtw7W4_l50kCQcZ1in1yS6g";
+    WxMpCardDeleteResult result = this.wxService.getCardService().deleteCard(cardId);
+    assertEquals(result.isSuccess(), true);
+    System.out.println(result);
+  }
+
 }
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMemberCardServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMemberCardServiceImplTest.java
index fbdb7cea29..96eb671370 100644
--- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMemberCardServiceImplTest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMemberCardServiceImplTest.java
@@ -23,9 +23,9 @@ public class WxMpMemberCardServiceImplTest {
 
   @Inject
   protected WxMpService wxService;
-  private String cardId = "p2iQk1uwOUYlzHm4s-UYdZnABW88";
-  private String code = "435223630779";
-  private String openId = "o2iQk1u5X-XIJkatmAK1Q8VVuS90";
+  private String cardId = "p4p-v1bKn9tiQHxyO79aKmuTIZlQ";
+  private String code = "224765120681";
+  private String openId = "o4p-v1TIemEIpBSrSrTprkCaG6Xc";
 
   @Test
   public void createMemberCard() throws Exception {
@@ -150,6 +150,15 @@ public void testGetActivateUrl() throws Exception {
     WxMpMemberCardService memberCardService = this.wxService.getMemberCardService();
     ActivatePluginParam response = memberCardService.getActivatePluginParam(cardId, "test");
     System.out.println(response);
+  }
 
+  @Test
+  public void testGetActivateTempInfo() throws Exception {
+    String activateTicket = "fDZv9eMQAFfrNr3XBoqhb8eUX67DFb6h8yXDelGSMDLfg2OAIGQcU7mEKecnWZBK%2B%2Bvm%2FtZxZJrbRkdJB%2FUmpVoJkEsbeH%2BOefcntAsYDKA%3D";
+    WxMpMemberCardService memberCardService = this.wxService.getMemberCardService();
+    WxMpMemberCardActivateTempInfoResult result = memberCardService.getActivateTempInfo(activateTicket);
+    assertNotNull(result);
+    System.out.println(result);
   }
+
 }
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMenuServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMenuServiceImplTest.java
index c25c946df4..45c5e8af99 100644
--- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMenuServiceImplTest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMenuServiceImplTest.java
@@ -9,9 +9,11 @@
 import me.chanjar.weixin.mp.api.test.ApiTestModule;
 import me.chanjar.weixin.mp.bean.menu.WxMpGetSelfMenuInfoResult;
 import me.chanjar.weixin.mp.bean.menu.WxMpMenu;
-import org.testng.annotations.*;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Guice;
+import org.testng.annotations.Test;
 
-import static org.testng.Assert.*;
+import static org.testng.Assert.assertNotNull;
 
 /**
  * 测试菜单
@@ -19,7 +21,7 @@
  * @author chanjarster
  * @author Binary Wang
  */
-@Test(groups = "menuAPI")
+@Test
 @Guice(modules = ApiTestModule.class)
 public class WxMpMenuServiceImplTest {
 
@@ -88,6 +90,48 @@ public void testCreateConditionalMenu() throws WxErrorException {
     System.out.println(this.menuId);
   }
 
+  @Test
+  public void testMultiCreateConditionalMenu() throws WxErrorException {
+    String json = "{\n" +
+      " 	\"button\":[\n" +
+      " 	{	\n" +
+      "    	\"type\":\"click\",\n" +
+      "    	\"name\":\"今日歌曲\",\n" +
+      "     	\"key\":\"V1001_TODAY_MUSIC\" \n" +
+      "	},\n" +
+      "	{ \n" +
+      "		\"name\":\"菜单\",\n" +
+      "		\"sub_button\":[\n" +
+      "		{	\n" +
+      "			\"type\":\"view\",\n" +
+      "			\"name\":\"搜索\",\n" +
+      "			\"url\":\"http://www.soso.com/\"\n" +
+      "		},\n" +
+      "		{\n" +
+      "			\"type\":\"view\",\n" +
+      "			\"name\":\"视频\",\n" +
+      "			\"url\":\"http://v.qq.com/\"\n" +
+      "		},\n" +
+      "		{\n" +
+      "			\"type\":\"click\",\n" +
+      "			\"name\":\"赞一下我们\",\n" +
+      "			\"key\":\"V1001_GOOD\"\n" +
+      "		}]\n" +
+      " }],\n" +
+      "\"matchrule\":{\n" +
+      "  \"tag_id\":\"2\",\n" +
+      "  \"sex\":\"1\",\n" +
+      "  \"country\":\"中国\",\n" +
+      "  \"province\":\"广东\",\n" +
+      "  \"city\":\"广州\",\n" +
+      "  \"client_platform_type\":\"2\",\n" +
+      "  \"language\":\"zh_CN\"\n" +
+      "  }\n" +
+      "}";
+    this.menuId = this.wxService.getMenuService().menuCreate(json);
+    System.out.println(this.menuId);
+  }
+
   @Test(dependsOnMethods = {"testCreateConditionalMenu"})
   public void testMenuGet_AfterCreateConditionalMenu() throws WxErrorException {
     WxMpMenu wxMenu = this.wxService.getMenuService().menuGet();
@@ -147,7 +191,7 @@ public void testMenuGet() throws WxErrorException {
     System.out.println(wxMenu.toJson());
   }
 
-  @Test(dependsOnMethods = {"testMenuGet","testMenuCreate"})
+  @Test(dependsOnMethods = {"testMenuGet", "testMenuCreate"})
   public void testMenuDelete() throws WxErrorException {
     this.wxService.getMenuService().menuDelete();
   }
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/test/ApiTestModule.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/test/ApiTestModule.java
index 98173f7d35..29c8d2e2b8 100644
--- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/test/ApiTestModule.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/test/ApiTestModule.java
@@ -14,7 +14,6 @@
 import me.chanjar.weixin.common.util.xml.XStreamInitializer;
 import me.chanjar.weixin.mp.api.WxMpConfigStorage;
 import me.chanjar.weixin.mp.api.WxMpService;
-import me.chanjar.weixin.mp.api.impl.WxMpServiceOkHttpImpl;
 
 public class ApiTestModule implements Module {
   private final Logger log = LoggerFactory.getLogger(this.getClass());
@@ -29,11 +28,13 @@ public void configure(Binder binder) {
 
       TestConfigStorage config = this.fromXml(TestConfigStorage.class, inputStream);
       config.setAccessTokenLock(new ReentrantLock());
-      WxMpService wxService = new WxMpServiceHttpClientImpl();
-      wxService.setWxMpConfigStorage(config);
+      WxMpService mpService = new WxMpServiceHttpClientImpl();
+
+      mpService.setWxMpConfigStorage(config);
+      mpService.addConfigStorage("another", config);
 
-      binder.bind(WxMpService.class).toInstance(wxService);
       binder.bind(WxMpConfigStorage.class).toInstance(config);
+      binder.bind(WxMpService.class).toInstance(mpService);
     } catch (IOException e) {
       this.log.error(e.getMessage(), e);
     }
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/result/WxMpAdLeadResultTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/result/WxMpAdLeadResultTest.java
new file mode 100644
index 0000000000..faf354f11d
--- /dev/null
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/result/WxMpAdLeadResultTest.java
@@ -0,0 +1,75 @@
+package me.chanjar.weixin.mp.bean.result;
+
+import me.chanjar.weixin.mp.bean.marketing.WxMpAdLeadResult;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+/**
+ * @author 007
+ */
+public class WxMpAdLeadResultTest {
+  @Test
+  public void testFromJson() throws Exception {
+    String json = "{\n" +
+      "\t\"data\": {\n" +
+      "\t\t\"page_info\": {\n" +
+      "\t\t\t\"total_number\": 39,\n" +
+      "\t\t\t\"page\": 1,\n" +
+      "\t\t\t\"page_size\": 100,\n" +
+      "\t\t\t\"total_page\": 1\n" +
+      "\t\t},\n" +
+      "\t\t\"list\": [{\n" +
+      "\t\t\t\"click_id\": \"\",\n" +
+      "\t\t\t\"adgroup_name\": \"\",\n" +
+      "\t\t\t\"campaign_id\": 1800000001,\n" +
+      "\t\t\t\"leads_info\": [{\n" +
+      "\t\t\t\t\"value\": \"13800138000\",\n" +
+      "\t\t\t\t\"key\": \"电话号码\"\n" +
+      "\t\t\t}, {\n" +
+      "\t\t\t\t\"value\": \"2019-03-14 00:54:34\",\n" +
+      "\t\t\t\t\"key\": \"提交时间\"\n" +
+      "\t\t\t}, {\n" +
+      "\t\t\t\t\"value\": \"123\",\n" +
+      "\t\t\t\t\"key\": \"自定义问题\"\n" +
+      "\t\t\t}],\n" +
+      "\t\t\t\"agency_name\": \"\",\n" +
+      "\t\t\t\"agency_id\": \"\",\n" +
+      "\t\t\t\"campaign_name\": \"\",\n" +
+      "\t\t\t\"adgroup_id\": 1800000002\n" +
+      "\t\t}, {\n" +
+      "\t\t\t\"click_id\": \"\",\n" +
+      "\t\t\t\"adgroup_name\": \"\",\n" +
+      "\t\t\t\"campaign_id\": 1800000001,\n" +
+      "\t\t\t\"leads_info\": [{\n" +
+      "\t\t\t\t\"value\": \"13800138001\",\n" +
+      "\t\t\t\t\"key\": \"电话号码\"\n" +
+      "\t\t\t}, {\n" +
+      "\t\t\t\t\"value\": \"2019-03-14 02:10:39\",\n" +
+      "\t\t\t\t\"key\": \"提交时间\"\n" +
+      "\t\t\t}, {\n" +
+      "\t\t\t\t\"value\": \"321\",\n" +
+      "\t\t\t\t\"key\": \"自定义问题\"\n" +
+      "\t\t\t}],\n" +
+      "\t\t\t\"agency_name\": \"\",\n" +
+      "\t\t\t\"agency_id\": \"\",\n" +
+      "\t\t\t\"campaign_name\": \"\",\n" +
+      "\t\t\t\"adgroup_id\": 1800000002\n" +
+      "\t\t}]\n" +
+      "\t},\n" +
+      "\t\"errcode\": 0,\n" +
+      "\t\"errmsg\": \"\"\n" +
+      "}";
+
+    WxMpAdLeadResult adLeadResult = WxMpAdLeadResult.fromJson(json);
+
+    assertNotNull(adLeadResult);
+    assertNotNull(adLeadResult.getPageInfo());
+    assertNotNull(adLeadResult.getAdLeads());
+    assertTrue(adLeadResult.getAdLeads().size() > 0);
+
+    System.out.println(adLeadResult);
+  }
+
+}
diff --git a/weixin-java-open/pom.xml b/weixin-java-open/pom.xml
index 3507ad53c5..a624a38c7f 100644
--- a/weixin-java-open/pom.xml
+++ b/weixin-java-open/pom.xml
@@ -3,16 +3,17 @@
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
   xmlns="http://maven.apache.org/POM/4.0.0">
-
   4.0.0
   
     com.github.binarywang
     wx-java
-    3.3.0
+    3.4.0
   
+
   weixin-java-open
-  WxJava - Open
-  微信开放平台Java SDK
+  WxJava - Open Java SDK
+  微信开放平台 Java SDK
+
   
     
       007
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 69a3aeba56..4b2ddbc412 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
@@ -4,11 +4,13 @@
 import me.chanjar.weixin.common.error.WxErrorException;
 import me.chanjar.weixin.mp.api.WxMpService;
 import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;
+import me.chanjar.weixin.open.bean.WxOpenCreateResult;
 import me.chanjar.weixin.open.bean.WxOpenMaCodeTemplate;
 import me.chanjar.weixin.open.bean.message.WxOpenXmlMessage;
 import me.chanjar.weixin.open.bean.result.WxOpenAuthorizerInfoResult;
 import me.chanjar.weixin.open.bean.result.WxOpenAuthorizerOptionResult;
 import me.chanjar.weixin.open.bean.result.WxOpenQueryAuthResult;
+import me.chanjar.weixin.open.bean.result.WxOpenResult;
 
 import java.util.List;
 
@@ -24,11 +26,12 @@ public interface WxOpenComponentService {
   String API_GET_AUTHORIZER_OPTION_URL = "https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_option";
   String API_SET_AUTHORIZER_OPTION_URL = "https://api.weixin.qq.com/cgi-bin/component/api_set_authorizer_option";
 
-  String COMPONENT_LOGIN_PAGE_URL = "https://mp.weixin.qq.com/cgi-bin/componentloginpage?component_appid=%s&pre_auth_code=%s&redirect_uri=%s";
+  String COMPONENT_LOGIN_PAGE_URL = "https://mp.weixin.qq.com/cgi-bin/componentloginpage?component_appid=%s&pre_auth_code=%s&redirect_uri=%s&auth_type=xxx&biz_appid=xxx";
+
   /**
    * 手机端打开授权链接
    */
-  String COMPONENT_MOBILE_LOGIN_PAGE_URL = "https://mp.weixin.qq.com/safe/bindcomponent?action=bindcomponent&no_scan=1&auth_type=3&component_appid=%s&pre_auth_code=%s&redirect_uri=%s&auth_type=xxx&biz_appid=xxx$#wechat_redirect";
+  String COMPONENT_MOBILE_LOGIN_PAGE_URL = "https://mp.weixin.qq.com/safe/bindcomponent?action=bindcomponent&no_scan=1&auth_type=3&component_appid=%s&pre_auth_code=%s&redirect_uri=%s&auth_type=xxx&biz_appid=xxx#wechat_redirect";
   String CONNECT_OAUTH2_AUTHORIZE_URL = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=%s&component_appid=%s#wechat_redirect";
 
   /**
@@ -42,6 +45,15 @@ public interface WxOpenComponentService {
 
   String MINIAPP_JSCODE_2_SESSION = "https://api.weixin.qq.com/sns/component/jscode2session?appid=%s&js_code=%s&grant_type=authorization_code&component_appid=%s";
 
+  String CREATE_OPEN_URL= "https://api.weixin.qq.com/cgi-bin/open/create";
+
+  /**
+   * 快速创建小程序接口
+   */
+  String FAST_REGISTER_WEAPP_URL = "https://api.weixin.qq.com/cgi-bin/component/fastregisterweapp?action=create";
+  String FAST_REGISTER_WEAPP_SEARCH_URL = "https://api.weixin.qq.com/cgi-bin/component/fastregisterweapp?action=search";
+
+
   WxMpService getWxMpServiceByAppid(String appid);
 
   /**
@@ -52,6 +64,13 @@ public interface WxOpenComponentService {
    */
   WxOpenMaService getWxMaServiceByAppid(String appid);
 
+  /**
+   * 获取指定appid的快速创建的小程序服务
+   * @param appid
+   * @return
+   */
+  WxOpenFastMaService getWxFastMaServiceByAppid(String appid);
+
   WxOpenConfigStorage getWxOpenConfigStorage();
 
   boolean checkSignature(String timestamp, String nonce, String signature);
@@ -168,4 +187,48 @@ public interface WxOpenComponentService {
    * @see #getTemplateList
    */
   void deleteTemplate(long templateId) throws WxErrorException;
+
+  /**
+   * https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1498704199_1bcax&token=6df5e3650041eff2cd3ec3662425ad8d7beec8d9&lang=zh_CN
+   * 创建 开放平台帐号并绑定公众号/小程序
+   *
+   * https://api.weixin.qq.com/cgi-bin/open/create
+   *
+   * @param appId 公众号/小程序的appId
+   * @return
+   */
+  WxOpenCreateResult createOpenAccount(String appId) throws WxErrorException;
+
+  /**
+   *  https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=21538208049W8uwq&token=&lang=zh_CN
+   *  第三方平台快速创建小程序
+   *  
+   *      注意:创建任务逻辑串行,单次任务结束后才可以使用相同信息下发第二次任务,请注意规避任务阻塞
+   *  
+ * @param name 企业名(需与工商部门登记信息一致) + * @param code 企业代码 + * @param codeType 企业代码类型 1:统一社会信用代码(18位) 2:组织机构代码(9位xxxxxxxx-x) 3:营业执照注册号(15位) + * @param legalPersonaWechat 法人微信号 + * @param legalPersonaName 法人姓名(绑定银行卡) + * @param componentPhone 第三方联系电话(方便法人与第三方联系) + * @return + * @throws WxErrorException + */ + WxOpenResult fastRegisterWeapp(String name, String code, String codeType, String legalPersonaWechat, String legalPersonaName, String componentPhone) throws WxErrorException; + + /** + * https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=21538208049W8uwq&token=&lang=zh_CN + * 查询第三方平台快速创建小程序的任务状态 + *
+   *      注意:该接口只提供当下任务结果查询,不建议过分依赖该接口查询所创建小程序。
+   *            小程序的成功状态可在第三方服务器中自行对账、查询。
+   *            不要频繁调用search接口,消息接收需通过服务器查看。调用search接口会消耗接口整体调用quato
+   *  
+ * + * @param name 企业名(需与工商部门登记信息一致) + * @param legalPersonaWechat 法人微信号 + * @param legalPersonaName 法人姓名(绑定银行卡) + * @throws WxErrorException + */ + WxOpenResult fastRegisterWeappSearch(String name, String legalPersonaWechat, String legalPersonaName) throws WxErrorException; } diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenConfigStorage.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenConfigStorage.java index de9044ee1b..066cf8c00f 100644 --- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenConfigStorage.java +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenConfigStorage.java @@ -37,7 +37,7 @@ public interface WxOpenConfigStorage { void expireComponentAccessToken(); - void updateComponentAccessTokent(WxOpenComponentAccessToken componentAccessToken); + void updateComponentAccessToken(WxOpenComponentAccessToken componentAccessToken); String getHttpProxyHost(); @@ -59,7 +59,7 @@ public interface WxOpenConfigStorage { * @param componentAccessToken 新的accessToken值 * @param expiresInSeconds 过期时间,以秒为单位 */ - void updateComponentAccessTokent(String componentAccessToken, int expiresInSeconds); + void updateComponentAccessToken(String componentAccessToken, int expiresInSeconds); /** * 是否自动刷新token diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenFastMaService.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenFastMaService.java new file mode 100644 index 0000000000..64db79b929 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenFastMaService.java @@ -0,0 +1,187 @@ +package me.chanjar.weixin.open.api; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.open.bean.fastma.WxFastMaCategory; +import me.chanjar.weixin.open.bean.result.*; + +import java.util.List; + +/** + *
+ *     微信开放平台【快速创建小程序】的专用接口
+ *     https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=21528465979XX32V&token=&lang=zh_CN
+ *    注意:该类的接口仅限通过快速创建小程序接口的小程序使用
+ * 
+ * + * @author Hipple + * @date 2019/01/23 + */ +public interface WxOpenFastMaService extends WxMaService { + + /** + * 1 获取帐号基本信息 + */ + String OPEN_GET_ACCOUNT_BASIC_INFO = "https://api.weixin.qq.com/cgi-bin/account/getaccountbasicinfo"; + + /** + * 2 小程序名称设置及改名 + */ + String OPEN_SET_NICKNAME = "https://api.weixin.qq.com/wxa/setnickname"; + + /** + * 3 小程序改名审核状态查询 + */ + String OPEN_API_WXA_QUERYNICKNAME = "https://api.weixin.qq.com/wxa/api_wxa_querynickname"; + + /** + * 4 微信认证名称检测 + */ + String OPEN_CHECK_WX_VERIFY_NICKNAME = "https://api.weixin.qq.com/cgi-bin/wxverify/checkwxverifynickname"; + + /** + * 5 修改头像 + */ + String OPEN_MODIFY_HEADIMAGE = "https://api.weixin.qq.com/cgi-bin/account/modifyheadimage"; + + /** + * 6修改功能介绍 + */ + String OPEN_MODIFY_SIGNATURE = "https://api.weixin.qq.com/cgi-bin/account/modifysignature"; + + /** + * 7 换绑小程序管理员接口 + */ + String OPEN_COMPONENT_REBIND_ADMIN = "https://api.weixin.qq.com/cgi- bin/account/componentrebindadmin"; + + /** + * 8.1 获取账号可以设置的所有类目 + */ + String OPEN_GET_ALL_CATEGORIES = "https://api.weixin.qq.com/cgi-bin/wxopen/getallcategories"; + /** + * 8.2 添加类目 + */ + String OPEN_ADD_CATEGORY = "https://api.weixin.qq.com/cgi-bin/wxopen/addcategory"; + /** + * 8.3 删除类目 + */ + String OPEN_DELETE_CATEGORY = "https://api.weixin.qq.com/cgi-bin/wxopen/deletecategory"; + /** + * 8.4 获取账号已经设置的所有类目 + */ + String OPEN_GET_CATEGORY = "https://api.weixin.qq.com/cgi-bin/wxopen/getcategory"; + /** + * 8.5 修改类目 + */ + String OPEN_MODIFY_CATEGORY = "https://api.weixin.qq.com/cgi-bin/wxopen/modifycategory"; + + + /** + * 1.获取小程序的信息 + * + * @return + * @throws WxErrorException + */ + WxFastMaAccountBasicInfoResult getAccountBasicInfo() throws WxErrorException; + + /** + * 2.小程序名称设置及改名 + *
+   *      若接口未返回audit_id,说明名称已直接设置成功,无需审核;若返回audit_id则名称正在审核中。
+   *  
+ * @param nickname 昵称 + * @param idCard 身份证照片–临时素材mediaid(个人号必填) + * @param license 组织机构代码证或营业执照–临时素材mediaid(组织号必填) + * @param namingOtherStuff1 其他证明材料---临时素材 mediaid + * @param namingOtherStuff2 其他证明材料---临时素材 mediaid + * @throws WxErrorException + */ + WxFastMaSetNickameResult setNickname(String nickname, String idCard, String license, String namingOtherStuff1, String namingOtherStuff2) throws WxErrorException; + + /** + * 3 小程序改名审核状态查询 + * @param auditId 审核单id + * @return + * @throws WxErrorException + */ + WxFastMaQueryNicknameStatusResult querySetNicknameStatus(String auditId) throws WxErrorException; + + /** + * 4. 微信认证名称检测 + * @param nickname 名称 + * @throws WxErrorException + */ + WxFastMaCheckNickameResult checkWxVerifyNickname(String nickname) throws WxErrorException; + + /** + * 5.修改头像 + *
+   *     图片格式只支持:BMP、JPEG、JPG、GIF、PNG,大小不超过2M
+   *      注:实际头像始终为正方形
+   * 
+ * @param headImgMediaId 头像素材media_id + * @param x1 裁剪框左上角x坐标(取值范围:[0, 1]) + * @param y1 裁剪框左上角y坐标(取值范围:[0, 1]) + * @param x2 裁剪框右下角x坐标(取值范围:[0, 1]) + * @param y2 裁剪框右下角y坐标(取值范围:[0, 1]) + * @throws WxErrorException + */ + WxOpenResult modifyHeadImage(String headImgMediaId, float x1, float y1, float x2, float y2) throws WxErrorException; + + /** + * 6.修改功能介绍 + * @param signature 简介:4-120字 + * @throws WxErrorException + */ + WxOpenResult modifySignature(String signature) throws WxErrorException; + + /** + * 7.3 管理员换绑 + * @param taskid 换绑管理员任务序列号(公众平台最终点击提交回跳到第三方平台时携带) + * @return + * @throws WxErrorException + */ + WxOpenResult componentRebindAdmin(String taskid) throws WxErrorException; + + /** + * 8.1 获取账号可以设置的所有类目 + *
+   *     因为不同类目含有特定字段
+   *     目前没有完整的类目信息数据
+   *     为保证兼容性,放弃将response转换为实体
+   * 
+ * @return + */ + String getAllCategories() throws WxErrorException; + + /** + *8.2添加类目 + * @return + * @throws WxErrorException + */ + WxOpenResult addCategory(List categoryList) throws WxErrorException; + + /** + * 8.3删除类目 + * @param first 一级类目ID + * @param second 二级类目ID + * @return + * @throws WxErrorException + */ + WxOpenResult deleteCategory(int first, int second) throws WxErrorException; + + /** + * 8.4获取账号已经设置的所有类目 + * @return + * @throws WxErrorException + */ + WxFastMaBeenSetCategoryResult getCategory() throws WxErrorException; + + /** + * 8.5修改类目 + * @param category 实体 + * @return + * @throws WxErrorException + */ + WxOpenResult modifyCategory(WxFastMaCategory category) 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 7d688d31c8..8dfd7a85f9 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 @@ -22,7 +22,7 @@ public interface WxOpenMaService extends WxMaService { /** - * 设置小程序服务器域名 + * 设置小程序服务器域名. * *
    *     授权给第三方的小程序,其服务器域名只可以为第三方的服务器,当小程序通过第三方发布代码上线后,小程序原先自己配置的服务器域名将被删除,
@@ -170,21 +170,13 @@ public interface WxOpenMaService extends WxMaService {
 
   /**
    * 获得小程序的域名配置信息
-   *
-   * @return
    */
   WxOpenMaDomainResult getDomain() throws WxErrorException;
 
   /**
    * 修改域名
    *
-   * @param action              delete删除, set覆盖, get获取
-   * @param requestdomainList
-   * @param wsrequestdomainList
-   * @param uploaddomainList
-   * @param downloaddomainList
-   * @return
-   * @throws WxErrorException
+   * @param action delete删除, set覆盖, get获取
    */
   WxOpenMaDomainResult modifyDomain(String action, List requestdomainList, List wsrequestdomainList, List uploaddomainList, List downloaddomainList) throws WxErrorException;
 
@@ -198,17 +190,13 @@ public interface WxOpenMaService extends WxMaService {
   /**
    * 设置小程序的业务域名
    *
-   * @param action     add添加, delete删除, set覆盖
-   * @param domainList
+   * @param action add添加, delete删除, set覆盖
    * @return 直接返回字符串
    */
   String setWebViewDomain(String action, List domainList) throws WxErrorException;
 
   /**
    * 获取小程序的信息
-   *
-   * @return
-   * @throws WxErrorException
    */
   String getAccountBasicInfo() throws WxErrorException;
 
@@ -225,16 +213,11 @@ public interface WxOpenMaService extends WxMaService {
    * 解除绑定小程序体验者
    *
    * @param wechatid 体验者微信号(不是openid)
-   * @return
-   * @throws WxErrorException
    */
   WxOpenResult unbindTester(String wechatid) throws WxErrorException;
 
   /**
    * 获得体验者列表
-   *
-   * @return
-   * @throws WxErrorException
    */
   WxOpenMaTesterListResult getTesterList() throws WxErrorException;
 
@@ -245,17 +228,11 @@ public interface WxOpenMaService extends WxMaService {
    * @param userVersion 用户定义版本
    * @param userDesc    用户定义版本描述
    * @param extInfo     第三方自定义的配置
-   * @return
-   * @throws WxErrorException
    */
   WxOpenResult codeCommit(Long templateId, String userVersion, String userDesc, WxMaOpenCommitExtInfo extInfo) throws WxErrorException;
 
   /**
    * 获取体验小程序的体验二维码
-   *
-   * @param pagePath
-   * @param params
-   * @return
    */
   File getTestQrcode(String pagePath, Map params) throws WxErrorException;
 
@@ -264,9 +241,6 @@ public interface WxOpenMaService extends WxMaService {
    * 

* 注意:该接口可获取已设置的二级类目及用于代码审核的可选三级类目。 *

- * - * @return WxOpenMaCategoryListResult - * @throws WxErrorException */ WxOpenMaCategoryListResult getCategoryList() throws WxErrorException; @@ -280,69 +254,42 @@ public interface WxOpenMaService extends WxMaService { /** * 将第三方提交的代码包提交审核(仅供第三方开发者代小程序调用) - * - * @param submitAuditMessage - * @return - * @throws WxErrorException */ WxOpenMaSubmitAuditResult submitAudit(WxOpenMaSubmitAuditMessage submitAuditMessage) throws WxErrorException; /** * 查询某个指定版本的审核状态(仅供第三方代小程序调用) - * - * @param auditid - * @return - * @throws WxErrorException */ WxOpenMaQueryAuditResult getAuditStatus(Long auditid) throws WxErrorException; /** - * 查询最新一次提交的审核状态(仅供第三方代小程序调用) - * - * @return - * @throws WxErrorException + * 查询最新一次提交的审核状态(仅供第三方代小程序调用). */ WxOpenMaQueryAuditResult getLatestAuditStatus() throws WxErrorException; /** - * 发布已通过审核的小程序(仅供第三方代小程序调用) - * - * @return - * @throws WxErrorException + * 发布已通过审核的小程序(仅供第三方代小程序调用). */ WxOpenResult releaesAudited() throws WxErrorException; /** * 11. 小程序版本回退(仅供第三方代小程序调用) - * - * @return - * @throws WxErrorException */ WxOpenResult revertCodeReleaes() throws WxErrorException; /** * 15. 小程序审核撤回 - *

* 单个帐号每天审核撤回次数最多不超过1次,一个月不超过10次。 - *

- * - * @return - * @throws WxErrorException */ WxOpenResult undoCodeAudit() throws WxErrorException; /** * 查询当前设置的最低基础库版本及各版本用户占比 (仅供第三方代小程序调用) - * @return - * @throws WxErrorException */ String getSupportVersion() throws WxErrorException; /** * 设置最低基础库版本(仅供第三方代小程序调用) - * @param version - * @return - * @throws WxErrorException */ String setSupportVersion(String version) throws WxErrorException; 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 93155690c9..319e12ffa2 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 @@ -11,34 +11,34 @@ import me.chanjar.weixin.common.util.json.WxGsonBuilder; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken; -import me.chanjar.weixin.open.api.WxOpenComponentService; -import me.chanjar.weixin.open.api.WxOpenConfigStorage; -import me.chanjar.weixin.open.api.WxOpenMaService; -import me.chanjar.weixin.open.api.WxOpenService; +import me.chanjar.weixin.open.api.*; import me.chanjar.weixin.open.bean.WxOpenAuthorizerAccessToken; import me.chanjar.weixin.open.bean.WxOpenComponentAccessToken; +import me.chanjar.weixin.open.bean.WxOpenCreateResult; import me.chanjar.weixin.open.bean.WxOpenMaCodeTemplate; import me.chanjar.weixin.open.bean.auth.WxOpenAuthorizationInfo; import me.chanjar.weixin.open.bean.message.WxOpenXmlMessage; import me.chanjar.weixin.open.bean.result.WxOpenAuthorizerInfoResult; import me.chanjar.weixin.open.bean.result.WxOpenAuthorizerOptionResult; import me.chanjar.weixin.open.bean.result.WxOpenQueryAuthResult; +import me.chanjar.weixin.open.bean.result.WxOpenResult; import me.chanjar.weixin.open.util.json.WxOpenGsonBuilder; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Hashtable; import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; /** * @author 007 */ public class WxOpenComponentServiceImpl implements WxOpenComponentService { private static final JsonParser JSON_PARSER = new JsonParser(); - private static final Map WX_OPEN_MA_SERVICE_MAP = new Hashtable<>(); - private static final Map WX_OPEN_MP_SERVICE_MAP = new Hashtable<>(); + private static final Map WX_OPEN_MA_SERVICE_MAP = new ConcurrentHashMap<>(); + private static final Map WX_OPEN_MP_SERVICE_MAP = new ConcurrentHashMap<>(); + private static final Map WX_OPEN_FAST_MA_SERVICE_MAP = new ConcurrentHashMap<>(); protected final Logger log = LoggerFactory.getLogger(this.getClass()); private WxOpenService wxOpenService; @@ -78,6 +78,21 @@ public WxOpenMaService getWxMaServiceByAppid(String appId) { return wxOpenMaService; } + @Override + public WxOpenFastMaService getWxFastMaServiceByAppid(String appId) { + WxOpenFastMaService fastMaService = WX_OPEN_FAST_MA_SERVICE_MAP.get(appId); + if (fastMaService == null) { + synchronized (WX_OPEN_FAST_MA_SERVICE_MAP) { + fastMaService = WX_OPEN_FAST_MA_SERVICE_MAP.get(appId); + if (fastMaService == null) { + fastMaService = new WxOpenFastMaServiceImpl(this, appId, getWxOpenConfigStorage().getWxMaConfig(appId)); + WX_OPEN_FAST_MA_SERVICE_MAP.put(appId, fastMaService); + } + } + } + return fastMaService; + } + public WxOpenService getWxOpenService() { return wxOpenService; } @@ -109,7 +124,7 @@ public String getComponentAccessToken(boolean forceRefresh) throws WxErrorExcept String responseContent = this.getWxOpenService().post(API_COMPONENT_TOKEN_URL, jsonObject.toString()); WxOpenComponentAccessToken componentAccessToken = WxOpenComponentAccessToken.fromJson(responseContent); - getWxOpenConfigStorage().updateComponentAccessTokent(componentAccessToken); + getWxOpenConfigStorage().updateComponentAccessToken(componentAccessToken); } return this.getWxOpenConfigStorage().getComponentAccessToken(); } @@ -181,14 +196,17 @@ public String getPreAuthUrl(String redirectURI) throws WxErrorException { return getPreAuthUrl(redirectURI, null, null); } + @Override public String getPreAuthUrl(String redirectURI, String authType, String bizAppid) throws WxErrorException { return createPreAuthUrl(redirectURI, authType, bizAppid, false); } + @Override public String getMobilePreAuthUrl(String redirectURI) throws WxErrorException { return getMobilePreAuthUrl(redirectURI, null, null); } + @Override public String getMobilePreAuthUrl(String redirectURI, String authType, String bizAppid) throws WxErrorException { return createPreAuthUrl(redirectURI, authType, bizAppid, true); } @@ -237,7 +255,7 @@ public String route(final WxOpenXmlMessage wxMessage) throws WxErrorException { getWxOpenConfigStorage().setComponentVerifyTicket(wxMessage.getComponentVerifyTicket()); return "success"; } - //新增、跟新授权 + //新增、更新授权 if (StringUtils.equalsAnyIgnoreCase(wxMessage.getInfoType(), "authorized", "updateauthorized")) { WxOpenQueryAuthResult queryAuth = wxOpenService.getWxOpenComponentService().getQueryAuth(wxMessage.getAuthorizationCode()); if (queryAuth == null || queryAuth.getAuthorizationInfo() == null || queryAuth.getAuthorizationInfo().getAuthorizerAppid() == null) { @@ -245,6 +263,14 @@ public String route(final WxOpenXmlMessage wxMessage) throws WxErrorException { } return "success"; } + //快速创建小程序 + if (StringUtils.equalsIgnoreCase(wxMessage.getInfoType(), "notify_third_fasteregister") && wxMessage.getStatus () == 0) { + WxOpenQueryAuthResult queryAuth = wxOpenService.getWxOpenComponentService().getQueryAuth(wxMessage.getAuthCode ()); + if (queryAuth == null || queryAuth.getAuthorizationInfo() == null || queryAuth.getAuthorizationInfo().getAuthorizerAppid() == null) { + throw new NullPointerException("getQueryAuth"); + } + return "success"; + } return ""; } @@ -387,4 +413,37 @@ public void deleteTemplate(long templateId) throws WxErrorException { param.addProperty("template_id", templateId); post(DELETE_TEMPLATE_URL, param.toString(), "access_token"); } + + @Override + public WxOpenCreateResult createOpenAccount(String appId) throws WxErrorException { + JsonObject param = new JsonObject(); + param.addProperty("appid", appId); + + String json = post(CREATE_OPEN_URL, param.toString(), "access_token"); + + return WxOpenCreateResult.fromJson(json); + } + + @Override + public WxOpenResult fastRegisterWeapp(String name, String code, String codeType, String legalPersonaWechat, String legalPersonaName, String componentPhone) throws WxErrorException{ + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("name",name); + jsonObject.addProperty("code", code); + jsonObject.addProperty("code_type", codeType); + jsonObject.addProperty("legal_persona_wechat", legalPersonaWechat); + jsonObject.addProperty("legal_persona_name", legalPersonaName); + jsonObject.addProperty("component_phone", componentPhone); + String response = post(FAST_REGISTER_WEAPP_URL, jsonObject.toString (), "component_access_token"); + return WxOpenGsonBuilder.create ().fromJson (response, WxOpenResult.class); + } + + @Override + public WxOpenResult fastRegisterWeappSearch(String name, String legalPersonaWechat, String legalPersonaName) throws WxErrorException{ + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("name",name); + jsonObject.addProperty("legal_persona_wechat", legalPersonaWechat); + jsonObject.addProperty("legal_persona_name", legalPersonaName); + String response = post(FAST_REGISTER_WEAPP_SEARCH_URL, jsonObject.toString (), "component_access_token"); + return WxOpenGsonBuilder.create ().fromJson (response, WxOpenResult.class); + } } diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenFastMaServiceImpl.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenFastMaServiceImpl.java new file mode 100644 index 0000000000..1314f37f44 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenFastMaServiceImpl.java @@ -0,0 +1,256 @@ +package me.chanjar.weixin.open.api.impl; + +import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl; +import cn.binarywang.wx.miniapp.config.WxMaConfig; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.open.api.WxOpenComponentService; +import me.chanjar.weixin.open.api.WxOpenFastMaService; +import me.chanjar.weixin.open.bean.fastma.WxFastMaCategory; +import me.chanjar.weixin.open.bean.result.*; +import me.chanjar.weixin.open.util.json.WxOpenGsonBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author Hipple + * @description + * @since 2019/1/23 15:27 + */ +public class WxOpenFastMaServiceImpl extends WxMaServiceImpl implements WxOpenFastMaService { + + protected final Logger log = LoggerFactory.getLogger (this.getClass ()); + + private WxOpenComponentService wxOpenComponentService; + private WxMaConfig wxMaConfig; + private String appId; + + public WxOpenFastMaServiceImpl (WxOpenComponentService wxOpenComponentService, String appId, WxMaConfig wxMaConfig) { + this.wxOpenComponentService = wxOpenComponentService; + this.appId = appId; + this.wxMaConfig = wxMaConfig; + initHttp (); + } + + @Override + public WxMaConfig getWxMaConfig () { + return wxMaConfig; + } + + @Override + public String getAccessToken (boolean forceRefresh) throws WxErrorException { + return wxOpenComponentService.getAuthorizerAccessToken (appId, forceRefresh); + } + + /** + * 1.获取小程序的信息,GET请求 + *
+   *     注意:这里不能直接用小程序的access_token
+   * 
+ * + * @return + * @throws WxErrorException + */ + @Override + public WxFastMaAccountBasicInfoResult getAccountBasicInfo () throws WxErrorException { + String response = get (OPEN_GET_ACCOUNT_BASIC_INFO, ""); + return WxOpenGsonBuilder.create ().fromJson (response, WxFastMaAccountBasicInfoResult.class); + } + + /** + * 2.小程序名称设置及改名 + * + * @param nickname 昵称 + * @param idCard 身份证照片–临时素材mediaid(个人号必填) + * @param license 组织机构代码证或营业执照–临时素材mediaid(组织号必填) + * @param namingOtherStuff1 其他证明材料---临时素材 mediaid + * @param namingOtherStuff2 其他证明材料---临时素材 mediaid + * @throws WxErrorException + */ + @Override + public WxFastMaSetNickameResult setNickname (String nickname, String idCard, String license, String namingOtherStuff1, String namingOtherStuff2) throws WxErrorException { + JsonObject params = new JsonObject (); + params.addProperty ("nick_name", nickname); + params.addProperty ("id_card", idCard); + params.addProperty ("license", license); + params.addProperty ("naming_other_stuff_1", namingOtherStuff1); + params.addProperty ("naming_other_stuff_2", namingOtherStuff2); + String response = post (OPEN_SET_NICKNAME, GSON.toJson (params)); + return WxOpenGsonBuilder.create ().fromJson (response, WxFastMaSetNickameResult.class); + } + + /** + * 3 小程序改名审核状态查询 + * + * @param auditId 审核单id + * @return + * @throws WxErrorException + */ + @Override + public WxFastMaQueryNicknameStatusResult querySetNicknameStatus (String auditId) throws WxErrorException { + JsonObject params = new JsonObject (); + params.addProperty ("audit_id", auditId); + String response = post (OPEN_API_WXA_QUERYNICKNAME, GSON.toJson (params)); + return WxOpenGsonBuilder.create ().fromJson (response, WxFastMaQueryNicknameStatusResult.class); + } + + /** + * 4. 微信认证名称检测 + *
+   *      命中关键字策略时返回命中关键字的说明描述
+   *  
+ * + * @param nickname 名称 + * @throws WxErrorException + */ + @Override + public WxFastMaCheckNickameResult checkWxVerifyNickname (String nickname) throws WxErrorException { + JsonObject params = new JsonObject (); + params.addProperty ("nick_name", nickname); + String response = post (OPEN_CHECK_WX_VERIFY_NICKNAME, GSON.toJson (params)); + return WxOpenGsonBuilder.create ().fromJson (response, WxFastMaCheckNickameResult.class); + } + + /** + * 5.修改头像 + *
+   *     图片格式只支持:BMP、JPEG、JPG、GIF、PNG,大小不超过2M
+   *      注:实际头像始终为正方形
+   * 
+ * + * @param headImgMediaId 头像素材media_id + * @param x1 裁剪框左上角x坐标(取值范围:[0, 1]) + * @param y1 裁剪框左上角y坐标(取值范围:[0, 1]) + * @param x2 裁剪框右下角x坐标(取值范围:[0, 1]) + * @param y2 裁剪框右下角y坐标(取值范围:[0, 1]) + * @throws WxErrorException + */ + @Override + public WxOpenResult modifyHeadImage (String headImgMediaId, float x1, float y1, float x2, float y2) throws WxErrorException { + JsonObject params = new JsonObject (); + params.addProperty ("head_img_media_id", headImgMediaId); + params.addProperty ("x1", x1); + params.addProperty ("y1", y1); + params.addProperty ("x2", x2); + params.addProperty ("y2", y2); + String response = post (OPEN_MODIFY_HEADIMAGE, GSON.toJson (params)); + return WxOpenGsonBuilder.create ().fromJson (response, WxOpenResult.class); + } + + /** + * 6.修改功能介绍 + * + * @param signature 简介:4-120字 + * @throws WxErrorException + */ + @Override + public WxOpenResult modifySignature (String signature) throws WxErrorException { + JsonObject params = new JsonObject (); + params.addProperty ("signature", signature); + String response = post (OPEN_MODIFY_SIGNATURE, GSON.toJson (params)); + return WxOpenGsonBuilder.create ().fromJson (response, WxOpenResult.class); + } + + /** + * 7.3 管理员换绑 + * + * @param taskid 换绑管理员任务序列号(公众平台最终点击提交回跳到第三方平台时携带) + * @return + * @throws WxErrorException + */ + @Override + public WxOpenResult componentRebindAdmin (String taskid) throws WxErrorException { + JsonObject params = new JsonObject (); + params.addProperty ("taskid", taskid); + String response = post (OPEN_COMPONENT_REBIND_ADMIN, GSON.toJson (params)); + return WxOpenGsonBuilder.create ().fromJson (response, WxOpenResult.class); + } + + /** + * 8.1 获取账号可以设置的所有类目 + * + * @return + */ + @Override + public String getAllCategories () throws WxErrorException { + return get (OPEN_GET_ALL_CATEGORIES, ""); + } + + /** + * 8.2添加类目 + * + * @param categoryList + * @return + * @throws WxErrorException + */ + @Override + public WxOpenResult addCategory (List categoryList) throws WxErrorException { + Map map = new HashMap<> (); + map.put ("categories", categoryList); + String response = post (OPEN_ADD_CATEGORY, WxOpenGsonBuilder.create ().toJson (map)); + return WxOpenGsonBuilder.create ().fromJson (response, WxOpenResult.class); + } + + /** + * 8.3删除类目 + * + * @param first 一级类目ID + * @param second 二级类目ID + * @return + * @throws WxErrorException + */ + @Override + public WxOpenResult deleteCategory (int first, int second) throws WxErrorException { + JsonObject params = new JsonObject (); + params.addProperty ("first", first); + params.addProperty ("Second", second); + String response = post (OPEN_DELETE_CATEGORY, GSON.toJson (params)); + return WxOpenGsonBuilder.create ().fromJson (response, WxOpenResult.class); + } + + /** + * 8.4获取账号已经设置的所有类目 + * + * @return + * @throws WxErrorException + */ + @Override + public WxFastMaBeenSetCategoryResult getCategory () throws WxErrorException { + String response = get (OPEN_GET_CATEGORY, ""); + return WxOpenGsonBuilder.create ().fromJson (response, WxFastMaBeenSetCategoryResult.class); + } + + /** + * 8.5修改类目 + * + * @param category 实体 + * @return + * @throws WxErrorException + */ + @Override + public WxOpenResult modifyCategory (WxFastMaCategory category) throws WxErrorException { + String response = post (OPEN_MODIFY_CATEGORY, GSON.toJson (category)); + return WxOpenGsonBuilder.create ().fromJson (response, WxOpenResult.class); + } + + /** + * 将字符串对象转化为GsonArray对象 + * + * @param strList + * @return + */ + private JsonArray toJsonArray (List strList) { + JsonArray jsonArray = new JsonArray (); + if (strList != null && ! strList.isEmpty ()) { + for (String str : strList) { + jsonArray.add (str); + } + } + return jsonArray; + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInMemoryConfigStorage.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInMemoryConfigStorage.java index 570721f1fd..da34e808d4 100644 --- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInMemoryConfigStorage.java +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInMemoryConfigStorage.java @@ -2,8 +2,8 @@ import java.io.File; -import java.util.Hashtable; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -37,10 +37,10 @@ public class WxOpenInMemoryConfigStorage implements WxOpenConfigStorage { private String httpProxyPassword; private ApacheHttpClientBuilder apacheHttpClientBuilder; - private Map authorizerRefreshTokens = new Hashtable<>(); - private Map authorizerAccessTokens = new Hashtable<>(); - private Map jsapiTickets = new Hashtable<>(); - private Map cardApiTickets = new Hashtable<>(); + private Map authorizerRefreshTokens = new ConcurrentHashMap<>(); + private Map authorizerAccessTokens = new ConcurrentHashMap<>(); + private Map jsapiTickets = new ConcurrentHashMap<>(); + private Map cardApiTickets = new ConcurrentHashMap<>(); @Override public String getComponentAppId() { @@ -108,8 +108,8 @@ public void expireComponentAccessToken() { } @Override - public void updateComponentAccessTokent(WxOpenComponentAccessToken componentAccessToken) { - updateComponentAccessTokent(componentAccessToken.getComponentAccessToken(), componentAccessToken.getExpiresIn()); + public void updateComponentAccessToken(WxOpenComponentAccessToken componentAccessToken) { + updateComponentAccessToken(componentAccessToken.getComponentAccessToken(), componentAccessToken.getExpiresIn()); } @Override @@ -168,7 +168,7 @@ public WxMaConfig getWxMaConfig(String appId) { } @Override - public void updateComponentAccessTokent(String componentAccessToken, int expiresInSeconds) { + public void updateComponentAccessToken(String componentAccessToken, int expiresInSeconds) { this.componentAccessToken = componentAccessToken; this.componentExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L; } diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInRedisConfigStorage.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInRedisConfigStorage.java index 3745a15034..1ddfc0ff11 100644 --- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInRedisConfigStorage.java +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInRedisConfigStorage.java @@ -91,7 +91,7 @@ public void expireComponentAccessToken(){ } @Override - public void updateComponentAccessTokent(String componentAccessToken, int expiresInSeconds) { + public void updateComponentAccessToken(String componentAccessToken, int expiresInSeconds) { try (Jedis jedis = this.jedisPool.getResource()) { jedis.setex(this.componentAccessTokenKey, expiresInSeconds - 200, componentAccessToken); } 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 623338d001..b1ba0f7f65 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 @@ -76,6 +76,7 @@ public WxOpenMaDomainResult getDomain() throws WxErrorException { * @return * @throws WxErrorException */ + @Override public WxOpenMaDomainResult modifyDomain(String action, List requestdomainList, List wsrequestdomainList, List uploaddomainList, List downloaddomainList) throws WxErrorException { // if (!"get".equals(action) && (requestdomainList == null || wsrequestdomainList == null || uploaddomainList == null || downloaddomainList == null)) { @@ -252,6 +253,7 @@ public WxOpenMaPageListResult getPageList() throws WxErrorException { * @return * @throws WxErrorException */ + @Override public WxOpenMaSubmitAuditResult submitAudit(WxOpenMaSubmitAuditMessage submitAuditMessage) throws WxErrorException { String response = post(API_SUBMIT_AUDIT, GSON.toJson(submitAuditMessage)); return WxMaGsonBuilder.create().fromJson(response, WxOpenMaSubmitAuditResult.class); diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/WxOpenCreateResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/WxOpenCreateResult.java new file mode 100644 index 0000000000..6e06a16d4a --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/WxOpenCreateResult.java @@ -0,0 +1,36 @@ +package me.chanjar.weixin.open.bean; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +import java.io.Serializable; + +/** + *
+ * code换取session_key接口的响应
+ * 文档地址:https://mp.weixin.qq.com/debug/wxadoc/dev/api/api-login.html#wxloginobject
+ * 微信返回报文:{"session_key":"nzoqhc3OnwHzeTxJs+inbQ==","openid":"oVBkZ0aYgDMDIywRdgPW8-joxXc4"}
+ * 
+ * @author Binary Wang + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class WxOpenCreateResult implements Serializable { + + @SerializedName("open_appid") + private String openAppid; + + @SerializedName("errcode") + private String errcode; + + @SerializedName("errmsg") + private String errmsg; + + public static WxOpenCreateResult fromJson(String json) { + return WxGsonBuilder.create().fromJson(json, WxOpenCreateResult.class); + } + +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/fastma/WxFastMaCategory.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/fastma/WxFastMaCategory.java new file mode 100644 index 0000000000..1c0cbeb35a --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/fastma/WxFastMaCategory.java @@ -0,0 +1,36 @@ +package me.chanjar.weixin.open.bean.fastma; + +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * @author Hipple + * @description 修改更新类目所需实体 + * @since 2019/1/25 10:49 + */ +@Data +public class WxFastMaCategory implements Serializable { + + /** + * 一级类目ID + */ + private int first; + + /** + * 二级类目ID + */ + private int second; + + /** + * 资质信息 + */ + private List certicates; + + @Data + public static class certicaty { + private String key; + private String value; + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaOpenCommitExtInfo.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaOpenCommitExtInfo.java index c38867bae7..66aebec3fd 100644 --- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaOpenCommitExtInfo.java +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaOpenCommitExtInfo.java @@ -43,6 +43,12 @@ public class WxMaOpenCommitExtInfo implements Serializable { @SerializedName("pages") private List pageList; + /** + * 分包结构配置 + */ + @SerializedName("subpackages") + private List subpackageList; + @SerializedName("window") private WxMaOpenWindow window; diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaOpenSubpackage.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaOpenSubpackage.java new file mode 100644 index 0000000000..e74049a530 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaOpenSubpackage.java @@ -0,0 +1,35 @@ +package me.chanjar.weixin.open.bean.ma; + +import lombok.Builder; +import lombok.Data; + +/** + * @author: momorans + * @create: 2019-03-12 + **/ +@Data +@Builder +public class WxMaOpenSubpackage { + /** + * 分包根目录 + */ + private String root; + + /** + * 分包别名,分包预下载时可以使用 + */ + private String name; + + + /** + * 分包页面路径,相对与分包根目录 + */ + private String pages; + + /** + * 分包是否是独立分包 + */ + private Boolean independent; + + +} 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 a6ba9945d3..0473cc3c5e 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 @@ -1,12 +1,5 @@ package me.chanjar.weixin.open.bean.message; -import java.io.IOException; -import java.io.InputStream; -import java.io.Serializable; -import java.nio.charset.StandardCharsets; - -import org.apache.commons.io.IOUtils; - import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.annotations.XStreamConverter; import lombok.Data; @@ -17,6 +10,12 @@ import me.chanjar.weixin.open.api.WxOpenConfigStorage; import me.chanjar.weixin.open.util.WxOpenCryptUtil; import me.chanjar.weixin.open.util.xml.XStreamTransformer; +import org.apache.commons.io.IOUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Serializable; +import java.nio.charset.StandardCharsets; /** * @author 007 @@ -57,6 +56,53 @@ public class WxOpenXmlMessage implements Serializable { @XStreamConverter(value = XStreamCDataConverter.class) private String preAuthCode; + // 以下为快速创建小程序接口推送的的信息 + + @XStreamAlias ("appid") + private String registAppId; + + @XStreamAlias ("status") + private int status; + + @XStreamAlias ("auth_code") + private String authCode; + + @XStreamAlias ("msg") + @XStreamConverter (value = XStreamCDataConverter.class) + private String msg; + + @XStreamAlias ("info") + private Info info = new Info(); + + @XStreamAlias ("info") + @Data + public static class Info implements Serializable { + private static final long serialVersionUID = 7706235740094081194L; + + @XStreamAlias ("name") + @XStreamConverter (value = XStreamCDataConverter.class) + private String name; + + @XStreamAlias ("code") + @XStreamConverter (value = XStreamCDataConverter.class) + private String code; + + @XStreamAlias ("code_type") + private int codeType; + + @XStreamAlias ("legal_persona_wechat") + @XStreamConverter (value = XStreamCDataConverter.class) + private String legalPersonaWechat; + + @XStreamAlias ("legal_persona_name") + @XStreamConverter (value = XStreamCDataConverter.class) + private String legalPersonaName; + + @XStreamAlias ("component_phone") + @XStreamConverter (value = XStreamCDataConverter.class) + private String componentPhone; + } + public static String wxMpOutXmlMessageToEncryptedXml(WxMpXmlOutMessage message, WxOpenConfigStorage wxOpenConfigStorage) { String plainXml = message.toXml(); WxOpenCryptUtil pc = new WxOpenCryptUtil(wxOpenConfigStorage); diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxFastMaAccountBasicInfoResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxFastMaAccountBasicInfoResult.java new file mode 100644 index 0000000000..b8bf8d83ba --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxFastMaAccountBasicInfoResult.java @@ -0,0 +1,135 @@ +package me.chanjar.weixin.open.bean.result; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * @author Hipple + * @description 快速创建的小程序的账号基本信息 + * @since 2019/1/23 14:39 + */ +@Data +@EqualsAndHashCode (callSuper = true) +public class WxFastMaAccountBasicInfoResult extends WxOpenResult{ + private static final long serialVersionUID = - 8713680081353954208L; + + /** + * 小程序ID + */ + @SerializedName ("appid") + private String appId; + + /** + * 帐号类型(1:订阅号,2:服务号,3:小程序) + */ + @SerializedName ("account_type") + private Integer accountType; + + /** + * 主体类型(1:企业) + */ + @SerializedName ("principal_type") + private Integer principalType; + + /** + * 主体名称 + */ + @SerializedName ("principal_name") + private String principalName; + + /** + * 实名验证状态(1:实名验证成功,2:实名验证中,3:实名验证失败)调用接口1.1创建帐号时,realname_status会初始化为2对于注册方式为微信认证的帐号,资质认证成功时,realname_status会更新为1 注意!!!当realname_status不为1时,帐号只允许调用本文档内的以下API:(即无权限调用其他API) 微信认证相关接口(参考2.x) 帐号设置相关接口(参考3.x) + */ + @SerializedName ("realname_status") + private Integer realnameStatus; + + + /** + * 微信认证信息 + */ + @SerializedName ("wx_verify_info") + private WxVerifyInfo wxVerifyInfo; + /** + * 功能介绍信息 + */ + @SerializedName ("signature_info") + private SignatureInfo signatureInfo; + /** + * 头像信息 + */ + @SerializedName ("head_image_info") + private HeadImageInfo headImageInfo; + + @Data + public static class WxVerifyInfo { + /** + * 是否资质认证(true:是,false:否)若是,拥有微信认证相关的权限 + */ + @SerializedName ("qualification_verify") + private Boolean qualificationVerify; + /** + * 是否名称认证(true:是,false:否)对于公众号(订阅号、服务号),是名称认证,微信客户端才会有微信认证打勾的标识。 + */ + @SerializedName ("naming_verify") + private Boolean namingVerify; + /** + * 是否需要年审(true:是,false:否)(qualification_verify = true时才有该字段) + */ + @SerializedName ("annual_review") + private Boolean annualReview; + + /** + * 年审开始时间,时间戳(qualification_verify = true时才有该字段) + */ + @SerializedName ("annual_review_begin_time") + private String annualReviewBeginTime; + + /** + * 年审截止时间,时间戳(qualification_verify = true时才有该字段) + */ + @SerializedName ("annual_review_end_time") + private String annualReviewEndTime; + } + + + @Data + public static class SignatureInfo { + /** + * 功能介绍 + */ + @SerializedName ("signature") + private String signature; + /** + * 头像已使用修改次数(本月) + */ + @SerializedName ("modify_used_count") + private Integer modifyUsedCount; + /** + * 头像修改次数总额度(本月) + */ + @SerializedName ("modify_quota") + private Integer modifyQuota; + } + + + @Data + public static class HeadImageInfo { + /** + * 头像url + */ + @SerializedName ("head_image_url") + private String headImageUrl; + /** + * 头像已使用修改次数(本月) + */ + @SerializedName ("modify_used_count") + private Integer modifyUsedCount; + + /** + * 头像修改次数总额度(本月) + */ + @SerializedName ("modify_quota") + private Integer modifyQuota; + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxFastMaBeenSetCategoryResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxFastMaBeenSetCategoryResult.java new file mode 100644 index 0000000000..11d6693c5b --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxFastMaBeenSetCategoryResult.java @@ -0,0 +1,73 @@ +package me.chanjar.weixin.open.bean.result; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +/** + * @author Hipple + * @description 获取小程序已经设置的类目结果类 + * @since 2019/1/26 18:27 + */ +@Data +@EqualsAndHashCode (callSuper = true) +public class WxFastMaBeenSetCategoryResult extends WxOpenResult { + private static final long serialVersionUID = - 7662344448437700644L; + + /** + * 一个更改周期内可以设置类目的次数 + */ + @SerializedName ("limit") + private int limit; + /** + * 本更改周期内还可以设置类目的次数 + */ + @SerializedName ("quota") + private int quota; + /** + * 最多可以设置的类目数量 + */ + @SerializedName ("category_limit") + private int categoryLimit; + /** + * 类目 + */ + @SerializedName ("categories") + private List categories; + + @Data + public static class CategoriesBean { + /** + * 一级类目ID + */ + @SerializedName ("first") + private int first; + /** + * 一级类目名称 + */ + @SerializedName ("first_name") + private String firstName; + /** + * 二级类目ID + */ + @SerializedName ("second") + private int second; + /** + * 二级类目名称 + */ + @SerializedName ("second_name") + private String secondName; + /** + * 审核状态(1审核中 2审核不通过 3审核通过) + */ + @SerializedName ("audit_status") + private int auditStatus; + /** + * 审核不通过原因 + */ + @SerializedName ("audit_reason") + private String auditReason; + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxFastMaCanSetCategoryResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxFastMaCanSetCategoryResult.java new file mode 100644 index 0000000000..59d1f45653 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxFastMaCanSetCategoryResult.java @@ -0,0 +1,58 @@ +package me.chanjar.weixin.open.bean.result; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.util.List; + +/** + * @author Hipple + * @description 获取账号所有可以设置的类目 + * @since 2019/1/26 18:43 + */ +@Data +public class WxFastMaCanSetCategoryResult extends WxOpenResult { + private static final long serialVersionUID = - 2469386233538980102L; + @SerializedName ("errcode") + private int errcodeX; + @SerializedName ("categories_list") + private CategoriesListBean categoriesList; + + @Data + public static class CategoriesListBean { + private List categories; + @Data + public static class CategoriesBean { + private int id; + private QualifyBean qualify; + private String name; + private int level; + private int father; + @SerializedName ("sensitive_type") + private int sensitiveType; + @SerializedName ("available_for_plugin") + private boolean availableForPlugin; + @SerializedName ("is_hidden") + private boolean isHidden; + private String type; + @SerializedName ("need_report") + private int needReport; + @SerializedName ("can_use_cityserivce") + private int canUseCityserivce; + private List children; + @SerializedName ("type_list") + private List typeList; + @SerializedName ("available_api_list") + private List availableApiList; + private List apis; + + @Data + public static class QualifyBean { + private String remark; + @SerializedName ("exter_list") + private List exterList; + } + + } + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxFastMaCheckNickameResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxFastMaCheckNickameResult.java new file mode 100644 index 0000000000..943645e35f --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxFastMaCheckNickameResult.java @@ -0,0 +1,29 @@ +package me.chanjar.weixin.open.bean.result; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * @author Hipple + * @description 微信认证名称检测结果类 + * @since 2019/1/26 17:39 + */ +@Data +@EqualsAndHashCode (callSuper = true) +public class WxFastMaCheckNickameResult extends WxOpenResult { + private static final long serialVersionUID = 8022192589710319473L; + + /** + * 审核编号. + */ + @SerializedName ("hit_condition") + boolean hitCondition; + + /** + * 材料说明 + */ + @SerializedName ("wording") + String wording; + +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxFastMaQueryNicknameStatusResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxFastMaQueryNicknameStatusResult.java new file mode 100644 index 0000000000..6594c48322 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxFastMaQueryNicknameStatusResult.java @@ -0,0 +1,43 @@ +package me.chanjar.weixin.open.bean.result; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * @author Hipple + * @description 查询改名状态实体类 + * @since 2019/1/26 17:52 + */ +@EqualsAndHashCode (callSuper = true) +@Data +public class WxFastMaQueryNicknameStatusResult extends WxOpenResult { + + private static final long serialVersionUID = 8492116046290791757L; + + /** + * 审核昵称 + */ + @SerializedName ("nickname") + private String nickname; + /** + * 审核状态,1:审核中,2:审核失败,3:审核成功 + */ + @SerializedName ("audit_stat") + private int auditStat; + /** + * 失败原因 + */ + @SerializedName ("fail_reason") + private String failReason; + /** + * 审核提交时间 + */ + @SerializedName ("create_time") + private String createTime; + /** + * 审核提交时间 + */ + @SerializedName ("audit_time") + private String auditTime; +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxFastMaSetNickameResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxFastMaSetNickameResult.java new file mode 100644 index 0000000000..7a2cab2304 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxFastMaSetNickameResult.java @@ -0,0 +1,29 @@ +package me.chanjar.weixin.open.bean.result; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * @author Hipple + * @description 设置小程序名称结果类 + * @since 2019/1/26 17:39 + */ +@Data +@EqualsAndHashCode (callSuper = true) +public class WxFastMaSetNickameResult extends WxOpenResult { + private static final long serialVersionUID = 8022192589710319473L; + + /** + * 审核编号. + */ + @SerializedName ("audit_id") + Long auditId; + + /** + * 材料说明 + */ + @SerializedName ("wording") + String wording; + +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaQueryAuditResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaQueryAuditResult.java index 41a761d647..8f0739f9a6 100644 --- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaQueryAuditResult.java +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaQueryAuditResult.java @@ -29,4 +29,9 @@ public class WxOpenMaQueryAuditResult extends WxOpenResult { * 审核失败原因. */ String reason; + /** + * 当status=1,审核被拒绝时,会返回审核失败的小程序截图示例。 xxx丨yyy丨zzz是media_id可通过获取永久素材接口 拉取截图内容). + */ + @SerializedName(value = "screenshot") + private String screenShot; } diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxFastMaAccountBasicInfoGsonAdapter.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxFastMaAccountBasicInfoGsonAdapter.java new file mode 100644 index 0000000000..a21d334ed8 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxFastMaAccountBasicInfoGsonAdapter.java @@ -0,0 +1,44 @@ +package me.chanjar.weixin.open.util.json; + +import com.google.gson.*; +import com.google.gson.reflect.TypeToken; +import me.chanjar.weixin.common.util.json.GsonHelper; +import me.chanjar.weixin.open.bean.result.WxFastMaAccountBasicInfoResult; + +import java.lang.reflect.Type; + +/** + * @author Hipple + * @description + * @since 2019/1/23 15:02 + */ +public class WxFastMaAccountBasicInfoGsonAdapter implements JsonDeserializer { + @Override + public WxFastMaAccountBasicInfoResult deserialize (JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { + WxFastMaAccountBasicInfoResult accountBasicInfo = new WxFastMaAccountBasicInfoResult (); + JsonObject jsonObject = jsonElement.getAsJsonObject(); + + accountBasicInfo.setAppId (GsonHelper.getString(jsonObject, "appid")); + accountBasicInfo.setAccountType (GsonHelper.getInteger (jsonObject, "account_type")); + accountBasicInfo.setPrincipalType (GsonHelper.getInteger (jsonObject, "principal_type")); + accountBasicInfo.setPrincipalName (GsonHelper.getString(jsonObject, "principal_name")); + accountBasicInfo.setRealnameStatus (GsonHelper.getInteger (jsonObject, "realname_status")); + + WxFastMaAccountBasicInfoResult.WxVerifyInfo verifyInfo = WxOpenGsonBuilder.create().fromJson(jsonObject.get("wx_verify_info"), + new TypeToken () { + }.getType()); + accountBasicInfo.setWxVerifyInfo (verifyInfo); + + WxFastMaAccountBasicInfoResult.SignatureInfo signatureInfo = WxOpenGsonBuilder.create().fromJson(jsonObject.get("signature_info"), + new TypeToken () { + }.getType()); + accountBasicInfo.setSignatureInfo (signatureInfo); + + WxFastMaAccountBasicInfoResult.HeadImageInfo headImageInfo = WxOpenGsonBuilder.create().fromJson(jsonObject.get("head_image_info"), + new TypeToken () { + }.getType()); + accountBasicInfo.setHeadImageInfo (headImageInfo); + + return accountBasicInfo; + } +} 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 703aff1218..10c2911df2 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 @@ -6,6 +6,7 @@ import me.chanjar.weixin.open.bean.WxOpenComponentAccessToken; import me.chanjar.weixin.open.bean.auth.WxOpenAuthorizationInfo; import me.chanjar.weixin.open.bean.auth.WxOpenAuthorizerInfo; +import me.chanjar.weixin.open.bean.result.WxFastMaAccountBasicInfoResult; import me.chanjar.weixin.open.bean.result.WxOpenAuthorizerInfoResult; import me.chanjar.weixin.open.bean.result.WxOpenAuthorizerOptionResult; import me.chanjar.weixin.open.bean.result.WxOpenQueryAuthResult; @@ -27,6 +28,8 @@ public class WxOpenGsonBuilder { INSTANCE.registerTypeAdapter(WxOpenAuthorizerInfoResult.class, new WxOpenAuthorizerInfoResultGsonAdapter()); INSTANCE.registerTypeAdapter(WxOpenAuthorizerOptionResult.class, new WxOpenAuthorizerOptionResultGsonAdapter()); + INSTANCE.registerTypeAdapter(WxFastMaAccountBasicInfoResult.class, new WxFastMaAccountBasicInfoGsonAdapter ()); + } public static Gson create() { diff --git a/weixin-java-open/src/test/java/me/chanjar/weixin/open/bean/result/WxFastMaAccountBasicInfoResultTest.java b/weixin-java-open/src/test/java/me/chanjar/weixin/open/bean/result/WxFastMaAccountBasicInfoResultTest.java new file mode 100644 index 0000000000..5232881c50 --- /dev/null +++ b/weixin-java-open/src/test/java/me/chanjar/weixin/open/bean/result/WxFastMaAccountBasicInfoResultTest.java @@ -0,0 +1,51 @@ +package me.chanjar.weixin.open.bean.result; + +import me.chanjar.weixin.open.util.json.WxOpenGsonBuilder; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + + +public class WxFastMaAccountBasicInfoResultTest { + @Test + public void testFromJson() throws Exception { + String json = "{\n" + + " \"errcode\": 0,\n" + + " \"errmsg\": \"ok\",\n" + + "\t\"appid\": \"wxdc685123d955453\",\n" + + " \"account_type\": 2,\n" + + "\t\"principal_type\": 1,\n" + + "\t\"principal_name\": \"深圳市腾讯计算机系统有限公司\",\n" + + " \"realname_status\": 1,\n" + + " \"wx_verify_info\": {\n" + + " \"qualification_verify\": true,\n" + + " \"naming_verify\": true,\n" + + " \"annual_review\": true,\n" + + " \"annual_review_begin_time\": 1550490981,\n" + + " \"annual_review_end_time\": 1558266981\n" + + " },\n" + + " \"signature_info\": {\n" + + " \"signature\": \"功能介绍\",\n" + + " \"modify_used_count\": 1,\n" + + " \"modify_quota\": 5\n" + + " },\n" + + "\t\"head_image_info\": {\n" + + " \"head_image_url\": \"http://mmbiz.qpic.cn/mmbiz/a5icZrUmbV8p5jb6RZ8aYfjfS2AVle8URwBt8QIu6XbGewB9wiaWYWkPwq4R7pfdsFibuLkic16UcxDSNYtB8HnC1Q/0\",\n" + + " \"modify_used_count\": 3,\n" + + " \"modify_quota\": 5\n" + + " }\n" + + "}"; + + WxFastMaAccountBasicInfoResult res = WxOpenGsonBuilder.create ().fromJson (json, WxFastMaAccountBasicInfoResult.class); + + assertNotNull(res); + assertNotNull(res.getAppId ()); + assertNotNull(res.getSignatureInfo ().getModifyQuota ()); + assertNotNull(res.getHeadImageInfo ().getHeadImageUrl ()); + assertNotNull(res.getWxVerifyInfo ().getNamingVerify ()); + assertTrue(res.getWxVerifyInfo ().getNamingVerify ()); + System.out.println(res); + } + +} diff --git a/weixin-java-open/src/test/java/me/chanjar/weixin/open/bean/result/WxFastMaBeenSetCategoryResultTest.java b/weixin-java-open/src/test/java/me/chanjar/weixin/open/bean/result/WxFastMaBeenSetCategoryResultTest.java new file mode 100644 index 0000000000..3cd952542b --- /dev/null +++ b/weixin-java-open/src/test/java/me/chanjar/weixin/open/bean/result/WxFastMaBeenSetCategoryResultTest.java @@ -0,0 +1,40 @@ +package me.chanjar.weixin.open.bean.result; + +import me.chanjar.weixin.open.util.json.WxOpenGsonBuilder; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + + +public class WxFastMaBeenSetCategoryResultTest { + @Test + public void testFromJson() throws Exception { + String json = "{\n" + + " \"errcode\": 0,\n" + + " \"errmsg\":\"ok\",\n" + + " \"categories\": [\n" + + " {\n" + + " \"first\": 8,\n" + + " \"first_name\": \"教育\",\n" + + " \"second\": 39,\n" + + " \"second_name\": \"出国移民\",\n" + + " \"audit_status\": 1,\n" + + " \"audit_reason\": \"不通过啊啊\"\n" + + " }\n" + + " ],\n" + + "\t\"limit\": 5,\n" + + " \"quota\": 4,\n" + + " \"category_limit\": 20\n" + + "}"; + + WxFastMaBeenSetCategoryResult res = WxOpenGsonBuilder.create ().fromJson (json, WxFastMaBeenSetCategoryResult.class); + + assertNotNull(res); + assertTrue(res.getCategories ().size ()> 0); + assertNotNull(res.getCategories ().get (0)); + assertNotNull(res.getCategories ().get (0).getFirstName ()); + System.out.println(res); + } + +} diff --git a/weixin-java-open/src/test/java/me/chanjar/weixin/open/bean/result/WxFastMaCanSetCategoryResultTest.java b/weixin-java-open/src/test/java/me/chanjar/weixin/open/bean/result/WxFastMaCanSetCategoryResultTest.java new file mode 100644 index 0000000000..7e7d220a3a --- /dev/null +++ b/weixin-java-open/src/test/java/me/chanjar/weixin/open/bean/result/WxFastMaCanSetCategoryResultTest.java @@ -0,0 +1,79 @@ +package me.chanjar.weixin.open.bean.result; + +import me.chanjar.weixin.open.util.json.WxOpenGsonBuilder; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertNotNull; + + +public class WxFastMaCanSetCategoryResultTest { + @Test + public void testFromJson() throws Exception { + String json = "{\n" + + " \"errcode\": 0, \n" + + " \"errmsg\": \"ok\", \n" + + " \"categories_list\": {\n" + + " \"categories\": [\n" + + " {\n" + + " \"id\": 1, \n" + + " \"name\": \"快递业与邮政\", \n" + + " \"level\": 1, \n" + + " \"father\": 0, \n" + + " \"children\": [\n" + + " 2, \n" + + " 5, \n" + + " 556, \n" + + " 558, \n" + + " 1033\n" + + " ], \n" + + " \"sensitive_type\": 0, \n" + + " \"type_list\": [ ], \n" + + " \"qualify\": {\n" + + " \"exter_list\": [ ], \n" + + " \"remark\": \"\"\n" + + " }, \n" + + " \"available_api_list\": [ ], \n" + + " \"apis\": [ ], \n" + + " \"available_for_plugin\": true\n" + + " }, \n" + + " {\n" + + " \"id\": 8, \n" + + " \"name\": \"教育\", \n" + + " \"level\": 1, \n" + + " \"father\": 0, \n" + + " \"children\": [\n" + + " 9, \n" + + " 590, \n" + + " 592, \n" + + " 27, \n" + + " 32, \n" + + " 37, \n" + + " 39, \n" + + " 578, \n" + + " 580, \n" + + " 582, \n" + + " 1043\n" + + " ], \n" + + " \"sensitive_type\": 0, \n" + + " \"type_list\": [ ], \n" + + " \"qualify\": {\n" + + " \"exter_list\": [ ], \n" + + " \"remark\": \"\"\n" + + " }, \n" + + " \"is_hidden\": false, \n" + + " \"available_api_list\": [ ], \n" + + " \"type\": \"NORMAL\", \n" + + " \"apis\": [ ], \n" + + " \"available_for_plugin\": true\n" + + " }\n" + + " ]\n" + + " }\n" + + "}"; + WxFastMaCanSetCategoryResult res = WxOpenGsonBuilder.create ().fromJson (json, WxFastMaCanSetCategoryResult.class); + + assertNotNull(res); + assertNotNull(res.getCategoriesList ()); + System.out.println(res); + } + +} diff --git a/weixin-java-open/src/test/resources/logback-test.xml b/weixin-java-open/src/test/resources/logback-test.xml new file mode 100644 index 0000000000..e4a33acd88 --- /dev/null +++ b/weixin-java-open/src/test/resources/logback-test.xml @@ -0,0 +1,13 @@ + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %replace(%caller{1}){'Caller', ''} - %msg%n + + + + + + + + diff --git a/weixin-java-open/src/test/resources/test-config.sample.xml b/weixin-java-open/src/test/resources/test-config.sample.xml new file mode 100644 index 0000000000..896969e438 --- /dev/null +++ b/weixin-java-open/src/test/resources/test-config.sample.xml @@ -0,0 +1,7 @@ + + 第三方平台appID + 第三方平台appsecret + 第三方平台Token + 第三方平台EncodingAESKey + 测试APPID + diff --git a/weixin-java-open/src/test/resources/testng.xml b/weixin-java-open/src/test/resources/testng.xml new file mode 100644 index 0000000000..8ade76f3e3 --- /dev/null +++ b/weixin-java-open/src/test/resources/testng.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/weixin-java-pay/pom.xml b/weixin-java-pay/pom.xml index 9f6b3cd71f..a6130d13ee 100644 --- a/weixin-java-pay/pom.xml +++ b/weixin-java-pay/pom.xml @@ -5,12 +5,12 @@ com.github.binarywang wx-java - 3.3.0 + 3.4.0 4.0.0 weixin-java-pay - WxJava - PAY + WxJava - PAY Java SDK 微信支付 Java SDK diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyResult.java index 513dbfa84e..ad56852824 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyResult.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyResult.java @@ -215,7 +215,8 @@ public String toString() { * 变量名:success_time * 是否必填:否 * 类型: String(20) - * 示例值:20160725152626 + * 示例值:2017-12-15 09:46:01 + * 资金退款至用户帐号的时间,格式2017-12-15 09:46:01 *
*/ @XStreamAlias("success_time") diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRedpackQueryRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRedpackQueryRequest.java index 4bbee5aaf2..e8ade81d9f 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRedpackQueryRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRedpackQueryRequest.java @@ -24,6 +24,11 @@ @AllArgsConstructor @XStreamAlias("xml") public class WxPayRedpackQueryRequest extends BaseWxPayRequest { + + @Override + protected String[] getIgnoredParamsForSign() { + return new String[]{"sub_appid","sub_mch_id"}; + } /** * 商户订单号 * mch_billno diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPaySendRedpackRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPaySendRedpackRequest.java index efa04d17f2..977f363f6a 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPaySendRedpackRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPaySendRedpackRequest.java @@ -1,11 +1,7 @@ package com.github.binarywang.wxpay.bean.request; import com.thoughtworks.xstream.annotations.XStreamAlias; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; +import lombok.*; /** * 发送红包请求参数对象. @@ -20,9 +16,11 @@ @AllArgsConstructor @XStreamAlias("xml") public class WxPaySendRedpackRequest extends BaseWxPayRequest { + private static final long serialVersionUID = -2035425086824987567L; + @Override protected String[] getIgnoredParamsForSign() { - return new String[]{"sign_type"}; + return new String[]{"sign_type", "sub_appid"}; } /** diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPaySendRedpackRequest.zip b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPaySendRedpackRequest.zip new file mode 100644 index 0000000000..b02696333a Binary files /dev/null and b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPaySendRedpackRequest.zip differ diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayUnifiedOrderRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayUnifiedOrderRequest.java index 4ef7ab446a..78bb789d25 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayUnifiedOrderRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayUnifiedOrderRequest.java @@ -302,6 +302,19 @@ public class WxPayUnifiedOrderRequest extends BaseWxPayRequest { @XStreamAlias("sub_openid") private String subOpenid; + /** + *
+   * 字段名:电子发票入口开放标识.
+   * 变量名:	receipt
+   * 是否必填:否
+   * 类型:String(8)
+   * 示例值:Y
+   * 描述:	Y,传入Y时,支付成功消息和支付详情页将出现开票入口。需要在微信支付商户平台或微信公众平台开通电子发票功能,传此字段才可生效
+   * 
+ */ + @XStreamAlias("receipt") + private String receipt; + /** *
    * 字段名:场景信息.
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 b0233ded4d..5a1ea64e83 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
@@ -189,6 +189,7 @@ private Document getXmlDoc() {
     try {
       final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
       factory.setExpandEntityReferences(false);
+      factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
       this.xmlDoc = factory.newDocumentBuilder()
         .parse(new ByteArrayInputStream(this.xmlString.getBytes(StandardCharsets.UTF_8)));
       return xmlDoc;
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayMicropayResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayMicropayResult.java
index 87aad5f436..36f910be63 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayMicropayResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayMicropayResult.java
@@ -18,9 +18,11 @@
 @NoArgsConstructor
 @XStreamAlias("xml")
 public class WxPayMicropayResult extends BaseWxPayResult {
+  private static final long serialVersionUID = 529670965722059189L;
+
   /**
    * 
-   * 用户标识
+   * 用户标识.
    * openid
    * 是
    * String(128)
@@ -33,7 +35,7 @@ public class WxPayMicropayResult extends BaseWxPayResult {
 
   /**
    * 
-   * 是否关注公众账号
+   * 是否关注公众账号.
    * is_subscribe
    * 是
    * String(1)
@@ -46,7 +48,7 @@ public class WxPayMicropayResult extends BaseWxPayResult {
 
   /**
    * 
-   * 交易类型
+   * 交易类型.
    * trade_type
    * 是
    * String(16)
@@ -59,7 +61,7 @@ public class WxPayMicropayResult extends BaseWxPayResult {
 
   /**
    * 
-   * 付款银行
+   * 付款银行.
    * bank_type
    * 是
    * String(32)
@@ -72,7 +74,7 @@ public class WxPayMicropayResult extends BaseWxPayResult {
 
   /**
    * 
-   * 货币类型
+   * 货币类型.
    * fee_type
    * 否
    * String(16)
@@ -85,7 +87,7 @@ public class WxPayMicropayResult extends BaseWxPayResult {
 
   /**
    * 
-   * 订单金额
+   * 订单金额.
    * total_fee
    * 是
    * Int
@@ -94,11 +96,11 @@ public class WxPayMicropayResult extends BaseWxPayResult {
    * 
**/ @XStreamAlias("total_fee") - private String totalFee; + private Integer totalFee; /** *
-   * 应结订单金额
+   * 应结订单金额.
    * settlement_total_fee
    * 否
    * Int
@@ -111,7 +113,7 @@ public class WxPayMicropayResult extends BaseWxPayResult {
 
   /**
    * 
-   * 代金券金额
+   * 代金券金额.
    * coupon_fee
    * 否
    * Int
@@ -124,7 +126,7 @@ public class WxPayMicropayResult extends BaseWxPayResult {
 
   /**
    * 
-   * 现金支付货币类型
+   * 现金支付货币类型.
    * cash_fee_type
    * 否
    * String(16)
@@ -137,7 +139,7 @@ public class WxPayMicropayResult extends BaseWxPayResult {
 
   /**
    * 
-   * 现金支付金额
+   * 现金支付金额.
    * cash_fee
    * 是
    * Int
@@ -150,7 +152,7 @@ public class WxPayMicropayResult extends BaseWxPayResult {
 
   /**
    * 
-   * 微信支付订单号
+   * 微信支付订单号.
    * transaction_id
    * 是
    * String(32)
@@ -163,7 +165,7 @@ public class WxPayMicropayResult extends BaseWxPayResult {
 
   /**
    * 
-   * 商户订单号
+   * 商户订单号.
    * out_trade_no
    * 是
    * String(32)
@@ -176,7 +178,7 @@ public class WxPayMicropayResult extends BaseWxPayResult {
 
   /**
    * 
-   * 商家数据包
+   * 商家数据包.
    * attach
    * 否
    * String(128)
@@ -189,7 +191,7 @@ public class WxPayMicropayResult extends BaseWxPayResult {
 
   /**
    * 
-   * 支付完成时间
+   * 支付完成时间.
    * time_end
    * 是
    * String(14)
@@ -202,7 +204,7 @@ public class WxPayMicropayResult extends BaseWxPayResult {
 
   /**
    * 
-   * 营销详情
+   * 营销详情.
    * promotion_detail
    * 否
    * String(6000)
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRefundResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRefundResult.java
index b60011baf2..9b971123aa 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRefundResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRefundResult.java
@@ -90,10 +90,10 @@ public class WxPayRefundResult extends BaseWxPayResult implements Serializable {
   private String cashFeeType;
 
   /**
-   * 现金退款金额.
+   * 现金退款金额,单位为分,只能为整数,详见支付金额.
    */
   @XStreamAlias("cash_refund_fee")
-  private String cashRefundFee;
+  private Integer cashRefundFee;
 
   /**
    * 退款代金券使用数量.
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EntPayService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EntPayService.java
index 43162f79d3..d0be5c4b4f 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EntPayService.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EntPayService.java
@@ -42,6 +42,19 @@ public interface EntPayService {
    * @throws WxPayException the wx pay exception
    */
   EntPayQueryResult queryEntPay(String partnerTradeNo) throws WxPayException;
+  /**
+   * 
+   * 查询企业付款API.
+   * 用于商户的企业付款操作进行结果查询,返回付款操作详细结果。
+   * 文档详见:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_3
+   * 接口链接:https://api.mch.weixin.qq.com/mmpaymkttransfers/gettransferinfo
+   * 
+ * + * @param partnerTradeNo 商户订单号 + * @return the ent pay query result + * @throws WxPayException the wx pay exception + */ + EntPayQueryResult queryEntPay(EntPayQueryRequest request) throws WxPayException; /** *
@@ -92,4 +105,17 @@ public interface EntPayService {
    * @throws WxPayException the wx pay exception
    */
   EntPayBankQueryResult queryPayBank(String partnerTradeNo) throws WxPayException;
+  /**
+   * 企业付款到银行卡查询.
+   * 
+   * 用于对商户企业付款到银行卡操作进行结果查询,返回付款操作详细结果。
+   * 文档详见:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=24_3
+   * 接口链接:https://api.mch.weixin.qq.com/mmpaysptrans/query_bank
+   * 
+ * + * @param partnerTradeNo 商户订单号 + * @return the ent pay bank query result + * @throws WxPayException the wx pay exception + */ + EntPayBankQueryResult queryPayBank(EntPayBankQueryRequest request) 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 e8bb107962..51d46512a2 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 @@ -14,30 +14,8 @@ import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult; import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult; import com.github.binarywang.wxpay.bean.notify.WxScanPayNotifyResult; -import com.github.binarywang.wxpay.bean.request.WxPayAuthcode2OpenidRequest; -import com.github.binarywang.wxpay.bean.request.WxPayDownloadBillRequest; -import com.github.binarywang.wxpay.bean.request.WxPayDownloadFundFlowRequest; -import com.github.binarywang.wxpay.bean.request.WxPayMicropayRequest; -import com.github.binarywang.wxpay.bean.request.WxPayOrderCloseRequest; -import com.github.binarywang.wxpay.bean.request.WxPayOrderQueryRequest; -import com.github.binarywang.wxpay.bean.request.WxPayOrderReverseRequest; -import com.github.binarywang.wxpay.bean.request.WxPayRefundQueryRequest; -import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest; -import com.github.binarywang.wxpay.bean.request.WxPayReportRequest; -import com.github.binarywang.wxpay.bean.request.WxPaySendRedpackRequest; -import com.github.binarywang.wxpay.bean.request.WxPayShorturlRequest; -import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest; -import com.github.binarywang.wxpay.bean.result.WxPayBillResult; -import com.github.binarywang.wxpay.bean.result.WxPayFundFlowResult; -import com.github.binarywang.wxpay.bean.result.WxPayMicropayResult; -import com.github.binarywang.wxpay.bean.result.WxPayOrderCloseResult; -import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryResult; -import com.github.binarywang.wxpay.bean.result.WxPayOrderReverseResult; -import com.github.binarywang.wxpay.bean.result.WxPayRedpackQueryResult; -import com.github.binarywang.wxpay.bean.result.WxPayRefundQueryResult; -import com.github.binarywang.wxpay.bean.result.WxPayRefundResult; -import com.github.binarywang.wxpay.bean.result.WxPaySendRedpackResult; -import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderResult; +import com.github.binarywang.wxpay.bean.request.*; +import com.github.binarywang.wxpay.bean.result.*; import com.github.binarywang.wxpay.config.WxPayConfig; import com.github.binarywang.wxpay.exception.WxPayException; @@ -326,6 +304,20 @@ WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, Stri * @throws WxPayException the wx pay exception */ WxPayRedpackQueryResult queryRedpack(String mchBillNo) throws WxPayException; + /** + *
+   *   查询红包记录.
+   *   用于商户对已发放的红包进行查询红包的具体信息,可支持普通红包和裂变包。
+   *   请求Url:https://api.mch.weixin.qq.com/mmpaymkttransfers/gethbinfo
+   *   是否需要证书:是(证书及使用说明详见商户证书)
+   *   请求方式:POST
+   * 
+ * + * @param request 红包查询请求 + * @return the wx pay redpack query result + * @throws WxPayException the wx pay exception + */ + WxPayRedpackQueryResult queryRedpack(WxPayRedpackQueryRequest request) throws WxPayException; /** *
@@ -685,4 +677,23 @@ WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, Stri
    * @throws WxPayException the wx pay exception
    */
   String queryComment(Date beginDate, Date endDate, Integer offset, Integer limit) throws WxPayException;
+
+  /**
+   * 
+   * 拉取订单评价数据.
+   * 商户可以通过该接口拉取用户在微信支付交易记录中针对你的支付记录进行的评价内容。商户可结合商户系统逻辑对该内容数据进行存储、分析、展示、客服回访以及其他使用。如商户业务对评价内容有依赖,可主动引导用户进入微信支付交易记录进行评价。
+   * 注意:
+   * 1. 该内容所有权为提供内容的微信用户,商户在使用内容的过程中应遵从用户意愿
+   * 2. 一次最多拉取200条评价数据,可根据时间区间分批次拉取
+   * 3. 接口只能拉取最近三个月以内的评价数据
+   * 接口链接:https://api.mch.weixin.qq.com/billcommentsp/batchquerycomment
+   * 是否需要证书:需要
+   * 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_17&index=10
+   * 
+ * + * @param request 查询请求 + * @return the string + * @throws WxPayException the wx pay exception + */ + String queryComment(WxPayQueryCommentRequest request) throws WxPayException; } 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 ec57e745f9..24677ba908 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 @@ -1,29 +1,8 @@ package com.github.binarywang.wxpay.service.impl; -import java.io.File; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Date; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.zip.ZipException; - -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import com.github.binarywang.utils.qrcode.QrcodeUtils; import com.github.binarywang.wxpay.bean.WxPayApiData; -import com.github.binarywang.wxpay.bean.coupon.WxPayCouponInfoQueryRequest; -import com.github.binarywang.wxpay.bean.coupon.WxPayCouponInfoQueryResult; -import com.github.binarywang.wxpay.bean.coupon.WxPayCouponSendRequest; -import com.github.binarywang.wxpay.bean.coupon.WxPayCouponSendResult; -import com.github.binarywang.wxpay.bean.coupon.WxPayCouponStockQueryRequest; -import com.github.binarywang.wxpay.bean.coupon.WxPayCouponStockQueryResult; +import com.github.binarywang.wxpay.bean.coupon.*; import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult; import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult; import com.github.binarywang.wxpay.bean.notify.WxScanPayNotifyResult; @@ -31,39 +10,8 @@ import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult; import com.github.binarywang.wxpay.bean.order.WxPayMwebOrderResult; import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult; -import com.github.binarywang.wxpay.bean.request.WxPayAuthcode2OpenidRequest; -import com.github.binarywang.wxpay.bean.request.WxPayDefaultRequest; -import com.github.binarywang.wxpay.bean.request.WxPayDownloadBillRequest; -import com.github.binarywang.wxpay.bean.request.WxPayDownloadFundFlowRequest; -import com.github.binarywang.wxpay.bean.request.WxPayMicropayRequest; -import com.github.binarywang.wxpay.bean.request.WxPayOrderCloseRequest; -import com.github.binarywang.wxpay.bean.request.WxPayOrderQueryRequest; -import com.github.binarywang.wxpay.bean.request.WxPayOrderReverseRequest; -import com.github.binarywang.wxpay.bean.request.WxPayQueryCommentRequest; -import com.github.binarywang.wxpay.bean.request.WxPayRedpackQueryRequest; -import com.github.binarywang.wxpay.bean.request.WxPayRefundQueryRequest; -import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest; -import com.github.binarywang.wxpay.bean.request.WxPayReportRequest; -import com.github.binarywang.wxpay.bean.request.WxPaySendRedpackRequest; -import com.github.binarywang.wxpay.bean.request.WxPayShorturlRequest; -import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest; -import com.github.binarywang.wxpay.bean.result.BaseWxPayResult; -import com.github.binarywang.wxpay.bean.result.WxPayAuthcode2OpenidResult; -import com.github.binarywang.wxpay.bean.result.WxPayBillResult; -import com.github.binarywang.wxpay.bean.result.WxPayCommonResult; -import com.github.binarywang.wxpay.bean.result.WxPayFundFlowBaseResult; -import com.github.binarywang.wxpay.bean.result.WxPayFundFlowResult; -import com.github.binarywang.wxpay.bean.result.WxPayMicropayResult; -import com.github.binarywang.wxpay.bean.result.WxPayOrderCloseResult; -import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryResult; -import com.github.binarywang.wxpay.bean.result.WxPayOrderReverseResult; -import com.github.binarywang.wxpay.bean.result.WxPayRedpackQueryResult; -import com.github.binarywang.wxpay.bean.result.WxPayRefundQueryResult; -import com.github.binarywang.wxpay.bean.result.WxPayRefundResult; -import com.github.binarywang.wxpay.bean.result.WxPaySandboxSignKeyResult; -import com.github.binarywang.wxpay.bean.result.WxPaySendRedpackResult; -import com.github.binarywang.wxpay.bean.result.WxPayShorturlResult; -import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderResult; +import com.github.binarywang.wxpay.bean.request.*; +import com.github.binarywang.wxpay.bean.result.*; import com.github.binarywang.wxpay.config.WxPayConfig; import com.github.binarywang.wxpay.constant.WxPayConstants.BillType; import com.github.binarywang.wxpay.constant.WxPayConstants.SignType; @@ -75,6 +23,17 @@ import com.google.common.base.Joiner; import com.google.common.collect.Maps; import jodd.io.ZipUtil; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; +import java.util.zip.ZipException; import static com.github.binarywang.wxpay.constant.WxPayConstants.QUERY_COMMENT_DATE_FORMAT; import static com.github.binarywang.wxpay.constant.WxPayConstants.TarType; @@ -141,6 +100,10 @@ public WxPayRefundResult refund(WxPayRefundRequest request) throws WxPayExceptio request.checkAndSign(this.getConfig()); String url = this.getPayBaseUrl() + "/secapi/pay/refund"; + if (this.getConfig().isUseSandboxEnv()) { + url = PAY_BASE_URL + "/sandboxnew/pay/refund"; + } + String responseContent = this.post(url, request.toXML(), true); WxPayRefundResult result = WxPayRefundResult.fromXML(responseContent); result.checkResult(this, request.getSignType(), true); @@ -230,14 +193,20 @@ public WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request) throw } String responseContent = this.post(url, request.toXML(), true); - //无需校验,因为没有返回签名信息 - return BaseWxPayResult.fromXML(responseContent, WxPaySendRedpackResult.class); + final WxPaySendRedpackResult result = BaseWxPayResult.fromXML(responseContent, WxPaySendRedpackResult.class); + result.checkResult(this, request.getSignType(), true); + return result; } @Override public WxPayRedpackQueryResult queryRedpack(String mchBillNo) throws WxPayException { WxPayRedpackQueryRequest request = new WxPayRedpackQueryRequest(); request.setMchBillNo(mchBillNo); + return this.queryRedpack(request); + } + + @Override + public WxPayRedpackQueryResult queryRedpack(WxPayRedpackQueryRequest request) throws WxPayException { request.setBillType(BillType.MCHT); request.checkAndSign(this.getConfig()); @@ -326,11 +295,9 @@ public T createOrder(WxPayUnifiedOrderRequest request) throws WxPayException Map configMap = new HashMap<>(8); // 此map用于参与调起sdk支付的二次签名,格式全小写,timestamp只能是10位,格式固定,切勿修改 - String partnerId; - if (StringUtils.isEmpty(request.getMchId())) { - partnerId = this.getConfig().getMchId(); - } else { - partnerId = request.getMchId(); + String partnerId = unifiedOrderResult.getMchId(); + if (StringUtils.isNotEmpty(unifiedOrderResult.getSubMchId())) { + partnerId = unifiedOrderResult.getSubMchId(); } configMap.put("prepayid", prepayId); @@ -807,12 +774,16 @@ public String queryComment(Date beginDate, Date endDate, Integer offset, Integer request.setEndTime(QUERY_COMMENT_DATE_FORMAT.format(endDate)); request.setOffset(offset); request.setLimit(limit); - request.setSignType(SignType.HMAC_SHA256); + return this.queryComment(request); + } + + @Override + public String queryComment(WxPayQueryCommentRequest request) throws WxPayException { request.checkAndSign(this.getConfig()); + request.setSignType(SignType.HMAC_SHA256); String url = this.getPayBaseUrl() + "/billcommentsp/batchquerycomment"; - String responseContent = this.post(url, request.toXML(), true); if (responseContent.startsWith("<")) { throw WxPayException.from(BaseWxPayResult.fromXML(responseContent, WxPayCommonResult.class)); @@ -820,4 +791,5 @@ public String queryComment(Date beginDate, Date endDate, Integer offset, Integer return responseContent; } + } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EntPayServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EntPayServiceImpl.java index d694f42e78..59db3ee078 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EntPayServiceImpl.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EntPayServiceImpl.java @@ -74,6 +74,17 @@ public EntPayQueryResult queryEntPay(String partnerTradeNo) throws WxPayExceptio return result; } + @Override + public EntPayQueryResult queryEntPay(EntPayQueryRequest request) throws WxPayException { + request.checkAndSign(this.payService.getConfig()); + + String url = this.payService.getPayBaseUrl() + "/mmpaymkttransfers/gettransferinfo"; + String responseContent = this.payService.post(url, request.toXML(), true); + EntPayQueryResult result = BaseWxPayResult.fromXML(responseContent, EntPayQueryResult.class); + result.checkResult(this.payService, request.getSignType(), true); + return result; + } + @Override public String getPublicKey() throws WxPayException { WxPayDefaultRequest request = new WxPayDefaultRequest(); @@ -118,6 +129,17 @@ public EntPayBankQueryResult queryPayBank(String partnerTradeNo) throws WxPayExc return result; } + @Override + public EntPayBankQueryResult queryPayBank(EntPayBankQueryRequest request) throws WxPayException { + request.checkAndSign(this.payService.getConfig()); + + String url = this.payService.getPayBaseUrl() + "/mmpaysptrans/query_bank"; + String responseContent = this.payService.post(url, request.toXML(), true); + EntPayBankQueryResult result = BaseWxPayResult.fromXML(responseContent, EntPayBankQueryResult.class); + result.checkResult(this.payService, request.getSignType(), true); + return result; + } + private String encryptRSA(File publicKeyFile, String srcString) throws WxPayException { try { Security.addProvider(new BouncyCastleProvider()); @@ -145,26 +167,4 @@ private File buildPublicKeyFile() throws WxPayException { throw new WxPayException("生成加密公钥文件时发生异常", e); } } - - /** - * The entry point of application. - * - * @param args the input arguments - * @throws WxPayException the wx pay exception - * @throws IOException the io exception - */ - public static void main(String[] args) throws WxPayException, IOException { - String key = "-----BEGIN RSA PUBLIC KEY-----\n" + - "MIIBCgKCAQEAtEeUSop/YGqZ53Y++R9NapFSZmorj+SL/brmJUU7+hyClEnPOeG/\n" + - "v6/ZrX9qo25JAojrBDbqaW9L+HtzI141vusarRYIGPvVqTV30L5db0Yq7AmX7Hs9\n" + - "s+nEtoMAwMWUzQPXLUs2mt6rpu85HwAIK3F4Xb+OFIbXCJTbDvWYtQssn07lr+IY\n" + - "jPA00sON71egmuRrCoQClkhf0vgrhj7eHUCRZRJ2zf4UU31fHv+kO441hVD5TTP8\n" + - "bjJvFm6TW3sgQE8aCDbomtu+syk4Tv/4ONCqxG8d/kF1TlU+idGWEU179uR/KSjP\n" + - "p7kM7BoaY2goFgYAe4DsI8Fh33dCOiKyVwIDAQAB\n" + - "-----END RSA PUBLIC KEY-----"; - Path tmpFile = Files.createTempFile("payToBank", ".pem"); - Files.write(tmpFile, key.getBytes(StandardCharsets.UTF_8)); - System.out.println(new EntPayServiceImpl(null).encryptRSA(tmpFile.toFile(), "111111")); - } - } 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 df9a82fbe2..1703c200f5 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 @@ -4,6 +4,13 @@ import java.nio.charset.StandardCharsets; import javax.net.ssl.SSLContext; +import com.github.binarywang.wxpay.bean.WxPayApiData; +import com.github.binarywang.wxpay.bean.request.WxPayQueryCommentRequest; +import com.github.binarywang.wxpay.bean.request.WxPayRedpackQueryRequest; +import com.github.binarywang.wxpay.bean.result.WxPayCommonResult; +import com.github.binarywang.wxpay.bean.result.WxPayRedpackQueryResult; +import com.github.binarywang.wxpay.exception.WxPayException; +import jodd.util.Base64; import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpHost; import org.apache.http.auth.AuthScope; @@ -100,6 +107,10 @@ private HttpClientBuilder createHttpClientBuilder(boolean useKey) throws WxPayEx } if (StringUtils.isNotBlank(this.getConfig().getHttpProxyHost()) && this.getConfig().getHttpProxyPort() > 0) { + if (StringUtils.isEmpty(this.getConfig().getHttpProxyUsername())) { + this.getConfig().setHttpProxyUsername("whatever"); + } + // 使用代理服务器 需要用户认证的代理服务器 CredentialsProvider provider = new BasicCredentialsProvider(); provider.setCredentials(new AuthScope(this.getConfig().getHttpProxyHost(), this.getConfig().getHttpProxyPort()), 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 a598809938..81d35614d5 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 @@ -3,6 +3,10 @@ import java.nio.charset.StandardCharsets; import javax.net.ssl.SSLContext; +import com.github.binarywang.wxpay.bean.request.WxPayQueryCommentRequest; +import com.github.binarywang.wxpay.bean.request.WxPayRedpackQueryRequest; +import com.github.binarywang.wxpay.bean.result.WxPayCommonResult; +import com.github.binarywang.wxpay.bean.result.WxPayRedpackQueryResult; import org.apache.commons.lang3.StringUtils; import com.github.binarywang.wxpay.bean.WxPayApiData; @@ -15,6 +19,10 @@ import jodd.http.net.SSLSocketHttpConnectionProvider; import jodd.http.net.SocketHttpConnectionProvider; import jodd.util.Base64; +import org.apache.commons.lang3.StringUtils; + +import javax.net.ssl.SSLContext; +import java.nio.charset.StandardCharsets; /** * 微信支付请求实现类,jodd-http实现. @@ -76,6 +84,10 @@ private HttpRequest buildHttpRequest(String url, String requestStr, boolean useK } if (StringUtils.isNotBlank(this.getConfig().getHttpProxyHost()) && this.getConfig().getHttpProxyPort() > 0) { + if (StringUtils.isEmpty(this.getConfig().getHttpProxyUsername())) { + this.getConfig().setHttpProxyUsername("whatever"); + } + ProxyInfo httpProxy = new ProxyInfo(ProxyType.HTTP, this.getConfig().getHttpProxyHost(), this.getConfig().getHttpProxyPort(), this.getConfig().getHttpProxyUsername(), this.getConfig().getHttpProxyPassword()); HttpConnectionProvider provider = request.connectionProvider(); diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/BaseWxPayResultTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/BaseWxPayResultTest.java index 83fcc5f365..6b70d5542f 100644 --- a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/BaseWxPayResultTest.java +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/BaseWxPayResultTest.java @@ -75,7 +75,9 @@ public void testToMap() throws Exception { @Test(expectedExceptions = {RuntimeException.class}) public void testToMap_with_empty_xmlString() { WxPayOrderQueryResult result = new WxPayOrderQueryResult(); - result.setXmlString(" "); + result.setXmlString( "]" + + ">&win;"); Map map = result.toMap(); System.out.println(map); }