diff --git a/pom.xml b/pom.xml index 930c7ce901..93b5df6b87 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ 4.0.0 com.github.binarywang wx-java - 3.6.0 + 3.7.0 pom WxJava - Weixin/Wechat Java SDK 微信开发Java SDK @@ -99,6 +99,7 @@ + weixin-graal weixin-java-common weixin-java-cp weixin-java-mp @@ -172,7 +173,7 @@ com.thoughtworks.xstream xstream - 1.4.11 + 1.4.11.1 @@ -242,6 +243,12 @@ 2.9.0 provided + + org.redisson + redisson + 3.12.0 + provided + org.projectlombok lombok @@ -325,6 +332,14 @@ + + + native-image + + false + + + diff --git a/spring-boot-starters/pom.xml b/spring-boot-starters/pom.xml index a04dc13547..ecf51716dd 100644 --- a/spring-boot-starters/pom.xml +++ b/spring-boot-starters/pom.xml @@ -6,7 +6,7 @@ com.github.binarywang wx-java - 3.6.0 + 3.7.0 pom wx-java-spring-boot-starters diff --git a/spring-boot-starters/wx-java-miniapp-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-miniapp-spring-boot-starter/pom.xml index c538357675..899b7bb2e0 100644 --- a/spring-boot-starters/wx-java-miniapp-spring-boot-starter/pom.xml +++ b/spring-boot-starters/wx-java-miniapp-spring-boot-starter/pom.xml @@ -5,7 +5,7 @@ wx-java-spring-boot-starters com.github.binarywang - 3.6.0 + 3.7.0 4.0.0 diff --git a/spring-boot-starters/wx-java-mp-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-mp-spring-boot-starter/pom.xml index d45c1bd729..9053e72ba7 100644 --- a/spring-boot-starters/wx-java-mp-spring-boot-starter/pom.xml +++ b/spring-boot-starters/wx-java-mp-spring-boot-starter/pom.xml @@ -5,7 +5,7 @@ wx-java-spring-boot-starters com.github.binarywang - 3.6.0 + 3.7.0 4.0.0 @@ -24,6 +24,11 @@ jedis compile + + org.redisson + redisson + compile + diff --git a/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/WxMpStorageAutoConfiguration.java b/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/WxMpStorageAutoConfiguration.java index 18024707a4..4ddff5a72a 100644 --- a/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/WxMpStorageAutoConfiguration.java +++ b/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/WxMpStorageAutoConfiguration.java @@ -6,6 +6,7 @@ import me.chanjar.weixin.mp.config.WxMpConfigStorage; import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl; import me.chanjar.weixin.mp.config.impl.WxMpRedisConfigImpl; +import org.redisson.api.RedissonClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; @@ -26,6 +27,9 @@ public class WxMpStorageAutoConfiguration { @Autowired(required = false) private JedisPool jedisPool; + @Autowired(required = false) + private RedissonClient redissonClient; + @Bean @ConditionalOnMissingBean(WxMpConfigStorage.class) public WxMpConfigStorage wxMpInMemoryConfigStorage() { diff --git a/spring-boot-starters/wx-java-open-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-open-spring-boot-starter/pom.xml index 52ab7e3da5..9d77c2c76a 100644 --- a/spring-boot-starters/wx-java-open-spring-boot-starter/pom.xml +++ b/spring-boot-starters/wx-java-open-spring-boot-starter/pom.xml @@ -5,7 +5,7 @@ wx-java-spring-boot-starters com.github.binarywang - 3.6.0 + 3.7.0 4.0.0 @@ -24,6 +24,11 @@ jedis compile + + org.redisson + redisson + compile + diff --git a/spring-boot-starters/wx-java-open-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/config/WxOpenStorageAutoConfiguration.java b/spring-boot-starters/wx-java-open-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/config/WxOpenStorageAutoConfiguration.java index 5b57551973..a92b3483b9 100644 --- a/spring-boot-starters/wx-java-open-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/config/WxOpenStorageAutoConfiguration.java +++ b/spring-boot-starters/wx-java-open-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/config/WxOpenStorageAutoConfiguration.java @@ -6,7 +6,12 @@ import me.chanjar.weixin.open.api.WxOpenConfigStorage; import me.chanjar.weixin.open.api.impl.WxOpenInMemoryConfigStorage; import me.chanjar.weixin.open.api.impl.WxOpenInRedisConfigStorage; +import me.chanjar.weixin.open.api.impl.WxOpenInRedissonConfigStorage; import org.apache.commons.lang3.StringUtils; +import org.redisson.Redisson; +import org.redisson.api.RedissonClient; +import org.redisson.config.Config; +import org.redisson.config.TransportMode; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -28,6 +33,9 @@ public class WxOpenStorageAutoConfiguration { @Autowired(required = false) private JedisPool jedisPool; + @Autowired(required = false) + private RedissonClient redissonClient; + @Value("${wx.open.config-storage.redis.host:}") private String redisHost; @@ -40,12 +48,20 @@ public WxOpenConfigStorage wxOpenConfigStorage() { if (type == WxOpenProperties.StorageType.redis) { return getWxOpenInRedisConfigStorage(); } + + if (type == WxOpenProperties.StorageType.jedis) { + return getWxOpenInRedisConfigStorage(); + } + + if (type == WxOpenProperties.StorageType.redisson) { + return getWxOpenInRedissonConfigStorage(); + } return getWxOpenInMemoryConfigStorage(); } private WxOpenInMemoryConfigStorage getWxOpenInMemoryConfigStorage() { WxOpenInMemoryConfigStorage config = new WxOpenInMemoryConfigStorage(); - setWxOpenInfo(config); + config.setWxOpenInfo(properties.getAppId(), properties.getSecret(), properties.getToken(), properties.getAesKey()); return config; } @@ -54,18 +70,22 @@ private WxOpenInRedisConfigStorage getWxOpenInRedisConfigStorage() { if (jedisPool == null || StringUtils.isNotEmpty(redisHost)) { poolToUse = getJedisPool(); } - WxOpenInRedisConfigStorage config = new WxOpenInRedisConfigStorage(poolToUse); - setWxOpenInfo(config); + WxOpenInRedisConfigStorage config = new WxOpenInRedisConfigStorage(poolToUse, properties.getConfigStorage().getKeyPrefix()); + config.setWxOpenInfo(properties.getAppId(), properties.getSecret(), properties.getToken(), properties.getAesKey()); return config; } - private void setWxOpenInfo(WxOpenConfigStorage config) { - config.setComponentAppId(properties.getAppId()); - config.setComponentAppSecret(properties.getSecret()); - config.setComponentToken(properties.getToken()); - config.setComponentAesKey(properties.getAesKey()); + private WxOpenInRedissonConfigStorage getWxOpenInRedissonConfigStorage() { + RedissonClient redissonClientToUse = this.redissonClient; + if (redissonClient == null) { + redissonClientToUse = getRedissonClient(); + } + WxOpenInRedissonConfigStorage config = new WxOpenInRedissonConfigStorage(redissonClientToUse, properties.getConfigStorage().getKeyPrefix()); + config.setWxOpenInfo(properties.getAppId(), properties.getSecret(), properties.getToken(), properties.getAesKey()); + return config; } + private JedisPool getJedisPool() { WxOpenProperties.ConfigStorage storage = properties.getConfigStorage(); RedisProperties redis = storage.getRedis(); @@ -90,4 +110,16 @@ private JedisPool getJedisPool() { redis.getTimeout(), redis.getPassword(), redis.getDatabase()); return pool; } + + private RedissonClient getRedissonClient() { + WxOpenProperties.ConfigStorage storage = properties.getConfigStorage(); + RedisProperties redis = storage.getRedis(); + + Config config = new Config(); + config.useSingleServer() + .setAddress("redis://" + redis.getHost() + ":" + redis.getPort()) + .setPassword(redis.getPassword()); + config.setTransportMode(TransportMode.NIO); + return Redisson.create(config); + } } diff --git a/spring-boot-starters/wx-java-open-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/properties/WxOpenProperties.java b/spring-boot-starters/wx-java-open-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/properties/WxOpenProperties.java index 64cc3d0672..77aabad54a 100644 --- a/spring-boot-starters/wx-java-open-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/properties/WxOpenProperties.java +++ b/spring-boot-starters/wx-java-open-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/properties/WxOpenProperties.java @@ -53,6 +53,8 @@ public static class ConfigStorage implements Serializable { private RedisProperties redis = new RedisProperties(); + private String keyPrefix; + } public enum StorageType { @@ -63,6 +65,14 @@ public enum StorageType { /** * redis. */ - redis + redis, + /** + * jedis. + */ + jedis, + /** + * redisson. + */ + redisson } } diff --git a/spring-boot-starters/wx-java-pay-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-pay-spring-boot-starter/pom.xml index 6c643615c0..870f3e7c77 100644 --- a/spring-boot-starters/wx-java-pay-spring-boot-starter/pom.xml +++ b/spring-boot-starters/wx-java-pay-spring-boot-starter/pom.xml @@ -5,7 +5,7 @@ wx-java-spring-boot-starters com.github.binarywang - 3.6.0 + 3.7.0 4.0.0 diff --git a/weixin-graal/pom.xml b/weixin-graal/pom.xml new file mode 100644 index 0000000000..1f351580e1 --- /dev/null +++ b/weixin-graal/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + + com.github.binarywang + wx-java + 3.7.0 + + + weixin-graal + WxJava - Graal + 微信开发Java内部配合graal以产生native-image配置的辅助工具, 可以通过项目的 native-image Profile 来启用: mvn -P native-image ... + + + + + + org.projectlombok + lombok + compile + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.5.1 + + none + + + + + + diff --git a/weixin-graal/src/main/java/cn/binarywang/wx/graal/GraalProcessor.java b/weixin-graal/src/main/java/cn/binarywang/wx/graal/GraalProcessor.java new file mode 100644 index 0000000000..c09190c3c5 --- /dev/null +++ b/weixin-graal/src/main/java/cn/binarywang/wx/graal/GraalProcessor.java @@ -0,0 +1,167 @@ +package cn.binarywang.wx.graal; + +import lombok.Data; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.annotation.processing.SupportedSourceVersion; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.ElementFilter; +import javax.tools.FileObject; +import javax.tools.StandardLocation; +import java.io.IOException; +import java.io.Writer; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +// 目前仅仅处理@Data,且必须在lombok自己的processor之前执行,千万注意!!!!! +@SupportedAnnotationTypes("lombok.Data") +@SupportedSourceVersion(SourceVersion.RELEASE_7) +public class GraalProcessor extends AbstractProcessor { + + private static final String REFLECTION_CONFIG_JSON = "reflection-config.json"; + private static final String NATIVE_IMAGE_PROPERTIES = "native-image.properties"; + + private SortedSet classSet = new TreeSet<>(); + private String shortestPackageName = null; + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + for (TypeElement annotatedClass : ElementFilter.typesIn(roundEnv.getElementsAnnotatedWith(Data.class))) { + + registerClass(annotatedClass.getQualifiedName().toString()); + handleSuperClass(annotatedClass); + } + + //只有最后一轮才可以写文件,否则文件会被重复打开,报错! + if (!roundEnv.processingOver()) return false; + + // 如果没有文件要写,跳过 + if (classSet.isEmpty()) return false; + + writeFiles(); + + //必须返回false,以便让lombok能继续处理。 + return false; + } + + /** + * 设置当前最短的package名称 + * + * @param packageName 包名 + */ + private void setShortestPackageName(String packageName) { + if (shortestPackageName == null) { + shortestPackageName = packageName; + } else if (packageName.length() < shortestPackageName.length()) { + shortestPackageName = packageName; + } + } + + /** + * 更加完整的类名来获取package名称 + * + * @param fullClassName 完整的类名 + * @return package name + */ + private String getPackageName(String fullClassName) { + int last = fullClassName.lastIndexOf('.'); + if (last == -1) return fullClassName; + return fullClassName.substring(0, last); + } + + /** + * 保存文件 + * META-INF/native-image/.../reflection-config.json + * META-INF/native-image/.../native-image.properties + */ + private void writeFiles() { + String basePackage = shortestPackageName; + + String module; + if (basePackage.contains(".")) { + final int i = basePackage.lastIndexOf('.'); + module = basePackage.substring(i + 1); + basePackage = basePackage.substring(0, i); + } else { + module = basePackage; + } + + String path = "META-INF/native-image/" + basePackage + "/" + module + "/"; + String reflectFile = path + REFLECTION_CONFIG_JSON; + String propsFile = path + NATIVE_IMAGE_PROPERTIES; + try { + FileObject fileObject = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", propsFile); + Writer writer = fileObject.openWriter(); + writer.append("Args = -H:ReflectionConfigurationResources=${.}/" + REFLECTION_CONFIG_JSON); + writer.close(); + } catch (IOException e) { + e.printStackTrace(); + } + + try { + FileObject fileObject = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", reflectFile); + Writer writer = fileObject.openWriter(); + writer.write("[\n"); + boolean first = true; + for (String name : classSet) { + if (first) { + first = false; + } else { + writer.write(","); + } + writer.write(assetGraalJsonElement(name)); + writer.append('\n'); + } + writer.write("]"); + writer.close(); + } catch (IOException e) { + e.printStackTrace(); + } + + } + + private String assetGraalJsonElement(String className) { + return "{\n" + + " \"name\" : \"" + className + "\",\n" + + " \"allDeclaredFields\":true,\n" + + " \"allDeclaredMethods\":true,\n" + + " \"allDeclaredConstructors\":true,\n" + + " \"allPublicMethods\" : true\n" + + "}"; + } + + /** + * 登记一个class + * + * @param className 完整的类名 + */ + private void registerClass(String className) { + classSet.add(className); + setShortestPackageName(getPackageName(className)); + } + + /** + * 获取一个类型的所有的父类,并登记 + * + * @param typeElement 类型元素 + */ + private void handleSuperClass(TypeElement typeElement) { + TypeMirror superclass = typeElement.getSuperclass(); + if (superclass.getKind() == TypeKind.DECLARED) { + TypeElement s = (TypeElement) ((DeclaredType) superclass).asElement(); + String sName = s.toString(); + // ignore java.**/javax.** + if (sName.startsWith("java.") || sName.startsWith("javax.")) return; + registerClass(sName); + handleSuperClass(s); + } + } + +} diff --git a/weixin-graal/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/weixin-graal/src/main/resources/META-INF/services/javax.annotation.processing.Processor new file mode 100644 index 0000000000..fed7c4d9cd --- /dev/null +++ b/weixin-graal/src/main/resources/META-INF/services/javax.annotation.processing.Processor @@ -0,0 +1 @@ +cn.binarywang.wx.graal.GraalProcessor diff --git a/weixin-java-common/pom.xml b/weixin-java-common/pom.xml index 68c430f5c8..0620e6ddfa 100644 --- a/weixin-java-common/pom.xml +++ b/weixin-java-common/pom.xml @@ -1,12 +1,12 @@ - + 4.0.0 com.github.binarywang wx-java - 3.6.0 + 3.7.0 weixin-java-common @@ -134,4 +134,36 @@ + + + native-image + + false + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.5.1 + + + cn.binarywang.wx.graal.GraalProcessor,lombok.launch.AnnotationProcessorHider$AnnotationProcessor,lombok.launch.AnnotationProcessorHider$ClaimingProcessor + + + + com.github.binarywang + weixin-graal + ${project.version} + + + + + + + + + + diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxMaErrorMsgEnum.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxMaErrorMsgEnum.java index f1ced3fd09..eced6027e9 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxMaErrorMsgEnum.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxMaErrorMsgEnum.java @@ -452,6 +452,19 @@ public enum WxMaErrorMsgEnum { CODE_43101(43101, "用户拒绝接受消息,如果用户之前曾经订阅过,则表示用户取消了订阅关系"), CODE_47003(47003, "模板参数不准确,可能为空或者不满足规则,errmsg会提示具体是哪个字段出错"), + + /** + * 小程序绑定体验者 + */ + CODE_85001(85001, "微信号不存在或微信号设置为不可搜索"), + + CODE_85002(85002, "小程序绑定的体验者数量达到上限"), + + CODE_85003(85003, "微信号绑定的小程序体验者达到上限"), + + CODE_85004(85004, "微信号已经绑定"), + +// CODE_504002(-504002, "云函数未找到 Function not found"), ; private int code; diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/SimplePostRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/SimplePostRequestExecutor.java index 692387f9f2..1209790b65 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/SimplePostRequestExecutor.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/SimplePostRequestExecutor.java @@ -1,15 +1,14 @@ package me.chanjar.weixin.common.util.http; -import java.io.IOException; - import me.chanjar.weixin.common.WxType; import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.util.http.apache.ApacheSimplePostRequestExecutor; import me.chanjar.weixin.common.util.http.jodd.JoddHttpSimplePostRequestExecutor; import me.chanjar.weixin.common.util.http.okhttp.OkHttpSimplePostRequestExecutor; +import java.io.IOException; + /** - * 用装饰模式实现 * 简单的POST请求执行器,请求的参数是String, 返回的结果也是String * * @author Daniel Qian diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheSimplePostRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheSimplePostRequestExecutor.java index 3405aaab78..960ea865af 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheSimplePostRequestExecutor.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheSimplePostRequestExecutor.java @@ -36,6 +36,7 @@ public String execute(String uri, String postEntity, WxType wxType) throws WxErr if (postEntity != null) { StringEntity entity = new StringEntity(postEntity, Consts.UTF_8); + entity.setContentType("application/json; charset=utf-8"); httpPost.setEntity(entity); } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/Utf8ResponseHandler.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/Utf8ResponseHandler.java index 697d4695e2..035726d44f 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/Utf8ResponseHandler.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/Utf8ResponseHandler.java @@ -25,7 +25,7 @@ public String handleResponse(final HttpResponse response) throws IOException { final HttpEntity entity = response.getEntity(); if (statusLine.getStatusCode() >= 300) { EntityUtils.consume(entity); - throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase()); + throw new HttpResponseException(statusLine.getStatusCode(), statusLine.toString()); } return entity == null ? null : EntityUtils.toString(entity, Consts.UTF_8); } diff --git a/weixin-java-cp/pom.xml b/weixin-java-cp/pom.xml index 2dc09480d4..5892d6a3d6 100644 --- a/weixin-java-cp/pom.xml +++ b/weixin-java-cp/pom.xml @@ -7,7 +7,7 @@ com.github.binarywang wx-java - 3.6.0 + 3.7.0 weixin-java-cp @@ -95,4 +95,36 @@ + + + native-image + + false + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.5.1 + + + cn.binarywang.wx.graal.GraalProcessor,lombok.launch.AnnotationProcessorHider$AnnotationProcessor,lombok.launch.AnnotationProcessorHider$ClaimingProcessor + + + + com.github.binarywang + weixin-graal + ${project.version} + + + + + + + + + + diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpExternalContactService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpExternalContactService.java index 92f2258696..df5326f249 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpExternalContactService.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpExternalContactService.java @@ -24,25 +24,54 @@ public interface WxCpExternalContactService { * * * @param userId 外部联系人的userid + * @return . + * @deprecated 建议使用 {@link #getContactDetail(String)} */ + @Deprecated WxCpUserExternalContactInfo getExternalContact(String userId) throws WxErrorException; /** - * 获取外部联系人列表. + * 获取客户详情. *
-   *   企业可通过此接口获取指定成员添加的客户列表。
-   *   客户是指配置了客户联系功能的成员所添加的外部联系人。
-   *   没有配置客户联系功能的成员,所添加的外部联系人将不会作为客户返回。
+   *
+   * 企业可通过此接口,根据外部联系人的userid(如何获取?),拉取客户详情。
+   *
+   * 请求方式:GET(HTTPS)
+   * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/externalcontact/get?access_token=ACCESS_TOKEN&external_userid=EXTERNAL_USERID
+   *
+   * 权限说明:
+   *
+   * 企业需要使用“客户联系”secret或配置到“可调用应用”列表中的自建应用secret所获取的accesstoken来调用(accesstoken如何获取?);
+   * 第三方/自建应用调用时,返回的跟进人follow_user仅包含应用可见范围之内的成员。
+   * 
+ * + * @param userId 外部联系人的userid,注意不是企业成员的帐号 + * @return . + * @throws WxErrorException . + */ + WxCpUserExternalContactInfo getContactDetail(String userId) throws WxErrorException; + + /** + * 获取客户列表. + *
+   *   企业可通过此接口获取指定成员添加的客户列表。客户是指配置了客户联系功能的成员所添加的外部联系人。没有配置客户联系功能的成员,所添加的外部联系人将不会作为客户返回。
+   *
+   * 请求方式:GET(HTTPS)
+   * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/externalcontact/list?access_token=ACCESS_TOKEN&userid=USERID
+   *
+   * 权限说明:
+   *
+   * 企业需要使用“客户联系”secret或配置到“可调用应用”列表中的自建应用secret所获取的accesstoken来调用(accesstoken如何获取?);
    * 第三方应用需拥有“企业客户”权限。
-   * 第三方应用调用时,返回的跟进人follow_user仅包含应用可见范围之内的成员。
+   * 第三方/自建应用只能获取到可见范围内的配置了客户联系功能的成员。
    * 
* - * @param userId 外部联系人的userid + * @param userId 企业成员的userid * @return List of External wx id + * @throws WxErrorException . */ List listExternalContacts(String userId) throws WxErrorException; - /** * 企业和第三方服务商可通过此接口获取配置了客户联系功能的成员(Customer Contact)列表。 *
@@ -52,6 +81,7 @@ public interface WxCpExternalContactService {
    * 
* * @return List of CpUser id + * @throws WxErrorException . */ List listFollowUser() throws WxErrorException; diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOaService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOaService.java index c6f90d3e64..9af5a36e4b 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOaService.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOaService.java @@ -1,10 +1,8 @@ package me.chanjar.weixin.cp.api; +import lombok.NonNull; 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 me.chanjar.weixin.cp.bean.oa.*; import java.util.Date; import java.util.List; @@ -30,7 +28,8 @@ public interface WxCpOaService { * @return 打卡数据列表 * @throws WxErrorException 异常 */ - List getCheckinData(Integer openCheckinDataType, Date startTime, Date endTime, List userIdList) throws WxErrorException; + List getCheckinData(Integer openCheckinDataType, Date startTime, Date endTime, + List userIdList) throws WxErrorException; /** *
@@ -41,13 +40,63 @@ public interface WxCpOaService {
    * @param datetime   需要获取规则的当天日期
    * @param userIdList 需要获取打卡规则的用户列表
    * @return 打卡规则列表
-   * @throws WxErrorException 异常
+   * @throws WxErrorException
    */
   List getCheckinOption(Date datetime, List userIdList) throws WxErrorException;
 
   /**
    * 
-   *   获取审批数据
+   *
+   * 批量获取审批单号
+   *
+   * 审批应用及有权限的自建应用,可通过Secret调用本接口,以获取企业一段时间内企业微信“审批应用”单据的审批编号,支持按模板类型、申请人、部门、申请单审批状态等条件筛选。
+   * 自建应用调用此接口,需在“管理后台-应用管理-审批-API-审批数据权限”中,授权应用允许提交审批单据。
+   *
+   * 一次拉取调用最多拉取100个审批记录,可以通过多次拉取的方式来满足需求,但调用频率不可超过600次/分。
+   *
+   * API doc : https://work.weixin.qq.com/api/doc/90000/90135/91816
+   * 
+ * + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param cursor 分页查询游标,默认为0,后续使用返回的next_cursor进行分页拉取 + * @param size 一次请求拉取审批单数量,默认值为100,上限值为100 + * @param filters 筛选条件,可对批量拉取的审批申请设置约束条件,支持设置多个条件,nullable + * @return WxCpApprovalInfo + * @throws WxErrorException + */ + WxCpApprovalInfo getApprovalInfo(@NonNull Date startTime, @NonNull Date endTime, Integer cursor, Integer size, + List filters) throws WxErrorException; + + /** + * short method + * + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return WxCpApprovalInfo + * @throws WxErrorException + * @see me.chanjar.weixin.cp.api.WxCpOaService#getApprovalInfo + */ + WxCpApprovalInfo getApprovalInfo(@NonNull Date startTime, @NonNull Date endTime) throws WxErrorException; + + /** + *
+   *   获取审批申请详情
+   *
+   *   企业可通过审批应用或自建应用Secret调用本接口,根据审批单号查询企业微信“审批应用”的审批申请详情。
+   *
+   *   API Doc : https://work.weixin.qq.com/api/doc/90000/90135/91983
+   * 
+ * + * @param spNo 审批单编号。 + * @return WxCpApprovaldetail + * @throws WxErrorException + */ + WxCpApprovalDetailResult getApprovalDetail(@NonNull String spNo) throws WxErrorException; + + /** + *
+   *   获取审批数据 (已过期, 请使用"批量获取审批单号" && "获取审批申请详情")
    *   通过本接口来获取公司一段时间内的审批记录。一次拉取调用最多拉取10000个审批记录,可以通过多次拉取的方式来满足需求,但调用频率不可超过600次/分。
    *   API doc : https://work.weixin.qq.com/api/doc#90000/90135/91530
    * 
@@ -55,10 +104,32 @@ public interface WxCpOaService { * @param startTime 获取审批记录的开始时间 * @param endTime 获取审批记录的结束时间 * @param nextSpnum 第一个拉取的审批单号,不填从该时间段的第一个审批单拉取 - * @throws WxErrorException 异常 + * @throws WxErrorException + * @see me.chanjar.weixin.cp.api.WxCpOaService#getApprovalInfo + * @see me.chanjar.weixin.cp.api.WxCpOaService#getApprovalDetail */ + @Deprecated WxCpApprovalDataResult getApprovalData(Date startTime, Date endTime, Long nextSpnum) throws WxErrorException; - List getDialRecord(Date startTime, Date endTime, Integer offset, Integer limit) throws WxErrorException; + /** + * 获取公费电话拨打记录 + * + * @param startTime 查询的起始时间戳 + * @param endTime 查询的结束时间戳 + * @param offset 分页查询的偏移量 + * @param limit 分页查询的每页大小,默认为100条,如该参数大于100则按100处理 + * @return + * @throws WxErrorException + */ + List getDialRecord(Date startTime, Date endTime, Integer offset, + Integer limit) throws WxErrorException; + + /** + * 获取审批模板详情 + * @param templateId 模板ID + * @return + * @throws WxErrorException + */ + WxCpTemplateResult getTemplateDetail(@NonNull String templateId)throws WxErrorException; } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImpl.java index 9c7396ab1b..9dd1333bfa 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImpl.java @@ -205,7 +205,7 @@ public WxCpProviderToken getProviderToken(String corpId, String providerSecret) JsonObject jsonObject = new JsonObject(); jsonObject.addProperty("corpid", corpId); jsonObject.addProperty("provider_secret", providerSecret); - return WxCpProviderToken.fromJson(this.post(this.configStorage.getApiUrl(GET_PROVIDER_TOKEN), jsonObject.toString())); + return WxCpProviderToken.fromJson(this.post(this.configStorage.getApiUrl(Tp.GET_PROVIDER_TOKEN), jsonObject.toString())); } @Override @@ -281,7 +281,9 @@ protected T executeInternal(RequestExecutor executor, String uri, E if (error.getErrorCode() == 42001 || error.getErrorCode() == 40001 || error.getErrorCode() == 40014) { // 强制设置wxCpConfigStorage它的access token过期了,这样在下一次请求里就会刷新access token this.configStorage.expireAccessToken(); - return execute(executor, uri, data); + if (this.getWxCpConfigStorage().autoRefreshToken()) { + return this.execute(executor, uri, data); + } } if (error.getErrorCode() != 0) { diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpTpServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpTpServiceImpl.java index fe8e3c08e3..dea5428c1b 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpTpServiceImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpTpServiceImpl.java @@ -194,7 +194,9 @@ protected T executeInternal(RequestExecutor executor, String uri, E if (error.getErrorCode() == 42009) { // 强制设置wxCpTpConfigStorage它的suite access token过期了,这样在下一次请求里就会刷新suite access token this.configStorage.expireSuiteAccessToken(); - return execute(executor, uri, data); + if (this.getWxCpTpConfigStorage().autoRefreshToken()) { + return this.execute(executor, uri, data); + } } if (error.getErrorCode() != 0) { diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpExternalContactServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpExternalContactServiceImpl.java index 8a5b7d56e8..044b1e5d49 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpExternalContactServiceImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpExternalContactServiceImpl.java @@ -25,6 +25,13 @@ public WxCpUserExternalContactInfo getExternalContact(String userId) throws WxEr return WxCpUserExternalContactInfo.fromJson(responseContent); } + @Override + public WxCpUserExternalContactInfo getContactDetail(String userId) throws WxErrorException { + final String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_CONTACT_DETAIL + userId); + String responseContent = this.mainService.get(url, null); + return WxCpUserExternalContactInfo.fromJson(responseContent); + } + @Override public List listExternalContacts(String userId) throws WxErrorException { final String url = this.mainService.getWxCpConfigStorage().getApiUrl(LIST_EXTERNAL_CONTACT + userId); diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpOaServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpOaServiceImpl.java index b8449ced13..0484e1f62a 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpOaServiceImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpOaServiceImpl.java @@ -5,15 +5,12 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.google.gson.reflect.TypeToken; +import lombok.NonNull; import lombok.RequiredArgsConstructor; 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.constant.WxCpApiPathConsts; +import me.chanjar.weixin.cp.bean.oa.*; import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; import java.util.Date; @@ -22,7 +19,7 @@ import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Oa.*; /** - * . + * 企业微信 OA 接口实现 * * @author Element * @date 2019-04-06 11:20 @@ -31,6 +28,9 @@ public class WxCpOaServiceImpl implements WxCpOaService { private final WxCpService mainService; + private static final int MONTH_SECONDS = 30 * 24 * 60 * 60; + private static final int USER_IDS_LIMIT = 100; + @Override public List getCheckinData(Integer openCheckinDataType, Date startTime, Date endTime, List userIdList) throws WxErrorException { @@ -38,14 +38,14 @@ public List getCheckinData(Integer openCheckinDataType, Date st throw new RuntimeException("starttime and endtime can't be null"); } - if (userIdList == null || userIdList.size() > 100) { - throw new RuntimeException("用户列表不能为空,不超过100个,若用户超过100个,请分批获取"); + if (userIdList == null || userIdList.size() > USER_IDS_LIMIT) { + throw new RuntimeException("用户列表不能为空,不超过 " + USER_IDS_LIMIT + " 个,若用户超过 " + USER_IDS_LIMIT + " 个,请分批获取"); } long endtimestamp = endTime.getTime() / 1000L; long starttimestamp = startTime.getTime() / 1000L; - if (endtimestamp - starttimestamp < 0 || endtimestamp - starttimestamp >= 30 * 24 * 60 * 60) { + if (endtimestamp - starttimestamp < 0 || endtimestamp - starttimestamp >= MONTH_SECONDS) { throw new RuntimeException("获取记录时间跨度不超过一个月"); } @@ -79,8 +79,8 @@ public List getCheckinOption(Date datetime, List user throw new RuntimeException("datetime can't be null"); } - if (userIdList == null || userIdList.size() > 100) { - throw new RuntimeException("用户列表不能为空,不超过100个,若用户超过100个,请分批获取"); + if (userIdList == null || userIdList.size() > USER_IDS_LIMIT) { + throw new RuntimeException("用户列表不能为空,不超过 " + USER_IDS_LIMIT + " 个,若用户超过 " + USER_IDS_LIMIT + " 个,请分批获取"); } JsonArray jsonArray = new JsonArray(); @@ -104,6 +104,59 @@ public List getCheckinOption(Date datetime, List user ); } + @Override + public WxCpApprovalInfo getApprovalInfo(@NonNull Date startTime, @NonNull Date endTime, + Integer cursor, Integer size, List filters) throws WxErrorException { + + if (cursor == null) { + cursor = 0; + } + + if (size == null) { + size = 100; + } + + if (size < 0 || size > 100) { + throw new IllegalArgumentException("size参数错误,请使用[1-100]填充,默认100"); + } + + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("starttime", startTime.getTime() / 1000L); + jsonObject.addProperty("endtime", endTime.getTime() / 1000L); + jsonObject.addProperty("size", size); + jsonObject.addProperty("cursor", cursor); + + if (filters != null && !filters.isEmpty()) { + JsonArray filterJsonArray = new JsonArray(); + for (WxCpApprovalInfoQueryFilter filter : filters) { + filterJsonArray.add(new JsonParser().parse(filter.toJson())); + } + jsonObject.add("filters", filterJsonArray); + } + + final String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_APPROVAL_INFO); + String responseContent = this.mainService.post(url, jsonObject.toString()); + + return WxCpGsonBuilder.create().fromJson(responseContent, WxCpApprovalInfo.class); + } + + @Override + public WxCpApprovalInfo getApprovalInfo(@NonNull Date startTime, @NonNull Date endTime) throws WxErrorException { + return this.getApprovalInfo(startTime, endTime, null, null, null); + } + + @Override + public WxCpApprovalDetailResult getApprovalDetail(@NonNull String spNo) throws WxErrorException { + + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("sp_no", spNo); + + final String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_APPROVAL_DETAIL); + String responseContent = this.mainService.post(url, jsonObject.toString()); + + return WxCpGsonBuilder.create().fromJson(responseContent, WxCpApprovalDetailResult.class); + } + @Override public WxCpApprovalDataResult getApprovalData(Date startTime, Date endTime, Long nextSpnum) throws WxErrorException { JsonObject jsonObject = new JsonObject(); @@ -139,7 +192,7 @@ public List getDialRecord(Date startTime, Date endTime, Integer long endtimestamp = endTime.getTime() / 1000L; long starttimestamp = startTime.getTime() / 1000L; - if (endtimestamp - starttimestamp < 0 || endtimestamp - starttimestamp >= 30 * 24 * 60 * 60) { + if (endtimestamp - starttimestamp < 0 || endtimestamp - starttimestamp >= MONTH_SECONDS) { throw new RuntimeException("受限于网络传输,起止时间的最大跨度为30天,如超过30天,则以结束时间为基准向前取30天进行查询"); } @@ -156,4 +209,13 @@ public List getDialRecord(Date startTime, Date endTime, Integer }.getType() ); } + + @Override + public WxCpTemplateResult getTemplateDetail(@NonNull String templateId) throws WxErrorException { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("template_id",templateId); + final String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_TEMPLATE_DETAIL); + String responseContent = this.mainService.post(url, jsonObject.toString()); + return WxCpGsonBuilder.create().fromJson(responseContent,WxCpTemplateResult.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 b7389afa2b..90354154a6 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 @@ -1,15 +1,15 @@ package me.chanjar.weixin.cp.bean; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; - import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + /** * 微信用户信息. * @@ -27,6 +27,8 @@ public class WxCpUser implements Serializable { private Gender gender; private String email; private String avatar; + private String thumbAvatar; + /** * 地址。长度最大128个字符 */ @@ -34,6 +36,10 @@ public class WxCpUser implements Serializable { private String avatarMediaId; private Integer status; private Integer enable; + /** + * 别名;第三方仅通讯录应用可获取 + */ + private String alias; private Integer isLeader; /** * is_leader_in_dept. diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalApplyData.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalApplyData.java new file mode 100644 index 0000000000..26b37aea9c --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalApplyData.java @@ -0,0 +1,21 @@ +package me.chanjar.weixin.cp.bean.oa; + +import lombok.Data; +import me.chanjar.weixin.cp.bean.oa.applydata.Content; + +import java.io.Serializable; +import java.util.List; + +/** + * 审批申请数据 + * + * @author element + */ +@Data +public class WxCpApprovalApplyData implements Serializable { + + private static final long serialVersionUID = 4061352949894274704L; + + private List contents; + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalApplyer.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalApplyer.java new file mode 100644 index 0000000000..f648a6a915 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalApplyer.java @@ -0,0 +1,23 @@ +package me.chanjar.weixin.cp.bean.oa; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; + +/** + * 申请人信息 + * @author element + */ +@Data +public class WxCpApprovalApplyer extends WxCpOperator implements Serializable { + + private static final long serialVersionUID = -8974662568286821271L; + + /** + * 申请人所在部门id + */ + @SerializedName("partyid") + private String partyId; + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalComment.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalComment.java new file mode 100644 index 0000000000..8a70e3e6e9 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalComment.java @@ -0,0 +1,48 @@ +package me.chanjar.weixin.cp.bean.oa; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 审批申请备注信息 + * + * @author element + */ +@Data +public class WxCpApprovalComment implements Serializable { + + private static final long serialVersionUID = -5430367411926856292L; + + /** + * 备注人信息 + */ + private WxCpOperator commentUserInfo; + + /** + * 备注提交时间戳,Unix时间戳 + */ + @SerializedName("commenttime") + private Long commentTime; + + /** + * 备注id + */ + @SerializedName("commentid") + private String commentId; + + /** + * 备注文本内容 + */ + @SerializedName("commentcontent") + private String commentContent; + + /** + * 备注附件id,可能有多个 + */ + @SerializedName("media_id") + private List mediaIds; + +} 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/oa/WxCpApprovalDataResult.java similarity index 96% rename from weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpApprovalDataResult.java rename to weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalDataResult.java index 383c526568..b348c97a94 100644 --- 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/oa/WxCpApprovalDataResult.java @@ -1,4 +1,4 @@ -package me.chanjar.weixin.cp.bean; +package me.chanjar.weixin.cp.bean.oa; import com.google.gson.annotations.SerializedName; import lombok.Data; @@ -12,6 +12,7 @@ * @author Element * @date 2019-04-06 14:36 */ +@Deprecated @Data public class WxCpApprovalDataResult implements Serializable { private static final long serialVersionUID = -1046940445840716590L; diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalDetail.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalDetail.java new file mode 100644 index 0000000000..5021a57ee6 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalDetail.java @@ -0,0 +1,78 @@ +package me.chanjar.weixin.cp.bean.oa; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 审批申请详情 + * + * @author element + */ +@Data +public class WxCpApprovalDetail implements Serializable { + + private static final long serialVersionUID = 1353393306564207170L; + + /** + * 审批编号 + */ + @SerializedName("sp_no") + private String spNo; + + /** + * 审批申请类型名称(审批模板名称) + */ + @SerializedName("sp_name") + private String spName; + + /** + * 申请单状态:1-审批中;2-已通过;3-已驳回;4-已撤销;6-通过后撤销;7-已删除;10-已支付 + */ + @SerializedName("sp_status") + private WxCpSpStatus spStatus; + + /** + * 审批模板id。可在“获取审批申请详情”、“审批状态变化回调通知”中获得,也可在审批模板的模板编辑页面链接中获得。 + */ + @SerializedName("template_id") + private String templateId; + + /** + * 审批申请提交时间,Unix时间戳 + */ + @SerializedName("apply_time") + private Long applyTime; + + /** + * 申请人信息 + */ + private WxCpApprovalApplyer applyer; + + /** + * 审批流程信息,可能有多个审批节点 + */ + @SerializedName("sp_record") + private WxCpApprovalRecord spRecord; + + /** + * 抄送信息,可能有多个抄送节点 + */ + @SerializedName("notifyer") + private WxCpOperator notifyer; + + /** + * 审批申请数据 + */ + @SerializedName("apply_data") + private WxCpApprovalApplyData applyData; + + /** + * 审批申请备注信息,可能有多个备注节点 + */ + @SerializedName("comments") + private List comments; + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalDetailResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalDetailResult.java new file mode 100644 index 0000000000..eb8cd1c1ad --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalDetailResult.java @@ -0,0 +1,27 @@ +package me.chanjar.weixin.cp.bean.oa; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; + +/** + * 审批申请详情响应结果 + * + * @author element + */ +@Data +public class WxCpApprovalDetailResult implements Serializable { + + private static final long serialVersionUID = 3909779949756252918L; + + @SerializedName("errcode") + private Integer errCode; + + @SerializedName("errmsg") + private String errMsg; + + @SerializedName("info") + private WxCpApprovalDetail info; + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalInfo.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalInfo.java new file mode 100644 index 0000000000..b12d88baf8 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalInfo.java @@ -0,0 +1,29 @@ +package me.chanjar.weixin.cp.bean.oa; + +import java.io.Serializable; +import java.util.List; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +/** + * @author element + */ +@Data +public class WxCpApprovalInfo implements Serializable { + + private static final long serialVersionUID = 7387181805254287167L; + + @SerializedName("errcode") + private Integer errCode; + + @SerializedName("errmsg") + private String errMsg; + + @SerializedName("sp_no_list") + private List spNoList; + + @SerializedName("next_cursor") + private Integer nextCursor; + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalInfoQueryFilter.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalInfoQueryFilter.java new file mode 100644 index 0000000000..5271312081 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalInfoQueryFilter.java @@ -0,0 +1,61 @@ +package me.chanjar.weixin.cp.bean.oa; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +import java.io.Serializable; + +/** + *
+ *  批量获取审批单号的筛选条件,可对批量拉取的审批申请设置约束条件,支持设置多个条件
+ *  注意:
+ *  仅“部门”支持同时配置多个筛选条件。
+ *  不同类型的筛选条件之间为“与”的关系,同类型筛选条件之间为“或”的关系
+ * 
+ * + * @author element + */ +@Data +public class WxCpApprovalInfoQueryFilter implements Serializable { + + private static final long serialVersionUID = 3318064927980231802L; + + private WxCpApprovalInfoQueryFilter.KEY key; + + private Object value; + + public String toJson() { + return WxGsonBuilder.create().toJson(this); + } + + public static enum KEY { + + /** + * template_id - 模板类型/模板id; + */ + @SerializedName("template_id") + TEMPLATE_ID("template_id"), + /** + * creator - 申请人; + */ + @SerializedName("creator") + CREATOR("creator"), + /** + * department - 审批单提单者所在部门; + */ + @SerializedName("department") + DEPARTMENT("department"), + /** + * sp_status - 审批状态。 + */ + @SerializedName("sp_status") + SP_STATUS("sp_status"); + + private String value; + + private KEY(String value) { + this.value = value; + } + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalRecord.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalRecord.java new file mode 100644 index 0000000000..3325eaa4ac --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalRecord.java @@ -0,0 +1,26 @@ +package me.chanjar.weixin.cp.bean.oa; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 审批流程信息 + * @author element + */ +@Data +public class WxCpApprovalRecord implements Serializable { + + private static final long serialVersionUID = -327230786004105887L; + + @SerializedName("sp_status") + private WxCpRecordSpStatus status; + + @SerializedName("approverattr") + private WxCpApproverAttr approverAttr; + + private List details; + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalRecordDetail.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalRecordDetail.java new file mode 100644 index 0000000000..4c966c9d6f --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalRecordDetail.java @@ -0,0 +1,48 @@ +package me.chanjar.weixin.cp.bean.oa; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 审批节点详情 + * @author element + */ +@Data +public class WxCpApprovalRecordDetail implements Serializable { + + private static final long serialVersionUID = -9142079764088495301L; + + /** + * 分支审批人 + */ + @SerializedName("approver") + private WxCpOperator approver; + + /** + * 审批意见 + */ + @SerializedName("speech") + private String speech; + + /** + * 分支审批人审批状态 + */ + @SerializedName("sp_status") + private WxCpRecordSpStatus spStatus; + + /** + * 节点分支审批人审批操作时间戳,0表示未操作 + */ + @SerializedName("sptime") + private Long spTime; + + /** + * 节点分支审批人审批意见附件 + */ + @SerializedName("media_id") + private List mediaIds; + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApproverAttr.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApproverAttr.java new file mode 100644 index 0000000000..15e55f2f72 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApproverAttr.java @@ -0,0 +1,28 @@ +package me.chanjar.weixin.cp.bean.oa; + +import com.google.gson.annotations.SerializedName; + +/** + * 审批方式 + * + * @author element + */ +public enum WxCpApproverAttr { + /** + * 或签 + */ + @SerializedName("1") + ONE_SIGN(1), + /** + * 会签 + */ + @SerializedName("2") + ALL_SIGN(2); + + private Integer attr; + + private WxCpApproverAttr(Integer attr) { + this.attr = attr; + } + +} 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/oa/WxCpCheckinData.java similarity index 92% rename from weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpCheckinData.java rename to weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpCheckinData.java index d2fbf0f9d7..b76553ca63 100644 --- 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/oa/WxCpCheckinData.java @@ -1,4 +1,4 @@ -package me.chanjar.weixin.cp.bean; +package me.chanjar.weixin.cp.bean.oa; import com.google.gson.annotations.SerializedName; import lombok.Data; @@ -47,4 +47,8 @@ public class WxCpCheckinData implements Serializable { @SerializedName("mediaids") private List mediaIds; + + private Integer lat; + + private Integer lng; } 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/oa/WxCpCheckinOption.java similarity index 98% rename from weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpCheckinOption.java rename to weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpCheckinOption.java index c554e3d706..70cd4b202a 100644 --- 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/oa/WxCpCheckinOption.java @@ -1,4 +1,4 @@ -package me.chanjar.weixin.cp.bean; +package me.chanjar.weixin.cp.bean.oa; import com.google.gson.annotations.SerializedName; import lombok.Data; 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/oa/WxCpDialRecord.java similarity index 97% rename from weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpDialRecord.java rename to weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpDialRecord.java index 4f93b3decc..f3cf7d9881 100644 --- 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/oa/WxCpDialRecord.java @@ -1,4 +1,4 @@ -package me.chanjar.weixin.cp.bean; +package me.chanjar.weixin.cp.bean.oa; import com.google.gson.annotations.SerializedName; import lombok.Data; diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpOperator.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpOperator.java new file mode 100644 index 0000000000..063234dac2 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpOperator.java @@ -0,0 +1,24 @@ +package me.chanjar.weixin.cp.bean.oa; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; + + +/** + * 企业微信操作人 + * + * @author element + */ +@Data +public class WxCpOperator implements Serializable { + + private static final long serialVersionUID = 5797144853574346736L; + + /** + * 企业微信userid + */ + @SerializedName("userid") + private String userId; +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpRecordSpStatus.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpRecordSpStatus.java new file mode 100644 index 0000000000..206a0aa04e --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpRecordSpStatus.java @@ -0,0 +1,41 @@ +package me.chanjar.weixin.cp.bean.oa; + +import com.google.gson.annotations.SerializedName; + +/** + * 审批记录(节点)分支审批状态 + * + * 1-审批中;2-已同意;3-已驳回;4-已转审 + * + * @author element + */ +public enum WxCpRecordSpStatus { + + /** + * 审批中 + */ + @SerializedName("1") + AUDITING(1), + /** + * 已同意 + */ + @SerializedName("2") + PASSED(2), + /** + * 已驳回 + */ + @SerializedName("3") + REJECTED(3), + /** + * 已转审 + */ + @SerializedName("4") + TURNED(4); + + private Integer status; + + private WxCpRecordSpStatus(Integer status) { + this.status = status; + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpSpStatus.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpSpStatus.java new file mode 100644 index 0000000000..c3d8005f50 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpSpStatus.java @@ -0,0 +1,54 @@ +package me.chanjar.weixin.cp.bean.oa; + +import com.google.gson.annotations.SerializedName; + +/** + * 审批单状态 + * (1-审批中;2-已通过;3-已驳回;4-已撤销;6-通过后撤销;7-已删除;10-已支付) + * + * @author element + */ +public enum WxCpSpStatus { + + /** + * 审批中 + */ + @SerializedName("1") + AUDITING(1), + /** + * 已通过 + */ + @SerializedName("2") + PASSED(2), + /** + * 已驳回 + */ + @SerializedName("3") + REJECTED(3), + /** + * 已撤销 + */ + @SerializedName("4") + UNDONE(4), + /** + * 通过后撤销 + */ + @SerializedName("6") + PASS_UNDONE(6), + /** + * 已删除 + */ + @SerializedName("7") + DELETED(7), + /** + * 已支付 + */ + @SerializedName("10") + ALREADY_PAY(10); + + private Integer status; + + private WxCpSpStatus(Integer status) { + this.status = status; + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpTemplateResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpTemplateResult.java new file mode 100644 index 0000000000..df25aabf37 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpTemplateResult.java @@ -0,0 +1,35 @@ +package me.chanjar.weixin.cp.bean.oa; + +import com.google.gson.JsonObject; +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.cp.bean.oa.templatedata.TemplateContent; +import me.chanjar.weixin.cp.bean.oa.templatedata.TemplateControls; +import me.chanjar.weixin.cp.bean.oa.templatedata.TemplateTitle; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; + +/** + * 审批模板详情 + * + * @author gyv12345@163.com + */ +@Data +public class WxCpTemplateResult implements Serializable { + private static final long serialVersionUID = 6690547131189343887L; + + @SerializedName("errcode") + private Integer errCode; + + @SerializedName("errmsg") + private String errMsg; + + @SerializedName("template_names") + private List templateNames; + + @SerializedName("template_content") + private TemplateContent templateContent; + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/applydata/Content.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/applydata/Content.java new file mode 100644 index 0000000000..c4eb4ada0f --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/applydata/Content.java @@ -0,0 +1,24 @@ +package me.chanjar.weixin.cp.bean.oa.applydata; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * @author element + */ +@Data +public class Content implements Serializable { + private static final long serialVersionUID = 8456821731930526935L; + + private String control; + + private String id; + + @SerializedName("title") + private List titles; + + private ContentValue value; +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/applydata/ContentTitle.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/applydata/ContentTitle.java new file mode 100644 index 0000000000..24d5fafc2d --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/applydata/ContentTitle.java @@ -0,0 +1,18 @@ +package me.chanjar.weixin.cp.bean.oa.applydata; + +import lombok.Data; + +import java.io.Serializable; + +/** + * @author element + */ +@Data +public class ContentTitle implements Serializable { + + private static final long serialVersionUID = -4501999157383517007L; + + private String text; + private String lang; + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/applydata/ContentValue.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/applydata/ContentValue.java new file mode 100644 index 0000000000..7798338aa1 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/applydata/ContentValue.java @@ -0,0 +1,103 @@ +package me.chanjar.weixin.cp.bean.oa.applydata; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * @author element + */ +@Data +public class ContentValue implements Serializable { + + private static final long serialVersionUID = -5607678965965065261L; + + private String text; + + @SerializedName("new_number") + private Integer newNumber; + + @SerializedName("new_money") + private Integer newMoney; + + private ContentValue.Date date; + + private ContentValue.Selector selector; + + private List members; + + private List departments; + + private List files; + + private List children; + + @Data + public static class Date implements Serializable { + + private static final long serialVersionUID = -6181554080062231138L; + private String type; + + @SerializedName("s_timestamp") + private Long timestamp; + } + + @Data + public static class Selector implements Serializable { + + private static final long serialVersionUID = 7305458759126951773L; + private String type; + private List
+ * + * @param templateMessage 模版消息 + * @throws WxErrorException . */ + @Deprecated void sendTemplateMsg(WxMaTemplateMessage templateMessage) throws WxErrorException; - /** *
+   * 发送订阅消息
    * https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.send.html
    * 
- * 发送订阅消息 + * + * @param subscribeMessage 订阅消息 + * @throws WxErrorException . */ void sendSubscribeMsg(WxMaSubscribeMessage subscribeMessage) throws WxErrorException; - /** *
    * 下发小程序和公众号统一的服务消息
    * 详情请见: 下发小程序和公众号统一的服务消息
    * 接口url格式:https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token=ACCESS_TOKEN
    * 
+ * + * @param uniformMessage 消息 + * @throws WxErrorException . */ void sendUniformMsg(WxMaUniformMessage uniformMessage) throws WxErrorException; + + /** + *
+   *  创建被分享动态消息的 activity_id.
+   *  动态消息: https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/share/updatable-message.html
+   *
+   *  文档地址:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/updatable-message/updatableMessage.createActivityId.html
+   *  接口地址:GET https://api.weixin.qq.com/cgi-bin/message/wxopen/activityid/create?access_token=ACCESS_TOKEN
+   * 
+ * + * @return . + * @throws WxErrorException . + */ + JsonObject createUpdatableMessageActivityId() throws WxErrorException; + + /** + *
+   *  修改被分享的动态消息.
+   *  动态消息: https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/share/updatable-message.html
+   *
+   *  文档地址:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/updatable-message/updatableMessage.setUpdatableMsg.html
+   *  接口地址:POST https://api.weixin.qq.com/cgi-bin/message/wxopen/activityid/create?access_token=ACCESS_TOKEN
+   * 
+ * + * @param msg 动态消息 + * @throws WxErrorException . + */ + void setUpdatableMsg(WxMaUpdatableMsg msg) throws WxErrorException; } diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaSecCheckService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaSecCheckService.java index 4c821e0714..7733b77ed8 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaSecCheckService.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaSecCheckService.java @@ -32,6 +32,14 @@ public interface WxMaSecCheckService { */ boolean checkImage(File file) throws WxErrorException; + /** + * 校验一张图片是否含有违法违规内容 + * @param fileUrl 文件网络地址 + * @return 是否违规 + * @throws WxErrorException . + */ + boolean checkImage(String fileUrl) throws WxErrorException; + /** *
    * 检查一段文本是否含有违法违规内容。
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaService.java
index 52988f36e2..80150968dc 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
@@ -86,6 +86,11 @@ public interface WxMaService {
    */
   String post(String url, String postData) throws WxErrorException;
 
+  /**
+   * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的POST请求.
+   */
+  String post(String url, Object obj) throws WxErrorException;
+
   /**
    * 
    * Service没有实现某个API的时候,可以用这个,
@@ -158,6 +163,13 @@ public interface WxMaService {
    */
   WxMaTemplateService getTemplateService();
 
+  /**
+   * 返回订阅消息配置相关接口方法的实现类对象, 以方便调用其各个接口.
+   *
+   * @return WxMaSubscribeService
+   */
+  WxMaSubscribeService getSubscribeService();
+
   /**
    * 数据分析相关查询服务.
    *
@@ -224,5 +236,15 @@ public interface WxMaService {
    */
   RequestHttp getRequestHttp();
 
+  /**
+   * 获取物流助手接口服务对象
+   *
+   * @return
+   */
+  WxMaExpressService getExpressService();
 
+  /**
+   * 获取云开发接口服务对象
+   */
+  WxMaCloudService getCloudService();
 }
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaSubscribeService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaSubscribeService.java
new file mode 100644
index 0000000000..19e22a5eb6
--- /dev/null
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaSubscribeService.java
@@ -0,0 +1,153 @@
+package cn.binarywang.wx.miniapp.api;
+
+import cn.binarywang.wx.miniapp.bean.template.WxMaPubTemplateTitleListResult;
+import lombok.Data;
+import me.chanjar.weixin.common.error.WxErrorException;
+
+import java.util.List;
+
+/**
+ * 订阅消息类
+ *
+ * @author Binary Wang
+ * @date 2019-12-15
+ */
+public interface WxMaSubscribeService {
+  /**
+   * 获取模板标题下的关键词列表.
+   */
+  String GET_PUB_TEMPLATE_TITLE_LIST_URL = "https://api.weixin.qq.com/wxaapi/newtmpl/getpubtemplatetitles";
+
+  /**
+   * 获取模板标题下的关键词列表.
+   */
+  String GET_PUB_TEMPLATE_KEY_WORDS_BY_ID_URL = "https://api.weixin.qq.com/wxaapi/newtmpl/getpubtemplatekeywords";
+
+  /**
+   * 组合模板并添加至帐号下的个人模板库.
+   */
+  String TEMPLATE_ADD_URL = "https://api.weixin.qq.com/wxaapi/newtmpl/addtemplate";
+
+  /**
+   * 获取当前帐号下的个人模板列表.
+   */
+  String TEMPLATE_LIST_URL = "https://api.weixin.qq.com/wxaapi/newtmpl/gettemplate";
+
+  /**
+   * 删除帐号下的某个模板.
+   */
+  String TEMPLATE_DEL_URL = "https://api.weixin.qq.com/wxaapi/newtmpl/deltemplate";
+
+  /**
+   * 获取小程序账号的类目
+   */
+  String GET_CATEGORY_URL = "https://api.weixin.qq.com/wxaapi/newtmpl/getcategory";
+
+  /**
+   * 
+   * 获取帐号所属类目下的公共模板标题
+   *
+   * 详情请见: 获取帐号所属类目下的公共模板标题
+   * 接口url格式: https://api.weixin.qq.com/wxaapi/newtmpl/getpubtemplatetitles?access_token=ACCESS_TOKEN
+   * 
+ * + * @param ids 类目 id,多个用逗号隔开 + * @param limit 用于分页,表示拉取 limit 条记录。最大为 30。 + * @param start 用于分页,表示从 start 开始。从 0 开始计数。 + * @return . + * @throws WxErrorException . + */ + WxMaPubTemplateTitleListResult getPubTemplateTitleList(String[] ids, int start, int limit) throws WxErrorException; + + /** + *
+   * 获取模板库某个模板标题下关键词库
+   *
+   * 详情请见: 获取模板标题下的关键词列表
+   * 接口url格式: GET https://api.weixin.qq.com/wxaapi/newtmpl/getpubtemplatekeywords?access_token=ACCESS_TOKEN
+   * 
+ * + * @param id 模板标题 id,可通过接口获取 + * @return . + * @throws WxErrorException . + */ + List getPubTemplateKeyWordsById(String id) throws WxErrorException; + + /** + *
+   * 组合模板并添加至帐号下的个人模板库
+   *
+   * 详情请见: 获取小程序模板库标题列表
+   * 接口url格式: POST https://api.weixin.qq.com/wxaapi/newtmpl/addtemplate?access_token=ACCESS_TOKEN
+   * 
+ * + * @param id 模板标题 id,可通过接口获取,也可登录小程序后台查看获取 + * @param keywordIdList 模板关键词列表 + * @param sceneDesc 服务场景描述,15个字以内 + * @return 添加至帐号下的模板id,发送小程序订阅消息时所需 + * @throws WxErrorException . + */ + String addTemplate(String id, List keywordIdList, String sceneDesc) throws WxErrorException; + + /** + *
+   * 获取当前帐号下的个人模板列表
+   *
+   * 详情请见: 获取当前帐号下的个人模板列表
+   * 接口url格式: GET https://api.weixin.qq.com/wxaapi/newtmpl/gettemplate?access_token=ACCESS_TOKEN
+   * 
+ * + * @return . + * @throws WxErrorException . + */ + List getTemplateList() throws WxErrorException; + + /** + *
+   * 删除帐号下的某个模板
+   *
+   * 详情请见: 删除帐号下的个人模板
+   * 接口url格式: POST https://api.weixin.qq.com/wxaapi/newtmpl/deltemplate?access_token=ACCESS_TOKEN
+   * 
+ * + * @param templateId 要删除的模板id + * @return 删除是否成功 + * @throws WxErrorException . + */ + boolean delTemplate(String templateId) throws WxErrorException; + + /** + *
+   * 获取小程序账号的类目
+   * https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.getCategory.html
+   * GET https://api.weixin.qq.com/wxaapi/newtmpl/getcategory?access_token=ACCESS_TOKEN
+   * 
+ * + * @return . + * @throws WxErrorException . + */ + List getCategory() throws WxErrorException; + + @Data + class CategoryData { + int id; + String name; + } + + @Data + class TemplateInfo { + private String priTmplId; + private String title; + private String content; + private String example; + private int type; + } + + @Data + class PubTemplateKeyword { + private int kid; + private String name; + private String example; + private String rule; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaTemplateService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaTemplateService.java index 3973774f00..cbdd3c7585 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaTemplateService.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaTemplateService.java @@ -8,6 +8,10 @@ import java.util.List; +/** + * @author IOMan(lewis.lynn1006@gmail.com) + */ +@Deprecated public interface WxMaTemplateService { /** * 获取小程序模板库标题列表. diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaCloudServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaCloudServiceImpl.java new file mode 100644 index 0000000000..984185649c --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaCloudServiceImpl.java @@ -0,0 +1,178 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaCloudService; +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.cloud.*; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 云开发相关接口实现类. + * + * @author Binary Wang + * @date 2020-01-22 + */ +@Slf4j +@RequiredArgsConstructor +public class WxMaCloudServiceImpl implements WxMaCloudService { + private static final JsonParser JSON_PARSER = new JsonParser(); + private final WxMaService wxMaService; + + @Override + public String invokeCloudFunction(String env, String name, String body) throws WxErrorException { + final String response = this.wxMaService.post(String.format(INVOKE_CLOUD_FUNCTION_URL, env, name), body); + return JSON_PARSER.parse(response).getAsJsonObject().get("resp_data").getAsString(); + } + + @Override + public JsonArray databaseAdd(String env, String query) throws WxErrorException { + String response = this.wxMaService.post(DATABASE_ADD_URL, ImmutableMap.of("env", env, "query", query)); + return JSON_PARSER.parse(response).getAsJsonObject().get("id_list").getAsJsonArray(); + } + + @Override + public int databaseDelete(String env, String query) throws WxErrorException { + String response = this.wxMaService.post(DATABASE_DELETE_URL, ImmutableMap.of("env", env, "query", query)); + return JSON_PARSER.parse(response).getAsJsonObject().get("deleted").getAsInt(); + } + + @Override + public WxCloudDatabaseUpdateResult databaseUpdate(String env, String query) throws WxErrorException { + String response = this.wxMaService.post(DATABASE_UPDATE_URL, ImmutableMap.of("env", env, "query", query)); + return WxGsonBuilder.create().fromJson(response, WxCloudDatabaseUpdateResult.class); + } + + @Override + public WxCloudDatabaseQueryResult databaseQuery(String env, String query) throws WxErrorException { + String response = this.wxMaService.post(DATABASE_QUERY_URL, ImmutableMap.of("env", env, "query", query)); + return WxGsonBuilder.create().fromJson(response, WxCloudDatabaseQueryResult.class); + } + + @Override + public JsonArray databaseAggregate(String env, String query) throws WxErrorException { + String response = this.wxMaService.post(DATABASE_AGGREGATE_URL, ImmutableMap.of("env", env, "query", query)); + return JSON_PARSER.parse(response).getAsJsonObject().get("data").getAsJsonArray(); + } + + @Override + public Long databaseCount(String env, String query) throws WxErrorException { + String response = this.wxMaService.post(DATABASE_COUNT_URL, ImmutableMap.of("env", env, "query", query)); + return JSON_PARSER.parse(response).getAsJsonObject().get("count").getAsLong(); + } + + @Override + public void updateIndex(String env, String collectionName, List createIndexes, + List dropIndexNames) throws WxErrorException { + List> dropIndexes = Lists.newArrayList(); + if (dropIndexNames != null) { + for (String index : dropIndexNames) { + dropIndexes.add(ImmutableMap.of("name", index)); + } + } + + this.wxMaService.post(UPDATE_INDEX_URL, ImmutableMap.of("env", env, + "collection_name", collectionName, "create_indexes", createIndexes, "drop_indexes", dropIndexes)); + } + + @Override + public Long databaseMigrateImport(String env, String collectionName, String filePath, int fileType, + boolean stopOnError, int conflictMode) throws WxErrorException { + JsonObject params = new JsonObject(); + params.addProperty("env", env); + params.addProperty("collection_name", collectionName); + params.addProperty("file_path", filePath); + params.addProperty("file_type", fileType); + params.addProperty("stop_on_error", stopOnError); + params.addProperty("conflict_mode", conflictMode); + + String response = this.wxMaService.post(DATABASE_MIGRATE_IMPORT_URL, params.toString()); + return JSON_PARSER.parse(response).getAsJsonObject().get("job_id").getAsLong(); + } + + @Override + public Long databaseMigrateExport(String env, String filePath, int fileType, String query) throws WxErrorException { + JsonObject params = new JsonObject(); + params.addProperty("env", env); + params.addProperty("file_path", filePath); + params.addProperty("file_type", fileType); + params.addProperty("query", query); + + String response = this.wxMaService.post(DATABASE_MIGRATE_EXPORT_URL, params.toString()); + return JSON_PARSER.parse(response).getAsJsonObject().get("job_id").getAsLong(); + } + + @Override + public WxCloudCloudDatabaseMigrateQueryInfoResult databaseMigrateQueryInfo(String env, Long jobId) throws WxErrorException { + String response = this.wxMaService.post(DATABASE_MIGRATE_QUERY_INFO_URL, ImmutableMap.of("env", env, "job_id", jobId)); + return WxGsonBuilder.create().fromJson(response, WxCloudCloudDatabaseMigrateQueryInfoResult.class); + } + + @Override + public WxCloudUploadFileResult uploadFile(String env, String path) throws WxErrorException { + String response = this.wxMaService.post(UPLOAD_FILE_URL, ImmutableMap.of("env", env, "path", path)); + return WxGsonBuilder.create().fromJson(response, WxCloudUploadFileResult.class); + } + + @Override + public WxCloudBatchDownloadFileResult batchDownloadFile(String env, String[] fileIds, long[] maxAges) throws WxErrorException { + List> fileList = Lists.newArrayList(); + int i = 0; + for (String fileId : fileIds) { + fileList.add(ImmutableMap.of("fileid", fileId, "max_age", (Serializable) maxAges[i++])); + } + + String response = this.wxMaService.post(GET_QCLOUD_TOKEN_URL, ImmutableMap.of("env", env, "file_list", fileList)); + return WxGsonBuilder.create().fromJson(response, WxCloudBatchDownloadFileResult.class); + } + + @Override + public WxCloudBatchDeleteFileResult batchDeleteFile(String env, String[] fileIds) throws WxErrorException { + String response = this.wxMaService.post(BATCH_DELETE_FILE_URL, ImmutableMap.of("env", env, "fileid_list", fileIds)); + return WxGsonBuilder.create().fromJson(response, WxCloudBatchDeleteFileResult.class); + } + + @Override + public WxCloudGetQcloudTokenResult getQcloudToken(long lifeSpan) throws WxErrorException { + String response = this.wxMaService.post(GET_QCLOUD_TOKEN_URL, ImmutableMap.of("lifespan", lifeSpan)); + return WxGsonBuilder.create().fromJson(response, WxCloudGetQcloudTokenResult.class); + } + + @Override + public void databaseCollectionAdd(String env, String collectionName) throws WxErrorException { + this.wxMaService.post(DATABASE_COLLECTION_ADD_URL, ImmutableMap.of("env", env, "collection_name", collectionName)); + } + + @Override + public void databaseCollectionDelete(String env, String collectionName) throws WxErrorException { + this.wxMaService.post(DATABASE_COLLECTION_DELETE_URL, ImmutableMap.of("env", env, "collection_name", collectionName)); + } + + @Override + public WxCloudDatabaseCollectionGetResult databaseCollectionGet(String env, Long limit, Long offset) throws WxErrorException { + Map params = new HashMap<>(2); + params.put("env", env); + if (limit != null) { + params.put("limit", limit); + } + + if (offset != null) { + params.put("offset", offset); + } + + String response = this.wxMaService.post(DATABASE_COLLECTION_GET_URL, params); + return WxGsonBuilder.create().fromJson(response, WxCloudDatabaseCollectionGetResult.class); + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaExpressServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaExpressServiceImpl.java new file mode 100644 index 0000000000..21d0bfe0b0 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaExpressServiceImpl.java @@ -0,0 +1,98 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaExpressService; +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.express.WxMaExpressAccount; +import cn.binarywang.wx.miniapp.bean.express.WxMaExpressDelivery; +import cn.binarywang.wx.miniapp.bean.express.WxMaExpressPath; +import cn.binarywang.wx.miniapp.bean.express.WxMaExpressPrinter; +import cn.binarywang.wx.miniapp.bean.express.request.*; +import cn.binarywang.wx.miniapp.bean.express.result.WxMaExpressOrderInfoResult; +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import lombok.AllArgsConstructor; +import me.chanjar.weixin.common.error.WxErrorException; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author xiaoyu + * @since 2019-11-26 + */ +@AllArgsConstructor +public class WxMaExpressServiceImpl implements WxMaExpressService { + + private WxMaService wxMaService; + + @Override + public List getAllDelivery() throws WxErrorException { + String responseContent = this.wxMaService.get(ALL_DELIVERY_URL, null); + return WxMaExpressDelivery.fromJson(responseContent); + } + + @Override + public List getAllAccount() throws WxErrorException { + String responseContent = this.wxMaService.get(ALL_ACCOUNT_URL, null); + return WxMaExpressAccount.fromJsonList(responseContent); + } + + @Override + public void bindAccount(WxMaExpressBindAccountRequest wxMaExpressBindAccountRequest) throws WxErrorException { + this.wxMaService.post(BIND_ACCOUNT_URL,wxMaExpressBindAccountRequest.toJson()); + } + + @Override + public Integer getQuota(WxMaExpressBindAccountRequest wxMaExpressBindAccountRequest) throws WxErrorException { + String responseContent = this.wxMaService.post(GET_QUOTA_URL,wxMaExpressBindAccountRequest.toJson()); + WxMaExpressAccount account = WxMaExpressAccount.fromJson(responseContent); + return account.getQuotaNum(); + } + + @Override + public void updatePrinter(WxMaExpressPrinterUpdateRequest wxMaExpressPrinterUpdateRequest) throws WxErrorException { + this.wxMaService.post(UPDATE_PRINTER_URL,wxMaExpressPrinterUpdateRequest.toJson()); + } + + @Override + public WxMaExpressPrinter getPrinter() throws WxErrorException { + String responseContent = this.wxMaService.get(GET_PRINTER_URL, null); + return WxMaExpressPrinter.fromJson(responseContent); + } + + @Override + public WxMaExpressOrderInfoResult addOrder(WxMaExpressAddOrderRequest wxMaExpressAddOrderRequest) throws WxErrorException { + String responseContent = this.wxMaService.post(ADD_ORDER_URL,wxMaExpressAddOrderRequest.toJson()); + return WxMaExpressOrderInfoResult.fromJson(responseContent); + } + + @Override + public List batchGetOrder(List requests) throws WxErrorException { + Map param = new HashMap<>(1); + param.put("order_list", requests); + String responseContent = this.wxMaService.post(BATCH_GET_ORDER_URL, WxMaGsonBuilder.create().toJson(param)); + return WxMaExpressOrderInfoResult.toList(responseContent); + } + + @Override + public void cancelOrder(WxMaExpressGetOrderRequest wxMaExpressGetOrderRequest) throws WxErrorException { + this.wxMaService.post(CANCEL_ORDER_URL,wxMaExpressGetOrderRequest.toJson()); + } + + @Override + public WxMaExpressOrderInfoResult getOrder(WxMaExpressGetOrderRequest wxMaExpressGetOrderRequest) throws WxErrorException { + String responseContent = this.wxMaService.post(GET_ORDER_URL,wxMaExpressGetOrderRequest.toJson()); + return WxMaExpressOrderInfoResult.fromJson(responseContent); + } + + @Override + public WxMaExpressPath getPath(WxMaExpressGetOrderRequest wxMaExpressGetOrderRequest) throws WxErrorException { + String responseContent = this.wxMaService.post(GET_PATH_URL,wxMaExpressGetOrderRequest.toJson()); + return WxMaExpressPath.fromJson(responseContent); + } + + @Override + public void testUpdateOrder(WxMaExpressTestUpdateOrderRequest wxMaExpressTestUpdateOrderRequest) throws WxErrorException { + this.wxMaService.post(TEST_UPDATE_ORDER_URL,wxMaExpressTestUpdateOrderRequest.toJson()); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaJsapiServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaJsapiServiceImpl.java index 83dbc929b4..ed5b375e24 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaJsapiServiceImpl.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaJsapiServiceImpl.java @@ -51,7 +51,7 @@ public String getCardApiTicket(boolean forceRefresh) throws WxErrorException { } finally { lock.unlock(); } - return this.wxMaService.getWxMaConfig().getJsapiTicket(); + return this.wxMaService.getWxMaConfig().getCardApiTicket(); } @Override diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaMsgServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaMsgServiceImpl.java index 26ebd74bc6..c9e229e9ef 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaMsgServiceImpl.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaMsgServiceImpl.java @@ -2,11 +2,9 @@ import cn.binarywang.wx.miniapp.api.WxMaMsgService; import cn.binarywang.wx.miniapp.api.WxMaService; -import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage; -import cn.binarywang.wx.miniapp.bean.WxMaSubscribeMessage; -import cn.binarywang.wx.miniapp.bean.WxMaTemplateMessage; -import cn.binarywang.wx.miniapp.bean.WxMaUniformMessage; +import cn.binarywang.wx.miniapp.bean.*; import cn.binarywang.wx.miniapp.constant.WxMaConstants; +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import lombok.AllArgsConstructor; @@ -28,11 +26,6 @@ public boolean sendKefuMsg(WxMaKefuMessage message) throws WxErrorException { return responseContent != null; } - /** - *
-   * 小程序模板消息接口将于2020年1月10日下线,开发者可使用订阅消息功能
-   * 
- */ @Override public void sendTemplateMsg(WxMaTemplateMessage templateMessage) throws WxErrorException { String responseContent = this.wxMaService.post(TEMPLATE_MSG_SEND_URL, templateMessage.toJson()); @@ -60,4 +53,15 @@ public void sendUniformMsg(WxMaUniformMessage uniformMessage) throws WxErrorExce } } + @Override + public JsonObject createUpdatableMessageActivityId() throws WxErrorException { + final String responseContent = this.wxMaService.get(ACTIVITY_ID_CREATE_URL, null); + return JSON_PARSER.parse(responseContent).getAsJsonObject(); + } + + @Override + public void setUpdatableMsg(WxMaUpdatableMsg msg) throws WxErrorException { + this.wxMaService.post(UPDATABLE_MSG_SEND_URL, WxMaGsonBuilder.create().toJson(msg)); + } + } diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaSecCheckServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaSecCheckServiceImpl.java index c69ca5eb11..1dd58bb698 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaSecCheckServiceImpl.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaSecCheckServiceImpl.java @@ -4,11 +4,16 @@ import cn.binarywang.wx.miniapp.api.WxMaService; import cn.binarywang.wx.miniapp.bean.WxMaMediaAsyncCheckResult; import com.google.gson.JsonObject; -import java.io.File; import lombok.AllArgsConstructor; import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; +import me.chanjar.weixin.common.error.WxError; import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; +import org.apache.commons.io.FileUtils; + +import java.io.File; +import java.io.IOException; +import java.net.URL; /** *
@@ -20,17 +25,28 @@
  */
 @AllArgsConstructor
 public class WxMaSecCheckServiceImpl implements WxMaSecCheckService {
-
   private WxMaService service;
 
   @Override
   public boolean checkImage(File file) throws WxErrorException {
-    //这里只是借用MediaUploadRequestExecutor,并不使用其返回值WxMediaUploadResult
     WxMediaUploadResult result = this.service.execute(MediaUploadRequestExecutor
       .create(this.service.getRequestHttp()), IMG_SEC_CHECK_URL, file);
     return result != null;
   }
 
+  @Override
+  public boolean checkImage(String fileUrl) throws WxErrorException {
+    File file = new File(FileUtils.getTempDirectory(), System.currentTimeMillis() + ".tmp");
+    try {
+      URL url = new URL(fileUrl);
+      FileUtils.copyURLToFile(url, file);
+    } catch (IOException e) {
+      throw new WxErrorException(WxError.builder().errorCode(-1).errorMsg("文件地址读取异常").build(), e);
+    }
+    
+    return this.checkImage(file);
+  }
+
   @Override
   public boolean checkMessage(String msgString) throws WxErrorException {
     JsonObject jsonObject = new JsonObject();
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 44ba6258d7..537364f288 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
@@ -16,6 +16,7 @@
 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 me.chanjar.weixin.common.util.json.WxGsonBuilder;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.http.HttpHost;
 import org.apache.http.client.config.RequestConfig;
@@ -54,6 +55,9 @@ public class WxMaServiceImpl implements WxMaService, RequestHttpBinary Wang
+ * @date 2019-12-15
+ */
+@AllArgsConstructor
+public class WxMaSubscribeServiceImpl implements WxMaSubscribeService {
+  private WxMaService wxMaService;
+
+  @Override
+  public WxMaPubTemplateTitleListResult getPubTemplateTitleList(String[] ids, int start, int limit) throws WxErrorException {
+    ImmutableMap params = ImmutableMap.of("ids", StringUtils.join(ids, ","),
+      "start", start, "limit", limit);
+    String responseText = this.wxMaService.get(GET_PUB_TEMPLATE_TITLE_LIST_URL,
+      Joiner.on("&").withKeyValueSeparator("=").join(params));
+    return WxMaPubTemplateTitleListResult.fromJson(responseText);
+  }
+
+  @Override
+  public List getPubTemplateKeyWordsById(String id) throws WxErrorException {
+    String responseText = this.wxMaService.get(GET_PUB_TEMPLATE_KEY_WORDS_BY_ID_URL,
+      Joiner.on("&").withKeyValueSeparator("=").join(ImmutableMap.of("tid", id)));
+    return WxMaGsonBuilder.create().fromJson(new JsonParser().parse(responseText).getAsJsonObject()
+      .getAsJsonArray("data"), new TypeToken>() {
+    }.getType());
+  }
+
+  @Override
+  public String addTemplate(String id, List keywordIdList, String sceneDesc) throws WxErrorException {
+    String responseText = this.wxMaService.post(TEMPLATE_ADD_URL, ImmutableMap.of("tid", id,
+      "kidList", keywordIdList.toArray(),
+      "sceneDesc", sceneDesc));
+    return new JsonParser().parse(responseText).getAsJsonObject().get("priTmplId").getAsString();
+  }
+
+  @Override
+  public List getTemplateList() throws WxErrorException {
+    String responseText = this.wxMaService.get(TEMPLATE_LIST_URL, null);
+    return WxMaGsonBuilder.create().fromJson(new JsonParser().parse(responseText).getAsJsonObject()
+      .getAsJsonArray("data"), new TypeToken>() {
+    }.getType());
+  }
+
+  @Override
+  public boolean delTemplate(String templateId) throws WxErrorException {
+    this.wxMaService.post(TEMPLATE_DEL_URL, ImmutableMap.of("priTmplId", templateId));
+    return true;
+  }
+
+  @Override
+  public List getCategory() throws WxErrorException {
+    String responseText = this.wxMaService.get(GET_CATEGORY_URL, null);
+    return WxMaGsonBuilder.create().fromJson(new JsonParser().parse(responseText).getAsJsonObject()
+      .getAsJsonArray("data"), new TypeToken>() {
+    }.getType());
+  }
+}
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaMediaAsyncCheckResult.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaMediaAsyncCheckResult.java
index 807f807652..e7fda61a02 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaMediaAsyncCheckResult.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaMediaAsyncCheckResult.java
@@ -2,10 +2,15 @@
 
 import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder;
 import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+
 import java.io.Serializable;
 
+/**
+ * @author borisbao
+ */
+@Data
 public class WxMaMediaAsyncCheckResult implements Serializable {
-
   private static final long serialVersionUID = 3928132365399916183L;
 
   /**
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaMessage.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaMessage.java
index 1152820df2..483dd7c54a 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaMessage.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaMessage.java
@@ -1,12 +1,5 @@
 package cn.binarywang.wx.miniapp.bean;
 
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.Serializable;
-import java.nio.charset.StandardCharsets;
-
-import org.apache.commons.io.IOUtils;
-
 import cn.binarywang.wx.miniapp.config.WxMaConfig;
 import cn.binarywang.wx.miniapp.util.crypt.WxMaCryptUtils;
 import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder;
@@ -16,6 +9,12 @@
 import com.thoughtworks.xstream.annotations.XStreamConverter;
 import lombok.Data;
 import me.chanjar.weixin.common.util.xml.XStreamCDataConverter;
+import org.apache.commons.io.IOUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Serializable;
+import java.nio.charset.StandardCharsets;
 
 /**
  * @author Binary Wang
@@ -108,6 +107,34 @@ public class WxMaMessage implements Serializable {
   @XStreamConverter(value = XStreamCDataConverter.class)
   private String sessionFrom;
 
+  /**
+   * 以下是异步校验图片/音频是否含有违法违规内容的异步检测结果推送报文中的参数
+   */
+  @SerializedName("isrisky")
+  @XStreamAlias("isrisky")
+  @XStreamConverter(value = XStreamCDataConverter.class)
+  private String isRisky;
+
+  @SerializedName("extra_info_json")
+  @XStreamAlias("extra_info_json")
+  @XStreamConverter(value = XStreamCDataConverter.class)
+  private String extraInfoJson;
+
+  @SerializedName("appid")
+  @XStreamAlias("appid")
+  @XStreamConverter(value = XStreamCDataConverter.class)
+  private String appid;
+
+  @SerializedName("trace_id")
+  @XStreamAlias("trace_id")
+  @XStreamConverter(value = XStreamCDataConverter.class)
+  private String traceId;
+
+  @SerializedName("status_code")
+  @XStreamAlias("status_code")
+  @XStreamConverter(value = XStreamCDataConverter.class)
+  private String statusCode;
+
   public static WxMaMessage fromXml(String xml) {
     return XStreamTransformer.fromXml(WxMaMessage.class, xml);
   }
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaSubscribeData.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaSubscribeData.java
deleted file mode 100644
index 048e6dae89..0000000000
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaSubscribeData.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package cn.binarywang.wx.miniapp.bean;
-
-import lombok.Data;
-import lombok.NoArgsConstructor;
-
-/**
- * 
- * 参考文档 https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.send.html
- * 
- */ -@Data -@NoArgsConstructor -public class WxMaSubscribeData { - private String name; - private String value; - private String color; - - public WxMaSubscribeData(String name, String value) { - this.name = name; - this.value = value; - } - - public WxMaSubscribeData(String name, String value, String color) { - this.name = name; - this.value = value; - this.color = color; - } - - -} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaSubscribeMessage.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaSubscribeMessage.java index a43fd5b47f..791687b6c0 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaSubscribeMessage.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaSubscribeMessage.java @@ -1,5 +1,6 @@ package cn.binarywang.wx.miniapp.bean; +import cn.binarywang.wx.miniapp.constant.WxMaConstants; import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; import lombok.*; @@ -10,6 +11,8 @@ /** * 订阅消息. * https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.send.html + * + * @author S */ @Getter @Setter @@ -17,7 +20,6 @@ @AllArgsConstructor @Builder public class WxMaSubscribeMessage implements Serializable { - private static final long serialVersionUID = 6846729898251286686L; /** @@ -58,13 +60,23 @@ public class WxMaSubscribeMessage implements Serializable { * 描述: 模板内容,不填则下发空模板 *
*/ - private List data; + private List data; + /** + * 跳转小程序类型:developer为开发版;trial为体验版;formal为正式版;默认为正式版 + */ + private String miniprogramState = WxMaConstants.MiniprogramState.FORMAL; + + /** + * 进入小程序查看的语言类型,支持zh_CN(简体中文)、en_US(英文)、zh_HK(繁体中文)、zh_TW(繁体中文),默认为zh_CN + */ + private String lang = WxMaConstants.MiniprogramLang.ZH_CN; - public WxMaSubscribeMessage addData(WxMaSubscribeData datum) { + public WxMaSubscribeMessage addData(Data datum) { if (this.data == null) { this.data = new ArrayList<>(); } + this.data.add(datum); return this; @@ -74,4 +86,14 @@ public String toJson() { return WxMaGsonBuilder.create().toJson(this); } + @lombok.Data + @NoArgsConstructor + @AllArgsConstructor + public static class Data implements Serializable { + private static final long serialVersionUID = 1L; + + private String name; + private String value; + } + } diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaUpdatableMsg.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaUpdatableMsg.java new file mode 100644 index 0000000000..98429b850c --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaUpdatableMsg.java @@ -0,0 +1,76 @@ +package cn.binarywang.wx.miniapp.bean; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.List; + +/** + * 动态消息. + * + * @author Binary Wang + * @date 2020-02-17 + */ +@Data +@Accessors(chain = true) +public class WxMaUpdatableMsg implements Serializable { + private static final long serialVersionUID = 6231957192034798165L; + + /** + * 动态消息的 ID,通过 updatableMessage.createActivityId 接口获取 + */ + @SerializedName("activity_id") + private String activityId; + + /** + * 动态消息修改后的状态 + * 0 未开始 + * 1 已开始 + */ + @SerializedName("target_state") + private Integer targetState; + + /** + * 动态消息对应的模板信息 + */ + @SerializedName("template_info") + private TemplateInfo templateInfo; + + @Data + @Accessors(chain = true) + public static class TemplateInfo implements Serializable { + private static final long serialVersionUID = -9218473401759062841L; + + /** + * 模板中需要修改的参数 + */ + @SerializedName("parameter_list") + private List parameterList; + } + + @Data + @Accessors(chain = true) + public static class Parameter implements Serializable { + private static final long serialVersionUID = 7444716050341038046L; + + /** + * 要修改的参数名 + *
+     * 合法值:
+     * member_count	target_state = 0 时必填,文字内容模板中 member_count 的值
+     * room_limit	target_state = 0 时必填,文字内容模板中 room_limit 的值
+     * path	target_state = 1 时必填,点击「进入」启动小程序时使用的路径。对于小游戏,没有页面的概念,可以用于传递查询字符串(query),如 "?foo=bar"
+     * version_type	target_state = 1 时必填,点击「进入」启动小程序时使用的版本。
+     * 有效参数值为:develop(开发版),trial(体验版),release(正式版)
+     * 
+ */ + private String name; + + /** + * 修改后的参数值 + */ + private String value; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudBatchDeleteFileResult.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudBatchDeleteFileResult.java new file mode 100644 index 0000000000..52a70037f7 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudBatchDeleteFileResult.java @@ -0,0 +1,44 @@ +package cn.binarywang.wx.miniapp.bean.cloud; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 文件删除结果. + * + * @author Binary Wang + * @date 2020-01-27 + */ +@Data +public class WxCloudBatchDeleteFileResult implements Serializable { + private static final long serialVersionUID = -1133274298839868115L; + + @SerializedName("delete_list") + private List fileList; + + @Data + public static class FileDownloadInfo implements Serializable { + private static final long serialVersionUID = 5812969045277862211L; + + /** + * fileid string 文件ID + */ + @SerializedName("fileid") + private String fileId; + + /** + * status number 状态码 + */ + @SerializedName("status") + private Integer status; + + /** + * errmsg string 该文件错误信息 + */ + @SerializedName("errmsg") + private String errMsg; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudBatchDownloadFileResult.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudBatchDownloadFileResult.java new file mode 100644 index 0000000000..1519e72318 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudBatchDownloadFileResult.java @@ -0,0 +1,50 @@ +package cn.binarywang.wx.miniapp.bean.cloud; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 获取文件下载链接结果. + * + * @author Binary Wang + * @date 2020-01-27 + */ +@Data +public class WxCloudBatchDownloadFileResult implements Serializable { + private static final long serialVersionUID = 646423661372964402L; + + @SerializedName("file_list") + private List fileList; + + @Data + public static class FileDownloadInfo implements Serializable { + private static final long serialVersionUID = 5812969045277862211L; + + /** + * fileid string 文件ID + */ + @SerializedName("fileid") + private String fileId; + + /** + * download_url string 下载链接 + */ + @SerializedName("download_url") + private String downloadUrl; + + /** + * status number 状态码 + */ + @SerializedName("status") + private Integer status; + + /** + * errmsg string 该文件错误信息 + */ + @SerializedName("errmsg") + private String errMsg; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudCloudDatabaseMigrateQueryInfoResult.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudCloudDatabaseMigrateQueryInfoResult.java new file mode 100644 index 0000000000..b2639c5545 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudCloudDatabaseMigrateQueryInfoResult.java @@ -0,0 +1,47 @@ +package cn.binarywang.wx.miniapp.bean.cloud; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; + +/** + * 云开发数据库迁移状态查询结果. + * + * @author Binary Wang + * @date 2020-01-26 + */ +@Data +public class WxCloudCloudDatabaseMigrateQueryInfoResult implements Serializable { + private static final long serialVersionUID = 2014197503355968243L; + + /** + * status string 导出状态 + */ + private String status; + + /** + * record_success number 导出成功记录数 + */ + @SerializedName("record_success") + private Integer recordSuccess; + + /** + * record_fail number 导出失败记录数 + */ + @SerializedName("record_fail") + private Integer recordFail; + + /** + * err_msg string 导出错误信息 + */ + @SerializedName("err_msg") + private String errMsg; + + /** + * file_url string 导出文件下载地址 + */ + @SerializedName("file_url") + private String fileUrl; + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudDatabaseCollectionGetResult.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudDatabaseCollectionGetResult.java new file mode 100644 index 0000000000..347762b4c1 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudDatabaseCollectionGetResult.java @@ -0,0 +1,86 @@ +package cn.binarywang.wx.miniapp.bean.cloud; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; + +/** + * 云开发获取集合接口的结果. + * + * @author Binary Wang + * @date 2020-01-28 + */ +@Data +public class WxCloudDatabaseCollectionGetResult implements Serializable { + private static final long serialVersionUID = 3702855196387039823L; + + /** + * 分页信息 + */ + private WxCloudDatabaseQueryResult.Pager pager; + + /** + * 查询结果 + */ + private CollectionInfo[] collections; + + @Data + public static class CollectionInfo implements Serializable { + private static final long serialVersionUID = -3280126948752330438L; + + /** + * name string 集合名 + */ + @SerializedName("name") + private String name; + + /** + * count number 表中文档数量 + */ + @SerializedName("count") + private Long count; + + /** + * size number 表的大小(即表中文档总大小),单位:字节 + */ + @SerializedName("size") + private Long size; + + /** + * index_count number 索引数量 + */ + @SerializedName("index_count") + private Long indexCount; + + /** + * index_size number 索引占用大小,单位:字节 + */ + @SerializedName("index_size") + private Long indexSize; + } + + @Data + public static class Pager implements Serializable { + private static final long serialVersionUID = 5045727687673687839L; + + /** + * Offset number 偏移 + */ + @SerializedName("Offset") + private Long offset; + + /** + * Limit number 单次查询限制 + */ + @SerializedName("Limit") + private Long limit; + + /** + * Total number 符合查询条件的记录总数 + */ + @SerializedName("Total") + private Long total; + + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudDatabaseCreateIndexRequest.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudDatabaseCreateIndexRequest.java new file mode 100644 index 0000000000..fbdba099f7 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudDatabaseCreateIndexRequest.java @@ -0,0 +1,59 @@ +package cn.binarywang.wx.miniapp.bean.cloud; + +import lombok.Builder; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.List; + +/** + * 云开发新增索引的请求对象. + * + * @author Binary Wang + * @date 2020-01-26 + */ +@Accessors(chain = true) +@Data +public class WxCloudDatabaseCreateIndexRequest implements Serializable { + private static final long serialVersionUID = -8308393731157121109L; + + /** + * name string 是 索引名 + */ + private String name; + + /** + * unique boolean 是 是否唯一 + */ + private boolean unique; + + /** + * keys Array. 是 索引字段 + */ + private List keys; + + @Data + @Accessors(chain = true) + public static class IndexKey implements Serializable { + private static final long serialVersionUID = -252641130547960325L; + + /** + * name string 是 字段名 + */ + private String name; + + /** + * direction string 是 字段排序 + *
+     *   direction 的合法值
+     * 值	说明
+     * "1"	升序
+     * "-1"	降序
+     * "2dsphere"	地理位置
+     *
+     * 
+ */ + private String direction; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudDatabaseQueryResult.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudDatabaseQueryResult.java new file mode 100644 index 0000000000..19ca4dce1f --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudDatabaseQueryResult.java @@ -0,0 +1,51 @@ +package cn.binarywang.wx.miniapp.bean.cloud; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; + +/** + * 云开发数据库查询记录接口请求结果. + * + * @author Binary Wang + * @date 2020-01-26 + */ +@Data +public class WxCloudDatabaseQueryResult implements Serializable { + private static final long serialVersionUID = 8291820029137872536L; + + /** + * 分页信息 + */ + private Pager pager; + + /** + * 查询结果 + */ + private String[] data; + + @Data + public static class Pager implements Serializable{ + private static final long serialVersionUID = 8556239063823985674L; + + /** + * Offset number 偏移 + */ + @SerializedName("Offset") + private Long offset; + + /** + * Limit number 单次查询限制 + */ + @SerializedName("Limit") + private Long limit; + + /** + * Total number 符合查询条件的记录总数 + */ + @SerializedName("Total") + private Long total; + + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudDatabaseUpdateResult.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudDatabaseUpdateResult.java new file mode 100644 index 0000000000..000774132f --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudDatabaseUpdateResult.java @@ -0,0 +1,32 @@ +package cn.binarywang.wx.miniapp.bean.cloud; + +import lombok.Data; + +import java.io.Serializable; + +/** + * 云开发数据库更新记录接口请求结果. + * + * @author Binary Wang + * @date 2020-01-26 + */ +@Data +public class WxCloudDatabaseUpdateResult implements Serializable { + private static final long serialVersionUID = -3905429931117999004L; + + /** + * matched number 更新条件匹配到的结果数 + */ + private Long matched; + + /** + * modified number 修改的记录数,注意:使用set操作新插入的数据不计入修改数目 + */ + private Long modified; + + /** + * id string 新插入记录的id,注意:只有使用set操作新插入数据时这个字段会有值 + */ + private String id; + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudGetQcloudTokenResult.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudGetQcloudTokenResult.java new file mode 100644 index 0000000000..f2b8bf0a1d --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudGetQcloudTokenResult.java @@ -0,0 +1,41 @@ +package cn.binarywang.wx.miniapp.bean.cloud; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; + +/** + * 获取腾讯云API调用凭证结果. + * + * @author Binary Wang + * @date 2020-01-27 + */ +@Data +public class WxCloudGetQcloudTokenResult implements Serializable { + private static final long serialVersionUID = -1505786395531138286L; + + /** + * secretid + */ + @SerializedName("secretid") + private String secretId; + + /** + * secretkey + */ + @SerializedName("secretkey") + private String secretKey; + + /** + * token + */ + @SerializedName("token") + private String token; + + /** + * 过期时间戳 + */ + @SerializedName("expired_time") + private Long expiredTime; +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudUploadFileResult.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudUploadFileResult.java new file mode 100644 index 0000000000..300ae86200 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/cloud/WxCloudUploadFileResult.java @@ -0,0 +1,47 @@ +package cn.binarywang.wx.miniapp.bean.cloud; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; + +/** + * 云开发文件上传接口响应结果. + * + * @author Binary Wang + * @date 2020-01-27 + */ +@Data +public class WxCloudUploadFileResult implements Serializable { + private static final long serialVersionUID = 787346474470048318L; + + /** + * 上传url + */ + @SerializedName("url") + private String url; + + /** + * token + */ + @SerializedName("token") + private String token; + + /** + * authorization + */ + @SerializedName("authorization") + private String authorization; + + /** + * 文件ID + */ + @SerializedName("file_id") + private String fileId; + + /** + * cos文件ID + */ + @SerializedName("cos_file_id") + private String cosFileId; +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/WxMaExpressAccount.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/WxMaExpressAccount.java new file mode 100644 index 0000000000..bdf72efebd --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/WxMaExpressAccount.java @@ -0,0 +1,103 @@ +package cn.binarywang.wx.miniapp.bean.express; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.annotations.SerializedName; +import com.google.gson.reflect.TypeToken; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + *
+ * 物流账号对象
+ * 
+ * @author xiaoyu + * @since 2019-11-26 + */ +@Data +public class WxMaExpressAccount implements Serializable { + + private static final JsonParser JSON_PARSER = new JsonParser(); + private static final long serialVersionUID = -4991983596742569736L; + + /** + * 快递公司客户编码 + */ + @SerializedName("biz_id") + private String bizId; + + /** + * 快递公司ID + */ + @SerializedName("delivery_id") + private String deliveryId; + + /** + * 账号绑定时间 + */ + @SerializedName("create_time") + private Long createTime; + + /** + * 账号更新时间 + */ + @SerializedName("update_time") + private Long updateTime; + + /** + * 绑定状态 + * status_code 的合法值 : 0-绑定成功;1-审核中;2-绑定失败 + */ + @SerializedName("status_code") + private Integer statusCode; + + /** + * 账号别名 + */ + @SerializedName("alias") + private String alias; + + /** + * 账号绑定失败的错误信息(EMS审核结果) + */ + @SerializedName("remark_wrong_msg") + private String remarkWrongMsg; + + /** + * 账号绑定时的备注内容(提交EMS审核需要) + */ + @SerializedName("remark_content") + private String remarkContent; + + /** + * 电子面单余额 + */ + @SerializedName("quota_num") + private Integer quotaNum; + + /** + * 电子面单余额更新时间 + */ + @SerializedName("quota_update_time") + private Integer quotaUpdateTime; + + /** + * 支持的服务类型 + */ + @SerializedName("service_type") + private List serviceType; + + public static List fromJsonList(String json) { + JsonObject jsonObject = JSON_PARSER.parse(json).getAsJsonObject(); + return WxMaGsonBuilder.create().fromJson(jsonObject.get("list").toString(), + new TypeToken>() { + }.getType()); + } + + public static WxMaExpressAccount fromJson(String json) { + return WxMaGsonBuilder.create().fromJson(json, WxMaExpressAccount.class); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/WxMaExpressDelivery.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/WxMaExpressDelivery.java new file mode 100644 index 0000000000..1127479b4c --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/WxMaExpressDelivery.java @@ -0,0 +1,83 @@ +package cn.binarywang.wx.miniapp.bean.express; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.annotations.SerializedName; +import com.google.gson.reflect.TypeToken; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + *
+ * 快递公司对象
+ * 
+ * @author xiaoyu + * @since 2019-11-26 + */ +@Data +public class WxMaExpressDelivery implements Serializable { + private static final JsonParser JSON_PARSER = new JsonParser(); + private static final long serialVersionUID = -8394544895730223810L; + + /** + * 快递公司 ID + */ + @SerializedName("delivery_id") + private String deliveryId; + + /** + * 快递公司名称 + */ + @SerializedName("delivery_name") + private String deliveryName; + + /** + * 是否支持散单, 1表示支持 + */ + @SerializedName("can_use_cash") + private Integer canUseCash; + + /** + * 是否支持查询面单余额, 1表示支持 + */ + @SerializedName("can_get_quota") + private Integer canGetQuota; + + /** + * 散单对应的bizid,当can_use_cash=1时有效 + */ + @SerializedName("cash_biz_id") + private String cashBizId; + + /** + * 支持的服务类型 + */ + @SerializedName("service_type") + private List serviceType; + + public static List fromJson(String json) { + JsonObject jsonObject = JSON_PARSER.parse(json).getAsJsonObject(); + return WxMaGsonBuilder.create().fromJson(jsonObject.get("data").toString(), + new TypeToken>() { + }.getType()); + } + + @Data + public static class ServiceType{ + + /** + * 服务类型ID + */ + @SerializedName("service_type") + private Integer serviceType; + + /** + * 服务类型名称 + */ + @SerializedName("service_name") + private String serviceName; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/WxMaExpressPath.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/WxMaExpressPath.java new file mode 100644 index 0000000000..ba5a903f4d --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/WxMaExpressPath.java @@ -0,0 +1,75 @@ +package cn.binarywang.wx.miniapp.bean.express; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 运单轨迹对象 + * @author xiaoyu + * @since 2019-11-26 + */ +@Data +public class WxMaExpressPath implements Serializable { + + private static final long serialVersionUID = 5643624677715536605L; + + /** + * 用户openid + */ + private String openid; + + /** + * 快递公司 ID + */ + @SerializedName("delivery_id") + private String deliveryId; + + /** + * 运单 ID + */ + @SerializedName("waybill_id") + private String waybillId; + + /** + * 轨迹节点数量 + */ + @SerializedName("path_item_num") + private Integer pathItemNum; + + /** + * 轨迹节点列表 + */ + @SerializedName("path_item_list") + private List pathItemList; + + public static WxMaExpressPath fromJson(String json) { + return WxMaGsonBuilder.create().fromJson(json, WxMaExpressPath.class); + } + + @Data + private static class PathItem{ + + /** + * 轨迹节点 Unix 时间戳 + */ + @SerializedName("action_time") + private Long actionTime; + + /** + * 轨迹节点类型 + */ + @SerializedName("action_type") + private Integer actionType; + + /** + * 轨迹节点详情 + */ + @SerializedName("action_msg") + private String actionMsg; + + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/WxMaExpressPrinter.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/WxMaExpressPrinter.java new file mode 100644 index 0000000000..832fa2e376 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/WxMaExpressPrinter.java @@ -0,0 +1,44 @@ +package cn.binarywang.wx.miniapp.bean.express; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.gson.JsonParser; +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + *
+ * 面单打印员对象
+ * 
+ * @author xiaoyu + * @since 2019-11-26 + */ +@Data +public class WxMaExpressPrinter implements Serializable { + + private static final JsonParser JSON_PARSER = new JsonParser(); + private static final long serialVersionUID = 7164449984700322531L; + + /** + * 数量 + */ + private Integer count; + + /** + * 打印员openid + */ + private List openid; + + /** + * 打印员面单打印权限 + */ + @SerializedName("tagid_list") + private List tagidList; + + + public static WxMaExpressPrinter fromJson(String json) { + return WxMaGsonBuilder.create().fromJson(json, WxMaExpressPrinter.class); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressAddOrderRequest.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressAddOrderRequest.java new file mode 100644 index 0000000000..4b404e332b --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressAddOrderRequest.java @@ -0,0 +1,169 @@ +package cn.binarywang.wx.miniapp.bean.express.request; + + +import cn.binarywang.wx.miniapp.bean.express.*; +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import lombok.Builder; +import lombok.Data; + +import java.io.Serializable; + +/** + *
+ * 生成运单请求对象
+ * 
+ * @author xiaoyu + * @since 2019-11-26 + */ +@Data +@Builder +public class WxMaExpressAddOrderRequest implements Serializable { + + private static final long serialVersionUID = -7538739003766268386L; + + + /** + * 订单来源 + *
+   * 是否必填: 是
+   * 描述: 0为小程序订单,2为App或H5订单,填2则不发送物流服务通知
+   * 
+ */ + @SerializedName("add_source") + private Integer addSource; + + /** + * App或H5的appid + *
+   * 是否必填: 否
+   * 描述: add_source=2时必填,需和开通了物流助手的小程序绑定同一open帐号
+   * 
+ */ + @SerializedName("wx_appid") + private String wxAppid; + + /** + * 订单ID + *
+   * 是否必填: 是
+   * 描述: 须保证全局唯一,不超过512字节
+   * 
+ */ + @SerializedName("order_id") + private String orderId; + + /** + * 用户openid + *
+   * 是否必填: 否
+   * 描述: 当add_source=2时无需填写(不发送物流服务通知)
+   * 
+ */ + @SerializedName("openid") + private String openid; + + /** + * 快递公司ID + *
+   * 是否必填: 是
+   * 描述: 可通过getAllDelivery查询
+   * 
+ */ + @SerializedName("delivery_id") + private String deliveryId; + + /** + * 快递客户编码或者现付编码 + *
+   * 是否必填: 是
+   * 
+ */ + @SerializedName("biz_id") + private String bizId; + + /** + * 快递备注信息 + *
+   * 是否必填: 否
+   * 描述: 比如"易碎物品",不超过1024字节
+   * 
+ */ + @SerializedName("custom_remark") + private String customRemark; + + /** + * 订单标签id + *
+   * 是否必填: 否
+   * 描述: 用于平台型小程序区分平台上的入驻方,tagid须与入驻方账号一一对应,非平台型小程序无需填写该字段
+   * 
+ */ + @SerializedName("tagid") + private Integer tagid; + + /** + * 预期的上门揽件时间 + *
+   * 是否必填: 否
+   * 描述: 顺丰必须传,0表示已事先约定取件时间;否则请传预期揽件时间戳,需大于当前时间,收件员会在预期时间附近上门。例如expect_time为“1557989929”,表示希望收件员将在2019年05月16日14:58:49-15:58:49内上门取货。说明:若选择 了预期揽件时间,请不要自己打单,由上门揽件的时候打印。
+   * 
+ */ + @SerializedName("expect_time") + private Long expectTime; + + /** + * 发件人信息 + *
+   * 是否必填: 是
+   * 
+ */ + private WxMaExpressOrderPerson sender; + + /** + * 收件人信息 + *
+   * 是否必填: 是
+   * 
+ */ + private WxMaExpressOrderPerson receiver; + + /** + * 包裹信息 + *
+   * 是否必填: 是
+   * 描述: 将传递给快递公司
+   * 
+ */ + private WxMaExpressOrderCargo cargo; + + /** + * 商品信息 + *
+   * 是否必填: 否
+   * 描述: 会展示到物流服务通知和电子面单中
+   * 
+ */ + private WxMaExpressOrderShop shop; + + /** + * 保价信息 + *
+   * 是否必填: 是
+   * 
+ */ + private WxMaExpressOrderInsured insured; + + /** + * 服务类型 + *
+   * 是否必填: 是
+   * 
+ */ + private WxMaExpressDelivery.ServiceType service; + + + public String toJson() { + return WxMaGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressBindAccountRequest.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressBindAccountRequest.java new file mode 100644 index 0000000000..fe6877fa9f --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressBindAccountRequest.java @@ -0,0 +1,73 @@ +package cn.binarywang.wx.miniapp.bean.express.request; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import lombok.Builder; +import lombok.Data; + +import java.io.Serializable; + +/** + *
+ * 绑定、解绑物流账号请求对象
+ * 
+ * @author xiaoyu + * @since 2019-11-26 + */ +@Data +@Builder +public class WxMaExpressBindAccountRequest implements Serializable { + + private static final long serialVersionUID = 3868003945297939946L; + + /** + * 类型 + *
+   * 是否必填: 是
+   * 描述: bind表示绑定,unbind表示解除绑定
+   * 
+ */ + @SerializedName("type") + private String type; + + /** + * 快递公司客户编码 + *
+   * 是否必填: 是
+   * 
+ */ + @SerializedName("biz_id") + private String bizId; + + /** + * 快递公司ID + *
+   * 是否必填: 是
+   * 
+ */ + @SerializedName("delivery_id") + private String deliveryId; + + /** + * 快递公司客户密码 + *
+   * 是否必填: 是
+   * 
+ */ + @SerializedName("password") + private String password; + + /** + * 备注内容(提交EMS审核需要) + *
+   * 是否必填: 是
+   * 
+ */ + @SerializedName("remark_content") + private String remarkContent; + + public String toJson() { + return WxMaGsonBuilder.create().toJson(this); + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressGetOrderRequest.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressGetOrderRequest.java new file mode 100644 index 0000000000..b21de172e9 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressGetOrderRequest.java @@ -0,0 +1,63 @@ +package cn.binarywang.wx.miniapp.bean.express.request; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import lombok.Builder; +import lombok.Data; + +import java.io.Serializable; + +/** + *
+ * 获取运单请求对象
+ * 
+ * @author xiaoyu + * @since 2019-11-26 + */ +@Data +@Builder +public class WxMaExpressGetOrderRequest implements Serializable { + + private static final long serialVersionUID = 8239315305577639778L; + + /** + * 订单ID + *
+   * 是否必填: 是
+   * 
+ */ + @SerializedName("order_id") + private String orderId; + + /** + * 用户openid + *
+   * 是否必填: 否
+   * 描述: 当add_source=2时无需填写(不发送物流服务通知)
+   * 
+ */ + private String openid; + + /** + * 快递公司ID + *
+   * 是否必填: 是
+   * 
+ */ + @SerializedName("delivery_id") + private String deliveryId; + + /** + * 运单ID + *
+   * 是否必填: 是
+   * 
+ */ + @SerializedName("waybill_id") + private String waybillId; + + + public String toJson() { + return WxMaGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressOrderCargo.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressOrderCargo.java new file mode 100644 index 0000000000..ec2c39709d --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressOrderCargo.java @@ -0,0 +1,74 @@ +package cn.binarywang.wx.miniapp.bean.express.request; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 包裹信息对象 + * @author xiaoyu + * @since 2019-11-26 + */ +@Data +public class WxMaExpressOrderCargo implements Serializable { + + private static final long serialVersionUID = 6642536671375396150L; + + /** + * 包裹数量 + *
+   * 是否必填: 是
+   * 描述: 需要和detail_list size保持一致
+   * 
+ */ + private Integer count; + /** + * 包裹总重量 + *
+   * 是否必填: 是
+   * 描述: 单位是千克(kg)
+   * 
+ */ + private Integer weight; + + /** + * 包裹长度 + *
+   * 是否必填: 是
+   * 描述: 单位是厘米(cm)
+   * 
+ */ + @SerializedName("space_x") + private Integer spaceLength; + + /** + * 包裹宽度 + *
+   * 是否必填: 是
+   * 描述: 单位是厘米(cm)
+   * 
+ */ + @SerializedName("space_y") + private Integer spaceWidth; + + /** + * 包裹高度 + *
+   * 是否必填: 是
+   * 描述: 单位是厘米(cm)
+   * 
+ */ + @SerializedName("space_z") + private Integer spaceHeight; + + /** + * 包裹中商品详情列表 + *
+   * 是否必填: 是
+   * 
+ */ + @SerializedName("detail_list") + private List detailList; +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressOrderCargoDetail.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressOrderCargoDetail.java new file mode 100644 index 0000000000..1e61bba897 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressOrderCargoDetail.java @@ -0,0 +1,33 @@ +package cn.binarywang.wx.miniapp.bean.express.request; + +import lombok.Data; + +import java.io.Serializable; + +/** + * 包裹商品详情对象 + * @author xiaoyu + * @since 2019-11-26 + */ +@Data +public class WxMaExpressOrderCargoDetail implements Serializable { + + private static final long serialVersionUID = 5988620921216969796L; + + /** + * 商品名 + *
+   * 是否必填: 是
+   * 描述: 不超过128字节
+   * 
+ */ + private String name; + + /** + * 商品数量 + *
+   * 是否必填: 是
+   * 
+ */ + private Integer count; +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressOrderInsured.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressOrderInsured.java new file mode 100644 index 0000000000..7a30dacadc --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressOrderInsured.java @@ -0,0 +1,43 @@ +package cn.binarywang.wx.miniapp.bean.express.request; + + +import cn.binarywang.wx.miniapp.constant.WxMaConstants; +import com.google.gson.annotations.SerializedName; +import lombok.Builder; +import lombok.Data; + +import java.io.Serializable; + +/** + * 保价信息对象 + * @author xiaoyu + * @since 2019-11-26 + */ +@Data +@Builder +public class WxMaExpressOrderInsured implements Serializable { + + private static final long serialVersionUID = -8636857630937445422L; + + /** + * 是否保价 + *
+   * 是否必填: 是
+   * 描述: 0 表示不保价,1 表示保价
+   * 
+ */ + @SerializedName("use_insured") + private final Integer useInsured = WxMaConstants.OrderAddInsured.INSURED_PROGRAM; + + /** + * 保价金额 + *
+   * 是否必填: 是
+   * 描述: 单位是分,比如: 10000 表示 100 元
+   * 
+ */ + @SerializedName("insured_value") + @Builder.Default + private final Integer insuredValue = WxMaConstants.OrderAddInsured.DEFAULT_INSURED_VALUE; + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressOrderPerson.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressOrderPerson.java new file mode 100644 index 0000000000..989380fb49 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressOrderPerson.java @@ -0,0 +1,110 @@ +package cn.binarywang.wx.miniapp.bean.express.request; + + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; + +/** + * 发件人/收件人信息对象 + * @author xiaoyu + * @since 2019-11-26 + */ +@Data +public class WxMaExpressOrderPerson implements Serializable { + + private static final long serialVersionUID = -7816060207882761506L; + + /** + * 发件人/收件人姓名 + *
+   * 是否必填: 是
+   * 描述: 不超过64字节
+   * 
+ */ + private String name; + + /** + * 发件人/收件人座机号码 + *
+   * 是否必填: 否
+   * 描述: 若不填写则必须填写 mobile,不超过32字节
+   * 
+ */ + private String tel; + + /** + * 发件人/收件人手机号码 + *
+   * 是否必填: 否
+   * 描述: 若不填写则必须填写 tel,不超过32字节
+   * 
+ */ + private String mobile; + + /** + * 发件人/收件人公司名 + *
+   * 是否必填: 否
+   * 描述: 不超过64字节
+   * 
+ */ + private String company; + + /** + * 发件人/收件人邮编 + *
+   * 是否必填: 否
+   * 描述: 不超过10字节
+   * 
+ */ + @SerializedName("post_code") + private String postCode; + + /** + * 发件人/收件人所在国家 + *
+   * 是否必填: 否
+   * 描述: 不超过64字节
+   * 
+ */ + private String country; + + /** + * 发件人/收件人省份 + *
+   * 是否必填: 是
+   * 描述: 比如:"广东省",不超过64字节
+   * 
+ */ + private String province; + + /** + * 发件人/收件人地区或市 + *
+   * 是否必填: 是
+   * 描述: 比如:"广州市",不超过64字节
+   * 
+ */ + private String city; + + /** + * 发件人/收件人区或县 + *
+   * 是否必填: 是
+   * 描述: 比如:"天河区",不超过64字节
+   * 
+ */ + private String area; + + /** + * 发件人/收件人详细地址 + *
+   * 是否必填: 是
+   * 描述: 比如:"XX路XX号XX大厦XX",不超过512字节
+   * 
+ */ + private String address; + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressOrderShop.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressOrderShop.java new file mode 100644 index 0000000000..9b1e3d884c --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressOrderShop.java @@ -0,0 +1,55 @@ +package cn.binarywang.wx.miniapp.bean.express.request; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.io.Serializable; + +/** + * 商品信息对象 + * @author xiaoyu + * @since 2019-11-26 + */ +@Data +public class WxMaExpressOrderShop implements Serializable { + + private static final long serialVersionUID = 7256509453502211830L; + + /** + * 商家小程序的路径 + *
+   * 是否必填: 是
+   * 描述: 建议为订单页面
+   * 
+ */ + @SerializedName("wxa_path") + private String wxaPath; + + /** + * 商品缩略图url + *
+   * 是否必填: 是
+   * 
+ */ + @SerializedName("img_url") + private String imgUrl; + + /** + * 商品名称 + *
+   * 是否必填: 是
+   * 
+ */ + @SerializedName("goods_name") + private String goodsName; + + /** + * 商品数量 + *
+   * 是否必填: 是
+   * 
+ */ + @SerializedName("goods_count") + private Integer goodsCount; + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressPrinterUpdateRequest.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressPrinterUpdateRequest.java new file mode 100644 index 0000000000..a5f9f50dbf --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressPrinterUpdateRequest.java @@ -0,0 +1,54 @@ +package cn.binarywang.wx.miniapp.bean.express.request; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import lombok.Builder; +import lombok.Data; + +import java.io.Serializable; + +/** + *
+ * 配置面单打印员请求对象
+ * 
+ * @author xiaoyu + * @since 2019-11-26 + */ +@Data +@Builder +public class WxMaExpressPrinterUpdateRequest implements Serializable { + + private static final long serialVersionUID = 9119040050963924127L; + + /** + * 打印员 openid + *
+   * 是否必填: 是
+   * 
+ */ + private String openid; + + /** + * 更新类型 + *
+   * 是否必填: 是
+   * 描述: bind表示绑定,unbind表示解除绑定
+   * 
+ */ + @SerializedName("update_type") + private String updateType; + + /** + * 打印员面单打印权限 + *
+   * 是否必填: 否
+   * 描述: 用于平台型小程序设置入驻方的打印员面单打印权限,同一打印员最多支持10个tagid,使用逗号分隔,如填写123,456,表示该打印员可以拉取到tagid为123和456的下的单,非平台型小程序无需填写该字段
+   * 
+ */ + @SerializedName("tagid_list") + private String tagidList; + + public String toJson() { + return WxMaGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressTestUpdateOrderRequest.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressTestUpdateOrderRequest.java new file mode 100644 index 0000000000..e5d4cb448b --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressTestUpdateOrderRequest.java @@ -0,0 +1,94 @@ +package cn.binarywang.wx.miniapp.bean.express.request; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import lombok.Builder; +import lombok.Data; + +import java.io.Serializable; + +/** + *
+ * 模拟快递公司更新订单状态请求对象
+ * 
+ * @author xiaoyu + * @since 2019-11-26 + */ +@Data +@Builder +public class WxMaExpressTestUpdateOrderRequest implements Serializable { + + private static final long serialVersionUID = -3701602332580704140L; + + /** + * 商户id + *
+   * 是否必填: 是
+   * 描述: 需填test_biz_id,默认值已设置
+   * 
+ */ + @SerializedName("biz_id") + @Builder.Default + private final String bizId = "test_biz_id"; + + /** + * 快递公司id + *
+   * 是否必填: 是
+   * 描述: 需填TEST,默认值已设置
+   * 
+ */ + @SerializedName("delivery_id") + @Builder.Default + private final String deliveryId = "TEST"; + + /** + * 订单号 + *
+   * 是否必填: 是
+   * 
+ */ + @SerializedName("order_id") + private String orderId; + + /** + * 运单号 + *
+   * 是否必填: 是
+   * 
+ */ + @SerializedName("waybill_id") + private String waybillId; + + /** + * 轨迹变化 Unix 时间戳 + *
+   * 是否必填: 是
+   * 
+ */ + @SerializedName("action_time") + private Long actionTime; + + /** + * 轨迹变化类型 + *
+   * 是否必填: 是
+   * 
+ */ + @SerializedName("action_type") + private Integer actionType; + + /** + * 轨迹变化具体信息说明,使用UTF-8编码 + *
+   * 是否必填: 是
+   * 
+ */ + @SerializedName("action_msg") + private String actionMsg; + + public String toJson() { + return WxMaGsonBuilder.create().toJson(this); + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/result/WxMaExpressOrderInfoResult.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/result/WxMaExpressOrderInfoResult.java new file mode 100644 index 0000000000..01f274fb9c --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/result/WxMaExpressOrderInfoResult.java @@ -0,0 +1,70 @@ +package cn.binarywang.wx.miniapp.bean.express.result; + +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.annotations.SerializedName; +import com.google.gson.reflect.TypeToken; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; + +/** + *
+ * 运单信息返回结果对象
+ * 
+ * @author xiaoyu + * @since 2019-11-26 + */ +@Data +public class WxMaExpressOrderInfoResult implements Serializable { + private static final JsonParser JSON_PARSER = new JsonParser(); + private static final long serialVersionUID = -9166603059965942285L; + + /** + * 错误码 + */ + private Integer errcode; + + /** + * 错误信息 + */ + private String errmsg; + /** + * 订单ID + */ + @SerializedName("order_id") + private String orderId; + + /** + * 运单ID + */ + @SerializedName("waybill_id") + private String waybillId; + + /** + * 运单 html 的 BASE64 结果 + */ + @SerializedName("print_html") + private String printHtml; + + /** + * 运单信息 + */ + @SerializedName("waybill_data") + private List> waybillData; + + + public static WxMaExpressOrderInfoResult fromJson(String json) { + return WxMaGsonBuilder.create().fromJson(json, WxMaExpressOrderInfoResult.class); + } + + public static List toList(String json) { + JsonObject jsonObject = JSON_PARSER.parse(json).getAsJsonObject(); + return WxMaGsonBuilder.create().fromJson(jsonObject.get("order_list").toString(), + new TypeToken>() { + }.getType()); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/template/WxMaPubTemplateTitleListResult.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/template/WxMaPubTemplateTitleListResult.java new file mode 100644 index 0000000000..a249619f9a --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/template/WxMaPubTemplateTitleListResult.java @@ -0,0 +1,32 @@ +package cn.binarywang.wx.miniapp.bean.template; + +import lombok.Data; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +import java.io.Serializable; +import java.util.List; + +@Data +public class WxMaPubTemplateTitleListResult implements Serializable { + private static final long serialVersionUID = -7718911668757837527L; + + private int count; + + private List data; + + public static WxMaPubTemplateTitleListResult fromJson(String json) { + return WxGsonBuilder.create().fromJson(json, WxMaPubTemplateTitleListResult.class); + } + + @Data + public static class TemplateItem { + + private Integer type; + + private Integer tid; + + private String categoryId; + + private String title; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/AbstractWxMaRedisConfig.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/AbstractWxMaRedisConfig.java new file mode 100644 index 0000000000..ac75b3697d --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/AbstractWxMaRedisConfig.java @@ -0,0 +1,280 @@ +package cn.binarywang.wx.miniapp.config.impl; + +import com.github.jedis.lock.JedisLock; +import redis.clients.jedis.Jedis; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; + +/** + * @author winter + */ +public abstract class AbstractWxMaRedisConfig extends WxMaDefaultConfigImpl { + + public interface JedisConfig { + Jedis config(Jedis jedis); + } + + private static final String ACCESS_TOKEN = "accessToken"; + private static final String JSAPI_TICKET = "jsapiTicket"; + private static final String CARD_API_TICKET = "cardApiTicket"; + + private static final String HASH_VALUE_FIELD = "value"; + private static final String HASH_EXPIRE_FIELD = "expire"; + + /** + * Redis Key 的前缀,默认为 maConfig + */ + private String redisKeyPrefix = "maConfig"; + + /** + * 微信小程序唯一id,用于拼接存储到redis时的key,防止key重复. + */ + private String maId; + + private Lock accessTokenLock; + private Lock jsapiTicketLock; + private Lock cardApiTicketLock; + + /** + * 临时文件目录. + */ + protected volatile File tmpDirFile; + + /** + * 对从 JedisPool.getResource() 获取到的每个 Jedis 实例进行配置 + */ + private JedisConfig jedisConfig; + + protected abstract Jedis getJedis(); + + private Jedis getConfiguredJedis() { + Jedis jedis = getJedis(); + if (jedisConfig != null) { + return jedisConfig.config(jedis); + } else { + return jedis; + } + } + + private String getRedisKey(String key) { + StringBuilder redisKey = new StringBuilder(redisKeyPrefix).append(":"); + if (maId == null) { + return redisKey.append(key).toString(); + } else { + return redisKey.append(maId).append(":").append(key).toString(); + } + } + + private String getValueFromRedis(String key) { + try (Jedis jedis = getConfiguredJedis()) { + return jedis.hget(getRedisKey(key), HASH_VALUE_FIELD); + } + } + + private void setValueToRedis(String key, long expiresTime, String value) { + try (Jedis jedis = getConfiguredJedis()) { + Map hash = new HashMap(); + hash.put(HASH_VALUE_FIELD, value); + hash.put(HASH_EXPIRE_FIELD, String.valueOf(expiresTime)); + jedis.hmset(getRedisKey(key), hash); + } + } + + private long getExpireFromRedis(String key) { + try (Jedis jedis = getConfiguredJedis()) { + String expire = jedis.hget(getRedisKey(key), HASH_EXPIRE_FIELD); + return expire == null ? 0 : Long.parseLong(expire); + } + } + + private void setExpire(String key, long expiresTime) { + try (Jedis jedis = getConfiguredJedis()) { + jedis.hset(getRedisKey(key), HASH_EXPIRE_FIELD, String.valueOf(expiresTime)); + } + } + + public void setRedisKeyPrefix(String redisKeyPrefix) { + this.redisKeyPrefix = redisKeyPrefix; + } + + public void setJedisConfig(JedisConfig jedisConfig) { + this.jedisConfig = jedisConfig; + } + + public void setMaId(String maId) { + this.maId = maId; + } + + @Override + public String getAccessToken() { + return getValueFromRedis(ACCESS_TOKEN); + } + + @Override + public Lock getAccessTokenLock() { + if (accessTokenLock == null) { + synchronized (this) { + if (accessTokenLock == null) { + accessTokenLock = new DistributedLock(getRedisKey("accessTokenLock")); + } + } + } + return accessTokenLock; + } + + @Override + public boolean isAccessTokenExpired() { + return isExpired(getExpireFromRedis(ACCESS_TOKEN)); + } + + @Override + public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) { + setValueToRedis(ACCESS_TOKEN, expiresAheadInMillis(expiresInSeconds), accessToken); + } + + @Override + public String getJsapiTicket() { + return getValueFromRedis(JSAPI_TICKET); + } + + @Override + public Lock getJsapiTicketLock() { + if (jsapiTicketLock == null) { + synchronized (this) { + if (jsapiTicketLock == null) { + jsapiTicketLock = new DistributedLock(getRedisKey("jsapiTicketLock")); + } + } + } + return jsapiTicketLock; + } + + @Override + public boolean isJsapiTicketExpired() { + return isExpired(getExpireFromRedis(JSAPI_TICKET)); + } + + @Override + public void expireJsapiTicket() { + setExpire(JSAPI_TICKET, 0); + } + + @Override + public void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) { + setValueToRedis(JSAPI_TICKET, expiresAheadInMillis(expiresInSeconds), jsapiTicket); + } + + + @Override + public String getCardApiTicket() { + return getValueFromRedis(CARD_API_TICKET); + } + + @Override + public Lock getCardApiTicketLock() { + if (cardApiTicketLock == null) { + synchronized (this) { + if (cardApiTicketLock == null) { + cardApiTicketLock = new DistributedLock(getRedisKey("cardApiTicketLock")); + } + } + } + return cardApiTicketLock; + } + + @Override + public boolean isCardApiTicketExpired() { + return isExpired(getExpireFromRedis(CARD_API_TICKET)); + } + + @Override + public void expireCardApiTicket() { + setExpire(CARD_API_TICKET, 0); + } + + @Override + public void updateCardApiTicket(String cardApiTicket, int expiresInSeconds) { + setValueToRedis(CARD_API_TICKET, expiresAheadInMillis(expiresInSeconds), cardApiTicket); + } + + @Override + public void expireAccessToken() { + setExpiresTime(0); + } + + @Override + public long getExpiresTime() { + return getExpireFromRedis(ACCESS_TOKEN); + } + + @Override + public void setExpiresTime(long expiresTime) { + setExpire(ACCESS_TOKEN, expiresTime); + } + + /** + * 基于redis的简单分布式锁. + */ + private class DistributedLock implements Lock { + + private JedisLock lock; + + private DistributedLock(String key) { + this.lock = new JedisLock(getRedisKey(key)); + } + + @Override + public void lock() { + try (Jedis jedis = getConfiguredJedis()) { + if (!lock.acquire(jedis)) { + throw new RuntimeException("acquire timeouted"); + } + } catch (InterruptedException e) { + throw new RuntimeException("lock failed", e); + } + } + + @Override + public void lockInterruptibly() throws InterruptedException { + try (Jedis jedis = getConfiguredJedis()) { + if (!lock.acquire(jedis)) { + throw new RuntimeException("acquire timeouted"); + } + } + } + + @Override + public boolean tryLock() { + try (Jedis jedis = getConfiguredJedis()) { + return lock.acquire(jedis); + } catch (InterruptedException e) { + throw new RuntimeException("lock failed", e); + } + } + + @Override + public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { + try (Jedis jedis = getConfiguredJedis()) { + return lock.acquire(jedis); + } + } + + @Override + public void unlock() { + try (Jedis jedis = getConfiguredJedis()) { + lock.release(jedis); + } + } + + @Override + public Condition newCondition() { + throw new RuntimeException("unsupported method"); + } + + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/WxMaDefaultConfigImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/WxMaDefaultConfigImpl.java index 76ff7611b4..98929be166 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/WxMaDefaultConfigImpl.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/WxMaDefaultConfigImpl.java @@ -48,6 +48,20 @@ public class WxMaDefaultConfigImpl implements WxMaConfig { private volatile ApacheHttpClientBuilder apacheHttpClientBuilder; + /** + * 会过期的数据提前过期时间,默认预留200秒的时间 + */ + protected long expiresAheadInMillis(int expiresInSeconds) { + return System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L; + } + + /** + * 判断 expiresTime 是否已经过期 + */ + protected boolean isExpired(long expiresTime) { + return System.currentTimeMillis() > expiresTime; + } + @Override public String getAccessToken() { return this.accessToken; @@ -68,7 +82,7 @@ public void setAccessTokenLock(Lock accessTokenLock) { @Override public boolean isAccessTokenExpired() { - return System.currentTimeMillis() > this.expiresTime; + return isExpired(this.expiresTime); } @Override @@ -78,8 +92,8 @@ public synchronized void updateAccessToken(WxAccessToken accessToken) { @Override public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) { - this.accessToken = accessToken; - this.expiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L; + setAccessToken(accessToken); + setExpiresTime(expiresAheadInMillis(expiresInSeconds)); } @Override @@ -94,7 +108,7 @@ public Lock getJsapiTicketLock() { @Override public boolean isJsapiTicketExpired() { - return System.currentTimeMillis() > this.jsapiTicketExpiresTime; + return isExpired(this.jsapiTicketExpiresTime); } @Override @@ -105,8 +119,7 @@ public void expireJsapiTicket() { @Override public void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) { this.jsapiTicket = jsapiTicket; - // 预留200秒的时间 - this.jsapiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L; + this.jsapiTicketExpiresTime = expiresAheadInMillis(expiresInSeconds); } @@ -122,7 +135,7 @@ public Lock getCardApiTicketLock() { @Override public boolean isCardApiTicketExpired() { - return System.currentTimeMillis() > this.cardApiTicketExpiresTime; + return isExpired(this.cardApiTicketExpiresTime); } @Override @@ -133,8 +146,7 @@ public void expireCardApiTicket() { @Override public void updateCardApiTicket(String cardApiTicket, int expiresInSeconds) { this.cardApiTicket = cardApiTicket; - // 预留200秒的时间 - this.cardApiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L; + this.cardApiTicketExpiresTime = expiresAheadInMillis(expiresInSeconds); } @Override diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/WxMaRedisConfigImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/WxMaRedisConfigImpl.java index c9340f8d79..dfff1ceefc 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/WxMaRedisConfigImpl.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/WxMaRedisConfigImpl.java @@ -1,377 +1,40 @@ package cn.binarywang.wx.miniapp.config.impl; -import java.io.File; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.Condition; -import java.util.concurrent.locks.Lock; - -import cn.binarywang.wx.miniapp.config.WxMaConfig; -import com.github.jedis.lock.JedisLock; - -import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; -import me.chanjar.weixin.common.bean.WxAccessToken; -import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; /** - * 基于Redis的微信配置provider. + * 基于Redis的微信配置provider. 使用连接池 JedisPool 进行 Redis 操作。 * *
  * 需要引入依赖jedis-lock,才能使用该类。
  * 
- * - * @author winter */ -public class WxMaRedisConfigImpl implements WxMaConfig { - private static final String ACCESS_TOKEN = "accessToken"; - private static final String JSAPI_TICKET = "jsapiTicket"; - private static final String CARD_API_TICKET = "cardApiTicket"; - - private static final String HASH_VALUE_FIELD = "value"; - private static final String HASH_EXPIRE_FIELD = "expire"; +public class WxMaRedisConfigImpl extends AbstractWxMaRedisConfig { private JedisPool jedisPool; - /** - * 微信小程序唯一id,用于拼接存储到redis时的key,防止key重复. - */ - private String maId; - - private volatile String msgDataFormat; - protected volatile String appid; - private volatile String secret; - protected volatile String token; - private volatile String aesKey; - - private volatile String httpProxyHost; - private volatile int httpProxyPort; - private volatile String httpProxyUsername; - private volatile String httpProxyPassword; - - private Lock accessTokenLock; - private Lock jsapiTicketLock; - private Lock cardApiTicketLock; /** - * 临时文件目录. + * JedisPool 在此配置类是必须项,使用 WxMaRedisConfigImpl(JedisPool) 构造方法来构造实例 */ - protected volatile File tmpDirFile; - - private volatile ApacheHttpClientBuilder apacheHttpClientBuilder; - - private String getRedisKey(String key) { - StringBuilder redisKey = new StringBuilder("maConfig:"); - if (maId == null) { - return redisKey.append(key).toString(); - } else { - return redisKey.append(maId).append(":").append(key).toString(); - } + @Deprecated + public WxMaRedisConfigImpl() { } - private String getValueFromRedis(String key) { - try (Jedis jedis = jedisPool.getResource()) { - return jedis.hget(getRedisKey(key), HASH_VALUE_FIELD); - } - } - - private void setValueToRedis(String key, long expiresTime, String value) { - try (Jedis jedis = jedisPool.getResource()) { - Map hash = new HashMap(); - hash.put(HASH_VALUE_FIELD, value); - hash.put(HASH_EXPIRE_FIELD, String.valueOf(expiresTime)); - jedis.hmset(getRedisKey(key), hash); - } - } - - private long getExpireFromRedis(String key) { - try (Jedis jedis = jedisPool.getResource()) { - String expire = jedis.hget(getRedisKey(key), HASH_EXPIRE_FIELD); - return expire == null ? 0 : Long.parseLong(expire); - } - } - - private void setExpire(String key, long expiresTime) { - try (Jedis jedis = jedisPool.getResource()) { - jedis.hset(getRedisKey(key), HASH_EXPIRE_FIELD, String.valueOf(expiresTime)); - } + public WxMaRedisConfigImpl(JedisPool jedisPool) { + this.jedisPool = jedisPool; } + /** + * 使用 WxMaRedisConfigImpl(JedisPool) 构造方法来设置 JedisPool + */ + @Deprecated public void setJedisPool(JedisPool jedisPool) { this.jedisPool = jedisPool; } - public void setMaId(String maId) { - this.maId = maId; - } - - @Override - public String getAccessToken() { - return getValueFromRedis(ACCESS_TOKEN); - } - - @Override - public Lock getAccessTokenLock() { - if (accessTokenLock == null) { - synchronized (this) { - if (accessTokenLock == null) { - accessTokenLock = new DistributedLock(getRedisKey("accessTokenLock")); - } - } - } - return accessTokenLock; - } - - @Override - public boolean isAccessTokenExpired() { - return System.currentTimeMillis() > getExpireFromRedis(ACCESS_TOKEN); - } - - @Override - public synchronized void updateAccessToken(WxAccessToken accessToken) { - updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); - } - - @Override - public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) { - setValueToRedis(ACCESS_TOKEN, System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L, accessToken); - } - - @Override - public String getJsapiTicket() { - return getValueFromRedis(JSAPI_TICKET); - } - - @Override - public Lock getJsapiTicketLock() { - if (jsapiTicketLock == null) { - synchronized (this) { - if (jsapiTicketLock == null) { - jsapiTicketLock = new DistributedLock(getRedisKey("jsapiTicketLock")); - } - } - } - return jsapiTicketLock; - } - - @Override - public boolean isJsapiTicketExpired() { - return System.currentTimeMillis() > getExpireFromRedis(JSAPI_TICKET); - } - - @Override - public void expireJsapiTicket() { - setExpire(JSAPI_TICKET, 0); - } - - @Override - public void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) { - // 预留200秒的时间 - setValueToRedis(JSAPI_TICKET, System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L, jsapiTicket); - } - - - @Override - public String getCardApiTicket() { - return getValueFromRedis(CARD_API_TICKET); - } - - @Override - public Lock getCardApiTicketLock() { - if (cardApiTicketLock == null) { - synchronized (this) { - if (cardApiTicketLock == null) { - cardApiTicketLock = new DistributedLock(getRedisKey("cardApiTicketLock")); - } - } - } - return cardApiTicketLock; - } - - @Override - public boolean isCardApiTicketExpired() { - return System.currentTimeMillis() > getExpireFromRedis(CARD_API_TICKET); - } - - @Override - public void expireCardApiTicket() { - setExpire(CARD_API_TICKET, 0); - } - - @Override - public void updateCardApiTicket(String cardApiTicket, int expiresInSeconds) { - setValueToRedis(CARD_API_TICKET, System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L, cardApiTicket); - } - - @Override - public void expireAccessToken() { - setExpire(ACCESS_TOKEN, 0); - } - - @Override - public String getSecret() { - return this.secret; - } - - public void setSecret(String secret) { - this.secret = secret; - } - - @Override - public String getToken() { - return this.token; - } - - public void setToken(String token) { - this.token = token; - } - - @Override - public long getExpiresTime() { - return getExpireFromRedis(ACCESS_TOKEN); - } - - @Override - public String getAesKey() { - return this.aesKey; - } - - public void setAesKey(String aesKey) { - this.aesKey = aesKey; - } - - @Override - public String getMsgDataFormat() { - return this.msgDataFormat; - } - - public void setMsgDataFormat(String msgDataFormat) { - this.msgDataFormat = msgDataFormat; - } - - @Override - public String getHttpProxyHost() { - return this.httpProxyHost; - } - - public void setHttpProxyHost(String httpProxyHost) { - this.httpProxyHost = httpProxyHost; - } - - @Override - public int getHttpProxyPort() { - return this.httpProxyPort; - } - - public void setHttpProxyPort(int httpProxyPort) { - this.httpProxyPort = httpProxyPort; - } - - @Override - public String getHttpProxyUsername() { - return this.httpProxyUsername; - } - - public void setHttpProxyUsername(String httpProxyUsername) { - this.httpProxyUsername = httpProxyUsername; - } - - @Override - public String getHttpProxyPassword() { - return this.httpProxyPassword; - } - - public void setHttpProxyPassword(String httpProxyPassword) { - this.httpProxyPassword = httpProxyPassword; - } - @Override - public String toString() { - return WxMaGsonBuilder.create().toJson(this); - } - - @Override - public ApacheHttpClientBuilder getApacheHttpClientBuilder() { - return this.apacheHttpClientBuilder; - } - - public void setApacheHttpClientBuilder(ApacheHttpClientBuilder apacheHttpClientBuilder) { - this.apacheHttpClientBuilder = apacheHttpClientBuilder; - } - - @Override - public boolean autoRefreshToken() { - return true; - } - - @Override - public String getAppid() { - return appid; - } - - public void setAppid(String appid) { - this.appid = appid; - } - - /** - * 基于redis的简单分布式锁. - */ - private class DistributedLock implements Lock { - - private JedisLock lock; - - private DistributedLock(String key) { - this.lock = new JedisLock(getRedisKey(key)); - } - - @Override - public void lock() { - try (Jedis jedis = jedisPool.getResource()) { - if (!lock.acquire(jedis)) { - throw new RuntimeException("acquire timeouted"); - } - } catch (InterruptedException e) { - throw new RuntimeException("lock failed", e); - } - } - - @Override - public void lockInterruptibly() throws InterruptedException { - try (Jedis jedis = jedisPool.getResource()) { - if (!lock.acquire(jedis)) { - throw new RuntimeException("acquire timeouted"); - } - } - } - - @Override - public boolean tryLock() { - try (Jedis jedis = jedisPool.getResource()) { - return lock.acquire(jedis); - } catch (InterruptedException e) { - throw new RuntimeException("lock failed", e); - } - } - - @Override - public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { - try (Jedis jedis = jedisPool.getResource()) { - return lock.acquire(jedis); - } - } - - @Override - public void unlock() { - try (Jedis jedis = jedisPool.getResource()) { - lock.release(jedis); - } - } - - @Override - public Condition newCondition() { - throw new RuntimeException("unsupported method"); - } - + protected Jedis getJedis() { + return jedisPool.getResource(); } } diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/WxMaRedisConnectionConfigImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/WxMaRedisConnectionConfigImpl.java new file mode 100644 index 0000000000..e48cda2040 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/WxMaRedisConnectionConfigImpl.java @@ -0,0 +1,24 @@ +package cn.binarywang.wx.miniapp.config.impl; + +import redis.clients.jedis.Jedis; + +/** + * 基于Redis的微信配置provider. 使用自己管理的 Jedis 实例进行 Redis 操作。 + * + *
+ * 需要引入依赖jedis-lock,才能使用该类。
+ * 
+ */ +public class WxMaRedisConnectionConfigImpl extends AbstractWxMaRedisConfig { + + public WxMaRedisConnectionConfigImpl(Jedis jedis) { + this.jedis = jedis; + } + + private Jedis jedis; + + @Override + protected Jedis getJedis() { + return jedis; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaConstants.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaConstants.java index efd2b22916..0d2a8675eb 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaConstants.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaConstants.java @@ -85,4 +85,106 @@ public static final class SecCheckMediaType { */ public static final int IMAGE = 2; } + + /** + * 快递账号绑定类型 + */ + public static final class BindAccountType{ + + /** + * 绑定 + */ + public static final String BIND = "bind"; + + /** + * 解绑 + */ + public static final String UNBIND = "unbind"; + } + + /** + * 快递下单订单来源 + */ + public static final class OrderAddSource{ + + /** + * 小程序 + */ + public static final int MINI_PROGRAM = 0; + + /** + * APP或H5 + */ + public static final int APP_OR_H5 = 2; + } + + /** + * 快递下单保价 + */ + public static final class OrderAddInsured{ + /** + * 不保价 + */ + public static final int INSURED_PROGRAM = 0; + + /** + * 保价 + */ + public static final int USE_INSURED = 1; + + /** + * 默认保价金额 + */ + public static final int DEFAULT_INSURED_VALUE = 0; + } + + + /** + * 小程序订阅消息跳转小程序类型 + * + * developer为开发版;trial为体验版;formal为正式版;默认为正式版 + */ + public static final class MiniprogramState{ + /** + * 开发版 + */ + public static final String DEVELOPER = "developer"; + + /** + * 体验版 + */ + public static final String TRIAL = "trial"; + + /** + * 正式版 + */ + public static final String FORMAL = "formal"; + } + + + /** + * 进入小程序查看的语言类型 + * 支持zh_CN(简体中文)、en_US(英文)、zh_HK(繁体中文)、zh_TW(繁体中文),默认为zh_CN + */ + public static final class MiniprogramLang{ + /** + * 简体中文 + */ + public static final String ZH_CN = "zh_CN"; + + /** + * 英文 + */ + public static final String EN_US = "en_US"; + + /** + * 繁体中文 + */ + public static final String ZH_HK = "zh_HK"; + + /** + * 繁体中文 + */ + public static final String ZH_TW = "zh_TW"; + } } diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaSubscribeMessageGsonAdapter.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaSubscribeMessageGsonAdapter.java index d15226a9e7..89f8bad8d3 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaSubscribeMessageGsonAdapter.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaSubscribeMessageGsonAdapter.java @@ -1,6 +1,5 @@ package cn.binarywang.wx.miniapp.util.json; -import cn.binarywang.wx.miniapp.bean.WxMaSubscribeData; import cn.binarywang.wx.miniapp.bean.WxMaSubscribeMessage; import com.google.gson.JsonElement; import com.google.gson.JsonObject; @@ -9,8 +8,12 @@ import java.lang.reflect.Type; +/** + * . + * + * @author S + */ public class WxMaSubscribeMessageGsonAdapter implements JsonSerializer { - @Override public JsonElement serialize(WxMaSubscribeMessage message, Type typeOfSrc, JsonSerializationContext context) { JsonObject messageJson = new JsonObject(); @@ -20,6 +23,13 @@ public JsonElement serialize(WxMaSubscribeMessage message, Type typeOfSrc, JsonS messageJson.addProperty("page", message.getPage()); } + if (message.getMiniprogramState() != null) { + messageJson.addProperty("miniprogram_state", message.getMiniprogramState()); + } + + if (message.getLang() != null) { + messageJson.addProperty("lang", message.getLang()); + } JsonObject data = new JsonObject(); messageJson.add("data", data); @@ -28,7 +38,7 @@ public JsonElement serialize(WxMaSubscribeMessage message, Type typeOfSrc, JsonS return messageJson; } - for (WxMaSubscribeData datum : message.getData()) { + for (WxMaSubscribeMessage.Data datum : message.getData()) { JsonObject dataJson = new JsonObject(); dataJson.addProperty("value", datum.getValue()); data.add(datum.getName(), dataJson); diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaCloudServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaCloudServiceImplTest.java new file mode 100644 index 0000000000..c3e008fe8d --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaCloudServiceImplTest.java @@ -0,0 +1,224 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.cloud.*; +import cn.binarywang.wx.miniapp.test.ApiTestModule; +import com.google.common.collect.Lists; +import com.google.gson.JsonArray; +import com.google.inject.Inject; +import me.chanjar.weixin.common.error.WxErrorException; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * 测试类. + * + * @author Binary Wang + * @date 2020-01-22 + */ +@Guice(modules = ApiTestModule.class) +public class WxMaCloudServiceImplTest { + @Inject + private WxMaService wxMaService; + + @Test + public void testInvokeCloudFunction() throws WxErrorException { + final String result = this.wxMaService.getCloudService().invokeCloudFunction("rcn", "login", "{}"); + assertThat(result).isNotNull(); + } + + @Test + public void testDatabaseAdd() throws WxErrorException { + JsonArray array = this.wxMaService.getCloudService().databaseAdd("rcn", "db.collection(\"geo\").add({\n" + + " data: [{\n" + + " description: \"item1\",\n" + + " due:\n" + + " new Date(\"2019-09-09\"),\n" + + " tags: [\n" + + " \"cloud\",\n" + + " \"database\"\n" + + " ],\n" + + " location:\n" + + " new db.Geo.Point(113, 23),\n" + + " done:false\n" + + " },\n" + + " {\n" + + " description: \"item2\",\n" + + " due:\n" + + " new Date(\"2019-09-09\"),\n" + + " tags: [\n" + + " \"cloud\",\n" + + " \"database\"\n" + + " ],\n" + + " location:\n" + + " new db.Geo.Point(113, 23),\n" + + " done:false\n" + + " }\n" + + " ]\n" + + " })"); + + System.out.println(array); + assertThat(array).isNotEmpty(); + } + + @Test + public void testDatabaseDelete() throws WxErrorException { + final int result = this.wxMaService.getCloudService().databaseDelete("rcn", + "db.collection(\"geo\").doc(\"056120a7-c89e-4616-95bf-dfc9a11daa3b\").remove()"); + assertThat(result).isEqualTo(0); + } + + @Test + public void testDatabaseUpdate() throws WxErrorException { + final WxCloudDatabaseUpdateResult result = this.wxMaService.getCloudService().databaseUpdate("rcn", + "db.collection(\"geo\").where({description:\"item1\"}).update({data:{age: _.inc(1)}})"); + assertThat(result).isNotNull(); + assertThat(result.getMatched()).isGreaterThan(0); + assertThat(result.getId()).isEmpty(); + assertThat(result.getModified()).isGreaterThan(0); + } + + @Test + public void testDatabaseQuery() throws WxErrorException { + final WxCloudDatabaseQueryResult result = this.wxMaService.getCloudService().databaseQuery("rcn", + "db.collection(\"geo\").where({done:false}).limit(10).skip(1).get()"); + assertThat(result).isNotNull(); + assertThat(result.getPager()).isNotNull(); + assertThat(result.getPager().getLimit()).isEqualTo(10); + assertThat(result.getPager().getOffset()).isEqualTo(1); + assertThat(result.getPager().getTotal()).isGreaterThan(0); + assertThat(result.getData().length).isGreaterThan(0); + } + + @Test + public void testDatabaseAggregate() throws WxErrorException { + final JsonArray result = this.wxMaService.getCloudService().databaseAggregate("rcn", + "db.collection(\"geo\").aggregate().match({tags:\"cloud\"}).limit(10).end()"); + assertThat(result).isNotNull(); + } + + @Test + public void testDatabaseCount() throws WxErrorException { + final Long result = this.wxMaService.getCloudService().databaseCount("rcn", + "db.collection(\"geo\").where({done:false}).count()"); + assertThat(result).isGreaterThan(0); + } + + @Test + public void testUpdateIndex() throws WxErrorException { + this.wxMaService.getCloudService().updateIndex("rcn", "geo", + Lists.newArrayList(new WxCloudDatabaseCreateIndexRequest() + .setName("drop_index") + .setUnique(true) + .setKeys(Lists.newArrayList(new WxCloudDatabaseCreateIndexRequest.IndexKey().setDirection("2dsphere").setName("test")) + )), + Lists.newArrayList("add_index2")); + } + + @Test + public void testDatabaseMigrateImport() throws WxErrorException { + final Long result = this.wxMaService.getCloudService().databaseMigrateImport("rcn", "geo", "test.json", + 1, true, 1); + assertThat(result).isGreaterThan(0); + } + + @Test + public void testDatabaseMigrateExport() throws WxErrorException { + final Long result = this.wxMaService.getCloudService().databaseMigrateExport("rcn", "test.json", 1, + "const Point = db.Geo.Point;db.collection('geo').where({age: _.gt(1)}).limit(10).skip(1).orderBy('age','asc').orderBy('name', 'desc')" + + ".field({ name: true }).get()"); + assertThat(result).isGreaterThan(0); + } + + @Test + public void testDatabaseMigrateQueryInfo() throws WxErrorException { + final WxCloudCloudDatabaseMigrateQueryInfoResult result = this.wxMaService.getCloudService() + .databaseMigrateQueryInfo("rcn", 476629L); + assertThat(result).isNotNull(); + System.out.println(result.getFileUrl()); + } + + @Test + public void testUploadFile() throws WxErrorException { + final WxCloudUploadFileResult result = this.wxMaService.getCloudService().uploadFile("rcn", "E:\\MyDocs\\Desktop\\test.json"); + + assertThat(result).isNotNull(); + assertThat(result.getAuthorization()).isNotNull(); + assertThat(result.getToken()).isNotNull(); + assertThat(result.getUrl()).isNotNull(); + assertThat(result.getFileId()).isNotNull(); + assertThat(result.getCosFileId()).isNotNull(); + + } + + @Test + public void testBatchDownloadFile() throws WxErrorException { + final WxCloudBatchDownloadFileResult result = this.wxMaService.getCloudService().batchDownloadFile("rcn", + new String[]{"cloud://rcn.7263-rcn-1258788140/Snipaste_2019-11-13_00-21-27.png", "cloud://rcn.7263-rcn-1258788140/avatar.jpg"}, + new long[]{7200, 6800}); + + assertThat(result).isNotNull(); + assertThat(result.getFileList()).isNotNull(); + assertThat(result.getFileList().size()).isGreaterThan(0); + assertThat(result.getFileList().get(0).getDownloadUrl()).isNotNull(); + assertThat(result.getFileList().get(0).getErrMsg()).isEqualTo("ok"); + assertThat(result.getFileList().get(0).getStatus()).isEqualTo(0); + assertThat(result.getFileList().get(0).getFileId()).isNotNull(); + + } + + @Test + public void testBatchDeleteFile() throws WxErrorException { + final WxCloudBatchDeleteFileResult result = this.wxMaService.getCloudService().batchDeleteFile("rcn", + new String[]{"cloud://rcn.7263-rcn-1258788140/test.json"}); + + assertThat(result).isNotNull(); + assertThat(result.getFileList()).isNotNull(); + assertThat(result.getFileList().size()).isGreaterThan(0); + assertThat(result.getFileList().get(0).getErrMsg()).isEqualTo("ok"); + assertThat(result.getFileList().get(0).getStatus()).isEqualTo(0); + assertThat(result.getFileList().get(0).getFileId()).isNotNull(); + } + + @Test + public void testGetQcloudToken() throws WxErrorException { + final WxCloudGetQcloudTokenResult result = this.wxMaService.getCloudService().getQcloudToken(1800); + + assertThat(result).isNotNull(); + assertThat(result.getSecretId()).isNotNull(); + assertThat(result.getSecretKey()).isNotNull(); + assertThat(result.getToken()).isNotNull(); + assertThat(result.getExpiredTime()).isNotNull(); + } + + @Test + public void testDatabaseCollectionAdd() throws WxErrorException { + this.wxMaService.getCloudService().databaseCollectionAdd("rcn","test_add_collection"); + } + + @Test + public void testDatabaseCollectionDelete() throws WxErrorException { + this.wxMaService.getCloudService().databaseCollectionAdd("rcn","test_delete_collection"); + this.wxMaService.getCloudService().databaseCollectionDelete("rcn","test_delete_collection"); + } + + @Test + public void testDatabaseCollectionGet() throws WxErrorException { + final WxCloudDatabaseCollectionGetResult result = this.wxMaService.getCloudService().databaseCollectionGet("rcn", null, null); + + assertThat(result).isNotNull(); + assertThat(result.getPager()).isNotNull(); + assertThat(result.getPager().getLimit()).isEqualTo(10); + assertThat(result.getPager().getOffset()).isEqualTo(0); + assertThat(result.getPager().getTotal()).isGreaterThan(0); + + assertThat(result.getCollections().length).isGreaterThan(0); + assertThat(result.getCollections()[0].getCount()).isGreaterThan(0); + assertThat(result.getCollections()[0].getName()).isNotNull(); + assertThat(result.getCollections()[0].getSize()).isGreaterThan(0); + assertThat(result.getCollections()[0].getIndexCount()).isGreaterThan(0); + assertThat(result.getCollections()[0].getIndexSize()).isGreaterThan(0); + } +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaExpressServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaExpressServiceImplTest.java new file mode 100644 index 0000000000..6991ad9c25 --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaExpressServiceImplTest.java @@ -0,0 +1,231 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaExpressService; +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.express.*; +import cn.binarywang.wx.miniapp.bean.express.request.*; +import cn.binarywang.wx.miniapp.bean.express.result.WxMaExpressOrderInfoResult; +import cn.binarywang.wx.miniapp.constant.WxMaConstants; +import cn.binarywang.wx.miniapp.test.ApiTestModule; +import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; +import com.google.inject.Inject; +import me.chanjar.weixin.common.error.WxErrorException; +import org.apache.commons.lang3.StringUtils; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import java.util.ArrayList; +import java.util.List; + + +@Guice(modules = ApiTestModule.class) +public class WxMaExpressServiceImplTest { + + @Inject + private WxMaService wxMaService; + + @Test + public void testGetAllDelivery() throws WxErrorException { + final WxMaExpressService service = wxMaService.getExpressService(); + List list = service.getAllDelivery(); + System.out.println(WxMaGsonBuilder.create().toJson(list)); + } + + @Test + public void testGetAllAccount() throws WxErrorException { + final WxMaExpressService service = wxMaService.getExpressService(); + List list = service.getAllAccount(); + System.out.println(WxMaGsonBuilder.create().toJson(list)); + } + + @Test + public void testBindAccount() throws WxErrorException { + final WxMaExpressService service = wxMaService.getExpressService(); + WxMaExpressBindAccountRequest request = WxMaExpressBindAccountRequest.builder() + .deliveryId("YUNDA") + .bizId("******") + .password("*********") + .remarkContent("####") + .type(WxMaConstants.BindAccountType.BIND) + .build(); + service.bindAccount(request); + } + + @Test + public void testGetQuota() throws WxErrorException { + final WxMaExpressService service = wxMaService.getExpressService(); + WxMaExpressBindAccountRequest request = WxMaExpressBindAccountRequest.builder() + .deliveryId("YUNDA") + .bizId("******") + .build(); + Integer quotaNum = service.getQuota(request); + System.out.println(quotaNum); + } + + + + @Test + public void testUpdatePrinter() throws WxErrorException { + final WxMaExpressService service = wxMaService.getExpressService(); + WxMaExpressPrinterUpdateRequest request = WxMaExpressPrinterUpdateRequest.builder() + .openid("*************") + .updateType(WxMaConstants.BindAccountType.UNBIND) + .build(); + service.updatePrinter(request); + } + + @Test + public void testGetPrinter() throws WxErrorException { + final WxMaExpressService service = wxMaService.getExpressService(); + WxMaExpressPrinter printer = service.getPrinter(); + System.out.println(WxMaGsonBuilder.create().toJson(printer)); + } + + @Test + public void testAddOrder() throws WxErrorException { + final WxMaExpressService service = wxMaService.getExpressService(); + + WxMaExpressOrderPerson sender = new WxMaExpressOrderPerson(); + sender.setName("张三"); + sender.setMobile("177****9809"); + sender.setProvince("北京市"); + sender.setCity("朝阳区"); + sender.setArea("朝阳区"); + sender.setAddress("西坝河****C-102"); + + WxMaExpressOrderPerson receiver = new WxMaExpressOrderPerson(); + receiver.setName("李四"); + receiver.setMobile("180****8772"); + receiver.setProvince("北京市"); + receiver.setCity("朝阳区"); + receiver.setArea("朝阳区"); + receiver.setAddress("西坝河北里****101"); + + + WxMaExpressOrderCargo cargo = new WxMaExpressOrderCargo(); + List detailList = new ArrayList<>(); + List shopNames = new ArrayList<>(); + Integer goodsCount = 0; + for (int i = 0; i < 4; i++) { + WxMaExpressOrderCargoDetail detail = new WxMaExpressOrderCargoDetail(); + String shopName = "商品_"+i; + detail.setName(shopName); + detail.setCount(1); + detailList.add(detail); + shopNames.add(shopName); + goodsCount ++; + } + cargo.setCount(detailList.size()); + cargo.setWeight(5); + cargo.setSpaceHeight(10); + cargo.setSpaceLength(10); + cargo.setSpaceWidth(10); + cargo.setDetailList(detailList); + + + WxMaExpressOrderShop shop = new WxMaExpressOrderShop(); + shop.setWxaPath("/index/index?from=waybill&id=01234567890123456789"); + shop.setGoodsName(StringUtils.join(shopNames,"&")); + shop.setGoodsCount(goodsCount); + shop.setImgUrl("https://mmbiz.qpic.cn/mmbiz_png/OiaFLUqewuIDNQnTiaCInIG8ibdosYHhQHPbXJUrqYSNIcBL60vo4LIjlcoNG1QPkeH5GWWEB41Ny895CokeAah8A/640"); + + WxMaExpressDelivery.ServiceType serviceType = new WxMaExpressDelivery.ServiceType(); + serviceType.setServiceName("test_service_name"); + serviceType.setServiceType(1); + + WxMaExpressAddOrderRequest request = WxMaExpressAddOrderRequest.builder() + .addSource(WxMaConstants.OrderAddSource.MINI_PROGRAM) + .orderId("test201911271429004") + .openid("oAg_-0PDUPuLbQw9V9kXE9OkU-Vo") + .deliveryId("TEST") + .bizId("test_biz_id") + .customRemark("") + .expectTime(0L) + .sender(sender) + .receiver(receiver) + .cargo(cargo) + .shop(shop) + .insured(WxMaExpressOrderInsured.builder().build()) + .service(serviceType) + .build(); + + WxMaExpressOrderInfoResult result = service.addOrder(request); + System.out.println(WxMaGsonBuilder.create().toJson(result)); + } + + @Test + public void testBatchGetOrder() throws WxErrorException { + final WxMaExpressService service = wxMaService.getExpressService(); + List requests = new ArrayList<>(); + + List orderIds = new ArrayList<>(); + orderIds.add("test201911271429000"); + + List waybillIds = new ArrayList<>(); + waybillIds.add("test201911271429000_1574836404_waybill_id"); + for (int i = 0; i < orderIds.size(); i++) { + WxMaExpressGetOrderRequest request = WxMaExpressGetOrderRequest.builder() + .orderId(orderIds.get(i)) + .deliveryId("TEST") + .waybillId(waybillIds.get(i)) + .build(); + requests.add(request); + } + + List results = service.batchGetOrder(requests); + System.out.println(WxMaGsonBuilder.create().toJson(results)); + } + + @Test + public void testCancelOrder() throws WxErrorException { + final WxMaExpressService service = wxMaService.getExpressService(); + WxMaExpressGetOrderRequest request = WxMaExpressGetOrderRequest.builder() + .orderId("test201911271429000") + .deliveryId("TEST") + .waybillId("test201911271429000_1574836404_waybill_id") + .openid("oAg_-0PDUPuLbQw9V9kXE9OkU-Vo") + .build(); + service.cancelOrder(request); + } + + + @Test + public void testGetOrder() throws WxErrorException { + final WxMaExpressService service = wxMaService.getExpressService(); + WxMaExpressGetOrderRequest request = WxMaExpressGetOrderRequest.builder() + .orderId("test201911271429000") + .deliveryId("TEST") + .waybillId("test201911271429000_1574836404_waybill_id") + .openid("oAg_-0PDUPuLbQw9V9kXE9OkU-Vo") + .build(); + WxMaExpressOrderInfoResult result = service.getOrder(request); + System.out.println(WxMaGsonBuilder.create().toJson(result)); + } + + + @Test + public void testGetPath() throws WxErrorException { + final WxMaExpressService service = wxMaService.getExpressService(); + WxMaExpressGetOrderRequest request = WxMaExpressGetOrderRequest.builder() + .orderId("test201911271429000") + .deliveryId("TEST") + .waybillId("test201911271429000_1574836404_waybill_id") + .openid("oAg_-0PDUPuLbQw9V9kXE9OkU-Vo") + .build(); + WxMaExpressPath path = service.getPath(request); + System.out.println(WxMaGsonBuilder.create().toJson(path)); + } + + @Test + public void testTestUpdateOrder() throws WxErrorException { + final WxMaExpressService service = wxMaService.getExpressService(); + WxMaExpressTestUpdateOrderRequest request = WxMaExpressTestUpdateOrderRequest.builder() + .orderId("test201911271429000") + .waybillId("test201911271429000_1574836404_waybill_id") + .actionTime(1574850455L) + .actionType(300002) + .actionMsg("开始派送") + .build(); + service.testUpdateOrder(request); + } +} 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 f5f62cc57f..3ca42b46fc 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 @@ -1,17 +1,24 @@ package cn.binarywang.wx.miniapp.api.impl; -import java.text.SimpleDateFormat; -import java.util.Date; - +import cn.binarywang.wx.miniapp.api.WxMaService; import cn.binarywang.wx.miniapp.bean.*; +import cn.binarywang.wx.miniapp.constant.WxMaConstants; import org.testng.annotations.*; import cn.binarywang.wx.miniapp.api.WxMaService; import cn.binarywang.wx.miniapp.test.ApiTestModule; import cn.binarywang.wx.miniapp.test.TestConfig; import com.google.common.collect.Lists; +import com.google.gson.JsonObject; import com.google.inject.Inject; import me.chanjar.weixin.common.error.WxErrorException; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import java.text.SimpleDateFormat; +import java.util.Date; + +import static org.assertj.core.api.Assertions.assertThat; /** * 测试消息相关接口 @@ -25,7 +32,8 @@ public class WxMaMsgServiceImplTest { @Inject private WxMaService wxService; - public void testSendKefuMessage() throws WxErrorException { + @Test + public void testSendKefuMsg() throws WxErrorException { TestConfig config = (TestConfig) this.wxService.getWxMaConfig(); WxMaKefuMessage message = WxMaKefuMessage.newTextBuilder() .toUser(config.getOpenid()) @@ -64,9 +72,11 @@ public void testSendSubscribeMsg() throws WxErrorException { WxMaSubscribeMessage message = new WxMaSubscribeMessage(); message.setTemplateId(config.getTemplateId()); message.setToUser(config.getOpenid()); - message.addData(new WxMaSubscribeData("thing1", "苹果到货啦")); - message.addData(new WxMaSubscribeData("amount3", "¥5")); - message.addData(new WxMaSubscribeData("thing5", "记得领取哦")); + message.setLang(WxMaConstants.MiniprogramLang.ZH_CN); + message.setMiniprogramState(WxMaConstants.MiniprogramState.FORMAL); + message.addData(new WxMaSubscribeMessage.Data("thing1", "苹果到货啦")); + message.addData(new WxMaSubscribeMessage.Data("amount3", "¥5")); + message.addData(new WxMaSubscribeMessage.Data("thing5", "记得领取哦")); this.wxService.getMsgService().sendSubscribeMsg(message); } @@ -74,7 +84,7 @@ public void testSendSubscribeMsg() throws WxErrorException { @Test public void testSendUniformMsg() throws WxErrorException { TestConfig config = (TestConfig) this.wxService.getWxMaConfig(); - WxMaUniformMessage message = WxMaUniformMessage.builder() + WxMaUniformMessage message = WxMaUniformMessage.builder() .isMpTemplateMsg(false) .toUser(config.getOpenid()) .page("page/page/index") @@ -89,4 +99,22 @@ public void testSendUniformMsg() throws WxErrorException { this.wxService.getMsgService().sendUniformMsg(message); } + + @Test + public void testCreateUpdatableMessageActivityId() throws WxErrorException { + final JsonObject jsonObject = this.wxService.getMsgService().createUpdatableMessageActivityId(); + assertThat(jsonObject).isNotNull(); + assertThat(jsonObject.get("activity_id")).isNotNull(); + System.out.println(jsonObject.get("activity_id")); + assertThat(jsonObject.get("expiration_time")).isNotNull(); + } + + @Test + public void testSetUpdatableMsg() throws WxErrorException { + this.wxService.getMsgService().setUpdatableMsg(new WxMaUpdatableMsg() + .setActivityId("1048_4f61uDloWPZl9pAs1dGx07vDiHKZ7FwJ0suohS1iMH5z8zhFktYk4nRqqBY~") + .setTargetState(1) + .setTemplateInfo(new WxMaUpdatableMsg.TemplateInfo() + .setParameterList(Lists.newArrayList(new WxMaUpdatableMsg.Parameter().setName("member_count").setValue("1"))))); + } } diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaSubscribeServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaSubscribeServiceImplTest.java new file mode 100644 index 0000000000..e40cf10e71 --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaSubscribeServiceImplTest.java @@ -0,0 +1,65 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.api.WxMaSubscribeService; +import cn.binarywang.wx.miniapp.bean.template.WxMaPubTemplateTitleListResult; +import cn.binarywang.wx.miniapp.test.ApiTestModule; +import com.google.common.collect.Lists; +import com.google.inject.Inject; +import me.chanjar.weixin.common.error.WxErrorException; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * 测试类. + * + * @author Binary Wang + * @date 2019-12-15 + */ +@Test +@Guice(modules = ApiTestModule.class) +public class WxMaSubscribeServiceImplTest { + @Inject + protected WxMaService wxService; + + @Test + public void testGetPubTemplateTitleList() throws WxErrorException { + WxMaPubTemplateTitleListResult result = this.wxService.getSubscribeService().getPubTemplateTitleList(new String[]{"2", "616"}, 0, 30); + System.out.println(result); + + } + + @Test + public void testGetPubTemplateKeyWordsById() throws WxErrorException { + final List result = this.wxService.getSubscribeService().getPubTemplateKeyWordsById("99"); + System.out.println(result); + } + + @Test + public void testAddTemplate() throws WxErrorException { + final String templateId = this.wxService.getSubscribeService().addTemplate("401", Lists.newArrayList(1, 2), "测试数据"); + System.out.println(templateId); + } + + @Test + public void testGetTemplateList() throws WxErrorException { + final List templateList = this.wxService.getSubscribeService().getTemplateList(); + System.out.println(templateList); + } + + @Test + public void testDelTemplate() throws WxErrorException { + this.wxService.getSubscribeService().delTemplate("priTmplId"); + } + + @Test + public void testGetCategory() throws WxErrorException { + final List categoryData = this.wxService.getSubscribeService().getCategory(); + assertThat(categoryData).isNotNull(); + System.out.println(categoryData); + } +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaTemplateServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaTemplateServiceImplTest.java index 5dfa86a8b0..57896685bb 100644 --- a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaTemplateServiceImplTest.java +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaTemplateServiceImplTest.java @@ -17,7 +17,6 @@ @Test @Guice(modules = ApiTestModule.class) public class WxMaTemplateServiceImplTest { - @Inject protected WxMaService wxService; @@ -36,7 +35,7 @@ public void testFindTemplateLibraryKeywordList() throws Exception { } @Test - public void testAddTemplate() throws Exception{ + public void testAddTemplate() throws Exception { List list = Lists.newArrayList(); list.add(1); list.add(20); @@ -48,7 +47,7 @@ public void testAddTemplate() throws Exception{ } @Test - public void testFindTemplateList() throws Exception{ + public void testFindTemplateList() throws Exception { WxMaTemplateListResult result = this.wxService.getTemplateService().findTemplateList(0, 20); System.out.println(result); } diff --git a/weixin-java-mp/pom.xml b/weixin-java-mp/pom.xml index 7db1859375..7ee22b56c2 100644 --- a/weixin-java-mp/pom.xml +++ b/weixin-java-mp/pom.xml @@ -7,7 +7,7 @@ com.github.binarywang wx-java - 3.6.0 + 3.7.0 weixin-java-mp @@ -96,4 +96,35 @@ + + + native-image + + false + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.5.1 + + + cn.binarywang.wx.graal.GraalProcessor,lombok.launch.AnnotationProcessorHider$AnnotationProcessor,lombok.launch.AnnotationProcessorHider$ClaimingProcessor + + + + com.github.binarywang + weixin-graal + ${project.version} + + + + + + + + + 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 2202f5568b..f0bd0b8f04 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 @@ -283,4 +283,15 @@ WxMpCardLandingPageCreateResult createLandingPage(WxMpCardLandingPageCreateReque void cardSelfConsumeCellSet(String cardId, Boolean isOpen, Boolean needVerifyCod, Boolean needRemarkAmount) throws WxErrorException; + /** + * 获取用户已领取卡券接口 + * https://developers.weixin.qq.com/doc/offiaccount/Cards_and_Offer/Managing_Coupons_Vouchers_and_Cards.html#1 + * + * @param openId 需要查询的用户openid + * @param cardId 卡券ID。不填写时默认查询当前appid下的卡券 + * @return + * @throws WxErrorException + */ + WxUserCardListResult getUserCardList(String openId, String cardId) throws WxErrorException; + } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpCommentService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpCommentService.java index 423f61a739..c4ba7ca326 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpCommentService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpCommentService.java @@ -5,7 +5,7 @@ /** * 图文消息留言管理接口. - * https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1494572718_WzHIY + * https://developers.weixin.qq.com/doc/offiaccount/Comments_management/Image_Comments_Management_Interface.html * * @author Binary Wang * @date 2019-06-16 @@ -19,7 +19,7 @@ public interface WxMpCommentService { * @param index 多图文时,用来指定第几篇图文,从0开始,不带默认操作该msg_data_id的第一篇图文 * @throws WxErrorException 异常 */ - void open(Integer msgDataId, Integer index) throws WxErrorException; + void open(String msgDataId, Integer index) throws WxErrorException; /** * 关闭已群发文章评论. @@ -29,7 +29,7 @@ public interface WxMpCommentService { * @param index 多图文时,用来指定第几篇图文,从0开始,不带默认操作该msg_data_id的第一篇图文 * @throws WxErrorException 异常 */ - void close(Integer msgDataId, Integer index) throws WxErrorException; + void close(String msgDataId, Integer index) throws WxErrorException; /** * 查看指定文章的评论数据. @@ -43,5 +43,61 @@ public interface WxMpCommentService { * @return 评论列表数据 * @throws WxErrorException 异常 */ - WxMpCommentListVo list(Integer msgDataId, Integer index, int begin, int count, int type) throws WxErrorException; + WxMpCommentListVo list(String msgDataId, Integer index, int begin, int count, int type) throws WxErrorException; + + /** + * 2.4 将评论标记精选. + * 接口调用请求: POST https://api.weixin.qq.com/cgi-bin/comment/markelect?access_token=ACCESS_TOKEN + * + * @param msgDataId 群发返回的msg_data_id + * @param index 多图文时,用来指定第几篇图文,从0开始,不带默认操作该msg_data_id的第一篇图文 + * @param userCommentId 用户评论id + * @throws WxErrorException 异常 + */ + void markElect(String msgDataId, Integer index, Long userCommentId) throws WxErrorException; + + /** + * 2.5 将评论取消精选. + * 接口调用请求: POST https://api.weixin.qq.com/cgi-bin/comment/unmarkelect?access_token=ACCESS_TOKEN + * + * @param msgDataId 群发返回的msg_data_id + * @param index 多图文时,用来指定第几篇图文,从0开始,不带默认操作该msg_data_id的第一篇图文 + * @param userCommentId 用户评论id + * @throws WxErrorException 异常 + */ + void unmarkElect(String msgDataId, Integer index, Long userCommentId) throws WxErrorException; + + /** + * 2.6 删除评论. + * 接口调用请求: POST https://api.weixin.qq.com/cgi-bin/comment/delete?access_token=ACCESS_TOKEN + * + * @param msgDataId 群发返回的msg_data_id + * @param index 多图文时,用来指定第几篇图文,从0开始,不带默认操作该msg_data_id的第一篇图文 + * @param userCommentId 用户评论id + * @throws WxErrorException 异常 + */ + void delete(String msgDataId, Integer index, Long userCommentId) throws WxErrorException; + + /** + * 2.7 回复评论. + * 接口调用请求: POST https://api.weixin.qq.com/cgi-bin/comment/reply/add?access_token=ACCESS_TOKEN + * + * @param msgDataId 群发返回的msg_data_id + * @param index 多图文时,用来指定第几篇图文,从0开始,不带默认操作该msg_data_id的第一篇图文 + * @param userCommentId 用户评论id + * @param content 回复内容 + * @throws WxErrorException 异常 + */ + void replyAdd(String msgDataId, Integer index, Long userCommentId, String content) throws WxErrorException; + + /** + * 2.8 删除回复. + * 接口调用请求: POST https://api.weixin.qq.com/cgi-bin/comment/reply/delete?access_token=ACCESS_TOKEN + * + * @param msgDataId 群发返回的msg_data_id + * @param index 多图文时,用来指定第几篇图文,从0开始,不带默认操作该msg_data_id的第一篇图文 + * @param userCommentId 用户评论id + * @throws WxErrorException 异常 + */ + void replyDelete(String msgDataId, Integer index, Long userCommentId) throws WxErrorException; } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpCardServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpCardServiceImpl.java index 5cdc605baa..1bf61ffe39 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 @@ -81,6 +81,7 @@ public WxCardApiSignature createCardApiSignature(String... optionalSignParam) th signParams[optionalSignParam.length + 1] = nonceStr; signParams[optionalSignParam.length + 2] = cardApiTicket; StringBuilder sb = new StringBuilder(); + Arrays.sort(signParams); for (String a : signParams) { sb.append(a); } @@ -351,6 +352,16 @@ public void cardSelfConsumeCellSet(String cardId, Boolean isOpen, } + @Override + public WxUserCardListResult getUserCardList(String openId, String cardId) throws WxErrorException { + JsonObject param = new JsonObject(); + param.addProperty("openid", openId); + param.addProperty("card_id", cardId); + String response = this.wxMpService.post(WxMpApiUrl.Card.CARD_USER_CARD_LIST, param.toString()); + return WxUserCardListResult.fromJson(response); + } + + private void checkCardId(String cardId) throws WxErrorException { if (StringUtils.isEmpty(cardId)) { throw new WxErrorException(WxError.builder().errorCode(41012).errorMsg("cardId不能为空").build()); diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpCommentServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpCommentServiceImpl.java index 8d97af36e3..8f287a80f1 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpCommentServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpCommentServiceImpl.java @@ -18,7 +18,7 @@ public class WxMpCommentServiceImpl implements WxMpCommentService { private final WxMpService wxMpService; @Override - public void open(Integer msgDataId, Integer index) throws WxErrorException { + public void open(String msgDataId, Integer index) throws WxErrorException { JsonObject json = new JsonObject(); json.addProperty("msg_data_id", msgDataId); if (index != null) { @@ -29,7 +29,7 @@ public void open(Integer msgDataId, Integer index) throws WxErrorException { } @Override - public void close(Integer msgDataId, Integer index) throws WxErrorException { + public void close(String msgDataId, Integer index) throws WxErrorException { JsonObject json = new JsonObject(); json.addProperty("msg_data_id", msgDataId); if (index != null) { @@ -40,7 +40,7 @@ public void close(Integer msgDataId, Integer index) throws WxErrorException { } @Override - public WxMpCommentListVo list(Integer msgDataId, Integer index, int begin, int count, int type) throws WxErrorException { + public WxMpCommentListVo list(String msgDataId, Integer index, int begin, int count, int type) throws WxErrorException { JsonObject json = new JsonObject(); json.addProperty("msg_data_id", msgDataId); json.addProperty("begin", begin); @@ -53,4 +53,47 @@ public WxMpCommentListVo list(Integer msgDataId, Integer index, int begin, int c return WxMpCommentListVo.fromJson(this.wxMpService.post(LIST, json.toString())); } + + @Override + public void markElect(String msgDataId, Integer index, Long userCommentId) throws WxErrorException { + JsonObject json = this.buildJson(msgDataId, index, userCommentId); + this.wxMpService.post(MARK_ELECT, json.toString()); + } + + @Override + public void unmarkElect(String msgDataId, Integer index, Long userCommentId) throws WxErrorException { + JsonObject json = this.buildJson(msgDataId, index, userCommentId); + this.wxMpService.post(UNMARK_ELECT, json.toString()); + } + + @Override + public void delete(String msgDataId, Integer index, Long userCommentId) throws WxErrorException { + JsonObject json = this.buildJson(msgDataId, index, userCommentId); + + this.wxMpService.post(DELETE, json.toString()); + } + + @Override + public void replyAdd(String msgDataId, Integer index, Long userCommentId, String content) throws WxErrorException { + JsonObject json = this.buildJson(msgDataId, index, userCommentId); + json.addProperty("content", content); + + this.wxMpService.post(REPLY_ADD, json.toString()); + } + + @Override + public void replyDelete(String msgDataId, Integer index, Long userCommentId) throws WxErrorException { + JsonObject json = this.buildJson(msgDataId, index, userCommentId); + this.wxMpService.post(REPLY_DELETE, json.toString()); + } + + private JsonObject buildJson(String msgDataId, Integer index, Long userCommentId) { + JsonObject json = new JsonObject(); + json.addProperty("msg_data_id", msgDataId); + json.addProperty("user_comment_id", userCommentId); + if (index != null) { + json.addProperty("index", index); + } + return json; + } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/UserCard.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/UserCard.java new file mode 100644 index 0000000000..5985988e01 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/UserCard.java @@ -0,0 +1,34 @@ +package me.chanjar.weixin.mp.bean.card; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +/** + * 用户已领卡圈对象 + * @author yang229 + * @date 2019/12/22 + */ +@Data +public class UserCard implements java.io.Serializable { + /** + * 用户卡券code码 + */ + @SerializedName("code") + private String code; + + /** + * 卡券ID + */ + @SerializedName("card_id") + private String cardId; + + public static UserCard fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, UserCard.class); + } + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxUserCardListResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxUserCardListResult.java new file mode 100644 index 0000000000..9133a32f17 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxUserCardListResult.java @@ -0,0 +1,38 @@ +package me.chanjar.weixin.mp.bean.card; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.bean.result.WxMpResult; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.util.List; + +/** + * 用户已领卡券返回 + * @author yang229 + * @date 2019/12/22 + */ +@Data +public class WxUserCardListResult extends WxMpResult implements java.io.Serializable { + + /** + * 卡券列表 + */ + @SerializedName("card_list") + private List cardList; + + /** + * 是否有可用的朋友的券 + */ + @SerializedName("has_share_card") + private Boolean hasShareCard; + + public static WxUserCardListResult fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, WxUserCardListResult.class); + } + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialNews.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialNews.java index e2f27d9ec4..ba49a1d834 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialNews.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialNews.java @@ -61,7 +61,9 @@ public String toString() { * @author chanjarster */ @Data - public static class WxMpMaterialNewsArticle { + public static class WxMpMaterialNewsArticle implements Serializable{ + + private static final long serialVersionUID = -635384661692321171L; /** * (必填) 图文消息缩略图的media_id,可以在基础支持-上传多媒体文件接口中获得. */ diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateMessage.java index f04f8c91a8..99c3df358e 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateMessage.java @@ -83,7 +83,7 @@ public static class MiniProgram implements Serializable { * 是否使用path,否则使用pagepath. * 加入此字段是基于微信官方接口变化多端的考虑 */ - private boolean usePath = true; + private boolean usePath = false; } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/enums/WxMpApiUrl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/enums/WxMpApiUrl.java index 5963af929d..cbe3d4df92 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/enums/WxMpApiUrl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/enums/WxMpApiUrl.java @@ -596,6 +596,11 @@ enum Card implements WxMpApiUrl { * 设置自助核销接口 */ CARD_SELF_CONSUME_CELL_SET(API_DEFAULT_HOST_URL, "/card/selfconsumecell/set"), + + /** + * 获取用户已领取卡券接口 + */ + CARD_USER_CARD_LIST(API_DEFAULT_HOST_URL, "/card/user/getcardlist"), ; private String prefix; @@ -997,7 +1002,32 @@ enum Comment implements WxMpApiUrl { /** * 查看指定文章的评论数据. */ - LIST(API_DEFAULT_HOST_URL, "/cgi-bin/comment/list"); + LIST(API_DEFAULT_HOST_URL, "/cgi-bin/comment/list"), + + /** + * 将评论标记精选. + */ + MARK_ELECT(API_DEFAULT_HOST_URL, "/cgi-bin/comment/markelect"), + + /** + * 将评论取消精选. + */ + UNMARK_ELECT(API_DEFAULT_HOST_URL, "/cgi-bin/comment/unmarkelect"), + + /** + * 删除评论. + */ + DELETE(API_DEFAULT_HOST_URL, "/cgi-bin/comment/delete"), + + /** + * 回复评论. + */ + REPLY_ADD(API_DEFAULT_HOST_URL, "/cgi-bin/comment/reply/add"), + + /** + * 删除回复. + */ + REPLY_DELETE(API_DEFAULT_HOST_URL, "/cgi-bin/comment/reply/delete"); private String prefix; private String path; diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpIndustryGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpIndustryGsonAdapter.java index 0c45719548..04d9d7dadd 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpIndustryGsonAdapter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpIndustryGsonAdapter.java @@ -28,7 +28,12 @@ public WxMpTemplateIndustry deserialize(JsonElement jsonElement, Type type, Json } private WxMpTemplateIndustryEnum convertFromJson(JsonObject json) { - return WxMpTemplateIndustryEnum.findBySecondary(GsonHelper.getString(json, "second_class")); + String secondClass = GsonHelper.getString(json, "second_class"); + if (secondClass.contains("|")) { + secondClass = secondClass.split("\\|")[1]; + } + + return WxMpTemplateIndustryEnum.findBySecondary(secondClass); } } 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 948af00cd9..4fa75b7f98 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 @@ -227,4 +227,13 @@ public void testCreateQrcodeCard2() { @Test public void testCreateLandingPage() { } + + @Test + public void testGetUserCardList() throws WxErrorException { + String openId = "ou7Gr5sJZgFGgj38sRCNQg5pc3Fc"; + String cardId = "pu7Gr5secJXPkxBeuYUhmp8TYsuY"; + WxUserCardListResult result = this.wxService.getCardService().getUserCardList(openId, cardId); + assertTrue(result.isSuccess()); + System.out.println(result); + } } diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpCommentServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpCommentServiceImplTest.java index efbf6e1814..8efb70f9e3 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpCommentServiceImplTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpCommentServiceImplTest.java @@ -9,9 +9,7 @@ import org.testng.annotations.Guice; import org.testng.annotations.Test; -import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Comment.LIST; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; @@ -31,14 +29,14 @@ public class WxMpCommentServiceImplTest { @Test public void testOpen() throws WxErrorException { - this.wxService.getCommentService().open(1, null); - this.wxService.getCommentService().open(1, 0); + this.wxService.getCommentService().open("1", null); + this.wxService.getCommentService().open("1", 0); } @Test public void testClose() throws WxErrorException { - this.wxService.getCommentService().close(1000000001, null); - this.wxService.getCommentService().close(1, 0); + this.wxService.getCommentService().close("1000000001", null); + this.wxService.getCommentService().close("1", 0); } @Test @@ -66,7 +64,7 @@ public void testList() throws WxErrorException { WxMpCommentService commentService = new WxMpCommentServiceImpl(wxService); doReturn(expectedResponse).when(wxService).post(anyString(), anyString()); - final WxMpCommentListVo commentListVo = commentService.list(1, 1, 1, 1, 1); + final WxMpCommentListVo commentListVo = commentService.list("1", 1, 1, 1, 1); assertThat(commentListVo).isNotNull(); System.out.println(commentListVo); assertThat(commentListVo.getTotal()).isEqualTo(1); @@ -74,4 +72,29 @@ public void testList() throws WxErrorException { assertThat(commentListVo.getComment().get(0).getReply()).isNotNull(); } + + @Test + public void testMarkElect() throws WxErrorException { + this.wxService.getCommentService().markElect("1000000001", null, 1L); + } + + @Test + public void testUnmarkElect() throws WxErrorException { + this.wxService.getCommentService().unmarkElect("1000000001", null, 1L); + } + + @Test + public void testDelete() throws WxErrorException { + this.wxService.getCommentService().delete("1000000001", null, 1L); + } + + @Test + public void testReplyAdd() throws WxErrorException { + this.wxService.getCommentService().replyAdd("1000000001", null, 1L, "haha"); + } + + @Test + public void testReplyADelete() throws WxErrorException { + this.wxService.getCommentService().replyDelete("1000000001", null, 1L); + } } diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMarketingServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMarketingServiceImplTest.java index 900453b669..3d1b74e6c3 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMarketingServiceImplTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMarketingServiceImplTest.java @@ -1,5 +1,12 @@ package me.chanjar.weixin.mp.api.impl; +import com.google.common.collect.Lists; +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.bean.marketing.WxMpUserAction; +import org.testng.annotations.Guice; import org.testng.annotations.Test; import static org.testng.Assert.*; @@ -10,7 +17,11 @@ * @author Binary Wang * @date 2019-07-14 */ +@Test +@Guice(modules = ApiTestModule.class) public class WxMpMarketingServiceImplTest { + @Inject + protected WxMpService wxService; @Test public void testAddUserActionSets() { @@ -21,7 +32,8 @@ public void testGetUserActionSets() { } @Test - public void testAddUserAction() { + public void testAddUserAction() throws WxErrorException { + this.wxService.getMarketingService().addUserAction(Lists.newArrayList(new WxMpUserAction())); } @Test diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateIndustryTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateIndustryTest.java new file mode 100644 index 0000000000..d5affa288b --- /dev/null +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateIndustryTest.java @@ -0,0 +1,23 @@ +package me.chanjar.weixin.mp.bean.template; + +import org.testng.annotations.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.testng.Assert.*; + +/** + * 测试类. + * + * @author Binary Wang + * @date 2020-02-29 + */ +public class WxMpTemplateIndustryTest { + + @Test + public void testFromJson() { + String json="{\"primary_industry\":{\"first_class\":\"IT科技\",\"second_class\":\"IT软件与服务\"},\"secondary_industry\":{\"first_class\":\"房地产\",\"second_class\":\"房地产|建筑\"}}"; + final WxMpTemplateIndustry industry = WxMpTemplateIndustry.fromJson(json); + assertThat(industry).isNotNull(); + System.out.println(industry); + } +} diff --git a/weixin-java-open/pom.xml b/weixin-java-open/pom.xml index d25b508380..825a1e1a89 100644 --- a/weixin-java-open/pom.xml +++ b/weixin-java-open/pom.xml @@ -7,7 +7,7 @@ com.github.binarywang wx-java - 3.6.0 + 3.7.0 weixin-java-open @@ -87,6 +87,10 @@ org.projectlombok lombok + + org.redisson + redisson + @@ -103,5 +107,35 @@ + + + native-image + + false + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.5.1 + + + cn.binarywang.wx.graal.GraalProcessor,lombok.launch.AnnotationProcessorHider$AnnotationProcessor,lombok.launch.AnnotationProcessorHider$ClaimingProcessor + + + + com.github.binarywang + weixin-graal + ${project.version} + + + + + + + + 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 40833c1b98..79969228ad 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 @@ -128,4 +128,13 @@ public interface WxOpenConfigStorage { * @param expiresInSeconds 过期时间,以秒为单位 */ void updateCardApiTicket(String appId, String cardApiTicket, int expiresInSeconds); + + /** + * 设置第三方平台基础信息 + * @param componentAppId 第三方平台 appid + * @param componentAppSecret 第三方平台 appsecret + * @param componentToken 消息校验Token + * @param componentAesKey 消息加解密Key + */ + void setWxOpenInfo(String componentAppId, String componentAppSecret, String componentToken, String componentAesKey); } diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/AbstractWxOpenInRedisConfigStorage.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/AbstractWxOpenInRedisConfigStorage.java new file mode 100644 index 0000000000..0d0b1bf255 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/AbstractWxOpenInRedisConfigStorage.java @@ -0,0 +1,47 @@ +package me.chanjar.weixin.open.api.impl; + + +import org.apache.commons.lang3.StringUtils; + +/** + * @author yangyidian + * @date 2020/01/09 + **/ +public abstract class AbstractWxOpenInRedisConfigStorage extends WxOpenInMemoryConfigStorage { + protected final static String COMPONENT_VERIFY_TICKET_KEY = "wechat_component_verify_ticket:"; + protected final static String COMPONENT_ACCESS_TOKEN_KEY = "wechat_component_access_token:"; + + protected final static String AUTHORIZER_REFRESH_TOKEN_KEY = "wechat_authorizer_refresh_token:"; + protected final static String AUTHORIZER_ACCESS_TOKEN_KEY = "wechat_authorizer_access_token:"; + protected final static String JSAPI_TICKET_KEY = "wechat_jsapi_ticket:"; + protected final static String CARD_API_TICKET_KEY = "wechat_card_api_ticket:"; + + /** + * redis 存储的 key 的前缀,可为空 + */ + protected String keyPrefix; + protected String componentVerifyTicketKey; + protected String componentAccessTokenKey; + protected String authorizerRefreshTokenKey; + protected String authorizerAccessTokenKey; + protected String jsapiTicketKey; + protected String cardApiTicket; + + @Override + public void setComponentAppId(String componentAppId) { + super.setComponentAppId(componentAppId); + String prefix = StringUtils.isBlank(keyPrefix) ? "" : + (StringUtils.endsWith(keyPrefix, ":") ? keyPrefix : (keyPrefix + ":")); + componentVerifyTicketKey = prefix + COMPONENT_VERIFY_TICKET_KEY.concat(componentAppId); + componentAccessTokenKey = prefix + COMPONENT_ACCESS_TOKEN_KEY.concat(componentAppId); + authorizerRefreshTokenKey = prefix + AUTHORIZER_REFRESH_TOKEN_KEY.concat(componentAppId); + authorizerAccessTokenKey = prefix + AUTHORIZER_ACCESS_TOKEN_KEY.concat(componentAppId); + this.jsapiTicketKey = JSAPI_TICKET_KEY.concat(componentAppId); + this.cardApiTicket = CARD_API_TICKET_KEY.concat(componentAppId); + } + + protected String getKey(String prefix, String appId) { + return prefix.endsWith(":") ? prefix.concat(appId) : prefix.concat(":").concat(appId); + } + +} 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 c981147033..491dc6b7bc 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,6 +2,7 @@ import cn.binarywang.wx.miniapp.config.WxMaConfig; +import lombok.Data; import me.chanjar.weixin.common.bean.WxAccessToken; import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; import me.chanjar.weixin.mp.bean.WxMpHostConfig; @@ -23,6 +24,7 @@ * * @author 007 */ +@Data public class WxOpenInMemoryConfigStorage implements WxOpenConfigStorage { private String componentAppId; private String componentAppSecret; @@ -43,60 +45,7 @@ public class WxOpenInMemoryConfigStorage implements WxOpenConfigStorage { private Map jsapiTickets = new ConcurrentHashMap<>(); private Map cardApiTickets = new ConcurrentHashMap<>(); - @Override - public String getComponentAppId() { - return componentAppId; - } - - @Override - public void setComponentAppId(String componentAppId) { - this.componentAppId = componentAppId; - } - - @Override - public String getComponentAppSecret() { - return componentAppSecret; - } - - @Override - public void setComponentAppSecret(String componentAppSecret) { - this.componentAppSecret = componentAppSecret; - } - - @Override - public String getComponentToken() { - return componentToken; - } - - @Override - public void setComponentToken(String componentToken) { - this.componentToken = componentToken; - } - - @Override - public String getComponentAesKey() { - return componentAesKey; - } - @Override - public void setComponentAesKey(String componentAesKey) { - this.componentAesKey = componentAesKey; - } - - @Override - public String getComponentVerifyTicket() { - return componentVerifyTicket; - } - - @Override - public void setComponentVerifyTicket(String componentVerifyTicket) { - this.componentVerifyTicket = componentVerifyTicket; - } - - @Override - public String getComponentAccessToken() { - return componentAccessToken; - } @Override public boolean isComponentAccessTokenExpired() { @@ -113,51 +62,6 @@ public void updateComponentAccessToken(WxOpenComponentAccessToken componentAcces updateComponentAccessToken(componentAccessToken.getComponentAccessToken(), componentAccessToken.getExpiresIn()); } - @Override - public String getHttpProxyHost() { - return httpProxyHost; - } - - public void setHttpProxyHost(String httpProxyHost) { - this.httpProxyHost = httpProxyHost; - } - - @Override - public int getHttpProxyPort() { - return httpProxyPort; - } - - public void setHttpProxyPort(int httpProxyPort) { - this.httpProxyPort = httpProxyPort; - } - - @Override - public String getHttpProxyUsername() { - return httpProxyUsername; - } - - public void setHttpProxyUsername(String httpProxyUsername) { - this.httpProxyUsername = httpProxyUsername; - } - - @Override - public String getHttpProxyPassword() { - return httpProxyPassword; - } - - public void setHttpProxyPassword(String httpProxyPassword) { - this.httpProxyPassword = httpProxyPassword; - } - - @Override - public ApacheHttpClientBuilder getApacheHttpClientBuilder() { - return apacheHttpClientBuilder; - } - - public ApacheHttpClientBuilder setApacheHttpClientBuilder(ApacheHttpClientBuilder apacheHttpClientBuilder) { - return this.apacheHttpClientBuilder = apacheHttpClientBuilder; - } - @Override public WxMpConfigStorage getWxMpConfigStorage(String appId) { return new WxOpenInnerConfigStorage(this, appId); @@ -174,6 +78,14 @@ public void updateComponentAccessToken(String componentAccessToken, int expiresI this.componentExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L; } + @Override + public void setWxOpenInfo(String componentAppId, String componentAppSecret, String componentToken, String componentAesKey) { + setComponentAppId(componentAppId); + setComponentAppSecret(componentAppSecret); + setComponentToken(componentToken); + setComponentAesKey(componentAesKey); + } + @Override public boolean autoRefreshToken() { return true; @@ -546,7 +458,7 @@ public ApacheHttpClientBuilder getApacheHttpClientBuilder() { @Override public boolean autoRefreshToken() { - return true; + return wxOpenConfigStorage.autoRefreshToken(); } @Override 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 8962b38c3a..f1b903d472 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 @@ -1,6 +1,5 @@ package me.chanjar.weixin.open.api.impl; -import org.apache.commons.lang3.StringUtils; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.util.Pool; @@ -8,26 +7,9 @@ /** * @author 007 */ -public class WxOpenInRedisConfigStorage extends WxOpenInMemoryConfigStorage { - private final static String COMPONENT_VERIFY_TICKET_KEY = "wechat_component_verify_ticket:"; - private final static String COMPONENT_ACCESS_TOKEN_KEY = "wechat_component_access_token:"; - - private final static String AUTHORIZER_REFRESH_TOKEN_KEY = "wechat_authorizer_refresh_token:"; - private final static String AUTHORIZER_ACCESS_TOKEN_KEY = "wechat_authorizer_access_token:"; - private final static String JSAPI_TICKET_KEY = "wechat_jsapi_ticket:"; - private final static String CARD_API_TICKET_KEY = "wechat_card_api_ticket:"; +public class WxOpenInRedisConfigStorage extends AbstractWxOpenInRedisConfigStorage { protected final Pool jedisPool; - /** - * redis 存储的 key 的前缀,可为空 - */ - private String keyPrefix; - private String componentVerifyTicketKey; - private String componentAccessTokenKey; - private String authorizerRefreshTokenKey; - private String authorizerAccessTokenKey; - private String jsapiTicketKey; - private String cardApiTicket; public WxOpenInRedisConfigStorage(Pool jedisPool) { this.jedisPool = jedisPool; @@ -42,19 +24,6 @@ public WxOpenInRedisConfigStorage(JedisPool jedisPool) { this.jedisPool = jedisPool; } - @Override - public void setComponentAppId(String componentAppId) { - super.setComponentAppId(componentAppId); - String prefix = StringUtils.isBlank(keyPrefix) ? "" : - (StringUtils.endsWith(keyPrefix, ":") ? keyPrefix : (keyPrefix + ":")); - componentVerifyTicketKey = prefix + COMPONENT_VERIFY_TICKET_KEY.concat(componentAppId); - componentAccessTokenKey = prefix + COMPONENT_ACCESS_TOKEN_KEY.concat(componentAppId); - authorizerRefreshTokenKey = prefix + AUTHORIZER_REFRESH_TOKEN_KEY.concat(componentAppId); - authorizerAccessTokenKey = prefix + AUTHORIZER_ACCESS_TOKEN_KEY.concat(componentAppId); - this.jsapiTicketKey = JSAPI_TICKET_KEY.concat(componentAppId); - this.cardApiTicket = CARD_API_TICKET_KEY.concat(componentAppId); - } - @Override public String getComponentVerifyTicket() { try (Jedis jedis = this.jedisPool.getResource()) { @@ -97,10 +66,6 @@ public void updateComponentAccessToken(String componentAccessToken, int expiresI } } - private String getKey(String prefix, String appId) { - return prefix.endsWith(":") ? prefix.concat(appId) : prefix.concat(":").concat(appId); - } - @Override public String getAuthorizerRefreshToken(String appId) { try (Jedis jedis = this.jedisPool.getResource()) { diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInRedissonConfigStorage.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInRedissonConfigStorage.java new file mode 100644 index 0000000000..7fdf1ba1b6 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInRedissonConfigStorage.java @@ -0,0 +1,128 @@ +package me.chanjar.weixin.open.api.impl; + +import java.util.concurrent.TimeUnit; +import org.redisson.api.RedissonClient; + +/** + * @author yangyidian + * @date 2020/01/06 + **/ +public class WxOpenInRedissonConfigStorage extends AbstractWxOpenInRedisConfigStorage{ + + private RedissonClient redissonClient; + + public WxOpenInRedissonConfigStorage(RedissonClient redissonClient, String keyPrefix) { + this.keyPrefix = keyPrefix; + this.redissonClient = redissonClient; + } + + public WxOpenInRedissonConfigStorage(RedissonClient redissonClient) { + this.redissonClient = redissonClient; + } + + @Override + public String getComponentVerifyTicket() { + Object value = redissonClient.getBucket(this.componentVerifyTicketKey).get(); + return value == null ? null : value.toString(); + } + + @Override + public void setComponentVerifyTicket(String componentVerifyTicket) { + redissonClient.getBucket(this.componentVerifyTicketKey).set(componentVerifyTicket); + } + + @Override + public String getComponentAccessToken() { + Object value = redissonClient.getBucket(this.componentAccessTokenKey).get(); + return value == null ? null : value.toString(); + } + + @Override + public boolean isComponentAccessTokenExpired() { + return redissonClient.getBucket(this.componentAccessTokenKey).remainTimeToLive() < 2; + } + + @Override + public void expireComponentAccessToken() { + redissonClient.getBucket(this.componentAccessTokenKey).expire(0, TimeUnit.SECONDS); + } + + @Override + public void updateComponentAccessToken(String componentAccessToken, int expiresInSeconds) { + redissonClient.getBucket(this.componentAccessTokenKey).set(componentAccessToken, expiresInSeconds - 200, TimeUnit.SECONDS); + } + + @Override + public String getAuthorizerRefreshToken(String appId) { + Object value = redissonClient.getBucket(this.getKey(this.authorizerRefreshTokenKey, appId)).get(); + return value == null ? null : value.toString(); + } + + @Override + public void setAuthorizerRefreshToken(String appId, String authorizerRefreshToken) { + redissonClient.getBucket(this.getKey(this.authorizerRefreshTokenKey, appId)).set(authorizerRefreshToken); + } + + @Override + public String getAuthorizerAccessToken(String appId) { + Object value = redissonClient.getBucket(this.getKey(this.authorizerAccessTokenKey, appId)).get(); + return value == null ? null : value.toString(); + } + + @Override + public boolean isAuthorizerAccessTokenExpired(String appId) { + return redissonClient.getBucket(this.getKey(this.authorizerAccessTokenKey, appId)).remainTimeToLive() < 2; + } + + @Override + public void expireAuthorizerAccessToken(String appId) { + redissonClient.getBucket(this.getKey(this.authorizerAccessTokenKey, appId)).expire(0, TimeUnit.SECONDS); + } + + @Override + public void updateAuthorizerAccessToken(String appId, String authorizerAccessToken, int expiresInSeconds) { + redissonClient.getBucket(this.getKey(this.authorizerAccessTokenKey, appId)).set(authorizerAccessToken, expiresInSeconds - 200, TimeUnit.SECONDS); + } + + @Override + public String getJsapiTicket(String appId) { + Object value = redissonClient.getBucket(this.getKey(this.jsapiTicketKey, appId)).get(); + return value == null ? null : value.toString(); + } + + @Override + public boolean isJsapiTicketExpired(String appId) { + return redissonClient.getBucket(this.getKey(this.jsapiTicketKey, appId)).remainTimeToLive() < 2; + } + + @Override + public void expireJsapiTicket(String appId) { + redissonClient.getBucket(this.getKey(this.jsapiTicketKey, appId)).expire(0, TimeUnit.SECONDS); + } + + @Override + public void updateJsapiTicket(String appId, String jsapiTicket, int expiresInSeconds) { + redissonClient.getBucket(this.getKey(this.jsapiTicketKey, appId)).set(jsapiTicket, expiresInSeconds - 200, TimeUnit.SECONDS); + } + + @Override + public String getCardApiTicket(String appId) { + Object value = redissonClient.getBucket(this.getKey(this.cardApiTicket, appId)).get(); + return value == null ? null : value.toString(); + } + + @Override + public boolean isCardApiTicketExpired(String appId) { + return redissonClient.getBucket(this.getKey(this.cardApiTicket, appId)).remainTimeToLive() < 2; + } + + @Override + public void expireCardApiTicket(String appId) { + redissonClient.getBucket(this.getKey(this.cardApiTicket, appId)).expire(0 ,TimeUnit.SECONDS); + } + + @Override + public void updateCardApiTicket(String appId, String cardApiTicket, int expiresInSeconds) { + redissonClient.getBucket(this.getKey(this.cardApiTicket, appId)).set(cardApiTicket, expiresInSeconds - 200, TimeUnit.SECONDS); + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxOpenMaPreviewInfo.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxOpenMaPreviewInfo.java new file mode 100644 index 0000000000..a1f0416dd2 --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxOpenMaPreviewInfo.java @@ -0,0 +1,27 @@ +package me.chanjar.weixin.open.bean.ma; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.util.List; + +/** + * @author zxfreedom + * @description + * @date 2019/12/30 + */ +@Data +public class WxOpenMaPreviewInfo { + + /** + * 录屏mediaid列表,可以通过提审素材上传接口获得 + */ + @SerializedName("video_id_list") + private String[] videoIdList; + + /** + * 截屏mediaid列表,可以通过提审素材上传接口获得 + */ + @SerializedName("pic_id_list") + private String[] picIdList; +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/message/WxOpenMaSubmitAuditMessage.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/message/WxOpenMaSubmitAuditMessage.java index 51698a9c28..56129a9845 100644 --- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/message/WxOpenMaSubmitAuditMessage.java +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/message/WxOpenMaSubmitAuditMessage.java @@ -2,6 +2,7 @@ import com.google.gson.annotations.SerializedName; import lombok.Data; +import me.chanjar.weixin.open.bean.ma.WxOpenMaPreviewInfo; import me.chanjar.weixin.open.bean.ma.WxOpenMaSubmitAudit; import java.io.Serializable; @@ -22,6 +23,18 @@ public class WxOpenMaSubmitAuditMessage implements Serializable { @SerializedName("item_list") private List itemList; + /** + * 预览信息(小程序页面截图和操作录屏) + */ + @SerializedName("preview_info") + private List previewInfo; + + /** + * 小程序版本说明和功能解释 + */ + @SerializedName("version_desc") + private String versionDesc; + /** * 反馈内容,不超过200字 */ diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxQrcodeJumpRule.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxQrcodeJumpRule.java index eb8a074b56..93c06a2c22 100644 --- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxQrcodeJumpRule.java +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxQrcodeJumpRule.java @@ -3,41 +3,61 @@ import com.google.gson.annotations.SerializedName; import lombok.Data; +import java.io.Serializable; import java.util.List; /** * 二维码规则 * - * hanwei59 + * @author hanwei59 */ @Data -public class WxQrcodeJumpRule { - - //二维码规则 - @SerializedName("prefix") - private String prefix; - - //是否独占符合二维码前缀匹配规则的所有子规则:1为不占用,2为占用 - //详细说明:https://mp.weixin.qq.com/debug/wxadoc/introduction/qrcode.html#前缀占用规则 - @SerializedName("permit_sub_rule") - private String permitSubRule; - - //小程序功能页面 - @SerializedName("path") - private String path; - - //测试范围: - //1为开发版(配置只对开发者生效) - //2为体验版(配置对管理员、体验者生效) - //3为线上版本(配置对管理员、开发者和体验者生效) - @SerializedName("open_version") - private String openVersion; - - //测试链接(选填)可填写不多于5个用于测试的二维码完整链接,此链接必须符合已填写的二维码规则。 - @SerializedName("debug_url") - private List debugUrl; - - //编辑标志位,0表示新增二维码规则,1表示修改已有二维码规则 - @SerializedName("is_edit") - private String isEdit; +public class WxQrcodeJumpRule implements Serializable { + private static final long serialVersionUID = -7139573923977433678L; + + /** + * 二维码规则 + */ + @SerializedName("prefix") + private String prefix; + + /** + * 是否独占符合二维码前缀匹配规则的所有子规则:1为不占用,2为占用 + * 详细说明:https://mp.weixin.qq.com/debug/wxadoc/introduction/qrcode.html#前缀占用规则 + */ + @SerializedName("permit_sub_rule") + private String permitSubRule; + + /** + * 小程序功能页面 + */ + @SerializedName("path") + private String path; + + /** + * 测试范围: + * 1为开发版(配置只对开发者生效) + * 2为体验版(配置对管理员、体验者生效) + * 3为线上版本(配置对管理员、开发者和体验者生效) + */ + @SerializedName("open_version") + private String openVersion; + + /** + * 测试链接(选填)可填写不多于5个用于测试的二维码完整链接,此链接必须符合已填写的二维码规则。 + */ + @SerializedName("debug_url") + private List debugUrl; + + /** + * 编辑标志位,0表示新增二维码规则,1表示修改已有二维码规则 + */ + @SerializedName("is_edit") + private String isEdit; + + /** + * 发布标志位,1 表示未发布,2 表示已发布 + */ + @SerializedName("state") + private String state; } diff --git a/weixin-java-open/src/test/java/me/chanjar/weixin/open/api/impl/WxOpenInRedisConfigStorageTest.java b/weixin-java-open/src/test/java/me/chanjar/weixin/open/api/impl/WxOpenInRedisConfigStorageTest.java new file mode 100644 index 0000000000..26a30a2040 --- /dev/null +++ b/weixin-java-open/src/test/java/me/chanjar/weixin/open/api/impl/WxOpenInRedisConfigStorageTest.java @@ -0,0 +1,131 @@ +package me.chanjar.weixin.open.api.impl; + +import me.chanjar.weixin.open.api.WxOpenConfigStorage; +import me.chanjar.weixin.open.bean.WxOpenAuthorizerAccessToken; +import me.chanjar.weixin.open.bean.WxOpenComponentAccessToken; +import org.testng.Assert; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; +import redis.clients.jedis.JedisPool; + +public class WxOpenInRedisConfigStorageTest { + + private WxOpenConfigStorage wxOpenConfigStorage; + + private JedisPool pool; + + @BeforeClass + public void setWxOpenConfigStorage(){ + pool = new JedisPool("127.0.0.1", 6379); + this.wxOpenConfigStorage = new WxOpenInRedisConfigStorage(pool); + this.wxOpenConfigStorage.setWxOpenInfo("ComponentAppId", "ComponentAppSecret", "ComponentToken","ComponentAesKey"); + this.wxOpenConfigStorage.setComponentVerifyTicket("ComponentVerifyTicket"); + } + + @AfterClass + public void clearResource(){ + pool.close(); + } + + @Test + public void testGetComponentVerifyTicket() { + String componentVerifyTicket = this.wxOpenConfigStorage.getComponentVerifyTicket(); + Assert.assertEquals(componentVerifyTicket, "ComponentVerifyTicket"); + } + + @Test + public void testSetComponentVerifyTicket() { + this.wxOpenConfigStorage.setComponentVerifyTicket("new ComponentVerifyTicket"); + String componentVerifyTicket = this.wxOpenConfigStorage.getComponentVerifyTicket(); + Assert.assertEquals(componentVerifyTicket, "new ComponentVerifyTicket"); + } + + @Test + public void testIsComponentAccessTokenExpired() { + String responseContent = "{\"component_access_token\": \"new componentAccessToken\", \"expires_in\": 10000}"; + WxOpenComponentAccessToken componentAccessToken = WxOpenComponentAccessToken.fromJson(responseContent); + this.wxOpenConfigStorage.updateComponentAccessToken(componentAccessToken); + boolean expired = this.wxOpenConfigStorage.isComponentAccessTokenExpired(); + Assert.assertEquals(expired, false); + + this.wxOpenConfigStorage.expireComponentAccessToken(); + expired = this.wxOpenConfigStorage.isComponentAccessTokenExpired(); + Assert.assertEquals(expired, true); + + } + + @Test + public void testGetAuthorizerRefreshToken() { + String appid = "appid1"; + this.wxOpenConfigStorage.setAuthorizerRefreshToken(appid, "AuthorizerRefreshToken 1"); + String authorizerAccessToken = this.wxOpenConfigStorage.getAuthorizerRefreshToken(appid); + Assert.assertEquals(authorizerAccessToken, "AuthorizerRefreshToken 1"); + + + this.wxOpenConfigStorage.setAuthorizerRefreshToken(appid, "AuthorizerRefreshToken 2"); + authorizerAccessToken = this.wxOpenConfigStorage.getAuthorizerRefreshToken(appid); + Assert.assertEquals(authorizerAccessToken, "AuthorizerRefreshToken 2"); + } + + @Test + public void testGetAuthorizerAccessToken() { + String appid = "appid1"; + String responseContent = "{\"authorizer_access_token\": \"new authorizer_access_token\",\"expires_in\": 100000}"; + WxOpenAuthorizerAccessToken wxOpenAuthorizerAccessToken = WxOpenAuthorizerAccessToken.fromJson(responseContent); + this.wxOpenConfigStorage.updateAuthorizerAccessToken(appid, wxOpenAuthorizerAccessToken); + String authorizerAccessToken = this.wxOpenConfigStorage.getAuthorizerAccessToken(appid); + Assert.assertEquals(authorizerAccessToken, "new authorizer_access_token"); + } + + @Test + public void testIsAuthorizerAccessTokenExpired() { + String appid = "appid1"; + String responseContent = "{\"authorizer_access_token\": \"new authorizer_access_token\",\"expires_in\": 100000}"; + WxOpenAuthorizerAccessToken wxOpenAuthorizerAccessToken = WxOpenAuthorizerAccessToken.fromJson(responseContent); + this.wxOpenConfigStorage.updateAuthorizerAccessToken(appid, wxOpenAuthorizerAccessToken); + String authorizerAccessToken = this.wxOpenConfigStorage.getAuthorizerAccessToken(appid); + Assert.assertEquals(authorizerAccessToken, "new authorizer_access_token"); + + boolean expired = this.wxOpenConfigStorage.isAuthorizerAccessTokenExpired(appid); + Assert.assertEquals(expired, false); + + this.wxOpenConfigStorage.expireAuthorizerAccessToken(appid); + expired = this.wxOpenConfigStorage.isAuthorizerAccessTokenExpired(appid); + Assert.assertEquals(expired, true); + } + + + @Test + public void testGetJsapiTicket() { + String appid = "appid1"; + this.wxOpenConfigStorage.updateJsapiTicket(appid, "jsapiTicket", 100000); + String jsapiTicket = this.wxOpenConfigStorage.getJsapiTicket(appid); + Assert.assertEquals(jsapiTicket, "jsapiTicket"); + + boolean expired = this.wxOpenConfigStorage.isJsapiTicketExpired(appid); + Assert.assertEquals(expired, false); + + this.wxOpenConfigStorage.expireJsapiTicket(appid); + jsapiTicket = this.wxOpenConfigStorage.getJsapiTicket(appid); + Assert.assertEquals(jsapiTicket, null); + + expired = this.wxOpenConfigStorage.isJsapiTicketExpired(appid); + Assert.assertEquals(expired, true); + } + + @Test + public void testGetCardApiTicket() { + String appid = "appid1"; + this.wxOpenConfigStorage.updateCardApiTicket(appid, "new CardApiTicket", 10000); + String cardApiTicket = this.wxOpenConfigStorage.getCardApiTicket(appid); + Assert.assertEquals(cardApiTicket, "new CardApiTicket"); + + boolean expired = this.wxOpenConfigStorage.isCardApiTicketExpired(appid); + Assert.assertEquals(expired, false); + + this.wxOpenConfigStorage.expireCardApiTicket(appid); + expired = this.wxOpenConfigStorage.isCardApiTicketExpired(appid); + Assert.assertEquals(expired, true); + } +} diff --git a/weixin-java-open/src/test/java/me/chanjar/weixin/open/api/impl/WxOpenInRedissonConfigStorageTest.java b/weixin-java-open/src/test/java/me/chanjar/weixin/open/api/impl/WxOpenInRedissonConfigStorageTest.java new file mode 100644 index 0000000000..7168d93f1b --- /dev/null +++ b/weixin-java-open/src/test/java/me/chanjar/weixin/open/api/impl/WxOpenInRedissonConfigStorageTest.java @@ -0,0 +1,129 @@ +package me.chanjar.weixin.open.api.impl; + +import me.chanjar.weixin.open.api.WxOpenConfigStorage; +import me.chanjar.weixin.open.bean.WxOpenAuthorizerAccessToken; +import me.chanjar.weixin.open.bean.WxOpenComponentAccessToken; +import org.redisson.Redisson; +import org.redisson.api.RedissonClient; +import org.redisson.config.Config; +import org.redisson.config.TransportMode; +import org.testng.Assert; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +public class WxOpenInRedissonConfigStorageTest { + + private WxOpenConfigStorage wxOpenConfigStorage; + + @BeforeClass + public void setWxOpenConfigStorage(){ + Config config = new Config(); + config.useSingleServer().setAddress("redis://127.0.0.1:6379") + .setDatabase(0); + config.setTransportMode(TransportMode.NIO); + RedissonClient redisson = Redisson.create(config); + this.wxOpenConfigStorage = new WxOpenInRedissonConfigStorage(redisson); + this.wxOpenConfigStorage.setWxOpenInfo("ComponentAppId", "ComponentAppSecret", "ComponentToken","ComponentAesKey"); + this.wxOpenConfigStorage.setComponentVerifyTicket("ComponentVerifyTicket"); + } + + @Test + public void testGetComponentVerifyTicket() { + String componentVerifyTicket = this.wxOpenConfigStorage.getComponentVerifyTicket(); + Assert.assertEquals(componentVerifyTicket, "ComponentVerifyTicket"); + } + + @Test + public void testSetComponentVerifyTicket() { + this.wxOpenConfigStorage.setComponentVerifyTicket("new ComponentVerifyTicket"); + String componentVerifyTicket = this.wxOpenConfigStorage.getComponentVerifyTicket(); + Assert.assertEquals(componentVerifyTicket, "new ComponentVerifyTicket"); + } + + @Test + public void testIsComponentAccessTokenExpired() { + String responseContent = "{\"component_access_token\": \"new componentAccessToken\", \"expires_in\": 10000}"; + WxOpenComponentAccessToken componentAccessToken = WxOpenComponentAccessToken.fromJson(responseContent); + this.wxOpenConfigStorage.updateComponentAccessToken(componentAccessToken); + boolean expired = this.wxOpenConfigStorage.isComponentAccessTokenExpired(); + Assert.assertEquals(expired, false); + + this.wxOpenConfigStorage.expireComponentAccessToken(); + expired = this.wxOpenConfigStorage.isComponentAccessTokenExpired(); + Assert.assertEquals(expired, true); + + } + + @Test + public void testGetAuthorizerRefreshToken() { + String appid = "appid1"; + this.wxOpenConfigStorage.setAuthorizerRefreshToken(appid, "AuthorizerRefreshToken 1"); + String authorizerAccessToken = this.wxOpenConfigStorage.getAuthorizerRefreshToken(appid); + Assert.assertEquals(authorizerAccessToken, "AuthorizerRefreshToken 1"); + + this.wxOpenConfigStorage.setAuthorizerRefreshToken(appid, "AuthorizerRefreshToken 2"); + authorizerAccessToken = this.wxOpenConfigStorage.getAuthorizerRefreshToken(appid); + Assert.assertEquals(authorizerAccessToken, "AuthorizerRefreshToken 2"); + } + + @Test + public void testGetAuthorizerAccessToken() { + String appid = "appid1"; + String responseContent = "{\"authorizer_access_token\": \"new authorizer_access_token\",\"expires_in\": 100000}"; + WxOpenAuthorizerAccessToken wxOpenAuthorizerAccessToken = WxOpenAuthorizerAccessToken.fromJson(responseContent); + this.wxOpenConfigStorage.updateAuthorizerAccessToken(appid, wxOpenAuthorizerAccessToken); + String authorizerAccessToken = this.wxOpenConfigStorage.getAuthorizerAccessToken(appid); + Assert.assertEquals(authorizerAccessToken, "new authorizer_access_token"); + } + + @Test + public void testIsAuthorizerAccessTokenExpired() { + String appid = "appid1"; + String responseContent = "{\"authorizer_access_token\": \"new authorizer_access_token\",\"expires_in\": 100000}"; + WxOpenAuthorizerAccessToken wxOpenAuthorizerAccessToken = WxOpenAuthorizerAccessToken.fromJson(responseContent); + this.wxOpenConfigStorage.updateAuthorizerAccessToken(appid, wxOpenAuthorizerAccessToken); + String authorizerAccessToken = this.wxOpenConfigStorage.getAuthorizerAccessToken(appid); + Assert.assertEquals(authorizerAccessToken, "new authorizer_access_token"); + + boolean expired = this.wxOpenConfigStorage.isAuthorizerAccessTokenExpired(appid); + Assert.assertEquals(expired, false); + + this.wxOpenConfigStorage.expireAuthorizerAccessToken(appid); + expired = this.wxOpenConfigStorage.isAuthorizerAccessTokenExpired(appid); + Assert.assertEquals(expired, true); + } + + + @Test + public void testGetJsapiTicket() { + String appid = "appid1"; + this.wxOpenConfigStorage.updateJsapiTicket(appid, "jsapiTicket", 100000); + String jsapiTicket = this.wxOpenConfigStorage.getJsapiTicket(appid); + Assert.assertEquals(jsapiTicket, "jsapiTicket"); + + boolean expired = this.wxOpenConfigStorage.isJsapiTicketExpired(appid); + Assert.assertEquals(expired, false); + + this.wxOpenConfigStorage.expireJsapiTicket(appid); + jsapiTicket = this.wxOpenConfigStorage.getJsapiTicket(appid); + Assert.assertEquals(jsapiTicket, null); + + expired = this.wxOpenConfigStorage.isJsapiTicketExpired(appid); + Assert.assertEquals(expired, true); + } + + @Test + public void testGetCardApiTicket() { + String appid = "appid1"; + this.wxOpenConfigStorage.updateCardApiTicket(appid, "new CardApiTicket", 10000); + String cardApiTicket = this.wxOpenConfigStorage.getCardApiTicket(appid); + Assert.assertEquals(cardApiTicket, "new CardApiTicket"); + + boolean expired = this.wxOpenConfigStorage.isCardApiTicketExpired(appid); + Assert.assertEquals(expired, false); + + this.wxOpenConfigStorage.expireCardApiTicket(appid); + expired = this.wxOpenConfigStorage.isCardApiTicketExpired(appid); + Assert.assertEquals(expired, true); + } +} diff --git a/weixin-java-pay/pom.xml b/weixin-java-pay/pom.xml index fedc9f5045..7d9100c5cb 100644 --- a/weixin-java-pay/pom.xml +++ b/weixin-java-pay/pom.xml @@ -5,7 +5,7 @@ com.github.binarywang wx-java - 3.6.0 + 3.7.0 4.0.0 @@ -72,4 +72,35 @@ + + + native-image + + false + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.5.1 + + + cn.binarywang.wx.graal.GraalProcessor,lombok.launch.AnnotationProcessorHider$AnnotationProcessor,lombok.launch.AnnotationProcessorHider$ClaimingProcessor + + + + com.github.binarywang + weixin-graal + ${project.version} + + + + + + + + + diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponInfoQueryRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponInfoQueryRequest.java index 61726e0405..539ad988b9 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponInfoQueryRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponInfoQueryRequest.java @@ -5,6 +5,8 @@ import lombok.*; import me.chanjar.weixin.common.annotation.Required; +import java.util.Map; + /** *
  * 查询代金券信息请求对象类
@@ -120,4 +122,15 @@ protected void checkConstraints() {
     //do nothing
   }
 
+  @Override
+  protected void storeMap(Map map) {
+    map.put("coupon_id", couponId);
+    map.put("stock_id", stockId);
+    map.put("openid", openid);
+    map.put("op_user_id", opUserId);
+    map.put("device_info", deviceInfo);
+    map.put("version", version);
+    map.put("type", type);
+  }
+
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponInfoQueryResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponInfoQueryResult.java
index cab25062af..9001541b7a 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponInfoQueryResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponInfoQueryResult.java
@@ -5,6 +5,7 @@
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.NoArgsConstructor;
+import org.w3c.dom.Document;
 
 /**
  * 
@@ -227,4 +228,23 @@ public class WxPayCouponInfoQueryResult extends BaseWxPayResult {
   @XStreamAlias("is_partial_use")
   private String isPartialUse;
 
+  @Override
+  protected void loadXML(Document d) {
+    deviceInfo = readXMLString(d, "device_info");
+    couponStockId = readXMLString(d, "coupon_stock_id");
+    couponId = readXMLString(d, "coupon_id");
+    couponValue = readXMLInteger(d, "coupon_value");
+    couponMinimum = readXMLInteger(d, "coupon_minimum");
+    couponName = readXMLString(d, "coupon_name");
+    couponState = readXMLString(d, "coupon_state");
+    couponDesc = readXMLString(d, "coupon_desc");
+    couponUseValue = readXMLInteger(d, "coupon_use_value");
+    couponRemainValue = readXMLInteger(d, "coupon_remain_value");
+    beginTime = readXMLString(d, "begin_time");
+    endTime = readXMLString(d, "end_time");
+    sendTime = readXMLString(d, "send_time");
+    consumerMchId = readXMLString(d, "consumer_mch_id");
+    sendSource = readXMLString(d, "send_source");
+    isPartialUse = readXMLString(d, "is_partial_use");
+  }
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponSendRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponSendRequest.java
index 5fa6da7de2..a43ce51d99 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponSendRequest.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponSendRequest.java
@@ -5,6 +5,8 @@
 import lombok.*;
 import me.chanjar.weixin.common.annotation.Required;
 
+import java.util.Map;
+
 /**
  * 
  * 发送代金券请求对象类
@@ -132,4 +134,16 @@ public class WxPayCouponSendRequest extends BaseWxPayRequest {
   protected void checkConstraints() {
     //do nothing
   }
+
+  @Override
+  protected void storeMap(Map map) {
+    map.put("coupon_stock_id", couponStockId);
+    map.put("openid_count", openidCount.toString());
+    map.put("partner_trade_no", partnerTradeNo);
+    map.put("openid", openid);
+    map.put("op_user_id", opUserId);
+    map.put("device_info", deviceInfo);
+    map.put("version", version);
+    map.put("type", type);
+  }
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponSendResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponSendResult.java
index 314845e46e..9350e58847 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponSendResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponSendResult.java
@@ -5,6 +5,7 @@
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.NoArgsConstructor;
+import org.w3c.dom.Document;
 
 /**
  * 
@@ -136,4 +137,16 @@ public class WxPayCouponSendResult extends BaseWxPayResult {
   @XStreamAlias("ret_msg")
   private String retMsg;
 
+  @Override
+  protected void loadXML(Document d) {
+    deviceInfo = readXMLString(d, "device_info");
+    couponStockId = readXMLString(d, "coupon_stock_id");
+    respCount = readXMLInteger(d, "resp_count");
+    successCount = readXMLInteger(d, "success_count");
+    failedCount = readXMLInteger(d, "failed_count");
+    openid = readXMLString(d, "openid");
+    retCode = readXMLString(d, "ret_code");
+    couponId = readXMLString(d, "coupon_id");
+    retMsg = readXMLString(d, "ret_msg");
+  }
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponStockQueryRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponStockQueryRequest.java
index b8a78b40bf..bae6a563ea 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponStockQueryRequest.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponStockQueryRequest.java
@@ -5,6 +5,8 @@
 import lombok.*;
 import me.chanjar.weixin.common.annotation.Required;
 
+import java.util.Map;
+
 /**
  * 
  * 查询代金券批次请求对象类
@@ -91,4 +93,13 @@ protected void checkConstraints() {
     //do nothing
   }
 
+  @Override
+  protected void storeMap(Map map) {
+    map.put("coupon_stock_id", couponStockId);
+    map.put("op_user_id", opUserId);
+    map.put("device_info", deviceInfo);
+    map.put("version", version);
+    map.put("type", type);
+  }
+
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponStockQueryResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponStockQueryResult.java
index f3e91d030b..b338f4081b 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponStockQueryResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponStockQueryResult.java
@@ -6,6 +6,7 @@
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.NoArgsConstructor;
+import org.w3c.dom.Document;
 
 /**
  * 
@@ -191,4 +192,20 @@ public class WxPayCouponStockQueryResult extends BaseWxPayResult {
   @XStreamAlias("coupon_budget")
   private Integer couponBudget;
 
+  @Override
+  protected void loadXML(Document d) {
+    deviceInfo = readXMLString(d, "device_info");
+    couponStockId = readXMLString(d, "coupon_stock_id");
+    couponName = readXMLString(d, "coupon_name");
+    couponValue = readXMLInteger(d, "coupon_value");
+    couponMinimum = readXMLInteger(d, "coupon_mininumn");
+    couponStockStatus = readXMLInteger(d, "coupon_stock_status");
+    couponTotal = readXMLInteger(d, "coupon_total");
+    maxQuota = readXMLInteger(d, "max_quota");
+    isSendNum = readXMLInteger(d, "is_send_num");
+    beginTime = readXMLString(d, "begin_time");
+    endTime = readXMLString(d, "end_time");
+    createTime = readXMLString(d, "create_time");
+    couponBudget = readXMLInteger(d, "coupon_budget");
+  }
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayBankQueryResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayBankQueryResult.java
index 6020dbed77..72adc1c9aa 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayBankQueryResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayBankQueryResult.java
@@ -5,6 +5,7 @@
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.NoArgsConstructor;
+import org.w3c.dom.Document;
 
 /**
  * 
@@ -93,4 +94,17 @@ public class EntPayBankQueryResult extends BaseWxPayResult {
   @XStreamAlias("reason")
   private String failReason;
 
+  @Override
+  protected void loadXML(Document d) {
+    partnerTradeNo = readXMLString(d, "partner_trade_no");
+    paymentNo = readXMLString(d, "payment_no");
+    bankNoMd5 = readXMLString(d, "bank_no_md5");
+    trueNameMd5 = readXMLString(d, "true_name_md5");
+    amount = readXMLInteger(d, "amount");
+    status = readXMLString(d, "status");
+    cmmsAmount = readXMLInteger(d, "cmms_amt");
+    createTime = readXMLString(d, "create_time");
+    paySuccessTime = readXMLString(d, "pay_succ_time");
+    failReason = readXMLString(d, "reason");
+  }
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayBankRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayBankRequest.java
index 05ccebc23c..04c26403c2 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayBankRequest.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayBankRequest.java
@@ -10,6 +10,8 @@
 import lombok.NoArgsConstructor;
 import me.chanjar.weixin.common.annotation.Required;
 
+import java.util.Map;
+
 /**
  * 
  *  企业付款到银行卡的请求对象类
@@ -121,6 +123,16 @@ protected String[] getIgnoredParamsForSign() {
     return new String[]{"sign_type"};
   }
 
+  @Override
+  protected void storeMap(Map map) {
+    map.put("partner_trade_no", partnerTradeNo);
+    map.put("enc_bank_no", encBankNo);
+    map.put("enc_true_name", encTrueName);
+    map.put("bank_code", bankCode);
+    map.put("amount", amount.toString());
+    map.put("desc", description);
+  }
+
   @Override
   protected boolean ignoreAppid() {
     return true;
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayBankResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayBankResult.java
index 078e27e849..0d38645afe 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayBankResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayBankResult.java
@@ -5,6 +5,7 @@
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.NoArgsConstructor;
+import org.w3c.dom.Document;
 
 /**
  * 
@@ -48,4 +49,11 @@ public class EntPayBankResult extends BaseWxPayResult {
   @XStreamAlias("cmms_amt")
   private Integer cmmsAmount;
 
+  @Override
+  protected void loadXML(Document d) {
+    amount = readXMLInteger(d, "amount");
+    partnerTradeNo = readXMLString(d, "partner_trade_no");
+    paymentNo = readXMLString(d, "payment_no");
+    cmmsAmount = readXMLInteger(d, "cmms_amt");
+  }
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayQueryRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayQueryRequest.java
index 21de8fca35..779d59823b 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayQueryRequest.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayQueryRequest.java
@@ -10,6 +10,8 @@
 import me.chanjar.weixin.common.annotation.Required;
 import me.chanjar.weixin.common.util.json.WxGsonBuilder;
 
+import java.util.Map;
+
 /**
  * 
  * 企业付款请求对象.
@@ -55,4 +57,9 @@ public String toString() {
   protected String[] getIgnoredParamsForSign() {
     return new String[]{"sign_type"};
   }
+
+  @Override
+  protected void storeMap(Map map) {
+    map.put("partner_trade_no", partnerTradeNo);
+  }
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayQueryResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayQueryResult.java
index e9f6d2213f..6dee1d0911 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayQueryResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayQueryResult.java
@@ -5,6 +5,7 @@
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.NoArgsConstructor;
+import org.w3c.dom.Document;
 
 /**
  * 
@@ -81,4 +82,17 @@ public class EntPayQueryResult extends BaseWxPayResult {
   @XStreamAlias("desc")
   private String desc;
 
+  @Override
+  protected void loadXML(Document d) {
+    partnerTradeNo = readXMLString(d, "partner_trade_no");
+    detailId = readXMLString(d, "detail_id");
+    status = readXMLString(d, "status");
+    reason = readXMLString(d, "reason");
+    openid = readXMLString(d, "openid");
+    transferName = readXMLString(d, "transfer_name");
+    paymentAmount = readXMLInteger(d, "payment_amount");
+    transferTime = readXMLString(d, "transfer_time");
+    paymentTime = readXMLString(d, "payment_time");
+    desc = readXMLString(d, "desc");
+  }
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayRedpackQueryRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayRedpackQueryRequest.java
new file mode 100644
index 0000000000..74e5b4b1a0
--- /dev/null
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayRedpackQueryRequest.java
@@ -0,0 +1,41 @@
+package com.github.binarywang.wxpay.bean.entpay;
+
+import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest;
+import com.github.binarywang.wxpay.exception.WxPayException;
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import lombok.*;
+
+import java.util.Map;
+
+/**
+ * 红包发送记录查询请求
+ *
+ * @author wuyong
+ * @date 2019-12-01 17:19
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@Builder(builderMethodName = "newBuilder")
+@NoArgsConstructor
+@AllArgsConstructor
+@XStreamAlias("xml")
+public class EntPayRedpackQueryRequest extends BaseWxPayRequest {
+
+
+  /**
+   * 商户订单号
+   */
+  @XStreamAlias("mch_billno")
+  private String mchBillNo;
+
+
+  @Override
+  protected void checkConstraints() throws WxPayException {
+
+  }
+
+  @Override
+  protected void storeMap(Map map) {
+    map.put("mch_billno", mchBillNo);
+  }
+}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayRedpackQueryResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayRedpackQueryResult.java
new file mode 100644
index 0000000000..000498519f
--- /dev/null
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayRedpackQueryResult.java
@@ -0,0 +1,154 @@
+package com.github.binarywang.wxpay.bean.entpay;
+
+import com.github.binarywang.wxpay.bean.result.BaseWxPayResult;
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import org.w3c.dom.Document;
+
+/**
+ * 红包发送记录查询返回
+ *
+ * @author wuyong
+ * @date 2019-12-01 17:23
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@NoArgsConstructor
+@XStreamAlias("xml")
+public class EntPayRedpackQueryResult extends BaseWxPayResult {
+
+  /**
+   * 商户订单号
+   * 商户使用查询API填写的商户单号的原路返回
+   */
+  @XStreamAlias("mch_billno")
+  protected String mchBillNo;
+
+  /**
+   * 红包单号
+   * 使用API发放现金红包时返回的红包单号
+   */
+  @XStreamAlias("detailId")
+  private String detailId;
+  /**
+   * 红包状态
+   * SENDING:发放
+   * SENT:
+   * 已发放待领取
+   * FAILED:发放失败
+   * RECEIVED:已领取
+   * RFUND_ING:退款中 REFUND:已退款
+   */
+  @XStreamAlias("status")
+  private String status;
+
+  /**
+   * 发放类型
+   * API:通过API接口发放
+   */
+  @XStreamAlias("send_type")
+  private String sendType;
+
+  /**
+   * 红包金额
+   * 红包总金额(单位分)
+   */
+  @XStreamAlias("total_amount")
+  private Integer totalAmount;
+
+  /**
+   * 失败原因
+   * 发送失败原因
+   */
+  @XStreamAlias("reason")
+  private Integer reason;
+
+  /**
+   * 红包发送时间
+   */
+  @XStreamAlias("send_time")
+  private String sendTime;
+  /**
+   * 红包的退款时间
+   */
+  @XStreamAlias("refund_time")
+  private String refundTime;
+
+  /**
+   * 红包退款金额
+   */
+  @XStreamAlias("refund_amount")
+  private Integer refundAmount;
+
+  /**
+   * 祝福语
+   */
+  @XStreamAlias("wishing")
+  private String wishing;
+
+  /**
+   * 备注
+   */
+  @XStreamAlias("remark")
+  private String remark;
+
+  /**
+   * 活动名称
+   */
+  @XStreamAlias("act_name")
+  private String actName;
+
+  /**
+   * 领取红包的Openid
+   */
+  @XStreamAlias("openid")
+  private String openid;
+
+  /**
+   * 金额
+   */
+  @XStreamAlias("amount")
+  private Integer amount;
+
+  /**
+   * 接收时间
+   */
+  @XStreamAlias("rcv_time")
+  private Integer rcvTime;
+
+  /**
+   * 发送者名称
+   */
+  @XStreamAlias("sender_name")
+  private Integer senderName;
+
+  /**
+   * 发送者头像
+   * 通过企业微信开放接口上传获取
+   */
+  @XStreamAlias("sender_header_media_id")
+  private Integer senderHeaderMediaId;
+
+  @Override
+  protected void loadXML(Document d) {
+    mchBillNo = readXMLString(d, "mch_billno");
+    detailId = readXMLString(d, "detailId");
+    status = readXMLString(d, "status");
+    sendType = readXMLString(d, "send_type");
+    totalAmount = readXMLInteger(d, "total_amount");
+    reason = readXMLInteger(d, "reason");
+    sendTime = readXMLString(d, "send_time");
+    refundTime = readXMLString(d, "refund_time");
+    refundAmount = readXMLInteger(d, "refund_amount");
+    wishing = readXMLString(d, "wishing");
+    remark = readXMLString(d, "remark");
+    actName = readXMLString(d, "act_name");
+    openid = readXMLString(d, "openid");
+    amount = readXMLInteger(d, "amount");
+    rcvTime = readXMLInteger(d, "rcv_time");
+    senderName = readXMLInteger(d, "sender_name");
+    senderHeaderMediaId = readXMLInteger(d, "sender_header_media_id");
+  }
+}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayRedpackRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayRedpackRequest.java
new file mode 100644
index 0000000000..762499e693
--- /dev/null
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayRedpackRequest.java
@@ -0,0 +1,165 @@
+package com.github.binarywang.wxpay.bean.entpay;
+
+import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest;
+import com.github.binarywang.wxpay.exception.WxPayException;
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import lombok.*;
+import me.chanjar.weixin.common.annotation.Required;
+
+import java.util.Map;
+
+/**
+ * 发送企业红包
+ *
+ * @author wuyong
+ * @date 2019-12-1
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@Builder(builderMethodName = "newBuilder")
+@NoArgsConstructor
+@AllArgsConstructor
+@XStreamAlias("xml")
+public class EntPayRedpackRequest extends BaseWxPayRequest {
+
+  private static final long serialVersionUID = 1L;
+
+  @Override
+  protected void checkConstraints() throws WxPayException {
+
+  }
+
+  /**
+   * 商户订单号(每个订单号必须唯一)
+   * 组成:mch_id+yyyymmdd+10位一天内不能重复的数字。  接口根据商户订单号支持重入,如出现超时可再调用。
+   * 必填:是
+   */
+  @Required
+  @XStreamAlias("mch_billno")
+  private String mchBillNo;
+
+  /**
+   * 微信分配的公众账号ID(企业微信corpid即为此appId)
+   * 必填:是
+   */
+  @Required
+  @XStreamAlias("wxappid")
+  private String wxAppId;
+
+  /**
+   * 发送者名称
+   * 以个人名义发红包,红包发送者名称(需要utf-8格式)。与agentid互斥,二者只能填一个。
+   * 必填:否
+   */
+  @XStreamAlias("sender_name")
+  private String senderName;
+
+  /**
+   * 发送红包的应用id
+   * 以企业应用的名义发红包,企业应用id,整型,可在企业微信管理端应用的设置页面查看。与sender_name互斥,二者只能填一个。
+   * 必填:否
+   */
+  @XStreamAlias("agentid")
+  private String agentId;
+
+  /**
+   * 发送者头像
+   * 发送者头像素材id,通过企业微信开放上传素材接口获取
+   * 必填:否
+   */
+  @XStreamAlias("sender_header_media_id")
+  private String senderHeaderMediaId;
+
+  /**
+   * 用户openid
+   * 接受红包的用户.用户在wxappid下的openid。
+   * 必填:是
+   */
+  @Required
+  @XStreamAlias("re_openid")
+  private String reOpenid;
+
+  /**
+   * 金额
+   * 单位分,单笔最小金额默认为1元
+   * 必填:是
+   */
+  @Required
+  @XStreamAlias("total_amount")
+  private Integer totalAmount;
+
+  /**
+   * 红包祝福语
+   * 必填:是
+   */
+  @Required
+  @XStreamAlias("wishing")
+  private String wishing;
+
+  /**
+   * 项目名称
+   * 必填:是
+   */
+  @Required
+  @XStreamAlias("act_name")
+  private String actName;
+
+  /**
+   * 备注
+   * 必填:是
+   */
+  @Required
+  @XStreamAlias("remark")
+  private String remark;
+
+  /**
+   * 场景
+   * 发放红包使用场景,红包金额大于200时必传
+   * PRODUCT_1:商品促销
+   * PRODUCT_2:抽奖
+   * PRODUCT_3:虚拟物品兑奖
+   * PRODUCT_4:企业内部福利
+   * PRODUCT_5:渠道分润
+   * PRODUCT_6:保险回馈
+   * PRODUCT_7:彩票派奖
+   * PRODUCT_8:税务刮奖
+   */
+  @XStreamAlias("scene_id")
+  private String sceneId;
+
+
+  @Override
+  protected boolean ignoreAppid() {
+    return true;
+  }
+
+  @Override
+  protected boolean ignoreSubAppId() {
+    return true;
+  }
+
+  @Override
+  protected boolean ignoreSubMchId() {
+    return true;
+  }
+
+  @Override
+  protected boolean isWxWorkSign() {
+    return true;
+  }
+
+  @Override
+  protected void storeMap(Map map) {
+    map.put("mch_billno", mchBillNo);
+    map.put("wxappid", wxAppId);
+    map.put("sender_name", senderName);
+    map.put("agentid", agentId);
+    map.put("sender_header_media_id", senderHeaderMediaId);
+    map.put("re_openid", reOpenid);
+    map.put("total_amount", totalAmount.toString());
+    map.put("wishing", wishing);
+    map.put("act_name", actName);
+    map.put("remark", remark);
+    map.put("scene_id", sceneId);
+  }
+}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayRedpackResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayRedpackResult.java
new file mode 100644
index 0000000000..98cce357c9
--- /dev/null
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayRedpackResult.java
@@ -0,0 +1,93 @@
+package com.github.binarywang.wxpay.bean.entpay;
+
+import com.github.binarywang.wxpay.bean.result.BaseWxPayResult;
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import org.w3c.dom.Document;
+
+import java.io.Serializable;
+
+/**
+ * 企业微信红包返回
+ *
+ * @author wuyong
+ * @date 2019-12-01 11:31
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@NoArgsConstructor
+@XStreamAlias("xml")
+public class EntPayRedpackResult extends BaseWxPayResult implements Serializable {
+
+  private static final long serialVersionUID = 1L;
+
+  /**
+   * 商户订单号
+   * 商户订单号(每个订单号必须唯一)组成:mch_id+yyyymmdd+10位一天内不能重复的数字
+   */
+  @XStreamAlias("mch_billno")
+  private String mchBillNo;
+
+  /**
+   * 商户号
+   * 微信支付分配的商户号
+   */
+  @XStreamAlias("mch_id")
+  private String mchId;
+
+  /**
+   * 公众账号appid
+   * 商户appid,接口传入的所有appid应该为公众号的appid,不能为APP的appid
+   */
+  @XStreamAlias("wxappid")
+  private String wxAppId;
+
+  /**
+   * 用户openid
+   * 接受收红包的用户在wxappid下的openid
+   */
+  @XStreamAlias("re_openid")
+  private String reOpenid;
+
+  /**
+   * 付款金额
+   * 付款金额,单位分
+   */
+  @XStreamAlias("totalAmount")
+  private String totalAmount;
+
+  /**
+   * 微信单号
+   * 红包订单的微信单号
+   */
+  @XStreamAlias("sendListid")
+  private String sendListId;
+
+  /**
+   * 发送者名称
+   * 红包发送者名称(需要utf-8格式)
+   */
+  @XStreamAlias("sender_name")
+  private String senderName;
+
+  /**
+   * 发送者头像
+   * 发送者头像素材id,通过企业微信开放上传素材接口获取
+   */
+  @XStreamAlias("sender_header_media_id")
+  private String senderHeaderMediaId;
+
+  @Override
+  protected void loadXML(Document d) {
+    mchBillNo = readXMLString(d, "mch_billno");
+    mchId = readXMLString(d, "mch_id");
+    wxAppId = readXMLString(d, "wxappid");
+    reOpenid = readXMLString(d, "re_openid");
+    totalAmount = readXMLString(d, "totalAmount");
+    sendListId = readXMLString(d, "sendListid");
+    senderName = readXMLString(d, "sender_name");
+    senderHeaderMediaId = readXMLString(d, "sender_header_media_id");
+  }
+}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayRequest.java
index 24c1c185ac..6f89254714 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayRequest.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayRequest.java
@@ -10,6 +10,8 @@
 import me.chanjar.weixin.common.annotation.Required;
 import me.chanjar.weixin.common.util.json.WxGsonBuilder;
 
+import java.util.Map;
+
 /**
  * 
  * 企业付款请求对象.
@@ -195,4 +197,18 @@ public void setMchId(String mchId) {
   protected String[] getIgnoredParamsForSign() {
     return new String[]{"sign_type"};
   }
+
+  @Override
+  protected void storeMap(Map map) {
+    map.put("mch_appid", mchAppid);
+    map.put("mchid", mchId);
+    map.put("device_info", deviceInfo);
+    map.put("partner_trade_no", partnerTradeNo);
+    map.put("openid", openid);
+    map.put("check_name", checkName);
+    map.put("re_user_name", reUserName);
+    map.put("amount", amount.toString());
+    map.put("desc", description);
+    map.put("spbill_create_ip", spbillCreateIp);
+  }
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayResult.java
index 223c196395..9863e83bcc 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/EntPayResult.java
@@ -5,6 +5,7 @@
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.NoArgsConstructor;
+import org.w3c.dom.Document;
 
 /**
  * 
@@ -56,4 +57,13 @@ public class EntPayResult extends BaseWxPayResult {
   @XStreamAlias("payment_time")
   private String paymentTime;
 
+  @Override
+  protected void loadXML(Document d) {
+    mchId = readXMLString(d, "mchid");
+    mchAppid = readXMLString(d, "mch_appid");
+    deviceInfo = readXMLString(d, "device_info");
+    partnerTradeNo = readXMLString(d, "partner_trade_no");
+    paymentNo = readXMLString(d, "payment_no");
+    paymentTime = readXMLString(d, "payment_time");
+  }
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/GetPublicKeyResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/GetPublicKeyResult.java
index f7f62afb7e..2a9cc8c6ad 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/GetPublicKeyResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/entpay/GetPublicKeyResult.java
@@ -4,6 +4,7 @@
 import com.thoughtworks.xstream.annotations.XStreamAlias;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
+import org.w3c.dom.Document;
 
 /**
  * 
@@ -28,4 +29,10 @@ public class GetPublicKeyResult extends BaseWxPayResult {
    */
   @XStreamAlias("pub_key")
   private String pubKey;
+
+  @Override
+  protected void loadXML(Document d) {
+    mchId = readXMLString(d, "mch_id");
+    pubKey = readXMLString(d, "pub_key");
+  }
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayOrderNotifyResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayOrderNotifyResult.java
index d0242788a1..b1a8fe9d2d 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayOrderNotifyResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayOrderNotifyResult.java
@@ -13,7 +13,9 @@
 import lombok.NoArgsConstructor;
 import me.chanjar.weixin.common.util.json.WxGsonBuilder;
 import me.chanjar.weixin.common.util.xml.XStreamInitializer;
+import org.w3c.dom.Document;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
@@ -300,6 +302,18 @@ public class WxPayOrderNotifyResult extends BaseWxPayResult {
   @XStreamAlias("rate_value")
   private String rateValue;
 
+  /**
+   * 
+   * 字段名:签名类型.
+   * 变量名:sign_type
+   * 类型:String(32)
+   * 示例值:HMAC-SHA256
+   * 签名类型,目前支持HMAC-SHA256和MD5,默认为MD5
+   * 
+ */ + @XStreamAlias("sign_type") + private String signType; + @Override public void checkResult(WxPayService wxPayService, String signType, boolean checkSuccess) throws WxPayException { //防止伪造成功通知 @@ -337,6 +351,51 @@ public Map toMap() { return resultMap; } + @Override + protected void loadXML(Document d) { + promotionDetail = readXMLString(d, "promotion_detail"); + deviceInfo = readXMLString(d, "device_info"); + openid = readXMLString(d, "openid"); + isSubscribe = readXMLString(d, "is_subscribe"); + subOpenid = readXMLString(d, "sub_openid"); + subIsSubscribe = readXMLString(d, "sub_is_subscribe"); + tradeType = readXMLString(d, "trade_type"); + bankType = readXMLString(d, "bank_type"); + totalFee = readXMLInteger(d, "total_fee"); + settlementTotalFee = readXMLInteger(d, "settlement_total_fee"); + feeType = readXMLString(d, "fee_type"); + cashFee = readXMLInteger(d, "cash_fee"); + cashFeeType = readXMLString(d, "cash_fee_type"); + couponFee = readXMLInteger(d, "coupon_fee"); + couponCount = readXMLInteger(d, "coupon_count"); + transactionId = readXMLString(d, "transaction_id"); + outTradeNo = readXMLString(d, "out_trade_no"); + attach = readXMLString(d, "attach"); + timeEnd = readXMLString(d, "time_end"); + version = readXMLString(d, "version"); + rateValue = readXMLString(d, "rate_value"); + signType = readXMLString(d, "sign_type"); + + composeCoupons(); + } + + /** + * 通过xml组装couponList属性内容. + */ + protected void composeCoupons() { + if (this.couponCount == null || this.couponCount == 0) { + return; + } + this.couponList = new ArrayList(couponCount); + for (int i = 0; i < this.couponCount; i++) { + WxPayOrderNotifyCoupon coupon = new WxPayOrderNotifyCoupon(); + coupon.setCouponId(this.getXmlValue("xml/coupon_id_" + i)); + coupon.setCouponType(this.getXmlValue("xml/coupon_type_" + i)); + coupon.setCouponFee(this.getXmlValueAsInt("xml/coupon_fee_" + i)); + couponList.add(coupon); + } + } + @Override public String toString() { return WxGsonBuilder.create().toJson(this); 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 ad56852824..d9f4cbd5f8 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 @@ -19,6 +19,7 @@ import lombok.NoArgsConstructor; import me.chanjar.weixin.common.util.json.WxGsonBuilder; import me.chanjar.weixin.common.util.xml.XStreamInitializer; +import org.w3c.dom.Document; /** *
@@ -80,6 +81,45 @@ public static WxPayRefundNotifyResult fromXML(String xmlString, String mchKey) t
 
   private ReqInfo reqInfo;
 
+  // 解密后的reqInfo 字符串
+  private transient String decryptedReqInfo;
+
+  @Override
+  protected void loadXML(Document d) {
+    reqInfoString = readXMLString(d, "req_info");
+  }
+
+  /**
+   * 解密并解析reqInfo
+   *
+   * @param mchKey
+   * @throws WxPayException
+   */
+  public void decryptReqInfo(String mchKey) throws WxPayException {
+    //如果是失败,直接返回,不用解析
+    if (WxPayConstants.ResultCode.FAIL.equals(getReturnCode())) {
+      return;
+    }
+    try {
+      final String keyMd5String = DigestUtils.md5Hex(mchKey).toLowerCase();
+      SecretKeySpec key = new SecretKeySpec(keyMd5String.getBytes(StandardCharsets.UTF_8), "AES");
+
+      Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
+      cipher.init(Cipher.DECRYPT_MODE, key);
+      decryptedReqInfo = new String(cipher.doFinal(Base64.decodeBase64(reqInfoString)), StandardCharsets.UTF_8);
+      loadReqInfo(decryptedReqInfo);
+    } catch (Exception e) {
+      throw new WxPayException("解密退款通知加密信息时出错", e);
+    }
+  }
+
+  // 本方法独立出来方便测试
+  protected void loadReqInfo(String decryptedReqInfo) {
+    Document document = openXML(decryptedReqInfo);
+    reqInfo = new ReqInfo();
+    reqInfo.loadXML(document);
+  }
+
   /**
    * 加密信息字段解密后的内容.
    */
@@ -272,6 +312,23 @@ public static ReqInfo fromXML(String xmlString) {
       xstream.processAnnotations(ReqInfo.class);
       return (ReqInfo) xstream.fromXML(xmlString);
     }
+
+    public void loadXML(Document d) {
+      transactionId = readXMLString(d, "transaction_id");
+      outTradeNo = readXMLString(d, "out_trade_no");
+      refundId = readXMLString(d, "refund_id");
+      outRefundNo = readXMLString(d, "out_refund_no");
+      totalFee = readXMLInteger(d, "total_fee");
+      settlementTotalFee = readXMLInteger(d, "settlement_total_fee");
+      refundFee = readXMLInteger(d, "refund_fee");
+      settlementRefundFee = readXMLInteger(d, "settlement_refund_fee");
+      refundStatus = readXMLString(d, "refund_status");
+      successTime = readXMLString(d, "success_time");
+      refundRecvAccout = readXMLString(d, "refund_recv_accout");
+      refundAccount = readXMLString(d, "refund_account");
+      refundRequestSource = readXMLString(d, "refund_request_source");
+    }
   }
 
+
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxScanPayNotifyResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxScanPayNotifyResult.java
index a87adabedd..9c9c451372 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxScanPayNotifyResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxScanPayNotifyResult.java
@@ -5,6 +5,7 @@
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.NoArgsConstructor;
+import org.w3c.dom.Document;
 
 /**
  * 
@@ -45,4 +46,11 @@ public class WxScanPayNotifyResult extends BaseWxPayResult {
   @XStreamAlias("product_id")
   private String productId;
 
+  @Override
+  protected void loadXML(Document d) {
+    openid = readXMLString(d, "openid");
+    isSubscribe = readXMLString(d, "is_subscribe");
+    productId = readXMLString(d, "product_id");
+  }
+
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingFinishRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingFinishRequest.java
index 22cd545083..3a0ee93648 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingFinishRequest.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingFinishRequest.java
@@ -7,6 +7,8 @@
 import lombok.*;
 import me.chanjar.weixin.common.annotation.Required;
 
+import java.util.Map;
+
 /**
  * @author Wang GuangXin 2019/10/23 14:02
  * @version 1.0
@@ -67,4 +69,11 @@ public class ProfitSharingFinishRequest extends BaseWxPayRequest {
   protected void checkConstraints() throws WxPayException {
     this.setSignType(WxPayConstants.SignType.HMAC_SHA256);
   }
+
+  @Override
+  protected void storeMap(Map map) {
+    map.put("transaction_id", transactionId);
+    map.put("out_order_no", outOrderNo);
+    map.put("description", description);
+  }
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingQueryRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingQueryRequest.java
index 353170b011..5f5282dbd6 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingQueryRequest.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingQueryRequest.java
@@ -5,8 +5,11 @@
 import com.github.binarywang.wxpay.exception.WxPayException;
 import com.thoughtworks.xstream.annotations.XStreamAlias;
 import lombok.*;
+import lombok.experimental.Accessors;
 import me.chanjar.weixin.common.annotation.Required;
 
+import java.util.Map;
+
 /**
  * @author Wang GuangXin 2019/10/22 15:44
  * @version 1.0
@@ -14,6 +17,7 @@
 @Data
 @EqualsAndHashCode(callSuper = true)
 @Builder(builderMethodName = "newBuilder")
+@Accessors(chain = true)
 @NoArgsConstructor
 @AllArgsConstructor
 @XStreamAlias("xml")
@@ -51,4 +55,15 @@ public class ProfitSharingQueryRequest extends BaseWxPayRequest {
   protected void checkConstraints() throws WxPayException {
     this.setSignType(WxPayConstants.SignType.HMAC_SHA256);
   }
+
+  @Override
+  public boolean ignoreAppid() {
+    return true;
+  }
+
+  @Override
+  protected void storeMap(Map map) {
+    map.put("transaction_id", transactionId);
+    map.put("out_order_no", outOrderNo);
+  }
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingQueryResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingQueryResult.java
index 6affffe670..49fdf74552 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingQueryResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingQueryResult.java
@@ -8,6 +8,7 @@
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.NoArgsConstructor;
+import org.w3c.dom.Document;
 
 /**
  * @author Wang GuangXin 2019/10/22 15:51
@@ -67,6 +68,18 @@ public ProfitSharingQueryResult.Receivers formatReceivers() {
     return gson.fromJson(receivers, Receivers.class);
   }
 
+  @Override
+  protected void loadXML(Document d) {
+    transactionId = readXMLString(d, "transaction_id");
+    outOrderNo = readXMLString(d, "out_order_no");
+    orderId = readXMLString(d, "orderId");
+    status = readXMLString(d, "status");
+    closeReason = readXMLString(d, "close_reason");
+    receivers = readXMLString(d, "receivers");
+    amount = readXMLInteger(d, "amount");
+    description = readXMLString(d, "description");
+  }
+
   @Data
   public class Receivers {
     /**
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReceiverRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReceiverRequest.java
index 3d00d5dd7c..db64854395 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReceiverRequest.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReceiverRequest.java
@@ -7,6 +7,8 @@
 import lombok.*;
 import me.chanjar.weixin.common.annotation.Required;
 
+import java.util.Map;
+
 /**
  * 添加/删除分账接受方请求对象
  *
@@ -44,4 +46,9 @@ public class ProfitSharingReceiverRequest extends BaseWxPayRequest {
   protected void checkConstraints() throws WxPayException {
     this.setSignType(WxPayConstants.SignType.HMAC_SHA256);
   }
+
+  @Override
+  protected void storeMap(Map map) {
+    map.put("receiver", receiver);
+  }
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReceiverResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReceiverResult.java
index 0b9f53881c..806632fe54 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReceiverResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReceiverResult.java
@@ -6,6 +6,7 @@
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.NoArgsConstructor;
+import org.w3c.dom.Document;
 
 /**
  * @author Wang GuangXin 2019/10/22 14:54
@@ -22,4 +23,9 @@ public class ProfitSharingReceiverResult extends BaseWxPayResult {
    */
   @XStreamAlias("receiver")
   private String receiver;
+
+  @Override
+  protected void loadXML(Document d) {
+    receiver = readXMLString(d, "receiver");
+  }
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingRequest.java
index 6e6c2a3147..e3b1f5690c 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingRequest.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingRequest.java
@@ -7,6 +7,8 @@
 import lombok.*;
 import me.chanjar.weixin.common.annotation.Required;
 
+import java.util.Map;
+
 /**
  * @author Wang GuangXin 2019/10/21 17:57
  * @version 1.0
@@ -81,4 +83,11 @@ protected void checkConstraints() throws WxPayException {
     // 目前仅支持HMAC-SHA256.
     this.setSignType(WxPayConstants.SignType.HMAC_SHA256);
   }
+
+  @Override
+  protected void storeMap(Map map) {
+    map.put("transaction_id", transactionId);
+    map.put("out_order_no", outOrderNo);
+    map.put("receivers", receivers);
+  }
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingResult.java
index 122821cf91..daea0e64db 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingResult.java
@@ -5,6 +5,7 @@
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.NoArgsConstructor;
+import org.w3c.dom.Document;
 
 /**
  * @author Wang GuangXin 2019/10/22 10:06
@@ -30,4 +31,11 @@ public class ProfitSharingResult extends BaseWxPayResult {
    */
   @XStreamAlias("order_id")
   private String orderId;
+
+  @Override
+  protected void loadXML(Document d) {
+    transactionId = readXMLString(d, "transaction_id");
+    outOrderNo = readXMLString(d, "out_order_no");
+    orderId = readXMLString(d, "order_id");
+  }
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReturnQueryRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReturnQueryRequest.java
index e2a2da4739..734c805401 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReturnQueryRequest.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReturnQueryRequest.java
@@ -8,6 +8,8 @@
 import me.chanjar.weixin.common.annotation.Required;
 import org.apache.commons.lang3.StringUtils;
 
+import java.util.Map;
+
 /**
  * @author Wang GuangXin 2019/10/23 15:32
  * @version 1.0
@@ -69,4 +71,11 @@ protected void checkConstraints() throws WxPayException {
     }
     this.setSignType(WxPayConstants.SignType.HMAC_SHA256);
   }
+
+  @Override
+  protected void storeMap(Map map) {
+    map.put("order_id", orderId);
+    map.put("out_order_no", outOrderNo);
+    map.put("out_return_no", outReturnNo);
+  }
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReturnRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReturnRequest.java
index 6bdc73aa62..3e389a467a 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReturnRequest.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReturnRequest.java
@@ -8,6 +8,8 @@
 import me.chanjar.weixin.common.annotation.Required;
 import org.apache.commons.lang3.StringUtils;
 
+import java.util.Map;
+
 /**
  * @author Wang GuangXin 2019/10/23 14:27
  * @version 1.0
@@ -130,4 +132,15 @@ protected void checkConstraints() throws WxPayException {
     }
     this.setSignType(WxPayConstants.SignType.HMAC_SHA256);
   }
+
+  @Override
+  protected void storeMap(Map map) {
+    map.put("order_id", orderId);
+    map.put("out_order_no", outOrderNo);
+    map.put("out_return_no", outReturnNo);
+    map.put("return_account_type", returnAccountType);
+    map.put("return_account", returnAccount);
+    map.put("return_amount", returnAmount.toString());
+    map.put("description", description);
+  }
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReturnResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReturnResult.java
index 814bcf2d58..2828d4ab82 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReturnResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReturnResult.java
@@ -5,6 +5,7 @@
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.NoArgsConstructor;
+import org.w3c.dom.Document;
 
 /**
  * @author Wang GuangXin 2019/10/23 14:41
@@ -71,4 +72,19 @@ public class ProfitSharingReturnResult extends BaseWxPayResult {
    */
   @XStreamAlias("finish_time")
   private String finishTime;
+
+  @Override
+  protected void loadXML(Document d) {
+    orderId = readXMLString(d, "order_id");
+    outOrderNo = readXMLString(d, "out_order_no");
+    outReturnNo = readXMLString(d, "out_return_no");
+    returnNo = readXMLString(d, "return_no");
+    returnAccountType = readXMLString(d, "return_account_type");
+    returnAccount = readXMLString(d, "return_account");
+    returnAmount = readXMLInteger(d, "return_amount");
+    description = readXMLString(d, "description");
+    result = readXMLString(d, "result");
+    failReason = readXMLString(d, "fail_reason");
+    finishTime = readXMLString(d, "finish_time");
+  }
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/BaseWxPayRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/BaseWxPayRequest.java
index 73793a23ff..d60e56893b 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/BaseWxPayRequest.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/BaseWxPayRequest.java
@@ -1,21 +1,26 @@
 package com.github.binarywang.wxpay.bean.request;
 
-import java.io.Serializable;
-import java.math.BigDecimal;
-
-import lombok.experimental.Accessors;
-import org.apache.commons.lang3.StringUtils;
-
 import com.github.binarywang.wxpay.config.WxPayConfig;
 import com.github.binarywang.wxpay.exception.WxPayException;
 import com.github.binarywang.wxpay.util.SignUtils;
+import com.github.binarywang.wxpay.util.XmlConfig;
 import com.thoughtworks.xstream.XStream;
 import com.thoughtworks.xstream.annotations.XStreamAlias;
 import lombok.Data;
+import lombok.experimental.Accessors;
 import me.chanjar.weixin.common.error.WxErrorException;
 import me.chanjar.weixin.common.util.BeanUtils;
 import me.chanjar.weixin.common.util.json.WxGsonBuilder;
 import me.chanjar.weixin.common.util.xml.XStreamInitializer;
+import org.apache.commons.lang3.StringUtils;
+import org.dom4j.Document;
+import org.dom4j.DocumentHelper;
+import org.dom4j.Element;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.HashMap;
+import java.util.Map;
 
 import static com.github.binarywang.wxpay.constant.WxPayConstants.SignType.ALL_SIGN_TYPES;
 
@@ -118,6 +123,21 @@ public abstract class BaseWxPayRequest implements Serializable {
   @XStreamAlias("sign_type")
   private String signType;
 
+
+  /**
+   * 企业微信签名
+   */
+  @XStreamAlias("workwx_sign")
+  private String workWxSign;
+
+  public String getWorkWxSign() {
+    return workWxSign;
+  }
+
+  public void setWorkWxSign(String workWxSign) {
+    this.workWxSign = workWxSign;
+  }
+
   /**
    * 将单位为元转换为单位为分.
    *
@@ -188,14 +208,52 @@ public String toString() {
    * @return the string
    */
   public String toXML() {
-    XStream xstream = XStreamInitializer.getInstance();
     //涉及到服务商模式的两个参数,在为空值时置为null,以免在请求时将空值传给微信服务器
     this.setSubAppId(StringUtils.trimToNull(this.getSubAppId()));
     this.setSubMchId(StringUtils.trimToNull(this.getSubMchId()));
+    if (XmlConfig.fastMode) {
+      return toFastXml();
+    }
+    XStream xstream = XStreamInitializer.getInstance();
     xstream.processAnnotations(this.getClass());
     return xstream.toXML(this);
   }
 
+  /**
+   * 使用快速算法组装xml
+   *
+   * @return
+   */
+  private String toFastXml() {
+    try {
+      Document document = DocumentHelper.createDocument();
+      Element root = document.addElement(xmlRootTagName());
+
+      Map signParams = getSignParams();
+      signParams.put("sign", sign);
+      for (Map.Entry entry : signParams.entrySet()) {
+        if (entry.getValue() == null) {
+          continue;
+        }
+        Element elm = root.addElement(entry.getKey());
+        elm.addText(entry.getValue());
+      }
+
+      return document.asXML();
+    } catch (Exception e) {
+      throw new RuntimeException("generate xml error", e);
+    }
+  }
+
+  /**
+   * 返回xml结构的根节点名称
+   *
+   * @return 默认返回"xml", 特殊情况可以在子类中覆盖
+   */
+  protected String xmlRootTagName() {
+    return "xml";
+  }
+
   /**
    * 签名时,是否忽略appid.
    *
@@ -205,6 +263,26 @@ protected boolean ignoreAppid() {
     return false;
   }
 
+  /**
+   * 签名时,是否忽略sub_appid.
+   *
+   * @return the boolean
+   */
+  protected boolean ignoreSubAppId() {
+    return false;
+  }
+
+  protected boolean ignoreSubMchId() {
+    return false;
+  }
+
+  /**
+   * 是否是企业微信字段
+   */
+  protected boolean isWxWorkSign() {
+    return false;
+  }
+
   /**
    * 签名时,忽略的参数.
    *
@@ -214,6 +292,32 @@ protected String[] getIgnoredParamsForSign() {
     return new String[0];
   }
 
+  /**
+   * 获取签名时需要的参数.
+   * 注意:不含sign属性
+   */
+  public Map getSignParams() {
+    Map map = new HashMap<>();
+    map.put("appid", appid);
+    map.put("mch_id", mchId);
+    map.put("sub_appid", subAppId);
+    map.put("sub_mch_id", subMchId);
+    map.put("nonce_str", nonceStr);
+    map.put("sign_type", signType);
+
+    storeMap(map);
+    return map;
+  }
+
+  /**
+   * 将属性组装到一个Map中,供签名和最终发送XML时使用.
+   * 这里需要将所有的属性全部保存进来,签名的时候会自动调用getIgnoredParamsForSign进行忽略,
+   * 不用担心。否则最终生成的XML会缺失。
+   *
+   * @param map 传入的属性Map
+   */
+  abstract protected void storeMap(Map map);
+
   /**
    * 
    * 检查参数,并设置签名.
@@ -238,12 +342,16 @@ public void checkAndSign(WxPayConfig config) throws WxPayException {
       this.setMchId(config.getMchId());
     }
 
-    if (StringUtils.isBlank(getSubAppId())) {
-      this.setSubAppId(config.getSubAppId());
+    if (!ignoreSubAppId()) {
+      if (StringUtils.isBlank(getSubAppId())) {
+        this.setSubAppId(config.getSubAppId());
+      }
     }
 
-    if (StringUtils.isBlank(getSubMchId())) {
-      this.setSubMchId(config.getSubMchId());
+    if (!ignoreSubMchId()) {
+      if (StringUtils.isBlank(getSubMchId())) {
+        this.setSubMchId(config.getSubMchId());
+      }
     }
 
     if (StringUtils.isBlank(getSignType())) {
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayAuthcode2OpenidRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayAuthcode2OpenidRequest.java
index 3b156407db..c9d5505b36 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayAuthcode2OpenidRequest.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayAuthcode2OpenidRequest.java
@@ -3,6 +3,8 @@
 import com.thoughtworks.xstream.annotations.XStreamAlias;
 import lombok.*;
 
+import java.util.Map;
+
 /**
  * 
  * 授权码查询openid接口请求对象类
@@ -35,4 +37,10 @@ public class WxPayAuthcode2OpenidRequest extends BaseWxPayRequest {
   protected void checkConstraints() {
     // nothing to do
   }
+
+  @Override
+  protected void storeMap(Map map) {
+    map.put("auth_code", authCode);
+  }
+
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayDefaultRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayDefaultRequest.java
index e44a6111e7..34a707bed7 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayDefaultRequest.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayDefaultRequest.java
@@ -2,6 +2,8 @@
 
 import com.thoughtworks.xstream.annotations.XStreamAlias;
 
+import java.util.Map;
+
 /**
  * 
  *  支付请求默认对象类
@@ -21,4 +23,8 @@ protected void checkConstraints() {
   protected boolean ignoreAppid() {
     return true;
   }
+
+  @Override
+  protected void storeMap(Map map) {
+  }
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayDownloadBillRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayDownloadBillRequest.java
index e616224db7..383dbe5b7c 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayDownloadBillRequest.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayDownloadBillRequest.java
@@ -9,6 +9,7 @@
 import org.apache.commons.lang3.StringUtils;
 
 import java.util.Arrays;
+import java.util.Map;
 
 /**
  * 
@@ -95,4 +96,13 @@ protected void checkConstraints() throws WxPayException {
         Arrays.toString(BILL_TYPES), this.getBillType()));
     }
   }
+
+  @Override
+  protected void storeMap(Map map) {
+    map.put("device_info", deviceInfo);
+    map.put("bill_type", billType);
+    map.put("bill_date", billDate);
+    map.put("tar_type", tarType);
+  }
+
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayDownloadFundFlowRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayDownloadFundFlowRequest.java
index 8ce06238e6..efb14fc7c0 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayDownloadFundFlowRequest.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayDownloadFundFlowRequest.java
@@ -9,6 +9,7 @@
 import org.apache.commons.lang3.StringUtils;
 
 import java.util.Arrays;
+import java.util.Map;
 
 /**
  * 
@@ -87,4 +88,11 @@ protected void checkConstraints() throws WxPayException {
      */
     this.setSignType(SIGN_TYPE_HMAC_SHA256);
   }
+
+  @Override
+  protected void storeMap(Map map) {
+    map.put("bill_date", billDate);
+    map.put("account_type", accountType);
+    map.put("tar_type", tarType);
+  }
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayFaceAuthInfoRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayFaceAuthInfoRequest.java
index eb53e38c6a..cef831a3d6 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayFaceAuthInfoRequest.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayFaceAuthInfoRequest.java
@@ -4,6 +4,8 @@
 import lombok.*;
 import me.chanjar.weixin.common.annotation.Required;
 
+import java.util.Map;
+
 /**
  * 
  *  获取微信刷脸调用凭证请求对象类
@@ -123,4 +125,15 @@ protected void checkConstraints() {
     //do nothing
   }
 
+  @Override
+  protected void storeMap(Map map) {
+    map.put("store_id", storeId);
+    map.put("store_name", storeName);
+    map.put("device_id", deviceId);
+    map.put("attach", attach);
+    map.put("rawdata", rawdata);
+    map.put("now", now);
+    map.put("version", version);
+  }
+
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayFacepayRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayFacepayRequest.java
index 2c70a8945c..e9821c506c 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayFacepayRequest.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayFacepayRequest.java
@@ -4,6 +4,8 @@
 import lombok.*;
 import me.chanjar.weixin.common.annotation.Required;
 
+import java.util.Map;
+
 /**
  * 
  *  提交刷脸支付请求对象类
@@ -174,4 +176,19 @@ protected void checkConstraints() {
     //do nothing
   }
 
+  @Override
+  protected void storeMap(Map map) {
+    map.put("device_info", deviceInfo);
+    map.put("body", body);
+    map.put("detail", detail);
+    map.put("attach", attach);
+    map.put("out_trade_no", outTradeNo);
+    map.put("total_fee", totalFee.toString());
+    map.put("fee_type", feeType);
+    map.put("spbill_create_ip", spbillCreateIp);
+    map.put("goods_tag", goodsTag);
+    map.put("openid", openid);
+    map.put("face_code", faceCode);
+  }
+
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayMicropayRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayMicropayRequest.java
index 8cec4f0bd7..cdded3110d 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayMicropayRequest.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayMicropayRequest.java
@@ -4,9 +4,11 @@
 import lombok.*;
 import me.chanjar.weixin.common.annotation.Required;
 
+import java.util.Map;
+
 /**
  * 
- *  提交刷卡支付请求对象类
+ *  提交付款码支付请求对象类
  * Created by Binary Wang on 2017-3-23.
  * 
* @@ -19,6 +21,19 @@ @AllArgsConstructor @XStreamAlias("xml") public class WxPayMicropayRequest extends BaseWxPayRequest { + /** + *
+   * 字段名:设备号.
+   * 变量名:device_info
+   * 是否必填:否
+   * 类型:String(32)
+   * 示例值:013467007045764
+   * 描述:终端设备号(商户自定义,如门店编号)
+   * 
+ */ + @XStreamAlias("device_info") + private String deviceInfo; + /** *
    * 字段名:接口版本号.
@@ -180,6 +195,20 @@ public class WxPayMicropayRequest extends BaseWxPayRequest {
   @XStreamAlias("time_expire")
   private String timeExpire;
 
+  /**
+   * 
+   * 字段名:电子发票入口开放标识	.
+   * 变量名:receipt
+   * 是否必填:否
+   * 类型:String(8)
+   * 示例值:Y
+   * 描述:Y,传入Y时,支付成功消息和支付详情页将出现开票入口。需要在微信支付商户平台或微信公众平台开通电子发票功能,传此字段才可生效
+   * 
+ **/ + @Required + @XStreamAlias("receipt") + private String receipt; + /** *
    * 字段名:授权码.
@@ -211,9 +240,39 @@ public class WxPayMicropayRequest extends BaseWxPayRequest {
   @XStreamAlias("scene_info")
   private String sceneInfo;
 
+  /**
+   * 
+   * 字段名:是否指定服务商分账.
+   * 变量名:profit_sharing
+   * 是否必填:否
+   * 详情:Y-是,需要分账  N-否,不分账,字母要求大写,不传默认不分账
+   * 详细参考 https://pay.weixin.qq.com/wiki/doc/api/allocation_sl.php?chapter=24_3&index=3
+   * 
+ */ + @XStreamAlias("profit_sharing") + private String profitSharing; + @Override protected void checkConstraints() { //do nothing } + @Override + protected void storeMap(Map map) { + map.put("version", version); + map.put("body", body); + map.put("detail", detail); + map.put("attach", attach); + map.put("out_trade_no", outTradeNo); + map.put("total_fee", totalFee.toString()); + map.put("fee_type", feeType); + map.put("spbill_create_ip", spbillCreateIp); + map.put("goods_tag", goodsTag); + map.put("limit_pay", limitPay); + map.put("time_start", timeStart); + map.put("time_expire", timeExpire); + map.put("auth_code", authCode); + map.put("scene_info", sceneInfo); + } + } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderCloseRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderCloseRequest.java index e430460e39..3758653a03 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderCloseRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderCloseRequest.java @@ -3,6 +3,8 @@ import com.thoughtworks.xstream.annotations.XStreamAlias; import lombok.*; +import java.util.Map; + /** *
  *  关闭订单请求对象类
@@ -36,4 +38,10 @@ public class WxPayOrderCloseRequest extends BaseWxPayRequest {
   protected void checkConstraints() {
 
   }
+
+  @Override
+  protected void storeMap(Map map) {
+    map.put("out_trade_no", outTradeNo);
+  }
+
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderQueryRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderQueryRequest.java
index 221b04f172..ffcd52d171 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderQueryRequest.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderQueryRequest.java
@@ -5,6 +5,8 @@
 import lombok.*;
 import org.apache.commons.lang3.StringUtils;
 
+import java.util.Map;
+
 /**
  * 
  * 订单查询请求对象
@@ -76,4 +78,12 @@ protected void checkConstraints() throws WxPayException {
       throw new WxPayException("transaction_id 和 out_trade_no 不能同时存在或同时为空,必须二选一");
     }
   }
+
+  @Override
+  protected void storeMap(Map map) {
+    map.put("version", version);
+    map.put("transaction_id", transactionId);
+    map.put("out_trade_no", outTradeNo);
+  }
+
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderReverseRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderReverseRequest.java
index 5394631b3c..73c5034696 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderReverseRequest.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderReverseRequest.java
@@ -5,6 +5,8 @@
 import lombok.*;
 import org.apache.commons.lang3.StringUtils;
 
+import java.util.Map;
+
 /**
  * 
  * 撤销订单请求类
@@ -53,4 +55,10 @@ protected void checkConstraints() throws WxPayException {
     }
   }
 
+  @Override
+  protected void storeMap(Map map) {
+    map.put("transaction_id", transactionId);
+    map.put("out_trade_no", outTradeNo);
+  }
+
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayQueryCommentRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayQueryCommentRequest.java
index 5ff1c76a04..5c4a9fe2ff 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayQueryCommentRequest.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayQueryCommentRequest.java
@@ -5,6 +5,8 @@
 import lombok.*;
 import me.chanjar.weixin.common.annotation.Required;
 
+import java.util.Map;
+
 /**
  * 
  *  拉取订单评价数据接口的请求参数封装类.
@@ -84,4 +86,14 @@ public class WxPayQueryCommentRequest extends BaseWxPayRequest {
   protected void checkConstraints() throws WxPayException {
   }
 
+  @Override
+  protected void storeMap(Map map) {
+    map.put("begin_time", beginTime);
+    map.put("end_time", endTime);
+    map.put("offset", offset.toString());
+    if (limit != null) {
+      map.put("limit", limit.toString());
+    }
+  }
+
 }
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 9303810753..01ca5cafd4 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
@@ -3,6 +3,8 @@
 import com.thoughtworks.xstream.annotations.XStreamAlias;
 import lombok.*;
 
+import java.util.Map;
+
 /**
  * 
  *   注释中各行对应含义:
@@ -27,8 +29,9 @@ public class WxPayRedpackQueryRequest extends BaseWxPayRequest {
 
   @Override
   protected String[] getIgnoredParamsForSign() {
-    return new String[]{"sub_appid","sub_mch_id","sign_type"};
+    return new String[]{"sub_appid", "sub_mch_id", "sign_type"};
   }
+
   /**
    * 商户订单号
    * mch_billno
@@ -55,4 +58,10 @@ protected String[] getIgnoredParamsForSign() {
   protected void checkConstraints() {
 
   }
+
+  @Override
+  protected void storeMap(Map map) {
+    map.put("mch_billno", mchBillNo);
+    map.put("bill_type", billType);
+  }
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundQueryRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundQueryRequest.java
index 4df8a63db9..d9befee6f6 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundQueryRequest.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundQueryRequest.java
@@ -5,6 +5,8 @@
 import lombok.*;
 import org.apache.commons.lang3.StringUtils;
 
+import java.util.Map;
+
 /**
  * 
  * Created by Binary Wang on 2016-11-24.
@@ -91,4 +93,13 @@ protected void checkConstraints() throws WxPayException {
     }
 
   }
+
+  @Override
+  protected void storeMap(Map map) {
+    map.put("device_info", deviceInfo);
+    map.put("transaction_id", transactionId);
+    map.put("out_trade_no", outTradeNo);
+    map.put("out_refund_no", outRefundNo);
+    map.put("refund_id", refundId);
+  }
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundRequest.java
index 1383e4f5b1..924d46ffdd 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundRequest.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundRequest.java
@@ -10,6 +10,7 @@
 import org.apache.commons.lang3.StringUtils;
 
 import java.util.Arrays;
+import java.util.Map;
 
 /**
  * 
@@ -165,7 +166,7 @@ public class WxPayRefundRequest extends BaseWxPayRequest {
    * 类型:String(256)
    * 示例值:https://weixin.qq.com/notify/
    * 描述:	异步接收微信支付退款结果通知的回调地址,通知URL必须为外网可访问的url,不允许带参数
-   如果参数中传了notify_url,则商户平台上配置的回调地址将不会生效。
+   * 如果参数中传了notify_url,则商户平台上配置的回调地址将不会生效。
    * 
*/ @XStreamAlias("notify_url") @@ -194,4 +195,19 @@ protected void checkConstraints() throws WxPayException { } } + @Override + protected void storeMap(Map map) { + map.put("device_info", deviceInfo); + map.put("transaction_id", transactionId); + map.put("out_trade_no", outTradeNo); + map.put("out_refund_no", outRefundNo); + map.put("total_fee", totalFee.toString()); + map.put("refund_fee", refundFee.toString()); + map.put("refund_fee_type", refundFeeType); + map.put("op_user_id", opUserId); + map.put("refund_account", refundAccount); + map.put("refund_desc", refundDesc); + map.put("notify_url", notifyUrl); + } + } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayReportRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayReportRequest.java index 3e5364c5ce..56cd490a55 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayReportRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayReportRequest.java @@ -4,6 +4,8 @@ import lombok.*; import me.chanjar.weixin.common.annotation.Required; +import java.util.Map; + /** *
  * 微信支付-交易保障请求参数
@@ -174,4 +176,19 @@ public class WxPayReportRequest extends BaseWxPayRequest {
   protected void checkConstraints() {
     //do nothing
   }
+
+  @Override
+  protected void storeMap(Map map) {
+    map.put("device_info", deviceInfo);
+    map.put("interface_url", interfaceUrl);
+    map.put("execute_time_", executeTime.toString());
+    map.put("return_code", returnCode);
+    map.put("return_msg", returnMsg);
+    map.put("result_code", resultCode);
+    map.put("err_code", errCode);
+    map.put("err_code_des", errCodeDes);
+    map.put("out_trade_no", outTradeNo);
+    map.put("user_ip", userIp);
+    map.put("time", time);
+  }
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPaySendMiniProgramRedpackRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPaySendMiniProgramRedpackRequest.java
new file mode 100644
index 0000000000..fd727ab6aa
--- /dev/null
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPaySendMiniProgramRedpackRequest.java
@@ -0,0 +1,149 @@
+package com.github.binarywang.wxpay.bean.request;
+
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import lombok.*;
+import lombok.experimental.Accessors;
+
+import java.util.Map;
+
+/**
+ * 发送小程序红包请求参数对象.
+ *
+ * @author Binary Wang
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+@XStreamAlias("xml")
+@Accessors(chain = true)
+public class WxPaySendMiniProgramRedpackRequest extends BaseWxPayRequest {
+  private static final long serialVersionUID = -2035425086824987567L;
+
+  @Override
+  protected String[] getIgnoredParamsForSign() {
+    return new String[]{"sign_type", "sub_appid"};
+  }
+
+  /**
+   * mch_billno.
+   * 商户订单号(每个订单号必须唯一)
+   * 组成:mch_id+yyyymmdd+10位一天内不能重复的数字。  接口根据商户订单号支持重入,如出现超时可再调用。
+   */
+  @XStreamAlias("mch_billno")
+  private String mchBillNo;
+
+  /**
+   * send_name.
+   * 商户名称
+   * 红包发送者名称
+   */
+  @XStreamAlias("send_name")
+  private String sendName;
+
+  /**
+   * re_openid.
+   * 接受红包的用户   用户在wxappid下的openid
+   */
+  @XStreamAlias("re_openid")
+  private String reOpenid;
+
+  /**
+   * total_amount.
+   * 红包总额
+   */
+  @XStreamAlias("total_amount")
+  private Integer totalAmount;
+
+  /**
+   * total_num
+   * 红包发放总人数
+   */
+  @XStreamAlias("total_num")
+  private Integer totalNum;
+
+  /**
+   * wishing.
+   * 红包祝福语
+   */
+  @XStreamAlias("wishing")
+  private String wishing;
+
+  /**
+   * act_name.
+   * 活动名称
+   */
+  @XStreamAlias("act_name")
+  private String actName;
+
+  /**
+   * remark.
+   * 备注
+   */
+  @XStreamAlias("remark")
+  private String remark;
+
+  /**
+   * 通知用户形式	.
+   * 通过JSAPI方式领取红包,小程序红包固定传MINI_PROGRAM_JSAPI
+   */
+  @XStreamAlias("notify_way")
+  private String notifyWay = "MINI_PROGRAM_JSAPI";
+
+  /**
+   * 
+   * 发放红包使用场景,红包金额大于200时必传
+   * PRODUCT_1:商品促销
+   * PRODUCT_2:抽奖
+   * PRODUCT_3:虚拟物品兑奖
+   * PRODUCT_4:企业内部福利
+   * PRODUCT_5:渠道分润
+   * PRODUCT_6:保险回馈
+   * PRODUCT_7:彩票派奖
+   * PRODUCT_8:税务刮奖
+   * 
+ */ + @XStreamAlias("scene_id") + private String sceneId; + + /** + * wxappid. + * 微信分配的公众账号ID(企业号corpid即为此appId)。 + * 接口传入的所有appid应该为公众号的appid(在mp.weixin.qq.com申请的), + * 不能为APP的appid(在open.weixin.qq.com申请的) + */ + @XStreamAlias("wxappid") + private String wxAppid; + + @Override + protected void checkConstraints() { + + } + + @Override + protected void storeMap(Map map) { + map.put("mch_billno", mchBillNo); + map.put("send_name", sendName); + map.put("re_openid", reOpenid); + map.put("total_amount", totalAmount.toString()); + map.put("total_num", totalNum.toString()); + map.put("wishing", wishing); + map.put("act_name", actName); + map.put("remark", remark); + map.put("notify_way", notifyWay); + map.put("scene_id", sceneId); + map.put("wxappid", wxAppid); + } + + @Override + public String getAppid() { + return this.wxAppid; + } + + @Override + public void setAppid(String appid) { + this.wxAppid = appid; + } + +} 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 977f363f6a..4a8bae4f57 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 @@ -3,6 +3,8 @@ import com.thoughtworks.xstream.annotations.XStreamAlias; import lombok.*; +import java.util.Map; + /** * 发送红包请求参数对象. * Created by Binary Wang on 2016/9/24. @@ -181,4 +183,18 @@ public void setAppid(String appid) { this.wxAppid = appid; } + @Override + protected void storeMap(Map map) { + map.put("mch_billno", mchBillNo); + map.put("send_name", sendName); + map.put("re_openid", reOpenid); + map.put("total_amount", totalAmount.toString()); + map.put("total_num", totalNum.toString()); + map.put("amt_type", amtType); + map.put("wishing", wishing); + map.put("client_ip", clientIp); + map.put("act_name", actName); + map.put("remark", remark); + } + } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayShorturlRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayShorturlRequest.java index 87ac41054b..8602740bb8 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayShorturlRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayShorturlRequest.java @@ -3,6 +3,8 @@ import com.thoughtworks.xstream.annotations.XStreamAlias; import lombok.*; +import java.util.Map; + /** *
  * 转换短链接请求对象类
@@ -35,4 +37,9 @@ public class WxPayShorturlRequest extends BaseWxPayRequest {
   protected void checkConstraints() {
     //do nothing
   }
+
+  @Override
+  protected void storeMap(Map map) {
+    map.put("long_url", longUrl);
+  }
 }
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 54d443881d..fc5949dfdf 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
@@ -9,6 +9,8 @@
 import me.chanjar.weixin.common.annotation.Required;
 import org.apache.commons.lang3.StringUtils;
 
+import java.util.Map;
+
 /**
  * 
  * 统一下单请求参数对象.
@@ -396,4 +398,30 @@ public void checkAndSign(WxPayConfig config) throws WxPayException {
     super.checkAndSign(config);
   }
 
+  @Override
+  protected void storeMap(Map map) {
+    map.put("version", version);
+    map.put("device_info", deviceInfo);
+    map.put("body", body);
+    map.put("detail", detail);
+    map.put("attach", attach);
+    map.put("out_trade_no", outTradeNo);
+    map.put("fee_type", feeType);
+    map.put("total_fee", totalFee.toString());
+    map.put("spbill_create_ip", spbillCreateIp);
+    map.put("time_start", timeStart);
+    map.put("time_expire", timeExpire);
+    map.put("goods_tag", goodsTag);
+    map.put("notify_url", notifyUrl);
+    map.put("trade_type", tradeType);
+    map.put("product_id", productId);
+    map.put("limit_pay", limitPay);
+    map.put("openid", openid);
+    map.put("sub_openid", subOpenid);
+    map.put("receipt", receipt);
+    map.put("scene_info", sceneInfo);
+    map.put("fingerprint", fingerprint);
+    map.put("profit_sharing", profitSharing);
+  }
+
 }
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 5a1ea64e83..d58f20fb18 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
@@ -11,6 +11,7 @@
 import javax.xml.xpath.XPathExpressionException;
 import javax.xml.xpath.XPathFactory;
 
+import com.github.binarywang.wxpay.util.XmlConfig;
 import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -30,6 +31,8 @@
 import me.chanjar.weixin.common.util.json.WxGsonBuilder;
 import me.chanjar.weixin.common.util.xml.XStreamInitializer;
 
+import org.w3c.dom.*;
+
 /**
  * 
  * 微信支付结果共用属性类.
@@ -107,8 +110,9 @@ public abstract class BaseWxPayResult implements Serializable {
 
   /**
    * xml的Document对象,用于解析xml文本.
+   * make xmlDoc transient to ensure toString() can work.
    */
-  private Document xmlDoc;
+  private transient Document xmlDoc;
 
   /**
    * 将单位分转换成单位圆.
@@ -129,6 +133,18 @@ public static String fenToYuan(Integer fen) {
    * @return the t
    */
   public static  T fromXML(String xmlString, Class clz) {
+    if (XmlConfig.fastMode) {
+      try {
+        BaseWxPayResult t = clz.newInstance();
+        t.setXmlString(xmlString);
+        Document doc = t.getXmlDoc();
+        t.loadBasicXML(doc);
+        t.loadXML(doc);
+        return (T) t;
+      } catch (Exception e) {
+        throw new RuntimeException("parse xml error", e);
+      }
+    }
     XStream xstream = XStreamInitializer.getInstance();
     xstream.processAnnotations(clz);
     T result = (T) xstream.fromXML(xmlString);
@@ -136,6 +152,70 @@ public static  T fromXML(String xmlString, Class c
     return result;
   }
 
+  /**
+   * 从XML文档中加载属性,供子类覆盖加载额外的属性
+   *
+   * @param d Document
+   */
+  protected abstract void loadXML(Document d);
+
+  /**
+   * 从XML文档中加载基础属性
+   *
+   * @param d Document
+   */
+  private void loadBasicXML(Document d) {
+    returnCode = readXMLString(d, "return_code");
+    returnMsg = readXMLString(d, "return_msg");
+    resultCode = readXMLString(d, "result_code");
+    errCode = readXMLString(d, "err_code");
+    errCodeDes = readXMLString(d, "err_code_des");
+    appid = readXMLString(d, "appid");
+    mchId = readXMLString(d, "mch_id");
+    subAppId = readXMLString(d, "sub_appid");
+    subMchId = readXMLString(d, "sub_mch_id");
+    nonceStr = readXMLString(d, "nonce_str");
+    sign = readXMLString(d, "sign");
+  }
+
+  public static Integer readXMLInteger(Node d, String tagName) {
+    String content = readXMLString(d, tagName);
+    if (content == null || content.trim().length() == 0) return null;
+    return Integer.parseInt(content);
+  }
+
+  public static String readXMLString(Node d, String tagName) {
+    if (!d.hasChildNodes()) return null;
+    NodeList childNodes = d.getChildNodes();
+    for (int i = 0, j = childNodes.getLength(); i < j; i++) {
+      Node node = childNodes.item(i);
+      if (tagName.equals(node.getNodeName())) {
+        if (!node.hasChildNodes()) return null;
+        return node.getFirstChild().getNodeValue();
+      }
+    }
+    return null;
+  }
+
+  public static String readXMLString(Document d, String tagName) {
+    NodeList elements = d.getElementsByTagName(tagName);
+    if (elements == null || elements.getLength() == 0) {
+      return null;
+    }
+
+    Node node = elements.item(0).getFirstChild();
+    if (node == null) {
+      return null;
+    }
+    return node.getNodeValue();
+  }
+
+  public static Integer readXMLInteger(Document d, String tagName) {
+    String content = readXMLString(d, tagName);
+    if (content == null || content.trim().length() == 0) return null;
+    return Integer.parseInt(content);
+  }
+
   /**
    * Gets logger.
    *
@@ -185,18 +265,19 @@ private Document getXmlDoc() {
     if (this.xmlDoc != null) {
       return this.xmlDoc;
     }
+    xmlDoc = openXML(xmlString);
+    return xmlDoc;
+  }
 
+  protected Document openXML(String content) {
     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;
+      return factory.newDocumentBuilder().parse(new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)));
     } catch (Exception e) {
       throw new RuntimeException("非法的xml文本内容:\n" + this.xmlString, e);
     }
-
   }
 
   /**
@@ -205,7 +286,7 @@ private Document getXmlDoc() {
    * @param path the path
    * @return the xml value
    */
-  String getXmlValue(String... path) {
+  protected String getXmlValue(String... path) {
     Document doc = this.getXmlDoc();
     String expression = String.format("/%s//text()", Joiner.on("/").join(path));
     try {
@@ -225,7 +306,7 @@ String getXmlValue(String... path) {
    * @param path the path
    * @return the xml value as int
    */
-  Integer getXmlValueAsInt(String... path) {
+  protected Integer getXmlValueAsInt(String... path) {
     String result = this.getXmlValue(path);
     if (StringUtils.isBlank(result)) {
       return null;
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayAuthcode2OpenidResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayAuthcode2OpenidResult.java
index 2c36d32f03..76526ff685 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayAuthcode2OpenidResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayAuthcode2OpenidResult.java
@@ -4,6 +4,7 @@
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.NoArgsConstructor;
+import org.w3c.dom.Document;
 
 /**
  * 
@@ -30,4 +31,14 @@ public class WxPayAuthcode2OpenidResult extends BaseWxPayResult {
   @XStreamAlias("openid")
   private String openid;
 
+  /**
+   * 从XML结构中加载额外的熟悉
+   *
+   * @param d Document
+   */
+  @Override
+  protected void loadXML(Document d) {
+    openid = readXMLString(d, "openid");
+  }
+
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBillResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBillResult.java
index 15416b1f2a..84a382db3c 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBillResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBillResult.java
@@ -66,16 +66,16 @@ public String toString() {
    */
   public static WxPayBillResult fromRawBillResultString(String responseContent, String billType) {
     switch (billType) {
-      case "ALL":{
+      case "ALL": {
         return fromRawBillResultString(responseContent);
       }
-      case "SUCCESS":{
+      case "SUCCESS": {
         return fromRawBillResultStringToSuccess(responseContent);
       }
-      case "REFUND" :{
+      case "REFUND": {
         return fromRawBillResultStringToRefund(responseContent);
       }
-      case "RECHARGE_REFUND" :{
+      case "RECHARGE_REFUND": {
         return fromRawBillResultStringToRechargeRefund(responseContent);
       }
       default: {
@@ -106,7 +106,9 @@ private static WxPayBillResult fromRawBillResultString(String responseContent) {
     int j = tempStr.length / t.length;
     // 纪录数组下标
     int k = 1;
-    // 交易时间,公众账号ID,商户号,特约商户号,设备号,微信订单号,商户订单号,用户标识,交易类型,交易状态,付款银行,货币种类,应结订单金额,代金券金额,微信退款单号,商户退款单号,退款金额,充值券退款金额,退款类型,退款状态,商品名称,商户数据包,手续费,费率,订单金额,申请退款金额,费率备注
+    // 交易时间,公众账号ID,商户号,特约商户号,设备号,微信订单号,商户订单号,用户标识,交易类型,交易状态,付款银行,货币种类,
+    // 应结订单金额,代金券金额,微信退款单号,商户退款单号,退款金额,充值券退款金额,退款类型,退款状态,商品名称,商户数据包,手续费,费率,
+    // 订单金额,申请退款金额,费率备注  (开通免充值券后的结算对账单专有的三个字段)
     for (int i = 0; i < j; i++) {
       WxPayBillInfo result = new WxPayBillInfo();
       result.setTradeTime(tempStr[k].trim());
@@ -133,9 +135,14 @@ private static WxPayBillResult fromRawBillResultString(String responseContent) {
       result.setAttach(tempStr[k + 21].trim());
       result.setPoundage(tempStr[k + 22].trim());
       result.setPoundageRate(tempStr[k + 23].trim());
-      result.setTotalAmount(tempStr[k + 24].trim());
-      result.setAppliedRefundAmount(tempStr[k + 25].trim());
-      result.setFeeRemark(tempStr[k + 26].trim());
+
+      if (t.length > 24) {
+        // 开通免充值券后的结算对账单
+        result.setTotalAmount(tempStr[k + 24].trim());
+        result.setAppliedRefundAmount(tempStr[k + 25].trim());
+        result.setFeeRemark(tempStr[k + 26].trim());
+      }
+
       results.add(result);
       k += t.length;
     }
@@ -181,7 +188,8 @@ private static WxPayBillResult fromRawBillResultStringToSuccess(String responseC
     int j = tempStr.length / t.length;
     // 纪录数组下标
     int k = 1;
-    // 交易时间,公众账号ID,商户号,特约商户号,设备号,微信订单号,商户订单号,用户标识,交易类型,交易状态,付款银行,货币种类,应结订单金额,代金券金额,商品名称,商户数据包,手续费,费率,订单金额,费率备注
+    // 交易时间,公众账号ID,商户号,特约商户号,设备号,微信订单号,商户订单号,用户标识,交易类型,交易状态,付款银行,货币种类,
+    // 应结订单金额,代金券金额,商品名称,商户数据包,手续费,费率,订单金额,费率备注
     for (int i = 0; i < j; i++) {
       WxPayBillInfo result = new WxPayBillInfo();
       result.setTradeTime(tempStr[k].trim());
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayCommonResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayCommonResult.java
index cc0a3a6edc..1dda6483b4 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayCommonResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayCommonResult.java
@@ -1,6 +1,7 @@
 package com.github.binarywang.wxpay.bean.result;
 
 import com.thoughtworks.xstream.annotations.XStreamAlias;
+import org.w3c.dom.Document;
 
 /**
  * 
@@ -12,4 +13,7 @@
  */
 @XStreamAlias("xml")
 public class WxPayCommonResult extends BaseWxPayResult {
+  @Override
+  protected void loadXML(Document d) {
+  }
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayFaceAuthInfoResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayFaceAuthInfoResult.java
index 95dcf71eb4..5c357c560f 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayFaceAuthInfoResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayFaceAuthInfoResult.java
@@ -4,6 +4,7 @@
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.NoArgsConstructor;
+import org.w3c.dom.Document;
 
 import java.io.Serializable;
 
@@ -35,4 +36,15 @@ public class WxPayFaceAuthInfoResult extends BaseWxPayResult implements Serializ
   @XStreamAlias("expires_in")
   private String expiresIn;
 
+  /**
+   * 从XML结构中加载额外的熟悉
+   *
+   * @param d Document
+   */
+  @Override
+  protected void loadXML(Document d) {
+    authinfo = readXMLString(d, "authinfo");
+    expiresIn = readXMLString(d, "expires_in");
+  }
+
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayFacepayResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayFacepayResult.java
index 27e33d7bfc..c19a92c488 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayFacepayResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayFacepayResult.java
@@ -4,6 +4,7 @@
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.NoArgsConstructor;
+import org.w3c.dom.Document;
 
 /**
  * 
@@ -242,4 +243,30 @@ public class WxPayFacepayResult extends BaseWxPayResult {
   @XStreamAlias("time_end")
   private String timeEnd;
 
+  /**
+   * 从XML结构中加载额外的熟悉
+   *
+   * @param d Document
+   */
+  @Override
+  protected void loadXML(Document d) {
+    deviceInfo = readXMLString(d, "device_info");
+    openid = readXMLString(d, "openid");
+    isSubscribe = readXMLString(d, "is_subscribe");
+    subOpenid = readXMLString(d, "sub_openid");
+    subsSubscribe = readXMLString(d, "sub_is_subscribe");
+    tradeType = readXMLString(d, "trade_type");
+    bankType = readXMLString(d, "bank_type");
+    feeType = readXMLString(d, "fee_type");
+    totalFee = readXMLInteger(d, "total_fee");
+    cashFeeType = readXMLString(d, "cash_fee_type");
+    cashFee = readXMLInteger(d, "cash_fee");
+    transactionId = readXMLString(d, "transaction_id");
+    outTradeNo = readXMLString(d, "out_trade_no");
+    detail = readXMLString(d, "detail");
+    attach = readXMLString(d, "attach");
+    promotionDetail = readXMLString(d, "promotion_detail");
+    timeEnd = readXMLString(d, "time_end");
+  }
+
 }
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 36f910be63..b7de7ffec1 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
@@ -4,6 +4,7 @@
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.NoArgsConstructor;
+import org.w3c.dom.Document;
 
 /**
  * 
@@ -215,4 +216,28 @@ public class WxPayMicropayResult extends BaseWxPayResult {
   @XStreamAlias("promotion_detail")
   private String promotionDetail;
 
+  /**
+   * 从XML结构中加载额外的熟悉
+   *
+   * @param d Document
+   */
+  @Override
+  protected void loadXML(Document d) {
+    openid = readXMLString(d, "openid");
+    isSubscribe = readXMLString(d, "is_subscribe");
+    tradeType = readXMLString(d, "trade_type");
+    bankType = readXMLString(d, "bank_type");
+    feeType = readXMLString(d, "fee_type");
+    totalFee = readXMLInteger(d, "total_fee");
+    settlementTotalFee = readXMLInteger(d, "settlement_total_fee");
+    couponFee = readXMLInteger(d, "coupon_fee");
+    cashFeeType = readXMLString(d, "cash_fee_type");
+    cashFee = readXMLInteger(d, "cash_fee");
+    transactionId = readXMLString(d, "transaction_id");
+    outTradeNo = readXMLString(d, "out_trade_no");
+    attach = readXMLString(d, "attach");
+    timeEnd = readXMLString(d, "time_end");
+    promotionDetail = readXMLString(d, "promotion_detail");
+  }
+
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderCloseResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderCloseResult.java
index a71c8ab7ed..a098cbb5fc 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderCloseResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderCloseResult.java
@@ -4,6 +4,7 @@
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.NoArgsConstructor;
+import org.w3c.dom.Document;
 
 /**
  * 
@@ -25,4 +26,14 @@ public class WxPayOrderCloseResult extends BaseWxPayResult {
   @XStreamAlias("result_msg")
   private String resultMsg;
 
+  /**
+   * 从XML结构中加载额外的熟悉
+   *
+   * @param d Document
+   */
+  @Override
+  protected void loadXML(Document d) {
+    resultMsg = readXMLString(d, "result_msg");
+  }
+
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderQueryResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderQueryResult.java
index c7534b79c5..906a8cf12f 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderQueryResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderQueryResult.java
@@ -10,6 +10,7 @@
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.NoArgsConstructor;
+import org.w3c.dom.Document;
 
 /**
  * 
@@ -32,7 +33,7 @@
 @XStreamAlias("xml")
 public class WxPayOrderQueryResult extends BaseWxPayResult {
   private static final long serialVersionUID = 8241891654782412789L;
-  
+
   /**
    * 
    * 字段名:营销详情.
@@ -290,6 +291,29 @@ public void composeCoupons() {
     }
   }
 
+  /**
+   * 从XML结构中加载额外的熟悉
+   *
+   * @param d Document
+   */
+  @Override
+  protected void loadXML(Document d) {
+    promotionDetail = readXMLString(d, "promotion_detail");
+    deviceInfo = readXMLString(d, "device_info");
+    openid = readXMLString(d, "openid");
+    isSubscribe = readXMLString(d, "is_subscribe");
+    tradeType = readXMLString(d, "trade_type");
+    tradeState = readXMLString(d, "trade_state");
+    bankType = readXMLString(d, "bank_type");
+    totalFee = readXMLInteger(d, "total_fee");
+    settlementTotalFee = readXMLInteger(d, "settlement_total_fee");
+    feeType = readXMLString(d, "fee_type");
+    cashFee = readXMLInteger(d, "cash_fee");
+    cashFeeType = readXMLString(d, "cash_fee_type");
+    couponFee = readXMLInteger(d, "coupon_fee");
+    couponCount = readXMLInteger(d, "coupon_count");
+  }
+
   /**
    * The type Coupon.
    */
@@ -338,4 +362,5 @@ public static class Coupon implements Serializable {
     private Integer couponFee;
 
   }
+
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderReverseResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderReverseResult.java
index 578139929e..210e9e4aab 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderReverseResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderReverseResult.java
@@ -4,6 +4,7 @@
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.NoArgsConstructor;
+import org.w3c.dom.Document;
 
 /**
  * 
@@ -32,4 +33,14 @@ public class WxPayOrderReverseResult extends BaseWxPayResult {
   @XStreamAlias("recall")
   private String isRecall;
 
+  /**
+   * 从XML结构中加载额外的熟悉
+   *
+   * @param d Document
+   */
+  @Override
+  protected void loadXML(Document d) {
+    isRecall = readXMLString(d, "recall");
+  }
+
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRedpackQueryResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRedpackQueryResult.java
index 4f05bfde68..91c6eb7f22 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRedpackQueryResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRedpackQueryResult.java
@@ -1,12 +1,16 @@
 package com.github.binarywang.wxpay.bean.result;
 
 import java.io.Serializable;
+import java.util.ArrayList;
 import java.util.List;
 
 import com.thoughtworks.xstream.annotations.XStreamAlias;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.NoArgsConstructor;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
 
 /**
  * 
@@ -223,6 +227,43 @@ public class WxPayRedpackQueryResult extends BaseWxPayResult {
   @XStreamAlias("hblist")
   private List redpackList;
 
+  /**
+   * 从XML结构中加载额外的熟悉
+   *
+   * @param d Document
+   */
+  @Override
+  protected void loadXML(Document d) {
+    mchBillNo = readXMLString(d, "mch_billno");
+    detailId = readXMLString(d, "detail_id");
+    status = readXMLString(d, "status");
+    sendType = readXMLString(d, "send_type");
+    hbType = readXMLString(d, "hb_type");
+    totalNum = readXMLInteger(d, "total_num");
+    totalAmount = readXMLInteger(d, "total_amount");
+    sendTime = readXMLString(d, "send_time");
+    refundTime = readXMLString(d, "refund_time");
+    refundAmount = readXMLInteger(d, "refund_amount");
+    wishing = readXMLString(d, "wishing");
+    remark = readXMLString(d, "remark");
+    actName = readXMLString(d, "act_name");
+
+    NodeList nodeList = d.getElementsByTagName("hbinfo");
+    List list = new ArrayList<>(nodeList.getLength());
+
+    for (int i = 0, j = nodeList.getLength(); i < j; i++) {
+      Node node = nodeList.item(i);
+      RedpackInfo rp = new RedpackInfo();
+      rp.amount = readXMLInteger(node, "amount");
+      rp.openid = readXMLString(node, "openid");
+      rp.receiveTime = readXMLString(node, "rcv_time");
+      list.add(rp);
+    }
+
+    redpackList = list;
+
+  }
+
   /**
    * The type Redpack info.
    */
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRefundQueryResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRefundQueryResult.java
index 3bc6a2b4b6..ecdf933b6b 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRefundQueryResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRefundQueryResult.java
@@ -9,6 +9,7 @@
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.NoArgsConstructor;
+import org.w3c.dom.Document;
 
 /**
  * 
@@ -172,6 +173,23 @@ public void composeRefundRecords() {
     }
   }
 
+  /**
+   * 从XML结构中加载额外的熟悉
+   *
+   * @param d Document
+   */
+  @Override
+  protected void loadXML(Document d) {
+    deviceInfo = readXMLString(d, "device_info");
+    transactionId = readXMLString(d, "transaction_id");
+    outTradeNo = readXMLString(d, "out_trade_no");
+    totalFee = readXMLInteger(d, "total_fee");
+    settlementTotalFee = readXMLInteger(d, "settlement_total_fee");
+    feeType = readXMLString(d, "fee_type");
+    cashFee = readXMLInteger(d, "cash_fee");
+    refundCount = readXMLInteger(d, "refund_count");
+  }
+
   /**
    * The type Refund record.
    */
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 9b971123aa..57fb333595 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
@@ -8,6 +8,7 @@
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.NoArgsConstructor;
+import org.w3c.dom.Document;
 
 /**
  * 
@@ -119,7 +120,7 @@ public class WxPayRefundResult extends BaseWxPayResult implements Serializable {
   /**
    * 组装生成退款代金券信息.
    */
-  private void composeRefundCoupons() {
+  public void composeRefundCoupons() {
     List coupons = Lists.newArrayList();
     Integer refundCount = this.getCouponRefundCount();
     if (refundCount == null) {
@@ -140,9 +141,27 @@ private void composeRefundCoupons() {
     this.setRefundCoupons(coupons);
   }
 
-  public static WxPayRefundResult fromXML(String xml) {
-    WxPayRefundResult result = BaseWxPayResult.fromXML(xml, WxPayRefundResult.class);
-    result.composeRefundCoupons();
-    return result;
+  /**
+   * 从XML结构中加载额外的熟悉
+   *
+   * @param d Document
+   */
+  @Override
+  protected void loadXML(Document d) {
+    transactionId = readXMLString(d, "transaction_id");
+    outTradeNo = readXMLString(d, "out_trade_no");
+    outRefundNo = readXMLString(d, "out_refund_no");
+    refundId = readXMLString(d, "refund_id");
+    refundFee = readXMLInteger(d, "refund_fee");
+    settlementRefundFee = readXMLInteger(d, "settlement_refund_fee");
+    totalFee = readXMLInteger(d, "total_fee");
+    settlementTotalFee = readXMLInteger(d, "settlement_total_fee");
+    feeType = readXMLString(d, "fee_type");
+    cashFee = readXMLInteger(d, "cash_fee");
+    cashFeeType = readXMLString(d, "cash_fee_type");
+    cashRefundFee = readXMLInteger(d, "cash_refund_fee");
+    couponRefundCount = readXMLInteger(d, "coupon_refund_count");
+    couponRefundFee = readXMLInteger(d, "coupon_refund_fee");
   }
+
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPaySandboxSignKeyResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPaySandboxSignKeyResult.java
index cf3f9355db..b1af69c6b0 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPaySandboxSignKeyResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPaySandboxSignKeyResult.java
@@ -4,6 +4,7 @@
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.NoArgsConstructor;
+import org.w3c.dom.Document;
 
 /**
  * 
@@ -31,4 +32,14 @@ public class WxPaySandboxSignKeyResult extends BaseWxPayResult {
   @XStreamAlias("sandbox_signkey")
   private String sandboxSignKey;
 
+  /**
+   * 从XML结构中加载额外的熟悉
+   *
+   * @param d Document
+   */
+  @Override
+  protected void loadXML(Document d) {
+    sandboxSignKey = readXMLString(d, "sandbox_signkey");
+  }
+
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPaySendMiniProgramRedpackResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPaySendMiniProgramRedpackResult.java
new file mode 100644
index 0000000000..c707fee7f1
--- /dev/null
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPaySendMiniProgramRedpackResult.java
@@ -0,0 +1,66 @@
+package com.github.binarywang.wxpay.bean.result;
+
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import org.w3c.dom.Document;
+
+import java.io.Serializable;
+
+/**
+ * 发送小程序红包的返回结果
+ *
+ * @author Binary Wang
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@NoArgsConstructor
+@XStreamAlias("xml")
+public class WxPaySendMiniProgramRedpackResult extends BaseWxPayResult implements Serializable {
+  /**
+   * 商户订单号.
+   */
+  @XStreamAlias("mch_billno")
+  private String mchBillNo;
+
+  /**
+   * 公众账号appid.
+   */
+  @XStreamAlias("wxappid")
+  private String wxAppid;
+
+  /**
+   * 用户openid.
+   */
+  @XStreamAlias("re_openid")
+  private String reOpenid;
+
+  /**
+   * 付款金额.
+   */
+  @XStreamAlias("total_amount")
+  private int totalAmount;
+
+  /**
+   * 返回jaspi的入参package的值.
+   */
+  @XStreamAlias("package")
+  private String packageName;
+
+  /**
+   * 微信单号.
+   */
+  @XStreamAlias("send_listid")
+  private String sendListId;
+
+  @Override
+  protected void loadXML(Document d) {
+    mchBillNo = readXMLString(d, "mch_billno");
+    wxAppid = readXMLString(d, "wxappid");
+    reOpenid = readXMLString(d, "re_openid");
+    totalAmount = readXMLInteger(d, "total_amount");
+    packageName = readXMLString(d, "package");
+    sendListId = readXMLString(d, "send_listid");
+  }
+}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPaySendRedpackResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPaySendRedpackResult.java
index 2855daef79..adc6a688fc 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPaySendRedpackResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPaySendRedpackResult.java
@@ -4,6 +4,7 @@
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.NoArgsConstructor;
+import org.w3c.dom.Document;
 
 import java.io.Serializable;
 
@@ -38,4 +39,19 @@ public class WxPaySendRedpackResult extends BaseWxPayResult implements Serializa
   @XStreamAlias("send_listid")
   private String sendListid;
 
+  /**
+   * 从XML结构中加载额外的熟悉
+   *
+   * @param d Document
+   */
+  @Override
+  protected void loadXML(Document d) {
+    mchBillno = readXMLString(d, "mch_billno");
+    wxappid = readXMLString(d, "wxappid");
+    reOpenid = readXMLString(d, "re_openid");
+    totalAmount = readXMLInteger(d, "total_amount");
+    sendTime = readXMLString(d, "send_time");
+    sendListid = readXMLString(d, "send_listid");
+  }
+
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayShorturlResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayShorturlResult.java
index 1937004f47..b213024edc 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayShorturlResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayShorturlResult.java
@@ -4,6 +4,7 @@
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.NoArgsConstructor;
+import org.w3c.dom.Document;
 
 /**
  * 
@@ -31,4 +32,14 @@ public class WxPayShorturlResult extends BaseWxPayResult {
   @XStreamAlias("short_url")
   private String shortUrl;
 
+  /**
+   * 从XML结构中加载额外的熟悉
+   *
+   * @param d Document
+   */
+  @Override
+  protected void loadXML(Document d) {
+    shortUrl = readXMLString(d, "short_url");
+  }
+
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayUnifiedOrderResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayUnifiedOrderResult.java
index cf97f75abc..3345f0c0fb 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayUnifiedOrderResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayUnifiedOrderResult.java
@@ -4,6 +4,7 @@
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.NoArgsConstructor;
+import org.w3c.dom.Document;
 
 /**
  * 
@@ -43,4 +44,17 @@ public class WxPayUnifiedOrderResult extends BaseWxPayResult {
   @XStreamAlias("code_url")
   private String codeURL;
 
+  /**
+   * 从XML结构中加载额外的熟悉
+   *
+   * @param d Document
+   */
+  @Override
+  protected void loadXML(Document d) {
+    prepayId = readXMLString(d, "prepay_id");
+    tradeType = readXMLString(d, "trade_type");
+    mwebUrl = readXMLString(d, "mweb_url");
+    codeURL = readXMLString(d, "code_url");
+  }
+
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java
index 9192a4bafc..c0ca2cf552 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java
@@ -51,6 +51,10 @@ public class WxPayConfig {
    * 商户密钥.
    */
   private String mchKey;
+  /**
+   * 企业支付密钥.
+   */
+  private String entPayKey;
   /**
    * 服务商模式下的子商户号.
    */
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 df9330fc9f..1b1b76b154 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
@@ -119,4 +119,28 @@ public interface EntPayService {
    * @throws WxPayException the wx pay exception
    */
   EntPayBankQueryResult queryPayBank(EntPayBankQueryRequest request) throws WxPayException;
+
+  /**
+   * 企业发送微信红包给个人用户
+   * 
+   *   文档地址:https://work.weixin.qq.com/api/doc
+   *   接口地址: https://api.mch.weixin.qq.com/mmpaymkttransfers/sendworkwxredpack
+   * 
+ * @param request 请求对象 + * @return the wx pay send redpack result + * @throws WxPayException the wx pay exception + */ + EntPayRedpackResult sendEnterpriseRedpack(EntPayRedpackRequest request) throws WxPayException; + + /** + * 企业发送微信红包查询 + *
+   *   文档地址:https://work.weixin.qq.com/api/doc
+   *   接口地址: https://api.mch.weixin.qq.com/mmpaymkttransfers/queryworkwxredpack
+   * 
+ * @param request 请求对象 + * @return the wx pay send redpack result + * @throws WxPayException the wx pay exception + */ + EntPayRedpackQueryResult queryEnterpriseRedpack(EntPayRedpackQueryRequest request) throws WxPayException; } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/RedpackService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/RedpackService.java new file mode 100644 index 0000000000..131205d07a --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/RedpackService.java @@ -0,0 +1,76 @@ +package com.github.binarywang.wxpay.service; + +import com.github.binarywang.wxpay.bean.request.WxPayRedpackQueryRequest; +import com.github.binarywang.wxpay.bean.request.WxPaySendMiniProgramRedpackRequest; +import com.github.binarywang.wxpay.bean.request.WxPaySendRedpackRequest; +import com.github.binarywang.wxpay.bean.result.WxPayRedpackQueryResult; +import com.github.binarywang.wxpay.bean.result.WxPaySendMiniProgramRedpackResult; +import com.github.binarywang.wxpay.bean.result.WxPaySendRedpackResult; +import com.github.binarywang.wxpay.exception.WxPayException; + +/** + * 红包相关接口. + * + * @author Binary Wang + * @date 2019-12-26 + */ +public interface RedpackService { + /** + *
+   * 发送小程序红包.
+   * 文档详见: https://pay.weixin.qq.com/wiki/doc/api/tools/miniprogram_hb.php?chapter=13_9&index=2
+   *  接口地址:https://api.mch.weixin.qq.com/mmpaymkttransfers/sendminiprogramhb
+   * 
+ * + * @param request 请求对象 + * @return the result + * @throws WxPayException the exception + */ + WxPaySendMiniProgramRedpackResult sendMiniProgramRedpack(WxPaySendMiniProgramRedpackRequest request) throws WxPayException; + + /** + * 发送微信红包给个人用户. + *
+   * 文档详见:
+   * 发送普通红包 https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=13_4&index=3
+   *  接口地址:https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack
+   * 发送裂变红包 https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=13_5&index=4
+   *  接口地址:https://api.mch.weixin.qq.com/mmpaymkttransfers/sendgroupredpack
+   * 
+ * + * @param request 请求对象 + * @return the wx pay send redpack result + * @throws WxPayException the wx pay exception + */ + WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request) throws WxPayException; + + /** + *
+   *   查询红包记录.
+   *   用于商户对已发放的红包进行查询红包的具体信息,可支持普通红包和裂变包。
+   *   请求Url:https://api.mch.weixin.qq.com/mmpaymkttransfers/gethbinfo
+   *   是否需要证书:是(证书及使用说明详见商户证书)
+   *   请求方式:POST
+   * 
+ * + * @param mchBillNo 商户发放红包的商户订单号,比如10000098201411111234567890 + * @return the wx pay redpack query result + * @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; +} 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 d3fc4be7ff..52f99af6c4 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java @@ -1,16 +1,7 @@ package com.github.binarywang.wxpay.service; -import java.io.File; -import java.util.Date; -import java.util.Map; - 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; @@ -19,6 +10,10 @@ import com.github.binarywang.wxpay.config.WxPayConfig; import com.github.binarywang.wxpay.exception.WxPayException; +import java.io.File; +import java.util.Date; +import java.util.Map; + /** *
  * 微信支付相关接口.
@@ -65,6 +60,13 @@ public interface WxPayService {
    */
   EntPayService getEntPayService();
 
+  /**
+   * 获取红包接口服务类.
+   *
+   * @return .
+   */
+  RedpackService getRedpackService();
+
   /**
    * 获取分账服务类.
    *
@@ -282,49 +284,27 @@ WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, Stri
   WxScanPayNotifyResult parseScanPayNotifyResult(String xmlData) throws WxPayException;
 
   /**
-   * 发送微信红包给个人用户.
-   * 
-   * 文档详见:
-   * 发送普通红包 https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=13_4&index=3
-   *  接口地址:https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack
-   * 发送裂变红包 https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=13_5&index=4
-   *  接口地址:https://api.mch.weixin.qq.com/mmpaymkttransfers/sendgroupredpack
-   * 
- * - * @param request 请求对象 - * @return the wx pay send redpack result - * @throws WxPayException the wx pay exception + * @deprecated 建议使用 {@link RedpackService#sendMiniProgramRedpack(WxPaySendMiniProgramRedpackRequest)} + */ + @Deprecated + WxPaySendMiniProgramRedpackResult sendMiniProgramRedpack(WxPaySendMiniProgramRedpackRequest request) throws WxPayException; + + /** + * @deprecated 建议使用 {@link RedpackService#sendRedpack(WxPaySendRedpackRequest)} */ + @Deprecated WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request) throws WxPayException; /** - *
-   *   查询红包记录.
-   *   用于商户对已发放的红包进行查询红包的具体信息,可支持普通红包和裂变包。
-   *   请求Url:https://api.mch.weixin.qq.com/mmpaymkttransfers/gethbinfo
-   *   是否需要证书:是(证书及使用说明详见商户证书)
-   *   请求方式:POST
-   * 
- * - * @param mchBillNo 商户发放红包的商户订单号,比如10000098201411111234567890 - * @return the wx pay redpack query result - * @throws WxPayException the wx pay exception + * @deprecated 建议使用 {@link RedpackService#queryRedpack(String)} */ + @Deprecated 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 + * @deprecated 建议使用 {@link RedpackService#queryRedpack(WxPayRedpackQueryRequest)} */ + @Deprecated WxPayRedpackQueryResult queryRedpack(WxPayRedpackQueryRequest request) throws WxPayException; /** @@ -510,7 +490,7 @@ WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, Stri /** *
-   * 提交刷卡支付.
+   * 提交付款码支付.
    * 文档地址:https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_10&index=1
    * 应用场景:
    * 收银员使用扫码设备读取微信用户刷卡授权码以后,二维码或条码信息传送至商户收银台,由商户收银台或者商户后台调用该接口发起支付。
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 4c3465f8df..61dcee09ba 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
@@ -13,14 +13,15 @@
 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;
 import com.github.binarywang.wxpay.constant.WxPayConstants.TradeType;
 import com.github.binarywang.wxpay.exception.WxPayException;
 import com.github.binarywang.wxpay.service.EntPayService;
 import com.github.binarywang.wxpay.service.ProfitSharingService;
+import com.github.binarywang.wxpay.service.RedpackService;
 import com.github.binarywang.wxpay.service.WxPayService;
 import com.github.binarywang.wxpay.util.SignUtils;
+import com.github.binarywang.wxpay.util.XmlConfig;
 import com.google.common.base.Joiner;
 import com.google.common.collect.Maps;
 import jodd.io.ZipUtil;
@@ -61,6 +62,8 @@ public abstract class BaseWxPayServiceImpl implements WxPayService {
 
   private EntPayService entPayService = new EntPayServiceImpl(this);
   private ProfitSharingService profitSharingService = new ProfitSharingServiceImpl(this);
+  private RedpackService redpackService = new RedpackServiceImpl(this);
+
   /**
    * The Config.
    */
@@ -76,6 +79,11 @@ public ProfitSharingService getProfitSharingService() {
     return profitSharingService;
   }
 
+  @Override
+  public RedpackService getRedpackService() {
+    return this.redpackService;
+  }
+
   @Override
   public void setEntPayService(EntPayService entPayService) {
     this.entPayService = entPayService;
@@ -110,7 +118,8 @@ public WxPayRefundResult refund(WxPayRefundRequest request) throws WxPayExceptio
     }
 
     String responseContent = this.post(url, request.toXML(), true);
-    WxPayRefundResult result = WxPayRefundResult.fromXML(responseContent);
+    WxPayRefundResult result = BaseWxPayResult.fromXML(responseContent, WxPayRefundResult.class);
+    result.composeRefundCoupons();
     result.checkResult(this, request.getSignType(), true);
     return result;
   }
@@ -158,7 +167,13 @@ public WxPayOrderNotifyResult parseOrderNotifyResult(String xmlData) throws WxPa
   public WxPayRefundNotifyResult parseRefundNotifyResult(String xmlData) throws WxPayException {
     try {
       log.debug("微信支付退款异步通知参数:{}", xmlData);
-      WxPayRefundNotifyResult result = WxPayRefundNotifyResult.fromXML(xmlData, this.getConfig().getMchKey());
+      WxPayRefundNotifyResult result;
+      if (XmlConfig.fastMode) {
+        result = BaseWxPayResult.fromXML(xmlData, WxPayRefundNotifyResult.class);
+        result.decryptReqInfo(this.getConfig().getMchKey());
+      } else {
+        result = WxPayRefundNotifyResult.fromXML(xmlData, this.getConfig().getMchKey());
+      }
       log.debug("微信支付退款异步通知解析后的对象:{}", result);
       return result;
     } catch (Exception e) {
@@ -183,38 +198,24 @@ public WxScanPayNotifyResult parseScanPayNotifyResult(String xmlData) throws WxP
   }
 
   @Override
-  public WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request) throws WxPayException {
-    request.checkAndSign(this.getConfig());
-
-    String url = this.getPayBaseUrl() + "/mmpaymkttransfers/sendredpack";
-    if (request.getAmtType() != null) {
-      //裂变红包
-      url = this.getPayBaseUrl() + "/mmpaymkttransfers/sendgroupredpack";
-    }
+  public WxPaySendMiniProgramRedpackResult sendMiniProgramRedpack(WxPaySendMiniProgramRedpackRequest request)
+    throws WxPayException {
+    return this.redpackService.sendMiniProgramRedpack(request);
+  }
 
-    String responseContent = this.post(url, request.toXML(), true);
-    final WxPaySendRedpackResult result = BaseWxPayResult.fromXML(responseContent, WxPaySendRedpackResult.class);
-    result.checkResult(this, request.getSignType(), true);
-    return result;
+  @Override
+  public WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request) throws WxPayException {
+    return this.redpackService.sendRedpack(request);
   }
 
   @Override
   public WxPayRedpackQueryResult queryRedpack(String mchBillNo) throws WxPayException {
-    WxPayRedpackQueryRequest request = new WxPayRedpackQueryRequest();
-    request.setMchBillNo(mchBillNo);
-    return this.queryRedpack(request);
+    return this.redpackService.queryRedpack(mchBillNo);
   }
 
   @Override
   public WxPayRedpackQueryResult queryRedpack(WxPayRedpackQueryRequest request) throws WxPayException {
-    request.setBillType(BillType.MCHT);
-    request.checkAndSign(this.getConfig());
-
-    String url = this.getPayBaseUrl() + "/mmpaymkttransfers/gethbinfo";
-    String responseContent = this.post(url, request.toXML(), true);
-    WxPayRedpackQueryResult result = BaseWxPayResult.fromXML(responseContent, WxPayRedpackQueryResult.class);
-    result.checkResult(this, request.getSignType(), true);
-    return result;
+    return this.redpackService.queryRedpack(request);
   }
 
   @Override
@@ -321,7 +322,11 @@ public  T createOrder(WxPayUnifiedOrderRequest request) throws WxPayException
       }
 
       case TradeType.JSAPI: {
-        String signType = SignType.MD5;
+        String signType = request.getSignType();
+        if (signType == null) {
+          signType = SignType.MD5;
+        }
+
         String appid = unifiedOrderResult.getAppid();
         if (StringUtils.isNotEmpty(unifiedOrderResult.getSubAppId())) {
           appid = unifiedOrderResult.getSubAppId();
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 59db3ee078..5e768bef99 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
@@ -1,35 +1,27 @@
 package com.github.binarywang.wxpay.service.impl;
 
+import com.github.binarywang.wxpay.bean.entpay.*;
+import com.github.binarywang.wxpay.bean.request.WxPayDefaultRequest;
+import com.github.binarywang.wxpay.bean.result.BaseWxPayResult;
+import com.github.binarywang.wxpay.constant.WxPayConstants;
+import com.github.binarywang.wxpay.exception.WxPayException;
+import com.github.binarywang.wxpay.service.EntPayService;
+import com.github.binarywang.wxpay.service.WxPayService;
+import com.github.binarywang.wxpay.util.SignUtils;
+import org.apache.commons.codec.binary.Base64;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.openssl.PEMParser;
+import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
+
+import javax.crypto.Cipher;
 import java.io.File;
 import java.io.FileReader;
-import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.security.PublicKey;
 import java.security.Security;
-import javax.crypto.Cipher;
-
-import org.apache.commons.codec.binary.Base64;
-import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.bouncycastle.openssl.PEMParser;
-import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
-
-import com.github.binarywang.wxpay.bean.entpay.EntPayBankQueryRequest;
-import com.github.binarywang.wxpay.bean.entpay.EntPayBankQueryResult;
-import com.github.binarywang.wxpay.bean.entpay.EntPayBankRequest;
-import com.github.binarywang.wxpay.bean.entpay.EntPayBankResult;
-import com.github.binarywang.wxpay.bean.entpay.EntPayQueryRequest;
-import com.github.binarywang.wxpay.bean.entpay.EntPayQueryResult;
-import com.github.binarywang.wxpay.bean.entpay.EntPayRequest;
-import com.github.binarywang.wxpay.bean.entpay.EntPayResult;
-import com.github.binarywang.wxpay.bean.entpay.GetPublicKeyResult;
-import com.github.binarywang.wxpay.bean.request.WxPayDefaultRequest;
-import com.github.binarywang.wxpay.bean.result.BaseWxPayResult;
-import com.github.binarywang.wxpay.exception.WxPayException;
-import com.github.binarywang.wxpay.service.EntPayService;
-import com.github.binarywang.wxpay.service.WxPayService;
 
 /**
  * 
@@ -140,6 +132,32 @@ public EntPayBankQueryResult queryPayBank(EntPayBankQueryRequest request) throws
     return result;
   }
 
+  @Override
+  public EntPayRedpackResult sendEnterpriseRedpack(EntPayRedpackRequest request) throws WxPayException {
+    //企业微信签名,需要在请求签名之前
+    request.setNonceStr(String.valueOf(System.currentTimeMillis()));
+    request.setWorkWxSign(SignUtils.createEntSign(request.getActName(), request.getMchBillNo(), request.getMchId(), request.getNonceStr(), request.getReOpenid(), request.getTotalAmount(), request.getWxAppId(), payService.getConfig().getEntPayKey(), WxPayConstants.SignType.MD5));
+
+    request.checkAndSign(this.payService.getConfig());
+
+    String url = this.payService.getPayBaseUrl() + "/mmpaymkttransfers/sendworkwxredpack";
+    String responseContent = this.payService.post(url, request.toXML(), true);
+    final EntPayRedpackResult result = BaseWxPayResult.fromXML(responseContent, EntPayRedpackResult.class);
+
+    result.checkResult(this.payService, request.getSignType(), true);
+    return result;
+  }
+
+  @Override
+  public EntPayRedpackQueryResult queryEnterpriseRedpack(EntPayRedpackQueryRequest request) throws WxPayException {
+    request.checkAndSign(this.payService.getConfig());
+    String url = this.payService.getPayBaseUrl() + "/mmpaymkttransfers/queryworkwxredpack";
+    String responseContent = this.payService.post(url, request.toXML(), true);
+    final EntPayRedpackQueryResult result = BaseWxPayResult.fromXML(responseContent, EntPayRedpackQueryResult.class);
+    result.checkResult(this.payService, request.getSignType(), true);
+    return result;
+  }
+
   private String encryptRSA(File publicKeyFile, String srcString) throws WxPayException {
     try {
       Security.addProvider(new BouncyCastleProvider());
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/ProfitSharingServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/ProfitSharingServiceImpl.java
index 114ae023cd..09e305d19a 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/ProfitSharingServiceImpl.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/ProfitSharingServiceImpl.java
@@ -76,7 +76,6 @@ public ProfitSharingReceiverResult removeReceiver(ProfitSharingReceiverRequest r
 
   @Override
   public ProfitSharingQueryResult profitSharingQuery(ProfitSharingQueryRequest request) throws WxPayException {
-    if (true) throw new WxPayException("暂不支持,微信一直返回签名失败");
     request.checkAndSign(this.payService.getConfig());
     String url = this.payService.getPayBaseUrl() + "/pay/profitsharingquery";
 
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/RedpackServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/RedpackServiceImpl.java
new file mode 100644
index 0000000000..03ce8333e2
--- /dev/null
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/RedpackServiceImpl.java
@@ -0,0 +1,72 @@
+package com.github.binarywang.wxpay.service.impl;
+
+import com.github.binarywang.wxpay.bean.request.WxPayRedpackQueryRequest;
+import com.github.binarywang.wxpay.bean.request.WxPaySendMiniProgramRedpackRequest;
+import com.github.binarywang.wxpay.bean.request.WxPaySendRedpackRequest;
+import com.github.binarywang.wxpay.bean.result.BaseWxPayResult;
+import com.github.binarywang.wxpay.bean.result.WxPayRedpackQueryResult;
+import com.github.binarywang.wxpay.bean.result.WxPaySendMiniProgramRedpackResult;
+import com.github.binarywang.wxpay.bean.result.WxPaySendRedpackResult;
+import com.github.binarywang.wxpay.constant.WxPayConstants;
+import com.github.binarywang.wxpay.exception.WxPayException;
+import com.github.binarywang.wxpay.service.RedpackService;
+import com.github.binarywang.wxpay.service.WxPayService;
+import lombok.RequiredArgsConstructor;
+
+/**
+ * 老板加点注释吧.
+ *
+ * @author Binary Wang
+ * @date 2019-12-26
+ */
+@RequiredArgsConstructor
+public class RedpackServiceImpl implements RedpackService {
+  private final WxPayService payService;
+
+  @Override
+  public WxPaySendMiniProgramRedpackResult sendMiniProgramRedpack(WxPaySendMiniProgramRedpackRequest request)
+    throws WxPayException {
+    request.checkAndSign(this.payService.getConfig());
+    String url = this.payService.getPayBaseUrl() + "/mmpaymkttransfers/sendminiprogramhb";
+    String responseContent = this.payService.post(url, request.toXML(), true);
+
+    WxPaySendMiniProgramRedpackResult result = BaseWxPayResult.fromXML(responseContent, WxPaySendMiniProgramRedpackResult.class);
+    result.checkResult(this.payService, request.getSignType(), true);
+    return result;
+  }
+
+  @Override
+  public WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request) throws WxPayException {
+    request.checkAndSign(this.payService.getConfig());
+
+    String url = this.payService.getPayBaseUrl() + "/mmpaymkttransfers/sendredpack";
+    if (request.getAmtType() != null) {
+      //裂变红包
+      url = this.payService.getPayBaseUrl() + "/mmpaymkttransfers/sendgroupredpack";
+    }
+
+    String responseContent = this.payService.post(url, request.toXML(), true);
+    final WxPaySendRedpackResult result = BaseWxPayResult.fromXML(responseContent, WxPaySendRedpackResult.class);
+    result.checkResult(this.payService, 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(WxPayConstants.BillType.MCHT);
+    request.checkAndSign(this.payService.getConfig());
+
+    String url = this.payService.getPayBaseUrl() + "/mmpaymkttransfers/gethbinfo";
+    String responseContent = this.payService.post(url, request.toXML(), true);
+    WxPayRedpackQueryResult result = BaseWxPayResult.fromXML(responseContent, WxPayRedpackQueryResult.class);
+    result.checkResult(this.payService, request.getSignType(), true);
+    return result;
+  }
+}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/SignUtils.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/SignUtils.java
index 4712987249..0d1962ff43 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/SignUtils.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/SignUtils.java
@@ -1,18 +1,5 @@
 package com.github.binarywang.wxpay.util;
 
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-import java.util.SortedMap;
-import java.util.TreeMap;
-
-import org.apache.commons.codec.digest.DigestUtils;
-import org.apache.commons.lang3.ArrayUtils;
-import org.apache.commons.lang3.StringUtils;
-
 import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest;
 import com.github.binarywang.wxpay.bean.result.BaseWxPayResult;
 import com.github.binarywang.wxpay.constant.WxPayConstants.SignType;
@@ -20,6 +7,13 @@
 import com.google.common.collect.Maps;
 import com.thoughtworks.xstream.annotations.XStreamAlias;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.*;
 
 /**
  * 
@@ -31,6 +25,12 @@
  */
 @Slf4j
 public class SignUtils {
+
+  /**
+   * 签名的时候不携带的参数
+   */
+  private static List NO_SIGN_PARAMS = Lists.newArrayList("sign", "key", "xmlString", "xmlDoc", "couponList");
+
   /**
    * 请参考并使用 {@link #createSign(Object, String, String, String[])}.
    *
@@ -65,7 +65,18 @@ public static String createSign(Map params, String signKey) {
    * @return 签名字符串 string
    */
   public static String createSign(Object xmlBean, String signType, String signKey, String[] ignoredParams) {
-    return createSign(xmlBean2Map(xmlBean), signType, signKey, ignoredParams);
+    Map map = null;
+
+    if (XmlConfig.fastMode) {
+      if (xmlBean instanceof BaseWxPayRequest) {
+        map = ((BaseWxPayRequest) xmlBean).getSignParams();
+      }
+    }
+    if (map == null) {
+      map = xmlBean2Map(xmlBean);
+    }
+
+    return createSign(map, signType, signKey, ignoredParams);
   }
 
   /**
@@ -85,7 +96,7 @@ public static String createSign(Map params, String signType, Str
       String value = params.get(key);
       boolean shouldSign = false;
       if (StringUtils.isNotEmpty(value) && !ArrayUtils.contains(ignoredParams, key)
-        && !Lists.newArrayList("sign", "key", "xmlString", "xmlDoc", "couponList").contains(key)) {
+        && !NO_SIGN_PARAMS.contains(key)) {
         shouldSign = true;
       }
 
@@ -102,6 +113,50 @@ public static String createSign(Map params, String signType, Str
     }
   }
 
+  /**
+   * 企业微信签名
+   *
+   * @param signType md5 目前接口要求使用的加密类型
+   */
+  public static String createEntSign(String actName, String mchBillNo, String mchId, String nonceStr,
+                                     String reOpenid, Integer totalAmount, String wxAppId, String signKey,
+                                     String signType) {
+    Map sortedMap = new HashMap<>();
+    sortedMap.put("act_name", actName);
+    sortedMap.put("mch_billno", mchBillNo);
+    sortedMap.put("mch_id", mchId);
+    sortedMap.put("nonce_str", nonceStr);
+    sortedMap.put("re_openid", reOpenid);
+    sortedMap.put("total_amount", totalAmount + "");
+    sortedMap.put("wxappid", wxAppId);
+
+    Map sortParams = new TreeMap<>(sortedMap);
+    Set> entries = sortParams.entrySet();
+    Iterator> iterator = entries.iterator();
+    StringBuilder toSign = new StringBuilder();
+    while (iterator.hasNext()) {
+      Map.Entry entry = iterator.next();
+      String key = String.valueOf(entry.getKey());
+      String value = String.valueOf(entry.getValue());
+      boolean shouldSign = false;
+      if (StringUtils.isNotEmpty(value)) {
+        shouldSign = true;
+      }
+
+      if (shouldSign) {
+        toSign.append(key).append("=").append(value).append("&");
+      }
+    }
+    //企业微信这里字段名不一样
+    toSign.append("secret=").append(signKey);
+    if (SignType.HMAC_SHA256.equals(signType)) {
+      return me.chanjar.weixin.common.util.SignUtils.createHmacSha256Sign(toSign.toString(), signKey);
+    } else {
+      return DigestUtils.md5Hex(toSign.toString()).toUpperCase();
+    }
+
+  }
+
   /**
    * 校验签名是否正确.
    *
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/XmlConfig.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/XmlConfig.java
new file mode 100644
index 0000000000..3cd776841d
--- /dev/null
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/XmlConfig.java
@@ -0,0 +1,23 @@
+package com.github.binarywang.wxpay.util;
+
+public class XmlConfig {
+
+  /**
+   * 是否使用快速模式
+   *
+   * 如果设置为true,将会影响下面的方法,不再使用反射的方法来进行xml转换。
+   * 1: BaseWxPayRequest的toXML方法
+   * 2: BaseWxPayResult的fromXML方法
+   * @see com.github.binarywang.wxpay.bean.request.BaseWxPayRequest#toXML
+   * @see com.github.binarywang.wxpay.bean.result.BaseWxPayResult#fromXML
+   *
+   * 启用快速模式后,将能:
+   * 1:性能提升约 10 ~ 15倍
+   * 2:可以通过 graalvm 生成native image,大大减少系统开销(CPU,RAM),加快应用启动速度(亚秒级),加快系统部署速度(脱离JRE).
+   *
+   * 参考测试案例: com.github.binarywang.wxpay.bean.result.WxPayRedpackQueryResultTest#benchmark
+   * 参考网址: https://www.graalvm.org/
+   */
+  public static boolean fastMode = false;
+
+}
diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/notify/WxPayOrderNotifyResultTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/notify/WxPayOrderNotifyResultTest.java
index a22916dea7..f79e0859f0 100644
--- a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/notify/WxPayOrderNotifyResultTest.java
+++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/notify/WxPayOrderNotifyResultTest.java
@@ -1,5 +1,7 @@
 package com.github.binarywang.wxpay.bean.notify;
 
+import com.github.binarywang.wxpay.bean.result.BaseWxPayResult;
+import com.github.binarywang.wxpay.util.XmlConfig;
 import org.testng.*;
 import org.testng.annotations.*;
 
@@ -57,6 +59,27 @@ public void testFromXML() {
 
     Assert.assertEquals(result.getCouponList().get(0).getCouponId(), "10000");
     Assert.assertEquals(result.getCouponList().get(1).getCouponId(), "10001");
+
+    //fast mode test
+    XmlConfig.fastMode = true;
+    try {
+      result = BaseWxPayResult.fromXML(xmlString, WxPayOrderNotifyResult.class);
+
+      Assert.assertEquals(result.getCouponCount().intValue(), 2);
+      Assert.assertNotNull(result.getCouponList());
+      Assert.assertEquals(result.getCouponList().size(), 2);
+
+      Assert.assertEquals(result.getCouponList().get(0).getCouponFee().intValue(), 100);
+      Assert.assertEquals(result.getCouponList().get(1).getCouponFee().intValue(), 200);
+
+      Assert.assertEquals(result.getCouponList().get(0).getCouponType(), "CASH");
+      Assert.assertEquals(result.getCouponList().get(1).getCouponType(), "NO_CASH");
+
+      Assert.assertEquals(result.getCouponList().get(0).getCouponId(), "10000");
+      Assert.assertEquals(result.getCouponList().get(1).getCouponId(), "10001");
+    } finally {
+      XmlConfig.fastMode = false;
+    }
   }
 
 }
diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyResultTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyResultTest.java
index ba7ed54501..963afb2618 100644
--- a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyResultTest.java
+++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyResultTest.java
@@ -7,6 +7,8 @@
 import javax.crypto.spec.SecretKeySpec;
 import javax.inject.Inject;
 
+import com.github.binarywang.wxpay.bean.result.BaseWxPayResult;
+import com.github.binarywang.wxpay.util.XmlConfig;
 import org.apache.commons.codec.binary.Base64;
 import org.testng.annotations.*;
 
@@ -78,4 +80,50 @@ public void encodeReqInfo() throws Exception {
     cipher.init(Cipher.ENCRYPT_MODE, key);
     System.out.println(Base64.encodeBase64String(cipher.doFinal(xml.getBytes(StandardCharsets.UTF_8))));
   }
+
+  /**
+   * Test from xml.
+   * fast mode
+   *
+   * @throws WxPayException the wx pay exception
+   */
+  public void testFromXMLFastMode() throws WxPayException {
+    String xmlString = "" +
+      "SUCCESS" +
+      "" +
+      "" +
+      "" +
+      "";
+
+    String xmlDecryptedReqInfo = "\n" +
+      "\n" +
+      "\n" +
+      "\n" +
+      "\n" +
+      "\n" +
+      "\n" +
+      "\n" +
+      "\n" +
+      "\n" +
+      "\n" +
+      "\n" +
+      "\n" +
+      "\n" +
+      "";
+
+    XmlConfig.fastMode = true;
+    try {
+      WxPayRefundNotifyResult refundNotifyResult = BaseWxPayResult.fromXML(xmlString, WxPayRefundNotifyResult.class);
+      System.out.println(refundNotifyResult.getReqInfoString());
+
+      refundNotifyResult.loadReqInfo(xmlDecryptedReqInfo);
+      assertEquals(refundNotifyResult.getReqInfo().getRefundFee().intValue(), 15);
+      assertEquals(refundNotifyResult.getReqInfo().getRefundStatus(), "SUCCESS");
+      assertEquals(refundNotifyResult.getReqInfo().getRefundRecvAccout(), "用户零钱");
+      System.out.println(refundNotifyResult);
+    } finally {
+      XmlConfig.fastMode = false;
+    }
+  }
+
 }
diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/notify/WxScanPayNotifyResultTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/notify/WxScanPayNotifyResultTest.java
index 04853e15a2..3d77eb34ae 100644
--- a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/notify/WxScanPayNotifyResultTest.java
+++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/notify/WxScanPayNotifyResultTest.java
@@ -1,5 +1,6 @@
 package com.github.binarywang.wxpay.bean.notify;
 
+import com.github.binarywang.wxpay.util.XmlConfig;
 import org.testng.annotations.*;
 
 import com.github.binarywang.wxpay.bean.result.BaseWxPayResult;
@@ -50,4 +51,38 @@ public void testFromXML() {
     assertThat(result.getSign()).isEqualTo("C380BEC2BFD727A4B6845133519F3AD6");
   }
 
+
+  /**
+   * Test from xml.
+   * fast mode.
+   */
+  @Test
+  public void testFromXMLFastMode() {
+    String xmlString = "\n" +
+      "  \n" +
+      "  \n" +
+      "  \n" +
+      "  \n" +
+      "  \n" +
+      "  \n" +
+      "  \n" +
+      "";
+
+    XmlConfig.fastMode = true;
+    try {
+      WxScanPayNotifyResult result = BaseWxPayResult.fromXML(xmlString, WxScanPayNotifyResult.class);
+
+      assertThat(result).isNotNull();
+
+      assertThat(result.getAppid()).isEqualTo("wx8888888888888888");
+      assertThat(result.getOpenid()).isEqualTo("o8GeHuLAsgefS_80exEr1cTqekUs");
+      assertThat(result.getMchId()).isEqualTo("1900000109");
+      assertThat(result.getNonceStr()).isEqualTo("5K8264ILTKCH16CQ2502SI8ZNMTM67VS");
+      assertThat(result.getProductId()).isEqualTo("88888");
+      assertThat(result.getSign()).isEqualTo("C380BEC2BFD727A4B6845133519F3AD6");
+    } finally {
+      XmlConfig.fastMode = false;
+    }
+  }
+
 }
diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayBillResultTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayBillResultTest.java
index 76bbf05742..6d5d322602 100644
--- a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayBillResultTest.java
+++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayBillResultTest.java
@@ -1,27 +1,35 @@
 package com.github.binarywang.wxpay.bean.result;
 
 import com.github.binarywang.wxpay.constant.WxPayConstants;
-import org.testng.Assert;
 import org.testng.annotations.Test;
 
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+
 /**
  * @author m8cool
  */
 public class WxPayBillResultTest {
-
-  public static final String PAY_BILL_RESULT_ALL_CONTENT = "交易时间,公众账号ID,商户号,特约商户号,设备号,微信订单号,商户订单号,用户标识,交易类型,交易状态,付款银行,货币种类,应结订单金额,代金券金额,微信退款单号,商户退款单号,退款金额,充值券退款金额,退款类型,退款状态,商品名称,商户数据包,手续费,费率,订单金额,申请退款金额,费率备注\n" +
+  private static final String PAY_BILL_RESULT_ALL_CONTENT = "交易时间,公众账号ID,商户号,特约商户号,设备号,微信订单号,商户订单号,用户标识,交易类型,交易状态,付款银行,货币种类,应结订单金额,代金券金额,微信退款单号,商户退款单号,退款金额,充值券退款金额,退款类型,退款状态,商品名称,商户数据包,手续费,费率,订单金额,申请退款金额,费率备注\n" +
     "`2019-07-25 08:35:41,`WWWW,`XXXXXXXX,`0,`,`XXXXXXXXXXXXX,`XXXXXXXXXX,`XXXXXXXXXXX,`JSAPI,`SUCCESS,`PSBC_DEBIT,`CNY,`6.00,`0.00,`0,`0,`0.00,`0.00,`,`,`XXXXXX,`XXXXXXX,`0.04000,`0.60%,`6.00,`0.00,`\n" +
     "总交易单数,应结订单总金额,退款总金额,充值券退款总金额,手续费总金额,订单总金额,申请退款总金额\n" +
     "`48,`5.76,`1.42,`0.00,`0.01000,`5.76,`1.42\n";
-  public static final String PAY_BILL_RESULT_SUCCESS_CONTENT = "交易时间,公众账号ID,商户号,特约商户号,设备号,微信订单号,商户订单号,用户标识,交易类型,交易状态,付款银行,货币种类,应结订单金额,代金券金额,商品名称,商户数据包,手续费,费率,订单金额,费率备注\n" +
+  private static final String PAY_BILL_RESULT_ALL_CONTENT_1 = "交易时间,公众账号ID,商户号,子商户号,设备号,微信订单号,商户订单号,用户标识,交易类型,交易状态,付款银行,货币种类,总金额,代金券或立减优惠金额,微信退款单号,商户退款单号,退款金额,代金券或立减优惠退款金额,退款类型,退款状态,商品名称,商户数据包,手续费,费率\n" +
+    "`2014-11-10 16:33:45,`wx2421b1c4370ec43b,`10000100,`0,`1000,`1001690740201411100005734289,`1415640626,`085e9858e3ba5186aafcbaed1,`MICROPAY,`SUCCESS,`OTHERS,`CNY,`0.01,`0.0,`0,`0,`0,`0,`,`,`被扫支付测试,`订单额外描述,`0,`0.60%\n" +
+    "`2014-11-10 16:46:14,`wx2421b1c4370ec43b,`10000100,`0,`1000,`1002780740201411100005729794,`1415635270,`085e9858e90ca40c0b5aee463,`MICROPAY,`SUCCESS,`OTHERS,`CNY,`0.01,`0.0,`0,`0,`0,`0,`,`,`被扫支付测试,`订单额外描述,`0,`0.60%\n" +
+    "总交易单数,总交易额,总退款金额,总代金券或立减优惠退款金额,手续费总金额\n" +
+    "`2,`0.02,`0.0,`0.0,`0";
+
+  private static final String PAY_BILL_RESULT_SUCCESS_CONTENT = "交易时间,公众账号ID,商户号,特约商户号,设备号,微信订单号,商户订单号,用户标识,交易类型,交易状态,付款银行,货币种类,应结订单金额,代金券金额,商品名称,商户数据包,手续费,费率,订单金额,费率备注\n" +
     "`2019-07-23 18:46:41,`XXXX,`XXX,`XXX,`,`XXX,`XXX,`XXX,`JSAPI,`SUCCESS,`CFT,`CNY,`0.01,`0.00,`XXX,`XXXX,`0.00000,`0.60%,`0.01,`\n" +
     "总交易单数,应结订单总金额,退款总金额,充值券退款总金额,手续费总金额,订单总金额,申请退款总金额\n" +
     "`31,`3.05,`0.00,`0.00,`0.02000,`3.05,`0.00";
-  public static final String PAY_BILL_RESULT_REFUND_CONTENT = "交易时间,公众账号ID,商户号,特约商户号,设备号,微信订单号,商户订单号,用户标识,交易类型,交易状态,付款银行,货币种类,应结订单金额,代金券金额,退款申请时间,退款成功时间,微信退款单号,商户退款单号,退款金额,充值券退款金额,退款类型,退款状态,商品名称,商户数据包,手续费,费率,订单金额,申请退款金额,费率备注\n" +
+  private static final String PAY_BILL_RESULT_REFUND_CONTENT = "交易时间,公众账号ID,商户号,特约商户号,设备号,微信订单号,商户订单号,用户标识,交易类型,交易状态,付款银行,货币种类,应结订单金额,代金券金额,退款申请时间,退款成功时间,微信退款单号,商户退款单号,退款金额,充值券退款金额,退款类型,退款状态,商品名称,商户数据包,手续费,费率,订单金额,申请退款金额,费率备注\n" +
     "`2019-07-23 20:53:36,`xxx,`xxx,`xxx,`,`xxx,`xxxx,`xxxxx,`JSAPI,`REFUND,`CFT,`CNY,`0.00,`0.00,`2019-07-23 20:53:36,`2019-07-23 20:53:40,`xxxx,`xxx,`0.01,`0.00,`ORIGINAL,`SUCCESS,`xxxx,`xxxx,`0.00000,`0.60%,`0.00,`0.01,`\n" +
     "总交易单数,应结订单总金额,退款总金额,充值券退款总金额,手续费总金额,订单总金额,申请退款总金额\n" +
     "`4,`0.00,`2.02,`0.00,`-0.02000,`0.00,`2.02";
 
+
   /**
    * 测试微信返回类型为ALL时,解析结果是否正确
    */
@@ -29,17 +37,29 @@ public class WxPayBillResultTest {
   public void testFromRawBillResultStringAll() {
     WxPayBillResult result = WxPayBillResult.fromRawBillResultString(PAY_BILL_RESULT_ALL_CONTENT, WxPayConstants.BillType.ALL);
 
-    Assert.assertEquals(result.getTotalRecord(), "48");
-    Assert.assertEquals(result.getTotalFee(), "5.76");
-    Assert.assertEquals(result.getTotalRefundFee(), "1.42");
-    Assert.assertEquals(result.getTotalCouponFee(), "0.00");
-    Assert.assertEquals(result.getTotalPoundageFee(), "0.01000");
-    Assert.assertEquals(result.getTotalAmount(), "5.76");
-    Assert.assertEquals(result.getTotalAppliedRefundFee(), "1.42");
-    Assert.assertEquals(result.getBillInfoList().get(0).getTotalAmount(), "6.00");
-    Assert.assertEquals(result.getBillInfoList().get(0).getAppliedRefundAmount(), "0.00");
-    Assert.assertEquals(result.getBillInfoList().get(0).getFeeRemark(), "");
+    assertEquals(result.getTotalRecord(), "48");
+    assertEquals(result.getTotalFee(), "5.76");
+    assertEquals(result.getTotalRefundFee(), "1.42");
+    assertEquals(result.getTotalCouponFee(), "0.00");
+    assertEquals(result.getTotalPoundageFee(), "0.01000");
+    assertEquals(result.getTotalAmount(), "5.76");
+    assertEquals(result.getTotalAppliedRefundFee(), "1.42");
+    assertEquals(result.getBillInfoList().get(0).getTotalAmount(), "6.00");
+    assertEquals(result.getBillInfoList().get(0).getAppliedRefundAmount(), "0.00");
+    assertEquals(result.getBillInfoList().get(0).getFeeRemark(), "");
+
+    result = WxPayBillResult.fromRawBillResultString(PAY_BILL_RESULT_ALL_CONTENT_1, WxPayConstants.BillType.ALL);
 
+    assertEquals(result.getTotalRecord(), "2");
+    assertEquals(result.getTotalFee(), "0.02");
+    assertEquals(result.getTotalRefundFee(), "0.0");
+    assertEquals(result.getTotalCouponFee(), "0.0");
+    assertEquals(result.getTotalPoundageFee(), "0");
+    assertNull(result.getTotalAmount());
+    assertNull(result.getTotalAppliedRefundFee());
+    assertNull(result.getBillInfoList().get(0).getTotalAmount());
+    assertNull(result.getBillInfoList().get(0).getAppliedRefundAmount());
+    assertNull(result.getBillInfoList().get(0).getFeeRemark());
 
   }
 
@@ -50,15 +70,15 @@ public void testFromRawBillResultStringAll() {
   public void testFromRawBillResultStringSuccess() {
     WxPayBillResult result = WxPayBillResult.fromRawBillResultString(PAY_BILL_RESULT_SUCCESS_CONTENT, WxPayConstants.BillType.SUCCESS);
 
-    Assert.assertEquals(result.getTotalRecord(), "31");
-    Assert.assertEquals(result.getTotalFee(), "3.05");
-    Assert.assertEquals(result.getTotalRefundFee(), "0.00");
-    Assert.assertEquals(result.getTotalCouponFee(), "0.00");
-    Assert.assertEquals(result.getTotalPoundageFee(), "0.02000");
-    Assert.assertEquals(result.getTotalAmount(), "3.05");
-    Assert.assertEquals(result.getTotalAppliedRefundFee(), "0.00");
-    Assert.assertEquals(result.getBillInfoList().get(0).getTotalAmount(), "0.01");
-    Assert.assertEquals(result.getBillInfoList().get(0).getFeeRemark(), "");
+    assertEquals(result.getTotalRecord(), "31");
+    assertEquals(result.getTotalFee(), "3.05");
+    assertEquals(result.getTotalRefundFee(), "0.00");
+    assertEquals(result.getTotalCouponFee(), "0.00");
+    assertEquals(result.getTotalPoundageFee(), "0.02000");
+    assertEquals(result.getTotalAmount(), "3.05");
+    assertEquals(result.getTotalAppliedRefundFee(), "0.00");
+    assertEquals(result.getBillInfoList().get(0).getTotalAmount(), "0.01");
+    assertEquals(result.getBillInfoList().get(0).getFeeRemark(), "");
 
   }
 }
diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayRedpackQueryResultTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayRedpackQueryResultTest.java
index b771cbb1d5..617e2afdf4 100644
--- a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayRedpackQueryResultTest.java
+++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayRedpackQueryResultTest.java
@@ -1,5 +1,6 @@
 package com.github.binarywang.wxpay.bean.result;
 
+import com.github.binarywang.wxpay.util.XmlConfig;
 import org.testng.annotations.*;
 
 import static org.assertj.core.api.Assertions.assertThat;
@@ -42,8 +43,8 @@ public void testFromXML() {
       "\n" +
       "";
 
-    WxPayRedpackQueryResult orderQueryResult = WxPayRedpackQueryResult.fromXML(xmlString, WxPayRedpackQueryResult.class);
-    System.out.println(orderQueryResult);
+    WxPayRedpackQueryResult orderQueryResult = BaseWxPayResult.fromXML(xmlString, WxPayRedpackQueryResult.class);
+//    System.out.println(orderQueryResult);
     assertThat(orderQueryResult).isNotNull();
 
     assertThat(orderQueryResult.getRedpackList()).isNotEmpty();
@@ -51,4 +52,65 @@ public void testFromXML() {
     assertThat(orderQueryResult.getRedpackList().get(0).getOpenid()).isEqualTo("o3yHF0uHuckI3yE6lwWiFQBQdVDI");
     assertThat(orderQueryResult.getRedpackList().get(0).getReceiveTime()).isEqualTo("2018-01-23 13:45:31");
   }
+
+  /**
+   * Test from xml.
+   * FastMode
+   */
+  @Test
+  public void testFromXMLFastMode() {
+    XmlConfig.fastMode = true;
+    String xmlString = "\n" +
+      "\n" +
+      "\n" +
+      "\n" +
+      "\n" +
+      "\n" +
+      "\n" +
+      "\n" +
+      "\n" +
+      "\n" +
+      "\n" +
+      "\n" +
+      "1\n" +
+      "100\n" +
+      "\n" +
+      "\n" +
+      "\n" +
+      "\n" +
+      "100\n" +
+      "\n" +
+      "\n" +
+      "\n" +
+      "";
+
+    try {
+      WxPayRedpackQueryResult orderQueryResult = BaseWxPayResult.fromXML(xmlString, WxPayRedpackQueryResult.class);
+//      System.out.println(orderQueryResult);
+      assertThat(orderQueryResult).isNotNull();
+
+      assertThat(orderQueryResult.getRedpackList()).isNotEmpty();
+      assertThat(orderQueryResult.getRedpackList().get(0).getAmount()).isEqualTo(100);
+      assertThat(orderQueryResult.getRedpackList().get(0).getOpenid()).isEqualTo("o3yHF0uHuckI3yE6lwWiFQBQdVDI");
+      assertThat(orderQueryResult.getRedpackList().get(0).getReceiveTime()).isEqualTo("2018-01-23 13:45:31");
+    } finally {
+      XmlConfig.fastMode = false;
+    }
+  }
+
+  @Test
+  void benchmark() {
+    long now = System.currentTimeMillis();
+    int loops = 10000;
+    for (int i = 0; i < loops; i++) {
+      testFromXML();
+    }
+    System.out.println(" reflect mode:\t" + (System.currentTimeMillis() - now) + " (ms) ");
+
+    now = System.currentTimeMillis();
+    for (int i = 0; i < loops; i++) {
+      testFromXMLFastMode();
+    }
+    System.out.println(" fast    mode:\t" + (System.currentTimeMillis() - now) + " (ms) ");
+  }
 }
diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayRefundResultTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayRefundResultTest.java
index 730bd37a09..e03028ad42 100644
--- a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayRefundResultTest.java
+++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayRefundResultTest.java
@@ -1,5 +1,6 @@
 package com.github.binarywang.wxpay.bean.result;
 
+import com.github.binarywang.wxpay.util.XmlConfig;
 import org.testng.annotations.Test;
 
 import static org.assertj.core.api.Assertions.assertThat;
@@ -40,11 +41,67 @@ public void testFromXML() {
       "   2 \n" +
       "";
 
-    WxPayRefundResult result = WxPayRefundResult.fromXML(xmlString);
-
+    WxPayRefundResult result = BaseWxPayResult.fromXML(xmlString, WxPayRefundResult.class);
+    result.composeRefundCoupons();
     assertThat(result.getRefundCoupons()).isNotEmpty();
     assertThat(result.getRefundCoupons().get(0).getCouponRefundId()).isEqualTo("123");
     assertThat(result.getRefundCoupons().get(0).getCouponType()).isEqualTo("CASH");
     assertThat(result.getRefundCoupons().get(0).getCouponRefundFee()).isEqualTo(1);
   }
+
+  @Test
+  public void testFromXMLFastMode() {
+    /*
+      该xml字符串来自于官方文档示例,稍加改造,加上代金卷
+      refund_channel 是个什么鬼,官方文档只字不提
+     */
+    String xmlString = "\n" +
+      "   \n" +
+      "   \n" +
+      "   \n" +
+      "   \n" +
+      "   \n" +
+      "   \n" +
+      "   \n" +
+      "   \n" +
+      "   \n" +
+      "   \n" +
+      "   \n" +
+      "   \n" +
+      "   1\n" +
+      "   1\n" +
+      "   123\n" +
+      "   1\n" +
+      "   \n" +
+      "   2 \n" +
+      "";
+    XmlConfig.fastMode = true;
+    try {
+      WxPayRefundResult result = BaseWxPayResult.fromXML(xmlString, WxPayRefundResult.class);
+      result.composeRefundCoupons();
+      assertThat(result.getRefundCoupons()).isNotEmpty();
+      assertThat(result.getRefundCoupons().get(0).getCouponRefundId()).isEqualTo("123");
+      assertThat(result.getRefundCoupons().get(0).getCouponType()).isEqualTo("CASH");
+      assertThat(result.getRefundCoupons().get(0).getCouponRefundFee()).isEqualTo(1);
+    } finally {
+      XmlConfig.fastMode = false;
+    }
+  }
+
+  @Test
+  void benchmark() {
+    long now = System.currentTimeMillis();
+    int loops = 10000;
+    for (int i = 0; i < loops; i++) {
+      testFromXML();
+    }
+    System.out.println(" reflect mode:\t" + (System.currentTimeMillis() - now) + " (ms) ");
+
+    now = System.currentTimeMillis();
+    for (int i = 0; i < loops; i++) {
+      testFromXMLFastMode();
+    }
+    System.out.println(" fast    mode:\t" + (System.currentTimeMillis() - now) + " (ms) ");
+  }
+
 }
diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImplTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImplTest.java
index 13a507d9e5..ab850d0b5b 100644
--- a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImplTest.java
+++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImplTest.java
@@ -1,43 +1,14 @@
 package com.github.binarywang.wxpay.service.impl;
 
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.Calendar;
-import java.util.Date;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.testng.annotations.*;
-
 import com.github.binarywang.utils.qrcode.QrcodeUtils;
-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.WxPayOrderNotifyResultTest;
 import com.github.binarywang.wxpay.bean.order.WxPayAppOrderResult;
 import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
 import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult;
-import com.github.binarywang.wxpay.bean.request.WxPayAuthcode2OpenidRequest;
-import com.github.binarywang.wxpay.bean.request.WxPayMicropayRequest;
-import com.github.binarywang.wxpay.bean.request.WxPayOrderReverseRequest;
-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.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.constant.WxPayConstants.AccountType;
 import com.github.binarywang.wxpay.constant.WxPayConstants.BillType;
 import com.github.binarywang.wxpay.constant.WxPayConstants.SignType;
@@ -46,7 +17,19 @@
 import com.github.binarywang.wxpay.service.WxPayService;
 import com.github.binarywang.wxpay.testbase.ApiTestModule;
 import com.github.binarywang.wxpay.testbase.XmlWxPayConfig;
+import com.github.binarywang.wxpay.util.XmlConfig;
 import com.google.inject.Inject;
+import lombok.extern.slf4j.Slf4j;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Guice;
+import org.testng.annotations.Test;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Calendar;
+import java.util.Date;
 
 import static com.github.binarywang.wxpay.constant.WxPayConstants.TarType;
 import static org.assertj.core.api.Assertions.assertThat;
@@ -58,10 +41,10 @@
  *
  * @author Binary Wang
  */
+@Slf4j
 @Test
 @Guice(modules = ApiTestModule.class)
 public class BaseWxPayServiceImplTest {
-  private final Logger logger = LoggerFactory.getLogger(this.getClass());
 
   @Inject
   private WxPayService payService;
@@ -84,8 +67,8 @@ public void testUnifiedOrder() throws WxPayException {
       .build();
     request.setSignType(SignType.HMAC_SHA256);
     WxPayUnifiedOrderResult result = this.payService.unifiedOrder(request);
-    this.logger.info(result.toString());
-    this.logger.warn(this.payService.getWxApiData().toString());
+    log.info(result.toString());
+    log.warn(this.payService.getWxApiData().toString());
   }
 
   /**
@@ -115,8 +98,8 @@ public void testCreateOrder_jsapi() throws Exception {
         .openid(((XmlWxPayConfig) this.payService.getConfig()).getOpenid())
         .outTradeNo("1111112")
         .build());
-    this.logger.info(result.toString());
-    this.logger.warn(this.payService.getWxApiData().toString());
+    log.info(result.toString());
+    log.warn(this.payService.getWxApiData().toString());
   }
 
   /**
@@ -135,8 +118,8 @@ public void testCreateOrder_app() throws Exception {
         .tradeType(TradeType.APP)
         .outTradeNo("1111112")
         .build());
-    this.logger.info(result.toString());
-    this.logger.warn(this.payService.getWxApiData().toString());
+    log.info(result.toString());
+    log.warn(this.payService.getWxApiData().toString());
   }
 
   /**
@@ -156,8 +139,8 @@ public void testCreateOrder_native() throws Exception {
         .tradeType(TradeType.NATIVE)
         .outTradeNo("111111290")
         .build());
-    this.logger.info(result.toString());
-    this.logger.warn(this.payService.getWxApiData().toString());
+    log.info(result.toString());
+    log.warn(this.payService.getWxApiData().toString());
   }
 
   /**
@@ -177,8 +160,8 @@ public void testGetPayInfo() throws Exception {
    */
   @Test
   public void testQueryOrder() throws WxPayException {
-    this.logger.info(this.payService.queryOrder("11212121", null).toString());
-    this.logger.info(this.payService.queryOrder(null, "11111").toString());
+    log.info(this.payService.queryOrder("11212121", null).toString());
+    log.info(this.payService.queryOrder(null, "11111").toString());
   }
 
   /**
@@ -188,7 +171,7 @@ public void testQueryOrder() throws WxPayException {
    */
   @Test
   public void testCloseOrder() throws WxPayException {
-    this.logger.info(this.payService.closeOrder("11212121").toString());
+    log.info(this.payService.closeOrder("11212121").toString());
   }
 
   /**
@@ -224,7 +207,7 @@ public void testDownloadBill(String billDate, String billType,
                                String tarType, String deviceInfo) throws Exception {
     WxPayBillResult billResult = this.payService.downloadBill(billDate, billType, tarType, deviceInfo);
     assertThat(billResult).isNotNull();
-    this.logger.info(billResult.toString());
+    log.info(billResult.toString());
   }
 
   /**
@@ -267,7 +250,7 @@ public Object[][] fundFlowData() {
   public void testDownloadFundFlow(String billDate, String accountType, String tarType) throws Exception {
     WxPayFundFlowResult fundFlowResult = this.payService.downloadFundFlow(billDate, accountType, tarType);
     assertThat(fundFlowResult).isNotNull();
-    this.logger.info(fundFlowResult.toString());
+    log.info(fundFlowResult.toString());
   }
 
   /**
@@ -312,7 +295,7 @@ public void testRefund() throws Exception {
         .totalFee(1222)
         .refundFee(111)
         .build());
-    this.logger.info(result.toString());
+    log.info(result.toString());
   }
 
   /**
@@ -325,20 +308,20 @@ public void testRefundQuery() throws Exception {
     WxPayRefundQueryResult result;
 
     result = this.payService.refundQuery("1", "", "", "");
-    this.logger.info(result.toString());
+    log.info(result.toString());
 
     result = this.payService.refundQuery("", "2", "", "");
-    this.logger.info(result.toString());
+    log.info(result.toString());
 
     result = this.payService.refundQuery("", "", "3", "");
-    this.logger.info(result.toString());
+    log.info(result.toString());
 
     result = this.payService.refundQuery("", "", "", "4");
-    this.logger.info(result.toString());
+    log.info(result.toString());
 
     //测试四个参数都填的情况,应该报异常的
     result = this.payService.refundQuery("1", "2", "3", "4");
-    this.logger.info(result.toString());
+    log.info(result.toString());
   }
 
   /**
@@ -351,37 +334,6 @@ public void testParseRefundNotifyResult() throws Exception {
     // 请参考com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResultTest里的单元测试
   }
 
-  /**
-   * Test method for {@link WxPayService#sendRedpack(WxPaySendRedpackRequest)} .
-   *
-   * @throws Exception the exception
-   */
-  @Test
-  public void testSendRedpack() throws Exception {
-    WxPaySendRedpackRequest request = new WxPaySendRedpackRequest();
-    request.setActName("abc");
-    request.setClientIp("aaa");
-    request.setMchBillNo("aaaa");
-    request.setWishing("what");
-    request.setSendName("111");
-    request.setTotalAmount(1);
-    request.setTotalNum(1);
-    request.setReOpenid(((XmlWxPayConfig) this.payService.getConfig()).getOpenid());
-    WxPaySendRedpackResult redpackResult = this.payService.sendRedpack(request);
-    this.logger.info(redpackResult.toString());
-  }
-
-  /**
-   * Test method for {@link WxPayService#queryRedpack(String)}.
-   *
-   * @throws Exception the exception
-   */
-  @Test
-  public void testQueryRedpack() throws Exception {
-    WxPayRedpackQueryResult redpackResult = this.payService.queryRedpack("aaaa");
-    this.logger.info(redpackResult.toString());
-  }
-
   /**
    * Test create scan pay qrcode mode 1.
    *
@@ -394,7 +346,7 @@ public void testCreateScanPayQrcodeMode1() throws Exception {
     Path qrcodeFilePath = Files.createTempFile("qrcode_", ".jpg");
     Files.write(qrcodeFilePath, bytes);
     String qrcodeContent = QrcodeUtils.decodeQrcode(qrcodeFilePath.toFile());
-    this.logger.info(qrcodeContent);
+    log.info(qrcodeContent);
 
     assertTrue(qrcodeContent.startsWith("weixin://wxpay/bizpayurl?"));
     assertTrue(qrcodeContent.contains("product_id=" + productId));
@@ -430,7 +382,7 @@ public void testMicropay() throws Exception {
         .spbillCreateIp("127.0.0.1")
         .authCode("aaa")
         .build());
-    this.logger.info(result.toString());
+    log.info(result.toString());
   }
 
   /**
@@ -461,12 +413,11 @@ public void testSetConfig() throws Exception {
   @Test
   public void testReverseOrder() throws Exception {
     WxPayOrderReverseResult result = this.payService.reverseOrder(
-      WxPayOrderReverseRequest
-        .newBuilder()
+      WxPayOrderReverseRequest.newBuilder()
         .outTradeNo("1111")
         .build());
     assertNotNull(result);
-    this.logger.info(result.toString());
+    log.info(result.toString());
   }
 
   /**
@@ -480,11 +431,11 @@ public void testShorturl() throws Exception {
 
     String result = this.payService.shorturl(new WxPayShorturlRequest(longUrl));
     assertNotNull(result);
-    this.logger.info(result);
+    log.info(result);
 
     result = this.payService.shorturl(longUrl);
     assertNotNull(result);
-    this.logger.info(result);
+    log.info(result);
   }
 
   /**
@@ -498,11 +449,11 @@ public void testAuthcode2Openid() throws Exception {
 
     String result = this.payService.authcode2Openid(new WxPayAuthcode2OpenidRequest(authCode));
     assertNotNull(result);
-    this.logger.info(result);
+    log.info(result);
 
     result = this.payService.authcode2Openid(authCode);
     assertNotNull(result);
-    this.logger.info(result);
+    log.info(result);
   }
 
   /**
@@ -514,7 +465,7 @@ public void testAuthcode2Openid() throws Exception {
   public void testGetSandboxSignKey() throws Exception {
     final String signKey = this.payService.getSandboxSignKey();
     assertNotNull(signKey);
-    this.logger.info(signKey);
+    log.info(signKey);
   }
 
   /**
@@ -530,7 +481,7 @@ public void testSendCoupon() throws Exception {
       .partnerTradeNo("1212")
       .openidCount(1)
       .build());
-    this.logger.info(result.toString());
+    log.info(result.toString());
   }
 
   /**
@@ -541,11 +492,10 @@ public void testSendCoupon() throws Exception {
   @Test
   public void testQueryCouponStock() throws Exception {
     WxPayCouponStockQueryResult result = this.payService.queryCouponStock(
-      WxPayCouponStockQueryRequest
-        .newBuilder()
+      WxPayCouponStockQueryRequest.newBuilder()
         .couponStockId("123")
         .build());
-    this.logger.info(result.toString());
+    log.info(result.toString());
   }
 
   /**
@@ -556,13 +506,12 @@ public void testQueryCouponStock() throws Exception {
   @Test
   public void testQueryCouponInfo() throws Exception {
     WxPayCouponInfoQueryResult result = this.payService.queryCouponInfo(
-      WxPayCouponInfoQueryRequest
-        .newBuilder()
+      WxPayCouponInfoQueryRequest.newBuilder()
         .openid("ojOQA0y9o-Eb6Aep7uVTdbkJqrP4")
         .couponId("11")
         .stockId("1121")
         .build());
-    this.logger.info(result.toString());
+    log.info(result.toString());
   }
 
   /**
@@ -578,7 +527,7 @@ public void testQueryComment() throws Exception {
     calendar.add(Calendar.DAY_OF_MONTH, -88);
     Date beginDate = calendar.getTime();
     String result = this.payService.queryComment(beginDate, endDate, 0, 1);
-    this.logger.info(result);
+    log.info(result);
   }
 
   /**
@@ -618,8 +567,18 @@ public void testParseOrderNotifyResult() throws Exception {
       "   200\n" +
       "";
 
-    WxPayOrderNotifyResult result = this.payService.parseOrderNotifyResult(xmlString);
+    XmlConfig.fastMode = true;
+    WxPayOrderNotifyResult result;
+    try {
+      result = BaseWxPayResult.fromXML(xmlString, WxPayOrderNotifyResult.class);
+      System.out.println(result);
+    } finally {
+      XmlConfig.fastMode = false;
+    }
+
+    result = this.payService.parseOrderNotifyResult(xmlString);
     System.out.println(result);
+
   }
 
   /**
@@ -632,4 +591,19 @@ public void testGetWxApiData() throws Exception {
     //see test in testUnifiedOrder()
   }
 
+  @Test
+  public void testDownloadRawBill() {
+  }
+
+  @Test
+  public void testTestDownloadRawBill() {
+  }
+
+  @Test
+  public void testGetWxPayFaceAuthInfo() {
+  }
+
+  @Test
+  public void testFacepay() {
+  }
 }
diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/EntPayServiceImplTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/EntPayServiceImplTest.java
index 4c7f6e3246..142bbbc734 100644
--- a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/EntPayServiceImplTest.java
+++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/EntPayServiceImplTest.java
@@ -1,8 +1,6 @@
 package com.github.binarywang.wxpay.service.impl;
 
-import com.github.binarywang.wxpay.bean.entpay.EntPayBankRequest;
-import com.github.binarywang.wxpay.bean.entpay.EntPayBankResult;
-import com.github.binarywang.wxpay.bean.entpay.EntPayRequest;
+import com.github.binarywang.wxpay.bean.entpay.*;
 import com.github.binarywang.wxpay.constant.WxPayConstants.CheckNameOption;
 import com.github.binarywang.wxpay.exception.WxPayException;
 import com.github.binarywang.wxpay.service.WxPayService;
@@ -10,7 +8,10 @@
 import com.google.inject.Inject;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.testng.annotations.*;
+import org.testng.annotations.Guice;
+import org.testng.annotations.Test;
+
+import java.util.concurrent.TimeUnit;
 
 /**
  * 
@@ -94,4 +95,57 @@ public void testPayBank() throws Exception {
   public void testQueryPayBank() throws Exception {
     this.logger.info(this.payService.getEntPayService().queryPayBank("123").toString());
   }
+
+
+
+  /**
+   * 发送企业红包
+   * @throws Exception the exception
+   */
+  @Test
+  public void testSendEnterpriseRedpack() {
+    EntPayRedpackRequest request = new EntPayRedpackRequest();
+    request.setMchId("1");
+    //商户单号
+    request.setMchBillNo(request.getMchId()+"20191202"+"1");
+    //企业微信corpid即为此appId
+    request.setWxAppId("1");
+//    request.setSenderName("1");
+//    request.setSenderHeaderMediaId("2");
+    request.setAgentId("1");
+    request.setReOpenid("1");
+    //目前企业微信api红包最低1块钱
+    request.setTotalAmount(1000);
+    request.setWishing("1");
+    request.setActName("1");
+    request.setRemark("1");
+
+    EntPayRedpackResult redpackResult = null;
+    try {
+      redpackResult = this.payService.getEntPayService().sendEnterpriseRedpack(request);
+    } catch (WxPayException e) {
+    }
+    this.logger.info(redpackResult.toString());
+  }
+
+  /**
+   * 查询企业红包
+   * @throws Exception
+   */
+  @Test
+  public void testQueryEnterpriseRedpack() throws Exception {
+    while (true) {
+      EntPayRedpackQueryRequest request = new EntPayRedpackQueryRequest();
+      request.setAppid("1");
+      request.setMchId("1");
+      request.setMchBillNo("1");
+
+      try {
+        EntPayRedpackQueryResult result = this.payService.getEntPayService().queryEnterpriseRedpack(request);
+        this.logger.info(result.toString());
+      } catch (Exception e) {
+      }
+      TimeUnit.SECONDS.sleep(3);
+    }
+  }
 }
diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/ProfitSharingServiceImplTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/ProfitSharingServiceImplTest.java
index d21cca55af..087ba7fc34 100644
--- a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/ProfitSharingServiceImplTest.java
+++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/ProfitSharingServiceImplTest.java
@@ -23,7 +23,7 @@ public class ProfitSharingServiceImplTest {
   private WxPayService payService;
 
   @Test
-  public void testProfitsharing() throws WxPayException {
+  public void testProfitSharing() throws WxPayException {
     ReceiverList instance = ReceiverList.getInstance();
     instance.add(new Receiver(WxPayConstants.ReceiverType.PERSONAL_OPENID,
       "oyOUE5ql4TtzrBg5cVOwxq6tbjOs",
@@ -40,7 +40,7 @@ public void testProfitsharing() throws WxPayException {
   }
 
   @Test
-  public void testMultiprofitsharing() throws WxPayException {
+  public void testMultiProfitSharing() throws WxPayException {
     ReceiverList instance = ReceiverList.getInstance();
     instance.add(new Receiver(WxPayConstants.ReceiverType.MERCHANT_ID,
       "86693852",
@@ -56,7 +56,7 @@ public void testMultiprofitsharing() throws WxPayException {
   }
 
   @Test
-  public void testProfitsharingFinish() throws WxPayException {
+  public void testProfitSharingFinish() throws WxPayException {
     ProfitSharingFinishRequest request = ProfitSharingFinishRequest
       .newBuilder()
       .outOrderNo("20191023103251431856285")
@@ -67,7 +67,7 @@ public void testProfitsharingFinish() throws WxPayException {
   }
 
   @Test
-  public void testAddreceiver() throws WxPayException {
+  public void testAddReceiver() throws WxPayException {
     Receiver receiver = new Receiver(WxPayConstants.ReceiverType.PERSONAL_OPENID,
       "oyOUE5ql4TtzrBg5cVOwxq6tbjOs",
       "***",
@@ -92,7 +92,7 @@ public void testRemoveReceiver() throws WxPayException {
   }
 
   @Test
-  public void testProfitsharingQuery() throws WxPayException {
+  public void testProfitSharingQuery() throws WxPayException {
     ProfitSharingQueryRequest request = ProfitSharingQueryRequest
       .newBuilder()
       .outOrderNo("20191023112023031060677")
@@ -104,7 +104,7 @@ public void testProfitsharingQuery() throws WxPayException {
   }
 
   @Test
-  public void testProfitsharingReturn() throws WxPayException {
+  public void testProfitSharingReturn() throws WxPayException {
     ProfitSharingReturnRequest request = ProfitSharingReturnRequest
       .newBuilder()
       .outOrderNo("20191023154723316420060")
@@ -118,7 +118,7 @@ public void testProfitsharingReturn() throws WxPayException {
   }
 
   @Test
-  public void testProfitsharingReturnQuery() throws WxPayException {
+  public void testProfitSharingReturnQuery() throws WxPayException {
     ProfitSharingReturnQueryRequest request = ProfitSharingReturnQueryRequest
       .newBuilder()
       .outOrderNo("20191023154723316420060")
@@ -127,5 +127,4 @@ public void testProfitsharingReturnQuery() throws WxPayException {
     this.logger.info(this.payService.getProfitSharingService().profitSharingReturnQuery(request).toString());
   }
 
-
 }
diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/RedpackServiceImplTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/RedpackServiceImplTest.java
new file mode 100644
index 0000000000..161135b51f
--- /dev/null
+++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/RedpackServiceImplTest.java
@@ -0,0 +1,63 @@
+package com.github.binarywang.wxpay.service.impl;
+
+import com.github.binarywang.wxpay.bean.request.WxPaySendMiniProgramRedpackRequest;
+import com.github.binarywang.wxpay.bean.request.WxPaySendRedpackRequest;
+import com.github.binarywang.wxpay.bean.result.WxPayRedpackQueryResult;
+import com.github.binarywang.wxpay.bean.result.WxPaySendMiniProgramRedpackResult;
+import com.github.binarywang.wxpay.bean.result.WxPaySendRedpackResult;
+import com.github.binarywang.wxpay.exception.WxPayException;
+import com.github.binarywang.wxpay.service.WxPayService;
+import com.github.binarywang.wxpay.testbase.ApiTestModule;
+import com.github.binarywang.wxpay.testbase.XmlWxPayConfig;
+import com.google.inject.Inject;
+import lombok.extern.slf4j.Slf4j;
+import org.testng.annotations.Guice;
+import org.testng.annotations.Test;
+
+/**
+ * 测试类.
+ *
+ * @author Binary Wang
+ * @date 2019-12-26
+ */
+@Slf4j
+@Test
+@Guice(modules = ApiTestModule.class)
+public class RedpackServiceImplTest {
+  @Inject
+  private WxPayService payService;
+
+  @Test
+  public void testSendRedpack() throws Exception {
+    WxPaySendRedpackRequest request = new WxPaySendRedpackRequest();
+    request.setActName("abc");
+    request.setClientIp("aaa");
+    request.setMchBillNo("aaaa");
+    request.setWishing("what");
+    request.setSendName("111");
+    request.setTotalAmount(1);
+    request.setTotalNum(1);
+    request.setReOpenid(((XmlWxPayConfig) this.payService.getConfig()).getOpenid());
+    WxPaySendRedpackResult redpackResult = this.payService.getRedpackService().sendRedpack(request);
+    log.info(redpackResult.toString());
+  }
+
+  @Test
+  public void testQueryRedpack() throws Exception {
+    WxPayRedpackQueryResult redpackResult = this.payService.getRedpackService().queryRedpack("aaaa");
+    log.info(redpackResult.toString());
+  }
+
+  @Test
+  public void testSendMiniProgramRedpack() throws WxPayException {
+    final WxPaySendMiniProgramRedpackResult result = this.payService.getRedpackService()
+      .sendMiniProgramRedpack(new WxPaySendMiniProgramRedpackRequest()
+        .setReOpenid("ojOQA0y9o-Eb6Aep7uVTdbkJqrP4")
+        .setWishing("haha")
+        .setMchBillNo("123")
+        .setActName("11")
+        .setSendName("111")
+        .setTotalAmount(1));
+    System.out.println(result);
+  }
+}