From 31ecd4d83d43910d48b79f634e372a418800c5fe Mon Sep 17 00:00:00 2001 From: Binary Wang Date: Fri, 4 Sep 2020 21:45:36 +0800 Subject: [PATCH 1/5] =?UTF-8?q?:art:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=EF=BC=8C=E4=BD=BF=E7=94=A8java8=E8=87=AA=E5=B8=A6?= =?UTF-8?q?=E7=9A=84Base64=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wxpay/service/impl/EntPayServiceImpl.java | 4 ++-- .../service/impl/WxPayServiceApacheHttpImpl.java | 4 ++-- .../service/impl/WxPayServiceJoddHttpImpl.java | 15 +++++++-------- 3 files changed, 11 insertions(+), 12 deletions(-) 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 5e768bef9..00e2ede65 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 @@ -8,7 +8,6 @@ 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; @@ -22,6 +21,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.security.PublicKey; import java.security.Security; +import java.util.Base64; /** *
@@ -168,7 +168,7 @@ public class EntPayServiceImpl implements EntPayService {
 
         cipher.init(Cipher.ENCRYPT_MODE, publicKey);
         byte[] encrypt = cipher.doFinal(srcString.getBytes(StandardCharsets.UTF_8));
-        return Base64.encodeBase64String(encrypt);
+        return Base64.getEncoder().encodeToString(encrypt);
       }
     } catch (Exception e) {
       throw new WxPayException("加密出错", e);
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceApacheHttpImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceApacheHttpImpl.java
index c03713473..fe25bffc6 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceApacheHttpImpl.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceApacheHttpImpl.java
@@ -3,7 +3,6 @@ package com.github.binarywang.wxpay.service.impl;
 import com.github.binarywang.wxpay.bean.WxPayApiData;
 import com.github.binarywang.wxpay.exception.WxPayException;
 import com.google.gson.JsonObject;
-import jodd.util.Base64;
 import me.chanjar.weixin.common.util.json.GsonParser;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.http.HttpEntity;
@@ -29,6 +28,7 @@ import org.apache.http.util.EntityUtils;
 import javax.net.ssl.SSLContext;
 import java.net.URI;
 import java.nio.charset.StandardCharsets;
+import java.util.Base64;
 
 /**
  * 
@@ -48,7 +48,7 @@ public class WxPayServiceApacheHttpImpl extends BaseWxPayServiceImpl {
       try (CloseableHttpClient httpClient = httpClientBuilder.build()) {
         try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
           final byte[] bytes = EntityUtils.toByteArray(response.getEntity());
-          final String responseData = Base64.encodeToString(bytes);
+          final String responseData = Base64.getEncoder().encodeToString(bytes);
           this.log.info("\n【请求地址】:{}\n【请求数据】:{}\n【响应数据(Base64编码后)】:{}", url, requestStr, responseData);
           wxApiData.set(new WxPayApiData(url, requestStr, responseData, null));
           return bytes;
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceJoddHttpImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceJoddHttpImpl.java
index b7ef11695..65f5e869b 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceJoddHttpImpl.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceJoddHttpImpl.java
@@ -1,11 +1,5 @@
 package com.github.binarywang.wxpay.service.impl;
 
-import java.net.URI;
-import java.nio.charset.StandardCharsets;
-import javax.net.ssl.SSLContext;
-
-import org.apache.commons.lang3.StringUtils;
-
 import com.github.binarywang.wxpay.bean.WxPayApiData;
 import com.github.binarywang.wxpay.exception.WxPayException;
 import jodd.http.HttpConnectionProvider;
@@ -15,9 +9,14 @@ import jodd.http.ProxyInfo;
 import jodd.http.ProxyInfo.ProxyType;
 import jodd.http.net.SSLSocketHttpConnectionProvider;
 import jodd.http.net.SocketHttpConnectionProvider;
-import jodd.util.Base64;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.http.client.methods.HttpPost;
 
+import javax.net.ssl.SSLContext;
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+
 /**
  * 微信支付请求实现类,jodd-http实现.
  * Created by Binary Wang on 2016/7/28.
@@ -30,7 +29,7 @@ public class WxPayServiceJoddHttpImpl extends BaseWxPayServiceImpl {
     try {
       HttpRequest request = this.buildHttpRequest(url, requestStr, useKey);
       byte[] responseBytes = request.send().bodyBytes();
-      final String responseString = Base64.encodeToString(responseBytes);
+      final String responseString = Base64.getEncoder().encodeToString(responseBytes);
       this.log.info("\n【请求地址】:{}\n【请求数据】:{}\n【响应数据(Base64编码后)】:{}", url, requestStr, responseString);
       if (this.getConfig().isIfSaveApiData()) {
         wxApiData.set(new WxPayApiData(url, requestStr, responseString, null));

From 07352d183c02eee35935321bd56fc61ee90ecdf8 Mon Sep 17 00:00:00 2001
From: Binary Wang 
Date: Fri, 4 Sep 2020 21:46:57 +0800
Subject: [PATCH 2/5] =?UTF-8?q?:bug:=20#1738=20=E4=BF=AE=E5=A4=8D=E4=BC=81?=
 =?UTF-8?q?=E4=B8=9A=E5=BE=AE=E4=BF=A1=E5=88=9B=E5=BB=BA=E7=94=A8=E6=88=B7?=
 =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E8=87=AA=E5=AE=9A=E4=B9=89=E5=AD=97=E6=AE=B5?=
 =?UTF-8?q?=E7=BC=BA=E5=A4=B1=E7=9A=84=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 pom.xml                                       |   4 +-
 weixin-java-common/pom.xml                    |   2 +-
 .../weixin/common/util/json/GsonHelper.java   |  47 ++++++
 .../common/util/json/GsonHelperTest.java      | 139 ++++++++++++++++++
 .../me/chanjar/weixin/cp/bean/WxCpUser.java   |   3 +
 .../cp/util/json/WxCpUserGsonAdapter.java     |  53 +++----
 .../cp/util/json/WxCpUserGsonAdapterTest.java |  12 +-
 7 files changed, 219 insertions(+), 41 deletions(-)
 create mode 100644 weixin-java-common/src/test/java/me/chanjar/weixin/common/util/json/GsonHelperTest.java

diff --git a/pom.xml b/pom.xml
index 379f5cf45..99dc1146d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -172,7 +172,7 @@
       
         org.slf4j
         slf4j-api
-        1.7.24
+        1.7.30
       
       
         com.thoughtworks.xstream
@@ -268,7 +268,7 @@
       
         org.springframework.data
         spring-data-redis
-        1.8.23.RELEASE
+        2.3.3.RELEASE
         true
         provided
       
diff --git a/weixin-java-common/pom.xml b/weixin-java-common/pom.xml
index 8786e0458..a94e4f60e 100644
--- a/weixin-java-common/pom.xml
+++ b/weixin-java-common/pom.xml
@@ -50,7 +50,7 @@
     
       org.slf4j
       jcl-over-slf4j
-      1.7.24
+      1.7.30
     
 
     
diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/GsonHelper.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/GsonHelper.java
index 882853945..11a0742a7 100644
--- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/GsonHelper.java
+++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/GsonHelper.java
@@ -4,6 +4,7 @@ import com.google.common.collect.Lists;
 import com.google.gson.JsonArray;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
+import jodd.util.MathUtil;
 
 import java.util.List;
 
@@ -151,4 +152,50 @@ public class GsonHelper {
   public static JsonArray getAsJsonArray(JsonElement element) {
     return element == null ? null : element.getAsJsonArray();
   }
+
+  /**
+   * 快速构建JsonObject对象,批量添加一堆属性
+   *
+   * @param keyOrValue 包含key或value的数组
+   * @return JsonObject对象.
+   */
+  public static JsonObject buildJsonObject(Object... keyOrValue) {
+    JsonObject result = new JsonObject();
+    put(result, keyOrValue);
+    return result;
+  }
+
+  /**
+   * 批量向JsonObject对象中添加属性
+   *
+   * @param jsonObject 原始JsonObject对象
+   * @param keyOrValue 包含key或value的数组
+   */
+  public static void put(JsonObject jsonObject, Object... keyOrValue) {
+    if (MathUtil.isOdd(keyOrValue.length)) {
+      throw new RuntimeException("参数个数必须为偶数");
+    }
+
+    for (int i = 0; i < keyOrValue.length / 2; i++) {
+      final Object key = keyOrValue[2 * i];
+      final Object value = keyOrValue[2 * i + 1];
+      if (value == null) {
+        jsonObject.add(key.toString(), null);
+        continue;
+      }
+
+      if (value instanceof Boolean) {
+        jsonObject.addProperty(key.toString(), (Boolean) value);
+      } else if (value instanceof Character) {
+        jsonObject.addProperty(key.toString(), (Character) value);
+      } else if (value instanceof Number) {
+        jsonObject.addProperty(key.toString(), (Number) value);
+      } else if (value instanceof JsonElement) {
+        jsonObject.add(key.toString(), (JsonElement) value);
+      } else {
+        jsonObject.addProperty(key.toString(), value.toString());
+      }
+    }
+
+  }
 }
diff --git a/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/json/GsonHelperTest.java b/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/json/GsonHelperTest.java
new file mode 100644
index 000000000..396862e70
--- /dev/null
+++ b/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/json/GsonHelperTest.java
@@ -0,0 +1,139 @@
+package me.chanjar.weixin.common.util.json;
+
+import com.google.gson.JsonObject;
+import org.testng.annotations.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * GsonHelper 的单元测试.
+ *
+ * @author Binary Wang
+ * @date 2020-09-04
+ */
+public class GsonHelperTest {
+
+  @Test
+  public void testIsNull() {
+  }
+
+  @Test
+  public void testIsNotNull() {
+  }
+
+  @Test
+  public void testGetLong() {
+  }
+
+  @Test
+  public void testGetPrimitiveLong() {
+  }
+
+  @Test
+  public void testGetInteger() {
+  }
+
+  @Test
+  public void testGetPrimitiveInteger() {
+  }
+
+  @Test
+  public void testGetDouble() {
+  }
+
+  @Test
+  public void testGetPrimitiveDouble() {
+  }
+
+  @Test
+  public void testGetFloat() {
+  }
+
+  @Test
+  public void testGetPrimitiveFloat() {
+  }
+
+  @Test
+  public void testGetBoolean() {
+  }
+
+  @Test
+  public void testGetString() {
+  }
+
+  @Test
+  public void testGetAsString() {
+  }
+
+  @Test
+  public void testGetAsLong() {
+  }
+
+  @Test
+  public void testGetAsPrimitiveLong() {
+  }
+
+  @Test
+  public void testGetAsInteger() {
+  }
+
+  @Test
+  public void testGetAsPrimitiveInt() {
+  }
+
+  @Test
+  public void testGetAsBoolean() {
+  }
+
+  @Test
+  public void testGetAsPrimitiveBool() {
+  }
+
+  @Test
+  public void testGetAsDouble() {
+  }
+
+  @Test
+  public void testGetAsPrimitiveDouble() {
+  }
+
+  @Test
+  public void testGetAsFloat() {
+  }
+
+  @Test
+  public void testGetAsPrimitiveFloat() {
+  }
+
+  @Test
+  public void testGetIntArray() {
+  }
+
+  @Test
+  public void testGetStringArray() {
+  }
+
+  @Test
+  public void testGetLongArray() {
+  }
+
+  @Test
+  public void testGetAsJsonArray() {
+  }
+
+  @Test
+  public void testBuildSimpleJsonObject() {
+    try {
+      GsonHelper.buildJsonObject(1, 2, 3);
+    } catch (RuntimeException e) {
+      assertThat(e.getMessage()).isEqualTo("参数个数必须为偶数");
+    }
+
+    System.out.println(GsonHelper.buildJsonObject(1, 2));
+    System.out.println(GsonHelper.buildJsonObject(1, null));
+    System.out.println(GsonHelper.buildJsonObject("int", 1, "float", 2.1f, "double", 2.5));
+    System.out.println(GsonHelper.buildJsonObject("boolean", true, "string", "1av"));
+    System.out.println(GsonHelper.buildJsonObject(1, true, "jsonElement", new JsonObject()));
+    System.out.println(GsonHelper.buildJsonObject("num", 2, "string", "cde", "char", 'a', "bool", true));
+  }
+}
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 a6ceb4e55..221f49aa8 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
@@ -86,6 +86,9 @@ public class WxCpUser implements Serializable {
 
   @Data
   @Accessors(chain = true)
+  @Builder
+  @NoArgsConstructor
+  @AllArgsConstructor
   public static class Attr {
     /**
      * 属性类型: 0-文本 1-网页
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpUserGsonAdapter.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpUserGsonAdapter.java
index e721b33ac..5ec9d1482 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpUserGsonAdapter.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpUserGsonAdapter.java
@@ -276,11 +276,11 @@ public class WxCpUserGsonAdapter implements JsonDeserializer, JsonSeri
       o.addProperty("main_department", user.getMainDepartment());
     }
 
-    if (user.getExtAttrs().size() > 0) {
+    if (!user.getExtAttrs().isEmpty()) {
       JsonArray attrsJsonArray = new JsonArray();
       for (WxCpUser.Attr attr : user.getExtAttrs()) {
-        JsonObject attrJson = new JsonObject();
-
+        JsonObject attrJson = GsonHelper.buildJsonObject("type", attr.getType(),
+          "name", attr.getName());
         attrsJsonArray.add(attrJson);
 
         if (attr.getType() == null) {
@@ -290,19 +290,12 @@ public class WxCpUserGsonAdapter implements JsonDeserializer, JsonSeri
         }
 
         switch (attr.getType()) {
-          case 0: {
-            JsonObject text = new JsonObject();
-            text.addProperty("value", attr.getTextValue());
-            attrJson.add("text", text);
+          case 0:
+            attrJson.add("text", GsonHelper.buildJsonObject("value", attr.getTextValue()));
             break;
-          }
-          case 1: {
-            JsonObject web = new JsonObject();
-            web.addProperty("url", attr.getWebUrl());
-            web.addProperty("title", attr.getWebTitle());
-            attrJson.add("web", web);
+          case 1:
+            attrJson.add("web", GsonHelper.buildJsonObject("url", attr.getWebUrl(), "title", attr.getWebTitle()));
             break;
-          }
           default: //ignored
         }
       }
@@ -322,12 +315,11 @@ public class WxCpUserGsonAdapter implements JsonDeserializer, JsonSeri
       attrsJson.addProperty(EXTERNAL_CORP_NAME, user.getExternalCorpName());
     }
 
-    if (user.getExternalAttrs().size() > 0) {
+    if (!user.getExternalAttrs().isEmpty()) {
       JsonArray attrsJsonArray = new JsonArray();
       for (WxCpUser.ExternalAttribute attr : user.getExternalAttrs()) {
-        JsonObject attrJson = new JsonObject();
-        attrJson.addProperty("type", attr.getType());
-        attrJson.addProperty("name", attr.getName());
+        JsonObject attrJson = GsonHelper.buildJsonObject("type", attr.getType(),
+          "name", attr.getName());
 
         attrsJsonArray.add(attrJson);
 
@@ -336,27 +328,16 @@ public class WxCpUserGsonAdapter implements JsonDeserializer, JsonSeri
         }
 
         switch (attr.getType()) {
-          case 0: {
-            JsonObject text = new JsonObject();
-            text.addProperty("value", attr.getValue());
-            attrJson.add("text", text);
+          case 0:
+            attrJson.add("text", GsonHelper.buildJsonObject("value", attr.getValue()));
             break;
-          }
-          case 1: {
-            JsonObject web = new JsonObject();
-            web.addProperty("url", attr.getUrl());
-            web.addProperty("title", attr.getTitle());
-            attrJson.add("web", web);
+          case 1:
+            attrJson.add("web", GsonHelper.buildJsonObject("url", attr.getUrl(), "title", attr.getTitle()));
             break;
-          }
-          case 2: {
-            JsonObject miniprogram = new JsonObject();
-            miniprogram.addProperty("appid", attr.getAppid());
-            miniprogram.addProperty("pagepath", attr.getPagePath());
-            miniprogram.addProperty("title", attr.getTitle());
-            attrJson.add("miniprogram", miniprogram);
+          case 2:
+            attrJson.add("miniprogram", GsonHelper.buildJsonObject("appid", attr.getAppid(),
+              "pagepath", attr.getPagePath(), "title", attr.getTitle()));
             break;
-          }
           default://忽略
         }
       }
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/util/json/WxCpUserGsonAdapterTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/util/json/WxCpUserGsonAdapterTest.java
index d78175c1b..a83e8837d 100644
--- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/util/json/WxCpUserGsonAdapterTest.java
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/util/json/WxCpUserGsonAdapterTest.java
@@ -91,7 +91,7 @@ public class WxCpUserGsonAdapterTest {
     assertThat(user).isNotNull();
 
     assertThat(user.getOrders()).isNotEmpty();
-    assertThat(user.getOrders().length).isEqualTo(2);
+    assertThat(user.getOrders()).hasSize(2);
     assertThat(user.getOrders()[0]).isEqualTo(1);
     assertThat(user.getOrders()[1]).isEqualTo(2);
 
@@ -140,6 +140,12 @@ public class WxCpUserGsonAdapterTest {
   public void testSerialize() {
     WxCpUser user = new WxCpUser();
     user.setOrders(new Integer[]{1, 2});
+    user.addExtAttr(WxCpUser.Attr.builder()
+      .type(0)
+      .name("文本名称")
+      .textValue("文本")
+      .build());
+
     user.addExternalAttr(WxCpUser.ExternalAttribute.builder()
       .type(0)
       .name("文本名称")
@@ -159,7 +165,9 @@ public class WxCpUserGsonAdapterTest {
       .title("my miniprogram")
       .build());
 
-    assertThat(user.toJson()).isEqualTo("{\"order\":[1,2],\"external_profile\":{\"external_attr\":" +
+    assertThat(user.toJson()).isEqualTo("{\"order\":[1,2]," +
+      "\"extattr\":{\"attrs\":[{\"type\":0,\"name\":\"文本名称\",\"text\":{\"value\":\"文本\"}}]}," +
+      "\"external_profile\":{\"external_attr\":" +
       "[{\"type\":0,\"name\":\"文本名称\",\"text\":{\"value\":\"文本\"}}," +
       "{\"type\":1,\"name\":\"网页名称\",\"web\":{\"url\":\"http://www.test.com\",\"title\":\"标题\"}}," +
       "{\"type\":2,\"name\":\"测试app\"," +

From 010b43194f745afbf160ea0932a75a635d1e00e9 Mon Sep 17 00:00:00 2001
From: Binary Wang 
Date: Sat, 5 Sep 2020 09:30:39 +0800
Subject: [PATCH 3/5] =?UTF-8?q?:art:=20=E5=8D=87=E7=BA=A7=E4=BE=9D?=
 =?UTF-8?q?=E8=B5=96=E7=9A=84Spring=20Boot=E7=89=88=E6=9C=AC=E4=B8=BA?=
 =?UTF-8?q?=E6=9C=80=E6=96=B0=EF=BC=8C=E5=B9=B6=E4=BC=98=E5=8C=96=E9=83=A8?=
 =?UTF-8?q?=E5=88=86=E4=BB=A3=E7=A0=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 spring-boot-starters/pom.xml                  |  2 +-
 .../RedisTemplateSimpleDistributedLock.java   | 13 +---
 .../weixin/mp/api/WxMpMessageRouter.java      | 75 +++++++++----------
 3 files changed, 40 insertions(+), 50 deletions(-)

diff --git a/spring-boot-starters/pom.xml b/spring-boot-starters/pom.xml
index 3ec16a227..bd7efa330 100644
--- a/spring-boot-starters/pom.xml
+++ b/spring-boot-starters/pom.xml
@@ -14,7 +14,7 @@
   WxJava 各个模块的 Spring Boot Starter
 
   
-    2.1.4.RELEASE
+    2.3.3.RELEASE
   
 
   
diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/locks/RedisTemplateSimpleDistributedLock.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/locks/RedisTemplateSimpleDistributedLock.java
index dfac1c28f..8c5ccc26f 100644
--- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/locks/RedisTemplateSimpleDistributedLock.java
+++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/locks/RedisTemplateSimpleDistributedLock.java
@@ -3,8 +3,6 @@ package me.chanjar.weixin.common.util.locks;
 import lombok.Getter;
 import lombok.NonNull;
 import org.jetbrains.annotations.NotNull;
-import org.springframework.dao.DataAccessException;
-import org.springframework.data.redis.connection.RedisConnection;
 import org.springframework.data.redis.connection.RedisStringCommands;
 import org.springframework.data.redis.core.RedisCallback;
 import org.springframework.data.redis.core.StringRedisTemplate;
@@ -76,13 +74,10 @@ public class RedisTemplateSimpleDistributedLock implements Lock {
     }
     final byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
     final byte[] valueBytes = value.getBytes(StandardCharsets.UTF_8);
-    List redisResults = redisTemplate.executePipelined(new RedisCallback() {
-      @Override
-      public String doInRedis(RedisConnection connection) throws DataAccessException {
-        connection.set(keyBytes, valueBytes, Expiration.milliseconds(leaseMilliseconds), RedisStringCommands.SetOption.SET_IF_ABSENT);
-        connection.get(keyBytes);
-        return null;
-      }
+    List redisResults = redisTemplate.executePipelined((RedisCallback) connection -> {
+      connection.set(keyBytes, valueBytes, Expiration.milliseconds(leaseMilliseconds), RedisStringCommands.SetOption.SET_IF_ABSENT);
+      connection.get(keyBytes);
+      return null;
     });
     Object currentLockSecret = redisResults.size() > 1 ? redisResults.get(1) : redisResults.get(0);
     return currentLockSecret != null && currentLockSecret.toString().equals(value);
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageRouter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageRouter.java
index 98ef7716f..9b2ca03fe 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageRouter.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageRouter.java
@@ -1,18 +1,5 @@
 package me.chanjar.weixin.mp.api;
 
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-
-import org.apache.commons.lang3.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 import me.chanjar.weixin.common.api.WxErrorExceptionHandler;
 import me.chanjar.weixin.common.api.WxMessageDuplicateChecker;
 import me.chanjar.weixin.common.api.WxMessageInMemoryDuplicateChecker;
@@ -23,6 +10,18 @@ import me.chanjar.weixin.common.session.WxSessionManager;
 import me.chanjar.weixin.common.util.LogExceptionHandler;
 import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
 import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
 
 /**
  * 
@@ -183,7 +182,7 @@ public class WxMpMessageRouter {
       }
     }
 
-    if (matchRules.size() == 0) {
+    if (matchRules.isEmpty()) {
       return null;
     }
 
@@ -193,11 +192,8 @@ public class WxMpMessageRouter {
       // 返回最后一个非异步的rule的执行结果
       if (rule.isAsync()) {
         futures.add(
-          this.executorService.submit(new Runnable() {
-            @Override
-            public void run() {
-              rule.service(wxMessage, context, mpService, WxMpMessageRouter.this.sessionManager, WxMpMessageRouter.this.exceptionHandler);
-            }
+          this.executorService.submit(() -> {
+            rule.service(wxMessage, context, mpService, WxMpMessageRouter.this.sessionManager, WxMpMessageRouter.this.exceptionHandler);
           })
         );
       } else {
@@ -208,35 +204,34 @@ public class WxMpMessageRouter {
       }
     }
 
-    if (futures.size() > 0) {
-      this.executorService.submit(new Runnable() {
-        @Override
-        public void run() {
-          for (Future future : futures) {
-            try {
-              future.get();
-              WxMpMessageRouter.this.log.debug("End session access: async=true, sessionId={}", wxMessage.getFromUser());
-              // 异步操作结束,session访问结束
-              sessionEndAccess(wxMessage);
-            } catch (InterruptedException e) {
-              WxMpMessageRouter.this.log.error("Error happened when wait task finish", e);
-              Thread.currentThread().interrupt();
-            } catch (ExecutionException e) {
-              WxMpMessageRouter.this.log.error("Error happened when wait task finish", e);
-            }
-          }
-        }
-      });
+    if (futures.isEmpty()) {
+      return res;
     }
+
+    this.executorService.submit(() -> {
+      for (Future future : futures) {
+        try {
+          future.get();
+          WxMpMessageRouter.this.log.debug("End session access: async=true, sessionId={}", wxMessage.getFromUser());
+          // 异步操作结束,session访问结束
+          sessionEndAccess(wxMessage);
+        } catch (InterruptedException e) {
+          WxMpMessageRouter.this.log.error("Error happened when wait task finish", e);
+          Thread.currentThread().interrupt();
+        } catch (ExecutionException e) {
+          WxMpMessageRouter.this.log.error("Error happened when wait task finish", e);
+        }
+      }
+    });
     return res;
   }
 
   public WxMpXmlOutMessage route(final WxMpXmlMessage wxMessage) {
-    return this.route(wxMessage, new HashMap(2));
+    return this.route(wxMessage, new HashMap<>(2));
   }
 
   public WxMpXmlOutMessage route(String appid, final WxMpXmlMessage wxMessage) {
-    return this.route(appid, wxMessage, new HashMap(2));
+    return this.route(appid, wxMessage, new HashMap<>(2));
   }
 
   private boolean isMsgDuplicated(WxMpXmlMessage wxMessage) {

From e04eaf4389463fae41470da5dce9caee6c8cce40 Mon Sep 17 00:00:00 2001
From: Binary Wang 
Date: Sat, 5 Sep 2020 10:06:41 +0800
Subject: [PATCH 4/5] =?UTF-8?q?:bookmark:=20=20=E5=8F=91=E5=B8=83=203.9.1.?=
 =?UTF-8?q?B=20=E6=B5=8B=E8=AF=95=E7=89=88=E6=9C=AC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 pom.xml                                                         | 2 +-
 spring-boot-starters/pom.xml                                    | 2 +-
 .../wx-java-miniapp-spring-boot-starter/pom.xml                 | 2 +-
 spring-boot-starters/wx-java-mp-spring-boot-starter/pom.xml     | 2 +-
 spring-boot-starters/wx-java-open-spring-boot-starter/pom.xml   | 2 +-
 spring-boot-starters/wx-java-pay-spring-boot-starter/pom.xml    | 2 +-
 weixin-graal/pom.xml                                            | 2 +-
 weixin-java-common/pom.xml                                      | 2 +-
 weixin-java-cp/pom.xml                                          | 2 +-
 weixin-java-miniapp/pom.xml                                     | 2 +-
 weixin-java-mp/pom.xml                                          | 2 +-
 weixin-java-open/pom.xml                                        | 2 +-
 weixin-java-pay/pom.xml                                         | 2 +-
 13 files changed, 13 insertions(+), 13 deletions(-)

diff --git a/pom.xml b/pom.xml
index 99dc1146d..c87072185 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
   4.0.0
   com.github.binarywang
   wx-java
-  3.9.0
+  3.9.1.B
   pom
   WxJava - Weixin/Wechat Java SDK
   微信开发Java SDK
diff --git a/spring-boot-starters/pom.xml b/spring-boot-starters/pom.xml
index bd7efa330..05f541442 100644
--- a/spring-boot-starters/pom.xml
+++ b/spring-boot-starters/pom.xml
@@ -6,7 +6,7 @@
   
     com.github.binarywang
     wx-java
-    3.9.0
+    3.9.1.B
   
   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 b6e5904ac..72513a80d 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.9.0
+    3.9.1.B
   
   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 7e59fc8c8..623c2147e 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.9.0
+    3.9.1.B
   
   4.0.0
 
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 5a98c83a4..3d7cabcbe 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.9.0
+    3.9.1.B
   
   4.0.0
 
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 1a93eb54f..e4cffe7d9 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.9.0
+    3.9.1.B
   
   4.0.0
 
diff --git a/weixin-graal/pom.xml b/weixin-graal/pom.xml
index 016544bc6..d28ffff26 100644
--- a/weixin-graal/pom.xml
+++ b/weixin-graal/pom.xml
@@ -6,7 +6,7 @@
   
     com.github.binarywang
     wx-java
-    3.9.0
+    3.9.1.B
   
 
   weixin-graal
diff --git a/weixin-java-common/pom.xml b/weixin-java-common/pom.xml
index a94e4f60e..16abcf236 100644
--- a/weixin-java-common/pom.xml
+++ b/weixin-java-common/pom.xml
@@ -6,7 +6,7 @@
   
     com.github.binarywang
     wx-java
-    3.9.0
+    3.9.1.B
   
 
   weixin-java-common
diff --git a/weixin-java-cp/pom.xml b/weixin-java-cp/pom.xml
index 86d6feab3..4c0c2e9ea 100644
--- a/weixin-java-cp/pom.xml
+++ b/weixin-java-cp/pom.xml
@@ -7,7 +7,7 @@
   
     com.github.binarywang
     wx-java
-    3.9.0
+    3.9.1.B
   
 
   weixin-java-cp
diff --git a/weixin-java-miniapp/pom.xml b/weixin-java-miniapp/pom.xml
index 16d56b930..d708de8a9 100644
--- a/weixin-java-miniapp/pom.xml
+++ b/weixin-java-miniapp/pom.xml
@@ -7,7 +7,7 @@
   
     com.github.binarywang
     wx-java
-    3.9.0
+    3.9.1.B
   
 
   weixin-java-miniapp
diff --git a/weixin-java-mp/pom.xml b/weixin-java-mp/pom.xml
index a89664d46..f1fad08a8 100644
--- a/weixin-java-mp/pom.xml
+++ b/weixin-java-mp/pom.xml
@@ -7,7 +7,7 @@
   
     com.github.binarywang
     wx-java
-    3.9.0
+    3.9.1.B
   
 
   weixin-java-mp
diff --git a/weixin-java-open/pom.xml b/weixin-java-open/pom.xml
index cf743a404..8001b4ab8 100644
--- a/weixin-java-open/pom.xml
+++ b/weixin-java-open/pom.xml
@@ -7,7 +7,7 @@
   
     com.github.binarywang
     wx-java
-    3.9.0
+    3.9.1.B
   
 
   weixin-java-open
diff --git a/weixin-java-pay/pom.xml b/weixin-java-pay/pom.xml
index 1a51f3aad..bfc41e354 100644
--- a/weixin-java-pay/pom.xml
+++ b/weixin-java-pay/pom.xml
@@ -5,7 +5,7 @@
   
     com.github.binarywang
     wx-java
-    3.9.0
+    3.9.1.B
   
   4.0.0
 

From dfec2e3aef38deb7fabf631fff40f3d22a3ccf32 Mon Sep 17 00:00:00 2001
From: Boris 
Date: Mon, 7 Sep 2020 10:52:53 +0800
Subject: [PATCH 5/5] =?UTF-8?q?:art:=20#1743=20=E4=BC=81=E4=B8=9A=E5=BE=AE?=
 =?UTF-8?q?=E4=BF=A1=E8=8E=B7=E5=8F=96=E5=AE=A2=E6=88=B7=E7=BE=A4=E8=AF=A6?=
 =?UTF-8?q?=E6=83=85=E6=8E=A5=E5=8F=A3=E5=A2=9E=E5=8A=A0unionId=E5=B1=9E?=
 =?UTF-8?q?=E6=80=A7?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

获取客户群详情对象中 增加 unionId属性
---
 .../cp/bean/external/WxCpUserExternalGroupChatInfo.java   | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalGroupChatInfo.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalGroupChatInfo.java
index 0c33309bc..83eb5c376 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalGroupChatInfo.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalGroupChatInfo.java
@@ -58,6 +58,14 @@ public class WxCpUserExternalGroupChatInfo extends WxCpBaseResp {
 
     @SerializedName("join_time")
     private Long joinTime;
+    
+    /**
+    * 外部联系人在微信开放平台的唯一身份标识(微信unionid)
+    * 通过此字段企业可将外部联系人与公众号/小程序用户关联起来
+    * 仅当群成员类型是微信用户(包括企业成员未添加好友),且企业或第三方服务商绑定了微信开发者ID有此字段
+    */
+    @SerializedName("unionid")
+    private String unionId;
 
     /**
      * 入群方式。