+ * 字段名:直连商户号 + * 变量名:mchid + * 是否必填:是 + * 类型:string[1,32] + * 描述: + * 直连商户的商户号,由微信支付生成并下发。 + * 示例值:1900000100 + *+ */ + @SerializedName(value = "mchid") + private String mchid; + + /** + * 【商家批次单号】 + * 商户系统内部的商家批次单号,在商户系统内部唯一 + */ + @SerializedName(value = "out_batch_no") + private String outBatchNo; + + /** + * 【微信批次单号】 + * 微信批次单号,微信商家转账系统返回的唯一标识 + */ + @SerializedName(value = "batch_id") + private String batchId; + + /** + * 【批次状态】 + * WAIT_PAY: 待付款确认。需要付款出资商户在商家助手小程序或服务商助手小程序进行付款确认 + * ACCEPTED:已受理。批次已受理成功,若发起批量转账的30分钟后,转账批次单仍处于该状态,可能原因是商户账户余额不足等。商户可查询账户资金流水,若该笔转账批次单的扣款已经发生,则表示批次已经进入转账中,请再次查单确认 + * PROCESSING:转账中。已开始处理批次内的转账明细单 + * FINISHED:已完成。批次内的所有转账明细单都已处理完成 + * CLOSED:已关闭。可查询具体的批次关闭原因确认 + */ + @SerializedName(value = "batch_status") + private String batchStatus; + + /** + * 【批次总笔数】 + * 转账总笔数。 + */ + @SerializedName(value = "total_num") + private Integer totalNum; + + /** + * 【批次总金额】 + * 转账总金额,单位为“分”。 + */ + @SerializedName(value = "total_amount") + private Integer totalAmount; + + /** + * 【转账成功金额】 + * 转账成功的金额,单位为“分”。当批次状态为“PROCESSING”(转账中)时,转账成功金额随时可能变化 + */ + @SerializedName(value = "success_amount") + private Integer successAmount; + + /** + * 【转账成功笔数】 + * 转账成功的笔数。当批次状态为“PROCESSING”(转账中)时,转账成功笔数随时可能变化 + */ + @SerializedName(value = "success_num") + private Integer successNum; + + /** + * 【转账失败金额】 + * 转账失败的金额,单位为“分” + */ + @SerializedName(value = "fail_amount") + private Integer failAmount; + + /** + * 【转账失败笔数】 + * 转账失败的笔数 + */ + @SerializedName(value = "fail_num") + private Integer failNum; + + /** + * 【批次关闭原因】 + * 如果批次单状态为“CLOSED”(已关闭),则有关闭原因 + * 可选取值: + * OVERDUE_CLOSE:系统超时关闭,可能原因账户余额不足或其他错误 + * TRANSFER_SCENE_INVALID:付款确认时,转账场景已不可用,系统做关单处理 + */ + @SerializedName(value = "close_reason") + private String closeReason; + + /** + * 【批次更新时间】 + * 遵循rfc3339标准格式,格式为yyyy-MM-DDTHH:mm:ss+TIMEZONE,yyyy-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss.表示时分秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。例如:2015-05-20T13:29:35+08:00表示北京时间2015年05月20日13点29分35秒。 + */ + @SerializedName(value = "update_time") + private String updateTime; + + } +} 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 9aba25d049..b73029f4e1 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 @@ -980,6 +980,17 @@ WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, Stri */ WxPayRefundNotifyV3Result parseRefundNotifyV3Result(String notifyData, SignatureHeader header) throws WxPayException; + /** + * 解析商家转账批次回调通知 + * https://pay.weixin.qq.com/docs/merchant/apis/batch-transfer-to-balance/transfer-batch-callback-notice.html + * + * @param notifyData + * @param header + * @return + * @throws WxPayException + */ + WxPayTransferBatchesNotifyV3Result parseTransferBatchesNotifyV3Result(String notifyData, SignatureHeader header) throws WxPayException; + /** * 解析服务商模式退款结果通知 * 详见https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter4_1_11.shtml 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 8466a5e91e..1187880cb6 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 @@ -432,6 +432,11 @@ public WxPayRefundNotifyV3Result parseRefundNotifyV3Result(String notifyData, Si return this.baseParseOrderNotifyV3Result(notifyData, header, WxPayRefundNotifyV3Result.class, WxPayRefundNotifyV3Result.DecryptNotifyResult.class); } + @Override + public WxPayTransferBatchesNotifyV3Result parseTransferBatchesNotifyV3Result(String notifyData, SignatureHeader header) throws WxPayException { + return this.baseParseOrderNotifyV3Result(notifyData, header, WxPayTransferBatchesNotifyV3Result.class, WxPayTransferBatchesNotifyV3Result.DecryptNotifyResult.class); + } + @Override public WxPayPartnerRefundNotifyV3Result parsePartnerRefundNotifyV3Result(String notifyData, SignatureHeader header) throws WxPayException { return this.baseParseOrderNotifyV3Result(notifyData, header, WxPayPartnerRefundNotifyV3Result.class, WxPayPartnerRefundNotifyV3Result.DecryptNotifyResult.class); 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 3990f5b61e..e777d68977 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 @@ -841,6 +841,75 @@ public String testParseRefundNotifyV3Result(HttpServletRequest request, HttpServ return WxPayNotifyV3Response.success("成功"); } + /** + * 商家转账批次回调通知 + * https://pay.weixin.qq.com/docs/merchant/apis/batch-transfer-to-balance/transfer-batch-callback-notice.html + * + * @return + * @throws Exception + */ + @Test + public String parseTransferBatchesNotifyV3Result() throws Exception { + + String body = "{\n" + + " \"id\": \"1c8192d8-aba1-5898-a79c-7d3abb72eabe\",\n" + + " \"create_time\": \"2023-08-16T16:43:27+08:00\",\n" + + " \"resource_type\": \"encrypt-resource\",\n" + + " \"event_type\": \"MCHTRANSFER.BATCH.FINISHED\",\n" + + " \"summary\": \"商家转账批次完成通知\",\n" + + " \"resource\": {\n" + + " \"original_type\": \"mch_payment\",\n" + + " \"algorithm\": \"AEAD_AES_256_GCM\",\n" + + " \"ciphertext\": \"zTBf6DDPzZSoIBkoLFkC+ho97QrqnT6UU/ADM0tJP07ITaFPek4vofQjmclLUof78NqrPcJs5OIBl+gnKKJ4xCxcDmDnZZHvev5o1pk4gwtJIFIDxbq3piDr4Wq6cZpvGPPQTYC8YoVRTdVeeN+EcuklRrmaFzv8wCTSdI9wFJ9bsxtLedhq4gpkKqN5fbSguQg9JFsX3OJeT7KPfRd6SD1gu4Lpw5gwxthfOHcYsjM/eY5gaew8zzpN6mMUEJ1HqkNuQgOguHBxFnqFPiMz+Iadw7X38Yz+IgfUkOhN1iuvMhGYKbwKJ7rTiBVvGGpF6Wse1zFKgSiTLH2RnUAMkkHmxqk+JhbQKZpSWr6O8BfhHO1OKg7hpcHZtOJKNMjIF62WYDVf36w1h8h5fg==\",\n" + + " \"associated_data\": \"mch_payment\",\n" + + " \"nonce\": \"DdF3UJVNQaKT\"\n" + + " }\n" + + "}"; + WxPayTransferBatchesNotifyV3Result transferBatchesNotifyV3Body = GSON.fromJson(body, WxPayTransferBatchesNotifyV3Result.class); + log.info(GSON.toJson(transferBatchesNotifyV3Body)); + + // 处理业务逻辑 ... + + return WxPayNotifyV3Response.success("成功"); + } + + // 测试 + public static void main(String[] args){ + String body = "{\n" + + " \"id\": \"1c8192d8-aba1-5898-a79c-7d3abb72eabe\",\n" + + " \"create_time\": \"2023-08-16T16:43:27+08:00\",\n" + + " \"resource_type\": \"encrypt-resource\",\n" + + " \"event_type\": \"MCHTRANSFER.BATCH.FINISHED\",\n" + + " \"summary\": \"商家转账批次完成通知\",\n" + + " \"resource\": {\n" + + " \"original_type\": \"mch_payment\",\n" + + " \"algorithm\": \"AEAD_AES_256_GCM\",\n" + + " \"ciphertext\": \"zTBf6DDPzZSoIBkoLFkC+ho97QrqnT6UU/ADM0tJP07ITaFPek4vofQjmclLUof78NqrPcJs5OIBl+gnKKJ4xCxcDmDnZZHvev5o1pk4gwtJIFIDxbq3piDr4Wq6cZpvGPPQTYC8YoVRTdVeeN+EcuklRrmaFzv8wCTSdI9wFJ9bsxtLedhq4gpkKqN5fbSguQg9JFsX3OJeT7KPfRd6SD1gu4Lpw5gwxthfOHcYsjM/eY5gaew8zzpN6mMUEJ1HqkNuQgOguHBxFnqFPiMz+Iadw7X38Yz+IgfUkOhN1iuvMhGYKbwKJ7rTiBVvGGpF6Wse1zFKgSiTLH2RnUAMkkHmxqk+JhbQKZpSWr6O8BfhHO1OKg7hpcHZtOJKNMjIF62WYDVf36w1h8h5fg==\",\n" + + " \"associated_data\": \"mch_payment\",\n" + + " \"nonce\": \"DdF3UJVNQaKT\"\n" + + " }\n" + + "}"; + OriginNotifyResponse transferBatchesNotifyV3Body = GSON.fromJson(body, OriginNotifyResponse.class); + log.info(GSON.toJson(transferBatchesNotifyV3Body)); + + String decryptNotifyResult = "{\n" + + " \"out_batch_no\": \"bfatestnotify000033\",\n" + + " \"batch_id\": \"131000007026709999520922023081519403795655\",\n" + + " \"batch_status\": \"FINISHED\",\n" + + " \"total_num\": 2,\n" + + " \"total_amount\": 200,\n" + + " \"success_amount\": 100,\n" + + " \"success_num\": 1,\n" + + " \"fail_amount\": 100,\n" + + " \"fail_num\": 1,\n" + + " \"mchid\": \"2483775951\",\n" + + " \"update_time\": \"2023-08-15T20:33:22+08:00\"\n" + + "}"; + WxPayTransferBatchesNotifyV3Result.DecryptNotifyResult notifyResult = GSON.fromJson(decryptNotifyResult, WxPayTransferBatchesNotifyV3Result.DecryptNotifyResult.class); + log.info(GSON.toJson(notifyResult)); + + } + @Test public void testWxPayNotifyV3Response() { System.out.println(WxPayNotifyV3Response.success("success")); From 6fc1b7ad3aed7b29c59509ceed15b760a689081f Mon Sep 17 00:00:00 2001 From: ChenJiaXin520 <30996653+ChenJiaXin520@users.noreply.github.com> Date: Wed, 15 May 2024 23:07:57 +0800 Subject: [PATCH 004/385] =?UTF-8?q?:bug:=20#3273=20=E3=80=90=E5=85=AC?= =?UTF-8?q?=E4=BC=97=E5=8F=B7=E3=80=91=E4=BF=AE=E5=A4=8D=E5=8F=91=E9=80=81?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E4=B8=8A=E4=BC=A0=E8=AF=B7=E6=B1=82=E6=97=B6?= =?UTF-8?q?Content-Type=E6=B2=A1=E6=9C=89boundary=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../material/MaterialUploadApacheHttpRequestExecutor.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialUploadApacheHttpRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialUploadApacheHttpRequestExecutor.java index 053ff16cba..2d48341489 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialUploadApacheHttpRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialUploadApacheHttpRequestExecutor.java @@ -60,7 +60,9 @@ public WxMpMaterialUploadResult execute(String uri, WxMpMaterial material, WxTyp } httpPost.setEntity(multipartEntityBuilder.build()); - httpPost.setHeader("Content-Type", ContentType.MULTIPART_FORM_DATA.toString()); + //手动设置的Content-Type请求头没有boundary,是一个非标准的文件上传请求头,虽然微信提供了对这类非标准请求的支持,但如果请求需要先经过我们的tomcat server,那么都会报错:the request was rejected because no multipart boundary was found + //不设置Content-Type请求头,httpclient将会自动设置,值为entity的getContentType方法返回值。MultipartEntityBuilder的getContentType方法将会返回boundary + //httpPost.setHeader("Content-Type", ContentType.MULTIPART_FORM_DATA.toString()); try (CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpPost)) { String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); From 1c1044e7e64944b3c926f3e1b75cf7fc898a6dda Mon Sep 17 00:00:00 2001 From: chirizcc
@@ -888,12 +889,33 @@ public interface XPay {
*
* 文档地址:https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/industry/express/business/express_sale_return.html
*
- *
*/
- public interface ExpressDeliveryReturn{
+ public interface ExpressDeliveryReturn {
String ADD_DELIVERY_RETURN_URL = "https://api.weixin.qq.com/cgi-bin/express/delivery/return/add";
String GET_DELIVERY_RETURN_URL = "https://api.weixin.qq.com/cgi-bin/express/delivery/return/get";
String UNBIND_DELIVERY_RETURN_URL = "https://api.weixin.qq.com/cgi-bin/express/delivery/return/unbind";
}
+ /**
+ * 小程序推广员
+ * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/business-capabilities/promoter/instruction/instruction.html
+ *
+ */
+ public interface Promotion {
+ String PROMOTION_ADD_ROLE = "https://api.weixin.qq.com/promoter/addrole";
+ String PROMOTION_GET_ROLE = "https://api.weixin.qq.com/promoter/getrole";
+ String PROMOTION_UPDATE_ROLE = "https://api.weixin.qq.com/promoter/updaterole";
+ String PROMOTION_ADD_PROMOTER = "https://api.weixin.qq.com/promoter/addpromoter";
+ String PROMOTION_GET_PROMOTER = "https://api.weixin.qq.com/promoter/getpromoter";
+ String PROMOTION_UPDATE_PROMOTER = "https://api.weixin.qq.com/promoter/updatepromoter";
+ String PROMOTION_GET_INVITATION_MATERIAL = "https://api.weixin.qq.com/promoter/getinvitationmaterial";
+ String PROMOTION_SEND_MSG = "https://api.weixin.qq.com/promoter/sendmsg";
+ String PROMOTION_SINGLE_SEND_MSG = "https://api.weixin.qq.com/promoter/singlesendmsg";
+ String PROMOTION_GET_MSG = "https://api.weixin.qq.com/promoter/getmsg";
+ String PROMOTION_GET_MSG_CLICK_DATA = "https://api.weixin.qq.com/promoter/getmsgclickdata";
+ String PROMOTION_GET_SHARE_MATERIAL = "https://api.weixin.qq.com/promoter/getsharematerial";
+ String PROMOTION_GET_RELATION = "https://api.weixin.qq.com/promoter/getrelation";
+ String PROMOTION_GET_ORDER = "https://api.weixin.qq.com/promoter/getorder";
+ }
+
}
diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaPromotionServiceTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaPromotionServiceTest.java
new file mode 100644
index 0000000000..11023f7da8
--- /dev/null
+++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaPromotionServiceTest.java
@@ -0,0 +1,204 @@
+package cn.binarywang.wx.miniapp.api.impl;
+
+import cn.binarywang.wx.miniapp.api.WxMaService;
+import cn.binarywang.wx.miniapp.bean.promoter.request.*;
+import com.google.inject.Inject;
+import me.chanjar.weixin.common.error.WxErrorException;
+import org.testng.annotations.Test;
+
+import java.util.Collections;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class WxMaPromotionServiceTest {
+
+ @Inject
+ private WxMaService wxService;
+
+ @Test
+ public void testAddRole() throws WxErrorException {
+ WxMaPromotionAddRoleRequest request = WxMaPromotionAddRoleRequest.builder()
+ .name("推广员1号名字")
+ .desc("推广员1号描述")
+ .build();
+ var response = wxService.getWxMaPromotionService().addRole(request);
+ assertThat(response).isNotNull();
+ }
+
+ @Test
+ public void testGetRole() throws WxErrorException {
+ WxMaPromotionGetRoleRequest request = WxMaPromotionGetRoleRequest.builder()
+ .roleId(1L)
+ .build();
+ var response = wxService.getWxMaPromotionService().getRole(request);
+ assertThat(response).isNotNull();
+ }
+
+ @Test
+ public void testUpdateRole() throws WxErrorException {
+ WxMaPromoterUpdateRoleRequest request = WxMaPromoterUpdateRoleRequest.builder()
+ .roleId(1L)
+ .name("推广员1号名字")
+ .desc("推广员1号描述")
+ .build();
+ var response = wxService.getWxMaPromotionService().updateRole(request);
+ assertThat(response).isNotNull();
+ }
+
+ @Test
+ public void testAddPromoter() throws WxErrorException {
+ WxMaPromotionAddPromoterRequest.Promoter promoter = WxMaPromotionAddPromoterRequest.Promoter.builder()
+ .phone("15600000000")
+ .openid("")
+ .extraInfo("{}")
+ .retailId("1")
+ .roleId(1L)
+ .name("15600000000")
+ .build();
+
+ WxMaPromotionAddPromoterRequest request = WxMaPromotionAddPromoterRequest.builder()
+ .promoterList(Collections.singletonList(promoter))
+ .build();
+ var response = wxService.getWxMaPromotionService().addPromoter(request);
+ assertThat(response).isNotNull();
+ }
+
+ @Test
+ public void testGetPromoter() throws WxErrorException {
+ WxMaPromotionGetPromoterRequest request = WxMaPromotionGetPromoterRequest.builder()
+ .openid("")
+ .roleId(1L)
+ .retailId("")
+ .beginTime(1715938250L)
+ .endTime(1715938250L)
+ .startId("")
+ .needUnionid(null)
+ .authStatus(null)
+ .declStatus("1")
+ .build();
+ var response = wxService.getWxMaPromotionService().getPromoter(request);
+ assertThat(response).isNotNull();
+ }
+
+ @Test
+ public void testUpdatePromoter() throws WxErrorException {
+ WxMaPromotionUpdatePromoterRequest request = WxMaPromotionUpdatePromoterRequest.builder()
+ .id("")
+ .roleId(1L)
+ .retailId("")
+ .extraInfo("{}")
+ .name("15600000000")
+ .phone("15600000000")
+ .declStatus("1")
+ .build();
+ var response = wxService.getWxMaPromotionService().updatePromoter(request);
+ assertThat(response).isNotNull();
+ }
+
+ @Test
+ public void testGetInvitationMaterial() throws WxErrorException {
+ WxMaPromotionGetInvitationMaterialRequest request = WxMaPromotionGetInvitationMaterialRequest.builder()
+ .roleId(1L)
+ .invitationType(0L)
+ .build();
+ var response = wxService.getWxMaPromotionService().getInvitationMaterial(request);
+ assertThat(response).isNotNull();
+ }
+
+ @Test
+ public void testSendMsg() throws WxErrorException {
+ WxMaPromotionSendMsgRequest request = WxMaPromotionSendMsgRequest.builder()
+ .msgType(1)
+ .content("{}")
+ .appid("")
+ .path("")
+ .listType(0L)
+ .roleId(null)
+ .retailId(null)
+ .id(null)
+ .build();
+ var response = wxService.getWxMaPromotionService().sendMsg(request);
+ assertThat(response).isNotNull();
+ }
+
+ @Test
+ public void testSingleSendMsg() throws WxErrorException {
+ WxMaPromotionSingleSendMsgRequest request = WxMaPromotionSingleSendMsgRequest.builder()
+ .msgType(1)
+ .content("{}")
+ .appid("")
+ .path("")
+ .openid("")
+ .build();
+ var response = wxService.getWxMaPromotionService().singleSendMsg(request);
+ assertThat(response).isNotNull();
+ }
+
+ @Test
+ public void testGetMsg() throws WxErrorException {
+ WxMaPromotionGetMsgRequest request = WxMaPromotionGetMsgRequest.builder()
+ .msgId("")
+ .build();
+ var response = wxService.getWxMaPromotionService().getMsg(request);
+ assertThat(response).isNotNull();
+ }
+
+ @Test
+ public void testGetMsgClickData() throws WxErrorException {
+ WxMaPromotionGetMsgClickDataRequest request = WxMaPromotionGetMsgClickDataRequest.builder()
+ .sendDate("2024-05-17")
+ .dimonsion(0L)
+ .msgType(1)
+ .msgId("")
+ .beginSendTime(1715938250L)
+ .endSendTime(1715938250L)
+ .build();
+ var response = wxService.getWxMaPromotionService().getMsgClickData(request);
+ assertThat(response).isNotNull();
+ }
+
+ @Test
+ public void testGetShareMaterial() throws WxErrorException {
+ WxMaPromotionGetShareMaterialRequest request = WxMaPromotionGetShareMaterialRequest.builder()
+ .path("")
+ .openid("")
+ .extraInfo("{}")
+ .title("")
+ .shareType(0L)
+ .envVersion("")
+ .build();
+ var response = wxService.getWxMaPromotionService().getShareMaterial(request);
+ assertThat(response).isNotNull();
+ }
+
+ @Test
+ public void testGetRelation() throws WxErrorException {
+ WxMaPromotionGetRelationRequest request = WxMaPromotionGetRelationRequest.builder()
+ .openid("")
+ .beginTime(1715938250L)
+ .endTime(1715938250L)
+ .scene(0L)
+ .path("")
+ .startId("")
+ .needUnionid(0L)
+ .build();
+ var response = wxService.getWxMaPromotionService().getRelation(request);
+ assertThat(response).isNotNull();
+ }
+
+ @Test
+ public void testGetOrder() throws WxErrorException {
+ WxMaPromotionGetOrderRequest request = WxMaPromotionGetOrderRequest.builder()
+ .openid("")
+ .mchId("")
+ .tradeNo("")
+ .outTradeNo("")
+ .status(0L)
+ .startId("")
+ .needUnionid(0L)
+ .date(1715938250L)
+ .build();
+ var response = wxService.getWxMaPromotionService().getOrder(request);
+ assertThat(response).isNotNull();
+ }
+}
From 08c730e3692488c1bbf49cbcf0817b025aad412e Mon Sep 17 00:00:00 2001
From: zhuangzibin <53577469+zhuangzibin@users.noreply.github.com>
Date: Mon, 20 May 2024 16:58:44 +0800
Subject: [PATCH 008/385] :art: fix var to specific class name
---
.../api/impl/WxMaPromotionServiceTest.java | 29 ++++++++++---------
1 file changed, 15 insertions(+), 14 deletions(-)
diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaPromotionServiceTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaPromotionServiceTest.java
index 11023f7da8..89f4b43b5f 100644
--- a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaPromotionServiceTest.java
+++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaPromotionServiceTest.java
@@ -2,6 +2,7 @@
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.promoter.request.*;
+import cn.binarywang.wx.miniapp.bean.promoter.response.*;
import com.google.inject.Inject;
import me.chanjar.weixin.common.error.WxErrorException;
import org.testng.annotations.Test;
@@ -21,7 +22,7 @@ public void testAddRole() throws WxErrorException {
.name("推广员1号名字")
.desc("推广员1号描述")
.build();
- var response = wxService.getWxMaPromotionService().addRole(request);
+ WxMaPromotionAddRoleResponse response = wxService.getWxMaPromotionService().addRole(request);
assertThat(response).isNotNull();
}
@@ -30,7 +31,7 @@ public void testGetRole() throws WxErrorException {
WxMaPromotionGetRoleRequest request = WxMaPromotionGetRoleRequest.builder()
.roleId(1L)
.build();
- var response = wxService.getWxMaPromotionService().getRole(request);
+ WxMaPromotionGetRoleResponse response = wxService.getWxMaPromotionService().getRole(request);
assertThat(response).isNotNull();
}
@@ -41,7 +42,7 @@ public void testUpdateRole() throws WxErrorException {
.name("推广员1号名字")
.desc("推广员1号描述")
.build();
- var response = wxService.getWxMaPromotionService().updateRole(request);
+ WxMaPromotionUpdateRoleResponse response = wxService.getWxMaPromotionService().updateRole(request);
assertThat(response).isNotNull();
}
@@ -59,7 +60,7 @@ public void testAddPromoter() throws WxErrorException {
WxMaPromotionAddPromoterRequest request = WxMaPromotionAddPromoterRequest.builder()
.promoterList(Collections.singletonList(promoter))
.build();
- var response = wxService.getWxMaPromotionService().addPromoter(request);
+ WxMaPromotionAddPromoterResponse response = wxService.getWxMaPromotionService().addPromoter(request);
assertThat(response).isNotNull();
}
@@ -76,7 +77,7 @@ public void testGetPromoter() throws WxErrorException {
.authStatus(null)
.declStatus("1")
.build();
- var response = wxService.getWxMaPromotionService().getPromoter(request);
+ WxMaPromotionGetPromoterResponse response = wxService.getWxMaPromotionService().getPromoter(request);
assertThat(response).isNotNull();
}
@@ -91,7 +92,7 @@ public void testUpdatePromoter() throws WxErrorException {
.phone("15600000000")
.declStatus("1")
.build();
- var response = wxService.getWxMaPromotionService().updatePromoter(request);
+ WxMaPromotionUpdatePromoterResponse response = wxService.getWxMaPromotionService().updatePromoter(request);
assertThat(response).isNotNull();
}
@@ -101,7 +102,7 @@ public void testGetInvitationMaterial() throws WxErrorException {
.roleId(1L)
.invitationType(0L)
.build();
- var response = wxService.getWxMaPromotionService().getInvitationMaterial(request);
+ WxMaPromotionGetInvitationMaterialResponse response = wxService.getWxMaPromotionService().getInvitationMaterial(request);
assertThat(response).isNotNull();
}
@@ -117,7 +118,7 @@ public void testSendMsg() throws WxErrorException {
.retailId(null)
.id(null)
.build();
- var response = wxService.getWxMaPromotionService().sendMsg(request);
+ WxMaPromotionSendMsgResponse response = wxService.getWxMaPromotionService().sendMsg(request);
assertThat(response).isNotNull();
}
@@ -130,7 +131,7 @@ public void testSingleSendMsg() throws WxErrorException {
.path("")
.openid("")
.build();
- var response = wxService.getWxMaPromotionService().singleSendMsg(request);
+ WxMaPromotionSingleSendMsgResponse response = wxService.getWxMaPromotionService().singleSendMsg(request);
assertThat(response).isNotNull();
}
@@ -139,7 +140,7 @@ public void testGetMsg() throws WxErrorException {
WxMaPromotionGetMsgRequest request = WxMaPromotionGetMsgRequest.builder()
.msgId("")
.build();
- var response = wxService.getWxMaPromotionService().getMsg(request);
+ WxMaPromotionGetMsgResponse response = wxService.getWxMaPromotionService().getMsg(request);
assertThat(response).isNotNull();
}
@@ -153,7 +154,7 @@ public void testGetMsgClickData() throws WxErrorException {
.beginSendTime(1715938250L)
.endSendTime(1715938250L)
.build();
- var response = wxService.getWxMaPromotionService().getMsgClickData(request);
+ WxMaPromotionGetMsgClickDataResponse response = wxService.getWxMaPromotionService().getMsgClickData(request);
assertThat(response).isNotNull();
}
@@ -167,7 +168,7 @@ public void testGetShareMaterial() throws WxErrorException {
.shareType(0L)
.envVersion("")
.build();
- var response = wxService.getWxMaPromotionService().getShareMaterial(request);
+ WxMaPromotionGetShareMaterialResponse response = wxService.getWxMaPromotionService().getShareMaterial(request);
assertThat(response).isNotNull();
}
@@ -182,7 +183,7 @@ public void testGetRelation() throws WxErrorException {
.startId("")
.needUnionid(0L)
.build();
- var response = wxService.getWxMaPromotionService().getRelation(request);
+ WxMaPromotionGetRelationResponse response = wxService.getWxMaPromotionService().getRelation(request);
assertThat(response).isNotNull();
}
@@ -198,7 +199,7 @@ public void testGetOrder() throws WxErrorException {
.needUnionid(0L)
.date(1715938250L)
.build();
- var response = wxService.getWxMaPromotionService().getOrder(request);
+ WxMaPromotionGetOrderResponse response = wxService.getWxMaPromotionService().getOrder(request);
assertThat(response).isNotNull();
}
}
From 212c461acdd8a588e30960f0edebdfc1d59ebd99 Mon Sep 17 00:00:00 2001
From: The-blind <1043193478@qq.com>
Date: Mon, 3 Jun 2024 09:12:51 +0000
Subject: [PATCH 009/385] =?UTF-8?q?:memo:=20=E5=A2=9E=E5=8A=A0=E5=8F=8B?=
=?UTF-8?q?=E6=83=85=E9=93=BE=E6=8E=A5=E6=BA=90=E9=9B=80SCRM?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index f12b6e7cff..8479f64a00 100644
--- a/README.md
+++ b/README.md
@@ -40,8 +40,8 @@
-
-
+
+
From 2f5e8b988c1e0594e186bee67ab27f8f7183867c Mon Sep 17 00:00:00 2001
From: Aaron
Date: Mon, 3 Jun 2024 09:17:10 +0000
Subject: [PATCH 010/385] =?UTF-8?q?:bug:=20=E4=BF=AE=E5=A4=8D=20context=20?=
=?UTF-8?q?=E6=9C=AA=E4=BD=BF=E7=94=A8=20bug?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../me/chanjar/weixin/cp/tp/message/WxCpTpMessageRouter.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/message/WxCpTpMessageRouter.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/message/WxCpTpMessageRouter.java
index 99f7c85d9a..1df52149c8 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/message/WxCpTpMessageRouter.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/message/WxCpTpMessageRouter.java
@@ -265,7 +265,7 @@ public WxCpXmlOutMessage route(final String suiteId, final WxCpTpXmlMessage wxMe
* @return the wx cp xml out message
*/
public WxCpXmlOutMessage route(final WxCpTpXmlMessage wxMessage, final Map context) {
- return this.route(null, wxMessage, new HashMap<>(2));
+ return this.route(null, wxMessage, context);
}
/**
From 17710af4de612502ff3f4d767ffbbda54c870216 Mon Sep 17 00:00:00 2001
From: ChenJiaXin520 <30996653+ChenJiaXin520@users.noreply.github.com>
Date: Tue, 21 May 2024 18:16:42 +0800
Subject: [PATCH 011/385] =?UTF-8?q?:art:=20=E4=BF=AE=E5=A4=8D=E5=8F=91?=
=?UTF-8?q?=E9=80=81=E6=96=87=E4=BB=B6=E4=B8=8A=E4=BC=A0=E8=AF=B7=E6=B1=82?=
=?UTF-8?q?=E6=97=B6Content-Type=E6=B2=A1=E6=9C=89boundary=E7=9A=84?=
=?UTF-8?q?=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../material/MaterialUploadApacheHttpRequestExecutor.java | 4 ----
.../media/MediaImgUploadApacheHttpRequestExecutor.java | 1 -
.../voice/VoiceUploadApacheHttpRequestExecutor.java | 1 -
3 files changed, 6 deletions(-)
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialUploadApacheHttpRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialUploadApacheHttpRequestExecutor.java
index 2d48341489..6a31484420 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialUploadApacheHttpRequestExecutor.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialUploadApacheHttpRequestExecutor.java
@@ -58,11 +58,7 @@ public WxMpMaterialUploadResult execute(String uri, WxMpMaterial material, WxTyp
multipartEntityBuilder.addPart("description",
new StringBody(WxGsonBuilder.create().toJson(form), ContentType.create("text/plain", Consts.UTF_8)));
}
-
httpPost.setEntity(multipartEntityBuilder.build());
- //手动设置的Content-Type请求头没有boundary,是一个非标准的文件上传请求头,虽然微信提供了对这类非标准请求的支持,但如果请求需要先经过我们的tomcat server,那么都会报错:the request was rejected because no multipart boundary was found
- //不设置Content-Type请求头,httpclient将会自动设置,值为entity的getContentType方法返回值。MultipartEntityBuilder的getContentType方法将会返回boundary
- //httpPost.setHeader("Content-Type", ContentType.MULTIPART_FORM_DATA.toString());
try (CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpPost)) {
String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response);
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/media/MediaImgUploadApacheHttpRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/media/MediaImgUploadApacheHttpRequestExecutor.java
index b570a1c43b..7c4ba18598 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/media/MediaImgUploadApacheHttpRequestExecutor.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/media/MediaImgUploadApacheHttpRequestExecutor.java
@@ -47,7 +47,6 @@ public WxMediaImgUploadResult execute(String uri, File data, WxType wxType) thro
.setMode(HttpMultipartMode.RFC6532)
.build();
httpPost.setEntity(entity);
- httpPost.setHeader("Content-Type", ContentType.MULTIPART_FORM_DATA.toString());
try (CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpPost)) {
String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response);
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/voice/VoiceUploadApacheHttpRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/voice/VoiceUploadApacheHttpRequestExecutor.java
index 07af44b340..06aa1fcda1 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/voice/VoiceUploadApacheHttpRequestExecutor.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/voice/VoiceUploadApacheHttpRequestExecutor.java
@@ -48,7 +48,6 @@ public Boolean execute(String uri, File data, WxType wxType) throws WxErrorExcep
.setMode(HttpMultipartMode.RFC6532)
.build();
httpPost.setEntity(entity);
- httpPost.setHeader("Content-Type", ContentType.MULTIPART_FORM_DATA.toString());
try (CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpPost)) {
String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response);
From 43f31f46921b25582172e8abeb958d55f60c6772 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?H=E9=BB=84=E5=B0=8F=E9=A3=9EF?= <505860922@qq.com>
Date: Tue, 11 Jun 2024 06:17:52 +0000
Subject: [PATCH 012/385] =?UTF-8?q?:art:=20=E3=80=90=E8=A7=86=E9=A2=91?=
=?UTF-8?q?=E5=8F=B7=E3=80=91=E5=A2=9E=E5=8A=A0=E5=88=86=E4=BA=AB=E5=91=98?=
=?UTF-8?q?=E5=9B=9E=E8=B0=83=E7=9A=84=E7=9B=B8=E5=85=B3=E6=96=B9=E6=B3=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../api/BaseWxChannelMessageService.java | 13 +++++
.../impl/BaseWxChannelMessageServiceImpl.java | 9 ++++
.../message/sharer/SharerChangeMessage.java | 47 +++++++++++++++++++
.../constant/MessageEventConstants.java | 4 ++
4 files changed, 73 insertions(+)
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/message/sharer/SharerChangeMessage.java
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/BaseWxChannelMessageService.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/BaseWxChannelMessageService.java
index 5a1ecce581..a4a2c4240e 100644
--- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/BaseWxChannelMessageService.java
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/BaseWxChannelMessageService.java
@@ -433,4 +433,17 @@ void vipScoreExchange(ExchangeInfoMessage message, final String content, final S
*/
Object defaultMessageHandler(WxChannelMessage message, final String content, final String appId,
final Map context, final WxSessionManager sessionManager);
+
+
+ /**
+ * 分享员变更
+ *
+ * @param message the message
+ * @param content the content
+ * @param appId the app id
+ * @param context the context
+ * @param sessionManager the session manager
+ */
+ void sharerChange(WxChannelMessage message, final String content, final String appId,
+ final Map context, final WxSessionManager sessionManager);
}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/impl/BaseWxChannelMessageServiceImpl.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/impl/BaseWxChannelMessageServiceImpl.java
index 2cc75d0de1..46837deba8 100644
--- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/impl/BaseWxChannelMessageServiceImpl.java
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/impl/BaseWxChannelMessageServiceImpl.java
@@ -23,6 +23,7 @@
import me.chanjar.weixin.channel.bean.message.product.BrandMessage;
import me.chanjar.weixin.channel.bean.message.product.CategoryAuditMessage;
import me.chanjar.weixin.channel.bean.message.product.SpuAuditMessage;
+import me.chanjar.weixin.channel.bean.message.sharer.SharerChangeMessage;
import me.chanjar.weixin.channel.bean.message.supplier.SupplierItemMessage;
import me.chanjar.weixin.channel.bean.message.vip.ExchangeInfoMessage;
import me.chanjar.weixin.channel.bean.message.vip.UserInfoMessage;
@@ -121,6 +122,10 @@ protected void addDefaultRule() {
this.addRule(UserInfoMessage.class, USER_VIP_SCORE_UPDATE, false, this::vipScoreUpdate);
/* 用户积分兑换 */
this.addRule(ExchangeInfoMessage.class, USER_VIP_SCORE_EXCHANGE, false, this::vipScoreExchange);
+
+
+ /* 分享员变更 */
+ this.addRule(SharerChangeMessage.class,SHARER_CHANGE,false,this::sharerChange);
}
/**
@@ -333,6 +338,10 @@ public Object defaultMessageHandler(WxChannelMessage message, String content, St
return null;
}
+ @Override
+ public void sharerChange(WxChannelMessage message, String content, String appId, Map context, WxSessionManager sessionManager) {
+ log.info("分享员变更:{}", JsonUtils.encode(message));
+ }
@Override
public abstract void vipJoin(UserInfoMessage message, String content, String appId,
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/message/sharer/SharerChangeMessage.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/message/sharer/SharerChangeMessage.java
new file mode 100644
index 0000000000..ac997aeb47
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/message/sharer/SharerChangeMessage.java
@@ -0,0 +1,47 @@
+package me.chanjar.weixin.channel.bean.message.sharer;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import me.chanjar.weixin.channel.message.WxChannelMessage;
+
+/**
+ * 分享员变更消息
+ * https://developers.weixin.qq.com/doc/channels/API/sharer/callback/channels_ec_sharer_change.html
+ *
+ * @author sd-hxf
+ */
+@Data
+@NoArgsConstructor
+@EqualsAndHashCode(callSuper = true)
+@JacksonXmlRootElement(localName = "xml")
+public class SharerChangeMessage extends WxChannelMessage {
+
+ private static final long serialVersionUID = 4219477394934480421L;
+
+ /**
+ * 分享员OpenID
+ */
+ @JsonProperty("openid")
+ @JacksonXmlProperty(localName = "openid")
+ private String openid;
+
+ /**
+ * 分享员类型:0-普通分享员,1-店铺分享员
+ */
+ @JsonProperty("sharer_type")
+ @JacksonXmlProperty(localName = "sharer_type")
+ private Integer sharerType;
+
+ /**
+ * 分享员绑定状态:1-绑定,2-解绑
+ */
+ @JsonProperty("bind_status")
+ @JacksonXmlProperty(localName = "bind_status")
+ private String bindStatus;
+
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/constant/MessageEventConstants.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/constant/MessageEventConstants.java
index f1e445513c..48cf268b06 100644
--- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/constant/MessageEventConstants.java
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/constant/MessageEventConstants.java
@@ -79,4 +79,8 @@ public interface MessageEventConstants {
String USER_VIP_SCORE_UPDATE = "channels_ec_vip_score_update";
/** 用户积分兑换 */
String USER_VIP_SCORE_EXCHANGE = "channels_ec_vip_score_exchange";
+
+ // 分享员相关
+ /** 分享员变更 **/
+ String SHARER_CHANGE = "channels_ec_sharer_change";
}
From f91fc831a41c685a3cfc46b75ebf802875209407 Mon Sep 17 00:00:00 2001
From: willowHB <42429887+willowHB@users.noreply.github.com>
Date: Tue, 11 Jun 2024 14:23:04 +0800
Subject: [PATCH 013/385] =?UTF-8?q?:new:=20#3294=E3=80=90=E5=B0=8F?=
=?UTF-8?q?=E7=A8=8B=E5=BA=8F=E3=80=91=E5=BE=AE=E4=BF=A1=E7=89=A9=E6=B5=81?=
=?UTF-8?q?=E6=9C=8D=E5=8A=A1-=E6=B6=88=E6=81=AF=E7=BB=84=E4=BB=B6?=
=?UTF-8?q?=E4=BC=A0=E8=BF=90=E5=8D=95=E6=8E=A5=E5=8F=A3=E5=A2=9E=E5=8A=A0?=
=?UTF-8?q?=E5=8F=82=E6=95=B0=E8=BF=90=E5=8A=9Bid?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../wx/miniapp/bean/delivery/FollowWaybillRequest.java | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/FollowWaybillRequest.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/FollowWaybillRequest.java
index 92add53aa2..5bb796e0b2 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/FollowWaybillRequest.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/FollowWaybillRequest.java
@@ -60,6 +60,16 @@ public class FollowWaybillRequest implements Serializable {
@SerializedName("receiver_phone")
private String receiverPhone;
+ /**
+ * 运力id(运单号所属运力公司id),该字段从 get_delivery_list 获取。
+ *
+ * 是否必填: 否
+ * 描述:该参数用于提高运单号识别的准确度;特别是对非主流快递公司,建议传入该参数,确保查询正确率。
+ *
+ */
+ @SerializedName("delivery_id")
+ private String deliveryId;
+
/**
* 运单ID
*
From 0fe41444c2626c999ebca2c8b83fde8f19b62e75 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?H=E9=BB=84=E5=B0=8F=E9=A3=9EF?= <505860922@qq.com>
Date: Wed, 12 Jun 2024 14:43:02 +0000
Subject: [PATCH 014/385] =?UTF-8?q?:art:=20=E4=BF=AE=E6=94=B9bindStatus?=
=?UTF-8?q?=E4=B8=BAInteger=E7=B1=BB=E5=9E=8B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../channel/bean/message/sharer/SharerChangeMessage.java | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/message/sharer/SharerChangeMessage.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/message/sharer/SharerChangeMessage.java
index ac997aeb47..8b2036693e 100644
--- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/message/sharer/SharerChangeMessage.java
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/message/sharer/SharerChangeMessage.java
@@ -41,7 +41,8 @@ public class SharerChangeMessage extends WxChannelMessage {
*/
@JsonProperty("bind_status")
@JacksonXmlProperty(localName = "bind_status")
- private String bindStatus;
+ private Integer bindStatus;
+
}
From 0e417ed25deb64305275fc1abbcf03f9eeb716d3 Mon Sep 17 00:00:00 2001
From: Binary Wang
Date: Thu, 13 Jun 2024 11:23:03 +0800
Subject: [PATCH 015/385] : memo: add hellogithub badge
---
README.md | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 8479f64a00..3654f2cd80 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,6 @@
[](https://opensource.org/licenses/Apache-2.0)
#### 微信`Java`开发工具包,支持包括微信支付、开放平台、公众号、企业微信、视频号、小程序等微信功能模块的后端开发。
-
特别赞助
@@ -45,6 +44,14 @@
+
+
+
+
+
+
+
### 重要信息
From b7cbba14e92d54fc6fe98ed831c3ed401d3b97f8 Mon Sep 17 00:00:00 2001
From: foreveryang321 <453190450@qq.com>
Date: Thu, 13 Jun 2024 19:34:03 +0800
Subject: [PATCH 016/385] =?UTF-8?q?:memo:=20=E4=BF=AE=E6=AD=A3=20wx-java-m?=
=?UTF-8?q?p-multi-spring-boot-starter=20=E4=BD=BF=E7=94=A8=E6=96=87?=
=?UTF-8?q?=E6=A1=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../README.md | 22 +++++++++----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/README.md b/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/README.md
index 7796796bb8..8c8771beca 100644
--- a/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/README.md
+++ b/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/README.md
@@ -1,4 +1,4 @@
-# wx-java-mp-spring-boot-starter
+# wx-java-mp-multi-spring-boot-starter
## 快速开始
@@ -14,19 +14,19 @@
```properties
# 公众号配置
## 应用 1 配置(必填)
- wx.mp.tenantId1.app-id=appId
- wx.mp.tenantId1.app-secret=@secret
+ wx.mp.apps.tenantId1.app-id=appId
+ wx.mp.apps.tenantId1.app-secret=@secret
## 选填
- wx.mp.tenantId1.token=@token
- wx.mp.tenantId1.aes-key=@aesKey
- wx.mp.tenantId1.use-stable-access-token=@useStableAccessToken
+ wx.mp.apps.tenantId1.token=@token
+ wx.mp.apps.tenantId1.aes-key=@aesKey
+ wx.mp.apps.tenantId1.use-stable-access-token=@useStableAccessToken
## 应用 2 配置(必填)
- wx.mp.tenantId2.app-id=@appId
- wx.mp.tenantId2.app-secret =@secret
+ wx.mp.apps.tenantId2.app-id=@appId
+ wx.mp.apps.tenantId2.app-secret =@secret
## 选填
- wx.mp.tenantId2.token=@token
- wx.mp.tenantId2.aes-key=@aesKey
- wx.mp.tenantId2.use-stable-access-token=@useStableAccessToken
+ wx.mp.apps.tenantId2.token=@token
+ wx.mp.apps.tenantId2.aes-key=@aesKey
+ wx.mp.apps.tenantId2.use-stable-access-token=@useStableAccessToken
# ConfigStorage 配置(选填)
## 配置类型: memory(默认), jedis, redisson, redis_template
From 364f1cac07d19133910b41009927d2224a3dc37d Mon Sep 17 00:00:00 2001
From: zhangruhong
Date: Sun, 16 Jun 2024 05:49:19 +0800
Subject: [PATCH 017/385] =?UTF-8?q?:art:=20=E5=A2=9E=E5=8A=A0=E4=BB=A3?=
=?UTF-8?q?=E7=A0=81=E5=AE=A1=E6=A0=B8=E7=BB=93=E6=9E=9C=E6=8E=A8=E9=80=81?=
=?UTF-8?q?=E4=BA=8B=E4=BB=B6=E7=B1=BB=E5=9E=8B=E5=B8=B8=E9=87=8F-?=
=?UTF-8?q?=E5=AE=A1=E6=A0=B8=E5=BB=B6=E5=90=8E?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../main/java/me/chanjar/weixin/common/api/WxConsts.java | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java
index 14f8424f20..f32bdbe839 100644
--- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java
+++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java
@@ -431,6 +431,12 @@ public static class EventType {
*/
public static final String WEAPP_AUDIT_FAIL = "weapp_audit_fail";
+
+ /**
+ * 小程序审核事件:审核延后
+ */
+ public static final String WEAPP_AUDIT_DELAY = "weapp_audit_delay";
+
/**
* 小程序自定义交易组件支付通知
*/
From 7bd65465a2976dfc07a8641661980911bf85b4b6 Mon Sep 17 00:00:00 2001
From: willowHB <42429887+willowHB@users.noreply.github.com>
Date: Sun, 16 Jun 2024 05:51:14 +0800
Subject: [PATCH 018/385] =?UTF-8?q?=20:art:=20#3308=20=E3=80=90=E5=B0=8F?=
=?UTF-8?q?=E7=A8=8B=E5=BA=8F=E3=80=91=E6=9F=A5=E8=BF=90=E5=8D=95=E6=8E=A5?=
=?UTF-8?q?=E5=8F=A3=E6=8E=A5=E5=8F=A3=E5=93=8D=E5=BA=94=E4=B8=AD=E8=BF=90?=
=?UTF-8?q?=E5=8D=95=E4=BF=A1=E6=81=AF=E5=AD=97=E6=AE=B5=E5=8F=98=E6=9B=B4?=
=?UTF-8?q?(waybill=5Ftoken=20->=20waybill=5Fid)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../wx/miniapp/bean/delivery/FollowWaybillRequest.java | 4 ++--
.../wx/miniapp/bean/delivery/QueryFollowTraceResponse.java | 6 +++---
.../wx/miniapp/bean/delivery/QueryWaybillTraceResponse.java | 6 +++---
3 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/FollowWaybillRequest.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/FollowWaybillRequest.java
index 5bb796e0b2..78122bf26b 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/FollowWaybillRequest.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/FollowWaybillRequest.java
@@ -12,7 +12,7 @@
/**
*
- * 传运单接口 follow_waybil
+ * 传运单接口 follow_waybill
*
* 商户使用此接口向微信提供某交易单号对应的运单号。微信后台会跟踪运单的状态变化,在关键物流节点给下单用户推送消息通知。
*
@@ -53,7 +53,7 @@ public class FollowWaybillRequest implements Serializable {
/**
* 收件人手机号
*
- * 是否必填: 否
+ * 是否必填: 是
* 描述:部分运力需要用户手机号作为查单依据
*
*/
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/QueryFollowTraceResponse.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/QueryFollowTraceResponse.java
index a6e698d0df..740ea2999e 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/QueryFollowTraceResponse.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/QueryFollowTraceResponse.java
@@ -73,10 +73,10 @@ public static class WaybillInfo implements Serializable {
private Integer status;
/**
- * 查询id.
+ * 运单号.
*/
- @SerializedName("waybill_token")
- private String waybillToken;
+ @SerializedName("waybill_id")
+ private String waybillId;
}
/**
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/QueryWaybillTraceResponse.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/QueryWaybillTraceResponse.java
index 726705cf8d..5cba13a5cc 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/QueryWaybillTraceResponse.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/delivery/QueryWaybillTraceResponse.java
@@ -73,10 +73,10 @@ public static class WaybillInfo implements Serializable {
private Integer status;
/**
- * 查询id.
+ * 运单号.
*/
- @SerializedName("waybill_token")
- private String waybillToken;
+ @SerializedName("waybill_id")
+ private String waybillId;
}
/**
From e6d0161ae4002aea85929701b253fa38f392e9cf Mon Sep 17 00:00:00 2001
From: zhangruhong
Date: Sun, 16 Jun 2024 05:52:03 +0800
Subject: [PATCH 019/385] =?UTF-8?q?:art:=20=E8=A1=A5=E5=85=85=E5=AE=8C?=
=?UTF-8?q?=E5=96=84=E5=B0=8F=E7=A8=8B=E5=BA=8F=E6=B6=88=E6=81=AF=E4=BA=8B?=
=?UTF-8?q?=E4=BB=B6=E6=8E=A8=E9=80=81=E7=B1=BB=E5=9E=8B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../chanjar/weixin/common/api/WxConsts.java | 31 ++++++++++++++++++-
1 file changed, 30 insertions(+), 1 deletion(-)
diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java
index f32bdbe839..60aeb1c427 100644
--- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java
+++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java
@@ -460,7 +460,36 @@ public static class EventType {
* 订阅通知事件:发送订阅通知回调
*/
public static final String SUBSCRIBE_MSG_SENT_EVENT = "subscribe_msg_sent_event";
- }
+
+ /**
+ * 名称审核事件
+ */
+ public static final String WXA_NICKNAME_AUDIT = "wxa_nickname_audit" ;
+ /**
+ *小程序违规记录事件
+ */
+ public static final String WXA_ILLEGAL_RECORD= "wxa_illegal_record";
+ /**
+ *小程序申诉记录推送
+ */
+ public static final String WXA_APPEAL_RECORD= "wxa_appeal_record";
+ /**
+ * 隐私权限审核结果推送
+ */
+ public static final String WXA_PRIVACY_APPLY= "wxa_privacy_apply";
+ /**
+ * 类目审核结果事件推送
+ */
+ public static final String WXA_CATEGORY_AUDIT= "wxa_category_audit";
+ /**
+ * 小程序微信认证支付成功事件
+ */
+ public static final String WX_VERIFY_PAY_SUCC= "wx_verify_pay_succ";
+ /**
+ * 小程序微信认证派单事件
+ */
+ public static final String WX_VERIFY_DISPATCH= "wx_verify_dispatch";
+ }
/**
* 上传多媒体(临时素材)文件的类型.
From 00e797381818e88b3f65f0b71eb9877301eb84c8 Mon Sep 17 00:00:00 2001
From: Pei Yu <125331682@qq.com>
Date: Sun, 16 Jun 2024 05:53:01 +0800
Subject: [PATCH 020/385] =?UTF-8?q?:art:=20=E3=80=90=E5=BE=AE=E4=BF=A1?=
=?UTF-8?q?=E6=94=AF=E4=BB=98=E3=80=91spring-boot-starter=E6=A8=A1?=
=?UTF-8?q?=E5=9D=97=E5=A2=9E=E5=8A=A0=E6=B5=8B=E8=AF=95=E6=B2=99=E7=9B=92?=
=?UTF-8?q?=E5=BC=80=E5=85=B3=E9=85=8D=E7=BD=AE=E9=A1=B9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../starter/wxjava/pay/config/WxPayAutoConfiguration.java | 1 +
.../starter/wxjava/pay/properties/WxPayProperties.java | 6 ++++++
2 files changed, 7 insertions(+)
diff --git a/spring-boot-starters/wx-java-pay-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/pay/config/WxPayAutoConfiguration.java b/spring-boot-starters/wx-java-pay-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/pay/config/WxPayAutoConfiguration.java
index 2dd44004a6..9a9672de1a 100644
--- a/spring-boot-starters/wx-java-pay-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/pay/config/WxPayAutoConfiguration.java
+++ b/spring-boot-starters/wx-java-pay-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/pay/config/WxPayAutoConfiguration.java
@@ -49,6 +49,7 @@ public WxPayService wxPayService() {
payConfig.setSubAppId(StringUtils.trimToNull(this.properties.getSubAppId()));
payConfig.setSubMchId(StringUtils.trimToNull(this.properties.getSubMchId()));
payConfig.setKeyPath(StringUtils.trimToNull(this.properties.getKeyPath()));
+ payConfig.setUseSandboxEnv(this.properties.isUseSandboxEnv());
//以下是apiv3以及支付分相关
payConfig.setServiceId(StringUtils.trimToNull(this.properties.getServiceId()));
payConfig.setPayScoreNotifyUrl(StringUtils.trimToNull(this.properties.getPayScoreNotifyUrl()));
diff --git a/spring-boot-starters/wx-java-pay-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/pay/properties/WxPayProperties.java b/spring-boot-starters/wx-java-pay-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/pay/properties/WxPayProperties.java
index 940cdf5916..232bd33c47 100644
--- a/spring-boot-starters/wx-java-pay-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/pay/properties/WxPayProperties.java
+++ b/spring-boot-starters/wx-java-pay-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/pay/properties/WxPayProperties.java
@@ -73,5 +73,11 @@ public class WxPayProperties {
* apiv3 商户apiclient_cert.pem
*/
private String privateCertPath;
+
+ /**
+ * 微信支付是否使用仿真测试环境.
+ * 默认不使用
+ */
+ private boolean useSandboxEnv;
}
From 497aefe07f2b2a844194a59dbaa97efa38fcdf38 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=98=BF=E5=9D=A4=E5=90=8C=E5=AD=A6?= <1764441920@qq.com>
Date: Sat, 15 Jun 2024 21:56:25 +0000
Subject: [PATCH 021/385] =?UTF-8?q?:art:=20=E5=BD=93=E4=BD=BF=E7=94=A8Redi?=
=?UTF-8?q?sson=E5=AE=A2=E6=88=B7=E7=AB=AF=E4=BD=9C=E4=B8=BA=E7=BC=93?=
=?UTF-8?q?=E5=AD=98=E6=97=B6=EF=BC=8C=E5=AD=98=E5=9C=A8wxMaService.getWxM?=
=?UTF-8?q?aConfig().getExpiresTime()=E4=B8=80=E7=9B=B4=E4=B8=BA0=E9=97=AE?=
=?UTF-8?q?=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../wx/miniapp/config/impl/WxMaRedissonConfigImpl.java | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/WxMaRedissonConfigImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/WxMaRedissonConfigImpl.java
index 36d782506f..796121ec7c 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/WxMaRedissonConfigImpl.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/WxMaRedissonConfigImpl.java
@@ -147,4 +147,8 @@ public void expireAccessToken() {
redisOps.expire(this.accessTokenKey, 0, TimeUnit.SECONDS);
}
+ @Override
+ public long getExpiresTime() {
+ return redisOps.getExpire(this.accessTokenKey);
+ }
}
From 726269988a5a51878d2e3ef3035a496500362b51 Mon Sep 17 00:00:00 2001
From: zhangruhong
Date: Wed, 10 Jul 2024 10:55:11 +0800
Subject: [PATCH 022/385] =?UTF-8?q?:art:=20#3324=20=E3=80=90=E5=85=AC?=
=?UTF-8?q?=E4=BC=97=E5=8F=B7=E3=80=91=E6=B6=88=E6=81=AF=E8=B7=AF=E7=94=B1?=
=?UTF-8?q?=E8=A7=84=E5=88=99=E7=9A=84=E4=BA=8B=E4=BB=B6=E5=A2=9E=E5=8A=A0?=
=?UTF-8?q?=E6=AD=A3=E5=88=99=E5=8C=B9=E9=85=8D=E6=94=AF=E6=8C=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../weixin/mp/api/WxMpMessageRouterRule.java | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageRouterRule.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageRouterRule.java
index 79a4ff1a5e..abac350a7b 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageRouterRule.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageRouterRule.java
@@ -25,6 +25,8 @@ public class WxMpMessageRouterRule {
private String event;
+ private String eventRegex;
+
private String eventKey;
private String eventKeyRegex;
@@ -105,6 +107,18 @@ public WxMpMessageRouterRule eventKeyRegex(String regex) {
return this;
}
+
+ /**
+ * event匹配该正则表达式
+ * 比如"^weapp_audit_.*"用以匹配所有审核类类事件
+ *
+ * @param regex the regex
+ * @return the wx mp message router rule
+ */
+ public WxMpMessageRouterRule eventRegex(String regex) {
+ this.eventRegex = regex;
+ return this;
+ }
/**
* 如果content等于某值
*
@@ -236,6 +250,8 @@ protected boolean test(WxMpXmlMessage wxMessage) {
&&
(this.event == null || this.event.equalsIgnoreCase(wxMessage.getEvent()))
&&
+ (this.eventRegex == null || Pattern.matches(this.eventRegex, StringUtils.trimToEmpty(wxMessage.getEvent())))
+ &&
(this.eventKey == null || this.eventKey.equalsIgnoreCase(wxMessage.getEventKey()))
&&
(this.eventKeyRegex == null || Pattern.matches(this.eventKeyRegex, StringUtils.trimToEmpty(wxMessage.getEventKey())))
From f5bb2ba2d024bd3b6d3f6ad8c8ab8c4d748f047c Mon Sep 17 00:00:00 2001
From: Binary Wang
Date: Mon, 15 Jul 2024 17:03:14 +0800
Subject: [PATCH 023/385] =?UTF-8?q?:bug:=20=E3=80=90=E5=B0=8F=E7=A8=8B?=
=?UTF-8?q?=E5=BA=8F=E3=80=91=E4=BF=AE=E5=A4=8D=E8=8E=B7=E5=8F=96=E8=BF=90?=
=?UTF-8?q?=E5=8A=9Bid=E5=88=97=E8=A1=A8=E6=8E=A5=E5=8F=A3=E4=BC=A0?=
=?UTF-8?q?=E5=8F=82=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../wx/miniapp/api/impl/WxMaImmediateDeliveryServiceImpl.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaImmediateDeliveryServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaImmediateDeliveryServiceImpl.java
index fa1927ffd1..05e8f2e0a7 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaImmediateDeliveryServiceImpl.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaImmediateDeliveryServiceImpl.java
@@ -197,7 +197,7 @@ public QueryFollowTraceResponse queryFollowTrace(
@Override
public GetDeliveryListResponse getDeliveryList() throws WxErrorException {
- String responseContent = this.wxMaService.post(InstantDelivery.GET_DELIVERY_LIST_URL,"");
+ String responseContent = this.wxMaService.post(InstantDelivery.GET_DELIVERY_LIST_URL,"{}");
GetDeliveryListResponse response = GetDeliveryListResponse.fromJson(responseContent);
if (response.getErrcode() == -1) {
throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp));
From 3a577709fe7d9cc220da68c72df7683eba25c2cb Mon Sep 17 00:00:00 2001
From: imyzt
Date: Mon, 15 Jul 2024 21:22:11 +0800
Subject: [PATCH 024/385] =?UTF-8?q?:art:=20#3295=E3=80=90=E8=A7=86?=
=?UTF-8?q?=E9=A2=91=E5=8F=B7=E5=B0=8F=E5=BA=97=E3=80=91=E8=8E=B7=E5=8F=96?=
=?UTF-8?q?=E8=AE=A2=E5=8D=95=E8=AF=A6=E6=83=85=E6=8E=A5=E5=8F=A3=E8=A1=A5?=
=?UTF-8?q?=E5=85=85=E9=83=A8=E5=88=86=E8=BF=94=E5=9B=9E=E5=80=BC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../channel/bean/order/OrderAddressInfo.java | 18 +++++++++
.../channel/bean/order/TelNumberExtInfo.java | 37 +++++++++++++++++++
2 files changed, 55 insertions(+)
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/TelNumberExtInfo.java
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/OrderAddressInfo.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/OrderAddressInfo.java
index ff3e1ba332..1af5aee49e 100644
--- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/OrderAddressInfo.java
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/OrderAddressInfo.java
@@ -20,4 +20,22 @@ public class OrderAddressInfo extends AddressInfo {
/** 虚拟发货订单联系方式(deliver_method=1时返回) */
@JsonProperty("virtual_order_tel_number")
private String virtualOrderTelNumber;
+
+ /**
+ * 额外的联系方式信息(虚拟号码相关),具体结构请参考TelNumberExtInfo结构体
+ */
+ @JsonProperty("tel_number_ext_info")
+ private TelNumberExtInfo telNumberExtInfo;
+
+ /**
+ * 0:不使用虚拟号码,1:使用虚拟号码
+ */
+ @JsonProperty("use_tel_number")
+ private Integer useTelNumber;
+
+ /**
+ * 标识当前店铺下一个唯一的用户收货地址
+ */
+ @JsonProperty("hash_code")
+ private String hashCode;
}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/TelNumberExtInfo.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/TelNumberExtInfo.java
new file mode 100644
index 0000000000..1d9e8b7914
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/TelNumberExtInfo.java
@@ -0,0 +1,37 @@
+package me.chanjar.weixin.channel.bean.order;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+/**
+ * 联系方式信息
+ *
+ * @author imyzt
+ */
+@Data
+public class TelNumberExtInfo {
+
+ /**
+ * 脱敏手机号
+ */
+ @JsonProperty("real_tel_number")
+ private String realTelNumber;
+
+ /**
+ * 完整的虚拟号码
+ */
+ @JsonProperty("virtual_tel_number")
+ private String virtualTelNumber;
+
+ /**
+ * 主动兑换的虚拟号码过期时间
+ */
+ @JsonProperty("virtual_tel_expire_time")
+ private Long virtualTelExpireTime;
+
+ /**
+ * 主动兑换虚拟号码次数
+ */
+ @JsonProperty("get_virtual_tel_cnt")
+ private Long getVirtualTelCnt;
+}
From 9816fdfdcfd454a2e0a4d3ee7a053362b18c6588 Mon Sep 17 00:00:00 2001
From: imyzt
Date: Mon, 15 Jul 2024 21:24:37 +0800
Subject: [PATCH 025/385] =?UTF-8?q?:art:=20#3319=E3=80=90=E5=BE=AE?=
=?UTF-8?q?=E4=BF=A1=E6=94=AF=E4=BB=98=E3=80=91=E5=95=86=E5=AE=B6=E8=BD=AC?=
=?UTF-8?q?=E8=B4=A6=E5=88=B0=E9=9B=B6=E9=92=B1=E6=8E=A5=E5=8F=A3=E8=A1=A5?=
=?UTF-8?q?=E5=85=85=E5=AD=97=E6=AE=B5=EF=BC=9A=E8=AF=B7=E6=B1=82=E6=96=B0?=
=?UTF-8?q?=E5=A2=9E`notify=5Furl`=E3=80=81=E5=93=8D=E5=BA=94=E6=96=B0?=
=?UTF-8?q?=E5=A2=9E`batch=5Fstatus`?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../merchanttransfer/TransferCreateRequest.java | 15 +++++++++++++++
.../merchanttransfer/TransferCreateResult.java | 11 +++++++++++
2 files changed, 26 insertions(+)
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/merchanttransfer/TransferCreateRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/merchanttransfer/TransferCreateRequest.java
index 38bfcb9ed0..a94e68d11a 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/merchanttransfer/TransferCreateRequest.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/merchanttransfer/TransferCreateRequest.java
@@ -1,5 +1,6 @@
package com.github.binarywang.wxpay.bean.merchanttransfer;
+import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.v3.SpecEncrypt;
import com.google.gson.annotations.SerializedName;
import lombok.AllArgsConstructor;
@@ -136,6 +137,20 @@ public class TransferCreateRequest implements Serializable {
@SerializedName("transfer_scene_id")
private String transferSceneId;
+ /**
+ *
+ * 字段名:通知地址
+ * 变量名:notify_url
+ * 是否必填:否
+ * 类型:string(256)
+ * 描述:
+ * 异步接收微信支付结果通知的回调地址,通知url必须为公网可访问的url,必须为https,不能携带参数。
+ * 回调解析: {@link WxPayService#parseTransferBatchesNotifyV3Result}
+ *
+ */
+ @SerializedName("notify_url")
+ private String notifyUrl;
+
/**
* The type Transfer detail list.
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/merchanttransfer/TransferCreateResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/merchanttransfer/TransferCreateResult.java
index f2417c4687..026eee69ff 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/merchanttransfer/TransferCreateResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/merchanttransfer/TransferCreateResult.java
@@ -63,4 +63,15 @@ public class TransferCreateResult implements Serializable {
*/
@SerializedName("create_time")
private String createTime;
+
+ /**
+ * 批次状态
+ * 说明:
+ * ACCEPTED:已受理。批次已受理成功,若发起批量转账的30分钟后,转账批次单仍处于该状态,可能原因是商户账户余额不足等。商户可查询账户资金流水,若该笔转账批次单的扣款已经发生,则表示批次已经进入转账中,请再次查单确认
+ * PROCESSING:转账中。已开始处理批次内的转账明细单
+ * FINISHED:已完成。批次内的所有转账明细单都已处理完成
+ * CLOSED:已关闭。可查询具体的批次关闭原因确认
+ */
+ @SerializedName("batch_status")
+ private String batchStatus;
}
From 0886fa0fbd7d75ec3ac60bfd875442daec757d3e Mon Sep 17 00:00:00 2001
From: rayL <1061959822@qq.com>
Date: Mon, 15 Jul 2024 13:28:02 +0000
Subject: [PATCH 026/385] =?UTF-8?q?:art:=E3=80=90=E5=85=AC=E5=85=B1?=
=?UTF-8?q?=E3=80=91=E4=BF=AE=E5=A4=8D=E4=B8=8B=E8=BD=BD=E6=96=87=E4=BB=B6?=
=?UTF-8?q?=E7=9B=B8=E5=85=B3=E6=8E=A5=E5=8F=A3=E8=8E=B7=E5=8F=96=E6=96=87?=
=?UTF-8?q?=E4=BB=B6=E5=90=8D=E7=9A=84=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../weixin/common/util/http/HttpResponseProxy.java | 13 +++++--------
1 file changed, 5 insertions(+), 8 deletions(-)
diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/HttpResponseProxy.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/HttpResponseProxy.java
index f338ece672..3e0acb46fd 100644
--- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/HttpResponseProxy.java
+++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/HttpResponseProxy.java
@@ -1,15 +1,11 @@
package me.chanjar.weixin.common.util.http;
import jodd.http.HttpResponse;
-import me.chanjar.weixin.common.error.WxError;
import me.chanjar.weixin.common.error.WxErrorException;
import okhttp3.Response;
import org.apache.http.Header;
import org.apache.http.client.methods.CloseableHttpResponse;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
/**
*
* 三种http框架的response代理类,方便提取公共方法
@@ -19,7 +15,6 @@
* @author Binary Wang
*/
public class HttpResponseProxy {
- private static final Pattern PATTERN = Pattern.compile(".*filename=\"(.*)\"");
private CloseableHttpResponse apacheHttpResponse;
private HttpResponse joddHttpResponse;
@@ -79,9 +74,11 @@ private String extractFileNameFromContentString(String content) throws WxErrorEx
throw new WxErrorException("无法获取到文件名,content为空");
}
- Matcher m = PATTERN.matcher(content);
- if (m.matches()) {
- return m.group(1);
+ int startIndex = content.indexOf("filename=\"");
+ if (startIndex != -1) {
+ startIndex += "filename=\"".length();
+ int endIndex = content.indexOf('"', startIndex);
+ return content.substring(startIndex, endIndex);
}
throw new WxErrorException("无法获取到文件名,header信息有问题");
From c0f2d75d8c43f3e4449f2cfda3dd4cd1011670b7 Mon Sep 17 00:00:00 2001
From: Binary Wang
Date: Mon, 15 Jul 2024 21:33:44 +0700
Subject: [PATCH 027/385] =?UTF-8?q?:bookmark:=20=E5=8F=91=E5=B8=83=204.6.3?=
=?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-channel-spring-boot-starter/pom.xml | 2 +-
.../wx-java-cp-multi-spring-boot-starter/pom.xml | 2 +-
spring-boot-starters/wx-java-cp-spring-boot-starter/pom.xml | 2 +-
.../wx-java-miniapp-spring-boot-starter/pom.xml | 2 +-
.../wx-java-mp-multi-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 +-
spring-boot-starters/wx-java-qidian-spring-boot-starter/pom.xml | 2 +-
weixin-graal/pom.xml | 2 +-
weixin-java-channel/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 +-
weixin-java-qidian/pom.xml | 2 +-
20 files changed, 20 insertions(+), 20 deletions(-)
diff --git a/pom.xml b/pom.xml
index 5cf69419f0..47283cc777 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
4.0.0
com.github.binarywang
wx-java
- 4.6.2.B
+ 4.6.3.B
pom
WxJava - Weixin/Wechat Java SDK
微信开发Java SDK
diff --git a/spring-boot-starters/pom.xml b/spring-boot-starters/pom.xml
index 1641a63dbf..9bc9071cc2 100644
--- a/spring-boot-starters/pom.xml
+++ b/spring-boot-starters/pom.xml
@@ -6,7 +6,7 @@
com.github.binarywang
wx-java
- 4.6.2.B
+ 4.6.3.B
pom
wx-java-spring-boot-starters
diff --git a/spring-boot-starters/wx-java-channel-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-channel-spring-boot-starter/pom.xml
index 4e4ffc9804..b2d8fa1c9b 100644
--- a/spring-boot-starters/wx-java-channel-spring-boot-starter/pom.xml
+++ b/spring-boot-starters/wx-java-channel-spring-boot-starter/pom.xml
@@ -3,7 +3,7 @@
wx-java-spring-boot-starters
com.github.binarywang
- 4.6.2.B
+ 4.6.3.B
4.0.0
diff --git a/spring-boot-starters/wx-java-cp-multi-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-cp-multi-spring-boot-starter/pom.xml
index 7afbb1fd0a..3851cde34a 100644
--- a/spring-boot-starters/wx-java-cp-multi-spring-boot-starter/pom.xml
+++ b/spring-boot-starters/wx-java-cp-multi-spring-boot-starter/pom.xml
@@ -4,7 +4,7 @@
wx-java-spring-boot-starters
com.github.binarywang
- 4.6.2.B
+ 4.6.3.B
4.0.0
diff --git a/spring-boot-starters/wx-java-cp-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-cp-spring-boot-starter/pom.xml
index 199b234885..9840fb264c 100644
--- a/spring-boot-starters/wx-java-cp-spring-boot-starter/pom.xml
+++ b/spring-boot-starters/wx-java-cp-spring-boot-starter/pom.xml
@@ -4,7 +4,7 @@
wx-java-spring-boot-starters
com.github.binarywang
- 4.6.2.B
+ 4.6.3.B
4.0.0
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 cd5d9b851d..3a1ad2e189 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
@@ -4,7 +4,7 @@
wx-java-spring-boot-starters
com.github.binarywang
- 4.6.2.B
+ 4.6.3.B
4.0.0
diff --git a/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/pom.xml
index 2c5c5270e4..4b407c0564 100644
--- a/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/pom.xml
+++ b/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/pom.xml
@@ -5,7 +5,7 @@
wx-java-spring-boot-starters
com.github.binarywang
- 4.6.2.B
+ 4.6.3.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 86fe5adb6b..470d754412 100644
--- a/spring-boot-starters/wx-java-mp-spring-boot-starter/pom.xml
+++ b/spring-boot-starters/wx-java-mp-spring-boot-starter/pom.xml
@@ -5,7 +5,7 @@
wx-java-spring-boot-starters
com.github.binarywang
- 4.6.2.B
+ 4.6.3.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 56972f7f90..8ea934b0f0 100644
--- a/spring-boot-starters/wx-java-open-spring-boot-starter/pom.xml
+++ b/spring-boot-starters/wx-java-open-spring-boot-starter/pom.xml
@@ -5,7 +5,7 @@
wx-java-spring-boot-starters
com.github.binarywang
- 4.6.2.B
+ 4.6.3.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 0e7785429f..d318e0a132 100644
--- a/spring-boot-starters/wx-java-pay-spring-boot-starter/pom.xml
+++ b/spring-boot-starters/wx-java-pay-spring-boot-starter/pom.xml
@@ -5,7 +5,7 @@
wx-java-spring-boot-starters
com.github.binarywang
- 4.6.2.B
+ 4.6.3.B
4.0.0
diff --git a/spring-boot-starters/wx-java-qidian-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-qidian-spring-boot-starter/pom.xml
index 1fda99db4e..a2a088cc70 100644
--- a/spring-boot-starters/wx-java-qidian-spring-boot-starter/pom.xml
+++ b/spring-boot-starters/wx-java-qidian-spring-boot-starter/pom.xml
@@ -3,7 +3,7 @@
wx-java-spring-boot-starters
com.github.binarywang
- 4.6.2.B
+ 4.6.3.B
4.0.0
diff --git a/weixin-graal/pom.xml b/weixin-graal/pom.xml
index 09d4c55773..cbc0d30b67 100644
--- a/weixin-graal/pom.xml
+++ b/weixin-graal/pom.xml
@@ -6,7 +6,7 @@
com.github.binarywang
wx-java
- 4.6.2.B
+ 4.6.3.B
weixin-graal
diff --git a/weixin-java-channel/pom.xml b/weixin-java-channel/pom.xml
index b2c15b5de0..09b6c9895c 100644
--- a/weixin-java-channel/pom.xml
+++ b/weixin-java-channel/pom.xml
@@ -6,7 +6,7 @@
com.github.binarywang
wx-java
- 4.6.2.B
+ 4.6.3.B
weixin-java-channel
diff --git a/weixin-java-common/pom.xml b/weixin-java-common/pom.xml
index ca024a0513..5e03dfd931 100644
--- a/weixin-java-common/pom.xml
+++ b/weixin-java-common/pom.xml
@@ -6,7 +6,7 @@
com.github.binarywang
wx-java
- 4.6.2.B
+ 4.6.3.B
weixin-java-common
diff --git a/weixin-java-cp/pom.xml b/weixin-java-cp/pom.xml
index 100f2907d2..3c055d560d 100644
--- a/weixin-java-cp/pom.xml
+++ b/weixin-java-cp/pom.xml
@@ -7,7 +7,7 @@
com.github.binarywang
wx-java
- 4.6.2.B
+ 4.6.3.B
weixin-java-cp
diff --git a/weixin-java-miniapp/pom.xml b/weixin-java-miniapp/pom.xml
index c71ef28d15..b6ac06a0b5 100644
--- a/weixin-java-miniapp/pom.xml
+++ b/weixin-java-miniapp/pom.xml
@@ -7,7 +7,7 @@
com.github.binarywang
wx-java
- 4.6.2.B
+ 4.6.3.B
weixin-java-miniapp
diff --git a/weixin-java-mp/pom.xml b/weixin-java-mp/pom.xml
index f4fb2ba642..b1a145075c 100644
--- a/weixin-java-mp/pom.xml
+++ b/weixin-java-mp/pom.xml
@@ -7,7 +7,7 @@
com.github.binarywang
wx-java
- 4.6.2.B
+ 4.6.3.B
weixin-java-mp
diff --git a/weixin-java-open/pom.xml b/weixin-java-open/pom.xml
index 1d4345002e..0e89ecb228 100644
--- a/weixin-java-open/pom.xml
+++ b/weixin-java-open/pom.xml
@@ -7,7 +7,7 @@
com.github.binarywang
wx-java
- 4.6.2.B
+ 4.6.3.B
weixin-java-open
diff --git a/weixin-java-pay/pom.xml b/weixin-java-pay/pom.xml
index 5df43e285c..b0ee75d409 100644
--- a/weixin-java-pay/pom.xml
+++ b/weixin-java-pay/pom.xml
@@ -5,7 +5,7 @@
com.github.binarywang
wx-java
- 4.6.2.B
+ 4.6.3.B
4.0.0
diff --git a/weixin-java-qidian/pom.xml b/weixin-java-qidian/pom.xml
index acae76826c..914fd5d003 100644
--- a/weixin-java-qidian/pom.xml
+++ b/weixin-java-qidian/pom.xml
@@ -7,7 +7,7 @@
com.github.binarywang
wx-java
- 4.6.2.B
+ 4.6.3.B
weixin-java-qidian
From 11d525c54d80a3ed32ab29040007a7a22d6da8c4 Mon Sep 17 00:00:00 2001
From: Mingyuan Wu
Date: Mon, 22 Jul 2024 14:01:47 +0800
Subject: [PATCH 028/385] :art: Bump org.bouncycastle:bcpkix-jdk18on &
bcprov-jdk18on from 1.78 to 1.78.1
---
pom.xml | 2 +-
weixin-java-cp/pom.xml | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 47283cc777..482fda5471 100644
--- a/pom.xml
+++ b/pom.xml
@@ -326,7 +326,7 @@
org.bouncycastle
bcpkix-jdk18on
- 1.78
+ 1.78.1
diff --git a/weixin-java-cp/pom.xml b/weixin-java-cp/pom.xml
index 3c055d560d..1d3b845c52 100644
--- a/weixin-java-cp/pom.xml
+++ b/weixin-java-cp/pom.xml
@@ -84,7 +84,7 @@
org.bouncycastle
bcprov-jdk18on
- 1.78
+ 1.78.1
From 9e0b87a1de2555b4e5d4fd19e058d150f8382e9c Mon Sep 17 00:00:00 2001
From: 55 <38285521+lizhengwu@users.noreply.github.com>
Date: Mon, 29 Jul 2024 21:23:01 +0800
Subject: [PATCH 029/385] =?UTF-8?q?:art:=20#3337=20=E3=80=90=E8=A7=86?=
=?UTF-8?q?=E9=A2=91=E5=8F=B7=E5=B0=8F=E5=BA=97=E3=80=91=20=E8=AE=A2?=
=?UTF-8?q?=E5=8D=95=E8=AF=A6=E6=83=85=E5=AD=97=E6=AE=B5=E8=A1=A5=E5=85=85?=
=?UTF-8?q?=E3=80=81=E5=94=AE=E5=90=8E=E6=96=B0=E7=89=B9=E6=80=A7=E8=A1=A5?=
=?UTF-8?q?=E5=85=85?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../api/WxChannelAfterSaleService.java | 34 +++++-
.../impl/WxChannelAfterSaleServiceImpl.java | 43 +++----
.../bean/after/AfterSaleAcceptParam.java | 10 ++
.../channel/bean/after/AfterSaleInfo.java | 4 +
.../channel/bean/after/AfterSaleReason.java | 33 ++++++
.../bean/after/AfterSaleReasonResponse.java | 28 +++++
.../bean/after/AfterSaleRejectParam.java | 16 ++-
.../bean/after/AfterSaleRejectReason.java | 39 +++++++
.../after/AfterSaleRejectReasonResponse.java | 29 +++++
.../weixin/channel/bean/after/RefundInfo.java | 4 +
.../channel/bean/order/OrderExtInfo.java | 34 +++++-
.../channel/bean/order/OrderProductInfo.java | 105 ++++++++++++++----
.../channel/bean/order/OrderSettleInfo.java | 27 ++++-
.../channel/bean/order/OrderSharerInfo.java | 24 +++-
.../constant/WxChannelApiUrlConstants.java | 4 +
.../weixin/channel/enums/AfterSaleStatus.java | 4 +
.../weixin/channel/enums/OrderScene.java | 52 +++++++++
.../weixin/channel/enums/RefundReason.java | 51 +++++++++
.../WxChannelAfterSaleServiceImplTest.java | 27 ++++-
19 files changed, 507 insertions(+), 61 deletions(-)
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleReason.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleReasonResponse.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleRejectReason.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleRejectReasonResponse.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/OrderScene.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/RefundReason.java
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/WxChannelAfterSaleService.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/WxChannelAfterSaleService.java
index ac1e61729b..418feab7ac 100644
--- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/WxChannelAfterSaleService.java
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/WxChannelAfterSaleService.java
@@ -4,6 +4,8 @@
import java.util.List;
import me.chanjar.weixin.channel.bean.after.AfterSaleInfoResponse;
import me.chanjar.weixin.channel.bean.after.AfterSaleListResponse;
+import me.chanjar.weixin.channel.bean.after.AfterSaleReasonResponse;
+import me.chanjar.weixin.channel.bean.after.AfterSaleRejectReasonResponse;
import me.chanjar.weixin.channel.bean.base.WxChannelBaseResponse;
import me.chanjar.weixin.channel.bean.complaint.ComplaintOrderResponse;
import me.chanjar.weixin.common.error.WxErrorException;
@@ -39,26 +41,31 @@ AfterSaleListResponse listIds(Long beginCreateTime, Long endCreateTime, String n
AfterSaleInfoResponse get(String afterSaleOrderId) throws WxErrorException;
/**
- * 同意退款
+ * 同意售后
+ * 文档地址 https://developers.weixin.qq.com/doc/channels/API/aftersale/acceptapply.html
*
* @param afterSaleOrderId 售后单号
* @param addressId 同意退货时传入地址id
+ * @param acceptType 1. 同意退货退款,并通知用户退货; 2. 确认收到货并退款给用户。 如果不填则将根据当前的售后单状态自动选择相应操作。对于仅退款的情况,由于只存在一种同意的场景,无需填写此字段。
* @return BaseResponse
*
* @throws WxErrorException 异常
*/
- WxChannelBaseResponse accept(String afterSaleOrderId, String addressId) throws WxErrorException;
+ WxChannelBaseResponse accept(String afterSaleOrderId, String addressId, Integer acceptType) throws WxErrorException;
/**
* 拒绝售后
+ * 文档地址 https://developers.weixin.qq.com/doc/channels/API/aftersale/rejectapply.html
*
* @param afterSaleOrderId 售后单号
* @param rejectReason 拒绝原因
+ * @param rejectReasonType 拒绝原因枚举值
+ * @see #getRejectReason()
* @return BaseResponse
*
* @throws WxErrorException 异常
*/
- WxChannelBaseResponse reject(String afterSaleOrderId, String rejectReason) throws WxErrorException;
+ WxChannelBaseResponse reject(String afterSaleOrderId, String rejectReason, Integer rejectReasonType) throws WxErrorException;
/**
* 上传退款凭证
@@ -108,4 +115,25 @@ WxChannelBaseResponse addComplaintEvidence(String complaintId, String content, L
* @throws WxErrorException 异常
*/
ComplaintOrderResponse getComplaint(String complaintId) throws WxErrorException;
+
+
+ /**
+ * 获取全量售后原因
+ * 文档地址:https://developers.weixin.qq.com/doc/channels/API/aftersale/getaftersalereason.html
+ *
+ * @return 售后原因
+ *
+ * @throws WxErrorException 异常
+ */
+ AfterSaleReasonResponse getAllReason() throws WxErrorException;
+
+ /**
+ * 获取拒绝售后原因
+ * 文档地址:https://developers.weixin.qq.com/doc/channels/API/aftersale/getrejectreason.html
+ *
+ * @return 拒绝售后原因
+ *
+ * @throws WxErrorException 异常
+ */
+ AfterSaleRejectReasonResponse getRejectReason() throws WxErrorException;
}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/impl/WxChannelAfterSaleServiceImpl.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/impl/WxChannelAfterSaleServiceImpl.java
index c29ea49b34..a4be86f28d 100644
--- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/impl/WxChannelAfterSaleServiceImpl.java
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/impl/WxChannelAfterSaleServiceImpl.java
@@ -1,30 +1,19 @@
package me.chanjar.weixin.channel.api.impl;
-import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.AfterSale.AFTER_SALE_ACCEPT_URL;
-import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.AfterSale.AFTER_SALE_GET_URL;
-import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.AfterSale.AFTER_SALE_LIST_URL;
-import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.AfterSale.AFTER_SALE_REJECT_URL;
-import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.AfterSale.AFTER_SALE_UPLOAD_URL;
-import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.Complaint.ADD_COMPLAINT_MATERIAL_URL;
-import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.Complaint.ADD_COMPLAINT_PROOF_URL;
-import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.Complaint.GET_COMPLAINT_ORDER_URL;
-
-import java.util.List;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.channel.api.WxChannelAfterSaleService;
-import me.chanjar.weixin.channel.bean.after.AfterSaleAcceptParam;
-import me.chanjar.weixin.channel.bean.after.AfterSaleIdParam;
-import me.chanjar.weixin.channel.bean.after.AfterSaleInfoResponse;
-import me.chanjar.weixin.channel.bean.after.AfterSaleListParam;
-import me.chanjar.weixin.channel.bean.after.AfterSaleListResponse;
-import me.chanjar.weixin.channel.bean.after.AfterSaleRejectParam;
-import me.chanjar.weixin.channel.bean.after.RefundEvidenceParam;
+import me.chanjar.weixin.channel.bean.after.*;
import me.chanjar.weixin.channel.bean.base.WxChannelBaseResponse;
import me.chanjar.weixin.channel.bean.complaint.ComplaintOrderResponse;
import me.chanjar.weixin.channel.bean.complaint.ComplaintParam;
import me.chanjar.weixin.channel.util.ResponseUtils;
import me.chanjar.weixin.common.error.WxErrorException;
+import java.util.List;
+
+import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.AfterSale.*;
+import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.Complaint.*;
+
/**
* 视频号小店 售后服务实现
*
@@ -56,15 +45,15 @@ public AfterSaleInfoResponse get(String afterSaleOrderId) throws WxErrorExceptio
}
@Override
- public WxChannelBaseResponse accept(String afterSaleOrderId, String addressId) throws WxErrorException {
- AfterSaleAcceptParam param = new AfterSaleAcceptParam(afterSaleOrderId, addressId);
+ public WxChannelBaseResponse accept(String afterSaleOrderId, String addressId, Integer acceptType) throws WxErrorException {
+ AfterSaleAcceptParam param = new AfterSaleAcceptParam(afterSaleOrderId, addressId, acceptType);
String resJson = shopService.post(AFTER_SALE_ACCEPT_URL, param);
return ResponseUtils.decode(resJson, WxChannelBaseResponse.class);
}
@Override
- public WxChannelBaseResponse reject(String afterSaleOrderId, String rejectReason) throws WxErrorException {
- AfterSaleRejectParam param = new AfterSaleRejectParam(afterSaleOrderId, rejectReason);
+ public WxChannelBaseResponse reject(String afterSaleOrderId, String rejectReason, Integer rejectReasonType) throws WxErrorException {
+ AfterSaleRejectParam param = new AfterSaleRejectParam(afterSaleOrderId, rejectReason, rejectReasonType);
String resJson = shopService.post(AFTER_SALE_REJECT_URL, param);
return ResponseUtils.decode(resJson, WxChannelBaseResponse.class);
}
@@ -100,4 +89,16 @@ public ComplaintOrderResponse getComplaint(String complaintId) throws WxErrorExc
String resJson = shopService.post(GET_COMPLAINT_ORDER_URL, reqJson);
return ResponseUtils.decode(resJson, ComplaintOrderResponse.class);
}
+
+ @Override
+ public AfterSaleReasonResponse getAllReason() throws WxErrorException {
+ String resJson = shopService.post(AFTER_SALE_REASON_GET_URL, "{}");
+ return ResponseUtils.decode(resJson, AfterSaleReasonResponse.class);
+ }
+
+ @Override
+ public AfterSaleRejectReasonResponse getRejectReason() throws WxErrorException {
+ String resJson = shopService.post(AFTER_SALE_REJECT_REASON_GET_URL, "{}");
+ return ResponseUtils.decode(resJson, AfterSaleRejectReasonResponse.class);
+ }
}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleAcceptParam.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleAcceptParam.java
index ebc63a2190..32ad9154ee 100644
--- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleAcceptParam.java
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleAcceptParam.java
@@ -19,6 +19,10 @@ public class AfterSaleAcceptParam extends AfterSaleIdParam {
@JsonProperty("address_id")
private String addressId;
+ /** 针对退货退款同意售后的阶段: 1. 同意退货退款,并通知用户退货; 2. 确认收到货并退款给用户。 如果不填则将根据当前的售后单状态自动选择相应操作。对于仅退款的情况,由于只存在一种同意的场景,无需填写此字段。*/
+ @JsonProperty("accept_type")
+ private Integer acceptType;
+
public AfterSaleAcceptParam() {
}
@@ -26,4 +30,10 @@ public AfterSaleAcceptParam(String afterSaleOrderId, String addressId) {
super(afterSaleOrderId);
this.addressId = addressId;
}
+
+ public AfterSaleAcceptParam(String afterSaleOrderId, String addressId, Integer acceptType) {
+ super(afterSaleOrderId);
+ this.addressId = addressId;
+ this.acceptType = acceptType;
+ }
}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleInfo.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleInfo.java
index b0d668b30e..3a9d390c95 100644
--- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleInfo.java
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleInfo.java
@@ -82,4 +82,8 @@ public class AfterSaleInfo implements Serializable {
/** 纠纷id,该字段可用于获取纠纷信息 */
@JsonProperty("complaint_id")
private String complaintId;
+
+ /** 仅在待商家审核退款退货申请或收货期间返回,表示操作剩余时间(秒数)*/
+ @JsonProperty("deadline")
+ private Long deadline;
}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleReason.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleReason.java
new file mode 100644
index 0000000000..7c66eff18f
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleReason.java
@@ -0,0 +1,33 @@
+package me.chanjar.weixin.channel.bean.after;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 全量售后原因
+ *
+ * @author lizhengwu
+ * @date 2024/7/24
+ */
+@Data
+@NoArgsConstructor
+public class AfterSaleReason implements Serializable {
+
+ private static final long serialVersionUID = -3674527884494606230L;
+
+ /**
+ * 售后原因枚举
+ */
+ @JsonProperty("reason")
+ private Integer reason;
+
+ /**
+ * 售后原因说明
+ */
+ @JsonProperty("reason_text")
+ private String reasonText;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleReasonResponse.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleReasonResponse.java
new file mode 100644
index 0000000000..7372dea1f1
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleReasonResponse.java
@@ -0,0 +1,28 @@
+package me.chanjar.weixin.channel.bean.after;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import me.chanjar.weixin.channel.bean.base.WxChannelBaseResponse;
+
+import java.util.List;
+
+/**
+ * 售后原因
+ *
+ *
+ * @author lizhengwu
+ */
+@Data
+@NoArgsConstructor
+@EqualsAndHashCode
+public class AfterSaleReasonResponse extends WxChannelBaseResponse {
+
+
+ private static final long serialVersionUID = -580378623915041396L;
+
+ @JsonProperty("reason_list")
+ private List reasonList;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleRejectParam.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleRejectParam.java
index 080665ac00..cbde459fea 100644
--- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleRejectParam.java
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleRejectParam.java
@@ -15,10 +15,18 @@
public class AfterSaleRejectParam extends AfterSaleIdParam {
private static final long serialVersionUID = -7507483859864253314L;
- /** 拒绝原因 */
+ /**
+ * 拒绝原因
+ */
@JsonProperty("reject_reason")
private String rejectReason;
+ /**
+ * 拒绝原因枚举值
+ */
+ @JsonProperty("reject_reason_type")
+ private Integer rejectReasonType;
+
public AfterSaleRejectParam() {
}
@@ -26,4 +34,10 @@ public AfterSaleRejectParam(String afterSaleOrderId, String rejectReason) {
super(afterSaleOrderId);
this.rejectReason = rejectReason;
}
+
+ public AfterSaleRejectParam(String afterSaleOrderId, String rejectReason, Integer rejectReasonType) {
+ super(afterSaleOrderId);
+ this.rejectReason = rejectReason;
+ this.rejectReasonType = rejectReasonType;
+ }
}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleRejectReason.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleRejectReason.java
new file mode 100644
index 0000000000..51c88ae222
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleRejectReason.java
@@ -0,0 +1,39 @@
+package me.chanjar.weixin.channel.bean.after;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 拒绝售后原因
+ *
+ * @author lizhengwu
+ * @date 2024/7/24
+ */
+@Data
+@NoArgsConstructor
+public class AfterSaleRejectReason implements Serializable {
+
+ private static final long serialVersionUID = -3672834150982780L;
+
+ /**
+ * 售后拒绝原因枚举
+ */
+ @JsonProperty("reject_reason_type")
+ private Integer rejectReasonType;
+
+ /**
+ * 售后拒绝原因说明
+ */
+ @JsonProperty("reject_reason_type_text")
+ private String rejectReasonTypeText;
+
+ /**
+ * 售后拒绝原因默认描述
+ */
+ @JsonProperty("reject_reason")
+ private String rejectReason;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleRejectReasonResponse.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleRejectReasonResponse.java
new file mode 100644
index 0000000000..7b50691d00
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/AfterSaleRejectReasonResponse.java
@@ -0,0 +1,29 @@
+package me.chanjar.weixin.channel.bean.after;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import me.chanjar.weixin.channel.bean.base.WxChannelBaseResponse;
+
+import java.util.List;
+
+/**
+ * 售后原因
+ *
+ * @author lizhengwu
+ */
+@Data
+@NoArgsConstructor
+@EqualsAndHashCode
+public class AfterSaleRejectReasonResponse extends WxChannelBaseResponse {
+
+ private static final long serialVersionUID = -7946679037747710613L;
+
+ /**
+ * 售后原因列表
+ */
+ @JsonProperty("reject_reason_list")
+ private List rejectReasonList;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/RefundInfo.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/RefundInfo.java
index 9837b72b28..73aedf99cf 100644
--- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/RefundInfo.java
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/after/RefundInfo.java
@@ -18,4 +18,8 @@ public class RefundInfo implements Serializable {
/** 退款金额(分) */
@JsonProperty("amount")
private Integer amount;
+
+ /** 标明售后单退款直接原因, 枚举值详情请参考 {@link me.chanjar.weixin.channel.enums.RefundReason} */
+ @JsonProperty("refund_reason")
+ private Integer refundReason;
}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/OrderExtInfo.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/OrderExtInfo.java
index 3338d1a428..a846311c61 100644
--- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/OrderExtInfo.java
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/OrderExtInfo.java
@@ -1,10 +1,11 @@
package me.chanjar.weixin.channel.bean.order;
import com.fasterxml.jackson.annotation.JsonProperty;
-import java.io.Serializable;
import lombok.Data;
import lombok.NoArgsConstructor;
+import java.io.Serializable;
+
/**
* 订单备注信息
*
@@ -15,12 +16,39 @@
public class OrderExtInfo implements Serializable {
private static final long serialVersionUID = 4568097877621455429L;
- /** 用户备注 */
+ /**
+ * 用户备注
+ */
@JsonProperty("customer_notes")
private String customerNotes;
- /** 商家备注 */
+ /**
+ * 商家备注
+ */
@JsonProperty("merchant_notes")
private String merchantNotes;
+ /**
+ * 确认收货时间,包括用户主动确认收货和超时自动确认收货
+ */
+ @JsonProperty("confirm_receipt_time")
+ private Long confirmReceiptTime;
+
+ /**
+ * 视频号id
+ */
+ @JsonProperty("finder_id")
+ private String finderId;
+
+ /**
+ * 直播id
+ */
+ @JsonProperty("live_id")
+ private String liveId;
+
+ /**
+ * 下单场景,枚举值见 {@link me.chanjar.weixin.channel.enums.OrderScene}
+ */
+ @JsonProperty("order_scene")
+ private Integer orderScene;
}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/OrderProductInfo.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/OrderProductInfo.java
index acef8cc4f6..e7edeb8912 100644
--- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/OrderProductInfo.java
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/OrderProductInfo.java
@@ -1,8 +1,10 @@
package me.chanjar.weixin.channel.bean.order;
import com.fasterxml.jackson.annotation.JsonProperty;
+
import java.io.Serializable;
import java.util.List;
+
import lombok.Data;
import lombok.NoArgsConstructor;
import me.chanjar.weixin.channel.bean.base.AttrInfo;
@@ -17,96 +19,153 @@
public class OrderProductInfo implements Serializable {
private static final long serialVersionUID = -2193536732955185928L;
- /** 商品spu id */
+ /**
+ * 商品spu id
+ */
@JsonProperty("product_id")
private String productId;
- /** sku_id */
+ /**
+ * sku_id
+ */
@JsonProperty("sku_id")
private String skuId;
- /** sku小图 */
+ /**
+ * sku小图
+ */
@JsonProperty("thumb_img")
private String thumbImg;
- /** sku数量 */
+ /**
+ * sku数量
+ */
@JsonProperty("sku_cnt")
private Integer skuCnt;
- /** 售卖价格(单位:分) */
+ /**
+ * 售卖价格(单位:分)
+ */
@JsonProperty("sale_price")
private Integer salePrice;
- /** 商品标题 */
+ /**
+ * 商品标题
+ */
@JsonProperty("title")
private String title;
- /** 正在售后/退款流程中的 sku 数量 */
+ /**
+ * 正在售后/退款流程中的 sku 数量
+ */
@JsonProperty("on_aftersale_sku_cnt")
private Integer onAfterSaleSkuCnt;
- /** 完成售后/退款的 sku 数量 */
+ /**
+ * 完成售后/退款的 sku 数量
+ */
@JsonProperty("finish_aftersale_sku_cnt")
private Integer finishAfterSaleSkuCnt;
- /** 商品编码 */
+ /**
+ * 商品编码
+ */
@JsonProperty("sku_code")
private String skuCode;
- /** 市场价格(单位:分) */
+ /**
+ * 市场价格(单位:分)
+ */
@JsonProperty("market_price")
private Integer marketPrice;
- /** sku属性 */
+ /**
+ * sku属性
+ */
@JsonProperty("sku_attrs")
private List skuAttrs;
- /** sku实付价格 */
+ /**
+ * sku实付价格
+ */
@JsonProperty("real_price")
private Integer realPrice;
- /** 商品外部spu id */
+ /**
+ * 商品外部spu id
+ */
@JsonProperty("out_product_id")
private String outProductId;
- /** 商品外部sku id */
+ /**
+ * 商品外部sku id
+ */
@JsonProperty("out_sku_id")
private String outSkuId;
- /** 是否有优惠金额,非必填,默认为false */
+ /**
+ * 是否有优惠金额,非必填,默认为false
+ */
@JsonProperty("is_discounted")
private Boolean isDiscounted;
- /** 优惠后 sku 价格,非必填,is_discounted为 true 时有值 */
+ /**
+ * 优惠后 sku 价格,非必填,is_discounted为 true 时有值
+ */
@JsonProperty("estimate_price")
private Integer estimatePrice;
- /** 是否修改过价格,非必填,默认为false */
+ /**
+ * 是否修改过价格,非必填,默认为false
+ */
@JsonProperty("is_change_price")
private Boolean changePriced;
- /** 改价后 sku 价格,非必填,is_change_price为 true 时有值 */
+ /**
+ * 改价后 sku 价格,非必填,is_change_price为 true 时有值
+ */
@JsonProperty("change_price")
private Integer changePrice;
- /** 区域库存id */
+ /**
+ * 区域库存id
+ */
@JsonProperty("out_warehouse_id")
private String outWarehouseId;
- /** 商品发货信息 */
+ /**
+ * 商品发货信息
+ */
@JsonProperty("sku_deliver_info")
private OrderSkuDeliverInfo skuDeliverInfo;
- /** 商品额外服务信息 */
+ /**
+ * 商品额外服务信息
+ */
@JsonProperty("extra_service")
private OrderProductExtraService extraService;
- /** 是否使用了会员积分抵扣 */
+ /**
+ * 是否使用了会员积分抵扣
+ */
@JsonProperty("use_deduction")
private Boolean useDeduction;
- /** 会员积分抵扣金额,单位为分 */
+ /**
+ * 会员积分抵扣金额,单位为分
+ */
@JsonProperty("deduction_price")
private Integer deductionPrice;
+ /**
+ * 商品优惠券信息,具体结构请参考OrderProductCouponInfo结构体,逐步替换 order.order_detail.coupon_info
+ */
+ @JsonProperty("order_product_coupon_info_list")
+ private List orderProductCouponInfoList;
+
+ /**
+ * 商品发货时效,超时此时间未发货即为发货超时
+ */
+ @JsonProperty("delivery_deadline")
+ private Long deliveryDeadline;
}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/OrderSettleInfo.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/OrderSettleInfo.java
index c264a6289a..bd31931444 100644
--- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/OrderSettleInfo.java
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/OrderSettleInfo.java
@@ -1,7 +1,9 @@
package me.chanjar.weixin.channel.bean.order;
import com.fasterxml.jackson.annotation.JsonProperty;
+
import java.io.Serializable;
+
import lombok.Data;
import lombok.NoArgsConstructor;
@@ -15,12 +17,33 @@
public class OrderSettleInfo implements Serializable {
private static final long serialVersionUID = 2140632631448343656L;
- /** 预计技术服务费(单位为分) */
+ /**
+ * 预计技术服务费(单位为分)
+ */
@JsonProperty("predict_commission_fee")
private Integer predictCommissionFee;
- /** 实际技术服务费(单位为分)(未结算时本字段为空) */
+ /**
+ * 实际技术服务费(单位为分)(未结算时本字段为空)
+ */
@JsonProperty("commission_fee")
private Integer commissionFee;
+ /**
+ * 预计人气卡返佣金额,单位为分(未发起结算时本字段为空)
+ */
+ @JsonProperty("predict_wecoin_commission")
+ private Integer predictWecoinCommission;
+
+ /**
+ * 实际人气卡返佣金额,单位为分(未结算时本字段为空)
+ */
+ @JsonProperty("wecoin_commission")
+ private Integer wecoinCommission;
+
+ /**
+ * 商家结算时间
+ */
+ @JsonProperty("settle_time")
+ private Long settleTime;
}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/OrderSharerInfo.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/OrderSharerInfo.java
index be66463445..7ed41d2edf 100644
--- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/OrderSharerInfo.java
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/OrderSharerInfo.java
@@ -1,7 +1,9 @@
package me.chanjar.weixin.channel.bean.order;
import com.fasterxml.jackson.annotation.JsonProperty;
+
import java.io.Serializable;
+
import lombok.Data;
import lombok.NoArgsConstructor;
@@ -15,19 +17,33 @@
public class OrderSharerInfo implements Serializable {
private static final long serialVersionUID = 7183259072254660971L;
- /** 分享员openid */
+ /**
+ * 分享员openid
+ */
@JsonProperty("sharer_openid")
private String sharerOpenid;
- /** 分享员unionid */
+ /**
+ * 分享员unionid
+ */
@JsonProperty("sharer_unionid")
private String sharerUnionid;
- /** 分享员类型,0:普通分享员,1:店铺分享员 */
+ /**
+ * 分享员类型,0:普通分享员,1:店铺分享员
+ */
@JsonProperty("sharer_type")
private Integer sharerType;
- /** 分享场景 {@link me.chanjar.weixin.channel.enums.ShareScene} */
+ /**
+ * 分享场景 {@link me.chanjar.weixin.channel.enums.ShareScene}
+ */
@JsonProperty("share_scene")
private Integer shareScene;
+
+ /**
+ * 分享员数据是否已经解析完成【1:解析完成 0:解析中】
+ */
+ @JsonProperty("handling_progress")
+ private Integer handlingProgress;
}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/constant/WxChannelApiUrlConstants.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/constant/WxChannelApiUrlConstants.java
index 4be90c370b..79ff5f8f8d 100644
--- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/constant/WxChannelApiUrlConstants.java
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/constant/WxChannelApiUrlConstants.java
@@ -170,6 +170,10 @@ public interface AfterSale {
String AFTER_SALE_REJECT_URL = "https://api.weixin.qq.com/channels/ec/aftersale/rejectapply";
/** 上传退款凭证 */
String AFTER_SALE_UPLOAD_URL = "https://api.weixin.qq.com/channels/ec/aftersale/uploadrefundcertificate";
+ /** 获取全量售后原因*/
+ String AFTER_SALE_REASON_GET_URL = "https://api.weixin.qq.com/channels/ec/aftersale/reason/get";
+ /** 获取拒绝售后原因*/
+ String AFTER_SALE_REJECT_REASON_GET_URL = "https://api.weixin.qq.com/channels/ec/aftersale/rejectreason/get";
}
/** 纠纷相关接口 */
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/AfterSaleStatus.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/AfterSaleStatus.java
index 60e77d9e53..b249c96bcb 100644
--- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/AfterSaleStatus.java
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/AfterSaleStatus.java
@@ -39,6 +39,10 @@ public enum AfterSaleStatus {
MERCHANT_REFUND_RETRY_FAIL("MERCHANT_REFUND_RETRY_FAIL", "商家打款失败,客服关闭售后"),
/** 售后关闭 */
MERCHANT_FAIL("MERCHANT_FAIL", "售后关闭"),
+ /** 待用户处理商家协商 */
+ USER_WAIT_CONFIRM_UPDATE("USER_WAIT_CONFIRM_UPDATE", "待用户处理商家协商"),
+ /** 待用户处理商家代发起的售后申请 */
+ USER_WAIT_HANDLE_MERCHANT_AFTER_SALE("USER_WAIT_HANDLE_MERCHANT_AFTER_SALE", "待用户处理商家代发起的售后申请"),
;
private final String key;
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/OrderScene.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/OrderScene.java
new file mode 100644
index 0000000000..c00f6a8002
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/OrderScene.java
@@ -0,0 +1,52 @@
+package me.chanjar.weixin.channel.enums;
+
+/**
+ * 下单场景
+ *
+ * @author lizhengwu
+ * @description
+ */
+public enum OrderScene {
+ /**
+ * 其他
+ */
+ OTHER(1, "其他"),
+ /**
+ * 直播间下单
+ */
+ LIVE(2, "直播间"),
+ /**
+ * 短视频
+ */
+ VIDEO(3, "短视频"),
+ /**
+ * 商品分享
+ */
+ SHARE(4, "商品分享"),
+ /**
+ * 商品橱窗主页
+ */
+ SHOW_CASE(5, "商品橱窗主页"),
+ /**
+ * 公众号文章商品卡片
+ */
+ ARTICLE_CARD(6, "公众号文章商品卡片"),
+ ;
+
+ private final int key;
+ private final String value;
+
+ OrderScene(int key, String value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ public int getKey() {
+ return key;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/RefundReason.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/RefundReason.java
new file mode 100644
index 0000000000..8a2825c28c
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/RefundReason.java
@@ -0,0 +1,51 @@
+package me.chanjar.weixin.channel.enums;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+
+/**
+ * 售后单退款直接原因
+ *
+ * @author lizhengwu
+ */
+@JsonFormat(shape = JsonFormat.Shape.OBJECT)
+public enum RefundReason {
+ /** 1 商家通过店铺管理页或者小助手发起退款 */
+ MERCHANT_INITIATED_REFUND(1, "商家通过店铺管理页或者小助手发起退款"),
+ /** 2 退货退款场景,商家同意买家未上传物流单号情况下确认收货并退款,该场景限于订单无运费险 */
+ MERCHANT_AGREES_NO_TRACKING_REFUND(2, "退货退款场景,商家同意买家未上传物流单号情况下确认收货并退款,该场景限于订单无运费险"),
+ /** 3 商家通过后台api发起退款 */
+ MERCHANT_API_INITIATED_REFUND(3, "商家通过后台api发起退款"),
+ /** 4 未发货售后平台自动同意 */
+ PRE_SHIPMENT_AUTOMATIC_REFUND(4, "未发货售后平台自动同意"),
+ /** 5 平台介入纠纷退款 */
+ PLATFORM_INTERVENED_DISPUTE_REFUND(5, "平台介入纠纷退款"),
+ /** 6 特殊场景下平台强制退款 */
+ PLATFORM_FORCED_REFUND(6, "特殊场景下平台强制退款"),
+ /** 7 退货退款场景,买家同意没有上传物流单号情况下,商家确认收货并退款,该场景限于订单包含运费险,并无法理赔 */
+ BUYER_AGREES_NO_TRACKING_REFUND(7, "退货退款场景,买家同意没有上传物流单号情况下,商家确认收货并退款,该场景限于订单包含运费险,并无法理赔"),
+ /** 8 商家发货超时,平台退款 */
+ LATE_SHIPMENT_PLATFORM_REFUND(8, "商家发货超时,平台退款"),
+ /** 9 商家处理买家售后申请超时,平台自动同意退款 */
+ MERCHANT_OVERDUE_AUTO_REFUND(9, "商家处理买家售后申请超时,平台自动同意退款"),
+ /** 10 用户确认收货超时,平台退款 */
+ BUYER_OVERDUE_AUTO_REFUND(10, "用户确认收货超时,平台退款"),
+ /** 11 商家确认收货超时,平台退款 */
+ MERCHANT_OVERDUE_CONFIRMATION_REFUND(11, "商家确认收货超时,平台退款"),
+ ;
+
+ private final int key;
+ private final String value;
+
+ RefundReason(int key, String value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ public int getKey() {
+ return key;
+ }
+
+ public String getValue() {
+ return value;
+ }
+}
diff --git a/weixin-java-channel/src/test/java/me/chanjar/weixin/channel/api/impl/WxChannelAfterSaleServiceImplTest.java b/weixin-java-channel/src/test/java/me/chanjar/weixin/channel/api/impl/WxChannelAfterSaleServiceImplTest.java
index 7e54a9eaae..81122f7a03 100644
--- a/weixin-java-channel/src/test/java/me/chanjar/weixin/channel/api/impl/WxChannelAfterSaleServiceImplTest.java
+++ b/weixin-java-channel/src/test/java/me/chanjar/weixin/channel/api/impl/WxChannelAfterSaleServiceImplTest.java
@@ -12,6 +12,8 @@
import me.chanjar.weixin.channel.api.WxChannelService;
import me.chanjar.weixin.channel.bean.after.AfterSaleInfoResponse;
import me.chanjar.weixin.channel.bean.after.AfterSaleListResponse;
+import me.chanjar.weixin.channel.bean.after.AfterSaleReasonResponse;
+import me.chanjar.weixin.channel.bean.after.AfterSaleRejectReasonResponse;
import me.chanjar.weixin.channel.bean.base.WxChannelBaseResponse;
import me.chanjar.weixin.channel.bean.complaint.ComplaintOrderResponse;
import me.chanjar.weixin.channel.test.ApiTestModule;
@@ -52,8 +54,8 @@ public void testGet() throws WxErrorException {
public void testAccept() throws WxErrorException {
WxChannelAfterSaleService afterSaleService = channelService.getAfterSaleService();
String afterSaleOrderId = "";
- String addressId = "123";
- WxChannelBaseResponse response = afterSaleService.accept(afterSaleOrderId, addressId);
+ String addressId = null;
+ WxChannelBaseResponse response = afterSaleService.accept(afterSaleOrderId, addressId, 2);
assertNotNull(response);
assertTrue(response.isSuccess());
}
@@ -62,8 +64,8 @@ public void testAccept() throws WxErrorException {
public void testReject() throws WxErrorException {
WxChannelAfterSaleService afterSaleService = channelService.getAfterSaleService();
String afterSaleOrderId = "";
- String rejectReason = "123";
- WxChannelBaseResponse response = afterSaleService.reject(afterSaleOrderId, rejectReason);
+ String rejectReason = null;
+ WxChannelBaseResponse response = afterSaleService.reject(afterSaleOrderId, rejectReason,1);
assertNotNull(response);
assertTrue(response.isSuccess());
}
@@ -109,4 +111,21 @@ public void testGetComplaint() throws WxErrorException {
assertNotNull(response);
assertTrue(response.isSuccess());
}
+
+
+ @Test
+ public void testGetAllReason() throws WxErrorException {
+ WxChannelAfterSaleService afterSaleService = channelService.getAfterSaleService();
+ AfterSaleReasonResponse allReason = afterSaleService.getAllReason();
+ assertNotNull(allReason);
+ assertTrue(allReason.isSuccess());
+ }
+
+ @Test
+ public void testGetRejectReason() throws WxErrorException {
+ WxChannelAfterSaleService afterSaleService = channelService.getAfterSaleService();
+ AfterSaleRejectReasonResponse rejectReason = afterSaleService.getRejectReason();
+ assertNotNull(rejectReason);
+ assertTrue(rejectReason.isSuccess());
+ }
}
From 1a0d8882454ab1028dca9e64dc8998cd9907ec31 Mon Sep 17 00:00:00 2001
From: Sean Sun <1194458432@qq.com>
Date: Tue, 30 Jul 2024 19:24:21 +0800
Subject: [PATCH 030/385] =?UTF-8?q?:new:=20#3339=20=E3=80=90=E4=BC=81?=
=?UTF-8?q?=E4=B8=9A=E5=BE=AE=E4=BF=A1=E3=80=91=E5=A2=9E=E5=8A=A0=E4=B8=8A?=
=?UTF-8?q?=E4=BC=A0=E4=B8=B4=E6=97=B6=E7=B4=A0=E6=9D=90=E7=9A=84=E9=87=8D?=
=?UTF-8?q?=E8=BD=BD=E6=96=B9=E6=B3=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../weixin/cp/api/WxCpMediaService.java | 26 +++++++++++++++++++
.../cp/api/impl/WxCpMediaServiceImpl.java | 24 +++++++++++++++++
2 files changed, 50 insertions(+)
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMediaService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMediaService.java
index d0d4b661b0..82f6db9178 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMediaService.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMediaService.java
@@ -53,6 +53,32 @@ WxMediaUploadResult upload(String mediaType, String fileType, InputStream inputS
WxMediaUploadResult upload(String mediaType, String filename, String url)
throws WxErrorException, IOException;
+ /**
+ *
+ * 上传多媒体文件.
+ *
+ *
+ * @param mediaType 媒体类型, 请看{@link me.chanjar.weixin.common.api.WxConsts}
+ * @param file 文件对象, 上传的文件内容
+ * @param filename 上传内容的实际文件名.例如:wework.txt
+ * @return wx media upload result
+ * @throws WxErrorException the wx error exception
+ */
+ WxMediaUploadResult upload(String mediaType, File file, String filename) throws WxErrorException;
+
+ /**
+ *
+ * 上传多媒体文件.
+ *
+ *
+ * @param mediaType 媒体类型, 请看{@link me.chanjar.weixin.common.api.WxConsts}
+ * @param inputStream 上传的文件内容
+ * @param filename 上传内容的实际文件名.例如:wework.txt
+ * @return wx media upload result
+ * @throws WxErrorException the wx error exception
+ */
+ WxMediaUploadResult upload(String mediaType, InputStream inputStream, String filename) throws WxErrorException;
+
/**
* 上传多媒体文件.
*
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMediaServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMediaServiceImpl.java
index 7953d69e37..863dd7c1d4 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMediaServiceImpl.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMediaServiceImpl.java
@@ -3,6 +3,7 @@
import lombok.RequiredArgsConstructor;
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.common.error.WxRuntimeException;
import me.chanjar.weixin.common.util.fs.FileUtils;
import me.chanjar.weixin.common.util.http.BaseMediaDownloadRequestExecutor;
import me.chanjar.weixin.common.util.http.InputStreamData;
@@ -16,6 +17,7 @@
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
+import java.nio.file.Files;
import java.util.UUID;
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Media.*;
@@ -67,6 +69,28 @@ public WxMediaUploadResult upload(String mediaType, String filename, String url)
}
}
+ @Override
+ public WxMediaUploadResult upload(String mediaType, File file, String filename) throws WxErrorException {
+ if(!file.exists()){
+ throw new WxRuntimeException("文件[" + file.getAbsolutePath() + "]不存在");
+ }
+ try (InputStream inputStream = Files.newInputStream(file.toPath())) {
+ return this.mainService.execute(MediaInputStreamUploadRequestExecutor.create(this.mainService.getRequestHttp())
+ , this.mainService.getWxCpConfigStorage().getApiUrl(MEDIA_UPLOAD + mediaType),
+ new InputStreamData(inputStream, filename));
+ } catch (IOException e) {
+ throw new WxRuntimeException(e);
+ }
+ }
+
+ @Override
+ public WxMediaUploadResult upload(String mediaType, InputStream inputStream, String filename) throws WxErrorException{
+ return this.mainService.execute(MediaInputStreamUploadRequestExecutor.create(this.mainService.getRequestHttp())
+ , this.mainService.getWxCpConfigStorage().getApiUrl(MEDIA_UPLOAD + mediaType),
+ new InputStreamData(inputStream, filename));
+ }
+
+
@Override
public WxMediaUploadResult upload(String mediaType, File file) throws WxErrorException {
return this.mainService.execute(MediaUploadRequestExecutor.create(this.mainService.getRequestHttp()),
From 838fcd0de31010ebb5592d4590aaa2f4d5b21780 Mon Sep 17 00:00:00 2001
From: xxm
Date: Tue, 30 Jul 2024 19:27:36 +0800
Subject: [PATCH 031/385] =?UTF-8?q?:new:=20=20#3340=E3=80=90=E5=BE=AE?=
=?UTF-8?q?=E4=BF=A1=E6=94=AF=E4=BB=98=E3=80=91=E5=A2=9E=E5=8A=A0=E7=9B=B4?=
=?UTF-8?q?=E8=BF=9E=E5=95=86=E6=88=B7=E4=BB=98=E6=AC=BE=E7=A0=81=E6=94=AF?=
=?UTF-8?q?=E4=BB=98=E5=92=8C=E6=92=A4=E9=94=80=E6=94=AF=E4=BB=98=E8=AE=A2?=
=?UTF-8?q?=E5=8D=95=E7=9A=84V3=E7=89=88=E6=8E=A5=E5=8F=A3=E5=AE=9E?=
=?UTF-8?q?=E7=8E=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../bean/request/WxPayCodepayRequest.java | 593 ++++++++++++++++
.../request/WxPayOrderReverseV3Request.java | 59 ++
.../wxpay/bean/result/WxPayCodepayResult.java | 636 ++++++++++++++++++
.../result/WxPayOrderReverseV3Result.java | 57 ++
.../wxpay/service/WxPayService.java | 60 ++
.../service/impl/BaseWxPayServiceImpl.java | 38 ++
6 files changed, 1443 insertions(+)
create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayCodepayRequest.java
create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderReverseV3Request.java
create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayCodepayResult.java
create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderReverseV3Result.java
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayCodepayRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayCodepayRequest.java
new file mode 100644
index 0000000000..ecfa614a16
--- /dev/null
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayCodepayRequest.java
@@ -0,0 +1,593 @@
+package com.github.binarywang.wxpay.bean.request;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * V3付款码支付请求对象类
+ * @author DaxPay
+ * @date 2024/7/29
+ */
+@Data
+@NoArgsConstructor
+@Accessors(chain = true)
+public class WxPayCodepayRequest implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+ /**
+ *
+ * 字段名:应用ID
+ * 变量名:appid
+ * 是否必填:是
+ * 类型:string[1,32]
+ * 描述:
+ * 由微信生成的应用ID,全局唯一。请求统一下单接口时请注意APPID的应用属性,例如公众号场景下,需使用应用属性为公众号的APPID
+ * 示例值:wxd678efh567hg6787
+ *
+ */
+ @SerializedName(value = "appid")
+ protected String appid;
+ /**
+ *
+ * 字段名:直连商户号
+ * 变量名:mchid
+ * 是否必填:是
+ * 类型:string[1,32]
+ * 描述:
+ * 直连商户的商户号,由微信支付生成并下发。
+ * 示例值:1230000109
+ *
+ */
+ @SerializedName(value = "mchid")
+ protected String mchid;
+ /**
+ *
+ * 字段名:商品描述
+ * 变量名:description
+ * 是否必填:是
+ * 类型:string[1,127]
+ * 描述:
+ * 商品描述
+ * 示例值:Image形象店-深圳腾大-QQ公仔
+ *
+ */
+ @SerializedName(value = "description")
+ protected String description;
+ /**
+ *
+ * 字段名:商户订单号
+ * 变量名:out_trade_no
+ * 是否必填:是
+ * 类型:string[6,32]
+ * 描述:
+ * 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一
+ * 示例值:1217752501201407033233368018
+ *
+ */
+ @SerializedName(value = "out_trade_no")
+ protected String outTradeNo;
+
+ /**
+ *
+ * 字段名:微信支付返回的订单号
+ * 变量名:transaction_id
+ * 是否必填:是
+ * 类型:string(32)
+ * 描述:
+ * 微信分配的公众账号ID
+ * 示例值:1000320306201511078440737890
+ *
+ */
+ @SerializedName(value = "transaction_id")
+ private String transactionId;
+ /**
+ *
+ * 字段名:附加数据
+ * 变量名:attach
+ * 是否必填:否
+ * 类型:string[1,128]
+ * 描述:
+ * 附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用
+ * 示例值:自定义数据
+ *
+ */
+ @SerializedName(value = "attach")
+ protected String attach;
+
+ /**
+ *
+ * 字段名:订单优惠标记
+ * 变量名:goods_tag
+ * 是否必填:否
+ * 类型:string[1,256]
+ * 描述:
+ * 订单优惠标记
+ * 示例值:WXG
+ *
+ */
+ @SerializedName(value = "goods_tag")
+ private String goodsTag;
+ /**
+ *
+ * 字段名:电子发票入口开放标识
+ * 变量名:support_fapiao
+ * 是否必填:否
+ * 类型:boolean
+ * 描述:传入true时,支付成功消息和支付详情页将出现开票入口。需要在微信支付商户平台或微信公众平台开通电子发票功能,传此字段才可生效。
+ *
+ */
+ @SerializedName(value = "support_fapiao")
+ private Boolean supportFapiao;
+ /**
+ *
+ * 字段名:支付者
+ * 变量名:payer
+ * 是否必填:是
+ * 类型:object
+ * 描述:
+ * 支付者信息
+ *
+ */
+ @SerializedName(value = "payer")
+ private Payer payer;
+ /**
+ *
+ * 字段名:订单金额
+ * 变量名:amount
+ * 是否必填:是
+ * 类型:object
+ * 描述:
+ * 订单金额信息
+ *
+ */
+ @SerializedName(value = "amount")
+ private Amount amount;
+ /**
+ *
+ * 字段名:场景信息
+ * 变量名:scene_info
+ * 是否必填:否
+ * 类型:object
+ * 描述:
+ * 支付场景描述
+ *
+ */
+ @SerializedName(value = "scene_info")
+ private SceneInfo sceneInfo;
+
+ /**
+ *
+ * 字段名:优惠功能
+ * 变量名:promotion_detail
+ * 是否必填:否
+ * 类型:array
+ * 描述:
+ * 优惠功能,享受优惠时返回该字段。
+ *
+ */
+ @SerializedName(value = "promotion_detail")
+ private List promotionDetails;
+
+ /**
+ *
+ * 字段名:结算信息
+ * 变量名:settle_info
+ * 是否必填:否
+ * 类型:Object
+ * 描述:结算信息
+ *
+ */
+ @SerializedName(value = "settle_info")
+ private SettleInfo settleInfo;
+
+
+ @Data
+ @NoArgsConstructor
+ public static class Amount implements Serializable {
+ private static final long serialVersionUID = 1L;
+ /**
+ *
+ * 字段名:总金额
+ * 变量名:total
+ * 是否必填:否
+ * 类型:int
+ * 描述:
+ * 订单总金额,单位为分。
+ * 示例值:100
+ *
+ */
+ @SerializedName(value = "total")
+ private Integer total;
+ /**
+ *
+ * 字段名:用户支付金额
+ * 变量名:payer_total
+ * 是否必填:否
+ * 类型:int
+ * 描述:
+ * 用户支付金额,单位为分。
+ * 示例值:100
+ *
+ */
+ @SerializedName(value = "payer_total")
+ private Integer payerTotal;
+ /**
+ *
+ * 字段名:货币类型
+ * 变量名:currency
+ * 是否必填:否
+ * 类型:string[1,16]
+ * 描述:
+ * CNY:人民币,境内商户号仅支持人民币。
+ * 示例值:CNY
+ *
+ */
+ @SerializedName(value = "currency")
+ private String currency;
+ /**
+ *
+ * 字段名:用户支付币种
+ * 变量名:payer_currency
+ * 是否必填:否
+ * 类型:string[1,16]
+ * 描述:
+ * 用户支付币种
+ * 示例值: CNY
+ *
+ */
+ @SerializedName(value = "payer_currency")
+ private String payerCurrency;
+ }
+
+ @Data
+ @NoArgsConstructor
+ public static class Payer implements Serializable {
+ private static final long serialVersionUID = -1L;
+ /**
+ *
+ * 字段名:用户标识
+ * 变量名:auth_code
+ * 是否必填:是
+ * 类型:string[32]
+ * 描述:
+ * 付款码支付授权码,即用户打开微信钱包显示的码。
+ * 示例值:130061098828009406
+ *
+ */
+ @SerializedName(value = "auth_code")
+ private String authCode;
+ }
+
+ @Data
+ @NoArgsConstructor
+ public static class SceneInfo implements Serializable {
+ private static final long serialVersionUID = 1L;
+ /**
+ *
+ * 字段名:商户端设备 IP
+ * 变量名:device_ip
+ * 是否必填:是
+ * 类型:string[1,45]
+ * 描述:
+ * 用户的客户端IP,支持IPv4和IPv6两种格式的IP地址。
+ * 示例值:14.23.150.211
+ *
+ */
+ @SerializedName(value = "device_ip")
+ private String deviceIp;
+ /**
+ *
+ * 字段名:商户端设备号
+ * 变量名:device_id
+ * 是否必填:否
+ * 类型:string[1,32]
+ * 描述:
+ * 商户端设备号(门店号或收银设备ID)。
+ * 示例值:013467007045764
+ *
+ */
+ @SerializedName(value = "device_id")
+ private String deviceId;
+ /**
+ *
+ * 字段名:商户门店信息
+ * 变量名:store_info
+ * 是否必填:否
+ * 类型:object
+ * 描述:
+ * 商户门店信息
+ *
+ */
+ @SerializedName(value = "store_info")
+ private StoreInfo storeInfo;
+ }
+
+ /**
+ * 商户门店信息
+ */
+ @Data
+ @NoArgsConstructor
+ public static class StoreInfo implements Serializable {
+ private static final long serialVersionUID = -1L;
+ /**
+ *
+ * 字段名:门店编号
+ * 变量名:id
+ * 是否必填:是
+ * 类型:string[1,32]
+ * 描述:
+ * 此参数与商家自定义编码(out_id)二选一必填。
+ * 微信支付线下场所ID,格式为纯数字。
+ * 基于合规要求与风险管理目的,线下条码支付时需传入用户实际付款的场景信息。
+ * 指引参见:https://kf.qq.com/faq/230817neeaem2308177ZFfqM.html。
+ * 示例值:0001
+ *
+ */
+ @SerializedName(value = "id")
+ private String id;
+ /**
+ *
+ * 字段名:商家自定义编码
+ * 变量名:out_id
+ * 是否必填:否
+ * 类型:string[1,256]
+ * 描述:
+ * 此参数与门店(id)二选一必填。
+ * 商户系统的门店编码,支持大小写英文字母、数字,仅支持utf-8格式。
+ * 基于合规要求与风险管理目的,线下条码支付时需传入用户实际付款的场景信息。
+ * 示例值:A1111
+ *
+ */
+ @SerializedName(value = "out_id")
+ private String outId;
+ }
+
+
+ @Data
+ @NoArgsConstructor
+ public static class SettleInfo implements Serializable {
+ private static final long serialVersionUID = 1L;
+ /**
+ *
+ * 字段名:是否指定分账
+ * 变量名:profit_sharing
+ * 是否必填:否
+ * 类型:boolean
+ * 描述:
+ * 是否指定分账
+ * 示例值:false
+ *
+ */
+ @SerializedName(value = "profit_sharing")
+ private Boolean profitSharing;
+ }
+
+
+ /**
+ * 优惠功能
+ */
+ @Data
+ @NoArgsConstructor
+ public static class PromotionDetail implements Serializable {
+ /**
+ *
+ * 字段名:券ID
+ * 变量名:coupon_id
+ * 是否必填:是
+ * 类型:string[1,32]
+ * 描述:
+ * 券ID
+ * 示例值:109519
+ *
+ */
+ @SerializedName(value = "coupon_id")
+ private String couponId;
+ /**
+ *
+ * 字段名:优惠名称
+ * 变量名:name
+ * 是否必填:否
+ * 类型:string[1,64]
+ * 描述:
+ * 优惠名称
+ * 示例值:单品惠-6
+ *
+ */
+ @SerializedName(value = "name")
+ private String name;
+ /**
+ *
+ * 字段名:优惠范围
+ * 变量名:scope
+ * 是否必填:否
+ * 类型:string[1,32]
+ * 描述:
+ * GLOBAL:全场代金券
+ * SINGLE:单品优惠
+ * 示例值:GLOBAL
+ *
+ */
+ @SerializedName(value = "scope")
+ private String scope;
+ /**
+ *
+ * 字段名:优惠类型
+ * 变量名:type
+ * 是否必填:否
+ * 类型:string[1,32]
+ * 描述:
+ * CASH:充值
+ * NOCASH:预充值
+ * 示例值:CASH
+ *
+ */
+ @SerializedName(value = "type")
+ private String type;
+ /**
+ *
+ * 字段名:优惠券面额
+ * 变量名:amount
+ * 是否必填:是
+ * 类型:int
+ * 描述:
+ * 优惠券面额
+ * 示例值:100
+ *
+ */
+ @SerializedName(value = "amount")
+ private Integer amount;
+ /**
+ *
+ * 字段名:活动ID
+ * 变量名:stock_id
+ * 是否必填:否
+ * 类型:string[1,32]
+ * 描述:
+ * 活动ID
+ * 示例值:931386
+ *
+ */
+ @SerializedName(value = "stock_id")
+ private String stockId;
+ /**
+ *
+ * 字段名:微信出资
+ * 变量名:wechatpay_contribute
+ * 是否必填:否
+ * 类型:int
+ * 描述:
+ * 微信出资,单位为分
+ * 示例值:0
+ *
+ */
+ @SerializedName(value = "wechatpay_contribute")
+ private Integer wechatpayContribute;
+ /**
+ *
+ * 字段名:商户出资
+ * 变量名:merchant_contribute
+ * 是否必填:否
+ * 类型:int
+ * 描述:
+ * 商户出资,单位为分
+ * 示例值:0
+ *
+ */
+ @SerializedName(value = "merchant_contribute")
+ private Integer merchantContribute;
+ /**
+ *
+ * 字段名:其他出资
+ * 变量名:other_contribute
+ * 是否必填:否
+ * 类型:int
+ * 描述:
+ * 其他出资,单位为分
+ * 示例值:0
+ *
+ */
+ @SerializedName(value = "other_contribute")
+ private Integer otherContribute;
+ /**
+ *
+ * 字段名:优惠币种
+ * 变量名:currency
+ * 是否必填:否
+ * 类型:string[1,16]
+ * 描述:
+ * CNY:人民币,境内商户号仅支持人民币。
+ * 示例值:CNY
+ *
+ */
+ @SerializedName(value = "currency")
+ private String currency;
+ /**
+ *
+ * 字段名:单品列表
+ * 变量名:goods_detail
+ * 是否必填:否
+ * 类型:array
+ * 描述:
+ * 单品列表信息
+ *
+ */
+ @SerializedName(value = "goods_detail")
+ private List goodsDetails;
+ }
+
+ @Data
+ @NoArgsConstructor
+ public static class GoodsDetail implements Serializable {
+ private static final long serialVersionUID = 1L;
+ /**
+ *
+ * 字段名:商品编码
+ * 变量名:goods_id
+ * 是否必填:是
+ * 类型:string[1,32]
+ * 描述:
+ * 商品编码
+ * 示例值:M1006
+ *
+ */
+ @SerializedName(value = "goods_id")
+ private String goodsId;
+ /**
+ *
+ * 字段名:商品数量
+ * 变量名:quantity
+ * 是否必填:是
+ * 类型:int
+ * 描述:
+ * 用户购买的数量
+ * 示例值:1
+ *
+ */
+ @SerializedName(value = "quantity")
+ private Integer quantity;
+ /**
+ *
+ * 字段名:商品单价
+ * 变量名:unit_price
+ * 是否必填:是
+ * 类型:int
+ * 描述:
+ * 商品单价,单位为分
+ * 示例值:100
+ *
+ */
+ @SerializedName(value = "unit_price")
+ private Integer unitPrice;
+ /**
+ *
+ * 字段名:商品优惠金额
+ * 变量名:discount_amount
+ * 是否必填:是
+ * 类型:int
+ * 描述:
+ * 商品优惠金额
+ * 示例值:0
+ *
+ */
+ @SerializedName(value = "discount_amount")
+ private Integer discountAmount;
+ /**
+ *
+ * 字段名:商品备注
+ * 变量名:goods_remark
+ * 是否必填:否
+ * 类型:string[1,128]
+ * 描述:
+ * 商品备注信息
+ * 示例值:商品备注信息
+ *
+ */
+ @SerializedName(value = "goods_remark")
+ private String goodsRemark;
+ }
+}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderReverseV3Request.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderReverseV3Request.java
new file mode 100644
index 0000000000..2505d6130e
--- /dev/null
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderReverseV3Request.java
@@ -0,0 +1,59 @@
+package com.github.binarywang.wxpay.bean.request;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+
+/**
+ * V3撤销订单请求对象类
+ * @author DaxPay
+ * @date 2024/7/29
+ */
+@Data
+@NoArgsConstructor
+@Accessors(chain = true)
+public class WxPayOrderReverseV3Request implements Serializable {
+ private static final long serialVersionUID = 1L;
+ /**
+ *
+ * 字段名:应用ID
+ * 变量名:appid
+ * 是否必填:是
+ * 类型:string[1,32]
+ * 描述:
+ * 由微信生成的应用ID,全局唯一。请求统一下单接口时请注意APPID的应用属性,例如公众号场景下,需使用应用属性为公众号的APPID
+ * 示例值:wxd678efh567hg6787
+ *
+ */
+ @SerializedName(value = "appid")
+ protected String appid;
+ /**
+ *
+ * 字段名:直连商户号
+ * 变量名:mchid
+ * 是否必填:是
+ * 类型:string[1,32]
+ * 描述:
+ * 直连商户的商户号,由微信支付生成并下发。
+ * 示例值:1230000109
+ *
+ */
+ @SerializedName(value = "mchid")
+ protected String mchid;
+
+ /**
+ *
+ * 字段名:商户订单号
+ * 变量名:out_trade_no
+ * 是否必填:是
+ * 类型:string[6,32]
+ * 描述:
+ * 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一
+ * 示例值:1217752501201407033233368018
+ *
+ */
+ private transient String outTradeNo;
+}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayCodepayResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayCodepayResult.java
new file mode 100644
index 0000000000..ad6a8be705
--- /dev/null
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayCodepayResult.java
@@ -0,0 +1,636 @@
+package com.github.binarywang.wxpay.bean.result;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 微信V3付款码返回结果
+ * @author DaxPay
+ * @date 2024/7/29
+ */
+@Data
+@Accessors(chain = true)
+public class WxPayCodepayResult implements Serializable {
+ private static final long serialVersionUID = 1L;
+ /**
+ *
+ * 字段名:应用ID
+ * 变量名:appid
+ * 是否必填:是
+ * 类型:string[1,32]
+ * 描述:
+ * 由微信生成的应用ID,全局唯一。请求统一下单接口时请注意APPID的应用属性,例如公众号场景下,需使用应用属性为公众号的APPID
+ * 示例值:wxd678efh567hg6787
+ *
+ */
+ @SerializedName(value = "appid")
+ protected String appid;
+ /**
+ *
+ * 字段名:直连商户号
+ * 变量名:mchid
+ * 是否必填:是
+ * 类型:string[1,32]
+ * 描述:
+ * 直连商户的商户号,由微信支付生成并下发。
+ * 示例值:1230000109
+ *
+ */
+ @SerializedName(value = "mchid")
+ protected String mchid;
+ /**
+ *
+ * 字段名:商户订单号
+ * 变量名:out_trade_no
+ * 是否必填:是
+ * 类型:string[6,32]
+ * 描述:
+ * 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一
+ * 示例值:1217752501201407033233368018
+ *
+ */
+ @SerializedName(value = "out_trade_no")
+ protected String outTradeNo;
+
+ /**
+ *
+ * 字段名:微信支付返回的订单号
+ * 变量名:transaction_id
+ * 是否必填:是
+ * 类型:string(32)
+ * 描述:
+ * 微信分配的公众账号ID
+ * 示例值:1000320306201511078440737890
+ *
+ */
+ @SerializedName(value = "transaction_id")
+ private String transactionId;
+ /**
+ *
+ * 字段名:交易类型
+ * 变量名:trade_type
+ * 是否必填:是
+ * 类型:string[1,16]
+ * 描述:
+ * 枚举值:
+ * NATIVE:扫码支付
+ * JSAPI:公众号支付
+ * APP:APP支付
+ * MWEB:H5支付
+ * 示例值: JSAPI
+ *
+ */
+ @SerializedName(value = "trade_type")
+ private String tradeType;
+ /**
+ *
+ * 字段名:付款银行
+ * 变量名:bank_type
+ * 是否必填:否
+ * 类型:string(16)
+ * 描述:
+ * 银行类型,采用字符串类型的银行标识。
+ * 示例值:CMC
+ *
+ */
+ @SerializedName(value = "bank_type")
+ private String bankType;
+ /**
+ *
+ * 字段名:支付完成时间
+ * 变量名:success_time
+ * 是否必填:否
+ * 类型:string(64)
+ * 描述:支付完成时间,遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss+TIMEZONE,YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss表示时分秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。例如:2015-05-20T13:29:35+08:00表示,北京时间2015年5月20日 13点29分35秒。
+ * 示例值:2018-06-08T10:34:56+08:00
+ *
+ */
+ @SerializedName(value = "success_time")
+ private String successTime;
+ /**
+ *
+ * 字段名:交易状态
+ * 变量名:trade_state
+ * 是否必填:是
+ * 类型:string[1,32]
+ * 描述:
+ * 交易状态,枚举值:
+ * SUCCESS:支付成功
+ * REFUND:转入退款
+ * NOTPAY:未支付
+ * REVOKED:已撤销(付款码支付)
+ * USERPAYING:用户支付中(付款码支付)
+ * PAYERROR:支付失败(其他原因,如银行返回失败)
+ * 示例值:SUCCESS
+ *
+ */
+ @SerializedName(value = "trade_state")
+ private String tradeState;
+ /**
+ *
+ * 字段名:交易状态描述
+ * 变量名:trade_state_desc
+ * 是否必填:是
+ * 类型:string(256)
+ * 描述:交易状态描述
+ * 示例值:支付失败,请重新下单支付
+ *
+ */
+ @SerializedName(value = "trade_state_desc")
+ private String tradeStateDesc;
+ /**
+ *
+ * 字段名:附加数据
+ * 变量名:attach
+ * 是否必填:否
+ * 类型:string[1,128]
+ * 描述:
+ * 附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用
+ * 示例值:自定义数据
+ *
+ */
+ @SerializedName(value = "attach")
+ protected String attach;
+
+ /**
+ *
+ * 字段名:订单优惠标记
+ * 变量名:goods_tag
+ * 是否必填:否
+ * 类型:string[1,256]
+ * 描述:
+ * 订单优惠标记
+ * 示例值:WXG
+ *
+ */
+ @SerializedName(value = "goods_tag")
+ private String goodsTag;
+ /**
+ *
+ * 字段名:电子发票入口开放标识
+ * 变量名:support_fapiao
+ * 是否必填:否
+ * 类型:boolean
+ * 描述:传入true时,支付成功消息和支付详情页将出现开票入口。需要在微信支付商户平台或微信公众平台开通电子发票功能,传此字段才可生效。
+ *
+ */
+ @SerializedName(value = "support_fapiao")
+ private Boolean supportFapiao;
+ /**
+ *
+ * 字段名:支付者
+ * 变量名:payer
+ * 是否必填:是
+ * 类型:object
+ * 描述:
+ * 支付者信息
+ *
+ */
+ @SerializedName(value = "payer")
+ private Payer payer;
+ /**
+ *
+ * 字段名:订单金额
+ * 变量名:amount
+ * 是否必填:是
+ * 类型:object
+ * 描述:
+ * 订单金额信息
+ *
+ */
+ @SerializedName(value = "amount")
+ private Amount amount;
+ /**
+ *
+ * 字段名:场景信息
+ * 变量名:scene_info
+ * 是否必填:否
+ * 类型:object
+ * 描述:
+ * 支付场景描述
+ *
+ */
+ @SerializedName(value = "scene_info")
+ private SceneInfo sceneInfo;
+
+ /**
+ *
+ * 字段名:优惠功能
+ * 变量名:promotion_detail
+ * 是否必填:否
+ * 类型:array
+ * 描述:
+ * 优惠功能,享受优惠时返回该字段。
+ *
+ */
+ @SerializedName(value = "promotion_detail")
+ private List promotionDetails;
+
+ @Data
+ @NoArgsConstructor
+ public static class Amount implements Serializable {
+ private static final long serialVersionUID = 1L;
+ /**
+ *
+ * 字段名:总金额
+ * 变量名:total
+ * 是否必填:否
+ * 类型:int
+ * 描述:
+ * 订单总金额,单位为分。
+ * 示例值:100
+ *
+ */
+ @SerializedName(value = "total")
+ private Integer total;
+ /**
+ *
+ * 字段名:用户支付金额
+ * 变量名:payer_total
+ * 是否必填:否
+ * 类型:int
+ * 描述:
+ * 用户支付金额,单位为分。
+ * 示例值:100
+ *
+ */
+ @SerializedName(value = "payer_total")
+ private Integer payerTotal;
+ /**
+ *
+ * 字段名:货币类型
+ * 变量名:currency
+ * 是否必填:否
+ * 类型:string[1,16]
+ * 描述:
+ * CNY:人民币,境内商户号仅支持人民币。
+ * 示例值:CNY
+ *
+ */
+ @SerializedName(value = "currency")
+ private String currency;
+ /**
+ *
+ * 字段名:用户支付币种
+ * 变量名:payer_currency
+ * 是否必填:否
+ * 类型:string[1,16]
+ * 描述:
+ * 用户支付币种
+ * 示例值: CNY
+ *
+ */
+ @SerializedName(value = "payer_currency")
+ private String payerCurrency;
+ }
+
+ @Data
+ @NoArgsConstructor
+ public static class Payer implements Serializable {
+ private static final long serialVersionUID = -1L;
+ /**
+ *
+ * 字段名:用户标识
+ * 变量名:auth_code
+ * 是否必填:是
+ * 类型:string[32]
+ * 描述:
+ * 付款码支付授权码,即用户打开微信钱包显示的码。
+ * 示例值:130061098828009406
+ *
+ */
+ @SerializedName(value = "auth_code")
+ private String authCode;
+ }
+
+ @Data
+ @NoArgsConstructor
+ public static class SceneInfo implements Serializable {
+ private static final long serialVersionUID = 1L;
+ /**
+ *
+ * 字段名:商户端设备 IP
+ * 变量名:device_ip
+ * 是否必填:是
+ * 类型:string[1,45]
+ * 描述:
+ * 用户的客户端IP,支持IPv4和IPv6两种格式的IP地址。
+ * 示例值:14.23.150.211
+ *
+ */
+ @SerializedName(value = "device_ip")
+ private String deviceIp;
+ /**
+ *
+ * 字段名:商户端设备号
+ * 变量名:device_id
+ * 是否必填:否
+ * 类型:string[1,32]
+ * 描述:
+ * 商户端设备号(门店号或收银设备ID)。
+ * 示例值:013467007045764
+ *
+ */
+ @SerializedName(value = "device_id")
+ private String deviceId;
+ /**
+ *
+ * 字段名:商户门店信息
+ * 变量名:store_info
+ * 是否必填:否
+ * 类型:object
+ * 描述:
+ * 商户门店信息
+ *
+ */
+ @SerializedName(value = "store_info")
+ private StoreInfo storeInfo;
+ }
+
+ /**
+ * 商户门店信息
+ */
+ @Data
+ @NoArgsConstructor
+ public static class StoreInfo implements Serializable {
+ private static final long serialVersionUID = -1L;
+ /**
+ *
+ * 字段名:门店编号
+ * 变量名:id
+ * 是否必填:是
+ * 类型:string[1,32]
+ * 描述:
+ * 此参数与商家自定义编码(out_id)二选一必填。
+ * 微信支付线下场所ID,格式为纯数字。
+ * 基于合规要求与风险管理目的,线下条码支付时需传入用户实际付款的场景信息。
+ * 指引参见:https://kf.qq.com/faq/230817neeaem2308177ZFfqM.html。
+ * 示例值:0001
+ *
+ */
+ @SerializedName(value = "id")
+ private String id;
+ /**
+ *
+ * 字段名:商家自定义编码
+ * 变量名:out_id
+ * 是否必填:否
+ * 类型:string[1,256]
+ * 描述:
+ * 此参数与门店(id)二选一必填。
+ * 商户系统的门店编码,支持大小写英文字母、数字,仅支持utf-8格式。
+ * 基于合规要求与风险管理目的,线下条码支付时需传入用户实际付款的场景信息。
+ * 示例值:A1111
+ *
+ */
+ @SerializedName(value = "out_id")
+ private String outId;
+ }
+
+
+ @Data
+ @NoArgsConstructor
+ public static class SettleInfo implements Serializable {
+ private static final long serialVersionUID = 1L;
+ /**
+ *
+ * 字段名:是否指定分账
+ * 变量名:profit_sharing
+ * 是否必填:否
+ * 类型:boolean
+ * 描述:
+ * 是否指定分账
+ * 示例值:false
+ *
+ */
+ @SerializedName(value = "profit_sharing")
+ private Boolean profitSharing;
+ }
+
+
+ @Data
+ @NoArgsConstructor
+ public static class PromotionDetail implements Serializable {
+ /**
+ *
+ * 字段名:券ID
+ * 变量名:coupon_id
+ * 是否必填:是
+ * 类型:string[1,32]
+ * 描述:
+ * 券ID
+ * 示例值:109519
+ *
+ */
+ @SerializedName(value = "coupon_id")
+ private String couponId;
+ /**
+ *
+ * 字段名:优惠名称
+ * 变量名:name
+ * 是否必填:否
+ * 类型:string[1,64]
+ * 描述:
+ * 优惠名称
+ * 示例值:单品惠-6
+ *
+ */
+ @SerializedName(value = "name")
+ private String name;
+ /**
+ *
+ * 字段名:优惠范围
+ * 变量名:scope
+ * 是否必填:否
+ * 类型:string[1,32]
+ * 描述:
+ * GLOBAL:全场代金券
+ * SINGLE:单品优惠
+ * 示例值:GLOBAL
+ *
+ */
+ @SerializedName(value = "scope")
+ private String scope;
+ /**
+ *
+ * 字段名:优惠类型
+ * 变量名:type
+ * 是否必填:否
+ * 类型:string[1,32]
+ * 描述:
+ * CASH:充值
+ * NOCASH:预充值
+ * 示例值:CASH
+ *
+ */
+ @SerializedName(value = "type")
+ private String type;
+ /**
+ *
+ * 字段名:优惠券面额
+ * 变量名:amount
+ * 是否必填:是
+ * 类型:int
+ * 描述:
+ * 优惠券面额
+ * 示例值:100
+ *
+ */
+ @SerializedName(value = "amount")
+ private Integer amount;
+ /**
+ *
+ * 字段名:活动ID
+ * 变量名:stock_id
+ * 是否必填:否
+ * 类型:string[1,32]
+ * 描述:
+ * 活动ID
+ * 示例值:931386
+ *
+ */
+ @SerializedName(value = "stock_id")
+ private String stockId;
+ /**
+ *
+ * 字段名:微信出资
+ * 变量名:wechatpay_contribute
+ * 是否必填:否
+ * 类型:int
+ * 描述:
+ * 微信出资,单位为分
+ * 示例值:0
+ *
+ */
+ @SerializedName(value = "wechatpay_contribute")
+ private Integer wechatpayContribute;
+ /**
+ *
+ * 字段名:商户出资
+ * 变量名:merchant_contribute
+ * 是否必填:否
+ * 类型:int
+ * 描述:
+ * 商户出资,单位为分
+ * 示例值:0
+ *
+ */
+ @SerializedName(value = "merchant_contribute")
+ private Integer merchantContribute;
+ /**
+ *
+ * 字段名:其他出资
+ * 变量名:other_contribute
+ * 是否必填:否
+ * 类型:int
+ * 描述:
+ * 其他出资,单位为分
+ * 示例值:0
+ *
+ */
+ @SerializedName(value = "other_contribute")
+ private Integer otherContribute;
+ /**
+ *
+ * 字段名:优惠币种
+ * 变量名:currency
+ * 是否必填:否
+ * 类型:string[1,16]
+ * 描述:
+ * CNY:人民币,境内商户号仅支持人民币。
+ * 示例值:CNY
+ *
+ */
+ @SerializedName(value = "currency")
+ private String currency;
+ /**
+ *
+ * 字段名:单品列表
+ * 变量名:goods_detail
+ * 是否必填:否
+ * 类型:array
+ * 描述:
+ * 单品列表信息
+ *
+ */
+ @SerializedName(value = "goods_detail")
+ private List goodsDetails;
+ }
+
+ @Data
+ @NoArgsConstructor
+ public static class GoodsDetail implements Serializable {
+ private static final long serialVersionUID = 1L;
+ /**
+ *
+ * 字段名:商品编码
+ * 变量名:goods_id
+ * 是否必填:是
+ * 类型:string[1,32]
+ * 描述:
+ * 商品编码
+ * 示例值:M1006
+ *
+ */
+ @SerializedName(value = "goods_id")
+ private String goodsId;
+ /**
+ *
+ * 字段名:商品数量
+ * 变量名:quantity
+ * 是否必填:是
+ * 类型:int
+ * 描述:
+ * 用户购买的数量
+ * 示例值:1
+ *
+ */
+ @SerializedName(value = "quantity")
+ private Integer quantity;
+ /**
+ *
+ * 字段名:商品单价
+ * 变量名:unit_price
+ * 是否必填:是
+ * 类型:int
+ * 描述:
+ * 商品单价,单位为分
+ * 示例值:100
+ *
+ */
+ @SerializedName(value = "unit_price")
+ private Integer unitPrice;
+ /**
+ *
+ * 字段名:商品优惠金额
+ * 变量名:discount_amount
+ * 是否必填:是
+ * 类型:int
+ * 描述:
+ * 商品优惠金额
+ * 示例值:0
+ *
+ */
+ @SerializedName(value = "discount_amount")
+ private Integer discountAmount;
+ /**
+ *
+ * 字段名:商品备注
+ * 变量名:goods_remark
+ * 是否必填:否
+ * 类型:string[1,128]
+ * 描述:
+ * 商品备注信息
+ * 示例值:商品备注信息
+ *
+ */
+ @SerializedName(value = "goods_remark")
+ private String goodsRemark;
+ }
+}
+
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderReverseV3Result.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderReverseV3Result.java
new file mode 100644
index 0000000000..bcc990face
--- /dev/null
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderReverseV3Result.java
@@ -0,0 +1,57 @@
+package com.github.binarywang.wxpay.bean.result;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+
+/**
+ * 微信V3撤销支付订单返回结果
+ * @author DaxPay
+ * @date 2024/7/29
+ */
+@Data
+@Accessors(chain = true)
+public class WxPayOrderReverseV3Result implements Serializable {
+ private static final long serialVersionUID = 1L;
+ /**
+ *
+ * 字段名:应用ID
+ * 变量名:appid
+ * 是否必填:是
+ * 类型:string[1,32]
+ * 描述:
+ * 由微信生成的应用ID,全局唯一。请求统一下单接口时请注意APPID的应用属性,例如公众号场景下,需使用应用属性为公众号的APPID
+ * 示例值:wxd678efh567hg6787
+ *
+ */
+ @SerializedName(value = "appid")
+ protected String appid;
+ /**
+ *
+ * 字段名:直连商户号
+ * 变量名:mchid
+ * 是否必填:是
+ * 类型:string[1,32]
+ * 描述:
+ * 直连商户的商户号,由微信支付生成并下发。
+ * 示例值:1230000109
+ *
+ */
+ @SerializedName(value = "mchid")
+ protected String mchid;
+ /**
+ *
+ * 字段名:商户订单号
+ * 变量名:out_trade_no
+ * 是否必填:是
+ * 类型:string[6,32]
+ * 描述:
+ * 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一
+ * 示例值:1217752501201407033233368018
+ *
+ */
+ @SerializedName(value = "out_trade_no")
+ protected String outTradeNo;
+}
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 b73029f4e1..57c2937c62 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
@@ -1275,6 +1275,25 @@ WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, Stri
*/
WxPayMicropayResult micropay(WxPayMicropayRequest request) throws WxPayException;
+ /**
+ *
+ * 付款码支付API.
+ * 文档地址:https://pay.weixin.qq.com/docs/merchant/apis/code-payment-v3/direct/code-pay.html
+ * 应用场景:
+ * 收银员使用扫码设备读取微信用户付款码以后,二维码或条码信息会传送至商户收银台,由商户收银台或者商户后台调用该接口发起支付。
+ * 提醒1:提交支付请求后微信会同步返回支付结果。当返回结果为“系统错误”时,商户系统等待5秒后调用【查询订单API】,查询支付实际交易结果;当返回结果为“USERPAYING”时,商户系统可设置间隔时间(建议10秒)重新查询支付结果,直到支付成功或超时(建议30秒);
+ * 提醒2:在调用查询接口返回后,如果交易状况不明晰,请调用【撤销订单API】,此时如果交易失败则关闭订单,该单不能再支付成功;如果交易成功,则将扣款退回到用户账户。当撤销无返回或错误时,请再次调用。注意:请勿扣款后立即调用【撤销订单API】,建议至少15秒后再调用。撤销订单API需要双向证书。
+ * 接口地址: https://pay.weixin.qq.com/docs/merchant/apis/code-payment-v3/direct/reverse.html
+ * 是否需要证书:不需要。
+ *
+ *
+ * @param request the request
+ * @return the wx codepay result
+ * @throws WxPayException the wx pay exception
+ */
+ WxPayCodepayResult codepay(WxPayCodepayRequest request) throws WxPayException;
+
+
/**
*
* 撤销订单API.
@@ -1295,6 +1314,47 @@ WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, Stri
*/
WxPayOrderReverseResult reverseOrder(WxPayOrderReverseRequest request) throws WxPayException;
+ /**
+ *
+ * 撤销订单API.
+ * 文档地址:https://pay.weixin.qq.com/docs/merchant/apis/code-payment-v3/direct/reverse.html
+ * 应用场景:
+ * 支付交易返回失败或支付系统超时,调用该接口撤销交易。如果此订单用户支付失败,微信支付系统会将此订单关闭;
+ * 如果用户支付成功,微信支付系统会将此订单资金退还给用户。
+ * 注意:7天以内的交易单可调用撤销,其他正常支付的单如需实现相同功能请调用申请退款API。
+ * 提交支付交易后调用【查询订单API】,没有明确的支付结果再调用【撤销订单API】。
+ * 调用支付接口后请勿立即调用撤销订单API,建议支付后至少15s后再调用撤销订单接口。
+ * 接口链接 :https://pay.weixin.qq.com/docs/merchant/apis/code-payment-v3/direct/reverse.html
+ * 是否需要证书:请求需要双向证书。
+ *
+ *
+ * @param request the request
+ * @return the wx pay order reverse result
+ * @throws WxPayException the wx pay exception
+ */
+ WxPayOrderReverseV3Result reverseOrderV3(WxPayOrderReverseV3Request request) throws WxPayException;
+
+ /**
+ *
+ * 撤销订单API.
+ * 文档地址:https://pay.weixin.qq.com/docs/merchant/apis/code-payment-v3/direct/reverse.html
+ * 应用场景:
+ * 支付交易返回失败或支付系统超时,调用该接口撤销交易。如果此订单用户支付失败,微信支付系统会将此订单关闭;
+ * 如果用户支付成功,微信支付系统会将此订单资金退还给用户。
+ * 注意:7天以内的交易单可调用撤销,其他正常支付的单如需实现相同功能请调用申请退款API。
+ * 提交支付交易后调用【查询订单API】,没有明确的支付结果再调用【撤销订单API】。
+ * 调用支付接口后请勿立即调用撤销订单API,建议支付后至少15s后再调用撤销订单接口。
+ * 接口链接 :https://pay.weixin.qq.com/docs/merchant/apis/code-payment-v3/direct/reverse.html
+ * 是否需要证书:请求需要双向证书。
+ *
+ *
+ * @param outTradeNo 商户系统内部的订单号
+ * @return the wx pay order reverse result
+ * @throws WxPayException the wx pay exception
+ */
+ WxPayOrderReverseV3Result reverseOrderV3(String outTradeNo) throws WxPayException;
+
+
/**
*
* 转换短链接.
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java
index 1187880cb6..851040dd13 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
@@ -1130,6 +1130,19 @@ public WxPayMicropayResult micropay(WxPayMicropayRequest request) throws WxPayEx
return result;
}
+ @Override
+ public WxPayCodepayResult codepay(WxPayCodepayRequest request) throws WxPayException {
+ if (StringUtils.isBlank(request.getAppid())) {
+ request.setAppid(this.getConfig().getAppId());
+ }
+ if (StringUtils.isBlank(request.getMchid())) {
+ request.setMchid(this.getConfig().getMchId());
+ }
+ String url = String.format("%s/v3/pay/transactions/codepay", this.getPayBaseUrl());
+ String body = this.postV3(url, GSON.toJson(request));
+ return GSON.fromJson(body, WxPayCodepayResult.class);
+ }
+
@Override
public WxPayOrderReverseResult reverseOrder(WxPayOrderReverseRequest request) throws WxPayException {
request.checkAndSign(this.getConfig());
@@ -1141,6 +1154,31 @@ public WxPayOrderReverseResult reverseOrder(WxPayOrderReverseRequest request) th
return result;
}
+
+ @Override
+ public WxPayOrderReverseV3Result reverseOrderV3(WxPayOrderReverseV3Request request) throws WxPayException {
+ if (StringUtils.isBlank(request.getAppid())) {
+ request.setAppid(this.getConfig().getAppId());
+ }
+ if (StringUtils.isBlank(request.getMchid())) {
+ request.setMchid(this.getConfig().getMchId());
+ }
+ // 拼接参数请求路径并发送
+ String url = String.format("%s/v3/pay/transactions/out-trade-no/%s/reverse", this.getPayBaseUrl(), request.getOutTradeNo());
+ String response = this.postV3(url, GSON.toJson(request));
+ return GSON.fromJson(response, WxPayOrderReverseV3Result.class);
+ }
+
+ @Override
+ public WxPayOrderReverseV3Result reverseOrderV3(String outTradeNo) throws WxPayException {
+ if (StringUtils.isBlank(outTradeNo)) {
+ throw new WxPayException("out_trade_no不能为空");
+ }
+ WxPayOrderReverseV3Request request = new WxPayOrderReverseV3Request();
+ request.setOutTradeNo(StringUtils.trimToNull(outTradeNo));
+ return this.reverseOrderV3(request);
+ }
+
@Override
public String shorturl(WxPayShorturlRequest request) throws WxPayException {
request.checkAndSign(this.getConfig());
From 7e0af32529ff595387f5f0e49f30a3bc4ba9cfd7 Mon Sep 17 00:00:00 2001
From: Macro <715292834@qq.com>
Date: Wed, 7 Aug 2024 12:41:00 +0800
Subject: [PATCH 032/385] =?UTF-8?q?:art:=20#3333=20=E3=80=90=E4=BC=81?=
=?UTF-8?q?=E4=B8=9A=E5=BE=AE=E4=BF=A1=E3=80=91=E8=AE=BE=E7=BD=AE=E5=BA=94?=
=?UTF-8?q?=E7=94=A8=E5=9C=A8=E7=94=A8=E6=88=B7=E5=B7=A5=E4=BD=9C=E5=8F=B0?=
=?UTF-8?q?=E5=B1=95=E7=A4=BA=E7=9A=84webview=E5=9E=8B=E6=95=B0=E6=8D=AE?=
=?UTF-8?q?=E6=97=B6=EF=BC=8C=E6=94=AF=E6=8C=81enable=5Fwebview=5Fclick?=
=?UTF-8?q?=E5=8F=82=E6=95=B0=E8=AE=BE=E7=BD=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../me/chanjar/weixin/cp/bean/WxCpAgentWorkBench.java | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpAgentWorkBench.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpAgentWorkBench.java
index a2737f7237..e74173ee3f 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpAgentWorkBench.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpAgentWorkBench.java
@@ -53,6 +53,11 @@ public class WxCpAgentWorkBench implements Serializable {
* 是否覆盖用户工作台的数据。设置为true的时候,会覆盖企业所有用户当前设置的数据。若设置为false,则不会覆盖用户当前设置的所有数据
*/
private Boolean replaceUserData;
+ /**
+ * 是否开启webview内的链接跳转能力,默认值为false。注意:开启之后,会使jump_url失效。 链接跳转仅支持以下schema方式:wxwork://openurl?url=xxxx,注意url需要进行编码。
+ * 参考示例:今日要闻
+ */
+ private Boolean enableWebviewClick;
private List keyDataList;
@@ -135,6 +140,9 @@ private void handle(JsonObject templateObject) {
webview.addProperty("url", this.url);
webview.addProperty("jump_url", this.jumpUrl);
webview.addProperty("pagepath", this.pagePath);
+ if (null != this.enableWebviewClick) {
+ webview.addProperty("enable_webview_click", this.enableWebviewClick);
+ }
templateObject.add("webview", webview);
break;
}
From 8d108a04674a337847c8996d2a7aa4aec6535375 Mon Sep 17 00:00:00 2001
From: chenquan
Date: Sun, 11 Aug 2024 10:12:34 +0800
Subject: [PATCH 033/385] =?UTF-8?q?:art:=20#3343=20=E3=80=90=E5=B0=8F?=
=?UTF-8?q?=E7=A8=8B=E5=BA=8F=E3=80=91=E5=AA=92=E8=B5=84=E7=AE=A1=E7=90=86?=
=?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=AD=97=E6=AE=B5status=E4=BB=A5=E6=94=AF?=
=?UTF-8?q?=E6=8C=81=E5=AE=A1=E6=A0=B8=E7=8A=B6=E6=80=81=E5=B1=95=E7=A4=BA?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../cn/binarywang/wx/miniapp/bean/vod/WxMaVodDramaInfo.java | 2 ++
1 file changed, 2 insertions(+)
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/vod/WxMaVodDramaInfo.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/vod/WxMaVodDramaInfo.java
index 405f8e37c8..3f2054518c 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/vod/WxMaVodDramaInfo.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/vod/WxMaVodDramaInfo.java
@@ -36,6 +36,8 @@ public class WxMaVodDramaInfo implements Serializable {
private String productionLicense;
@SerializedName("description")
private String description;
+ @SerializedName("status")
+ private String status;
@SerializedName("audit_detail")
private DramaAuditDetail auditDetail;
From 606e932647e74fd4e4a7aeb8eeffb91ade7d4c34 Mon Sep 17 00:00:00 2001
From: Molzx <31435895+Molzx@users.noreply.github.com>
Date: Thu, 15 Aug 2024 22:07:20 +0800
Subject: [PATCH 034/385] =?UTF-8?q?:new:=20#3347=20=E3=80=90=E5=BC=80?=
=?UTF-8?q?=E6=94=BE=E5=B9=B3=E5=8F=B0=E3=80=91=E6=96=B0=E5=A2=9E=E5=B0=8F?=
=?UTF-8?q?=E7=A8=8B=E5=BA=8F=E5=A4=87=E6=A1=88=E7=9B=B8=E5=85=B3=E6=8E=A5?=
=?UTF-8?q?=E5=8F=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../weixin/open/api/WxOpenMaIcpService.java | 207 +++++++
.../weixin/open/api/WxOpenMaService.java | 7 +
.../open/api/impl/WxOpenMaIcpServiceImpl.java | 215 +++++++
.../open/api/impl/WxOpenMaServiceImpl.java | 3 +
.../open/bean/CommonUploadMultiParam.java | 39 ++
.../bean/icp/WxOpenApplyIcpFilingParam.java | 557 ++++++++++++++++++
.../bean/icp/WxOpenApplyIcpFilingResult.java | 40 ++
.../WxOpenIcpCreateIcpVerifyTaskResult.java | 28 +
.../bean/icp/WxOpenIcpEntranceInfoResult.java | 92 +++
.../bean/icp/WxOpenIcpVerifyTaskResult.java | 33 ++
.../bean/icp/WxOpenOnlineIcpOrderResult.java | 36 ++
.../WxOpenQueryIcpCertificateTypeResult.java | 50 ++
.../icp/WxOpenQueryIcpDistrictCodeResult.java | 58 ++
.../icp/WxOpenQueryIcpNrlxTypesResult.java | 50 ++
...OpenQueryIcpServiceContentTypesResult.java | 60 ++
.../icp/WxOpenQueryIcpSubjectTypeResult.java | 53 ++
.../bean/icp/WxOpenUploadIcpMediaParam.java | 64 ++
.../bean/icp/WxOpenUploadIcpMediaResult.java | 40 ++
.../open/bean/message/WxOpenXmlMessage.java | 31 +
.../CommonUploadMultiRequestExecutor.java | 49 ++
...nUploadMultiRequestExecutorApacheImpl.java | 95 +++
...ploadMultiRequestExecutorJoddHttpImpl.java | 109 ++++
...nUploadMultiRequestExecutorOkHttpImpl.java | 104 ++++
23 files changed, 2020 insertions(+)
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenMaIcpService.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMaIcpServiceImpl.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/CommonUploadMultiParam.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenApplyIcpFilingParam.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenApplyIcpFilingResult.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenIcpCreateIcpVerifyTaskResult.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenIcpEntranceInfoResult.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenIcpVerifyTaskResult.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenOnlineIcpOrderResult.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenQueryIcpCertificateTypeResult.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenQueryIcpDistrictCodeResult.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenQueryIcpNrlxTypesResult.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenQueryIcpServiceContentTypesResult.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenQueryIcpSubjectTypeResult.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenUploadIcpMediaParam.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenUploadIcpMediaResult.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/executor/CommonUploadMultiRequestExecutor.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/executor/CommonUploadMultiRequestExecutorApacheImpl.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/executor/CommonUploadMultiRequestExecutorJoddHttpImpl.java
create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/executor/CommonUploadMultiRequestExecutorOkHttpImpl.java
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenMaIcpService.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenMaIcpService.java
new file mode 100644
index 0000000000..ad59b246c7
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenMaIcpService.java
@@ -0,0 +1,207 @@
+package me.chanjar.weixin.open.api;
+
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.open.bean.icp.*;
+import me.chanjar.weixin.open.bean.result.WxOpenResult;
+
+import java.io.File;
+
+/**
+ * @author xzh
+ * @Description 小程序备案
+ * https://developers.weixin.qq.com/doc/oplatform/openApi/OpenApiDoc/miniprogram-management/record/queryIcpVerifyTask.html
+ * @createTime 2024/08/14 10:52
+ */
+public interface WxOpenMaIcpService {
+ /**
+ * 查询人脸核身任务状态
+ * https://developers.weixin.qq.com/doc/oplatform/openApi/OpenApiDoc/miniprogram-management/record/queryIcpVerifyTask.html
+ */
+ String QUERY_ICP_VERIFY_TASK = "https://api.weixin.qq.com/wxa/icp/query_icp_verifytask";
+
+ /**
+ * 发起小程序管理员人脸核身
+ * https://developers.weixin.qq.com/doc/oplatform/openApi/OpenApiDoc/miniprogram-management/record/createIcpVerifyTask.html
+ */
+ String CREATE_ICP_VERIFY_TASK = "https://api.weixin.qq.com/wxa/icp/create_icp_verifytask";
+
+ /**
+ * 上传小程序备案媒体材料
+ * https://developers.weixin.qq.com/doc/oplatform/openApi/OpenApiDoc/miniprogram-management/record/uploadIcpMedia.html
+ */
+ String UPLOAD_ICP_MEDIA = "https://api.weixin.qq.com/wxa/icp/upload_icp_media";
+
+ /**
+ * 撤回小程序备案申请
+ * https://developers.weixin.qq.com/doc/oplatform/openApi/OpenApiDoc/miniprogram-management/record/cancelApplyIcpFiling.html
+ */
+ String CANCEL_APPLY_ICP_FILING = "https://api.weixin.qq.com/wxa/icp/cancel_apply_icp_filing";
+
+ /**
+ * 申请小程序备案
+ * https://developers.weixin.qq.com/doc/oplatform/openApi/OpenApiDoc/miniprogram-management/record/applyIcpFiling.html
+ */
+ String APPLY_ICP_FILING = "https://api.weixin.qq.com/wxa/icp/apply_icp_filing";
+
+ /**
+ * 注销小程序备案
+ * https://developers.weixin.qq.com/doc/oplatform/openApi/OpenApiDoc/miniprogram-management/record/cancelIcpfiling.html
+ */
+ String CANCEL_ICP_FILING = "https://api.weixin.qq.com/wxa/icp/cancel_icp_filing";
+
+ /**
+ * 获取小程序备案状态及驳回原因
+ * https://developers.weixin.qq.com/doc/oplatform/openApi/OpenApiDoc/miniprogram-management/record/getIcpEntranceInfo.html
+ */
+ String GET_ICP_ENTRANCE_INFO = "https://api.weixin.qq.com/wxa/icp/get_icp_entrance_info";
+
+ /**
+ * 获取小程序已备案详情
+ * https://developers.weixin.qq.com/doc/oplatform/openApi/OpenApiDoc/miniprogram-management/record/getOnlineIcpOrder.html
+ */
+ String GET_ONLINE_ICP_ORDER = "https://api.weixin.qq.com/wxa/icp/get_online_icp_order";
+
+ /**
+ * 获取小程序服务内容类型
+ * https://developers.weixin.qq.com/doc/oplatform/openApi/OpenApiDoc/miniprogram-management/record/queryIcpServiceContentTypes.html
+ */
+ String QUERY_ICP_SERVICE_CONTENT_TYPES = "https://api.weixin.qq.com/wxa/icp/query_icp_service_content_types";
+
+ /**
+ * 获取证件类型
+ * https://developers.weixin.qq.com/doc/oplatform/openApi/OpenApiDoc/miniprogram-management/record/queryIcpCertificateTypes.html
+ */
+ String QUERY_ICP_CERTIFICATE_TYPES = "https://api.weixin.qq.com/wxa/icp/query_icp_certificate_types";
+
+ /**
+ * 获取区域信息
+ * https://developers.weixin.qq.com/doc/oplatform/openApi/OpenApiDoc/miniprogram-management/record/queryIcpDistrictCode.html
+ */
+ String QUERY_ICP_DISTRICT_CODE = "https://api.weixin.qq.com/wxa/icp/query_icp_district_code";
+
+ /**
+ * 获取前置审批项类型
+ * https://developers.weixin.qq.com/doc/oplatform/openApi/OpenApiDoc/miniprogram-management/record/queryIcpNrlxTypes.html
+ */
+ String QUERY_ICP_NRLX_TYPES = "https://api.weixin.qq.com/wxa/icp/query_icp_nrlx_types";
+
+ /**
+ * 获取单位性质
+ * https://developers.weixin.qq.com/doc/oplatform/openApi/OpenApiDoc/miniprogram-management/record/queryIcpSubjectTypes.html
+ */
+ String QUERY_ICP_SUBJECT_TYPES = "https://api.weixin.qq.com/wxa/icp/query_icp_subject_types";
+
+ /**
+ * 获取小程序备案媒体材料
+ * https://developers.weixin.qq.com/doc/oplatform/openApi/OpenApiDoc/miniprogram-management/record/getIcpMedia.html
+ */
+ String GET_ICP_MEDIA = "https://api.weixin.qq.com/wxa/icp/get_icp_media";
+
+ /**
+ * 查询人脸核身任务状态
+ *
+ * @param taskId 任务id
+ * @return 人脸核身任务的状态和结果
+ * @throws WxErrorException e
+ */
+ WxOpenIcpVerifyTaskResult queryIcpVerifyTask(String taskId) throws WxErrorException;
+
+ /**
+ * 发起小程序管理员人脸核身
+ *
+ * @return 人脸核验任务结果
+ * @throws WxErrorException e
+ */
+ WxOpenIcpCreateIcpVerifyTaskResult createIcpVerifyTask() throws WxErrorException;
+
+ /**
+ * 上传小程序备案媒体材料
+ *
+ * @param param 备案媒体材料
+ * @return 备案媒体材料结果
+ * @throws WxErrorException e
+ */
+ WxOpenUploadIcpMediaResult uploadIcpMedia(WxOpenUploadIcpMediaParam param) throws WxErrorException;
+
+ /**
+ * 撤回小程序备案申请
+ *
+ * @return r
+ * @throws WxErrorException e
+ */
+ WxOpenResult cancelApplyIcpFiling() throws WxErrorException;
+
+ /**
+ * 申请小程序备案
+ *
+ * @param param 参数
+ * @return r
+ * @throws WxErrorException e
+ */
+ WxOpenApplyIcpFilingResult applyIcpFiling(WxOpenApplyIcpFilingParam param) throws WxErrorException;
+
+ /**
+ * 注销小程序备案
+ * @param cancelType 注销类型:1 -- 注销主体, 2 -- 注销小程序, 3 -- 注销微信小程序
+ * @return r
+ * @throws WxErrorException e
+ */
+ WxOpenResult cancelIcpFiling(Integer cancelType) throws WxErrorException;
+
+ /**
+ * 获取小程序备案状态及驳回原因
+ * @return r
+ * @throws WxErrorException e
+ */
+ WxOpenIcpEntranceInfoResult getIcpEntranceInfo() throws WxErrorException;
+
+ /**
+ * 获取小程序已备案详情
+ * @return 已备案详情
+ * @throws WxErrorException e
+ */
+ WxOpenOnlineIcpOrderResult getOnlineIcpOrder() throws WxErrorException;
+
+ /**
+ * 获取小程序服务内容类型
+ * @return 小程序服务内容类型定义
+ * @throws WxErrorException e
+ */
+ WxOpenQueryIcpServiceContentTypesResult queryIcpServiceContentTypes() throws WxErrorException;
+
+ /**
+ * 获取证件类型
+ * @return 证件类型定义
+ * @throws WxErrorException e
+ */
+ WxOpenQueryIcpCertificateTypeResult queryIcpCertificateTypes() throws WxErrorException;
+
+ /**
+ * 获取区域信息
+ * @return 省市区的区域信息
+ * @throws WxErrorException e
+ */
+ WxOpenQueryIcpDistrictCodeResult queryIcpDistrictCode() throws WxErrorException;
+
+ /**
+ * 获取前置审批项类型
+ * @return 小程序备案前置审批项类型定义
+ * @throws WxErrorException e
+ */
+ WxOpenQueryIcpNrlxTypesResult queryIcpNrlxTypes() throws WxErrorException;
+
+ /**
+ * 获取单位性质
+ * @return 单位性质定义
+ * @throws WxErrorException e
+ */
+ WxOpenQueryIcpSubjectTypeResult queryIcpSubjectTypes() throws WxErrorException;
+
+ /**
+ * 获取小程序备案媒体材料
+ * @param mediaId 上传小程序备案媒体材料接口返回的 media_id,示例值:4ahCGpd3CYkE6RpkNkUR5czt3LvG8xDnDdKAz6bBKttSfM8p4k5Rj6823HXugPwQBurgMezyib7
+ * @return 所上传的图片或视频媒体材料
+ * @throws WxErrorException e
+ */
+ File getIcpMedia(String mediaId) throws WxErrorException;
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenMaService.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenMaService.java
index 2afb5277d4..6d540940c0 100644
--- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenMaService.java
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenMaService.java
@@ -724,6 +724,13 @@ WxOpenMaDomainResult modifyDomain(String action, List requestDomains, Li
*/
WxOpenMaAuthService getAuthService();
+ /**
+ * 小程序备案服务
+ *
+ * @return 小程序备案服务
+ */
+ WxOpenMaIcpService getIcpService();
+
/**
* 小程序用户隐私保护指引服务
*
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMaIcpServiceImpl.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMaIcpServiceImpl.java
new file mode 100644
index 0000000000..dc78f22fe3
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMaIcpServiceImpl.java
@@ -0,0 +1,215 @@
+package me.chanjar.weixin.open.api.impl;
+
+import cn.binarywang.wx.miniapp.api.WxMaService;
+import cn.binarywang.wx.miniapp.json.WxMaGsonBuilder;
+import com.google.gson.JsonObject;
+import me.chanjar.weixin.open.bean.CommonUploadMultiParam;
+import me.chanjar.weixin.common.error.WxError;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.open.executor.CommonUploadMultiRequestExecutor;
+import me.chanjar.weixin.common.util.http.BaseMediaDownloadRequestExecutor;
+import me.chanjar.weixin.common.util.http.RequestExecutor;
+import me.chanjar.weixin.open.api.WxOpenMaIcpService;
+import me.chanjar.weixin.open.bean.icp.*;
+import me.chanjar.weixin.open.bean.result.WxOpenResult;
+import me.chanjar.weixin.open.util.json.WxOpenGsonBuilder;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+
+/**
+ * @author xzh
+ * @Description
+ * @createTime 2024/08/14 14:48
+ */
+public class WxOpenMaIcpServiceImpl implements WxOpenMaIcpService {
+
+ private final WxMaService wxMaService;
+
+ public WxOpenMaIcpServiceImpl(WxMaService wxMaService) {
+ this.wxMaService = wxMaService;
+ }
+
+ /**
+ * 查询人脸核身任务状态
+ *
+ * @param taskId 任务id
+ * @return 人脸核身任务的状态和结果
+ * @throws WxErrorException e
+ */
+ @Override
+ public WxOpenIcpVerifyTaskResult queryIcpVerifyTask(String taskId) throws WxErrorException {
+ JsonObject params = new JsonObject();
+ params.addProperty("task_id", taskId);
+ String response = wxMaService.post(QUERY_ICP_VERIFY_TASK, params);
+ return WxOpenGsonBuilder.create().fromJson(response, WxOpenIcpVerifyTaskResult.class);
+ }
+
+ /**
+ * 发起小程序管理员人脸核身
+ *
+ * @return 人脸核验任务结果
+ * @throws WxErrorException e
+ */
+ @Override
+ public WxOpenIcpCreateIcpVerifyTaskResult createIcpVerifyTask() throws WxErrorException {
+ String response = wxMaService.post(CREATE_ICP_VERIFY_TASK, "");
+ return WxMaGsonBuilder.create().fromJson(response, WxOpenIcpCreateIcpVerifyTaskResult.class);
+ }
+
+ /**
+ * 上传小程序备案媒体材料
+ *
+ * @param param 备案媒体材料
+ * @return 备案媒体材料结果
+ * @throws WxErrorException e
+ */
+ @Override
+ public WxOpenUploadIcpMediaResult uploadIcpMedia(WxOpenUploadIcpMediaParam param) throws WxErrorException {
+ RequestExecutor executor = CommonUploadMultiRequestExecutor.create(wxMaService.getRequestHttp());
+ String response = wxMaService.execute(executor, UPLOAD_ICP_MEDIA, param.toCommonUploadMultiParam());
+ return WxMaGsonBuilder.create().fromJson(response, WxOpenUploadIcpMediaResult.class);
+ }
+
+ /**
+ * 撤回小程序备案申请
+ *
+ * @return r
+ * @throws WxErrorException e
+ */
+ @Override
+ public WxOpenResult cancelApplyIcpFiling() throws WxErrorException {
+ String response = wxMaService.post(CANCEL_APPLY_ICP_FILING, "");
+ return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class);
+ }
+
+ /**
+ * 申请小程序备案
+ *
+ * @param param 参数
+ * @return r
+ * @throws WxErrorException e
+ */
+ @Override
+ public WxOpenApplyIcpFilingResult applyIcpFiling(WxOpenApplyIcpFilingParam param) throws WxErrorException {
+ String response = wxMaService.post(APPLY_ICP_FILING, param);
+ return WxMaGsonBuilder.create().fromJson(response, WxOpenApplyIcpFilingResult.class);
+ }
+
+ /**
+ * 注销小程序备案
+ *
+ * @param cancelType 注销类型:1 -- 注销主体, 2 -- 注销小程序, 3 -- 注销微信小程序
+ * @return r
+ * @throws WxErrorException e
+ */
+ @Override
+ public WxOpenResult cancelIcpFiling(Integer cancelType) throws WxErrorException {
+ JsonObject params = new JsonObject();
+ params.addProperty("cancel_type", cancelType);
+ String response = wxMaService.post(CANCEL_ICP_FILING, params);
+ return WxOpenGsonBuilder.create().fromJson(response, WxOpenResult.class);
+ }
+
+ /**
+ * 获取小程序备案状态及驳回原因
+ *
+ * @return r
+ * @throws WxErrorException e
+ */
+ @Override
+ public WxOpenIcpEntranceInfoResult getIcpEntranceInfo() throws WxErrorException {
+ String response = wxMaService.get(GET_ICP_ENTRANCE_INFO, null);
+ return WxMaGsonBuilder.create().fromJson(response, WxOpenIcpEntranceInfoResult.class);
+ }
+
+ /**
+ * 获取小程序已备案详情
+ *
+ * @return 已备案详情
+ * @throws WxErrorException e
+ */
+ @Override
+ public WxOpenOnlineIcpOrderResult getOnlineIcpOrder() throws WxErrorException {
+ String response = wxMaService.get(GET_ONLINE_ICP_ORDER, null);
+ return WxMaGsonBuilder.create().fromJson(response, WxOpenOnlineIcpOrderResult.class);
+ }
+
+ /**
+ * 获取小程序服务内容类型
+ *
+ * @return 小程序服务内容类型定义
+ * @throws WxErrorException e
+ */
+ @Override
+ public WxOpenQueryIcpServiceContentTypesResult queryIcpServiceContentTypes() throws WxErrorException {
+ String response = wxMaService.get(QUERY_ICP_SERVICE_CONTENT_TYPES, null);
+ return WxMaGsonBuilder.create().fromJson(response, WxOpenQueryIcpServiceContentTypesResult.class);
+ }
+
+ /**
+ * 获取证件类型
+ *
+ * @return 证件类型定义
+ * @throws WxErrorException e
+ */
+ @Override
+ public WxOpenQueryIcpCertificateTypeResult queryIcpCertificateTypes() throws WxErrorException {
+ String response = wxMaService.get(QUERY_ICP_CERTIFICATE_TYPES, null);
+ return WxMaGsonBuilder.create().fromJson(response, WxOpenQueryIcpCertificateTypeResult.class);
+ }
+
+ /**
+ * 获取区域信息
+ *
+ * @return 省市区的区域信息
+ * @throws WxErrorException e
+ */
+ @Override
+ public WxOpenQueryIcpDistrictCodeResult queryIcpDistrictCode() throws WxErrorException {
+ String response = wxMaService.get(QUERY_ICP_DISTRICT_CODE, null);
+ return WxMaGsonBuilder.create().fromJson(response, WxOpenQueryIcpDistrictCodeResult.class);
+ }
+
+ /**
+ * 获取前置审批项类型
+ *
+ * @return 小程序备案前置审批项类型定义
+ * @throws WxErrorException e
+ */
+ @Override
+ public WxOpenQueryIcpNrlxTypesResult queryIcpNrlxTypes() throws WxErrorException {
+ String response = wxMaService.get(QUERY_ICP_NRLX_TYPES, null);
+ return WxMaGsonBuilder.create().fromJson(response, WxOpenQueryIcpNrlxTypesResult.class);
+ }
+
+ /**
+ * 获取单位性质
+ *
+ * @return 单位性质定义
+ * @throws WxErrorException e
+ */
+ @Override
+ public WxOpenQueryIcpSubjectTypeResult queryIcpSubjectTypes() throws WxErrorException {
+ String response = wxMaService.get(QUERY_ICP_SUBJECT_TYPES, null);
+ return WxMaGsonBuilder.create().fromJson(response, WxOpenQueryIcpSubjectTypeResult.class);
+ }
+
+ /**
+ * 获取小程序备案媒体材料
+ * @param mediaId 上传小程序备案媒体材料接口返回的 media_id,示例值:4ahCGpd3CYkE6RpkNkUR5czt3LvG8xDnDdKAz6bBKttSfM8p4k5Rj6823HXugPwQBurgMezyib7
+ * @return 所上传的图片或视频媒体材料
+ * @throws WxErrorException e
+ */
+ @Override
+ public File getIcpMedia(String mediaId) throws WxErrorException {
+ try {
+ RequestExecutor executor = BaseMediaDownloadRequestExecutor
+ .create(this.wxMaService.getRequestHttp(), Files.createTempDirectory("wxma").toFile());
+ return this.wxMaService.execute(executor, GET_ICP_MEDIA, "media_id=" + mediaId);
+ } catch (IOException e) {
+ throw new WxErrorException(WxError.builder().errorMsg(e.getMessage()).build(), e);
+ }
+ }
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMaServiceImpl.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMaServiceImpl.java
index fd65601928..75be6bd4e1 100644
--- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMaServiceImpl.java
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMaServiceImpl.java
@@ -49,6 +49,8 @@ public class WxOpenMaServiceImpl extends WxMaServiceImpl implements WxOpenMaServ
@Getter
private final WxOpenMaAuthService authService;
@Getter
+ private final WxOpenMaIcpService icpService;
+ @Getter
private final WxOpenMaPrivacyService privacyService;
@Getter
private final WxOpenMaShoppingOrdersService shoppingOrdersService;
@@ -59,6 +61,7 @@ public WxOpenMaServiceImpl(WxOpenComponentService wxOpenComponentService, String
this.wxMaConfig = wxMaConfig;
this.basicService = new WxOpenMaBasicServiceImpl(this);
this.authService = new WxOpenMaAuthServiceImpl(this);
+ this.icpService = new WxOpenMaIcpServiceImpl(this);
this.privacyService = new WxOpenMaPrivacyServiceImpl(this);
this.shoppingOrdersService = new WxOpenMaShoppingOrdersServiceImpl(this);
initHttp();
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/CommonUploadMultiParam.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/CommonUploadMultiParam.java
new file mode 100644
index 0000000000..b3bcd1b1b2
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/CommonUploadMultiParam.java
@@ -0,0 +1,39 @@
+package me.chanjar.weixin.open.bean;
+
+import lombok.Builder;
+import lombok.Data;
+import me.chanjar.weixin.common.bean.CommonUploadParam;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * @author xzh
+ * @Description
+ * @createTime 2024/08/14 16:20
+ */
+@Data
+@Builder
+public class CommonUploadMultiParam implements Serializable {
+ private static final long serialVersionUID = -7935687108401829869L;
+
+ private List normalParams;
+
+ private CommonUploadParam uploadParam;
+
+ @Data
+ @Builder
+ public static class NormalParam implements Serializable {
+ private static final long serialVersionUID = 4345164516827726194L;
+
+ /**
+ * 参数名称(非文件名),如:type
+ */
+ private String name;
+
+ /**
+ * 参数名称对应值,如:image
+ */
+ private String value;
+ }
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenApplyIcpFilingParam.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenApplyIcpFilingParam.java
new file mode 100644
index 0000000000..37f84cf3d5
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenApplyIcpFilingParam.java
@@ -0,0 +1,557 @@
+package me.chanjar.weixin.open.bean.icp;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.*;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * @author xzh
+ * @Description
+ * @createTime 2024/08/14 15:09
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class WxOpenApplyIcpFilingParam implements Serializable {
+
+ private static final long serialVersionUID = -1175687030580685304L;
+
+ /**
+ * 备案主体信息
+ */
+ @SerializedName("icp_subject")
+ private Subject icpSubject;
+
+ /**
+ * 微信小程序信息
+ */
+ @SerializedName("icp_applets")
+ private Applets icpApplets;
+
+ /**
+ * 其他备案媒体材料
+ */
+ @SerializedName("icp_materials")
+ private Materials icpMaterials;
+
+ //region Subject define
+
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ public static class Subject implements Serializable {
+
+ private static final long serialVersionUID = -3760060095514905158L;
+
+ /**
+ * 主体基本信息
+ */
+ @SerializedName("base_info")
+ private SubjectBaseInfo baseInfo;
+
+ /**
+ * 个人主体额外信息
+ */
+ @SerializedName("personal_info")
+ private SubjectPersonalInfo personalInfo;
+
+ /**
+ * 主体额外信息(个人备案时,如果存在与主体负责人信息相同的字段,则填入相同的值)
+ */
+ @SerializedName("organize_info")
+ private SubjectOrganizeInfo organizeInfo;
+
+ /**
+ * 主体负责人信息
+ */
+ @SerializedName("principal_info")
+ private SubjectPrincipalInfo principalInfo;
+
+ /**
+ * 法人信息(非个人备案,且主体负责人不是法人时,必填)
+ */
+ @SerializedName("legal_person_info")
+ private SubjectLegaPersonInfo legalPersonInfo;
+
+ }
+
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ public static class SubjectBaseInfo implements Serializable {
+ private static final long serialVersionUID = 6040247507213564709L;
+
+ /**
+ * 主体性质,示例值:5
+ */
+ @SerializedName("type")
+ private Integer type;
+
+ /**
+ * 主办单位名称
+ */
+ @SerializedName("name")
+ private String name;
+
+ /**
+ * 备案省份
+ * 使用省份代码,示例值:"110000"(参考:获取区域信息接口)
+ */
+ @SerializedName("province")
+ private String province;
+
+ /**
+ * 备案城市
+ * 使用城市代码,示例值:"110100"(参考:获取区域信息接口)
+ */
+ @SerializedName("city")
+ private String city;
+
+ /**
+ * 备案县区
+ * 使用县区代码,示例值:"110105"(参考:获取区域信息接口)
+ */
+ @SerializedName("district")
+ private String district;
+
+ /**
+ * 通讯地址,必须属于备案省市区,地址开头的省市区不用填入,
+ * 例如:通信地址为“北京市朝阳区高碑店路181号1栋12345室”时,
+ * 只需要填写 "高碑店路181号1栋12345室" 即可
+ */
+ @SerializedName("address")
+ private String address;
+
+ /**
+ * 主体信息备注,根据需要,如实填写
+ */
+ @SerializedName("comment")
+ private String comment;
+
+ /**
+ * 主体备案号,示例值:粤B2-20090059(申请小程序备案时不用填写,查询已备案详情时会返回)
+ */
+ @SerializedName("record_number")
+ private String recordNumber;
+ }
+
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ public static class SubjectPersonalInfo implements Serializable {
+ private static final long serialVersionUID = 2453569107311102079L;
+
+ /**
+ * 临时居住证明照片 media_id,个人备案且非本省人员,需要提供居住证、暂住证、社保证明、房产证等临时居住证明,
+ */
+ @SerializedName("residence_permit")
+ private String residencePermit;
+ }
+
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ public static class SubjectOrganizeInfo implements Serializable {
+ private static final long serialVersionUID = 562578565445293345L;
+
+ /**
+ * 主体证件类型,
+ * 示例值:2(参考:获取证件类型接口)
+ */
+ @SerializedName("certificate_type")
+ private Integer certificateType;
+
+ /**
+ * 主体证件号码,
+ * 示例值:"110105199001011234"
+ */
+ @SerializedName("certificate_number")
+ private String certificateNumber;
+
+ /**
+ * 主体证件住所,
+ * 示例值:"北京市朝阳区高碑店路181号1栋12345室"
+ */
+ @SerializedName("certificate_address")
+ private String certificateAddress;
+
+ /**
+ * 主体证件照片 media_id,
+ * 如果小程序主体为非个人类型,则必填
+ */
+ @SerializedName("certificate_photo")
+ private String certificatePhoto;
+ }
+
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ public static class SubjectPrincipalInfo implements Serializable {
+
+ private static final long serialVersionUID = -6840245946309353916L;
+
+ /**
+ * 负责人姓名
+ */
+ @SerializedName("name")
+ private String name;
+
+ /**
+ * 负责人联系方式
+ */
+ @SerializedName("mobile")
+ private String mobile;
+
+ /**
+ * 负责人电子邮件
+ */
+ @SerializedName("email")
+ private String email;
+
+ /**
+ * 负责人应急联系方式
+ */
+ @SerializedName("emergency_contact")
+ private String emergencyContact;
+
+ /**
+ * 负责人证件类型
+ */
+ @SerializedName("certificate_type")
+ private Integer certificateType;
+
+ /**
+ * 负责人证件号码
+ */
+ @SerializedName("certificate_number")
+ private String certificateNumber;
+
+ /**
+ * 负责人证件有效期起始日期
+ * 格式为 YYYYmmdd,示例值:"20230815"
+ */
+ @SerializedName("certificate_validity_date_start")
+ private String certificateValidityDateStart;
+
+ /**
+ * 负责人证件有效期终止日期
+ * 格式为 YYYYmmdd,如证件长期有效,请填写 "长期",示例值:"20330815"
+ */
+ @SerializedName("certificate_validity_date_end")
+ private String certificateValidityDateEnd;
+
+ /**
+ * 负责人证件正面照片 media_id(身份证为人像面)
+ */
+ @SerializedName("certificate_photo_front")
+ private String certificatePhotoFront;
+
+ /**
+ * 负责人证件背面照片 media_id
+ */
+ @SerializedName("certificate_photo_back")
+ private String certificatePhotoBack;
+
+ /**
+ * 授权书 media_id,当主体负责人不是法人时需要主体负责人授权书,
+ * 当小程序负责人不是法人时需要小程序负责人授权书
+ */
+ @SerializedName("authorization_letter")
+ private String authorizationLetter;
+
+ /**
+ * 扫脸认证任务id(扫脸认证接口返回的task_id),
+ * 仅小程序负责人需要扫脸,主体负责人无需扫脸,
+ */
+ @SerializedName("verify_task_id")
+ private String verifyTaskId;
+ }
+
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ public static class SubjectLegaPersonInfo implements Serializable {
+
+ private static final long serialVersionUID = -7386716346559073571L;
+
+ /**
+ * 法人代表姓名
+ */
+ @SerializedName("name")
+ private String name;
+
+ /**
+ * 法人证件号码
+ */
+ @SerializedName("certificate_number")
+ private String certificateNumber;
+ }
+
+ //endregion Subject define
+
+ //region Applets define
+
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ public static class Applets implements Serializable {
+
+ private static final long serialVersionUID = -2938469180388648595L;
+
+ /**
+ * 微信小程序基本信息
+ */
+ @SerializedName("base_info")
+ private AppletsBaseInfo basInfo;
+
+ /**
+ * 小程序负责人信息
+ */
+ @SerializedName("principal_info")
+ private AppletsPrincipalInfo principalInfo;
+ }
+
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ public static class AppletsBaseInfo implements Serializable {
+
+ private static final long serialVersionUID = 8404017028547715919L;
+
+ /**
+ * 小程序ID,不用填写,后台自动拉取
+ */
+ @SerializedName("appid")
+ private String appId;
+
+ /**
+ * 小程序名称,不用填写,后台自动拉取
+ */
+ @SerializedName("name")
+ private String name;
+
+ /**
+ * 小程序服务内容类型,只能填写二级服务内容类型,最多5个
+ */
+ @SerializedName("service_content_types")
+ private List serviceContentTypes;
+
+ /**
+ * 前置审批项,列表中不能存在重复的前置审批类型id,如不涉及前置审批项,也需要填“以上都不涉及”
+ */
+ @SerializedName("nrlx_details")
+ private List nrlxDetails;
+
+ /**
+ * 请具体描述小程序实际经营内容、主要服务内容,该信息为主管部门审核重要依据,备注内容字数限制20-200字,请认真填写。
+ */
+ @SerializedName("comment")
+ private String comment;
+
+ /**
+ * 小程序备案号,示例值:粤B2-20090059-1626X
+ * (申请小程序备案时不用填写,查询已备案详情时会返回)
+ */
+ @SerializedName("record_number")
+ private String recordNumber;
+ }
+
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ public static class AppletsNrlxDetailItem implements Serializable {
+
+ private static final long serialVersionUID = -9144721738792167000L;
+
+ /**
+ * 前置审批类型,示例值:2
+ * (参考:获取前置审批项接口)
+ */
+ @SerializedName("type")
+ private Integer type;
+
+ /**
+ * 前置审批号,如果前置审批类型不是“以上都不涉及”,
+ * 则必填,示例值:"粤-12345号
+ */
+ @SerializedName("code")
+ private String code;
+
+ /**
+ * 前置审批媒体材料 media_id
+ */
+ @SerializedName("media")
+ private String media;
+ }
+
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ public static class AppletsPrincipalInfo implements Serializable {
+
+ private static final long serialVersionUID = 5088256283066784463L;
+
+ /**
+ * 负责人姓名
+ */
+ @SerializedName("name")
+ private String name;
+
+ /**
+ * 负责人联系方式
+ */
+ @SerializedName("mobile")
+ private String mobile;
+
+ /**
+ * 负责人电子邮件
+ */
+ @SerializedName("email")
+ private String email;
+
+ /**
+ * 负责人应急联系方式
+ */
+ @SerializedName("emergency_contact")
+ private String emergencyContact;
+
+ /**
+ * 负责人证件类型,示例值:2(参考:获取证件类型接口,此处只能填入单位性质属于个人的证件类型)
+ */
+ @SerializedName("certificate_type")
+ private Integer certificateType;
+
+ /**
+ * 负责人证件号码
+ */
+ @SerializedName("certificate_number")
+ private String certificateNumber;
+
+ /**
+ * 负责人证件有效期起始日期,
+ * 格式为 YYYYmmdd,示例值:"20230815"
+ */
+ @SerializedName("certificate_validity_date_start")
+ private String certificateValidityDateStart;
+
+ /**
+ * 负责人证件有效期终止日期,
+ * 格式为 YYYYmmdd,
+ * 如证件长期有效,请填写 "长期",示例值:"20330815"
+ */
+ @SerializedName("certificate_validity_date_end")
+ private String certificateValidityDateEnd;
+
+ /**
+ * 负责人证件正面照片 media_id
+ * (身份证为人像面)
+ */
+ @SerializedName("certificate_photo_front")
+ private String certificatePhotoFront;
+
+ /**
+ * 负责人证件背面照片 media_id
+ * (身份证为国徽面)
+ */
+ @SerializedName("certificate_photo_back")
+ private String certificatePhotoBack;
+
+ /**
+ * 授权书 media_id,
+ * 当主体负责人不是法人时需要主体负责人授权书,
+ * 当小程序负责人不是法人时需要小程序负责人授权书
+ */
+ @SerializedName("authorization_letter")
+ private String authorizationLetter;
+
+ /**
+ * 扫脸认证任务id(扫脸认证接口返回的task_id),
+ * 仅小程序负责人需要扫脸,主体负责人无需扫脸
+ */
+ @SerializedName("verify_task_id")
+ private String verifyTaskId;
+ }
+ //endregion Applets define
+
+ //region Materials define
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ public static class Materials {
+
+ /**
+ * 互联网信息服务承诺书 media_id,最多上传1个
+ */
+ @SerializedName("commitment_letter")
+ private List commitmentLetter;
+
+ /**
+ * 主体更名函 media_id(非个人类型,且发生过更名时需要上传),最多上传1个
+ */
+ @SerializedName("business_name_change_letter")
+ private List businessNameChangeLetter;
+
+ /**
+ * 党建确认函 media_id,最多上传1个
+ */
+ @SerializedName("party_building_confirmation_letter")
+ private List partyBuildingConfirmationLetter;
+
+ /**
+ * 承诺视频 media_id,最多上传1个
+ */
+ @SerializedName("promise_video")
+ private List promiseVideo;
+
+ /**
+ * 网站备案信息真实性责任告知书 media_id,最多上传1个
+ */
+ @SerializedName("authenticity_responsibility_letter")
+ private List authenticityResponsibilityLetter;
+
+ /**
+ * 小程序备案信息真实性承诺书 media_id,最多上传1个
+ */
+ @SerializedName("authenticity_commitment_letter")
+ private List authenticityCommitmentLetter;
+
+ /**
+ * 小程序建设方案书 media_id,最多上传1个
+ */
+ @SerializedName("website_construction_proposal")
+ private List websiteConstructionProposal;
+
+ /**
+ * 主体其它附件 media_id,最多上传10个
+ */
+ @SerializedName("subject_other_materials")
+ private List subjectOtherMaterials;
+
+ /**
+ * 小程序其它附件 media_id,最多上传10个
+ */
+ @SerializedName("applets_other_materials")
+ private List appletsOtherMaterials;
+
+ /**
+ * 手持证件照 media_id,最多上传1个
+ */
+ @SerializedName("holding_certificate_photo")
+ private List holdingCertificatePhoto;
+ }
+ //endregion Materials define
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenApplyIcpFilingResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenApplyIcpFilingResult.java
new file mode 100644
index 0000000000..223e5944fd
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenApplyIcpFilingResult.java
@@ -0,0 +1,40 @@
+package me.chanjar.weixin.open.bean.icp;
+
+import java.util.List;
+
+import com.google.gson.annotations.SerializedName;
+
+import lombok.*;
+import me.chanjar.weixin.open.bean.result.WxOpenResult;
+
+
+/**
+ * @author xzh
+ * @Description 申请小程序备案
+ * @createTime 2024/08/14 10:52
+ */
+@Getter
+@Setter
+@NoArgsConstructor
+public class WxOpenApplyIcpFilingResult extends WxOpenResult {
+ private static final long serialVersionUID = 9215343492997218227L;
+
+ /**
+ * 错误提示
+ */
+ @SerializedName("hints")
+ private List hints;
+
+ @Data
+ @EqualsAndHashCode(callSuper = true)
+ public static class Hint extends WxOpenResult {
+
+ private static final long serialVersionUID = 6585787444231217123L;
+
+ /**
+ * 校验失败的字段
+ */
+ @SerializedName("err_field")
+ private String errField;
+ }
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenIcpCreateIcpVerifyTaskResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenIcpCreateIcpVerifyTaskResult.java
new file mode 100644
index 0000000000..782db16e94
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenIcpCreateIcpVerifyTaskResult.java
@@ -0,0 +1,28 @@
+package me.chanjar.weixin.open.bean.icp;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import me.chanjar.weixin.open.bean.result.WxOpenResult;
+
+/**
+ * @author xzh
+ * @Description 人脸核验任务结果
+ * @createTime 2024/08/14 10:52
+ */
+@Getter
+@Setter
+@NoArgsConstructor
+public class WxOpenIcpCreateIcpVerifyTaskResult extends WxOpenResult {
+
+ private static final long serialVersionUID = -8960874090439615220L;
+
+ /**
+ * 人脸核验任务id
+ */
+ @JsonProperty("task_id")
+ private String taskId;
+
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenIcpEntranceInfoResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenIcpEntranceInfoResult.java
new file mode 100644
index 0000000000..9538a64b0a
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenIcpEntranceInfoResult.java
@@ -0,0 +1,92 @@
+package me.chanjar.weixin.open.bean.icp;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import me.chanjar.weixin.open.bean.result.WxOpenResult;
+
+import java.io.Serializable;
+
+
+/**
+ * @author xzh
+ * @Description 获取小程序备案状态及驳回原因
+ * @createTime 2024/08/14 10:52
+ */
+@Getter
+@Setter
+@NoArgsConstructor
+public class WxOpenIcpEntranceInfoResult extends WxOpenResult {
+
+ private static final long serialVersionUID = 4661275080517814216L;
+
+ /**
+ * 备案状态信息
+ */
+ private Info info;
+
+ @Getter
+ @Setter
+ @NoArgsConstructor
+ public static class Info implements Serializable {
+
+ private static final long serialVersionUID = 879913578935521216L;
+
+ /**
+ * 备案状态,取值参考备案状态枚举,示例值:1024
+ */
+ @SerializedName("status")
+ private Integer status;
+
+ /**
+ * 是否正在注销备案
+ */
+ @SerializedName("is_canceling")
+ private Boolean canceling;
+
+ /**
+ * 驳回原因,备案不通过时返回
+ */
+ @SerializedName("audit_data")
+ private AuditData auditData;
+
+ /**
+ * 备案入口是否对该小程序开放,0:不开放,1:开放。特定情况下入口不会开放,如小程序昵称包含某些关键词时、管局系统不可用时,当备案入口开放时才能提交备案申请
+ */
+ @SerializedName("available")
+ private Integer available;
+
+ /**
+ * 管局短信核验状态,仅当备案状态为 4(管局审核中)的时候才有效。1:等待核验中,2:核验完成,3:核验超时。
+ */
+ @SerializedName("sms_verify_status")
+ private Integer smsVerifyStatus;
+ }
+
+ @Getter
+ @Setter
+ @NoArgsConstructor
+ public static class AuditData implements Serializable {
+
+ private static final long serialVersionUID = 2217833539540191890L;
+
+ /**
+ * 审核不通过的字段中文名
+ */
+ @SerializedName("key_name")
+ private String keyName;
+
+ /**
+ * 字段不通过的原因
+ */
+ @SerializedName("error")
+ private String error;
+
+ /**
+ * 修改建议
+ */
+ @SerializedName("suggest")
+ private String suggest;
+ }
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenIcpVerifyTaskResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenIcpVerifyTaskResult.java
new file mode 100644
index 0000000000..cb59fac1c0
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenIcpVerifyTaskResult.java
@@ -0,0 +1,33 @@
+package me.chanjar.weixin.open.bean.icp;
+
+import com.google.gson.annotations.SerializedName;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import me.chanjar.weixin.open.bean.result.WxOpenResult;
+
+/**
+ * @author xzh
+ * @Description 人脸核身任务的状态和结果
+ * @createTime 2024/08/14 10:52
+ */
+@Getter
+@Setter
+@NoArgsConstructor
+public class WxOpenIcpVerifyTaskResult extends WxOpenResult {
+
+ private static final long serialVersionUID = 3134332406149779364L;
+
+ /**
+ * 人脸核身任务是否已完成
+ */
+ @SerializedName("is_finish")
+ private Boolean finish;
+
+ /**
+ * 任务状态枚举:0. 未开始;1. 等待中;2. 失败;3. 成功。返回的 is_finish 字段为 true 时,face_status 才是最终状态。
+ */
+ @SerializedName("face_status")
+ private Integer faceStatus;
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenOnlineIcpOrderResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenOnlineIcpOrderResult.java
new file mode 100644
index 0000000000..89f8e8c397
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenOnlineIcpOrderResult.java
@@ -0,0 +1,36 @@
+package me.chanjar.weixin.open.bean.icp;
+
+import java.io.Serializable;
+import java.util.List;
+
+import com.google.gson.annotations.SerializedName;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+
+/**
+ * @author xzh
+ * @Description 已备案详情
+ * @createTime 2024/08/14 10:52
+ */
+@Getter
+@Setter
+@NoArgsConstructor
+public class WxOpenOnlineIcpOrderResult implements Serializable {
+
+ private static final long serialVersionUID = -8670174116784375577L;
+
+ /**
+ * 备案主体信息
+ */
+ @SerializedName("icp_subject")
+ private WxOpenApplyIcpFilingParam.Subject icpSubject;
+
+ /**
+ * 微信小程序信息
+ */
+ @SerializedName("icp_applets")
+ private WxOpenApplyIcpFilingParam.Applets icpApplets;
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenQueryIcpCertificateTypeResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenQueryIcpCertificateTypeResult.java
new file mode 100644
index 0000000000..5e042b4cbf
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenQueryIcpCertificateTypeResult.java
@@ -0,0 +1,50 @@
+package me.chanjar.weixin.open.bean.icp;
+
+import java.io.Serializable;
+import java.util.List;
+
+import com.google.gson.annotations.SerializedName;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import me.chanjar.weixin.open.bean.result.WxOpenResult;
+
+/**
+ * @author xzh
+ * @Description 证件类型定义
+ * @createTime 2024/08/14 10:52
+ */
+@Getter
+@Setter
+@NoArgsConstructor
+public class WxOpenQueryIcpCertificateTypeResult extends WxOpenResult {
+
+ private static final long serialVersionUID = -6492653564753189104L;
+
+ /**
+ * 证件类型列表
+ */
+ @SerializedName("items")
+ private List- items;
+
+ @Getter
+ @Setter
+ @NoArgsConstructor
+ public static class Item implements Serializable {
+
+ private static final long serialVersionUID = -5353506906838811002L;
+
+ @SerializedName("type")
+ private Integer type;
+
+ @SerializedName("subject_type")
+ private Integer subjectType;
+
+ @SerializedName("name")
+ private String name;
+
+ @SerializedName("remark")
+ private String remark;
+ }
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenQueryIcpDistrictCodeResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenQueryIcpDistrictCodeResult.java
new file mode 100644
index 0000000000..28ca0ef5c5
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenQueryIcpDistrictCodeResult.java
@@ -0,0 +1,58 @@
+package me.chanjar.weixin.open.bean.icp;
+
+import java.io.Serializable;
+import java.util.List;
+
+import com.google.gson.annotations.SerializedName;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import me.chanjar.weixin.open.bean.result.WxOpenResult;
+
+/**
+ * @author xzh
+ * @Description 省市区的区域信息
+ * @createTime 2024/08/14 10:52
+ */
+@Getter
+@Setter
+@NoArgsConstructor
+public class WxOpenQueryIcpDistrictCodeResult extends WxOpenResult {
+ private static final long serialVersionUID = -5087976503275542589L;
+
+ @SerializedName("items")
+ private List
- items;
+
+ @Getter
+ @Setter
+ @NoArgsConstructor
+ public static class Item implements Serializable {
+
+ private static final long serialVersionUID = -8598323103982035055L;
+
+ /**
+ * 区域类型: 1 -- 省份,2 -- 市,3 -- 区
+ */
+ @SerializedName("type")
+ private Integer type;
+
+ /**
+ * 区域代码
+ */
+ @SerializedName("code")
+ private Integer code;
+
+ /**
+ * 下级区域信息列表
+ */
+ @SerializedName("name")
+ private String name;
+
+ /**
+ * 下级区域信息列表
+ */
+ @SerializedName("sub_list")
+ private List
- subList;
+ }
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenQueryIcpNrlxTypesResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenQueryIcpNrlxTypesResult.java
new file mode 100644
index 0000000000..e877029893
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenQueryIcpNrlxTypesResult.java
@@ -0,0 +1,50 @@
+package me.chanjar.weixin.open.bean.icp;
+
+import java.io.Serializable;
+import java.util.List;
+
+import com.google.gson.annotations.SerializedName;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import me.chanjar.weixin.open.bean.result.WxOpenResult;
+
+
+/**
+ * @author xzh
+ * @Description 小程序备案前置审批项类型定义
+ * @createTime 2024/08/14 10:52
+ */
+@Getter
+@Setter
+@NoArgsConstructor
+public class WxOpenQueryIcpNrlxTypesResult extends WxOpenResult {
+ private static final long serialVersionUID = 4986067025882451072L;
+
+ /**
+ * 前置审批项类型列表
+ */
+ @SerializedName("items")
+ private List
- items;
+
+ @Getter
+ @Setter
+ @NoArgsConstructor
+ public static class Item implements Serializable {
+
+ private static final long serialVersionUID = 9069126796573950000L;
+
+ /**
+ * 前置审批项类型id
+ */
+ @SerializedName("type")
+ private Integer type;
+
+ /**
+ * 名称
+ */
+ @SerializedName("name")
+ private String name;
+ }
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenQueryIcpServiceContentTypesResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenQueryIcpServiceContentTypesResult.java
new file mode 100644
index 0000000000..1d800fbab5
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenQueryIcpServiceContentTypesResult.java
@@ -0,0 +1,60 @@
+package me.chanjar.weixin.open.bean.icp;
+
+import java.io.Serializable;
+import java.util.List;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import me.chanjar.weixin.open.bean.result.WxOpenResult;
+
+
+/**
+ * @author xzh
+ * @Description 小程序服务内容类型
+ * @createTime 2024/08/14 10:52
+ */
+@Getter
+@Setter
+@NoArgsConstructor
+public class WxOpenQueryIcpServiceContentTypesResult extends WxOpenResult {
+ private static final long serialVersionUID = 2982244171428787389L;
+
+ /**
+ * 服务内容类型列表
+ */
+ private List
- items;
+
+ @Getter
+ @Setter
+ @NoArgsConstructor
+ public static class Item implements Serializable {
+
+ private static final long serialVersionUID = 1432103347845426732L;
+
+ /**
+ * 服务内容类型id
+ */
+ @SerializedName("type")
+ private Integer type;
+
+ /**
+ * 该服务内容类型的父类型id
+ */
+ @SerializedName("parent_type")
+ private Integer parentType;
+
+ /**
+ * 名称
+ */
+ @SerializedName("name")
+ private String name;
+
+ /**
+ * 备注
+ */
+ @SerializedName("remark")
+ private String remark;
+ }
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenQueryIcpSubjectTypeResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenQueryIcpSubjectTypeResult.java
new file mode 100644
index 0000000000..a03e056db5
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenQueryIcpSubjectTypeResult.java
@@ -0,0 +1,53 @@
+package me.chanjar.weixin.open.bean.icp;
+
+import java.io.Serializable;
+import java.util.List;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+/**
+ * @author xzh
+ * @Description 单位性质定义
+ * @createTime 2024/08/14 10:52
+ */
+@Getter
+@Setter
+@NoArgsConstructor
+public class WxOpenQueryIcpSubjectTypeResult implements Serializable {
+
+ private static final long serialVersionUID = -2090825609788654435L;
+
+ /**
+ * 单位性质列表
+ */
+ private List
- items;
+
+ @Getter
+ @Setter
+ @NoArgsConstructor
+ public static class Item implements Serializable {
+
+ private static final long serialVersionUID = -1284830856404207970L;
+
+ /**
+ * 单位性质类型id
+ */
+ @SerializedName("type")
+ private Integer type;
+
+ /**
+ * 名称
+ */
+ @SerializedName("name")
+ private String name;
+
+ /**
+ * 备注
+ */
+ @SerializedName("remark")
+ private String remark;
+ }
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenUploadIcpMediaParam.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenUploadIcpMediaParam.java
new file mode 100644
index 0000000000..a5eda8ab4e
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenUploadIcpMediaParam.java
@@ -0,0 +1,64 @@
+package me.chanjar.weixin.open.bean.icp;
+
+import java.io.Serializable;
+import java.util.*;
+
+import com.google.gson.annotations.SerializedName;
+
+import lombok.*;
+import me.chanjar.weixin.common.bean.CommonUploadData;
+import me.chanjar.weixin.open.bean.CommonUploadMultiParam;
+import me.chanjar.weixin.common.bean.CommonUploadParam;
+
+
+/**
+ * @author xzh
+ * @Description 文件上传
+ * @createTime 2024/08/14 10:52
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class WxOpenUploadIcpMediaParam implements Serializable {
+
+ private static final long serialVersionUID = -9082174509776922969L;
+
+ /**
+ * 媒体材料类型。目前支持两种:图片("image")和视频("video"),示例值:"image"
+ */
+ @SerializedName("type")
+ private String type;
+
+ /**
+ * 证件类型(参考:获取证件类型),如果上传的是证件媒体材料,则必填,示例值:2
+ */
+ @SerializedName("certificate_type")
+ private String certificateType;
+
+ /**
+ * 媒体材料所属的备案字段名(参考:申请小程序备案接口),如要用于多个备案字段,则填写其中一个字段名即可。
+ * 例如:要上传身份证头像面照片作为备案主体负责人和小程序负责人的证件照正面, 就填写
+ * "icp_subject.principal_info.certificate_photo_front"
+ */
+ @SerializedName("icp_order_field")
+ private String icpOrderField;
+
+ /**
+ * 待上传的图片或视频
+ */
+ private CommonUploadData media;
+
+
+ public CommonUploadMultiParam toCommonUploadMultiParam() {
+ return CommonUploadMultiParam.builder()
+ .normalParams(Arrays.asList(
+ CommonUploadMultiParam.NormalParam.builder().name("type").value(type).build(),
+ CommonUploadMultiParam.NormalParam.builder().name("certificate_type").value(certificateType).build(),
+ CommonUploadMultiParam.NormalParam.builder().name("icp_order_field").value(icpOrderField).build()
+ ))
+ .uploadParam(new CommonUploadParam("media", media))
+ .build();
+ }
+
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenUploadIcpMediaResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenUploadIcpMediaResult.java
new file mode 100644
index 0000000000..04977113e1
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenUploadIcpMediaResult.java
@@ -0,0 +1,40 @@
+package me.chanjar.weixin.open.bean.icp;
+
+import com.google.gson.annotations.SerializedName;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder;
+import me.chanjar.weixin.open.bean.result.WxOpenResult;
+
+/**
+ * @author xzh
+ * @Description 上传小程序备案媒体材料结果
+ * @createTime 2024/08/14 10:52
+ */
+@Getter
+@Setter
+@NoArgsConstructor
+public class WxOpenUploadIcpMediaResult extends WxOpenResult {
+
+ private static final long serialVersionUID = 6929154695595201734L;
+
+ /**
+ * 媒体材料类型。目前支持两种:图片("image")和视频("video"),示例值:"image"
+ */
+ @SerializedName("type")
+ private String type;
+
+ /**
+ * 媒体id,示例值:"4ahCGpd3CYkE6RpkNkUR5czt3LvG8xDnDdKAz6bBKttSfM8p4k5Rj6823HXugPwQBurgMezyib7"
+ */
+ @SerializedName("media_id")
+ private String mediaId;
+
+ /**
+ * 创建时间,UNIX时间戳,示例值:1692883651
+ */
+ @SerializedName("created_at")
+ private String createdAt;
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/message/WxOpenXmlMessage.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/message/WxOpenXmlMessage.java
index f8bde1582a..e676f0ab66 100644
--- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/message/WxOpenXmlMessage.java
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/message/WxOpenXmlMessage.java
@@ -123,6 +123,37 @@ public class WxOpenXmlMessage implements Serializable {
@XStreamAlias("expired")
private Long expired;
+ //region 以下为小程序管理员人脸核身完成事件 推送的消息 infoType=notify_icpfiling_verify_result
+
+ /**
+ * 人脸核验任务id
+ */
+ @XStreamAlias("task_id")
+ private String IcpVerifyTaskId;
+ /**
+ * 小程序唯一id
+ */
+ @XStreamAlias("verify_appid")
+ private String verifyAppId;
+ /**
+ * 人脸核验结果: 2-核验失败;3-核验成功
+ */
+ @XStreamAlias("result")
+ private Integer result;
+ //endregion
+
+ //region 当备案审核被驳回或通过时会推送该事件 推送的消息 infoType=notify_apply_icpfiling_result
+ /**
+ * 小程序唯一id
+ */
+ @XStreamAlias("authorizer_appid")
+ private String authorizerAppId;
+ /**
+ * 备案状态,参考“获取小程序备案状态及驳回原因”接口的备案状态枚举¬
+ */
+ @XStreamAlias("beian_status")
+ private Integer beianStatus;
+ //endregion
/**
* 快速创建的小程序appId,已弃用,未来将删除
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/executor/CommonUploadMultiRequestExecutor.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/executor/CommonUploadMultiRequestExecutor.java
new file mode 100644
index 0000000000..4812dd325c
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/executor/CommonUploadMultiRequestExecutor.java
@@ -0,0 +1,49 @@
+package me.chanjar.weixin.open.executor;
+
+import me.chanjar.weixin.open.bean.CommonUploadMultiParam;
+import me.chanjar.weixin.common.enums.WxType;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.common.util.http.RequestExecutor;
+import me.chanjar.weixin.common.util.http.RequestHttp;
+import me.chanjar.weixin.common.util.http.ResponseHandler;
+
+import java.io.IOException;
+
+/**
+ * @author xzh
+ * @Description
+ * @createTime 2024/08/14 16:28
+ */
+public abstract class CommonUploadMultiRequestExecutor
implements RequestExecutor {
+
+ protected RequestHttp requestHttp;
+
+ public CommonUploadMultiRequestExecutor(RequestHttp requestHttp) {
+ this.requestHttp = requestHttp;
+ }
+
+ @Override
+ public void execute(String uri, CommonUploadMultiParam data, ResponseHandler handler, WxType wxType) throws WxErrorException, IOException {
+ handler.handle(this.execute(uri, data, wxType));
+ }
+
+ /**
+ * 构造通用文件上传执行器
+ *
+ * @param requestHttp 请求信息
+ * @return 执行器
+ */
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ public static RequestExecutor create(RequestHttp requestHttp) {
+ switch (requestHttp.getRequestType()) {
+ case APACHE_HTTP:
+ return new CommonUploadMultiRequestExecutorApacheImpl(requestHttp);
+ case JODD_HTTP:
+ return new CommonUploadMultiRequestExecutorJoddHttpImpl(requestHttp);
+ case OK_HTTP:
+ return new CommonUploadMultiRequestExecutorOkHttpImpl(requestHttp);
+ default:
+ throw new IllegalArgumentException("不支持的http执行器类型:" + requestHttp.getRequestType());
+ }
+ }
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/executor/CommonUploadMultiRequestExecutorApacheImpl.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/executor/CommonUploadMultiRequestExecutorApacheImpl.java
new file mode 100644
index 0000000000..2d386ea5d6
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/executor/CommonUploadMultiRequestExecutorApacheImpl.java
@@ -0,0 +1,95 @@
+package me.chanjar.weixin.open.executor;
+
+import lombok.Getter;
+import me.chanjar.weixin.common.bean.CommonUploadData;
+import me.chanjar.weixin.open.bean.CommonUploadMultiParam;
+import me.chanjar.weixin.common.bean.CommonUploadParam;
+import me.chanjar.weixin.common.enums.WxType;
+import me.chanjar.weixin.common.error.WxError;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.common.util.http.RequestHttp;
+import me.chanjar.weixin.common.util.http.apache.Utf8ResponseHandler;
+import org.apache.http.Consts;
+import org.apache.http.HttpHost;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.mime.HttpMultipartMode;
+import org.apache.http.entity.mime.MultipartEntityBuilder;
+import org.apache.http.entity.mime.content.InputStreamBody;
+import org.apache.http.entity.mime.content.StringBody;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.springframework.util.CollectionUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+
+/**
+ * @author xzh
+ * @Description
+ * @createTime 2024/08/14 16:28
+ */
+public class CommonUploadMultiRequestExecutorApacheImpl extends CommonUploadMultiRequestExecutor {
+
+ public CommonUploadMultiRequestExecutorApacheImpl(RequestHttp requestHttp) {
+ super(requestHttp);
+ }
+
+ @Override
+ public String execute(String uri, CommonUploadMultiParam param, WxType wxType) throws WxErrorException, IOException {
+ HttpPost httpPost = new HttpPost(uri);
+ if (requestHttp.getRequestHttpProxy() != null) {
+ RequestConfig config = RequestConfig.custom().setProxy(requestHttp.getRequestHttpProxy()).build();
+ httpPost.setConfig(config);
+ }
+ if (param != null) {
+ MultipartEntityBuilder entity = MultipartEntityBuilder.create();
+
+ List normalParams = param.getNormalParams();
+ if (!CollectionUtils.isEmpty(normalParams)) {
+ for (CommonUploadMultiParam.NormalParam normalParam : normalParams) {
+ entity.addPart(normalParam.getName(), new StringBody(normalParam.getValue(), ContentType.create("multipart/form-data", Consts.UTF_8)));
+ }
+ }
+
+ CommonUploadParam uploadParam = param.getUploadParam();
+ if (uploadParam != null) {
+ CommonUploadData data = uploadParam.getData();
+ InnerStreamBody part = new InnerStreamBody(data.getInputStream(), ContentType.DEFAULT_BINARY, data.getFileName(), data.getLength());
+ entity.addPart(uploadParam.getName(), part)
+ .setMode(HttpMultipartMode.RFC6532);
+ }
+
+ httpPost.setEntity(entity.build());
+ }
+ try (CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpPost)) {
+ String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response);
+ if (responseContent == null || responseContent.isEmpty()) {
+ throw new WxErrorException(String.format("上传失败,服务器响应空 url:%s param:%s", uri, param));
+ }
+ WxError error = WxError.fromJson(responseContent, wxType);
+ if (error.getErrorCode() != 0) {
+ throw new WxErrorException(error);
+ }
+ return responseContent;
+ } finally {
+ httpPost.releaseConnection();
+ }
+ }
+
+ /**
+ * 内部流 请求体
+ */
+ @Getter
+ public static class InnerStreamBody extends InputStreamBody {
+
+ private final long contentLength;
+
+ public InnerStreamBody(final InputStream in, final ContentType contentType, final String filename, long contentLength) {
+ super(in, contentType, filename);
+ this.contentLength = contentLength;
+ }
+ }
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/executor/CommonUploadMultiRequestExecutorJoddHttpImpl.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/executor/CommonUploadMultiRequestExecutorJoddHttpImpl.java
new file mode 100644
index 0000000000..1650c16740
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/executor/CommonUploadMultiRequestExecutorJoddHttpImpl.java
@@ -0,0 +1,109 @@
+package me.chanjar.weixin.open.executor;
+
+import jodd.http.HttpConnectionProvider;
+import jodd.http.HttpRequest;
+import jodd.http.HttpResponse;
+import jodd.http.ProxyInfo;
+import jodd.http.upload.Uploadable;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.SneakyThrows;
+import me.chanjar.weixin.common.bean.CommonUploadData;
+import me.chanjar.weixin.open.bean.CommonUploadMultiParam;
+import me.chanjar.weixin.common.bean.CommonUploadParam;
+import me.chanjar.weixin.common.enums.WxType;
+import me.chanjar.weixin.common.error.WxError;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.common.util.http.RequestHttp;
+import org.apache.http.Consts;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.mime.content.StringBody;
+import org.springframework.util.CollectionUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+/**
+ * @author xzh
+ * @Description
+ * @createTime 2024/08/14 16:43
+ */
+public class CommonUploadMultiRequestExecutorJoddHttpImpl extends CommonUploadMultiRequestExecutor {
+
+ public CommonUploadMultiRequestExecutorJoddHttpImpl(RequestHttp requestHttp) {
+ super(requestHttp);
+ }
+
+ @Override
+ public String execute(String uri, CommonUploadMultiParam param, WxType wxType) throws WxErrorException, IOException {
+ HttpRequest request = HttpRequest.post(uri);
+ if (requestHttp.getRequestHttpProxy() != null) {
+ requestHttp.getRequestHttpClient().useProxy(requestHttp.getRequestHttpProxy());
+ }
+ request.withConnectionProvider(requestHttp.getRequestHttpClient());
+
+ List normalParams = param.getNormalParams();
+ if (!CollectionUtils.isEmpty(normalParams)) {
+ for (CommonUploadMultiParam.NormalParam normalParam : normalParams) {
+ request.form(normalParam.getName(), new StringBody(normalParam.getValue(), ContentType.create("multipart/form-data", Consts.UTF_8)));
+ }
+ }
+
+ CommonUploadParam uploadParam = param.getUploadParam();
+ if (uploadParam != null) {
+ request.form(uploadParam.getName(), new CommonUploadParamToUploadableAdapter(uploadParam.getData()));
+ }
+
+ HttpResponse response = request.send();
+ response.charset(StandardCharsets.UTF_8.name());
+ String responseContent = response.bodyText();
+ if (responseContent.isEmpty()) {
+ throw new WxErrorException(String.format("上传失败,服务器响应空 url:%s param:%s", uri, param));
+ }
+ WxError error = WxError.fromJson(responseContent, wxType);
+ if (error.getErrorCode() != 0) {
+ throw new WxErrorException(error);
+ }
+ return responseContent;
+ }
+
+ /**
+ * 通用上传参数 到 Uploadable 的适配器
+ */
+ @Getter
+ @AllArgsConstructor
+ public static class CommonUploadParamToUploadableAdapter implements Uploadable {
+
+ private CommonUploadData content;
+
+ @SneakyThrows
+ @Override
+ public byte[] getBytes() {
+ return content.readAllBytes();
+ }
+
+ @Override
+ public String getFileName() {
+ return content.getFileName();
+ }
+
+ @Override
+ public String getMimeType() {
+ return null;
+ }
+
+ @SneakyThrows
+ @Override
+ public int getSize() {
+ return (int) content.getLength();
+ }
+
+ @Override
+ public InputStream openInputStream() {
+ return content.getInputStream();
+ }
+ }
+}
+
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/executor/CommonUploadMultiRequestExecutorOkHttpImpl.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/executor/CommonUploadMultiRequestExecutorOkHttpImpl.java
new file mode 100644
index 0000000000..ba64bbf6db
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/executor/CommonUploadMultiRequestExecutorOkHttpImpl.java
@@ -0,0 +1,104 @@
+package me.chanjar.weixin.open.executor;
+
+import lombok.AllArgsConstructor;
+import me.chanjar.weixin.common.bean.CommonUploadData;
+import me.chanjar.weixin.open.bean.CommonUploadMultiParam;
+import me.chanjar.weixin.common.bean.CommonUploadParam;
+import me.chanjar.weixin.common.enums.WxType;
+import me.chanjar.weixin.common.error.WxError;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.common.util.http.RequestHttp;
+import me.chanjar.weixin.common.util.http.okhttp.OkHttpProxyInfo;
+import okhttp3.*;
+import okio.BufferedSink;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.springframework.util.CollectionUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+
+/**
+ * @author xzh
+ * @Description
+ * @createTime 2024/08/14 16:48
+ */
+public class CommonUploadMultiRequestExecutorOkHttpImpl extends CommonUploadMultiRequestExecutor {
+
+ public CommonUploadMultiRequestExecutorOkHttpImpl(RequestHttp requestHttp) {
+ super(requestHttp);
+ }
+
+ @Override
+ public String execute(String uri, CommonUploadMultiParam param, WxType wxType) throws WxErrorException, IOException {
+ MultipartBody.Builder builder = new MultipartBody.Builder()
+ .setType(MediaType.get("multipart/form-data"));
+
+ List normalParams = param.getNormalParams();
+ if (!CollectionUtils.isEmpty(normalParams)) {
+ for (CommonUploadMultiParam.NormalParam normalParam : normalParams) {
+ builder.addFormDataPart(normalParam.getName(), normalParam.getValue());
+ }
+ }
+
+ CommonUploadParam uploadParam = param.getUploadParam();
+ if (uploadParam != null) {
+ RequestBody requestBody = new CommonUpdateDataToRequestBodyAdapter(uploadParam.getData());
+ builder.addFormDataPart(uploadParam.getName(), uploadParam.getData().getFileName(), requestBody);
+ }
+
+ Request request = new Request.Builder().url(uri).post(builder.build()).build();
+
+ try (Response response = requestHttp.getRequestHttpClient().newCall(request).execute()) {
+ ResponseBody responseBody = response.body();
+ String responseContent = responseBody == null ? "" : responseBody.string();
+ if (responseContent.isEmpty()) {
+ throw new WxErrorException(String.format("上传失败,服务器响应空 url:%s param:%s", uri, param));
+ }
+ WxError error = WxError.fromJson(responseContent, wxType);
+ if (error.getErrorCode() != 0) {
+ throw new WxErrorException(error);
+ }
+ return responseContent;
+ }
+ }
+
+ /**
+ * 通用上传输入 到 OkHttp 请求提 适配器
+ */
+ @AllArgsConstructor
+ public static class CommonUpdateDataToRequestBodyAdapter extends RequestBody {
+
+ private static final MediaType CONTENT_TYPE = MediaType.get("application/octet-stream");
+
+ private CommonUploadData data;
+
+ @Override
+ public long contentLength() {
+ return data.getLength();
+ }
+
+ @Nullable
+ @Override
+ public MediaType contentType() {
+ return CONTENT_TYPE;
+ }
+
+ @Override
+ public void writeTo(@NotNull BufferedSink bufferedSink) throws IOException {
+ InputStream inputStream = data.getInputStream();
+ int count;
+ byte[] buffer = new byte[4096];
+ while ((count = inputStream.read(buffer)) != -1) {
+ bufferedSink.write(buffer, 0, count);
+ }
+ inputStream.close();
+ }
+
+ @Override
+ public boolean isOneShot() {
+ return true;
+ }
+ }
+}
From 39a152b1fb2e39b746286ff48a7b12c31be555b3 Mon Sep 17 00:00:00 2001
From: foreveryang321 <453190450@qq.com>
Date: Thu, 15 Aug 2024 22:10:33 +0800
Subject: [PATCH 035/385] =?UTF-8?q?:art:=20=E5=AE=8C=E5=96=84=E9=83=A8?=
=?UTF-8?q?=E5=88=86=E4=BB=A3=E7=A0=81=E6=B3=A8=E9=87=8A=EF=BC=8C=E4=BF=AE?=
=?UTF-8?q?=E5=A4=8D=20yaml=20=E6=9C=AA=E8=87=AA=E5=8A=A8=E6=8F=90?=
=?UTF-8?q?=E7=A4=BA=20hosts=20=E9=85=8D=E7=BD=AE=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../wxjava/mp/properties/WxMpMultiProperties.java | 9 +++++++++
.../spring/starter/wxjava/mp/properties/HostConfig.java | 9 +++++++++
.../starter/wxjava/mp/properties/WxMpProperties.java | 3 ++-
3 files changed, 20 insertions(+), 1 deletion(-)
diff --git a/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/properties/WxMpMultiProperties.java b/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/properties/WxMpMultiProperties.java
index 7e8949d1bd..c0d331382f 100644
--- a/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/properties/WxMpMultiProperties.java
+++ b/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/properties/WxMpMultiProperties.java
@@ -37,10 +37,19 @@ public class WxMpMultiProperties implements Serializable {
public static class HostConfig implements Serializable {
private static final long serialVersionUID = -4172767630740346001L;
+ /**
+ * 对应于:https://api.weixin.qq.com
+ */
private String apiHost;
+ /**
+ * 对应于:https://open.weixin.qq.com
+ */
private String openHost;
+ /**
+ * 对应于:https://mp.weixin.qq.com
+ */
private String mpHost;
}
diff --git a/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/properties/HostConfig.java b/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/properties/HostConfig.java
index b8c0f1594f..5b29400738 100644
--- a/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/properties/HostConfig.java
+++ b/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/properties/HostConfig.java
@@ -9,10 +9,19 @@ public class HostConfig implements Serializable {
private static final long serialVersionUID = -4172767630740346001L;
+ /**
+ * 对应于:https://api.weixin.qq.com
+ */
private String apiHost;
+ /**
+ * 对应于:https://open.weixin.qq.com
+ */
private String openHost;
+ /**
+ * 对应于:https://mp.weixin.qq.com
+ */
private String mpHost;
}
diff --git a/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/properties/WxMpProperties.java b/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/properties/WxMpProperties.java
index c4c97c4026..a01fc0a521 100644
--- a/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/properties/WxMpProperties.java
+++ b/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/properties/WxMpProperties.java
@@ -40,7 +40,7 @@ public class WxMpProperties {
* 设置微信公众号的EncodingAESKey.
*/
private String aesKey;
-
+
/**
* 是否使用稳定版 Access Token
*/
@@ -49,6 +49,7 @@ public class WxMpProperties {
/**
* 自定义host配置
*/
+ @NestedConfigurationProperty
private HostConfig hosts;
/**
From 167ffdb3baa7cfdce0ad02b3443eb8b3f96be005 Mon Sep 17 00:00:00 2001
From: kevinzhwl
Date: Mon, 19 Aug 2024 14:58:11 +0000
Subject: [PATCH 036/385] =?UTF-8?q?:bug:=20#3223=20=E3=80=90=E5=B0=8F?=
=?UTF-8?q?=E7=A8=8B=E5=BA=8F=E3=80=91=E4=BF=AE=E5=A4=8D=E5=BE=AE=E4=BF=A1?=
=?UTF-8?q?=E8=99=9A=E6=8B=9F=E6=94=AF=E4=BB=98=E7=AD=BE=E5=90=8D=E6=A0=A1?=
=?UTF-8?q?=E9=AA=8C=E5=A4=B1=E8=B4=A5=E7=9A=84=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../cn/binarywang/wx/miniapp/bean/xpay/WxMaXPaySigParams.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/xpay/WxMaXPaySigParams.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/xpay/WxMaXPaySigParams.java
index 089f2d5390..3e11c83b2c 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/xpay/WxMaXPaySigParams.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/xpay/WxMaXPaySigParams.java
@@ -52,7 +52,7 @@ public String calcPaySig(String url, String postBody) {
String ak = StringUtils.trimToEmpty(this.appKey);
final String sigUri = convUrlToSigUri(url);
final String paySig = calcPaySignature(sigUri, postBody, ak);
- return paySig;
+ return paySig.toLowerCase();
}
public String calcSig(String postBody) {
From b6a8721bd1850f551b94374bed517f64fd29d29b Mon Sep 17 00:00:00 2001
From: Binary Wang
Date: Mon, 19 Aug 2024 23:01:33 +0800
Subject: [PATCH 037/385] =?UTF-8?q?:art:=20=E4=BC=98=E5=8C=96=E4=BB=A3?=
=?UTF-8?q?=E7=A0=81=EF=BC=8C=E8=A7=84=E8=8C=83=E6=B3=A8=E9=87=8A?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../miniapp/bean/xpay/WxMaXPaySigParams.java | 47 ++++++++-----------
1 file changed, 20 insertions(+), 27 deletions(-)
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/xpay/WxMaXPaySigParams.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/xpay/WxMaXPaySigParams.java
index 3e11c83b2c..abe6b2b982 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/xpay/WxMaXPaySigParams.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/xpay/WxMaXPaySigParams.java
@@ -22,14 +22,12 @@ public class WxMaXPaySigParams implements Serializable {
public String signUriWithBoth(String url, String postData) {
final String sig = this.calcSig(postData);
final String paySig = this.calcPaySig(url, postData);
- final String uri = String.format(url, paySig, sig);
- return uri;
+ return String.format(url, paySig, sig);
}
public String signUriWithPay(String url, String postData) {
final String paySig = this.calcPaySig(url, postData);
- final String uri = String.format(url, paySig);
- return uri;
+ return String.format(url, paySig);
}
public String signUriWithUser(String url, String postData) {
@@ -57,35 +55,30 @@ public String calcPaySig(String url, String postBody) {
public String calcSig(String postBody) {
String sk = StringUtils.trimToEmpty(this.sessionKey);
- final String sig = calcSignature(postBody, sk);
- return sig;
+ return calcSignature(postBody, sk);
}
+ /**
+ * 用户登录态signature签名算法
+ *
+ * @param postBody - http POST的数据包体
+ * @param sessionKey - 当前用户有效的session_key,参考auth.code2Session接口
+ * @return 用户登录态签名signature
+ */
protected String calcSignature(String postBody, String sessionKey) {
-// """ 用户登录态signature签名算法
-// Args:
-// postBody - http POST的数据包体
-// sessionKey - 当前用户有效的session_key,参考auth.code2Session接口
-// Returns:
-// 用户登录态签名signature
-// """
- String needSignData = postBody;
- String signature = SignUtils.createHmacSha256Sign(needSignData, sessionKey);
- return signature;
+ return SignUtils.createHmacSha256Sign(postBody, sessionKey);
}
-
+ /**
+ * pay_sig签名算法
+ *
+ * @param uri - 当前请求的API的uri部分,不带query_string 例如:/xpay/query_user_balance
+ * @param postBody - http POST的数据包体
+ * @param appKey - 对应环境的AppKey
+ * @return 支付请求签名pay_sig
+ */
protected String calcPaySignature(String uri, String postBody, String appKey) {
-// """ pay_sig签名算法
-// Args:
-// uri - 当前请求的API的uri部分,不带query_string 例如:/xpay/query_user_balance
-// postBody - http POST的数据包体
-// appKey - 对应环境的AppKey
-// Returns:
-// 支付请求签名pay_sig
-// """
String needSignData = uri + '&' + postBody;
- String paySig = SignUtils.createHmacSha256Sign(needSignData, appKey);
- return paySig;
+ return SignUtils.createHmacSha256Sign(needSignData, appKey);
}
}
From 748c19f1085f3dd11d24e235ec54d3ab96993a00 Mon Sep 17 00:00:00 2001
From: Binary Wang
Date: Tue, 20 Aug 2024 11:39:54 +0800
Subject: [PATCH 038/385] =?UTF-8?q?:bug:=20#3348=20=E3=80=90=E5=85=AC?=
=?UTF-8?q?=E5=85=B1=E9=97=AE=E9=A2=98=E3=80=91=E4=BF=AE=E5=A4=8D=E6=97=A0?=
=?UTF-8?q?=E6=B3=95=E8=8E=B7=E5=8F=96=E6=AD=A3=E7=A1=AE=E6=96=87=E4=BB=B6?=
=?UTF-8?q?=E5=90=8D=E7=9A=84=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../common/util/http/HttpResponseProxy.java | 39 ++++++++++++++-----
.../util/http/HttpResponseProxyTest.java | 26 +++++++++++++
2 files changed, 55 insertions(+), 10 deletions(-)
create mode 100644 weixin-java-common/src/test/java/me/chanjar/weixin/common/util/http/HttpResponseProxyTest.java
diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/HttpResponseProxy.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/HttpResponseProxy.java
index 3e0acb46fd..11b1209460 100644
--- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/HttpResponseProxy.java
+++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/HttpResponseProxy.java
@@ -6,6 +6,12 @@
import org.apache.http.Header;
import org.apache.http.client.methods.CloseableHttpResponse;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
/**
*
* 三种http框架的response代理类,方便提取公共方法
@@ -56,29 +62,42 @@ private String getFileName(CloseableHttpResponse response) throws WxErrorExcepti
throw new WxErrorException("无法获取到文件名,Content-disposition为空");
}
- return this.extractFileNameFromContentString(contentDispositionHeader[0].getValue());
+ return extractFileNameFromContentString(contentDispositionHeader[0].getValue());
}
private String getFileName(HttpResponse response) throws WxErrorException {
String content = response.header("Content-disposition");
- return this.extractFileNameFromContentString(content);
+ return extractFileNameFromContentString(content);
}
private String getFileName(Response response) throws WxErrorException {
String content = response.header("Content-disposition");
- return this.extractFileNameFromContentString(content);
+ return extractFileNameFromContentString(content);
}
- private String extractFileNameFromContentString(String content) throws WxErrorException {
- if (content == null || content.length() == 0) {
+ public static String extractFileNameFromContentString(String content) throws WxErrorException {
+ if (content == null || content.isEmpty()) {
throw new WxErrorException("无法获取到文件名,content为空");
}
- int startIndex = content.indexOf("filename=\"");
- if (startIndex != -1) {
- startIndex += "filename=\"".length();
- int endIndex = content.indexOf('"', startIndex);
- return content.substring(startIndex, endIndex);
+ // 查找filename*=utf-8''开头的部分
+ Pattern pattern = Pattern.compile("filename\\*=utf-8''(.*?)($|;|\\s|,)");
+ Matcher matcher = pattern.matcher(content);
+ if (matcher.find()) {
+ String encodedFileName = matcher.group(1);
+ // 解码URL编码的文件名
+ try {
+ return URLDecoder.decode(encodedFileName, StandardCharsets.UTF_8.name());
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ // 查找普通filename="..."部分
+ pattern = Pattern.compile("filename=\"(.*?)\"");
+ matcher = pattern.matcher(content);
+ if (matcher.find()) {
+ return new String(matcher.group(1).getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
}
throw new WxErrorException("无法获取到文件名,header信息有问题");
diff --git a/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/http/HttpResponseProxyTest.java b/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/http/HttpResponseProxyTest.java
new file mode 100644
index 0000000000..4d188b50bc
--- /dev/null
+++ b/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/http/HttpResponseProxyTest.java
@@ -0,0 +1,26 @@
+package me.chanjar.weixin.common.util.http;
+
+import me.chanjar.weixin.common.error.WxErrorException;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.*;
+
+public class HttpResponseProxyTest {
+
+ @Test
+ public void testExtractFileNameFromContentString() throws WxErrorException {
+ String content = "attachment; filename*=utf-8''%E6%B5%8B%E8%AF%95.xlsx; filename=\"��.xlsx\"";
+ String filename = HttpResponseProxy.extractFileNameFromContentString(content);
+ assertNotNull(filename);
+ assertEquals(filename, "测试.xlsx");
+ }
+
+ @Test
+ public void testExtractFileNameFromContentString_another() throws WxErrorException {
+ String content = "attachment; filename*=utf-8''%E8%90%A5%E4%B8%9A%E6%89%A7%E7%85%A7.jpg; filename=\"����.jpg\"";
+// String content = "attachment; filename=\"����.jpg\"";
+ String filename = HttpResponseProxy.extractFileNameFromContentString(content);
+ assertNotNull(filename);
+ assertEquals(filename, "营业执照.jpg");
+ }
+}
From e6ec2a51731c9b280beeb8cf2320a454681400f2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=B0=B4=E4=BE=9D=E5=AF=92?=
Date: Tue, 20 Aug 2024 18:32:44 +0800
Subject: [PATCH 039/385] =?UTF-8?q?:art:=20#3345=20=20=E3=80=90=E5=B0=8F?=
=?UTF-8?q?=E7=A8=8B=E5=BA=8F=E3=80=91=E8=8E=B7=E5=8F=96=E6=89=8B=E6=9C=BA?=
=?UTF-8?q?=E5=8F=B7=20getPhoneNoInfo=E6=96=B9=E6=B3=95=E5=85=BC=E5=AE=B9?=
=?UTF-8?q?=E6=97=A7=E7=89=88=E6=9C=AC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../wx/miniapp/api/WxMaUserService.java | 16 ++++++++++++++--
.../wx/miniapp/api/impl/WxMaUserServiceImpl.java | 6 +++++-
.../api/impl/WxMaUserServiceImplTest.java | 11 ++++++++++-
3 files changed, 29 insertions(+), 4 deletions(-)
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaUserService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaUserService.java
index 3b2c3f74d7..8c6a8ef871 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaUserService.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaUserService.java
@@ -45,7 +45,19 @@ public interface WxMaUserService {
void setUserStorage(Map kvMap, String sessionKey, String openid) throws WxErrorException;
/**
- * 获取手机号信息,2023年8月28日起
+ * 解密用户手机号信息.
+ *
+ * @param sessionKey 会话密钥
+ * @param encryptedData 消息密文
+ * @param ivStr 加密算法的初始向量
+ * @return .
+ * @deprecated 当前(基础库2.21.2以下使用)旧版本,以上请使用替代方法 {@link #getPhoneNoInfo(String)}
+ */
+ @Deprecated
+ WxMaPhoneNumberInfo getPhoneNoInfo(String sessionKey, String encryptedData, String ivStr);
+
+ /**
+ * 获取手机号信息,基础库:2.21.2及以上或2023年8月28日起
*
* @param code 每个code只能使用一次,code的有效期为5min。code获取方式参考手机号快速验证组件
* @return 用户手机号信息
@@ -55,7 +67,7 @@ public interface WxMaUserService {
WxMaPhoneNumberInfo getPhoneNumber(String code) throws WxErrorException;
/**
- * 获取手机号信息,2023年8月28日起
+ * 获取手机号信息,基础库:2.21.2及以上或2023年8月28日起
*
* @param code 每个code只能使用一次,code的有效期为5min。code获取方式参考手机号快速验证组件
* @return 用户手机号信息
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImpl.java
index 8a921d05a6..c9f5c2e335 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImpl.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImpl.java
@@ -57,6 +57,11 @@ public void setUserStorage(Map kvMap, String sessionKey, String
this.service.post(url, params);
}
+ @Override
+ public WxMaPhoneNumberInfo getPhoneNoInfo(String sessionKey, String encryptedData, String ivStr) {
+ return WxMaPhoneNumberInfo.fromJson(WxMaCryptUtils.decrypt(sessionKey, encryptedData, ivStr));
+ }
+
@Override
public WxMaPhoneNumberInfo getPhoneNumber(String code) throws WxErrorException {
JsonObject param = new JsonObject();
@@ -67,7 +72,6 @@ public WxMaPhoneNumberInfo getPhoneNumber(String code) throws WxErrorException {
return WxMaGsonBuilder.create().fromJson(response.getAsJsonObject(PHONE_INFO),
WxMaPhoneNumberInfo.class);
}
-
return null;
}
diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImplTest.java
index d3cd1b7d2a..7c6d610821 100644
--- a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImplTest.java
+++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaUserServiceImplTest.java
@@ -49,7 +49,16 @@ public void testCheckUserInfo() {
@Test
- public void testGetPhoneNoInfo() throws WxErrorException {
+ public void testGetPhoneNoInfo() {
+ WxMaPhoneNumberInfo phoneNoInfo = this.wxService.getUserService().getPhoneNoInfo("tiihtNczf5v6AKRyjwEUhQ==",
+ "CiyLU1Aw2KjvrjMdj8YKliAjtP4gsMZMQmRzooG2xrDcvSnxIMXFufNstNGTyaGS9uT5geRa0W4oTOb1WT7fJlAC+oNPdbB+3hVbJSRgv+4lGOETKUQz6OYStslQ142dNCuabNPGBzlooOmB231qMM85d2/fV6ChevvXvQP8Hkue1poOFtnEtpyxVLW1zAo6/1Xx1COxFvrc2d7UL/lmHInNlxuacJXwu0fjpXfz/YqYzBIBzD6WUfTIF9GRHpOn/Hz7saL8xz+W//FRAUid1OksQaQx4CMs8LOddcQhULW4ucetDf96JcR3g0gfRK4PC7E/r7Z6xNrXd2UIeorGj5Ef7b1pJAYB6Y5anaHqZ9J6nKEBvB4DnNLIVWSgARns/8wR2SiRS7MNACwTyrGvt9ts8p12PKFdlqYTopNHR1Vf7XjfhQlVsAJdNiKdYmYVoKlaRv85IfVunYzO0IKXsyl7JCUjCpoG20f0a04COwfneQAGGwd5oa+T8yO5hzuyDb/XcxxmK01EpqOyuxINew==",
+ "r7BXXKkLb8qrSNn05n0qiA==");
+ assertNotNull(phoneNoInfo);
+ System.out.println(phoneNoInfo.toString());
+ }
+
+ @Test
+ public void testGetPhoneInfo() throws WxErrorException {
WxMaPhoneNumberInfo phoneNoInfo = this.wxService.getUserService().getPhoneNumber("tiihtNczf5v6AKRyjwEUhQ==");
assertNotNull(phoneNoInfo);
System.out.println(phoneNoInfo.toString());
From 317ccada3683e9dda4129a53301babd01485c9d9 Mon Sep 17 00:00:00 2001
From: wzkris <1439804473@qq.com>
Date: Fri, 30 Aug 2024 10:21:46 +0000
Subject: [PATCH 040/385] =?UTF-8?q?:bug:=E3=80=90=E5=BE=AE=E4=BF=A1?=
=?UTF-8?q?=E6=94=AF=E4=BB=98=E3=80=91=E4=BF=AE=E5=A4=8D=E5=95=86=E5=AE=B6?=
=?UTF-8?q?=E8=BD=AC=E8=B4=A6=E5=88=B0=E9=9B=B6=E9=92=B1-=E6=9F=A5?=
=?UTF-8?q?=E8=AF=A2=E6=89=B9=E6=AC=A1=E5=8D=95needQueryDetail=E4=B8=BAfal?=
=?UTF-8?q?se=E6=97=B6=E9=9D=9E=E5=BF=85=E4=BC=A0=E5=8F=82=E6=95=B0?=
=?UTF-8?q?=E4=B8=BAnull=E7=9A=84=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../service/impl/TransferServiceImpl.java | 22 +++++++++++++++----
1 file changed, 18 insertions(+), 4 deletions(-)
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/TransferServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/TransferServiceImpl.java
index b328ded73e..749551b12b 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/TransferServiceImpl.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/TransferServiceImpl.java
@@ -38,8 +38,15 @@ public TransferBatchesResult transferBatches(TransferBatchesRequest request) thr
@Override
public QueryTransferBatchesResult transferBatchesBatchId(QueryTransferBatchesRequest request) throws WxPayException {
- String url = String.format("%s/v3/transfer/batches/batch-id/%s?need_query_detail=%s&offset=%s&limit=%s&detail_status=%s",
- this.payService.getPayBaseUrl(), request.getBatchId(), request.getNeedQueryDetail(), request.getOffset(), request.getLimit(), request.getDetailStatus());
+ String url;
+ if (request.getNeedQueryDetail()) {
+ url = String.format("%s/v3/transfer/batches/batch-id/%s?need_query_detail=true&offset=%s&limit=%s&detail_status=%s",
+ this.payService.getPayBaseUrl(), request.getBatchId(), request.getOffset(), request.getLimit(), request.getDetailStatus());
+ }
+ else {
+ url = String.format("%s/v3/transfer/batches/batch-id/%s?need_query_detail=false",
+ this.payService.getPayBaseUrl(), request.getBatchId());
+ }
String result = this.payService.getV3(url);
return GSON.fromJson(result, QueryTransferBatchesResult.class);
}
@@ -53,8 +60,15 @@ public TransferBatchDetailResult transferBatchesBatchIdDetail(String batchId, St
@Override
public QueryTransferBatchesResult transferBatchesOutBatchNo(QueryTransferBatchesRequest request) throws WxPayException {
- String url = String.format("%s/v3/transfer/batches/out-batch-no/%s?need_query_detail=%s&offset=%s&limit=%s&detail_status=%s",
- this.payService.getPayBaseUrl(), request.getOutBatchNo(), request.getNeedQueryDetail(), request.getOffset(), request.getLimit(), request.getDetailStatus());
+ String url;
+ if (request.getNeedQueryDetail()) {
+ url = String.format("%s/v3/transfer/batches/out-batch-no/%s?need_query_detail=true&offset=%s&limit=%s&detail_status=%s",
+ this.payService.getPayBaseUrl(), request.getOutBatchNo(), request.getOffset(), request.getLimit(), request.getDetailStatus());
+ }
+ else {
+ url = String.format("%s/v3/transfer/batches/out-batch-no/%s?need_query_detail=false",
+ this.payService.getPayBaseUrl(), request.getOutBatchNo());
+ }
String result = this.payService.getV3(url);
return GSON.fromJson(result, QueryTransferBatchesResult.class);
}
From 43d270ac78fbe75c13ce1895969bbc86cfdecf41 Mon Sep 17 00:00:00 2001
From: wzkris <1439804473@qq.com>
Date: Fri, 30 Aug 2024 11:23:11 +0000
Subject: [PATCH 041/385] =?UTF-8?q?:new:=E3=80=90=E5=BE=AE=E4=BF=A1?=
=?UTF-8?q?=E6=94=AF=E4=BB=98=E3=80=91=E5=A2=9E=E5=8A=A0=E5=95=86=E5=AE=B6?=
=?UTF-8?q?=E8=BD=AC=E8=B4=A6=E5=88=B0=E9=9B=B6=E9=92=B1=E7=BB=93=E6=9E=9C?=
=?UTF-8?q?=E5=9B=9E=E8=B0=83=E7=9A=84=E8=A7=A3=E6=9E=90=E6=96=B9=E6=B3=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../bean/transfer/TransferBatchesRequest.java | 6 ++
.../bean/transfer/TransferNotifyResult.java | 92 +++++++++++++++++++
.../wxpay/service/TransferService.java | 12 +++
.../service/impl/TransferServiceImpl.java | 6 ++
4 files changed, 116 insertions(+)
create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferNotifyResult.java
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferBatchesRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferBatchesRequest.java
index 6e6b3846d6..e9f0026e35 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferBatchesRequest.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferBatchesRequest.java
@@ -72,6 +72,12 @@ public class TransferBatchesRequest implements Serializable {
@SerializedName("transfer_scene_id")
private String transferSceneId;
+ /**
+ * 异步接收微信支付结果通知的回调地址,通知url必须为公网可访问的url,必须为https,不能携带参数
+ */
+ @SerializedName("notify_url")
+ private String notifyUrl;
+
@Data
@Builder(builderMethodName = "newBuilder")
@AllArgsConstructor
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferNotifyResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferNotifyResult.java
new file mode 100644
index 0000000000..532bfa3c6e
--- /dev/null
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferNotifyResult.java
@@ -0,0 +1,92 @@
+package com.github.binarywang.wxpay.bean.transfer;
+
+import com.github.binarywang.wxpay.bean.notify.OriginNotifyResponse;
+import com.github.binarywang.wxpay.bean.notify.WxPayBaseNotifyV3Result;
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ *
+ * 商家转账到零钱接口将转账结果通知用户
+ * 文档地址:https://pay.weixin.qq.com/docs/merchant/apis/batch-transfer-to-balance/transfer-batch-callback-notice.html
+ *
+ */
+@Data
+public class TransferNotifyResult implements Serializable, WxPayBaseNotifyV3Result {
+ /**
+ * 源数据
+ */
+ private OriginNotifyResponse rawData;
+ /**
+ * 解密后的数据
+ */
+ private TransferNotifyResult.DecryptNotifyResult result;
+
+ @Data
+ @NoArgsConstructor
+ public static class DecryptNotifyResult implements Serializable {
+ /**
+ * 商户号
+ */
+ @SerializedName(value = "mchid")
+ private String mchid;
+ /**
+ * 商家批次单号
+ */
+ @SerializedName(value = "out_batch_no")
+ private String outBatchNo;
+ /**
+ * 微信批次单号
+ */
+ @SerializedName(value = "batch_id")
+ private String batchId;
+ /**
+ * 批次状态
+ */
+ @SerializedName(value = "batch_status")
+ private String batchStatus;
+ /**
+ * 批次总笔数
+ */
+ @SerializedName(value = "total_num")
+ private Integer totalNum;
+ /**
+ * 批次总金额
+ */
+ @SerializedName(value = "total_amount")
+ private Integer totalAmount;
+ /**
+ * 转账成功金额
+ */
+ @SerializedName(value = "success_amount")
+ private Integer successAmount;
+ /**
+ * 转账成功笔数
+ */
+ @SerializedName(value = "success_num")
+ private Integer successNum;
+ /**
+ * 转账失败金额
+ */
+ @SerializedName(value = "fail_amount")
+ private Integer failAmount;
+ /**
+ * 转账失败笔数
+ */
+ @SerializedName(value = "fail_num")
+ private Integer failNum;
+ /**
+ * 批次更新时间
+ */
+ @SerializedName(value = "update_time")
+ private String updateTime;
+ /**
+ * 批次关闭原因
+ */
+ @SerializedName(value = "close_reason")
+ private String closeReason;
+ }
+}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/TransferService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/TransferService.java
index 8f29f7dce9..ebf746214d 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/TransferService.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/TransferService.java
@@ -1,5 +1,6 @@
package com.github.binarywang.wxpay.service;
+import com.github.binarywang.wxpay.bean.notify.SignatureHeader;
import com.github.binarywang.wxpay.bean.transfer.*;
import com.github.binarywang.wxpay.exception.WxPayException;
@@ -28,6 +29,17 @@ public interface TransferService {
*/
TransferBatchesResult transferBatches(TransferBatchesRequest request) throws WxPayException;
+ /**
+ * 解析商家转账结果
+ * 详见
+ *
+ * @param notifyData 通知数据
+ * @param header 通知头部数据,不传则表示不校验头
+ * @return the wx transfer notify result
+ * @throws WxPayException the wx pay exception
+ */
+ TransferNotifyResult parseTransferNotifyResult(String notifyData, SignatureHeader header) throws WxPayException;
+
/**
*
*
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/TransferServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/TransferServiceImpl.java
index 749551b12b..e62dc9c053 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/TransferServiceImpl.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/TransferServiceImpl.java
@@ -1,5 +1,6 @@
package com.github.binarywang.wxpay.service.impl;
+import com.github.binarywang.wxpay.bean.notify.SignatureHeader;
import com.github.binarywang.wxpay.bean.transfer.*;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.TransferService;
@@ -36,6 +37,11 @@ public TransferBatchesResult transferBatches(TransferBatchesRequest request) thr
return GSON.fromJson(result, TransferBatchesResult.class);
}
+ @Override
+ public TransferNotifyResult parseTransferNotifyResult(String notifyData, SignatureHeader header) throws WxPayException {
+ return this.payService.baseParseOrderNotifyV3Result(notifyData, header, TransferNotifyResult.class, TransferNotifyResult.DecryptNotifyResult.class);
+ }
+
@Override
public QueryTransferBatchesResult transferBatchesBatchId(QueryTransferBatchesRequest request) throws WxPayException {
String url;
From 80a35f30b984d4e561b17d27c5d57b25b89e5505 Mon Sep 17 00:00:00 2001
From: YT <306932545@qq.com>
Date: Fri, 30 Aug 2024 11:25:18 +0000
Subject: [PATCH 042/385] =?UTF-8?q?:new:=20=E3=80=90=E5=BE=AE=E4=BF=A1?=
=?UTF-8?q?=E6=94=AF=E4=BB=98=E3=80=91=E6=96=B0=E5=A2=9E=20=E5=B9=B3?=
=?UTF-8?q?=E5=8F=B0=E6=94=B6=E4=BB=98=E9=80=9A=EF=BC=88=E8=A1=A5=E5=B7=AE?=
=?UTF-8?q?=EF=BC=89=E7=9A=843=E4=B8=AA=E6=8E=A5=E5=8F=A3=E6=96=B9?=
=?UTF-8?q?=E6=B3=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../ecommerce/SubsidiesCancelRequest.java | 61 +++++++
.../bean/ecommerce/SubsidiesCancelResult.java | 83 ++++++++++
.../ecommerce/SubsidiesCreateRequest.java | 105 ++++++++++++
.../bean/ecommerce/SubsidiesCreateResult.java | 126 ++++++++++++++
.../ecommerce/SubsidiesReturnRequest.java | 105 ++++++++++++
.../bean/ecommerce/SubsidiesReturnResult.java | 155 ++++++++++++++++++
.../wxpay/service/EcommerceService.java | 37 +++++
.../service/impl/EcommerceServiceImpl.java | 21 +++
8 files changed, 693 insertions(+)
create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SubsidiesCancelRequest.java
create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SubsidiesCancelResult.java
create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SubsidiesCreateRequest.java
create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SubsidiesCreateResult.java
create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SubsidiesReturnRequest.java
create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SubsidiesReturnResult.java
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SubsidiesCancelRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SubsidiesCancelRequest.java
new file mode 100644
index 0000000000..071fd8d319
--- /dev/null
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SubsidiesCancelRequest.java
@@ -0,0 +1,61 @@
+package com.github.binarywang.wxpay.bean.ecommerce;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * add by 306932545@qq.com
+ * 取消补差请求对象
+ *
+ * https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_5_3.shtml
+ *
+ */
+@Data
+@NoArgsConstructor
+public class SubsidiesCancelRequest implements Serializable {
+
+ /**
+ *
+ * 字段名:二级商户号
+ * 变量名:sub_mchid
+ * 是否必填:是
+ * 类型:string(32)
+ * 描述:
+ * 补差的电商平台二级商户,填写微信支付分配的商户号。
+ * 示例值:1900000109
+ *
+ */
+ @SerializedName(value = "sub_mchid")
+ private String subMchid;
+
+ /**
+ *
+ * 字段名:微信订单号
+ * 变量名:transaction_id
+ * 是否必填:是
+ * 类型:string(64)
+ * 描述:
+ * 微信支付订单号。
+ * 示例值: 4208450740201411110007820472
+ *
+ */
+ @SerializedName(value = "transaction_id")
+ private String transactionId;
+
+ /**
+ *
+ * 字段名:取消补差描述
+ * 变量名:description
+ * 是否必填:是
+ * 类型:string(80)
+ * 描述:
+ * 取消补差描述,查询的时候原样带回。
+ * 示例值:订单退款
+ *
+ */
+ @SerializedName(value = "description")
+ private String description;
+}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SubsidiesCancelResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SubsidiesCancelResult.java
new file mode 100644
index 0000000000..92234df1e4
--- /dev/null
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SubsidiesCancelResult.java
@@ -0,0 +1,83 @@
+package com.github.binarywang.wxpay.bean.ecommerce;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.*;
+
+import java.io.Serializable;
+
+/**
+ * add by 306932545@qq.com
+ * 取消补差返回对象
+ *
+ * https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_5_1.shtml
+ *
+ */
+@Data
+@Builder
+@ToString
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+@AllArgsConstructor(access = AccessLevel.PRIVATE)
+public class SubsidiesCancelResult implements Serializable {
+ private static final long serialVersionUID = 5008480977464421822L;
+
+ /**
+ *
+ * 字段名:二级商户号
+ * 变量名:sub_mchid
+ * 是否必填:是
+ * 类型:string(32)
+ * 描述:
+ * 补差的电商平台二级商户,填写微信支付分配的商户号。
+ * 示例值:1900000109
+ *
+ */
+ @SerializedName(value = "sub_mchid")
+ private String subMchid;
+
+
+ /**
+ *
+ * 字段名:微信订单号
+ * 变量名:transaction_id
+ * 是否必填:是
+ * 类型:string(64)
+ * 描述:
+ * 微信支付订单号。
+ * 示例值: 4208450740201411110007820472
+ *
+ */
+ @SerializedName(value = "transaction_id")
+ private String transactionId;
+
+ /**
+ *
+ * 字段名:取消补差结果
+ * 变量名:result
+ * 是否必填:是
+ * 类型:string(16)
+ * 描述:
+ * 取消补差结果,枚举值:
+ * SUCCESS:成功
+ * FAIL:失败
+ * 示例值:SUCCESS
+ *
+ */
+ @SerializedName(value = "result")
+ private String result;
+
+ /**
+ *
+ * 字段名:取消补差描述
+ * 变量名:description
+ * 是否必填:是
+ * 类型:string(80)
+ * 描述:
+ * 取消补差描述
+ * 示例值:订单退款
+ *
+ */
+ @SerializedName(value = "description")
+ private String description;
+
+
+}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SubsidiesCreateRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SubsidiesCreateRequest.java
new file mode 100644
index 0000000000..313a070ff7
--- /dev/null
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SubsidiesCreateRequest.java
@@ -0,0 +1,105 @@
+package com.github.binarywang.wxpay.bean.ecommerce;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * add by 306932545@qq.com
+ * 请求补差请求对象
+ *
+ * https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_5_1.shtml
+ *
+ */
+@Data
+@NoArgsConstructor
+public class SubsidiesCreateRequest implements Serializable {
+
+ /**
+ *
+ * 字段名:二级商户号
+ * 变量名:sub_mchid
+ * 是否必填:是
+ * 类型:string(32)
+ * 描述:
+ * 补差的电商平台二级商户,填写微信支付分配的商户号。
+ * 示例值:1900000109
+ *
+ */
+ @SerializedName(value = "sub_mchid")
+ private String subMchid;
+
+ /**
+ *
+ * 字段名:微信订单号
+ * 变量名:transaction_id
+ * 是否必填:是
+ * 类型:string(64)
+ * 描述:
+ * 微信支付订单号。
+ * 示例值: 4208450740201411110007820472
+ *
+ */
+ @SerializedName(value = "transaction_id")
+ private String transactionId;
+
+ /**
+ *
+ * 字段名:商户补差单号
+ * 变量名:out_subsidy_no
+ * 是否必填:是
+ * 类型:string(64)
+ * 描述:
+ * 商户系统内部的补差单号,在商户系统内部唯一,同一补差单号多次请求等同一次。
+ * 示例值:P20150806125347
+ *
+ */
+ @SerializedName(value = "out_subsidy_no")
+ private String outSubsidyNo;
+
+ /**
+ *
+ * 字段名:补差金额
+ * 变量名:amount
+ * 是否必填:是
+ * 类型:int64
+ * 描述:
+ * 补差金额,单位为分,只能为整数,不能超过下单时候的最大补差金额。
+ * 注意:单笔订单最高补差金额为10000元
+ * 示例值:10
+ *
+ */
+ @SerializedName(value = "amount")
+ private Integer amount;
+
+ /**
+ *
+ * 字段名:补差描述
+ * 变量名:description
+ * 是否必填:是
+ * 类型:string(80)
+ * 描述:
+ * 补差备注描述,查询的时候原样带回。
+ * 示例值:测试备注
+ *
+ */
+ @SerializedName(value = "description")
+ private String description;
+
+ /**
+ *
+ * 字段名:微信退款单号
+ * 变量名:refund_id
+ * 是否必填:否
+ * 类型:string(32)
+ * 描述:
+ * 微信退款单号,微信支付系统退款返回的唯一标识,当补差金额小于下单时候的金额,该字段必填
+ * 示例值:3008450740201411110007820472
+ *
+ */
+ @SerializedName(value = "refund_id")
+ private String refundId;
+
+}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SubsidiesCreateResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SubsidiesCreateResult.java
new file mode 100644
index 0000000000..c3a4bd1516
--- /dev/null
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SubsidiesCreateResult.java
@@ -0,0 +1,126 @@
+package com.github.binarywang.wxpay.bean.ecommerce;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.*;
+
+import java.io.Serializable;
+
+/**
+ * add by 306932545@qq.com
+ * 请求补差返回对象
+ *
+ * https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_5_1.shtml
+ *
+ */
+@Data
+@Builder
+@ToString
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+@AllArgsConstructor(access = AccessLevel.PRIVATE)
+public class SubsidiesCreateResult implements Serializable {
+ private static final long serialVersionUID = 5008480977464421822L;
+
+ /**
+ *
+ * 字段名:二级商户号
+ * 变量名:sub_mchid
+ * 是否必填:是
+ * 类型:string(32)
+ * 描述:
+ * 补差的电商平台二级商户,填写微信支付分配的商户号。
+ * 示例值:1900000109
+ *
+ */
+ @SerializedName(value = "sub_mchid")
+ private String subMchid;
+
+
+ /**
+ *
+ * 字段名:微信订单号
+ * 变量名:transaction_id
+ * 是否必填:是
+ * 类型:string(64)
+ * 描述:
+ * 微信支付订单号。
+ * 示例值: 4208450740201411110007820472
+ *
+ */
+ @SerializedName(value = "transaction_id")
+ private String transactionId;
+
+ /**
+ *
+ * 字段名:微信补差单号
+ * 变量名:subsidy_id
+ * 是否必填:是
+ * 类型:string(64)
+ * 描述:
+ * 微信补差单号,微信支付系统返回的唯一标识。
+ * 示例值: 3008450740201411110007820472
+ *
+ */
+ @SerializedName(value = "subsidy_id")
+ private String subsidyId;
+
+ /**
+ *
+ * 字段名:补差描述
+ * 变量名:description
+ * 是否必填:是
+ * 类型:string(80)
+ * 描述:
+ * 补差备注描述,查询的时候原样带回。
+ * 示例值:测试备注
+ *
+ */
+ @SerializedName(value = "description")
+ private String description;
+
+ /**
+ *
+ * 字段名:补差金额
+ * 变量名:amount
+ * 是否必填:是
+ * 类型:int64
+ * 描述:
+ * 补差金额,单位为分,只能为整数,不能超过下单时候的最大补差金额。
+ * 注意:单笔订单最高补差金额为10000元
+ * 示例值:10
+ *
+ */
+ @SerializedName(value = "amount")
+ private Integer amount;
+
+ /**
+ *
+ * 字段名:补差单结果
+ * 变量名:result
+ * 是否必填:是
+ * 类型:string(16)
+ * 描述:
+ * 补差单状态,枚举值:
+ * SUCCESS:补差成功
+ * FAIL:补差失败
+ * REFUND:已全额回退
+ * 示例值:SUCCESS
+ *
+ */
+ @SerializedName(value = "result")
+ private String result;
+
+ /**
+ *
+ * 字段名:补差完成时间
+ * 变量名:success_time
+ * 是否必填:是
+ * 类型:string(32)
+ * 描述:
+ * 补贴完成时间,遵循rfc3339标准格式,格式为yyyy-MM-DDTHH:mm:ss:sss+TIMEZONE,yyyy-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss:sss表示时分秒毫秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。例如:2015-05-20T13:29:35+08:00表示,北京时间2015年5月20日13点29分35秒。
+ * 示例值: 2015-05-20T13:29:35.120+08:00
+ *
+ */
+ @SerializedName(value = "success_time")
+ private String successTime;
+
+}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SubsidiesReturnRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SubsidiesReturnRequest.java
new file mode 100644
index 0000000000..257d7abe45
--- /dev/null
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SubsidiesReturnRequest.java
@@ -0,0 +1,105 @@
+package com.github.binarywang.wxpay.bean.ecommerce;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * add by 306932545@qq.com
+ * 请求补差回退API-请求对象
+ *
+ * https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_5_2.shtml
+ *
+ */
+@Data
+@NoArgsConstructor
+public class SubsidiesReturnRequest implements Serializable {
+
+ /**
+ *
+ * 字段名:二级商户号
+ * 变量名:sub_mchid
+ * 是否必填:是
+ * 类型:string(32)
+ * 描述:
+ * 补差的电商平台二级商户,填写微信支付分配的商户号。
+ * 示例值:1900000109
+ *
+ */
+ @SerializedName(value = "sub_mchid")
+ private String subMchid;
+
+ /**
+ *
+ * 字段名:微信订单号
+ * 变量名:transaction_id
+ * 是否必填:是
+ * 类型:string(64)
+ * 描述:
+ * 微信支付订单号。
+ * 示例值: 4208450740201411110007820472
+ *
+ */
+ @SerializedName(value = "transaction_id")
+ private String transactionId;
+
+ /**
+ *
+ * 字段名:商户补差回退单号
+ * 变量名:out_order_no
+ * 是否必填:是
+ * 类型:string(64)
+ * 描述:
+ * 原发起补差请求时使用的商户系统内部的补差单号。
+ * 示例值:P20150806125346
+ *
+ */
+ @SerializedName(value = "out_order_no")
+ private String outOrderNo;
+
+ /**
+ *
+ * 字段名:补差金额
+ * 变量名:amount
+ * 是否必填:是
+ * 类型:int64
+ * 描述:
+ * 补差金额,单位为分,只能为整数,不能超过下单时候的最大补差金额。
+ * 注意:单笔订单最高补差金额为10000元
+ * 示例值:10
+ *
+ */
+ @SerializedName(value = "amount")
+ private Integer amount;
+
+ /**
+ *
+ * 字段名:补差描述
+ * 变量名:description
+ * 是否必填:是
+ * 类型:string(80)
+ * 描述:
+ * 补差备注描述,查询的时候原样带回。
+ * 示例值:测试备注
+ *
+ */
+ @SerializedName(value = "description")
+ private String description;
+
+ /**
+ *
+ * 字段名:微信退款单号
+ * 变量名:refund_id
+ * 是否必填:否
+ * 类型:string(64)
+ * 描述:
+ * 微信退款单号,微信支付系统退款返回的唯一标识。
+ * 用户零钱账户异常,无法在线发起退款时,此字段可以不传;其他情况下必传。
+ * 示例值:3008450740201411110007820472
+ *
+ */
+ @SerializedName(value = "refund_id")
+ private String refundId;
+}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SubsidiesReturnResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SubsidiesReturnResult.java
new file mode 100644
index 0000000000..fc319a016a
--- /dev/null
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SubsidiesReturnResult.java
@@ -0,0 +1,155 @@
+package com.github.binarywang.wxpay.bean.ecommerce;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.*;
+
+import java.io.Serializable;
+
+/**
+ * add by 306932545@qq.com
+ * 请求补差返回对象
+ *
+ * https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_5_2.shtml
+ *
+ */
+@Data
+@Builder
+@ToString
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+@AllArgsConstructor(access = AccessLevel.PRIVATE)
+public class SubsidiesReturnResult implements Serializable {
+ private static final long serialVersionUID = 5008480977464421822L;
+
+ /**
+ *
+ * 字段名:二级商户号
+ * 变量名:sub_mchid
+ * 是否必填:是
+ * 类型:string(32)
+ * 描述:
+ * 补差的电商平台二级商户,填写微信支付分配的商户号。
+ * 示例值:1900000109
+ *
+ */
+ @SerializedName(value = "sub_mchid")
+ private String subMchid;
+
+
+ /**
+ *
+ * 字段名:微信订单号
+ * 变量名:transaction_id
+ * 是否必填:是
+ * 类型:string(64)
+ * 描述:
+ * 微信支付订单号。
+ * 示例值: 4208450740201411110007820472
+ *
+ */
+ @SerializedName(value = "transaction_id")
+ private String transactionId;
+
+ /**
+ *
+ * 字段名:微信补差单号
+ * 变量名:subsidy_refund_id
+ * 是否必填:是
+ * 类型:string(64)
+ * 描述:
+ * 微信补差单号,微信支付系统返回的唯一标识。
+ * 示例值: 3008450740201411110007820472
+ *
+ */
+ @SerializedName(value = "subsidy_refund_id")
+ private String subsidyRefundId;
+
+ /**
+ *
+ * 字段名:微信退款单号
+ * 变量名:refund_id
+ * 是否必填:否
+ * 类型:string(64)
+ * 描述:
+ * 微信退款单号,微信支付系统退款返回的唯一标识。
+ * 示例值: 3008450740201411110007820472
+ *
+ */
+ @SerializedName(value = "refund_id")
+ private String refundId;
+
+ /**
+ *
+ * 字段名:商户补差回退单号
+ * 变量名:out_order_no
+ * 是否必填:是
+ * 类型:string(64)
+ * 描述:
+ * 商户系统内部的补差回退单号,在商户系统内部唯一,只能是数字、大小写字母_-|*@ ,同一补差回退单号多次请求等同一次。
+ * 示例值:P20150806125346
+ *
+ */
+ @SerializedName(value = "out_order_no")
+ private String outOrderNo;
+
+ /**
+ *
+ * 字段名:补差描述
+ * 变量名:description
+ * 是否必填:是
+ * 类型:string(80)
+ * 描述:
+ * 补差备注描述,查询的时候原样带回。
+ * 示例值:测试备注
+ *
+ */
+ @SerializedName(value = "description")
+ private String description;
+
+ /**
+ *
+ * 字段名:补差金额
+ * 变量名:amount
+ * 是否必填:是
+ * 类型:int64
+ * 描述:
+ * 补差金额,单位为分,只能为整数,不能超过下单时候的最大补差金额。
+ * 注意:单笔订单最高补差金额为10000元
+ * 示例值:10
+ *
+ */
+ @SerializedName(value = "amount")
+ private Integer amount;
+
+ /**
+ *
+ * 字段名:补差单结果
+ * 变量名:result
+ * 是否必填:是
+ * 类型:string(16)
+ * 描述:
+ * 补差单状态,枚举值:
+ * SUCCESS:补差成功
+ * FAIL:补差失败
+ * REFUND:已全额回退
+ * 示例值:SUCCESS
+ *
+ */
+ @SerializedName(value = "result")
+ private String result;
+
+ /**
+ *
+ * 字段名:补差完成时间
+ * 变量名:success_time
+ * 是否必填:是
+ * 类型:string(32)
+ * 描述:
+ * 补贴完成时间,遵循rfc3339标准格式,格式为yyyy-MM-DDTHH:mm:ss:sss+TIMEZONE,yyyy-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss:sss表示时分秒毫秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。例如:2015-05-20T13:29:35+08:00表示,北京时间2015年5月20日13点29分35秒。
+ * 示例值: 2015-05-20T13:29:35.120+08:00
+ *
+ */
+ @SerializedName(value = "success_time")
+ private String successTime;
+
+
+}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java
index 5b2a8beae7..ca1cfb66b5 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java
@@ -498,4 +498,41 @@ public interface EcommerceService {
*/
InputStream downloadBill(String url) throws WxPayException;
+
+ /**
+ *
+ * 请求补差API
+ * 文档地址: https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_5_1.shtml
+ *
+ *
+ * @param subsidiesCreateRequest 请求补差。
+ * @return 返回数据 return SubsidiesCreateResult
+ * @throws WxPayException the wx pay exception
+ */
+ SubsidiesCreateResult subsidiesCreate(SubsidiesCreateRequest subsidiesCreateRequest) throws WxPayException;
+
+ /**
+ *
+ * 请求补差回退API
+ * 文档地址: https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_5_2.shtml
+ *
+ *
+ * @param subsidiesReturnRequest 请求补差。
+ * @return 返回数据 return SubsidiesReturnResult
+ * @throws WxPayException the wx pay exception
+ */
+ SubsidiesReturnResult subsidiesReturn(SubsidiesReturnRequest subsidiesReturnRequest) throws WxPayException;
+
+ /**
+ *
+ * 取消补差API
+ * 文档地址: https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_5_3.shtml
+ *
+ *
+ * @param subsidiesCancelRequest 请求补差。
+ * @return 返回数据 return SubsidiesCancelResult
+ * @throws WxPayException the wx pay exception
+ */
+ SubsidiesCancelResult subsidiesCancel(SubsidiesCancelRequest subsidiesCancelRequest) throws WxPayException;
+
}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java
index 3d9a1af626..edd2a2f4a6 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java
@@ -374,6 +374,27 @@ public InputStream downloadBill(String url) throws WxPayException {
return this.payService.downloadV3(url);
}
+ @Override
+ public SubsidiesCreateResult subsidiesCreate(SubsidiesCreateRequest subsidiesCreateRequest) throws WxPayException{
+ String url = String.format("%s/v3/ecommerce/subsidies/create", this.payService.getPayBaseUrl());
+ String response = this.payService.postV3(url, GSON.toJson(subsidiesCreateRequest));
+ return GSON.fromJson(response, SubsidiesCreateResult.class);
+ }
+
+ @Override
+ public SubsidiesReturnResult subsidiesReturn(SubsidiesReturnRequest subsidiesReturnRequest) throws WxPayException{
+ String url = String.format("%s/v3/ecommerce/subsidies/return", this.payService.getPayBaseUrl());
+ String response = this.payService.postV3(url, GSON.toJson(subsidiesReturnRequest));
+ return GSON.fromJson(response, SubsidiesReturnResult.class);
+ }
+
+
+ @Override
+ public SubsidiesCancelResult subsidiesCancel(SubsidiesCancelRequest subsidiesCancelRequest) throws WxPayException{
+ String url = String.format("%s/v3/ecommerce/subsidies/cancel", this.payService.getPayBaseUrl());
+ String response = this.payService.postV3(url, GSON.toJson(subsidiesCancelRequest));
+ return GSON.fromJson(response, SubsidiesCancelResult.class);
+ }
/**
* 校验通知签名
*
From 8ceca63f280d56b0941c285755cfaca7839e865d Mon Sep 17 00:00:00 2001
From: Binary Wang
Date: Sun, 1 Sep 2024 21:46:25 +0800
Subject: [PATCH 043/385] =?UTF-8?q?:bookmark:=20=E5=8F=91=E5=B8=83=204.6.4?=
=?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-channel-spring-boot-starter/pom.xml | 2 +-
.../wx-java-cp-multi-spring-boot-starter/pom.xml | 2 +-
spring-boot-starters/wx-java-cp-spring-boot-starter/pom.xml | 2 +-
.../wx-java-miniapp-spring-boot-starter/pom.xml | 2 +-
.../wx-java-mp-multi-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 +-
spring-boot-starters/wx-java-qidian-spring-boot-starter/pom.xml | 2 +-
weixin-graal/pom.xml | 2 +-
weixin-java-channel/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 +-
weixin-java-qidian/pom.xml | 2 +-
20 files changed, 20 insertions(+), 20 deletions(-)
diff --git a/pom.xml b/pom.xml
index 482fda5471..9e91f60778 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
4.0.0
com.github.binarywang
wx-java
- 4.6.3.B
+ 4.6.4.B
pom
WxJava - Weixin/Wechat Java SDK
微信开发Java SDK
diff --git a/spring-boot-starters/pom.xml b/spring-boot-starters/pom.xml
index 9bc9071cc2..ae90dd12f5 100644
--- a/spring-boot-starters/pom.xml
+++ b/spring-boot-starters/pom.xml
@@ -6,7 +6,7 @@
com.github.binarywang
wx-java
- 4.6.3.B
+ 4.6.4.B
pom
wx-java-spring-boot-starters
diff --git a/spring-boot-starters/wx-java-channel-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-channel-spring-boot-starter/pom.xml
index b2d8fa1c9b..216a5ede68 100644
--- a/spring-boot-starters/wx-java-channel-spring-boot-starter/pom.xml
+++ b/spring-boot-starters/wx-java-channel-spring-boot-starter/pom.xml
@@ -3,7 +3,7 @@
wx-java-spring-boot-starters
com.github.binarywang
- 4.6.3.B
+ 4.6.4.B
4.0.0
diff --git a/spring-boot-starters/wx-java-cp-multi-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-cp-multi-spring-boot-starter/pom.xml
index 3851cde34a..45b55e1444 100644
--- a/spring-boot-starters/wx-java-cp-multi-spring-boot-starter/pom.xml
+++ b/spring-boot-starters/wx-java-cp-multi-spring-boot-starter/pom.xml
@@ -4,7 +4,7 @@
wx-java-spring-boot-starters
com.github.binarywang
- 4.6.3.B
+ 4.6.4.B
4.0.0
diff --git a/spring-boot-starters/wx-java-cp-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-cp-spring-boot-starter/pom.xml
index 9840fb264c..f718a0b0aa 100644
--- a/spring-boot-starters/wx-java-cp-spring-boot-starter/pom.xml
+++ b/spring-boot-starters/wx-java-cp-spring-boot-starter/pom.xml
@@ -4,7 +4,7 @@
wx-java-spring-boot-starters
com.github.binarywang
- 4.6.3.B
+ 4.6.4.B
4.0.0
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 3a1ad2e189..e38a606435 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
@@ -4,7 +4,7 @@
wx-java-spring-boot-starters
com.github.binarywang
- 4.6.3.B
+ 4.6.4.B
4.0.0
diff --git a/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/pom.xml
index 4b407c0564..14232f0589 100644
--- a/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/pom.xml
+++ b/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/pom.xml
@@ -5,7 +5,7 @@
wx-java-spring-boot-starters
com.github.binarywang
- 4.6.3.B
+ 4.6.4.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 470d754412..7d61def348 100644
--- a/spring-boot-starters/wx-java-mp-spring-boot-starter/pom.xml
+++ b/spring-boot-starters/wx-java-mp-spring-boot-starter/pom.xml
@@ -5,7 +5,7 @@
wx-java-spring-boot-starters
com.github.binarywang
- 4.6.3.B
+ 4.6.4.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 8ea934b0f0..4d65890853 100644
--- a/spring-boot-starters/wx-java-open-spring-boot-starter/pom.xml
+++ b/spring-boot-starters/wx-java-open-spring-boot-starter/pom.xml
@@ -5,7 +5,7 @@
wx-java-spring-boot-starters
com.github.binarywang
- 4.6.3.B
+ 4.6.4.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 d318e0a132..48480875ec 100644
--- a/spring-boot-starters/wx-java-pay-spring-boot-starter/pom.xml
+++ b/spring-boot-starters/wx-java-pay-spring-boot-starter/pom.xml
@@ -5,7 +5,7 @@
wx-java-spring-boot-starters
com.github.binarywang
- 4.6.3.B
+ 4.6.4.B
4.0.0
diff --git a/spring-boot-starters/wx-java-qidian-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-qidian-spring-boot-starter/pom.xml
index a2a088cc70..cf0a73c5c8 100644
--- a/spring-boot-starters/wx-java-qidian-spring-boot-starter/pom.xml
+++ b/spring-boot-starters/wx-java-qidian-spring-boot-starter/pom.xml
@@ -3,7 +3,7 @@
wx-java-spring-boot-starters
com.github.binarywang
- 4.6.3.B
+ 4.6.4.B
4.0.0
diff --git a/weixin-graal/pom.xml b/weixin-graal/pom.xml
index cbc0d30b67..125efdaded 100644
--- a/weixin-graal/pom.xml
+++ b/weixin-graal/pom.xml
@@ -6,7 +6,7 @@
com.github.binarywang
wx-java
- 4.6.3.B
+ 4.6.4.B
weixin-graal
diff --git a/weixin-java-channel/pom.xml b/weixin-java-channel/pom.xml
index 09b6c9895c..7062d58fda 100644
--- a/weixin-java-channel/pom.xml
+++ b/weixin-java-channel/pom.xml
@@ -6,7 +6,7 @@
com.github.binarywang
wx-java
- 4.6.3.B
+ 4.6.4.B
weixin-java-channel
diff --git a/weixin-java-common/pom.xml b/weixin-java-common/pom.xml
index 5e03dfd931..0ef68637ee 100644
--- a/weixin-java-common/pom.xml
+++ b/weixin-java-common/pom.xml
@@ -6,7 +6,7 @@
com.github.binarywang
wx-java
- 4.6.3.B
+ 4.6.4.B
weixin-java-common
diff --git a/weixin-java-cp/pom.xml b/weixin-java-cp/pom.xml
index 1d3b845c52..4e41cd107b 100644
--- a/weixin-java-cp/pom.xml
+++ b/weixin-java-cp/pom.xml
@@ -7,7 +7,7 @@
com.github.binarywang
wx-java
- 4.6.3.B
+ 4.6.4.B
weixin-java-cp
diff --git a/weixin-java-miniapp/pom.xml b/weixin-java-miniapp/pom.xml
index b6ac06a0b5..84120767fb 100644
--- a/weixin-java-miniapp/pom.xml
+++ b/weixin-java-miniapp/pom.xml
@@ -7,7 +7,7 @@
com.github.binarywang
wx-java
- 4.6.3.B
+ 4.6.4.B
weixin-java-miniapp
diff --git a/weixin-java-mp/pom.xml b/weixin-java-mp/pom.xml
index b1a145075c..093ceefa57 100644
--- a/weixin-java-mp/pom.xml
+++ b/weixin-java-mp/pom.xml
@@ -7,7 +7,7 @@
com.github.binarywang
wx-java
- 4.6.3.B
+ 4.6.4.B
weixin-java-mp
diff --git a/weixin-java-open/pom.xml b/weixin-java-open/pom.xml
index 0e89ecb228..1594a1dccf 100644
--- a/weixin-java-open/pom.xml
+++ b/weixin-java-open/pom.xml
@@ -7,7 +7,7 @@
com.github.binarywang
wx-java
- 4.6.3.B
+ 4.6.4.B
weixin-java-open
diff --git a/weixin-java-pay/pom.xml b/weixin-java-pay/pom.xml
index b0ee75d409..2623e57b97 100644
--- a/weixin-java-pay/pom.xml
+++ b/weixin-java-pay/pom.xml
@@ -5,7 +5,7 @@
com.github.binarywang
wx-java
- 4.6.3.B
+ 4.6.4.B
4.0.0
diff --git a/weixin-java-qidian/pom.xml b/weixin-java-qidian/pom.xml
index 914fd5d003..998bfa6bdf 100644
--- a/weixin-java-qidian/pom.xml
+++ b/weixin-java-qidian/pom.xml
@@ -7,7 +7,7 @@
com.github.binarywang
wx-java
- 4.6.3.B
+ 4.6.4.B
weixin-java-qidian
From 41bb5e44cc2248261323d2a20d0c0eeb3654f392 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=A5=BF=E4=B8=9C?=
Date: Mon, 2 Sep 2024 17:18:56 +0800
Subject: [PATCH 044/385] =?UTF-8?q?:new:=20#3217=20=E5=A2=9E=E5=8A=A0=20so?=
=?UTF-8?q?lon-plugins=20=E9=80=82=E9=85=8D?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
pom.xml | 1 +
solon-plugins/pom.xml | 44 +++++
.../wx-java-channel-solon-plugin/pom.xml | 31 ++++
.../WxChannelServiceAutoConfiguration.java | 35 ++++
...ctWxChannelConfigStorageConfiguration.java | 39 +++++
...nnelInJedisConfigStorageConfiguration.java | 74 ++++++++
...nelInMemoryConfigStorageConfiguration.java | 29 +++
...lInRedissonConfigStorageConfiguration.java | 62 +++++++
.../wxjava/channel/enums/HttpClientType.java | 13 ++
.../wxjava/channel/enums/StorageType.java | 25 +++
.../integration/WxChannelPluginImpl.java | 25 +++
.../channel/properties/RedisProperties.java | 42 +++++
.../properties/WxChannelProperties.java | 109 ++++++++++++
.../wx-java-channel-solon-plugin.properties | 2 +
.../wx-java-cp-multi-solon-plugin/README.md | 95 ++++++++++
.../wx-java-cp-multi-solon-plugin/pom.xml | 32 ++++
.../services/AbstractWxCpConfiguration.java | 162 +++++++++++++++++
.../services/WxCpInJedisConfiguration.java | 77 ++++++++
.../services/WxCpInMemoryConfiguration.java | 38 ++++
.../services/WxCpInRedissonConfiguration.java | 68 ++++++++
.../integration/WxCpMultiPluginImpl.java | 21 +++
.../properties/WxCpMultiProperties.java | 129 ++++++++++++++
.../properties/WxCpMultiRedisProperties.java | 48 +++++
.../properties/WxCpSingleProperties.java | 46 +++++
.../cp_multi/service/WxCpMultiServices.java | 26 +++
.../service/WxCpMultiServicesImpl.java | 42 +++++
.../wx-java-cp-multi-solon-plugin.properties | 2 +
.../wx-java-cp-solon-plugin/README.md | 41 +++++
solon-plugins/wx-java-cp-solon-plugin/pom.xml | 30 ++++
.../config/WxCpServiceAutoConfiguration.java | 43 +++++
.../wxjava/cp/integration/WxCpPluginImpl.java | 25 +++
.../wxjava/cp/properties/WxCpProperties.java | 133 ++++++++++++++
.../cp/properties/WxCpRedisProperties.java | 46 +++++
...bstractWxCpConfigStorageConfiguration.java | 61 +++++++
...WxCpInJedisConfigStorageConfiguration.java | 74 ++++++++
...xCpInMemoryConfigStorageConfiguration.java | 31 ++++
...pInRedissonConfigStorageConfiguration.java | 65 +++++++
.../solon/wx-java-cp-solon-plugin.properties | 2 +
.../wx-java-miniapp-solon-plugin/README.md | 35 ++++
.../wx-java-miniapp-solon-plugin/pom.xml | 43 +++++
.../config/WxMaServiceAutoConfiguration.java | 54 ++++++
...bstractWxMaConfigStorageConfiguration.java | 39 +++++
...WxMaInJedisConfigStorageConfiguration.java | 72 ++++++++
...xMaInMemoryConfigStorageConfiguration.java | 28 +++
...aInRedissonConfigStorageConfiguration.java | 61 +++++++
.../wxjava/miniapp/enums/HttpClientType.java | 22 +++
.../wxjava/miniapp/enums/StorageType.java | 26 +++
.../integration/WxMiniappPluginImpl.java | 25 +++
.../miniapp/properties/RedisProperties.java | 43 +++++
.../miniapp/properties/WxMaProperties.java | 112 ++++++++++++
.../wx-java-miniapp-solon-plugin.properties | 2 +
.../wx-java-mp-multi-solon-plugin/README.md | 100 +++++++++++
.../wx-java-mp-multi-solon-plugin/pom.xml | 44 +++++
.../services/AbstractWxMpConfiguration.java | 165 ++++++++++++++++++
.../services/WxMpInJedisConfiguration.java | 78 +++++++++
.../services/WxMpInMemoryConfiguration.java | 40 +++++
.../services/WxMpInRedissonConfiguration.java | 68 ++++++++
.../integration/WxMpMultiPluginImpl.java | 23 +++
.../properties/WxMpMultiProperties.java | 154 ++++++++++++++++
.../properties/WxMpMultiRedisProperties.java | 56 ++++++
.../properties/WxMpSingleProperties.java | 40 +++++
.../mp_multi/service/WxMpMultiServices.java | 27 +++
.../service/WxMpMultiServicesImpl.java | 36 ++++
.../wx-java-mp-multi-solon-plugin.properties | 2 +
.../wx-java-mp-solon-plugin/README.md | 46 +++++
solon-plugins/wx-java-mp-solon-plugin/pom.xml | 39 +++++
.../config/WxMpServiceAutoConfiguration.java | 63 +++++++
.../config/WxMpStorageAutoConfiguration.java | 128 ++++++++++++++
.../solon/wxjava/mp/enums/HttpClientType.java | 22 +++
.../solon/wxjava/mp/enums/StorageType.java | 26 +++
.../wxjava/mp/integration/WxMpPluginImpl.java | 20 +++
.../wxjava/mp/properties/HostConfig.java | 27 +++
.../wxjava/mp/properties/RedisProperties.java | 56 ++++++
.../wxjava/mp/properties/WxMpProperties.java | 106 +++++++++++
.../solon/wx-java-mp-solon-plugin.properties | 2 +
.../wx-java-open-solon-plugin/README.md | 39 +++++
.../wx-java-open-solon-plugin/pom.xml | 32 ++++
.../WxOpenServiceAutoConfiguration.java | 37 ++++
...tractWxOpenConfigStorageConfiguration.java | 33 ++++
...OpenInJedisConfigStorageConfiguration.java | 71 ++++++++
...penInMemoryConfigStorageConfiguration.java | 28 +++
...nInRedissonConfigStorageConfiguration.java | 62 +++++++
.../open/integration/WxOpenPluginImpl.java | 25 +++
.../open/properties/WxOpenProperties.java | 138 +++++++++++++++
.../properties/WxOpenRedisProperties.java | 45 +++++
.../wx-java-open-solon-plugin.properties | 2 +
.../wx-java-pay-solon-plugin/README.md | 43 +++++
.../wx-java-pay-solon-plugin/pom.xml | 24 +++
.../pay/config/WxPayAutoConfiguration.java | 61 +++++++
.../pay/integration/WxPayPluginImpl.java | 18 ++
.../pay/properties/WxPayProperties.java | 85 +++++++++
.../solon/wx-java-pay-solon-plugin.properties | 2 +
.../wx-java-qidian-solon-plugin/README.md | 45 +++++
.../wx-java-qidian-solon-plugin/pom.xml | 38 ++++
.../WxQidianServiceAutoConfiguration.java | 63 +++++++
.../WxQidianStorageAutoConfiguration.java | 135 ++++++++++++++
.../wxjava/qidian/enums/HttpClientType.java | 22 +++
.../wxjava/qidian/enums/StorageType.java | 26 +++
.../integration/WxQidianPluginImpl.java | 20 +++
.../wxjava/qidian/properties/HostConfig.java | 18 ++
.../qidian/properties/RedisProperties.java | 56 ++++++
.../qidian/properties/WxQidianProperties.java | 101 +++++++++++
.../wx-java-qidian-solon-plugin.properties | 2 +
103 files changed, 5069 insertions(+)
create mode 100644 solon-plugins/pom.xml
create mode 100644 solon-plugins/wx-java-channel-solon-plugin/pom.xml
create mode 100644 solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/config/WxChannelServiceAutoConfiguration.java
create mode 100644 solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/config/storage/AbstractWxChannelConfigStorageConfiguration.java
create mode 100644 solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/config/storage/WxChannelInJedisConfigStorageConfiguration.java
create mode 100644 solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/config/storage/WxChannelInMemoryConfigStorageConfiguration.java
create mode 100644 solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/config/storage/WxChannelInRedissonConfigStorageConfiguration.java
create mode 100644 solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/enums/HttpClientType.java
create mode 100644 solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/enums/StorageType.java
create mode 100644 solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/integration/WxChannelPluginImpl.java
create mode 100644 solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/properties/RedisProperties.java
create mode 100644 solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/properties/WxChannelProperties.java
create mode 100644 solon-plugins/wx-java-channel-solon-plugin/src/main/resources/META-INF/solon/wx-java-channel-solon-plugin.properties
create mode 100644 solon-plugins/wx-java-cp-multi-solon-plugin/README.md
create mode 100644 solon-plugins/wx-java-cp-multi-solon-plugin/pom.xml
create mode 100644 solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/configuration/services/AbstractWxCpConfiguration.java
create mode 100644 solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/configuration/services/WxCpInJedisConfiguration.java
create mode 100644 solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/configuration/services/WxCpInMemoryConfiguration.java
create mode 100644 solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/configuration/services/WxCpInRedissonConfiguration.java
create mode 100644 solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/integration/WxCpMultiPluginImpl.java
create mode 100644 solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/properties/WxCpMultiProperties.java
create mode 100644 solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/properties/WxCpMultiRedisProperties.java
create mode 100644 solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/properties/WxCpSingleProperties.java
create mode 100644 solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/service/WxCpMultiServices.java
create mode 100644 solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/service/WxCpMultiServicesImpl.java
create mode 100644 solon-plugins/wx-java-cp-multi-solon-plugin/src/main/resources/META-INF/solon/wx-java-cp-multi-solon-plugin.properties
create mode 100644 solon-plugins/wx-java-cp-solon-plugin/README.md
create mode 100644 solon-plugins/wx-java-cp-solon-plugin/pom.xml
create mode 100644 solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/config/WxCpServiceAutoConfiguration.java
create mode 100644 solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/integration/WxCpPluginImpl.java
create mode 100644 solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/properties/WxCpProperties.java
create mode 100644 solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/properties/WxCpRedisProperties.java
create mode 100644 solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/storage/AbstractWxCpConfigStorageConfiguration.java
create mode 100644 solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/storage/WxCpInJedisConfigStorageConfiguration.java
create mode 100644 solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/storage/WxCpInMemoryConfigStorageConfiguration.java
create mode 100644 solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/storage/WxCpInRedissonConfigStorageConfiguration.java
create mode 100644 solon-plugins/wx-java-cp-solon-plugin/src/main/resources/META-INF/solon/wx-java-cp-solon-plugin.properties
create mode 100644 solon-plugins/wx-java-miniapp-solon-plugin/README.md
create mode 100644 solon-plugins/wx-java-miniapp-solon-plugin/pom.xml
create mode 100644 solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/config/WxMaServiceAutoConfiguration.java
create mode 100644 solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/config/storage/AbstractWxMaConfigStorageConfiguration.java
create mode 100644 solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/config/storage/WxMaInJedisConfigStorageConfiguration.java
create mode 100644 solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/config/storage/WxMaInMemoryConfigStorageConfiguration.java
create mode 100644 solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/config/storage/WxMaInRedissonConfigStorageConfiguration.java
create mode 100644 solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/enums/HttpClientType.java
create mode 100644 solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/enums/StorageType.java
create mode 100644 solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/integration/WxMiniappPluginImpl.java
create mode 100644 solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/properties/RedisProperties.java
create mode 100644 solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/properties/WxMaProperties.java
create mode 100644 solon-plugins/wx-java-miniapp-solon-plugin/src/main/resources/META-INF/solon/wx-java-miniapp-solon-plugin.properties
create mode 100644 solon-plugins/wx-java-mp-multi-solon-plugin/README.md
create mode 100644 solon-plugins/wx-java-mp-multi-solon-plugin/pom.xml
create mode 100644 solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/configuration/services/AbstractWxMpConfiguration.java
create mode 100644 solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/configuration/services/WxMpInJedisConfiguration.java
create mode 100644 solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/configuration/services/WxMpInMemoryConfiguration.java
create mode 100644 solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/configuration/services/WxMpInRedissonConfiguration.java
create mode 100644 solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/integration/WxMpMultiPluginImpl.java
create mode 100644 solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/properties/WxMpMultiProperties.java
create mode 100644 solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/properties/WxMpMultiRedisProperties.java
create mode 100644 solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/properties/WxMpSingleProperties.java
create mode 100644 solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/service/WxMpMultiServices.java
create mode 100644 solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/service/WxMpMultiServicesImpl.java
create mode 100644 solon-plugins/wx-java-mp-multi-solon-plugin/src/main/resources/META-INF/solon/wx-java-mp-multi-solon-plugin.properties
create mode 100644 solon-plugins/wx-java-mp-solon-plugin/README.md
create mode 100644 solon-plugins/wx-java-mp-solon-plugin/pom.xml
create mode 100644 solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/config/WxMpServiceAutoConfiguration.java
create mode 100644 solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/config/WxMpStorageAutoConfiguration.java
create mode 100644 solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/enums/HttpClientType.java
create mode 100644 solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/enums/StorageType.java
create mode 100644 solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/integration/WxMpPluginImpl.java
create mode 100644 solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/properties/HostConfig.java
create mode 100644 solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/properties/RedisProperties.java
create mode 100644 solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/properties/WxMpProperties.java
create mode 100644 solon-plugins/wx-java-mp-solon-plugin/src/main/resources/META-INF/solon/wx-java-mp-solon-plugin.properties
create mode 100644 solon-plugins/wx-java-open-solon-plugin/README.md
create mode 100644 solon-plugins/wx-java-open-solon-plugin/pom.xml
create mode 100644 solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/config/WxOpenServiceAutoConfiguration.java
create mode 100644 solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/config/storage/AbstractWxOpenConfigStorageConfiguration.java
create mode 100644 solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/config/storage/WxOpenInJedisConfigStorageConfiguration.java
create mode 100644 solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/config/storage/WxOpenInMemoryConfigStorageConfiguration.java
create mode 100644 solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/config/storage/WxOpenInRedissonConfigStorageConfiguration.java
create mode 100644 solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/integration/WxOpenPluginImpl.java
create mode 100644 solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/properties/WxOpenProperties.java
create mode 100644 solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/properties/WxOpenRedisProperties.java
create mode 100644 solon-plugins/wx-java-open-solon-plugin/src/main/resources/META-INF/solon/wx-java-open-solon-plugin.properties
create mode 100644 solon-plugins/wx-java-pay-solon-plugin/README.md
create mode 100644 solon-plugins/wx-java-pay-solon-plugin/pom.xml
create mode 100644 solon-plugins/wx-java-pay-solon-plugin/src/main/java/com/binarywang/solon/wxjava/pay/config/WxPayAutoConfiguration.java
create mode 100644 solon-plugins/wx-java-pay-solon-plugin/src/main/java/com/binarywang/solon/wxjava/pay/integration/WxPayPluginImpl.java
create mode 100644 solon-plugins/wx-java-pay-solon-plugin/src/main/java/com/binarywang/solon/wxjava/pay/properties/WxPayProperties.java
create mode 100644 solon-plugins/wx-java-pay-solon-plugin/src/main/resources/META-INF/solon/wx-java-pay-solon-plugin.properties
create mode 100644 solon-plugins/wx-java-qidian-solon-plugin/README.md
create mode 100644 solon-plugins/wx-java-qidian-solon-plugin/pom.xml
create mode 100644 solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/config/WxQidianServiceAutoConfiguration.java
create mode 100644 solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/config/WxQidianStorageAutoConfiguration.java
create mode 100644 solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/enums/HttpClientType.java
create mode 100644 solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/enums/StorageType.java
create mode 100644 solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/integration/WxQidianPluginImpl.java
create mode 100644 solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/properties/HostConfig.java
create mode 100644 solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/properties/RedisProperties.java
create mode 100644 solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/properties/WxQidianProperties.java
create mode 100644 solon-plugins/wx-java-qidian-solon-plugin/src/main/resources/META-INF/solon/wx-java-qidian-solon-plugin.properties
diff --git a/pom.xml b/pom.xml
index 9e91f60778..13cabe0e47 100644
--- a/pom.xml
+++ b/pom.xml
@@ -126,6 +126,7 @@
weixin-java-qidian
weixin-java-channel
spring-boot-starters
+ solon-plugins
diff --git a/solon-plugins/pom.xml b/solon-plugins/pom.xml
new file mode 100644
index 0000000000..278e26dc9f
--- /dev/null
+++ b/solon-plugins/pom.xml
@@ -0,0 +1,44 @@
+
+
+ 4.0.0
+
+ com.github.binarywang
+ wx-java
+ 4.6.4.B
+
+ pom
+ wx-java-solon-plugins
+ WxJava - Solon Plugins
+ WxJava 各个模块的 Solon Plugin
+
+
+ 2.9.2
+
+
+
+ wx-java-miniapp-solon-plugin
+ wx-java-mp-multi-solon-plugin
+ wx-java-mp-solon-plugin
+ wx-java-pay-solon-plugin
+ wx-java-open-solon-plugin
+ wx-java-qidian-solon-plugin
+ wx-java-cp-multi-solon-plugin
+ wx-java-cp-solon-plugin
+ wx-java-channel-solon-plugin
+
+
+
+
+ org.noear
+ solon
+ ${solon.version}
+
+
+ org.projectlombok
+ lombok
+ provided
+
+
+
diff --git a/solon-plugins/wx-java-channel-solon-plugin/pom.xml b/solon-plugins/wx-java-channel-solon-plugin/pom.xml
new file mode 100644
index 0000000000..fbd17094ae
--- /dev/null
+++ b/solon-plugins/wx-java-channel-solon-plugin/pom.xml
@@ -0,0 +1,31 @@
+
+
+ wx-java-solon-plugins
+ com.github.binarywang
+ 4.6.4.B
+
+ 4.0.0
+
+ wx-java-channel-solon-plugin
+ WxJava - Solon Plugin for Channel
+ 微信视频号开发的 Solon Plugin
+
+
+
+ com.github.binarywang
+ weixin-java-channel
+ ${project.version}
+
+
+ redis.clients
+ jedis
+ provided
+
+
+ org.redisson
+ redisson
+ provided
+
+
+
diff --git a/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/config/WxChannelServiceAutoConfiguration.java b/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/config/WxChannelServiceAutoConfiguration.java
new file mode 100644
index 0000000000..9ffccc64bf
--- /dev/null
+++ b/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/config/WxChannelServiceAutoConfiguration.java
@@ -0,0 +1,35 @@
+package com.binarywang.solon.wxjava.channel.config;
+
+
+import com.binarywang.solon.wxjava.channel.properties.WxChannelProperties;
+import lombok.AllArgsConstructor;
+import me.chanjar.weixin.channel.api.WxChannelService;
+import me.chanjar.weixin.channel.api.impl.WxChannelServiceImpl;
+import me.chanjar.weixin.channel.config.WxChannelConfig;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+
+/**
+ * 微信小程序平台相关服务自动注册
+ *
+ * @author Zeyes
+ */
+@Configuration
+@AllArgsConstructor
+public class WxChannelServiceAutoConfiguration {
+ private final WxChannelProperties properties;
+
+ /**
+ * Channel Service
+ *
+ * @return Channel Service
+ */
+ @Bean
+ @Condition(onMissingBean=WxChannelService.class, onBean = WxChannelConfig.class)
+ public WxChannelService wxChannelService(WxChannelConfig wxChannelConfig) {
+ WxChannelService wxChannelService = new WxChannelServiceImpl();
+ wxChannelService.setConfig(wxChannelConfig);
+ return wxChannelService;
+ }
+}
diff --git a/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/config/storage/AbstractWxChannelConfigStorageConfiguration.java b/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/config/storage/AbstractWxChannelConfigStorageConfiguration.java
new file mode 100644
index 0000000000..41d002b419
--- /dev/null
+++ b/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/config/storage/AbstractWxChannelConfigStorageConfiguration.java
@@ -0,0 +1,39 @@
+package com.binarywang.solon.wxjava.channel.config.storage;
+
+import com.binarywang.solon.wxjava.channel.properties.WxChannelProperties;
+import me.chanjar.weixin.channel.config.impl.WxChannelDefaultConfigImpl;
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * @author Zeyes
+ */
+public abstract class AbstractWxChannelConfigStorageConfiguration {
+
+ protected WxChannelDefaultConfigImpl config(WxChannelDefaultConfigImpl config, WxChannelProperties properties) {
+ config.setAppid(StringUtils.trimToNull(properties.getAppid()));
+ config.setSecret(StringUtils.trimToNull(properties.getSecret()));
+ config.setToken(StringUtils.trimToNull(properties.getToken()));
+ config.setAesKey(StringUtils.trimToNull(properties.getAesKey()));
+ config.setMsgDataFormat(StringUtils.trimToNull(properties.getMsgDataFormat()));
+
+ WxChannelProperties.ConfigStorage configStorageProperties = properties.getConfigStorage();
+ config.setHttpProxyHost(configStorageProperties.getHttpProxyHost());
+ config.setHttpProxyUsername(configStorageProperties.getHttpProxyUsername());
+ config.setHttpProxyPassword(configStorageProperties.getHttpProxyPassword());
+ if (configStorageProperties.getHttpProxyPort() != null) {
+ config.setHttpProxyPort(configStorageProperties.getHttpProxyPort());
+ }
+
+ int maxRetryTimes = configStorageProperties.getMaxRetryTimes();
+ if (configStorageProperties.getMaxRetryTimes() < 0) {
+ maxRetryTimes = 0;
+ }
+ int retrySleepMillis = configStorageProperties.getRetrySleepMillis();
+ if (retrySleepMillis < 0) {
+ retrySleepMillis = 1000;
+ }
+ config.setRetrySleepMillis(retrySleepMillis);
+ config.setMaxRetryTimes(maxRetryTimes);
+ return config;
+ }
+}
diff --git a/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/config/storage/WxChannelInJedisConfigStorageConfiguration.java b/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/config/storage/WxChannelInJedisConfigStorageConfiguration.java
new file mode 100644
index 0000000000..f074241914
--- /dev/null
+++ b/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/config/storage/WxChannelInJedisConfigStorageConfiguration.java
@@ -0,0 +1,74 @@
+package com.binarywang.solon.wxjava.channel.config.storage;
+
+
+import com.binarywang.solon.wxjava.channel.properties.RedisProperties;
+import com.binarywang.solon.wxjava.channel.properties.WxChannelProperties;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.channel.config.WxChannelConfig;
+import me.chanjar.weixin.channel.config.impl.WxChannelRedisConfigImpl;
+import me.chanjar.weixin.common.redis.JedisWxRedisOps;
+import me.chanjar.weixin.common.redis.WxRedisOps;
+import org.apache.commons.lang3.StringUtils;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+import org.noear.solon.core.AppContext;
+import redis.clients.jedis.JedisPool;
+import redis.clients.jedis.JedisPoolConfig;
+
+/**
+ * @author Zeyes
+ * @author noear
+ */
+@Configuration
+@Condition(
+ onProperty = "${"+WxChannelProperties.PREFIX + ".configStorage.type} = jedis",
+ onClass = JedisPool.class
+)
+@RequiredArgsConstructor
+public class WxChannelInJedisConfigStorageConfiguration extends AbstractWxChannelConfigStorageConfiguration {
+ private final WxChannelProperties properties;
+ private final AppContext applicationContext;
+
+ @Bean
+ @Condition(onMissingBean=WxChannelConfig.class)
+ public WxChannelConfig wxChannelConfig() {
+ WxChannelRedisConfigImpl config = getWxChannelRedisConfig();
+ return this.config(config, properties);
+ }
+
+ private WxChannelRedisConfigImpl getWxChannelRedisConfig() {
+ RedisProperties redisProperties = properties.getConfigStorage().getRedis();
+ JedisPool jedisPool;
+ if (redisProperties != null && StringUtils.isNotEmpty(redisProperties.getHost())) {
+ jedisPool = getJedisPool();
+ } else {
+ jedisPool = applicationContext.getBean(JedisPool.class);
+ }
+ WxRedisOps redisOps = new JedisWxRedisOps(jedisPool);
+ return new WxChannelRedisConfigImpl(redisOps, properties.getConfigStorage().getKeyPrefix());
+ }
+
+ private JedisPool getJedisPool() {
+ WxChannelProperties.ConfigStorage storage = properties.getConfigStorage();
+ RedisProperties redis = storage.getRedis();
+
+ JedisPoolConfig config = new JedisPoolConfig();
+ if (redis.getMaxActive() != null) {
+ config.setMaxTotal(redis.getMaxActive());
+ }
+ if (redis.getMaxIdle() != null) {
+ config.setMaxIdle(redis.getMaxIdle());
+ }
+ if (redis.getMaxWaitMillis() != null) {
+ config.setMaxWaitMillis(redis.getMaxWaitMillis());
+ }
+ if (redis.getMinIdle() != null) {
+ config.setMinIdle(redis.getMinIdle());
+ }
+ config.setTestOnBorrow(true);
+ config.setTestWhileIdle(true);
+
+ return new JedisPool(config, redis.getHost(), redis.getPort(), redis.getTimeout(), redis.getPassword(), redis.getDatabase());
+ }
+}
diff --git a/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/config/storage/WxChannelInMemoryConfigStorageConfiguration.java b/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/config/storage/WxChannelInMemoryConfigStorageConfiguration.java
new file mode 100644
index 0000000000..a560db29ac
--- /dev/null
+++ b/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/config/storage/WxChannelInMemoryConfigStorageConfiguration.java
@@ -0,0 +1,29 @@
+package com.binarywang.solon.wxjava.channel.config.storage;
+
+
+import com.binarywang.solon.wxjava.channel.properties.WxChannelProperties;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.channel.config.WxChannelConfig;
+import me.chanjar.weixin.channel.config.impl.WxChannelDefaultConfigImpl;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+
+/**
+ * @author Zeyes
+ */
+@Configuration
+@Condition(
+ onProperty = "${"+WxChannelProperties.PREFIX + ".configStorage.type:memory} = memory"
+)
+@RequiredArgsConstructor
+public class WxChannelInMemoryConfigStorageConfiguration extends AbstractWxChannelConfigStorageConfiguration {
+ private final WxChannelProperties properties;
+
+ @Bean
+ @Condition(onMissingBean = WxChannelProperties.class)
+ public WxChannelConfig wxChannelConfig() {
+ WxChannelDefaultConfigImpl config = new WxChannelDefaultConfigImpl();
+ return this.config(config, properties);
+ }
+}
diff --git a/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/config/storage/WxChannelInRedissonConfigStorageConfiguration.java b/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/config/storage/WxChannelInRedissonConfigStorageConfiguration.java
new file mode 100644
index 0000000000..cd4de68f21
--- /dev/null
+++ b/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/config/storage/WxChannelInRedissonConfigStorageConfiguration.java
@@ -0,0 +1,62 @@
+package com.binarywang.solon.wxjava.channel.config.storage;
+
+
+import com.binarywang.solon.wxjava.channel.properties.RedisProperties;
+import com.binarywang.solon.wxjava.channel.properties.WxChannelProperties;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.channel.config.WxChannelConfig;
+import me.chanjar.weixin.channel.config.impl.WxChannelRedissonConfigImpl;
+import org.apache.commons.lang3.StringUtils;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+import org.noear.solon.core.AppContext;
+import org.redisson.Redisson;
+import org.redisson.api.RedissonClient;
+import org.redisson.config.Config;
+import org.redisson.config.TransportMode;
+
+/**
+ * @author Zeyes
+ */
+@Configuration
+@Condition(
+ onProperty = "${"+WxChannelProperties.PREFIX + ".configStorage.type} = redisson",
+ onClass = Redisson.class
+)
+@RequiredArgsConstructor
+public class WxChannelInRedissonConfigStorageConfiguration extends AbstractWxChannelConfigStorageConfiguration {
+ private final WxChannelProperties properties;
+ private final AppContext applicationContext;
+
+ @Bean
+ @Condition(onMissingBean=WxChannelConfig.class)
+ public WxChannelConfig wxChannelConfig() {
+ WxChannelRedissonConfigImpl config = getWxChannelRedissonConfig();
+ return this.config(config, properties);
+ }
+
+ private WxChannelRedissonConfigImpl getWxChannelRedissonConfig() {
+ RedisProperties redisProperties = properties.getConfigStorage().getRedis();
+ RedissonClient redissonClient;
+ if (redisProperties != null && StringUtils.isNotEmpty(redisProperties.getHost())) {
+ redissonClient = getRedissonClient();
+ } else {
+ redissonClient = applicationContext.getBean(RedissonClient.class);
+ }
+ return new WxChannelRedissonConfigImpl(redissonClient, properties.getConfigStorage().getKeyPrefix());
+ }
+
+ private RedissonClient getRedissonClient() {
+ WxChannelProperties.ConfigStorage storage = properties.getConfigStorage();
+ RedisProperties redis = storage.getRedis();
+
+ Config config = new Config();
+ config.useSingleServer()
+ .setAddress("redis://" + redis.getHost() + ":" + redis.getPort())
+ .setDatabase(redis.getDatabase())
+ .setPassword(redis.getPassword());
+ config.setTransportMode(TransportMode.NIO);
+ return Redisson.create(config);
+ }
+}
diff --git a/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/enums/HttpClientType.java b/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/enums/HttpClientType.java
new file mode 100644
index 0000000000..0c00dbcaa7
--- /dev/null
+++ b/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/enums/HttpClientType.java
@@ -0,0 +1,13 @@
+package com.binarywang.solon.wxjava.channel.enums;
+
+/**
+ * httpclient类型
+ *
+ * @author Zeyes
+ */
+public enum HttpClientType {
+ /**
+ * HttpClient
+ */
+ HttpClient
+}
diff --git a/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/enums/StorageType.java b/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/enums/StorageType.java
new file mode 100644
index 0000000000..976f869438
--- /dev/null
+++ b/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/enums/StorageType.java
@@ -0,0 +1,25 @@
+package com.binarywang.solon.wxjava.channel.enums;
+
+/**
+ * storage类型
+ *
+ * @author Zeyes
+ */
+public enum StorageType {
+ /**
+ * 内存
+ */
+ Memory,
+ /**
+ * redis(JedisClient)
+ */
+ Jedis,
+ /**
+ * redis(Redisson)
+ */
+ Redisson,
+ /**
+ * redis(RedisTemplate)
+ */
+ RedisTemplate
+}
diff --git a/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/integration/WxChannelPluginImpl.java b/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/integration/WxChannelPluginImpl.java
new file mode 100644
index 0000000000..0377bc6f41
--- /dev/null
+++ b/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/integration/WxChannelPluginImpl.java
@@ -0,0 +1,25 @@
+package com.binarywang.solon.wxjava.channel.integration;
+
+
+import com.binarywang.solon.wxjava.channel.config.WxChannelServiceAutoConfiguration;
+import com.binarywang.solon.wxjava.channel.config.storage.WxChannelInJedisConfigStorageConfiguration;
+import com.binarywang.solon.wxjava.channel.config.storage.WxChannelInMemoryConfigStorageConfiguration;
+import com.binarywang.solon.wxjava.channel.config.storage.WxChannelInRedissonConfigStorageConfiguration;
+import com.binarywang.solon.wxjava.channel.properties.WxChannelProperties;
+import org.noear.solon.core.AppContext;
+import org.noear.solon.core.Plugin;
+
+/**
+ * @author noear 2024/9/2 created
+ */
+public class WxChannelPluginImpl implements Plugin {
+ @Override
+ public void start(AppContext context) throws Throwable {
+ context.beanMake(WxChannelProperties.class);
+ context.beanMake(WxChannelServiceAutoConfiguration.class);
+
+ context.beanMake(WxChannelInMemoryConfigStorageConfiguration.class);
+ context.beanMake(WxChannelInJedisConfigStorageConfiguration.class);
+ context.beanMake(WxChannelInRedissonConfigStorageConfiguration.class);
+ }
+}
diff --git a/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/properties/RedisProperties.java b/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/properties/RedisProperties.java
new file mode 100644
index 0000000000..b74ad89f4e
--- /dev/null
+++ b/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/properties/RedisProperties.java
@@ -0,0 +1,42 @@
+package com.binarywang.solon.wxjava.channel.properties;
+
+import lombok.Data;
+
+/**
+ * redis 配置
+ *
+ * @author Zeyes
+ */
+@Data
+public class RedisProperties {
+
+ /**
+ * 主机地址,不填则从solon容器内获取JedisPool
+ */
+ private String host;
+
+ /**
+ * 端口号
+ */
+ private int port = 6379;
+
+ /**
+ * 密码
+ */
+ private String password;
+
+ /**
+ * 超时
+ */
+ private int timeout = 2000;
+
+ /**
+ * 数据库
+ */
+ private int database = 0;
+
+ private Integer maxActive;
+ private Integer maxIdle;
+ private Integer maxWaitMillis;
+ private Integer minIdle;
+}
diff --git a/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/properties/WxChannelProperties.java b/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/properties/WxChannelProperties.java
new file mode 100644
index 0000000000..f40aa82115
--- /dev/null
+++ b/solon-plugins/wx-java-channel-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/properties/WxChannelProperties.java
@@ -0,0 +1,109 @@
+package com.binarywang.solon.wxjava.channel.properties;
+
+import com.binarywang.solon.wxjava.channel.enums.HttpClientType;
+import com.binarywang.solon.wxjava.channel.enums.StorageType;
+import lombok.Data;
+import org.noear.solon.annotation.Configuration;
+import org.noear.solon.annotation.Inject;
+
+/**
+ * 属性配置类
+ *
+ * @author Zeyes
+ */
+@Data
+@Configuration
+@Inject("${" + WxChannelProperties.PREFIX +"}")
+public class WxChannelProperties {
+ public static final String PREFIX = "wx.channel";
+
+ /**
+ * 设置视频号小店的appid
+ */
+ private String appid;
+
+ /**
+ * 设置视频号小店的Secret
+ */
+ private String secret;
+
+ /**
+ * 设置视频号小店消息服务器配置的token.
+ */
+ private String token;
+
+ /**
+ * 设置视频号小店消息服务器配置的EncodingAESKey
+ */
+ private String aesKey;
+
+ /**
+ * 消息格式,XML或者JSON
+ */
+ private String msgDataFormat = "JSON";
+
+ /**
+ * 存储策略
+ */
+ private final ConfigStorage configStorage = new ConfigStorage();
+
+ @Data
+ public static class ConfigStorage {
+
+ /**
+ * 存储类型
+ */
+ private StorageType type = StorageType.Memory;
+
+ /**
+ * 指定key前缀
+ */
+ private String keyPrefix = "wh";
+
+ /**
+ * redis连接配置
+ */
+ private final RedisProperties redis = new RedisProperties();
+
+ /**
+ * http客户端类型
+ */
+ private HttpClientType httpClientType = HttpClientType.HttpClient;
+
+ /**
+ * http代理主机
+ */
+ private String httpProxyHost;
+
+ /**
+ * http代理端口
+ */
+ private Integer httpProxyPort;
+
+ /**
+ * http代理用户名
+ */
+ private String httpProxyUsername;
+
+ /**
+ * http代理密码
+ */
+ private String httpProxyPassword;
+
+ /**
+ * http 请求重试间隔
+ *
+ * {@link me.chanjar.weixin.channel.api.BaseWxChannelService#setRetrySleepMillis(int)}
+ *
+ */
+ private int retrySleepMillis = 1000;
+ /**
+ * http 请求最大重试次数
+ *
+ * {@link me.chanjar.weixin.channel.api.BaseWxChannelService#setMaxRetryTimes(int)}
+ *
+ */
+ private int maxRetryTimes = 5;
+ }
+
+}
diff --git a/solon-plugins/wx-java-channel-solon-plugin/src/main/resources/META-INF/solon/wx-java-channel-solon-plugin.properties b/solon-plugins/wx-java-channel-solon-plugin/src/main/resources/META-INF/solon/wx-java-channel-solon-plugin.properties
new file mode 100644
index 0000000000..d8ec8f5112
--- /dev/null
+++ b/solon-plugins/wx-java-channel-solon-plugin/src/main/resources/META-INF/solon/wx-java-channel-solon-plugin.properties
@@ -0,0 +1,2 @@
+solon.plugin=com.binarywang.solon.wxjava.channel.integration.WxChannelPluginImpl
+solon.plugin.priority=10
diff --git a/solon-plugins/wx-java-cp-multi-solon-plugin/README.md b/solon-plugins/wx-java-cp-multi-solon-plugin/README.md
new file mode 100644
index 0000000000..c6acb0889b
--- /dev/null
+++ b/solon-plugins/wx-java-cp-multi-solon-plugin/README.md
@@ -0,0 +1,95 @@
+# wx-java-cp-multi-solon-plugin
+
+企业微信多账号配置
+
+- 实现多 WxCpService 初始化。
+- 未实现 WxCpTpService 初始化,需要的小伙伴可以参考多 WxCpService 配置的实现。
+- 未实现 WxCpCgService 初始化,需要的小伙伴可以参考多 WxCpService 配置的实现。
+
+## 快速开始
+
+1. 引入依赖
+ ```xml
+
+ com.github.binarywang
+ wx-java-cp-multi-solon-plugin
+ ${version}
+
+ ```
+2. 添加配置(app.properties)
+ ```properties
+ # 应用 1 配置
+ wx.cp.corps.tenantId1.corp-id = @corp-id
+ wx.cp.corps.tenantId1.corp-secret = @corp-secret
+ ## 选填
+ wx.cp.corps.tenantId1.agent-id = @agent-id
+ wx.cp.corps.tenantId1.token = @token
+ wx.cp.corps.tenantId1.aes-key = @aes-key
+ wx.cp.corps.tenantId1.msg-audit-priKey = @msg-audit-priKey
+ wx.cp.corps.tenantId1.msg-audit-lib-path = @msg-audit-lib-path
+
+ # 应用 2 配置
+ wx.cp.corps.tenantId2.corp-id = @corp-id
+ wx.cp.corps.tenantId2.corp-secret = @corp-secret
+ ## 选填
+ wx.cp.corps.tenantId2.agent-id = @agent-id
+ wx.cp.corps.tenantId2.token = @token
+ wx.cp.corps.tenantId2.aes-key = @aes-key
+ wx.cp.corps.tenantId2.msg-audit-priKey = @msg-audit-priKey
+ wx.cp.corps.tenantId2.msg-audit-lib-path = @msg-audit-lib-path
+
+ # 公共配置
+ ## ConfigStorage 配置(选填)
+ wx.cp.config-storage.type=memory # 配置类型: memory(默认), jedis, redisson, redistemplate
+ ## http 客户端配置(选填)
+ ## # http客户端类型: http_client(默认), ok_http, jodd_http
+ wx.cp.config-storage.http-client-type=http_client
+ wx.cp.config-storage.http-proxy-host=
+ wx.cp.config-storage.http-proxy-port=
+ wx.cp.config-storage.http-proxy-username=
+ wx.cp.config-storage.http-proxy-password=
+ ## 最大重试次数,默认:5 次,如果小于 0,则为 0
+ wx.cp.config-storage.max-retry-times=5
+ ## 重试时间间隔步进,默认:1000 毫秒,如果小于 0,则为 1000
+ wx.cp.config-storage.retry-sleep-millis=1000
+ ```
+3. 支持自动注入的类型: `WxCpMultiServices`
+
+4. 使用样例
+
+```java
+import com.binarywang.solon.wxjava.cp_multi.service.WxCpMultiServices;
+import me.chanjar.weixin.cp.api.WxCpService;
+import me.chanjar.weixin.cp.api.WxCpUserService;
+
+@Component
+public class DemoService {
+ @Inject
+ private WxCpMultiServices wxCpMultiServices;
+
+ public void test() {
+ // 应用 1 的 WxCpService
+ WxCpService wxCpService1 = wxCpMultiServices.getWxCpService("tenantId1");
+ WxCpUserService userService1 = wxCpService1.getUserService();
+ userService1.getUserId("xxx");
+ // todo ...
+
+ // 应用 2 的 WxCpService
+ WxCpService wxCpService2 = wxCpMultiServices.getWxCpService("tenantId2");
+ WxCpUserService userService2 = wxCpService2.getUserService();
+ userService2.getUserId("xxx");
+ // todo ...
+
+ // 应用 3 的 WxCpService
+ WxCpService wxCpService3 = wxCpMultiServices.getWxCpService("tenantId3");
+ // 判断是否为空
+ if (wxCpService3 == null) {
+ // todo wxCpService3 为空,请先配置 tenantId3 企业微信应用参数
+ return;
+ }
+ WxCpUserService userService3 = wxCpService3.getUserService();
+ userService3.getUserId("xxx");
+ // todo ...
+ }
+}
+```
diff --git a/solon-plugins/wx-java-cp-multi-solon-plugin/pom.xml b/solon-plugins/wx-java-cp-multi-solon-plugin/pom.xml
new file mode 100644
index 0000000000..edca6bda61
--- /dev/null
+++ b/solon-plugins/wx-java-cp-multi-solon-plugin/pom.xml
@@ -0,0 +1,32 @@
+
+
+
+ wx-java-solon-plugins
+ com.github.binarywang
+ 4.6.4.B
+
+ 4.0.0
+
+ wx-java-cp-multi-solon-plugin
+ WxJava - Solon Plugin for WxCp::支持多账号配置
+ 微信企业号开发的 Solon Plugin::支持多账号配置
+
+
+
+ com.github.binarywang
+ weixin-java-cp
+ ${project.version}
+
+
+ redis.clients
+ jedis
+ provided
+
+
+ org.redisson
+ redisson
+ provided
+
+
+
diff --git a/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/configuration/services/AbstractWxCpConfiguration.java b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/configuration/services/AbstractWxCpConfiguration.java
new file mode 100644
index 0000000000..8710bba3ca
--- /dev/null
+++ b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/configuration/services/AbstractWxCpConfiguration.java
@@ -0,0 +1,162 @@
+package com.binarywang.solon.wxjava.cp_multi.configuration.services;
+
+import com.binarywang.solon.wxjava.cp_multi.properties.WxCpMultiProperties;
+import com.binarywang.solon.wxjava.cp_multi.properties.WxCpSingleProperties;
+import com.binarywang.solon.wxjava.cp_multi.service.WxCpMultiServices;
+import com.binarywang.solon.wxjava.cp_multi.service.WxCpMultiServicesImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.cp.api.WxCpService;
+import me.chanjar.weixin.cp.api.impl.WxCpServiceApacheHttpClientImpl;
+import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl;
+import me.chanjar.weixin.cp.api.impl.WxCpServiceJoddHttpImpl;
+import me.chanjar.weixin.cp.api.impl.WxCpServiceOkHttpImpl;
+import me.chanjar.weixin.cp.config.WxCpConfigStorage;
+import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * WxCpConfigStorage 抽象配置类
+ *
+ * @author yl
+ * created on 2023/10/16
+ */
+@RequiredArgsConstructor
+@Slf4j
+public abstract class AbstractWxCpConfiguration {
+
+ protected WxCpMultiServices wxCpMultiServices(WxCpMultiProperties wxCpMultiProperties) {
+ Map corps = wxCpMultiProperties.getCorps();
+ if (corps == null || corps.isEmpty()) {
+ log.warn("企业微信应用参数未配置,通过 WxCpMultiServices#getWxCpService(\"tenantId\")获取实例将返回空");
+ return new WxCpMultiServicesImpl();
+ }
+ /**
+ * 校验同一个企业下,agentId 是否唯一,避免使用 redis 缓存 token、ticket 时错乱。
+ *
+ * 查看 {@link me.chanjar.weixin.cp.config.impl.AbstractWxCpInRedisConfigImpl#setAgentId(Integer)}
+ */
+ Collection corpList = corps.values();
+ if (corpList.size() > 1) {
+ // 先按 corpId 分组统计
+ Map> corpsMap = corpList.stream()
+ .collect(Collectors.groupingBy(WxCpSingleProperties::getCorpId));
+ Set>> entries = corpsMap.entrySet();
+ for (Map.Entry> entry : entries) {
+ String corpId = entry.getKey();
+ // 校验每个企业下,agentId 是否唯一
+ boolean multi = entry.getValue().stream()
+ // 通讯录没有 agentId,如果不判断是否为空,这里会报 NPE 异常
+ .collect(Collectors.groupingBy(c -> c.getAgentId() == null ? 0 : c.getAgentId(), Collectors.counting()))
+ .entrySet().stream().anyMatch(e -> e.getValue() > 1);
+ if (multi) {
+ throw new RuntimeException("请确保企业微信配置唯一性[" + corpId + "]");
+ }
+ }
+ }
+ WxCpMultiServicesImpl services = new WxCpMultiServicesImpl();
+
+ Set> entries = corps.entrySet();
+ for (Map.Entry entry : entries) {
+ String tenantId = entry.getKey();
+ WxCpSingleProperties wxCpSingleProperties = entry.getValue();
+ WxCpDefaultConfigImpl storage = this.wxCpConfigStorage(wxCpMultiProperties);
+ this.configCorp(storage, wxCpSingleProperties);
+ this.configHttp(storage, wxCpMultiProperties.getConfigStorage());
+ WxCpService wxCpService = this.wxCpService(storage, wxCpMultiProperties.getConfigStorage());
+ services.addWxCpService(tenantId, wxCpService);
+ }
+ return services;
+ }
+
+ /**
+ * 配置 WxCpDefaultConfigImpl
+ *
+ * @param wxCpMultiProperties 参数
+ * @return WxCpDefaultConfigImpl
+ */
+ protected abstract WxCpDefaultConfigImpl wxCpConfigStorage(WxCpMultiProperties wxCpMultiProperties);
+
+ private WxCpService wxCpService(WxCpConfigStorage wxCpConfigStorage, WxCpMultiProperties.ConfigStorage storage) {
+ WxCpMultiProperties.HttpClientType httpClientType = storage.getHttpClientType();
+ WxCpService wxCpService;
+ switch (httpClientType) {
+ case OK_HTTP:
+ wxCpService = new WxCpServiceOkHttpImpl();
+ break;
+ case JODD_HTTP:
+ wxCpService = new WxCpServiceJoddHttpImpl();
+ break;
+ case HTTP_CLIENT:
+ wxCpService = new WxCpServiceApacheHttpClientImpl();
+ break;
+ default:
+ wxCpService = new WxCpServiceImpl();
+ break;
+ }
+ wxCpService.setWxCpConfigStorage(wxCpConfigStorage);
+ int maxRetryTimes = storage.getMaxRetryTimes();
+ if (maxRetryTimes < 0) {
+ maxRetryTimes = 0;
+ }
+ int retrySleepMillis = storage.getRetrySleepMillis();
+ if (retrySleepMillis < 0) {
+ retrySleepMillis = 1000;
+ }
+ wxCpService.setRetrySleepMillis(retrySleepMillis);
+ wxCpService.setMaxRetryTimes(maxRetryTimes);
+ return wxCpService;
+ }
+
+ private void configCorp(WxCpDefaultConfigImpl config, WxCpSingleProperties wxCpSingleProperties) {
+ String corpId = wxCpSingleProperties.getCorpId();
+ String corpSecret = wxCpSingleProperties.getCorpSecret();
+ Integer agentId = wxCpSingleProperties.getAgentId();
+ String token = wxCpSingleProperties.getToken();
+ String aesKey = wxCpSingleProperties.getAesKey();
+ // 企业微信,私钥,会话存档路径
+ String msgAuditPriKey = wxCpSingleProperties.getMsgAuditPriKey();
+ String msgAuditLibPath = wxCpSingleProperties.getMsgAuditLibPath();
+
+ config.setCorpId(corpId);
+ config.setCorpSecret(corpSecret);
+ config.setAgentId(agentId);
+ if (StringUtils.isNotBlank(token)) {
+ config.setToken(token);
+ }
+ if (StringUtils.isNotBlank(aesKey)) {
+ config.setAesKey(aesKey);
+ }
+ if (StringUtils.isNotBlank(msgAuditPriKey)) {
+ config.setMsgAuditPriKey(msgAuditPriKey);
+ }
+ if (StringUtils.isNotBlank(msgAuditLibPath)) {
+ config.setMsgAuditLibPath(msgAuditLibPath);
+ }
+ }
+
+ private void configHttp(WxCpDefaultConfigImpl config, WxCpMultiProperties.ConfigStorage storage) {
+ String httpProxyHost = storage.getHttpProxyHost();
+ Integer httpProxyPort = storage.getHttpProxyPort();
+ String httpProxyUsername = storage.getHttpProxyUsername();
+ String httpProxyPassword = storage.getHttpProxyPassword();
+ if (StringUtils.isNotBlank(httpProxyHost)) {
+ config.setHttpProxyHost(httpProxyHost);
+ if (httpProxyPort != null) {
+ config.setHttpProxyPort(httpProxyPort);
+ }
+ if (StringUtils.isNotBlank(httpProxyUsername)) {
+ config.setHttpProxyUsername(httpProxyUsername);
+ }
+ if (StringUtils.isNotBlank(httpProxyPassword)) {
+ config.setHttpProxyPassword(httpProxyPassword);
+ }
+ }
+ }
+}
diff --git a/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/configuration/services/WxCpInJedisConfiguration.java b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/configuration/services/WxCpInJedisConfiguration.java
new file mode 100644
index 0000000000..71f5fd6725
--- /dev/null
+++ b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/configuration/services/WxCpInJedisConfiguration.java
@@ -0,0 +1,77 @@
+package com.binarywang.solon.wxjava.cp_multi.configuration.services;
+
+import com.binarywang.solon.wxjava.cp_multi.properties.WxCpMultiProperties;
+import com.binarywang.solon.wxjava.cp_multi.properties.WxCpMultiRedisProperties;
+import com.binarywang.solon.wxjava.cp_multi.service.WxCpMultiServices;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl;
+import me.chanjar.weixin.cp.config.impl.WxCpJedisConfigImpl;
+import org.apache.commons.lang3.StringUtils;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+import org.noear.solon.core.AppContext;
+import redis.clients.jedis.JedisPool;
+import redis.clients.jedis.JedisPoolConfig;
+
+/**
+ * 自动装配基于 jedis 策略配置
+ *
+ * @author yl
+ * created on 2023/10/16
+ */
+@Configuration
+@Condition(
+ onProperty = "${"+WxCpMultiProperties.PREFIX + ".configStorage.type} = jedis",
+ onClass = JedisPool.class
+)
+@RequiredArgsConstructor
+public class WxCpInJedisConfiguration extends AbstractWxCpConfiguration {
+ private final WxCpMultiProperties wxCpMultiProperties;
+ private final AppContext applicationContext;
+
+ @Bean
+ public WxCpMultiServices wxCpMultiServices() {
+ return this.wxCpMultiServices(wxCpMultiProperties);
+ }
+
+ @Override
+ protected WxCpDefaultConfigImpl wxCpConfigStorage(WxCpMultiProperties wxCpMultiProperties) {
+ return this.configRedis(wxCpMultiProperties);
+ }
+
+ private WxCpDefaultConfigImpl configRedis(WxCpMultiProperties wxCpMultiProperties) {
+ WxCpMultiRedisProperties wxCpMultiRedisProperties = wxCpMultiProperties.getConfigStorage().getRedis();
+ JedisPool jedisPool;
+ if (wxCpMultiRedisProperties != null && StringUtils.isNotEmpty(wxCpMultiRedisProperties.getHost())) {
+ jedisPool = getJedisPool(wxCpMultiProperties);
+ } else {
+ jedisPool = applicationContext.getBean(JedisPool.class);
+ }
+ return new WxCpJedisConfigImpl(jedisPool, wxCpMultiProperties.getConfigStorage().getKeyPrefix());
+ }
+
+ private JedisPool getJedisPool(WxCpMultiProperties wxCpMultiProperties) {
+ WxCpMultiProperties.ConfigStorage storage = wxCpMultiProperties.getConfigStorage();
+ WxCpMultiRedisProperties redis = storage.getRedis();
+
+ JedisPoolConfig config = new JedisPoolConfig();
+ if (redis.getMaxActive() != null) {
+ config.setMaxTotal(redis.getMaxActive());
+ }
+ if (redis.getMaxIdle() != null) {
+ config.setMaxIdle(redis.getMaxIdle());
+ }
+ if (redis.getMaxWaitMillis() != null) {
+ config.setMaxWaitMillis(redis.getMaxWaitMillis());
+ }
+ if (redis.getMinIdle() != null) {
+ config.setMinIdle(redis.getMinIdle());
+ }
+ config.setTestOnBorrow(true);
+ config.setTestWhileIdle(true);
+
+ return new JedisPool(config, redis.getHost(), redis.getPort(),
+ redis.getTimeout(), redis.getPassword(), redis.getDatabase());
+ }
+}
diff --git a/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/configuration/services/WxCpInMemoryConfiguration.java b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/configuration/services/WxCpInMemoryConfiguration.java
new file mode 100644
index 0000000000..3dfb36e258
--- /dev/null
+++ b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/configuration/services/WxCpInMemoryConfiguration.java
@@ -0,0 +1,38 @@
+package com.binarywang.solon.wxjava.cp_multi.configuration.services;
+
+import com.binarywang.solon.wxjava.cp_multi.properties.WxCpMultiProperties;
+import com.binarywang.solon.wxjava.cp_multi.service.WxCpMultiServices;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+
+/**
+ * 自动装配基于内存策略配置
+ *
+ * @author yl
+ * created on 2023/10/16
+ */
+@Configuration
+@Condition(
+ onProperty = "${"+WxCpMultiProperties.PREFIX + ".configStorage.type:memory} = memory"
+)
+@RequiredArgsConstructor
+public class WxCpInMemoryConfiguration extends AbstractWxCpConfiguration {
+ private final WxCpMultiProperties wxCpMultiProperties;
+
+ @Bean
+ public WxCpMultiServices wxCpMultiServices() {
+ return this.wxCpMultiServices(wxCpMultiProperties);
+ }
+
+ @Override
+ protected WxCpDefaultConfigImpl wxCpConfigStorage(WxCpMultiProperties wxCpMultiProperties) {
+ return this.configInMemory();
+ }
+
+ private WxCpDefaultConfigImpl configInMemory() {
+ return new WxCpDefaultConfigImpl();
+ }
+}
diff --git a/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/configuration/services/WxCpInRedissonConfiguration.java b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/configuration/services/WxCpInRedissonConfiguration.java
new file mode 100644
index 0000000000..6700570af8
--- /dev/null
+++ b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/configuration/services/WxCpInRedissonConfiguration.java
@@ -0,0 +1,68 @@
+package com.binarywang.solon.wxjava.cp_multi.configuration.services;
+
+import com.binarywang.solon.wxjava.cp_multi.properties.WxCpMultiProperties;
+import com.binarywang.solon.wxjava.cp_multi.properties.WxCpMultiRedisProperties;
+import com.binarywang.solon.wxjava.cp_multi.service.WxCpMultiServices;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl;
+import me.chanjar.weixin.cp.config.impl.WxCpRedissonConfigImpl;
+import org.apache.commons.lang3.StringUtils;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+import org.noear.solon.core.AppContext;
+import org.redisson.Redisson;
+import org.redisson.api.RedissonClient;
+import org.redisson.config.Config;
+import org.redisson.config.TransportMode;
+
+/**
+ * 自动装配基于 redisson 策略配置
+ *
+ * @author yl
+ * created on 2023/10/16
+ */
+@Configuration
+@Condition(
+ onProperty = "${"+WxCpMultiProperties.PREFIX + ".configStorage.type} = redisson",
+ onClass = Redisson.class
+)
+@RequiredArgsConstructor
+public class WxCpInRedissonConfiguration extends AbstractWxCpConfiguration {
+ private final WxCpMultiProperties wxCpMultiProperties;
+ private final AppContext applicationContext;
+
+ @Bean
+ public WxCpMultiServices wxCpMultiServices() {
+ return this.wxCpMultiServices(wxCpMultiProperties);
+ }
+
+ @Override
+ protected WxCpDefaultConfigImpl wxCpConfigStorage(WxCpMultiProperties wxCpMultiProperties) {
+ return this.configRedisson(wxCpMultiProperties);
+ }
+
+ private WxCpDefaultConfigImpl configRedisson(WxCpMultiProperties wxCpMultiProperties) {
+ WxCpMultiRedisProperties redisProperties = wxCpMultiProperties.getConfigStorage().getRedis();
+ RedissonClient redissonClient;
+ if (redisProperties != null && StringUtils.isNotEmpty(redisProperties.getHost())) {
+ redissonClient = getRedissonClient(wxCpMultiProperties);
+ } else {
+ redissonClient = applicationContext.getBean(RedissonClient.class);
+ }
+ return new WxCpRedissonConfigImpl(redissonClient, wxCpMultiProperties.getConfigStorage().getKeyPrefix());
+ }
+
+ private RedissonClient getRedissonClient(WxCpMultiProperties wxCpMultiProperties) {
+ WxCpMultiProperties.ConfigStorage storage = wxCpMultiProperties.getConfigStorage();
+ WxCpMultiRedisProperties redis = storage.getRedis();
+
+ Config config = new Config();
+ config.useSingleServer()
+ .setAddress("redis://" + redis.getHost() + ":" + redis.getPort())
+ .setDatabase(redis.getDatabase())
+ .setPassword(redis.getPassword());
+ config.setTransportMode(TransportMode.NIO);
+ return Redisson.create(config);
+ }
+}
diff --git a/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/integration/WxCpMultiPluginImpl.java b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/integration/WxCpMultiPluginImpl.java
new file mode 100644
index 0000000000..b2a078c727
--- /dev/null
+++ b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/integration/WxCpMultiPluginImpl.java
@@ -0,0 +1,21 @@
+package com.binarywang.solon.wxjava.cp_multi.integration;
+
+import com.binarywang.solon.wxjava.cp_multi.configuration.services.WxCpInJedisConfiguration;
+import com.binarywang.solon.wxjava.cp_multi.configuration.services.WxCpInMemoryConfiguration;
+import com.binarywang.solon.wxjava.cp_multi.configuration.services.WxCpInRedissonConfiguration;
+import com.binarywang.solon.wxjava.cp_multi.properties.WxCpMultiProperties;
+import org.noear.solon.core.AppContext;
+import org.noear.solon.core.Plugin;
+
+/**
+ * @author noear 2024/9/2 created
+ */
+public class WxCpMultiPluginImpl implements Plugin {
+ @Override
+ public void start(AppContext context) throws Throwable {
+ context.beanMake(WxCpMultiProperties.class);
+ context.beanMake(WxCpInJedisConfiguration.class);
+ context.beanMake(WxCpInMemoryConfiguration.class);
+ context.beanMake(WxCpInRedissonConfiguration.class);
+ }
+}
diff --git a/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/properties/WxCpMultiProperties.java b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/properties/WxCpMultiProperties.java
new file mode 100644
index 0000000000..5544a92e00
--- /dev/null
+++ b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/properties/WxCpMultiProperties.java
@@ -0,0 +1,129 @@
+package com.binarywang.solon.wxjava.cp_multi.properties;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.noear.solon.annotation.Configuration;
+import org.noear.solon.annotation.Inject;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 企业微信多企业接入相关配置属性
+ *
+ * @author yl
+ * created on 2023/10/16
+ */
+@Data
+@NoArgsConstructor
+@Configuration
+@Inject("${" + WxCpMultiProperties.PREFIX + "}")
+public class WxCpMultiProperties implements Serializable {
+ private static final long serialVersionUID = -1569510477055668503L;
+ public static final String PREFIX = "wx.cp";
+
+ private Map corps = new HashMap<>();
+
+ /**
+ * 配置存储策略,默认内存
+ */
+ private ConfigStorage configStorage = new ConfigStorage();
+
+ @Data
+ @NoArgsConstructor
+ public static class ConfigStorage implements Serializable {
+ private static final long serialVersionUID = 4815731027000065434L;
+ /**
+ * 存储类型
+ */
+ private StorageType type = StorageType.memory;
+
+ /**
+ * 指定key前缀
+ */
+ private String keyPrefix = "wx:cp";
+
+ /**
+ * redis连接配置
+ */
+ private WxCpMultiRedisProperties redis = new WxCpMultiRedisProperties();
+
+ /**
+ * http客户端类型.
+ */
+ private HttpClientType httpClientType = HttpClientType.HTTP_CLIENT;
+
+ /**
+ * http代理主机
+ */
+ private String httpProxyHost;
+
+ /**
+ * http代理端口
+ */
+ private Integer httpProxyPort;
+
+ /**
+ * http代理用户名
+ */
+ private String httpProxyUsername;
+
+ /**
+ * http代理密码
+ */
+ private String httpProxyPassword;
+
+ /**
+ * http 请求最大重试次数
+ *
+ * {@link me.chanjar.weixin.cp.api.WxCpService#setMaxRetryTimes(int)}
+ * {@link me.chanjar.weixin.cp.api.impl.BaseWxCpServiceImpl#setMaxRetryTimes(int)}
+ *
+ */
+ private int maxRetryTimes = 5;
+
+ /**
+ * http 请求重试间隔
+ *
+ * {@link me.chanjar.weixin.cp.api.WxCpService#setRetrySleepMillis(int)}
+ * {@link me.chanjar.weixin.cp.api.impl.BaseWxCpServiceImpl#setRetrySleepMillis(int)}
+ *
+ */
+ private int retrySleepMillis = 1000;
+ }
+
+ public enum StorageType {
+ /**
+ * 内存
+ */
+ memory,
+ /**
+ * jedis
+ */
+ jedis,
+ /**
+ * redisson
+ */
+ redisson,
+ /**
+ * redistemplate
+ */
+ redistemplate
+ }
+
+ public enum HttpClientType {
+ /**
+ * HttpClient
+ */
+ HTTP_CLIENT,
+ /**
+ * OkHttp
+ */
+ OK_HTTP,
+ /**
+ * JoddHttp
+ */
+ JODD_HTTP
+ }
+}
diff --git a/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/properties/WxCpMultiRedisProperties.java b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/properties/WxCpMultiRedisProperties.java
new file mode 100644
index 0000000000..14952d69d9
--- /dev/null
+++ b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/properties/WxCpMultiRedisProperties.java
@@ -0,0 +1,48 @@
+package com.binarywang.solon.wxjava.cp_multi.properties;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * Redis配置.
+ *
+ * @author yl
+ * created on 2023/10/16
+ */
+@Data
+@NoArgsConstructor
+public class WxCpMultiRedisProperties implements Serializable {
+ private static final long serialVersionUID = -5924815351660074401L;
+
+ /**
+ * 主机地址.
+ */
+ private String host;
+
+ /**
+ * 端口号.
+ */
+ private int port = 6379;
+
+ /**
+ * 密码.
+ */
+ private String password;
+
+ /**
+ * 超时.
+ */
+ private int timeout = 2000;
+
+ /**
+ * 数据库.
+ */
+ private int database = 0;
+
+ private Integer maxActive;
+ private Integer maxIdle;
+ private Integer maxWaitMillis;
+ private Integer minIdle;
+}
diff --git a/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/properties/WxCpSingleProperties.java b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/properties/WxCpSingleProperties.java
new file mode 100644
index 0000000000..e761a09062
--- /dev/null
+++ b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/properties/WxCpSingleProperties.java
@@ -0,0 +1,46 @@
+package com.binarywang.solon.wxjava.cp_multi.properties;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 企业微信企业相关配置属性
+ *
+ * @author yl
+ * created on 2023/10/16
+ */
+@Data
+@NoArgsConstructor
+public class WxCpSingleProperties implements Serializable {
+ private static final long serialVersionUID = -7502823825007859418L;
+ /**
+ * 微信企业号 corpId
+ */
+ private String corpId;
+ /**
+ * 微信企业号 corpSecret
+ */
+ private String corpSecret;
+ /**
+ * 微信企业号应用 token
+ */
+ private String token;
+ /**
+ * 微信企业号应用 ID
+ */
+ private Integer agentId;
+ /**
+ * 微信企业号应用 EncodingAESKey
+ */
+ private String aesKey;
+ /**
+ * 微信企业号应用 会话存档私钥
+ */
+ private String msgAuditPriKey;
+ /**
+ * 微信企业号应用 会话存档类库路径
+ */
+ private String msgAuditLibPath;
+}
diff --git a/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/service/WxCpMultiServices.java b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/service/WxCpMultiServices.java
new file mode 100644
index 0000000000..c66c28233d
--- /dev/null
+++ b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/service/WxCpMultiServices.java
@@ -0,0 +1,26 @@
+package com.binarywang.solon.wxjava.cp_multi.service;
+
+import me.chanjar.weixin.cp.api.WxCpService;
+
+/**
+ * 企业微信 {@link WxCpService} 所有实例存放类.
+ *
+ * @author yl
+ * created on 2023/10/16
+ */
+public interface WxCpMultiServices {
+ /**
+ * 通过租户 Id 获取 WxCpService
+ *
+ * @param tenantId 租户 Id
+ * @return WxCpService
+ */
+ WxCpService getWxCpService(String tenantId);
+
+ /**
+ * 根据租户 Id,从列表中移除一个 WxCpService 实例
+ *
+ * @param tenantId 租户 Id
+ */
+ void removeWxCpService(String tenantId);
+}
diff --git a/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/service/WxCpMultiServicesImpl.java b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/service/WxCpMultiServicesImpl.java
new file mode 100644
index 0000000000..d7833a05f9
--- /dev/null
+++ b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp_multi/service/WxCpMultiServicesImpl.java
@@ -0,0 +1,42 @@
+package com.binarywang.solon.wxjava.cp_multi.service;
+
+import me.chanjar.weixin.cp.api.WxCpService;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 企业微信 {@link WxCpMultiServices} 默认实现
+ *
+ * @author yl
+ * created on 2023/10/16
+ */
+public class WxCpMultiServicesImpl implements WxCpMultiServices {
+ private final Map services = new ConcurrentHashMap<>();
+
+ /**
+ * 通过租户 Id 获取 WxCpService
+ *
+ * @param tenantId 租户 Id
+ * @return WxCpService
+ */
+ @Override
+ public WxCpService getWxCpService(String tenantId) {
+ return this.services.get(tenantId);
+ }
+
+ /**
+ * 根据租户 Id,添加一个 WxCpService 到列表
+ *
+ * @param tenantId 租户 Id
+ * @param wxCpService WxCpService 实例
+ */
+ public void addWxCpService(String tenantId, WxCpService wxCpService) {
+ this.services.put(tenantId, wxCpService);
+ }
+
+ @Override
+ public void removeWxCpService(String tenantId) {
+ this.services.remove(tenantId);
+ }
+}
diff --git a/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/resources/META-INF/solon/wx-java-cp-multi-solon-plugin.properties b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/resources/META-INF/solon/wx-java-cp-multi-solon-plugin.properties
new file mode 100644
index 0000000000..eb537e9a66
--- /dev/null
+++ b/solon-plugins/wx-java-cp-multi-solon-plugin/src/main/resources/META-INF/solon/wx-java-cp-multi-solon-plugin.properties
@@ -0,0 +1,2 @@
+solon.plugin=com.binarywang.solon.wxjava.cp_multi.integration.WxCpMultiPluginImpl
+solon.plugin.priority=10
diff --git a/solon-plugins/wx-java-cp-solon-plugin/README.md b/solon-plugins/wx-java-cp-solon-plugin/README.md
new file mode 100644
index 0000000000..04d5dfab58
--- /dev/null
+++ b/solon-plugins/wx-java-cp-solon-plugin/README.md
@@ -0,0 +1,41 @@
+# wx-java-cp-solon-plugin
+
+## 快速开始
+
+1. 引入依赖
+ ```xml
+
+ com.github.binarywang
+ wx-java-cp-solon-plugin
+ ${version}
+
+ ```
+2. 添加配置(app.properties)
+ ```properties
+ # 企业微信号配置(必填)
+ wx.cp.corp-id = @corp-id
+ wx.cp.corp-secret = @corp-secret
+ # 选填
+ wx.cp.agent-id = @agent-id
+ wx.cp.token = @token
+ wx.cp.aes-key = @aes-key
+ wx.cp.msg-audit-priKey = @msg-audit-priKey
+ wx.cp.msg-audit-lib-path = @msg-audit-lib-path
+ # ConfigStorage 配置(选填)
+ wx.cp.config-storage.type=memory # 配置类型: memory(默认), jedis, redisson, redistemplate
+ # http 客户端配置(选填)
+ wx.cp.config-storage.http-proxy-host=
+ wx.cp.config-storage.http-proxy-port=
+ wx.cp.config-storage.http-proxy-username=
+ wx.cp.config-storage.http-proxy-password=
+ # 最大重试次数,默认:5 次,如果小于 0,则为 0
+ wx.cp.config-storage.max-retry-times=5
+ # 重试时间间隔步进,默认:1000 毫秒,如果小于 0,则为 1000
+ wx.cp.config-storage.retry-sleep-millis=1000
+ ```
+3. 支持自动注入的类型: `WxCpService`, `WxCpConfigStorage`
+
+4. 覆盖自动配置: 自定义注入的bean会覆盖自动注入的
+
+- WxCpService
+- WxCpConfigStorage
diff --git a/solon-plugins/wx-java-cp-solon-plugin/pom.xml b/solon-plugins/wx-java-cp-solon-plugin/pom.xml
new file mode 100644
index 0000000000..6b71454c68
--- /dev/null
+++ b/solon-plugins/wx-java-cp-solon-plugin/pom.xml
@@ -0,0 +1,30 @@
+
+
+
+ wx-java-solon-plugins
+ com.github.binarywang
+ 4.6.4.B
+
+ 4.0.0
+
+ wx-java-cp-solon-plugin
+ WxJava - Solon Plugin for WxCp
+ 微信企业号开发的 Solon Plugin
+
+
+
+ com.github.binarywang
+ weixin-java-cp
+ ${project.version}
+
+
+ redis.clients
+ jedis
+
+
+ org.redisson
+ redisson
+
+
+
diff --git a/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/config/WxCpServiceAutoConfiguration.java b/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/config/WxCpServiceAutoConfiguration.java
new file mode 100644
index 0000000000..82aeeaf859
--- /dev/null
+++ b/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/config/WxCpServiceAutoConfiguration.java
@@ -0,0 +1,43 @@
+package com.binarywang.solon.wxjava.cp.config;
+
+import com.binarywang.solon.wxjava.cp.properties.WxCpProperties;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.cp.api.WxCpService;
+import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl;
+import me.chanjar.weixin.cp.config.WxCpConfigStorage;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+
+/**
+ * 企业微信平台相关服务自动注册
+ *
+ * @author yl
+ * created on 2021/12/6
+ */
+@Configuration
+@RequiredArgsConstructor
+public class WxCpServiceAutoConfiguration {
+ private final WxCpProperties wxCpProperties;
+
+ @Bean
+ @Condition(onMissingBean = WxCpService.class,
+ onBean = WxCpConfigStorage.class)
+ public WxCpService wxCpService(WxCpConfigStorage wxCpConfigStorage) {
+ WxCpService wxCpService = new WxCpServiceImpl();
+ wxCpService.setWxCpConfigStorage(wxCpConfigStorage);
+
+ WxCpProperties.ConfigStorage storage = wxCpProperties.getConfigStorage();
+ int maxRetryTimes = storage.getMaxRetryTimes();
+ if (maxRetryTimes < 0) {
+ maxRetryTimes = 0;
+ }
+ int retrySleepMillis = storage.getRetrySleepMillis();
+ if (retrySleepMillis < 0) {
+ retrySleepMillis = 1000;
+ }
+ wxCpService.setRetrySleepMillis(retrySleepMillis);
+ wxCpService.setMaxRetryTimes(maxRetryTimes);
+ return wxCpService;
+ }
+}
diff --git a/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/integration/WxCpPluginImpl.java b/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/integration/WxCpPluginImpl.java
new file mode 100644
index 0000000000..fda64b3a17
--- /dev/null
+++ b/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/integration/WxCpPluginImpl.java
@@ -0,0 +1,25 @@
+package com.binarywang.solon.wxjava.cp.integration;
+
+import com.binarywang.solon.wxjava.cp.config.WxCpServiceAutoConfiguration;
+import com.binarywang.solon.wxjava.cp.properties.WxCpProperties;
+import com.binarywang.solon.wxjava.cp.storage.WxCpInJedisConfigStorageConfiguration;
+import com.binarywang.solon.wxjava.cp.storage.WxCpInMemoryConfigStorageConfiguration;
+import com.binarywang.solon.wxjava.cp.storage.WxCpInRedissonConfigStorageConfiguration;
+import org.noear.solon.core.AppContext;
+import org.noear.solon.core.Plugin;
+
+/**
+ * @author noear 2024/9/2 created
+ */
+public class WxCpPluginImpl implements Plugin {
+ @Override
+ public void start(AppContext context) throws Throwable {
+ context.beanMake(WxCpProperties.class);
+
+ context.beanMake(WxCpServiceAutoConfiguration.class);
+
+ context.beanMake(WxCpInMemoryConfigStorageConfiguration.class);
+ context.beanMake(WxCpInJedisConfigStorageConfiguration.class);
+ context.beanMake(WxCpInRedissonConfigStorageConfiguration.class);
+ }
+}
diff --git a/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/properties/WxCpProperties.java b/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/properties/WxCpProperties.java
new file mode 100644
index 0000000000..60524f5228
--- /dev/null
+++ b/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/properties/WxCpProperties.java
@@ -0,0 +1,133 @@
+package com.binarywang.solon.wxjava.cp.properties;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.noear.solon.annotation.Configuration;
+import org.noear.solon.annotation.Inject;
+
+import java.io.Serializable;
+
+/**
+ * 企业微信接入相关配置属性
+ *
+ * @author yl
+ * created on 2021/12/6
+ */
+@Data
+@NoArgsConstructor
+@Configuration
+@Inject("${" + WxCpProperties.PREFIX + "}")
+public class WxCpProperties {
+ public static final String PREFIX = "wx.cp";
+
+ /**
+ * 微信企业号 corpId
+ */
+ private String corpId;
+ /**
+ * 微信企业号 corpSecret
+ */
+ private String corpSecret;
+ /**
+ * 微信企业号应用 token
+ */
+ private String token;
+ /**
+ * 微信企业号应用 ID
+ */
+ private Integer agentId;
+ /**
+ * 微信企业号应用 EncodingAESKey
+ */
+ private String aesKey;
+ /**
+ * 微信企业号应用 会话存档私钥
+ */
+ private String msgAuditPriKey;
+ /**
+ * 微信企业号应用 会话存档类库路径
+ */
+ private String msgAuditLibPath;
+
+ /**
+ * 配置存储策略,默认内存
+ */
+ private ConfigStorage configStorage = new ConfigStorage();
+
+ @Data
+ @NoArgsConstructor
+ public static class ConfigStorage implements Serializable {
+ private static final long serialVersionUID = 4815731027000065434L;
+ /**
+ * 存储类型
+ */
+ private StorageType type = StorageType.memory;
+
+ /**
+ * 指定key前缀
+ */
+ private String keyPrefix = "wx:cp";
+
+ /**
+ * redis连接配置
+ */
+ private WxCpRedisProperties redis = new WxCpRedisProperties();
+
+ /**
+ * http代理主机
+ */
+ private String httpProxyHost;
+
+ /**
+ * http代理端口
+ */
+ private Integer httpProxyPort;
+
+ /**
+ * http代理用户名
+ */
+ private String httpProxyUsername;
+
+ /**
+ * http代理密码
+ */
+ private String httpProxyPassword;
+
+ /**
+ * http 请求最大重试次数
+ *
+ * {@link me.chanjar.weixin.cp.api.WxCpService#setMaxRetryTimes(int)}
+ * {@link me.chanjar.weixin.cp.api.impl.BaseWxCpServiceImpl#setMaxRetryTimes(int)}
+ *
+ */
+ private int maxRetryTimes = 5;
+
+ /**
+ * http 请求重试间隔
+ *
+ * {@link me.chanjar.weixin.cp.api.WxCpService#setRetrySleepMillis(int)}
+ * {@link me.chanjar.weixin.cp.api.impl.BaseWxCpServiceImpl#setRetrySleepMillis(int)}
+ *
+ */
+ private int retrySleepMillis = 1000;
+ }
+
+ public enum StorageType {
+ /**
+ * 内存
+ */
+ memory,
+ /**
+ * jedis
+ */
+ jedis,
+ /**
+ * redisson
+ */
+ redisson,
+ /**
+ * redistemplate
+ */
+ redistemplate
+ }
+}
diff --git a/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/properties/WxCpRedisProperties.java b/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/properties/WxCpRedisProperties.java
new file mode 100644
index 0000000000..43b8788d3f
--- /dev/null
+++ b/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/properties/WxCpRedisProperties.java
@@ -0,0 +1,46 @@
+package com.binarywang.solon.wxjava.cp.properties;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * Redis配置.
+ *
+ * @author yl
+ * created on 2023/04/23
+ */
+@Data
+public class WxCpRedisProperties implements Serializable {
+ private static final long serialVersionUID = -5924815351660074401L;
+
+ /**
+ * 主机地址.
+ */
+ private String host;
+
+ /**
+ * 端口号.
+ */
+ private int port = 6379;
+
+ /**
+ * 密码.
+ */
+ private String password;
+
+ /**
+ * 超时.
+ */
+ private int timeout = 2000;
+
+ /**
+ * 数据库.
+ */
+ private int database = 0;
+
+ private Integer maxActive;
+ private Integer maxIdle;
+ private Integer maxWaitMillis;
+ private Integer minIdle;
+}
diff --git a/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/storage/AbstractWxCpConfigStorageConfiguration.java b/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/storage/AbstractWxCpConfigStorageConfiguration.java
new file mode 100644
index 0000000000..9fcdd5779a
--- /dev/null
+++ b/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/storage/AbstractWxCpConfigStorageConfiguration.java
@@ -0,0 +1,61 @@
+package com.binarywang.solon.wxjava.cp.storage;
+
+import com.binarywang.solon.wxjava.cp.properties.WxCpProperties;
+import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl;
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * WxCpConfigStorage 抽象配置类
+ *
+ * @author yl & Wang_Wong
+ * created on 2021/12/6
+ */
+public abstract class AbstractWxCpConfigStorageConfiguration {
+
+ protected WxCpDefaultConfigImpl config(WxCpDefaultConfigImpl config, WxCpProperties properties) {
+ String corpId = properties.getCorpId();
+ String corpSecret = properties.getCorpSecret();
+ Integer agentId = properties.getAgentId();
+ String token = properties.getToken();
+ String aesKey = properties.getAesKey();
+ // 企业微信,私钥,会话存档路径
+ String msgAuditPriKey = properties.getMsgAuditPriKey();
+ String msgAuditLibPath = properties.getMsgAuditLibPath();
+
+ config.setCorpId(corpId);
+ config.setCorpSecret(corpSecret);
+ config.setAgentId(agentId);
+ if (StringUtils.isNotBlank(token)) {
+ config.setToken(token);
+ }
+ if (StringUtils.isNotBlank(aesKey)) {
+ config.setAesKey(aesKey);
+ }
+ if (StringUtils.isNotBlank(msgAuditPriKey)) {
+ config.setMsgAuditPriKey(msgAuditPriKey);
+ }
+ if (StringUtils.isNotBlank(msgAuditLibPath)) {
+ config.setMsgAuditLibPath(msgAuditLibPath);
+ }
+
+ WxCpProperties.ConfigStorage storage = properties.getConfigStorage();
+ String httpProxyHost = storage.getHttpProxyHost();
+ Integer httpProxyPort = storage.getHttpProxyPort();
+ String httpProxyUsername = storage.getHttpProxyUsername();
+ String httpProxyPassword = storage.getHttpProxyPassword();
+ if (StringUtils.isNotBlank(httpProxyHost)) {
+ config.setHttpProxyHost(httpProxyHost);
+ if (httpProxyPort != null) {
+ config.setHttpProxyPort(httpProxyPort);
+ }
+ if (StringUtils.isNotBlank(httpProxyUsername)) {
+ config.setHttpProxyUsername(httpProxyUsername);
+ }
+ if (StringUtils.isNotBlank(httpProxyPassword)) {
+ config.setHttpProxyPassword(httpProxyPassword);
+ }
+ }
+ return config;
+ }
+
+}
diff --git a/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/storage/WxCpInJedisConfigStorageConfiguration.java b/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/storage/WxCpInJedisConfigStorageConfiguration.java
new file mode 100644
index 0000000000..f6f6931992
--- /dev/null
+++ b/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/storage/WxCpInJedisConfigStorageConfiguration.java
@@ -0,0 +1,74 @@
+package com.binarywang.solon.wxjava.cp.storage;
+
+import com.binarywang.solon.wxjava.cp.properties.WxCpProperties;
+import com.binarywang.solon.wxjava.cp.properties.WxCpRedisProperties;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.cp.config.WxCpConfigStorage;
+import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl;
+import me.chanjar.weixin.cp.config.impl.WxCpJedisConfigImpl;
+import org.apache.commons.lang3.StringUtils;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+import org.noear.solon.core.AppContext;
+import redis.clients.jedis.JedisPool;
+import redis.clients.jedis.JedisPoolConfig;
+
+/**
+ * 自动装配基于 jedis 策略配置
+ *
+ * @author yl
+ * created on 2023/04/23
+ */
+@Configuration
+@Condition(
+ onProperty = "${"+WxCpProperties.PREFIX + ".configStorage.type} = jedis",
+ onClass = JedisPool.class
+)
+@RequiredArgsConstructor
+public class WxCpInJedisConfigStorageConfiguration extends AbstractWxCpConfigStorageConfiguration {
+ private final WxCpProperties wxCpProperties;
+ private final AppContext applicationContext;
+
+ @Bean
+ @Condition(onMissingBean=WxCpConfigStorage.class)
+ public WxCpConfigStorage wxCpConfigStorage() {
+ WxCpDefaultConfigImpl config = getConfigStorage();
+ return this.config(config, wxCpProperties);
+ }
+
+ private WxCpJedisConfigImpl getConfigStorage() {
+ WxCpRedisProperties wxCpRedisProperties = wxCpProperties.getConfigStorage().getRedis();
+ JedisPool jedisPool;
+ if (wxCpRedisProperties != null && StringUtils.isNotEmpty(wxCpRedisProperties.getHost())) {
+ jedisPool = getJedisPool();
+ } else {
+ jedisPool = applicationContext.getBean(JedisPool.class);
+ }
+ return new WxCpJedisConfigImpl(jedisPool, wxCpProperties.getConfigStorage().getKeyPrefix());
+ }
+
+ private JedisPool getJedisPool() {
+ WxCpProperties.ConfigStorage storage = wxCpProperties.getConfigStorage();
+ WxCpRedisProperties redis = storage.getRedis();
+
+ JedisPoolConfig config = new JedisPoolConfig();
+ if (redis.getMaxActive() != null) {
+ config.setMaxTotal(redis.getMaxActive());
+ }
+ if (redis.getMaxIdle() != null) {
+ config.setMaxIdle(redis.getMaxIdle());
+ }
+ if (redis.getMaxWaitMillis() != null) {
+ config.setMaxWaitMillis(redis.getMaxWaitMillis());
+ }
+ if (redis.getMinIdle() != null) {
+ config.setMinIdle(redis.getMinIdle());
+ }
+ config.setTestOnBorrow(true);
+ config.setTestWhileIdle(true);
+
+ return new JedisPool(config, redis.getHost(), redis.getPort(),
+ redis.getTimeout(), redis.getPassword(), redis.getDatabase());
+ }
+}
diff --git a/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/storage/WxCpInMemoryConfigStorageConfiguration.java b/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/storage/WxCpInMemoryConfigStorageConfiguration.java
new file mode 100644
index 0000000000..2776fea368
--- /dev/null
+++ b/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/storage/WxCpInMemoryConfigStorageConfiguration.java
@@ -0,0 +1,31 @@
+package com.binarywang.solon.wxjava.cp.storage;
+
+import com.binarywang.solon.wxjava.cp.properties.WxCpProperties;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.cp.config.WxCpConfigStorage;
+import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+
+/**
+ * 自动装配基于内存策略配置
+ *
+ * @author yl
+ * created on 2021/12/6
+ */
+@Configuration
+@Condition(
+ onProperty = "${"+WxCpProperties.PREFIX + ".configStorage.type:memory} = memory"
+)
+@RequiredArgsConstructor
+public class WxCpInMemoryConfigStorageConfiguration extends AbstractWxCpConfigStorageConfiguration {
+ private final WxCpProperties wxCpProperties;
+
+ @Bean
+ @Condition(onMissingBean=WxCpConfigStorage.class)
+ public WxCpConfigStorage wxCpConfigStorage() {
+ WxCpDefaultConfigImpl config = new WxCpDefaultConfigImpl();
+ return this.config(config, wxCpProperties);
+ }
+}
diff --git a/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/storage/WxCpInRedissonConfigStorageConfiguration.java b/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/storage/WxCpInRedissonConfigStorageConfiguration.java
new file mode 100644
index 0000000000..0aef4d520a
--- /dev/null
+++ b/solon-plugins/wx-java-cp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/cp/storage/WxCpInRedissonConfigStorageConfiguration.java
@@ -0,0 +1,65 @@
+package com.binarywang.solon.wxjava.cp.storage;
+
+import com.binarywang.solon.wxjava.cp.properties.WxCpProperties;
+import com.binarywang.solon.wxjava.cp.properties.WxCpRedisProperties;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.cp.config.WxCpConfigStorage;
+import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl;
+import me.chanjar.weixin.cp.config.impl.WxCpRedissonConfigImpl;
+import org.apache.commons.lang3.StringUtils;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+import org.noear.solon.core.AppContext;
+import org.redisson.Redisson;
+import org.redisson.api.RedissonClient;
+import org.redisson.config.Config;
+import org.redisson.config.TransportMode;
+
+/**
+ * 自动装配基于 redisson 策略配置
+ *
+ * @author yl
+ * created on 2023/04/23
+ */
+@Configuration
+@Condition(
+ onProperty = "${"+WxCpProperties.PREFIX + ".configStorage.type} = redisson",
+ onClass = Redisson.class
+)
+@RequiredArgsConstructor
+public class WxCpInRedissonConfigStorageConfiguration extends AbstractWxCpConfigStorageConfiguration {
+ private final WxCpProperties wxCpProperties;
+ private final AppContext applicationContext;
+
+ @Bean
+ @Condition(onMissingBean=WxCpConfigStorage.class)
+ public WxCpConfigStorage wxCpConfigStorage() {
+ WxCpDefaultConfigImpl config = getConfigStorage();
+ return this.config(config, wxCpProperties);
+ }
+
+ private WxCpRedissonConfigImpl getConfigStorage() {
+ WxCpRedisProperties redisProperties = wxCpProperties.getConfigStorage().getRedis();
+ RedissonClient redissonClient;
+ if (redisProperties != null && StringUtils.isNotEmpty(redisProperties.getHost())) {
+ redissonClient = getRedissonClient();
+ } else {
+ redissonClient = applicationContext.getBean(RedissonClient.class);
+ }
+ return new WxCpRedissonConfigImpl(redissonClient, wxCpProperties.getConfigStorage().getKeyPrefix());
+ }
+
+ private RedissonClient getRedissonClient() {
+ WxCpProperties.ConfigStorage storage = wxCpProperties.getConfigStorage();
+ WxCpRedisProperties redis = storage.getRedis();
+
+ Config config = new Config();
+ config.useSingleServer()
+ .setAddress("redis://" + redis.getHost() + ":" + redis.getPort())
+ .setDatabase(redis.getDatabase())
+ .setPassword(redis.getPassword());
+ config.setTransportMode(TransportMode.NIO);
+ return Redisson.create(config);
+ }
+}
diff --git a/solon-plugins/wx-java-cp-solon-plugin/src/main/resources/META-INF/solon/wx-java-cp-solon-plugin.properties b/solon-plugins/wx-java-cp-solon-plugin/src/main/resources/META-INF/solon/wx-java-cp-solon-plugin.properties
new file mode 100644
index 0000000000..c765affecb
--- /dev/null
+++ b/solon-plugins/wx-java-cp-solon-plugin/src/main/resources/META-INF/solon/wx-java-cp-solon-plugin.properties
@@ -0,0 +1,2 @@
+solon.plugin=com.binarywang.solon.wxjava.cp.integration.WxCpPluginImpl
+solon.plugin.priority=10
diff --git a/solon-plugins/wx-java-miniapp-solon-plugin/README.md b/solon-plugins/wx-java-miniapp-solon-plugin/README.md
new file mode 100644
index 0000000000..3d1d7517f7
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-solon-plugin/README.md
@@ -0,0 +1,35 @@
+# wx-java-miniapp-solon-plugin
+## 快速开始
+1. 引入依赖
+ ```xml
+
+ com.github.binarywang
+ wx-java-miniapp-solon-plugin
+ ${version}
+
+ ```
+2. 添加配置(app.properties)
+ ```properties
+ # 公众号配置(必填)
+ wx.miniapp.appid = appId
+ wx.miniapp.secret = @secret
+ wx.miniapp.token = @token
+ wx.miniapp.aesKey = @aesKey
+ wx.miniapp.msgDataFormat = @msgDataFormat # 消息格式,XML或者JSON.
+ # 存储配置redis(可选)
+ # 注意: 指定redis.host值后不会使用容器注入的redis连接(JedisPool)
+ wx.miniapp.config-storage.type = Jedis # 配置类型: Memory(默认), Jedis, RedisTemplate
+ wx.miniapp.config-storage.key-prefix = wa # 相关redis前缀配置: wa(默认)
+ wx.miniapp.config-storage.redis.host = 127.0.0.1
+ wx.miniapp.config-storage.redis.port = 6379
+ # http客户端配置
+ wx.miniapp.config-storage.http-client-type=HttpClient # http客户端类型: HttpClient(默认), OkHttp, JoddHttp
+ wx.miniapp.config-storage.http-proxy-host=
+ wx.miniapp.config-storage.http-proxy-port=
+ wx.miniapp.config-storage.http-proxy-username=
+ wx.miniapp.config-storage.http-proxy-password=
+ ```
+3. 自动注入的类型
+- `WxMaService`
+- `WxMaConfig`
+
diff --git a/solon-plugins/wx-java-miniapp-solon-plugin/pom.xml b/solon-plugins/wx-java-miniapp-solon-plugin/pom.xml
new file mode 100644
index 0000000000..b4d527d711
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-solon-plugin/pom.xml
@@ -0,0 +1,43 @@
+
+
+
+ wx-java-solon-plugins
+ com.github.binarywang
+ 4.6.4.B
+
+ 4.0.0
+
+ wx-java-miniapp-solon-plugin
+ WxJava - Solon Plugin for MiniApp
+ 微信小程序开发的 Solon Plugin
+
+
+
+ com.github.binarywang
+ weixin-java-miniapp
+ ${project.version}
+
+
+ redis.clients
+ jedis
+ provided
+
+
+ org.redisson
+ redisson
+ provided
+
+
+ org.jodd
+ jodd-http
+ provided
+
+
+ com.squareup.okhttp3
+ okhttp
+ provided
+
+
+
+
diff --git a/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/config/WxMaServiceAutoConfiguration.java b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/config/WxMaServiceAutoConfiguration.java
new file mode 100644
index 0000000000..5463ec08e9
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/config/WxMaServiceAutoConfiguration.java
@@ -0,0 +1,54 @@
+package com.binarywang.solon.wxjava.miniapp.config;
+
+import cn.binarywang.wx.miniapp.api.WxMaService;
+import cn.binarywang.wx.miniapp.api.impl.WxMaServiceHttpClientImpl;
+import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
+import cn.binarywang.wx.miniapp.api.impl.WxMaServiceJoddHttpImpl;
+import cn.binarywang.wx.miniapp.api.impl.WxMaServiceOkHttpImpl;
+import cn.binarywang.wx.miniapp.config.WxMaConfig;
+import com.binarywang.solon.wxjava.miniapp.enums.HttpClientType;
+import com.binarywang.solon.wxjava.miniapp.properties.WxMaProperties;
+import lombok.AllArgsConstructor;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+
+/**
+ * 微信小程序平台相关服务自动注册.
+ *
+ * @author someone TaoYu
+ */
+@Configuration
+@AllArgsConstructor
+public class WxMaServiceAutoConfiguration {
+
+ private final WxMaProperties wxMaProperties;
+
+ /**
+ * 小程序service.
+ *
+ * @return 小程序service
+ */
+ @Bean
+ @Condition(onMissingBean=WxMaService.class, onBean=WxMaConfig.class)
+ public WxMaService wxMaService(WxMaConfig wxMaConfig) {
+ HttpClientType httpClientType = wxMaProperties.getConfigStorage().getHttpClientType();
+ WxMaService wxMaService;
+ switch (httpClientType) {
+ case OkHttp:
+ wxMaService = new WxMaServiceOkHttpImpl();
+ break;
+ case JoddHttp:
+ wxMaService = new WxMaServiceJoddHttpImpl();
+ break;
+ case HttpClient:
+ wxMaService = new WxMaServiceHttpClientImpl();
+ break;
+ default:
+ wxMaService = new WxMaServiceImpl();
+ break;
+ }
+ wxMaService.setWxMaConfig(wxMaConfig);
+ return wxMaService;
+ }
+}
diff --git a/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/config/storage/AbstractWxMaConfigStorageConfiguration.java b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/config/storage/AbstractWxMaConfigStorageConfiguration.java
new file mode 100644
index 0000000000..9cc4fe161b
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/config/storage/AbstractWxMaConfigStorageConfiguration.java
@@ -0,0 +1,39 @@
+package com.binarywang.solon.wxjava.miniapp.config.storage;
+
+import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
+import com.binarywang.solon.wxjava.miniapp.properties.WxMaProperties;
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * @author yl TaoYu
+ */
+public abstract class AbstractWxMaConfigStorageConfiguration {
+
+ protected WxMaDefaultConfigImpl config(WxMaDefaultConfigImpl config, WxMaProperties properties) {
+ config.setAppid(StringUtils.trimToNull(properties.getAppid()));
+ config.setSecret(StringUtils.trimToNull(properties.getSecret()));
+ config.setToken(StringUtils.trimToNull(properties.getToken()));
+ config.setAesKey(StringUtils.trimToNull(properties.getAesKey()));
+ config.setMsgDataFormat(StringUtils.trimToNull(properties.getMsgDataFormat()));
+
+ WxMaProperties.ConfigStorage configStorageProperties = properties.getConfigStorage();
+ config.setHttpProxyHost(configStorageProperties.getHttpProxyHost());
+ config.setHttpProxyUsername(configStorageProperties.getHttpProxyUsername());
+ config.setHttpProxyPassword(configStorageProperties.getHttpProxyPassword());
+ if (configStorageProperties.getHttpProxyPort() != null) {
+ config.setHttpProxyPort(configStorageProperties.getHttpProxyPort());
+ }
+
+ int maxRetryTimes = configStorageProperties.getMaxRetryTimes();
+ if (configStorageProperties.getMaxRetryTimes() < 0) {
+ maxRetryTimes = 0;
+ }
+ int retrySleepMillis = configStorageProperties.getRetrySleepMillis();
+ if (retrySleepMillis < 0) {
+ retrySleepMillis = 1000;
+ }
+ config.setRetrySleepMillis(retrySleepMillis);
+ config.setMaxRetryTimes(maxRetryTimes);
+ return config;
+ }
+}
diff --git a/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/config/storage/WxMaInJedisConfigStorageConfiguration.java b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/config/storage/WxMaInJedisConfigStorageConfiguration.java
new file mode 100644
index 0000000000..da8c4701ba
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/config/storage/WxMaInJedisConfigStorageConfiguration.java
@@ -0,0 +1,72 @@
+package com.binarywang.solon.wxjava.miniapp.config.storage;
+
+import cn.binarywang.wx.miniapp.config.WxMaConfig;
+import cn.binarywang.wx.miniapp.config.impl.WxMaRedisBetterConfigImpl;
+import com.binarywang.solon.wxjava.miniapp.properties.RedisProperties;
+import com.binarywang.solon.wxjava.miniapp.properties.WxMaProperties;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.common.redis.JedisWxRedisOps;
+import me.chanjar.weixin.common.redis.WxRedisOps;
+import org.apache.commons.lang3.StringUtils;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+import org.noear.solon.core.AppContext;
+import redis.clients.jedis.JedisPool;
+import redis.clients.jedis.JedisPoolConfig;
+
+/**
+ * @author yl TaoYu
+ */
+@Configuration
+@Condition(
+ onProperty = "${"+WxMaProperties.PREFIX + ".configStorage.type} = jedis",
+ onClass = JedisPool.class
+)
+@RequiredArgsConstructor
+public class WxMaInJedisConfigStorageConfiguration extends AbstractWxMaConfigStorageConfiguration {
+ private final WxMaProperties properties;
+ private final AppContext applicationContext;
+
+ @Bean
+ @Condition(onMissingBean=WxMaConfig.class)
+ public WxMaConfig wxMaConfig() {
+ WxMaRedisBetterConfigImpl config = getWxMaRedisBetterConfigImpl();
+ return this.config(config, properties);
+ }
+
+ private WxMaRedisBetterConfigImpl getWxMaRedisBetterConfigImpl() {
+ RedisProperties redisProperties = properties.getConfigStorage().getRedis();
+ JedisPool jedisPool;
+ if (redisProperties != null && StringUtils.isNotEmpty(redisProperties.getHost())) {
+ jedisPool = getJedisPool();
+ } else {
+ jedisPool = applicationContext.getBean(JedisPool.class);
+ }
+ WxRedisOps redisOps = new JedisWxRedisOps(jedisPool);
+ return new WxMaRedisBetterConfigImpl(redisOps, properties.getConfigStorage().getKeyPrefix());
+ }
+
+ private JedisPool getJedisPool() {
+ WxMaProperties.ConfigStorage storage = properties.getConfigStorage();
+ RedisProperties redis = storage.getRedis();
+
+ JedisPoolConfig config = new JedisPoolConfig();
+ if (redis.getMaxActive() != null) {
+ config.setMaxTotal(redis.getMaxActive());
+ }
+ if (redis.getMaxIdle() != null) {
+ config.setMaxIdle(redis.getMaxIdle());
+ }
+ if (redis.getMaxWaitMillis() != null) {
+ config.setMaxWaitMillis(redis.getMaxWaitMillis());
+ }
+ if (redis.getMinIdle() != null) {
+ config.setMinIdle(redis.getMinIdle());
+ }
+ config.setTestOnBorrow(true);
+ config.setTestWhileIdle(true);
+
+ return new JedisPool(config, redis.getHost(), redis.getPort(), redis.getTimeout(), redis.getPassword(), redis.getDatabase());
+ }
+}
diff --git a/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/config/storage/WxMaInMemoryConfigStorageConfiguration.java b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/config/storage/WxMaInMemoryConfigStorageConfiguration.java
new file mode 100644
index 0000000000..958742d2aa
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/config/storage/WxMaInMemoryConfigStorageConfiguration.java
@@ -0,0 +1,28 @@
+package com.binarywang.solon.wxjava.miniapp.config.storage;
+
+import cn.binarywang.wx.miniapp.config.WxMaConfig;
+import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
+import com.binarywang.solon.wxjava.miniapp.properties.WxMaProperties;
+import lombok.RequiredArgsConstructor;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+
+/**
+ * @author yl TaoYu
+ */
+@Configuration
+@Condition(
+ onProperty = "${"+WxMaProperties.PREFIX + ".configStorage.type:memory} = memory"
+)
+@RequiredArgsConstructor
+public class WxMaInMemoryConfigStorageConfiguration extends AbstractWxMaConfigStorageConfiguration {
+ private final WxMaProperties properties;
+
+ @Bean
+ @Condition(onMissingBean=WxMaConfig.class)
+ public WxMaConfig wxMaConfig() {
+ WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl();
+ return this.config(config, properties);
+ }
+}
diff --git a/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/config/storage/WxMaInRedissonConfigStorageConfiguration.java b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/config/storage/WxMaInRedissonConfigStorageConfiguration.java
new file mode 100644
index 0000000000..af7c11448e
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/config/storage/WxMaInRedissonConfigStorageConfiguration.java
@@ -0,0 +1,61 @@
+package com.binarywang.solon.wxjava.miniapp.config.storage;
+
+import cn.binarywang.wx.miniapp.config.WxMaConfig;
+import cn.binarywang.wx.miniapp.config.impl.WxMaRedissonConfigImpl;
+import com.binarywang.solon.wxjava.miniapp.properties.RedisProperties;
+import com.binarywang.solon.wxjava.miniapp.properties.WxMaProperties;
+import lombok.RequiredArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+import org.noear.solon.core.AppContext;
+import org.redisson.Redisson;
+import org.redisson.api.RedissonClient;
+import org.redisson.config.Config;
+import org.redisson.config.TransportMode;
+
+/**
+ * @author yl TaoYu
+ */
+@Configuration
+@Condition(
+ onProperty = "${"+WxMaProperties.PREFIX + ".configStorage.type} = redisson",
+ onClass = Redisson.class
+)
+@RequiredArgsConstructor
+public class WxMaInRedissonConfigStorageConfiguration extends AbstractWxMaConfigStorageConfiguration {
+ private final WxMaProperties properties;
+ private final AppContext applicationContext;
+
+ @Bean
+ @Condition(onMissingBean=WxMaConfig.class)
+ public WxMaConfig wxMaConfig() {
+ WxMaRedissonConfigImpl config = getWxMaInRedissonConfigStorage();
+ return this.config(config, properties);
+ }
+
+ private WxMaRedissonConfigImpl getWxMaInRedissonConfigStorage() {
+ RedisProperties redisProperties = properties.getConfigStorage().getRedis();
+ RedissonClient redissonClient;
+ if (redisProperties != null && StringUtils.isNotEmpty(redisProperties.getHost())) {
+ redissonClient = getRedissonClient();
+ } else {
+ redissonClient = applicationContext.getBean(RedissonClient.class);
+ }
+ return new WxMaRedissonConfigImpl(redissonClient, properties.getConfigStorage().getKeyPrefix());
+ }
+
+ private RedissonClient getRedissonClient() {
+ WxMaProperties.ConfigStorage storage = properties.getConfigStorage();
+ RedisProperties redis = storage.getRedis();
+
+ Config config = new Config();
+ config.useSingleServer()
+ .setAddress("redis://" + redis.getHost() + ":" + redis.getPort())
+ .setDatabase(redis.getDatabase())
+ .setPassword(redis.getPassword());
+ config.setTransportMode(TransportMode.NIO);
+ return Redisson.create(config);
+ }
+}
diff --git a/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/enums/HttpClientType.java b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/enums/HttpClientType.java
new file mode 100644
index 0000000000..a4475a02c7
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/enums/HttpClientType.java
@@ -0,0 +1,22 @@
+package com.binarywang.solon.wxjava.miniapp.enums;
+
+/**
+ * httpclient类型.
+ *
+ * @author Binary Wang
+ * created on 2020-05-25
+ */
+public enum HttpClientType {
+ /**
+ * HttpClient.
+ */
+ HttpClient,
+ /**
+ * OkHttp.
+ */
+ OkHttp,
+ /**
+ * JoddHttp.
+ */
+ JoddHttp,
+}
diff --git a/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/enums/StorageType.java b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/enums/StorageType.java
new file mode 100644
index 0000000000..b82261ba8a
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/enums/StorageType.java
@@ -0,0 +1,26 @@
+package com.binarywang.solon.wxjava.miniapp.enums;
+
+/**
+ * storage类型.
+ *
+ * @author Binary Wang
+ * created on 2020-05-25
+ */
+public enum StorageType {
+ /**
+ * 内存.
+ */
+ Memory,
+ /**
+ * redis(JedisClient).
+ */
+ Jedis,
+ /**
+ * redis(Redisson).
+ */
+ Redisson,
+ /**
+ * redis(RedisTemplate).
+ */
+ RedisTemplate
+}
diff --git a/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/integration/WxMiniappPluginImpl.java b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/integration/WxMiniappPluginImpl.java
new file mode 100644
index 0000000000..88d1c3023a
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/integration/WxMiniappPluginImpl.java
@@ -0,0 +1,25 @@
+package com.binarywang.solon.wxjava.miniapp.integration;
+
+import com.binarywang.solon.wxjava.miniapp.config.WxMaServiceAutoConfiguration;
+import com.binarywang.solon.wxjava.miniapp.config.storage.WxMaInJedisConfigStorageConfiguration;
+import com.binarywang.solon.wxjava.miniapp.config.storage.WxMaInMemoryConfigStorageConfiguration;
+import com.binarywang.solon.wxjava.miniapp.config.storage.WxMaInRedissonConfigStorageConfiguration;
+import com.binarywang.solon.wxjava.miniapp.properties.WxMaProperties;
+import org.noear.solon.core.AppContext;
+import org.noear.solon.core.Plugin;
+
+/**
+ * @author noear 2024/9/2 created
+ */
+public class WxMiniappPluginImpl implements Plugin {
+ @Override
+ public void start(AppContext context) throws Throwable {
+ context.beanMake(WxMaProperties.class);
+
+ context.beanMake(WxMaServiceAutoConfiguration.class);
+
+ context.beanMake(WxMaInMemoryConfigStorageConfiguration.class);
+ context.beanMake(WxMaInJedisConfigStorageConfiguration.class);
+ context.beanMake(WxMaInRedissonConfigStorageConfiguration.class);
+ }
+}
diff --git a/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/properties/RedisProperties.java b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/properties/RedisProperties.java
new file mode 100644
index 0000000000..021a4b1b6b
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/properties/RedisProperties.java
@@ -0,0 +1,43 @@
+package com.binarywang.solon.wxjava.miniapp.properties;
+
+import lombok.Data;
+
+/**
+ * redis 配置.
+ *
+ * @author Binary Wang
+ * created on 2020-08-30
+ */
+@Data
+public class RedisProperties {
+
+ /**
+ * 主机地址.不填则从solon容器内获取JedisPool
+ */
+ private String host;
+
+ /**
+ * 端口号.
+ */
+ private int port = 6379;
+
+ /**
+ * 密码.
+ */
+ private String password;
+
+ /**
+ * 超时.
+ */
+ private int timeout = 2000;
+
+ /**
+ * 数据库.
+ */
+ private int database = 0;
+
+ private Integer maxActive;
+ private Integer maxIdle;
+ private Integer maxWaitMillis;
+ private Integer minIdle;
+}
diff --git a/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/properties/WxMaProperties.java b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/properties/WxMaProperties.java
new file mode 100644
index 0000000000..5a993cf667
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/properties/WxMaProperties.java
@@ -0,0 +1,112 @@
+package com.binarywang.solon.wxjava.miniapp.properties;
+
+import com.binarywang.solon.wxjava.miniapp.enums.HttpClientType;
+import com.binarywang.solon.wxjava.miniapp.enums.StorageType;
+import lombok.Data;
+import org.noear.solon.annotation.Configuration;
+import org.noear.solon.annotation.Inject;
+
+import static com.binarywang.solon.wxjava.miniapp.properties.WxMaProperties.PREFIX;
+
+/**
+ * 属性配置类.
+ *
+ * @author Binary Wang
+ * created on 2019-08-10
+ */
+@Data
+@Configuration
+@Inject("${" + PREFIX + "}")
+public class WxMaProperties {
+ public static final String PREFIX = "wx.miniapp";
+
+ /**
+ * 设置微信小程序的appid.
+ */
+ private String appid;
+
+ /**
+ * 设置微信小程序的Secret.
+ */
+ private String secret;
+
+ /**
+ * 设置微信小程序消息服务器配置的token.
+ */
+ private String token;
+
+ /**
+ * 设置微信小程序消息服务器配置的EncodingAESKey.
+ */
+ private String aesKey;
+
+ /**
+ * 消息格式,XML或者JSON.
+ */
+ private String msgDataFormat;
+
+ /**
+ * 存储策略
+ */
+ private final ConfigStorage configStorage = new ConfigStorage();
+
+ @Data
+ public static class ConfigStorage {
+
+ /**
+ * 存储类型.
+ */
+ private StorageType type = StorageType.Memory;
+
+ /**
+ * 指定key前缀.
+ */
+ private String keyPrefix = "wa";
+
+ /**
+ * redis连接配置.
+ */
+ private final RedisProperties redis = new RedisProperties();
+
+ /**
+ * http客户端类型.
+ */
+ private HttpClientType httpClientType = HttpClientType.HttpClient;
+
+ /**
+ * http代理主机.
+ */
+ private String httpProxyHost;
+
+ /**
+ * http代理端口.
+ */
+ private Integer httpProxyPort;
+
+ /**
+ * http代理用户名.
+ */
+ private String httpProxyUsername;
+
+ /**
+ * http代理密码.
+ */
+ private String httpProxyPassword;
+
+ /**
+ * http 请求重试间隔
+ *
+ * {@link cn.binarywang.wx.miniapp.api.impl.BaseWxMaServiceImpl#setRetrySleepMillis(int)}
+ *
+ */
+ private int retrySleepMillis = 1000;
+ /**
+ * http 请求最大重试次数
+ *
+ * {@link cn.binarywang.wx.miniapp.api.impl.BaseWxMaServiceImpl#setMaxRetryTimes(int)}
+ *
+ */
+ private int maxRetryTimes = 5;
+ }
+
+}
diff --git a/solon-plugins/wx-java-miniapp-solon-plugin/src/main/resources/META-INF/solon/wx-java-miniapp-solon-plugin.properties b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/resources/META-INF/solon/wx-java-miniapp-solon-plugin.properties
new file mode 100644
index 0000000000..ba1049647e
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-solon-plugin/src/main/resources/META-INF/solon/wx-java-miniapp-solon-plugin.properties
@@ -0,0 +1,2 @@
+solon.plugin=com.binarywang.solon.wxjava.miniapp.integration.WxMiniappPluginImpl
+solon.plugin.priority=10
diff --git a/solon-plugins/wx-java-mp-multi-solon-plugin/README.md b/solon-plugins/wx-java-mp-multi-solon-plugin/README.md
new file mode 100644
index 0000000000..0d2b332d5a
--- /dev/null
+++ b/solon-plugins/wx-java-mp-multi-solon-plugin/README.md
@@ -0,0 +1,100 @@
+# wx-java-mp-multi-solon-plugin
+
+## 快速开始
+
+1. 引入依赖
+ ```xml
+
+ com.github.binarywang
+ wx-java-mp-multi-solon-plugin
+ ${version}
+
+ ```
+2. 添加配置(app.properties)
+ ```properties
+ # 公众号配置
+ ## 应用 1 配置(必填)
+ wx.mp.apps.tenantId1.app-id=appId
+ wx.mp.apps.tenantId1.app-secret=@secret
+ ## 选填
+ wx.mp.apps.tenantId1.token=@token
+ wx.mp.apps.tenantId1.aes-key=@aesKey
+ wx.mp.apps.tenantId1.use-stable-access-token=@useStableAccessToken
+ ## 应用 2 配置(必填)
+ wx.mp.apps.tenantId2.app-id=@appId
+ wx.mp.apps.tenantId2.app-secret =@secret
+ ## 选填
+ wx.mp.apps.tenantId2.token=@token
+ wx.mp.apps.tenantId2.aes-key=@aesKey
+ wx.mp.apps.tenantId2.use-stable-access-token=@useStableAccessToken
+
+ # ConfigStorage 配置(选填)
+ ## 配置类型: memory(默认), jedis, redisson, redis_template
+ wx.mp.config-storage.type=memory
+ ## 相关redis前缀配置: wx:mp:multi(默认)
+ wx.mp.config-storage.key-prefix=wx:mp:multi
+ wx.mp.config-storage.redis.host=127.0.0.1
+ wx.mp.config-storage.redis.port=6379
+ ## 单机和 sentinel 同时存在时,优先使用sentinel配置
+ # wx.mp.config-storage.redis.sentinel-ips=127.0.0.1:16379,127.0.0.1:26379
+ # wx.mp.config-storage.redis.sentinel-name=mymaster
+
+ # http 客户端配置(选填)
+ ## # http客户端类型: http_client(默认), ok_http, jodd_http
+ wx.mp.config-storage.http-client-type=http_client
+ wx.mp.config-storage.http-proxy-host=
+ wx.mp.config-storage.http-proxy-port=
+ wx.mp.config-storage.http-proxy-username=
+ wx.mp.config-storage.http-proxy-password=
+ ## 最大重试次数,默认:5 次,如果小于 0,则为 0
+ wx.mp.config-storage.max-retry-times=5
+ ## 重试时间间隔步进,默认:1000 毫秒,如果小于 0,则为 1000
+ wx.mp.config-storage.retry-sleep-millis=1000
+
+ # 公众号地址 host 配置
+ # wx.mp.hosts.api-host=http://proxy.com/
+ # wx.mp.hosts.open-host=http://proxy.com/
+ # wx.mp.hosts.mp-host=http://proxy.com/
+ ```
+3. 自动注入的类型:`WxMpMultiServices`
+
+4. 使用样例
+
+```java
+import com.binarywang.solon.wxjava.mp_multi.service.WxMpMultiServices;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.api.WxMpUserService;
+import org.noear.solon.annotation.Component;
+import org.noear.solon.annotation.Inject;
+
+@Component
+public class DemoService {
+ @Inject
+ private WxMpMultiServices wxMpMultiServices;
+
+ public void test() {
+ // 应用 1 的 WxMpService
+ WxMpService wxMpService1 = wxMpMultiServices.getWxMpService("tenantId1");
+ WxMpUserService userService1 = wxMpService1.getUserService();
+ userService1.userInfo("xxx");
+ // todo ...
+
+ // 应用 2 的 WxMpService
+ WxMpService wxMpService2 = wxMpMultiServices.getWxMpService("tenantId2");
+ WxMpUserService userService2 = wxMpService2.getUserService();
+ userService2.userInfo("xxx");
+ // todo ...
+
+ // 应用 3 的 WxMpService
+ WxMpService wxMpService3 = wxMpMultiServices.getWxMpService("tenantId3");
+ // 判断是否为空
+ if (wxMpService3 == null) {
+ // todo wxMpService3 为空,请先配置 tenantId3 微信公众号应用参数
+ return;
+ }
+ WxMpUserService userService3 = wxMpService3.getUserService();
+ userService3.userInfo("xxx");
+ // todo ...
+ }
+}
+```
diff --git a/solon-plugins/wx-java-mp-multi-solon-plugin/pom.xml b/solon-plugins/wx-java-mp-multi-solon-plugin/pom.xml
new file mode 100644
index 0000000000..197561e68b
--- /dev/null
+++ b/solon-plugins/wx-java-mp-multi-solon-plugin/pom.xml
@@ -0,0 +1,44 @@
+
+
+
+ wx-java-solon-plugins
+ com.github.binarywang
+ 4.6.4.B
+
+ 4.0.0
+
+ wx-java-mp-multi-solon-plugin
+ WxJava - Solon Plugin for MP::支持多账号配置
+ 微信公众号开发的 Solon Plugin::支持多账号配置
+
+
+
+ com.github.binarywang
+ weixin-java-mp
+ ${project.version}
+
+
+ redis.clients
+ jedis
+ provided
+
+
+ org.redisson
+ redisson
+ provided
+
+
+ org.jodd
+ jodd-http
+ provided
+
+
+ com.squareup.okhttp3
+ okhttp
+ provided
+
+
+
+
diff --git a/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/configuration/services/AbstractWxMpConfiguration.java b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/configuration/services/AbstractWxMpConfiguration.java
new file mode 100644
index 0000000000..d534b98746
--- /dev/null
+++ b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/configuration/services/AbstractWxMpConfiguration.java
@@ -0,0 +1,165 @@
+package com.binarywang.solon.wxjava.mp_multi.configuration.services;
+
+import com.binarywang.solon.wxjava.mp_multi.properties.WxMpMultiProperties;
+import com.binarywang.solon.wxjava.mp_multi.properties.WxMpSingleProperties;
+import com.binarywang.solon.wxjava.mp_multi.service.WxMpMultiServices;
+import com.binarywang.solon.wxjava.mp_multi.service.WxMpMultiServicesImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.api.impl.WxMpServiceHttpClientImpl;
+import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
+import me.chanjar.weixin.mp.api.impl.WxMpServiceJoddHttpImpl;
+import me.chanjar.weixin.mp.api.impl.WxMpServiceOkHttpImpl;
+import me.chanjar.weixin.mp.config.WxMpConfigStorage;
+import me.chanjar.weixin.mp.config.WxMpHostConfig;
+import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * WxMpConfigStorage 抽象配置类
+ *
+ * @author yl
+ * created on 2024/1/23
+ */
+@RequiredArgsConstructor
+@Slf4j
+public abstract class AbstractWxMpConfiguration {
+
+ protected WxMpMultiServices wxMpMultiServices(WxMpMultiProperties wxCpMultiProperties) {
+ Map appsMap = wxCpMultiProperties.getApps();
+ if (appsMap == null || appsMap.isEmpty()) {
+ log.warn("微信公众号应用参数未配置,通过 WxMpMultiServices#getWxMpService(\"tenantId\")获取实例将返回空");
+ return new WxMpMultiServicesImpl();
+ }
+ /**
+ * 校验 appId 是否唯一,避免使用 redis 缓存 token、ticket 时错乱。
+ *
+ * 查看 {@link me.chanjar.weixin.mp.config.impl.WxMpRedisConfigImpl#setAppId(String)}
+ */
+ Collection apps = appsMap.values();
+ if (apps.size() > 1) {
+ // 校验 appId 是否唯一
+ boolean multi = apps.stream()
+ // 没有 appId,如果不判断是否为空,这里会报 NPE 异常
+ .collect(Collectors.groupingBy(c -> c.getAppId() == null ? 0 : c.getAppId(), Collectors.counting()))
+ .entrySet().stream().anyMatch(e -> e.getValue() > 1);
+ if (multi) {
+ throw new RuntimeException("请确保微信公众号配置 appId 的唯一性");
+ }
+ }
+ WxMpMultiServicesImpl services = new WxMpMultiServicesImpl();
+
+ Set> entries = appsMap.entrySet();
+ for (Map.Entry entry : entries) {
+ String tenantId = entry.getKey();
+ WxMpSingleProperties wxMpSingleProperties = entry.getValue();
+ WxMpDefaultConfigImpl storage = this.wxMpConfigStorage(wxCpMultiProperties);
+ this.configApp(storage, wxMpSingleProperties);
+ this.configHttp(storage, wxCpMultiProperties.getConfigStorage());
+ this.configHost(storage, wxCpMultiProperties.getHosts());
+ WxMpService wxCpService = this.wxMpService(storage, wxCpMultiProperties);
+ services.addWxMpService(tenantId, wxCpService);
+ }
+ return services;
+ }
+
+ /**
+ * 配置 WxMpDefaultConfigImpl
+ *
+ * @param wxMpMultiProperties 参数
+ * @return WxMpDefaultConfigImpl
+ */
+ protected abstract WxMpDefaultConfigImpl wxMpConfigStorage(WxMpMultiProperties wxMpMultiProperties);
+
+ public WxMpService wxMpService(WxMpConfigStorage configStorage, WxMpMultiProperties wxMpMultiProperties) {
+ WxMpMultiProperties.ConfigStorage storage = wxMpMultiProperties.getConfigStorage();
+ WxMpMultiProperties.HttpClientType httpClientType = storage.getHttpClientType();
+ WxMpService wxMpService;
+ switch (httpClientType) {
+ case OK_HTTP:
+ wxMpService = new WxMpServiceOkHttpImpl();
+ break;
+ case JODD_HTTP:
+ wxMpService = new WxMpServiceJoddHttpImpl();
+ break;
+ case HTTP_CLIENT:
+ wxMpService = new WxMpServiceHttpClientImpl();
+ break;
+ default:
+ wxMpService = new WxMpServiceImpl();
+ break;
+ }
+
+ wxMpService.setWxMpConfigStorage(configStorage);
+ int maxRetryTimes = storage.getMaxRetryTimes();
+ if (maxRetryTimes < 0) {
+ maxRetryTimes = 0;
+ }
+ int retrySleepMillis = storage.getRetrySleepMillis();
+ if (retrySleepMillis < 0) {
+ retrySleepMillis = 1000;
+ }
+ wxMpService.setRetrySleepMillis(retrySleepMillis);
+ wxMpService.setMaxRetryTimes(maxRetryTimes);
+ return wxMpService;
+ }
+
+ private void configApp(WxMpDefaultConfigImpl config, WxMpSingleProperties corpProperties) {
+ String appId = corpProperties.getAppId();
+ String appSecret = corpProperties.getAppSecret();
+ String token = corpProperties.getToken();
+ String aesKey = corpProperties.getAesKey();
+ boolean useStableAccessToken = corpProperties.isUseStableAccessToken();
+
+ config.setAppId(appId);
+ config.setSecret(appSecret);
+ if (StringUtils.isNotBlank(token)) {
+ config.setToken(token);
+ }
+ if (StringUtils.isNotBlank(aesKey)) {
+ config.setAesKey(aesKey);
+ }
+ config.setUseStableAccessToken(useStableAccessToken);
+ }
+
+ private void configHttp(WxMpDefaultConfigImpl config, WxMpMultiProperties.ConfigStorage storage) {
+ String httpProxyHost = storage.getHttpProxyHost();
+ Integer httpProxyPort = storage.getHttpProxyPort();
+ String httpProxyUsername = storage.getHttpProxyUsername();
+ String httpProxyPassword = storage.getHttpProxyPassword();
+ if (StringUtils.isNotBlank(httpProxyHost)) {
+ config.setHttpProxyHost(httpProxyHost);
+ if (httpProxyPort != null) {
+ config.setHttpProxyPort(httpProxyPort);
+ }
+ if (StringUtils.isNotBlank(httpProxyUsername)) {
+ config.setHttpProxyUsername(httpProxyUsername);
+ }
+ if (StringUtils.isNotBlank(httpProxyPassword)) {
+ config.setHttpProxyPassword(httpProxyPassword);
+ }
+ }
+ }
+
+ /**
+ * wx host config
+ */
+ private void configHost(WxMpDefaultConfigImpl config, WxMpMultiProperties.HostConfig hostConfig) {
+ if (hostConfig != null) {
+ String apiHost = hostConfig.getApiHost();
+ String mpHost = hostConfig.getMpHost();
+ String openHost = hostConfig.getOpenHost();
+ WxMpHostConfig wxMpHostConfig = new WxMpHostConfig();
+ wxMpHostConfig.setApiHost(StringUtils.isNotBlank(apiHost) ? apiHost : null);
+ wxMpHostConfig.setMpHost(StringUtils.isNotBlank(mpHost) ? mpHost : null);
+ wxMpHostConfig.setOpenHost(StringUtils.isNotBlank(openHost) ? openHost : null);
+ config.setHostConfig(wxMpHostConfig);
+ }
+ }
+}
diff --git a/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/configuration/services/WxMpInJedisConfiguration.java b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/configuration/services/WxMpInJedisConfiguration.java
new file mode 100644
index 0000000000..c00898a82d
--- /dev/null
+++ b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/configuration/services/WxMpInJedisConfiguration.java
@@ -0,0 +1,78 @@
+package com.binarywang.solon.wxjava.mp_multi.configuration.services;
+
+import com.binarywang.solon.wxjava.mp_multi.properties.WxMpMultiProperties;
+import com.binarywang.solon.wxjava.mp_multi.properties.WxMpMultiRedisProperties;
+import com.binarywang.solon.wxjava.mp_multi.service.WxMpMultiServices;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.common.redis.JedisWxRedisOps;
+import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
+import me.chanjar.weixin.mp.config.impl.WxMpRedisConfigImpl;
+import org.apache.commons.lang3.StringUtils;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+import org.noear.solon.core.AppContext;
+import redis.clients.jedis.JedisPool;
+import redis.clients.jedis.JedisPoolConfig;
+
+/**
+ * 自动装配基于 jedis 策略配置
+ *
+ * @author yl
+ * created on 2024/1/23
+ */
+@Configuration
+@Condition(
+ onProperty = "${"+WxMpMultiProperties.PREFIX + ".configStorage.type} = jedis",
+ onClass = JedisPool.class
+)
+@RequiredArgsConstructor
+public class WxMpInJedisConfiguration extends AbstractWxMpConfiguration {
+ private final WxMpMultiProperties wxCpMultiProperties;
+ private final AppContext applicationContext;
+
+ @Bean
+ public WxMpMultiServices wxMpMultiServices() {
+ return this.wxMpMultiServices(wxCpMultiProperties);
+ }
+
+ @Override
+ protected WxMpDefaultConfigImpl wxMpConfigStorage(WxMpMultiProperties wxCpMultiProperties) {
+ return this.configRedis(wxCpMultiProperties);
+ }
+
+ private WxMpDefaultConfigImpl configRedis(WxMpMultiProperties wxCpMultiProperties) {
+ WxMpMultiRedisProperties wxCpMultiRedisProperties = wxCpMultiProperties.getConfigStorage().getRedis();
+ JedisPool jedisPool;
+ if (wxCpMultiRedisProperties != null && StringUtils.isNotEmpty(wxCpMultiRedisProperties.getHost())) {
+ jedisPool = getJedisPool(wxCpMultiProperties);
+ } else {
+ jedisPool = applicationContext.getBean(JedisPool.class);
+ }
+ return new WxMpRedisConfigImpl(new JedisWxRedisOps(jedisPool), wxCpMultiProperties.getConfigStorage().getKeyPrefix());
+ }
+
+ private JedisPool getJedisPool(WxMpMultiProperties wxCpMultiProperties) {
+ WxMpMultiProperties.ConfigStorage storage = wxCpMultiProperties.getConfigStorage();
+ WxMpMultiRedisProperties redis = storage.getRedis();
+
+ JedisPoolConfig config = new JedisPoolConfig();
+ if (redis.getMaxActive() != null) {
+ config.setMaxTotal(redis.getMaxActive());
+ }
+ if (redis.getMaxIdle() != null) {
+ config.setMaxIdle(redis.getMaxIdle());
+ }
+ if (redis.getMaxWaitMillis() != null) {
+ config.setMaxWaitMillis(redis.getMaxWaitMillis());
+ }
+ if (redis.getMinIdle() != null) {
+ config.setMinIdle(redis.getMinIdle());
+ }
+ config.setTestOnBorrow(true);
+ config.setTestWhileIdle(true);
+
+ return new JedisPool(config, redis.getHost(), redis.getPort(),
+ redis.getTimeout(), redis.getPassword(), redis.getDatabase());
+ }
+}
diff --git a/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/configuration/services/WxMpInMemoryConfiguration.java b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/configuration/services/WxMpInMemoryConfiguration.java
new file mode 100644
index 0000000000..74bc13e03e
--- /dev/null
+++ b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/configuration/services/WxMpInMemoryConfiguration.java
@@ -0,0 +1,40 @@
+package com.binarywang.solon.wxjava.mp_multi.configuration.services;
+
+import com.binarywang.solon.wxjava.mp_multi.properties.WxMpMultiProperties;
+import com.binarywang.solon.wxjava.mp_multi.service.WxMpMultiServices;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
+import me.chanjar.weixin.mp.config.impl.WxMpMapConfigImpl;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+
+/**
+ * 自动装配基于内存策略配置
+ *
+ * @author yl
+ * created on 2024/1/23
+ */
+@Configuration
+@Condition(
+ onProperty = "${"+WxMpMultiProperties.PREFIX + ".configStorage.type:memory} = memory"
+)
+@RequiredArgsConstructor
+public class WxMpInMemoryConfiguration extends AbstractWxMpConfiguration {
+ private final WxMpMultiProperties wxCpMultiProperties;
+
+ @Bean
+ public WxMpMultiServices wxCpMultiServices() {
+ return this.wxMpMultiServices(wxCpMultiProperties);
+ }
+
+ @Override
+ protected WxMpDefaultConfigImpl wxMpConfigStorage(WxMpMultiProperties wxCpMultiProperties) {
+ return this.configInMemory();
+ }
+
+ private WxMpDefaultConfigImpl configInMemory() {
+ return new WxMpMapConfigImpl();
+ // return new WxMpDefaultConfigImpl();
+ }
+}
diff --git a/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/configuration/services/WxMpInRedissonConfiguration.java b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/configuration/services/WxMpInRedissonConfiguration.java
new file mode 100644
index 0000000000..89ffdfd912
--- /dev/null
+++ b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/configuration/services/WxMpInRedissonConfiguration.java
@@ -0,0 +1,68 @@
+package com.binarywang.solon.wxjava.mp_multi.configuration.services;
+
+import com.binarywang.solon.wxjava.mp_multi.properties.WxMpMultiProperties;
+import com.binarywang.solon.wxjava.mp_multi.properties.WxMpMultiRedisProperties;
+import com.binarywang.solon.wxjava.mp_multi.service.WxMpMultiServices;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
+import me.chanjar.weixin.mp.config.impl.WxMpRedissonConfigImpl;
+import org.apache.commons.lang3.StringUtils;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+import org.noear.solon.core.AppContext;
+import org.redisson.Redisson;
+import org.redisson.api.RedissonClient;
+import org.redisson.config.Config;
+import org.redisson.config.TransportMode;
+
+/**
+ * 自动装配基于 redisson 策略配置
+ *
+ * @author yl
+ * created on 2024/1/23
+ */
+@Configuration
+@Condition(
+ onProperty = "${"+WxMpMultiProperties.PREFIX + ".configStorage.type} = redisson",
+ onClass = Redisson.class
+)
+@RequiredArgsConstructor
+public class WxMpInRedissonConfiguration extends AbstractWxMpConfiguration {
+ private final WxMpMultiProperties wxCpMultiProperties;
+ private final AppContext applicationContext;
+
+ @Bean
+ public WxMpMultiServices wxMpMultiServices() {
+ return this.wxMpMultiServices(wxCpMultiProperties);
+ }
+
+ @Override
+ protected WxMpDefaultConfigImpl wxMpConfigStorage(WxMpMultiProperties wxCpMultiProperties) {
+ return this.configRedisson(wxCpMultiProperties);
+ }
+
+ private WxMpDefaultConfigImpl configRedisson(WxMpMultiProperties wxCpMultiProperties) {
+ WxMpMultiRedisProperties redisProperties = wxCpMultiProperties.getConfigStorage().getRedis();
+ RedissonClient redissonClient;
+ if (redisProperties != null && StringUtils.isNotEmpty(redisProperties.getHost())) {
+ redissonClient = getRedissonClient(wxCpMultiProperties);
+ } else {
+ redissonClient = applicationContext.getBean(RedissonClient.class);
+ }
+ return new WxMpRedissonConfigImpl(redissonClient, wxCpMultiProperties.getConfigStorage().getKeyPrefix());
+ }
+
+ private RedissonClient getRedissonClient(WxMpMultiProperties wxCpMultiProperties) {
+ WxMpMultiProperties.ConfigStorage storage = wxCpMultiProperties.getConfigStorage();
+ WxMpMultiRedisProperties redis = storage.getRedis();
+
+ Config config = new Config();
+ config.useSingleServer()
+ .setAddress("redis://" + redis.getHost() + ":" + redis.getPort())
+ .setDatabase(redis.getDatabase())
+ .setPassword(redis.getPassword());
+ config.setTransportMode(TransportMode.NIO);
+ return Redisson.create(config);
+ }
+}
diff --git a/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/integration/WxMpMultiPluginImpl.java b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/integration/WxMpMultiPluginImpl.java
new file mode 100644
index 0000000000..3629a8f78f
--- /dev/null
+++ b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/integration/WxMpMultiPluginImpl.java
@@ -0,0 +1,23 @@
+package com.binarywang.solon.wxjava.mp_multi.integration;
+
+import com.binarywang.solon.wxjava.mp_multi.configuration.services.WxMpInJedisConfiguration;
+import com.binarywang.solon.wxjava.mp_multi.configuration.services.WxMpInMemoryConfiguration;
+import com.binarywang.solon.wxjava.mp_multi.configuration.services.WxMpInRedissonConfiguration;
+import com.binarywang.solon.wxjava.mp_multi.properties.WxMpMultiProperties;
+import org.noear.solon.core.AppContext;
+import org.noear.solon.core.Plugin;
+
+/**
+ * @author noear 2024/9/2 created
+ */
+public class WxMpMultiPluginImpl implements Plugin {
+ @Override
+ public void start(AppContext context) throws Throwable {
+ context.beanMake(WxMpMultiProperties.class);
+
+ context.beanMake(WxMpInJedisConfiguration.class);
+ context.beanMake(WxMpInMemoryConfiguration.class);
+ context.beanMake(WxMpInRedissonConfiguration.class);
+
+ }
+}
diff --git a/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/properties/WxMpMultiProperties.java b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/properties/WxMpMultiProperties.java
new file mode 100644
index 0000000000..1929e92607
--- /dev/null
+++ b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/properties/WxMpMultiProperties.java
@@ -0,0 +1,154 @@
+package com.binarywang.solon.wxjava.mp_multi.properties;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.noear.solon.annotation.Configuration;
+import org.noear.solon.annotation.Inject;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author yl
+ * created on 2024/1/23
+ */
+@Data
+@NoArgsConstructor
+@Configuration
+@Inject("${"+WxMpMultiProperties.PREFIX+"}")
+public class WxMpMultiProperties implements Serializable {
+ private static final long serialVersionUID = -5358245184407791011L;
+ public static final String PREFIX = "wx.mp";
+
+ private Map apps = new HashMap<>();
+
+ /**
+ * 自定义host配置
+ */
+ private HostConfig hosts;
+
+ /**
+ * 存储策略
+ */
+ private final ConfigStorage configStorage = new ConfigStorage();
+
+ @Data
+ @NoArgsConstructor
+ public static class HostConfig implements Serializable {
+ private static final long serialVersionUID = -4172767630740346001L;
+
+ /**
+ * 对应于:https://api.weixin.qq.com
+ */
+ private String apiHost;
+
+ /**
+ * 对应于:https://open.weixin.qq.com
+ */
+ private String openHost;
+
+ /**
+ * 对应于:https://mp.weixin.qq.com
+ */
+ private String mpHost;
+ }
+
+ @Data
+ @NoArgsConstructor
+ public static class ConfigStorage implements Serializable {
+ private static final long serialVersionUID = 4815731027000065434L;
+
+ /**
+ * 存储类型.
+ */
+ private StorageType type = StorageType.MEMORY;
+
+ /**
+ * 指定key前缀.
+ */
+ private String keyPrefix = "wx:mp:multi";
+
+ /**
+ * redis连接配置.
+ */
+ private final WxMpMultiRedisProperties redis = new WxMpMultiRedisProperties();
+
+ /**
+ * http客户端类型.
+ */
+ private HttpClientType httpClientType = HttpClientType.HTTP_CLIENT;
+
+ /**
+ * http代理主机.
+ */
+ private String httpProxyHost;
+
+ /**
+ * http代理端口.
+ */
+ private Integer httpProxyPort;
+
+ /**
+ * http代理用户名.
+ */
+ private String httpProxyUsername;
+
+ /**
+ * http代理密码.
+ */
+ private String httpProxyPassword;
+
+ /**
+ * http 请求最大重试次数
+ *
+ * {@link me.chanjar.weixin.mp.api.WxMpService#setMaxRetryTimes(int)}
+ * {@link me.chanjar.weixin.mp.api.impl.BaseWxMpServiceImpl#setMaxRetryTimes(int)}
+ *
+ */
+ private int maxRetryTimes = 5;
+
+ /**
+ * http 请求重试间隔
+ *
+ * {@link me.chanjar.weixin.mp.api.WxMpService#setRetrySleepMillis(int)}
+ * {@link me.chanjar.weixin.mp.api.impl.BaseWxMpServiceImpl#setRetrySleepMillis(int)}
+ *
+ */
+ private int retrySleepMillis = 1000;
+ }
+
+ public enum StorageType {
+ /**
+ * 内存
+ */
+ MEMORY,
+ /**
+ * jedis
+ */
+ JEDIS,
+ /**
+ * redisson
+ */
+ REDISSON,
+ /**
+ * redisTemplate
+ */
+ REDIS_TEMPLATE
+ }
+
+ public enum HttpClientType {
+ /**
+ * HttpClient
+ */
+ HTTP_CLIENT,
+ /**
+ * OkHttp
+ */
+ OK_HTTP,
+ /**
+ * JoddHttp
+ */
+ JODD_HTTP
+ }
+}
diff --git a/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/properties/WxMpMultiRedisProperties.java b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/properties/WxMpMultiRedisProperties.java
new file mode 100644
index 0000000000..12646d4eaf
--- /dev/null
+++ b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/properties/WxMpMultiRedisProperties.java
@@ -0,0 +1,56 @@
+package com.binarywang.solon.wxjava.mp_multi.properties;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * @author yl
+ * created on 2024/1/23
+ */
+@Data
+@NoArgsConstructor
+public class WxMpMultiRedisProperties implements Serializable {
+ private static final long serialVersionUID = -5924815351660074401L;
+
+ /**
+ * 主机地址.
+ */
+ private String host = "127.0.0.1";
+
+ /**
+ * 端口号.
+ */
+ private int port = 6379;
+
+ /**
+ * 密码.
+ */
+ private String password;
+
+ /**
+ * 超时.
+ */
+ private int timeout = 2000;
+
+ /**
+ * 数据库.
+ */
+ private int database = 0;
+
+ /**
+ * sentinel ips
+ */
+ private String sentinelIps;
+
+ /**
+ * sentinel name
+ */
+ private String sentinelName;
+
+ private Integer maxActive;
+ private Integer maxIdle;
+ private Integer maxWaitMillis;
+ private Integer minIdle;
+}
diff --git a/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/properties/WxMpSingleProperties.java b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/properties/WxMpSingleProperties.java
new file mode 100644
index 0000000000..22938cb67c
--- /dev/null
+++ b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/properties/WxMpSingleProperties.java
@@ -0,0 +1,40 @@
+package com.binarywang.solon.wxjava.mp_multi.properties;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * @author yl
+ * created on 2024/1/23
+ */
+@Data
+@NoArgsConstructor
+public class WxMpSingleProperties implements Serializable {
+ private static final long serialVersionUID = 1980986361098922525L;
+ /**
+ * 设置微信公众号的 appid.
+ */
+ private String appId;
+
+ /**
+ * 设置微信公众号的 app secret.
+ */
+ private String appSecret;
+
+ /**
+ * 设置微信公众号的 token.
+ */
+ private String token;
+
+ /**
+ * 设置微信公众号的 EncodingAESKey.
+ */
+ private String aesKey;
+
+ /**
+ * 是否使用稳定版 Access Token
+ */
+ private boolean useStableAccessToken = false;
+}
diff --git a/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/service/WxMpMultiServices.java b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/service/WxMpMultiServices.java
new file mode 100644
index 0000000000..a59b5962ad
--- /dev/null
+++ b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/service/WxMpMultiServices.java
@@ -0,0 +1,27 @@
+package com.binarywang.solon.wxjava.mp_multi.service;
+
+
+import me.chanjar.weixin.mp.api.WxMpService;
+
+/**
+ * 企业微信 {@link WxMpService} 所有实例存放类.
+ *
+ * @author yl
+ * created on 2024/1/23
+ */
+public interface WxMpMultiServices {
+ /**
+ * 通过租户 Id 获取 WxMpService
+ *
+ * @param tenantId 租户 Id
+ * @return WxMpService
+ */
+ WxMpService getWxMpService(String tenantId);
+
+ /**
+ * 根据租户 Id,从列表中移除一个 WxMpService 实例
+ *
+ * @param tenantId 租户 Id
+ */
+ void removeWxMpService(String tenantId);
+}
diff --git a/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/service/WxMpMultiServicesImpl.java b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/service/WxMpMultiServicesImpl.java
new file mode 100644
index 0000000000..d87cd4e8df
--- /dev/null
+++ b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp_multi/service/WxMpMultiServicesImpl.java
@@ -0,0 +1,36 @@
+package com.binarywang.solon.wxjava.mp_multi.service;
+
+import me.chanjar.weixin.mp.api.WxMpService;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 企业微信 {@link WxMpMultiServices} 默认实现
+ *
+ * @author yl
+ * created on 2024/1/23
+ */
+public class WxMpMultiServicesImpl implements WxMpMultiServices {
+ private final Map services = new ConcurrentHashMap<>();
+
+ @Override
+ public WxMpService getWxMpService(String tenantId) {
+ return this.services.get(tenantId);
+ }
+
+ /**
+ * 根据租户 Id,添加一个 WxMpService 到列表
+ *
+ * @param tenantId 租户 Id
+ * @param wxMpService WxMpService 实例
+ */
+ public void addWxMpService(String tenantId, WxMpService wxMpService) {
+ this.services.put(tenantId, wxMpService);
+ }
+
+ @Override
+ public void removeWxMpService(String tenantId) {
+ this.services.remove(tenantId);
+ }
+}
diff --git a/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/resources/META-INF/solon/wx-java-mp-multi-solon-plugin.properties b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/resources/META-INF/solon/wx-java-mp-multi-solon-plugin.properties
new file mode 100644
index 0000000000..11c68ccc81
--- /dev/null
+++ b/solon-plugins/wx-java-mp-multi-solon-plugin/src/main/resources/META-INF/solon/wx-java-mp-multi-solon-plugin.properties
@@ -0,0 +1,2 @@
+solon.plugin=com.binarywang.solon.wxjava.mp_multi.integration.WxMpMultiPluginImpl
+solon.plugin.priority=10
diff --git a/solon-plugins/wx-java-mp-solon-plugin/README.md b/solon-plugins/wx-java-mp-solon-plugin/README.md
new file mode 100644
index 0000000000..e5d7d10e25
--- /dev/null
+++ b/solon-plugins/wx-java-mp-solon-plugin/README.md
@@ -0,0 +1,46 @@
+# wx-java-mp-solon-plugin
+
+## 快速开始
+
+1. 引入依赖
+ ```xml
+
+ com.github.binarywang
+ wx-java-mp-solon-plugin
+ ${version}
+
+ ```
+2. 添加配置(app.properties)
+ ```properties
+ # 公众号配置(必填)
+ wx.mp.app-id=appId
+ wx.mp.secret=@secret
+ wx.mp.token=@token
+ wx.mp.aes-key=@aesKey
+ wx.mp.use-stable-access-token=@useStableAccessToken
+ # 存储配置redis(可选)
+ wx.mp.config-storage.type= edis # 配置类型: Memory(默认), Jedis, RedisTemplate
+ wx.mp.config-storage.key-prefix=wx # 相关redis前缀配置: wx(默认)
+ wx.mp.config-storage.redis.host=127.0.0.1
+ wx.mp.config-storage.redis.port=6379
+ #单机和sentinel同时存在时,优先使用sentinel配置
+ #wx.mp.config-storage.redis.sentinel-ips=127.0.0.1:16379,127.0.0.1:26379
+ #wx.mp.config-storage.redis.sentinel-name=mymaster
+ # http客户端配置
+ wx.mp.config-storage.http-client-type=httpclient # http客户端类型: HttpClient(默认), OkHttp, JoddHttp
+ wx.mp.config-storage.http-proxy-host=
+ wx.mp.config-storage.http-proxy-port=
+ wx.mp.config-storage.http-proxy-username=
+ wx.mp.config-storage.http-proxy-password=
+ # 公众号地址host配置
+ #wx.mp.hosts.api-host=http://proxy.com/
+ #wx.mp.hosts.open-host=http://proxy.com/
+ #wx.mp.hosts.mp-host=http://proxy.com/
+ ```
+3. 自动注入的类型
+
+- `WxMpService`
+- `WxMpConfigStorage`
+
+4、参考demo:
+https://github.com/binarywang/wx-java-mp-demo
diff --git a/solon-plugins/wx-java-mp-solon-plugin/pom.xml b/solon-plugins/wx-java-mp-solon-plugin/pom.xml
new file mode 100644
index 0000000000..16aac18a57
--- /dev/null
+++ b/solon-plugins/wx-java-mp-solon-plugin/pom.xml
@@ -0,0 +1,39 @@
+
+
+
+ wx-java-solon-plugins
+ com.github.binarywang
+ 4.6.4.B
+
+ 4.0.0
+
+ wx-java-mp-solon-plugin
+ WxJava - Solon Plugin for MP
+ 微信公众号开发的 Solon Plugin
+
+
+
+ com.github.binarywang
+ weixin-java-mp
+ ${project.version}
+
+
+ redis.clients
+ jedis
+ compile
+
+
+ org.jodd
+ jodd-http
+ provided
+
+
+ com.squareup.okhttp3
+ okhttp
+ provided
+
+
+
+
diff --git a/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/config/WxMpServiceAutoConfiguration.java b/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/config/WxMpServiceAutoConfiguration.java
new file mode 100644
index 0000000000..3e7a598494
--- /dev/null
+++ b/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/config/WxMpServiceAutoConfiguration.java
@@ -0,0 +1,63 @@
+package com.binarywang.solon.wxjava.mp.config;
+
+import com.binarywang.solon.wxjava.mp.enums.HttpClientType;
+import com.binarywang.solon.wxjava.mp.properties.WxMpProperties;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.api.impl.WxMpServiceHttpClientImpl;
+import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
+import me.chanjar.weixin.mp.api.impl.WxMpServiceJoddHttpImpl;
+import me.chanjar.weixin.mp.api.impl.WxMpServiceOkHttpImpl;
+import me.chanjar.weixin.mp.config.WxMpConfigStorage;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+
+/**
+ * 微信公众号相关服务自动注册.
+ *
+ * @author someone
+ */
+@Configuration
+public class WxMpServiceAutoConfiguration {
+
+ @Bean
+ @Condition(onMissingBean = WxMpService.class)
+ public WxMpService wxMpService(WxMpConfigStorage configStorage, WxMpProperties wxMpProperties) {
+ HttpClientType httpClientType = wxMpProperties.getConfigStorage().getHttpClientType();
+ WxMpService wxMpService;
+ switch (httpClientType) {
+ case OkHttp:
+ wxMpService = newWxMpServiceOkHttpImpl();
+ break;
+ case JoddHttp:
+ wxMpService = newWxMpServiceJoddHttpImpl();
+ break;
+ case HttpClient:
+ wxMpService = newWxMpServiceHttpClientImpl();
+ break;
+ default:
+ wxMpService = newWxMpServiceImpl();
+ break;
+ }
+
+ wxMpService.setWxMpConfigStorage(configStorage);
+ return wxMpService;
+ }
+
+ private WxMpService newWxMpServiceImpl() {
+ return new WxMpServiceImpl();
+ }
+
+ private WxMpService newWxMpServiceHttpClientImpl() {
+ return new WxMpServiceHttpClientImpl();
+ }
+
+ private WxMpService newWxMpServiceOkHttpImpl() {
+ return new WxMpServiceOkHttpImpl();
+ }
+
+ private WxMpService newWxMpServiceJoddHttpImpl() {
+ return new WxMpServiceJoddHttpImpl();
+ }
+
+}
diff --git a/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/config/WxMpStorageAutoConfiguration.java b/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/config/WxMpStorageAutoConfiguration.java
new file mode 100644
index 0000000000..ac995dd1ec
--- /dev/null
+++ b/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/config/WxMpStorageAutoConfiguration.java
@@ -0,0 +1,128 @@
+package com.binarywang.solon.wxjava.mp.config;
+
+import com.binarywang.solon.wxjava.mp.enums.StorageType;
+import com.binarywang.solon.wxjava.mp.properties.RedisProperties;
+import com.binarywang.solon.wxjava.mp.properties.WxMpProperties;
+import com.google.common.collect.Sets;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.redis.JedisWxRedisOps;
+import me.chanjar.weixin.common.redis.WxRedisOps;
+import me.chanjar.weixin.mp.config.WxMpConfigStorage;
+import me.chanjar.weixin.mp.config.WxMpHostConfig;
+import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
+import me.chanjar.weixin.mp.config.impl.WxMpRedisConfigImpl;
+import org.apache.commons.lang3.StringUtils;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+import org.noear.solon.core.AppContext;
+import redis.clients.jedis.Jedis;
+import redis.clients.jedis.JedisPool;
+import redis.clients.jedis.JedisPoolConfig;
+import redis.clients.jedis.JedisSentinelPool;
+import redis.clients.jedis.util.Pool;
+
+import java.util.Set;
+
+/**
+ * 微信公众号存储策略自动配置.
+ *
+ * @author Luo
+ */
+@Slf4j
+@Configuration
+@RequiredArgsConstructor
+public class WxMpStorageAutoConfiguration {
+ private final AppContext applicationContext;
+
+ private final WxMpProperties wxMpProperties;
+
+ @Bean
+ @Condition(onMissingBean=WxMpConfigStorage.class)
+ public WxMpConfigStorage wxMpConfigStorage() {
+ StorageType type = wxMpProperties.getConfigStorage().getType();
+ WxMpConfigStorage config;
+ switch (type) {
+ case Jedis:
+ config = jedisConfigStorage();
+ break;
+ default:
+ config = defaultConfigStorage();
+ break;
+ }
+ // wx host config
+ if (null != wxMpProperties.getHosts() && StringUtils.isNotEmpty(wxMpProperties.getHosts().getApiHost())) {
+ WxMpHostConfig hostConfig = new WxMpHostConfig();
+ hostConfig.setApiHost(wxMpProperties.getHosts().getApiHost());
+ hostConfig.setMpHost(wxMpProperties.getHosts().getMpHost());
+ hostConfig.setOpenHost(wxMpProperties.getHosts().getOpenHost());
+ config.setHostConfig(hostConfig);
+ }
+ return config;
+ }
+
+ private WxMpConfigStorage defaultConfigStorage() {
+ WxMpDefaultConfigImpl config = new WxMpDefaultConfigImpl();
+ setWxMpInfo(config);
+ return config;
+ }
+
+ private WxMpConfigStorage jedisConfigStorage() {
+ Pool jedisPool;
+ if (wxMpProperties.getConfigStorage() != null && wxMpProperties.getConfigStorage().getRedis() != null
+ && StringUtils.isNotEmpty(wxMpProperties.getConfigStorage().getRedis().getHost())) {
+ jedisPool = getJedisPool();
+ } else {
+ jedisPool = applicationContext.getBean(JedisPool.class);
+ }
+ WxRedisOps redisOps = new JedisWxRedisOps(jedisPool);
+ WxMpRedisConfigImpl wxMpRedisConfig = new WxMpRedisConfigImpl(redisOps,
+ wxMpProperties.getConfigStorage().getKeyPrefix());
+ setWxMpInfo(wxMpRedisConfig);
+ return wxMpRedisConfig;
+ }
+
+ private void setWxMpInfo(WxMpDefaultConfigImpl config) {
+ WxMpProperties properties = wxMpProperties;
+ WxMpProperties.ConfigStorage configStorageProperties = properties.getConfigStorage();
+ config.setAppId(properties.getAppId());
+ config.setSecret(properties.getSecret());
+ config.setToken(properties.getToken());
+ config.setAesKey(properties.getAesKey());
+ config.setUseStableAccessToken(wxMpProperties.isUseStableAccessToken());
+ config.setHttpProxyHost(configStorageProperties.getHttpProxyHost());
+ config.setHttpProxyUsername(configStorageProperties.getHttpProxyUsername());
+ config.setHttpProxyPassword(configStorageProperties.getHttpProxyPassword());
+ if (configStorageProperties.getHttpProxyPort() != null) {
+ config.setHttpProxyPort(configStorageProperties.getHttpProxyPort());
+ }
+ }
+
+ private Pool getJedisPool() {
+ RedisProperties redis = wxMpProperties.getConfigStorage().getRedis();
+
+ JedisPoolConfig config = new JedisPoolConfig();
+ if (redis.getMaxActive() != null) {
+ config.setMaxTotal(redis.getMaxActive());
+ }
+ if (redis.getMaxIdle() != null) {
+ config.setMaxIdle(redis.getMaxIdle());
+ }
+ if (redis.getMaxWaitMillis() != null) {
+ config.setMaxWaitMillis(redis.getMaxWaitMillis());
+ }
+ if (redis.getMinIdle() != null) {
+ config.setMinIdle(redis.getMinIdle());
+ }
+ config.setTestOnBorrow(true);
+ config.setTestWhileIdle(true);
+ if (StringUtils.isNotEmpty(redis.getSentinelIps())) {
+ Set sentinels = Sets.newHashSet(redis.getSentinelIps().split(","));
+ return new JedisSentinelPool(redis.getSentinelName(), sentinels);
+ }
+
+ return new JedisPool(config, redis.getHost(), redis.getPort(), redis.getTimeout(), redis.getPassword(),
+ redis.getDatabase());
+ }
+}
diff --git a/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/enums/HttpClientType.java b/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/enums/HttpClientType.java
new file mode 100644
index 0000000000..9b1a8ccbf4
--- /dev/null
+++ b/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/enums/HttpClientType.java
@@ -0,0 +1,22 @@
+package com.binarywang.solon.wxjava.mp.enums;
+
+/**
+ * httpclient类型.
+ *
+ * @author Binary Wang
+ * created on 2020-08-30
+ */
+public enum HttpClientType {
+ /**
+ * HttpClient.
+ */
+ HttpClient,
+ /**
+ * OkHttp.
+ */
+ OkHttp,
+ /**
+ * JoddHttp.
+ */
+ JoddHttp,
+}
diff --git a/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/enums/StorageType.java b/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/enums/StorageType.java
new file mode 100644
index 0000000000..34433a8230
--- /dev/null
+++ b/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/enums/StorageType.java
@@ -0,0 +1,26 @@
+package com.binarywang.solon.wxjava.mp.enums;
+
+/**
+ * storage类型.
+ *
+ * @author Binary Wang
+ * created on 2020-08-30
+ */
+public enum StorageType {
+ /**
+ * 内存.
+ */
+ Memory,
+ /**
+ * redis(JedisClient).
+ */
+ Jedis,
+ /**
+ * redis(Redisson).
+ */
+ Redisson,
+ /**
+ * redis(RedisTemplate).
+ */
+ RedisTemplate
+}
diff --git a/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/integration/WxMpPluginImpl.java b/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/integration/WxMpPluginImpl.java
new file mode 100644
index 0000000000..3368d34269
--- /dev/null
+++ b/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/integration/WxMpPluginImpl.java
@@ -0,0 +1,20 @@
+package com.binarywang.solon.wxjava.mp.integration;
+
+import com.binarywang.solon.wxjava.mp.config.WxMpServiceAutoConfiguration;
+import com.binarywang.solon.wxjava.mp.config.WxMpStorageAutoConfiguration;
+import com.binarywang.solon.wxjava.mp.properties.WxMpProperties;
+import org.noear.solon.core.AppContext;
+import org.noear.solon.core.Plugin;
+
+/**
+ * @author noear 2024/9/2 created
+ */
+public class WxMpPluginImpl implements Plugin {
+ @Override
+ public void start(AppContext context) throws Throwable {
+ context.beanMake(WxMpProperties.class);
+
+ context.beanMake(WxMpStorageAutoConfiguration.class);
+ context.beanMake(WxMpServiceAutoConfiguration.class);
+ }
+}
diff --git a/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/properties/HostConfig.java b/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/properties/HostConfig.java
new file mode 100644
index 0000000000..8ccedf9294
--- /dev/null
+++ b/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/properties/HostConfig.java
@@ -0,0 +1,27 @@
+package com.binarywang.solon.wxjava.mp.properties;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class HostConfig implements Serializable {
+
+ private static final long serialVersionUID = -4172767630740346001L;
+
+ /**
+ * 对应于:https://api.weixin.qq.com
+ */
+ private String apiHost;
+
+ /**
+ * 对应于:https://open.weixin.qq.com
+ */
+ private String openHost;
+
+ /**
+ * 对应于:https://mp.weixin.qq.com
+ */
+ private String mpHost;
+
+}
diff --git a/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/properties/RedisProperties.java b/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/properties/RedisProperties.java
new file mode 100644
index 0000000000..0376f947a7
--- /dev/null
+++ b/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/properties/RedisProperties.java
@@ -0,0 +1,56 @@
+package com.binarywang.solon.wxjava.mp.properties;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * redis 配置属性.
+ *
+ * @author Binary Wang
+ * created on 2020-08-30
+ */
+@Data
+public class RedisProperties implements Serializable {
+ private static final long serialVersionUID = -5924815351660074401L;
+
+ /**
+ * 主机地址.
+ */
+ private String host = "127.0.0.1";
+
+ /**
+ * 端口号.
+ */
+ private int port = 6379;
+
+ /**
+ * 密码.
+ */
+ private String password;
+
+ /**
+ * 超时.
+ */
+ private int timeout = 2000;
+
+ /**
+ * 数据库.
+ */
+ private int database = 0;
+
+ /**
+ * sentinel ips
+ */
+ private String sentinelIps;
+
+ /**
+ * sentinel name
+ */
+ private String sentinelName;
+
+ private Integer maxActive;
+ private Integer maxIdle;
+ private Integer maxWaitMillis;
+ private Integer minIdle;
+}
diff --git a/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/properties/WxMpProperties.java b/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/properties/WxMpProperties.java
new file mode 100644
index 0000000000..cda0aa88e7
--- /dev/null
+++ b/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/properties/WxMpProperties.java
@@ -0,0 +1,106 @@
+package com.binarywang.solon.wxjava.mp.properties;
+
+import com.binarywang.solon.wxjava.mp.enums.HttpClientType;
+import com.binarywang.solon.wxjava.mp.enums.StorageType;
+import lombok.Data;
+import org.noear.solon.annotation.Configuration;
+import org.noear.solon.annotation.Inject;
+
+import java.io.Serializable;
+
+import static com.binarywang.solon.wxjava.mp.enums.StorageType.Memory;
+import static com.binarywang.solon.wxjava.mp.properties.WxMpProperties.PREFIX;
+
+/**
+ * 微信接入相关配置属性.
+ *
+ * @author someone
+ */
+@Data
+@Configuration
+@Inject("${" + PREFIX + "}")
+public class WxMpProperties {
+ public static final String PREFIX = "wx.mp";
+
+ /**
+ * 设置微信公众号的appid.
+ */
+ private String appId;
+
+ /**
+ * 设置微信公众号的app secret.
+ */
+ private String secret;
+
+ /**
+ * 设置微信公众号的token.
+ */
+ private String token;
+
+ /**
+ * 设置微信公众号的EncodingAESKey.
+ */
+ private String aesKey;
+
+ /**
+ * 是否使用稳定版 Access Token
+ */
+ private boolean useStableAccessToken = false;
+
+ /**
+ * 自定义host配置
+ */
+ private HostConfig hosts;
+
+ /**
+ * 存储策略
+ */
+ private final ConfigStorage configStorage = new ConfigStorage();
+
+ @Data
+ public static class ConfigStorage implements Serializable {
+ private static final long serialVersionUID = 4815731027000065434L;
+
+ /**
+ * 存储类型.
+ */
+ private StorageType type = Memory;
+
+ /**
+ * 指定key前缀.
+ */
+ private String keyPrefix = "wx";
+
+ /**
+ * redis连接配置.
+ */
+ private final RedisProperties redis = new RedisProperties();
+
+ /**
+ * http客户端类型.
+ */
+ private HttpClientType httpClientType = HttpClientType.HttpClient;
+
+ /**
+ * http代理主机.
+ */
+ private String httpProxyHost;
+
+ /**
+ * http代理端口.
+ */
+ private Integer httpProxyPort;
+
+ /**
+ * http代理用户名.
+ */
+ private String httpProxyUsername;
+
+ /**
+ * http代理密码.
+ */
+ private String httpProxyPassword;
+
+ }
+
+}
diff --git a/solon-plugins/wx-java-mp-solon-plugin/src/main/resources/META-INF/solon/wx-java-mp-solon-plugin.properties b/solon-plugins/wx-java-mp-solon-plugin/src/main/resources/META-INF/solon/wx-java-mp-solon-plugin.properties
new file mode 100644
index 0000000000..c80357184c
--- /dev/null
+++ b/solon-plugins/wx-java-mp-solon-plugin/src/main/resources/META-INF/solon/wx-java-mp-solon-plugin.properties
@@ -0,0 +1,2 @@
+solon.plugin=com.binarywang.solon.wxjava.mp.integration.WxMpPluginImpl
+solon.plugin.priority=10
diff --git a/solon-plugins/wx-java-open-solon-plugin/README.md b/solon-plugins/wx-java-open-solon-plugin/README.md
new file mode 100644
index 0000000000..619e28dbdd
--- /dev/null
+++ b/solon-plugins/wx-java-open-solon-plugin/README.md
@@ -0,0 +1,39 @@
+# wx-java-open-solon-plugin
+## 快速开始
+1. 引入依赖
+ ```xml
+
+ com.github.binarywang
+ wx-java-open-solon-plugin
+ ${version}
+
+ ```
+2. 添加配置(app.properties)
+ ```properties
+ # 公众号配置(必填)
+ wx.open.appId = appId
+ wx.open.secret = @secret
+ wx.open.token = @token
+ wx.open.aesKey = @aesKey
+ # 存储配置redis(可选)
+ # 优先注入容器的(JedisPool, RedissonClient), 当配置了wx.open.config-storage.redis.host, 不会使用容器注入redis连接配置
+ wx.open.config-storage.type = redis # 配置类型: memory(默认), redis(jedis), jedis, redisson, redistemplate
+ wx.open.config-storage.key-prefix = wx # 相关redis前缀配置: wx(默认)
+ wx.open.config-storage.redis.host = 127.0.0.1
+ wx.open.config-storage.redis.port = 6379
+ # http客户端配置
+ wx.open.config-storage.http-client-type=httpclient # http客户端类型: httpclient(默认)
+ wx.open.config-storage.http-proxy-host=
+ wx.open.config-storage.http-proxy-port=
+ wx.open.config-storage.http-proxy-username=
+ wx.open.config-storage.http-proxy-password=
+ # 最大重试次数,默认:5 次,如果小于 0,则为 0
+ wx.open.config-storage.max-retry-times=5
+ # 重试时间间隔步进,默认:1000 毫秒,如果小于 0,则为 1000
+ wx.open.config-storage.retry-sleep-millis=1000
+ ```
+3. 支持自动注入的类型: `WxOpenService, WxOpenMessageRouter, WxOpenComponentService`
+
+4. 覆盖自动配置: 自定义注入的bean会覆盖自动注入的
+ - WxOpenConfigStorage
+ - WxOpenService
diff --git a/solon-plugins/wx-java-open-solon-plugin/pom.xml b/solon-plugins/wx-java-open-solon-plugin/pom.xml
new file mode 100644
index 0000000000..00fce6281e
--- /dev/null
+++ b/solon-plugins/wx-java-open-solon-plugin/pom.xml
@@ -0,0 +1,32 @@
+
+
+
+ wx-java-solon-plugins
+ com.github.binarywang
+ 4.6.4.B
+
+ 4.0.0
+
+ wx-java-open-solon-plugin
+ WxJava - Solon Plugin for WxOpen
+ 微信开放平台开发的 Solon Plugin
+
+
+
+ com.github.binarywang
+ weixin-java-open
+ ${project.version}
+
+
+ redis.clients
+ jedis
+
+
+ org.redisson
+ redisson
+
+
+
+
diff --git a/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/config/WxOpenServiceAutoConfiguration.java b/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/config/WxOpenServiceAutoConfiguration.java
new file mode 100644
index 0000000000..7bda6816ed
--- /dev/null
+++ b/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/config/WxOpenServiceAutoConfiguration.java
@@ -0,0 +1,37 @@
+package com.binarywang.solon.wxjava.open.config;
+
+import me.chanjar.weixin.open.api.WxOpenComponentService;
+import me.chanjar.weixin.open.api.WxOpenConfigStorage;
+import me.chanjar.weixin.open.api.WxOpenService;
+import me.chanjar.weixin.open.api.impl.WxOpenMessageRouter;
+import me.chanjar.weixin.open.api.impl.WxOpenServiceImpl;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+
+/**
+ * 微信开放平台相关服务自动注册.
+ *
+ * @author someone
+ */
+@Configuration
+public class WxOpenServiceAutoConfiguration {
+
+ @Bean
+ @Condition(onMissingBean = WxOpenService.class, onBean = WxOpenConfigStorage.class)
+ public WxOpenService wxOpenService(WxOpenConfigStorage wxOpenConfigStorage) {
+ WxOpenService wxOpenService = new WxOpenServiceImpl();
+ wxOpenService.setWxOpenConfigStorage(wxOpenConfigStorage);
+ return wxOpenService;
+ }
+
+ @Bean
+ public WxOpenMessageRouter wxOpenMessageRouter(WxOpenService wxOpenService) {
+ return new WxOpenMessageRouter(wxOpenService);
+ }
+
+ @Bean
+ public WxOpenComponentService wxOpenComponentService(WxOpenService wxOpenService) {
+ return wxOpenService.getWxOpenComponentService();
+ }
+}
diff --git a/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/config/storage/AbstractWxOpenConfigStorageConfiguration.java b/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/config/storage/AbstractWxOpenConfigStorageConfiguration.java
new file mode 100644
index 0000000000..4a65b311d9
--- /dev/null
+++ b/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/config/storage/AbstractWxOpenConfigStorageConfiguration.java
@@ -0,0 +1,33 @@
+package com.binarywang.solon.wxjava.open.config.storage;
+
+import com.binarywang.solon.wxjava.open.properties.WxOpenProperties;
+import me.chanjar.weixin.open.api.impl.WxOpenInMemoryConfigStorage;
+
+/**
+ * @author yl
+ */
+public abstract class AbstractWxOpenConfigStorageConfiguration {
+
+ protected WxOpenInMemoryConfigStorage config(WxOpenInMemoryConfigStorage config, WxOpenProperties properties) {
+ WxOpenProperties.ConfigStorage storage = properties.getConfigStorage();
+ config.setWxOpenInfo(properties.getAppId(), properties.getSecret(), properties.getToken(), properties.getAesKey());
+ config.setHttpProxyHost(storage.getHttpProxyHost());
+ config.setHttpProxyUsername(storage.getHttpProxyUsername());
+ config.setHttpProxyPassword(storage.getHttpProxyPassword());
+ Integer httpProxyPort = storage.getHttpProxyPort();
+ if (httpProxyPort != null) {
+ config.setHttpProxyPort(httpProxyPort);
+ }
+ int maxRetryTimes = storage.getMaxRetryTimes();
+ if (maxRetryTimes < 0) {
+ maxRetryTimes = 0;
+ }
+ int retrySleepMillis = storage.getRetrySleepMillis();
+ if (retrySleepMillis < 0) {
+ retrySleepMillis = 1000;
+ }
+ config.setRetrySleepMillis(retrySleepMillis);
+ config.setMaxRetryTimes(maxRetryTimes);
+ return config;
+ }
+}
diff --git a/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/config/storage/WxOpenInJedisConfigStorageConfiguration.java b/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/config/storage/WxOpenInJedisConfigStorageConfiguration.java
new file mode 100644
index 0000000000..59e65ef48c
--- /dev/null
+++ b/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/config/storage/WxOpenInJedisConfigStorageConfiguration.java
@@ -0,0 +1,71 @@
+package com.binarywang.solon.wxjava.open.config.storage;
+
+import com.binarywang.solon.wxjava.open.properties.WxOpenProperties;
+import com.binarywang.solon.wxjava.open.properties.WxOpenRedisProperties;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.open.api.WxOpenConfigStorage;
+import me.chanjar.weixin.open.api.impl.WxOpenInMemoryConfigStorage;
+import me.chanjar.weixin.open.api.impl.WxOpenInRedisConfigStorage;
+import org.apache.commons.lang3.StringUtils;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+import org.noear.solon.core.AppContext;
+import redis.clients.jedis.JedisPool;
+import redis.clients.jedis.JedisPoolConfig;
+
+/**
+ * @author yl
+ */
+@Configuration
+@Condition(
+ onProperty = "${"+WxOpenProperties.PREFIX + ".configStorage.type} = jedis",
+ onClass = JedisPool.class
+)
+@RequiredArgsConstructor
+public class WxOpenInJedisConfigStorageConfiguration extends AbstractWxOpenConfigStorageConfiguration {
+ private final WxOpenProperties properties;
+ private final AppContext applicationContext;
+
+ @Bean
+ @Condition(onMissingBean=WxOpenConfigStorage.class)
+ public WxOpenConfigStorage wxOpenConfigStorage() {
+ WxOpenInMemoryConfigStorage config = getWxOpenInRedisConfigStorage();
+ return this.config(config, properties);
+ }
+
+ private WxOpenInRedisConfigStorage getWxOpenInRedisConfigStorage() {
+ WxOpenRedisProperties wxOpenRedisProperties = properties.getConfigStorage().getRedis();
+ JedisPool jedisPool;
+ if (wxOpenRedisProperties != null && StringUtils.isNotEmpty(wxOpenRedisProperties.getHost())) {
+ jedisPool = getJedisPool();
+ } else {
+ jedisPool = applicationContext.getBean(JedisPool.class);
+ }
+ return new WxOpenInRedisConfigStorage(jedisPool, properties.getConfigStorage().getKeyPrefix());
+ }
+
+ private JedisPool getJedisPool() {
+ WxOpenProperties.ConfigStorage storage = properties.getConfigStorage();
+ WxOpenRedisProperties redis = storage.getRedis();
+
+ JedisPoolConfig config = new JedisPoolConfig();
+ if (redis.getMaxActive() != null) {
+ config.setMaxTotal(redis.getMaxActive());
+ }
+ if (redis.getMaxIdle() != null) {
+ config.setMaxIdle(redis.getMaxIdle());
+ }
+ if (redis.getMaxWaitMillis() != null) {
+ config.setMaxWaitMillis(redis.getMaxWaitMillis());
+ }
+ if (redis.getMinIdle() != null) {
+ config.setMinIdle(redis.getMinIdle());
+ }
+ config.setTestOnBorrow(true);
+ config.setTestWhileIdle(true);
+
+ return new JedisPool(config, redis.getHost(), redis.getPort(),
+ redis.getTimeout(), redis.getPassword(), redis.getDatabase());
+ }
+}
diff --git a/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/config/storage/WxOpenInMemoryConfigStorageConfiguration.java b/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/config/storage/WxOpenInMemoryConfigStorageConfiguration.java
new file mode 100644
index 0000000000..756b6525fc
--- /dev/null
+++ b/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/config/storage/WxOpenInMemoryConfigStorageConfiguration.java
@@ -0,0 +1,28 @@
+package com.binarywang.solon.wxjava.open.config.storage;
+
+import com.binarywang.solon.wxjava.open.properties.WxOpenProperties;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.open.api.WxOpenConfigStorage;
+import me.chanjar.weixin.open.api.impl.WxOpenInMemoryConfigStorage;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+
+/**
+ * @author yl
+ */
+@Configuration
+@Condition(
+ onProperty = "${"+WxOpenProperties.PREFIX + ".configStorage.type:memory} = memory"
+)
+@RequiredArgsConstructor
+public class WxOpenInMemoryConfigStorageConfiguration extends AbstractWxOpenConfigStorageConfiguration {
+ private final WxOpenProperties properties;
+
+ @Bean
+ @Condition(onMissingBean=WxOpenConfigStorage.class)
+ public WxOpenConfigStorage wxOpenConfigStorage() {
+ WxOpenInMemoryConfigStorage config = new WxOpenInMemoryConfigStorage();
+ return this.config(config, properties);
+ }
+}
diff --git a/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/config/storage/WxOpenInRedissonConfigStorageConfiguration.java b/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/config/storage/WxOpenInRedissonConfigStorageConfiguration.java
new file mode 100644
index 0000000000..70844e2888
--- /dev/null
+++ b/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/config/storage/WxOpenInRedissonConfigStorageConfiguration.java
@@ -0,0 +1,62 @@
+package com.binarywang.solon.wxjava.open.config.storage;
+
+import com.binarywang.solon.wxjava.open.properties.WxOpenProperties;
+import com.binarywang.solon.wxjava.open.properties.WxOpenRedisProperties;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.open.api.WxOpenConfigStorage;
+import me.chanjar.weixin.open.api.impl.WxOpenInMemoryConfigStorage;
+import me.chanjar.weixin.open.api.impl.WxOpenInRedissonConfigStorage;
+import org.apache.commons.lang3.StringUtils;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+import org.noear.solon.core.AppContext;
+import org.redisson.Redisson;
+import org.redisson.api.RedissonClient;
+import org.redisson.config.Config;
+import org.redisson.config.TransportMode;
+
+/**
+ * @author yl
+ */
+@Configuration
+@Condition(
+ onProperty = "${"+WxOpenProperties.PREFIX + ".configStorage.type} = redisson",
+ onClass = Redisson.class
+)
+@RequiredArgsConstructor
+public class WxOpenInRedissonConfigStorageConfiguration extends AbstractWxOpenConfigStorageConfiguration {
+ private final WxOpenProperties properties;
+ private final AppContext applicationContext;
+
+ @Bean
+ @Condition(onMissingBean=WxOpenConfigStorage.class)
+ public WxOpenConfigStorage wxOpenConfigStorage() {
+ WxOpenInMemoryConfigStorage config = getWxOpenInRedissonConfigStorage();
+ return this.config(config, properties);
+ }
+
+ private WxOpenInRedissonConfigStorage getWxOpenInRedissonConfigStorage() {
+ WxOpenRedisProperties wxOpenRedisProperties = properties.getConfigStorage().getRedis();
+ RedissonClient redissonClient;
+ if (wxOpenRedisProperties != null && StringUtils.isNotEmpty(wxOpenRedisProperties.getHost())) {
+ redissonClient = getRedissonClient();
+ } else {
+ redissonClient = applicationContext.getBean(RedissonClient.class);
+ }
+ return new WxOpenInRedissonConfigStorage(redissonClient, properties.getConfigStorage().getKeyPrefix());
+ }
+
+ private RedissonClient getRedissonClient() {
+ WxOpenProperties.ConfigStorage storage = properties.getConfigStorage();
+ WxOpenRedisProperties redis = storage.getRedis();
+
+ Config config = new Config();
+ config.useSingleServer()
+ .setAddress("redis://" + redis.getHost() + ":" + redis.getPort())
+ .setDatabase(redis.getDatabase())
+ .setPassword(redis.getPassword());
+ config.setTransportMode(TransportMode.NIO);
+ return Redisson.create(config);
+ }
+}
diff --git a/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/integration/WxOpenPluginImpl.java b/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/integration/WxOpenPluginImpl.java
new file mode 100644
index 0000000000..29352d81f0
--- /dev/null
+++ b/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/integration/WxOpenPluginImpl.java
@@ -0,0 +1,25 @@
+package com.binarywang.solon.wxjava.open.integration;
+
+import com.binarywang.solon.wxjava.open.config.WxOpenServiceAutoConfiguration;
+import com.binarywang.solon.wxjava.open.config.storage.WxOpenInJedisConfigStorageConfiguration;
+import com.binarywang.solon.wxjava.open.config.storage.WxOpenInMemoryConfigStorageConfiguration;
+import com.binarywang.solon.wxjava.open.config.storage.WxOpenInRedissonConfigStorageConfiguration;
+import com.binarywang.solon.wxjava.open.properties.WxOpenProperties;
+import org.noear.solon.core.AppContext;
+import org.noear.solon.core.Plugin;
+
+/**
+ * @author noear 2024/9/2 created
+ */
+public class WxOpenPluginImpl implements Plugin {
+ @Override
+ public void start(AppContext context) throws Throwable {
+ context.beanMake(WxOpenProperties.class);
+
+ context.beanMake(WxOpenServiceAutoConfiguration.class);
+
+ context.beanMake(WxOpenInMemoryConfigStorageConfiguration.class);
+ context.beanMake(WxOpenInJedisConfigStorageConfiguration.class);
+ context.beanMake(WxOpenInRedissonConfigStorageConfiguration.class);
+ }
+}
diff --git a/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/properties/WxOpenProperties.java b/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/properties/WxOpenProperties.java
new file mode 100644
index 0000000000..4ec34c02b8
--- /dev/null
+++ b/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/properties/WxOpenProperties.java
@@ -0,0 +1,138 @@
+package com.binarywang.solon.wxjava.open.properties;
+
+import lombok.Data;
+import org.noear.solon.annotation.Configuration;
+import org.noear.solon.annotation.Inject;
+
+import java.io.Serializable;
+
+import static com.binarywang.solon.wxjava.open.properties.WxOpenProperties.PREFIX;
+import static com.binarywang.solon.wxjava.open.properties.WxOpenProperties.StorageType.memory;
+
+
+/**
+ * 微信接入相关配置属性.
+ *
+ * @author someone
+ */
+@Data
+@Configuration
+@Inject("${"+PREFIX+"}")
+public class WxOpenProperties {
+ public static final String PREFIX = "wx.open";
+
+ /**
+ * 设置微信开放平台的appid.
+ */
+ private String appId;
+
+ /**
+ * 设置微信开放平台的app secret.
+ */
+ private String secret;
+
+ /**
+ * 设置微信开放平台的token.
+ */
+ private String token;
+
+ /**
+ * 设置微信开放平台的EncodingAESKey.
+ */
+ private String aesKey;
+
+ /**
+ * 存储策略.
+ */
+ private ConfigStorage configStorage = new ConfigStorage();
+
+
+ @Data
+ public static class ConfigStorage implements Serializable {
+ private static final long serialVersionUID = 4815731027000065434L;
+
+ /**
+ * 存储类型.
+ */
+ private StorageType type = memory;
+
+ /**
+ * 指定key前缀.
+ */
+ private String keyPrefix = "wx:open";
+
+ /**
+ * redis连接配置.
+ */
+ private WxOpenRedisProperties redis = new WxOpenRedisProperties();
+
+ /**
+ * http客户端类型.
+ */
+ private HttpClientType httpClientType = HttpClientType.httpclient;
+
+ /**
+ * http代理主机.
+ */
+ private String httpProxyHost;
+
+ /**
+ * http代理端口.
+ */
+ private Integer httpProxyPort;
+
+ /**
+ * http代理用户名.
+ */
+ private String httpProxyUsername;
+
+ /**
+ * http代理密码.
+ */
+ private String httpProxyPassword;
+
+ /**
+ * http 请求重试间隔
+ *
+ * {@link me.chanjar.weixin.mp.api.impl.BaseWxMpServiceImpl#setRetrySleepMillis(int)}
+ * {@link cn.binarywang.wx.miniapp.api.impl.BaseWxMaServiceImpl#setRetrySleepMillis(int)}
+ *
+ */
+ private int retrySleepMillis = 1000;
+ /**
+ * http 请求最大重试次数
+ *
+ * {@link me.chanjar.weixin.mp.api.impl.BaseWxMpServiceImpl#setMaxRetryTimes(int)}
+ * {@link cn.binarywang.wx.miniapp.api.impl.BaseWxMaServiceImpl#setMaxRetryTimes(int)}
+ *
+ */
+ private int maxRetryTimes = 5;
+
+ }
+
+ public enum StorageType {
+ /**
+ * 内存.
+ */
+ memory,
+ /**
+ * jedis.
+ */
+ jedis,
+ /**
+ * redisson.
+ */
+ redisson,
+ /**
+ * redistemplate
+ */
+ redistemplate
+ }
+
+ public enum HttpClientType {
+ /**
+ * HttpClient.
+ */
+ httpclient
+ }
+}
diff --git a/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/properties/WxOpenRedisProperties.java b/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/properties/WxOpenRedisProperties.java
new file mode 100644
index 0000000000..6b7a2d8654
--- /dev/null
+++ b/solon-plugins/wx-java-open-solon-plugin/src/main/java/com/binarywang/solon/wxjava/open/properties/WxOpenRedisProperties.java
@@ -0,0 +1,45 @@
+package com.binarywang.solon.wxjava.open.properties;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * Redis配置.
+ *
+ * @author someone
+ */
+@Data
+public class WxOpenRedisProperties implements Serializable {
+ private static final long serialVersionUID = -5924815351660074401L;
+
+ /**
+ * 主机地址.
+ */
+ private String host;
+
+ /**
+ * 端口号.
+ */
+ private int port = 6379;
+
+ /**
+ * 密码.
+ */
+ private String password;
+
+ /**
+ * 超时.
+ */
+ private int timeout = 2000;
+
+ /**
+ * 数据库.
+ */
+ private int database = 0;
+
+ private Integer maxActive;
+ private Integer maxIdle;
+ private Integer maxWaitMillis;
+ private Integer minIdle;
+}
diff --git a/solon-plugins/wx-java-open-solon-plugin/src/main/resources/META-INF/solon/wx-java-open-solon-plugin.properties b/solon-plugins/wx-java-open-solon-plugin/src/main/resources/META-INF/solon/wx-java-open-solon-plugin.properties
new file mode 100644
index 0000000000..289aca5eeb
--- /dev/null
+++ b/solon-plugins/wx-java-open-solon-plugin/src/main/resources/META-INF/solon/wx-java-open-solon-plugin.properties
@@ -0,0 +1,2 @@
+solon.plugin=com.binarywang.solon.wxjava.open.integration.WxOpenPluginImpl
+solon.plugin.priority=10
diff --git a/solon-plugins/wx-java-pay-solon-plugin/README.md b/solon-plugins/wx-java-pay-solon-plugin/README.md
new file mode 100644
index 0000000000..b0e212593b
--- /dev/null
+++ b/solon-plugins/wx-java-pay-solon-plugin/README.md
@@ -0,0 +1,43 @@
+# 使用说明
+1. 在自己的Solon项目里,引入maven依赖
+```xml
+
+ com.github.binarywang
+ wx-java-pay-solon-plugin
+ ${version}
+
+ ```
+2. 添加配置(app.yml)
+###### 1)V2版本
+```yml
+wx:
+ pay:
+ appId:
+ mchId:
+ mchKey:
+ keyPath:
+```
+###### 2)V3版本
+```yml
+wx:
+ pay:
+ appId: xxxxxxxxxxx
+ mchId: 15xxxxxxxxx #商户id
+ apiV3Key: Dc1DBwSc094jACxxxxxxxxxxxxxxx #V3密钥
+ certSerialNo: 62C6CEAA360BCxxxxxxxxxxxxxxx
+ privateKeyPath: classpath:cert/apiclient_key.pem #apiclient_key.pem证书文件的绝对路径或者以classpath:开头的类路径
+ privateCertPath: classpath:cert/apiclient_cert.pem #apiclient_cert.pem证书文件的绝对路径或者以classpath:开头的类路径
+```
+###### 3)V3服务商版本
+```yml
+wx:
+ pay: #微信服务商支付
+ configs:
+ - appId: wxe97b2x9c2b3d #spAppId
+ mchId: 16486610 #服务商商户
+ subAppId: wx118cexxe3c07679 #子appId
+ subMchId: 16496705 #子商户
+ apiV3Key: Dc1DBwSc094jAKDGR5aqqb7PTHr #apiV3密钥
+ privateKeyPath: classpath:cert/apiclient_key.pem #服务商证书文件,apiclient_key.pem证书文件的绝对路径或者以classpath:开头的类路径(可以配置绝对路径)
+ privateCertPath: classpath:cert/apiclient_cert.pem #apiclient_cert.pem证书文件的绝对路径或者以classpath:开头的类路径
+```
diff --git a/solon-plugins/wx-java-pay-solon-plugin/pom.xml b/solon-plugins/wx-java-pay-solon-plugin/pom.xml
new file mode 100644
index 0000000000..9805e1d174
--- /dev/null
+++ b/solon-plugins/wx-java-pay-solon-plugin/pom.xml
@@ -0,0 +1,24 @@
+
+
+
+ wx-java-solon-plugins
+ com.github.binarywang
+ 4.6.4.B
+
+ 4.0.0
+
+ wx-java-pay-solon-plugin
+ WxJava - Solon Plugin for WxPay
+ 微信支付开发的 Solon Plugin
+
+
+
+ com.github.binarywang
+ weixin-java-pay
+ ${project.version}
+
+
+
+
diff --git a/solon-plugins/wx-java-pay-solon-plugin/src/main/java/com/binarywang/solon/wxjava/pay/config/WxPayAutoConfiguration.java b/solon-plugins/wx-java-pay-solon-plugin/src/main/java/com/binarywang/solon/wxjava/pay/config/WxPayAutoConfiguration.java
new file mode 100644
index 0000000000..1957e4157e
--- /dev/null
+++ b/solon-plugins/wx-java-pay-solon-plugin/src/main/java/com/binarywang/solon/wxjava/pay/config/WxPayAutoConfiguration.java
@@ -0,0 +1,61 @@
+package com.binarywang.solon.wxjava.pay.config;
+
+import com.binarywang.solon.wxjava.pay.properties.WxPayProperties;
+import com.github.binarywang.wxpay.config.WxPayConfig;
+import com.github.binarywang.wxpay.service.WxPayService;
+import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
+import org.apache.commons.lang3.StringUtils;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+
+/**
+ *
+ * 微信支付自动配置
+ * Created by BinaryWang on 2019/4/17.
+ *
+ *
+ * @author Binary Wang
+ */
+@Configuration
+@Condition(
+ onProperty = "${wx.pay.enabled:true} = true",
+ onClass=WxPayService.class
+)
+public class WxPayAutoConfiguration {
+ private WxPayProperties properties;
+
+ public WxPayAutoConfiguration(WxPayProperties properties) {
+ this.properties = properties;
+ }
+
+ /**
+ * 构造微信支付服务对象.
+ *
+ * @return 微信支付service
+ */
+ @Bean
+ @Condition(onMissingBean=WxPayService.class)
+ public WxPayService wxPayService() {
+ final WxPayServiceImpl wxPayService = new WxPayServiceImpl();
+ WxPayConfig payConfig = new WxPayConfig();
+ payConfig.setAppId(StringUtils.trimToNull(this.properties.getAppId()));
+ payConfig.setMchId(StringUtils.trimToNull(this.properties.getMchId()));
+ payConfig.setMchKey(StringUtils.trimToNull(this.properties.getMchKey()));
+ payConfig.setSubAppId(StringUtils.trimToNull(this.properties.getSubAppId()));
+ payConfig.setSubMchId(StringUtils.trimToNull(this.properties.getSubMchId()));
+ payConfig.setKeyPath(StringUtils.trimToNull(this.properties.getKeyPath()));
+ payConfig.setUseSandboxEnv(this.properties.isUseSandboxEnv());
+ //以下是apiv3以及支付分相关
+ payConfig.setServiceId(StringUtils.trimToNull(this.properties.getServiceId()));
+ payConfig.setPayScoreNotifyUrl(StringUtils.trimToNull(this.properties.getPayScoreNotifyUrl()));
+ payConfig.setPrivateKeyPath(StringUtils.trimToNull(this.properties.getPrivateKeyPath()));
+ payConfig.setPrivateCertPath(StringUtils.trimToNull(this.properties.getPrivateCertPath()));
+ payConfig.setCertSerialNo(StringUtils.trimToNull(this.properties.getCertSerialNo()));
+ payConfig.setApiV3Key(StringUtils.trimToNull(this.properties.getApiv3Key()));
+
+ wxPayService.setConfig(payConfig);
+ return wxPayService;
+ }
+
+}
diff --git a/solon-plugins/wx-java-pay-solon-plugin/src/main/java/com/binarywang/solon/wxjava/pay/integration/WxPayPluginImpl.java b/solon-plugins/wx-java-pay-solon-plugin/src/main/java/com/binarywang/solon/wxjava/pay/integration/WxPayPluginImpl.java
new file mode 100644
index 0000000000..e7ba275ca0
--- /dev/null
+++ b/solon-plugins/wx-java-pay-solon-plugin/src/main/java/com/binarywang/solon/wxjava/pay/integration/WxPayPluginImpl.java
@@ -0,0 +1,18 @@
+package com.binarywang.solon.wxjava.pay.integration;
+
+import com.binarywang.solon.wxjava.pay.config.WxPayAutoConfiguration;
+import com.binarywang.solon.wxjava.pay.properties.WxPayProperties;
+import org.noear.solon.core.AppContext;
+import org.noear.solon.core.Plugin;
+
+/**
+ * @author noear 2024/9/2 created
+ */
+public class WxPayPluginImpl implements Plugin {
+ @Override
+ public void start(AppContext context) throws Throwable {
+ context.beanMake(WxPayProperties.class);
+
+ context.beanMake(WxPayAutoConfiguration.class);
+ }
+}
diff --git a/solon-plugins/wx-java-pay-solon-plugin/src/main/java/com/binarywang/solon/wxjava/pay/properties/WxPayProperties.java b/solon-plugins/wx-java-pay-solon-plugin/src/main/java/com/binarywang/solon/wxjava/pay/properties/WxPayProperties.java
new file mode 100644
index 0000000000..a882f6ac31
--- /dev/null
+++ b/solon-plugins/wx-java-pay-solon-plugin/src/main/java/com/binarywang/solon/wxjava/pay/properties/WxPayProperties.java
@@ -0,0 +1,85 @@
+package com.binarywang.solon.wxjava.pay.properties;
+
+import lombok.Data;
+import org.noear.solon.annotation.Configuration;
+import org.noear.solon.annotation.Inject;
+
+/**
+ *
+ * 微信支付属性配置类
+ * Created by Binary Wang on 2019/4/17.
+ *
+ *
+ * @author Binary Wang
+ */
+@Data
+@Configuration
+@Inject("${wx.pay}")
+public class WxPayProperties {
+ /**
+ * 设置微信公众号或者小程序等的appid.
+ */
+ private String appId;
+
+ /**
+ * 微信支付商户号.
+ */
+ private String mchId;
+
+ /**
+ * 微信支付商户密钥.
+ */
+ private String mchKey;
+
+ /**
+ * 服务商模式下的子商户公众账号ID,普通模式请不要配置,请在配置文件中将对应项删除.
+ */
+ private String subAppId;
+
+ /**
+ * 服务商模式下的子商户号,普通模式请不要配置,最好是请在配置文件中将对应项删除.
+ */
+ private String subMchId;
+
+ /**
+ * apiclient_cert.p12文件的绝对路径,或者如果放在项目中,请以classpath:开头指定.
+ */
+ private String keyPath;
+
+ /**
+ * 微信支付分serviceId
+ */
+ private String serviceId;
+
+ /**
+ * 证书序列号
+ */
+ private String certSerialNo;
+
+ /**
+ * apiV3秘钥
+ */
+ private String apiv3Key;
+
+ /**
+ * 微信支付分回调地址
+ */
+ private String payScoreNotifyUrl;
+
+ /**
+ * apiv3 商户apiclient_key.pem
+ */
+ private String privateKeyPath;
+
+ /**
+ * apiv3 商户apiclient_cert.pem
+ */
+ private String privateCertPath;
+
+ /**
+ * 微信支付是否使用仿真测试环境.
+ * 默认不使用
+ */
+ private boolean useSandboxEnv;
+
+}
diff --git a/solon-plugins/wx-java-pay-solon-plugin/src/main/resources/META-INF/solon/wx-java-pay-solon-plugin.properties b/solon-plugins/wx-java-pay-solon-plugin/src/main/resources/META-INF/solon/wx-java-pay-solon-plugin.properties
new file mode 100644
index 0000000000..98783176e2
--- /dev/null
+++ b/solon-plugins/wx-java-pay-solon-plugin/src/main/resources/META-INF/solon/wx-java-pay-solon-plugin.properties
@@ -0,0 +1,2 @@
+solon.plugin=com.binarywang.solon.wxjava.pay.integration.WxPayPluginImpl
+solon.plugin.priority=10
diff --git a/solon-plugins/wx-java-qidian-solon-plugin/README.md b/solon-plugins/wx-java-qidian-solon-plugin/README.md
new file mode 100644
index 0000000000..a409113c8c
--- /dev/null
+++ b/solon-plugins/wx-java-qidian-solon-plugin/README.md
@@ -0,0 +1,45 @@
+# wx-java-qidian-solon-plugin
+
+## 快速开始
+
+1. 引入依赖
+ ```xml
+
+ com.github.binarywang
+ wx-java-qidian-solon-plugin
+ ${version}
+
+ ```
+2. 添加配置(app.properties)
+ ```properties
+ # 公众号配置(必填)
+ wx.mp.appId = appId
+ wx.mp.secret = @secret
+ wx.mp.token = @token
+ wx.mp.aesKey = @aesKey
+ # 存储配置redis(可选)
+ wx.mp.config-storage.type = Jedis # 配置类型: Memory(默认), Jedis, RedisTemplate
+ wx.mp.config-storage.key-prefix = wx # 相关redis前缀配置: wx(默认)
+ wx.mp.config-storage.redis.host = 127.0.0.1
+ wx.mp.config-storage.redis.port = 6379
+ #单机和sentinel同时存在时,优先使用sentinel配置
+ #wx.mp.config-storage.redis.sentinel-ips=127.0.0.1:16379,127.0.0.1:26379
+ #wx.mp.config-storage.redis.sentinel-name=mymaster
+ # http客户端配置
+ wx.mp.config-storage.http-client-type=httpclient # http客户端类型: HttpClient(默认), OkHttp, JoddHttp
+ wx.mp.config-storage.http-proxy-host=
+ wx.mp.config-storage.http-proxy-port=
+ wx.mp.config-storage.http-proxy-username=
+ wx.mp.config-storage.http-proxy-password=
+ # 公众号地址host配置
+ #wx.mp.hosts.api-host=http://proxy.com/
+ #wx.mp.hosts.open-host=http://proxy.com/
+ #wx.mp.hosts.mp-host=http://proxy.com/
+ ```
+3. 自动注入的类型
+
+- `WxMpService`
+- `WxMpConfigStorage`
+
+4、参考 demo:
+https://github.com/binarywang/wx-java-mp-demo
diff --git a/solon-plugins/wx-java-qidian-solon-plugin/pom.xml b/solon-plugins/wx-java-qidian-solon-plugin/pom.xml
new file mode 100644
index 0000000000..c1e31e5839
--- /dev/null
+++ b/solon-plugins/wx-java-qidian-solon-plugin/pom.xml
@@ -0,0 +1,38 @@
+
+
+
+ wx-java-solon-plugins
+ com.github.binarywang
+ 4.6.4.B
+
+ 4.0.0
+
+ wx-java-qidian-solon-plugin
+ WxJava - Solon Plugin for QiDian
+ 腾讯企点的 Solon Plugin
+
+
+
+ com.github.binarywang
+ weixin-java-qidian
+ ${project.version}
+
+
+ redis.clients
+ jedis
+ 4.3.2
+ compile
+
+
+ org.jodd
+ jodd-http
+ provided
+
+
+ com.squareup.okhttp3
+ okhttp
+ provided
+
+
+
+
diff --git a/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/config/WxQidianServiceAutoConfiguration.java b/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/config/WxQidianServiceAutoConfiguration.java
new file mode 100644
index 0000000000..f3dce59a73
--- /dev/null
+++ b/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/config/WxQidianServiceAutoConfiguration.java
@@ -0,0 +1,63 @@
+package com.binarywang.solon.wxjava.qidian.config;
+
+import com.binarywang.solon.wxjava.qidian.enums.HttpClientType;
+import com.binarywang.solon.wxjava.qidian.properties.WxQidianProperties;
+import me.chanjar.weixin.qidian.api.WxQidianService;
+import me.chanjar.weixin.qidian.api.impl.WxQidianServiceHttpClientImpl;
+import me.chanjar.weixin.qidian.api.impl.WxQidianServiceImpl;
+import me.chanjar.weixin.qidian.api.impl.WxQidianServiceJoddHttpImpl;
+import me.chanjar.weixin.qidian.api.impl.WxQidianServiceOkHttpImpl;
+import me.chanjar.weixin.qidian.config.WxQidianConfigStorage;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+
+/**
+ * 腾讯企点相关服务自动注册.
+ *
+ * @author alegria
+ */
+@Configuration
+public class WxQidianServiceAutoConfiguration {
+
+ @Bean
+ @Condition(onMissingBean = WxQidianService.class)
+ public WxQidianService wxQidianService(WxQidianConfigStorage configStorage, WxQidianProperties wxQidianProperties) {
+ HttpClientType httpClientType = wxQidianProperties.getConfigStorage().getHttpClientType();
+ WxQidianService wxQidianService;
+ switch (httpClientType) {
+ case OkHttp:
+ wxQidianService = newWxQidianServiceOkHttpImpl();
+ break;
+ case JoddHttp:
+ wxQidianService = newWxQidianServiceJoddHttpImpl();
+ break;
+ case HttpClient:
+ wxQidianService = newWxQidianServiceHttpClientImpl();
+ break;
+ default:
+ wxQidianService = newWxQidianServiceImpl();
+ break;
+ }
+
+ wxQidianService.setWxMpConfigStorage(configStorage);
+ return wxQidianService;
+ }
+
+ private WxQidianService newWxQidianServiceImpl() {
+ return new WxQidianServiceImpl();
+ }
+
+ private WxQidianService newWxQidianServiceHttpClientImpl() {
+ return new WxQidianServiceHttpClientImpl();
+ }
+
+ private WxQidianService newWxQidianServiceOkHttpImpl() {
+ return new WxQidianServiceOkHttpImpl();
+ }
+
+ private WxQidianService newWxQidianServiceJoddHttpImpl() {
+ return new WxQidianServiceJoddHttpImpl();
+ }
+
+}
diff --git a/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/config/WxQidianStorageAutoConfiguration.java b/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/config/WxQidianStorageAutoConfiguration.java
new file mode 100644
index 0000000000..7f78864226
--- /dev/null
+++ b/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/config/WxQidianStorageAutoConfiguration.java
@@ -0,0 +1,135 @@
+package com.binarywang.solon.wxjava.qidian.config;
+
+import com.binarywang.solon.wxjava.qidian.enums.StorageType;
+import com.binarywang.solon.wxjava.qidian.properties.RedisProperties;
+import com.binarywang.solon.wxjava.qidian.properties.WxQidianProperties;
+import com.google.common.collect.Sets;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.redis.JedisWxRedisOps;
+import me.chanjar.weixin.common.redis.WxRedisOps;
+import me.chanjar.weixin.qidian.bean.WxQidianHostConfig;
+import me.chanjar.weixin.qidian.config.WxQidianConfigStorage;
+import me.chanjar.weixin.qidian.config.impl.WxQidianDefaultConfigImpl;
+import me.chanjar.weixin.qidian.config.impl.WxQidianRedisConfigImpl;
+import org.apache.commons.lang3.StringUtils;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+import org.noear.solon.annotation.Inject;
+import org.noear.solon.core.AppContext;
+import redis.clients.jedis.JedisPool;
+import redis.clients.jedis.JedisPoolConfig;
+import redis.clients.jedis.JedisSentinelPool;
+import redis.clients.jedis.util.Pool;
+
+import java.util.Set;
+
+/**
+ * 腾讯企点存储策略自动配置.
+ *
+ * @author alegria
+ */
+@Slf4j
+@Configuration
+@RequiredArgsConstructor
+public class WxQidianStorageAutoConfiguration {
+ private final AppContext applicationContext;
+
+ private final WxQidianProperties wxQidianProperties;
+
+ @Inject("${wx.mp.config-storage.redis.host:")
+ private String redisHost;
+
+ @Inject("${wx.mp.configStorage.redis.host:")
+ private String redisHost2;
+
+ @Bean
+ @Condition(onMissingBean=WxQidianConfigStorage.class)
+ public WxQidianConfigStorage wxQidianConfigStorage() {
+ StorageType type = wxQidianProperties.getConfigStorage().getType();
+ WxQidianConfigStorage config;
+ switch (type) {
+ case Jedis:
+ config = jedisConfigStorage();
+ break;
+ default:
+ config = defaultConfigStorage();
+ break;
+ }
+ // wx host config
+ if (null != wxQidianProperties.getHosts() && StringUtils.isNotEmpty(wxQidianProperties.getHosts().getApiHost())) {
+ WxQidianHostConfig hostConfig = new WxQidianHostConfig();
+ hostConfig.setApiHost(wxQidianProperties.getHosts().getApiHost());
+ hostConfig.setQidianHost(wxQidianProperties.getHosts().getQidianHost());
+ hostConfig.setOpenHost(wxQidianProperties.getHosts().getOpenHost());
+ config.setHostConfig(hostConfig);
+ }
+ return config;
+ }
+
+ private WxQidianConfigStorage defaultConfigStorage() {
+ WxQidianDefaultConfigImpl config = new WxQidianDefaultConfigImpl();
+ setWxMpInfo(config);
+ return config;
+ }
+
+ private WxQidianConfigStorage jedisConfigStorage() {
+ Pool jedisPool;
+ if (StringUtils.isNotEmpty(redisHost) || StringUtils.isNotEmpty(redisHost2)) {
+ jedisPool = getJedisPool();
+ } else {
+ jedisPool = applicationContext.getBean(JedisPool.class);
+ }
+ WxRedisOps redisOps = new JedisWxRedisOps(jedisPool);
+ WxQidianRedisConfigImpl wxQidianRedisConfig = new WxQidianRedisConfigImpl(redisOps,
+ wxQidianProperties.getConfigStorage().getKeyPrefix());
+ setWxMpInfo(wxQidianRedisConfig);
+ return wxQidianRedisConfig;
+ }
+
+ private void setWxMpInfo(WxQidianDefaultConfigImpl config) {
+ WxQidianProperties properties = wxQidianProperties;
+ WxQidianProperties.ConfigStorage configStorageProperties = properties.getConfigStorage();
+ config.setAppId(properties.getAppId());
+ config.setSecret(properties.getSecret());
+ config.setToken(properties.getToken());
+ config.setAesKey(properties.getAesKey());
+
+ config.setHttpProxyHost(configStorageProperties.getHttpProxyHost());
+ config.setHttpProxyUsername(configStorageProperties.getHttpProxyUsername());
+ config.setHttpProxyPassword(configStorageProperties.getHttpProxyPassword());
+ if (configStorageProperties.getHttpProxyPort() != null) {
+ config.setHttpProxyPort(configStorageProperties.getHttpProxyPort());
+ }
+ }
+
+ private Pool getJedisPool() {
+ WxQidianProperties.ConfigStorage storage = wxQidianProperties.getConfigStorage();
+ RedisProperties redis = storage.getRedis();
+
+ JedisPoolConfig config = new JedisPoolConfig();
+ if (redis.getMaxActive() != null) {
+ config.setMaxTotal(redis.getMaxActive());
+ }
+ if (redis.getMaxIdle() != null) {
+ config.setMaxIdle(redis.getMaxIdle());
+ }
+ if (redis.getMaxWaitMillis() != null) {
+ config.setMaxWaitMillis(redis.getMaxWaitMillis());
+ }
+ if (redis.getMinIdle() != null) {
+ config.setMinIdle(redis.getMinIdle());
+ }
+ config.setTestOnBorrow(true);
+ config.setTestWhileIdle(true);
+ if (StringUtils.isNotEmpty(redis.getSentinelIps())) {
+
+ Set sentinels = Sets.newHashSet(redis.getSentinelIps().split(","));
+ return new JedisSentinelPool(redis.getSentinelName(), sentinels,config);
+ }
+
+ return new JedisPool(config, redis.getHost(), redis.getPort(), redis.getTimeout(), redis.getPassword(),
+ redis.getDatabase());
+ }
+}
diff --git a/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/enums/HttpClientType.java b/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/enums/HttpClientType.java
new file mode 100644
index 0000000000..0b94821da7
--- /dev/null
+++ b/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/enums/HttpClientType.java
@@ -0,0 +1,22 @@
+package com.binarywang.solon.wxjava.qidian.enums;
+
+/**
+ * httpclient类型.
+ *
+ * @author Binary Wang
+ * created on 2020-08-30
+ */
+public enum HttpClientType {
+ /**
+ * HttpClient.
+ */
+ HttpClient,
+ /**
+ * OkHttp.
+ */
+ OkHttp,
+ /**
+ * JoddHttp.
+ */
+ JoddHttp,
+}
diff --git a/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/enums/StorageType.java b/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/enums/StorageType.java
new file mode 100644
index 0000000000..c4bd2f72cf
--- /dev/null
+++ b/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/enums/StorageType.java
@@ -0,0 +1,26 @@
+package com.binarywang.solon.wxjava.qidian.enums;
+
+/**
+ * storage类型.
+ *
+ * @author Binary Wang
+ * created on 2020-08-30
+ */
+public enum StorageType {
+ /**
+ * 内存.
+ */
+ Memory,
+ /**
+ * redis(JedisClient).
+ */
+ Jedis,
+ /**
+ * redis(Redisson).
+ */
+ Redisson,
+ /**
+ * redis(RedisTemplate).
+ */
+ RedisTemplate
+}
diff --git a/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/integration/WxQidianPluginImpl.java b/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/integration/WxQidianPluginImpl.java
new file mode 100644
index 0000000000..2a97b512fd
--- /dev/null
+++ b/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/integration/WxQidianPluginImpl.java
@@ -0,0 +1,20 @@
+package com.binarywang.solon.wxjava.qidian.integration;
+
+import com.binarywang.solon.wxjava.qidian.config.WxQidianServiceAutoConfiguration;
+import com.binarywang.solon.wxjava.qidian.config.WxQidianStorageAutoConfiguration;
+import com.binarywang.solon.wxjava.qidian.properties.WxQidianProperties;
+import org.noear.solon.core.AppContext;
+import org.noear.solon.core.Plugin;
+
+/**
+ * @author noear 2024/9/2 created
+ */
+public class WxQidianPluginImpl implements Plugin{
+ @Override
+ public void start(AppContext context) throws Throwable {
+ context.beanMake(WxQidianProperties.class);
+
+ context.beanMake(WxQidianStorageAutoConfiguration.class);
+ context.beanMake(WxQidianServiceAutoConfiguration.class);
+ }
+}
diff --git a/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/properties/HostConfig.java b/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/properties/HostConfig.java
new file mode 100644
index 0000000000..08546d8da6
--- /dev/null
+++ b/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/properties/HostConfig.java
@@ -0,0 +1,18 @@
+package com.binarywang.solon.wxjava.qidian.properties;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class HostConfig implements Serializable {
+
+ private static final long serialVersionUID = -4172767630740346001L;
+
+ private String apiHost;
+
+ private String openHost;
+
+ private String qidianHost;
+
+}
diff --git a/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/properties/RedisProperties.java b/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/properties/RedisProperties.java
new file mode 100644
index 0000000000..a6b10a9e0f
--- /dev/null
+++ b/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/properties/RedisProperties.java
@@ -0,0 +1,56 @@
+package com.binarywang.solon.wxjava.qidian.properties;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * redis 配置属性.
+ *
+ * @author Binary Wang
+ * created on 2020-08-30
+ */
+@Data
+public class RedisProperties implements Serializable {
+ private static final long serialVersionUID = -5924815351660074401L;
+
+ /**
+ * 主机地址.
+ */
+ private String host = "127.0.0.1";
+
+ /**
+ * 端口号.
+ */
+ private int port = 6379;
+
+ /**
+ * 密码.
+ */
+ private String password;
+
+ /**
+ * 超时.
+ */
+ private int timeout = 2000;
+
+ /**
+ * 数据库.
+ */
+ private int database = 0;
+
+ /**
+ * sentinel ips
+ */
+ private String sentinelIps;
+
+ /**
+ * sentinel name
+ */
+ private String sentinelName;
+
+ private Integer maxActive;
+ private Integer maxIdle;
+ private Integer maxWaitMillis;
+ private Integer minIdle;
+}
diff --git a/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/properties/WxQidianProperties.java b/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/properties/WxQidianProperties.java
new file mode 100644
index 0000000000..67c4dba543
--- /dev/null
+++ b/solon-plugins/wx-java-qidian-solon-plugin/src/main/java/com/binarywang/solon/wxjava/qidian/properties/WxQidianProperties.java
@@ -0,0 +1,101 @@
+package com.binarywang.solon.wxjava.qidian.properties;
+
+import com.binarywang.solon.wxjava.qidian.enums.HttpClientType;
+import com.binarywang.solon.wxjava.qidian.enums.StorageType;
+import lombok.Data;
+import org.noear.solon.annotation.Configuration;
+import org.noear.solon.annotation.Inject;
+
+import java.io.Serializable;
+
+import static com.binarywang.solon.wxjava.qidian.enums.StorageType.Memory;
+import static com.binarywang.solon.wxjava.qidian.properties.WxQidianProperties.PREFIX;
+
+/**
+ * 企点接入相关配置属性.
+ *
+ * @author someone
+ */
+@Data
+@Configuration
+@Inject("${" + PREFIX + "}")
+public class WxQidianProperties {
+ public static final String PREFIX = "wx.qidian";
+
+ /**
+ * 设置腾讯企点的appid.
+ */
+ private String appId;
+
+ /**
+ * 设置腾讯企点的app secret.
+ */
+ private String secret;
+
+ /**
+ * 设置腾讯企点的token.
+ */
+ private String token;
+
+ /**
+ * 设置腾讯企点的EncodingAESKey.
+ */
+ private String aesKey;
+
+ /**
+ * 自定义host配置
+ */
+ private HostConfig hosts;
+
+ /**
+ * 存储策略
+ */
+ private ConfigStorage configStorage = new ConfigStorage();
+
+ @Data
+ public static class ConfigStorage implements Serializable {
+ private static final long serialVersionUID = 4815731027000065434L;
+
+ /**
+ * 存储类型.
+ */
+ private StorageType type = Memory;
+
+ /**
+ * 指定key前缀.
+ */
+ private String keyPrefix = "wx";
+
+ /**
+ * redis连接配置.
+ */
+ private RedisProperties redis = new RedisProperties();
+
+ /**
+ * http客户端类型.
+ */
+ private HttpClientType httpClientType = HttpClientType.HttpClient;
+
+ /**
+ * http代理主机.
+ */
+ private String httpProxyHost;
+
+ /**
+ * http代理端口.
+ */
+ private Integer httpProxyPort;
+
+ /**
+ * http代理用户名.
+ */
+ private String httpProxyUsername;
+
+ /**
+ * http代理密码.
+ */
+ private String httpProxyPassword;
+
+ }
+
+}
diff --git a/solon-plugins/wx-java-qidian-solon-plugin/src/main/resources/META-INF/solon/wx-java-qidian-solon-plugin.properties b/solon-plugins/wx-java-qidian-solon-plugin/src/main/resources/META-INF/solon/wx-java-qidian-solon-plugin.properties
new file mode 100644
index 0000000000..a60c450b06
--- /dev/null
+++ b/solon-plugins/wx-java-qidian-solon-plugin/src/main/resources/META-INF/solon/wx-java-qidian-solon-plugin.properties
@@ -0,0 +1,2 @@
+solon.plugin=com.binarywang.solon.wxjava.qidian.integration.WxQidianPluginImpl
+solon.plugin.priority=10
From 6ef2e83fe20d4485deeba8784a1de8928bdfc3bc Mon Sep 17 00:00:00 2001
From: Molzx <31435895+Molzx@users.noreply.github.com>
Date: Wed, 4 Sep 2024 11:47:59 +0800
Subject: [PATCH 045/385] =?UTF-8?q?:bug:=20#3359=20=E3=80=90=E5=BC=80?=
=?UTF-8?q?=E6=94=BE=E5=B9=B3=E5=8F=B0=E3=80=91=E4=BF=AE=E5=A4=8D=E5=A4=87?=
=?UTF-8?q?=E6=A1=88=E6=8E=A5=E5=8F=A3=E5=B1=9E=E6=80=A7=E5=AE=9A=E4=B9=89?=
=?UTF-8?q?=E9=94=99=E8=AF=AF=E7=9A=84=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../weixin/open/bean/icp/WxOpenIcpEntranceInfoResult.java | 3 ++-
.../weixin/open/bean/icp/WxOpenUploadIcpMediaParam.java | 4 ++--
.../me/chanjar/weixin/open/bean/message/WxOpenXmlMessage.java | 2 +-
3 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenIcpEntranceInfoResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenIcpEntranceInfoResult.java
index 9538a64b0a..be52efa6fc 100644
--- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenIcpEntranceInfoResult.java
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenIcpEntranceInfoResult.java
@@ -7,6 +7,7 @@
import me.chanjar.weixin.open.bean.result.WxOpenResult;
import java.io.Serializable;
+import java.util.List;
/**
@@ -49,7 +50,7 @@ public static class Info implements Serializable {
* 驳回原因,备案不通过时返回
*/
@SerializedName("audit_data")
- private AuditData auditData;
+ private List auditData;
/**
* 备案入口是否对该小程序开放,0:不开放,1:开放。特定情况下入口不会开放,如小程序昵称包含某些关键词时、管局系统不可用时,当备案入口开放时才能提交备案申请
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenUploadIcpMediaParam.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenUploadIcpMediaParam.java
index a5eda8ab4e..e431ab8705 100644
--- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenUploadIcpMediaParam.java
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenUploadIcpMediaParam.java
@@ -34,7 +34,7 @@ public class WxOpenUploadIcpMediaParam implements Serializable {
* 证件类型(参考:获取证件类型),如果上传的是证件媒体材料,则必填,示例值:2
*/
@SerializedName("certificate_type")
- private String certificateType;
+ private Integer certificateType;
/**
* 媒体材料所属的备案字段名(参考:申请小程序备案接口),如要用于多个备案字段,则填写其中一个字段名即可。
@@ -54,7 +54,7 @@ public CommonUploadMultiParam toCommonUploadMultiParam() {
return CommonUploadMultiParam.builder()
.normalParams(Arrays.asList(
CommonUploadMultiParam.NormalParam.builder().name("type").value(type).build(),
- CommonUploadMultiParam.NormalParam.builder().name("certificate_type").value(certificateType).build(),
+ CommonUploadMultiParam.NormalParam.builder().name("certificate_type").value(String.valueOf(certificateType)).build(),
CommonUploadMultiParam.NormalParam.builder().name("icp_order_field").value(icpOrderField).build()
))
.uploadParam(new CommonUploadParam("media", media))
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/message/WxOpenXmlMessage.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/message/WxOpenXmlMessage.java
index e676f0ab66..dc6839c1a5 100644
--- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/message/WxOpenXmlMessage.java
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/message/WxOpenXmlMessage.java
@@ -147,7 +147,7 @@ public class WxOpenXmlMessage implements Serializable {
* 小程序唯一id
*/
@XStreamAlias("authorizer_appid")
- private String authorizerAppId;
+ private String beianAuthorizerAppId;
/**
* 备案状态,参考“获取小程序备案状态及驳回原因”接口的备案状态枚举¬
*/
From a6e3c865052988ab41e4cfd701e6ccde2d5c839e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=A5=BF=E4=B8=9C?=
Date: Fri, 6 Sep 2024 23:46:08 +0800
Subject: [PATCH 046/385] =?UTF-8?q?:art:=20solon-plugins=20=E6=B7=BB?=
=?UTF-8?q?=E5=8A=A0=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95=E6=94=AF=E6=8C=81?=
=?UTF-8?q?=EF=BC=8C=E5=8F=AF=E8=A7=A6=E5=8F=91=E5=BF=85=E8=A6=81=E9=94=99?=
=?UTF-8?q?=E8=AF=AF=E7=94=A8=E4=BA=8E=E4=BA=BA=E5=B7=A5=E6=A0=B8=E5=AF=B9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
solon-plugins/pom.xml | 6 +++++
.../src/test/java/features/test/LoadTest.java | 15 ++++++++++++
.../src/test/resources/app.yml | 0
.../src/test/java/features/test/LoadTest.java | 15 ++++++++++++
.../src/test/resources/app.properties | 19 +++++++++++++++
.../src/test/java/features/test/LoadTest.java | 15 ++++++++++++
.../src/test/resources/app.properties | 20 ++++++++++++++++
.../src/test/java/features/test/LoadTest.java | 15 ++++++++++++
.../src/test/resources/app.properties | 18 +++++++++++++++
.../src/test/java/features/test/LoadTest.java | 15 ++++++++++++
.../src/test/resources/app.properties | 23 +++++++++++++++++++
.../src/test/java/features/test/LoadTest.java | 15 ++++++++++++
.../src/test/resources/app.properties | 11 +++++++++
.../src/test/java/features/test/LoadTest.java | 15 ++++++++++++
.../src/test/resources/app.properties | 11 +++++++++
.../src/test/java/features/test/LoadTest.java | 15 ++++++++++++
.../src/test/resources/app.yml | 6 +++++
.../src/test/java/features/test/LoadTest.java | 15 ++++++++++++
.../src/test/resources/app.yml | 0
19 files changed, 249 insertions(+)
create mode 100644 solon-plugins/wx-java-channel-solon-plugin/src/test/java/features/test/LoadTest.java
create mode 100644 solon-plugins/wx-java-channel-solon-plugin/src/test/resources/app.yml
create mode 100644 solon-plugins/wx-java-cp-multi-solon-plugin/src/test/java/features/test/LoadTest.java
create mode 100644 solon-plugins/wx-java-cp-multi-solon-plugin/src/test/resources/app.properties
create mode 100644 solon-plugins/wx-java-cp-solon-plugin/src/test/java/features/test/LoadTest.java
create mode 100644 solon-plugins/wx-java-cp-solon-plugin/src/test/resources/app.properties
create mode 100644 solon-plugins/wx-java-miniapp-solon-plugin/src/test/java/features/test/LoadTest.java
create mode 100644 solon-plugins/wx-java-miniapp-solon-plugin/src/test/resources/app.properties
create mode 100644 solon-plugins/wx-java-mp-multi-solon-plugin/src/test/java/features/test/LoadTest.java
create mode 100644 solon-plugins/wx-java-mp-multi-solon-plugin/src/test/resources/app.properties
create mode 100644 solon-plugins/wx-java-mp-solon-plugin/src/test/java/features/test/LoadTest.java
create mode 100644 solon-plugins/wx-java-mp-solon-plugin/src/test/resources/app.properties
create mode 100644 solon-plugins/wx-java-open-solon-plugin/src/test/java/features/test/LoadTest.java
create mode 100644 solon-plugins/wx-java-open-solon-plugin/src/test/resources/app.properties
create mode 100644 solon-plugins/wx-java-pay-solon-plugin/src/test/java/features/test/LoadTest.java
create mode 100644 solon-plugins/wx-java-pay-solon-plugin/src/test/resources/app.yml
create mode 100644 solon-plugins/wx-java-qidian-solon-plugin/src/test/java/features/test/LoadTest.java
create mode 100644 solon-plugins/wx-java-qidian-solon-plugin/src/test/resources/app.yml
diff --git a/solon-plugins/pom.xml b/solon-plugins/pom.xml
index 278e26dc9f..87317902c8 100644
--- a/solon-plugins/pom.xml
+++ b/solon-plugins/pom.xml
@@ -40,5 +40,11 @@
lombok
provided
+
+ org.noear
+ solon-test
+ ${solon.version}
+ test
+
diff --git a/solon-plugins/wx-java-channel-solon-plugin/src/test/java/features/test/LoadTest.java b/solon-plugins/wx-java-channel-solon-plugin/src/test/java/features/test/LoadTest.java
new file mode 100644
index 0000000000..d049f5a51a
--- /dev/null
+++ b/solon-plugins/wx-java-channel-solon-plugin/src/test/java/features/test/LoadTest.java
@@ -0,0 +1,15 @@
+package features.test;
+
+import org.junit.jupiter.api.Test;
+import org.noear.solon.test.SolonTest;
+
+/**
+ * @author noear 2024/9/4 created
+ */
+@SolonTest
+public class LoadTest {
+ @Test
+ public void load(){
+
+ }
+}
diff --git a/solon-plugins/wx-java-channel-solon-plugin/src/test/resources/app.yml b/solon-plugins/wx-java-channel-solon-plugin/src/test/resources/app.yml
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/solon-plugins/wx-java-cp-multi-solon-plugin/src/test/java/features/test/LoadTest.java b/solon-plugins/wx-java-cp-multi-solon-plugin/src/test/java/features/test/LoadTest.java
new file mode 100644
index 0000000000..d049f5a51a
--- /dev/null
+++ b/solon-plugins/wx-java-cp-multi-solon-plugin/src/test/java/features/test/LoadTest.java
@@ -0,0 +1,15 @@
+package features.test;
+
+import org.junit.jupiter.api.Test;
+import org.noear.solon.test.SolonTest;
+
+/**
+ * @author noear 2024/9/4 created
+ */
+@SolonTest
+public class LoadTest {
+ @Test
+ public void load(){
+
+ }
+}
diff --git a/solon-plugins/wx-java-cp-multi-solon-plugin/src/test/resources/app.properties b/solon-plugins/wx-java-cp-multi-solon-plugin/src/test/resources/app.properties
new file mode 100644
index 0000000000..0602c0a807
--- /dev/null
+++ b/solon-plugins/wx-java-cp-multi-solon-plugin/src/test/resources/app.properties
@@ -0,0 +1,19 @@
+# ?? 1 ??
+wx.cp.corps.tenantId1.corp-id = @corp-id
+wx.cp.corps.tenantId1.corp-secret = @corp-secret
+ ## ??
+wx.cp.corps.tenantId1.agent-id = @agent-id
+wx.cp.corps.tenantId1.token = @token
+wx.cp.corps.tenantId1.aes-key = @aes-key
+wx.cp.corps.tenantId1.msg-audit-priKey = @msg-audit-priKey
+wx.cp.corps.tenantId1.msg-audit-lib-path = @msg-audit-lib-path
+
+ # ?? 2 ??
+wx.cp.corps.tenantId2.corp-id = @corp-id
+wx.cp.corps.tenantId2.corp-secret = @corp-secret
+ ## ??
+wx.cp.corps.tenantId2.agent-id = @agent-id
+wx.cp.corps.tenantId2.token = @token
+wx.cp.corps.tenantId2.aes-key = @aes-key
+wx.cp.corps.tenantId2.msg-audit-priKey = @msg-audit-priKey
+wx.cp.corps.tenantId2.msg-audit-lib-path = @msg-audit-lib-path
diff --git a/solon-plugins/wx-java-cp-solon-plugin/src/test/java/features/test/LoadTest.java b/solon-plugins/wx-java-cp-solon-plugin/src/test/java/features/test/LoadTest.java
new file mode 100644
index 0000000000..d049f5a51a
--- /dev/null
+++ b/solon-plugins/wx-java-cp-solon-plugin/src/test/java/features/test/LoadTest.java
@@ -0,0 +1,15 @@
+package features.test;
+
+import org.junit.jupiter.api.Test;
+import org.noear.solon.test.SolonTest;
+
+/**
+ * @author noear 2024/9/4 created
+ */
+@SolonTest
+public class LoadTest {
+ @Test
+ public void load(){
+
+ }
+}
diff --git a/solon-plugins/wx-java-cp-solon-plugin/src/test/resources/app.properties b/solon-plugins/wx-java-cp-solon-plugin/src/test/resources/app.properties
new file mode 100644
index 0000000000..0c99c8b64d
--- /dev/null
+++ b/solon-plugins/wx-java-cp-solon-plugin/src/test/resources/app.properties
@@ -0,0 +1,20 @@
+# ???????(??)
+wx.cp.corp-id = @corp-id
+wx.cp.corp-secret = @corp-secret
+# ??
+wx.cp.agent-id = @agent-id
+wx.cp.token = @token
+wx.cp.aes-key = @aes-key
+wx.cp.msg-audit-priKey = @msg-audit-priKey
+wx.cp.msg-audit-lib-path = @msg-audit-lib-path
+# ConfigStorage ??????
+wx.cp.config-storage.type=memory # ????: memory(??), jedis, redisson, redistemplate
+# http ?????????
+wx.cp.config-storage.http-proxy-host=
+wx.cp.config-storage.http-proxy-port=
+wx.cp.config-storage.http-proxy-username=
+wx.cp.config-storage.http-proxy-password=
+# ??????????5 ?????? 0??? 0
+wx.cp.config-storage.max-retry-times=5
+# ????????????1000 ??????? 0??? 1000
+wx.cp.config-storage.retry-sleep-millis=1000
diff --git a/solon-plugins/wx-java-miniapp-solon-plugin/src/test/java/features/test/LoadTest.java b/solon-plugins/wx-java-miniapp-solon-plugin/src/test/java/features/test/LoadTest.java
new file mode 100644
index 0000000000..d049f5a51a
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-solon-plugin/src/test/java/features/test/LoadTest.java
@@ -0,0 +1,15 @@
+package features.test;
+
+import org.junit.jupiter.api.Test;
+import org.noear.solon.test.SolonTest;
+
+/**
+ * @author noear 2024/9/4 created
+ */
+@SolonTest
+public class LoadTest {
+ @Test
+ public void load(){
+
+ }
+}
diff --git a/solon-plugins/wx-java-miniapp-solon-plugin/src/test/resources/app.properties b/solon-plugins/wx-java-miniapp-solon-plugin/src/test/resources/app.properties
new file mode 100644
index 0000000000..22a9a4e627
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-solon-plugin/src/test/resources/app.properties
@@ -0,0 +1,18 @@
+# ?????(??)
+wx.miniapp.appid = appId
+wx.miniapp.secret = @secret
+wx.miniapp.token = @token
+wx.miniapp.aesKey = @aesKey
+wx.miniapp.msgDataFormat = @msgDataFormat # ?????XML??JSON.
+# ????redis(??)
+# ??: ??redis.host???????????redis??(JedisPool)
+wx.miniapp.config-storage.type = Jedis # ????: Memory(??), Jedis, RedisTemplate
+wx.miniapp.config-storage.key-prefix = wa # ??redis????: wa(??)
+wx.miniapp.config-storage.redis.host = 127.0.0.1
+wx.miniapp.config-storage.redis.port = 6379
+# http?????
+wx.miniapp.config-storage.http-client-type=HttpClient # http?????: HttpClient(??), OkHttp, JoddHttp
+wx.miniapp.config-storage.http-proxy-host=
+wx.miniapp.config-storage.http-proxy-port=
+wx.miniapp.config-storage.http-proxy-username=
+wx.miniapp.config-storage.http-proxy-password=
diff --git a/solon-plugins/wx-java-mp-multi-solon-plugin/src/test/java/features/test/LoadTest.java b/solon-plugins/wx-java-mp-multi-solon-plugin/src/test/java/features/test/LoadTest.java
new file mode 100644
index 0000000000..d049f5a51a
--- /dev/null
+++ b/solon-plugins/wx-java-mp-multi-solon-plugin/src/test/java/features/test/LoadTest.java
@@ -0,0 +1,15 @@
+package features.test;
+
+import org.junit.jupiter.api.Test;
+import org.noear.solon.test.SolonTest;
+
+/**
+ * @author noear 2024/9/4 created
+ */
+@SolonTest
+public class LoadTest {
+ @Test
+ public void load(){
+
+ }
+}
diff --git a/solon-plugins/wx-java-mp-multi-solon-plugin/src/test/resources/app.properties b/solon-plugins/wx-java-mp-multi-solon-plugin/src/test/resources/app.properties
new file mode 100644
index 0000000000..3f3b21657c
--- /dev/null
+++ b/solon-plugins/wx-java-mp-multi-solon-plugin/src/test/resources/app.properties
@@ -0,0 +1,23 @@
+# ?????
+## ?? 1 ??(??)
+wx.mp.apps.tenantId1.app-id=appId
+wx.mp.apps.tenantId1.app-secret=@secret
+## ??
+wx.mp.apps.tenantId1.token=@token
+wx.mp.apps.tenantId1.aes-key=@aesKey
+wx.mp.apps.tenantId1.use-stable-access-token=@useStableAccessToken
+## ?? 2 ??(??)
+wx.mp.apps.tenantId2.app-id=@appId
+wx.mp.apps.tenantId2.app-secret =@secret
+## ??
+wx.mp.apps.tenantId2.token=@token
+wx.mp.apps.tenantId2.aes-key=@aesKey
+wx.mp.apps.tenantId2.use-stable-access-token=@useStableAccessToken
+
+# ConfigStorage ??????
+## ????: memory(??), jedis, redisson, redis_template
+wx.mp.config-storage.type=memory
+## ??redis????: wx:mp:multi(??)
+wx.mp.config-storage.key-prefix=wx:mp:multi
+wx.mp.config-storage.redis.host=127.0.0.1
+wx.mp.config-storage.redis.port=6379
diff --git a/solon-plugins/wx-java-mp-solon-plugin/src/test/java/features/test/LoadTest.java b/solon-plugins/wx-java-mp-solon-plugin/src/test/java/features/test/LoadTest.java
new file mode 100644
index 0000000000..d049f5a51a
--- /dev/null
+++ b/solon-plugins/wx-java-mp-solon-plugin/src/test/java/features/test/LoadTest.java
@@ -0,0 +1,15 @@
+package features.test;
+
+import org.junit.jupiter.api.Test;
+import org.noear.solon.test.SolonTest;
+
+/**
+ * @author noear 2024/9/4 created
+ */
+@SolonTest
+public class LoadTest {
+ @Test
+ public void load(){
+
+ }
+}
diff --git a/solon-plugins/wx-java-mp-solon-plugin/src/test/resources/app.properties b/solon-plugins/wx-java-mp-solon-plugin/src/test/resources/app.properties
new file mode 100644
index 0000000000..a06f6c7dba
--- /dev/null
+++ b/solon-plugins/wx-java-mp-solon-plugin/src/test/resources/app.properties
@@ -0,0 +1,11 @@
+# ?????(??)
+wx.mp.app-id=appId
+wx.mp.secret=@secret
+wx.mp.token=@token
+wx.mp.aes-key=@aesKey
+wx.mp.use-stable-access-token=@useStableAccessToken
+# ????redis(??)
+wx.mp.config-storage.type= edis # ????: Memory(??), Jedis, RedisTemplate
+wx.mp.config-storage.key-prefix=wx # ??redis????: wx(??)
+wx.mp.config-storage.redis.host=127.0.0.1
+wx.mp.config-storage.redis.port=6379
diff --git a/solon-plugins/wx-java-open-solon-plugin/src/test/java/features/test/LoadTest.java b/solon-plugins/wx-java-open-solon-plugin/src/test/java/features/test/LoadTest.java
new file mode 100644
index 0000000000..d049f5a51a
--- /dev/null
+++ b/solon-plugins/wx-java-open-solon-plugin/src/test/java/features/test/LoadTest.java
@@ -0,0 +1,15 @@
+package features.test;
+
+import org.junit.jupiter.api.Test;
+import org.noear.solon.test.SolonTest;
+
+/**
+ * @author noear 2024/9/4 created
+ */
+@SolonTest
+public class LoadTest {
+ @Test
+ public void load(){
+
+ }
+}
diff --git a/solon-plugins/wx-java-open-solon-plugin/src/test/resources/app.properties b/solon-plugins/wx-java-open-solon-plugin/src/test/resources/app.properties
new file mode 100644
index 0000000000..fc2e79c95b
--- /dev/null
+++ b/solon-plugins/wx-java-open-solon-plugin/src/test/resources/app.properties
@@ -0,0 +1,11 @@
+# ?????(??)
+wx.open.appId = appId
+wx.open.secret = @secret
+wx.open.token = @token
+wx.open.aesKey = @aesKey
+# ????redis(??)
+# ???????(JedisPool, RedissonClient), ????wx.open.config-storage.redis.host, ????????redis????
+wx.open.config-storage.type = redis # ????: memory(??), redis(jedis), jedis, redisson, redistemplate
+wx.open.config-storage.key-prefix = wx # ??redis????: wx(??)
+wx.open.config-storage.redis.host = 127.0.0.1
+wx.open.config-storage.redis.port = 6379
diff --git a/solon-plugins/wx-java-pay-solon-plugin/src/test/java/features/test/LoadTest.java b/solon-plugins/wx-java-pay-solon-plugin/src/test/java/features/test/LoadTest.java
new file mode 100644
index 0000000000..d049f5a51a
--- /dev/null
+++ b/solon-plugins/wx-java-pay-solon-plugin/src/test/java/features/test/LoadTest.java
@@ -0,0 +1,15 @@
+package features.test;
+
+import org.junit.jupiter.api.Test;
+import org.noear.solon.test.SolonTest;
+
+/**
+ * @author noear 2024/9/4 created
+ */
+@SolonTest
+public class LoadTest {
+ @Test
+ public void load(){
+
+ }
+}
diff --git a/solon-plugins/wx-java-pay-solon-plugin/src/test/resources/app.yml b/solon-plugins/wx-java-pay-solon-plugin/src/test/resources/app.yml
new file mode 100644
index 0000000000..1d6a61d7e5
--- /dev/null
+++ b/solon-plugins/wx-java-pay-solon-plugin/src/test/resources/app.yml
@@ -0,0 +1,6 @@
+wx:
+ pay:
+ appId:
+ mchId:
+ mchKey:
+ keyPath:
diff --git a/solon-plugins/wx-java-qidian-solon-plugin/src/test/java/features/test/LoadTest.java b/solon-plugins/wx-java-qidian-solon-plugin/src/test/java/features/test/LoadTest.java
new file mode 100644
index 0000000000..d049f5a51a
--- /dev/null
+++ b/solon-plugins/wx-java-qidian-solon-plugin/src/test/java/features/test/LoadTest.java
@@ -0,0 +1,15 @@
+package features.test;
+
+import org.junit.jupiter.api.Test;
+import org.noear.solon.test.SolonTest;
+
+/**
+ * @author noear 2024/9/4 created
+ */
+@SolonTest
+public class LoadTest {
+ @Test
+ public void load(){
+
+ }
+}
diff --git a/solon-plugins/wx-java-qidian-solon-plugin/src/test/resources/app.yml b/solon-plugins/wx-java-qidian-solon-plugin/src/test/resources/app.yml
new file mode 100644
index 0000000000..e69de29bb2
From 939f3eb2ff316949e8460f23dcc35fe251a3088f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=A5=BF=E4=B8=9C?=
Date: Sun, 8 Sep 2024 16:03:40 +0800
Subject: [PATCH 047/385] =?UTF-8?q?:art:=20=E4=BF=AE=E5=A4=8D=E6=96=87?=
=?UTF-8?q?=E6=A1=A3=E9=94=99=E8=AF=AF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../wx-java-qidian-solon-plugin/README.md | 40 +++++++++----------
.../README.md | 40 +++++++++----------
2 files changed, 40 insertions(+), 40 deletions(-)
diff --git a/solon-plugins/wx-java-qidian-solon-plugin/README.md b/solon-plugins/wx-java-qidian-solon-plugin/README.md
index a409113c8c..42daa3e4c8 100644
--- a/solon-plugins/wx-java-qidian-solon-plugin/README.md
+++ b/solon-plugins/wx-java-qidian-solon-plugin/README.md
@@ -13,33 +13,33 @@
2. 添加配置(app.properties)
```properties
# 公众号配置(必填)
- wx.mp.appId = appId
- wx.mp.secret = @secret
- wx.mp.token = @token
- wx.mp.aesKey = @aesKey
+ wx.qidian.appId = appId
+ wx.qidian.secret = @secret
+ wx.qidian.token = @token
+ wx.qidian.aesKey = @aesKey
# 存储配置redis(可选)
- wx.mp.config-storage.type = Jedis # 配置类型: Memory(默认), Jedis, RedisTemplate
- wx.mp.config-storage.key-prefix = wx # 相关redis前缀配置: wx(默认)
- wx.mp.config-storage.redis.host = 127.0.0.1
- wx.mp.config-storage.redis.port = 6379
+ wx.qidian.config-storage.type = Jedis # 配置类型: Memory(默认), Jedis, RedisTemplate
+ wx.qidian.config-storage.key-prefix = wx # 相关redis前缀配置: wx(默认)
+ wx.qidian.config-storage.redis.host = 127.0.0.1
+ wx.qidian.config-storage.redis.port = 6379
#单机和sentinel同时存在时,优先使用sentinel配置
- #wx.mp.config-storage.redis.sentinel-ips=127.0.0.1:16379,127.0.0.1:26379
- #wx.mp.config-storage.redis.sentinel-name=mymaster
+ #wx.qidian.config-storage.redis.sentinel-ips=127.0.0.1:16379,127.0.0.1:26379
+ #wx.qidian.config-storage.redis.sentinel-name=mymaster
# http客户端配置
- wx.mp.config-storage.http-client-type=httpclient # http客户端类型: HttpClient(默认), OkHttp, JoddHttp
- wx.mp.config-storage.http-proxy-host=
- wx.mp.config-storage.http-proxy-port=
- wx.mp.config-storage.http-proxy-username=
- wx.mp.config-storage.http-proxy-password=
+ wx.qidian.config-storage.http-client-type=httpclient # http客户端类型: HttpClient(默认), OkHttp, JoddHttp
+ wx.qidian.config-storage.http-proxy-host=
+ wx.qidian.config-storage.http-proxy-port=
+ wx.qidian.config-storage.http-proxy-username=
+ wx.qidian.config-storage.http-proxy-password=
# 公众号地址host配置
- #wx.mp.hosts.api-host=http://proxy.com/
- #wx.mp.hosts.open-host=http://proxy.com/
- #wx.mp.hosts.mp-host=http://proxy.com/
+ #wx.qidian.hosts.api-host=http://proxy.com/
+ #wx.qidian.hosts.open-host=http://proxy.com/
+ #wx.qidian.hosts.mp-host=http://proxy.com/
```
3. 自动注入的类型
-- `WxMpService`
-- `WxMpConfigStorage`
+- `WxQidianService`
+- `WxQidianConfigStorage`
4、参考 demo:
https://github.com/binarywang/wx-java-mp-demo
diff --git a/spring-boot-starters/wx-java-qidian-spring-boot-starter/README.md b/spring-boot-starters/wx-java-qidian-spring-boot-starter/README.md
index d676616de6..34069fa1fe 100644
--- a/spring-boot-starters/wx-java-qidian-spring-boot-starter/README.md
+++ b/spring-boot-starters/wx-java-qidian-spring-boot-starter/README.md
@@ -13,33 +13,33 @@
2. 添加配置(application.properties)
```properties
# 公众号配置(必填)
- wx.mp.appId = appId
- wx.mp.secret = @secret
- wx.mp.token = @token
- wx.mp.aesKey = @aesKey
+ wx.qidian.appId = appId
+ wx.qidian.secret = @secret
+ wx.qidian.token = @token
+ wx.qidian.aesKey = @aesKey
# 存储配置redis(可选)
- wx.mp.config-storage.type = Jedis # 配置类型: Memory(默认), Jedis, RedisTemplate
- wx.mp.config-storage.key-prefix = wx # 相关redis前缀配置: wx(默认)
- wx.mp.config-storage.redis.host = 127.0.0.1
- wx.mp.config-storage.redis.port = 6379
+ wx.qidian.config-storage.type = Jedis # 配置类型: Memory(默认), Jedis, RedisTemplate
+ wx.qidian.config-storage.key-prefix = wx # 相关redis前缀配置: wx(默认)
+ wx.qidian.config-storage.redis.host = 127.0.0.1
+ wx.qidian.config-storage.redis.port = 6379
#单机和sentinel同时存在时,优先使用sentinel配置
- #wx.mp.config-storage.redis.sentinel-ips=127.0.0.1:16379,127.0.0.1:26379
- #wx.mp.config-storage.redis.sentinel-name=mymaster
+ #wx.qidian.config-storage.redis.sentinel-ips=127.0.0.1:16379,127.0.0.1:26379
+ #wx.qidian.config-storage.redis.sentinel-name=mymaster
# http客户端配置
- wx.mp.config-storage.http-client-type=httpclient # http客户端类型: HttpClient(默认), OkHttp, JoddHttp
- wx.mp.config-storage.http-proxy-host=
- wx.mp.config-storage.http-proxy-port=
- wx.mp.config-storage.http-proxy-username=
- wx.mp.config-storage.http-proxy-password=
+ wx.qidian.config-storage.http-client-type=httpclient # http客户端类型: HttpClient(默认), OkHttp, JoddHttp
+ wx.qidian.config-storage.http-proxy-host=
+ wx.qidian.config-storage.http-proxy-port=
+ wx.qidian.config-storage.http-proxy-username=
+ wx.qidian.config-storage.http-proxy-password=
# 公众号地址host配置
- #wx.mp.hosts.api-host=http://proxy.com/
- #wx.mp.hosts.open-host=http://proxy.com/
- #wx.mp.hosts.mp-host=http://proxy.com/
+ #wx.qidian.hosts.api-host=http://proxy.com/
+ #wx.qidian.hosts.open-host=http://proxy.com/
+ #wx.qidian.hosts.mp-host=http://proxy.com/
```
3. 自动注入的类型
-- `WxMpService`
-- `WxMpConfigStorage`
+- `WxQidianService`
+- `WxQidianConfigStorage`
4、参考 demo:
https://github.com/binarywang/wx-java-mp-demo
From 43ad965cee5822ea4894687fe1d664019e52d363 Mon Sep 17 00:00:00 2001
From: monch <43719648+monchcc@users.noreply.github.com>
Date: Sun, 8 Sep 2024 16:06:54 +0800
Subject: [PATCH 048/385] =?UTF-8?q?:new:=20#3360=20=E3=80=90=E5=B0=8F?=
=?UTF-8?q?=E7=A8=8B=E5=BA=8F=E3=80=91=E5=A2=9E=E5=8A=A0=E6=94=AF=E6=8C=81?=
=?UTF-8?q?=E5=A4=9A=E5=B0=8F=E7=A8=8B=E5=BA=8F=E8=B4=A6=E5=8F=B7=E7=9A=84?=
=?UTF-8?q?spring-boot-starter=E7=BB=84=E4=BB=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
spring-boot-starters/pom.xml | 1 +
.../README.md | 96 +++++++++++
.../pom.xml | 72 ++++++++
.../WxMaMultiAutoConfiguration.java | 14 ++
.../WxMaMultiServiceConfiguration.java | 25 +++
.../services/AbstractWxMaConfiguration.java | 147 +++++++++++++++++
.../services/WxMaInJedisConfiguration.java | 76 +++++++++
.../services/WxMaInMemoryConfiguration.java | 39 +++++
.../services/WxMaInRedissonConfiguration.java | 67 ++++++++
.../properties/WxMaMultiProperties.java | 154 ++++++++++++++++++
.../properties/WxMaMultiRedisProperties.java | 56 +++++++
.../properties/WxMaSingleProperties.java | 40 +++++
.../miniapp/service/WxMaMultiServices.java | 27 +++
.../service/WxMaMultiServicesImpl.java | 36 ++++
.../main/resources/META-INF/spring.factories | 2 +
...ot.autoconfigure.AutoConfiguration.imports | 1 +
.../README.md | 2 +-
.../services/AbstractWxMpConfiguration.java | 14 +-
.../services/WxMpInJedisConfiguration.java | 22 +--
.../services/WxMpInMemoryConfiguration.java | 8 +-
.../services/WxMpInRedissonConfiguration.java | 20 +--
21 files changed, 886 insertions(+), 33 deletions(-)
create mode 100644 spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/README.md
create mode 100644 spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/pom.xml
create mode 100644 spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/autoconfigure/WxMaMultiAutoConfiguration.java
create mode 100644 spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/configuration/WxMaMultiServiceConfiguration.java
create mode 100644 spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/configuration/services/AbstractWxMaConfiguration.java
create mode 100644 spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/configuration/services/WxMaInJedisConfiguration.java
create mode 100644 spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/configuration/services/WxMaInMemoryConfiguration.java
create mode 100644 spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/configuration/services/WxMaInRedissonConfiguration.java
create mode 100644 spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/properties/WxMaMultiProperties.java
create mode 100644 spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/properties/WxMaMultiRedisProperties.java
create mode 100644 spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/properties/WxMaSingleProperties.java
create mode 100644 spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/service/WxMaMultiServices.java
create mode 100644 spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/service/WxMaMultiServicesImpl.java
create mode 100644 spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/resources/META-INF/spring.factories
create mode 100644 spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
diff --git a/spring-boot-starters/pom.xml b/spring-boot-starters/pom.xml
index ae90dd12f5..bc6b79267a 100644
--- a/spring-boot-starters/pom.xml
+++ b/spring-boot-starters/pom.xml
@@ -18,6 +18,7 @@
+ wx-java-miniapp-multi-spring-boot-starter
wx-java-miniapp-spring-boot-starter
wx-java-mp-multi-spring-boot-starter
wx-java-mp-spring-boot-starter
diff --git a/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/README.md b/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/README.md
new file mode 100644
index 0000000000..ccc0d5bf5f
--- /dev/null
+++ b/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/README.md
@@ -0,0 +1,96 @@
+# wx-java-miniapp-multi-spring-boot-starter
+
+## 快速开始
+
+1. 引入依赖
+ ```xml
+
+ com.github.binarywang
+ wx-java-miniapp-multi-spring-boot-starter
+ ${version}
+
+ ```
+2. 添加配置(application.properties)
+ ```properties
+ # 公众号配置
+ ## 应用 1 配置(必填)
+ wx.ma.apps.tenantId1.app-id=appId
+ wx.ma.apps.tenantId1.app-secret=@secret
+ ## 选填
+ wx.ma.apps.tenantId1.token=@token
+ wx.ma.apps.tenantId1.aes-key=@aesKey
+ wx.ma.apps.tenantId1.use-stable-access-token=@useStableAccessToken
+ ## 应用 2 配置(必填)
+ wx.ma.apps.tenantId2.app-id=@appId
+ wx.ma.apps.tenantId2.app-secret =@secret
+ ## 选填
+ wx.ma.apps.tenantId2.token=@token
+ wx.ma.apps.tenantId2.aes-key=@aesKey
+ wx.ma.apps.tenantId2.use-stable-access-token=@useStableAccessToken
+
+ # ConfigStorage 配置(选填)
+ ## 配置类型: memory(默认), jedis, redisson
+ wx.ma.config-storage.type=memory
+ ## 相关redis前缀配置: wx:ma:multi(默认)
+ wx.ma.config-storage.key-prefix=wx:ma:multi
+ wx.ma.config-storage.redis.host=127.0.0.1
+ wx.ma.config-storage.redis.port=6379
+ ## 单机和 sentinel 同时存在时,优先使用sentinel配置
+ # wx.ma.config-storage.redis.sentinel-ips=127.0.0.1:16379,127.0.0.1:26379
+ # wx.ma.config-storage.redis.sentinel-name=mymaster
+
+ # http 客户端配置(选填)
+ ## # http客户端类型: http_client(默认), ok_http, jodd_http
+ wx.ma.config-storage.http-client-type=http_client
+ wx.ma.config-storage.http-proxy-host=
+ wx.ma.config-storage.http-proxy-port=
+ wx.ma.config-storage.http-proxy-username=
+ wx.ma.config-storage.http-proxy-password=
+ ## 最大重试次数,默认:5 次,如果小于 0,则为 0
+ wx.ma.config-storage.max-retry-times=5
+ ## 重试时间间隔步进,默认:1000 毫秒,如果小于 0,则为 1000
+ wx.ma.config-storage.retry-sleep-millis=1000
+ ```
+3. 自动注入的类型:`WxMaMultiServices`
+
+4. 使用样例
+
+```java
+import com.binarywang.spring.starter.wxjava.miniapp.service.WxMaMultiServices;
+import com.binarywang.spring.starter.wxjava.miniapp.service.WxMaMultiServices;
+import cn.binarywang.wx.miniapp.api.WxMaService;
+import cn.binarywang.wx.miniapp.api.WxMaUserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class DemoService {
+ @Autowired
+ private WxMaMultiServices wxMaMultiServices;
+
+ public void test() {
+ // 应用 1 的 WxMaService
+ WxMaService wxMaService1 = wxMaMultiServices.getWxMaService("tenantId1");
+ WxMaUserService userService1 = wxMaService1.getUserService();
+ userService1.userInfo("xxx");
+ // todo ...
+
+ // 应用 2 的 WxMaService
+ WxMaService wxMaService2 = wxMaMultiServices.getWxMaService("tenantId2");
+ WxMaUserService userService2 = wxMaService2.getUserService();
+ userService2.userInfo("xxx");
+ // todo ...
+
+ // 应用 3 的 WxMaService
+ WxMaService wxMaService3 = wxMaMultiServices.getWxMaService("tenantId3");
+ // 判断是否为空
+ if (wxMaService3 == null) {
+ // todo wxMaService3 为空,请先配置 tenantId3 微信公众号应用参数
+ return;
+ }
+ WxMaUserService userService3 = wxMaService3.getUserService();
+ userService3.userInfo("xxx");
+ // todo ...
+ }
+}
+```
diff --git a/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/pom.xml
new file mode 100644
index 0000000000..d0ae396436
--- /dev/null
+++ b/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/pom.xml
@@ -0,0 +1,72 @@
+
+
+
+ wx-java-spring-boot-starters
+ com.github.binarywang
+ 4.6.4.B
+
+ 4.0.0
+
+ wx-java-miniapp-multi-spring-boot-starter
+ WxJava - Spring Boot Starter for MiniApp::支持多账号配置
+ 微信公众号开发的 Spring Boot Starter::支持多账号配置
+
+
+
+ com.github.binarywang
+ weixin-java-miniapp
+ ${project.version}
+
+
+ redis.clients
+ jedis
+ provided
+
+
+ org.redisson
+ redisson
+ provided
+
+
+ org.springframework.data
+ spring-data-redis
+ provided
+
+
+ org.jodd
+ jodd-http
+ provided
+
+
+ com.squareup.okhttp3
+ okhttp
+ provided
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ ${spring.boot.version}
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ 2.2.1
+
+
+ attach-sources
+
+ jar-no-fork
+
+
+
+
+
+
+
+
diff --git a/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/autoconfigure/WxMaMultiAutoConfiguration.java b/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/autoconfigure/WxMaMultiAutoConfiguration.java
new file mode 100644
index 0000000000..bd03751c37
--- /dev/null
+++ b/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/autoconfigure/WxMaMultiAutoConfiguration.java
@@ -0,0 +1,14 @@
+package com.binarywang.spring.starter.wxjava.miniapp.autoconfigure;
+
+import com.binarywang.spring.starter.wxjava.miniapp.configuration.WxMaMultiServiceConfiguration;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+
+/**
+ * @author monch
+ * created on 2024/9/6
+ */
+@Configuration
+@Import(WxMaMultiServiceConfiguration.class)
+public class WxMaMultiAutoConfiguration {
+}
diff --git a/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/configuration/WxMaMultiServiceConfiguration.java b/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/configuration/WxMaMultiServiceConfiguration.java
new file mode 100644
index 0000000000..69fb3b9a0e
--- /dev/null
+++ b/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/configuration/WxMaMultiServiceConfiguration.java
@@ -0,0 +1,25 @@
+package com.binarywang.spring.starter.wxjava.miniapp.configuration;
+
+import com.binarywang.spring.starter.wxjava.miniapp.configuration.services.WxMaInJedisConfiguration;
+import com.binarywang.spring.starter.wxjava.miniapp.configuration.services.WxMaInMemoryConfiguration;
+import com.binarywang.spring.starter.wxjava.miniapp.configuration.services.WxMaInRedissonConfiguration;
+import com.binarywang.spring.starter.wxjava.miniapp.properties.WxMaMultiProperties;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+
+/**
+ * 微信小程序相关服务自动注册
+ *
+ * @author monch
+ * created on 2024/9/6
+ */
+@Configuration
+@EnableConfigurationProperties(WxMaMultiProperties.class)
+@Import({
+ WxMaInJedisConfiguration.class,
+ WxMaInMemoryConfiguration.class,
+ WxMaInRedissonConfiguration.class,
+})
+public class WxMaMultiServiceConfiguration {
+}
diff --git a/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/configuration/services/AbstractWxMaConfiguration.java b/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/configuration/services/AbstractWxMaConfiguration.java
new file mode 100644
index 0000000000..27ff84763b
--- /dev/null
+++ b/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/configuration/services/AbstractWxMaConfiguration.java
@@ -0,0 +1,147 @@
+package com.binarywang.spring.starter.wxjava.miniapp.configuration.services;
+
+import com.binarywang.spring.starter.wxjava.miniapp.properties.WxMaMultiProperties;
+import com.binarywang.spring.starter.wxjava.miniapp.properties.WxMaSingleProperties;
+import com.binarywang.spring.starter.wxjava.miniapp.service.WxMaMultiServices;
+import com.binarywang.spring.starter.wxjava.miniapp.service.WxMaMultiServicesImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import cn.binarywang.wx.miniapp.api.WxMaService;
+import cn.binarywang.wx.miniapp.api.impl.WxMaServiceHttpClientImpl;
+import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
+import cn.binarywang.wx.miniapp.api.impl.WxMaServiceJoddHttpImpl;
+import cn.binarywang.wx.miniapp.api.impl.WxMaServiceOkHttpImpl;
+import cn.binarywang.wx.miniapp.config.WxMaConfig;
+import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * WxMaConfigStorage 抽象配置类
+ *
+ * @author monch
+ * created on 2024/9/6
+ */
+@RequiredArgsConstructor
+@Slf4j
+public abstract class AbstractWxMaConfiguration {
+
+ protected WxMaMultiServices wxMaMultiServices(WxMaMultiProperties wxMaMultiProperties) {
+ Map appsMap = wxMaMultiProperties.getApps();
+ if (appsMap == null || appsMap.isEmpty()) {
+ log.warn("微信公众号应用参数未配置,通过 WxMaMultiServices#getWxMaService(\"tenantId\")获取实例将返回空");
+ return new WxMaMultiServicesImpl();
+ }
+ /**
+ * 校验 appId 是否唯一,避免使用 redis 缓存 token、ticket 时错乱。
+ *
+ * 查看 {@link cn.binarywang.wx.miniapp.config.impl.WxMaRedisConfigImpl#setAppId(String)}
+ */
+ Collection apps = appsMap.values();
+ if (apps.size() > 1) {
+ // 校验 appId 是否唯一
+ boolean multi = apps.stream()
+ // 没有 appId,如果不判断是否为空,这里会报 NPE 异常
+ .collect(Collectors.groupingBy(c -> c.getAppId() == null ? 0 : c.getAppId(), Collectors.counting()))
+ .entrySet().stream().anyMatch(e -> e.getValue() > 1);
+ if (multi) {
+ throw new RuntimeException("请确保微信公众号配置 appId 的唯一性");
+ }
+ }
+ WxMaMultiServicesImpl services = new WxMaMultiServicesImpl();
+
+ Set> entries = appsMap.entrySet();
+ for (Map.Entry entry : entries) {
+ String tenantId = entry.getKey();
+ WxMaSingleProperties wxMaSingleProperties = entry.getValue();
+ WxMaDefaultConfigImpl storage = this.wxMaConfigStorage(wxMaMultiProperties);
+ this.configApp(storage, wxMaSingleProperties);
+ this.configHttp(storage, wxMaMultiProperties.getConfigStorage());
+ WxMaService wxMaService = this.wxMaService(storage, wxMaMultiProperties);
+ services.addWxMaService(tenantId, wxMaService);
+ }
+ return services;
+ }
+
+ /**
+ * 配置 WxMaDefaultConfigImpl
+ *
+ * @param wxMaMultiProperties 参数
+ * @return WxMaDefaultConfigImpl
+ */
+ protected abstract WxMaDefaultConfigImpl wxMaConfigStorage(WxMaMultiProperties wxMaMultiProperties);
+
+ public WxMaService wxMaService(WxMaConfig wxMaConfig, WxMaMultiProperties wxMaMultiProperties) {
+ WxMaMultiProperties.ConfigStorage storage = wxMaMultiProperties.getConfigStorage();
+ WxMaMultiProperties.HttpClientType httpClientType = storage.getHttpClientType();
+ WxMaService wxMaService;
+ switch (httpClientType) {
+ case OK_HTTP:
+ wxMaService = new WxMaServiceOkHttpImpl();
+ break;
+ case JODD_HTTP:
+ wxMaService = new WxMaServiceJoddHttpImpl();
+ break;
+ case HTTP_CLIENT:
+ wxMaService = new WxMaServiceHttpClientImpl();
+ break;
+ default:
+ wxMaService = new WxMaServiceImpl();
+ break;
+ }
+
+ wxMaService.setWxMaConfig(wxMaConfig);
+ int maxRetryTimes = storage.getMaxRetryTimes();
+ if (maxRetryTimes < 0) {
+ maxRetryTimes = 0;
+ }
+ int retrySleepMillis = storage.getRetrySleepMillis();
+ if (retrySleepMillis < 0) {
+ retrySleepMillis = 1000;
+ }
+ wxMaService.setRetrySleepMillis(retrySleepMillis);
+ wxMaService.setMaxRetryTimes(maxRetryTimes);
+ return wxMaService;
+ }
+
+ private void configApp(WxMaDefaultConfigImpl config, WxMaSingleProperties corpProperties) {
+ String appId = corpProperties.getAppId();
+ String appSecret = corpProperties.getAppSecret();
+ String token = corpProperties.getToken();
+ String aesKey = corpProperties.getAesKey();
+ boolean useStableAccessToken = corpProperties.isUseStableAccessToken();
+
+ config.setAppid(appId);
+ config.setSecret(appSecret);
+ if (StringUtils.isNotBlank(token)) {
+ config.setToken(token);
+ }
+ if (StringUtils.isNotBlank(aesKey)) {
+ config.setAesKey(aesKey);
+ }
+ config.useStableAccessToken(useStableAccessToken);
+ }
+
+ private void configHttp(WxMaDefaultConfigImpl config, WxMaMultiProperties.ConfigStorage storage) {
+ String httpProxyHost = storage.getHttpProxyHost();
+ Integer httpProxyPort = storage.getHttpProxyPort();
+ String httpProxyUsername = storage.getHttpProxyUsername();
+ String httpProxyPassword = storage.getHttpProxyPassword();
+ if (StringUtils.isNotBlank(httpProxyHost)) {
+ config.setHttpProxyHost(httpProxyHost);
+ if (httpProxyPort != null) {
+ config.setHttpProxyPort(httpProxyPort);
+ }
+ if (StringUtils.isNotBlank(httpProxyUsername)) {
+ config.setHttpProxyUsername(httpProxyUsername);
+ }
+ if (StringUtils.isNotBlank(httpProxyPassword)) {
+ config.setHttpProxyPassword(httpProxyPassword);
+ }
+ }
+ }
+}
diff --git a/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/configuration/services/WxMaInJedisConfiguration.java b/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/configuration/services/WxMaInJedisConfiguration.java
new file mode 100644
index 0000000000..52eeffe7e4
--- /dev/null
+++ b/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/configuration/services/WxMaInJedisConfiguration.java
@@ -0,0 +1,76 @@
+package com.binarywang.spring.starter.wxjava.miniapp.configuration.services;
+
+import cn.binarywang.wx.miniapp.config.impl.WxMaRedisConfigImpl;
+import com.binarywang.spring.starter.wxjava.miniapp.properties.WxMaMultiProperties;
+import com.binarywang.spring.starter.wxjava.miniapp.properties.WxMaMultiRedisProperties;
+import com.binarywang.spring.starter.wxjava.miniapp.service.WxMaMultiServices;
+import lombok.RequiredArgsConstructor;
+import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import redis.clients.jedis.JedisPool;
+import redis.clients.jedis.JedisPoolConfig;
+
+/**
+ * 自动装配基于 jedis 策略配置
+ *
+ * @author monch
+ * created on 2024/9/6
+ */
+@Configuration
+@ConditionalOnProperty(
+ prefix = WxMaMultiProperties.PREFIX + ".config-storage", name = "type", havingValue = "jedis"
+)
+@RequiredArgsConstructor
+public class WxMaInJedisConfiguration extends AbstractWxMaConfiguration {
+ private final WxMaMultiProperties wxMaMultiProperties;
+ private final ApplicationContext applicationContext;
+
+ @Bean
+ public WxMaMultiServices wxMaMultiServices() {
+ return this.wxMaMultiServices(wxMaMultiProperties);
+ }
+
+ @Override
+ protected WxMaDefaultConfigImpl wxMaConfigStorage(WxMaMultiProperties wxMaMultiProperties) {
+ return this.configRedis(wxMaMultiProperties);
+ }
+
+ private WxMaDefaultConfigImpl configRedis(WxMaMultiProperties wxMaMultiProperties) {
+ WxMaMultiRedisProperties wxMaMultiRedisProperties = wxMaMultiProperties.getConfigStorage().getRedis();
+ JedisPool jedisPool;
+ if (wxMaMultiRedisProperties != null && StringUtils.isNotEmpty(wxMaMultiRedisProperties.getHost())) {
+ jedisPool = getJedisPool(wxMaMultiProperties);
+ } else {
+ jedisPool = applicationContext.getBean(JedisPool.class);
+ }
+ return new WxMaRedisConfigImpl(jedisPool);
+ }
+
+ private JedisPool getJedisPool(WxMaMultiProperties wxMaMultiProperties) {
+ WxMaMultiProperties.ConfigStorage storage = wxMaMultiProperties.getConfigStorage();
+ WxMaMultiRedisProperties redis = storage.getRedis();
+
+ JedisPoolConfig config = new JedisPoolConfig();
+ if (redis.getMaxActive() != null) {
+ config.setMaxTotal(redis.getMaxActive());
+ }
+ if (redis.getMaxIdle() != null) {
+ config.setMaxIdle(redis.getMaxIdle());
+ }
+ if (redis.getMaxWaitMillis() != null) {
+ config.setMaxWaitMillis(redis.getMaxWaitMillis());
+ }
+ if (redis.getMinIdle() != null) {
+ config.setMinIdle(redis.getMinIdle());
+ }
+ config.setTestOnBorrow(true);
+ config.setTestWhileIdle(true);
+
+ return new JedisPool(config, redis.getHost(), redis.getPort(),
+ redis.getTimeout(), redis.getPassword(), redis.getDatabase());
+ }
+}
diff --git a/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/configuration/services/WxMaInMemoryConfiguration.java b/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/configuration/services/WxMaInMemoryConfiguration.java
new file mode 100644
index 0000000000..3c8202a6b3
--- /dev/null
+++ b/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/configuration/services/WxMaInMemoryConfiguration.java
@@ -0,0 +1,39 @@
+package com.binarywang.spring.starter.wxjava.miniapp.configuration.services;
+
+import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
+import com.binarywang.spring.starter.wxjava.miniapp.properties.WxMaMultiProperties;
+import com.binarywang.spring.starter.wxjava.miniapp.service.WxMaMultiServices;
+import lombok.RequiredArgsConstructor;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 自动装配基于内存策略配置
+ *
+ * @author monch
+ * created on 2024/9/6
+ */
+@Configuration
+@ConditionalOnProperty(
+ prefix = WxMaMultiProperties.PREFIX + ".config-storage", name = "type", havingValue = "memory", matchIfMissing = true
+)
+@RequiredArgsConstructor
+public class WxMaInMemoryConfiguration extends AbstractWxMaConfiguration {
+ private final WxMaMultiProperties wxMaMultiProperties;
+
+ @Bean
+ public WxMaMultiServices wxMaMultiServices() {
+ return this.wxMaMultiServices(wxMaMultiProperties);
+ }
+
+ @Override
+ protected WxMaDefaultConfigImpl wxMaConfigStorage(WxMaMultiProperties wxMaMultiProperties) {
+ return this.configInMemory();
+ }
+
+ private WxMaDefaultConfigImpl configInMemory() {
+ return new WxMaDefaultConfigImpl();
+ // return new WxMaDefaultConfigImpl();
+ }
+}
diff --git a/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/configuration/services/WxMaInRedissonConfiguration.java b/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/configuration/services/WxMaInRedissonConfiguration.java
new file mode 100644
index 0000000000..c1915400d3
--- /dev/null
+++ b/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/configuration/services/WxMaInRedissonConfiguration.java
@@ -0,0 +1,67 @@
+package com.binarywang.spring.starter.wxjava.miniapp.configuration.services;
+
+import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
+import cn.binarywang.wx.miniapp.config.impl.WxMaRedissonConfigImpl;
+import com.binarywang.spring.starter.wxjava.miniapp.properties.WxMaMultiProperties;
+import com.binarywang.spring.starter.wxjava.miniapp.properties.WxMaMultiRedisProperties;
+import com.binarywang.spring.starter.wxjava.miniapp.service.WxMaMultiServices;
+import lombok.RequiredArgsConstructor;
+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.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 自动装配基于 redisson 策略配置
+ *
+ * @author monch
+ * created on 2024/9/6
+ */
+@Configuration
+@ConditionalOnProperty(
+ prefix = WxMaMultiProperties.PREFIX + ".config-storage", name = "type", havingValue = "redisson"
+)
+@RequiredArgsConstructor
+public class WxMaInRedissonConfiguration extends AbstractWxMaConfiguration {
+ private final WxMaMultiProperties wxMaMultiProperties;
+ private final ApplicationContext applicationContext;
+
+ @Bean
+ public WxMaMultiServices wxMaMultiServices() {
+ return this.wxMaMultiServices(wxMaMultiProperties);
+ }
+
+ @Override
+ protected WxMaDefaultConfigImpl wxMaConfigStorage(WxMaMultiProperties wxMaMultiProperties) {
+ return this.configRedisson(wxMaMultiProperties);
+ }
+
+ private WxMaDefaultConfigImpl configRedisson(WxMaMultiProperties wxMaMultiProperties) {
+ WxMaMultiRedisProperties redisProperties = wxMaMultiProperties.getConfigStorage().getRedis();
+ RedissonClient redissonClient;
+ if (redisProperties != null && StringUtils.isNotEmpty(redisProperties.getHost())) {
+ redissonClient = getRedissonClient(wxMaMultiProperties);
+ } else {
+ redissonClient = applicationContext.getBean(RedissonClient.class);
+ }
+ return new WxMaRedissonConfigImpl(redissonClient, wxMaMultiProperties.getConfigStorage().getKeyPrefix());
+ }
+
+ private RedissonClient getRedissonClient(WxMaMultiProperties wxMaMultiProperties) {
+ WxMaMultiProperties.ConfigStorage storage = wxMaMultiProperties.getConfigStorage();
+ WxMaMultiRedisProperties redis = storage.getRedis();
+
+ Config config = new Config();
+ config.useSingleServer()
+ .setAddress("redis://" + redis.getHost() + ":" + redis.getPort())
+ .setDatabase(redis.getDatabase())
+ .setPassword(redis.getPassword());
+ config.setTransportMode(TransportMode.NIO);
+ return Redisson.create(config);
+ }
+}
diff --git a/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/properties/WxMaMultiProperties.java b/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/properties/WxMaMultiProperties.java
new file mode 100644
index 0000000000..6dae33d584
--- /dev/null
+++ b/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/properties/WxMaMultiProperties.java
@@ -0,0 +1,154 @@
+package com.binarywang.spring.starter.wxjava.miniapp.properties;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.context.properties.NestedConfigurationProperty;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author monch
+ * created on 2024/9/6
+ */
+@Data
+@NoArgsConstructor
+@ConfigurationProperties(WxMaMultiProperties.PREFIX)
+public class WxMaMultiProperties implements Serializable {
+ private static final long serialVersionUID = -5358245184407791011L;
+ public static final String PREFIX = "wx.ma";
+
+ private Map apps = new HashMap<>();
+
+ /**
+ * 自定义host配置
+ */
+ private HostConfig hosts;
+
+ /**
+ * 存储策略
+ */
+ private final ConfigStorage configStorage = new ConfigStorage();
+
+ @Data
+ @NoArgsConstructor
+ public static class HostConfig implements Serializable {
+ private static final long serialVersionUID = -4172767630740346001L;
+
+ /**
+ * 对应于:https://api.weixin.qq.com
+ */
+ private String apiHost;
+
+ /**
+ * 对应于:https://open.weixin.qq.com
+ */
+ private String openHost;
+
+ /**
+ * 对应于:https://mp.weixin.qq.com
+ */
+ private String mpHost;
+ }
+
+ @Data
+ @NoArgsConstructor
+ public static class ConfigStorage implements Serializable {
+ private static final long serialVersionUID = 4815731027000065434L;
+
+ /**
+ * 存储类型.
+ */
+ private StorageType type = StorageType.MEMORY;
+
+ /**
+ * 指定key前缀.
+ */
+ private String keyPrefix = "wx:ma:multi";
+
+ /**
+ * redis连接配置.
+ */
+ @NestedConfigurationProperty
+ private final WxMaMultiRedisProperties redis = new WxMaMultiRedisProperties();
+
+ /**
+ * http客户端类型.
+ */
+ private HttpClientType httpClientType = HttpClientType.HTTP_CLIENT;
+
+ /**
+ * http代理主机.
+ */
+ private String httpProxyHost;
+
+ /**
+ * http代理端口.
+ */
+ private Integer httpProxyPort;
+
+ /**
+ * http代理用户名.
+ */
+ private String httpProxyUsername;
+
+ /**
+ * http代理密码.
+ */
+ private String httpProxyPassword;
+
+ /**
+ * http 请求最大重试次数
+ *
+ * {@link cn.binarywang.wx.miniapp.api.WxMaService#setMaxRetryTimes(int)}
+ * {@link cn.binarywang.wx.miniapp.api.impl.BaseWxMaServiceImpl#setMaxRetryTimes(int)}
+ *
+ */
+ private int maxRetryTimes = 5;
+
+ /**
+ * http 请求重试间隔
+ *
+ * {@link cn.binarywang.wx.miniapp.api.WxMaService#setRetrySleepMillis(int)}
+ * {@link cn.binarywang.wx.miniapp.api.impl.BaseWxMaServiceImpl#setRetrySleepMillis(int)}
+ *
+ */
+ private int retrySleepMillis = 1000;
+ }
+
+ public enum StorageType {
+ /**
+ * 内存
+ */
+ MEMORY,
+ /**
+ * jedis
+ */
+ JEDIS,
+ /**
+ * redisson
+ */
+ REDISSON,
+ /**
+ * redisTemplate
+ */
+ REDIS_TEMPLATE
+ }
+
+ public enum HttpClientType {
+ /**
+ * HttpClient
+ */
+ HTTP_CLIENT,
+ /**
+ * OkHttp
+ */
+ OK_HTTP,
+ /**
+ * JoddHttp
+ */
+ JODD_HTTP
+ }
+}
diff --git a/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/properties/WxMaMultiRedisProperties.java b/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/properties/WxMaMultiRedisProperties.java
new file mode 100644
index 0000000000..67562c69a4
--- /dev/null
+++ b/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/properties/WxMaMultiRedisProperties.java
@@ -0,0 +1,56 @@
+package com.binarywang.spring.starter.wxjava.miniapp.properties;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * @author monch
+ * created on 2024/9/6
+ */
+@Data
+@NoArgsConstructor
+public class WxMaMultiRedisProperties implements Serializable {
+ private static final long serialVersionUID = -5924815351660074401L;
+
+ /**
+ * 主机地址.
+ */
+ private String host = "127.0.0.1";
+
+ /**
+ * 端口号.
+ */
+ private int port = 6379;
+
+ /**
+ * 密码.
+ */
+ private String password;
+
+ /**
+ * 超时.
+ */
+ private int timeout = 2000;
+
+ /**
+ * 数据库.
+ */
+ private int database = 0;
+
+ /**
+ * sentinel ips
+ */
+ private String sentinelIps;
+
+ /**
+ * sentinel name
+ */
+ private String sentinelName;
+
+ private Integer maxActive;
+ private Integer maxIdle;
+ private Integer maxWaitMillis;
+ private Integer minIdle;
+}
diff --git a/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/properties/WxMaSingleProperties.java b/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/properties/WxMaSingleProperties.java
new file mode 100644
index 0000000000..2842a2d970
--- /dev/null
+++ b/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/properties/WxMaSingleProperties.java
@@ -0,0 +1,40 @@
+package com.binarywang.spring.starter.wxjava.miniapp.properties;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * @author monch
+ * created on 2024/9/6
+ */
+@Data
+@NoArgsConstructor
+public class WxMaSingleProperties implements Serializable {
+ private static final long serialVersionUID = 1980986361098922525L;
+ /**
+ * 设置微信公众号的 appid.
+ */
+ private String appId;
+
+ /**
+ * 设置微信公众号的 app secret.
+ */
+ private String appSecret;
+
+ /**
+ * 设置微信公众号的 token.
+ */
+ private String token;
+
+ /**
+ * 设置微信公众号的 EncodingAESKey.
+ */
+ private String aesKey;
+
+ /**
+ * 是否使用稳定版 Access Token
+ */
+ private boolean useStableAccessToken = false;
+}
diff --git a/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/service/WxMaMultiServices.java b/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/service/WxMaMultiServices.java
new file mode 100644
index 0000000000..90fce690c7
--- /dev/null
+++ b/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/service/WxMaMultiServices.java
@@ -0,0 +1,27 @@
+package com.binarywang.spring.starter.wxjava.miniapp.service;
+
+
+import cn.binarywang.wx.miniapp.api.WxMaService;
+
+/**
+ * 微信小程序 {@link WxMaService} 所有实例存放类.
+ *
+ * @author monch
+ * created on 2024/9/6
+ */
+public interface WxMaMultiServices {
+ /**
+ * 通过租户 Id 获取 WxMaService
+ *
+ * @param tenantId 租户 Id
+ * @return WxMaService
+ */
+ WxMaService getWxMaService(String tenantId);
+
+ /**
+ * 根据租户 Id,从列表中移除一个 WxMaService 实例
+ *
+ * @param tenantId 租户 Id
+ */
+ void removeWxMaService(String tenantId);
+}
diff --git a/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/service/WxMaMultiServicesImpl.java b/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/service/WxMaMultiServicesImpl.java
new file mode 100644
index 0000000000..913a371f52
--- /dev/null
+++ b/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/service/WxMaMultiServicesImpl.java
@@ -0,0 +1,36 @@
+package com.binarywang.spring.starter.wxjava.miniapp.service;
+
+import cn.binarywang.wx.miniapp.api.WxMaService;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 微信小程序 {@link com.binarywang.spring.starter.wxjava.miniapp.service.WxMaMultiServices} 默认实现
+ *
+ * @author monch
+ * created on 2024/9/6
+ */
+public class WxMaMultiServicesImpl implements com.binarywang.spring.starter.wxjava.miniapp.service.WxMaMultiServices {
+ private final Map services = new ConcurrentHashMap<>();
+
+ @Override
+ public WxMaService getWxMaService(String tenantId) {
+ return this.services.get(tenantId);
+ }
+
+ /**
+ * 根据租户 Id,添加一个 WxMaService 到列表
+ *
+ * @param tenantId 租户 Id
+ * @param wxMaService WxMaService 实例
+ */
+ public void addWxMaService(String tenantId, WxMaService wxMaService) {
+ this.services.put(tenantId, wxMaService);
+ }
+
+ @Override
+ public void removeWxMaService(String tenantId) {
+ this.services.remove(tenantId);
+ }
+}
diff --git a/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/resources/META-INF/spring.factories b/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000000..bc9bec9bfb
--- /dev/null
+++ b/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+com.binarywang.spring.starter.wxjava.miniapp.autoconfigure.WxMaMultiAutoConfiguration
diff --git a/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
new file mode 100644
index 0000000000..3023f06bdd
--- /dev/null
+++ b/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -0,0 +1 @@
+com.binarywang.spring.starter.wxjava.miniapp.autoconfigure.WxMaMultiAutoConfiguration
diff --git a/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/README.md b/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/README.md
index 8c8771beca..26b593addd 100644
--- a/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/README.md
+++ b/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/README.md
@@ -61,7 +61,7 @@
4. 使用样例
```java
-import com.binarywang.spring.starter.wxjava.mp.service.WxMpMultiServices;
+import com.binarywang.spring.starter.wxjava.mp.service.WxMaMultiServices;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.WxMpUserService;
import org.springframework.beans.factory.annotation.Autowired;
diff --git a/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/configuration/services/AbstractWxMpConfiguration.java b/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/configuration/services/AbstractWxMpConfiguration.java
index 0fa722a611..4e55fb4580 100644
--- a/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/configuration/services/AbstractWxMpConfiguration.java
+++ b/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/configuration/services/AbstractWxMpConfiguration.java
@@ -31,8 +31,8 @@
@Slf4j
public abstract class AbstractWxMpConfiguration {
- protected WxMpMultiServices wxMpMultiServices(WxMpMultiProperties wxCpMultiProperties) {
- Map appsMap = wxCpMultiProperties.getApps();
+ protected WxMpMultiServices wxMpMultiServices(WxMpMultiProperties wxMpMultiProperties) {
+ Map appsMap = wxMpMultiProperties.getApps();
if (appsMap == null || appsMap.isEmpty()) {
log.warn("微信公众号应用参数未配置,通过 WxMpMultiServices#getWxMpService(\"tenantId\")获取实例将返回空");
return new WxMpMultiServicesImpl();
@@ -59,12 +59,12 @@ protected WxMpMultiServices wxMpMultiServices(WxMpMultiProperties wxCpMultiPrope
for (Map.Entry entry : entries) {
String tenantId = entry.getKey();
WxMpSingleProperties wxMpSingleProperties = entry.getValue();
- WxMpDefaultConfigImpl storage = this.wxMpConfigStorage(wxCpMultiProperties);
+ WxMpDefaultConfigImpl storage = this.wxMpConfigStorage(wxMpMultiProperties);
this.configApp(storage, wxMpSingleProperties);
- this.configHttp(storage, wxCpMultiProperties.getConfigStorage());
- this.configHost(storage, wxCpMultiProperties.getHosts());
- WxMpService wxCpService = this.wxMpService(storage, wxCpMultiProperties);
- services.addWxMpService(tenantId, wxCpService);
+ this.configHttp(storage, wxMpMultiProperties.getConfigStorage());
+ this.configHost(storage, wxMpMultiProperties.getHosts());
+ WxMpService wxMpService = this.wxMpService(storage, wxMpMultiProperties);
+ services.addWxMpService(tenantId, wxMpService);
}
return services;
}
diff --git a/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/configuration/services/WxMpInJedisConfiguration.java b/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/configuration/services/WxMpInJedisConfiguration.java
index 023602d296..c137d0c087 100644
--- a/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/configuration/services/WxMpInJedisConfiguration.java
+++ b/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/configuration/services/WxMpInJedisConfiguration.java
@@ -27,32 +27,32 @@
)
@RequiredArgsConstructor
public class WxMpInJedisConfiguration extends AbstractWxMpConfiguration {
- private final WxMpMultiProperties wxCpMultiProperties;
+ private final WxMpMultiProperties wxMpMultiProperties;
private final ApplicationContext applicationContext;
@Bean
public WxMpMultiServices wxMpMultiServices() {
- return this.wxMpMultiServices(wxCpMultiProperties);
+ return this.wxMpMultiServices(wxMpMultiProperties);
}
@Override
- protected WxMpDefaultConfigImpl wxMpConfigStorage(WxMpMultiProperties wxCpMultiProperties) {
- return this.configRedis(wxCpMultiProperties);
+ protected WxMpDefaultConfigImpl wxMpConfigStorage(WxMpMultiProperties wxMpMultiProperties) {
+ return this.configRedis(wxMpMultiProperties);
}
- private WxMpDefaultConfigImpl configRedis(WxMpMultiProperties wxCpMultiProperties) {
- WxMpMultiRedisProperties wxCpMultiRedisProperties = wxCpMultiProperties.getConfigStorage().getRedis();
+ private WxMpDefaultConfigImpl configRedis(WxMpMultiProperties wxMpMultiProperties) {
+ WxMpMultiRedisProperties wxMpMultiRedisProperties = wxMpMultiProperties.getConfigStorage().getRedis();
JedisPool jedisPool;
- if (wxCpMultiRedisProperties != null && StringUtils.isNotEmpty(wxCpMultiRedisProperties.getHost())) {
- jedisPool = getJedisPool(wxCpMultiProperties);
+ if (wxMpMultiRedisProperties != null && StringUtils.isNotEmpty(wxMpMultiRedisProperties.getHost())) {
+ jedisPool = getJedisPool(wxMpMultiProperties);
} else {
jedisPool = applicationContext.getBean(JedisPool.class);
}
- return new WxMpRedisConfigImpl(new JedisWxRedisOps(jedisPool), wxCpMultiProperties.getConfigStorage().getKeyPrefix());
+ return new WxMpRedisConfigImpl(new JedisWxRedisOps(jedisPool), wxMpMultiProperties.getConfigStorage().getKeyPrefix());
}
- private JedisPool getJedisPool(WxMpMultiProperties wxCpMultiProperties) {
- WxMpMultiProperties.ConfigStorage storage = wxCpMultiProperties.getConfigStorage();
+ private JedisPool getJedisPool(WxMpMultiProperties wxMpMultiProperties) {
+ WxMpMultiProperties.ConfigStorage storage = wxMpMultiProperties.getConfigStorage();
WxMpMultiRedisProperties redis = storage.getRedis();
JedisPoolConfig config = new JedisPoolConfig();
diff --git a/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/configuration/services/WxMpInMemoryConfiguration.java b/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/configuration/services/WxMpInMemoryConfiguration.java
index 3e7dabed48..cd90eba114 100644
--- a/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/configuration/services/WxMpInMemoryConfiguration.java
+++ b/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/configuration/services/WxMpInMemoryConfiguration.java
@@ -21,15 +21,15 @@
)
@RequiredArgsConstructor
public class WxMpInMemoryConfiguration extends AbstractWxMpConfiguration {
- private final WxMpMultiProperties wxCpMultiProperties;
+ private final WxMpMultiProperties wxMpMultiProperties;
@Bean
- public WxMpMultiServices wxCpMultiServices() {
- return this.wxMpMultiServices(wxCpMultiProperties);
+ public WxMpMultiServices wxMpMultiServices() {
+ return this.wxMpMultiServices(wxMpMultiProperties);
}
@Override
- protected WxMpDefaultConfigImpl wxMpConfigStorage(WxMpMultiProperties wxCpMultiProperties) {
+ protected WxMpDefaultConfigImpl wxMpConfigStorage(WxMpMultiProperties wxMpMultiProperties) {
return this.configInMemory();
}
diff --git a/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/configuration/services/WxMpInRedissonConfiguration.java b/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/configuration/services/WxMpInRedissonConfiguration.java
index f679ca4d4e..a2b606c4a6 100644
--- a/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/configuration/services/WxMpInRedissonConfiguration.java
+++ b/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/configuration/services/WxMpInRedissonConfiguration.java
@@ -28,32 +28,32 @@
)
@RequiredArgsConstructor
public class WxMpInRedissonConfiguration extends AbstractWxMpConfiguration {
- private final WxMpMultiProperties wxCpMultiProperties;
+ private final WxMpMultiProperties wxMpMultiProperties;
private final ApplicationContext applicationContext;
@Bean
public WxMpMultiServices wxMpMultiServices() {
- return this.wxMpMultiServices(wxCpMultiProperties);
+ return this.wxMpMultiServices(wxMpMultiProperties);
}
@Override
- protected WxMpDefaultConfigImpl wxMpConfigStorage(WxMpMultiProperties wxCpMultiProperties) {
- return this.configRedisson(wxCpMultiProperties);
+ protected WxMpDefaultConfigImpl wxMpConfigStorage(WxMpMultiProperties wxMpMultiProperties) {
+ return this.configRedisson(wxMpMultiProperties);
}
- private WxMpDefaultConfigImpl configRedisson(WxMpMultiProperties wxCpMultiProperties) {
- WxMpMultiRedisProperties redisProperties = wxCpMultiProperties.getConfigStorage().getRedis();
+ private WxMpDefaultConfigImpl configRedisson(WxMpMultiProperties wxMpMultiProperties) {
+ WxMpMultiRedisProperties redisProperties = wxMpMultiProperties.getConfigStorage().getRedis();
RedissonClient redissonClient;
if (redisProperties != null && StringUtils.isNotEmpty(redisProperties.getHost())) {
- redissonClient = getRedissonClient(wxCpMultiProperties);
+ redissonClient = getRedissonClient(wxMpMultiProperties);
} else {
redissonClient = applicationContext.getBean(RedissonClient.class);
}
- return new WxMpRedissonConfigImpl(redissonClient, wxCpMultiProperties.getConfigStorage().getKeyPrefix());
+ return new WxMpRedissonConfigImpl(redissonClient, wxMpMultiProperties.getConfigStorage().getKeyPrefix());
}
- private RedissonClient getRedissonClient(WxMpMultiProperties wxCpMultiProperties) {
- WxMpMultiProperties.ConfigStorage storage = wxCpMultiProperties.getConfigStorage();
+ private RedissonClient getRedissonClient(WxMpMultiProperties wxMpMultiProperties) {
+ WxMpMultiProperties.ConfigStorage storage = wxMpMultiProperties.getConfigStorage();
WxMpMultiRedisProperties redis = storage.getRedis();
Config config = new Config();
From 0b9444d948df3bdd3df51a5c9996db25dc6ed063 Mon Sep 17 00:00:00 2001
From: msgpo
Date: Tue, 10 Sep 2024 14:48:02 +0800
Subject: [PATCH 049/385] =?UTF-8?q?:art:=20#3367=E3=80=90=E4=BC=81?=
=?UTF-8?q?=E4=B8=9A=E5=BE=AE=E4=BF=A1=E3=80=91=E8=8E=B7=E5=8F=96=E5=AE=A1?=
=?UTF-8?q?=E6=89=B9=E7=94=B3=E8=AF=B7=E8=AF=A6=E6=83=85=E6=8E=A5=E5=8F=A3?=
=?UTF-8?q?=E4=B8=AD=E5=81=87=E5=8B=A4=E7=BB=84=E4=BB=B6=E6=96=B0=E5=A2=9E?=
=?UTF-8?q?=E6=97=B6=E9=95=BF=E6=94=AF=E6=8C=81=E6=8C=89=E5=A4=A9=E5=88=86?=
=?UTF-8?q?=E7=89=87=E4=BF=A1=E6=81=AF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../cp/bean/oa/applydata/ContentValue.java | 25 +++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/applydata/ContentValue.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/applydata/ContentValue.java
index 039ccaa9dc..e8c379bd33 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/applydata/ContentValue.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/applydata/ContentValue.java
@@ -143,6 +143,8 @@ public static class Attendance implements Serializable {
@SerializedName("date_range")
private DataRange dateRange;
private Integer type;
+ @SerializedName("slice_info")
+ private SliceInfo sliceInfo;
/**
* The type Data range.
@@ -158,6 +160,29 @@ public static class DataRange implements Serializable {
@SerializedName("new_duration")
private Long duration;
}
+
+ /**
+ * The type slice_info
+ */
+ @Data
+ public static class SliceInfo implements Serializable {
+ private static final long serialVersionUID = 4369560551634923348L;
+ @SerializedName("day_items")
+ private List dayItems;
+ private Long duration;
+ private Integer state;
+
+ /**
+ * The type day_items
+ */
+ @Data
+ public static class DayItems implements Serializable {
+ private static final long serialVersionUID = -7076615961077782776L;
+ private Long daytime;
+ private Long duration;
+ }
+ }
+
}
/**
From 31d2f721945e68b0a390f24ca246cb6b17ec53cd Mon Sep 17 00:00:00 2001
From: Winnie
Date: Tue, 10 Sep 2024 15:04:26 +0800
Subject: [PATCH 050/385] =?UTF-8?q?:new:=20#3368=20=E3=80=90=E8=A7=86?=
=?UTF-8?q?=E9=A2=91=E5=8F=B7=E3=80=91=E6=96=B0=E5=A2=9E=E8=A7=86=E9=A2=91?=
=?UTF-8?q?=E5=8F=B7=E5=8A=A9=E6=89=8B-=E7=9B=B4=E6=92=AD=E5=A4=A7?=
=?UTF-8?q?=E5=B1=8F=E6=95=B0=E6=8D=AE=E3=80=81=E7=BD=97=E7=9B=98=E8=BE=BE?=
=?UTF-8?q?=E4=BA=BA=E7=89=88API=E7=9B=B8=E5=85=B3=E6=8E=A5=E5=8F=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../api/WxChannelCompassFinderService.java | 58 +++
.../api/WxChannelLiveDashboardService.java | 34 ++
.../weixin/channel/api/WxChannelService.java | 15 +
.../api/impl/BaseWxChannelServiceImpl.java | 11 +
.../WxChannelCompassFinderServiceImpl.java | 54 +++
.../WxChannelLiveDashboardServiceImpl.java | 81 ++++
.../finder/CompassFinderBaseParam.java | 30 ++
.../channel/bean/compass/finder/Field.java | 33 ++
.../bean/compass/finder/FieldData.java | 32 ++
.../channel/bean/compass/finder/Overall.java | 56 +++
.../bean/compass/finder/OverallResponse.java | 27 ++
.../compass/finder/ProductCompassData.java | 170 +++++++++
.../bean/compass/finder/ProductDataParam.java | 33 ++
.../compass/finder/ProductDataResponse.java | 27 ++
.../bean/compass/finder/ProductInfo.java | 68 ++++
.../compass/finder/ProductListResponse.java | 29 ++
.../bean/compass/finder/SaleProfileData.java | 27 ++
.../compass/finder/SaleProfileDataParam.java | 33 ++
.../finder/SaleProfileDataResponse.java | 27 ++
.../bean/live/dashboard/ConversionMetric.java | 61 +++
.../channel/bean/live/dashboard/DataNode.java | 26 ++
.../bean/live/dashboard/DataNodeList.java | 32 ++
.../live/dashboard/DataNodeSecondList.java | 33 ++
.../live/dashboard/DataNodeThirdList.java | 33 ++
.../bean/live/dashboard/Dimension.java | 38 ++
.../channel/bean/live/dashboard/Ended.java | 44 +++
.../bean/live/dashboard/EndedIndexItem.java | 38 ++
.../channel/bean/live/dashboard/Fields.java | 38 ++
.../live/dashboard/ItemConversionMetric.java | 44 +++
.../live/dashboard/LiveComparisonIndex.java | 38 ++
.../live/dashboard/LiveDashboardData.java | 38 ++
.../live/dashboard/LiveDashboardData2.java | 38 ++
.../dashboard/LiveDashboardData2Portrait.java | 33 ++
.../dashboard/LiveDashboardData2Source.java | 27 ++
.../dashboard/LiveDashboardData2Summary.java | 80 ++++
.../bean/live/dashboard/LiveDataParam.java | 30 ++
.../bean/live/dashboard/LiveDataResponse.java | 69 ++++
.../dashboard/LiveDistChannelSourceStats.java | 81 ++++
.../LiveDistributionByFlowTypeStat.java | 81 ++++
.../dashboard/LiveDistributionChannel.java | 51 +++
.../dashboard/LiveDistributionSceneStat.java | 75 ++++
.../dashboard/LiveEcConversionMetric.java | 32 ++
.../live/dashboard/LiveEcDataSummary.java | 212 +++++++++++
.../bean/live/dashboard/LiveEcProfile.java | 33 ++
.../channel/bean/live/dashboard/LiveItem.java | 32 ++
.../bean/live/dashboard/LiveListParam.java | 30 ++
.../bean/live/dashboard/LiveListResponse.java | 41 ++
.../channel/bean/live/dashboard/OnAir.java | 44 +++
.../bean/live/dashboard/OnAirIndexItem.java | 56 +++
.../channel/bean/live/dashboard/Point.java | 32 ++
.../live/dashboard/QuarterlyGrowthRate.java | 32 ++
.../channel/bean/live/dashboard/Series.java | 51 +++
.../dashboard/SingleLiveEcSpuDataPageV2.java | 39 ++
.../bean/live/dashboard/SpuBaseData.java | 68 ++++
.../channel/bean/live/dashboard/SpuData.java | 350 ++++++++++++++++++
.../SubLiveDistChannelSourceStats.java | 92 +++++
.../constant/WxChannelApiUrlConstants.java | 40 ++
.../weixin/channel/enums/DimensionType.java | 80 ++++
.../channel/enums/EcProfileDataNodeKey.java | 64 ++++
.../enums/LiveDistributionFlowType.java | 56 +++
.../enums/LiveDistributionSceneType.java | 52 +++
.../channel/enums/SaleProfileUserType.java | 56 +++
...WxChannelCompassFinderServiceImplTest.java | 66 ++++
...WxChannelLiveDashboardServiceImplTest.java | 44 +++
64 files changed, 3475 insertions(+)
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/WxChannelCompassFinderService.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/WxChannelLiveDashboardService.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/impl/WxChannelCompassFinderServiceImpl.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/impl/WxChannelLiveDashboardServiceImpl.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/CompassFinderBaseParam.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/Field.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/FieldData.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/Overall.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/OverallResponse.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/ProductCompassData.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/ProductDataParam.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/ProductDataResponse.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/ProductInfo.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/ProductListResponse.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/SaleProfileData.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/SaleProfileDataParam.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/SaleProfileDataResponse.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/ConversionMetric.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/DataNode.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/DataNodeList.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/DataNodeSecondList.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/DataNodeThirdList.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/Dimension.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/Ended.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/EndedIndexItem.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/Fields.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/ItemConversionMetric.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveComparisonIndex.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDashboardData.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDashboardData2.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDashboardData2Portrait.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDashboardData2Source.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDashboardData2Summary.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDataParam.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDataResponse.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDistChannelSourceStats.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDistributionByFlowTypeStat.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDistributionChannel.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDistributionSceneStat.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveEcConversionMetric.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveEcDataSummary.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveEcProfile.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveItem.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveListParam.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveListResponse.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/OnAir.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/OnAirIndexItem.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/Point.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/QuarterlyGrowthRate.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/Series.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/SingleLiveEcSpuDataPageV2.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/SpuBaseData.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/SpuData.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/SubLiveDistChannelSourceStats.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/DimensionType.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/EcProfileDataNodeKey.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/LiveDistributionFlowType.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/LiveDistributionSceneType.java
create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/SaleProfileUserType.java
create mode 100644 weixin-java-channel/src/test/java/me/chanjar/weixin/channel/api/impl/WxChannelCompassFinderServiceImplTest.java
create mode 100644 weixin-java-channel/src/test/java/me/chanjar/weixin/channel/api/impl/WxChannelLiveDashboardServiceImplTest.java
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/WxChannelCompassFinderService.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/WxChannelCompassFinderService.java
new file mode 100644
index 0000000000..db123f61a4
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/WxChannelCompassFinderService.java
@@ -0,0 +1,58 @@
+package me.chanjar.weixin.channel.api;
+
+import me.chanjar.weixin.channel.bean.compass.finder.OverallResponse;
+import me.chanjar.weixin.channel.bean.compass.finder.ProductDataResponse;
+import me.chanjar.weixin.channel.bean.compass.finder.ProductListResponse;
+import me.chanjar.weixin.channel.bean.compass.finder.SaleProfileDataResponse;
+import me.chanjar.weixin.common.error.WxErrorException;
+
+/**
+ * 视频号助手 罗盘达人版服务
+ *
+ * @author Winnie
+ */
+public interface WxChannelCompassFinderService {
+
+ /**
+ * 获取电商概览数据
+ *
+ * @param ds 日期,格式 yyyyMMdd
+ * @return 电商概览数据
+ *
+ * @throws WxErrorException 异常
+ */
+ OverallResponse getOverall(String ds) throws WxErrorException;
+
+ /**
+ * 获取带货商品数据
+ *
+ * @param ds 日期,格式 yyyyMMdd
+ * @param productId 商品id
+ * @return 带货商品数据
+ *
+ * @throws WxErrorException 异常
+ */
+ ProductDataResponse getProductData(String ds, String productId) throws WxErrorException;
+
+ /**
+ * 获取带货商品列表
+ *
+ * @param ds 日期,格式 yyyyMMdd
+ * @return 带货商品列表
+ *
+ * @throws WxErrorException 异常
+ */
+ ProductListResponse getProductList(String ds) throws WxErrorException;
+
+ /**
+ * 获取带货人群数据
+ *
+ * @param ds 日期,格式 yyyyMMdd
+ * @param type 用户类型,1=商品曝光用户, 2=商品点击用户, 3=购买用户, 4=首购用户, 5=复购用户, 6=直播观看用户
+ * @return 带货人群数据
+ *
+ * @throws WxErrorException 异常
+ */
+ SaleProfileDataResponse getSaleProfileData(String ds, Integer type) throws WxErrorException;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/WxChannelLiveDashboardService.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/WxChannelLiveDashboardService.java
new file mode 100644
index 0000000000..be93b06a97
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/WxChannelLiveDashboardService.java
@@ -0,0 +1,34 @@
+package me.chanjar.weixin.channel.api;
+
+import me.chanjar.weixin.channel.bean.live.dashboard.LiveDataResponse;
+import me.chanjar.weixin.channel.bean.live.dashboard.LiveListResponse;
+import me.chanjar.weixin.common.error.WxErrorException;
+
+/**
+ * 视频号助手 直播大屏数据服务
+ *
+ * @author Winnie
+ */
+public interface WxChannelLiveDashboardService {
+
+ /**
+ * 获取直播大屏直播列表
+ *
+ * @param ds 日期,格式 yyyyMMdd
+ * @return 播大屏直播列表
+ *
+ * @throws WxErrorException 异常
+ */
+ LiveListResponse getLiveList(Long ds) throws WxErrorException;
+
+ /**
+ * 获取直播大屏数据
+ *
+ * @param exportId 直播唯一ID
+ * @return 播大屏数据
+ *
+ * @throws WxErrorException 异常
+ */
+ LiveDataResponse getLiveData(String exportId) throws WxErrorException;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/WxChannelService.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/WxChannelService.java
index 9d10f51c06..48d82206b3 100644
--- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/WxChannelService.java
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/WxChannelService.java
@@ -147,4 +147,19 @@ public interface WxChannelService extends BaseWxChannelService {
* @return 会员服务
*/
WxChannelVipService getVipService();
+
+ /**
+ * 视频号助手-罗盘达人版服务
+ *
+ * @return 罗盘达人版服务
+ */
+ WxChannelCompassFinderService getCompassFinderService();
+
+ /**
+ * 视频号助手-直播大屏数据服务
+ *
+ * @return 直播大屏数据服务
+ */
+ WxChannelLiveDashboardService getLiveDashboardService();
+
}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/impl/BaseWxChannelServiceImpl.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/impl/BaseWxChannelServiceImpl.java
index 307a9e77d1..c9f874c99e 100644
--- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/impl/BaseWxChannelServiceImpl.java
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/impl/BaseWxChannelServiceImpl.java
@@ -55,6 +55,10 @@ public abstract class BaseWxChannelServiceImpl implements WxChannelService
private WxFinderLiveService finderLiveService = null;
private WxAssistantService assistantService = null;
private WxChannelVipService vipService = new WxChannelVipServiceImpl(this);
+ private final WxChannelCompassFinderService compassFinderService =
+ new WxChannelCompassFinderServiceImpl(this);
+ private final WxChannelLiveDashboardService liveDashboardService =
+ new WxChannelLiveDashboardServiceImpl(this);
protected WxChannelConfig config;
private int retrySleepMillis = 1000;
@@ -411,4 +415,11 @@ public WxAssistantService getAssistantService() {
public WxChannelVipService getVipService() {
return vipService;
}
+
+ @Override
+ public WxChannelCompassFinderService getCompassFinderService() { return compassFinderService; }
+
+ @Override
+ public WxChannelLiveDashboardService getLiveDashboardService() { return liveDashboardService; }
+
}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/impl/WxChannelCompassFinderServiceImpl.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/impl/WxChannelCompassFinderServiceImpl.java
new file mode 100644
index 0000000000..34bead89d6
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/impl/WxChannelCompassFinderServiceImpl.java
@@ -0,0 +1,54 @@
+package me.chanjar.weixin.channel.api.impl;
+
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.channel.api.WxChannelCompassFinderService;
+import me.chanjar.weixin.channel.bean.compass.finder.*;
+import me.chanjar.weixin.channel.util.ResponseUtils;
+import me.chanjar.weixin.common.error.WxErrorException;
+
+import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.CompassFinder.*;
+
+/**
+ * 视频号助手 罗盘达人版服务实现
+ *
+ * @author Winnie
+ */
+@Slf4j
+public class WxChannelCompassFinderServiceImpl implements WxChannelCompassFinderService {
+
+ /**
+ * 微信商店服务
+ */
+ private final BaseWxChannelServiceImpl shopService;
+
+ public WxChannelCompassFinderServiceImpl(BaseWxChannelServiceImpl shopService) {this.shopService = shopService;}
+
+ @Override
+ public OverallResponse getOverall(String ds) throws WxErrorException {
+ CompassFinderBaseParam param = new CompassFinderBaseParam(ds);
+ String resJson = shopService.post(GET_OVERALL_URL, param);
+ return ResponseUtils.decode(resJson, OverallResponse.class);
+ }
+
+ @Override
+ public ProductDataResponse getProductData(String ds, String productId) throws WxErrorException {
+ ProductDataParam param = new ProductDataParam(ds, productId);
+ String resJson = shopService.post(GET_PRODUCT_DATA_URL, param);
+ return ResponseUtils.decode(resJson, ProductDataResponse.class);
+ }
+
+ @Override
+ public ProductListResponse getProductList(String ds) throws WxErrorException {
+ CompassFinderBaseParam param = new CompassFinderBaseParam(ds);
+ String resJson = shopService.post(GET_PRODUCT_LIST_URL, param);
+ return ResponseUtils.decode(resJson, ProductListResponse.class);
+ }
+
+ @Override
+ public SaleProfileDataResponse getSaleProfileData(String ds, Integer type) throws WxErrorException {
+ SaleProfileDataParam param = new SaleProfileDataParam(ds, type);
+ String resJson = shopService.post(GET_SALE_PROFILE_DATA_URL, param);
+ return ResponseUtils.decode(resJson, SaleProfileDataResponse.class);
+ }
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/impl/WxChannelLiveDashboardServiceImpl.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/impl/WxChannelLiveDashboardServiceImpl.java
new file mode 100644
index 0000000000..7c9c876e9b
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/impl/WxChannelLiveDashboardServiceImpl.java
@@ -0,0 +1,81 @@
+package me.chanjar.weixin.channel.api.impl;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.channel.api.WxChannelLiveDashboardService;
+import me.chanjar.weixin.channel.bean.live.dashboard.LiveDataParam;
+import me.chanjar.weixin.channel.bean.live.dashboard.LiveDataResponse;
+import me.chanjar.weixin.channel.bean.live.dashboard.LiveListParam;
+import me.chanjar.weixin.channel.bean.live.dashboard.LiveListResponse;
+import me.chanjar.weixin.channel.util.ResponseUtils;
+import me.chanjar.weixin.common.error.WxErrorException;
+import org.apache.commons.lang3.ObjectUtils;
+
+import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.LiveDashboard.*;
+
+/**
+ * 视频号助手 直播大屏数据服务实现
+ *
+ * @author Winnie
+ */
+@Slf4j
+public class WxChannelLiveDashboardServiceImpl implements WxChannelLiveDashboardService {
+
+ /**
+ * 微信商店服务
+ */
+ private final BaseWxChannelServiceImpl shopService;
+ private final ObjectMapper objectMapper = new ObjectMapper();
+
+ public WxChannelLiveDashboardServiceImpl(BaseWxChannelServiceImpl shopService) {this.shopService = shopService;}
+
+ @Override
+ public LiveListResponse getLiveList(Long ds) throws WxErrorException {
+ LiveListParam param = new LiveListParam(ds);
+ String resJson = shopService.post(GET_LIVE_LIST_URL, param);
+ return ResponseUtils.decode(resJson, LiveListResponse.class);
+ }
+
+ @Override
+ public LiveDataResponse getLiveData(String exportId) throws WxErrorException {
+ LiveDataParam param = new LiveDataParam(exportId);
+ String resJson = shopService.post(GET_LIVE_DATA_URL, param);
+ return this.convertLiveDataResponse(resJson);
+ }
+
+ /**
+ * 微信接口获取直播数据中存在非标准JSON,方便业务处理返回前做好解析
+ * 处理参数:
+ * live_dashboard_data,live_comparison_index,live_ec_data_summary,live_ec_conversion_metric,
+ * live_ec_profile,live_distribution_channel,single_live_ec_spu_data_page_v2
+ *
+ * @param resJson 直播数据返回JSON
+ * @return LiveDataResponse
+ *
+ * @throws WxErrorException 异常
+ */
+ private LiveDataResponse convertLiveDataResponse(String resJson) throws WxErrorException {
+ try {
+ ObjectNode rootNode = (ObjectNode) objectMapper.readTree(resJson);
+ String[] dataKeyArray = new String[] {
+ "live_dashboard_data", "live_comparison_index", "live_ec_data_summary", "live_ec_conversion_metric",
+ "live_ec_profile", "live_distribution_channel", "single_live_ec_spu_data_page_v2"
+ };
+ for(String dataKey : dataKeyArray) {
+ JsonNode jsonNode = rootNode.get(dataKey);
+ if (ObjectUtils.isNotEmpty(jsonNode)) {
+ JsonNode dataJsonNode = objectMapper.readTree(jsonNode.asText());
+ rootNode.set(dataKey, dataJsonNode);
+ }
+ }
+ String json = objectMapper.writeValueAsString(rootNode);
+ return ResponseUtils.decode(json, LiveDataResponse.class);
+ } catch (JsonProcessingException e) {
+ throw new WxErrorException(e);
+ }
+ }
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/CompassFinderBaseParam.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/CompassFinderBaseParam.java
new file mode 100644
index 0000000000..78e088f9ec
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/CompassFinderBaseParam.java
@@ -0,0 +1,30 @@
+package me.chanjar.weixin.channel.bean.compass.finder;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 获取达人罗盘数据通用请求参数
+ *
+ * @author Winnie
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class CompassFinderBaseParam implements Serializable {
+
+ private static final long serialVersionUID = - 4900361041041434435L;
+
+ /**
+ * 日期,格式 yyyyMMdd
+ */
+ @JsonProperty("ds")
+ private String ds;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/Field.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/Field.java
new file mode 100644
index 0000000000..a23cde1878
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/Field.java
@@ -0,0 +1,33 @@
+package me.chanjar.weixin.channel.bean.compass.finder;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 维度数据
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+public class Field implements Serializable {
+
+ private static final long serialVersionUID = - 4243469984232948689L;
+
+ /**
+ * 维度类别名
+ */
+ @JsonProperty("field_name")
+ private String fieldName;
+
+ /**
+ * 维度指标数据列表
+ */
+ @JsonProperty("data_list")
+ private List dataList;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/FieldData.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/FieldData.java
new file mode 100644
index 0000000000..a8b82c8326
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/FieldData.java
@@ -0,0 +1,32 @@
+package me.chanjar.weixin.channel.bean.compass.finder;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 维度指标数据
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+public class FieldData implements Serializable {
+
+ private static final long serialVersionUID = - 4022953139259283599L;
+
+ /**
+ * 维度指标名
+ */
+ @JsonProperty("dim_key")
+ private String dimKey;
+
+ /**
+ * 维度指标值
+ */
+ @JsonProperty("dim_value")
+ private String dimValue;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/Overall.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/Overall.java
new file mode 100644
index 0000000000..ab77df0f97
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/Overall.java
@@ -0,0 +1,56 @@
+package me.chanjar.weixin.channel.bean.compass.finder;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 电商概览数据
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+public class Overall implements Serializable {
+
+ private static final long serialVersionUID = 2456038666608345011L;
+
+ /**
+ * 成交金额,单位分
+ */
+ @JsonProperty("pay_gmv")
+ private String payGmv;
+
+ /**
+ * 直播成交金额,单位分
+ */
+ @JsonProperty("live_pay_gmv")
+ private String livePayGmv;
+
+ /**
+ * 短视频成交金额,单位分
+ */
+ @JsonProperty("feed_pay_gmv")
+ private String feedPayGmv;
+
+ /**
+ * 橱窗成交金额,单位分
+ */
+ @JsonProperty("window_pay_gmv")
+ private String windowPayGmv;
+
+ /**
+ * 商品分享支付金额,单位分
+ */
+ @JsonProperty("product_pay_gmv")
+ private String productPayGmv;
+
+ /**
+ * 其他渠道成交金额,单位分
+ */
+ @JsonProperty("other_pay_gmv")
+ private String otherPayGmv;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/OverallResponse.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/OverallResponse.java
new file mode 100644
index 0000000000..8331726c13
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/OverallResponse.java
@@ -0,0 +1,27 @@
+package me.chanjar.weixin.channel.bean.compass.finder;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import me.chanjar.weixin.channel.bean.base.WxChannelBaseResponse;
+
+/**
+ * 获取电商概览数据响应
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+@EqualsAndHashCode(callSuper = true)
+public class OverallResponse extends WxChannelBaseResponse {
+
+ private static final long serialVersionUID = 6350218415876820956L;
+
+ /**
+ * 电商概览数据
+ */
+ @JsonProperty("data")
+ private Overall data;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/ProductCompassData.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/ProductCompassData.java
new file mode 100644
index 0000000000..d84c8d367b
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/ProductCompassData.java
@@ -0,0 +1,170 @@
+package me.chanjar.weixin.channel.bean.compass.finder;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 商品罗盘数据
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+public class ProductCompassData implements Serializable {
+
+ private static final long serialVersionUID = - 1009289493985863096L;
+
+ /**
+ * 成交金额
+ */
+ @JsonProperty("pay_gmv")
+ private String payGmv;
+
+ /**
+ * 下单金额(单位:分)
+ */
+ @JsonProperty("create_gmv")
+ private String createGmv;
+
+ /**
+ * 下单订单数
+ */
+ @JsonProperty("create_cnt")
+ private String createCnt;
+
+ /**
+ * 下单人数
+ */
+ @JsonProperty("create_uv")
+ private String createUv;
+
+ /**
+ * 下单件数
+ */
+ @JsonProperty("create_product_cnt")
+ private String createProductCnt;
+
+ /**
+ * 成交订单数
+ */
+ @JsonProperty("pay_cnt")
+ private String payCnt;
+
+ /**
+ * 成交人数
+ */
+ @JsonProperty("pay_uv")
+ private String payUv;
+
+ /**
+ * 成交件数
+ */
+ @JsonProperty("pay_product_cnt")
+ private String payProductCnt;
+
+ /**
+ * 成交金额(剔除退款)(单位:分)
+ */
+ @JsonProperty("pure_pay_gmv")
+ private String purePayGmv;
+
+ /**
+ * 成交客单价(单位:分)
+ */
+ @JsonProperty("pay_gmv_per_uv")
+ private String payGmvPerUv;
+
+ /**
+ * 实际结算金额(单位:分)
+ */
+ @JsonProperty("actual_commission")
+ private String actualCommission;
+
+ /**
+ * 预估佣金金额(单位:分)
+ */
+ @JsonProperty("predict_commission")
+ private String predictCommission;
+
+ /**
+ * 商品点击人数
+ */
+ @JsonProperty("product_click_uv")
+ private String productClickUv;
+
+ /**
+ * 商品点击次数
+ */
+ @JsonProperty("product_click_cnt")
+ private String productClickCnt;
+
+ /**
+ * 成交退款金额
+ */
+ @JsonProperty("pay_refund_gmv")
+ private String payRefundGmv;
+
+ /**
+ * 成交退款人数
+ */
+ @JsonProperty("pay_refund_uv")
+ private String payRefundUv;
+
+ /**
+ * 成交退款率
+ */
+ @JsonProperty("pay_refund_ratio")
+ private Double payRefundRatio;
+
+ /**
+ * 发货后成交退款率
+ */
+ @JsonProperty("pay_refund_after_send_ratio")
+ private Double payRefundAfterSendRatio;
+
+ /**
+ * 成交退款订单数
+ */
+ @JsonProperty("pay_refund_cnt")
+ private String payRefundCnt;
+
+ /**
+ * 成交退款件数
+ */
+ @JsonProperty("pay_refund_product_cnt")
+ private String payRefundProductCnt;
+
+ /**
+ * 发货前成交退款率
+ */
+ @JsonProperty("pay_refund_before_send_ratio")
+ private Double payRefundBeforeSendRatio;
+
+ /**
+ * 退款金额(单位:分)
+ */
+ @JsonProperty("refund_gmv")
+ private String refundGmv;
+
+ /**
+ * 退款件数
+ */
+ @JsonProperty("refund_product_cnt")
+ private String refundProductCnt;
+
+ /**
+ * 退款订单数
+ */
+ @JsonProperty("refund_cnt")
+ private String refundCnt;
+
+ /**
+ * 退款人数
+ */
+ @JsonProperty("refund_uv")
+ private String refundUv;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/ProductDataParam.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/ProductDataParam.java
new file mode 100644
index 0000000000..bddb9f3356
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/ProductDataParam.java
@@ -0,0 +1,33 @@
+package me.chanjar.weixin.channel.bean.compass.finder;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+/**
+ * 获取带货商品数据请求参数
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+@EqualsAndHashCode(callSuper = true)
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class ProductDataParam extends CompassFinderBaseParam {
+
+ private static final long serialVersionUID = - 5016298274452168329L;
+
+ /**
+ * 商品id
+ */
+ @JsonProperty("product_id")
+ private String productId;
+
+ public ProductDataParam(String ds, String productId) {
+ super(ds);
+ this.productId = productId;
+ }
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/ProductDataResponse.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/ProductDataResponse.java
new file mode 100644
index 0000000000..628e0cc221
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/ProductDataResponse.java
@@ -0,0 +1,27 @@
+package me.chanjar.weixin.channel.bean.compass.finder;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import me.chanjar.weixin.channel.bean.base.WxChannelBaseResponse;
+
+/**
+ * 获取带货商品数据响应
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+@EqualsAndHashCode(callSuper = true)
+public class ProductDataResponse extends WxChannelBaseResponse {
+
+ private static final long serialVersionUID = 7264776818163943719L;
+
+ /**
+ * 带货商品数据
+ */
+ @JsonProperty("product_info")
+ private ProductInfo productInfo;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/ProductInfo.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/ProductInfo.java
new file mode 100644
index 0000000000..3d1071b261
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/ProductInfo.java
@@ -0,0 +1,68 @@
+package me.chanjar.weixin.channel.bean.compass.finder;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 带货商品数据
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+public class ProductInfo implements Serializable {
+
+ private static final long serialVersionUID = - 3347940276601700091L;
+
+ /**
+ * 商品id
+ */
+ @JsonProperty("product_id")
+ private String productId;
+
+ /**
+ * 商品头图
+ */
+ @JsonProperty("head_img_url")
+ private String headImgUrl;
+
+ /**
+ * 商品标题
+ */
+ @JsonProperty("title")
+ private String title;
+
+ /**
+ * 商品价格
+ */
+ @JsonProperty("price")
+ private String price;
+
+ /**
+ * 1级类目
+ */
+ @JsonProperty("first_category_id")
+ private String firstCategoryId;
+
+ /**
+ * 2级类目
+ */
+ @JsonProperty("second_category_id")
+ private String secondCategoryId;
+
+ /**
+ * 3级类目
+ */
+ @JsonProperty("third_category_id")
+ private String thirdCategoryId;
+
+ /**
+ * 商品罗盘数据
+ */
+ @JsonProperty("data")
+ private ProductCompassData data;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/ProductListResponse.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/ProductListResponse.java
new file mode 100644
index 0000000000..e327531305
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/ProductListResponse.java
@@ -0,0 +1,29 @@
+package me.chanjar.weixin.channel.bean.compass.finder;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import me.chanjar.weixin.channel.bean.base.WxChannelBaseResponse;
+
+import java.util.List;
+
+/**
+ * 获取带货商品列表响应
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+@EqualsAndHashCode(callSuper = true)
+public class ProductListResponse extends WxChannelBaseResponse {
+
+ private static final long serialVersionUID = 7903039293558611066L;
+
+ /**
+ * 带货商品列表
+ */
+ @JsonProperty("product_list")
+ private List productList;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/SaleProfileData.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/SaleProfileData.java
new file mode 100644
index 0000000000..379943903e
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/SaleProfileData.java
@@ -0,0 +1,27 @@
+package me.chanjar.weixin.channel.bean.compass.finder;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 带货人群数据
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+public class SaleProfileData implements Serializable {
+
+ private static final long serialVersionUID = - 5542602540358792014L;
+
+ /**
+ * 维度数据列表
+ */
+ @JsonProperty("field_list")
+ private List fieldList;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/SaleProfileDataParam.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/SaleProfileDataParam.java
new file mode 100644
index 0000000000..57a5854e71
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/SaleProfileDataParam.java
@@ -0,0 +1,33 @@
+package me.chanjar.weixin.channel.bean.compass.finder;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+/**
+ * 获取带货人群数据请求参数
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+@EqualsAndHashCode(callSuper = true)
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class SaleProfileDataParam extends CompassFinderBaseParam {
+
+ private static final long serialVersionUID = 4037843292285732855L;
+
+ /**
+ * 用户类型 {@link me.chanjar.weixin.channel.enums.SaleProfileUserType}
+ */
+ @JsonProperty("type")
+ private Integer type;
+
+ public SaleProfileDataParam(String ds, Integer type) {
+ super(ds);
+ this.type = type;
+ }
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/SaleProfileDataResponse.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/SaleProfileDataResponse.java
new file mode 100644
index 0000000000..a976671ba0
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/compass/finder/SaleProfileDataResponse.java
@@ -0,0 +1,27 @@
+package me.chanjar.weixin.channel.bean.compass.finder;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import me.chanjar.weixin.channel.bean.base.WxChannelBaseResponse;
+
+/**
+ * 获取带货人群数据响应
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+@EqualsAndHashCode(callSuper = true)
+public class SaleProfileDataResponse extends WxChannelBaseResponse {
+
+ private static final long serialVersionUID = - 6409722880191468272L;
+
+ /**
+ * 带货人群数据
+ */
+ @JsonProperty("data")
+ private SaleProfileData data;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/ConversionMetric.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/ConversionMetric.java
new file mode 100644
index 0000000000..96a708be89
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/ConversionMetric.java
@@ -0,0 +1,61 @@
+package me.chanjar.weixin.channel.bean.live.dashboard;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 转化率
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+public class ConversionMetric implements Serializable {
+
+ private static final long serialVersionUID = - 3411290344181494863L;
+
+ /**
+ * 商品曝光-点击转化率
+ */
+ @JsonProperty("product_view_click_conversion_ratio")
+ private ItemConversionMetric productViewClickConversionRatio;
+
+ /**
+ * 气泡曝光-点击转化率
+ */
+ @JsonProperty("bubble_view_click_conversion_ratio")
+ private ItemConversionMetric bubbleViewClickConversionRatio;
+
+ /**
+ * 成交转化率
+ */
+ @JsonProperty("pay_conversion_ratio")
+ private ItemConversionMetric payConversionRatio;
+
+ /**
+ * 千次观看成交金额(单位:分)
+ */
+ @JsonProperty("k_view_pay_conversion_ratio")
+ private ItemConversionMetric kViewPayConversionRatio;
+
+ /**
+ * 更新时间
+ */
+ @JsonProperty("update_time")
+ private Long updateTime;
+
+ /**
+ * 购物袋商品点击率
+ */
+ @JsonProperty("product_list_click_conversion_ratio")
+ private ItemConversionMetric productListClickConversionRatio;
+
+ /**
+ * 挂车时间(>0 则为挂车)
+ */
+ @JsonProperty("shelftime")
+ private Long shelftime;
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/DataNode.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/DataNode.java
new file mode 100644
index 0000000000..ab749e0f82
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/DataNode.java
@@ -0,0 +1,26 @@
+package me.chanjar.weixin.channel.bean.live.dashboard;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 统计数值
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+public class DataNode implements Serializable {
+
+ private static final long serialVersionUID = 3192158546911682577L;
+
+ /**
+ * 统计数值维度指标
+ */
+ @JsonProperty("fields")
+ private Fields fields;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/DataNodeList.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/DataNodeList.java
new file mode 100644
index 0000000000..6469e48e6e
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/DataNodeList.java
@@ -0,0 +1,32 @@
+package me.chanjar.weixin.channel.bean.live.dashboard;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 分类下的数据
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+public class DataNodeList implements Serializable {
+
+ private static final long serialVersionUID = - 497502210938812386L;
+
+ /**
+ * 细分类别的名称,如 "女"、"30-39岁"、"天津市"
+ */
+ @JsonProperty("key")
+ private String key;
+
+ /**
+ * 包含具体的统计数值
+ */
+ @JsonProperty("row")
+ private DataNode row;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/DataNodeSecondList.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/DataNodeSecondList.java
new file mode 100644
index 0000000000..7a033378c0
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/DataNodeSecondList.java
@@ -0,0 +1,33 @@
+package me.chanjar.weixin.channel.bean.live.dashboard;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 用户群体下不同分类的统计数据
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+public class DataNodeSecondList implements Serializable {
+
+ private static final long serialVersionUID = 42973481125049275L;
+
+ /**
+ * 每个分类对象都有一个 key,表示分类的名称,例如 "sex_distribution"、"age_distribution" {@link me.chanjar.weixin.channel.enums.EcProfileDataNodeKey}
+ */
+ @JsonProperty("key")
+ private String key;
+
+ /**
+ * 进一步细分该分类下的数据
+ */
+ @JsonProperty("row")
+ private List row;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/DataNodeThirdList.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/DataNodeThirdList.java
new file mode 100644
index 0000000000..7c6424b63f
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/DataNodeThirdList.java
@@ -0,0 +1,33 @@
+package me.chanjar.weixin.channel.bean.live.dashboard;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 不同用户群体的统计数据
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+public class DataNodeThirdList implements Serializable {
+
+ private static final long serialVersionUID = - 7534433586440870881L;
+
+ /**
+ * 每个对象包含一个 key,表示用户群体的名称,例如 "已成交用户"、"首次购买用户"、"复购用户"
+ */
+ @JsonProperty("key")
+ private String key;
+
+ /**
+ * 包含该用户群体下不同分类的统计数据
+ */
+ @JsonProperty("row")
+ private List row;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/Dimension.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/Dimension.java
new file mode 100644
index 0000000000..a07e6670b4
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/Dimension.java
@@ -0,0 +1,38 @@
+package me.chanjar.weixin.channel.bean.live.dashboard;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 描述时间序列的维度标签
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+public class Dimension implements Serializable {
+
+ private static final long serialVersionUID = - 1879006149576217182L;
+
+ /**
+ * 维度的类型 {@link me.chanjar.weixin.channel.enums.DimensionType}
+ */
+ @JsonProperty("type")
+ private Integer type;
+
+ /**
+ * 维度标签
+ */
+ @JsonProperty("ux_label")
+ private String uxLabel;
+
+ /**
+ * 维度值
+ */
+ @JsonProperty("dimension_value")
+ private Long dimensionValue;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/Ended.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/Ended.java
new file mode 100644
index 0000000000..361a52663b
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/Ended.java
@@ -0,0 +1,44 @@
+package me.chanjar.weixin.channel.bean.live.dashboard;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 关播内容力数据
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+public class Ended implements Serializable {
+
+ private static final long serialVersionUID = 576815272236922652L;
+
+ /**
+ * 曝光有效CTR(万分比)
+ */
+ @JsonProperty("recommend_effective_new_watch_2_uv_over_impression_uv")
+ private EndedIndexItem recommendEffectiveNewWatch2UvOverImpressionUv;
+
+ /**
+ * 人均看播时长
+ */
+ @JsonProperty("average_watch_seconds")
+ private EndedIndexItem averageWatchSeconds;
+
+ /**
+ * 评论率(万分比)
+ */
+ @JsonProperty("comment_uv_over_new_watch_uv")
+ private EndedIndexItem commentUvOverNewWatchUv;
+
+ /**
+ * 点赞率(万分比)
+ */
+ @JsonProperty("like_uv_over_new_watch_uv")
+ private EndedIndexItem likeUvOverNewWatchUv;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/EndedIndexItem.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/EndedIndexItem.java
new file mode 100644
index 0000000000..0823819491
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/EndedIndexItem.java
@@ -0,0 +1,38 @@
+package me.chanjar.weixin.channel.bean.live.dashboard;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 关播内容力指标数据
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+public class EndedIndexItem implements Serializable {
+
+ private static final long serialVersionUID = 7529336638744298238L;
+
+ /**
+ * 整场直播的指标值
+ */
+ @JsonProperty("value")
+ private Long value;
+
+ /**
+ * 整场直播该指标值打败了 xx% 的主播
+ */
+ @JsonProperty("percentile")
+ private Integer percentile;
+
+ /**
+ * 该指标 7 天中位数
+ */
+ @JsonProperty("median_7_days")
+ private Long median7Days;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/Fields.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/Fields.java
new file mode 100644
index 0000000000..b199f255a5
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/Fields.java
@@ -0,0 +1,38 @@
+package me.chanjar.weixin.channel.bean.live.dashboard;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 统计数值维度指标数据
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+public class Fields implements Serializable {
+
+ private static final long serialVersionUID = 228387216076265877L;
+
+ /**
+ * 维度值
+ */
+ @JsonProperty("dim_key")
+ private String dimKey;
+
+ /**
+ * 指标值
+ */
+ @JsonProperty("dim_val")
+ private String dimVal;
+
+ /**
+ * 指标值比例
+ */
+ @JsonProperty("dim_val_ratio")
+ private String dimValRatio;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/ItemConversionMetric.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/ItemConversionMetric.java
new file mode 100644
index 0000000000..6d5142b52f
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/ItemConversionMetric.java
@@ -0,0 +1,44 @@
+package me.chanjar.weixin.channel.bean.live.dashboard;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 转化率数据
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+public class ItemConversionMetric implements Serializable {
+
+ private static final long serialVersionUID = - 8317027740221390754L;
+
+ /**
+ * 指标值
+ */
+ @JsonProperty("metric_value")
+ private Double metricValue;
+
+ /**
+ * 较近7天中位数
+ */
+ @JsonProperty("median_to_recent_7_days")
+ private Double medianToRecent7Days;
+
+ /**
+ * 同行对比
+ */
+ @JsonProperty("within_industry_percentage")
+ private Double withinIndustryPercentage;
+
+ /**
+ * 环比数据
+ */
+ @JsonProperty("quarterly_growth_rate")
+ private QuarterlyGrowthRate quarterlyGrowthRate;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveComparisonIndex.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveComparisonIndex.java
new file mode 100644
index 0000000000..84bfd6c226
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveComparisonIndex.java
@@ -0,0 +1,38 @@
+package me.chanjar.weixin.channel.bean.live.dashboard;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 内容力数据
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+public class LiveComparisonIndex implements Serializable {
+
+ private static final long serialVersionUID = 525214144965479881L;
+
+ /**
+ * 是否正在直播
+ */
+ @JsonProperty("is_living")
+ private Boolean isLiving;
+
+ /**
+ * 在播数据
+ */
+ @JsonProperty("on_air")
+ private OnAir onAir;
+
+ /**
+ * 关播数据
+ */
+ @JsonProperty("ended")
+ private Ended ended;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDashboardData.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDashboardData.java
new file mode 100644
index 0000000000..2568593f6b
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDashboardData.java
@@ -0,0 +1,38 @@
+package me.chanjar.weixin.channel.bean.live.dashboard;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 直播大屏数据
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+public class LiveDashboardData implements Serializable {
+
+ private static final long serialVersionUID = 7917049411269553153L;
+
+ /**
+ * 直播大屏数据实体
+ */
+ @JsonProperty("live_dashboard_data")
+ private LiveDashboardData2 liveDashboardData;
+
+ /**
+ * 直播时长
+ */
+ @JsonProperty("live_duration")
+ private Long liveDuration;
+
+ /**
+ * 直播开始时间
+ */
+ @JsonProperty("start_time")
+ private Long startTime;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDashboardData2.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDashboardData2.java
new file mode 100644
index 0000000000..7a66f9ed1f
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDashboardData2.java
@@ -0,0 +1,38 @@
+package me.chanjar.weixin.channel.bean.live.dashboard;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 直播大屏实体
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+public class LiveDashboardData2 implements Serializable {
+
+ private static final long serialVersionUID = 3657714024563123097L;
+
+ /**
+ * 直播基础数据
+ */
+ @JsonProperty("summary")
+ private LiveDashboardData2Summary summary;
+
+ /**
+ * 直播流量渠道
+ */
+ @JsonProperty("source")
+ private LiveDashboardData2Source source;
+
+ /**
+ * 直播观众画像
+ */
+ @JsonProperty("portrait")
+ private LiveDashboardData2Portrait portrait;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDashboardData2Portrait.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDashboardData2Portrait.java
new file mode 100644
index 0000000000..964a6936fc
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDashboardData2Portrait.java
@@ -0,0 +1,33 @@
+package me.chanjar.weixin.channel.bean.live.dashboard;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 直播观众画像
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+public class LiveDashboardData2Portrait implements Serializable {
+
+ private static final long serialVersionUID = - 5603781471063785276L;
+
+ /**
+ * 在线人数的画像
+ */
+ @JsonProperty("online_watch_uv")
+ private List onlineWatchUv;
+
+ /**
+ * 观看人数的画像
+ */
+ @JsonProperty("new_watch_uv")
+ private List newWatchUv;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDashboardData2Source.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDashboardData2Source.java
new file mode 100644
index 0000000000..12c6121bc7
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDashboardData2Source.java
@@ -0,0 +1,27 @@
+package me.chanjar.weixin.channel.bean.live.dashboard;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 直播流量渠道
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+public class LiveDashboardData2Source implements Serializable {
+
+ private static final long serialVersionUID = 7347276250944913612L;
+
+ /**
+ * 观看人数的渠道分布
+ */
+ @JsonProperty("new_watch_uv")
+ private List newWatchUv;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDashboardData2Summary.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDashboardData2Summary.java
new file mode 100644
index 0000000000..a9c46ea6e3
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDashboardData2Summary.java
@@ -0,0 +1,80 @@
+package me.chanjar.weixin.channel.bean.live.dashboard;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 直播基础数据
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+public class LiveDashboardData2Summary implements Serializable {
+
+ private static final long serialVersionUID = - 9029702302333930066L;
+
+ /**
+ * 观看人数
+ */
+ @JsonProperty("new_watch_uv")
+ private Long newWatchUv;
+
+ /**
+ * 最大在线人数
+ */
+ @JsonProperty("max_online_watch_uv")
+ private Long maxOnlineWatchUv;
+
+ /**
+ * 曝光人数
+ */
+ @JsonProperty("impression_uv")
+ private Long impressionUv;
+
+ /**
+ * 平均观看时长(秒)
+ */
+ @JsonProperty("average_watch_seconds_per_audience")
+ private Long averageWatchSecondsPerAudience;
+
+ /**
+ * 新增关注人数
+ */
+ @JsonProperty("new_follow_uv")
+ private Long newFollowUv;
+
+ /**
+ * 新增粉丝团人数
+ */
+ @JsonProperty("new_fans_club_uv")
+ private Long newFansClubUv;
+
+ /**
+ * 评论人数
+ */
+ @JsonProperty("comment_uv")
+ private Long commentUv;
+
+ /**
+ * 打赏人数
+ */
+ @JsonProperty("reward_uv")
+ private Long rewardUv;
+
+ /**
+ * 分享直播间人数
+ */
+ @JsonProperty("sharing_uv")
+ private Long sharingUv;
+
+ /**
+ * 热度
+ */
+ @JsonProperty("hot_quota")
+ private Long hotQuota;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDataParam.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDataParam.java
new file mode 100644
index 0000000000..965ed6c8c3
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDataParam.java
@@ -0,0 +1,30 @@
+package me.chanjar.weixin.channel.bean.live.dashboard;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 获取直播大屏数据请求参数
+ *
+ * @author Winnie
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class LiveDataParam implements Serializable {
+
+ private static final long serialVersionUID = 6346941931704153857L;
+
+ /**
+ * 直播唯一ID
+ */
+ @JsonProperty("export_id")
+ private String exportId;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDataResponse.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDataResponse.java
new file mode 100644
index 0000000000..4a5f4673bd
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDataResponse.java
@@ -0,0 +1,69 @@
+package me.chanjar.weixin.channel.bean.live.dashboard;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import me.chanjar.weixin.channel.bean.base.WxChannelBaseResponse;
+
+/**
+ * 获取直播大屏数据响应
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+@EqualsAndHashCode(callSuper = true)
+public class LiveDataResponse extends WxChannelBaseResponse {
+
+ private static final long serialVersionUID = - 8416743234527598719L;
+
+ /**
+ * 追踪ID,报bug带
+ */
+ @JsonProperty("trace_id")
+ private String traceId;
+
+ /**
+ * 直播大屏基础数据
+ */
+ @JsonProperty("live_dashboard_data")
+ private LiveDashboardData liveDashboardData;
+
+ /**
+ * 内容力数据
+ */
+ @JsonProperty("live_comparison_index")
+ private LiveComparisonIndex liveComparisonIndex;
+
+ /**
+ * 电商数据概要数据
+ */
+ @JsonProperty("live_ec_data_summary")
+ private LiveEcDataSummary liveEcDataSummary;
+
+ /**
+ * 电商转化力数据
+ */
+ @JsonProperty("live_ec_conversion_metric")
+ private LiveEcConversionMetric liveEcConversionMetric;
+
+ /**
+ * 电商画像数据
+ */
+ @JsonProperty("live_ec_profile")
+ private LiveEcProfile liveEcProfile;
+
+ /**
+ * 电商渠道分布
+ */
+ @JsonProperty("live_distribution_channel")
+ private LiveDistributionChannel liveDistributionChannel;
+
+ /**
+ * 电商商品数据
+ */
+ @JsonProperty("single_live_ec_spu_data_page_v2")
+ private SingleLiveEcSpuDataPageV2 singleLiveEcSpuDataPageV2;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDistChannelSourceStats.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDistChannelSourceStats.java
new file mode 100644
index 0000000000..9f4d876992
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDistChannelSourceStats.java
@@ -0,0 +1,81 @@
+package me.chanjar.weixin.channel.bean.live.dashboard;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 流量来源渠道指标数据
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+public class LiveDistChannelSourceStats implements Serializable {
+
+ private static final long serialVersionUID = - 6802106934852140579L;
+
+ /**
+ * 渠道层级
+ */
+ @JsonProperty("level")
+ private Integer level;
+
+ /**
+ * 来源渠道ID
+ */
+ @JsonProperty("source_channel_id")
+ private Long sourceChannelId;
+
+ /**
+ * 流量来源子渠道指标数据统计值
+ */
+ @JsonProperty("sub_channel_source_stats")
+ private List subChannelSourceStats;
+
+ /**
+ * GMV总值(单位:分)
+ */
+ @JsonProperty("gmv")
+ private Long gmv;
+
+ /**
+ * UV总值
+ */
+ @JsonProperty("uv")
+ private Long uv;
+
+ /**
+ * 千次看播成交(单位: 分)(GPV)
+ */
+ @JsonProperty("gmv_per_uv")
+ private Long gmvPerUv;
+
+ /**
+ * gmv占比
+ */
+ @JsonProperty("gmv_ratio")
+ private Double gmvRatio;
+
+ /**
+ * uv占比
+ */
+ @JsonProperty("uv_ratio")
+ private Double uvRatio;
+
+ /**
+ * 渠道名称
+ */
+ @JsonProperty("source_channel_name")
+ private String sourceChannelName;
+
+ /**
+ * 当前层级pv占总pv的比例
+ */
+ @JsonProperty("pv_ratio")
+ private Double pvRatio;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDistributionByFlowTypeStat.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDistributionByFlowTypeStat.java
new file mode 100644
index 0000000000..7b9765f955
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDistributionByFlowTypeStat.java
@@ -0,0 +1,81 @@
+package me.chanjar.weixin.channel.bean.live.dashboard;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 流量类型、渠道层级的渠道分析统计数据
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+public class LiveDistributionByFlowTypeStat implements Serializable {
+
+ private static final long serialVersionUID = 5885014384803438677L;
+
+ /**
+ * 渠道流量类型 {@link me.chanjar.weixin.channel.enums.LiveDistributionFlowType}
+ */
+ @JsonProperty("live_dst_channel_type")
+ private Integer liveDstChannelType;
+
+ /**
+ * 一级类目渠道来源指标划分
+ */
+ @JsonProperty("channel_source_stats")
+ private List channelSourceStats;
+
+ /**
+ * 在该渠道下的统计值
+ */
+ @JsonProperty("metric_value")
+ private Long metricValue;
+
+ /**
+ * GMV总值(单位:分)
+ */
+ @JsonProperty("gmv")
+ private Long gmv;
+
+ /**
+ * UV总值
+ */
+ @JsonProperty("uv")
+ private Long uv;
+
+ /**
+ * 千次看播成交(单位: 分)(GPV)
+ */
+ @JsonProperty("gmv_per_uv")
+ private Long gmvPerUv;
+
+ /**
+ * PV总值
+ */
+ @JsonProperty("pv")
+ private Long pv;
+
+ /**
+ * 当前层级pv占总pv的比例
+ */
+ @JsonProperty("pv_ratio")
+ private Double pvRatio;
+
+ /**
+ * uv占比
+ */
+ @JsonProperty("uv_ratio")
+ private Double uvRatio;
+
+ /**
+ * 在该渠道下的统计值比率
+ */
+ @JsonProperty("metric_value_ratio")
+ private Double metricValueRatio;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDistributionChannel.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDistributionChannel.java
new file mode 100644
index 0000000000..24eb64d4a2
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDistributionChannel.java
@@ -0,0 +1,51 @@
+package me.chanjar.weixin.channel.bean.live.dashboard;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 电商渠道分布
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+public class LiveDistributionChannel implements Serializable {
+
+ private static final long serialVersionUID = - 5898886208775573377L;
+
+ /**
+ * 客户数
+ */
+ @JsonProperty("audience_count")
+ private Long audienceCount;
+
+ /**
+ * 总进入直播数
+ */
+ @JsonProperty("total_joinlive_count")
+ private Long totalJoinliveCount;
+
+ /**
+ * 按场景划分的渠道分析统计值
+ */
+ @JsonProperty("live_dist_channel_source_by_scene_stats")
+ private List liveDistChannelSourceBySceneStats;
+
+ /**
+ * 按照流量类型、渠道层级划分的渠道分析统计数据
+ */
+ @JsonProperty("live_dist_channel_source_stats")
+ private List liveDistChannelSourceStats;
+
+ /**
+ * 数据版本(无实际意义)
+ */
+ @JsonProperty("data_key")
+ private List dataKey;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDistributionSceneStat.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDistributionSceneStat.java
new file mode 100644
index 0000000000..425d69cca0
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveDistributionSceneStat.java
@@ -0,0 +1,75 @@
+package me.chanjar.weixin.channel.bean.live.dashboard;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 场景的渠道分析统计值
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+public class LiveDistributionSceneStat implements Serializable {
+
+ private static final long serialVersionUID = 4261140121141859416L;
+
+ /**
+ * 场景类型 {@link me.chanjar.weixin.channel.enums.LiveDistributionSceneType}
+ */
+ @JsonProperty("scene_type")
+ private Integer sceneType;
+
+ /**
+ * 该场景下的渠道分析统计值
+ */
+ @JsonProperty("dist_flow_type_stats")
+ private List distFlowTypeStats;
+
+ /**
+ * 指标值总数
+ */
+ @JsonProperty("metric_value_total")
+ private Long metricValueTotal;
+
+ /**
+ * GMV总值(单位:分)
+ */
+ @JsonProperty("gmv")
+ private Long gmv;
+
+ /**
+ * UV总值
+ */
+ @JsonProperty("uv")
+ private Long uv;
+
+ /**
+ * 千次看播成交(单位: 分)
+ */
+ @JsonProperty("gmv_per_uv")
+ private Long gmvPerUv;
+
+ /**
+ * 指标值
+ */
+ @JsonProperty("metric_value")
+ private Long metricValue;
+
+ /**
+ * 在该渠道下的统计值比率
+ */
+ @JsonProperty("metric_value_ratio")
+ private Double metricValueRatio;
+
+ /**
+ * pv
+ */
+ @JsonProperty("pv")
+ private Long pv;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveEcConversionMetric.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveEcConversionMetric.java
new file mode 100644
index 0000000000..e9b92821fe
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveEcConversionMetric.java
@@ -0,0 +1,32 @@
+package me.chanjar.weixin.channel.bean.live.dashboard;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 电商转化力数据
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+public class LiveEcConversionMetric implements Serializable {
+
+ private static final long serialVersionUID = - 7332281175637902883L;
+
+ /**
+ * 近10分钟转化率数据
+ */
+ @JsonProperty("recent_10_min_conversion")
+ private ConversionMetric recent10MinConversion;
+
+ /**
+ * 整场直播
+ */
+ @JsonProperty("whole_live_conversion")
+ private ConversionMetric wholeLiveConversion;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveEcDataSummary.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveEcDataSummary.java
new file mode 100644
index 0000000000..d77209da56
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveEcDataSummary.java
@@ -0,0 +1,212 @@
+package me.chanjar.weixin.channel.bean.live.dashboard;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 电商数据概要数据
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+public class LiveEcDataSummary implements Serializable {
+
+ private static final long serialVersionUID = - 6634047980552575196L;
+
+ /**
+ * 成交金额(单位:分)
+ */
+ @JsonProperty("total_gmv")
+ private Long totalGmv;
+
+ /**
+ * 成交次数
+ */
+ @JsonProperty("total_pay_pv")
+ private Long totalPayPv;
+
+ /**
+ * 成交人数
+ */
+ @JsonProperty("total_pay_uv")
+ private Long totalPayUv;
+
+ /**
+ * 订单创建次数
+ */
+ @JsonProperty("total_create_pv")
+ private Long totalCreatePv;
+
+ /**
+ * 订单创建人数
+ */
+ @JsonProperty("total_create_uv")
+ private Long totalCreateUv;
+
+ /**
+ * 总点击次数
+ */
+ @JsonProperty("total_clk_pv")
+ private Long totalClkPv;
+
+ /**
+ * 总点击人数
+ */
+ @JsonProperty("total_clk_uv")
+ private Long totalClkUv;
+
+ /**
+ * 总曝光次数
+ */
+ @JsonProperty("total_exp_pv")
+ private Long totalExpPv;
+
+ /**
+ * 总曝光人数
+ */
+ @JsonProperty("total_exp_uv")
+ private Long totalExpUv;
+
+ /**
+ * 在线观众数
+ */
+ @JsonProperty("online_audience_count")
+ private Long onlineAudienceCount;
+
+ /**
+ * 累计观众数
+ */
+ @JsonProperty("cumulative_audience_count")
+ private Long cumulativeAudienceCount;
+
+ /**
+ * 新增观众数
+ */
+ @JsonProperty("new_audience_count")
+ private Long newAudienceCount;
+
+ /**
+ * 剩余观众数
+ */
+ @JsonProperty("leaved_audience_count")
+ private Long leavedAudienceCount;
+
+ /**
+ * 观众平均观看秒数
+ */
+ @JsonProperty("average_watch_seconds_per_audience")
+ private Long averageWatchSecondsPerAudience;
+
+ /**
+ * 新增关注数
+ */
+ @JsonProperty("new_follow_count")
+ private Long newFollowCount;
+
+ /**
+ * 新增评论数
+ */
+ @JsonProperty("new_comment_count")
+ private Long newCommentCount;
+
+ /**
+ * 分享直播观众数
+ */
+ @JsonProperty("share_live_audience_count")
+ private Long shareLiveAudienceCount;
+
+ /**
+ * 新粉丝俱乐部数
+ */
+ @JsonProperty("new_fans_club_count")
+ private Long newFansClubCount;
+
+ /**
+ * 退费次数
+ */
+ @JsonProperty("refund_pv")
+ private Long refundPv;
+
+ /**
+ * 退费人数
+ */
+ @JsonProperty("refund_uv")
+ private Long refundUv;
+
+ /**
+ * 退费率
+ */
+ @JsonProperty("refund_rate")
+ private Double refundRate;
+
+ /**
+ * 退款金额(单位:分)
+ */
+ @JsonProperty("refund_amount")
+ private Long refundAmount;
+
+ /**
+ * 退费商品件数
+ */
+ @JsonProperty("refund_product_cnt")
+ private Long refundProductCnt;
+
+ /**
+ * 广告累计观众数
+ */
+ @JsonProperty("ads_cumulative_audience_count")
+ private Long adsCumulativeAudienceCount;
+
+ /**
+ * 广告累计观看数
+ */
+ @JsonProperty("ads_cumulative_watch_count")
+ private Long adsCumulativeWatchCount;
+
+ /**
+ * 促销累计观看数
+ */
+ @JsonProperty("promotion_cumulative_watch_count")
+ private Long promotionCumulativeWatchCount;
+
+ /**
+ * 千次看播成交总额
+ */
+ @JsonProperty("gmv_per_thousand_cumulative_watch_pv")
+ private Double gmvPerThousandCumulativeWatchPv;
+
+ /**
+ * 观众成交率
+ */
+ @JsonProperty("audience_pay_ratio")
+ private Double audiencePayRatio;
+
+ /**
+ * 点击成交率
+ */
+ @JsonProperty("clk_pay_ratio")
+ private Double clkPayRatio;
+
+ /**
+ * 新买家人数
+ */
+ @JsonProperty("new_buyer_uv")
+ private Long newBuyerUv;
+
+ /**
+ * 老买家人数
+ */
+ @JsonProperty("old_buyer_uv")
+ private Long oldBuyerUv;
+
+ /**
+ * 客户价格
+ */
+ @JsonProperty("customer_price")
+ private Long customerPrice;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveEcProfile.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveEcProfile.java
new file mode 100644
index 0000000000..76f90c9942
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveEcProfile.java
@@ -0,0 +1,33 @@
+package me.chanjar.weixin.channel.bean.live.dashboard;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 电商画像数据
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+public class LiveEcProfile implements Serializable {
+
+ private static final long serialVersionUID = 1996741772652344438L;
+
+ /**
+ * 包含不同用户群体的统计数据
+ */
+ @JsonProperty("profiles")
+ private List profiles;
+
+ /**
+ * 总体数据统计信息
+ */
+ @JsonProperty("totals")
+ private List totals;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveItem.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveItem.java
new file mode 100644
index 0000000000..acea012018
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveItem.java
@@ -0,0 +1,32 @@
+package me.chanjar.weixin.channel.bean.live.dashboard;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 直播列表数据
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+public class LiveItem implements Serializable {
+
+ private static final long serialVersionUID = 6693176992531666035L;
+
+ /**
+ * 直播唯一ID
+ */
+ @JsonProperty("export_id")
+ private String exportId;
+
+ /**
+ * 直播创建时间
+ */
+ @JsonProperty("create_time")
+ private Long createTime;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveListParam.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveListParam.java
new file mode 100644
index 0000000000..3072e990ef
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveListParam.java
@@ -0,0 +1,30 @@
+package me.chanjar.weixin.channel.bean.live.dashboard;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 获取直播大屏直播列表请求参数
+ *
+ * @author Winnie
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class LiveListParam implements Serializable {
+
+ private static final long serialVersionUID = - 8451283214646387030L;
+
+ /**
+ * 日期,格式 yyyyMMdd
+ */
+ @JsonProperty("ds")
+ private Long ds;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveListResponse.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveListResponse.java
new file mode 100644
index 0000000000..f9b1981dfa
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/LiveListResponse.java
@@ -0,0 +1,41 @@
+package me.chanjar.weixin.channel.bean.live.dashboard;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import me.chanjar.weixin.channel.bean.base.WxChannelBaseResponse;
+
+import java.util.List;
+
+/**
+ * 获取直播大屏直播列表响应
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+@EqualsAndHashCode(callSuper = true)
+public class LiveListResponse extends WxChannelBaseResponse {
+
+ private static final long serialVersionUID = - 5062337147636715367L;
+
+ /**
+ * 追踪ID,报bug带
+ */
+ @JsonProperty("trace_id")
+ private String traceId;
+
+ /**
+ * 直播列表
+ */
+ @JsonProperty("live_items")
+ private List liveItems;
+
+ /**
+ * 是否还有更多的直播
+ */
+ @JsonProperty("has_more")
+ private Boolean hasMore;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/OnAir.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/OnAir.java
new file mode 100644
index 0000000000..29aef35236
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/OnAir.java
@@ -0,0 +1,44 @@
+package me.chanjar.weixin.channel.bean.live.dashboard;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 在播内容力数据
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+public class OnAir implements Serializable {
+
+ private static final long serialVersionUID = 6354207471314033499L;
+
+ /**
+ * 曝光有效CTR(万分比)
+ */
+ @JsonProperty("recommend_effective_new_watch_2_uv_over_impression_uv")
+ private OnAirIndexItem recommendEffectiveNewWatch2UvOverImpressionUv;
+
+ /**
+ * 人均看播时长
+ */
+ @JsonProperty("average_watch_seconds")
+ private OnAirIndexItem averageWatchSeconds;
+
+ /**
+ * 评论率(万分比)
+ */
+ @JsonProperty("comment_uv_over_new_watch_uv")
+ private OnAirIndexItem commentUvOverNewWatchUv;
+
+ /**
+ * 点赞率(万分比)
+ */
+ @JsonProperty("like_uv_over_new_watch_uv")
+ private OnAirIndexItem likeUvOverNewWatchUv;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/OnAirIndexItem.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/OnAirIndexItem.java
new file mode 100644
index 0000000000..ebad794338
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/OnAirIndexItem.java
@@ -0,0 +1,56 @@
+package me.chanjar.weixin.channel.bean.live.dashboard;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 在播内容力指标数据
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+public class OnAirIndexItem implements Serializable {
+
+ private static final long serialVersionUID = - 2988342521964183666L;
+
+ /**
+ * 描述最近多少分钟的指标值
+ */
+ @JsonProperty("n")
+ private Integer n;
+
+ /**
+ * 最近 n 分钟该指标的值
+ */
+ @JsonProperty("last_n_mins_value")
+ private Integer lastNMinsValue;
+
+ /**
+ * 最近 2n 到 n 分钟该指标的值(用于环比)
+ */
+ @JsonProperty("last_2n_to_n_mins_value")
+ private Integer last2nToNMinsValue;
+
+ /**
+ * 最近 n 分钟该指标值打败了 xx% 的在播主播
+ */
+ @JsonProperty("last_n_mins_percentile")
+ private Integer lastNMinsPercentile;
+
+ /**
+ * 整场直播的指标值
+ */
+ @JsonProperty("value")
+ private Long value;
+
+ /**
+ * 整场直播该指标值打败了 xx% 的主播
+ */
+ @JsonProperty("percentile")
+ private Integer percentile;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/Point.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/Point.java
new file mode 100644
index 0000000000..7f11086442
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/Point.java
@@ -0,0 +1,32 @@
+package me.chanjar.weixin.channel.bean.live.dashboard;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 数据点
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+public class Point implements Serializable {
+
+ private static final long serialVersionUID = 3332256418933163389L;
+
+ /**
+ * 时间戳
+ */
+ @JsonProperty("ts")
+ private Long ts;
+
+ /**
+ * 指标值
+ */
+ @JsonProperty("value")
+ private Long value;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/QuarterlyGrowthRate.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/QuarterlyGrowthRate.java
new file mode 100644
index 0000000000..0acfdd7d18
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/QuarterlyGrowthRate.java
@@ -0,0 +1,32 @@
+package me.chanjar.weixin.channel.bean.live.dashboard;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 转化率环比数据
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+public class QuarterlyGrowthRate implements Serializable {
+
+ private static final long serialVersionUID = 1683118806978367016L;
+
+ /**
+ * 环比(近10分钟转化率数据才有)
+ */
+ @JsonProperty("value")
+ private Long value;
+
+ /**
+ * 环比是否是有效值(如果是false说明分母是0)
+ */
+ @JsonProperty("is_valid")
+ private Boolean isValid;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/Series.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/Series.java
new file mode 100644
index 0000000000..570c1b1b0d
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/Series.java
@@ -0,0 +1,51 @@
+package me.chanjar.weixin.channel.bean.live.dashboard;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 维度标签的时间序列(与指标的类型无关)
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+public class Series implements Serializable {
+
+ private static final long serialVersionUID = 507937573085880287L;
+
+ /**
+ * 数据点
+ */
+ @JsonProperty("points")
+ private List points;
+
+ /**
+ * 描述时间序列的维度标签
+ */
+ @JsonProperty("dimensions")
+ private List dimensions;
+
+ /**
+ * 每个数据点描述的时间长度(秒)
+ */
+ @JsonProperty("step")
+ private Long step;
+
+ /**
+ * 该时间序列的起始时间戳
+ */
+ @JsonProperty("begin_ts")
+ private Long beginTs;
+
+ /**
+ * 该时间序列的结束时间戳
+ */
+ @JsonProperty("end_ts")
+ private Long endTs;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/SingleLiveEcSpuDataPageV2.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/SingleLiveEcSpuDataPageV2.java
new file mode 100644
index 0000000000..5cdb3a97d0
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/SingleLiveEcSpuDataPageV2.java
@@ -0,0 +1,39 @@
+package me.chanjar.weixin.channel.bean.live.dashboard;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 电商商品数据
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+public class SingleLiveEcSpuDataPageV2 implements Serializable {
+
+ private static final long serialVersionUID = - 761977668198342583L;
+
+ /**
+ * 商品明细数据列表
+ */
+ @JsonProperty("spu_data_list")
+ private List spuDataList;
+
+ /**
+ * spu_data_list 的总长度
+ */
+ @JsonProperty("total_cnt")
+ private Integer totalCnt;
+
+ /**
+ * 数据版本(无实际意义)
+ */
+ @JsonProperty("data_key")
+ private List dataKey;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/SpuBaseData.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/SpuBaseData.java
new file mode 100644
index 0000000000..b86279bdab
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/SpuBaseData.java
@@ -0,0 +1,68 @@
+package me.chanjar.weixin.channel.bean.live.dashboard;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 商品基础数据
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+public class SpuBaseData implements Serializable {
+
+ private static final long serialVersionUID = 3170611962212344198L;
+
+ /**
+ * 店铺商品id
+ */
+ @JsonProperty("src_spu_id")
+ private String srcSpuId;
+
+ /**
+ * 店铺id
+ */
+ @JsonProperty("src")
+ private Long src;
+
+ /**
+ * 商品名称
+ */
+ @JsonProperty("spu_name")
+ private String spuName;
+
+ /**
+ * 商品id
+ */
+ @JsonProperty("spu_id")
+ private Long spuId;
+
+ /**
+ * 商品小图
+ */
+ @JsonProperty("thumb_url")
+ private String thumbUrl;
+
+ /**
+ * 商品价格
+ */
+ @JsonProperty("price")
+ private Long price;
+
+ /**
+ * 店铺名称
+ */
+ @JsonProperty("src_name")
+ private String srcName;
+
+ /**
+ * 库存
+ */
+ @JsonProperty("stock")
+ private Long stock;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/SpuData.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/SpuData.java
new file mode 100644
index 0000000000..41a44a926d
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/SpuData.java
@@ -0,0 +1,350 @@
+package me.chanjar.weixin.channel.bean.live.dashboard;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 商品明细数据
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+public class SpuData implements Serializable {
+
+ private static final long serialVersionUID = 7409791549917863816L;
+
+ /**
+ * 商品基础数据
+ */
+ @JsonProperty("base_data")
+ private SpuBaseData baseData;
+
+ /**
+ * 商品曝光人数
+ */
+ @JsonProperty("exp_uv")
+ private Long expUv;
+
+ /**
+ * 商品曝光次数
+ */
+ @JsonProperty("exp_pv")
+ private Long expPv;
+
+ /**
+ * 商品粉丝曝光人数
+ */
+ @JsonProperty("fans_exp_uv")
+ private Long fansExpUv;
+
+ /**
+ * 商品粉丝曝光次数
+ */
+ @JsonProperty("fans_exp_pv")
+ private Long fansExpPv;
+
+ /**
+ * 商品非粉丝曝光人数
+ */
+ @JsonProperty("non_fans_exp_uv")
+ private Long nonFansExpUv;
+
+ /**
+ * 商品非粉丝曝光次数
+ */
+ @JsonProperty("non_fans_exp_pv")
+ private Long nonFansExpPv;
+
+ /**
+ * 商品新客户曝光人数
+ */
+ @JsonProperty("new_customer_exp_uv")
+ private Long newCustomerExpUv;
+
+ /**
+ * 商品新客户曝光次数
+ */
+ @JsonProperty("new_customer_exp_pv")
+ private Long newCustomerExpPv;
+
+ /**
+ * 商品老客户曝光人数
+ */
+ @JsonProperty("repeated_customer_exp_uv")
+ private Long repeatedCustomerExpUv;
+
+ /**
+ * 商品老客户曝光次数
+ */
+ @JsonProperty("repeated_customer_exp_pv")
+ private Long repeatedCustomerExpPv;
+
+ /**
+ * 商品点击人数
+ */
+ @JsonProperty("clk_uv")
+ private Long clkUv;
+
+ /**
+ * 商品点击次数
+ */
+ @JsonProperty("clk_pv")
+ private Long clkPv;
+
+ /**
+ * 商品新客户点击人数
+ */
+ @JsonProperty("new_customer_clk_uv")
+ private Long newCustomerClkUv;
+
+ /**
+ * 商品新客户点击次数
+ */
+ @JsonProperty("new_customer_clk_pv")
+ private Long newCustomerClkPv;
+
+ /**
+ * 商品老客户点击人数
+ */
+ @JsonProperty("repeated_customer_clk_uv")
+ private Long repeatedCustomerClkUv;
+
+ /**
+ * 商品老客户点击次数
+ */
+ @JsonProperty("repeated_customer_clk_pv")
+ private Long repeatedCustomerClkPv;
+
+ /**
+ * 商品粉丝点击人数
+ */
+ @JsonProperty("fans_clk_uv")
+ private Long fansClkUv;
+
+ /**
+ * 商品粉丝点击次数
+ */
+ @JsonProperty("fans_clk_pv")
+ private Long fansClkPv;
+
+ /**
+ * 商品非粉丝点击人数
+ */
+ @JsonProperty("non_fans_clk_uv")
+ private Long nonFansClkUv;
+
+ /**
+ * 商品非粉丝点击次数
+ */
+ @JsonProperty("non_fans_clk_pv")
+ private Long nonFansClkPv;
+
+ /**
+ * 商品分享人数
+ */
+ @JsonProperty("share_uv")
+ private Long shareUv;
+
+ /**
+ * 商品分享次数
+ */
+ @JsonProperty("share_pv")
+ private Long sharePv;
+
+ /**
+ * 商品曝光点击率
+ */
+ @JsonProperty("exp_clk_ratio")
+ private Double expClkRatio;
+
+ /**
+ * 商品点击成交率
+ */
+ @JsonProperty("clk_pay_ratio")
+ private Double clkPayRatio;
+
+ /**
+ * 商品成交金额(单位:分)
+ */
+ @JsonProperty("gmv")
+ private Long gmv;
+
+ /**
+ * 商品成交订单数
+ */
+ @JsonProperty("pay_pv")
+ private Long payPv;
+
+ /**
+ * 商品成交人数
+ */
+ @JsonProperty("pay_uv")
+ private Long payUv;
+
+ /**
+ * 商品粉丝成交订单数
+ */
+ @JsonProperty("fans_pay_pv")
+ private Long fansPayPv;
+
+ /**
+ * 商品粉丝成交人数
+ */
+ @JsonProperty("fans_pay_uv")
+ private Long fansPayUv;
+
+ /**
+ * 商品非粉丝成交订单数
+ */
+ @JsonProperty("non_fans_pay_pv")
+ private Long nonFansPayPv;
+
+ /**
+ * 商品非粉丝成交人数
+ */
+ @JsonProperty("non_fans_pay_uv")
+ private Long nonFansPayUv;
+
+ /**
+ * 商品新客户成交次数
+ */
+ @JsonProperty("new_customer_pay_pv")
+ private Long newCustomerPayPv;
+
+ /**
+ * 商品新客户成交人数
+ */
+ @JsonProperty("new_customer_pay_uv")
+ private Long newCustomerPayUv;
+
+ /**
+ * 商品老客户成交次数
+ */
+ @JsonProperty("repeated_customer_pay_pv")
+ private Long repeatedCustomerPayPv;
+
+ /**
+ * 商品老客户成交人数
+ */
+ @JsonProperty("repeated_customer_pay_uv")
+ private Long repeatedCustomerPayUv;
+
+ /**
+ * 商品退款人数
+ */
+ @JsonProperty("refund_uv")
+ private Long refundUv;
+
+ /**
+ * 商品退款订单数
+ */
+ @JsonProperty("refund_pv")
+ private Long refundPv;
+
+ /**
+ * 商品退款金额(单位:分)
+ */
+ @JsonProperty("refund_amount")
+ private Long refundAmount;
+
+ /**
+ * 商品订单创建人数
+ */
+ @JsonProperty("create_uv")
+ private Long createUv;
+
+ /**
+ * 商品订单创建次数
+ */
+ @JsonProperty("create_pv")
+ private Long createPv;
+
+ /**
+ * 商品粉丝订单创建人数
+ */
+ @JsonProperty("fans_create_uv")
+ private Long fansCreateUv;
+
+ /**
+ * 商品粉丝订单创建次数
+ */
+ @JsonProperty("fans_create_pv")
+ private Long fansCreatePv;
+
+ /**
+ * 商品非粉丝订单创建人数
+ */
+ @JsonProperty("non_fans_create_uv")
+ private Long nonFansCreateUv;
+
+ /**
+ * 商品非粉丝订单创建次数
+ */
+ @JsonProperty("non_fans_create_pv")
+ private Long nonFansCreatePv;
+
+ /**
+ * 商品新客户订单创建人数
+ */
+ @JsonProperty("new_customer_create_uv")
+ private Long newCustomerCreateUv;
+
+ /**
+ * 商品新客户订单创建次数
+ */
+ @JsonProperty("new_customer_create_pv")
+ private Long newCustomerCreatePv;
+
+ /**
+ * 商品老客户订单创建人数
+ */
+ @JsonProperty("repeated_customer_create_uv")
+ private Long repeatedCustomerCreateUv;
+
+ /**
+ * 商品老客户订单创建次数
+ */
+ @JsonProperty("repeated_customer_create_pv")
+ private Long repeatedCustomerCreatePv;
+
+ /**
+ * 商品库存
+ */
+ @JsonProperty("stock")
+ private Long stock;
+
+ /**
+ * 商品退费率
+ */
+ @JsonProperty("refund_rate")
+ private Double refundRate;
+
+ /**
+ * 商品完成订单数
+ */
+ @JsonProperty("finish_pv")
+ private Long finishPv;
+
+ /**
+ * 商品未完成订单数
+ */
+ @JsonProperty("no_finish_pv")
+ private Long noFinishPv;
+
+ /**
+ * 商品新客户转换率
+ */
+ @JsonProperty("new_customer_conversion_rate")
+ private Double newCustomerConversionRate;
+
+ /**
+ * 商品讲解数
+ */
+ @JsonProperty("explanation_count")
+ private Long explanationCount;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/SubLiveDistChannelSourceStats.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/SubLiveDistChannelSourceStats.java
new file mode 100644
index 0000000000..7d183b738b
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/live/dashboard/SubLiveDistChannelSourceStats.java
@@ -0,0 +1,92 @@
+package me.chanjar.weixin.channel.bean.live.dashboard;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 流量来源子渠道指标数据
+ *
+ * @author Winnie
+ */
+@Data
+@NoArgsConstructor
+public class SubLiveDistChannelSourceStats implements Serializable {
+
+ private static final long serialVersionUID = - 5279814435684116105L;
+
+ /**
+ * 渠道层级
+ */
+ @JsonProperty("level")
+ private Integer level;
+
+ /**
+ * 来源渠道ID
+ */
+ @JsonProperty("source_channel_id")
+ private Long sourceChannelId;
+
+ /**
+ * 在该渠道下的统计值
+ */
+ @JsonProperty("metric_value")
+ private Long metricValue;
+
+ /**
+ * GMV总值(单位:分)
+ */
+ @JsonProperty("gmv")
+ private Long gmv;
+
+ /**
+ * UV总值
+ */
+ @JsonProperty("uv")
+ private Long uv;
+
+ /**
+ * 千次看播成交(单位: 分)
+ */
+ @JsonProperty("gmv_per_uv")
+ private Long gmvPerUv;
+
+ /**
+ * gmv占比
+ */
+ @JsonProperty("gmv_ratio")
+ private Double gmvRatio;
+
+ /**
+ * uv占比
+ */
+ @JsonProperty("uv_ratio")
+ private Double uvRatio;
+
+ /**
+ * 在该渠道下的统计值比率
+ */
+ @JsonProperty("metric_value_ratio")
+ private Double metricValueRatio;
+
+ /**
+ * 渠道名称
+ */
+ @JsonProperty("source_channel_name")
+ private String sourceChannelName;
+
+ /**
+ * pv
+ */
+ @JsonProperty("pv")
+ private Long pv;
+
+ /**
+ * 当前层级pv占总pv的比例
+ */
+ @JsonProperty("pv_ratio")
+ private Double pvRatio;
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/constant/WxChannelApiUrlConstants.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/constant/WxChannelApiUrlConstants.java
index 79ff5f8f8d..1fddd1de84 100644
--- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/constant/WxChannelApiUrlConstants.java
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/constant/WxChannelApiUrlConstants.java
@@ -428,4 +428,44 @@ public interface Vip {
/** 更新用户等级 */
String GRADE_UPDATE_URL = "https://api.weixin.qq.com/channels/ec/vip/user/grade/update";
}
+
+ /**
+ * 直播大屏数据
+ */
+ public interface LiveDashboard {
+ /**
+ * 获取直播大屏直播列表
+ */
+ String GET_LIVE_LIST_URL = "https://api.weixin.qq.com/channels/livedashboard/getlivelist";
+
+ /**
+ * 获取直播大屏数据
+ */
+ String GET_LIVE_DATA_URL = "https://api.weixin.qq.com/channels/livedashboard/getlivedata";
+ }
+
+ /**
+ * 罗盘达人版API
+ */
+ public interface CompassFinder {
+ /**
+ * 获取电商概览数据
+ */
+ String GET_OVERALL_URL = "https://api.weixin.qq.com/channels/ec/compass/finder/overall/get";
+
+ /**
+ * 获取带货商品数据
+ */
+ String GET_PRODUCT_DATA_URL = "https://api.weixin.qq.com/channels/ec/compass/finder/product/data/get";
+
+ /**
+ * 获取带货商品列表
+ */
+ String GET_PRODUCT_LIST_URL = "https://api.weixin.qq.com/channels/ec/compass/finder/product/list/get";
+
+ /**
+ * 获取带货人群数据
+ */
+ String GET_SALE_PROFILE_DATA_URL = "https://api.weixin.qq.com/channels/ec/compass/finder/sale/profile/data/get";
+ }
}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/DimensionType.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/DimensionType.java
new file mode 100644
index 0000000000..b713f518ab
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/DimensionType.java
@@ -0,0 +1,80 @@
+package me.chanjar.weixin.channel.enums;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+
+/**
+ * 视频号助手 直播数据维度类型
+ *
+ * @author Winnie
+ */
+@JsonFormat(shape = JsonFormat.Shape.OBJECT)
+public enum DimensionType {
+
+ /**
+ * 一级渠道
+ */
+ PRIMARY_CHANNEL(1, "一级渠道"),
+ /**
+ * 年龄段
+ */
+ AGE(2, "年龄段"),
+ /**
+ * 性别
+ */
+ SEX(3, "性别"),
+ /**
+ * 关注关系
+ */
+ FOLLOW(5, "关注关系"),
+ /**
+ * 二级渠道
+ */
+ SECONDARY_CHANNEL(7, "二级渠道"),
+ /**
+ * 策略人群
+ */
+ CATE(9, "策略人群"),
+ /**
+ * 省级行政区
+ */
+ PROVINCE(10, "省级行政区"),
+ /**
+ * 地级行政区
+ */
+ CITY(11, "地级行政区"),
+ /**
+ * 消费者商品类目偏好
+ */
+ ECOM_USER_LEVEL(12, "消费者商品类目偏好"),
+ /**
+ * 客单价区间
+ */
+ GMV_PER_CNT(13, "客单价区间"),
+// /**
+// * 关注关系
+// */
+// FOLLOW(15, "关注关系"),
+ /**
+ * 流量类型(自然流量、直播加热、广告投流)
+ */
+ FLOW(16, "流量类型(自然流量、直播加热、广告投流)"),
+
+ ;
+
+ private final Integer key;
+ private final String value;
+
+ DimensionType(Integer key, String value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ public Integer getKey() {
+ return key;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/EcProfileDataNodeKey.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/EcProfileDataNodeKey.java
new file mode 100644
index 0000000000..2374576557
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/EcProfileDataNodeKey.java
@@ -0,0 +1,64 @@
+package me.chanjar.weixin.channel.enums;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+
+/**
+ * 视频号助手 用户群体数据节点键
+ *
+ * @author Winnie
+ */
+@JsonFormat(shape = JsonFormat.Shape.OBJECT)
+public enum EcProfileDataNodeKey {
+
+ /**
+ * 性别分布
+ */
+ SEX("sex_distribution", "性别分布"),
+ /**
+ * 年龄分布
+ */
+ AGE("age_distribution", "年龄分布"),
+ /**
+ * 省份分布
+ */
+ PROVINCE("province_distribution", "省份分布"),
+ /**
+ * 城市分布
+ */
+ CITY("city_distribution", "城市分布"),
+ /**
+ * 关注关系分布
+ */
+ FOLLOW("follow_distribution", "关注关系分布"),
+ /**
+ * 策略人群分布
+ */
+ CATE("cate_distribution", "策略人群分布"),
+ /**
+ * 商品类目偏好分布
+ */
+ ECOM_USER_LEVEL("ecom_user_level_distribution", "商品类目偏好分布"),
+ /**
+ * 平均客单价分布
+ */
+ GMV_PER_CNT("gmv_per_cnt_distribution", "平均客单价分布"),
+
+ ;
+
+ private final String key;
+ private final String value;
+
+ EcProfileDataNodeKey(String key, String value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/LiveDistributionFlowType.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/LiveDistributionFlowType.java
new file mode 100644
index 0000000000..b086d02c34
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/LiveDistributionFlowType.java
@@ -0,0 +1,56 @@
+package me.chanjar.weixin.channel.enums;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+
+/**
+ * 视频号助手 直播分布流量类型
+ *
+ * @author Winnie
+ */
+@JsonFormat(shape = JsonFormat.Shape.OBJECT)
+public enum LiveDistributionFlowType {
+
+ /**
+ * 无效值
+ */
+ INVALID(0, "无效值"),
+ /**
+ * 自然流量
+ */
+ NATURAL(1, "自然流量"),
+ /**
+ * 加热流量
+ */
+ PROMOTE(2, "加热流量"),
+ /**
+ * 广告流量
+ */
+ ADS(3, "广告流量"),
+ /**
+ * 公域流量
+ */
+ COMMON_DOMAIN(4, "公域流量"),
+ /**
+ * 私域流量
+ */
+ PRIVATE_DOMAIN(5, "私域流量"),
+
+ ;
+
+ private final Integer key;
+ private final String value;
+
+ LiveDistributionFlowType(Integer key, String value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ public Integer getKey() {
+ return key;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/LiveDistributionSceneType.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/LiveDistributionSceneType.java
new file mode 100644
index 0000000000..46a65f02b4
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/LiveDistributionSceneType.java
@@ -0,0 +1,52 @@
+package me.chanjar.weixin.channel.enums;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+
+/**
+ * 视频号助手 直播分布场景类型
+ *
+ * @author Winnie
+ */
+@JsonFormat(shape = JsonFormat.Shape.OBJECT)
+public enum LiveDistributionSceneType {
+
+ /**
+ * 商品曝光
+ */
+ PRODUCT_IMPRESSION(6, "商品曝光"),
+ /**
+ * 直播间曝光次数
+ */
+ LIVE_ROOM_IMPRESSION_PV(7, "直播间曝光次数"),
+ /**
+ * 商品点击次数
+ */
+ PRODUCT_CLICK_PV(8, "商品点击次数"),
+ /**
+ * 创建订单数按渠道统计
+ */
+ CHANNEL_TOTAL_CREATE_PV(9, "创建订单数按渠道统计"),
+ /**
+ * 成交订单数按渠道统计
+ */
+ CHANNEL_TOTAL_PAY_PV(10, "成交订单数按渠道统计"),
+
+ ;
+
+ private final Integer key;
+ private final String value;
+
+ LiveDistributionSceneType(Integer key, String value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ public Integer getKey() {
+ return key;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+}
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/SaleProfileUserType.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/SaleProfileUserType.java
new file mode 100644
index 0000000000..4bef97641e
--- /dev/null
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/enums/SaleProfileUserType.java
@@ -0,0 +1,56 @@
+package me.chanjar.weixin.channel.enums;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+
+/**
+ * 带货人群用户类型
+ *
+ * @author Winnie
+ */
+@JsonFormat(shape = JsonFormat.Shape.OBJECT)
+public enum SaleProfileUserType {
+
+ /**
+ * 商品曝光用户
+ */
+ PRODUCT_IMPRESSION_USER(1, "商品曝光用户"),
+ /**
+ * 商品点击用户
+ */
+ PRODUCT_CLICK_USER(2, "商品点击用户"),
+ /**
+ * 购买用户
+ */
+ PURCHASING_USER(3, "购买用户"),
+ /**
+ * 首购用户
+ */
+ FIRST_PURCHASE_USER(4, "首购用户"),
+ /**
+ * 复购用户
+ */
+ REPURCHASE_USER(5, "复购用户"),
+ /**
+ * 直播观看用户
+ */
+ LIVE_WATCHER_USER(6, "直播观看用户"),
+
+ ;
+
+ private final Integer key;
+ private final String value;
+
+ SaleProfileUserType(Integer key, String value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ public Integer getKey() {
+ return key;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+}
diff --git a/weixin-java-channel/src/test/java/me/chanjar/weixin/channel/api/impl/WxChannelCompassFinderServiceImplTest.java b/weixin-java-channel/src/test/java/me/chanjar/weixin/channel/api/impl/WxChannelCompassFinderServiceImplTest.java
new file mode 100644
index 0000000000..be178ba947
--- /dev/null
+++ b/weixin-java-channel/src/test/java/me/chanjar/weixin/channel/api/impl/WxChannelCompassFinderServiceImplTest.java
@@ -0,0 +1,66 @@
+package me.chanjar.weixin.channel.api.impl;
+
+import com.google.inject.Inject;
+import me.chanjar.weixin.channel.api.WxChannelCompassFinderService;
+import me.chanjar.weixin.channel.api.WxChannelService;
+import me.chanjar.weixin.channel.bean.compass.finder.OverallResponse;
+import me.chanjar.weixin.channel.bean.compass.finder.ProductDataResponse;
+import me.chanjar.weixin.channel.bean.compass.finder.ProductListResponse;
+import me.chanjar.weixin.channel.bean.compass.finder.SaleProfileDataResponse;
+import me.chanjar.weixin.channel.enums.SaleProfileUserType;
+import me.chanjar.weixin.channel.test.ApiTestModule;
+import me.chanjar.weixin.common.error.WxErrorException;
+import org.testng.annotations.Guice;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+/**
+ * @author Winnie
+ */
+@Guice(modules = ApiTestModule.class)
+public class WxChannelCompassFinderServiceImplTest {
+
+ @Inject
+ private WxChannelService channelService;
+
+ @Test
+ public void testGetOverAll() throws WxErrorException {
+ WxChannelCompassFinderService compassFinderService = channelService.getCompassFinderService();
+ String ds = "20240630";
+ OverallResponse response = compassFinderService.getOverall(ds);
+ assertNotNull(response);
+ assertTrue(response.isSuccess());
+ }
+
+ @Test
+ public void testGetProductData() throws WxErrorException {
+ WxChannelCompassFinderService compassFinderService = channelService.getCompassFinderService();
+ String ds = "20240630";
+ String productId = "10000017457793";
+ ProductDataResponse response = compassFinderService.getProductData(ds, productId);
+ assertNotNull(response);
+ assertTrue(response.isSuccess());
+ }
+
+ @Test
+ public void testGetProductList() throws WxErrorException {
+ WxChannelCompassFinderService compassFinderService = channelService.getCompassFinderService();
+ String ds = "20240630";
+ ProductListResponse response = compassFinderService.getProductList(ds);
+ assertNotNull(response);
+ assertTrue(response.isSuccess());
+ }
+
+ @Test
+ public void testGetSaleProfileData() throws WxErrorException {
+ WxChannelCompassFinderService compassFinderService = channelService.getCompassFinderService();
+ String ds = "20240630";
+ Integer type = SaleProfileUserType.PRODUCT_IMPRESSION_USER.getKey();
+ SaleProfileDataResponse response = compassFinderService.getSaleProfileData(ds, type);
+ assertNotNull(response);
+ assertTrue(response.isSuccess());
+ }
+
+}
diff --git a/weixin-java-channel/src/test/java/me/chanjar/weixin/channel/api/impl/WxChannelLiveDashboardServiceImplTest.java b/weixin-java-channel/src/test/java/me/chanjar/weixin/channel/api/impl/WxChannelLiveDashboardServiceImplTest.java
new file mode 100644
index 0000000000..ec2e161da1
--- /dev/null
+++ b/weixin-java-channel/src/test/java/me/chanjar/weixin/channel/api/impl/WxChannelLiveDashboardServiceImplTest.java
@@ -0,0 +1,44 @@
+package me.chanjar.weixin.channel.api.impl;
+
+import com.google.inject.Inject;
+import me.chanjar.weixin.channel.api.WxChannelLiveDashboardService;
+import me.chanjar.weixin.channel.api.WxChannelService;
+import me.chanjar.weixin.channel.bean.live.dashboard.LiveDataResponse;
+import me.chanjar.weixin.channel.bean.live.dashboard.LiveListResponse;
+import me.chanjar.weixin.channel.test.ApiTestModule;
+import me.chanjar.weixin.common.error.WxErrorException;
+import org.testng.annotations.Guice;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+/**
+ * @author Winnie
+ */
+@Guice(modules = ApiTestModule.class)
+public class WxChannelLiveDashboardServiceImplTest {
+
+ @Inject
+ private WxChannelService channelService;
+
+ @Test
+ public void testGetLiveList() throws WxErrorException {
+ WxChannelLiveDashboardService liveDashboardService = channelService.getLiveDashboardService();
+ // yyyyMMdd
+ Long ds = 20240630L;
+ LiveListResponse response = liveDashboardService.getLiveList(ds);
+ assertNotNull(response);
+ assertTrue(response.isSuccess());
+ }
+
+ @Test
+ public void testGetLiveData() throws WxErrorException {
+ WxChannelLiveDashboardService liveDashboardService = channelService.getLiveDashboardService();
+ String exportId = "export/UzFf*****************************************************************************************64V";
+ LiveDataResponse response = liveDashboardService.getLiveData(exportId);
+ assertNotNull(response);
+ assertTrue(response.isSuccess());
+ }
+
+}
From 3aba0b7ec554c93efb2acf93adee3d29f1022657 Mon Sep 17 00:00:00 2001
From: Binary Wang
Date: Tue, 10 Sep 2024 23:55:49 +0800
Subject: [PATCH 051/385] =?UTF-8?q?:bookmark:=20=E5=8F=91=E5=B8=83=204.6.5?=
=?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 +-
solon-plugins/pom.xml | 2 +-
solon-plugins/wx-java-channel-solon-plugin/pom.xml | 2 +-
solon-plugins/wx-java-cp-multi-solon-plugin/pom.xml | 2 +-
solon-plugins/wx-java-cp-solon-plugin/pom.xml | 2 +-
solon-plugins/wx-java-miniapp-solon-plugin/pom.xml | 2 +-
solon-plugins/wx-java-mp-multi-solon-plugin/pom.xml | 2 +-
solon-plugins/wx-java-mp-solon-plugin/pom.xml | 2 +-
solon-plugins/wx-java-open-solon-plugin/pom.xml | 2 +-
solon-plugins/wx-java-pay-solon-plugin/pom.xml | 2 +-
solon-plugins/wx-java-qidian-solon-plugin/pom.xml | 2 +-
spring-boot-starters/pom.xml | 2 +-
.../wx-java-channel-spring-boot-starter/pom.xml | 2 +-
.../wx-java-cp-multi-spring-boot-starter/pom.xml | 2 +-
spring-boot-starters/wx-java-cp-spring-boot-starter/pom.xml | 2 +-
.../wx-java-miniapp-multi-spring-boot-starter/pom.xml | 2 +-
.../wx-java-miniapp-spring-boot-starter/pom.xml | 2 +-
.../wx-java-mp-multi-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 +-
spring-boot-starters/wx-java-qidian-spring-boot-starter/pom.xml | 2 +-
weixin-graal/pom.xml | 2 +-
weixin-java-channel/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 +-
weixin-java-qidian/pom.xml | 2 +-
31 files changed, 31 insertions(+), 31 deletions(-)
diff --git a/pom.xml b/pom.xml
index 13cabe0e47..08a20de0a5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
4.0.0
com.github.binarywang
wx-java
- 4.6.4.B
+ 4.6.5.B
pom
WxJava - Weixin/Wechat Java SDK
微信开发Java SDK
diff --git a/solon-plugins/pom.xml b/solon-plugins/pom.xml
index 87317902c8..afa8ccec3a 100644
--- a/solon-plugins/pom.xml
+++ b/solon-plugins/pom.xml
@@ -6,7 +6,7 @@
com.github.binarywang
wx-java
- 4.6.4.B
+ 4.6.5.B
pom
wx-java-solon-plugins
diff --git a/solon-plugins/wx-java-channel-solon-plugin/pom.xml b/solon-plugins/wx-java-channel-solon-plugin/pom.xml
index fbd17094ae..8e9fb98d38 100644
--- a/solon-plugins/wx-java-channel-solon-plugin/pom.xml
+++ b/solon-plugins/wx-java-channel-solon-plugin/pom.xml
@@ -3,7 +3,7 @@
wx-java-solon-plugins
com.github.binarywang
- 4.6.4.B
+ 4.6.5.B
4.0.0
diff --git a/solon-plugins/wx-java-cp-multi-solon-plugin/pom.xml b/solon-plugins/wx-java-cp-multi-solon-plugin/pom.xml
index edca6bda61..1df40064b3 100644
--- a/solon-plugins/wx-java-cp-multi-solon-plugin/pom.xml
+++ b/solon-plugins/wx-java-cp-multi-solon-plugin/pom.xml
@@ -4,7 +4,7 @@
wx-java-solon-plugins
com.github.binarywang
- 4.6.4.B
+ 4.6.5.B
4.0.0
diff --git a/solon-plugins/wx-java-cp-solon-plugin/pom.xml b/solon-plugins/wx-java-cp-solon-plugin/pom.xml
index 6b71454c68..2355daa5ea 100644
--- a/solon-plugins/wx-java-cp-solon-plugin/pom.xml
+++ b/solon-plugins/wx-java-cp-solon-plugin/pom.xml
@@ -4,7 +4,7 @@
wx-java-solon-plugins
com.github.binarywang
- 4.6.4.B
+ 4.6.5.B
4.0.0
diff --git a/solon-plugins/wx-java-miniapp-solon-plugin/pom.xml b/solon-plugins/wx-java-miniapp-solon-plugin/pom.xml
index b4d527d711..2271693f65 100644
--- a/solon-plugins/wx-java-miniapp-solon-plugin/pom.xml
+++ b/solon-plugins/wx-java-miniapp-solon-plugin/pom.xml
@@ -4,7 +4,7 @@
wx-java-solon-plugins
com.github.binarywang
- 4.6.4.B
+ 4.6.5.B
4.0.0
diff --git a/solon-plugins/wx-java-mp-multi-solon-plugin/pom.xml b/solon-plugins/wx-java-mp-multi-solon-plugin/pom.xml
index 197561e68b..7fccc78c31 100644
--- a/solon-plugins/wx-java-mp-multi-solon-plugin/pom.xml
+++ b/solon-plugins/wx-java-mp-multi-solon-plugin/pom.xml
@@ -5,7 +5,7 @@
wx-java-solon-plugins
com.github.binarywang
- 4.6.4.B
+ 4.6.5.B
4.0.0
diff --git a/solon-plugins/wx-java-mp-solon-plugin/pom.xml b/solon-plugins/wx-java-mp-solon-plugin/pom.xml
index 16aac18a57..975d14eccf 100644
--- a/solon-plugins/wx-java-mp-solon-plugin/pom.xml
+++ b/solon-plugins/wx-java-mp-solon-plugin/pom.xml
@@ -5,7 +5,7 @@
wx-java-solon-plugins
com.github.binarywang
- 4.6.4.B
+ 4.6.5.B
4.0.0
diff --git a/solon-plugins/wx-java-open-solon-plugin/pom.xml b/solon-plugins/wx-java-open-solon-plugin/pom.xml
index 00fce6281e..9479a7af36 100644
--- a/solon-plugins/wx-java-open-solon-plugin/pom.xml
+++ b/solon-plugins/wx-java-open-solon-plugin/pom.xml
@@ -5,7 +5,7 @@
wx-java-solon-plugins
com.github.binarywang
- 4.6.4.B
+ 4.6.5.B
4.0.0
diff --git a/solon-plugins/wx-java-pay-solon-plugin/pom.xml b/solon-plugins/wx-java-pay-solon-plugin/pom.xml
index 9805e1d174..daab97a932 100644
--- a/solon-plugins/wx-java-pay-solon-plugin/pom.xml
+++ b/solon-plugins/wx-java-pay-solon-plugin/pom.xml
@@ -5,7 +5,7 @@
wx-java-solon-plugins
com.github.binarywang
- 4.6.4.B
+ 4.6.5.B
4.0.0
diff --git a/solon-plugins/wx-java-qidian-solon-plugin/pom.xml b/solon-plugins/wx-java-qidian-solon-plugin/pom.xml
index c1e31e5839..5a87c882e2 100644
--- a/solon-plugins/wx-java-qidian-solon-plugin/pom.xml
+++ b/solon-plugins/wx-java-qidian-solon-plugin/pom.xml
@@ -3,7 +3,7 @@
wx-java-solon-plugins
com.github.binarywang
- 4.6.4.B
+ 4.6.5.B
4.0.0
diff --git a/spring-boot-starters/pom.xml b/spring-boot-starters/pom.xml
index bc6b79267a..9c5f270dc6 100644
--- a/spring-boot-starters/pom.xml
+++ b/spring-boot-starters/pom.xml
@@ -6,7 +6,7 @@
com.github.binarywang
wx-java
- 4.6.4.B
+ 4.6.5.B
pom
wx-java-spring-boot-starters
diff --git a/spring-boot-starters/wx-java-channel-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-channel-spring-boot-starter/pom.xml
index 216a5ede68..3a5f881414 100644
--- a/spring-boot-starters/wx-java-channel-spring-boot-starter/pom.xml
+++ b/spring-boot-starters/wx-java-channel-spring-boot-starter/pom.xml
@@ -3,7 +3,7 @@
wx-java-spring-boot-starters
com.github.binarywang
- 4.6.4.B
+ 4.6.5.B
4.0.0
diff --git a/spring-boot-starters/wx-java-cp-multi-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-cp-multi-spring-boot-starter/pom.xml
index 45b55e1444..f5afb8c224 100644
--- a/spring-boot-starters/wx-java-cp-multi-spring-boot-starter/pom.xml
+++ b/spring-boot-starters/wx-java-cp-multi-spring-boot-starter/pom.xml
@@ -4,7 +4,7 @@
wx-java-spring-boot-starters
com.github.binarywang
- 4.6.4.B
+ 4.6.5.B
4.0.0
diff --git a/spring-boot-starters/wx-java-cp-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-cp-spring-boot-starter/pom.xml
index f718a0b0aa..d8fd64e39d 100644
--- a/spring-boot-starters/wx-java-cp-spring-boot-starter/pom.xml
+++ b/spring-boot-starters/wx-java-cp-spring-boot-starter/pom.xml
@@ -4,7 +4,7 @@
wx-java-spring-boot-starters
com.github.binarywang
- 4.6.4.B
+ 4.6.5.B
4.0.0
diff --git a/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/pom.xml
index d0ae396436..ddc6d9a021 100644
--- a/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/pom.xml
+++ b/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/pom.xml
@@ -5,7 +5,7 @@
wx-java-spring-boot-starters
com.github.binarywang
- 4.6.4.B
+ 4.6.5.B
4.0.0
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 e38a606435..d1dc9f778f 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
@@ -4,7 +4,7 @@
wx-java-spring-boot-starters
com.github.binarywang
- 4.6.4.B
+ 4.6.5.B
4.0.0
diff --git a/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/pom.xml
index 14232f0589..efef2888ef 100644
--- a/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/pom.xml
+++ b/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/pom.xml
@@ -5,7 +5,7 @@
wx-java-spring-boot-starters
com.github.binarywang
- 4.6.4.B
+ 4.6.5.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 7d61def348..bea36d56f7 100644
--- a/spring-boot-starters/wx-java-mp-spring-boot-starter/pom.xml
+++ b/spring-boot-starters/wx-java-mp-spring-boot-starter/pom.xml
@@ -5,7 +5,7 @@
wx-java-spring-boot-starters
com.github.binarywang
- 4.6.4.B
+ 4.6.5.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 4d65890853..fd78ab5ece 100644
--- a/spring-boot-starters/wx-java-open-spring-boot-starter/pom.xml
+++ b/spring-boot-starters/wx-java-open-spring-boot-starter/pom.xml
@@ -5,7 +5,7 @@
wx-java-spring-boot-starters
com.github.binarywang
- 4.6.4.B
+ 4.6.5.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 48480875ec..18ca983e3e 100644
--- a/spring-boot-starters/wx-java-pay-spring-boot-starter/pom.xml
+++ b/spring-boot-starters/wx-java-pay-spring-boot-starter/pom.xml
@@ -5,7 +5,7 @@
wx-java-spring-boot-starters
com.github.binarywang
- 4.6.4.B
+ 4.6.5.B
4.0.0
diff --git a/spring-boot-starters/wx-java-qidian-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-qidian-spring-boot-starter/pom.xml
index cf0a73c5c8..f917ed262d 100644
--- a/spring-boot-starters/wx-java-qidian-spring-boot-starter/pom.xml
+++ b/spring-boot-starters/wx-java-qidian-spring-boot-starter/pom.xml
@@ -3,7 +3,7 @@
wx-java-spring-boot-starters
com.github.binarywang
- 4.6.4.B
+ 4.6.5.B
4.0.0
diff --git a/weixin-graal/pom.xml b/weixin-graal/pom.xml
index 125efdaded..6f28e0b030 100644
--- a/weixin-graal/pom.xml
+++ b/weixin-graal/pom.xml
@@ -6,7 +6,7 @@
com.github.binarywang
wx-java
- 4.6.4.B
+ 4.6.5.B
weixin-graal
diff --git a/weixin-java-channel/pom.xml b/weixin-java-channel/pom.xml
index 7062d58fda..b3a8f1b539 100644
--- a/weixin-java-channel/pom.xml
+++ b/weixin-java-channel/pom.xml
@@ -6,7 +6,7 @@
com.github.binarywang
wx-java
- 4.6.4.B
+ 4.6.5.B
weixin-java-channel
diff --git a/weixin-java-common/pom.xml b/weixin-java-common/pom.xml
index 0ef68637ee..0c9919afb0 100644
--- a/weixin-java-common/pom.xml
+++ b/weixin-java-common/pom.xml
@@ -6,7 +6,7 @@
com.github.binarywang
wx-java
- 4.6.4.B
+ 4.6.5.B
weixin-java-common
diff --git a/weixin-java-cp/pom.xml b/weixin-java-cp/pom.xml
index 4e41cd107b..f7fe59cc73 100644
--- a/weixin-java-cp/pom.xml
+++ b/weixin-java-cp/pom.xml
@@ -7,7 +7,7 @@
com.github.binarywang
wx-java
- 4.6.4.B
+ 4.6.5.B
weixin-java-cp
diff --git a/weixin-java-miniapp/pom.xml b/weixin-java-miniapp/pom.xml
index 84120767fb..62b281e5ec 100644
--- a/weixin-java-miniapp/pom.xml
+++ b/weixin-java-miniapp/pom.xml
@@ -7,7 +7,7 @@
com.github.binarywang
wx-java
- 4.6.4.B
+ 4.6.5.B
weixin-java-miniapp
diff --git a/weixin-java-mp/pom.xml b/weixin-java-mp/pom.xml
index 093ceefa57..0cdf42def9 100644
--- a/weixin-java-mp/pom.xml
+++ b/weixin-java-mp/pom.xml
@@ -7,7 +7,7 @@
com.github.binarywang
wx-java
- 4.6.4.B
+ 4.6.5.B
weixin-java-mp
diff --git a/weixin-java-open/pom.xml b/weixin-java-open/pom.xml
index 1594a1dccf..aa83300b00 100644
--- a/weixin-java-open/pom.xml
+++ b/weixin-java-open/pom.xml
@@ -7,7 +7,7 @@
com.github.binarywang
wx-java
- 4.6.4.B
+ 4.6.5.B
weixin-java-open
diff --git a/weixin-java-pay/pom.xml b/weixin-java-pay/pom.xml
index 2623e57b97..171e64658a 100644
--- a/weixin-java-pay/pom.xml
+++ b/weixin-java-pay/pom.xml
@@ -5,7 +5,7 @@
com.github.binarywang
wx-java
- 4.6.4.B
+ 4.6.5.B
4.0.0
diff --git a/weixin-java-qidian/pom.xml b/weixin-java-qidian/pom.xml
index 998bfa6bdf..7b843d8b7c 100644
--- a/weixin-java-qidian/pom.xml
+++ b/weixin-java-qidian/pom.xml
@@ -7,7 +7,7 @@
com.github.binarywang
wx-java
- 4.6.4.B
+ 4.6.5.B
weixin-java-qidian
From b0b487fee101a3b6e313667dbdf816147a976a3d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=AD=94=E5=A4=8D=E5=93=88?=
Date: Thu, 12 Sep 2024 19:57:01 +0800
Subject: [PATCH 052/385] =?UTF-8?q?:bug:=20#3369=E3=80=90=E5=BE=AE?=
=?UTF-8?q?=E4=BF=A1=E6=94=AF=E4=BB=98=E3=80=91=E4=BF=AE=E5=A4=8D=E6=9F=A5?=
=?UTF-8?q?=E8=AF=A2=E5=88=86=E8=B4=A6=E7=BB=93=E6=9E=9Ctransaction=5Fid?=
=?UTF-8?q?=E5=8F=96=E5=80=BC=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../binarywang/wxpay/service/impl/ProfitSharingServiceImpl.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
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 7f46a3f303..6be5ffc8c1 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
@@ -146,7 +146,7 @@ public ProfitSharingV3Result profitSharingQueryV3(String outOrderNo, String tran
@Override
public ProfitSharingV3Result profitSharingQueryV3(ProfitSharingQueryV3Request request) throws WxPayException {
String url = String.format("%s/v3/profitsharing/orders/%s?transaction_id=%s", this.payService.getPayBaseUrl(),
- request.getOutOrderNo(), request.getOutOrderNo());
+ request.getOutOrderNo(), request.getTransactionId());
if(StringUtils.isNotEmpty(request.getSubMchId())){
url += "&sub_mchid=" + request.getSubMchId();
}
From 0ac68ee68049cd253f013d553f51cddfecc5ba32 Mon Sep 17 00:00:00 2001
From: Winnie
Date: Mon, 16 Sep 2024 16:06:15 +0800
Subject: [PATCH 053/385] =?UTF-8?q?:new:=20#3372=E3=80=90=E8=A7=86?=
=?UTF-8?q?=E9=A2=91=E5=8F=B7=E3=80=91=E5=A2=9E=E5=8A=A0=E6=94=AF=E6=8C=81?=
=?UTF-8?q?=E5=A4=9A=E8=A7=86=E9=A2=91=E5=8F=B7=E8=B4=A6=E5=8F=B7=E7=9A=84?=
=?UTF-8?q?spring-boot-starter=E7=BB=84=E4=BB=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
spring-boot-starters/pom.xml | 1 +
.../README.md | 123 +++++++++++++++
.../pom.xml | 71 +++++++++
.../WxChannelMultiAutoConfiguration.java | 15 ++
.../WxChannelMultiServiceConfiguration.java | 21 +++
.../AbstractWxChannelConfiguration.java | 140 ++++++++++++++++++
.../WxChannelInJedisConfiguration.java | 74 +++++++++
.../WxChannelInMemoryConfiguration.java | 36 +++++
...WxChannelInRedisTemplateConfiguration.java | 42 ++++++
.../WxChannelInRedissonConfiguration.java | 62 ++++++++
.../wxjava/channel/enums/HttpClientType.java | 19 +++
.../wxjava/channel/enums/StorageType.java | 26 ++++
.../properties/WxChannelMultiProperties.java | 96 ++++++++++++
.../WxChannelMultiRedisProperties.java | 63 ++++++++
.../properties/WxChannelSingleProperties.java | 43 ++++++
.../service/WxChannelMultiServices.java | 26 ++++
.../service/WxChannelMultiServicesImpl.java | 36 +++++
.../main/resources/META-INF/spring.factories | 2 +
...ot.autoconfigure.AutoConfiguration.imports | 1 +
19 files changed, 897 insertions(+)
create mode 100644 spring-boot-starters/wx-java-channel-multi-spring-boot-starter/README.md
create mode 100644 spring-boot-starters/wx-java-channel-multi-spring-boot-starter/pom.xml
create mode 100644 spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/autoconfigure/WxChannelMultiAutoConfiguration.java
create mode 100644 spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/configuration/WxChannelMultiServiceConfiguration.java
create mode 100644 spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/configuration/services/AbstractWxChannelConfiguration.java
create mode 100644 spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/configuration/services/WxChannelInJedisConfiguration.java
create mode 100644 spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/configuration/services/WxChannelInMemoryConfiguration.java
create mode 100644 spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/configuration/services/WxChannelInRedisTemplateConfiguration.java
create mode 100644 spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/configuration/services/WxChannelInRedissonConfiguration.java
create mode 100644 spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/enums/HttpClientType.java
create mode 100644 spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/enums/StorageType.java
create mode 100644 spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/properties/WxChannelMultiProperties.java
create mode 100644 spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/properties/WxChannelMultiRedisProperties.java
create mode 100644 spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/properties/WxChannelSingleProperties.java
create mode 100644 spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/service/WxChannelMultiServices.java
create mode 100644 spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/service/WxChannelMultiServicesImpl.java
create mode 100644 spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/resources/META-INF/spring.factories
create mode 100644 spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
diff --git a/spring-boot-starters/pom.xml b/spring-boot-starters/pom.xml
index 9c5f270dc6..fa24d4edab 100644
--- a/spring-boot-starters/pom.xml
+++ b/spring-boot-starters/pom.xml
@@ -28,6 +28,7 @@
wx-java-cp-multi-spring-boot-starter
wx-java-cp-spring-boot-starter
wx-java-channel-spring-boot-starter
+ wx-java-channel-multi-spring-boot-starter
diff --git a/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/README.md b/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/README.md
new file mode 100644
index 0000000000..c2f082bec8
--- /dev/null
+++ b/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/README.md
@@ -0,0 +1,123 @@
+# wx-java-channel-multi-spring-boot-starter
+
+## 快速开始
+
+1. 引入依赖
+ ```xml
+
+
+ com.github.binarywang
+ wx-java-channel-multi-spring-boot-starter
+ ${version}
+
+
+
+
+ redis.clients
+ jedis
+ ${jedis.version}
+
+
+
+
+ org.redisson
+ redisson
+ ${redisson.version}
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-redis
+
+
+ ```
+2. 添加配置(application.properties)
+ ```properties
+ # 视频号配置
+ ## 应用 1 配置(必填)
+ wx.channel.apps.tenantId1.app-id=@appId
+ wx.channel.apps.tenantId1.secret=@secret
+ ## 选填
+ wx.channel.apps.tenantId1.use-stable-access-token=false
+ wx.channel.apps.tenantId1.token=
+ wx.channel.apps.tenantId1.aes-key=
+ ## 应用 2 配置(必填)
+ wx.channel.apps.tenantId2.app-id=@appId
+ wx.channel.apps.tenantId2.secret=@secret
+ ## 选填
+ wx.channel.apps.tenantId2.use-stable-access-token=false
+ wx.channel.apps.tenantId2.token=
+ wx.channel.apps.tenantId2.aes-key=
+
+ # ConfigStorage 配置(选填)
+ ## 配置类型: memory(默认), jedis, redisson, redis_template
+ wx.channel.config-storage.type=memory
+ ## 相关redis前缀配置: wx:channel:multi(默认)
+ wx.channel.config-storage.key-prefix=wx:channel:multi
+ wx.channel.config-storage.redis.host=127.0.0.1
+ wx.channel.config-storage.redis.port=6379
+ wx.channel.config-storage.redis.password=123456
+
+ # redis_template 方式使用spring data redis配置
+ spring.data.redis.database=0
+ spring.data.redis.host=127.0.0.1
+ spring.data.redis.password=123456
+ spring.data.redis.port=6379
+
+ # http 客户端配置(选填)
+ ## # http客户端类型: http_client(默认)
+ wx.channel.config-storage.http-client-type=http_client
+ wx.channel.config-storage.http-proxy-host=
+ wx.channel.config-storage.http-proxy-port=
+ wx.channel.config-storage.http-proxy-username=
+ wx.channel.config-storage.http-proxy-password=
+ ## 最大重试次数,默认:5 次,如果小于 0,则为 0
+ wx.channel.config-storage.max-retry-times=5
+ ## 重试时间间隔步进,默认:1000 毫秒,如果小于 0,则为 1000
+ wx.channel.config-storage.retry-sleep-millis=1000
+ ```
+3. 自动注入的类型:`WxChannelMultiServices`
+
+4. 使用样例
+
+ ```java
+ import com.binarywang.spring.starter.wxjava.channel.service.WxChannelMultiServices;
+ import me.chanjar.weixin.channel.api.WxChannelService;
+ import me.chanjar.weixin.channel.api.WxFinderLiveService;
+ import me.chanjar.weixin.channel.bean.lead.component.response.FinderAttrResponse;
+ import me.chanjar.weixin.common.error.WxErrorException;
+ import org.springframework.beans.factory.annotation.Autowired;
+ import org.springframework.stereotype.Service;
+
+ @Service
+ public class DemoService {
+ @Autowired
+ private WxChannelMultiServices wxChannelMultiServices;
+
+ public void test() throws WxErrorException {
+ // 应用 1 的 WxChannelService
+ WxChannelService wxChannelService1 = wxChannelMultiServices.getWxChannelService("tenantId1");
+ WxFinderLiveService finderLiveService = wxChannelService1.getFinderLiveService();
+ FinderAttrResponse response1 = finderLiveService.getFinderAttrByAppid();
+ // todo ...
+
+ // 应用 2 的 WxChannelService
+ WxChannelService wxChannelService2 = wxChannelMultiServices.getWxChannelService("tenantId2");
+ WxFinderLiveService finderLiveService2 = wxChannelService2.getFinderLiveService();
+ FinderAttrResponse response2 = finderLiveService2.getFinderAttrByAppid();
+ // todo ...
+
+ // 应用 3 的 WxChannelService
+ WxChannelService wxChannelService3 = wxChannelMultiServices.getWxChannelService("tenantId3");
+ // 判断是否为空
+ if (wxChannelService3 == null) {
+ // todo wxChannelService3 为空,请先配置 tenantId3 微信视频号应用参数
+ return;
+ }
+ WxFinderLiveService finderLiveService3 = wxChannelService3.getFinderLiveService();
+ FinderAttrResponse response3 = finderLiveService3.getFinderAttrByAppid();
+ // todo ...
+ }
+ }
+ ```
diff --git a/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/pom.xml
new file mode 100644
index 0000000000..563465e3b3
--- /dev/null
+++ b/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/pom.xml
@@ -0,0 +1,71 @@
+
+
+
+ wx-java-spring-boot-starters
+ com.github.binarywang
+ 4.6.5.B
+
+ 4.0.0
+
+ wx-java-channel-multi-spring-boot-starter
+ WxJava - Spring Boot Starter for Channel::支持多账号配置
+ 微信视频号开发的 Spring Boot Starter::支持多账号配置
+
+
+
+ com.github.binarywang
+ weixin-java-channel
+ ${project.version}
+
+
+ redis.clients
+ jedis
+ provided
+
+
+ org.redisson
+ redisson
+ provided
+
+
+ org.springframework.data
+ spring-data-redis
+ provided
+
+
+ org.jodd
+ jodd-http
+ provided
+
+
+ com.squareup.okhttp3
+ okhttp
+ provided
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ ${spring.boot.version}
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ 2.2.1
+
+
+ attach-sources
+
+ jar-no-fork
+
+
+
+
+
+
+
diff --git a/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/autoconfigure/WxChannelMultiAutoConfiguration.java b/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/autoconfigure/WxChannelMultiAutoConfiguration.java
new file mode 100644
index 0000000000..e6ef922b43
--- /dev/null
+++ b/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/autoconfigure/WxChannelMultiAutoConfiguration.java
@@ -0,0 +1,15 @@
+package com.binarywang.spring.starter.wxjava.channel.autoconfigure;
+
+import com.binarywang.spring.starter.wxjava.channel.configuration.WxChannelMultiServiceConfiguration;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+
+/**
+ * 微信视频号自动注册
+ *
+ * @author Winnie
+ * @date 2024/9/13
+ */
+@Configuration
+@Import(WxChannelMultiServiceConfiguration.class)
+public class WxChannelMultiAutoConfiguration {}
diff --git a/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/configuration/WxChannelMultiServiceConfiguration.java b/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/configuration/WxChannelMultiServiceConfiguration.java
new file mode 100644
index 0000000000..17cd0da723
--- /dev/null
+++ b/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/configuration/WxChannelMultiServiceConfiguration.java
@@ -0,0 +1,21 @@
+package com.binarywang.spring.starter.wxjava.channel.configuration;
+
+import com.binarywang.spring.starter.wxjava.channel.configuration.services.WxChannelInJedisConfiguration;
+import com.binarywang.spring.starter.wxjava.channel.configuration.services.WxChannelInMemoryConfiguration;
+import com.binarywang.spring.starter.wxjava.channel.configuration.services.WxChannelInRedisTemplateConfiguration;
+import com.binarywang.spring.starter.wxjava.channel.configuration.services.WxChannelInRedissonConfiguration;
+import com.binarywang.spring.starter.wxjava.channel.properties.WxChannelMultiProperties;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+
+/**
+ * 微信视频号相关服务自动注册
+ *
+ * @author Winnie
+ * @date 2024/9/13
+ */
+@Configuration
+@EnableConfigurationProperties(WxChannelMultiProperties.class)
+@Import({WxChannelInJedisConfiguration.class, WxChannelInMemoryConfiguration.class, WxChannelInRedissonConfiguration.class, WxChannelInRedisTemplateConfiguration.class})
+public class WxChannelMultiServiceConfiguration {}
diff --git a/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/configuration/services/AbstractWxChannelConfiguration.java b/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/configuration/services/AbstractWxChannelConfiguration.java
new file mode 100644
index 0000000000..2e3f92a5f4
--- /dev/null
+++ b/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/configuration/services/AbstractWxChannelConfiguration.java
@@ -0,0 +1,140 @@
+package com.binarywang.spring.starter.wxjava.channel.configuration.services;
+
+import com.binarywang.spring.starter.wxjava.channel.enums.HttpClientType;
+import com.binarywang.spring.starter.wxjava.channel.properties.WxChannelMultiProperties;
+import com.binarywang.spring.starter.wxjava.channel.properties.WxChannelSingleProperties;
+import com.binarywang.spring.starter.wxjava.channel.service.WxChannelMultiServices;
+import com.binarywang.spring.starter.wxjava.channel.service.WxChannelMultiServicesImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.channel.api.WxChannelService;
+import me.chanjar.weixin.channel.api.impl.WxChannelServiceHttpClientImpl;
+import me.chanjar.weixin.channel.api.impl.WxChannelServiceImpl;
+import me.chanjar.weixin.channel.config.WxChannelConfig;
+import me.chanjar.weixin.channel.config.impl.WxChannelDefaultConfigImpl;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * WxChannelConfigStorage 抽象配置类
+ *
+ * @author Winnie
+ * @date 2024/9/13
+ */
+@RequiredArgsConstructor
+@Slf4j
+public abstract class AbstractWxChannelConfiguration {
+ protected WxChannelMultiServices wxChannelMultiServices(WxChannelMultiProperties wxChannelMultiProperties) {
+ Map appsMap = wxChannelMultiProperties.getApps();
+ if (appsMap == null || appsMap.isEmpty()) {
+ log.warn("微信视频号应用参数未配置,通过 WxChannelMultiServices#getWxChannelService(\"tenantId\")获取实例将返回空");
+ return new WxChannelMultiServicesImpl();
+ }
+ /**
+ * 校验 appId 是否唯一,避免使用 redis 缓存 token、ticket 时错乱。
+ *
+ * 查看 {@link me.chanjar.weixin.channel.config.impl.WxChannelRedisConfigImpl#setAppid(String)}
+ */
+ Collection apps = appsMap.values();
+ if (apps.size() > 1) {
+ // 校验 appId 是否唯一
+ boolean multi = apps.stream()
+ // 没有 appId,如果不判断是否为空,这里会报 NPE 异常
+ .collect(Collectors.groupingBy(c -> c.getAppId() == null ? 0 : c.getAppId(), Collectors.counting()))
+ .entrySet().stream().anyMatch(e -> e.getValue() > 1);
+ if (multi) {
+ throw new RuntimeException("请确保微信视频号配置 appId 的唯一性");
+ }
+ }
+ WxChannelMultiServicesImpl services = new WxChannelMultiServicesImpl();
+
+ Set> entries = appsMap.entrySet();
+ for (Map.Entry entry : entries) {
+ String tenantId = entry.getKey();
+ WxChannelSingleProperties wxChannelSingleProperties = entry.getValue();
+ WxChannelDefaultConfigImpl storage = this.wxChannelConfigStorage(wxChannelMultiProperties);
+ this.configApp(storage, wxChannelSingleProperties);
+ this.configHttp(storage, wxChannelMultiProperties.getConfigStorage());
+ WxChannelService wxChannelService = this.wxChannelService(storage, wxChannelMultiProperties, wxChannelSingleProperties.isUseStableAccessToken());
+ services.addWxChannelService(tenantId, wxChannelService);
+ }
+ return services;
+ }
+
+ /**
+ * 配置 WxChannelDefaultConfigImpl
+ *
+ * @param wxChannelMultiProperties 参数
+ * @return WxChannelDefaultConfigImpl
+ */
+ protected abstract WxChannelDefaultConfigImpl wxChannelConfigStorage(WxChannelMultiProperties wxChannelMultiProperties);
+
+ public WxChannelService wxChannelService(WxChannelConfig wxChannelConfig, WxChannelMultiProperties wxChannelMultiProperties, boolean useStableAccessToken) {
+ WxChannelMultiProperties.ConfigStorage storage = wxChannelMultiProperties.getConfigStorage();
+ HttpClientType httpClientType = storage.getHttpClientType();
+ WxChannelService wxChannelService;
+ switch (httpClientType) {
+// case OK_HTTP:
+// wxChannelService = new WxChannelServiceOkHttpImpl(false, false);
+// break;
+ case HTTP_CLIENT:
+ wxChannelService = new WxChannelServiceHttpClientImpl(useStableAccessToken, false);
+ break;
+ default:
+ wxChannelService = new WxChannelServiceImpl();
+ break;
+ }
+
+ wxChannelService.setConfig(wxChannelConfig);
+ int maxRetryTimes = storage.getMaxRetryTimes();
+ if (maxRetryTimes < 0) {
+ maxRetryTimes = 0;
+ }
+ int retrySleepMillis = storage.getRetrySleepMillis();
+ if (retrySleepMillis < 0) {
+ retrySleepMillis = 1000;
+ }
+ wxChannelService.setRetrySleepMillis(retrySleepMillis);
+ wxChannelService.setMaxRetryTimes(maxRetryTimes);
+ return wxChannelService;
+ }
+
+ private void configApp(WxChannelDefaultConfigImpl config, WxChannelSingleProperties wxChannelSingleProperties) {
+ String appId = wxChannelSingleProperties.getAppId();
+ String appSecret = wxChannelSingleProperties.getSecret();
+ String token = wxChannelSingleProperties.getToken();
+ String aesKey = wxChannelSingleProperties.getAesKey();
+
+ config.setAppid(appId);
+ config.setSecret(appSecret);
+ if (StringUtils.isNotBlank(token)) {
+ config.setToken(token);
+ }
+ if (StringUtils.isNotBlank(aesKey)) {
+ config.setAesKey(aesKey);
+ }
+ }
+
+ private void configHttp(WxChannelDefaultConfigImpl config, WxChannelMultiProperties.ConfigStorage storage) {
+ String httpProxyHost = storage.getHttpProxyHost();
+ Integer httpProxyPort = storage.getHttpProxyPort();
+ String httpProxyUsername = storage.getHttpProxyUsername();
+ String httpProxyPassword = storage.getHttpProxyPassword();
+ if (StringUtils.isNotBlank(httpProxyHost)) {
+ config.setHttpProxyHost(httpProxyHost);
+ if (httpProxyPort != null) {
+ config.setHttpProxyPort(httpProxyPort);
+ }
+ if (StringUtils.isNotBlank(httpProxyUsername)) {
+ config.setHttpProxyUsername(httpProxyUsername);
+ }
+ if (StringUtils.isNotBlank(httpProxyPassword)) {
+ config.setHttpProxyPassword(httpProxyPassword);
+ }
+ }
+ }
+}
diff --git a/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/configuration/services/WxChannelInJedisConfiguration.java b/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/configuration/services/WxChannelInJedisConfiguration.java
new file mode 100644
index 0000000000..d19b0fc4b5
--- /dev/null
+++ b/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/configuration/services/WxChannelInJedisConfiguration.java
@@ -0,0 +1,74 @@
+package com.binarywang.spring.starter.wxjava.channel.configuration.services;
+
+import com.binarywang.spring.starter.wxjava.channel.properties.WxChannelMultiProperties;
+import com.binarywang.spring.starter.wxjava.channel.properties.WxChannelMultiRedisProperties;
+import com.binarywang.spring.starter.wxjava.channel.service.WxChannelMultiServices;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.channel.config.impl.WxChannelDefaultConfigImpl;
+import me.chanjar.weixin.channel.config.impl.WxChannelRedisConfigImpl;
+import me.chanjar.weixin.common.redis.JedisWxRedisOps;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import redis.clients.jedis.JedisPool;
+import redis.clients.jedis.JedisPoolConfig;
+
+/**
+ * 自动装配基于 jedis 策略配置
+ *
+ * @author Winnie
+ * @date 2024/9/13
+ */
+@Configuration
+@ConditionalOnProperty(prefix = WxChannelMultiProperties.PREFIX + ".config-storage", name = "type", havingValue = "jedis")
+@RequiredArgsConstructor
+public class WxChannelInJedisConfiguration extends AbstractWxChannelConfiguration {
+ private final WxChannelMultiProperties wxChannelMultiProperties;
+ private final ApplicationContext applicationContext;
+
+ @Bean
+ public WxChannelMultiServices wxChannelMultiServices() {
+ return this.wxChannelMultiServices(wxChannelMultiProperties);
+ }
+
+ @Override
+ protected WxChannelDefaultConfigImpl wxChannelConfigStorage(WxChannelMultiProperties wxChannelMultiProperties) {
+ return this.configRedis(wxChannelMultiProperties);
+ }
+
+ private WxChannelDefaultConfigImpl configRedis(WxChannelMultiProperties wxChannelMultiProperties) {
+ WxChannelMultiRedisProperties wxChannelMultiRedisProperties = wxChannelMultiProperties.getConfigStorage().getRedis();
+ JedisPool jedisPool;
+ if (wxChannelMultiRedisProperties != null && StringUtils.isNotEmpty(wxChannelMultiRedisProperties.getHost())) {
+ jedisPool = getJedisPool(wxChannelMultiProperties);
+ } else {
+ jedisPool = applicationContext.getBean(JedisPool.class);
+ }
+ return new WxChannelRedisConfigImpl(new JedisWxRedisOps(jedisPool), wxChannelMultiProperties.getConfigStorage().getKeyPrefix());
+ }
+
+ private JedisPool getJedisPool(WxChannelMultiProperties wxChannelMultiProperties) {
+ WxChannelMultiProperties.ConfigStorage storage = wxChannelMultiProperties.getConfigStorage();
+ WxChannelMultiRedisProperties redis = storage.getRedis();
+
+ JedisPoolConfig config = new JedisPoolConfig();
+ if (redis.getMaxActive() != null) {
+ config.setMaxTotal(redis.getMaxActive());
+ }
+ if (redis.getMaxIdle() != null) {
+ config.setMaxIdle(redis.getMaxIdle());
+ }
+ if (redis.getMaxWaitMillis() != null) {
+ config.setMaxWaitMillis(redis.getMaxWaitMillis());
+ }
+ if (redis.getMinIdle() != null) {
+ config.setMinIdle(redis.getMinIdle());
+ }
+ config.setTestOnBorrow(true);
+ config.setTestWhileIdle(true);
+
+ return new JedisPool(config, redis.getHost(), redis.getPort(), redis.getTimeout(), redis.getPassword(), redis.getDatabase());
+ }
+}
diff --git a/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/configuration/services/WxChannelInMemoryConfiguration.java b/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/configuration/services/WxChannelInMemoryConfiguration.java
new file mode 100644
index 0000000000..77bb221f25
--- /dev/null
+++ b/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/configuration/services/WxChannelInMemoryConfiguration.java
@@ -0,0 +1,36 @@
+package com.binarywang.spring.starter.wxjava.channel.configuration.services;
+
+import com.binarywang.spring.starter.wxjava.channel.properties.WxChannelMultiProperties;
+import com.binarywang.spring.starter.wxjava.channel.service.WxChannelMultiServices;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.channel.config.impl.WxChannelDefaultConfigImpl;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 自动装配基于内存策略配置
+ *
+ * @author Winnie
+ * @date 2024/9/13
+ */
+@Configuration
+@ConditionalOnProperty(prefix = WxChannelMultiProperties.PREFIX + ".config-storage", name = "type", havingValue = "memory", matchIfMissing = true)
+@RequiredArgsConstructor
+public class WxChannelInMemoryConfiguration extends AbstractWxChannelConfiguration {
+ private final WxChannelMultiProperties wxChannelMultiProperties;
+
+ @Bean
+ public WxChannelMultiServices wxChannelMultiServices() {
+ return this.wxChannelMultiServices(wxChannelMultiProperties);
+ }
+
+ @Override
+ protected WxChannelDefaultConfigImpl wxChannelConfigStorage(WxChannelMultiProperties wxChannelMultiProperties) {
+ return this.configInMemory();
+ }
+
+ private WxChannelDefaultConfigImpl configInMemory() {
+ return new WxChannelDefaultConfigImpl();
+ }
+}
diff --git a/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/configuration/services/WxChannelInRedisTemplateConfiguration.java b/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/configuration/services/WxChannelInRedisTemplateConfiguration.java
new file mode 100644
index 0000000000..f9defdb94a
--- /dev/null
+++ b/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/configuration/services/WxChannelInRedisTemplateConfiguration.java
@@ -0,0 +1,42 @@
+package com.binarywang.spring.starter.wxjava.channel.configuration.services;
+
+import com.binarywang.spring.starter.wxjava.channel.properties.WxChannelMultiProperties;
+import com.binarywang.spring.starter.wxjava.channel.service.WxChannelMultiServices;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.channel.config.impl.WxChannelDefaultConfigImpl;
+import me.chanjar.weixin.channel.config.impl.WxChannelRedisConfigImpl;
+import me.chanjar.weixin.common.redis.RedisTemplateWxRedisOps;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.core.StringRedisTemplate;
+
+/**
+ * 自动装配基于 redisTemplate 策略配置
+ *
+ * @author Winnie
+ * @date 2024/9/13
+ */
+@Configuration
+@ConditionalOnProperty(prefix = WxChannelMultiProperties.PREFIX + ".config-storage", name = "type", havingValue = "redis_template")
+@RequiredArgsConstructor
+public class WxChannelInRedisTemplateConfiguration extends AbstractWxChannelConfiguration {
+ private final WxChannelMultiProperties wxChannelMultiProperties;
+ private final ApplicationContext applicationContext;
+
+ @Bean
+ public WxChannelMultiServices wxChannelMultiServices() {
+ return this.wxChannelMultiServices(wxChannelMultiProperties);
+ }
+
+ @Override
+ protected WxChannelDefaultConfigImpl wxChannelConfigStorage(WxChannelMultiProperties wxChannelMultiProperties) {
+ return this.configRedisTemplate(wxChannelMultiProperties);
+ }
+
+ private WxChannelDefaultConfigImpl configRedisTemplate(WxChannelMultiProperties wxChannelMultiProperties) {
+ StringRedisTemplate redisTemplate = applicationContext.getBean(StringRedisTemplate.class);
+ return new WxChannelRedisConfigImpl(new RedisTemplateWxRedisOps(redisTemplate), wxChannelMultiProperties.getConfigStorage().getKeyPrefix());
+ }
+}
diff --git a/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/configuration/services/WxChannelInRedissonConfiguration.java b/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/configuration/services/WxChannelInRedissonConfiguration.java
new file mode 100644
index 0000000000..fa4798a18b
--- /dev/null
+++ b/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/configuration/services/WxChannelInRedissonConfiguration.java
@@ -0,0 +1,62 @@
+package com.binarywang.spring.starter.wxjava.channel.configuration.services;
+
+import com.binarywang.spring.starter.wxjava.channel.properties.WxChannelMultiProperties;
+import com.binarywang.spring.starter.wxjava.channel.properties.WxChannelMultiRedisProperties;
+import com.binarywang.spring.starter.wxjava.channel.service.WxChannelMultiServices;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.channel.config.impl.WxChannelDefaultConfigImpl;
+import me.chanjar.weixin.channel.config.impl.WxChannelRedissonConfigImpl;
+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.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 自动装配基于 redisson 策略配置
+ *
+ * @author Winnie
+ * @date 2024/9/13
+ */
+@Configuration
+@ConditionalOnProperty(prefix = WxChannelMultiProperties.PREFIX + ".config-storage", name = "type", havingValue = "redisson")
+@RequiredArgsConstructor
+public class WxChannelInRedissonConfiguration extends AbstractWxChannelConfiguration {
+ private final WxChannelMultiProperties wxChannelMultiProperties;
+ private final ApplicationContext applicationContext;
+
+ @Bean
+ public WxChannelMultiServices wxChannelMultiServices() {
+ return this.wxChannelMultiServices(wxChannelMultiProperties);
+ }
+
+ @Override
+ protected WxChannelDefaultConfigImpl wxChannelConfigStorage(WxChannelMultiProperties wxChannelMultiProperties) {
+ return this.configRedisson(wxChannelMultiProperties);
+ }
+
+ private WxChannelDefaultConfigImpl configRedisson(WxChannelMultiProperties wxChannelMultiProperties) {
+ WxChannelMultiRedisProperties redisProperties = wxChannelMultiProperties.getConfigStorage().getRedis();
+ RedissonClient redissonClient;
+ if (redisProperties != null && StringUtils.isNotEmpty(redisProperties.getHost())) {
+ redissonClient = getRedissonClient(wxChannelMultiProperties);
+ } else {
+ redissonClient = applicationContext.getBean(RedissonClient.class);
+ }
+ return new WxChannelRedissonConfigImpl(redissonClient, wxChannelMultiProperties.getConfigStorage().getKeyPrefix());
+ }
+
+ private RedissonClient getRedissonClient(WxChannelMultiProperties wxChannelMultiProperties) {
+ WxChannelMultiProperties.ConfigStorage storage = wxChannelMultiProperties.getConfigStorage();
+ WxChannelMultiRedisProperties redis = storage.getRedis();
+
+ Config config = new Config();
+ config.useSingleServer().setAddress("redis://" + redis.getHost() + ":" + redis.getPort()).setDatabase(redis.getDatabase()).setPassword(redis.getPassword());
+ config.setTransportMode(TransportMode.NIO);
+ return Redisson.create(config);
+ }
+}
diff --git a/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/enums/HttpClientType.java b/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/enums/HttpClientType.java
new file mode 100644
index 0000000000..6ca09354a3
--- /dev/null
+++ b/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/enums/HttpClientType.java
@@ -0,0 +1,19 @@
+package com.binarywang.spring.starter.wxjava.channel.enums;
+
+/**
+ * httpclient类型
+ *
+ * @author Winnie
+ * @date 2024/9/13
+ */
+public enum HttpClientType {
+ /**
+ * HttpClient
+ */
+ HTTP_CLIENT,
+ // WxChannelServiceOkHttpImpl 实现经测试无法正常完成业务固暂不支持OK_HTTP方式
+// /**
+// * OkHttp.
+// */
+// OK_HTTP,
+}
diff --git a/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/enums/StorageType.java b/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/enums/StorageType.java
new file mode 100644
index 0000000000..0ee69eca73
--- /dev/null
+++ b/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/enums/StorageType.java
@@ -0,0 +1,26 @@
+package com.binarywang.spring.starter.wxjava.channel.enums;
+
+/**
+ * storage类型
+ *
+ * @author Winnie
+ * @date 2024/9/13
+ */
+public enum StorageType {
+ /**
+ * 内存
+ */
+ MEMORY,
+ /**
+ * redis(JedisClient)
+ */
+ JEDIS,
+ /**
+ * redis(Redisson)
+ */
+ REDISSON,
+ /**
+ * redis(RedisTemplate)
+ */
+ REDIS_TEMPLATE
+}
diff --git a/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/properties/WxChannelMultiProperties.java b/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/properties/WxChannelMultiProperties.java
new file mode 100644
index 0000000000..d22f560282
--- /dev/null
+++ b/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/properties/WxChannelMultiProperties.java
@@ -0,0 +1,96 @@
+package com.binarywang.spring.starter.wxjava.channel.properties;
+
+import com.binarywang.spring.starter.wxjava.channel.enums.HttpClientType;
+import com.binarywang.spring.starter.wxjava.channel.enums.StorageType;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.context.properties.NestedConfigurationProperty;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 微信多视频号接入相关配置属性
+ *
+ * @author Winnie
+ * @date 2024/9/13
+ */
+@Data
+@NoArgsConstructor
+@ConfigurationProperties(WxChannelMultiProperties.PREFIX)
+public class WxChannelMultiProperties implements Serializable {
+ private static final long serialVersionUID = - 8361973118805546037L;
+ public static final String PREFIX = "wx.channel";
+
+ private Map apps = new HashMap<>();
+
+ /**
+ * 存储策略
+ */
+ private final ConfigStorage configStorage = new ConfigStorage();
+
+ @Data
+ @NoArgsConstructor
+ public static class ConfigStorage implements Serializable {
+ private static final long serialVersionUID = - 5152619132544179942L;
+
+ /**
+ * 存储类型.
+ */
+ private StorageType type = StorageType.MEMORY;
+
+ /**
+ * 指定key前缀.
+ */
+ private String keyPrefix = "wx:channel:multi";
+
+ /**
+ * redis连接配置.
+ */
+ @NestedConfigurationProperty
+ private final WxChannelMultiRedisProperties redis = new WxChannelMultiRedisProperties();
+
+ /**
+ * http客户端类型.
+ */
+ private HttpClientType httpClientType = HttpClientType.HTTP_CLIENT;
+
+ /**
+ * http代理主机.
+ */
+ private String httpProxyHost;
+
+ /**
+ * http代理端口.
+ */
+ private Integer httpProxyPort;
+
+ /**
+ * http代理用户名.
+ */
+ private String httpProxyUsername;
+
+ /**
+ * http代理密码.
+ */
+ private String httpProxyPassword;
+
+ /**
+ * http 请求最大重试次数
+ *
+ * {@link me.chanjar.weixin.channel.api.WxChannelService#setMaxRetryTimes(int)}
+ * {@link me.chanjar.weixin.channel.api.impl.BaseWxChannelServiceImpl#setMaxRetryTimes(int)}
+ */
+ private int maxRetryTimes = 5;
+
+ /**
+ * http 请求重试间隔
+ *
+ * {@link me.chanjar.weixin.channel.api.WxChannelService#setRetrySleepMillis(int)}
+ * {@link me.chanjar.weixin.channel.api.impl.BaseWxChannelServiceImpl#setRetrySleepMillis(int)}
+ */
+ private int retrySleepMillis = 1000;
+ }
+}
diff --git a/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/properties/WxChannelMultiRedisProperties.java b/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/properties/WxChannelMultiRedisProperties.java
new file mode 100644
index 0000000000..99c426765c
--- /dev/null
+++ b/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/properties/WxChannelMultiRedisProperties.java
@@ -0,0 +1,63 @@
+package com.binarywang.spring.starter.wxjava.channel.properties;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * Redis配置
+ *
+ * @author Winnie
+ * @date 2024/9/13
+ */
+@Data
+@NoArgsConstructor
+public class WxChannelMultiRedisProperties implements Serializable {
+ private static final long serialVersionUID = 9061055444734277357L;
+
+ /**
+ * 主机地址.
+ */
+ private String host = "127.0.0.1";
+
+ /**
+ * 端口号.
+ */
+ private int port = 6379;
+
+ /**
+ * 密码.
+ */
+ private String password;
+
+ /**
+ * 超时.
+ */
+ private int timeout = 2000;
+
+ /**
+ * 数据库.
+ */
+ private int database = 0;
+
+ /**
+ * 最大活动连接数
+ */
+ private Integer maxActive;
+
+ /**
+ * 最大空闲连接数
+ */
+ private Integer maxIdle;
+
+ /**
+ * 最小空闲连接数
+ */
+ private Integer minIdle;
+
+ /**
+ * 最大等待时间
+ */
+ private Integer maxWaitMillis;
+}
diff --git a/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/properties/WxChannelSingleProperties.java b/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/properties/WxChannelSingleProperties.java
new file mode 100644
index 0000000000..3e8e2f52bf
--- /dev/null
+++ b/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/properties/WxChannelSingleProperties.java
@@ -0,0 +1,43 @@
+package com.binarywang.spring.starter.wxjava.channel.properties;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 微信视频号相关配置属性
+ *
+ * @author Winnie
+ * @date 2024/9/13
+ */
+@Data
+@NoArgsConstructor
+public class WxChannelSingleProperties implements Serializable {
+ private static final long serialVersionUID = 5306630351265124825L;
+
+ /**
+ * 设置微信视频号的 appid.
+ */
+ private String appId;
+
+ /**
+ * 设置微信视频号的 secret.
+ */
+ private String secret;
+
+ /**
+ * 设置微信视频号的 token.
+ */
+ private String token;
+
+ /**
+ * 设置微信视频号的 EncodingAESKey.
+ */
+ private String aesKey;
+
+ /**
+ * 是否使用稳定版 Access Token
+ */
+ private boolean useStableAccessToken = false;
+}
diff --git a/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/service/WxChannelMultiServices.java b/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/service/WxChannelMultiServices.java
new file mode 100644
index 0000000000..acd4ebf20b
--- /dev/null
+++ b/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/service/WxChannelMultiServices.java
@@ -0,0 +1,26 @@
+package com.binarywang.spring.starter.wxjava.channel.service;
+
+import me.chanjar.weixin.channel.api.WxChannelService;
+
+/**
+ * 视频号 {@link WxChannelService} 所有实例存放类.
+ *
+ * @author Winnie
+ * @date 2024/9/13
+ */
+public interface WxChannelMultiServices {
+ /**
+ * 通过租户 Id 获取 WxChannelService
+ *
+ * @param tenantId 租户 Id
+ * @return WxChannelService
+ */
+ WxChannelService getWxChannelService(String tenantId);
+
+ /**
+ * 根据租户 Id,从列表中移除一个 WxChannelService 实例
+ *
+ * @param tenantId 租户 Id
+ */
+ void removeWxChannelService(String tenantId);
+}
diff --git a/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/service/WxChannelMultiServicesImpl.java b/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/service/WxChannelMultiServicesImpl.java
new file mode 100644
index 0000000000..1673289cb5
--- /dev/null
+++ b/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/service/WxChannelMultiServicesImpl.java
@@ -0,0 +1,36 @@
+package com.binarywang.spring.starter.wxjava.channel.service;
+
+import me.chanjar.weixin.channel.api.WxChannelService;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 视频号 {@link WxChannelMultiServices} 实现
+ *
+ * @author Winnie
+ * @date 2024/9/13
+ */
+public class WxChannelMultiServicesImpl implements WxChannelMultiServices {
+ private final Map services = new ConcurrentHashMap<>();
+
+ @Override
+ public WxChannelService getWxChannelService(String tenantId) {
+ return this.services.get(tenantId);
+ }
+
+ /**
+ * 根据租户 Id,添加一个 WxChannelService 到列表
+ *
+ * @param tenantId 租户 Id
+ * @param wxChannelService WxChannelService 实例
+ */
+ public void addWxChannelService(String tenantId, WxChannelService wxChannelService) {
+ this.services.put(tenantId, wxChannelService);
+ }
+
+ @Override
+ public void removeWxChannelService(String tenantId) {
+ this.services.remove(tenantId);
+ }
+}
diff --git a/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/resources/META-INF/spring.factories b/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000000..2c5a939c32
--- /dev/null
+++ b/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+com.binarywang.spring.starter.wxjava.channel.autoconfigure.WxChannelMultiAutoConfiguration
diff --git a/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
new file mode 100644
index 0000000000..d21a2cdc8d
--- /dev/null
+++ b/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -0,0 +1 @@
+com.binarywang.spring.starter.wxjava.channel.autoconfigure.WxChannelMultiAutoConfiguration
From 9efafa347e2829f2656e6b4f446c30487b1dcf0d Mon Sep 17 00:00:00 2001
From: azhen001 <936124096@qq.com>
Date: Mon, 16 Sep 2024 16:08:22 +0800
Subject: [PATCH 054/385] =?UTF-8?q?:art:=20=E4=BF=AE=E5=A4=8D=E9=83=A8?=
=?UTF-8?q?=E5=88=86=E6=B3=A8=E9=87=8A=E6=96=87=E5=AD=97=EF=BC=9A=E5=B8=90?=
=?UTF-8?q?=E6=94=B9=E4=B8=BA=E8=B4=A6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../weixin/cp/api/WxCpAgentService.java | 2 +-
.../cp/tp/service/WxCpTpLicenseService.java | 42 +++++++++----------
.../wx/miniapp/api/WxMaSubscribeService.java | 16 +++----
.../weixin/mp/api/WxMpKefuService.java | 2 +-
.../mp/api/WxMpSubscribeMsgService.java | 12 +++---
.../weixin/mp/api/WxMpTemplateMsgService.java | 8 ++--
.../weixin/mp/api/WxMpUserService.java | 4 +-
.../weixin/mp/bean/result/WxMpUser.java | 2 +-
.../chanjar/weixin/mp/enums/WxMpApiUrl.java | 6 +--
.../wxpay/service/EcommerceService.java | 4 +-
10 files changed, 49 insertions(+), 49 deletions(-)
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpAgentService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpAgentService.java
index 0c5ccb3b0c..9eddc0f507 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpAgentService.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpAgentService.java
@@ -18,7 +18,7 @@ public interface WxCpAgentService {
/**
*
* 获取企业号应用信息
- * 该API用于获取企业号某个应用的基本信息,包括头像、昵称、帐号类型、认证类型、可见范围等信息
+ * 该API用于获取企业号某个应用的基本信息,包括头像、昵称、账号类型、认证类型、可见范围等信息
* 详情请见: ...
*
*
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/WxCpTpLicenseService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/WxCpTpLicenseService.java
index 66c5166d45..48480ce5a7 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/WxCpTpLicenseService.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/WxCpTpLicenseService.java
@@ -23,8 +23,8 @@ public interface WxCpTpLicenseService {
/**
- * 下单购买帐号
- * 服务商下单为企业购买新的帐号,可以同时购买基础帐号与互通帐号。
+ * 下单购买账号
+ * 服务商下单为企业购买新的账号,可以同时购买基础账号与互通账号。
* 下单之后,需要到服务商管理端发起支付,支付完成之后,订单才能生效。
* 文档地址:https://developer.work.weixin.qq.com/document/path/95644
*
@@ -36,9 +36,9 @@ public interface WxCpTpLicenseService {
/**
- * 创建下单续期帐号任务
+ * 创建下单续期账号任务
*
- * 可以下单为一批已激活帐号的成员续期,续期下单分为两个步骤:
+ * 可以下单为一批已激活账号的成员续期,续期下单分为两个步骤:
* 传入userid列表创建一个任务,创建之后,可以往同一个任务继续追加待续期的userid列表;
* 根据步骤1得到的jobid提交订单。
*
@@ -81,8 +81,8 @@ public interface WxCpTpLicenseService {
/**
* 获取订单详情
- * 查询某个订单的详情,包括订单的状态、基础帐号个数、互通帐号个数、帐号购买时长等。
- * 注意,该接口不返回订单中的帐号激活码列表或者续期的帐号成员列表,请调用获取订单中的帐号列表接口以获取帐号列表。
+ * 查询某个订单的详情,包括订单的状态、基础账号个数、互通账号个数、账号购买时长等。
+ * 注意,该接口不返回订单中的账号激活码列表或者续期的账号成员列表,请调用获取订单中的账号列表接口以获取账号列表。
*
* @param orderId 订单ID
* @return 单条订单信息 order info
@@ -92,10 +92,10 @@ public interface WxCpTpLicenseService {
/**
- * 查询指定订单下的平台能力服务帐号列表。
- * 若为购买帐号的订单或者存量企业的版本付费迁移订单,则返回帐号激活码列表;
- * 若为续期帐号的订单,则返回续期帐号的成员列表。注意,若是购买帐号的订单,
- * 则仅订单支付完成时,系统才会生成帐号,故支付完成之前,该接口不会返回帐号激活码。
+ * 查询指定订单下的平台能力服务账号列表。
+ * 若为购买账号的订单或者存量企业的版本付费迁移订单,则返回账号激活码列表;
+ * 若为续期账号的订单,则返回续期账号的成员列表。注意,若是购买账号的订单,
+ * 则仅订单支付完成时,系统才会生成账号,故支付完成之前,该接口不会返回账号激活码。
* 文档地址:https://developer.work.weixin.qq.com/document/path/95649
*
* @param orderId 订单ID
@@ -108,8 +108,8 @@ public interface WxCpTpLicenseService {
/**
- * 激活帐号
- * 下单购买帐号并支付完成之后,先调用获取订单中的帐号列表接口获取到帐号激活码,
+ * 激活账号
+ * 下单购买账号并支付完成之后,先调用获取订单中的账号列表接口获取到账号激活码,
* 然后可以调用该接口将激活码绑定到某个企业员工,以对其激活相应的平台服务能力。
* 文档地址:https://developer.work.weixin.qq.com/document/path/95553
*
@@ -123,9 +123,9 @@ public interface WxCpTpLicenseService {
/**
- * 批量激活帐号
- * 可在一次请求里为一个企业的多个成员激活许可帐号,便于服务商批量化处理。
- * 一个userid允许激活一个基础帐号以及一个互通帐号。
+ * 批量激活账号
+ * 可在一次请求里为一个企业的多个成员激活许可账号,便于服务商批量化处理。
+ * 一个userid允许激活一个基础账号以及一个互通账号。
* 单次激活的员工数量不超过1000
*
* @param corpId 企业ID
@@ -139,7 +139,7 @@ WxCpTpLicenseBatchActiveResultResp batchActiveCode(String corpId,
/**
* 获取激活码详情
- * 查询某个帐号激活码的状态以及激活绑定情况。
+ * 查询某个账号激活码的状态以及激活绑定情况。
* 文档地址:https://developer.work.weixin.qq.com/document/path/95552
*
* @param code 激活码
@@ -152,7 +152,7 @@ WxCpTpLicenseBatchActiveResultResp batchActiveCode(String corpId,
/**
* 获取激活码详情
- * 查询某个帐号激活码的状态以及激活绑定情况。
+ * 查询某个账号激活码的状态以及激活绑定情况。
* 文档地址:https://developer.work.weixin.qq.com/document/path/95552
*
* @param codes 激活码
@@ -164,8 +164,8 @@ WxCpTpLicenseBatchActiveResultResp batchActiveCode(String corpId,
/**
- * 获取企业的帐号列表
- * 查询指定企业下的平台能力服务帐号列表。
+ * 获取企业的账号列表
+ * 查询指定企业下的平台能力服务账号列表。
* 文档地址:https://developer.work.weixin.qq.com/document/path/95544
*
* @param corpId 企业ID
@@ -191,8 +191,8 @@ WxCpTpLicenseBatchActiveResultResp batchActiveCode(String corpId,
/**
- * 帐号继承
- * 在企业员工离职或者工作范围的有变更时,允许将其许可帐号继承给其他员工。
+ * 账号继承
+ * 在企业员工离职或者工作范围的有变更时,允许将其许可账号继承给其他员工。
*
* @param corpId 企业ID
* @param transferList 转移列表
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
index 694404d980..e6b1ed16a2 100644
--- 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
@@ -19,9 +19,9 @@ public interface WxMaSubscribeService {
/**
*
- * 获取帐号所属类目下的公共模板标题
+ * 获取账号所属类目下的公共模板标题
*
- * 详情请见: 获取帐号所属类目下的公共模板标题
+ * 详情请见: 获取账号所属类目下的公共模板标题
* 接口url格式: https://api.weixin.qq.com/wxaapi/newtmpl/getpubtemplatetitles?access_token=ACCESS_TOKEN
*
*
@@ -49,7 +49,7 @@ public interface WxMaSubscribeService {
/**
*
- * 组合模板并添加至帐号下的个人模板库
+ * 组合模板并添加至账号下的个人模板库
*
* 详情请见: 获取小程序模板库标题列表
* 接口url格式: POST https://api.weixin.qq.com/wxaapi/newtmpl/addtemplate?access_token=ACCESS_TOKEN
@@ -58,16 +58,16 @@ public interface WxMaSubscribeService {
* @param id 模板标题 id,可通过接口获取,也可登录小程序后台查看获取
* @param keywordIdList 模板关键词列表
* @param sceneDesc 服务场景描述,15个字以内
- * @return 添加至帐号下的模板id,发送小程序订阅消息时所需
+ * @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
*
*
@@ -78,9 +78,9 @@ public interface WxMaSubscribeService {
/**
*
- * 删除帐号下的某个模板
+ * 删除账号下的某个模板
*
- * 详情请见: 删除帐号下的个人模板
+ * 详情请见: 删除账号下的个人模板
* 接口url格式: POST https://api.weixin.qq.com/wxaapi/newtmpl/deltemplate?access_token=ACCESS_TOKEN
*
*
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpKefuService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpKefuService.java
index e36238c334..bceb80448d 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpKefuService.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpKefuService.java
@@ -145,7 +145,7 @@ public interface WxMpKefuService {
/**
*
* 创建会话
- * 此接口在客服和用户之间创建一个会话,如果该客服和用户会话已存在,则直接返回0。指定的客服帐号必须已经绑定微信号且在线。
+ * 此接口在客服和用户之间创建一个会话,如果该客服和用户会话已存在,则直接返回0。指定的客服账号必须已经绑定微信号且在线。
* 详情请见:客服会话控制接口
* 接口url格式: https://api.weixin.qq.com/customservice/kfsession/create?access_token=ACCESS_TOKEN
*
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpSubscribeMsgService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpSubscribeMsgService.java
index fa5a5dbbfb..7dbe39f3af 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpSubscribeMsgService.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpSubscribeMsgService.java
@@ -44,9 +44,9 @@ public interface WxMpSubscribeMsgService {
/**
*
- * 获取帐号所属类目下的公共模板标题
+ * 获取账号所属类目下的公共模板标题
*
- * 详情请见: 获取帐号所属类目下的公共模板标题
+ * 详情请见: 获取账号所属类目下的公共模板标题
* 接口url格式: https://api.weixin.qq.com/wxaapi/newtmpl/getpubtemplatetitles?access_token=ACCESS_TOKEN
*
*
@@ -74,7 +74,7 @@ public interface WxMpSubscribeMsgService {
/**
*
- * 组合模板并添加至帐号下的个人模板库
+ * 组合模板并添加至账号下的个人模板库
*
* 详情请见: https://developers.weixin.qq.com/doc/offiaccount/Subscription_Messages/api.html
* 接口url格式: POST https://api.weixin.qq.com/wxaapi/newtmpl/addtemplate?access_token=ACCESS_TOKEN
@@ -83,14 +83,14 @@ public interface WxMpSubscribeMsgService {
* @param id 模板标题 id,可通过接口获取,也可登录小程序后台查看获取
* @param keywordIdList 模板关键词列表
* @param sceneDesc 服务场景描述,15个字以内
- * @return 添加至帐号下的模板id ,发送小程序订阅消息时所需
+ * @return 添加至账号下的模板id ,发送小程序订阅消息时所需
* @throws WxErrorException .
*/
String addTemplate(String id, List keywordIdList, String sceneDesc) throws WxErrorException;
/**
*
- * 获取当前帐号下的个人模板列表
+ * 获取当前账号下的个人模板列表
*
* 详情请见: https://developers.weixin.qq.com/doc/offiaccount/Subscription_Messages/api.html
* 接口url格式: GET https://api.weixin.qq.com/wxaapi/newtmpl/gettemplate?access_token=ACCESS_TOKEN
@@ -103,7 +103,7 @@ public interface WxMpSubscribeMsgService {
/**
*
- * 删除帐号下的某个模板
+ * 删除账号下的某个模板
*
* 详情请见: https://developers.weixin.qq.com/doc/offiaccount/Subscription_Messages/api.html
* 接口url格式: POST https://api.weixin.qq.com/wxaapi/newtmpl/deltemplate?access_token=ACCESS_TOKEN
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpTemplateMsgService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpTemplateMsgService.java
index 08522992d6..5605c93651 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpTemplateMsgService.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpTemplateMsgService.java
@@ -55,7 +55,7 @@ public interface WxMpTemplateMsgService {
/**
*
* 获得模板ID
- * 从行业模板库选择模板到帐号后台,获得模板ID的过程可在MP中完成
+ * 从行业模板库选择模板到账号后台,获得模板ID的过程可在MP中完成
* 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433751277&token=&lang=zh_CN
* 接口地址格式:https://api.weixin.qq.com/cgi-bin/template/api_add_template?access_token=ACCESS_TOKEN
*
@@ -71,7 +71,7 @@ public interface WxMpTemplateMsgService {
/**
*
* 获得模板ID
- * 从类目模板库选择模板到帐号后台,获得模板ID的过程可在MP中完成
+ * 从类目模板库选择模板到账号后台,获得模板ID的过程可在MP中完成
* 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433751277&token=&lang=zh_CN
* 接口地址格式:https://api.weixin.qq.com/cgi-bin/template/api_add_template?access_token=ACCESS_TOKEN
*
@@ -86,7 +86,7 @@ public interface WxMpTemplateMsgService {
/**
*
* 获取模板列表
- * 获取已添加至帐号下所有模板列表,可在MP中查看模板列表信息,为方便第三方开发者,提供通过接口调用的方式来获取帐号下所有模板信息
+ * 获取已添加至账号下所有模板列表,可在MP中查看模板列表信息,为方便第三方开发者,提供通过接口调用的方式来获取账号下所有模板信息
* 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433751277&token=&lang=zh_CN
* 接口地址格式:https://api.weixin.qq.com/cgi-bin/template/get_all_private_template?access_token=ACCESS_TOKEN
*
@@ -99,7 +99,7 @@ public interface WxMpTemplateMsgService {
/**
*
* 删除模板
- * 删除模板可在MP中完成,为方便第三方开发者,提供通过接口调用的方式来删除某帐号下的模板
+ * 删除模板可在MP中完成,为方便第三方开发者,提供通过接口调用的方式来删除某账号下的模板
* 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433751277&token=&lang=zh_CN
* 接口地址格式:https://api.weixin.qq.com/cgi-bin/template/del_private_template?access_token=ACCESS_TOKEN
*
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpUserService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpUserService.java
index 08e599509d..882fe93c00 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpUserService.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpUserService.java
@@ -90,7 +90,7 @@ public interface WxMpUserService {
/**
*
* 获取用户列表
- * 公众号可通过本接口来获取帐号的关注者列表,
+ * 公众号可通过本接口来获取账号的关注者列表,
* 关注者列表由一串OpenID(加密后的微信号,每个用户对每个公众号的OpenID是唯一的)组成。
* 一次拉取调用最多拉取10000个关注者的OpenID,可以通过多次拉取的方式来满足需求。
* 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140840&token=&lang=zh_CN
@@ -107,7 +107,7 @@ public interface WxMpUserService {
/**
*
* 获取用户列表(全部)
- * 公众号可通过本接口来获取帐号的关注者列表,
+ * 公众号可通过本接口来获取账号的关注者列表,
* 关注者列表由一串OpenID(加密后的微信号,每个用户对每个公众号的OpenID是唯一的)组成。
* @return the wx mp user list
* @throws WxErrorException the wx error exception
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpUser.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpUser.java
index bfd8b6d8dd..5fe05bfb91 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpUser.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpUser.java
@@ -36,7 +36,7 @@ public class WxMpUser implements Serializable {
/**
* https://mp.weixin.qq.com/cgi-bin/announce?action=getannouncement&announce_id=11513156443eZYea&version=&lang=zh_CN
*
- * 只有在将公众号绑定到微信开放平台帐号后,才会出现该字段。
+ * 只有在将公众号绑定到微信开放平台账号后,才会出现该字段。
* 另外,在用户未关注公众号时,将不返回用户unionID信息。
* 已关注的用户,开发者可使用“获取用户基本信息接口”获取unionID;
* 未关注用户,开发者可使用“微信授权登录接口”并将scope参数设置为snsapi_userinfo,获取用户unionID
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 a9255f9dd4..dc317bd40e 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
@@ -313,15 +313,15 @@ enum SubscribeMsg implements WxMpApiUrl {
*/
GET_PUB_TEMPLATE_KEY_WORDS_BY_ID_URL(API_DEFAULT_HOST_URL, "/wxaapi/newtmpl/getpubtemplatekeywords"),
/**
- * 组合模板并添加至帐号下的个人模板库.
+ * 组合模板并添加至账号下的个人模板库.
*/
TEMPLATE_ADD_URL(API_DEFAULT_HOST_URL, "/wxaapi/newtmpl/addtemplate"),
/**
- * 获取当前帐号下的个人模板列表.
+ * 获取当前账号下的个人模板列表.
*/
TEMPLATE_LIST_URL(API_DEFAULT_HOST_URL, "/wxaapi/newtmpl/gettemplate"),
/**
- * 删除帐号下的某个模板.
+ * 删除账号下的某个模板.
*/
TEMPLATE_DEL_URL(API_DEFAULT_HOST_URL, "/wxaapi/newtmpl/deltemplate"),
/**
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java
index ca1cfb66b5..af9a1b38b8 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java
@@ -439,12 +439,12 @@ public interface EcommerceService {
/**
*
- * 修改结算帐号API
+ * 修改结算账号API
* 文档地址: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/ecommerce/applyments/chapter3_4.shtml
*
*
* @param subMchid 二级商户号。
- * @param request 结算帐号
+ * @param request 结算账号
* @throws WxPayException the wx pay exception
*/
void modifySettlement(String subMchid, SettlementRequest request) throws WxPayException;
From ff8532b3dfb2c5f9cbcfb637d6e70020bd4113c5 Mon Sep 17 00:00:00 2001
From: Molzx <31435895+Molzx@users.noreply.github.com>
Date: Sat, 28 Sep 2024 20:50:02 +0800
Subject: [PATCH 055/385] =?UTF-8?q?:bug:=20=E4=BF=AE=E5=A4=8D=E5=8F=91?=
=?UTF-8?q?=E8=B5=B7icp=E4=BA=BA=E8=84=B8=E6=A0=B8=E9=AA=8C=E7=9A=84?=
=?UTF-8?q?=E8=BF=94=E5=9B=9E=E7=BB=93=E6=9E=9C=E7=BC=BA=E5=B0=91=E6=A0=B8?=
=?UTF-8?q?=E9=AA=8Cid=E7=9A=84=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../open/bean/icp/WxOpenIcpCreateIcpVerifyTaskResult.java | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenIcpCreateIcpVerifyTaskResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenIcpCreateIcpVerifyTaskResult.java
index 782db16e94..8deb401335 100644
--- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenIcpCreateIcpVerifyTaskResult.java
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenIcpCreateIcpVerifyTaskResult.java
@@ -2,6 +2,7 @@
import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.gson.annotations.SerializedName;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@@ -22,7 +23,7 @@ public class WxOpenIcpCreateIcpVerifyTaskResult extends WxOpenResult {
/**
* 人脸核验任务id
*/
- @JsonProperty("task_id")
+ @SerializedName("task_id")
private String taskId;
}
From 17399b59f5140a003ef3c2da7b37c2c63a0462a2 Mon Sep 17 00:00:00 2001
From: slbiscoding <128227426+slbiscoding@users.noreply.github.com>
Date: Sat, 28 Sep 2024 20:51:26 +0800
Subject: [PATCH 056/385] =?UTF-8?q?:art:=20#3377=20=E3=80=90=E4=BC=81?=
=?UTF-8?q?=E4=B8=9A=E5=BE=AE=E4=BF=A1=E3=80=91=E4=BC=81=E5=BE=AE=E5=AE=A1?=
=?UTF-8?q?=E6=89=B9=E5=A2=9E=E5=8A=A0=E9=99=84=E4=BB=B6=E7=9B=B8=E5=85=B3?=
=?UTF-8?q?=E5=8F=82=E6=95=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../me/chanjar/weixin/cp/bean/oa/applydata/ContentValue.java | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/applydata/ContentValue.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/applydata/ContentValue.java
index e8c379bd33..158206867e 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/applydata/ContentValue.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/applydata/ContentValue.java
@@ -123,6 +123,10 @@ public static class File implements Serializable {
@SerializedName("file_id")
private String fileId;
+ @SerializedName("file_name")
+ private String fileName;
+ @SerializedName("file_url")
+ private String fileUrl;
}
/**
From 42621285fbdeeeb31fb203a8605c42d4b60d142c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=A5=BF=E4=B8=9C?=
Date: Wed, 9 Oct 2024 12:33:11 +0800
Subject: [PATCH 057/385] =?UTF-8?q?:new:=20=E6=B7=BB=E5=8A=A0=20solon-plug?=
=?UTF-8?q?ins=20=20=E4=B8=8B=E7=9A=84=E4=B8=A4=E4=B8=AA=E6=96=B0=E6=A8=A1?=
=?UTF-8?q?=E5=9D=97?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
solon-plugins/pom.xml | 4 +-
.../README.md | 111 +++++++++++++
.../pom.xml | 43 +++++
.../AbstractWxChannelConfiguration.java | 140 ++++++++++++++++
.../WxChannelInJedisConfiguration.java | 77 +++++++++
.../WxChannelInMemoryConfiguration.java | 40 +++++
.../WxChannelInRedissonConfiguration.java | 65 ++++++++
.../wxjava/channel/enums/HttpClientType.java | 19 +++
.../wxjava/channel/enums/StorageType.java | 26 +++
.../integration/WxChannelMultiPluginImpl.java | 25 +++
.../properties/WxChannelMultiProperties.java | 96 +++++++++++
.../WxChannelMultiRedisProperties.java | 63 +++++++
.../properties/WxChannelSingleProperties.java | 43 +++++
.../service/WxChannelMultiServices.java | 26 +++
.../service/WxChannelMultiServicesImpl.java | 36 ++++
...java-multi-channel-solon-plugin.properties | 2 +
.../src/test/java/features/test/LoadTest.java | 15 ++
.../src/test/resources/app.properties | 36 ++++
.../wx-java-cp-multi-solon-plugin/README.md | 2 +
.../README.md | 95 +++++++++++
.../pom.xml | 43 +++++
.../services/AbstractWxMaConfiguration.java | 147 +++++++++++++++++
.../services/WxMaInJedisConfiguration.java | 77 +++++++++
.../services/WxMaInMemoryConfiguration.java | 39 +++++
.../services/WxMaInRedissonConfiguration.java | 68 ++++++++
.../integration/WxMiniappMultiPluginImpl.java | 22 +++
.../properties/WxMaMultiProperties.java | 154 ++++++++++++++++++
.../properties/WxMaMultiRedisProperties.java | 56 +++++++
.../properties/WxMaSingleProperties.java | 40 +++++
.../miniapp/service/WxMaMultiServices.java | 27 +++
.../service/WxMaMultiServicesImpl.java | 36 ++++
...java-miniapp-multi-solon-plugin.properties | 2 +
.../src/test/java/features/test/LoadTest.java | 15 ++
.../src/test/resources/app.properties | 38 +++++
.../wx-java-mp-solon-plugin/README.md | 14 +-
.../src/test/resources/app.properties | 6 +-
36 files changed, 1737 insertions(+), 11 deletions(-)
create mode 100644 solon-plugins/wx-java-channel-multi-solon-plugin/README.md
create mode 100644 solon-plugins/wx-java-channel-multi-solon-plugin/pom.xml
create mode 100644 solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/configuration/services/AbstractWxChannelConfiguration.java
create mode 100644 solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/configuration/services/WxChannelInJedisConfiguration.java
create mode 100644 solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/configuration/services/WxChannelInMemoryConfiguration.java
create mode 100644 solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/configuration/services/WxChannelInRedissonConfiguration.java
create mode 100644 solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/enums/HttpClientType.java
create mode 100644 solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/enums/StorageType.java
create mode 100644 solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/integration/WxChannelMultiPluginImpl.java
create mode 100644 solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/properties/WxChannelMultiProperties.java
create mode 100644 solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/properties/WxChannelMultiRedisProperties.java
create mode 100644 solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/properties/WxChannelSingleProperties.java
create mode 100644 solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/service/WxChannelMultiServices.java
create mode 100644 solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/service/WxChannelMultiServicesImpl.java
create mode 100644 solon-plugins/wx-java-channel-multi-solon-plugin/src/main/resources/META-INF/solon/wx-java-multi-channel-solon-plugin.properties
create mode 100644 solon-plugins/wx-java-channel-multi-solon-plugin/src/test/java/features/test/LoadTest.java
create mode 100644 solon-plugins/wx-java-channel-multi-solon-plugin/src/test/resources/app.properties
create mode 100644 solon-plugins/wx-java-miniapp-multi-solon-plugin/README.md
create mode 100644 solon-plugins/wx-java-miniapp-multi-solon-plugin/pom.xml
create mode 100644 solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/configuration/services/AbstractWxMaConfiguration.java
create mode 100644 solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/configuration/services/WxMaInJedisConfiguration.java
create mode 100644 solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/configuration/services/WxMaInMemoryConfiguration.java
create mode 100644 solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/configuration/services/WxMaInRedissonConfiguration.java
create mode 100644 solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/integration/WxMiniappMultiPluginImpl.java
create mode 100644 solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/properties/WxMaMultiProperties.java
create mode 100644 solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/properties/WxMaMultiRedisProperties.java
create mode 100644 solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/properties/WxMaSingleProperties.java
create mode 100644 solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/service/WxMaMultiServices.java
create mode 100644 solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/service/WxMaMultiServicesImpl.java
create mode 100644 solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/resources/META-INF/solon/wx-java-miniapp-multi-solon-plugin.properties
create mode 100644 solon-plugins/wx-java-miniapp-multi-solon-plugin/src/test/java/features/test/LoadTest.java
create mode 100644 solon-plugins/wx-java-miniapp-multi-solon-plugin/src/test/resources/app.properties
diff --git a/solon-plugins/pom.xml b/solon-plugins/pom.xml
index afa8ccec3a..3c81870e21 100644
--- a/solon-plugins/pom.xml
+++ b/solon-plugins/pom.xml
@@ -14,10 +14,11 @@
WxJava 各个模块的 Solon Plugin
- 2.9.2
+ 3.0.1
+ wx-java-miniapp-multi-solon-plugin
wx-java-miniapp-solon-plugin
wx-java-mp-multi-solon-plugin
wx-java-mp-solon-plugin
@@ -27,6 +28,7 @@
wx-java-cp-multi-solon-plugin
wx-java-cp-solon-plugin
wx-java-channel-solon-plugin
+ wx-java-channel-multi-solon-plugin
diff --git a/solon-plugins/wx-java-channel-multi-solon-plugin/README.md b/solon-plugins/wx-java-channel-multi-solon-plugin/README.md
new file mode 100644
index 0000000000..6285f54953
--- /dev/null
+++ b/solon-plugins/wx-java-channel-multi-solon-plugin/README.md
@@ -0,0 +1,111 @@
+# wx-java-channel-multi-solon-plugin
+
+## 快速开始
+
+1. 引入依赖
+ ```xml
+
+
+ com.github.binarywang
+ wx-java-channel-multi-solon-plugin
+ ${version}
+
+
+
+
+ redis.clients
+ jedis
+ ${jedis.version}
+
+
+
+
+ org.redisson
+ redisson
+ ${redisson.version}
+
+
+ ```
+2. 添加配置(app.properties)
+ ```properties
+ # 视频号配置
+ ## 应用 1 配置(必填)
+ wx.channel.apps.tenantId1.app-id=@appId
+ wx.channel.apps.tenantId1.secret=@secret
+ ## 选填
+ wx.channel.apps.tenantId1.use-stable-access-token=false
+ wx.channel.apps.tenantId1.token=
+ wx.channel.apps.tenantId1.aes-key=
+ ## 应用 2 配置(必填)
+ wx.channel.apps.tenantId2.app-id=@appId
+ wx.channel.apps.tenantId2.secret=@secret
+ ## 选填
+ wx.channel.apps.tenantId2.use-stable-access-token=false
+ wx.channel.apps.tenantId2.token=
+ wx.channel.apps.tenantId2.aes-key=
+
+ # ConfigStorage 配置(选填)
+ ## 配置类型: memory(默认), jedis, redisson, redis_template
+ wx.channel.config-storage.type=memory
+ ## 相关redis前缀配置: wx:channel:multi(默认)
+ wx.channel.config-storage.key-prefix=wx:channel:multi
+ wx.channel.config-storage.redis.host=127.0.0.1
+ wx.channel.config-storage.redis.port=6379
+ wx.channel.config-storage.redis.password=123456
+
+ # http 客户端配置(选填)
+ ## # http客户端类型: http_client(默认)
+ wx.channel.config-storage.http-client-type=http_client
+ wx.channel.config-storage.http-proxy-host=
+ wx.channel.config-storage.http-proxy-port=
+ wx.channel.config-storage.http-proxy-username=
+ wx.channel.config-storage.http-proxy-password=
+ ## 最大重试次数,默认:5 次,如果小于 0,则为 0
+ wx.channel.config-storage.max-retry-times=5
+ ## 重试时间间隔步进,默认:1000 毫秒,如果小于 0,则为 1000
+ wx.channel.config-storage.retry-sleep-millis=1000
+ ```
+3. 自动注入的类型:`WxChannelMultiServices`
+
+4. 使用样例
+
+ ```java
+ import com.binarywang.solon.wxjava.channel.service.WxChannelMultiServices;
+ import me.chanjar.weixin.channel.api.WxChannelService;
+ import me.chanjar.weixin.channel.api.WxFinderLiveService;
+ import me.chanjar.weixin.channel.bean.lead.component.response.FinderAttrResponse;
+ import me.chanjar.weixin.common.error.WxErrorException;
+ import org.noear.solon.annotation.Component;
+ import org.noear.solon.annotation.Inject;
+
+ @Component
+ public class DemoService {
+ @Inject
+ private WxChannelMultiServices wxChannelMultiServices;
+
+ public void test() throws WxErrorException {
+ // 应用 1 的 WxChannelService
+ WxChannelService wxChannelService1 = wxChannelMultiServices.getWxChannelService("tenantId1");
+ WxFinderLiveService finderLiveService = wxChannelService1.getFinderLiveService();
+ FinderAttrResponse response1 = finderLiveService.getFinderAttrByAppid();
+ // todo ...
+
+ // 应用 2 的 WxChannelService
+ WxChannelService wxChannelService2 = wxChannelMultiServices.getWxChannelService("tenantId2");
+ WxFinderLiveService finderLiveService2 = wxChannelService2.getFinderLiveService();
+ FinderAttrResponse response2 = finderLiveService2.getFinderAttrByAppid();
+ // todo ...
+
+ // 应用 3 的 WxChannelService
+ WxChannelService wxChannelService3 = wxChannelMultiServices.getWxChannelService("tenantId3");
+ // 判断是否为空
+ if (wxChannelService3 == null) {
+ // todo wxChannelService3 为空,请先配置 tenantId3 微信视频号应用参数
+ return;
+ }
+ WxFinderLiveService finderLiveService3 = wxChannelService3.getFinderLiveService();
+ FinderAttrResponse response3 = finderLiveService3.getFinderAttrByAppid();
+ // todo ...
+ }
+ }
+ ```
diff --git a/solon-plugins/wx-java-channel-multi-solon-plugin/pom.xml b/solon-plugins/wx-java-channel-multi-solon-plugin/pom.xml
new file mode 100644
index 0000000000..f11e9936bd
--- /dev/null
+++ b/solon-plugins/wx-java-channel-multi-solon-plugin/pom.xml
@@ -0,0 +1,43 @@
+
+
+
+ wx-java-solon-plugins
+ com.github.binarywang
+ 4.6.5.B
+
+ 4.0.0
+
+ wx-java-channel-multi-solon-plugin
+ WxJava - Solon Plugin for Channel::支持多账号配置
+ 微信视频号开发的 Solon Plugin::支持多账号配置
+
+
+
+ com.github.binarywang
+ weixin-java-channel
+ ${project.version}
+
+
+ redis.clients
+ jedis
+ provided
+
+
+ org.redisson
+ redisson
+ provided
+
+
+ org.jodd
+ jodd-http
+ provided
+
+
+ com.squareup.okhttp3
+ okhttp
+ provided
+
+
+
diff --git a/solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/configuration/services/AbstractWxChannelConfiguration.java b/solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/configuration/services/AbstractWxChannelConfiguration.java
new file mode 100644
index 0000000000..5521dff86a
--- /dev/null
+++ b/solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/configuration/services/AbstractWxChannelConfiguration.java
@@ -0,0 +1,140 @@
+package com.binarywang.solon.wxjava.channel.configuration.services;
+
+import com.binarywang.solon.wxjava.channel.enums.HttpClientType;
+import com.binarywang.solon.wxjava.channel.properties.WxChannelMultiProperties;
+import com.binarywang.solon.wxjava.channel.properties.WxChannelSingleProperties;
+import com.binarywang.solon.wxjava.channel.service.WxChannelMultiServices;
+import com.binarywang.solon.wxjava.channel.service.WxChannelMultiServicesImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.channel.api.WxChannelService;
+import me.chanjar.weixin.channel.api.impl.WxChannelServiceHttpClientImpl;
+import me.chanjar.weixin.channel.api.impl.WxChannelServiceImpl;
+import me.chanjar.weixin.channel.config.WxChannelConfig;
+import me.chanjar.weixin.channel.config.impl.WxChannelDefaultConfigImpl;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * WxChannelConfigStorage 抽象配置类
+ *
+ * @author Winnie 2024/9/13
+ * @author noear
+ */
+@RequiredArgsConstructor
+@Slf4j
+public abstract class AbstractWxChannelConfiguration {
+ protected WxChannelMultiServices wxChannelMultiServices(WxChannelMultiProperties wxChannelMultiProperties) {
+ Map appsMap = wxChannelMultiProperties.getApps();
+ if (appsMap == null || appsMap.isEmpty()) {
+ log.warn("微信视频号应用参数未配置,通过 WxChannelMultiServices#getWxChannelService(\"tenantId\")获取实例将返回空");
+ return new WxChannelMultiServicesImpl();
+ }
+ /**
+ * 校验 appId 是否唯一,避免使用 redis 缓存 token、ticket 时错乱。
+ *
+ * 查看 {@link me.chanjar.weixin.channel.config.impl.WxChannelRedisConfigImpl#setAppid(String)}
+ */
+ Collection apps = appsMap.values();
+ if (apps.size() > 1) {
+ // 校验 appId 是否唯一
+ boolean multi = apps.stream()
+ // 没有 appId,如果不判断是否为空,这里会报 NPE 异常
+ .collect(Collectors.groupingBy(c -> c.getAppId() == null ? 0 : c.getAppId(), Collectors.counting()))
+ .entrySet().stream().anyMatch(e -> e.getValue() > 1);
+ if (multi) {
+ throw new RuntimeException("请确保微信视频号配置 appId 的唯一性");
+ }
+ }
+ WxChannelMultiServicesImpl services = new WxChannelMultiServicesImpl();
+
+ Set> entries = appsMap.entrySet();
+ for (Map.Entry entry : entries) {
+ String tenantId = entry.getKey();
+ WxChannelSingleProperties wxChannelSingleProperties = entry.getValue();
+ WxChannelDefaultConfigImpl storage = this.wxChannelConfigStorage(wxChannelMultiProperties);
+ this.configApp(storage, wxChannelSingleProperties);
+ this.configHttp(storage, wxChannelMultiProperties.getConfigStorage());
+ WxChannelService wxChannelService = this.wxChannelService(storage, wxChannelMultiProperties, wxChannelSingleProperties.isUseStableAccessToken());
+ services.addWxChannelService(tenantId, wxChannelService);
+ }
+ return services;
+ }
+
+ /**
+ * 配置 WxChannelDefaultConfigImpl
+ *
+ * @param wxChannelMultiProperties 参数
+ * @return WxChannelDefaultConfigImpl
+ */
+ protected abstract WxChannelDefaultConfigImpl wxChannelConfigStorage(WxChannelMultiProperties wxChannelMultiProperties);
+
+ public WxChannelService wxChannelService(WxChannelConfig wxChannelConfig, WxChannelMultiProperties wxChannelMultiProperties, boolean useStableAccessToken) {
+ WxChannelMultiProperties.ConfigStorage storage = wxChannelMultiProperties.getConfigStorage();
+ HttpClientType httpClientType = storage.getHttpClientType();
+ WxChannelService wxChannelService;
+ switch (httpClientType) {
+// case OK_HTTP:
+// wxChannelService = new WxChannelServiceOkHttpImpl(false, false);
+// break;
+ case HTTP_CLIENT:
+ wxChannelService = new WxChannelServiceHttpClientImpl(useStableAccessToken, false);
+ break;
+ default:
+ wxChannelService = new WxChannelServiceImpl();
+ break;
+ }
+
+ wxChannelService.setConfig(wxChannelConfig);
+ int maxRetryTimes = storage.getMaxRetryTimes();
+ if (maxRetryTimes < 0) {
+ maxRetryTimes = 0;
+ }
+ int retrySleepMillis = storage.getRetrySleepMillis();
+ if (retrySleepMillis < 0) {
+ retrySleepMillis = 1000;
+ }
+ wxChannelService.setRetrySleepMillis(retrySleepMillis);
+ wxChannelService.setMaxRetryTimes(maxRetryTimes);
+ return wxChannelService;
+ }
+
+ private void configApp(WxChannelDefaultConfigImpl config, WxChannelSingleProperties wxChannelSingleProperties) {
+ String appId = wxChannelSingleProperties.getAppId();
+ String appSecret = wxChannelSingleProperties.getSecret();
+ String token = wxChannelSingleProperties.getToken();
+ String aesKey = wxChannelSingleProperties.getAesKey();
+
+ config.setAppid(appId);
+ config.setSecret(appSecret);
+ if (StringUtils.isNotBlank(token)) {
+ config.setToken(token);
+ }
+ if (StringUtils.isNotBlank(aesKey)) {
+ config.setAesKey(aesKey);
+ }
+ }
+
+ private void configHttp(WxChannelDefaultConfigImpl config, WxChannelMultiProperties.ConfigStorage storage) {
+ String httpProxyHost = storage.getHttpProxyHost();
+ Integer httpProxyPort = storage.getHttpProxyPort();
+ String httpProxyUsername = storage.getHttpProxyUsername();
+ String httpProxyPassword = storage.getHttpProxyPassword();
+ if (StringUtils.isNotBlank(httpProxyHost)) {
+ config.setHttpProxyHost(httpProxyHost);
+ if (httpProxyPort != null) {
+ config.setHttpProxyPort(httpProxyPort);
+ }
+ if (StringUtils.isNotBlank(httpProxyUsername)) {
+ config.setHttpProxyUsername(httpProxyUsername);
+ }
+ if (StringUtils.isNotBlank(httpProxyPassword)) {
+ config.setHttpProxyPassword(httpProxyPassword);
+ }
+ }
+ }
+}
diff --git a/solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/configuration/services/WxChannelInJedisConfiguration.java b/solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/configuration/services/WxChannelInJedisConfiguration.java
new file mode 100644
index 0000000000..68afc13320
--- /dev/null
+++ b/solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/configuration/services/WxChannelInJedisConfiguration.java
@@ -0,0 +1,77 @@
+package com.binarywang.solon.wxjava.channel.configuration.services;
+
+import com.binarywang.solon.wxjava.channel.properties.WxChannelMultiProperties;
+import com.binarywang.solon.wxjava.channel.properties.WxChannelMultiRedisProperties;
+import com.binarywang.solon.wxjava.channel.service.WxChannelMultiServices;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.channel.config.impl.WxChannelDefaultConfigImpl;
+import me.chanjar.weixin.channel.config.impl.WxChannelRedisConfigImpl;
+import me.chanjar.weixin.common.redis.JedisWxRedisOps;
+import org.apache.commons.lang3.StringUtils;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+import org.noear.solon.core.AppContext;
+import redis.clients.jedis.JedisPool;
+import redis.clients.jedis.JedisPoolConfig;
+
+/**
+ * 自动装配基于 jedis 策略配置
+ *
+ * @author Winnie 2024/9/13
+ * @author noear
+ */
+@Configuration
+@Condition(
+ onProperty = "${"+WxChannelMultiProperties.PREFIX + ".configStorage.type} = jedis",
+ onClass = JedisPool.class
+)
+@RequiredArgsConstructor
+public class WxChannelInJedisConfiguration extends AbstractWxChannelConfiguration {
+ private final WxChannelMultiProperties wxChannelMultiProperties;
+ private final AppContext applicationContext;
+
+ @Bean
+ public WxChannelMultiServices wxChannelMultiServices() {
+ return this.wxChannelMultiServices(wxChannelMultiProperties);
+ }
+
+ @Override
+ protected WxChannelDefaultConfigImpl wxChannelConfigStorage(WxChannelMultiProperties wxChannelMultiProperties) {
+ return this.configRedis(wxChannelMultiProperties);
+ }
+
+ private WxChannelDefaultConfigImpl configRedis(WxChannelMultiProperties wxChannelMultiProperties) {
+ WxChannelMultiRedisProperties wxChannelMultiRedisProperties = wxChannelMultiProperties.getConfigStorage().getRedis();
+ JedisPool jedisPool;
+ if (wxChannelMultiRedisProperties != null && StringUtils.isNotEmpty(wxChannelMultiRedisProperties.getHost())) {
+ jedisPool = getJedisPool(wxChannelMultiProperties);
+ } else {
+ jedisPool = applicationContext.getBean(JedisPool.class);
+ }
+ return new WxChannelRedisConfigImpl(new JedisWxRedisOps(jedisPool), wxChannelMultiProperties.getConfigStorage().getKeyPrefix());
+ }
+
+ private JedisPool getJedisPool(WxChannelMultiProperties wxChannelMultiProperties) {
+ WxChannelMultiProperties.ConfigStorage storage = wxChannelMultiProperties.getConfigStorage();
+ WxChannelMultiRedisProperties redis = storage.getRedis();
+
+ JedisPoolConfig config = new JedisPoolConfig();
+ if (redis.getMaxActive() != null) {
+ config.setMaxTotal(redis.getMaxActive());
+ }
+ if (redis.getMaxIdle() != null) {
+ config.setMaxIdle(redis.getMaxIdle());
+ }
+ if (redis.getMaxWaitMillis() != null) {
+ config.setMaxWaitMillis(redis.getMaxWaitMillis());
+ }
+ if (redis.getMinIdle() != null) {
+ config.setMinIdle(redis.getMinIdle());
+ }
+ config.setTestOnBorrow(true);
+ config.setTestWhileIdle(true);
+
+ return new JedisPool(config, redis.getHost(), redis.getPort(), redis.getTimeout(), redis.getPassword(), redis.getDatabase());
+ }
+}
diff --git a/solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/configuration/services/WxChannelInMemoryConfiguration.java b/solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/configuration/services/WxChannelInMemoryConfiguration.java
new file mode 100644
index 0000000000..71cd5ca33c
--- /dev/null
+++ b/solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/configuration/services/WxChannelInMemoryConfiguration.java
@@ -0,0 +1,40 @@
+package com.binarywang.solon.wxjava.channel.configuration.services;
+
+import com.binarywang.solon.wxjava.channel.properties.WxChannelMultiProperties;
+import com.binarywang.solon.wxjava.channel.service.WxChannelMultiServices;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.channel.config.impl.WxChannelDefaultConfigImpl;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+import redis.clients.jedis.JedisPool;
+
+/**
+ * 自动装配基于内存策略配置
+ *
+ * @author Winnie 2024/9/13
+ * @author noear
+ */
+@Configuration
+@Condition(
+ onProperty = "${"+WxChannelMultiProperties.PREFIX + ".configStorage.type} = memory",
+ onClass = JedisPool.class
+)
+@RequiredArgsConstructor
+public class WxChannelInMemoryConfiguration extends AbstractWxChannelConfiguration {
+ private final WxChannelMultiProperties wxChannelMultiProperties;
+
+ @Bean
+ public WxChannelMultiServices wxChannelMultiServices() {
+ return this.wxChannelMultiServices(wxChannelMultiProperties);
+ }
+
+ @Override
+ protected WxChannelDefaultConfigImpl wxChannelConfigStorage(WxChannelMultiProperties wxChannelMultiProperties) {
+ return this.configInMemory();
+ }
+
+ private WxChannelDefaultConfigImpl configInMemory() {
+ return new WxChannelDefaultConfigImpl();
+ }
+}
diff --git a/solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/configuration/services/WxChannelInRedissonConfiguration.java b/solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/configuration/services/WxChannelInRedissonConfiguration.java
new file mode 100644
index 0000000000..fce6a735ea
--- /dev/null
+++ b/solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/configuration/services/WxChannelInRedissonConfiguration.java
@@ -0,0 +1,65 @@
+package com.binarywang.solon.wxjava.channel.configuration.services;
+
+import com.binarywang.solon.wxjava.channel.properties.WxChannelMultiProperties;
+import com.binarywang.solon.wxjava.channel.properties.WxChannelMultiRedisProperties;
+import com.binarywang.solon.wxjava.channel.service.WxChannelMultiServices;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.channel.config.impl.WxChannelDefaultConfigImpl;
+import me.chanjar.weixin.channel.config.impl.WxChannelRedissonConfigImpl;
+import org.apache.commons.lang3.StringUtils;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+import org.noear.solon.core.AppContext;
+import org.redisson.Redisson;
+import org.redisson.api.RedissonClient;
+import org.redisson.config.Config;
+import org.redisson.config.TransportMode;
+
+/**
+ * 自动装配基于 redisson 策略配置
+ *
+ * @author Winnie 2024/9/13
+ * @author noear
+ */
+@Configuration
+@Condition(
+ onProperty = "${"+WxChannelMultiProperties.PREFIX + ".configStorage.type} = redisson",
+ onClass = Redisson.class
+)
+@RequiredArgsConstructor
+public class WxChannelInRedissonConfiguration extends AbstractWxChannelConfiguration {
+ private final WxChannelMultiProperties wxChannelMultiProperties;
+ private final AppContext applicationContext;
+
+ @Bean
+ public WxChannelMultiServices wxChannelMultiServices() {
+ return this.wxChannelMultiServices(wxChannelMultiProperties);
+ }
+
+ @Override
+ protected WxChannelDefaultConfigImpl wxChannelConfigStorage(WxChannelMultiProperties wxChannelMultiProperties) {
+ return this.configRedisson(wxChannelMultiProperties);
+ }
+
+ private WxChannelDefaultConfigImpl configRedisson(WxChannelMultiProperties wxChannelMultiProperties) {
+ WxChannelMultiRedisProperties redisProperties = wxChannelMultiProperties.getConfigStorage().getRedis();
+ RedissonClient redissonClient;
+ if (redisProperties != null && StringUtils.isNotEmpty(redisProperties.getHost())) {
+ redissonClient = getRedissonClient(wxChannelMultiProperties);
+ } else {
+ redissonClient = applicationContext.getBean(RedissonClient.class);
+ }
+ return new WxChannelRedissonConfigImpl(redissonClient, wxChannelMultiProperties.getConfigStorage().getKeyPrefix());
+ }
+
+ private RedissonClient getRedissonClient(WxChannelMultiProperties wxChannelMultiProperties) {
+ WxChannelMultiProperties.ConfigStorage storage = wxChannelMultiProperties.getConfigStorage();
+ WxChannelMultiRedisProperties redis = storage.getRedis();
+
+ Config config = new Config();
+ config.useSingleServer().setAddress("redis://" + redis.getHost() + ":" + redis.getPort()).setDatabase(redis.getDatabase()).setPassword(redis.getPassword());
+ config.setTransportMode(TransportMode.NIO);
+ return Redisson.create(config);
+ }
+}
diff --git a/solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/enums/HttpClientType.java b/solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/enums/HttpClientType.java
new file mode 100644
index 0000000000..1899e9e9f6
--- /dev/null
+++ b/solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/enums/HttpClientType.java
@@ -0,0 +1,19 @@
+package com.binarywang.solon.wxjava.channel.enums;
+
+/**
+ * httpclient类型
+ *
+ * @author Winnie
+ * @date 2024/9/13
+ */
+public enum HttpClientType {
+ /**
+ * HttpClient
+ */
+ HTTP_CLIENT,
+ // WxChannelServiceOkHttpImpl 实现经测试无法正常完成业务固暂不支持OK_HTTP方式
+// /**
+// * OkHttp.
+// */
+// OK_HTTP,
+}
diff --git a/solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/enums/StorageType.java b/solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/enums/StorageType.java
new file mode 100644
index 0000000000..a1b710cd2a
--- /dev/null
+++ b/solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/enums/StorageType.java
@@ -0,0 +1,26 @@
+package com.binarywang.solon.wxjava.channel.enums;
+
+/**
+ * storage类型
+ *
+ * @author Winnie
+ * @date 2024/9/13
+ */
+public enum StorageType {
+ /**
+ * 内存
+ */
+ MEMORY,
+ /**
+ * redis(JedisClient)
+ */
+ JEDIS,
+ /**
+ * redis(Redisson)
+ */
+ REDISSON,
+ /**
+ * redis(RedisTemplate)
+ */
+ REDIS_TEMPLATE
+}
diff --git a/solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/integration/WxChannelMultiPluginImpl.java b/solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/integration/WxChannelMultiPluginImpl.java
new file mode 100644
index 0000000000..3b84794eac
--- /dev/null
+++ b/solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/integration/WxChannelMultiPluginImpl.java
@@ -0,0 +1,25 @@
+package com.binarywang.solon.wxjava.channel.integration;
+
+import com.binarywang.solon.wxjava.channel.configuration.services.WxChannelInJedisConfiguration;
+import com.binarywang.solon.wxjava.channel.configuration.services.WxChannelInMemoryConfiguration;
+import com.binarywang.solon.wxjava.channel.configuration.services.WxChannelInRedissonConfiguration;
+import com.binarywang.solon.wxjava.channel.properties.WxChannelMultiProperties;
+import org.noear.solon.core.AppContext;
+import org.noear.solon.core.Plugin;
+
+/**
+ * 微信视频号自动注册
+ *
+ * @author Winnie 2024/9/13
+ * @author noear 2024/10/9 created
+ */
+public class WxChannelMultiPluginImpl implements Plugin {
+ @Override
+ public void start(AppContext context) throws Throwable {
+ context.beanMake(WxChannelMultiProperties.class);
+
+ context.beanMake(WxChannelInJedisConfiguration.class);
+ context.beanMake(WxChannelInMemoryConfiguration.class);
+ context.beanMake(WxChannelInRedissonConfiguration.class);
+ }
+}
diff --git a/solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/properties/WxChannelMultiProperties.java b/solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/properties/WxChannelMultiProperties.java
new file mode 100644
index 0000000000..2e2da1add7
--- /dev/null
+++ b/solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/properties/WxChannelMultiProperties.java
@@ -0,0 +1,96 @@
+package com.binarywang.solon.wxjava.channel.properties;
+
+import com.binarywang.solon.wxjava.channel.enums.HttpClientType;
+import com.binarywang.solon.wxjava.channel.enums.StorageType;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.noear.solon.annotation.Configuration;
+import org.noear.solon.annotation.Inject;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 微信多视频号接入相关配置属性
+ *
+ * @author Winnie
+ * @date 2024/9/13
+ */
+@Data
+@NoArgsConstructor
+@Configuration
+@Inject("${" + WxChannelMultiProperties.PREFIX +"}")
+public class WxChannelMultiProperties implements Serializable {
+ private static final long serialVersionUID = - 8361973118805546037L;
+ public static final String PREFIX = "wx.channel";
+
+ private Map apps = new HashMap<>();
+
+ /**
+ * 存储策略
+ */
+ private final ConfigStorage configStorage = new ConfigStorage();
+
+ @Data
+ @NoArgsConstructor
+ public static class ConfigStorage implements Serializable {
+ private static final long serialVersionUID = - 5152619132544179942L;
+
+ /**
+ * 存储类型.
+ */
+ private StorageType type = StorageType.MEMORY;
+
+ /**
+ * 指定key前缀.
+ */
+ private String keyPrefix = "wx:channel:multi";
+
+ /**
+ * redis连接配置.
+ */
+ private final WxChannelMultiRedisProperties redis = new WxChannelMultiRedisProperties();
+
+ /**
+ * http客户端类型.
+ */
+ private HttpClientType httpClientType = HttpClientType.HTTP_CLIENT;
+
+ /**
+ * http代理主机.
+ */
+ private String httpProxyHost;
+
+ /**
+ * http代理端口.
+ */
+ private Integer httpProxyPort;
+
+ /**
+ * http代理用户名.
+ */
+ private String httpProxyUsername;
+
+ /**
+ * http代理密码.
+ */
+ private String httpProxyPassword;
+
+ /**
+ * http 请求最大重试次数
+ *
+ * {@link me.chanjar.weixin.channel.api.WxChannelService#setMaxRetryTimes(int)}
+ * {@link me.chanjar.weixin.channel.api.impl.BaseWxChannelServiceImpl#setMaxRetryTimes(int)}
+ */
+ private int maxRetryTimes = 5;
+
+ /**
+ * http 请求重试间隔
+ *
+ * {@link me.chanjar.weixin.channel.api.WxChannelService#setRetrySleepMillis(int)}
+ * {@link me.chanjar.weixin.channel.api.impl.BaseWxChannelServiceImpl#setRetrySleepMillis(int)}
+ */
+ private int retrySleepMillis = 1000;
+ }
+}
diff --git a/solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/properties/WxChannelMultiRedisProperties.java b/solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/properties/WxChannelMultiRedisProperties.java
new file mode 100644
index 0000000000..36c649b311
--- /dev/null
+++ b/solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/properties/WxChannelMultiRedisProperties.java
@@ -0,0 +1,63 @@
+package com.binarywang.solon.wxjava.channel.properties;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * Redis配置
+ *
+ * @author Winnie
+ * @date 2024/9/13
+ */
+@Data
+@NoArgsConstructor
+public class WxChannelMultiRedisProperties implements Serializable {
+ private static final long serialVersionUID = 9061055444734277357L;
+
+ /**
+ * 主机地址.
+ */
+ private String host = "127.0.0.1";
+
+ /**
+ * 端口号.
+ */
+ private int port = 6379;
+
+ /**
+ * 密码.
+ */
+ private String password;
+
+ /**
+ * 超时.
+ */
+ private int timeout = 2000;
+
+ /**
+ * 数据库.
+ */
+ private int database = 0;
+
+ /**
+ * 最大活动连接数
+ */
+ private Integer maxActive;
+
+ /**
+ * 最大空闲连接数
+ */
+ private Integer maxIdle;
+
+ /**
+ * 最小空闲连接数
+ */
+ private Integer minIdle;
+
+ /**
+ * 最大等待时间
+ */
+ private Integer maxWaitMillis;
+}
diff --git a/solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/properties/WxChannelSingleProperties.java b/solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/properties/WxChannelSingleProperties.java
new file mode 100644
index 0000000000..438c3ecb03
--- /dev/null
+++ b/solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/properties/WxChannelSingleProperties.java
@@ -0,0 +1,43 @@
+package com.binarywang.solon.wxjava.channel.properties;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 微信视频号相关配置属性
+ *
+ * @author Winnie
+ * @date 2024/9/13
+ */
+@Data
+@NoArgsConstructor
+public class WxChannelSingleProperties implements Serializable {
+ private static final long serialVersionUID = 5306630351265124825L;
+
+ /**
+ * 设置微信视频号的 appid.
+ */
+ private String appId;
+
+ /**
+ * 设置微信视频号的 secret.
+ */
+ private String secret;
+
+ /**
+ * 设置微信视频号的 token.
+ */
+ private String token;
+
+ /**
+ * 设置微信视频号的 EncodingAESKey.
+ */
+ private String aesKey;
+
+ /**
+ * 是否使用稳定版 Access Token
+ */
+ private boolean useStableAccessToken = false;
+}
diff --git a/solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/service/WxChannelMultiServices.java b/solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/service/WxChannelMultiServices.java
new file mode 100644
index 0000000000..f12461e197
--- /dev/null
+++ b/solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/service/WxChannelMultiServices.java
@@ -0,0 +1,26 @@
+package com.binarywang.solon.wxjava.channel.service;
+
+import me.chanjar.weixin.channel.api.WxChannelService;
+
+/**
+ * 视频号 {@link WxChannelService} 所有实例存放类.
+ *
+ * @author Winnie
+ * @date 2024/9/13
+ */
+public interface WxChannelMultiServices {
+ /**
+ * 通过租户 Id 获取 WxChannelService
+ *
+ * @param tenantId 租户 Id
+ * @return WxChannelService
+ */
+ WxChannelService getWxChannelService(String tenantId);
+
+ /**
+ * 根据租户 Id,从列表中移除一个 WxChannelService 实例
+ *
+ * @param tenantId 租户 Id
+ */
+ void removeWxChannelService(String tenantId);
+}
diff --git a/solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/service/WxChannelMultiServicesImpl.java b/solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/service/WxChannelMultiServicesImpl.java
new file mode 100644
index 0000000000..8420e29d73
--- /dev/null
+++ b/solon-plugins/wx-java-channel-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/channel/service/WxChannelMultiServicesImpl.java
@@ -0,0 +1,36 @@
+package com.binarywang.solon.wxjava.channel.service;
+
+import me.chanjar.weixin.channel.api.WxChannelService;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 视频号 {@link WxChannelMultiServices} 实现
+ *
+ * @author Winnie
+ * @date 2024/9/13
+ */
+public class WxChannelMultiServicesImpl implements WxChannelMultiServices {
+ private final Map services = new ConcurrentHashMap<>();
+
+ @Override
+ public WxChannelService getWxChannelService(String tenantId) {
+ return this.services.get(tenantId);
+ }
+
+ /**
+ * 根据租户 Id,添加一个 WxChannelService 到列表
+ *
+ * @param tenantId 租户 Id
+ * @param wxChannelService WxChannelService 实例
+ */
+ public void addWxChannelService(String tenantId, WxChannelService wxChannelService) {
+ this.services.put(tenantId, wxChannelService);
+ }
+
+ @Override
+ public void removeWxChannelService(String tenantId) {
+ this.services.remove(tenantId);
+ }
+}
diff --git a/solon-plugins/wx-java-channel-multi-solon-plugin/src/main/resources/META-INF/solon/wx-java-multi-channel-solon-plugin.properties b/solon-plugins/wx-java-channel-multi-solon-plugin/src/main/resources/META-INF/solon/wx-java-multi-channel-solon-plugin.properties
new file mode 100644
index 0000000000..b9fc24b210
--- /dev/null
+++ b/solon-plugins/wx-java-channel-multi-solon-plugin/src/main/resources/META-INF/solon/wx-java-multi-channel-solon-plugin.properties
@@ -0,0 +1,2 @@
+solon.plugin=com.binarywang.solon.wxjava.channel.integration.WxChannelMultiPluginImpl
+solon.plugin.priority=10
diff --git a/solon-plugins/wx-java-channel-multi-solon-plugin/src/test/java/features/test/LoadTest.java b/solon-plugins/wx-java-channel-multi-solon-plugin/src/test/java/features/test/LoadTest.java
new file mode 100644
index 0000000000..d049f5a51a
--- /dev/null
+++ b/solon-plugins/wx-java-channel-multi-solon-plugin/src/test/java/features/test/LoadTest.java
@@ -0,0 +1,15 @@
+package features.test;
+
+import org.junit.jupiter.api.Test;
+import org.noear.solon.test.SolonTest;
+
+/**
+ * @author noear 2024/9/4 created
+ */
+@SolonTest
+public class LoadTest {
+ @Test
+ public void load(){
+
+ }
+}
diff --git a/solon-plugins/wx-java-channel-multi-solon-plugin/src/test/resources/app.properties b/solon-plugins/wx-java-channel-multi-solon-plugin/src/test/resources/app.properties
new file mode 100644
index 0000000000..c90a560a82
--- /dev/null
+++ b/solon-plugins/wx-java-channel-multi-solon-plugin/src/test/resources/app.properties
@@ -0,0 +1,36 @@
+# 视频号配置
+## 应用 1 配置(必填)
+wx.channel.apps.tenantId1.app-id=appId
+wx.channel.apps.tenantId1.secret=secret
+## 选填
+wx.channel.apps.tenantId1.use-stable-access-token=false
+wx.channel.apps.tenantId1.token=
+wx.channel.apps.tenantId1.aes-key=
+## 应用 2 配置(必填)
+wx.channel.apps.tenantId2.app-id=@appId
+wx.channel.apps.tenantId2.secret=@secret
+## 选填
+wx.channel.apps.tenantId2.use-stable-access-token=false
+wx.channel.apps.tenantId2.token=
+wx.channel.apps.tenantId2.aes-key=
+
+# ConfigStorage 配置(选填)
+## 配置类型: memory(默认), jedis, redisson, redis_template
+wx.channel.config-storage.type=memory
+## 相关redis前缀配置: wx:channel:multi(默认)
+wx.channel.config-storage.key-prefix=wx:channel:multi
+wx.channel.config-storage.redis.host=127.0.0.1
+wx.channel.config-storage.redis.port=6379
+wx.channel.config-storage.redis.password=123456
+
+# http 客户端配置(选填)
+## # http客户端类型: http_client(默认)
+wx.channel.config-storage.http-client-type=http_client
+wx.channel.config-storage.http-proxy-host=
+wx.channel.config-storage.http-proxy-port=
+wx.channel.config-storage.http-proxy-username=
+wx.channel.config-storage.http-proxy-password=
+## 最大重试次数,默认:5 次,如果小于 0,则为 0
+wx.channel.config-storage.max-retry-times=5
+## 重试时间间隔步进,默认:1000 毫秒,如果小于 0,则为 1000
+wx.channel.config-storage.retry-sleep-millis=1000
diff --git a/solon-plugins/wx-java-cp-multi-solon-plugin/README.md b/solon-plugins/wx-java-cp-multi-solon-plugin/README.md
index c6acb0889b..97bcf0723f 100644
--- a/solon-plugins/wx-java-cp-multi-solon-plugin/README.md
+++ b/solon-plugins/wx-java-cp-multi-solon-plugin/README.md
@@ -61,6 +61,8 @@
import com.binarywang.solon.wxjava.cp_multi.service.WxCpMultiServices;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.api.WxCpUserService;
+import org.noear.solon.annotation.Component;
+import org.noear.solon.annotation.Inject;
@Component
public class DemoService {
diff --git a/solon-plugins/wx-java-miniapp-multi-solon-plugin/README.md b/solon-plugins/wx-java-miniapp-multi-solon-plugin/README.md
new file mode 100644
index 0000000000..4555a4fc5e
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-multi-solon-plugin/README.md
@@ -0,0 +1,95 @@
+# wx-java-miniapp-multi-solon-plugin
+
+## 快速开始
+
+1. 引入依赖
+ ```xml
+
+ com.github.binarywang
+ wx-java-miniapp-multi-solon-plugin
+ ${version}
+
+ ```
+2. 添加配置(app.properties)
+ ```properties
+ # 公众号配置
+ ## 应用 1 配置(必填)
+ wx.ma.apps.tenantId1.app-id=appId
+ wx.ma.apps.tenantId1.app-secret=@secret
+ ## 选填
+ wx.ma.apps.tenantId1.token=@token
+ wx.ma.apps.tenantId1.aes-key=@aesKey
+ wx.ma.apps.tenantId1.use-stable-access-token=@useStableAccessToken
+ ## 应用 2 配置(必填)
+ wx.ma.apps.tenantId2.app-id=@appId
+ wx.ma.apps.tenantId2.app-secret =@secret
+ ## 选填
+ wx.ma.apps.tenantId2.token=@token
+ wx.ma.apps.tenantId2.aes-key=@aesKey
+ wx.ma.apps.tenantId2.use-stable-access-token=@useStableAccessToken
+
+ # ConfigStorage 配置(选填)
+ ## 配置类型: memory(默认), jedis, redisson
+ wx.ma.config-storage.type=memory
+ ## 相关redis前缀配置: wx:ma:multi(默认)
+ wx.ma.config-storage.key-prefix=wx:ma:multi
+ wx.ma.config-storage.redis.host=127.0.0.1
+ wx.ma.config-storage.redis.port=6379
+ ## 单机和 sentinel 同时存在时,优先使用sentinel配置
+ # wx.ma.config-storage.redis.sentinel-ips=127.0.0.1:16379,127.0.0.1:26379
+ # wx.ma.config-storage.redis.sentinel-name=mymaster
+
+ # http 客户端配置(选填)
+ ## # http客户端类型: http_client(默认), ok_http, jodd_http
+ wx.ma.config-storage.http-client-type=http_client
+ wx.ma.config-storage.http-proxy-host=
+ wx.ma.config-storage.http-proxy-port=
+ wx.ma.config-storage.http-proxy-username=
+ wx.ma.config-storage.http-proxy-password=
+ ## 最大重试次数,默认:5 次,如果小于 0,则为 0
+ wx.ma.config-storage.max-retry-times=5
+ ## 重试时间间隔步进,默认:1000 毫秒,如果小于 0,则为 1000
+ wx.ma.config-storage.retry-sleep-millis=1000
+ ```
+3. 自动注入的类型:`WxMaMultiServices`
+
+4. 使用样例
+
+```java
+import com.binarywang.solon.wxjava.miniapp.service.WxMaMultiServices;
+import cn.binarywang.wx.miniapp.api.WxMaService;
+import cn.binarywang.wx.miniapp.api.WxMaUserService;
+import org.noear.solon.annotation.Component;
+import org.noear.solon.annotation.Inject;
+
+@Component
+public class DemoService {
+ @Inject
+ private WxMaMultiServices wxMaMultiServices;
+
+ public void test() {
+ // 应用 1 的 WxMaService
+ WxMaService wxMaService1 = wxMaMultiServices.getWxMaService("tenantId1");
+ WxMaUserService userService1 = wxMaService1.getUserService();
+ userService1.userInfo("xxx");
+ // todo ...
+
+ // 应用 2 的 WxMaService
+ WxMaService wxMaService2 = wxMaMultiServices.getWxMaService("tenantId2");
+ WxMaUserService userService2 = wxMaService2.getUserService();
+ userService2.userInfo("xxx");
+ // todo ...
+
+ // 应用 3 的 WxMaService
+ WxMaService wxMaService3 = wxMaMultiServices.getWxMaService("tenantId3");
+ // 判断是否为空
+ if (wxMaService3 == null) {
+ // todo wxMaService3 为空,请先配置 tenantId3 微信公众号应用参数
+ return;
+ }
+ WxMaUserService userService3 = wxMaService3.getUserService();
+ userService3.userInfo("xxx");
+ // todo ...
+ }
+}
+```
diff --git a/solon-plugins/wx-java-miniapp-multi-solon-plugin/pom.xml b/solon-plugins/wx-java-miniapp-multi-solon-plugin/pom.xml
new file mode 100644
index 0000000000..249d1511a4
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-multi-solon-plugin/pom.xml
@@ -0,0 +1,43 @@
+
+
+
+ wx-java-solon-plugins
+ com.github.binarywang
+ 4.6.5.B
+
+ 4.0.0
+
+ wx-java-miniapp-multi-solon-plugin
+ WxJava - Solon Plugin for MiniApp::支持多账号配置
+ 微信公众号开发的 Solon Plugin::支持多账号配置
+
+
+
+ com.github.binarywang
+ weixin-java-miniapp
+ ${project.version}
+
+
+ redis.clients
+ jedis
+ provided
+
+
+ org.redisson
+ redisson
+ provided
+
+
+ org.jodd
+ jodd-http
+ provided
+
+
+ com.squareup.okhttp3
+ okhttp
+ provided
+
+
+
diff --git a/solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/configuration/services/AbstractWxMaConfiguration.java b/solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/configuration/services/AbstractWxMaConfiguration.java
new file mode 100644
index 0000000000..fd94200e58
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/configuration/services/AbstractWxMaConfiguration.java
@@ -0,0 +1,147 @@
+package com.binarywang.solon.wxjava.miniapp.configuration.services;
+
+import cn.binarywang.wx.miniapp.api.WxMaService;
+import cn.binarywang.wx.miniapp.api.impl.WxMaServiceHttpClientImpl;
+import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
+import cn.binarywang.wx.miniapp.api.impl.WxMaServiceJoddHttpImpl;
+import cn.binarywang.wx.miniapp.api.impl.WxMaServiceOkHttpImpl;
+import cn.binarywang.wx.miniapp.config.WxMaConfig;
+import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
+import com.binarywang.solon.wxjava.miniapp.properties.WxMaMultiProperties;
+import com.binarywang.solon.wxjava.miniapp.properties.WxMaSingleProperties;
+import com.binarywang.solon.wxjava.miniapp.service.WxMaMultiServices;
+import com.binarywang.solon.wxjava.miniapp.service.WxMaMultiServicesImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * WxMaConfigStorage 抽象配置类
+ *
+ * @author monch
+ * created on 2024/9/6
+ */
+@RequiredArgsConstructor
+@Slf4j
+public abstract class AbstractWxMaConfiguration {
+
+ protected WxMaMultiServices wxMaMultiServices(WxMaMultiProperties wxMaMultiProperties) {
+ Map appsMap = wxMaMultiProperties.getApps();
+ if (appsMap == null || appsMap.isEmpty()) {
+ log.warn("微信公众号应用参数未配置,通过 WxMaMultiServices#getWxMaService(\"tenantId\")获取实例将返回空");
+ return new WxMaMultiServicesImpl();
+ }
+ /**
+ * 校验 appId 是否唯一,避免使用 redis 缓存 token、ticket 时错乱。
+ *
+ * 查看 {@link cn.binarywang.wx.miniapp.config.impl.WxMaRedisConfigImpl#setAppId(String)}
+ */
+ Collection apps = appsMap.values();
+ if (apps.size() > 1) {
+ // 校验 appId 是否唯一
+ boolean multi = apps.stream()
+ // 没有 appId,如果不判断是否为空,这里会报 NPE 异常
+ .collect(Collectors.groupingBy(c -> c.getAppId() == null ? 0 : c.getAppId(), Collectors.counting()))
+ .entrySet().stream().anyMatch(e -> e.getValue() > 1);
+ if (multi) {
+ throw new RuntimeException("请确保微信公众号配置 appId 的唯一性");
+ }
+ }
+ WxMaMultiServicesImpl services = new WxMaMultiServicesImpl();
+
+ Set> entries = appsMap.entrySet();
+ for (Map.Entry entry : entries) {
+ String tenantId = entry.getKey();
+ WxMaSingleProperties wxMaSingleProperties = entry.getValue();
+ WxMaDefaultConfigImpl storage = this.wxMaConfigStorage(wxMaMultiProperties);
+ this.configApp(storage, wxMaSingleProperties);
+ this.configHttp(storage, wxMaMultiProperties.getConfigStorage());
+ WxMaService wxMaService = this.wxMaService(storage, wxMaMultiProperties);
+ services.addWxMaService(tenantId, wxMaService);
+ }
+ return services;
+ }
+
+ /**
+ * 配置 WxMaDefaultConfigImpl
+ *
+ * @param wxMaMultiProperties 参数
+ * @return WxMaDefaultConfigImpl
+ */
+ protected abstract WxMaDefaultConfigImpl wxMaConfigStorage(WxMaMultiProperties wxMaMultiProperties);
+
+ public WxMaService wxMaService(WxMaConfig wxMaConfig, WxMaMultiProperties wxMaMultiProperties) {
+ WxMaMultiProperties.ConfigStorage storage = wxMaMultiProperties.getConfigStorage();
+ WxMaMultiProperties.HttpClientType httpClientType = storage.getHttpClientType();
+ WxMaService wxMaService;
+ switch (httpClientType) {
+ case OK_HTTP:
+ wxMaService = new WxMaServiceOkHttpImpl();
+ break;
+ case JODD_HTTP:
+ wxMaService = new WxMaServiceJoddHttpImpl();
+ break;
+ case HTTP_CLIENT:
+ wxMaService = new WxMaServiceHttpClientImpl();
+ break;
+ default:
+ wxMaService = new WxMaServiceImpl();
+ break;
+ }
+
+ wxMaService.setWxMaConfig(wxMaConfig);
+ int maxRetryTimes = storage.getMaxRetryTimes();
+ if (maxRetryTimes < 0) {
+ maxRetryTimes = 0;
+ }
+ int retrySleepMillis = storage.getRetrySleepMillis();
+ if (retrySleepMillis < 0) {
+ retrySleepMillis = 1000;
+ }
+ wxMaService.setRetrySleepMillis(retrySleepMillis);
+ wxMaService.setMaxRetryTimes(maxRetryTimes);
+ return wxMaService;
+ }
+
+ private void configApp(WxMaDefaultConfigImpl config, WxMaSingleProperties corpProperties) {
+ String appId = corpProperties.getAppId();
+ String appSecret = corpProperties.getAppSecret();
+ String token = corpProperties.getToken();
+ String aesKey = corpProperties.getAesKey();
+ boolean useStableAccessToken = corpProperties.isUseStableAccessToken();
+
+ config.setAppid(appId);
+ config.setSecret(appSecret);
+ if (StringUtils.isNotBlank(token)) {
+ config.setToken(token);
+ }
+ if (StringUtils.isNotBlank(aesKey)) {
+ config.setAesKey(aesKey);
+ }
+ config.useStableAccessToken(useStableAccessToken);
+ }
+
+ private void configHttp(WxMaDefaultConfigImpl config, WxMaMultiProperties.ConfigStorage storage) {
+ String httpProxyHost = storage.getHttpProxyHost();
+ Integer httpProxyPort = storage.getHttpProxyPort();
+ String httpProxyUsername = storage.getHttpProxyUsername();
+ String httpProxyPassword = storage.getHttpProxyPassword();
+ if (StringUtils.isNotBlank(httpProxyHost)) {
+ config.setHttpProxyHost(httpProxyHost);
+ if (httpProxyPort != null) {
+ config.setHttpProxyPort(httpProxyPort);
+ }
+ if (StringUtils.isNotBlank(httpProxyUsername)) {
+ config.setHttpProxyUsername(httpProxyUsername);
+ }
+ if (StringUtils.isNotBlank(httpProxyPassword)) {
+ config.setHttpProxyPassword(httpProxyPassword);
+ }
+ }
+ }
+}
diff --git a/solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/configuration/services/WxMaInJedisConfiguration.java b/solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/configuration/services/WxMaInJedisConfiguration.java
new file mode 100644
index 0000000000..24950fae10
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/configuration/services/WxMaInJedisConfiguration.java
@@ -0,0 +1,77 @@
+package com.binarywang.solon.wxjava.miniapp.configuration.services;
+
+import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
+import cn.binarywang.wx.miniapp.config.impl.WxMaRedisConfigImpl;
+import com.binarywang.solon.wxjava.miniapp.properties.WxMaMultiProperties;
+import com.binarywang.solon.wxjava.miniapp.properties.WxMaMultiRedisProperties;
+import com.binarywang.solon.wxjava.miniapp.service.WxMaMultiServices;
+import lombok.RequiredArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+import org.noear.solon.core.AppContext;
+import redis.clients.jedis.JedisPool;
+import redis.clients.jedis.JedisPoolConfig;
+
+/**
+ * 自动装配基于 jedis 策略配置
+ *
+ * @author monch
+ * created on 2024/9/6
+ */
+@Configuration
+@Condition(
+ onProperty = "${"+WxMaMultiProperties.PREFIX + ".configStorage.type} = jedis",
+ onClass = JedisPool.class
+)
+@RequiredArgsConstructor
+public class WxMaInJedisConfiguration extends AbstractWxMaConfiguration {
+ private final WxMaMultiProperties wxMaMultiProperties;
+ private final AppContext applicationContext;
+
+ @Bean
+ public WxMaMultiServices wxMaMultiServices() {
+ return this.wxMaMultiServices(wxMaMultiProperties);
+ }
+
+ @Override
+ protected WxMaDefaultConfigImpl wxMaConfigStorage(WxMaMultiProperties wxMaMultiProperties) {
+ return this.configRedis(wxMaMultiProperties);
+ }
+
+ private WxMaDefaultConfigImpl configRedis(WxMaMultiProperties wxMaMultiProperties) {
+ WxMaMultiRedisProperties wxMaMultiRedisProperties = wxMaMultiProperties.getConfigStorage().getRedis();
+ JedisPool jedisPool;
+ if (wxMaMultiRedisProperties != null && StringUtils.isNotEmpty(wxMaMultiRedisProperties.getHost())) {
+ jedisPool = getJedisPool(wxMaMultiProperties);
+ } else {
+ jedisPool = applicationContext.getBean(JedisPool.class);
+ }
+ return new WxMaRedisConfigImpl(jedisPool);
+ }
+
+ private JedisPool getJedisPool(WxMaMultiProperties wxMaMultiProperties) {
+ WxMaMultiProperties.ConfigStorage storage = wxMaMultiProperties.getConfigStorage();
+ WxMaMultiRedisProperties redis = storage.getRedis();
+
+ JedisPoolConfig config = new JedisPoolConfig();
+ if (redis.getMaxActive() != null) {
+ config.setMaxTotal(redis.getMaxActive());
+ }
+ if (redis.getMaxIdle() != null) {
+ config.setMaxIdle(redis.getMaxIdle());
+ }
+ if (redis.getMaxWaitMillis() != null) {
+ config.setMaxWaitMillis(redis.getMaxWaitMillis());
+ }
+ if (redis.getMinIdle() != null) {
+ config.setMinIdle(redis.getMinIdle());
+ }
+ config.setTestOnBorrow(true);
+ config.setTestWhileIdle(true);
+
+ return new JedisPool(config, redis.getHost(), redis.getPort(),
+ redis.getTimeout(), redis.getPassword(), redis.getDatabase());
+ }
+}
diff --git a/solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/configuration/services/WxMaInMemoryConfiguration.java b/solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/configuration/services/WxMaInMemoryConfiguration.java
new file mode 100644
index 0000000000..0b9ef1c4a8
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/configuration/services/WxMaInMemoryConfiguration.java
@@ -0,0 +1,39 @@
+package com.binarywang.solon.wxjava.miniapp.configuration.services;
+
+import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
+import com.binarywang.solon.wxjava.miniapp.properties.WxMaMultiProperties;
+import com.binarywang.solon.wxjava.miniapp.service.WxMaMultiServices;
+import lombok.RequiredArgsConstructor;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+
+/**
+ * 自动装配基于内存策略配置
+ *
+ * @author monch
+ * created on 2024/9/6
+ */
+@Configuration
+@Condition(
+ onProperty = "${"+WxMaMultiProperties.PREFIX + ".configStorage.type:memory} = memory"
+)
+@RequiredArgsConstructor
+public class WxMaInMemoryConfiguration extends AbstractWxMaConfiguration {
+ private final WxMaMultiProperties wxMaMultiProperties;
+
+ @Bean
+ public WxMaMultiServices wxMaMultiServices() {
+ return this.wxMaMultiServices(wxMaMultiProperties);
+ }
+
+ @Override
+ protected WxMaDefaultConfigImpl wxMaConfigStorage(WxMaMultiProperties wxMaMultiProperties) {
+ return this.configInMemory();
+ }
+
+ private WxMaDefaultConfigImpl configInMemory() {
+ return new WxMaDefaultConfigImpl();
+ // return new WxMaDefaultConfigImpl();
+ }
+}
diff --git a/solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/configuration/services/WxMaInRedissonConfiguration.java b/solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/configuration/services/WxMaInRedissonConfiguration.java
new file mode 100644
index 0000000000..4e97071f01
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/configuration/services/WxMaInRedissonConfiguration.java
@@ -0,0 +1,68 @@
+package com.binarywang.solon.wxjava.miniapp.configuration.services;
+
+import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
+import cn.binarywang.wx.miniapp.config.impl.WxMaRedissonConfigImpl;
+import com.binarywang.solon.wxjava.miniapp.properties.WxMaMultiProperties;
+import com.binarywang.solon.wxjava.miniapp.properties.WxMaMultiRedisProperties;
+import com.binarywang.solon.wxjava.miniapp.service.WxMaMultiServices;
+import lombok.RequiredArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+import org.noear.solon.annotation.Bean;
+import org.noear.solon.annotation.Condition;
+import org.noear.solon.annotation.Configuration;
+import org.noear.solon.core.AppContext;
+import org.redisson.Redisson;
+import org.redisson.api.RedissonClient;
+import org.redisson.config.Config;
+import org.redisson.config.TransportMode;
+
+/**
+ * 自动装配基于 redisson 策略配置
+ *
+ * @author monch
+ * created on 2024/9/6
+ */
+@Configuration
+@Condition(
+ onProperty = "${"+WxMaMultiProperties.PREFIX + ".configStorage.type} = redisson",
+ onClass = Redisson.class
+)
+@RequiredArgsConstructor
+public class WxMaInRedissonConfiguration extends AbstractWxMaConfiguration {
+ private final WxMaMultiProperties wxMaMultiProperties;
+ private final AppContext applicationContext;
+
+ @Bean
+ public WxMaMultiServices wxMaMultiServices() {
+ return this.wxMaMultiServices(wxMaMultiProperties);
+ }
+
+ @Override
+ protected WxMaDefaultConfigImpl wxMaConfigStorage(WxMaMultiProperties wxMaMultiProperties) {
+ return this.configRedisson(wxMaMultiProperties);
+ }
+
+ private WxMaDefaultConfigImpl configRedisson(WxMaMultiProperties wxMaMultiProperties) {
+ WxMaMultiRedisProperties redisProperties = wxMaMultiProperties.getConfigStorage().getRedis();
+ RedissonClient redissonClient;
+ if (redisProperties != null && StringUtils.isNotEmpty(redisProperties.getHost())) {
+ redissonClient = getRedissonClient(wxMaMultiProperties);
+ } else {
+ redissonClient = applicationContext.getBean(RedissonClient.class);
+ }
+ return new WxMaRedissonConfigImpl(redissonClient, wxMaMultiProperties.getConfigStorage().getKeyPrefix());
+ }
+
+ private RedissonClient getRedissonClient(WxMaMultiProperties wxMaMultiProperties) {
+ WxMaMultiProperties.ConfigStorage storage = wxMaMultiProperties.getConfigStorage();
+ WxMaMultiRedisProperties redis = storage.getRedis();
+
+ Config config = new Config();
+ config.useSingleServer()
+ .setAddress("redis://" + redis.getHost() + ":" + redis.getPort())
+ .setDatabase(redis.getDatabase())
+ .setPassword(redis.getPassword());
+ config.setTransportMode(TransportMode.NIO);
+ return Redisson.create(config);
+ }
+}
diff --git a/solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/integration/WxMiniappMultiPluginImpl.java b/solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/integration/WxMiniappMultiPluginImpl.java
new file mode 100644
index 0000000000..c1153be1bb
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/integration/WxMiniappMultiPluginImpl.java
@@ -0,0 +1,22 @@
+package com.binarywang.solon.wxjava.miniapp.integration;
+
+import com.binarywang.solon.wxjava.miniapp.configuration.services.WxMaInJedisConfiguration;
+import com.binarywang.solon.wxjava.miniapp.configuration.services.WxMaInMemoryConfiguration;
+import com.binarywang.solon.wxjava.miniapp.configuration.services.WxMaInRedissonConfiguration;
+import com.binarywang.solon.wxjava.miniapp.properties.WxMaMultiProperties;
+import org.noear.solon.core.AppContext;
+import org.noear.solon.core.Plugin;
+
+/**
+ * @author noear 2024/10/9 created
+ */
+public class WxMiniappMultiPluginImpl implements Plugin {
+ @Override
+ public void start(AppContext context) throws Throwable {
+ context.beanMake(WxMaMultiProperties.class);
+
+ context.beanMake(WxMaInJedisConfiguration.class);
+ context.beanMake(WxMaInMemoryConfiguration.class);
+ context.beanMake(WxMaInRedissonConfiguration.class);
+ }
+}
diff --git a/solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/properties/WxMaMultiProperties.java b/solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/properties/WxMaMultiProperties.java
new file mode 100644
index 0000000000..87fcd42f03
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/properties/WxMaMultiProperties.java
@@ -0,0 +1,154 @@
+package com.binarywang.solon.wxjava.miniapp.properties;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.noear.solon.annotation.Configuration;
+import org.noear.solon.annotation.Inject;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author monch created on 2024/9/6
+ * @author noear
+ */
+@Data
+@NoArgsConstructor
+@Configuration
+@Inject("${" + WxMaMultiProperties.PREFIX + "}")
+public class WxMaMultiProperties implements Serializable {
+ private static final long serialVersionUID = -5358245184407791011L;
+ public static final String PREFIX = "wx.ma";
+
+ private Map apps = new HashMap<>();
+
+ /**
+ * 自定义host配置
+ */
+ private HostConfig hosts;
+
+ /**
+ * 存储策略
+ */
+ private final ConfigStorage configStorage = new ConfigStorage();
+
+ @Data
+ @NoArgsConstructor
+ public static class HostConfig implements Serializable {
+ private static final long serialVersionUID = -4172767630740346001L;
+
+ /**
+ * 对应于:https://api.weixin.qq.com
+ */
+ private String apiHost;
+
+ /**
+ * 对应于:https://open.weixin.qq.com
+ */
+ private String openHost;
+
+ /**
+ * 对应于:https://mp.weixin.qq.com
+ */
+ private String mpHost;
+ }
+
+ @Data
+ @NoArgsConstructor
+ public static class ConfigStorage implements Serializable {
+ private static final long serialVersionUID = 4815731027000065434L;
+
+ /**
+ * 存储类型.
+ */
+ private StorageType type = StorageType.MEMORY;
+
+ /**
+ * 指定key前缀.
+ */
+ private String keyPrefix = "wx:ma:multi";
+
+ /**
+ * redis连接配置.
+ */
+ private final WxMaMultiRedisProperties redis = new WxMaMultiRedisProperties();
+
+ /**
+ * http客户端类型.
+ */
+ private HttpClientType httpClientType = HttpClientType.HTTP_CLIENT;
+
+ /**
+ * http代理主机.
+ */
+ private String httpProxyHost;
+
+ /**
+ * http代理端口.
+ */
+ private Integer httpProxyPort;
+
+ /**
+ * http代理用户名.
+ */
+ private String httpProxyUsername;
+
+ /**
+ * http代理密码.
+ */
+ private String httpProxyPassword;
+
+ /**
+ * http 请求最大重试次数
+ *
+ * {@link cn.binarywang.wx.miniapp.api.WxMaService#setMaxRetryTimes(int)}
+ * {@link cn.binarywang.wx.miniapp.api.impl.BaseWxMaServiceImpl#setMaxRetryTimes(int)}
+ *
+ */
+ private int maxRetryTimes = 5;
+
+ /**
+ * http 请求重试间隔
+ *
+ * {@link cn.binarywang.wx.miniapp.api.WxMaService#setRetrySleepMillis(int)}
+ * {@link cn.binarywang.wx.miniapp.api.impl.BaseWxMaServiceImpl#setRetrySleepMillis(int)}
+ *
+ */
+ private int retrySleepMillis = 1000;
+ }
+
+ public enum StorageType {
+ /**
+ * 内存
+ */
+ MEMORY,
+ /**
+ * jedis
+ */
+ JEDIS,
+ /**
+ * redisson
+ */
+ REDISSON,
+ /**
+ * redisTemplate
+ */
+ REDIS_TEMPLATE
+ }
+
+ public enum HttpClientType {
+ /**
+ * HttpClient
+ */
+ HTTP_CLIENT,
+ /**
+ * OkHttp
+ */
+ OK_HTTP,
+ /**
+ * JoddHttp
+ */
+ JODD_HTTP
+ }
+}
diff --git a/solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/properties/WxMaMultiRedisProperties.java b/solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/properties/WxMaMultiRedisProperties.java
new file mode 100644
index 0000000000..1f4c07806e
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/properties/WxMaMultiRedisProperties.java
@@ -0,0 +1,56 @@
+package com.binarywang.solon.wxjava.miniapp.properties;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * @author monch
+ * created on 2024/9/6
+ */
+@Data
+@NoArgsConstructor
+public class WxMaMultiRedisProperties implements Serializable {
+ private static final long serialVersionUID = -5924815351660074401L;
+
+ /**
+ * 主机地址.
+ */
+ private String host = "127.0.0.1";
+
+ /**
+ * 端口号.
+ */
+ private int port = 6379;
+
+ /**
+ * 密码.
+ */
+ private String password;
+
+ /**
+ * 超时.
+ */
+ private int timeout = 2000;
+
+ /**
+ * 数据库.
+ */
+ private int database = 0;
+
+ /**
+ * sentinel ips
+ */
+ private String sentinelIps;
+
+ /**
+ * sentinel name
+ */
+ private String sentinelName;
+
+ private Integer maxActive;
+ private Integer maxIdle;
+ private Integer maxWaitMillis;
+ private Integer minIdle;
+}
diff --git a/solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/properties/WxMaSingleProperties.java b/solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/properties/WxMaSingleProperties.java
new file mode 100644
index 0000000000..f61985716e
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/properties/WxMaSingleProperties.java
@@ -0,0 +1,40 @@
+package com.binarywang.solon.wxjava.miniapp.properties;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * @author monch
+ * created on 2024/9/6
+ */
+@Data
+@NoArgsConstructor
+public class WxMaSingleProperties implements Serializable {
+ private static final long serialVersionUID = 1980986361098922525L;
+ /**
+ * 设置微信公众号的 appid.
+ */
+ private String appId;
+
+ /**
+ * 设置微信公众号的 app secret.
+ */
+ private String appSecret;
+
+ /**
+ * 设置微信公众号的 token.
+ */
+ private String token;
+
+ /**
+ * 设置微信公众号的 EncodingAESKey.
+ */
+ private String aesKey;
+
+ /**
+ * 是否使用稳定版 Access Token
+ */
+ private boolean useStableAccessToken = false;
+}
diff --git a/solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/service/WxMaMultiServices.java b/solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/service/WxMaMultiServices.java
new file mode 100644
index 0000000000..80d073cceb
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/service/WxMaMultiServices.java
@@ -0,0 +1,27 @@
+package com.binarywang.solon.wxjava.miniapp.service;
+
+
+import cn.binarywang.wx.miniapp.api.WxMaService;
+
+/**
+ * 微信小程序 {@link WxMaService} 所有实例存放类.
+ *
+ * @author monch
+ * created on 2024/9/6
+ */
+public interface WxMaMultiServices {
+ /**
+ * 通过租户 Id 获取 WxMaService
+ *
+ * @param tenantId 租户 Id
+ * @return WxMaService
+ */
+ WxMaService getWxMaService(String tenantId);
+
+ /**
+ * 根据租户 Id,从列表中移除一个 WxMaService 实例
+ *
+ * @param tenantId 租户 Id
+ */
+ void removeWxMaService(String tenantId);
+}
diff --git a/solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/service/WxMaMultiServicesImpl.java b/solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/service/WxMaMultiServicesImpl.java
new file mode 100644
index 0000000000..d0ba21cdb8
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/java/com/binarywang/solon/wxjava/miniapp/service/WxMaMultiServicesImpl.java
@@ -0,0 +1,36 @@
+package com.binarywang.solon.wxjava.miniapp.service;
+
+import cn.binarywang.wx.miniapp.api.WxMaService;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 微信小程序 {@link WxMaMultiServices} 默认实现
+ *
+ * @author monch
+ * created on 2024/9/6
+ */
+public class WxMaMultiServicesImpl implements WxMaMultiServices {
+ private final Map services = new ConcurrentHashMap<>();
+
+ @Override
+ public WxMaService getWxMaService(String tenantId) {
+ return this.services.get(tenantId);
+ }
+
+ /**
+ * 根据租户 Id,添加一个 WxMaService 到列表
+ *
+ * @param tenantId 租户 Id
+ * @param wxMaService WxMaService 实例
+ */
+ public void addWxMaService(String tenantId, WxMaService wxMaService) {
+ this.services.put(tenantId, wxMaService);
+ }
+
+ @Override
+ public void removeWxMaService(String tenantId) {
+ this.services.remove(tenantId);
+ }
+}
diff --git a/solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/resources/META-INF/solon/wx-java-miniapp-multi-solon-plugin.properties b/solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/resources/META-INF/solon/wx-java-miniapp-multi-solon-plugin.properties
new file mode 100644
index 0000000000..9d3e2557a8
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-multi-solon-plugin/src/main/resources/META-INF/solon/wx-java-miniapp-multi-solon-plugin.properties
@@ -0,0 +1,2 @@
+solon.plugin=com.binarywang.solon.wxjava.miniapp.integration.WxMiniappMultiPluginImpl
+solon.plugin.priority=10
diff --git a/solon-plugins/wx-java-miniapp-multi-solon-plugin/src/test/java/features/test/LoadTest.java b/solon-plugins/wx-java-miniapp-multi-solon-plugin/src/test/java/features/test/LoadTest.java
new file mode 100644
index 0000000000..d049f5a51a
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-multi-solon-plugin/src/test/java/features/test/LoadTest.java
@@ -0,0 +1,15 @@
+package features.test;
+
+import org.junit.jupiter.api.Test;
+import org.noear.solon.test.SolonTest;
+
+/**
+ * @author noear 2024/9/4 created
+ */
+@SolonTest
+public class LoadTest {
+ @Test
+ public void load(){
+
+ }
+}
diff --git a/solon-plugins/wx-java-miniapp-multi-solon-plugin/src/test/resources/app.properties b/solon-plugins/wx-java-miniapp-multi-solon-plugin/src/test/resources/app.properties
new file mode 100644
index 0000000000..6522b172c6
--- /dev/null
+++ b/solon-plugins/wx-java-miniapp-multi-solon-plugin/src/test/resources/app.properties
@@ -0,0 +1,38 @@
+# 公众号配置
+## 应用 1 配置(必填)
+wx.ma.apps.tenantId1.app-id=appId
+wx.ma.apps.tenantId1.app-secret=@secret
+## 选填
+wx.ma.apps.tenantId1.token=@token
+wx.ma.apps.tenantId1.aes-key=@aesKey
+wx.ma.apps.tenantId1.use-stable-access-token=@useStableAccessToken
+## 应用 2 配置(必填)
+wx.ma.apps.tenantId2.app-id=@appId
+wx.ma.apps.tenantId2.app-secret =@secret
+## 选填
+wx.ma.apps.tenantId2.token=@token
+wx.ma.apps.tenantId2.aes-key=@aesKey
+wx.ma.apps.tenantId2.use-stable-access-token=@useStableAccessToken
+
+# ConfigStorage 配置(选填)
+## 配置类型: memory(默认), jedis, redisson
+wx.ma.config-storage.type=memory
+## 相关redis前缀配置: wx:ma:multi(默认)
+wx.ma.config-storage.key-prefix=wx:ma:multi
+wx.ma.config-storage.redis.host=127.0.0.1
+wx.ma.config-storage.redis.port=6379
+## 单机和 sentinel 同时存在时,优先使用sentinel配置
+# wx.ma.config-storage.redis.sentinel-ips=127.0.0.1:16379,127.0.0.1:26379
+# wx.ma.config-storage.redis.sentinel-name=mymaster
+
+# http 客户端配置(选填)
+## # http客户端类型: http_client(默认), ok_http, jodd_http
+wx.ma.config-storage.http-client-type=http_client
+wx.ma.config-storage.http-proxy-host=
+wx.ma.config-storage.http-proxy-port=
+wx.ma.config-storage.http-proxy-username=
+wx.ma.config-storage.http-proxy-password=
+## 最大重试次数,默认:5 次,如果小于 0,则为 0
+wx.ma.config-storage.max-retry-times=5
+## 重试时间间隔步进,默认:1000 毫秒,如果小于 0,则为 1000
+wx.ma.config-storage.retry-sleep-millis=1000
diff --git a/solon-plugins/wx-java-mp-solon-plugin/README.md b/solon-plugins/wx-java-mp-solon-plugin/README.md
index e5d7d10e25..58dcbfddbe 100644
--- a/solon-plugins/wx-java-mp-solon-plugin/README.md
+++ b/solon-plugins/wx-java-mp-solon-plugin/README.md
@@ -23,19 +23,19 @@
wx.mp.config-storage.key-prefix=wx # 相关redis前缀配置: wx(默认)
wx.mp.config-storage.redis.host=127.0.0.1
wx.mp.config-storage.redis.port=6379
- #单机和sentinel同时存在时,优先使用sentinel配置
- #wx.mp.config-storage.redis.sentinel-ips=127.0.0.1:16379,127.0.0.1:26379
- #wx.mp.config-storage.redis.sentinel-name=mymaster
+ #单机和sentinel同时存在时,优先使用sentinel配置
+ #wx.mp.config-storage.redis.sentinel-ips=127.0.0.1:16379,127.0.0.1:26379
+ #wx.mp.config-storage.redis.sentinel-name=mymaster
# http客户端配置
wx.mp.config-storage.http-client-type=httpclient # http客户端类型: HttpClient(默认), OkHttp, JoddHttp
wx.mp.config-storage.http-proxy-host=
wx.mp.config-storage.http-proxy-port=
wx.mp.config-storage.http-proxy-username=
wx.mp.config-storage.http-proxy-password=
- # 公众号地址host配置
- #wx.mp.hosts.api-host=http://proxy.com/
- #wx.mp.hosts.open-host=http://proxy.com/
- #wx.mp.hosts.mp-host=http://proxy.com/
+ # 公众号地址host配置
+ #wx.mp.hosts.api-host=http://proxy.com/
+ #wx.mp.hosts.open-host=http://proxy.com/
+ #wx.mp.hosts.mp-host=http://proxy.com/
```
3. 自动注入的类型
diff --git a/solon-plugins/wx-java-mp-solon-plugin/src/test/resources/app.properties b/solon-plugins/wx-java-mp-solon-plugin/src/test/resources/app.properties
index a06f6c7dba..06abfa5bb8 100644
--- a/solon-plugins/wx-java-mp-solon-plugin/src/test/resources/app.properties
+++ b/solon-plugins/wx-java-mp-solon-plugin/src/test/resources/app.properties
@@ -4,8 +4,8 @@ wx.mp.secret=@secret
wx.mp.token=@token
wx.mp.aes-key=@aesKey
wx.mp.use-stable-access-token=@useStableAccessToken
-# ????redis(??)
-wx.mp.config-storage.type= edis # ????: Memory(??), Jedis, RedisTemplate
-wx.mp.config-storage.key-prefix=wx # ??redis????: wx(??)
+# ????redis(??) # ????: Memory(??), Jedis, RedisTemplate
+wx.mp.config-storage.type=memory
+wx.mp.config-storage.key-prefix=wx
wx.mp.config-storage.redis.host=127.0.0.1
wx.mp.config-storage.redis.port=6379
From 172a49c18c1cb2494eebf99a5cf5d269be9ea109 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 9 Oct 2024 12:33:37 +0800
Subject: [PATCH 058/385] :arrow_up: Bump commons-io:commons-io from 2.7 to
2.14.0
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index 08a20de0a5..aa7a7b558e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -178,7 +178,7 @@
commons-io
commons-io
- 2.7
+ 2.14.0
org.apache.commons
From 2226693f595b069a464a7ad1bf0ad9060f23a7ca Mon Sep 17 00:00:00 2001
From: zhuangzibin <53577469+zhuangzibin@users.noreply.github.com>
Date: Wed, 9 Oct 2024 12:35:32 +0800
Subject: [PATCH 059/385] =?UTF-8?q?:art:=20#3356=E3=80=90=E5=85=AC?=
=?UTF-8?q?=E4=BC=97=E5=8F=B7=E3=80=91=E8=8D=89=E7=A8=BF=E7=AE=B1=E6=A8=A1?=
=?UTF-8?q?=E5=9D=97=E6=96=B0=E5=BB=BA=E8=8D=89=E7=A8=BF/=E4=BF=AE?=
=?UTF-8?q?=E6=94=B9=E8=8D=89=E7=A8=BF=E6=8E=A5=E5=8F=A3=E8=A1=A5=E5=85=85?=
=?UTF-8?q?=E5=AD=97=E6=AE=B5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../weixin/mp/bean/draft/WxMpDraftArticles.java | 12 ++++++++++++
.../mp/api/impl/WxMpDraftServiceImplTest.java | 13 +++++++------
2 files changed, 19 insertions(+), 6 deletions(-)
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/draft/WxMpDraftArticles.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/draft/WxMpDraftArticles.java
index f9dcb23240..80a7d37d4b 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/draft/WxMpDraftArticles.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/draft/WxMpDraftArticles.java
@@ -79,6 +79,18 @@ public class WxMpDraftArticles implements ToJson, Serializable {
@SerializedName("thumb_url")
private String thumbUrl;
+ /**
+ * 封面裁剪为2.35:1规格的坐标字段。以原始图片(thumb_media_id)左上角(0,0),右下角(1,1)建立平面坐标系,经过裁剪后的图片,其左上角所在的坐标即为(X1,Y1),右下角所在的坐标则为(X2,Y2),用分隔符_拼接为X1_Y1_X2_Y2,每个坐标值的精度为不超过小数点后6位数字。示例见下图,图中(X1,Y1) 等于(0.1945,0),(X2,Y2)等于(1,0.5236),所以请求参数值为0.1945_0_1_0.5236。
+ */
+ @SerializedName("pic_crop_235_1")
+ private String picCrop2351;
+
+ /**
+ * 封面裁剪为1:1规格的坐标字段,裁剪原理同pic_crop_235_1,裁剪后的图片必须符合规格要求。
+ */
+ @SerializedName("pic_crop_1_1")
+ private String picCrop11;
+
public static WxMpDraftArticles fromJson(String json) {
return WxGsonBuilder.create().fromJson(json, WxMpDraftArticles.class);
}
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpDraftServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpDraftServiceImplTest.java
index 2413c7fcaf..15966d6727 100644
--- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpDraftServiceImplTest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpDraftServiceImplTest.java
@@ -4,11 +4,7 @@
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.draft.WxMpAddDraft;
-import me.chanjar.weixin.mp.bean.draft.WxMpDraftArticles;
-import me.chanjar.weixin.mp.bean.draft.WxMpDraftInfo;
-import me.chanjar.weixin.mp.bean.draft.WxMpDraftList;
-import me.chanjar.weixin.mp.bean.draft.WxMpUpdateDraft;
+import me.chanjar.weixin.mp.bean.draft.*;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;
@@ -59,6 +55,8 @@ public void testAddGuide_another() throws WxErrorException {
.thumbMediaId(thumbMediaId)
// 显示封面、打开评论、所有人可评论
.showCoverPic(1).needOpenComment(1).onlyFansCanComment(0)
+ .picCrop2351("0.1945_0_1_0.5236")
+ .picCrop11("0.1945_0_1_0.5236")
.build();
draftArticleList.add(draftArticle);
@@ -78,7 +76,10 @@ public void testGetDraft() throws WxErrorException {
@Test
public void testUpdateDraft() throws WxErrorException {
WxMpDraftArticles draftArticles = WxMpDraftArticles.builder()
- .title("新标题").content("新图文消息的具体内容").thumbMediaId(thumbMediaId).build();
+ .title("新标题").content("新图文消息的具体内容").thumbMediaId(thumbMediaId)
+ .picCrop2351("0.1945_0_1_0.5236")
+ .picCrop11("0.1945_0_1_0.5236")
+ .build();
WxMpUpdateDraft updateDraft = WxMpUpdateDraft.builder()
.mediaId(mediaId)
.index(0)
From 5821710e076a09584bd8af941983c9c6987ec996 Mon Sep 17 00:00:00 2001
From: zhuangzibin <53577469+zhuangzibin@users.noreply.github.com>
Date: Wed, 9 Oct 2024 12:36:50 +0800
Subject: [PATCH 060/385] =?UTF-8?q?:new:=20#3327=E3=80=90=E5=BE=AE?=
=?UTF-8?q?=E4=BF=A1=E6=94=AF=E4=BB=98=E3=80=91=E5=A2=9E=E5=8A=A0=E5=B9=B3?=
=?UTF-8?q?=E5=8F=B0=E6=94=B6=E4=BB=98=E9=80=9A=EF=BC=88=E6=B3=A8=E9=94=80?=
=?UTF-8?q?=E7=94=B3=E8=AF=B7=EF=BC=89=E7=9B=B8=E5=85=B3=E6=8E=A5=E5=8F=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../AccountCancelApplicationsMediaResult.java | 24 +++++++
.../AccountCancelApplicationsRequest.java | 64 +++++++++++++++++++
.../AccountCancelApplicationsResult.java | 52 +++++++++++++++
.../wxpay/service/EcommerceService.java | 37 +++++++++++
.../service/impl/EcommerceServiceImpl.java | 35 ++++++++++
.../wxpay/v3/WechatPayUploadHttpPost.java | 28 ++++++++
.../impl/EcommerceServiceImplTest.java | 36 +++++++++--
7 files changed, 269 insertions(+), 7 deletions(-)
create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/AccountCancelApplicationsMediaResult.java
create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/AccountCancelApplicationsRequest.java
create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/AccountCancelApplicationsResult.java
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/AccountCancelApplicationsMediaResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/AccountCancelApplicationsMediaResult.java
new file mode 100644
index 0000000000..e14f8447a8
--- /dev/null
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/AccountCancelApplicationsMediaResult.java
@@ -0,0 +1,24 @@
+package com.github.binarywang.wxpay.bean.ecommerce;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 图片上传API
+ *
+ * https://pay.weixin.qq.com/docs/partner/apis/ecommerce-cancel/media/upload-media.html
+ *
+ */
+@Data
+@NoArgsConstructor
+public class AccountCancelApplicationsMediaResult implements Serializable {
+
+ /**
+ * 微信返回的媒体文件标识ID。
+ */
+ @SerializedName(value = "media_id")
+ private String mediaId;
+}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/AccountCancelApplicationsRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/AccountCancelApplicationsRequest.java
new file mode 100644
index 0000000000..6f1b60f398
--- /dev/null
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/AccountCancelApplicationsRequest.java
@@ -0,0 +1,64 @@
+package com.github.binarywang.wxpay.bean.ecommerce;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 提交注销申请单
+ *
+ * https://pay.weixin.qq.com/docs/partner/apis/ecommerce-cancel/cancel-applications/create-cancel-application.html
+ *
+ */
+@Data
+@NoArgsConstructor
+public class AccountCancelApplicationsRequest implements Serializable {
+
+ /**
+ * 【申请注销的二级商户号】 电商平台二级商户号,由微信支付生成并下发
+ */
+ @SerializedName(value = "sub_mchid")
+ private String subMchid;
+
+ /**
+ * 【商户注销申请单号】 商户注销申请单号,由商户自定义生成,要求在服务商维度下是唯一的,必须仅包含大小写字母与数字
+ */
+ @SerializedName(value = "out_apply_no")
+ private String outApplyNo;
+
+ /**
+ * 【注销申请材料】 注销申请材料,详见文档:注销申请材料
+ */
+ @SerializedName(value = "application_info")
+ private List applicationInfo;
+
+ @Data
+ @Builder
+ @AllArgsConstructor
+ @NoArgsConstructor
+ public static class CancelApplicationInfo implements Serializable {
+
+ /**
+ *【注销申请材料类型】 注销申请材料类型,详见文档:注销申请材料
+ * 可选取值:
+ * SP_MERCHANT_APPLICATION: 此枚举值已废弃,请使用新字段 SP_CANCEL_ACCOUNT_APPLICATION 以及新版本材料
+ * SUB_MERCHANT_APPLICATION: 此枚举值已废弃,请使用新字段 SUB_CANCEL_ACCOUNT_APPLICATION 以及新版本材料
+ * MISSING_OFFICIAL_SEAL_LETTER: 此材料已废弃,无需上传
+ * SP_CANCEL_ACCOUNT_APPLICATION: 电商服务商注销电商子申请书,请下载模板打印纸质版、填写盖章后拍照。模板文档详见:微信支付商户号注销申请书-服务商(纸质版)
+ * SUB_CANCEL_ACCOUNT_APPLICATION: 电商服务商子商户注销申请书,详见文档:微信支付商户号注销申请书-电商平台子商户适用(纸质版)
+ */
+ @SerializedName("application_type")
+ private String applicationType;
+
+ /**
+ * 【注销申请材料照片ID】 注销申请材料照片ID,请填写通过上传图片接口预先上传图片生成好的media_id
+ */
+ @SerializedName("application_media_id")
+ private String applicationMediaId;
+ }
+}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/AccountCancelApplicationsResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/AccountCancelApplicationsResult.java
new file mode 100644
index 0000000000..6d75102bd6
--- /dev/null
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/AccountCancelApplicationsResult.java
@@ -0,0 +1,52 @@
+package com.github.binarywang.wxpay.bean.ecommerce;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 提交注销申请单
+ *
+ * https://pay.weixin.qq.com/docs/partner/apis/ecommerce-cancel/cancel-applications/create-cancel-application.html
+ *
+ */
+@Data
+@NoArgsConstructor
+public class AccountCancelApplicationsResult implements Serializable {
+
+ /**
+ * 【商户注销申请单号】 商户注销申请单号,原样返回请求参数里的内容
+ */
+ @SerializedName(value = "out_apply_no")
+ private String outApplyNo;
+
+ /**
+ * 【二级商户号】 二级商户号
+ */
+ @SerializedName(value = "sub_mchid")
+ private String subMchid;
+
+ /**
+ * 【驳回原因】 受理失败原因
+ */
+ @SerializedName(value = "reject_reason")
+ private String rejectReason;
+
+ /**
+ * 【注销状态】 注销状态
+ * 可选取值:
+ * REVIEWING: 审核中
+ * REJECTED: 审核驳回,驳回原因详见reject_reason
+ * CANCEL_SUCCESS: 注销成功
+ */
+ @SerializedName(value = "cancel_state")
+ private String cancelState;
+
+ /**
+ * 【最后更新时间】 最后更新时间。遵循rfc3339标准格式,格式为yyyy-MM-DDTHH:mm:ss+TIMEZONE,yyyy-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss表示时分秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。例如:2015-05-20T13:29:35+08:00表示,北京时间2015年5月20日 13点29分35秒。
+ */
+ @SerializedName(value = "update_time")
+ private String updateTime;
+}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java
index af9a1b38b8..99d17e0732 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java
@@ -6,6 +6,8 @@
import com.github.binarywang.wxpay.bean.ecommerce.enums.TradeTypeEnum;
import com.github.binarywang.wxpay.exception.WxPayException;
+import java.io.File;
+import java.io.IOException;
import java.io.InputStream;
/**
@@ -535,4 +537,39 @@ public interface EcommerceService {
*/
SubsidiesCancelResult subsidiesCancel(SubsidiesCancelRequest subsidiesCancelRequest) throws WxPayException;
+ /**
+ *
+ * 提交注销申请单
+ * 文档地址: https://pay.weixin.qq.com/docs/partner/apis/ecommerce-cancel/cancel-applications/create-cancel-application.html
+ *
+ *
+ * @param accountCancelApplicationsRequest 提交注销申请单
+ * @return 返回数据 return AccountCancelApplicationsResult
+ * @throws WxPayException the wx pay exception
+ */
+ AccountCancelApplicationsResult createdAccountCancelApplication(AccountCancelApplicationsRequest accountCancelApplicationsRequest) throws WxPayException;
+
+ /**
+ *
+ * 查询注销单状态
+ * 文档地址: https://pay.weixin.qq.com/docs/partner/apis/ecommerce-cancel/cancel-applications/get-cancel-application.html
+ *
+ *
+ * @param outApplyNo 注销申请单号
+ * @return 返回数据 return AccountCancelApplicationsResult
+ * @throws WxPayException the wx pay exception
+ */
+ AccountCancelApplicationsResult getAccountCancelApplication(String outApplyNo) throws WxPayException;
+
+ /**
+ *
+ * 注销单资料图片上传
+ * 文档地址: https://pay.weixin.qq.com/docs/partner/apis/ecommerce-cancel/media/upload-media.html
+ *
+ *
+ * @param imageFile 图片
+ * @return 返回数据 return AccountCancelApplicationsResult
+ * @throws WxPayException the wx pay exception
+ */
+ AccountCancelApplicationsMediaResult uploadMediaAccountCancelApplication(File imageFile) throws WxPayException, IOException;;
}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java
index edd2a2f4a6..3671ab72ba 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java
@@ -7,22 +7,27 @@
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.EcommerceService;
import com.github.binarywang.wxpay.service.WxPayService;
+import com.github.binarywang.wxpay.v3.WechatPayUploadHttpPost;
import com.github.binarywang.wxpay.v3.util.AesUtils;
import com.github.binarywang.wxpay.v3.util.RsaCryptoUtil;
import com.google.common.base.CaseFormat;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import lombok.RequiredArgsConstructor;
+import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
+import java.io.File;
+import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.text.DateFormat;
@@ -395,6 +400,36 @@ public SubsidiesCancelResult subsidiesCancel(SubsidiesCancelRequest subsidiesCan
String response = this.payService.postV3(url, GSON.toJson(subsidiesCancelRequest));
return GSON.fromJson(response, SubsidiesCancelResult.class);
}
+
+ @Override
+ public AccountCancelApplicationsResult createdAccountCancelApplication(AccountCancelApplicationsRequest accountCancelApplicationsRequest) throws WxPayException {
+ String url = String.format("%s/v3/ecommerce/account/cancel-applications", this.payService.getPayBaseUrl());
+ String response = this.payService.postV3(url, GSON.toJson(accountCancelApplicationsRequest));
+ return GSON.fromJson(response, AccountCancelApplicationsResult.class);
+ }
+
+ @Override
+ public AccountCancelApplicationsResult getAccountCancelApplication(String outApplyNo) throws WxPayException {
+ String url = String.format("%s/v3/ecommerce/account/cancel-applications/out-apply-no/%s", this.payService.getPayBaseUrl(), outApplyNo);
+ String result = this.payService.getV3(url);
+ return GSON.fromJson(result, AccountCancelApplicationsResult.class);
+ }
+
+ @Override
+ public AccountCancelApplicationsMediaResult uploadMediaAccountCancelApplication(File imageFile) throws WxPayException, IOException {
+ String url = String.format("%s/v3/ecommerce/account/cancel-applications/media", this.payService.getPayBaseUrl());
+ try (FileInputStream s1 = new FileInputStream(imageFile)) {
+ String sha256 = DigestUtils.sha256Hex(s1);
+ try (InputStream s2 = new FileInputStream(imageFile)) {
+ WechatPayUploadHttpPost request = new WechatPayUploadHttpPost.Builder(URI.create(url))
+ .withImage(imageFile.getName(), sha256, s2)
+ .buildEcommerceAccount();
+ String result = this.payService.postV3(url, request);
+ return GSON.fromJson(result, AccountCancelApplicationsMediaResult.class);
+ }
+ }
+ }
+
/**
* 校验通知签名
*
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/WechatPayUploadHttpPost.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/WechatPayUploadHttpPost.java
index df0ee4e2fb..5f5e52d2ff 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/WechatPayUploadHttpPost.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/WechatPayUploadHttpPost.java
@@ -72,5 +72,33 @@ public WechatPayUploadHttpPost build() {
return request;
}
+
+ /**
+ * 平台收付通(注销申请)-图片上传-图片上传
+ * https://pay.weixin.qq.com/docs/partner/apis/ecommerce-cancel/media/upload-media.html
+ * @return WechatPayUploadHttpPost
+ */
+ public WechatPayUploadHttpPost buildEcommerceAccount() {
+ if (fileName == null || fileSha256 == null || fileInputStream == null) {
+ throw new IllegalArgumentException("缺少待上传图片文件信息");
+ }
+
+ if (uri == null) {
+ throw new IllegalArgumentException("缺少上传图片接口URL");
+ }
+
+ String meta = String.format("{\"file_name\":\"%s\",\"file_digest\":\"%s\"}", fileName, fileSha256);
+ WechatPayUploadHttpPost request = new WechatPayUploadHttpPost(uri, meta);
+
+ MultipartEntityBuilder entityBuilder = MultipartEntityBuilder.create();
+ entityBuilder.setMode(HttpMultipartMode.RFC6532)
+ .addBinaryBody("file", fileInputStream, fileContentType, fileName)
+ .addTextBody("meta", meta, ContentType.APPLICATION_JSON);
+
+ request.setEntity(entityBuilder.build());
+ request.addHeader("Accept", ContentType.APPLICATION_JSON.toString());
+
+ return request;
+ }
}
}
diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImplTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImplTest.java
index 97e85f4139..e250b9ea1c 100644
--- a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImplTest.java
+++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImplTest.java
@@ -1,12 +1,7 @@
package com.github.binarywang.wxpay.service.impl;
+import com.google.common.collect.Lists;
-import com.github.binarywang.wxpay.bean.ecommerce.CombineTransactionsRequest;
-import com.github.binarywang.wxpay.bean.ecommerce.PartnerTransactionsQueryRequest;
-import com.github.binarywang.wxpay.bean.ecommerce.PartnerTransactionsResult;
-import com.github.binarywang.wxpay.bean.ecommerce.ProfitSharingReceiverRequest;
-import com.github.binarywang.wxpay.bean.ecommerce.ProfitSharingReceiverResult;
-import com.github.binarywang.wxpay.bean.ecommerce.SignatureHeader;
-import com.github.binarywang.wxpay.bean.ecommerce.TransactionsResult;
+import com.github.binarywang.wxpay.bean.ecommerce.*;
import com.github.binarywang.wxpay.bean.ecommerce.enums.SpAccountTypeEnum;
import com.github.binarywang.wxpay.bean.ecommerce.enums.TradeTypeEnum;
import com.github.binarywang.wxpay.exception.WxPayException;
@@ -19,6 +14,8 @@
import org.testng.annotations.Guice;
import org.testng.annotations.Test;
+import java.io.File;
+import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
@@ -151,4 +148,29 @@ public void testSubDayEndBalance() throws WxPayException {
String date = "";
wxPayService.getEcommerceService().subDayEndBalance(subMchid, date);
}
+
+ @Test
+ public void testCreatedAccountCancelApplication() throws WxPayException {
+ AccountCancelApplicationsRequest request = new AccountCancelApplicationsRequest();
+ request.setSubMchid("");
+ request.setOutApplyNo("");
+ request.setApplicationInfo(Lists.newArrayList());
+
+ AccountCancelApplicationsResult result = wxPayService.getEcommerceService().createdAccountCancelApplication(request);
+ log.info("请求参数:{} 响应结果:{}", request, result);
+ }
+
+ @Test
+ public void testGetAccountCancelApplication() throws WxPayException {
+ String request = "申请单号";
+ AccountCancelApplicationsResult result = wxPayService.getEcommerceService().getAccountCancelApplication(request);
+ log.info("请求参数:{} 响应结果:{}", request, result);
+ }
+
+ @Test
+ public void testUploadMediaAccountCancelApplication() throws WxPayException, IOException {
+ AccountCancelApplicationsMediaResult result = wxPayService.getEcommerceService()
+ .uploadMediaAccountCancelApplication(new File("src\\test\\resources\\mm.jpeg"));
+ log.info("响应结果:{}", result);
+ }
}
From 20688541aad11431b8c16bf6f200050fe881fd23 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 16 Oct 2024 20:59:20 +0800
Subject: [PATCH 061/385] :arrow_up: Bump org.eclipse.jetty:jetty-server
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index aa7a7b558e..7376576c3b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -136,7 +136,7 @@
UTF-8
4.5.13
- 9.4.51.v20230217
+ 9.4.55.v20240627
From 948cfbb310b98d7c928c1e767995c2a23ae08adc Mon Sep 17 00:00:00 2001
From: Molzx <31435895+Molzx@users.noreply.github.com>
Date: Wed, 16 Oct 2024 21:00:33 +0800
Subject: [PATCH 062/385] =?UTF-8?q?:bug:=20#3389=20=E3=80=90=E5=B0=8F?=
=?UTF-8?q?=E7=A8=8B=E5=BA=8F=E3=80=91=E4=BF=AE=E5=A4=8D=E8=8E=B7=E5=8F=96?=
=?UTF-8?q?=E5=B8=90=E5=8F=B7=E5=9F=BA=E6=9C=AC=E4=BF=A1=E6=81=AF=E7=9A=84?=
=?UTF-8?q?=E7=BB=93=E6=9E=9C=E4=B8=ADcustomerType=E7=AD=89=E5=AD=97?=
=?UTF-8?q?=E6=AE=B5=E4=B8=BAnull=E7=9A=84=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../WxFastMaAccountBasicInfoGsonAdapter.java | 56 -------------------
.../open/util/json/WxOpenGsonBuilder.java | 1 -
2 files changed, 57 deletions(-)
delete mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxFastMaAccountBasicInfoGsonAdapter.java
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxFastMaAccountBasicInfoGsonAdapter.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxFastMaAccountBasicInfoGsonAdapter.java
deleted file mode 100644
index 2a4795aba4..0000000000
--- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxFastMaAccountBasicInfoGsonAdapter.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package me.chanjar.weixin.open.util.json;
-
-import com.google.gson.*;
-import com.google.gson.reflect.TypeToken;
-import me.chanjar.weixin.common.util.json.GsonHelper;
-import me.chanjar.weixin.open.bean.result.WxFastMaAccountBasicInfoResult;
-
-import java.lang.reflect.Type;
-
-/**
- * .
- *
- * @author Hipple
- * @since 2019/1/23 15:02
- */
-public class WxFastMaAccountBasicInfoGsonAdapter implements JsonDeserializer {
- @Override
- public WxFastMaAccountBasicInfoResult deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext context)
- throws JsonParseException {
- WxFastMaAccountBasicInfoResult accountBasicInfo = new WxFastMaAccountBasicInfoResult();
- JsonObject jsonObject = jsonElement.getAsJsonObject();
-
- accountBasicInfo.setAppId(GsonHelper.getString(jsonObject, "appid"));
- accountBasicInfo.setAccountType(GsonHelper.getInteger(jsonObject, "account_type"));
- accountBasicInfo.setPrincipalType(GsonHelper.getInteger(jsonObject, "principal_type"));
- accountBasicInfo.setPrincipalName(GsonHelper.getString(jsonObject, "principal_name"));
- accountBasicInfo.setRealnameStatus(GsonHelper.getInteger(jsonObject, "realname_status"));
- accountBasicInfo.setNickname(GsonHelper.getString(jsonObject, "nickname"));
-
- WxFastMaAccountBasicInfoResult.NicknameInfo nicknameInfo = WxOpenGsonBuilder.create()
- .fromJson(jsonObject.get("nickname_info"),
- new TypeToken() {
- }.getType());
- accountBasicInfo.setNicknameInfo(nicknameInfo);
-
- WxFastMaAccountBasicInfoResult.WxVerifyInfo verifyInfo = WxOpenGsonBuilder.create()
- .fromJson(jsonObject.get("wx_verify_info"),
- new TypeToken() {
- }.getType());
- accountBasicInfo.setWxVerifyInfo(verifyInfo);
-
- WxFastMaAccountBasicInfoResult.SignatureInfo signatureInfo = WxOpenGsonBuilder.create()
- .fromJson(jsonObject.get("signature_info"),
- new TypeToken() {
- }.getType());
- accountBasicInfo.setSignatureInfo(signatureInfo);
-
- WxFastMaAccountBasicInfoResult.HeadImageInfo headImageInfo = WxOpenGsonBuilder.create()
- .fromJson(jsonObject.get("head_image_info"),
- new TypeToken() {
- }.getType());
- accountBasicInfo.setHeadImageInfo(headImageInfo);
-
- return accountBasicInfo;
- }
-}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenGsonBuilder.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenGsonBuilder.java
index 5dbae037a2..9cb4abd072 100644
--- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenGsonBuilder.java
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/json/WxOpenGsonBuilder.java
@@ -26,7 +26,6 @@ public class WxOpenGsonBuilder {
INSTANCE.registerTypeAdapter(WxOpenQueryAuthResult.class, new WxOpenQueryAuthResultGsonAdapter());
INSTANCE.registerTypeAdapter(WxOpenAuthorizerInfoResult.class, new WxOpenAuthorizerInfoResultGsonAdapter());
INSTANCE.registerTypeAdapter(WxOpenAuthorizerOptionResult.class, new WxOpenAuthorizerOptionResultGsonAdapter());
- INSTANCE.registerTypeAdapter(WxFastMaAccountBasicInfoResult.class, new WxFastMaAccountBasicInfoGsonAdapter());
INSTANCE.registerTypeAdapter(WxOpenAuthorizerListResult.class, new WxOpenAuthorizerListResultGsonAdapter());
}
From 25e0d780b177ae0fe64e5de76833327cd0fce5cb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=BC=AB=E5=A4=A9=E7=9A=84=E6=B2=99?=
Date: Thu, 17 Oct 2024 00:13:39 +0800
Subject: [PATCH 063/385] =?UTF-8?q?:art:=20=E5=B0=8F=E7=A8=8B=E5=BA=8F?=
=?UTF-8?q?=E5=88=87=E6=8D=A2=E6=97=B6=E6=94=AF=E6=8C=81=E4=BC=A0=E9=80=92?=
=?UTF-8?q?=E5=87=BD=E6=95=B0=EF=BC=8C=E6=8F=90=E9=AB=98=E6=89=A9=E5=B1=95?=
=?UTF-8?q?=E6=80=A7?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../wx/miniapp/api/WxMaService.java | 16 +++++++++++---
.../miniapp/api/impl/BaseWxMaServiceImpl.java | 22 +++++++++++++++----
2 files changed, 31 insertions(+), 7 deletions(-)
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 97f80784d8..83cbf40a4e 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
@@ -2,6 +2,7 @@
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.config.WxMaConfig;
+import java.util.function.Function;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.service.WxImgProcService;
import me.chanjar.weixin.common.service.WxOcrService;
@@ -213,12 +214,21 @@ public interface WxMaService extends WxService {
boolean switchover(String mpId);
/**
- * 进行相应的公众号切换.
+ * 进行相应的小程序切换.
+ *
+ * @param miniAppId 小程序标识
+ * @return 切换成功 ,则返回当前对象,方便链式调用,否则抛出异常
+ */
+ WxMaService switchoverTo(String miniAppId);
+
+ /**
+ * 进行相应的小程序切换.
*
- * @param miniappId 小程序标识
+ * @param miniAppId 小程序标识
+ * @param func 当对应的小程序配置不存在时,允许通过函数的方式进行调用获取
* @return 切换成功 ,则返回当前对象,方便链式调用,否则抛出异常
*/
- WxMaService switchoverTo(String miniappId);
+ WxMaService switchoverTo(String miniAppId, Function func);
/**
* 返回消息(客服消息和模版消息)发送接口方法实现类,以方便调用其各个接口.
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/BaseWxMaServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/BaseWxMaServiceImpl.java
index a5ab3df18a..6b67b3c28d 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/BaseWxMaServiceImpl.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/BaseWxMaServiceImpl.java
@@ -10,6 +10,7 @@
import com.google.common.collect.Maps;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
+import java.util.function.Function;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.bean.CommonUploadParam;
@@ -431,13 +432,26 @@ public void removeConfig(String miniappId) {
}
@Override
- public WxMaService switchoverTo(String miniappId) {
- if (this.configMap.containsKey(miniappId)) {
- WxMaConfigHolder.set(miniappId);
+ public WxMaService switchoverTo(String miniAppId) {
+ return switchoverTo(miniAppId, null);
+ }
+
+ @Override
+ public WxMaService switchoverTo(String miniAppId, Function func) {
+ if (this.configMap.containsKey(miniAppId)) {
+ WxMaConfigHolder.set(miniAppId);
return this;
}
- throw new WxRuntimeException(String.format("无法找到对应【%s】的小程序配置信息,请核实!", miniappId));
+ if (func != null) {
+ WxMaConfig config = func.apply(miniAppId);
+ if (config != null) {
+ this.addConfig(miniAppId, config);
+ return this;
+ }
+ }
+
+ throw new WxRuntimeException(String.format("无法找到对应【%s】的小程序配置信息,请核实!", miniAppId));
}
@Override
From 9eba04dce9573ac810b9b87cda9d46d9ba1a6140 Mon Sep 17 00:00:00 2001
From: Liu <101158568+feathers-l@users.noreply.github.com>
Date: Thu, 17 Oct 2024 00:16:36 +0800
Subject: [PATCH 064/385] =?UTF-8?q?:art:=20=E3=80=90=E5=BE=AE=E4=BF=A1?=
=?UTF-8?q?=E6=94=AF=E4=BB=98=E3=80=91=E5=88=9D=E5=A7=8B=E5=8C=96v3?=
=?UTF-8?q?=E5=AE=A2=E6=88=B7=E7=AB=AF=E6=97=B6=EF=BC=8C=E6=9C=AA=E4=BD=BF?=
=?UTF-8?q?=E7=94=A8p12=E8=AF=81=E4=B9=A6=E4=B8=94=E6=9C=AA=E8=AE=BE?=
=?UTF-8?q?=E7=BD=AE=E8=AF=81=E4=B9=A6=E5=BA=8F=E5=88=97=E5=8F=B7=E5=80=BC?=
=?UTF-8?q?=E6=89=8D=E5=B0=9D=E8=AF=95=E5=8A=A0=E8=BD=BD=E8=AF=81=E4=B9=A6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../com/github/binarywang/wxpay/config/WxPayConfig.java | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
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 d53bdf05e1..83a4b042c1 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
@@ -281,15 +281,13 @@ public CloseableHttpClient initApiV3HttpClient() throws WxPayException {
merchantPrivateKey = PemUtils.loadPrivateKey(keyInputStream);
}
- if (certificate == null) {
+ if (certificate == null && StringUtils.isBlank(this.getCertSerialNo())) {
InputStream certInputStream = this.loadConfigInputStream(this.getPrivateCertString(), this.getPrivateCertPath(),
this.privateCertContent, "privateCertPath");
certificate = PemUtils.loadCertificate(certInputStream);
- }
-
- if (StringUtils.isBlank(this.getCertSerialNo())) {
this.certSerialNo = certificate.getSerialNumber().toString(16).toUpperCase();
}
+
//构造Http Proxy正向代理
WxPayHttpProxy wxPayHttpProxy = getWxPayHttpProxy();
From b87da90b699ba72ae50ecb428cc28d211f7f7432 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=9C=A8=E6=9C=89=E9=B1=BC=E4=B8=B8?=
<77617245+llw5181@users.noreply.github.com>
Date: Sun, 27 Oct 2024 19:20:29 +0800
Subject: [PATCH 065/385] =?UTF-8?q?:art:=20=E3=80=90=E4=BC=81=E4=B8=9A?=
=?UTF-8?q?=E5=BE=AE=E4=BF=A1=E3=80=91=E4=BF=AE=E5=A4=8D=E6=9B=B4=E6=96=B0?=
=?UTF-8?q?=E6=A8=A1=E7=89=88=E5=8D=A1=E7=89=87=E6=B6=88=E6=81=AF=E6=8E=A5?=
=?UTF-8?q?=E5=8F=A3=E7=9A=84json=E5=BA=8F=E5=88=97=E5=8C=96=E9=97=AE?=
=?UTF-8?q?=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../cp/api/impl/WxCpTaskCardServiceImpl.java | 2 +-
.../cp/bean/message/TemplateCardMessage.java | 176 +++++++++---------
.../bean/message/TemplateCardMessageTest.java | 39 ++++
3 files changed, 133 insertions(+), 84 deletions(-)
create mode 100644 weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/message/TemplateCardMessageTest.java
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpTaskCardServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpTaskCardServiceImpl.java
index 4802c5549c..8469451428 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpTaskCardServiceImpl.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpTaskCardServiceImpl.java
@@ -65,6 +65,6 @@ public void updateTemplateCardButton(List userIds, List partyId
@Override
public void updateTemplateCardButton(TemplateCardMessage templateCardMessage) throws WxErrorException {
String url = this.mainService.getWxCpConfigStorage().getApiUrl(UPDATE_TEMPLATE_CARD);
- this.mainService.post(url, WxGsonBuilder.create().toJson(templateCardMessage));
+ this.mainService.post(url, templateCardMessage.toJson());
}
}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/TemplateCardMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/TemplateCardMessage.java
index 417b5e80e7..0d905e10f7 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/TemplateCardMessage.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/TemplateCardMessage.java
@@ -1,9 +1,10 @@
package me.chanjar.weixin.cp.bean.message;
-import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.gson.annotations.SerializedName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
+import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
import java.io.Serializable;
import java.util.List;
@@ -14,152 +15,161 @@
public class TemplateCardMessage implements Serializable {
private static final long serialVersionUID = 8833792280163704239L;
- @JsonProperty("userids")
+ @SerializedName("userids")
private List userids;
- @JsonProperty("partyids")
+ @SerializedName("partyids")
private List partyids;
- @JsonProperty("tagids")
+ @SerializedName("tagids")
private List tagids;
- @JsonProperty("atall")
+ @SerializedName("atall")
private Integer atall;
- @JsonProperty("agentid")
+ @SerializedName("agentid")
private Integer agentid;
- @JsonProperty("response_code")
+ @SerializedName("response_code")
private String responseCode;
- @JsonProperty("enable_id_trans")
+ @SerializedName("enable_id_trans")
private Integer enableIdTrans;
- @JsonProperty("template_card")
+ @SerializedName("template_card")
private TemplateCardDTO templateCard;
+ /**
+ * To json string.
+ *
+ * @return the string
+ */
+ public String toJson() {
+ return WxCpGsonBuilder.create().toJson(this);
+ }
+
@NoArgsConstructor
@Data
public static class TemplateCardDTO {
- @JsonProperty("card_type")
+ @SerializedName("card_type")
private String cardType;
- @JsonProperty("source")
+ @SerializedName("source")
private SourceDTO source;
- @JsonProperty("main_title")
+ @SerializedName("main_title")
private MainTitleDTO mainTitle;
- @JsonProperty("select_list")
+ @SerializedName("select_list")
private List selectList;
- @JsonProperty("submit_button")
+ @SerializedName("submit_button")
private SubmitButtonDTO submitButton;
- @JsonProperty("replace_text")
+ @SerializedName("replace_text")
private String replaceText;
- @JsonProperty("checkbox")
+ @SerializedName("checkbox")
private CheckboxDTO checkbox;
- @JsonProperty("action_menu")
+ @SerializedName("action_menu")
private ActionMenuDTO actionMenu;
- @JsonProperty("quote_area")
+ @SerializedName("quote_area")
private QuoteAreaDTO quoteArea;
- @JsonProperty("sub_title_text")
+ @SerializedName("sub_title_text")
private String subTitleText;
- @JsonProperty("horizontal_content_list")
+ @SerializedName("horizontal_content_list")
private List horizontalContentList;
- @JsonProperty("card_action")
+ @SerializedName("card_action")
private CardActionDTO cardAction;
- @JsonProperty("button_selection")
+ @SerializedName("button_selection")
private ButtonSelectionDTO buttonSelection;
- @JsonProperty("button_list")
+ @SerializedName("button_list")
private List buttonList;
- @JsonProperty("image_text_area")
+ @SerializedName("image_text_area")
private ImageTextAreaDTO imageTextArea;
- @JsonProperty("card_image")
+ @SerializedName("card_image")
private CardImageDTO cardImage;
- @JsonProperty("vertical_content_list")
+ @SerializedName("vertical_content_list")
private List verticalContentList;
- @JsonProperty("jump_list")
+ @SerializedName("jump_list")
private List jumpList;
@NoArgsConstructor
@Data
public static class SourceDTO {
- @JsonProperty("icon_url")
+ @SerializedName("icon_url")
private String iconUrl;
- @JsonProperty("desc")
+ @SerializedName("desc")
private String desc;
- @JsonProperty("desc_color")
+ @SerializedName("desc_color")
private Integer descColor;
}
@NoArgsConstructor
@Data
public static class ActionMenuDTO {
- @JsonProperty("desc")
+ @SerializedName("desc")
private String desc;
- @JsonProperty("action_list")
+ @SerializedName("action_list")
private List actionList;
}
@NoArgsConstructor
@Data
public static class QuoteAreaDTO {
- @JsonProperty("type")
+ @SerializedName("type")
private Integer type;
- @JsonProperty("url")
+ @SerializedName("url")
private String url;
- @JsonProperty("title")
+ @SerializedName("title")
private String title;
- @JsonProperty("quote_text")
+ @SerializedName("quote_text")
private String quoteText;
}
@NoArgsConstructor
@Data
public static class CardActionDTO {
- @JsonProperty("type")
+ @SerializedName("type")
private Integer type;
- @JsonProperty("url")
+ @SerializedName("url")
private String url;
- @JsonProperty("appid")
+ @SerializedName("appid")
private String appid;
- @JsonProperty("pagepath")
+ @SerializedName("pagepath")
private String pagepath;
}
@NoArgsConstructor
@Data
public static class ButtonSelectionDTO {
- @JsonProperty("question_key")
+ @SerializedName("question_key")
private String questionKey;
- @JsonProperty("title")
+ @SerializedName("title")
private String title;
- @JsonProperty("option_list")
+ @SerializedName("option_list")
private List optionList;
- @JsonProperty("selected_id")
+ @SerializedName("selected_id")
private String selectedId;
}
@NoArgsConstructor
@Data
public static class HorizontalContentListDTO {
- @JsonProperty("keyname")
+ @SerializedName("keyname")
private String keyname;
- @JsonProperty("value")
+ @SerializedName("value")
private String value;
- @JsonProperty("type")
+ @SerializedName("type")
private Integer type;
- @JsonProperty("url")
+ @SerializedName("url")
private String url;
- @JsonProperty("media_id")
+ @SerializedName("media_id")
private String mediaId;
- @JsonProperty("userid")
+ @SerializedName("userid")
private String userid;
}
@NoArgsConstructor
@Data
public static class ButtonListDTO {
- @JsonProperty("text")
+ @SerializedName("text")
private String text;
- @JsonProperty("style")
+ @SerializedName("style")
private Integer style;
- @JsonProperty("key")
+ @SerializedName("key")
private String key;
}
@@ -167,23 +177,23 @@ public static class ButtonListDTO {
@NoArgsConstructor
@Data
public static class CheckboxDTO {
- @JsonProperty("question_key")
+ @SerializedName("question_key")
private String questionKey;
- @JsonProperty("option_list")
+ @SerializedName("option_list")
private List optionList;
- @JsonProperty("disable")
+ @SerializedName("disable")
private Boolean disable;
- @JsonProperty("mode")
+ @SerializedName("mode")
private Integer mode;
@NoArgsConstructor
@Data
public static class OptionListDTO {
- @JsonProperty("id")
+ @SerializedName("id")
private String id;
- @JsonProperty("text")
+ @SerializedName("text")
private String text;
- @JsonProperty("is_checked")
+ @SerializedName("is_checked")
private Boolean isChecked;
}
@@ -192,41 +202,41 @@ public static class OptionListDTO {
@NoArgsConstructor
@Data
public static class MainTitleDTO {
- @JsonProperty("title")
+ @SerializedName("title")
private String title;
- @JsonProperty("desc")
+ @SerializedName("desc")
private String desc;
}
@NoArgsConstructor
@Data
public static class SubmitButtonDTO {
- @JsonProperty("text")
+ @SerializedName("text")
private String text;
- @JsonProperty("key")
+ @SerializedName("key")
private String key;
}
@NoArgsConstructor
@Data
public static class SelectListDTO {
- @JsonProperty("question_key")
+ @SerializedName("question_key")
private String questionKey;
- @JsonProperty("title")
+ @SerializedName("title")
private String title;
- @JsonProperty("selected_id")
+ @SerializedName("selected_id")
private String selectedId;
- @JsonProperty("disable")
+ @SerializedName("disable")
private Boolean disable;
- @JsonProperty("option_list")
+ @SerializedName("option_list")
private List optionList;
@NoArgsConstructor
@Data
public static class OptionListDTO {
- @JsonProperty("id")
+ @SerializedName("id")
private String id;
- @JsonProperty("text")
+ @SerializedName("text")
private String text;
}
}
@@ -234,39 +244,39 @@ public static class OptionListDTO {
@NoArgsConstructor
@Data
public static class ImageTextAreaDTO {
- @JsonProperty("type")
+ @SerializedName("type")
private Integer type;
- @JsonProperty("url")
+ @SerializedName("url")
private String url;
- @JsonProperty("title")
+ @SerializedName("title")
private String title;
- @JsonProperty("desc")
+ @SerializedName("desc")
private String desc;
- @JsonProperty("image_url")
+ @SerializedName("image_url")
private String imageUrl;
}
@NoArgsConstructor
@Data
public static class CardImageDTO {
- @JsonProperty("url")
+ @SerializedName("url")
private String url;
- @JsonProperty("aspect_ratio")
+ @SerializedName("aspect_ratio")
private Double aspectRatio;
}
@NoArgsConstructor
@Data
public static class JumpListDTO {
- @JsonProperty("type")
+ @SerializedName("type")
private Integer type;
- @JsonProperty("title")
+ @SerializedName("title")
private String title;
- @JsonProperty("url")
+ @SerializedName("url")
private String url;
- @JsonProperty("appid")
+ @SerializedName("appid")
private String appid;
- @JsonProperty("pagepath")
+ @SerializedName("pagepath")
private String pagepath;
}
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/message/TemplateCardMessageTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/message/TemplateCardMessageTest.java
new file mode 100644
index 0000000000..b2231a488b
--- /dev/null
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/message/TemplateCardMessageTest.java
@@ -0,0 +1,39 @@
+package me.chanjar.weixin.cp.bean.message;
+
+import com.google.common.collect.Lists;
+import me.chanjar.weixin.common.util.json.GsonParser;
+import org.testng.annotations.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * 测试用例中的json参考 https://developer.work.weixin.qq.com/document/path/94888
+ *
+ * created on 2024-10-22
+ */
+public class TemplateCardMessageTest {
+
+ /**
+ * Test to json video.
+ */
+ @Test
+ public void testToJson() {
+
+ TemplateCardMessage templateCardMessage = new TemplateCardMessage();
+ templateCardMessage.setAgentid(0);
+ templateCardMessage.setUserids(Lists.newArrayList("userid1", "userid2"));
+ templateCardMessage.setResponseCode("xihrjiohewirfhwripsiqwjerdio_dhweu");
+ TemplateCardMessage.TemplateCardDTO templateCardDTO = new TemplateCardMessage.TemplateCardDTO();
+ templateCardMessage.setTemplateCard(templateCardDTO);
+ TemplateCardMessage.TemplateCardDTO.SelectListDTO selectListDTO = new TemplateCardMessage.TemplateCardDTO.SelectListDTO();
+ selectListDTO.setSelectedId("id");
+ selectListDTO.setQuestionKey("question");
+ templateCardDTO.setSelectList(Lists.newArrayList(selectListDTO));
+ final String json = templateCardMessage.toJson();
+ System.out.println(json);
+ String expectedJson = "{\"userids\":[\"userid1\",\"userid2\"],\"agentid\":0,\"response_code\":\"xihrjiohewirfhwripsiqwjerdio_dhweu\",\"template_card\":{\"select_list\":[{\"question_key\":\"question\",\"selected_id\":\"id\"}]}}";
+
+ assertThat(json).isEqualTo(GsonParser.parse(expectedJson).toString());
+ }
+
+}
From 94b375f5d8caccdc297e295afb4ff70a8a442b53 Mon Sep 17 00:00:00 2001
From: RickSun
Date: Sat, 26 Oct 2024 06:20:13 +0000
Subject: [PATCH 066/385] =?UTF-8?q?:art:=20=E3=80=90=E4=BC=81=E4=B8=9A?=
=?UTF-8?q?=E5=BE=AE=E4=BF=A1=E3=80=91OA=E5=AE=A1=E6=89=B9=E6=A8=A1?=
=?UTF-8?q?=E6=9D=BF=E5=A2=9E=E5=8A=A0=E4=BD=8D=E7=BD=AE=E7=9A=84=E8=8C=83?=
=?UTF-8?q?=E5=9B=B4=E9=85=8D=E7=BD=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../bean/oa/templatedata/TemplateConfig.java | 2 ++
.../bean/oa/templatedata/TemplateLocation.java | 18 ++++++++++++++++++
2 files changed, 20 insertions(+)
create mode 100644 weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateLocation.java
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateConfig.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateConfig.java
index 2bb7a8abac..1a00baad0f 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateConfig.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateConfig.java
@@ -32,6 +32,8 @@ public class TemplateConfig implements Serializable {
private TemplateAttendance attendance;
+ private TemplateLocation location;
+
@SerializedName("vacation_list")
private TemplateVacation vacationList;
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateLocation.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateLocation.java
new file mode 100644
index 0000000000..62ed452ca8
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateLocation.java
@@ -0,0 +1,18 @@
+package me.chanjar.weixin.cp.bean.oa.templatedata;
+
+import lombok.Data;
+
+/**
+ * The type Template location.
+ *
+ * @author RickSun sunalee@dingtalk.com
+ */
+@Data
+public class TemplateLocation {
+
+ /**
+ * 模板位置的范围
+ */
+ private Integer distance;
+
+}
From 3bc4b350d1fe0ad33070ccc24bd04e444b402e0b Mon Sep 17 00:00:00 2001
From: Sacher
Date: Sun, 27 Oct 2024 11:24:37 +0000
Subject: [PATCH 067/385] =?UTF-8?q?:art:=20=E3=80=90=E5=BE=AE=E4=BF=A1?=
=?UTF-8?q?=E6=94=AF=E4=BB=98=E3=80=91=E5=B9=B3=E5=8F=B0=E6=94=B6=E4=BB=98?=
=?UTF-8?q?=E9=80=9AAPP=E6=94=AF=E4=BB=98=E6=8E=A5=E5=8F=A3=E6=96=B0?=
=?UTF-8?q?=E5=A2=9ESDK=E6=89=80=E9=9C=80=E8=A6=81=E7=AD=BE=E5=90=8D?=
=?UTF-8?q?=E5=8F=82=E6=95=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../binarywang/wxpay/bean/ecommerce/TransactionsResult.java | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/TransactionsResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/TransactionsResult.java
index 6bb04f9a63..818bc5ec99 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/TransactionsResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/TransactionsResult.java
@@ -83,7 +83,11 @@ public static class AppResult implements Serializable {
private String packageValue;
private String noncestr;
private String timestamp;
+ private String sign;
+ private String getSignStr() {
+ return String.format("%s\n%s\n%s\n%s\n", appid, timestamp, noncestr, prepayid);
+ }
}
public T getPayInfo(TradeTypeEnum tradeType, String appId, String mchId, PrivateKey privateKey) {
@@ -104,7 +108,7 @@ public T getPayInfo(TradeTypeEnum tradeType, String appId, String mchId, Pri
appResult.setAppid(appId).setPrepayid(this.prepayId).setPartnerid(mchId)
.setNoncestr(nonceStr).setTimestamp(timestamp)
//暂填写固定值Sign=WXPay
- .setPackageValue("Sign=WXPay");
+ .setPackageValue("Sign=WXPay").setSign(SignUtils.sign(appResult.getSignStr(), privateKey));
return (T) appResult;
case NATIVE:
return (T) this.codeUrl;
From 542f93c3bed974c727f870dcea62ff783d21a0d1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=B0=8F=E6=A2=81?=
<77617245+llw5181@users.noreply.github.com>
Date: Mon, 28 Oct 2024 21:31:07 +0800
Subject: [PATCH 068/385] =?UTF-8?q?:art:=20=20#3398=E3=80=90=E4=BC=81?=
=?UTF-8?q?=E4=B8=9A=E5=BE=AE=E4=BF=A1=E3=80=91=E6=9B=B4=E6=96=B0"?=
=?UTF-8?q?=E4=BC=81=E4=B8=9A=E5=BE=AE=E4=BF=A1=E6=97=A5=E7=A8=8B"?=
=?UTF-8?q?=E5=AF=B9=E8=B1=A1=E7=9A=84=E7=9B=B8=E5=85=B3=E5=AD=97=E6=AE=B5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../weixin/cp/bean/oa/WxCpOaSchedule.java | 37 ++++++++++++++++++-
1 file changed, 36 insertions(+), 1 deletion(-)
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpOaSchedule.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpOaSchedule.java
index 53229cd81e..20b1f45e20 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpOaSchedule.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpOaSchedule.java
@@ -33,6 +33,11 @@ public class WxCpOaSchedule implements Serializable, ToJson {
*/
@SerializedName("organizer")
private String organizer;
+ /**
+ * 管理员userid列表
+ */
+ @SerializedName("admins")
+ private List admins;
/**
* 日程参与者列表。最多支持2000人
*/
@@ -70,7 +75,7 @@ public class WxCpOaSchedule implements Serializable, ToJson {
@SerializedName("end_time")
private Long endTime;
/**
- *
+ * 日程状态。0-正常;1-已取消
*/
@SerializedName("status")
private Integer status;
@@ -83,6 +88,11 @@ public class WxCpOaSchedule implements Serializable, ToJson {
*/
@SerializedName("cal_id")
private String calId;
+ /**
+ * 是否全天日程,0-否;1-是
+ */
+ @SerializedName("is_whole_day")
+ private Integer isWholeDay;
@Override
public String toJson() {
@@ -140,9 +150,18 @@ public static class Reminder implements Serializable {
* 900 - 事件开始前15分钟
* 3600 - 事件开始前1小时
* 86400 - 事件开始前1天
+ * 注意:建议使用 remind_time_diffs 字段,该字段后续将会废弃。
*/
@SerializedName("remind_before_event_secs")
private Integer remindBeforeEventSecs;
+ /**
+ * 提醒时间与日程开始时间(start_time)的差值,当is_remind为1时有效。例如:-300表示日程开始前5分钟提醒。
+ * 特殊情况:企业微信终端设置的“全天”类型的日程,由于start_time是0点时间戳,提醒如果设置了当天9点,则会出现正数32400。
+ *
+ * 取值范围:-604800 ~ 86399
+ */
+ @SerializedName("remind_time_diffs")
+ private List remindTimeDiffs;
/**
* 重复类型,当is_repeat为1时有效。目前支持如下类型:
* 0 - 每日
@@ -195,5 +214,21 @@ public static class Reminder implements Serializable {
*/
@SerializedName("timezone")
private Integer timezone;
+ /**
+ * 重复日程不包含的日期列表。对重复日程修改/删除特定一天或多天,则原来的日程将会排除对应的日期。
+ */
+ @SerializedName("exclude_time_list")
+ private List excludeTimeList;
+
+ @Data
+ @Accessors(chain = true)
+ public static class ExcludeTime implements Serializable {
+ private static final long serialVersionUID = 5030527150838243359L;
+ /**
+ * 不包含的日期时间戳。
+ */
+ @SerializedName("start_time")
+ private Long startTime;
+ }
}
}
From 63131ec61f5a7a150fb6e9ea6805af57be48f38f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=B0=8F=E6=A2=81?=
<77617245+llw5181@users.noreply.github.com>
Date: Tue, 29 Oct 2024 14:44:06 +0800
Subject: [PATCH 069/385] =?UTF-8?q?:bug:=20#3394=E3=80=90=E4=BC=81?=
=?UTF-8?q?=E4=B8=9A=E5=BE=AE=E4=BF=A1=E3=80=91=E4=BF=AE=E5=A4=8D=E2=80=9C?=
=?UTF-8?q?=E5=AE=A1=E6=89=B9=E7=8A=B6=E6=80=81=E9=80=9A=E7=9F=A5=E4=BA=8B?=
=?UTF-8?q?=E4=BB=B6=E2=80=9D=E4=BC=81=E5=BE=AE=E5=9B=9E=E8=B0=83XML?=
=?UTF-8?q?=E5=BA=8F=E5=88=97=E5=8C=96=E7=9A=84bug?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../cp/bean/message/WxCpXmlApprovalInfo.java | 12 +--
.../cp/bean/message/WxCpXmlMessageTest.java | 80 +++++++++++++++++++
2 files changed, 86 insertions(+), 6 deletions(-)
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlApprovalInfo.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlApprovalInfo.java
index c9e8ffa709..7193c7cf6f 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlApprovalInfo.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlApprovalInfo.java
@@ -91,19 +91,19 @@ public class WxCpXmlApprovalInfo implements Serializable {
/**
* 审批流程信息
*/
- @XStreamImplicit(itemFieldName = "ApprovalNodes")
+ @XStreamAlias("ApprovalNodes")
private List approvalNodes;
/**
* 抄送信息,可能有多个抄送人
*/
- @XStreamImplicit(itemFieldName = "NotifyNodes")
+ @XStreamAlias("NotifyNodes")
private List notifyNodes;
/**
* 抄送人信息
*/
- @XStreamAlias("NotifyNodes")
+ @XStreamAlias("NotifyNode")
@Data
public static class NotifyNode implements Serializable {
private static final long serialVersionUID = -979255011922209018L;
@@ -141,7 +141,7 @@ public static class NotifyNode implements Serializable {
/**
* 审批流程信息,可以有多个审批节点
*/
- @XStreamAlias("ApprovalNodes")
+ @XStreamAlias("ApprovalNode")
@Data
public static class ApprovalNode implements Serializable {
private static final long serialVersionUID = -979255011922209018L;
@@ -167,7 +167,7 @@ public static class ApprovalNode implements Serializable {
/**
* 审批节点信息,当节点为标签或上级时,一个节点可能有多个分支
*/
- @XStreamImplicit(itemFieldName = "Items")
+ @XStreamAlias("Items")
private List- items;
}
@@ -175,7 +175,7 @@ public static class ApprovalNode implements Serializable {
/**
* 审批节点分支,当节点为标签或上级时,一个节点可能有多个分支
*/
- @XStreamAlias("Items")
+ @XStreamAlias("Item")
@Data
public static class Item implements Serializable {
private static final long serialVersionUID = -979255011922209018L;
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/message/WxCpXmlMessageTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/message/WxCpXmlMessageTest.java
index cb5a9b02e8..04a4c69980 100644
--- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/message/WxCpXmlMessageTest.java
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/message/WxCpXmlMessageTest.java
@@ -301,4 +301,84 @@ public void testChangeContact() {
System.out.println(XStreamTransformer.toXml(WxCpXmlMessage.class, wxCpXmlMessage));
}
+
+ /**
+ * Test open approval change.
+ */
+ public void testOpenApprovalChange() {
+ String xml = "
\n" +
+ " \n" +
+ " \n" +
+ " 1527838022 \n" +
+ " \n" +
+ " \n" +
+ " 1 \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " 1 \n" +
+ " 1527837645 \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " 1 \n" +
+ " 1 \n" +
+ " 1 \n" +
+ " \n" +
+ " - \n" +
+ "
\n" +
+ " \n" +
+ " \n" +
+ " 1 \n" +
+ " \n" +
+ " 0 \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " 1 \n" +
+ " 1 \n" +
+ " 1 \n" +
+ " \n" +
+ " - \n" +
+ "
\n" +
+ " \n" +
+ " \n" +
+ " 1 \n" +
+ " \n" +
+ " 0 \n" +
+ " \n" +
+ " - \n" +
+ "
\n" +
+ " \n" +
+ " \n" +
+ " 1 \n" +
+ " \n" +
+ " 0 \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " 0 \n" +
+ " \n" +
+ " \n";
+
+ WxCpXmlMessage wxCpXmlMessage = WxCpXmlMessage.fromXml(xml);
+ assertThat(wxCpXmlMessage).isNotNull();
+ assertThat(wxCpXmlMessage.getApprovalInfo().getApprovalNodes()).isNotEmpty();
+ assertThat(wxCpXmlMessage.getApprovalInfo().getApprovalNodes().get(0).getItems()).isNotEmpty();
+ assertThat(wxCpXmlMessage.getApprovalInfo().getApprovalNodes().get(0).getItems().get(0).getItemName()).isNotEmpty();
+ assertThat(wxCpXmlMessage.getApprovalInfo().getNotifyNodes().get(0).getItemName()).isNotEmpty();
+ }
}
From b7dc6468a407b6c77987013816a1c4058f3a0579 Mon Sep 17 00:00:00 2001
From: NotePlus <76406840@qq.com>
Date: Tue, 29 Oct 2024 08:29:17 +0000
Subject: [PATCH 070/385] =?UTF-8?q?:art:=20=E7=BB=99=E7=A7=81=E6=9C=89?=
=?UTF-8?q?=E7=B1=BB=E6=B7=BB=E5=8A=A0=E5=BA=8F=E5=88=97=E5=8C=96=E5=AE=9E?=
=?UTF-8?q?=E7=8E=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../wxpay/bean/applyment/ApplymentStateQueryResult.java | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/ApplymentStateQueryResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/ApplymentStateQueryResult.java
index 24019fb914..45d7333fbe 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/ApplymentStateQueryResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/ApplymentStateQueryResult.java
@@ -66,7 +66,7 @@ public class ApplymentStateQueryResult implements Serializable {
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
- public static class AuditDetail {
+ public static class AuditDetail implements Serializable {
/**
* 字段名
*/
@@ -84,4 +84,4 @@ public static class AuditDetail {
private String rejectReason;
}
-}
+}
\ No newline at end of file
From 95163980bef825b7e9b3335af5fcca375e491992 Mon Sep 17 00:00:00 2001
From: Binary Wang
Date: Tue, 29 Oct 2024 16:31:01 +0800
Subject: [PATCH 071/385] =?UTF-8?q?:art:=20=E5=A2=9E=E5=8A=A0serialVersion?=
=?UTF-8?q?UID?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../wxpay/bean/applyment/ApplymentStateQueryResult.java | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/ApplymentStateQueryResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/ApplymentStateQueryResult.java
index 45d7333fbe..219fdf18a4 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/ApplymentStateQueryResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/ApplymentStateQueryResult.java
@@ -67,6 +67,8 @@ public class ApplymentStateQueryResult implements Serializable {
@AllArgsConstructor
@Accessors(chain = true)
public static class AuditDetail implements Serializable {
+ private static final long serialVersionUID = 8006953382311911508L;
+
/**
* 字段名
*/
@@ -84,4 +86,4 @@ public static class AuditDetail implements Serializable {
private String rejectReason;
}
-}
\ No newline at end of file
+}
From 9c8ac1f15d292f47b478432242405e4af0755441 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=B0=8F=E6=A2=81?=
<77617245+llw5181@users.noreply.github.com>
Date: Tue, 29 Oct 2024 16:44:46 +0800
Subject: [PATCH 072/385] =?UTF-8?q?:new:=20#3397=E3=80=90=E4=BC=81?=
=?UTF-8?q?=E4=B8=9A=E5=BE=AE=E4=BF=A1=E3=80=91=E5=A2=9E=E5=8A=A0=E4=BC=9A?=
=?UTF-8?q?=E8=AE=AE=E5=AE=A4=E9=A2=84=E5=AE=9A=E7=AE=A1=E7=90=86=E7=9B=B8?=
=?UTF-8?q?=E5=85=B3=E6=8E=A5=E5=8F=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../cp/api/WxCpOaMeetingRoomService.java | 101 +++++++++++++++++-
.../impl/WxCpOaMeetingRoomServiceImpl.java | 38 ++++++-
.../cp/bean/message/WxCpXmlMessage.java | 13 +++
...WxCpOaMeetingRoomBookByMeetingRequest.java | 50 +++++++++
...xCpOaMeetingRoomBookByScheduleRequest.java | 50 +++++++++
.../WxCpOaMeetingRoomBookRequest.java | 65 +++++++++++
.../WxCpOaMeetingRoomBookResult.java | 48 +++++++++
...tingRoomBookingInfoByBookingIdRequest.java | 45 ++++++++
...etingRoomBookingInfoByBookingIdResult.java | 75 +++++++++++++
.../WxCpOaMeetingRoomBookingInfoRequest.java | 64 +++++++++++
.../WxCpOaMeetingRoomBookingInfoResult.java | 85 +++++++++++++++
.../WxCpOaMeetingRoomCancelBookRequest.java | 50 +++++++++
.../weixin/cp/constant/WxCpApiPathConsts.java | 24 +++++
.../weixin/cp/constant/WxCpConsts.java | 15 +++
.../WxCpOaMeetingRoomServiceImplTest.java | 62 ++++++++++-
15 files changed, 775 insertions(+), 10 deletions(-)
create mode 100644 weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meetingroom/WxCpOaMeetingRoomBookByMeetingRequest.java
create mode 100644 weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meetingroom/WxCpOaMeetingRoomBookByScheduleRequest.java
create mode 100644 weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meetingroom/WxCpOaMeetingRoomBookRequest.java
create mode 100644 weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meetingroom/WxCpOaMeetingRoomBookResult.java
create mode 100644 weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meetingroom/WxCpOaMeetingRoomBookingInfoByBookingIdRequest.java
create mode 100644 weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meetingroom/WxCpOaMeetingRoomBookingInfoByBookingIdResult.java
create mode 100644 weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meetingroom/WxCpOaMeetingRoomBookingInfoRequest.java
create mode 100644 weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meetingroom/WxCpOaMeetingRoomBookingInfoResult.java
create mode 100644 weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meetingroom/WxCpOaMeetingRoomCancelBookRequest.java
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOaMeetingRoomService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOaMeetingRoomService.java
index 94535fe1da..c2e6c5c872 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOaMeetingRoomService.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOaMeetingRoomService.java
@@ -1,7 +1,7 @@
package me.chanjar.weixin.cp.api;
import me.chanjar.weixin.common.error.WxErrorException;
-import me.chanjar.weixin.cp.bean.oa.meetingroom.WxCpOaMeetingRoom;
+import me.chanjar.weixin.cp.bean.oa.meetingroom.*;
import java.util.List;
@@ -59,17 +59,110 @@ public interface WxCpOaMeetingRoomService {
void editMeetingRoom(WxCpOaMeetingRoom meetingRoom) throws WxErrorException;
/**
- * 编辑会议室.
+ * 删除会议室.
*
- * 该接口用于通过应用在企业内编辑会议室。
+ * 企业可通过此接口删除指定的会议室。
* 请求方式: POST(HTTPS)
* 请求地址: https://qyapi.weixin.qq.com/cgi-bin/oa/meetingroom/del?access_token=ACCESS_TOKEN
*
* 文档地址:https://developer.work.weixin.qq.com/document/path/93619
*
*
- * @param meetingRoomId 会议室对象
+ * @param meetingRoomId 会议室ID
* @throws WxErrorException .
*/
void deleteMeetingRoom(Integer meetingRoomId) throws WxErrorException;
+
+ /**
+ * 查询会议室的预定信息.
+ *
+ * 企业可通过此接口查询相关会议室在指定时间段的预定情况,如是否已被预定,预定者的userid等信息,不支持跨天查询。
+ * 请求方式: POST(HTTPS)
+ * 请求地址: https://qyapi.weixin.qq.com/cgi-bin/oa/meetingroom/get_booking_info?access_token=ACCESS_TOKEN
+ *
+ * 文档地址:https://developer.work.weixin.qq.com/document/path/93620
+ *
+ *
+ * @param wxCpOaMeetingRoomBookingInfoRequest 会议室预定信息查询对象
+ * @throws WxErrorException .
+ */
+ WxCpOaMeetingRoomBookingInfoResult getMeetingRoomBookingInfo(WxCpOaMeetingRoomBookingInfoRequest wxCpOaMeetingRoomBookingInfoRequest) throws WxErrorException;
+
+ /**
+ * 预定会议室.
+ *
+ * 企业可通过此接口预定会议室并自动关联日程。
+ * 请求方式: POST(HTTPS)
+ * 请求地址: https://qyapi.weixin.qq.com/cgi-bin/oa/meetingroom/book?access_token=ACCESS_TOKEN
+ *
+ * 文档地址:https://developer.work.weixin.qq.com/document/path/93620
+ *
+ *
+ * @param wxCpOaMeetingRoomBookRequest 会议室预定对象
+ * @throws WxErrorException .
+ */
+ WxCpOaMeetingRoomBookResult bookingMeetingRoom(WxCpOaMeetingRoomBookRequest wxCpOaMeetingRoomBookRequest) throws WxErrorException;
+
+ /**
+ * 通过日程预定会议室.
+ *
+ * 企业可通过此接口为指定日程预定会议室,支持重复日程预定。
+ * 请求方式: POST(HTTPS)
+ * 请求地址: https://qyapi.weixin.qq.com/cgi-bin/oa/meetingroom/book_by_schedule?access_token=ACCESS_TOKEN
+ *
+ * 文档地址:https://developer.work.weixin.qq.com/document/path/93620
+ *
+ *
+ * @param wxCpOaMeetingRoomBookByScheduleRequest 会议室预定对象
+ * @throws WxErrorException .
+ */
+ WxCpOaMeetingRoomBookResult bookingMeetingRoomBySchedule(WxCpOaMeetingRoomBookByScheduleRequest wxCpOaMeetingRoomBookByScheduleRequest) throws WxErrorException;
+
+ /**
+ * 通过会议预定会议室.
+ *
+ * 企业可通过此接口为指定会议预定会议室,支持重复会议预定。
+ * 请求方式: POST(HTTPS)
+ * 请求地址: https://qyapi.weixin.qq.com/cgi-bin/oa/meetingroom/book_by_meeting?access_token=ACCESS_TOKEN
+ *
+ * 文档地址:https://developer.work.weixin.qq.com/document/path/93620
+ *
+ *
+ * @param wxCpOaMeetingRoomBookByMeetingRequest 会议室预定对象
+ * @throws WxErrorException .
+ */
+ WxCpOaMeetingRoomBookResult bookingMeetingRoomByMeeting(WxCpOaMeetingRoomBookByMeetingRequest wxCpOaMeetingRoomBookByMeetingRequest) throws WxErrorException;
+
+
+ /**
+ * 取消预定会议室.
+ *
+ * 企业可通过此接口取消会议室的预定
+ * 请求方式: POST(HTTPS)
+ * 请求地址: https://qyapi.weixin.qq.com/cgi-bin/oa/meetingroom/cancel_book?access_token=ACCESS_TOKEN
+ *
+ * 文档地址:https://developer.work.weixin.qq.com/document/path/93620
+ *
+ *
+ * @param wxCpOaMeetingRoomCancelBookRequest 取消预定会议室对象
+ * @throws WxErrorException .
+ */
+ void cancelBookMeetingRoom(WxCpOaMeetingRoomCancelBookRequest wxCpOaMeetingRoomCancelBookRequest) throws WxErrorException;
+
+
+ /**
+ * 根据会议室预定ID查询预定详情.
+ *
+ * 企业可通过此接口根据预定id查询相关会议室的预定情况
+ * 请求方式: POST(HTTPS)
+ * 请求地址: https://qyapi.weixin.qq.com/cgi-bin/oa/meetingroom/bookinfo/get?access_token=ACCESS_TOKEN
+ *
+ * 文档地址:https://developer.work.weixin.qq.com/document/path/93620
+ *
+ *
+ * @param wxCpOaMeetingRoomBookingInfoByBookingIdRequest 根据会议室预定ID查询预定详情对象
+ * @throws WxErrorException .
+ */
+ WxCpOaMeetingRoomBookingInfoByBookingIdResult getBookingInfoByBookingId(WxCpOaMeetingRoomBookingInfoByBookingIdRequest wxCpOaMeetingRoomBookingInfoByBookingIdRequest) throws WxErrorException;
+
}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpOaMeetingRoomServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpOaMeetingRoomServiceImpl.java
index f486028a0a..9c32a45235 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpOaMeetingRoomServiceImpl.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpOaMeetingRoomServiceImpl.java
@@ -7,7 +7,7 @@
import me.chanjar.weixin.common.util.json.GsonParser;
import me.chanjar.weixin.cp.api.WxCpOaMeetingRoomService;
import me.chanjar.weixin.cp.api.WxCpService;
-import me.chanjar.weixin.cp.bean.oa.meetingroom.WxCpOaMeetingRoom;
+import me.chanjar.weixin.cp.bean.oa.meetingroom.*;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
import java.util.List;
@@ -48,4 +48,40 @@ public void deleteMeetingRoom(Integer meetingRoomId) throws WxErrorException {
this.wxCpService.post(this.wxCpService.getWxCpConfigStorage().getApiUrl(MEETINGROOM_DEL),
GsonHelper.buildJsonObject("meetingroom_id", meetingRoomId));
}
+
+ @Override
+ public WxCpOaMeetingRoomBookingInfoResult getMeetingRoomBookingInfo(WxCpOaMeetingRoomBookingInfoRequest wxCpOaMeetingRoomBookingInfoRequest) throws WxErrorException {
+ String response = this.wxCpService.post(this.wxCpService.getWxCpConfigStorage().getApiUrl(MEETINGROOM_GET_BOOKING_INFO), wxCpOaMeetingRoomBookingInfoRequest);
+ return WxCpOaMeetingRoomBookingInfoResult.fromJson(response);
+ }
+
+ @Override
+ public WxCpOaMeetingRoomBookResult bookingMeetingRoom(WxCpOaMeetingRoomBookRequest wxCpOaMeetingRoomBookRequest) throws WxErrorException {
+ String response = this.wxCpService.post(this.wxCpService.getWxCpConfigStorage().getApiUrl(MEETINGROOM_BOOK), wxCpOaMeetingRoomBookRequest);
+ return WxCpOaMeetingRoomBookResult.fromJson(response);
+ }
+
+ @Override
+ public WxCpOaMeetingRoomBookResult bookingMeetingRoomBySchedule(WxCpOaMeetingRoomBookByScheduleRequest wxCpOaMeetingRoomBookByScheduleRequest) throws WxErrorException {
+ String response = this.wxCpService.post(this.wxCpService.getWxCpConfigStorage().getApiUrl(MEETINGROOM_BOOK_BY_SCHEDULE), wxCpOaMeetingRoomBookByScheduleRequest);
+ return WxCpOaMeetingRoomBookResult.fromJson(response);
+ }
+
+ @Override
+ public WxCpOaMeetingRoomBookResult bookingMeetingRoomByMeeting(WxCpOaMeetingRoomBookByMeetingRequest wxCpOaMeetingRoomBookByMeetingRequest) throws WxErrorException {
+ String response = this.wxCpService.post(this.wxCpService.getWxCpConfigStorage().getApiUrl(MEETINGROOM_BOOK_BY_MEETING), wxCpOaMeetingRoomBookByMeetingRequest);
+ return WxCpOaMeetingRoomBookResult.fromJson(response);
+ }
+
+ @Override
+ public void cancelBookMeetingRoom(WxCpOaMeetingRoomCancelBookRequest wxCpOaMeetingRoomCancelBookRequest) throws WxErrorException {
+ this.wxCpService.post(this.wxCpService.getWxCpConfigStorage().getApiUrl(MEETINGROOM_CANCEL_BOOK), wxCpOaMeetingRoomCancelBookRequest);
+
+ }
+
+ @Override
+ public WxCpOaMeetingRoomBookingInfoByBookingIdResult getBookingInfoByBookingId(WxCpOaMeetingRoomBookingInfoByBookingIdRequest wxCpOaMeetingRoomBookingInfoByBookingIdRequest) throws WxErrorException {
+ String response = this.wxCpService.post(this.wxCpService.getWxCpConfigStorage().getApiUrl(MEETINGROOM_BOOKINFO_GET), wxCpOaMeetingRoomBookingInfoByBookingIdRequest);
+ return WxCpOaMeetingRoomBookingInfoByBookingIdResult.fromJson(response);
+ }
}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlMessage.java
index 11a1aa62a8..f6d2c3f2e8 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlMessage.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlMessage.java
@@ -386,6 +386,19 @@ public class WxCpXmlMessage implements Serializable {
@XStreamConverter(value = XStreamCDataConverter.class)
private String calId;
+ /**
+ * 会议室ID.
+ */
+ @XStreamAlias("MeetingRoomId")
+ private String meetingRoomId;
+
+ /**
+ * 会议室预定id,可根据该ID查询具体的会议预定情况
+ */
+ @XStreamAlias("BookingId")
+ @XStreamConverter(value = XStreamCDataConverter.class)
+ private String bookingId;
+
/**
* 扩展属性.
*/
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meetingroom/WxCpOaMeetingRoomBookByMeetingRequest.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meetingroom/WxCpOaMeetingRoomBookByMeetingRequest.java
new file mode 100644
index 0000000000..dd0702deca
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meetingroom/WxCpOaMeetingRoomBookByMeetingRequest.java
@@ -0,0 +1,50 @@
+package me.chanjar.weixin.cp.bean.oa.meetingroom;
+
+
+import com.google.gson.annotations.SerializedName;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import me.chanjar.weixin.common.bean.ToJson;
+import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
+
+import java.io.Serializable;
+
+/**
+ * 通过会议预定会议室
+ *
+ * @author 小梁
+ * @version 1.0 Create by 2024/10/28
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@Accessors(chain = true)
+public class WxCpOaMeetingRoomBookByMeetingRequest implements Serializable, ToJson {
+ private static final long serialVersionUID = 2825289798463742531L;
+ /**
+ * 会议室Id
+ */
+ @SerializedName("meetingroom_id")
+ private Integer meetingroomId;
+ /**
+ * 会议id,仅可使用同应用创建的会议
+ */
+ @SerializedName("meetingid")
+ private String meetingid;
+ /**
+ * 预定人的userid
+ */
+ @SerializedName("booker")
+ private String booker;
+
+
+ @Override
+ public String toJson() {
+ return WxCpGsonBuilder.create().toJson(this);
+ }
+
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meetingroom/WxCpOaMeetingRoomBookByScheduleRequest.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meetingroom/WxCpOaMeetingRoomBookByScheduleRequest.java
new file mode 100644
index 0000000000..2949955470
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meetingroom/WxCpOaMeetingRoomBookByScheduleRequest.java
@@ -0,0 +1,50 @@
+package me.chanjar.weixin.cp.bean.oa.meetingroom;
+
+
+import com.google.gson.annotations.SerializedName;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import me.chanjar.weixin.common.bean.ToJson;
+import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
+
+import java.io.Serializable;
+
+/**
+ * 通过日程预定会议室
+ *
+ * @author 小梁
+ * @version 1.0 Create by 2024/10/28
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@Accessors(chain = true)
+public class WxCpOaMeetingRoomBookByScheduleRequest implements Serializable, ToJson {
+ private static final long serialVersionUID = 2825289798463742532L;
+ /**
+ * 会议室Id
+ */
+ @SerializedName("meetingroom_id")
+ private Integer meetingroomId;
+ /**
+ * 日程id,仅可使用同应用创建的日程
+ */
+ @SerializedName("schedule_id")
+ private String schedule_id;
+ /**
+ * 预定人的userid
+ */
+ @SerializedName("booker")
+ private String booker;
+
+
+ @Override
+ public String toJson() {
+ return WxCpGsonBuilder.create().toJson(this);
+ }
+
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meetingroom/WxCpOaMeetingRoomBookRequest.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meetingroom/WxCpOaMeetingRoomBookRequest.java
new file mode 100644
index 0000000000..09ca1e9652
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meetingroom/WxCpOaMeetingRoomBookRequest.java
@@ -0,0 +1,65 @@
+package me.chanjar.weixin.cp.bean.oa.meetingroom;
+
+
+import com.google.gson.annotations.SerializedName;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import me.chanjar.weixin.common.bean.ToJson;
+import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 预定会议室的请求类
+ *
+ * @author 小梁
+ * @version 1.0 Create by 2024/10/28
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@Accessors(chain = true)
+public class WxCpOaMeetingRoomBookRequest implements Serializable, ToJson {
+ private static final long serialVersionUID = 2825289798463742536L;
+ /**
+ * 会议室Id
+ */
+ @SerializedName("meetingroom_id")
+ private Integer meetingroomId;
+ /**
+ * 预定开始时间
+ */
+ @SerializedName("start_time")
+ private Integer startTime;
+ /**
+ * 预定结束时间
+ */
+ @SerializedName("end_time")
+ private Integer endTime;
+ /**
+ * 会议主题
+ */
+ @SerializedName("subject")
+ private String subject;
+ /**
+ * 预定人的userid
+ */
+ @SerializedName("booker")
+ private String booker;
+ /**
+ * 参与人的userid列表
+ */
+ @SerializedName("attendees")
+ private List attendees;
+
+ @Override
+ public String toJson() {
+ return WxCpGsonBuilder.create().toJson(this);
+ }
+
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meetingroom/WxCpOaMeetingRoomBookResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meetingroom/WxCpOaMeetingRoomBookResult.java
new file mode 100644
index 0000000000..16cf32fa5c
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meetingroom/WxCpOaMeetingRoomBookResult.java
@@ -0,0 +1,48 @@
+package me.chanjar.weixin.cp.bean.oa.meetingroom;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import me.chanjar.weixin.cp.bean.WxCpBaseResp;
+import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 预定会议室的返回结果类
+ *
+ * @author 小梁
+ * @version 1.0 Create by 2024/10/28
+ */
+@Data
+public class WxCpOaMeetingRoomBookResult extends WxCpBaseResp implements Serializable {
+ private static final long serialVersionUID = -4993287594652231098L;
+
+ @Override
+ public String toString() {
+ return WxCpGsonBuilder.create().toJson(this);
+ }
+
+ public static WxCpOaMeetingRoomBookResult fromJson(String json) {
+ return WxCpGsonBuilder.create().fromJson(json, WxCpOaMeetingRoomBookResult.class);
+ }
+
+ /**
+ * 会议室的预定id
+ */
+ @SerializedName("booking_id")
+ private String booking_id;
+ /**
+ * 会议关联日程的id
+ */
+ @SerializedName("schedule_id")
+ private String schedule_id;
+ /**
+ * 通过会议预定会议室 和 通过日程预定会议室 接口返回
+ *
+ * 会议室冲突日期列表,为当天0点的时间戳;使用重复会议预定会议室,部分日期与会议室预定情况冲突时返回
+ */
+ @SerializedName("conflict_date")
+ private List conflict_date;
+
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meetingroom/WxCpOaMeetingRoomBookingInfoByBookingIdRequest.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meetingroom/WxCpOaMeetingRoomBookingInfoByBookingIdRequest.java
new file mode 100644
index 0000000000..4e5351c490
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meetingroom/WxCpOaMeetingRoomBookingInfoByBookingIdRequest.java
@@ -0,0 +1,45 @@
+package me.chanjar.weixin.cp.bean.oa.meetingroom;
+
+
+import com.google.gson.annotations.SerializedName;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import me.chanjar.weixin.common.bean.ToJson;
+import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
+
+import java.io.Serializable;
+
+/**
+ * 根据会议室预定ID查询预定详情请求类
+ *
+ * @author 小梁
+ * @version 1.0 Create by 2024/10/28
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@Accessors(chain = true)
+public class WxCpOaMeetingRoomBookingInfoByBookingIdRequest implements Serializable, ToJson {
+ private static final long serialVersionUID = 2825289798463742533L;
+ /**
+ * 会议室id
+ */
+ @SerializedName("meetingroom_id")
+ private Integer meetingroom_id;
+ /**
+ * 会议室的预定id
+ */
+ @SerializedName("booking_id")
+ private String booking_id;
+
+
+ @Override
+ public String toJson() {
+ return WxCpGsonBuilder.create().toJson(this);
+ }
+
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meetingroom/WxCpOaMeetingRoomBookingInfoByBookingIdResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meetingroom/WxCpOaMeetingRoomBookingInfoByBookingIdResult.java
new file mode 100644
index 0000000000..7f9788f79c
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meetingroom/WxCpOaMeetingRoomBookingInfoByBookingIdResult.java
@@ -0,0 +1,75 @@
+package me.chanjar.weixin.cp.bean.oa.meetingroom;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import me.chanjar.weixin.cp.bean.WxCpBaseResp;
+import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
+
+import java.io.Serializable;
+
+/**
+ * 根据会议室预定ID查询预定详情返回结果类
+ *
+ * @author 小梁
+ * @version 1.0 Create by 2024/10/28
+ */
+@Data
+public class WxCpOaMeetingRoomBookingInfoByBookingIdResult extends WxCpBaseResp implements Serializable {
+ private static final long serialVersionUID = -4993287594652231097L;
+
+ @Override
+ public String toString() {
+ return WxCpGsonBuilder.create().toJson(this);
+ }
+
+ public static WxCpOaMeetingRoomBookingInfoByBookingIdResult fromJson(String json) {
+ return WxCpGsonBuilder.create().fromJson(json, WxCpOaMeetingRoomBookingInfoByBookingIdResult.class);
+ }
+
+ /**
+ * 会议室ID
+ */
+ @SerializedName("meetingroom_id")
+ private Integer meetingroomId;
+ /**
+ * 该会议室查询时间段内的预定情况
+ */
+ @SerializedName("schedule")
+ private Schedule schedule;
+
+
+ @Data
+ public static class Schedule {
+ /**
+ * 会议室的预定id
+ */
+ @SerializedName("booking_id")
+ private String bookingId;
+ /**
+ * 会议关联日程的id,若会议室已取消预定(未保留日历),则schedule_id将无法再获取到日程详情
+ */
+ @SerializedName("schedule_id")
+ private String scheduleId;
+ /**
+ * 开始时间的时间戳
+ */
+ @SerializedName("start_time")
+ private Integer startTime;
+ /**
+ * 结束时间的时间戳
+ */
+ @SerializedName("end_time")
+ private Integer endTime;
+ /**
+ * 预定人的userid
+ */
+ @SerializedName("booker")
+ private String booker;
+ /**
+ * 会议室的预定状态,0:已预定 、2:申请中、3:审批中
+ */
+ @SerializedName("status")
+ private Integer status;
+ }
+
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meetingroom/WxCpOaMeetingRoomBookingInfoRequest.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meetingroom/WxCpOaMeetingRoomBookingInfoRequest.java
new file mode 100644
index 0000000000..b1c4c9e326
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meetingroom/WxCpOaMeetingRoomBookingInfoRequest.java
@@ -0,0 +1,64 @@
+package me.chanjar.weixin.cp.bean.oa.meetingroom;
+
+
+import com.google.gson.annotations.SerializedName;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import me.chanjar.weixin.common.bean.ToJson;
+import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
+
+import java.io.Serializable;
+
+/**
+ * 查询会议室的预定信息的请求类
+ *
+ * @author 小梁
+ * @version 1.0 Create by 2024/10/28
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@Accessors(chain = true)
+public class WxCpOaMeetingRoomBookingInfoRequest implements Serializable, ToJson {
+ private static final long serialVersionUID = 2825289798463742534L;
+ /**
+ * 会议室Id
+ */
+ @SerializedName("meetingroom_id")
+ private Integer meetingroomId;
+ /**
+ * 查询预定的起始时间,默认为当前时间
+ */
+ @SerializedName("start_time")
+ private Integer startTime;
+ /**
+ * 查询预定的结束时间, 默认为明日0时
+ */
+ @SerializedName("end_time")
+ private Integer endTime;
+ /**
+ * 会议室所在城市
+ */
+ @SerializedName("city")
+ private String city;
+ /**
+ * 会议室所在楼宇
+ */
+ @SerializedName("building")
+ private String building;
+ /**
+ * 会议室所在楼层
+ */
+ @SerializedName("floor")
+ private String floor;
+
+ @Override
+ public String toJson() {
+ return WxCpGsonBuilder.create().toJson(this);
+ }
+
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meetingroom/WxCpOaMeetingRoomBookingInfoResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meetingroom/WxCpOaMeetingRoomBookingInfoResult.java
new file mode 100644
index 0000000000..31f21cabd9
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meetingroom/WxCpOaMeetingRoomBookingInfoResult.java
@@ -0,0 +1,85 @@
+package me.chanjar.weixin.cp.bean.oa.meetingroom;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import me.chanjar.weixin.cp.bean.WxCpBaseResp;
+import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 查询会议室的预定信息的返回结果类
+ *
+ * @author 小梁
+ * @version 1.0 Create by 2024/10/28
+ */
+@Data
+public class WxCpOaMeetingRoomBookingInfoResult extends WxCpBaseResp implements Serializable {
+ private static final long serialVersionUID = -4993287594652231095L;
+
+ @Override
+ public String toString() {
+ return WxCpGsonBuilder.create().toJson(this);
+ }
+
+ public static WxCpOaMeetingRoomBookingInfoResult fromJson(String json) {
+ return WxCpGsonBuilder.create().fromJson(json, WxCpOaMeetingRoomBookingInfoResult.class);
+ }
+
+ /**
+ * 会议室预订信息列表
+ */
+ @SerializedName("booking_list")
+ private List bookingList;
+
+ @Data
+ public static class Booking {
+ /**
+ * 会议室ID
+ */
+ @SerializedName("meetingroom_id")
+ private Integer meetingroomId;
+ /**
+ * 该会议室查询时间段内的预定情况
+ */
+ @SerializedName("schedule")
+ private List schedule;
+
+ }
+
+ @Data
+ public static class Schedule {
+ /**
+ * 会议室的预定id
+ */
+ @SerializedName("booking_id")
+ private String bookingId;
+ /**
+ * 会议关联日程的id,若会议室已取消预定(未保留日历),则schedule_id将无法再获取到日程详情
+ */
+ @SerializedName("schedule_id")
+ private String scheduleId;
+ /**
+ * 开始时间的时间戳
+ */
+ @SerializedName("start_time")
+ private Integer startTime;
+ /**
+ * 结束时间的时间戳
+ */
+ @SerializedName("end_time")
+ private Integer endTime;
+ /**
+ * 预定人的userid
+ */
+ @SerializedName("booker")
+ private String booker;
+ /**
+ * 会议室的预定状态,0:已预定 、2:申请中、3:审批中
+ */
+ @SerializedName("status")
+ private Integer status;
+ }
+
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meetingroom/WxCpOaMeetingRoomCancelBookRequest.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meetingroom/WxCpOaMeetingRoomCancelBookRequest.java
new file mode 100644
index 0000000000..18f2dfa4b1
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/meetingroom/WxCpOaMeetingRoomCancelBookRequest.java
@@ -0,0 +1,50 @@
+package me.chanjar.weixin.cp.bean.oa.meetingroom;
+
+
+import com.google.gson.annotations.SerializedName;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import me.chanjar.weixin.common.bean.ToJson;
+import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
+
+import java.io.Serializable;
+
+/**
+ * 取消预定会议室请求类
+ *
+ * @author 小梁
+ * @version 1.0 Create by 2024/10/28
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@Accessors(chain = true)
+public class WxCpOaMeetingRoomCancelBookRequest implements Serializable, ToJson {
+ private static final long serialVersionUID = 2825289798463742539L;
+ /**
+ * 会议室的预定id
+ */
+ @SerializedName("booking_id")
+ private String booking_id;
+ /**
+ * 是否保留日程,0-同步删除 1-保留,仅对非重复日程有效
+ */
+ @SerializedName("keep_schedule")
+ private Integer keep_schedule;
+ /**
+ * 对于重复日程,如果不填写此参数,表示取消所有重复预定;如果填写,则表示取消对应日期当天的会议室预定
+ */
+ @SerializedName("cancel_date")
+ private Integer cancel_date;
+
+
+ @Override
+ public String toJson() {
+ return WxCpGsonBuilder.create().toJson(this);
+ }
+
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpApiPathConsts.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpApiPathConsts.java
index c2f8a93100..d90bda6ccc 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpApiPathConsts.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpApiPathConsts.java
@@ -423,6 +423,30 @@ interface Oa {
* The constant MEETINGROOM_DEL.
*/
String MEETINGROOM_DEL = "/cgi-bin/oa/meetingroom/del";
+ /**
+ * The constant MEETINGROOM_GET_BOOKING_INFO.
+ */
+ String MEETINGROOM_GET_BOOKING_INFO = "/cgi-bin/oa/meetingroom/get_booking_info";
+ /**
+ * The constant MEETINGROOM_BOOK.
+ */
+ String MEETINGROOM_BOOK = "/cgi-bin/oa/meetingroom/book";
+ /**
+ * The constant MEETINGROOM_BOOK_BY_SCHEDULE.
+ */
+ String MEETINGROOM_BOOK_BY_SCHEDULE = "/cgi-bin/oa/meetingroom/book_by_schedule";
+ /**
+ * The constant MEETINGROOM_BOOK_BY_MEETING.
+ */
+ String MEETINGROOM_BOOK_BY_MEETING = "/cgi-bin/oa/meetingroom//book_by_meeting";
+ /**
+ * The constant MEETINGROOM_CANCEL_BOOK.
+ */
+ String MEETINGROOM_CANCEL_BOOK = "/cgi-bin/oa/meetingroom/cancel_book";
+ /**
+ * The constant MEETINGROOM_BOOKINFO_GET.
+ */
+ String MEETINGROOM_BOOKINFO_GET = "/cgi-bin/oa/meetingroom/bookinfo/get";
/**
* 微盘
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpConsts.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpConsts.java
index 8101745e96..ba67b86d3f 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpConsts.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpConsts.java
@@ -144,6 +144,21 @@ public static class EventType {
*/
public static final String DELETE_SCHEDULE = "delete_schedule";
+ /**
+ * 日程回执事件
+ */
+ public static final String RESPOND_SCHEDULE = "respond_schedule";
+
+ /**
+ * 会议室预定事件.
+ */
+ public static final String BOOK_MEETING_ROOM = "book_meeting_room";
+
+ /**
+ * 会议室取消事件.
+ */
+ public static final String CANCEL_MEETING_ROOM = "cancel_meeting_room";
+
/**
* 家校通讯录事件
*/
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpOaMeetingRoomServiceImplTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpOaMeetingRoomServiceImplTest.java
index c6ed846e8c..50bc2ea11b 100644
--- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpOaMeetingRoomServiceImplTest.java
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpOaMeetingRoomServiceImplTest.java
@@ -4,7 +4,7 @@
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.api.ApiTestModule;
import me.chanjar.weixin.cp.api.WxCpService;
-import me.chanjar.weixin.cp.bean.oa.meetingroom.WxCpOaMeetingRoom;
+import me.chanjar.weixin.cp.bean.oa.meetingroom.*;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;
@@ -73,10 +73,10 @@ public void testUpdate() throws WxErrorException {
public void testGet() throws WxErrorException {
final List meetingRooms =
this.wxService.getOaMeetingRoomService().listMeetingRoom(WxCpOaMeetingRoom.builder()
- .building("腾讯大厦")
- .city("深圳")
- .equipment(Arrays.asList(1, 2))
- .build());
+ .building("腾讯大厦")
+ .city("深圳")
+ .equipment(Arrays.asList(1, 2))
+ .build());
assertThat(meetingRooms).isNotEmpty();
}
@@ -90,4 +90,56 @@ public void testDelete() throws WxErrorException {
Integer calId = 1;
this.wxService.getOaMeetingRoomService().deleteMeetingRoom(calId);
}
+
+ @Test
+ public void testGetMeetingRoomBookingInfo() throws WxErrorException {
+ final WxCpOaMeetingRoomBookingInfoResult meetingRoomBookingInfo = this.wxService.getOaMeetingRoomService().getMeetingRoomBookingInfo(WxCpOaMeetingRoomBookingInfoRequest.builder()
+ .meetingroomId(19)
+ .build());
+ System.out.println(meetingRoomBookingInfo);
+ assertThat(meetingRoomBookingInfo).isNotNull();
+ }
+
+ @Test
+ public void testBookingMeetingRoom() throws WxErrorException {
+ WxCpOaMeetingRoomBookResult wxCpOaMeetingRoomBookResult = this.wxService.getOaMeetingRoomService().bookingMeetingRoom(WxCpOaMeetingRoomBookRequest.builder().subject("测试会议").meetingroomId(19).startTime(1730118646).endTime(1730120446).booker("LiangLinWei").attendees(Arrays.asList("LiangLinWei", "ZhaoYuCheng")).build());
+ System.out.println(wxCpOaMeetingRoomBookResult);
+ assertThat(wxCpOaMeetingRoomBookResult).isNotNull();
+ }
+
+ @Test
+ public void testBookingMeetingRoomBySchedule() throws WxErrorException {
+ WxCpOaMeetingRoomBookResult wxCpOaMeetingRoomBookResult = this.wxService.getOaMeetingRoomService().bookingMeetingRoomBySchedule(WxCpOaMeetingRoomBookByScheduleRequest.builder()
+ .booker("LiangLinWei")
+ .meetingroomId(19)
+ .schedule_id("bkWChLPrv9YpPRLeeYU-uFwl9BQX3G2_rQYQRg1W1uR3A")
+ .build());
+ System.out.println(wxCpOaMeetingRoomBookResult);
+ assertThat(wxCpOaMeetingRoomBookResult).isNotNull();
+ }
+
+ @Test
+ public void testBookingMeetingRoomByMeeting() throws WxErrorException {
+ WxCpOaMeetingRoomBookResult wxCpOaMeetingRoomBookResult = this.wxService.getOaMeetingRoomService().bookingMeetingRoomByMeeting(WxCpOaMeetingRoomBookByMeetingRequest.builder()
+ .booker("LiangLinWei")
+ .meetingroomId(19)
+ .meetingid("bkWChLPrv9YpPRLeeYU-uFwl9BQX3G2_rQYQRg1W1uR3A")
+ .build());
+ System.out.println(wxCpOaMeetingRoomBookResult);
+ assertThat(wxCpOaMeetingRoomBookResult).isNotNull();
+ }
+
+ @Test
+ public void testCancelBookMeetingRoom() throws WxErrorException {
+ this.wxService.getOaMeetingRoomService().cancelBookMeetingRoom(WxCpOaMeetingRoomCancelBookRequest.builder().booking_id("bkWChLPrv9YpPRLeeYU-uFwl9BQX3G2_rQYQRg1W1uR3A").build());
+ }
+
+ @Test
+ public void testGetBookingInfoByBookingId() throws WxErrorException {
+ WxCpOaMeetingRoomBookingInfoByBookingIdResult bookingInfoByBookingId = this.wxService.getOaMeetingRoomService().getBookingInfoByBookingId(WxCpOaMeetingRoomBookingInfoByBookingIdRequest.builder().meetingroom_id(19).booking_id("bkWChLPrv9YpPRLeeYU-uFwl9BQX3G2_rQYQRg1W1uR3A").build());
+ System.out.println(bookingInfoByBookingId);
+ assertThat(bookingInfoByBookingId).isNotNull();
+ }
+
+
}
From 3e25e409b017d9339969ee0dba1419ce53c0ab75 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=B0=8F=E6=A2=81?=
<77617245+llw5181@users.noreply.github.com>
Date: Wed, 30 Oct 2024 11:27:48 +0800
Subject: [PATCH 073/385] =?UTF-8?q?:art:=20=E3=80=90=E4=BC=81=E4=B8=9A?=
=?UTF-8?q?=E5=BE=AE=E4=BF=A1=E3=80=91=E6=B6=88=E6=81=AF=E6=8E=A8=E9=80=81?=
=?UTF-8?q?=E9=87=8C=E6=B7=BB=E5=8A=A0=E6=9B=B4=E5=A4=9A=E7=9A=84=E4=BA=8B?=
=?UTF-8?q?=E4=BB=B6=E7=B1=BB=E5=9E=8B=E5=B8=B8=E9=87=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../weixin/cp/constant/WxCpConsts.java | 25 +++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpConsts.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpConsts.java
index ba67b86d3f..f0c7601fe0 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpConsts.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpConsts.java
@@ -93,6 +93,31 @@ public static class EventType {
*/
public static final String TASKCARD_CLICK = "taskcard_click";
+ /**
+ * 企业互联共享应用事件回调.
+ */
+ public static final String SHARE_AGENT_CHANGE = "share_agent_change";
+
+ /**
+ * 上下游共享应用事件回调.
+ */
+ public static final String SHARE_CHAIN_CHANGE = "share_chain_change";
+
+ /**
+ * 通用模板卡片右上角菜单事件推送.
+ */
+ public static final String TEMPLATE_CARD_MENU_EVENT = "template_card_menu_event";
+
+ /**
+ * 长期未使用应用临时停用事件.
+ */
+ public static final String CLOSE_INACTIVE_AGENT = "close_inactive_agent";
+
+ /**
+ * 长期未使用应用重新启用事件.
+ */
+ public static final String REOPEN_INACTIVE_AGENT = "reopen_inactive_agent";
+
/**
* 企业成员添加外部联系人事件推送 & 会话存档客户同意进行聊天内容存档事件回调事件
*/
From bf7356e808d9f0555dc8dc4a1c028778bf2056dd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=B0=8F=E6=A2=81?=
<77617245+llw5181@users.noreply.github.com>
Date: Wed, 30 Oct 2024 13:04:01 +0800
Subject: [PATCH 074/385] =?UTF-8?q?:art:=20#3395=E3=80=90=E4=BC=81?=
=?UTF-8?q?=E4=B8=9A=E5=BE=AE=E4=BF=A1=E3=80=91=E5=A2=9E=E5=8A=A0"?=
=?UTF-8?q?=E6=A8=A1=E6=9D=BF=E5=8D=A1=E7=89=87=E4=BA=8B=E4=BB=B6=E6=8E=A8?=
=?UTF-8?q?=E9=80=81"=E4=BA=8B=E4=BB=B6=E7=9A=84=E7=9B=B8=E5=85=B3?=
=?UTF-8?q?=E5=B1=9E=E6=80=A7?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../cp/bean/message/WxCpXmlMessage.java | 28 +++++++++++++
.../weixin/cp/constant/WxCpConsts.java | 5 +++
.../cp/util/xml/XStreamTransformer.java | 5 +++
.../cp/bean/message/WxCpXmlMessageTest.java | 40 +++++++++++++++++++
4 files changed, 78 insertions(+)
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlMessage.java
index f6d2c3f2e8..fb4213f504 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlMessage.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlMessage.java
@@ -187,6 +187,17 @@ public class WxCpXmlMessage implements Serializable {
@XStreamConverter(value = XStreamCDataConverter.class)
private String taskId;
+ @XStreamAlias("CardType")
+ @XStreamConverter(value = XStreamCDataConverter.class)
+ private String cardType;
+
+ @XStreamAlias("ResponseCode")
+ @XStreamConverter(value = XStreamCDataConverter.class)
+ private String responseCode;
+
+ @XStreamAlias("SelectedItems")
+ private List selectedItems;
+
/**
* 微信客服
* 调用拉取消息接口时,需要传此token,用于校验请求的合法性
@@ -750,4 +761,21 @@ public static class SendLocationInfo implements Serializable {
}
+
+ /**
+ * The type selected Items.
+ */
+ @Data
+ @XStreamAlias("SelectedItem")
+ public static class SelectedItem implements Serializable {
+ private static final long serialVersionUID = 6319921121637597406L;
+
+ @XStreamAlias("QuestionKey")
+ @XStreamConverter(value = XStreamCDataConverter.class)
+ private String questionKey;
+
+ @XStreamAlias(value = "OptionIds")
+ private List optionIds;
+ }
+
}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpConsts.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpConsts.java
index f0c7601fe0..606dcea6d2 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpConsts.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpConsts.java
@@ -48,6 +48,11 @@ public static class EventType {
*/
public static final String CHANGE_CONTACT = "change_contact";
+ /**
+ * 企业微信模板卡片事件推送
+ */
+ public static final String TEMPLATE_CARD_EVENT = "template_card_event";
+
/**
* 点击菜单拉取消息的事件推送.
*/
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/xml/XStreamTransformer.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/xml/XStreamTransformer.java
index 8f540020a1..c4753befd2 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/xml/XStreamTransformer.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/xml/XStreamTransformer.java
@@ -93,6 +93,11 @@ private static XStream configWxCpXmlMessage() {
xstream.processAnnotations(WxCpXmlMessage.SendPicsInfo.class);
xstream.processAnnotations(WxCpXmlMessage.SendPicsInfo.Item.class);
xstream.processAnnotations(WxCpXmlMessage.SendLocationInfo.class);
+ xstream.processAnnotations(WxCpXmlMessage.SelectedItem.class);
+ // 显式允许 String 类
+ xstream.allowTypes(new Class[]{String.class});
+ // 模板卡片事件推送独属
+ xstream.alias("OptionId",String.class);
return xstream;
}
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/message/WxCpXmlMessageTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/message/WxCpXmlMessageTest.java
index 04a4c69980..a760a17ff6 100644
--- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/message/WxCpXmlMessageTest.java
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/message/WxCpXmlMessageTest.java
@@ -302,6 +302,46 @@ public void testChangeContact() {
System.out.println(XStreamTransformer.toXml(WxCpXmlMessage.class, wxCpXmlMessage));
}
+ /**
+ * Test template card event.
+ */
+ public void testTemplateCardEvent() {
+ String xml = "\n" +
+ " \n" +
+ " \n" +
+ "123456789 \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ "1 \n" +
+ "\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " ";
+
+ WxCpXmlMessage wxCpXmlMessage = WxCpXmlMessage.fromXml(xml);
+ assertThat(wxCpXmlMessage).isNotNull();
+ assertThat(wxCpXmlMessage.getSelectedItems()).isNotEmpty();
+ assertThat(wxCpXmlMessage.getSelectedItems().get(0).getQuestionKey()).isNotEmpty();
+ assertThat(wxCpXmlMessage.getSelectedItems().get(0).getOptionIds().get(0)).isNotEmpty();
+ }
+
/**
* Test open approval change.
*/
From c483d6f1abfa09ac54d79971183f56fc4324380a Mon Sep 17 00:00:00 2001
From: leung
Date: Thu, 31 Oct 2024 15:55:00 +0800
Subject: [PATCH 075/385] =?UTF-8?q?:bug:=20#3392=E3=80=90=E5=BE=AE?=
=?UTF-8?q?=E4=BF=A1=E6=94=AF=E4=BB=98=E3=80=91=E4=BF=AE=E5=A4=8DV3?=
=?UTF-8?q?=E5=AE=A2=E6=88=B7=E7=AB=AF=E5=88=9D=E5=A7=8B=E5=8C=96=E6=97=B6?=
=?UTF-8?q?p12=E8=AF=81=E4=B9=A6=E5=8A=A0=E8=BD=BD=E5=A4=B1=E8=B4=A5?=
=?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../binarywang/wxpay/config/WxPayConfig.java | 29 +++++--------------
1 file changed, 8 insertions(+), 21 deletions(-)
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 83a4b042c1..3bc868d072 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
@@ -270,6 +270,7 @@ public CloseableHttpClient initApiV3HttpClient() throws WxPayException {
if (objects != null) {
merchantPrivateKey = (PrivateKey) objects[0];
certificate = (X509Certificate) objects[1];
+ this.certSerialNo = certificate.getSerialNumber().toString(16).toUpperCase();
}
try {
if (merchantPrivateKey == null) {
@@ -405,30 +406,12 @@ private InputStream loadConfigInputStream(String configPath) throws WxPayExcepti
}
}
- /**
- * 从配置路径 加载p12证书文件流
- *
- * @return 文件流
- */
- private InputStream loadP12InputStream() {
- try (InputStream inputStream = this.loadConfigInputStream(this.keyString, this.getKeyPath(),
- this.keyContent, "p12证书");) {
- return inputStream;
- } catch (Exception e) {
- return null;
- }
- }
-
/**
* 分解p12证书文件
*
* @return
*/
private Object[] p12ToPem() {
- InputStream inputStream = this.loadP12InputStream();
- if (inputStream == null) {
- return null;
- }
String key = getMchId();
if (StringUtils.isBlank(key)) {
return null;
@@ -436,7 +419,11 @@ private Object[] p12ToPem() {
// 分解p12证书文件
PrivateKey privateKey = null;
X509Certificate x509Certificate = null;
- try {
+ try (InputStream inputStream = this.loadConfigInputStream(this.keyString, this.getKeyPath(),
+ this.keyContent, "p12证书");){
+ if (inputStream == null) {
+ return null;
+ }
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(inputStream, key.toCharArray());
@@ -446,8 +433,8 @@ private Object[] p12ToPem() {
Certificate certificate = keyStore.getCertificate(alias);
x509Certificate = (X509Certificate) certificate;
return new Object[]{privateKey, x509Certificate};
- } catch (Exception ignored) {
-
+ } catch (Exception e) {
+ e.printStackTrace();
}
return null;
From 12b83affe4f22755a5407148063af4fd31c99707 Mon Sep 17 00:00:00 2001
From: Binary Wang
Date: Thu, 31 Oct 2024 16:03:55 +0800
Subject: [PATCH 076/385] =?UTF-8?q?:art:=20=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
---
.../binarywang/wxpay/config/WxPayConfig.java | 26 ++++++++-----------
1 file changed, 11 insertions(+), 15 deletions(-)
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 3bc868d072..932fa323e0 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
@@ -10,6 +10,7 @@
import lombok.EqualsAndHashCode;
import lombok.SneakyThrows;
import lombok.ToString;
+import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RegExUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.impl.client.CloseableHttpClient;
@@ -32,6 +33,7 @@
* @author Binary Wang (...)
*/
@Data
+@Slf4j
@ToString(exclude = "verifier")
@EqualsAndHashCode(exclude = "verifier")
public class WxPayConfig {
@@ -253,7 +255,7 @@ public SSLContext initSSLContext() throws WxPayException {
/**
* 初始化api v3请求头 自动签名验签
- * 方法参照微信官方https://github.com/wechatpay-apiv3/wechatpay-apache-httpclient
+ * 方法参照 微信支付官方api项目
*
* @return org.apache.http.impl.client.CloseableHttpClient
* @author doger.wang
@@ -397,8 +399,8 @@ private InputStream loadConfigInputStream(String configPath) throws WxPayExcepti
if (!file.exists()) {
throw new WxPayException(fileNotFoundMsg);
}
-
-// return Files.newInputStream(file.toPath());
+ //使用Files.newInputStream打开公私钥文件,会存在无法释放句柄的问题
+ //return Files.newInputStream(file.toPath());
return new FileInputStream(file);
} catch (IOException e) {
throw new WxPayException(fileHasProblemMsg, e);
@@ -408,36 +410,30 @@ private InputStream loadConfigInputStream(String configPath) throws WxPayExcepti
/**
* 分解p12证书文件
- *
- * @return
*/
private Object[] p12ToPem() {
String key = getMchId();
if (StringUtils.isBlank(key)) {
return null;
}
+
// 分解p12证书文件
- PrivateKey privateKey = null;
- X509Certificate x509Certificate = null;
try (InputStream inputStream = this.loadConfigInputStream(this.keyString, this.getKeyPath(),
- this.keyContent, "p12证书");){
- if (inputStream == null) {
- return null;
- }
+ this.keyContent, "p12证书");) {
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(inputStream, key.toCharArray());
String alias = keyStore.aliases().nextElement();
- privateKey = (PrivateKey) keyStore.getKey(alias, key.toCharArray());
+ PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, key.toCharArray());
Certificate certificate = keyStore.getCertificate(alias);
- x509Certificate = (X509Certificate) certificate;
+ X509Certificate x509Certificate = (X509Certificate) certificate;
return new Object[]{privateKey, x509Certificate};
} catch (Exception e) {
- e.printStackTrace();
+ log.error("加载证书时发生异常", e);
}
- return null;
+ return null;
}
}
From 5a811fff9d8fbfbc7eafbe0c93d0c60d74f921a6 Mon Sep 17 00:00:00 2001
From: Binary Wang
Date: Thu, 31 Oct 2024 16:15:21 +0800
Subject: [PATCH 077/385] =?UTF-8?q?:art=EF=BC=9A=E3=80=90=E4=BC=81?=
=?UTF-8?q?=E4=B8=9A=E5=BE=AE=E4=BF=A1=E3=80=91=E7=AC=AC=E4=B8=89=E6=96=B9?=
=?UTF-8?q?=E5=BA=94=E7=94=A8=E6=8E=A5=E5=8F=A3=E5=A2=9E=E5=8A=A0=E6=94=AF?=
=?UTF-8?q?=E6=8C=81=E6=98=AF=E5=90=A6=E5=BF=BD=E7=95=A5SuiteAccessToken?=
=?UTF-8?q?=E7=9A=84post=E9=87=8D=E8=BD=BD=E6=96=B9=E6=B3=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../weixin/cp/tp/service/WxCpTpService.java | 27 +++++++++++++------
.../service/impl/BaseWxCpTpServiceImpl.java | 1 +
2 files changed, 20 insertions(+), 8 deletions(-)
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/WxCpTpService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/WxCpTpService.java
index 356fe64adb..286f2e9673 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/WxCpTpService.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/WxCpTpService.java
@@ -21,7 +21,7 @@ public interface WxCpTpService {
/**
*
* 验证推送过来的消息的正确性
- * 详情请见: https://work.weixin.qq.com/api/doc#90000/90139/90968/消息体签名校验
+ * 详情请见: 消息体签名校验
*
*
* @param msgSignature 消息签名
@@ -48,7 +48,7 @@ public interface WxCpTpService {
* 且在多线程同时刷新时只刷新一次,避免超出2000次/日的调用次数上限
* 另:本service的所有方法都会在suite_access_token过期是调用此方法
* 程序员在非必要情况下尽量不要主动调用此方法
- * 详情请见: https://work.weixin.qq.com/api/doc#90001/90143/90600
+ * 详情请见: 文档
*
*
* @param forceRefresh 强制刷新
@@ -86,7 +86,7 @@ public interface WxCpTpService {
/**
*
* 保存企业微信定时推送的suite_ticket,(每10分钟)
- * 详情请见:https://work.weixin.qq.com/api/doc#90001/90143/90628
+ * 详情请见:文档
*
* 注意:微信不是固定10分钟推送suite_ticket的, 且suite_ticket的有效期为30分钟
* https://work.weixin.qq.com/api/doc/10975#%E8%8E%B7%E5%8F%96%E7%AC%AC%E4%B8%89%E6%96%B9%E5%BA%94%E7%94%A8%E5%87%AD%E8%AF%81
@@ -101,7 +101,7 @@ public interface WxCpTpService {
* 获得suite_ticket
* 由于suite_ticket是微信服务器定时推送(每10分钟),不能主动获取,如果碰到过期只能抛异常
*
- * 详情请见:https://work.weixin.qq.com/api/doc#90001/90143/90628
+ * 详情请见:文档
*
*
* @param forceRefresh 强制刷新
@@ -116,7 +116,7 @@ public interface WxCpTpService {
/**
*
* 保存企业微信定时推送的suite_ticket,(每10分钟)
- * 详情请见:https://work.weixin.qq.com/api/doc#90001/90143/90628
+ * 详情请见:文档
*
* 注意:微信不是固定10分钟推送suite_ticket的, 且suite_ticket的有效期为30分钟
* https://work.weixin.qq.com/api/doc/10975#%E8%8E%B7%E5%8F%96%E7%AC%AC%E4%B8%89%E6%96%B9%E5%BA%94%E7%94%A8%E5%87%AD%E8%AF%81
@@ -286,6 +286,17 @@ public interface WxCpTpService {
*/
String post(String url, String postData) throws WxErrorException;
+ /**
+ * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的POST请求.
+ *
+ * @param url 接口地址
+ * @param postData 请求body字符串
+ * @param withoutSuiteAccessToken 请求是否忽略SuiteAccessToken 默认不忽略-false
+ * @return the string
+ * @throws WxErrorException the wx error exception
+ */
+ String post(String url, String postData, boolean withoutSuiteAccessToken) throws WxErrorException;
+
/**
*
* Service没有实现某个API的时候,可以用这个,
@@ -395,7 +406,7 @@ public interface WxCpTpService {
/**
* 获取带参授权链接
*
- * 文档地址:https://developer.work.weixin.qq.com/document/path/95436
+ * 查看文档
*
* @param state state
* @param templateIdList 代开发自建应用模版ID列表,数量不能超过9个
@@ -548,7 +559,7 @@ public interface WxCpTpService {
/**
* 创建机构级jsApiTicket签名
- * 详情参见企业微信第三方应用开发文档:https://work.weixin.qq.com/api/doc/90001/90144/90539
+ * 详情参见企业微信第三方应用开发文档
*
* @param url 调用JS接口页面的完整URL
* @param authCorpId the auth corp id
@@ -559,7 +570,7 @@ public interface WxCpTpService {
/**
* 创建应用级jsapiTicket签名
- * 详情参见企业微信第三方应用开发文档:https://work.weixin.qq.com/api/doc/90001/90144/90539
+ * 详情参见:企业微信第三方应用开发文档
*
* @param url 调用JS接口页面的完整URL
* @param authCorpId the auth corp id
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/impl/BaseWxCpTpServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/impl/BaseWxCpTpServiceImpl.java
index 407702439a..aa874f8549 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/impl/BaseWxCpTpServiceImpl.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/impl/BaseWxCpTpServiceImpl.java
@@ -337,6 +337,7 @@ public String post(String url, String postData) throws WxErrorException {
* @return the string
* @throws WxErrorException the wx error exception
*/
+ @Override
public String post(String url, String postData, boolean withoutSuiteAccessToken) throws WxErrorException {
return execute(SimplePostRequestExecutor.create(this), url, postData, withoutSuiteAccessToken);
}
From f6e300b10a74af6cc1df2b78aa97d8fe5effa1ad Mon Sep 17 00:00:00 2001
From: Binary Wang
Date: Thu, 31 Oct 2024 16:20:24 +0800
Subject: [PATCH 078/385] =?UTF-8?q?:art:=20=E5=8F=8A=E6=97=B6=E5=85=B3?=
=?UTF-8?q?=E9=97=AD=E6=89=93=E5=BC=80=E7=9A=84InputStream=E5=AF=B9?=
=?UTF-8?q?=E8=B1=A1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../binarywang/wxpay/config/WxPayConfig.java | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
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 932fa323e0..637d46e986 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
@@ -279,15 +279,18 @@ public CloseableHttpClient initApiV3HttpClient() throws WxPayException {
if (StringUtils.isNotBlank(this.getPrivateKeyString())) {
this.setPrivateKeyString(Base64.getEncoder().encodeToString(this.getPrivateKeyString().getBytes()));
}
- InputStream keyInputStream = this.loadConfigInputStream(this.getPrivateKeyString(), this.getPrivateKeyPath(),
- this.privateKeyContent, "privateKeyPath");
- merchantPrivateKey = PemUtils.loadPrivateKey(keyInputStream);
+
+ try (InputStream keyInputStream = this.loadConfigInputStream(this.getPrivateKeyString(), this.getPrivateKeyPath(),
+ this.privateKeyContent, "privateKeyPath")) {
+ merchantPrivateKey = PemUtils.loadPrivateKey(keyInputStream);
+ }
}
if (certificate == null && StringUtils.isBlank(this.getCertSerialNo())) {
- InputStream certInputStream = this.loadConfigInputStream(this.getPrivateCertString(), this.getPrivateCertPath(),
- this.privateCertContent, "privateCertPath");
- certificate = PemUtils.loadCertificate(certInputStream);
+ try (InputStream certInputStream = this.loadConfigInputStream(this.getPrivateCertString(), this.getPrivateCertPath(),
+ this.privateCertContent, "privateCertPath")) {
+ certificate = PemUtils.loadCertificate(certInputStream);
+ }
this.certSerialNo = certificate.getSerialNumber().toString(16).toUpperCase();
}
From cff5616463c28450076dd19d488dd0d1a60f1405 Mon Sep 17 00:00:00 2001
From: GeXiangDong
Date: Fri, 1 Nov 2024 00:14:29 +0800
Subject: [PATCH 079/385] =?UTF-8?q?:new:=20#3404=20=E3=80=90=E5=B0=8F?=
=?UTF-8?q?=E7=A8=8B=E5=BA=8F=E3=80=91=E5=A2=9E=E5=8A=A0=E5=90=8C=E5=9F=8E?=
=?UTF-8?q?=E9=85=8D=E9=80=81=E7=9B=B8=E5=85=B3=E6=8E=A5=E5=8F=A3=EF=BC=8C?=
=?UTF-8?q?=E5=90=8C=E6=97=B6=E4=B8=BAWxMaService=E5=A2=9E=E5=8A=A0?=
=?UTF-8?q?=E4=BA=86API=E7=AD=BE=E5=90=8D=E6=94=AF=E6=8C=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
images/api-signature/api-signature-1.png | Bin 0 -> 168576 bytes
images/api-signature/api-signature-2.png | Bin 0 -> 304493 bytes
weixin-java-miniapp/api-signature-readme.md | 46 ++
.../wx/miniapp/api/WxMaIntracityService.java | 86 +++
.../wx/miniapp/api/WxMaService.java | 100 +--
.../miniapp/api/impl/BaseWxMaServiceImpl.java | 374 +++++++++--
.../api/impl/WxMaIntracityServiceImpl.java | 276 ++++++++
.../wx/miniapp/bean/WxMaApiResponse.java | 34 +
.../bean/intractiy/BasicWxMaOrder.java | 128 ++++
.../BasicWxMaStoreChargeRefundRequest.java | 49 ++
.../wx/miniapp/bean/intractiy/PayMode.java | 16 +
.../bean/intractiy/WxMaAddOrderRequest.java | 133 ++++
.../bean/intractiy/WxMaAddOrderResponse.java | 115 ++++
.../intractiy/WxMaCancelOrderResponse.java | 67 ++
.../intractiy/WxMaGetPayModeResponse.java | 42 ++
.../wx/miniapp/bean/intractiy/WxMaOrder.java | 344 ++++++++++
.../intractiy/WxMaPreAddOrderRequest.java | 22 +
.../bean/intractiy/WxMaQueryFlowRequest.java | 88 +++
.../wx/miniapp/bean/intractiy/WxMaStore.java | 187 ++++++
.../bean/intractiy/WxMaStoreBalance.java | 115 ++++
.../intractiy/WxMaStoreChargeRequest.java | 22 +
.../bean/intractiy/WxMaStoreFlowResponse.java | 318 +++++++++
.../intractiy/WxMaStoreRefundRequest.java | 11 +
.../miniapp/bean/intractiy/WxMaTransCity.java | 56 ++
.../wx/miniapp/config/WxMaConfig.java | 67 +-
.../config/impl/WxMaDefaultConfigImpl.java | 116 ++--
.../miniapp/constant/WxMaApiUrlConstants.java | 629 +++++++++---------
...ApacheApiSignaturePostRequestExecutor.java | 71 ++
.../ApiSignaturePostRequestExecutor.java | 69 ++
.../JoddApiSignaturePostRequestExecutor.java | 59 ++
...OkHttpApiSignaturePostRequestExecutor.java | 51 ++
.../impl/WxMaIntracityServiceImpleTest.java | 234 +++++++
.../src/test/resources/test-config-sample.xml | 5 +
.../weixin/open/api/WxOpenConfigStorage.java | 65 +-
.../api/impl/WxOpenInMemoryConfigStorage.java | 205 +++---
35 files changed, 3626 insertions(+), 574 deletions(-)
create mode 100644 images/api-signature/api-signature-1.png
create mode 100644 images/api-signature/api-signature-2.png
create mode 100644 weixin-java-miniapp/api-signature-readme.md
create mode 100644 weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaIntracityService.java
create mode 100644 weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaIntracityServiceImpl.java
create mode 100644 weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaApiResponse.java
create mode 100644 weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/intractiy/BasicWxMaOrder.java
create mode 100644 weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/intractiy/BasicWxMaStoreChargeRefundRequest.java
create mode 100644 weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/intractiy/PayMode.java
create mode 100644 weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/intractiy/WxMaAddOrderRequest.java
create mode 100644 weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/intractiy/WxMaAddOrderResponse.java
create mode 100644 weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/intractiy/WxMaCancelOrderResponse.java
create mode 100644 weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/intractiy/WxMaGetPayModeResponse.java
create mode 100644 weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/intractiy/WxMaOrder.java
create mode 100644 weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/intractiy/WxMaPreAddOrderRequest.java
create mode 100644 weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/intractiy/WxMaQueryFlowRequest.java
create mode 100644 weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/intractiy/WxMaStore.java
create mode 100644 weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/intractiy/WxMaStoreBalance.java
create mode 100644 weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/intractiy/WxMaStoreChargeRequest.java
create mode 100644 weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/intractiy/WxMaStoreFlowResponse.java
create mode 100644 weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/intractiy/WxMaStoreRefundRequest.java
create mode 100644 weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/intractiy/WxMaTransCity.java
create mode 100644 weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/executor/ApacheApiSignaturePostRequestExecutor.java
create mode 100644 weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/executor/ApiSignaturePostRequestExecutor.java
create mode 100644 weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/executor/JoddApiSignaturePostRequestExecutor.java
create mode 100644 weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/executor/OkHttpApiSignaturePostRequestExecutor.java
create mode 100644 weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaIntracityServiceImpleTest.java
diff --git a/images/api-signature/api-signature-1.png b/images/api-signature/api-signature-1.png
new file mode 100644
index 0000000000000000000000000000000000000000..e4d4e1e2786214f5ecf73a54d90742099096855f
GIT binary patch
literal 168576
zcmeFZby!sU+6FwdfPg_rqbO3+-AF1RIdn=142*QQg3{gHjdYhvcQ->wcMKihV(s(wp7u$X@^(-JD&>%%$N%01%J|CiLhpL6JZ6H6$l}2R1fqG5r{7jSvi|HD
z7|`|Th`6eAsYarRZ7_M!4d1=FQITKRXRQVarQUnF$5L%_k6i2sNrk=>8DtCECuZoy
z?LOuCZu%%g=u>hhmr0mlCzqbrV8+*&FHHE~*+6^Gu_eNgL7k8GhfAy9NRov?a5$bN
z34QuV_MObcWS#F5?*k68_doA-AFw`h>j*!pN-^LKLv?9)lULwDgLVA`R6W`(&zj$Ad%h}J7
zB$>jIY++`e<{j>7r#^D*))$%;<5M5mlIQ%=jTUL2S`*#}8PuYhm}bl*_ZW8{u2M;e
z%34NIm=@Jnj_jqZKZ%-Q4E5~CfMRROXGUNMq
zt36*r$J>@JPT#eQb}hBW&K*?z{4VFnr?bX*Vr=lad$(7VtZ&^}WSL)j0omsrv#zHN
zg{TtZ2Z|>(2M845$%!=6_vo||C?7t5VLTMmVX`zl@vNxgMQ>kQ9|CA{GSP)?PX1-z
zbI}IAZ90eh9?n~*rw;Aqj8E3v2ZcdjehdpZQG$MjgETdNM7}Mha{VxggX%~}jliCQ
zZWGGsP4f-Wt{J@=fpH!aQTW~$P>Jrtc`t(yAEidzU!LHUdl4Qox^y|n?7Ey2IM`l4
zQt)g%SmzO|5sSR1=bx^jAkE`FK_G8_f-KDS<-RCo4mpZDHlSmKpMe3
zPa@Ix97-`B-XE46B1ofvTZlL5$JZL^2ljjA$BC;$_b?(LGUly+gjHl$WDvEMB9Sr`
zbv$)h1a}u*M1Djt9SlqcPR*If`;?QBTcBbhSChe#05Jy^ZA$bbpXz}Ro7&&VSu_zJl6EOa%9-%t4ZSeE7k(SAM~RT-$`5RiJ3vZ$@;F#I26c&!P9ROdV~63OKeJT)yCB3
zDxRq^7nT+f6`mL5jAeo$Z5mbif@xI*@@6;zy8>%K{t+BF$OKBxWT;s#aw&6V5z2Bw_3KVoWQj}tH5ky
zUSnuuWTWim{mZaRqAT%B(yQpRWb9^SWE2Q?b@!N_^(1^NRA{w%*>lxPQg}f)Qdriz
z*ZW1ww)H%>r*&nIBipz`(FY5&d-wEwA?S`IZ*jC9olo;^)eks=50iW+eU?AlepdWW
z;LG{R{mWj^#FKrdAvOh~Ny1Fr3W_{|4gGRi8xtF7PeIHwvF5_b4Uimetic)eV;E
zhpwy8M8R*(mcoNxyqN2lWdz#l85NS%Yd_y-wkEVnEHdv8?qluia6e7p>HC1lo64j2
zse934sd3S9zso_y#omqPMl8+wy~(0(nDbCnMyf&ouY+HD2vTx14&jeN6v7Xf*`M3$
zY*aI;PbB!r`pfb%DBxxK^<{2m8E2n-dG%9laqdNtR3!spDDsW()$eobp@=lf>5wF%xdLe3U9#g
z6J8=6mI{6mNykY)tjVvM)^>Ckg##rMWmiR_*n)9{riXD$XQ?TxM6yIu!N$IDdMYk?
zKoH$+L)*B?;-Yr9wSKYt1;>l!7dqqC<2u)q$E$wH?VQgo*5=mAEmx_ws0Oom
z^2fpMGcD7W`q|@sSwx9(VR4Lg$aOoT=i`+lu_KYYZ99EaS1z(6NkuwOnnrdtQ9rC9&kP9KROsc)!8M%
z=9zK)WSOy^vp=|@AEDp+N>1usx1^bs8B_zJ(ZAokl(=oN-R@R)ZLZ_%CNYW*e@ra<
zNESbdL@?6X^r-ywXfHN{^(xVympmzu+j(=Q-gP_{WDcH&POt;tB*
z)Kfj<)Vlt*8{D4X?q+{^rCOzE-_5>iW_ot_xS3IHhC({5|fU9F;Cr&n(X#Z@;v4kGUG0*&K0B>(AcZ7~adq
zxv04+PBP@X;E_8`xU9eYJm-HdT-Orxv?+CyakYuW;e{JJhvrdsSMq4Fj1Ku#_hxZE
zB{L-w^f|N*R^*<2W&v~dJxNSYO_vqOaD8&2dimyX__{x!UrQ&gss4$^Io?UdV*F&)
z@vbn8JTN;j*q{4R-%o0f*SPPbk>|Gs4BmY~@$l+d{&^N&2LB`!4num3eEnFq5B7Kw
zS?KY51O11aY#`qU?41%;l;fqL)Za0ikB_YqIt0sO7|TK
zj$FxN8mN&r3vM@o9>DK^JjksDFyL=o<^fc
zLHeI-;eYn*cA)?L`g5L!4krH^$-?@tWdRFhynTm}nSqJ%KQ*&4F}kDK?K^)q`(s^y
z4##)f8IPQagCRs!)WjUvYG7&t%v>yNe1DAd|9b0RGyStE*xJxa1Zobnv=R8%Vg1$k
z?{EHl!#~DU``egIEL?vd^KWnct?BI|coYn+p%B~KO$1w**a)!jG5)99|K3XNU(E!V
z*?{%^(dh4Y|GkCke{AvhyZ_!o*2)Cf2))~>2{8XP!r$-x^*kTr?cx7T82*&BKkfpe
zCV;`m_`hN+fDtq71?Z*_=#8kbk^{olEP4X&XwAt!QqQ;DFCO%WrRiTvF=+iLsA+fP
zmmZ}*3Y02RlRlst{6%Hdz{#l{9^){}IU^>I`|yFB5V8S*;lqb`A+8dr4*fSh2}yBD
z+Ihyc?MX?n{b9vfaP~VD*ZkT&MOpq;rRl1hW<3#M5CZZ8q2K))qLAaqG9N%jyhq^y
z`rWU2d51}dGv8=!_pWl!AK*$61eD$~E-OeUM#sg$9MQw8LpH=}(4g0@S
z`|J2JWGEQTUrqX8lM9fT6scF3hMv^_lOT8=VHlV}ie?Zbw^a*t^*{O!OT97sCy{s{
zMWMaLpgb$xOJ@&KAkHF1MD8Ejv=T!l>Bu%J*}VV?@6
zXN~5ousS-jO9yU@>Klm~{pc&j`iDvU3iDXBLBkR$V)*vedvsR7O;Nbx
zJ5<(l5}M-`jD|zTR$;pG*hRV}IPV{3XYg7G-P>=L#%DDg9d9~+EzAluWXx~+!)%IF
zt|}k*A9VA<=?V&No#K7j55$}7^g8w!TV=1I$2d*N}wU=Cm6LL{4n+(l)yvC
z4jpKitUNLX%P0uh;_?3d6t!hbljUtS=D4oxnkc^8ZC$9MwumA*jFEK&5ZZMYGPmb5|EK!or^1
zF4bO53B@85-iV`lgyGTA#v3Dfh8yGg;Vsa#bydKh>gO`y+=*m7N${z;90nk>jl(2d
zav?vQJBz@E2q+`AZkw4D&f(%%(%OMU;Mk1UCy5|~&sK+q|7TtQM+aOE5d}_Tl2v|;
z6e>=S&Pe}g&0=Y{9_
zf3w}cDm`k`m2!<)$?-zuq7)W_%U=pH*
zh=THYNnos}-+H#tbTi^b8?LV=KhDVLH$R{cv}*$;ARZNWG$FbBq_!SW`?1#+NF?tY
zS>McvA(7(-VT$#ol*va`ERDwiYTzuyK-Gq
zNPn$aJ=mJ8#t`}*$K|b)QqW=gUe12iGa%kp{=@>i8{7%uW1I#EWW8WL-RqDEroTjA
z*`j*r(K9L19rX0>tx#;q?5P{To{%Af&^&Z#3=}Ev09k)v<2!`mdw%
z+O6y(q-vDtx=4P$q;sVmlr0;A(js)cb-y=x=hh)0cTqjy9A-3??ofcM?leo)kTCq&
zzU!DyudozAr}X|rn|J%W5F|uP)>9WMaz*sbuX#{?^V>5ijorw^|}g|yQ?(~G3gBWhGxu;U1vrl{3eWvVqeRXDDt`bx)h
zn0E~+iH{E+2a*fUxwdn!Cg}6n?<5TFrd!?-)=^9&M@*_b(Ny%A)>FK!`6%TtSVofE
z<>qEEUCdZj<$J$lMxYVaF~@E)^pjq*dY~s+5C)m9?B%<;JcM=FJwG2>?v9QphSm=T
zkn)Z-9JzIdz4)rEM|j7=LaY%5%4A#EBNzKW*QsB|E}tr012R*v0u=+cvf$n_?J4QF
zj=QTtain3j{q9`HD)hX!)Udm!mlFoD+nRVd8Ij0qAE#ka(~2TP$?AAlpz*63q))uk
zHsM3k{;{38-Mvq(!WoDUHa`C9Yr9X#^3!H}YIHlq%elDe+DUn9X0g>@D@A@f!)g?)
z5*u$(+t~msGY&p$!Ew_y>Wy0#I7GWcLON^_k{3G44SZgb)vSkZHmyRYnoKW_$*KAL
z!yu7v5Wo}xBml}vvnNuP+Hl*Ov7B0e_R5rmv0@#l)A5!`949o1#Rt(q-F>Mo(CFfL
zdu1&>n1kJHEMW3oB9CoPs010Iy<y60XnV
z2@PM*xt`1#tq*0cvT&`AK<=JSlo1qU5Ia$@D
z#cVC1EwA&mc@?3dJ1wTdk|-^)?cRKg<+wh=9l9(>h_YzJHFeQLOW*!9x(~@-;V}F4
zEz=t6I98x$v^n-&;FEW6<9-|QN>;pumFp(zYW9JEHbL%&w#^S8ETiqIij~Z0Z5G8W
z>91_O#(gA*{qEPpGfta@p^nRr>CDu4KQR?Y0!?OXZGR{Qs!1#{MOAD~SM?jR2wWWv
zTg)g_4CjIA4x^hcTW!{dUYL~ST{I)%#E@UDNf-^LiS5MD`dj_#2wm|Nyi6tTXEC8v
zx(fx+dU}vg4^A+XnpZQkP-VbuqNYojw3zUdyWHJQ%i>JL?hT-r5k*F>uSqd!yhHe2
zW4(GVW|C;yEQivAdjE;;^=Z3cocrYxc^06wp}P)?UtiJ|V5;6^4rj~StNCEKG@dT{
zyV-q~UhR!vtxa*=$jyG%(k|e7Ud^ET1DX6voWpdaLk%0SOZ_Yu&vl&EGnvaYDI*>3
zvTjPx0IQQTANF>C^?M@qwI)1jbMBhderCa`%YU`Z@zBV+1%)7nS>J+bqvYEJ9jn{h
z>zeb8{9dTrA@K=HD@+9*6bw?Bz4@O09p?9d0u)QNOv-B)L&$0*s$RDeXV&xOA?35=
z^md;Bddb6hx2yA5z~H_Z%5!t+3Mj})3+TDnsq44JyqI&|jg6#JQSMOLe0FmIyP0zV
zeseZo!
zxzDF4Po|l!Cw!LLgY8eQbllDiqX}t;J}|&`2=Ko~#1bHO|8TD6^B`
zn%!P?a(?QLuO#IS()yN#ATJ{w$40|~ukVlls%O8Q9BQ{ch1VGs&tseCtdPbx8|E`R
z;?LMPk<+alsF%Y6*v@nYQMxp0sgI+1;GO&ttxel;t?0qx$da}?5?;Fk`SJY5qm9w-
z!0Cx1EndNVX+ISG{bMLonBAsr&f9<99zdu+RcWq~b*zSdht8i;
zp}3$D4u=tby5(>&gz_|P2E{_9>r#eaCcC`Xc3j3&F$R`e6#e?-X&X7bXuL$fU6Qvp
zy2v2OK#ZTp&W_gA=K^7GRCLaUl$7Qf_nWIw
zo@gDn8WTXFe_7K)H^z&hw=PHd>u4L`rp=SwuN@Efe&F!xz$(3xIOiPFYqm^j3(B}gnk>3pWS!@PvR5o?nR
zD3o(^Ngp44)RpeYt8`hZV-4IXJlUEY1{`=rM1Rt(vu3eNt!x2{`?br7pMA5e;FS%5
z$5GJ5@eV67r$vAYKa0-EEcnt1lYqe{+1X}S{(YkkS*-H@lFF;LYhI>nr5Jz?WRs&C
zs50mblay-Q9IdbCvu-0a7Ht!Dfsd-tJqP7ttq-J%7&spV`!YmGklHF4XisortgPc@
z<>ZeuHlBV5n@@a8t=Y)S|6YJ!)<=__@czh8Wn^|6c70sc@jmc#?tD=tI5vb*A}D}f
zYBe4>tO3KV8yI>|Oz&{rGRYJQ)Xd0b*)@9w#hb1*BCW2_`s<*N@+#XoR
zrFYNv9sFt9J-N9W(-GaZYHhxj=jr|e6lYCf6a5Q(bG)uGg%XeDh-!i~xAC>Bta0hu
z1)0-YYKO{)`_fuUtY9B((v2j(!B)Fw4+L?2mUGNgz)uHUBXo$$f0Gw$2O}uJcLB35
zOKj*&s}bS#f8xV`*#{*mPsujTdg%^YE}fHocI0R$E)ot%4Dr(bH0-*PknKZA4SY#JsYSLQk+u4W(Ij+|`+>ufibIqTkpQ|*w4ukj2fd(Td6bmCu$
zSkF1G#sUkFP+4aEjlMt&zn4!e09MXkOi#*5emZ_fI)G=)0Z{D?C)Hf0m$ovz&)oIoQnVS~#)#1>2d8fCiy
zJlsrLrTKK$WkbuZB4y1n_f$ZC?Bm;=(YTt2vwdP5M36-30=9)gm9{IY#|ET8@Lf0h
zTUXbJU4}ZY=zUZbMK$-Xh@ZKz-15v?A`!(U9#pAdvd^xXXshe4n`4E&W&Hxg{B1(&^im;TK=@I8!w8+%VJ*$4hKBD{jCK#8U+wfGYxia7*7edq`suNKFSuX
zag`|#(#bN1FBvCUwhpsIxu#L-f^q;V~zz1UeB9BBj0|
zCm!#@pmS~)=BySoT%4M!-{#_>jq6^)Zyy}QMqr?#j>{fRR%M>+xL?_3M2qz%^EsBv
z+vm5yU1b9WK!nH6c?$f{uJZQ)(v13{)*@kRpQq5^WXtMux>sFD!}W(3ewEB-5>|6{
zJWVMp`BRJLn{KNg9!KlBknXz$4HV&9{EJ!-w$HtOwnVpq?(c&|d>%|A6A#1w51MBZ^M@+?F1sdCF7Un)#TyZ8=!
zSdID;xK}z~CfhK+B4*O{AOy@CHvsOn6z4P$dQ=SoO$l|{cw{%6C6guQk1u`*bJ~1m
zeFYzvh}sMc#ySOMK$A^652sW2|FGCm-rN&cdFQqkQ{^TzV{F(NI6^xUB|o%M*iIo6
z2mU&j&mdqGS%g6#f@ii7V#;jurwsZ8Rabe%t+M(G1Zv0ILzAaN^+>7~SyNoQ9m8^}CZn8b_Q*
z!4!(iKulK>%Vn8{DO99cGGwzcLlY)nqxS<JSS=SIg55dZhyO$WC^v#e7Z+TK9)R
zZ`5ofVuqXC-B0p>;7i8R}vkKb%8EQK|aIvBAM!IC&@8G0r)U#N3@i&v5KHaI?t=;C@$Vs13YLUQey2{J~
z+ti3G`~d;!Da&ek`EAzZ49vBZjxaQC$qa_qdfs-XDofmLw6+@~mbv3jrNXn#v`_N<
z<*58oN5KyiG9`k^RxS_wajtBM(ua!kOPFN&_J0m+Qsp`9%?qa&nf{0>Zak+|0PjXK
z>X4l!Yz4N#d&(_K&2_o&&F>8To1O;KY@kMZr<|f_Ob{w?qt+yyr
zw?P4C6A#e)cv@aq$u>>HqC4c&K;BwKtdV>>O6WpLewftC?PcO;s
z+=SJr=gA>JKhzpVYg)aT+5I|Nq?~7ZNQ&!DD+=(ygfYbC_Ee)u6D+9Pl?3bw3)*~?Viw50Xl(vkoVm5Iq>8e9A3YRA5~
z%(LxK8VKRFqVol>)@8M#3pQkAg&R#rN6hq0V2hPMF6fE?6wCPJ1%t_DCjeAy$#89h
z8&4hZDm5>;T`mNubMdXruHKu^8EW4yFhIw7yP`vV10Np9Imh(24qGvT`xF3wV_@)*huy7o9b8SDKnU~Z&@Fe;=_dd85AfS4}
zUPx)WW`4c_kbuqZFg#niP~GyPGYC$K@tiJRF;k^jyG?N6QNG0yAG}lMu9_vrw(J7>
zCE3tH*&i32=2Ig%#@{8#T}NizF1CxN$GaJuX5doLidDR2>clM&|AY1Q;PZ5q8Tpti2({zu5V|@UV
zT4+jVCT?b4@hwLvYVuSkg*jaAw^P=gmL*^Hz!gbjLwz`jFck@=zb!BLk_RDELQt5shI6{kSctCbg}9l~d`77`2anvUz4(LE6~iUB*|&FktN
zUOJDHTRT^ObA7P_;PCwQ**bf9af5}Evr5-Gz3i^$k?L?j-ECX?Z;c&0p50ZRYQF|+
z;S&VJ5E`B)fF?8oY;b7Bk&Otg7-6_KDi#a0>8esgW-^=;=3@P8{fp96mrpIgWY{1m
zZT~1xE7Mf|6+pZlV>kNNcr}9xF64ece?U0S{uF#n$ey~SqAsudb51%X{CA`g1
z$*Yg9xc(GGLupq%-*Vaci0M-zH
zBP>DYn32B`Jzk_WT@HoK(QDUFumK!;gHj8lvHF376V7fg+acnHO-z`V5D=yVmbs?I
zKv8uTjD`9tGL7H3FF`53qxKv~HQCErUoPOdf1C`lpRx2^A553%*`BF_CJ%~*-xVfN
z?Db+$KYo`vG7^YRsNYzeywrUqzt1q342wI8;s&Vu*#Z4qBS*+$@P#;SD>pmIs-%u0+ZV0ZpNJ!MV2%--an!5x*n0baOq_qj{%4>WP-yx
zYpPPbkX?*ab928Y3ghQSFLm=b9jl;oy_FnlfHvK@UchY+`}hu}trtEM#NoeEuLhU9
zEPVgGa*L(an%o)&tApLI50fcH!p`701ZOBs#=sO;mZNp-2qU|Sl-5Ub!*3bLU(R?2
zO4Az~6#x=nSrxpgIV-+7XJmCghMe%97c5Q(yClytaKeD2I&GbH_iawbcnI)nh-GbV
z^wiW}3Lf?t1oYhB7uOe0vYRs5=|lsoFuiM$M0sJIN}Z@lz^A3@x(;h#WAj68X&sfVcbQm3qr2cUM9tOEZZ#%r8&(83wqGdSYT=tJAr8!}FHQt}7@2;j2rs
zrl)
zEVbD1KDeB(XV01e;c(HEFS*+qLlHpjuyW4YgcI`E?P!G=z(CHjxHf4&
ze);Z?(6%Q_CSG%I-~HyqT?TNVGc19OXLYcvJ``SZ$6pUQk>hF&%x9{Z+u1iRkK4UX
zZWrytrVXUsHs6h?jEkl4W$HA!S;(o{Zsw)w8W>^<(SFREPgQ7mDIKE=WUb`AL`@c&
zuC}34$fl8x+gGjC)=uiJkqRVt98)unZFIdLdI3LOR=-&a7UXch
z+SKVy6Z1bBoQ0}X8UWd>8DrS>o0nKtT4lGnm6ch0m~E%fZ4!o(FoFd3z2qG!5r9Ne
zrW&u%Fn*lBX?*vxUcntNM}8ptkMx(Wr6Iw9eZ->y}Wb*$|_d-s{DA!J3I;$veis^^RqdQ%6(W!
ze?$N&xIPpz-}{7*oaoyD$^n3yHEjW8{8QAknyy9{JwJgoLma>{j9vbEDad>a_hMvH
zu!hgT(pITde?UEjF0w9c1F0UXewC)7!1LY)ydJqDPy#
zzF46Ca+(a0q8QyjFmPxRnmAmv^kZVN_PC(mb$q}lj$VE|-P5a*m$n@E8nU@6e0+syZSQ|i
zXl19wu>TIF2L_U)o@XUD$Ii%C0kZcM|ElVsZ9kKD7&+B=2t+@0uVZ)b%6$R#<(Mcg
zZSlRX)*826=mP}+n$KFvP9u(TNLD2l%D5tne`|}`o
zsH3fNl_R*n$SR*Jr6n6>!juq0BCj8mBHbYkzyU|dP*`N?jKqXwZj{V;Mq6=f{@Ffa
zTjjgD;q58mvya^c@`1fV1LFI=GiiaW9qOeBV*S@E&9aq4TZljziFM9|+}#2(Los@jCs{oTF-QdlN?4E5(&TDo4!S!$P}#59?pNm5`TLc
z5(#jVp(dU81^KLoDe2rlao5TtQ&=B0m;R>O=K142!XQ8lPH)Aa(+!7SHW^5-9H|XG
zx?Aqm8088-sEVLWE*hQxL7b3v&ss7pD}2(xN-j9q7kul!bI~u&`VuG@x2`O|Q%OC9
z#?XH)FV~o#p9^_@jbA5Vr!vTOqPO?S)P%^XEHeqHEtlPFB3U?J*6a%Gy-dH|fG(+}
zyCuaSS}0MyVg_}4u8y%(&yw}bMVwsG__N|NF|mU1BEQ&bt>6jeO93bgm7F>38tL=G
z>2sY_q}$R2ML9WLY!WWrICk>{11$Wz3#mMWI51o)4C5Qdb#xC7Ok+xX`xDcJ$~5_xs6^y=Fe!22)H2-?^E8ydi{-4wTuY
zdADT!P3Qho<(u3jU=)>)DLamTBS*JghKK2A{
zNZge>xb;{pv?0-hWBHOxOWJOeBYEKN1gnG2ETAI6qBz?1G&T14m~A*4N-rJzha&U7>{bBvEnXMfK0<1WziiZscgA!G&y>aaRNu+0d`fa?o
zB|CmD@7m*qz0qRHX6v%kX{M+jsa9De4oYPL5tW#=we`Vs>8%lb?@eZ^8bvD}yPMNqXF4wIDE0Oz4imH^3ql4-FQcpaT2CTKd#Q*GU
z%B^nO(OR$5D<
zM8P{U8QYrNiC3?Hn8cA=ilVrhZRmt!@4`1ZMZF4hp`Oeb2N~*J&MtUzQ-jB?o^1I;q5`x2{aa5jUj%>2Y}7ZX)b9U$m;Ucq!O
zgw;D*e>bz9;P^3?ubsY!5G!)*lotl|4umxv^R9d*&|x8G(6JzMzC^b^TaK>T)chvH
z?{EktXVHvbxWWoGR1RZZb>G*RbE@5v49q1xL5U}I*r)1swbr}d}6#9%S4Zt
zQyv+~h1OZu8RkbBoqp5%x|!X=dyWarHOb8UQLNfh
z@i#4KMpA=NnN>+vVv3!yihM$=sR!XIxJT%9GO*J2O!*%^diixu!`b~z-NOm3^#rABzdA#ugF*P|wJu<8u8O6VX(26fhn!%Eu(T
zQeX+ASOQlN8rg#$}*!VYwgwc7X&(xI5rc=^{ywHj8t7;
z&1!((hzX?5(R{qJkdSRHqoaO}bGO&6pI)ny-pQ_~pFrEeGneq*mY{BvB2FK*nWCqX
z4pjp@%JF12)@ZNOdlov-pUh&I7b~FebJKzQp96y-dO4S9hF&a{&nrOT;d{I3qRoi2
z9OBZ(4~<=9@qx@HLmF;&LnZ{yWxPC~+^tGwN(m}0PU+99b^_$Ipj=(O7?0Hc4b%5f
z`l@@D>4H5RZF%Vdgtij79>spd(LHkoD)U0chQ~he7txvtOmg3B)>#fOF&V4lFN?4)
zB!s0&DN)RTflZ4pb}G=3zCdM?AV3*~R^g6crO2d$&Q!CnHXX#{q>HX;8!^4{UFkZl
zv$y+H`TC1Iya|wGY1)>AYn2~js#9b(^#kJKH4UTbUBx}+*lgPCx>?^R?n4Wt>1x9h
zsGJJieRGEszo4qyj)u+~k4`z}X?BvS&%_R6H&5R@
zV4-WR6+C%$aMwnVP@Ni}RSRC2eT=p;a9eh;$2K?d2SsJuZ#nUD4QynKKot_?w&aFo
z%G)-q0j7UrKH!a*i>nn3d8?fR#zynzWo6)QDQIvZVO9=4#
z7^Agq`x-T-k-g8PlUfP#T1fITn&)qejS#g-w_+?-%cv?XU
z#gf@$stJ!aP0sw}-Wa|{6~_3dqg^?5>L_HeQZ)|KrKj;NHp4QURQCJPD(vv@U+a#!RbrOI2D%}m%^9*H@96Yj>N
z5csZ55P?$2VLAnw$EpkjJawH}P1{fYJvai8Ap9~gO;C`tFJaptsd6?A+p!#Pa@bz4
ziZeaVC!?2}XHg!sRbHOZKM_L=DcJxY)x&NUf|;8Rc3?M8S6r?H?m(k*mH=Aq{%{q#
zJ-3G$56=n+(r)mpd!|%n;R`Y90uj6L_1aZE$>KX$(=Sp75~%vh$aa?8H7@Hh>E}Ihc4sU7QXb?FN|gpCI2QY7#|6-yi{p3v!hJ#<-OqKF-C
zI+By>IA)|kqnqtv0G$>yEM=KA!le~?tB;ILZ0Dl^E7V6~-}h@Xbe=>-jvN`jI9&?2
z>)&j5P^^mRQj*F1K%$63KH8dJ%f>g1sxZ>9M@uX%rPQ^Vn6oWSFV(&eZtg}iK?9PP
zdka6F#pls8HQ)xg0L8u^hTQhF!SnJKUov1-11fF&+Y#
z9Ag}tEg*i}6nOk(K`$|cGqr`mIFhCaR*LA30irEao>f7s8&ay{@fE043qv|T;dH$?
zrr8H~jXAqpEc^hKk{K0xH$Abv%7q-DUGE~4jRvLGIP8bTAatmeVU&I0Er&uG%EqH~
z33{gN0-1AQ<(1YYSb4M|s!@<$b#}8F_m^+CL>&d5R|_D=M`~$Iv)iPlZONm=fY~@c
zrUfjt9rNJPZ&-n6$MOoUYzpIP=_)J|Yj^Z;_~lZCO8knZMSSsfIA?oUuDNHm-EFa-
z(*g-1=6J|_Q!=T}aBwe4v0Hql`QoehcLcT(BDd+_y^+9_eoZqdRQn<&rglNI`eW*C
zWqbDsCPyJXvEeW3VfrWcfZCUE02&5vhRLgOmH4&sjRd-*FncXowC2T29kR`(7l7F;
zV(hVZ(piXj?RS281$VJ1&Qm@KP_45oJR50LVlzs`5vU~;bB{v!1ZgER0$2jm?SYDiSW0u1ALNsujJE?^r-kc#*p(^|ce??U`ZHG0
zy>VK}a23@%xMf>#Do7ad9&d89c2Wc@
zK@8b2)mW0Qe8VzUsKZH|WxiGO#3ryx<}pO2#>peS0V{;FBz8j2zsS0a0w-$TSPRoN
zDv#{R8x;`CWCeJ7$vyaGf`b<^4qJ6#{eKPyJ%J=5C^oGOQ4=3ykIgH6
zqv+^fI{eUtA5RfaoXlO=4^J4SUbW*`4AZNu^wDh{7fJM!x#15#^K`A;lrtpAD>ow+
z43|(#?Q&E4W)+O(hU^_cJ~{8>7bMY-+~x|2ibM>-cw|=4s5=(G(pXN~yG_TV*v+ne
zBr%;Dztfc?r8JU+IKiV5B-j{DEXby&oLXz<+J}lO1xP&yg-_-CAdpC9r&Ip$65oT)Kh)V6C@aNd=3*|?p@r7TaO(;{dX
zW(FBrJFNHY)gG|SZ&+bvQ-5$1zW
zA{l0@V!E6|SO#-u45N8cK|Rv$za;`{alKce@Z~MMFdH$1$JhHJzqz)^x3i7h7U%VZ
ze{35%n6rXNP&GE)`yEebIn=$J@Mgp84J*C~B+GHe2*=%seCD3wWr@@2g2Zd%!o2tm
zeMEh3-!rm}|4eZRxd7qwzWI9wd7t22Q)X3Rty0?X5}@?dsJR)TMGPI5U&n(|=`?kp
z=j|KMok~p6hOo#)Ub|Xa3>H8W;?CCNWM^3n_g`S7Ra8!9HWrj@A|nCE_Zb~`yWEpH
z(&*TMik1~9wWB@+Pv;4}jY$*ouO-v`rt4XA&;_ltWv^T^7E{yQpW^!9%}ASEaQ)K-
z+@#%OigElC1K(t;UiDC)LxGKzbnfevpaGISM!=_6dK*zEr#mxh4%a5}VYZb8>J
zMrvYgE&$s|A5uAsz9D`F0L(BZDF!0(*&Cn|o;s$yH;7PmEUh3})G)YrXe7o&&!61a
zY&feoD{K2bqqO`<84)L|mRPKez~;O5=d}flUE%>`P4uHwH`OlPP9%sFj?NC05
z1&0ui-TeN50O(_21l(4u;oB;-!tr)HOV>2V=)|ok4MYN8wn|v8MG72^q)22D^(b=TubzFh|Nuir$|3b<_gkL$QZSVeZBrDGMcC+f|W?H9OY
zM#z?lphC*XItws!sFpDO(9o0%CDeCWpRH}%
z`*ZhTyj_;Rg`zd?FT4W1W48f?)UATS&Jf03Ioi6HiR;FP@z1JP&gVZL?=8J-&POpI
z9}3d!e@iGBY1sG$zN@z5hk4z8J<01PaZl%V|Jq!-fZe%B5n|o6I3gD%Qo=S7pLBnJ
zFYCvlvm!Z+UlXozI9+L~yWnRvpJywK4B?d4t{Wj-E9#^%Fl!ujNnBIZ)gnkb|!MC9qYxW5f6vVWgpu34dfP-Z#Ot}Q0ctlwf8hxwtBjX(NeWW>tk
zftC07eQHLF(+&fnpZXK2h2k_Ov1Ub23nvPbM##}M1F!p0tq?plnj#h7bwmYuPFZjx
zNL2|`+jYT1rxZS-6`YQ!KP0N8j`y+rx!Flp?^*`p&Dri4$H~%q{#fg=5VQ?u?%$zl
znW-V33Tt-6q1?P0#PyL
zrwjCo*oFs|9lR$!%_@I1DiG#t&Hn=8jp<-+wf$vh>DJn@-l~*n&mmIp-G#m7(eozI
zV(La;aFR8*JB{GPb&QXc5nW4`5rI9_VS^e-rA{qxCZN2C%m5WWujB#cNSb|%RZB4c
z1PxC*djDfok9H9mINmVtcI|6j&EQpJt^{Z%SY7A1Ig(wCf7^POab-9=RTA6&@JL$y^3rNYANZ<_n}jfG
zyo9mIovz7x@??<6>n;G26NwACYfSe6NGm70EBqXr~F*$M^dScuUkAIUaBf
z9bIXa&6=8V^)DiTy-Fu5y_&TnXwE!=#8A;<&ezTU4aC?u{PJrOWS1=WU-b2B<}vg?
z9Tv>i*o9N^3rfkpovRc2e-96kW-zWV(p;<<1kAMkl~WyFZqB=DeJgj`zD4tXv2`(c
zlEU<2gpgXG4}ZoEXGS%{ymds8X#Gm`lywAJO1kFBopA{GNw~2@ewC>3Bmmt^2Uatg
zG64mA(D|OxXS?YO=e#_-#*1H4
zVk2KL2-B@DHv;&aS9$)FR*^TWsVxhmJ{{}lqha>YD&xSj$r~TQ@4yC>Y)<80hxE@4
zR8gx_4!Ly~;sxNI=;*#T{e)Fsr?;1xn%F`*3r*niVQVt;W*I3{s;ing3A4|4Y}N`8
zz|CQR(k!#JUS@Ny9)2137kMrXQ03ChO~GBaweVmS9^(Wv+(VRFXv^KVSuLK3q{i|t
zPFz}Be0%8&q&+HgBho97(J64O#2^YSXFkhy_Hv=m+x_V~HU
z&fxXav(#v^(SAaM{Q=R4#xX)8AFJK5j6H%V{=15o3o^BbTC*N$=bAq?cO1Qr81o98
zK0jPeH+Suw%mZm8O+yn3h98ZhK(8vJG-G&!TXf}dKYwLDRJS-99?YI14(^d{qxD6+M
zY4?lB=2oTU4~uyzmrXa!4Sck+7$1(Y)m$&rDA)g~IgH1IthdAS)3i3t)I1V
zqpM+BiH=1hY*8Am%yK9Nb-|yDbnA;oH@OaW?Q2%Lgv~hK;p_844FppB)Apr10w{+T
z)!f2RL89_JH~A3(iJicC4m&tY<`0?_hXgn2IH_$a@SQ%B=22hbZ@GG>;D-mlaq=q%}
z29Gr6HWz8ltZhj3qmV0DdixR+-jg3eE2GHoG)<{tN%n!QLvKeVC+r#dpK>o;m0)N>
zu>L}M{8sTN4SP41&9l}xe>R_$t2?XYXckRxT1Ndew%&Z48wO7`B5wY$ptE3z_l-#4
z((0@bbDEyF_?QfQ#;3yveVAZ*(z;;&j((WU@U4Ww%}pM7GpVvEW0uaAAd9^j?^EmM
zl2*&<*yhaKeWhMgOFs%>YE+T#6ai|wSIV%bbENnLpvIomnvHa!qnGKllAQ7N=Zgg$v=Oq
zs9z{ER?vyIuV}(|h@1hH4Fu14NFHHIWa1px}Hy0iOKTbG8Ps;)BNeh!Tbzl`#M9UfmeFBR_Zj(+J9>(VzRIP}7M
zr%6~4ChvSFG-SPILh(sfYoVp~r{~oDx7`El$;SM(@Ng)+k^sMD=E6R^Lv!Da{CQ}(
zPfC@6evTFp{EMTJNz`3q4n37Y022*h9QFg$2x*Qu9sU7pwVvTmQgdM64n@s5)+>YQ
z+i}Hz)}bjmfC_PHzifS8K4Ycmi4G9S6K|{_=|LA~(Sc5XHnvZM(fUXwGIPIe0!o3Y
zDo|Smmgr&d`Pq2adR@Tu%$f%GbxAE=Um_s(rh9O|tssHf*qR%M@FFxJySDS-uViX9
zM*!=fzR2VJmR&^ykdH`XtiE?GRm{_d(Z`l}%c>_5wT`23X&f$ktIq|bqYMWANO;=L`h*0SPJ%ioM?$Y
z+Z}hCTVmtUr_fhF>=;*$o||zl#TOsBo)o^%b^3I+E!KoRDpmIS&_|<3200|-E82^>
zk#QbOTi4n;SFYo&3GO>~?Zlo}5UzuiuFFNF-`vOQb2BwkUHNy%cPV^BhNgp&tyCM<
zLrhm&y0jIqv{S96T|R33O%6dvOl!-??v>t_pCLMFhq)I1_Iso^vjPMCH)Kj6q$Y(-
z>300~=(0wvZ*t|_Z0hvG{Bcr>5!*26%C8ZwbX0EpFcR(VNH7gqC>G^o>|Yh
zE${QBE3E975DAjHR{n&Pzdxi+-c5_hfc4sAp$=;W^VY5@qCZ`3K;CBhog=Mw&5HZU
z)DD@g?{IRjA|E&;tyDE@s~p7tdB4Wvq=V1gXZ7<`6NtoZOv=7|04Ec~5u*WhJ0a%}
zC0|kI_9f^3oH|UP{!Xi_IalI6Yh&w>IppUpN!(3GjEL|rL;LBA5^}^3yC&6JUQz)u
zos*|codXNg^U;Avyb-5$%oiX0DFSOH24DDqd4yJ*%AXXF-)e(6BDTQbievXV0%WzV
z8Ttu_P^=8lI=FRITk~BbIAeO;3VE56;Lx!nn#23>kmV@4(VB|a
z^1@G8xLcTRA8;xEtI@E#`pYIA4|=_H20;&1H|B^
z687ong`$e~cnE*8K3vwzw7rPzjtOGcf_Yq^3tZ%I#8?{XOsn*HJ0cDPK)~vV64s6_
zee#Jv`UJok={u;bcnaU{n*vEenUrtTYP4&^+_6>98w4}xdC$+M^4vJ+gn8;st1jVl
zbI7J#$+_&)=c2*Cl@8rF7NR*yYzpPOI=vNyTymZFQ8JQRhNI11L0U_|c!`;(V*)H3
zs%cIZG5kwWQzfW)>WTe4ZmWVEfMMpip8dd+%q`RF-X>FK3sHH1{-xL1PLRTV-ZkWljhafkg9r0Q(1Avsm~?vZQe0d
z!y*V%RQgYcM__pW=CPhs?ch?KsG6a6pynKOTRK+!th(!1Bsl%}t>-nZx*5YiBh5t9
z0B2WnTGe(q`n$K+?!AD-DysRtFieC#LD^unvdJK*s*xu-zpd+R3k+&paDx(6eKhkJ
zQqF)BFp(mQrw6~1sTEw)$O0#o=G8KId${c?=4<7#A4PJEe5b==QIYaT2FKj3X7ZU&
zZ1%HAlow*kmnif`areoIDS;_G`gcNxVyY5r76;#$oa`0{zt9A4T1WcOAZb3?EaPxqf{rHLf`t2rxc%
z`jtq3Tqv4PPC9QMlg2vqs(mwj*wh^VmfyID`1jcbJ>h%x!dC^bIV~V?t+gXj`+!
z(0|?(#M4duv|b|Q62L-uNN>Gk;9Ul?*(u)TaE+o3z|eA6{h*mLNxY=FkTmp8XuuVx
z0#zMvTkW0p;15)fMxUbaN092X1V}ZTof_s_l<^ov_|v~I#r@cTOWTE@groU;c(7V*vcE>GTTv16YQTAm`F4eWH+PnwyQ_A7)^)Qnd0rMn=9%>
zAf_=8SHHuR;u)RNkd7wZfc9q!dqdm4SCL$FzWq?L1KM;s^;m~J?PQpWMcg@k;nN>^
z@I}W3zSR6a)eK*s?pk<+FR^N?{Nr3In@oC&;pguUW0&)5F!H;P0j!H5#@t@qGu`K$
zUSpa?d8iDX4{*kjyOVLQ1X?BbA#@F`qE5>jxu>YyhUI;vtASD;o|M53{xa%{i_*2_jSLIPBpHX-aziv?jLZ`3w8q#X=-7^L
zHTts&1&fE-eYrGN+Yh9@)3hk6OCPNpCR0gbIg1{(E3i*a)4v_w9hKdMhf94M-*AlQ1T46{6#2O7)PQp6Hb?xQKayHyf57EAuyv?`?pnjznqD`Lw!05
zV%NUAR3Y1u%)(4+`fk0(&cTC@H4)4sfvyK$&hC(!;<
zqp1$=Xw9~G8a*?T{(iQF$HKD>;>)rY)F=Zy3=-S${jU
zV*Gsjt$xa!>{e0KY_y;8K(KR&y$$d-=)0ytuQap<(SKT2@I!3O=snkSfW`@ZW!aI$
znwf(xQfFHG7)Mo!-@0ny9+-PAvzwV5)DkoY(;BGyzOPOF!`2m6$nlF>Rr@0JZ
z##DH6p|edjJu(#g^0Q^+-5&@?JxfW<%_Pe!HC>;}Q+!pJm2Te|S~alcs7hfn+#HD3
z=%BHcJ_4*cwz{uh4K{g)T$WiUv*_(YYbw`rI`1k(hE~WciTFIvE$c0=$n!;4I(&bJkgcmzDQrPY!1j5M
z*G+lmDc2=7mEnlNw>%O=MOcE@A4+TOzy^j!(=*^=(QdVihelEO^?F5J#F|I5!;#r2
zmRb`*6n#0RrGvr45~&cmRqAEQUuavkz$!i9N`Ivf{(d{DuqEFJbYq%T&w58%x})iN
zK)Sg3ip1a^Eq4Xpk@PpPeUuJ%13c3GPl1NU;fSc!N~zfOe+ms}oeW#1=y0nNLrgRDwdSrv%<+m+s_k3k$kkNdT6F>^Mv_=->!*SEI*B_eRcE$8bHwQF
zyAUb^YJNv>q0G}O-BAv
z)F!P`##h_iK)7A)co>~@^3Kj4ky$JFXr7IoHHin>lXbw!J`ocoh6XXk{hz5^OzF
zVaD%va5R8_c9v?M?H7P^7(eOepX#mm$~WS6QS=LYt>-)oRUckddGRqjP(hR0t9UCn
z370hqQ8ha^A6C4`Vvhf6x}|$?
z9_Hhc!@AnZsCU{n=QH>=P<5q}{Dq*M>S)$9CJDC#g1p%cP#UNh(3-}JwpDH2aGi~c
zE+N~Ik-U!4O3W?0{7m48dUvAZqLSmXw(8BI$LT3J^wHo!AorBkn+UM=rbJb(8@s6d
ztZ&7vLb*}`1K2PdyVjlE{X?q<(g%nYD0AQ+Phm*_;>;yn1e+mESqFP4i1F>)cc@3H
zhNJ1+RRQ`(?EeQ}dy>S*vt*FnHG@zPkkYLA{&Huf?QjR=ck$0#&HvS^%_{n2QsLeI
zaV~%p27pNAp~C!U4fwx%(kcD?fEL}so4-dr|DV4XZT(X;WyGTVz=>-
z%a-|n{vSEZKn*^uB>OLsq5u1p*MZmdeD)U&?tdEhKiK{8gwq!Mza5d+zmFiunWS?`
z$1(jQ&+~C&Ql&>m;+em*2aYIn|4e?l2a5M@%dq8MSos=_D`1{zyFeY0mR5)p!)an}
z-gUN;CZJ}ZHe;|dMe}{u&8JJ#O=|_JalyJj@o*>*f+XwrUmx8sbDmhq{e=A2XaBc@
z{>QJBgGg^hcVAHnP$IniPaZYE;kqwB)BR^he`7;HkL8p8_TqnklmFxO|LYG-z~RYA
zYjXef*}wgx3YpI-m%z6n4bcwOVuhjOzXh_$ocBQaTFHKo7S2Y@n%=WC6=
zqZrsHm1z`cPvkl3?D;=M-3Ac42|%1gp*)#+YjW?ZFd>b@;`{y0S)pxfoobnSXWpIscw_CiTu^!K`gw|130V3o~gC!OB$iRwnb
zw!_w(sj(kcy-&P#nteJF8A-afJh%5%ZUC~5nf;=n)pUvR`~!e~h`c|2Up)+{2!)^}
z?sy*i)f)H~+u7kv`LCOOHqYm?3P}t}h1x-;*fC(w3&+d*H$*(3B=xsi;wQ#&z7oE_
z=j7_#XIx7mC3{K}y@m#!%6iRwN{{y&&s*JZ%3jZu9xepq{>y@KjD@IGrO&lh^UB6&
z-5+%XrhMelDNq}KGR6PSJ*z12=rod;>t#VjID(Jk{nux$g?W;KTL?lE45hDL+Drp>
zF2KLd;&pixSHCwVR~rAu6wa{e1mOIVSk=4zL;d`I@;PnyskUiWh7Lw(W-8TW2u6%3Z8WRn
z#vlR;oup`*bjn$Y{SgFv`|mhGi;{=?+{l=jdP~jrJmMy?uupeQa3|H~`VTz;j)Z%Z
z+u|eDs)gaNr%P~L2&tRea+UXi066mP27>{#LA_d2$raWC&|mPZ7V4zFK8_o;V4t?U
zDdUCH`LJl$d^lMGrUC+3p+t);5B^Pl=lq`iX9}Md(jj$TCn`5P??Xa%!2A1F3k{&I
zL?#u
zHF!zse;5A*`P*A80i-m0+V{~;r9VdjyueFMKT?ie4F8`*<9^zgi#8%Lt;5t`FccyP
zMx!FEI-ub|>yW_NXoEmeZh;z?Ryh#>4#@%bV97=ge8fF~&lHo~6Nr)qLThe!Ca=TE
zQZ*hV16$7}kJ8iE85$TX5%ai?C&x2X8EVEk0mO-gT|PR$XEeMBXt0xeI7AdwH}({PeTJh*LGzhxYC3eT%818ADpy>G{`arF
z_Y{$rdRKx<9j#LRT@H>0cT!AxO^T}O-57)#<(f&xedOqKub0{Zg6}ZEc0jfpMGbk8
z1b{BnxUq@Xx6{u@d8AX}6Zu|EW*xN4>M64szBlD_OdOz%Ua5c-fh><(dX<@B#o%FU
zfJ{cE1n@UMC6{N#VzaNDV36h~bfwuMrBX?tC=3%kQU}$?jH|mSDJi+Jw60{|oV#n5
zYid}RlwQfTXu{~S+?N*~Aipc>
zlS=Rk_VD_1NlIi!L!XIuoqFJGt6KC`9^w6T@J4+Wn)CaYuP;tPsRhaM+StL(F@8%i
z*~}g8WL`1@M5v$jt9Z*B9Cx0%Tp1a?Cfg(~o4IsUWI=Nb(imB?=#bY8`=>JKi-5zK
z#F&fDPPZB?m9CkmZA^3_>*n$BV8!k_DY`%Dw+~f5-XR
z;Z!KE+G^D0SJ}sNci1}ze5-M2>I7P2gI&62S&wGQi>Ir##@(b&Yy_tH0GjuEb$SI>
zgLXX2r5cmziip<*P-OKr%vmCAMe~yRdZ3v?pVPb8mu`n|#S6;WeSNr}TeX_k9}%^V
zL|B@=Z=MpEQ665TAH|Js$)L#CWa2fHOpiO+4{(}BuLhz>z9?SZ)md#7DaKhHOq%&4
z%NDdX8cQ+Uk(~&zFN0)Po;lWfUL1}s?4G67TZ}+yYKNLWramiea~oz5(%e9_6Ur<%
z^oNGBcK&P!zI2#-1f(_CG1UDm__!O<7ZG{70@L1^EQZjxdDOmSyy|KJld
zO|ho}2aXn;6%Rj>M!bM8rS$`7a9ZHZ$3J>f&~G@v#f~z{#Hq%73p|vD9QLWa-%b`a
z&Fw%2Ic52^3Ru3(W3qT?xY$*^Fsh(aCDknEIphz}QZ|97H`dvDxHm(x^V=0t#wPQ<
zpdU0v$(8^_>-lmbK)y;pcr&xH|AJT8u~L2d3@d>77y*-*GZwVe^gVosu^@q;?Zlm(wDX^5I@0uQc&hYUR^xTgLQq1M#?TG~Hxwh^{J2`O?_icvNAm(~`vs{A
z*b?&F1_Ew!fU3(Lqrn6VYZ&Q~2-$8z51di&enGvSD>6;0pKm3xQAbjyJm<7Cls2>8
zY0i;4s>Oh*0?|59moX_VGEFucttrM;H=}Q~UGVqxVT5tv
z7V6>!)Ybo{%%-9>YICL0n~IZI91c_4i)uS98yndbWSMV9+B1EU%V3IukUR+$**;PJ-{vaYgK
zds(9=f4%bnYa})Yg8jvt-Q(jJWVRe%ZRD^325k2*(=|GDY`qVx?6aYXhclO6QeWBu
zryxlWia3^ja7=l`IGnoyerhPUQZiod{35y;AaA2BXDMZH#u;{a5%u&h%-#^05b0aW
z$z4QYjPhY+Q=$3%+>lY#=`Tv*KvHlcx$`a#H)>XoEUmWVYH6VEM}U(=3%5+vkRsCa
z=i^WcxE@iG3Ap^!$IrBJ2&P`4ll+!_zdb1Z?ZWmb)m;^Nxq0<5!&H8DkY26W+}rW<
z?GEej{-Y2G(wre|`_tV_JHWIZDj32{OFc!N!&j;)#noXe?mz(x$y~lay#-pkgD00^gVw;!wjn;GC_wQ
z?R2!WKS5iW{=#&nXV~Iy7T{le{S5@886*f!klDbXVXOlOoPClD*$vf(O`4*<+AwG_
zb`O>Nlr|6bl$vDR|#6L??D#3F6pN{V3{MPSuS>Wuy*(01MN6Ks#L!{=HWaXt3IA}e3UXcGK
zG%`J*tYJtPyd>VxmXpV}Jn*M?$<2dk=7m2&=_*lj7J;KSO19bb5TvxYXarD}2T
z(u)S;fB81Vc6TUu8+
z9~Ku6KK=qQuJhHq5BV`7aUBe|m=ou@${TQz$1R=!Z+~}!YVG_+L>xe1dv_wM63*}K
zkkcE+uMf-Vg{NwBs5Bj(J8lh-u-Sc!r5Z49_HYjG?q4uKZnoT-+)*%(6G~^lfbu_4
z*_$H5=G$lrlEv_!J8H{E`1&rRY)&~i5NCP)Z4lZ{^lFD^8h>m8tulKrsVwycgLOdB
ze&_iVEJsWlQxn)cvi|4ZQ%c*T2DLP8nJr
z3R4t`bViOAEudwtE;RuzZF>5OQlAIJJEW_kC}4h0dJHxqWD5RyI;@||Lre{sa0>2%
za@a~RLuj3;oFQxw{G9wK6LLdz;J!iJ6so3+bZc=K1+<4J9u*BaZ+M!k1o+N{;5fBkB>
zL4Ymj$t}*v4*Wf^H;&HcB|p<;$?RHhI|6FA$ZKs?HM%@$JqfUscaD{MOlcoWvY!pC
zbx&HZEd1>sLV!2`YP4EFl0A=V$ig>&Lx_co_j$1`7&tkO+CG|t)u|Jzcl`|YuqPI<&^wNj;SrWvE
z!sTdg%U(sC=NlsMtZ^`ek3t0AYuXs}qpIJN3frd2z4s()d^8_r2fvHJV}9u**m3Lh
zzW#+Qnrdlj>8_wl(?aL4>t
z*cI43rE@pMj~La6YL;B1yIB~Z{W>IY5qKt~mB{8;6Yl2ic|{N!$Bj&o
zb$!^h0IJu92euzj
zfBl$n>C`5iYyz0&f>DkYOM^Rq>mCEocXs`0ey`83s`hMFK%Go58Lhw8eJ(=7lOar>
zbM40YqGcy(uY?-b5M{uHSq6sm(1RtWe4tO`Q=GFqzOph!h{9VbN;2#Lut|WY7x%hD
zAjeRUE1KIYYjzgh2JK(F!#1>lCvTvAZPq1JA%&SE8`oQ3g@hB#l;@Z)y(=^+f=z@|
zQXT@PNiSJPL2ybzH^P2SGt@-i<}LGXEvr8#bBAsU+Yn4h0HwesHTHock>
z!^|uL`qEie?+r|E(y*1XvQ$mo#XjQUg^$m3pjXIPZJn+%eg2j23aC!y
zO~ISJ^pU3FiQP|8pMMkN0cFRnHiK5oGxIIo^rzG$Qtf9OtaeQtSg1Q#^b>@4d)cXkx!kfCDnl{0~
z3VM37Tn%iZI!`F7Ve%B34up_+|_QF?&<-UH~Hy=A3^>TuZr?nA$zwBWFbcI8(DCiNrK5
zhb#*cMZu&E;`F@0TVxJNRtwVth36@^^YJe=xyZfPdLOO%H5}DOPw8m@ef@d3=WpSJyHRgK#JlCnRDAk~v
z>lGYoT{4VSB~uLw!u^F@$=qhrkM8`yI=fgTI~v~5^Sx3ReT=9D#q=54+S5kq-NJ~J
zPt8Sw25qiD?+gH8V^b4LPw^Z^isMP&s)c5^%&AX$`_T~P!RD)h+o@_C!EBoFOvj5O
zEj^*%j&LB>6BS8(Vf)P|>wC-C5Iu
za5`H|Q?h>Z3GzD)_s0M4`&vJwJW8P8MAtqplC9Pr+nIE1e)emHPxjc>cV|drE73{W
z*|&)vZDnd@ZTwS9$nq51{xQDhr!w8bGEN7+-u6Xb!lIc{J(;O6^Jg!`np=TYdExNU
zSZBISz-hZ=wkgA6vL?9P;F%+iGSl>oM5O{r3B|mujRFM5
zG_@AV8g8J~gHpv+Y-u`<;WpZ*J?n3_{!zlHWadCW298xKOZ9exKTuY)-dsswxcy|c
zLuPjbQr;+WM-qo^CywT7RJwnW7w%rN^onI2uOEYYx@Mzq+qBY)&$mZBp^kIe$>F&7
zA<`7}bd+p<*C2h9u#q|L<*Q`}K}zZ-4Yqm$Ny#hm=#!6#Jui+Ifs1sQUkUR$4ng%~
zp}d>|KDAzhC6%la2`A-jl}?-Hmt#C@%sMO#psk
z?Kxd1{fV@E5R>|y>Tcew4SwZTn0meGz-KC&X~+Evk+4VM>z_2OReLOwpIRiZYz(B2
zcy1ew$zmN8#1$no`5}*#D3YIxyWLgPu(n8pNeH+E)-;-n$)9*=s8#TrHddJeAn3_*
zH`Xn7@K~E#_WNuFHxUA+SfdlHgRy=0N&$VL?`xgF$WE@Avc#m>(K@7WBSiOq>y!Ss
z=hfH8~Awlyl!WrZ?pG^3txh#0z3#M*T|
zIW5`O?SrY*wf_Rm#L!Wx|A}Oe_<=HYHTPPhRY_@x7862TKUoP5pQM2;MX4p1oSHfH
zN`7|9r>Sag%XvzUl_EHn)jKLtT^=K3q3Yg#{|D4&j0z6>s
zlCf5ii`5cSnHw(_j@@3FlEt%A2J#6YSw>SwwL-gWDd1*lFqVj?7J>7yr(#@Vv!^^+
zYO0y8+GdERh+U2i%z0x!k+p*IfU4yx+ceYyd)bIsE|26m>KV&LH96Q+h+n@UBHZqL
zxI<=<>eBACZB6^qP-uuS0g5#ALen?J#@L{JlparblOBLyZHY)twH$%HIaz|Ue>_jTG}C4WpILzobLij6d8u{`MTp6o|g_UBwgTGDi7
zgvnXIdzhZRX>IH)5uX6Qy+45Cw4K`cJzP{;43vNs%zJPkg4O&;f|6VN|Ilsz%y;1*QqMX3*yFjo2h2wARgU$%x^y=H04RyG*s41(0=uE{rh8Ri34KMvHT2wfmddR
z@!`EOUqcAS4?DUhn-+een9_N5BdJ>gUTkIQzzGxn?&>+jxV3mcftm2_^h8TS|y+%XGQ}?-$%}kFYD>Z+MKh
z**+%@EuJ$6&P^vjY6Ed7dF&T$^|i)4u5DLhuG@k9hE9Ven{Y7FR~cZk;tV>@^E|nh
znt)_b-~)3m7_Z#YV03$16EQ?jdGp7Hg!w7Zst9s%7LeF>L|3N5MD%IR6DK-Mv76&B
zmc0n~n|hIFOap$@R70@u4GMFTMW#lx^Fc5!@8aLhq!&^s{qRLAwg&Mvk_3`!hqz=g
z4L%s{&y2ZPyAOo8EYgy7{KS0_ixQ24XruN^=d0IeWGu4|GNiV)ANAmui&k`;_zdo1
z`@ykheJrSsRVMx6Mbh~Y!UM8FGcjNh@
z1PfCc0
zltY`~_p34G0@$T8j^wolHWUQ8tcBJ%F+LSRQ-7
z{RJUdhUvk9=!9;1k7`cgq?5h0lo8YjWobrw&JIQ*+B;9;)BbM@
zOc-+qn2yx^h!8lHI+G4@>nd4LLD}Kz8Ad1ywgWcyJtgJ!7bO80&zdqYUC(zH#`zfr
z?~>ZN4%&YzgHP(^pts8=#J`dnAkk09??JC&IP|7_xLLHNCkIlp11T`P86eNdR8#?Mr9g#4w
zY-zWU+f1%Uc%RF6sukMng3Uyj12#wHY
z<^7UY?A4^1yn+?}%AZ#0(lWj9qO>dx2I_X*&Tyg1mUP{x)0Z@p%~ms$XlYFb1#T_f
zaSVj?PI1YXdO{YJOSr?masuAB&y`j(e5$|l1>L<-UC$%Zu-gSMJI~Bqw0cWr@&%*i
z+wN#~waHYpt@4n=aK9IM5LwF$s^r!y)IkIs$@Pc^gPlfSB^#-+Tt5;vXa6oiuk?E7MX1Gl!N=70-#sA#Ax}RrVn_3#
zd6%$RL3)mN?mH#ERaW%!1sR}~hJ}TtDUop7e$=nSCRzcMX2|j6mP*Y>F{{-Ion3ZA
z3MY;?^zEWx@XGuq2YWCc04K`$!0ZAc0V3eZ%$@_fqFKGvU|%zEK~$vtPRp4SO||Nfo!4MULF
zYhJs#s>*H+Viwx6mPqx+44z`NL)w5rG;pr#!Q=u{u+6|Iot!~zs1VT=M@u->y|{kx
z6HFD73xl}MOXa~|jRH}OI50A{8px`^WOI&T<&bVhZAKPq^LEUMViKrr(x!Hx&KtL=
zI$oqxU?w!?0m_qN^x(qVKd3G0^e+pbe<~VMb6x+m5Fxt!lfaEohXdS=T^;CaA&3=h
z_PbbHbQ#uQhr!InRA#MFmZ-$$v0yhPjrrWEY}6HLtK5&&7sxDsUflsXST5g?A5
z(A{-z&G*rVxIi3~#Xq%9Ay_PsLh6Z8C-(i_Ung~cmb4}$B?B=JS?3_HiTjyJtn!Wv^k`FmsO$+K+~
zz~svIp|Npbeck_KiWaPD=QYdzM?vd!Du^)=1<#n05*zP9#&>gX*^Y21xE1i-xTbNY
zlnlD_WQw)czHgnd5RXFgSHm78&jjX`ys1?8a|u@JLf@cyJ4}0+81$qS7^J7BR+l)t
z(3SvL7-C=7kbNOFDO(A~B@*|T$jfaMW1}eb(yG!m%Sd;d#_dSi3`Z})9eDrlF12Ei
zNeuG!Sd{raS`0QN4mNt54gC8BY&L_l!YGEMSh;D5*
z2D#O$AY-Amgpx=^2zDm;o!T@UwV3>?+;ro;@KMgLWs=3~9jQCNtEj8Mt#GZ_?HVu^BX?HK&o@GDOTU080*(StK_8VIGwqEeTyB)VY
zC8K1%c4sl0ZGSu=PVYl)n1LoUXpZU9WJl}0W}Jn`6K3zzYcQDeRnUMe{xA04GAORD
zTNgzG!3hC^LkQsof&_QB;O-XOCD6Er;O@Z*?gY0$;}U{P;|`5`(>QnKo_oHt@BYr&
zb?g4#|7I6eP1Wo*=NjV~kL+L+?ZiFbMe|E^1nh3qEw@6$_lwQXk07pvPkg!m6{^#N
ze@2;()r|0mE1tFMAf@K_ooM&KXj#
zH>IpA;O3=*FVn>1G#?fyfrwD&XX0|tU%OPSXsheBOR|L9zH(ay?u)OCd=~DRb8;J7E8v2iMRHXewc3uk)a+DUUL2*@
zcRDzhVzL-eD?;}s&9>*yx6}-#ov#m8mS#SR3p9-pe2o9s%B&3HSVxrd^YV=8oYETq6a&A0j3HiiU
zs%-eoRW7`TIa?`KU=SHWQXtob#V6cJ^0q57&TYO7E9ZN&MAPf3%QaB`gG_M);eaP!
zOZA!3Xb(}(EKb*$%7yQp*#LJWnV9O7VR+Ow%y2eb?6^?%@!(>PzTRAW5&sqpCZo+>
z;*RfJ$@nxY*NtM+eA8kD42$9*gNMDSw>twd`d@YqK&~A;PRV<%%vR$?^?&vyxO1{y
z-~OM^au+-U=;Pnuu)e6?5MQn?d!2TW$8U6wUU=pT?aLP1>e|Bf8)Oo%KWKSHQ|=%TvBD&Jht!$e(6Za>H#J&>dNBq
zs9q;@=qkg)e+C4;YASH(qzu(Z2!Y!6TMDLcGJ;qp$c>0JS6e?2$nYAv0YcN_KumP<
z5a3zFmCvm9)?;r%ERz4*P-|QEd5h6N^lgu$_|Uz`Q}0@)cPI@(uWxJN;F;13ls{Ga
z&FNcC{hr~GfFdrz@!+3EBr~wTUy)^1#zhQ2?Rgi{b+dQ~##ismnA!bf|9{NV+8SbH
zD+XUzFA?4$pdnS8SE7mA#dit8;sU&d3K^Bj)2g8K`cd(cM!u)FoCSJf`wU?^tFaNE*WK63g=BvrQ-L@CRe_0z
z4P5e*6JHwq_PT%6Ie)hKBrnea`?F3NO{Zd4BYt~V=;WsixJ#nSY#8v&Lx!xHf-=Pe
z-3I-IQolp7uA;)kaVi2|o(t}xuUFzsdH55a5q(%=42#W41&G12?Wr(xlbTeuT7zNg
zsIUeGt-|<+l~RB977wPaLW7Qvnrcl{@dqg*(#)cpybrf)G9g!|4~g{S$QR&K1#FjP
ztr|70SLLB|D{d^k`HX!a#liIHH&tVNEpIxeq