From cd4317ab3e78e3ba1233c6f3d2610afa07538270 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Dec 2025 19:38:10 +0800 Subject: [PATCH 01/46] =?UTF-8?q?:art:=20#3384=20=E3=80=90=E5=85=AC?= =?UTF-8?q?=E4=BC=97=E5=8F=B7=E3=80=91=E4=B8=BA=20starter=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=20HttpComponents=20(httpclient5)=20=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wx-java-mp-multi-spring-boot-starter/pom.xml | 5 +++++ .../wx-java-mp-spring-boot-starter/pom.xml | 5 +++++ .../wxjava/mp/config/WxMpServiceAutoConfiguration.java | 8 ++++++++ .../spring/starter/wxjava/mp/enums/HttpClientType.java | 4 ++++ 4 files changed, 22 insertions(+) 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 fe10f8332f..1ded1a4e16 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 @@ -44,6 +44,11 @@ okhttp provided + + org.apache.httpcomponents.client5 + httpclient5 + provided + 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 06dfe0d511..2b83d966ac 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 @@ -39,6 +39,11 @@ okhttp provided + + org.apache.httpcomponents.client5 + httpclient5 + provided + diff --git a/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/WxMpServiceAutoConfiguration.java b/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/WxMpServiceAutoConfiguration.java index 3b8733c286..dc6dcafb82 100644 --- a/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/WxMpServiceAutoConfiguration.java +++ b/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/WxMpServiceAutoConfiguration.java @@ -4,6 +4,7 @@ import com.binarywang.spring.starter.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.WxMpServiceHttpComponentsImpl; import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl; import me.chanjar.weixin.mp.api.impl.WxMpServiceJoddHttpImpl; import me.chanjar.weixin.mp.api.impl.WxMpServiceOkHttpImpl; @@ -35,6 +36,9 @@ public WxMpService wxMpService(WxMpConfigStorage configStorage, WxMpProperties w case HttpClient: wxMpService = newWxMpServiceHttpClientImpl(); break; + case HttpComponents: + wxMpService = newWxMpServiceHttpComponentsImpl(); + break; default: wxMpService = newWxMpServiceImpl(); break; @@ -60,4 +64,8 @@ private WxMpService newWxMpServiceJoddHttpImpl() { return new WxMpServiceJoddHttpImpl(); } + private WxMpService newWxMpServiceHttpComponentsImpl() { + return new WxMpServiceHttpComponentsImpl(); + } + } diff --git a/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/enums/HttpClientType.java b/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/enums/HttpClientType.java index f67ef97c2e..0bf034417f 100644 --- a/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/enums/HttpClientType.java +++ b/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/enums/HttpClientType.java @@ -19,4 +19,8 @@ public enum HttpClientType { * JoddHttp. */ JoddHttp, + /** + * HttpComponents (Apache HttpClient 5.x). + */ + HttpComponents, } From 219a8f4f3657cba0ebcb524e0f3fb444c9668e3e Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Dec 2025 19:40:03 +0800 Subject: [PATCH 02/46] =?UTF-8?q?:art:=20#3620=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=90=8C=E5=9F=8E?= =?UTF-8?q?=E9=85=8D=E9=80=81API=E7=AD=BE=E5=90=8D=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E9=97=AE=E9=A2=98=EF=BC=88=E6=B7=BB=E5=8A=A0RSA=E7=A7=81?= =?UTF-8?q?=E9=92=A5=E5=BA=8F=E5=88=97=E5=8F=B7=E5=88=B0=E7=AD=BE=E5=90=8D?= =?UTF-8?q?payload=E5=92=8C=E8=AF=B7=E6=B1=82=E5=A4=B4=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wx/miniapp/api/impl/BaseWxMaServiceImpl.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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 a5d479b65a..93bb2656e6 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 @@ -912,6 +912,10 @@ public String postWithSignature(String url, JsonObject jsonObject) throws WxErro String rndStr = UUID.randomUUID().toString().replace("-", "").substring(0, 30); String aesKey = this.getWxMaConfig().getApiSignatureAesKey(); String aesKeySn = this.getWxMaConfig().getApiSignatureAesKeySn(); + String rsaKeySn = this.getWxMaConfig().getApiSignatureRsaPrivateKeySn(); + if (rsaKeySn == null || rsaKeySn.isEmpty()) { + throw new SecurityException("ApiSignatureRsaPrivateKeySn不能为空,请检查配置"); + } jsonObject.addProperty("_n", rndStr); jsonObject.addProperty("_appid", appId); @@ -956,7 +960,7 @@ public String postWithSignature(String url, JsonObject jsonObject) throws WxErro String requestJson = reqData.toString(); // 计算签名 RSA - String payload = urlPath + "\n" + appId + "\n" + timestamp + "\n" + requestJson; + String payload = urlPath + "\n" + appId + "\n" + timestamp + "\n" + rsaKeySn + "\n" + requestJson; byte[] dataBuffer = payload.getBytes(StandardCharsets.UTF_8); RSAPrivateKey priKey; try { @@ -985,6 +989,7 @@ public String postWithSignature(String url, JsonObject jsonObject) throws WxErro header.put("Wechatmp-Signature", signatureString); header.put("Wechatmp-Appid", appId); header.put("Wechatmp-TimeStamp", String.valueOf(timestamp)); + header.put("Wechatmp-Serial", rsaKeySn); log.debug("发送请求uri:{}, headers:{}, postData:{}", url, header, requestJson); WxMaApiResponse response = this.execute(ApiSignaturePostRequestExecutor.create(this), url, header, requestJson); From 4e46486f2c13f536603eaa1bd403e67173f03081 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Dec 2025 19:43:15 +0800 Subject: [PATCH 03/46] =?UTF-8?q?:art:=20#3598=20=E3=80=90=E4=BC=81?= =?UTF-8?q?=E4=B8=9A=E5=BE=AE=E4=BF=A1=E3=80=91=20=E4=BC=9A=E8=AF=9D?= =?UTF-8?q?=E5=AD=98=E6=A1=A3=E6=8E=A5=E5=8F=A3=E6=B7=BB=E5=8A=A0=E5=AF=B9?= =?UTF-8?q?=E9=9F=B3=E8=A7=86=E9=A2=91=E9=80=9A=E8=AF=9D(voiptext)?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E7=B1=BB=E5=9E=8B=E7=9A=84=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cp/bean/msgaudit/WxCpChatModel.java | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/msgaudit/WxCpChatModel.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/msgaudit/WxCpChatModel.java index c88cb7b9be..21a29abf8f 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/msgaudit/WxCpChatModel.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/msgaudit/WxCpChatModel.java @@ -202,6 +202,12 @@ public class WxCpChatModel implements Serializable { @SerializedName("sphfeed") private SphFeed sphFeed; + /** + * 音视频通话消息 + */ + @SerializedName("voiptext") + private VoipText voipText; + /** * From json wx cp chat model. * @@ -1333,4 +1339,40 @@ public String toJson() { } + /** + * 音视频通话消息 + */ + @Getter + @Setter + public static class VoipText implements Serializable { + private static final long serialVersionUID = -5028321625140879571L; + + @SerializedName("callduration") + private Integer callDuration; + + @SerializedName("invitetype") + private Integer inviteType; + + /** + * From json voip text. + * + * @param json the json + * @return the voip text + */ + public static VoipText fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, VoipText.class); + } + + /** + * To json string. + * + * @return the string + */ + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + } + + } From ddcdf658cf56d6a67cecc9dfacbeca2d5c974757 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Fri, 5 Dec 2025 16:19:06 +0800 Subject: [PATCH 04/46] =?UTF-8?q?:bug:=20#3797=20=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=E5=95=86?= =?UTF-8?q?=E5=AE=B6=E8=BD=AC=E8=B4=A6API=E8=B7=AF=E5=BE=84=E9=94=99?= =?UTF-8?q?=E8=AF=AF=EF=BC=8C=E7=A7=BB=E9=99=A4=E5=A4=9A=E4=BD=99=E7=9A=84?= =?UTF-8?q?operation=E9=83=A8=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wxpay/service/BusinessOperationTransferService.java | 8 ++++---- .../impl/BusinessOperationTransferServiceImpl.java | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/BusinessOperationTransferService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/BusinessOperationTransferService.java index 740c2af83f..195d3a8409 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/BusinessOperationTransferService.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/BusinessOperationTransferService.java @@ -21,7 +21,7 @@ public interface BusinessOperationTransferService { * 发起运营工具商家转账 * * 请求方式:POST(HTTPS) - * 请求地址:https://api.mch.weixin.qq.com/v3/fund-app/operation/mch-transfer/transfer-bills + * 请求地址:https://api.mch.weixin.qq.com/v3/fund-app/mch-transfer/transfer-bills * * 文档地址:运营工具-商家转账API * @@ -37,7 +37,7 @@ public interface BusinessOperationTransferService { * 查询运营工具转账结果 * * 请求方式:GET(HTTPS) - * 请求地址:https://api.mch.weixin.qq.com/v3/fund-app/operation/mch-transfer/transfer-bills/out-bill-no/{out_bill_no} + * 请求地址:https://api.mch.weixin.qq.com/v3/fund-app/mch-transfer/transfer-bills/out-bill-no/{out_bill_no} * * 文档地址:运营工具-商家转账API * @@ -53,7 +53,7 @@ public interface BusinessOperationTransferService { * 通过商户单号查询运营工具转账结果 * * 请求方式:GET(HTTPS) - * 请求地址:https://api.mch.weixin.qq.com/v3/fund-app/operation/mch-transfer/transfer-bills/out-bill-no/{out_bill_no} + * 请求地址:https://api.mch.weixin.qq.com/v3/fund-app/mch-transfer/transfer-bills/out-bill-no/{out_bill_no} * * 文档地址:运营工具-商家转账API * @@ -69,7 +69,7 @@ public interface BusinessOperationTransferService { * 通过微信转账单号查询运营工具转账结果 * * 请求方式:GET(HTTPS) - * 请求地址:https://api.mch.weixin.qq.com/v3/fund-app/operation/mch-transfer/transfer-bills/transfer-bill-no/{transfer_bill_no} + * 请求地址:https://api.mch.weixin.qq.com/v3/fund-app/mch-transfer/transfer-bills/transfer-bill-no/{transfer_bill_no} * * 文档地址:运营工具-商家转账API * diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BusinessOperationTransferServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BusinessOperationTransferServiceImpl.java index 5e74bdbaab..098db127e3 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BusinessOperationTransferServiceImpl.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BusinessOperationTransferServiceImpl.java @@ -33,7 +33,7 @@ public BusinessOperationTransferResult createOperationTransfer(BusinessOperation request.setAppid(this.wxPayService.getConfig().getAppId()); } - String url = String.format("%s/v3/fund-app/operation/mch-transfer/transfer-bills", this.wxPayService.getPayBaseUrl()); + String url = String.format("%s/v3/fund-app/mch-transfer/transfer-bills", this.wxPayService.getPayBaseUrl()); // 如果传入了用户姓名,需要进行RSA加密 if (StringUtils.isNotEmpty(request.getUserName())) { @@ -58,7 +58,7 @@ public BusinessOperationTransferQueryResult queryOperationTransfer(BusinessOpera @Override public BusinessOperationTransferQueryResult queryOperationTransferByOutBillNo(String outBillNo) throws WxPayException { - String url = String.format("%s/v3/fund-app/operation/mch-transfer/transfer-bills/out-bill-no/%s", + String url = String.format("%s/v3/fund-app/mch-transfer/transfer-bills/out-bill-no/%s", this.wxPayService.getPayBaseUrl(), outBillNo); String response = wxPayService.getV3(url); return GSON.fromJson(response, BusinessOperationTransferQueryResult.class); @@ -66,7 +66,7 @@ public BusinessOperationTransferQueryResult queryOperationTransferByOutBillNo(St @Override public BusinessOperationTransferQueryResult queryOperationTransferByTransferBillNo(String transferBillNo) throws WxPayException { - String url = String.format("%s/v3/fund-app/operation/mch-transfer/transfer-bills/transfer-bill-no/%s", + String url = String.format("%s/v3/fund-app/mch-transfer/transfer-bills/transfer-bill-no/%s", this.wxPayService.getPayBaseUrl(), transferBillNo); String response = wxPayService.getV3(url); return GSON.fromJson(response, BusinessOperationTransferQueryResult.class); From 85bd27424d3eacde81ff91c5aa28828de6ad30ee Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Fri, 5 Dec 2025 16:20:18 +0800 Subject: [PATCH 05/46] =?UTF-8?q?:art:=20#3795=20=E3=80=90=E5=B0=8F?= =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E3=80=91=E5=AE=A2=E6=9C=8D=E6=B6=88=E6=81=AF?= =?UTF-8?q?API=E6=96=B0=E5=A2=9E=E4=BA=86=20aimsgcontext=20=E5=AD=97?= =?UTF-8?q?=E6=AE=B5=EF=BC=8C=E7=94=A8=E4=BA=8EAI=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E4=B8=8A=E4=B8=8B=E6=96=87=E5=85=B3=E8=81=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../binarywang/wx/miniapp/bean/WxMaKefuMessage.java | 13 +++++++++++++ .../binarywang/wx/miniapp/builder/BaseBuilder.java | 10 ++++++++++ .../wx/miniapp/bean/WxMaKefuMessageTest.java | 10 ++++++++++ 3 files changed, 33 insertions(+) diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaKefuMessage.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaKefuMessage.java index 0cd77c7937..cdce681175 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaKefuMessage.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaKefuMessage.java @@ -40,6 +40,9 @@ public class WxMaKefuMessage implements Serializable { @SerializedName("miniprogrampage") private KfMaPage maPage; + @SerializedName("aimsgcontext") + private AiMsgContext aiMsgContext; + @Data @AllArgsConstructor @NoArgsConstructor @@ -90,6 +93,16 @@ public static class KfMaPage implements Serializable { private String thumbMediaId; } + @Data + @AllArgsConstructor + @NoArgsConstructor + public static class AiMsgContext implements Serializable { + private static final long serialVersionUID = 1L; + + @SerializedName("msgid") + private String msgId; + } + /** * 获得文本消息builder. */ diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/builder/BaseBuilder.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/builder/BaseBuilder.java index c353534c3f..71f49ee2d3 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/builder/BaseBuilder.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/builder/BaseBuilder.java @@ -8,6 +8,7 @@ public class BaseBuilder { protected String msgType; protected String toUser; + protected String aiMsgContextMsgId; @SuppressWarnings("unchecked") public T toUser(String toUser) { @@ -15,6 +16,12 @@ public T toUser(String toUser) { return (T) this; } + @SuppressWarnings("unchecked") + public T aiMsgContextMsgId(String msgId) { + this.aiMsgContextMsgId = msgId; + return (T) this; + } + /** * 构造器方法. */ @@ -22,6 +29,9 @@ public WxMaKefuMessage build() { WxMaKefuMessage m = new WxMaKefuMessage(); m.setMsgType(this.msgType); m.setToUser(this.toUser); + if (this.aiMsgContextMsgId != null) { + m.setAiMsgContext(new WxMaKefuMessage.AiMsgContext(this.aiMsgContextMsgId)); + } return m; } } diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/WxMaKefuMessageTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/WxMaKefuMessageTest.java index 6486c3237f..c855b2747a 100644 --- a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/WxMaKefuMessageTest.java +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/WxMaKefuMessageTest.java @@ -66,4 +66,14 @@ public void testURLEscaped() { "\"link\":{\"title\":\"title\",\"description\":\"description\",\"url\":\"https://mp.weixin.qq.com/s?__biz=MzI0MDA2OTY5NQ==\",\"thumb_url\":\"thumbUrl\"}}"); } + public void testTextBuilderWithAiMsgContext() { + WxMaKefuMessage reply = WxMaKefuMessage.newTextBuilder() + .toUser("OPENID") + .content("回复内容") + .aiMsgContextMsgId("MSG_ID_123") + .build(); + assertThat(reply.toJson()) + .isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"text\",\"text\":{\"content\":\"回复内容\"},\"aimsgcontext\":{\"msgid\":\"MSG_ID_123\"}}"); + } + } From c0db88091f7039e677f7d792beb3c81b657bdb80 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Fri, 5 Dec 2025 16:28:23 +0800 Subject: [PATCH 06/46] =?UTF-8?q?:art:=20#3791=20=E3=80=90=E5=BE=AE?= =?UTF-8?q?=E4=BF=A1=E6=94=AF=E4=BB=98=E3=80=91=E7=89=B9=E7=BA=A6=E5=95=86?= =?UTF-8?q?=E6=88=B7=E8=BF=9B=E4=BB=B6=E6=8E=A5=E5=8F=A3=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E4=BA=86=E5=B0=8F=E5=BE=AE=E5=95=86=E6=88=B7=EF=BC=88=E4=B8=AA?= =?UTF-8?q?=E4=BD=93=E7=BB=8F=E8=90=A5=E8=80=85=EF=BC=89=E7=9A=84=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=AD=97=E6=AE=B5=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WxPayApplyment4SubCreateRequest.java | 103 ++++++++++++++++-- 1 file changed, 96 insertions(+), 7 deletions(-) diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/WxPayApplyment4SubCreateRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/WxPayApplyment4SubCreateRequest.java index c204e2911e..b7b8a02050 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/WxPayApplyment4SubCreateRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/WxPayApplyment4SubCreateRequest.java @@ -23,7 +23,7 @@ @AllArgsConstructor @Accessors(chain = true) public class WxPayApplyment4SubCreateRequest implements Serializable { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 4104022969945059126L; /** * 业务申请编号 @@ -78,7 +78,7 @@ public class WxPayApplyment4SubCreateRequest implements Serializable { @AllArgsConstructor @Accessors(chain = true) public static class ContactInfo implements Serializable { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = -9087348002744428474L; /** * 超级管理员类型 @@ -211,7 +211,7 @@ public static class ContactInfo implements Serializable { @AllArgsConstructor @Accessors(chain = true) public static class SubjectInfo implements Serializable { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = -6651911735969445765L; /** * 主体类型 @@ -242,6 +242,13 @@ public static class SubjectInfo implements Serializable { @SerializedName("certificate_letter_copy") private String certificateLetterCopy; + /** + * 小微辅助证明材料 + * 主体类型为小微商户时,小微辅助证明材料必填 + */ + @SerializedName("micro_biz_info") + private MicroBizInfo microBizInfo; + /** * 金融机构许可证信息 */ @@ -393,6 +400,88 @@ public static class FinanceInstitutionInfo implements Serializable { private List financeLicensePics; } + /** + * 小微辅助证明材料 + */ + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + @Accessors(chain = true) + public static class MicroBizInfo implements Serializable { + + private static final long serialVersionUID = 2327302539406612422L; + + /** + * 小微经营类型 + * 枚举值: + * MICRO_TYPE_STORE:门店场所 + * MICRO_TYPE_MOBILE:流动经营/便民服务 + * MICRO_TYPE_ONLINE:线上商品/服务交易 + * 示例值:MICRO_TYPE_STORE + */ + @SerializedName("micro_biz_type") + private MicroBizTypeEnum microBizType; + + /** + * 门店名称 + * 1、填写规范: + * 门店场所:填写门店名称 + * 流动经营/便民服务:填写经营/服务名称 + * 线上商品/服务交易:填写线上店铺名称 + * 2、格式规范: + * 长度为1-50个字符 + * 前后不能有空格、制表符、换行符 + * 不能仅含数字、特殊字符 + * 仅能填写数字、英文字母、汉字及特殊字符 + * 仅支持utf-8格式 + * 示例值:大郎烧饼 + */ + @SerializedName("micro_name") + private String microName; + + /** + * 门店省市编码 + * 1、只能由数字组成 + * 2、详细参见微信支付提供的省市对照表 + * 3、填写规范: + * 门店场所:填写门店省市编码 + * 流动经营/便民服务:填写经营/服务所在地省市编码 + * 线上商品/服务交易:填写卖家所在地省市编码 + * 示例值:440305 + */ + @SerializedName("micro_address_code") + private String microAddressCode; + + /** + * 门店地址 + * 1、填写规范: + * 门店场所:填写店铺详细地址,具体区/县及街道门牌号或大厦楼层 + * 流动经营/便民服务:填写"无" + * 线上商品/服务交易:填写电商平台名称 + * 2、格式规范: + * 长度为4-512个字符 + * 前后不能有空格、制表符、换行符 + * 不能仅含数字、特殊字符 + * 仅能填写数字、英文字母、汉字及特殊字符 + * 仅支持utf-8格式 + * 示例值:广东省深圳市南山区xx大厦x层xxxx室 + */ + @SerializedName("micro_address") + private String microAddress; + + /** + * 门店门头照片/经营场景照片 + * 1、门店场所:请上传门头正面照片(要求门店招牌、门框完整、清晰、可辨识);若为停车场等无固定门头照片的经营场所,可上传岗亭/出入闸口; + * 2、流动经营/便民服务:填写媒体文件ID列表,最多5张; + * 3、线上商品/服务交易:请上传线上店铺网页截图(清晰度足够识别店铺名称的首页截图); + * 4、请填写通过《图片上传API》预先上传图片生成好的MediaID + * 示例值:0P3ng6KTIW4-Q_l2FjKLZuhHjBWoMAjmVtCz7ScmhEIThCaV-4BBgVwtNkCHO_XXqK5dE5YdOmFJBZR9FwczhJehHhAZN6BKXQPcs-VvdSo + */ + @SerializedName("micro_pics") + private List microPics; + } + @Data @Builder @NoArgsConstructor @@ -603,7 +692,7 @@ public static class UboInfo implements Serializable { @AllArgsConstructor @Accessors(chain = true) public static class BusinessInfo implements Serializable { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = -8605049544105644011L; /** * 商户简称 @@ -876,7 +965,7 @@ public static class WeworkInfo implements Serializable { @AllArgsConstructor @Accessors(chain = true) public static class SettlementInfo implements Serializable { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = -5025743467243760522L; /** * 入驻结算规则ID @@ -937,7 +1026,7 @@ public static class SettlementInfo implements Serializable { @AllArgsConstructor @Accessors(chain = true) public static class BankAccountInfo implements Serializable { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = -5853122395888860086L; /** * 账户类型 @@ -995,7 +1084,7 @@ public static class BankAccountInfo implements Serializable { @AllArgsConstructor @Accessors(chain = true) public static class AdditionInfo implements Serializable { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = -7526912529114022379L; /** * 法人开户承诺函 From c777dc32a89d9dbb054bfc5d49addecb0af4c4f4 Mon Sep 17 00:00:00 2001 From: buaazyl Date: Wed, 10 Dec 2025 10:07:36 +0800 Subject: [PATCH 07/46] =?UTF-8?q?:art:=20#3799=20=E3=80=90=E5=B0=8F?= =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E3=80=91=E5=8F=91=E8=B4=A7=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E5=8A=9F=E8=83=BD=E8=A1=A5=E5=85=85=E6=B6=88?= =?UTF-8?q?=E6=81=AF=E6=8E=A8=E9=80=81=E5=AD=97=E6=AE=B5=EF=BC=8C=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E8=AE=A2=E5=8D=95=E5=8F=91=E8=B4=A7=E5=92=8C=E7=BB=93?= =?UTF-8?q?=E7=AE=97=E4=BA=8B=E4=BB=B6=E7=9A=84=E5=AE=8C=E6=95=B4=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=E6=8E=A5=E6=94=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chanjar/weixin/common/api/WxConsts.java | 32 +++++++---- .../wx/miniapp/bean/WxMaMessage.java | 56 +++++++++++++++++++ 2 files changed, 76 insertions(+), 12 deletions(-) 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 20da30f586..70c4e47933 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 @@ -465,32 +465,40 @@ public static class EventType { /** * 名称审核事件 */ - public static final String WXA_NICKNAME_AUDIT = "wxa_nickname_audit" ; + 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_ILLEGAL_RECORD = "wxa_illegal_record"; /** - *小程序申诉记录推送 - */ - public static final String WXA_APPEAL_RECORD= "wxa_appeal_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_PRIVACY_APPLY = "wxa_privacy_apply"; /** * 类目审核结果事件推送 */ - public static final String WXA_CATEGORY_AUDIT= "wxa_category_audit"; + 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_PAY_SUCC = "wx_verify_pay_succ"; /** * 小程序微信认证派单事件 */ - public static final String WX_VERIFY_DISPATCH= "wx_verify_dispatch"; - } + public static final String WX_VERIFY_DISPATCH = "wx_verify_dispatch"; + /** + * 提醒需要上传发货信息事件:曾经发过货的小程序,订单超过48小时未发货时 + */ + public static final String TRADE_MANAGE_REMIND_SHIPPING = "trade_manage_remind_shipping"; + /** + * 订单完成发货时、订单结算时 + */ + public static final String TRADE_MANAGE_ORDER_SETTLEMENT = "trade_manage_order_settlement"; + } /** * 上传多媒体(临时素材)文件的类型. diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaMessage.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaMessage.java index d95882a240..88450403e3 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaMessage.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaMessage.java @@ -257,6 +257,62 @@ public class WxMaMessage implements Serializable { */ private String context; + /** + * 微信支付订单号 + */ + @XStreamAlias("transaction_id") + private String transactionId; + /** + * 商户号 + */ + @XStreamAlias("merchant_id") + private String merchantId; + /** + * 子商户号 + */ + @XStreamAlias("sub_merchant_id") + private String subMerchantId; + /** + * 商户订单号 + */ + @XStreamAlias("merchant_trade_no") + private String merchantTradeNo; + /** + * 支付成功时间,秒级时间戳 + */ + @XStreamAlias("pay_time") + private Long payTime; + /** + * 消息文本内容 + */ + @XStreamAlias("msg") + private String msg; + /** + * 发货时间,秒级时间戳 + */ + @XStreamAlias("shipped_time") + private Long shippedTime; + /** + * 预计结算时间,秒级时间戳。发货时推送才有该字段 + */ + @XStreamAlias("estimated_settlement_time") + private Long estimatedSettlementTime; + /** + * 确认收货方式:1. 手动确认收货;2. 自动确认收货。结算时推送才有该字段 + */ + @XStreamAlias("confirm_receive_method") + private Integer confirmReceiveMethod; + /** + * 确认收货时间,秒级时间戳。结算时推送才有该字段 + */ + @XStreamAlias("confirm_receive_time") + private Long confirmReceiveTime; + /** + * 订单结算时间,秒级时间戳。结算时推送才有该字段 + */ + @XStreamAlias("settlement_time") + private Long settlementTime; + /** * 不要直接使用这个字段, * 这个字段只是为了适配 SubscribeMsgPopupEvent SubscribeMsgChangeEvent SubscribeMsgSentEvent From d26172e3937a19bf69da38edb387953f880b73d3 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Wed, 10 Dec 2025 14:27:27 +0800 Subject: [PATCH 08/46] =?UTF-8?q?:art:=20#3802=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=20WxMaExpressOrde?= =?UTF-8?q?rCargo=20=E5=87=A0=E4=B8=AA=E5=AD=97=E6=AE=B5=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E4=BB=A5=E6=94=AF=E6=8C=81=E5=B0=8F=E6=95=B0=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bean/express/request/WxMaExpressOrderCargo.java | 8 ++++---- .../wx/miniapp/api/impl/WxMaExpressServiceImplTest.java | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressOrderCargo.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressOrderCargo.java index 96817a2256..b6de21b9e6 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressOrderCargo.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/express/request/WxMaExpressOrderCargo.java @@ -34,7 +34,7 @@ public class WxMaExpressOrderCargo implements Serializable { * 描述: 单位是千克(kg) * */ - private Integer weight; + private Double weight; /** * 包裹长度 @@ -44,7 +44,7 @@ public class WxMaExpressOrderCargo implements Serializable { * */ @SerializedName("space_x") - private Integer spaceLength; + private Double spaceLength; /** * 包裹宽度 @@ -54,7 +54,7 @@ public class WxMaExpressOrderCargo implements Serializable { * */ @SerializedName("space_y") - private Integer spaceWidth; + private Double spaceWidth; /** * 包裹高度 @@ -64,7 +64,7 @@ public class WxMaExpressOrderCargo implements Serializable { * */ @SerializedName("space_z") - private Integer spaceHeight; + private Double spaceHeight; /** * 包裹中商品详情列表 diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaExpressServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaExpressServiceImplTest.java index bf6e23797e..5cb96c2ab2 100644 --- a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaExpressServiceImplTest.java +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaExpressServiceImplTest.java @@ -116,10 +116,10 @@ public void testAddOrder() throws WxErrorException { goodsCount ++; } cargo.setCount(detailList.size()); - cargo.setWeight(5); - cargo.setSpaceHeight(10); - cargo.setSpaceLength(10); - cargo.setSpaceWidth(10); + cargo.setWeight(1.2); + cargo.setSpaceHeight(10.0); + cargo.setSpaceLength(20.0); + cargo.setSpaceWidth(15.0); cargo.setDetailList(detailList); From ef5f28cb2828ba0e05e4ce51dbac200a499856c5 Mon Sep 17 00:00:00 2001 From: hyf1844 Date: Thu, 11 Dec 2025 16:32:39 +0800 Subject: [PATCH 09/46] =?UTF-8?q?:art:=20#3806=20=E3=80=90=E8=A7=86?= =?UTF-8?q?=E9=A2=91=E5=8F=B7=E3=80=91=E5=BE=AE=E4=BF=A1=E5=B0=8F=E5=BA=97?= =?UTF-8?q?=E8=AE=A2=E5=8D=95=E8=AF=A6=E6=83=85=E6=8E=A5=E5=8F=A3=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E4=BC=9A=E5=91=98=E6=9D=83=E7=9B=8A=E7=AD=89=E4=BC=98?= =?UTF-8?q?=E6=83=A0=E9=87=91=E9=A2=9D=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../weixin/channel/bean/order/OrderProductInfo.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) 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 5c91c61897..e5c37e3cba 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 @@ -241,4 +241,16 @@ public class OrderProductInfo implements Serializable { @JsonProperty("national_subsidy_merchant_discounted_price") private Integer nationalSubsidyMerchantDiscountedPrice; + /** + * 订单内商品维度活动商家补贴,即参与平台补贴活动时商家通过活动报名价优惠的部分,单位为分 + */ + @JsonProperty("platform_activity_merchant_discounted_price") + private Integer platformActivityMerchantDiscountedPrice; + + /** + * 订单内商品维度平台券优惠金额,单位为分 + */ + @JsonProperty("cash_coupon_discounted_price") + private Integer cashCouponDiscountedPrice; + } From b56eddd47b125a3daa94a01530084a9f0ac100af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=8D=E5=87=BA?= Date: Mon, 15 Dec 2025 15:30:31 +0800 Subject: [PATCH 10/46] =?UTF-8?q?:art:=20#3800=20=E3=80=90=E5=BE=AE?= =?UTF-8?q?=E4=BF=A1=E6=94=AF=E4=BB=98=E3=80=91=E5=B0=8F=E5=BE=AE=E5=95=86?= =?UTF-8?q?=E5=AE=B6=E8=BF=9B=E4=BB=B6=20API=20=E8=BF=9B=E8=A1=8C=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E7=BB=93=E6=9E=84=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WxPayApplyment4SubCreateRequest.java | 220 ++++++++++++++---- .../WxPayApplyment4SubCreateRequestTest.java | 104 +++++++++ 2 files changed, 276 insertions(+), 48 deletions(-) create mode 100644 weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/applyment/WxPayApplyment4SubCreateRequestTest.java diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/WxPayApplyment4SubCreateRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/WxPayApplyment4SubCreateRequest.java index b7b8a02050..3251c764fa 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/WxPayApplyment4SubCreateRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyment/WxPayApplyment4SubCreateRequest.java @@ -424,62 +424,186 @@ public static class MicroBizInfo implements Serializable { private MicroBizTypeEnum microBizType; /** - * 门店名称 - * 1、填写规范: - * 门店场所:填写门店名称 - * 流动经营/便民服务:填写经营/服务名称 - * 线上商品/服务交易:填写线上店铺名称 - * 2、格式规范: - * 长度为1-50个字符 - * 前后不能有空格、制表符、换行符 - * 不能仅含数字、特殊字符 - * 仅能填写数字、英文字母、汉字及特殊字符 - * 仅支持utf-8格式 - * 示例值:大郎烧饼 + * 【门店场所】 经营类型为“门店场所”时填写 */ - @SerializedName("micro_name") - private String microName; + @SerializedName("micro_store_info") + private MicroStoreInfo microStoreInfo; /** - * 门店省市编码 - * 1、只能由数字组成 - * 2、详细参见微信支付提供的省市对照表 - * 3、填写规范: - * 门店场所:填写门店省市编码 - * 流动经营/便民服务:填写经营/服务所在地省市编码 - * 线上商品/服务交易:填写卖家所在地省市编码 - * 示例值:440305 + * 【流动经营/便民服务】 经营类型为“流动经营/便民服务”时填写 */ - @SerializedName("micro_address_code") - private String microAddressCode; + @SerializedName("micro_mobile_info") + private MicroMobileInfo microMobileInfo; /** - * 门店地址 - * 1、填写规范: - * 门店场所:填写店铺详细地址,具体区/县及街道门牌号或大厦楼层 - * 流动经营/便民服务:填写"无" - * 线上商品/服务交易:填写电商平台名称 - * 2、格式规范: - * 长度为4-512个字符 - * 前后不能有空格、制表符、换行符 - * 不能仅含数字、特殊字符 - * 仅能填写数字、英文字母、汉字及特殊字符 - * 仅支持utf-8格式 - * 示例值:广东省深圳市南山区xx大厦x层xxxx室 + * 【线上商品/服务交易】 经营场景为“线上商品/服务交易”时填写 */ - @SerializedName("micro_address") - private String microAddress; + @SerializedName("micro_online_info") + private MicroOnlineInfo microOnlineInfo; + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + @Accessors(chain = true) + public static class MicroOnlineInfo implements Serializable { + + private static final long serialVersionUID = -4672635122639034459L; + + /** + * 【线上店铺名称】 填写商家的线上店铺名称 + * 1、长度为1-50个字符; + * 2、前后不能有空格、制表符、换行符; + * 3、不能仅含数字、特殊字符; + * 4、仅能填写数字、英文字母、汉字及特殊字符; + * 5、仅支持utf-8格式。 + */ + @SerializedName("micro_online_store") + private String microOnlineStore; + + /** + * 【电商平台名称】 填写电商平台名称 + * 1、长度为1-50个字符; + * 2、前后不能有空格、制表符、换行符; + * 3、不能仅含数字、特殊字符; + * 4、仅能填写数字、英文字母、汉字及特殊字符; + * 5、仅支持utf-8格式。 + */ + @SerializedName("micro_ec_name") + private String microEcName; + + /** + * 【店铺二维码】 + * 1、店铺二维码或店铺链接二选一必填; + * 2、若为电商小程序,可上传店铺页面的小程序二维码; + * 3、可上传1张图片,请填写通过图片上传API预先上传图片生成好的MediaID。 + */ + @SerializedName("micro_qrcode") + private String microQrcode; + + /** + * 【店铺链接】 + * 1、店铺二维码或店铺链接二选一必填; + * 2、请填写店铺主页链接,需符合网站规范。 + */ + @SerializedName("micro_link") + private String microLink; + + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + @Accessors(chain = true) + public static class MicroMobileInfo implements Serializable { + + private static final long serialVersionUID = -4672635122639034460L; + + /** + * 【经营/服务名称】 请填写经营/服务名称 + * 1、长度为1-50个字符; + * 2、前后不能有空格、制表符、换行符; + * 3、不能仅含数字、特殊字符; + * 4、仅能填写数字、英文字母、汉字及特殊字符; + * 5、仅支持utf-8格式。 + */ + @SerializedName("micro_mobile_name") + private String microMobileName; + + /** + * 【经营/服务所在地省市】 请填写经营/服务所在地省市编码 + */ + @SerializedName("micro_mobile_city") + private String microMobileCity; + + /** + * 【经营/服务所在地(不含省市)】 填写“无" + */ + @SerializedName("micro_mobile_address") + private String microMobileAddress; + + /** + * 【经营/服务现场照片】 + * 1、提交流动经营现场照片,如摊位场景应提交摊位全景照片+商品照片。 + * 2、可上传多张图片,请填写通过图片上传API预先上传图片生成好的MediaID。 + */ + @SerializedName("micro_mobile_pics") + private List microMobilePics; + + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + @Accessors(chain = true) + public static class MicroStoreInfo implements Serializable { + + private static final long serialVersionUID = -4672635122639034461L; + + /** + * 【门店名称】 + * 请填写门店名称 + * 1、长度为1-50个字符; + * 2、前后不能有空格、制表符、换行符; + * 3、不能仅含数字、特殊字符; + * 4、仅能填写数字、英文字母、汉字及特殊字符; + * 5、仅支持utf-8格式。 + */ + @SerializedName("micro_name") + private String microName; + + /** + * 【门店省市编码】 + * 填写门店省市编码,只能由数字组成,详细参见微信支付提供的省市对照表 + */ + @SerializedName("micro_address_code") + private String microAddressCode; + + /** + * 【门店地址】 + * 请填写详细的经营场所信息,如有多个场所,选择一个主要场所填写即可。 + * 1、长度为4-512个字符; + * 2、前后不能有空格、制表符、换行符; + * 3、不能仅含数字、特殊字符; + * 4、仅能填写数字、英文字母、汉字及特殊字符; + * 5、仅支持utf-8格式。 + */ + @SerializedName("micro_address") + private String microAddress; + + /** + * 【门店门头照片】 + * 1、请上传门头正面照片(要求门店招牌、门框完整、清晰、可辨识);若为停车场等无固定门头照片的经营场所,可上传岗亭/出入闸口。 + * 2、可上传1张图片,请填写通过图片上传API预先上传图片生成好的MediaID。 + */ + @SerializedName("store_entrance_pic") + private String storeEntrancePic; + + /** + * 【店内环境照片】 + * 1、请上传门店内部环境照片(可辨识经营内容)。若为停车场等无固定门头的经营场所,可上传停车场内部照片。 + * 2、可上传1张图片,请填写通过图片上传API预先上传图片生成好的MediaID。 + */ + @SerializedName("micro_indoor_copy") + private String microIndoorCopy; + + /** + * 【门店经度】 数字或小数,商户自定义字段 + */ + @SerializedName("store_longitude") + private String storeLongitude; + + /** + * 【门店纬度】 纬度,商户自定义字段 + */ + @SerializedName("store_latitude") + private String storeLatitude; + + } + - /** - * 门店门头照片/经营场景照片 - * 1、门店场所:请上传门头正面照片(要求门店招牌、门框完整、清晰、可辨识);若为停车场等无固定门头照片的经营场所,可上传岗亭/出入闸口; - * 2、流动经营/便民服务:填写媒体文件ID列表,最多5张; - * 3、线上商品/服务交易:请上传线上店铺网页截图(清晰度足够识别店铺名称的首页截图); - * 4、请填写通过《图片上传API》预先上传图片生成好的MediaID - * 示例值:0P3ng6KTIW4-Q_l2FjKLZuhHjBWoMAjmVtCz7ScmhEIThCaV-4BBgVwtNkCHO_XXqK5dE5YdOmFJBZR9FwczhJehHhAZN6BKXQPcs-VvdSo - */ - @SerializedName("micro_pics") - private List microPics; } @Data diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/applyment/WxPayApplyment4SubCreateRequestTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/applyment/WxPayApplyment4SubCreateRequestTest.java new file mode 100644 index 0000000000..6dad6d2a80 --- /dev/null +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/applyment/WxPayApplyment4SubCreateRequestTest.java @@ -0,0 +1,104 @@ +package com.github.binarywang.wxpay.bean.applyment; + +import java.util.Arrays; + +import org.testng.annotations.Test; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +public class WxPayApplyment4SubCreateRequestTest { + + @Test + public void testMicroBizInfoSerialization() { + // 1. Test MicroStoreInfo + WxPayApplyment4SubCreateRequest.SubjectInfo.MicroBizInfo.MicroStoreInfo storeInfo = + WxPayApplyment4SubCreateRequest.SubjectInfo.MicroBizInfo.MicroStoreInfo.builder() + .microName("门店名称") + .microAddressCode("440305") + .microAddress("详细地址") + .storeEntrancePic("media_id_1") + .microIndoorCopy("media_id_2") + .storeLongitude("113.941046") + .storeLatitude("22.546154") + .build(); + + // 2. Test MicroMobileInfo + WxPayApplyment4SubCreateRequest.SubjectInfo.MicroBizInfo.MicroMobileInfo mobileInfo = + WxPayApplyment4SubCreateRequest.SubjectInfo.MicroBizInfo.MicroMobileInfo.builder() + .microMobileName("流动经营名称") + .microMobileCity("440305") + .microMobileAddress("无") + .microMobilePics(Arrays.asList("media_id_3", "media_id_4")) + .build(); + + // 3. Test MicroOnlineInfo + WxPayApplyment4SubCreateRequest.SubjectInfo.MicroBizInfo.MicroOnlineInfo onlineInfo = + WxPayApplyment4SubCreateRequest.SubjectInfo.MicroBizInfo.MicroOnlineInfo.builder() + .microOnlineStore("线上店铺名称") + .microEcName("电商平台名称") + .microQrcode("media_id_5") + .microLink("https://www.example.com") + .build(); + + WxPayApplyment4SubCreateRequest.SubjectInfo.MicroBizInfo microBizInfo = + WxPayApplyment4SubCreateRequest.SubjectInfo.MicroBizInfo.builder() + .microStoreInfo(storeInfo) + .microMobileInfo(mobileInfo) + .microOnlineInfo(onlineInfo) + .build(); + + Gson gson = new GsonBuilder().create(); + String json = gson.toJson(microBizInfo); + + // Verify MicroStoreInfo serialization + assertTrue(json.contains("\"micro_name\":\"门店名称\"")); + assertTrue(json.contains("\"micro_address_code\":\"440305\"")); + assertTrue(json.contains("\"micro_address\":\"详细地址\"")); + assertTrue(json.contains("\"store_entrance_pic\":\"media_id_1\"")); + assertTrue(json.contains("\"micro_indoor_copy\":\"media_id_2\"")); + assertTrue(json.contains("\"store_longitude\":\"113.941046\"")); + assertTrue(json.contains("\"store_latitude\":\"22.546154\"")); + + // Verify MicroMobileInfo serialization + assertTrue(json.contains("\"micro_mobile_name\":\"流动经营名称\"")); + assertTrue(json.contains("\"micro_mobile_city\":\"440305\"")); + assertTrue(json.contains("\"micro_mobile_address\":\"无\"")); + assertTrue(json.contains("\"micro_mobile_pics\":[\"media_id_3\",\"media_id_4\"]")); + + // Verify MicroOnlineInfo serialization + assertTrue(json.contains("\"micro_online_store\":\"线上店铺名称\"")); + assertTrue(json.contains("\"micro_ec_name\":\"电商平台名称\"")); + assertTrue(json.contains("\"micro_qrcode\":\"media_id_5\"")); + assertTrue(json.contains("\"micro_link\":\"https://www.example.com\"")); + + // Verify deserialization + WxPayApplyment4SubCreateRequest.SubjectInfo.MicroBizInfo deserialized = + gson.fromJson(json, WxPayApplyment4SubCreateRequest.SubjectInfo.MicroBizInfo.class); + + // Verify MicroStoreInfo deserialization + assertEquals(deserialized.getMicroStoreInfo().getMicroName(), "门店名称"); + assertEquals(deserialized.getMicroStoreInfo().getMicroAddressCode(), "440305"); + assertEquals(deserialized.getMicroStoreInfo().getMicroAddress(), "详细地址"); + assertEquals(deserialized.getMicroStoreInfo().getStoreEntrancePic(), "media_id_1"); + assertEquals(deserialized.getMicroStoreInfo().getMicroIndoorCopy(), "media_id_2"); + assertEquals(deserialized.getMicroStoreInfo().getStoreLongitude(), "113.941046"); + assertEquals(deserialized.getMicroStoreInfo().getStoreLatitude(), "22.546154"); + + // Verify MicroMobileInfo deserialization + assertEquals(deserialized.getMicroMobileInfo().getMicroMobileName(), "流动经营名称"); + assertEquals(deserialized.getMicroMobileInfo().getMicroMobileCity(), "440305"); + assertEquals(deserialized.getMicroMobileInfo().getMicroMobileAddress(), "无"); + assertEquals(deserialized.getMicroMobileInfo().getMicroMobilePics().size(), 2); + assertEquals(deserialized.getMicroMobileInfo().getMicroMobilePics(), Arrays.asList("media_id_3", "media_id_4")); + + // Verify MicroOnlineInfo deserialization + assertEquals(deserialized.getMicroOnlineInfo().getMicroOnlineStore(), "线上店铺名称"); + assertEquals(deserialized.getMicroOnlineInfo().getMicroEcName(), "电商平台名称"); + assertEquals(deserialized.getMicroOnlineInfo().getMicroQrcode(), "media_id_5"); + assertEquals(deserialized.getMicroOnlineInfo().getMicroLink(), "https://www.example.com"); + } +} From b9f49094c02aaae4b8b048fcd1cb9e7b29563cca Mon Sep 17 00:00:00 2001 From: Binary Wang Date: Sat, 20 Dec 2025 11:54:34 +0800 Subject: [PATCH 11/46] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=B8=AD=E6=96=87?= =?UTF-8?q?=E8=BE=93=E5=87=BA=E5=92=8C=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E8=A6=81=E6=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 更新了中文输出要求,添加了单元测试代码的说明。 --- .github/agents/my-agent.agent.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/agents/my-agent.agent.md b/.github/agents/my-agent.agent.md index dcce85fd88..0c8481288a 100644 --- a/.github/agents/my-agent.agent.md +++ b/.github/agents/my-agent.agent.md @@ -10,4 +10,5 @@ description: 需要用中文,包括PR标题和分析总结过程 # My Agent -请使用中文输出思考过程和总结,包括PR标题,提交commit信息也要使用中文 +1、请使用中文输出思考过程和总结,包括PR标题,提交commit信息也要使用中文; +2、生成代码时需要提供必要的单元测试代码。 From 81816456c1a99553e8641202d149892b6f0a76e7 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sat, 20 Dec 2025 17:18:22 +0800 Subject: [PATCH 12/46] =?UTF-8?q?:new:=20#3815=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=E6=99=BA?= =?UTF-8?q?=E8=83=BD=E6=9C=BA=E5=99=A8=E4=BA=BA=E6=B6=88=E6=81=AF=E6=8E=A5?= =?UTF-8?q?=E6=94=B6=E5=92=8C=E4=B8=BB=E5=8A=A8=E5=8F=91=E9=80=81=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- weixin-java-cp/INTELLIGENT_ROBOT.md | 42 ++++++++++++++ .../cp/api/WxCpIntelligentRobotService.java | 10 ++++ .../impl/WxCpIntelligentRobotServiceImpl.java | 6 ++ ...xCpIntelligentRobotSendMessageRequest.java | 56 +++++++++++++++++++ ...CpIntelligentRobotSendMessageResponse.java | 42 ++++++++++++++ .../cp/bean/message/WxCpXmlMessage.java | 18 ++++++ .../weixin/cp/constant/WxCpApiPathConsts.java | 6 ++ .../WxCpIntelligentRobotServiceImplTest.java | 34 +++++++++++ .../cp/bean/message/WxCpXmlMessageTest.java | 26 +++++++++ 9 files changed, 240 insertions(+) create mode 100644 weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/intelligentrobot/WxCpIntelligentRobotSendMessageRequest.java create mode 100644 weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/intelligentrobot/WxCpIntelligentRobotSendMessageResponse.java diff --git a/weixin-java-cp/INTELLIGENT_ROBOT.md b/weixin-java-cp/INTELLIGENT_ROBOT.md index f2641bd6b4..dcd90e1a1a 100644 --- a/weixin-java-cp/INTELLIGENT_ROBOT.md +++ b/weixin-java-cp/INTELLIGENT_ROBOT.md @@ -73,6 +73,42 @@ String sessionId = "session123"; robotService.resetSession(robotId, userid, sessionId); ``` +### 主动发送消息 + +智能机器人可以主动向用户发送消息,用于推送通知或提醒。 + +```java +WxCpIntelligentRobotSendMessageRequest request = new WxCpIntelligentRobotSendMessageRequest(); +request.setRobotId("robot_id_here"); +request.setUserid("user123"); +request.setMessage("您好,这是来自智能机器人的主动消息"); +request.setSessionId("session123"); // 可选,用于保持会话连续性 + +WxCpIntelligentRobotSendMessageResponse response = robotService.sendMessage(request); +String msgId = response.getMsgId(); +String sessionId = response.getSessionId(); +``` + +### 接收用户消息 + +当用户向智能机器人发送消息时,企业微信会通过回调接口推送消息。可以使用 `WxCpXmlMessage` 接收和解析这些消息: + +```java +// 在接收回调消息的接口中 +WxCpXmlMessage message = WxCpXmlMessage.fromEncryptedXml( + requestBody, wxCpConfigStorage, timestamp, nonce, msgSignature +); + +// 获取智能机器人相关字段 +String robotId = message.getRobotId(); // 机器人ID +String sessionId = message.getSessionId(); // 会话ID +String content = message.getContent(); // 消息内容 +String fromUser = message.getFromUserName(); // 发送用户 + +// 处理消息并回复 +// ... +``` + ### 删除智能机器人 ```java @@ -87,13 +123,19 @@ robotService.deleteRobot(robotId); - `WxCpIntelligentRobotCreateRequest`: 创建机器人请求 - `WxCpIntelligentRobotUpdateRequest`: 更新机器人请求 - `WxCpIntelligentRobotChatRequest`: 智能对话请求 +- `WxCpIntelligentRobotSendMessageRequest`: 主动发送消息请求 ### 响应类 - `WxCpIntelligentRobotCreateResponse`: 创建机器人响应 - `WxCpIntelligentRobotChatResponse`: 智能对话响应 +- `WxCpIntelligentRobotSendMessageResponse`: 主动发送消息响应 - `WxCpIntelligentRobot`: 机器人信息实体 +### 消息接收 + +- `WxCpXmlMessage`: 支持接收智能机器人回调消息,包含 `robotId` 和 `sessionId` 字段 + ### 服务接口 - `WxCpIntelligentRobotService`: 智能机器人服务接口 diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpIntelligentRobotService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpIntelligentRobotService.java index f68092918f..bc5f3f1915 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpIntelligentRobotService.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpIntelligentRobotService.java @@ -64,4 +64,14 @@ public interface WxCpIntelligentRobotService { */ void resetSession(String robotId, String userid, String sessionId) throws WxErrorException; + /** + * 智能机器人主动发送消息 + * 官方文档: https://developer.work.weixin.qq.com/document/path/100719 + * + * @param request 发送消息请求参数 + * @return 发送消息响应 + * @throws WxErrorException 微信接口异常 + */ + WxCpIntelligentRobotSendMessageResponse sendMessage(WxCpIntelligentRobotSendMessageRequest request) throws WxErrorException; + } \ No newline at end of file diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpIntelligentRobotServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpIntelligentRobotServiceImpl.java index c3bb23b38f..8a12fa4ff4 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpIntelligentRobotServiceImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpIntelligentRobotServiceImpl.java @@ -61,4 +61,10 @@ public void resetSession(String robotId, String userid, String sessionId) throws this.cpService.post(RESET_SESSION, jsonObject.toString()); } + @Override + public WxCpIntelligentRobotSendMessageResponse sendMessage(WxCpIntelligentRobotSendMessageRequest request) throws WxErrorException { + String responseText = this.cpService.post(SEND_MESSAGE, request.toJson()); + return WxCpIntelligentRobotSendMessageResponse.fromJson(responseText); + } + } \ No newline at end of file diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/intelligentrobot/WxCpIntelligentRobotSendMessageRequest.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/intelligentrobot/WxCpIntelligentRobotSendMessageRequest.java new file mode 100644 index 0000000000..405c67daff --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/intelligentrobot/WxCpIntelligentRobotSendMessageRequest.java @@ -0,0 +1,56 @@ +package me.chanjar.weixin.cp.bean.intelligentrobot; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.io.Serializable; + +/** + * 智能机器人发送消息请求 + * 官方文档: https://developer.work.weixin.qq.com/document/path/100719 + * + * @author Binary Wang + */ +@Data +public class WxCpIntelligentRobotSendMessageRequest implements Serializable { + private static final long serialVersionUID = -1L; + + /** + * 机器人ID,必填 + */ + @SerializedName("robot_id") + private String robotId; + + /** + * 接收消息的用户ID,必填 + */ + @SerializedName("userid") + private String userid; + + /** + * 消息内容,必填 + */ + @SerializedName("message") + private String message; + + /** + * 会话ID,可选,用于保持会话连续性 + */ + @SerializedName("session_id") + private String sessionId; + + /** + * 消息ID,可选 + */ + @SerializedName("msg_id") + private String msgId; + + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + public static WxCpIntelligentRobotSendMessageRequest fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpIntelligentRobotSendMessageRequest.class); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/intelligentrobot/WxCpIntelligentRobotSendMessageResponse.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/intelligentrobot/WxCpIntelligentRobotSendMessageResponse.java new file mode 100644 index 0000000000..8098d2037e --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/intelligentrobot/WxCpIntelligentRobotSendMessageResponse.java @@ -0,0 +1,42 @@ +package me.chanjar.weixin.cp.bean.intelligentrobot; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import me.chanjar.weixin.cp.bean.WxCpBaseResp; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +import java.io.Serializable; + +/** + * 智能机器人发送消息响应 + * 官方文档: https://developer.work.weixin.qq.com/document/path/100719 + * + * @author Binary Wang + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class WxCpIntelligentRobotSendMessageResponse extends WxCpBaseResp implements Serializable { + private static final long serialVersionUID = -1L; + + /** + * 消息ID + */ + @SerializedName("msg_id") + private String msgId; + + /** + * 会话ID + */ + @SerializedName("session_id") + private String sessionId; + + public static WxCpIntelligentRobotSendMessageResponse fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpIntelligentRobotSendMessageResponse.class); + } + + @Override + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } +} 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 c5e55220e5..d15eda8826 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 @@ -253,6 +253,24 @@ public class WxCpXmlMessage implements Serializable { @XStreamConverter(value = XStreamCDataConverter.class) private String linkId; + /** + * 智能机器人ID + * 接收智能机器人消息时使用 + * https://developer.work.weixin.qq.com/document/path/100719 + */ + @XStreamAlias("RobotId") + @XStreamConverter(value = XStreamCDataConverter.class) + private String robotId; + + /** + * 智能机器人会话ID + * 接收智能机器人消息时使用,用于保持会话连续性 + * https://developer.work.weixin.qq.com/document/path/100719 + */ + @XStreamAlias("SessionId") + @XStreamConverter(value = XStreamCDataConverter.class) + private String sessionId; + /** * 通讯录变更事件. * 请参考常量 me.chanjar.weixin.cp.constant.WxCpConsts.ContactChangeType 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 91314e5872..ad4d4f33f2 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 @@ -1666,5 +1666,11 @@ interface IntelligentRobot { * 重置智能机器人会话 */ String RESET_SESSION = "/cgi-bin/intelligent_robot/reset_session"; + + /** + * 智能机器人主动发送消息 + * 官方文档: https://developer.work.weixin.qq.com/document/path/100719 + */ + String SEND_MESSAGE = "/cgi-bin/intelligent_robot/send_message"; } } diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpIntelligentRobotServiceImplTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpIntelligentRobotServiceImplTest.java index 2765b49916..85104ee73a 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpIntelligentRobotServiceImplTest.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpIntelligentRobotServiceImplTest.java @@ -85,4 +85,38 @@ public void testServiceIntegration() { assert this.wxCpService.getIntelligentRobotService() != null; assert this.wxCpService.getIntelligentRobotService() instanceof WxCpIntelligentRobotServiceImpl; } + + @Test + public void testSendMessageRequest() { + // 测试主动发送消息请求对象创建 + WxCpIntelligentRobotSendMessageRequest request = new WxCpIntelligentRobotSendMessageRequest(); + request.setRobotId("robot123"); + request.setUserid("user123"); + request.setMessage("您好,这是来自智能机器人的主动消息"); + request.setSessionId("session123"); + request.setMsgId("msg123"); + + // 验证JSON序列化 + String json = request.toJson(); + assert json.contains("robot123"); + assert json.contains("您好,这是来自智能机器人的主动消息"); + assert json.contains("session123"); + + // 验证反序列化 + WxCpIntelligentRobotSendMessageRequest fromJson = WxCpIntelligentRobotSendMessageRequest.fromJson(json); + assert fromJson.getRobotId().equals("robot123"); + assert fromJson.getMessage().equals("您好,这是来自智能机器人的主动消息"); + assert fromJson.getSessionId().equals("session123"); + } + + @Test + public void testSendMessageResponse() { + // 测试主动发送消息响应对象 + String responseJson = "{\"errcode\":0,\"errmsg\":\"ok\",\"msg_id\":\"msg123\",\"session_id\":\"session123\"}"; + WxCpIntelligentRobotSendMessageResponse response = WxCpIntelligentRobotSendMessageResponse.fromJson(responseJson); + + assert response.getMsgId().equals("msg123"); + assert response.getSessionId().equals("session123"); + assert response.getErrcode() == 0; + } } \ No newline at end of file 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 ae4fbba8f6..94874519c0 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 @@ -471,4 +471,30 @@ public void testMsgIdStringAndNumericFormats() { WxCpXmlMessage wxMessageString = WxCpXmlMessage.fromXml(xmlWithString); assertEquals(wxMessageString.getMsgId(), "CAIQg/PKxgYY2sC9tpuAgAMg9/zKaw=="); } + + /** + * Test intelligent robot message parsing + * 测试智能机器人消息解析 + */ + public void testIntelligentRobotMessage() { + String xml = "" + + "" + + "" + + "1348831860" + + "" + + "" + + "msg123456" + + "" + + "" + + ""; + WxCpXmlMessage wxMessage = WxCpXmlMessage.fromXml(xml); + assertEquals(wxMessage.getToUserName(), "toUser"); + assertEquals(wxMessage.getFromUserName(), "fromUser"); + assertEquals(wxMessage.getCreateTime(), Long.valueOf(1348831860)); + assertEquals(wxMessage.getMsgType(), WxConsts.XmlMsgType.TEXT); + assertEquals(wxMessage.getContent(), "你好,智能机器人"); + assertEquals(wxMessage.getMsgId(), "msg123456"); + assertEquals(wxMessage.getRobotId(), "robot_id_123"); + assertEquals(wxMessage.getSessionId(), "session_id_456"); + } } From c8cdfb56e1c46f17b94386093a6349d1270bdb5d Mon Sep 17 00:00:00 2001 From: Sexy Six <38038850+xgl6@users.noreply.github.com> Date: Sat, 20 Dec 2025 17:20:45 +0800 Subject: [PATCH 13/46] =?UTF-8?q?:new:=20#3816=20=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=20V3=20?= =?UTF-8?q?=E5=8C=BB=E4=BF=9D=E7=9B=B8=E5=85=B3=E6=8E=A5=E5=8F=A3=E7=9A=84?= =?UTF-8?q?=E5=AE=8C=E6=95=B4=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wxpay/bean/mipay/MedInsOrdersRequest.java | 568 ++++++++++++++++++ .../wxpay/bean/mipay/MedInsOrdersResult.java | 497 +++++++++++++++ .../bean/mipay/MedInsRefundNotifyRequest.java | 116 ++++ .../bean/mipay/enums/CashAddTypeEnum.java | 29 + .../bean/mipay/enums/CashReduceTypeEnum.java | 44 ++ .../bean/mipay/enums/MedInsPayStatusEnum.java | 44 ++ .../bean/mipay/enums/MixPayStatusEnum.java | 39 ++ .../bean/mipay/enums/MixPayTypeEnum.java | 41 ++ .../wxpay/bean/mipay/enums/OrderTypeEnum.java | 87 +++ .../bean/mipay/enums/SelfPayStatusEnum.java | 44 ++ .../bean/mipay/enums/UserCardTypeEnum.java | 54 ++ .../bean/notify/MiPayNotifyV3Result.java | 265 ++++++++ .../wxpay/service/MiPayService.java | 94 +++ .../wxpay/service/WxPayService.java | 8 + .../service/impl/BaseWxPayServiceImpl.java | 39 +- .../wxpay/service/impl/MiPayServiceImpl.java | 68 +++ .../service/impl/MiPayServiceImplTest.java | 148 +++++ 17 files changed, 2166 insertions(+), 19 deletions(-) create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/MedInsOrdersRequest.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/MedInsOrdersResult.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/MedInsRefundNotifyRequest.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/enums/CashAddTypeEnum.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/enums/CashReduceTypeEnum.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/enums/MedInsPayStatusEnum.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/enums/MixPayStatusEnum.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/enums/MixPayTypeEnum.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/enums/OrderTypeEnum.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/enums/SelfPayStatusEnum.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/enums/UserCardTypeEnum.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/MiPayNotifyV3Result.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/MiPayService.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/MiPayServiceImpl.java create mode 100644 weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/MiPayServiceImplTest.java diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/MedInsOrdersRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/MedInsOrdersRequest.java new file mode 100644 index 0000000000..1819b328c8 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/MedInsOrdersRequest.java @@ -0,0 +1,568 @@ +package com.github.binarywang.wxpay.bean.mipay; + +import com.github.binarywang.wxpay.bean.mipay.enums.CashAddTypeEnum; +import com.github.binarywang.wxpay.bean.mipay.enums.CashReduceTypeEnum; +import com.github.binarywang.wxpay.bean.mipay.enums.MixPayTypeEnum; +import com.github.binarywang.wxpay.bean.mipay.enums.OrderTypeEnum; +import com.github.binarywang.wxpay.bean.mipay.enums.UserCardTypeEnum; +import com.github.binarywang.wxpay.v3.SpecEncrypt; +import com.google.gson.annotations.SerializedName; +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 医保自费混合收款下单请求 + *

+ * 从业机构调用该接口向微信医保后台下单 + * 文档地址:https://pay.weixin.qq.com/doc/v3/partner/4012503131 + * @author xgl + * @date 2025/12/19 14:37 + */ +@Data +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor +public class MedInsOrdersRequest { + + /** + *

+   * 字段名:混合支付类型
+   * 变量名:mix_pay_type
+   * 必填:是
+   * 类型:string
+   * 描述:
+   *   混合支付类型可选取值:
+   *   - UNKNOWN_MIX_PAY_TYPE: 未知的混合支付类型,会被拦截
+   *   - CASH_ONLY: 只向微信支付下单,没有向医保局下单
+   *   - INSURANCE_ONLY: 只向医保局下单,没有向微信支付下单
+   *   - CASH_AND_INSURANCE: 向医保局下单,也向微信支付下单
+   * 
+ */ + @SerializedName("mix_pay_type") + public MixPayTypeEnum mixPayType; + + /** + *
+   * 字段名:订单类型
+   * 变量名:order_type
+   * 必填:是
+   * 类型:string
+   * 描述:
+   *   订单类型可选取值:
+   *   - UNKNOWN_ORDER_TYPE: 未知类型,会被拦截
+   *   - REG_PAY: 挂号支付
+   *   - DIAG_PAY: 诊间支付
+   *   - COVID_EXAM_PAY: 新冠检测费用(核酸)
+   *   - IN_HOSP_PAY: 住院费支付
+   *   - PHARMACY_PAY: 药店支付
+   *   - INSURANCE_PAY: 保险费支付
+   *   - INT_REG_PAY: 互联网医院挂号支付
+   *   - INT_RE_DIAG_PAY: 互联网医院复诊支付
+   *   - INT_RX_PAY: 互联网医院处方支付
+   *   - COVID_ANTIGEN_PAY: 新冠抗原检测
+   *   - MED_PAY: 药费支付
+   * 
+ */ + @SerializedName("order_type") + public OrderTypeEnum orderType; + + /** + *
+   * 字段名:从业机构/服务商的公众号ID
+   * 变量名:appid
+   * 必填:是
+   * 类型:string(32)
+   * 描述:从业机构/服务商的公众号ID
+   * 
+ */ + @SerializedName("appid") + public String appid; + + /** + *
+   * 字段名:医疗机构的公众号ID
+   * 变量名:sub_appid
+   * 必填:是
+   * 类型:string(32)
+   * 描述:医疗机构的公众号ID
+   * 
+ */ + @SerializedName("sub_appid") + public String subAppid; + + /** + *
+   * 字段名:医疗机构的商户号
+   * 变量名:sub_mchid
+   * 必填:是
+   * 类型:string(32)
+   * 描述:医疗机构的商户号
+   * 
+ */ + @SerializedName("sub_mchid") + public String subMchid; + + /** + *
+   * 字段名:用户在appid下的唯一标识
+   * 变量名:openid
+   * 必填:否
+   * 类型:string(128)
+   * 描述:openid与sub_openid二选一,传入openid时需要使用appid调起医保自费混合支付
+   * 
+ */ + @SerializedName("openid") + public String openid; + + /** + *
+   * 字段名:用户在sub_appid下的唯一标识
+   * 变量名:sub_openid
+   * 必填:否
+   * 类型:string(128)
+   * 描述:openid与sub_openid二选一,传入sub_openid时需要使用sub_appid调起医保自费混合支付
+   * 
+ */ + @SerializedName("sub_openid") + public String subOpenid; + + /** + *
+   * 字段名:支付人身份信息
+   * 变量名:payer
+   * 必填:是
+   * 类型:object
+   * 描述:支付人身份信息
+   * 
+ */ + @SerializedName("payer") + @SpecEncrypt + public PersonIdentification payer; + + /** + *
+   * 字段名:是否代亲属支付
+   * 变量名:pay_for_relatives
+   * 必填:否
+   * 类型:boolean
+   * 描述:不传默认替本人支付
+   *   - true: 代亲属支付
+   *   - false: 本人支付
+   * 
+ */ + @SerializedName("pay_for_relatives") + public Boolean payForRelatives; + + /** + *
+   * 字段名:亲属身份信息
+   * 变量名:relative
+   * 必填:否
+   * 类型:object
+   * 描述:pay_for_relatives为true时,该字段必填
+   * 
+ */ + @SerializedName("relative") + @SpecEncrypt + public PersonIdentification relative; + + /** + *
+   * 字段名:从业机构订单号
+   * 变量名:out_trade_no
+   * 必填:是
+   * 类型:string(64)
+   * 描述:从业机构/服务商需要调两次接口:从业机构/服务商向微信支付下单获取微信支付凭证,请求中会带上out_trade_no。下单成功后,从业机构/服务商调用混合下单的接口(即该接口),请求中也会带上out_trade_no。
+   * 
+ */ + @SerializedName("out_trade_no") + public String outTradeNo; + + /** + *
+   * 字段名:医疗机构订单号
+   * 变量名:serial_no
+   * 必填:是
+   * 类型:string(40)
+   * 描述:例如医院HIS系统订单号。传与费用明细上传中medOrgOrd字段一样的值,局端会校验,不一致将会返回错误
+   * 
+ */ + @SerializedName("serial_no") + public String serialNo; + + /** + *
+   * 字段名:支付订单号
+   * 变量名:pay_order_id
+   * 必填:否
+   * 类型:string
+   * 描述:支付订单号
+   * 
+ */ + @SerializedName("pay_order_id") + public String payOrderId; + + /** + *
+   * 字段名:支付授权号
+   * 变量名:pay_auth_no
+   * 必填:否
+   * 类型:string
+   * 描述:支付授权号
+   * 
+ */ + @SerializedName("pay_auth_no") + public String payAuthNo; + + /** + *
+   * 字段名:地理位置
+   * 变量名:geo_location
+   * 必填:否
+   * 类型:string
+   * 描述:地理位置
+   * 
+ */ + @SerializedName("geo_location") + public String geoLocation; + + /** + *
+   * 字段名:城市ID
+   * 变量名:city_id
+   * 必填:否
+   * 类型:string
+   * 描述:城市ID
+   * 
+ */ + @SerializedName("city_id") + public String cityId; + + /** + *
+   * 字段名:医疗机构名称
+   * 变量名:med_inst_name
+   * 必填:否
+   * 类型:string
+   * 描述:医疗机构名称
+   * 
+ */ + @SerializedName("med_inst_name") + public String medInstName; + + /** + *
+   * 字段名:医疗机构编号
+   * 变量名:med_inst_no
+   * 必填:否
+   * 类型:string
+   * 描述:医疗机构编号
+   * 
+ */ + @SerializedName("med_inst_no") + public String medInstNo; + + /** + *
+   * 字段名:医保订单创建时间
+   * 变量名:med_ins_order_create_time
+   * 必填:否
+   * 类型:string
+   * 描述:医保订单创建时间
+   * 
+ */ + @SerializedName("med_ins_order_create_time") + public String medInsOrderCreateTime; + + /** + *
+   * 字段名:总金额
+   * 变量名:total_fee
+   * 必填:否
+   * 类型:long
+   * 描述:总金额
+   * 
+ */ + @SerializedName("total_fee") + public Long totalFee; + + /** + *
+   * 字段名:医保统筹基金支付金额
+   * 变量名:med_ins_gov_fee
+   * 必填:否
+   * 类型:long
+   * 描述:医保统筹基金支付金额
+   * 
+ */ + @SerializedName("med_ins_gov_fee") + public Long medInsGovFee; + + /** + *
+   * 字段名:医保个人账户支付金额
+   * 变量名:med_ins_self_fee
+   * 必填:否
+   * 类型:long
+   * 描述:医保个人账户支付金额
+   * 
+ */ + @SerializedName("med_ins_self_fee") + public Long medInsSelfFee; + + /** + *
+   * 字段名:医保其他基金支付金额
+   * 变量名:med_ins_other_fee
+   * 必填:否
+   * 类型:long
+   * 描述:医保其他基金支付金额
+   * 
+ */ + @SerializedName("med_ins_other_fee") + public Long medInsOtherFee; + + /** + *
+   * 字段名:医保现金支付金额
+   * 变量名:med_ins_cash_fee
+   * 必填:否
+   * 类型:long
+   * 描述:医保现金支付金额
+   * 
+ */ + @SerializedName("med_ins_cash_fee") + public Long medInsCashFee; + + /** + *
+   * 字段名:微信支付现金支付金额
+   * 变量名:wechat_pay_cash_fee
+   * 必填:否
+   * 类型:long
+   * 描述:微信支付现金支付金额
+   * 
+ */ + @SerializedName("wechat_pay_cash_fee") + public Long wechatPayCashFee; + + /** + *
+   * 字段名:现金增加明细
+   * 变量名:cash_add_detail
+   * 必填:否
+   * 类型:list
+   * 描述:现金增加明细
+   * 
+ */ + @SerializedName("cash_add_detail") + public List cashAddDetail; + + /** + *
+   * 字段名:现金减少明细
+   * 变量名:cash_reduce_detail
+   * 必填:否
+   * 类型:list
+   * 描述:现金减少明细
+   * 
+ */ + @SerializedName("cash_reduce_detail") + public List cashReduceDetail; + + /** + *
+   * 字段名:回调URL
+   * 变量名:callback_url
+   * 必填:否
+   * 类型:string
+   * 描述:回调URL
+   * 
+ */ + @SerializedName("callback_url") + public String callbackUrl; + + /** + *
+   * 字段名:预支付交易会话标识
+   * 变量名:prepay_id
+   * 必填:否
+   * 类型:string
+   * 描述:预支付交易会话标识
+   * 
+ */ + @SerializedName("prepay_id") + public String prepayId; + + /** + *
+   * 字段名:透传请求内容
+   * 变量名:passthrough_request_content
+   * 必填:否
+   * 类型:string
+   * 描述:透传请求内容
+   * 
+ */ + @SerializedName("passthrough_request_content") + public String passthroughRequestContent; + + /** + *
+   * 字段名:扩展字段
+   * 变量名:extends
+   * 必填:否
+   * 类型:string
+   * 描述:扩展字段
+   * 
+ */ + @SerializedName("extends") + public String _extends; + + /** + *
+   * 字段名:附加数据
+   * 变量名:attach
+   * 必填:否
+   * 类型:string
+   * 描述:附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用
+   * 
+ */ + @SerializedName("attach") + public String attach; + + /** + *
+   * 字段名:渠道编号
+   * 变量名:channel_no
+   * 必填:否
+   * 类型:string
+   * 描述:渠道编号
+   * 
+ */ + @SerializedName("channel_no") + public String channelNo; + + /** + *
+   * 字段名:医保测试环境标识
+   * 变量名:med_ins_test_env
+   * 必填:否
+   * 类型:boolean
+   * 描述:医保测试环境标识
+   * 
+ */ + @SerializedName("med_ins_test_env") + public Boolean medInsTestEnv; + + /** + *
+   * 支付人身份信息
+   * 
+ */ + public static class PersonIdentification { + /** + *
+     * 字段名:姓名
+     * 变量名:name
+     * 必填:是
+     * 类型:string
+     * 描述:姓名,需加密
+     * 
+ */ + @SerializedName("name") + @SpecEncrypt + public String name; + + /** + *
+     * 字段名:身份证摘要
+     * 变量名:id_digest
+     * 必填:是
+     * 类型:string
+     * 描述:身份证摘要,需加密
+     * 
+ */ + @SerializedName("id_digest") + @SpecEncrypt + public String idDigest; + + /** + *
+     * 字段名:证件类型
+     * 变量名:card_type
+     * 必填:是
+     * 类型:string
+     * 描述:证件类型
+     * 
+ */ + @SerializedName("card_type") + public UserCardTypeEnum cardType; + } + + /** + *
+   * 现金增加明细实体
+   * 
+ */ + public static class CashAddEntity { + /** + *
+     * 字段名:现金增加金额
+     * 变量名:cash_add_fee
+     * 必填:是
+     * 类型:long
+     * 描述:现金增加金额
+     * 
+ */ + @SerializedName("cash_add_fee") + public Long cashAddFee; + + /** + *
+     * 字段名:现金增加类型
+     * 变量名:cash_add_type
+     * 必填:是
+     * 类型:string
+     * 描述:现金增加类型
+     * 
+ */ + @SerializedName("cash_add_type") + public CashAddTypeEnum cashAddType; + } + + /** + *
+   * 现金减少明细实体
+   * 
+ */ + public static class CashReduceEntity { + /** + *
+     * 字段名:现金减少金额
+     * 变量名:cash_reduce_fee
+     * 必填:是
+     * 类型:long
+     * 描述:现金减少金额
+     * 
+ */ + @SerializedName("cash_reduce_fee") + public Long cashReduceFee; + + /** + *
+     * 字段名:现金减少类型
+     * 变量名:cash_reduce_type
+     * 必填:是
+     * 类型:string
+     * 描述:现金减少类型
+     * 
+ */ + @SerializedName("cash_reduce_type") + public CashReduceTypeEnum cashReduceType; + } + + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/MedInsOrdersResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/MedInsOrdersResult.java new file mode 100644 index 0000000000..4fc68e279f --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/MedInsOrdersResult.java @@ -0,0 +1,497 @@ +package com.github.binarywang.wxpay.bean.mipay; + +import com.github.binarywang.wxpay.bean.mipay.enums.MedInsPayStatusEnum; +import com.github.binarywang.wxpay.bean.mipay.enums.MixPayStatusEnum; +import com.github.binarywang.wxpay.bean.mipay.enums.MixPayTypeEnum; +import com.github.binarywang.wxpay.bean.mipay.enums.OrderTypeEnum; +import com.github.binarywang.wxpay.bean.mipay.enums.SelfPayStatusEnum; +import com.google.gson.annotations.SerializedName; +import java.util.List; +import lombok.Data; + +/** + * 医保自费混合收款下单响应 + *

+ * 从业机构调用医保自费混合收款下单接口后返回的结果 + * 文档地址:https://pay.weixin.qq.com/doc/v3/partner/4012503131 + * @author xgl + * @date 2025/12/19 14:37 + */ +@Data +public class MedInsOrdersResult { + /** + *

+   * 字段名:混合交易订单号
+   * 变量名:mix_trade_no
+   * 必填:是
+   * 类型:string
+   * 描述:微信支付生成的混合交易订单号
+   * 
+ */ + @SerializedName("mix_trade_no") + public String mixTradeNo; + + /** + *
+   * 字段名:混合支付状态
+   * 变量名:mix_pay_status
+   * 必填:是
+   * 类型:string
+   * 描述:混合支付整体状态
+   * 
+ */ + @SerializedName("mix_pay_status") + public MixPayStatusEnum mixPayStatus; + + /** + *
+   * 字段名:自费支付状态
+   * 变量名:self_pay_status
+   * 必填:是
+   * 类型:string
+   * 描述:自费部分支付状态
+   * 
+ */ + @SerializedName("self_pay_status") + public SelfPayStatusEnum selfPayStatus; + + /** + *
+   * 字段名:医保支付状态
+   * 变量名:med_ins_pay_status
+   * 必填:是
+   * 类型:string
+   * 描述:医保部分支付状态
+   * 
+ */ + @SerializedName("med_ins_pay_status") + public MedInsPayStatusEnum medInsPayStatusEnum; + + /** + *
+   * 字段名:支付完成时间
+   * 变量名:paid_time
+   * 必填:否
+   * 类型:string
+   * 描述:支付完成时间,格式为yyyyMMddHHmmss
+   * 
+ */ + @SerializedName("paid_time") + public String paidTime; + + /** + *
+   * 字段名:透传响应内容
+   * 变量名:passthrough_response_content
+   * 必填:否
+   * 类型:string
+   * 描述:透传响应内容
+   * 
+ */ + @SerializedName("passthrough_response_content") + public String passthroughResponseContent; + + /** + *
+   * 字段名:混合支付类型
+   * 变量名:mix_pay_type
+   * 必填:是
+   * 类型:string
+   * 描述:
+   *   混合支付类型可选取值:
+   *   - UNKNOWN_MIX_PAY_TYPE: 未知的混合支付类型,会被拦截
+   *   - CASH_ONLY: 只向微信支付下单,没有向医保局下单
+   *   - INSURANCE_ONLY: 只向医保局下单,没有向微信支付下单
+   *   - CASH_AND_INSURANCE: 向医保局下单,也向微信支付下单
+   * 
+ */ + @SerializedName("mix_pay_type") + public MixPayTypeEnum mixPayType; + + /** + *
+   * 字段名:订单类型
+   * 变量名:order_type
+   * 必填:是
+   * 类型:string
+   * 描述:
+   *   订单类型可选取值:
+   *   - UNKNOWN_ORDER_TYPE: 未知类型,会被拦截
+   *   - REG_PAY: 挂号支付
+   *   - DIAG_PAY: 诊间支付
+   *   - COVID_EXAM_PAY: 新冠检测费用(核酸)
+   *   - IN_HOSP_PAY: 住院费支付
+   *   - PHARMACY_PAY: 药店支付
+   *   - INSURANCE_PAY: 保险费支付
+   *   - INT_REG_PAY: 互联网医院挂号支付
+   *   - INT_RE_DIAG_PAY: 互联网医院复诊支付
+   *   - INT_RX_PAY: 互联网医院处方支付
+   *   - COVID_ANTIGEN_PAY: 新冠抗原检测
+   *   - MED_PAY: 药费支付
+   * 
+ */ + @SerializedName("order_type") + public OrderTypeEnum orderType; + + /** + *
+   * 字段名:从业机构/服务商的公众号ID
+   * 变量名:appid
+   * 必填:是
+   * 类型:string(32)
+   * 描述:从业机构/服务商的公众号ID
+   * 
+ */ + @SerializedName("appid") + public String appid; + + /** + *
+   * 字段名:医疗机构的公众号ID
+   * 变量名:sub_appid
+   * 必填:是
+   * 类型:string(32)
+   * 描述:医疗机构的公众号ID
+   * 
+ */ + @SerializedName("sub_appid") + public String subAppid; + + /** + *
+   * 字段名:医疗机构的商户号
+   * 变量名:sub_mchid
+   * 必填:是
+   * 类型:string(32)
+   * 描述:医疗机构的商户号
+   * 
+ */ + @SerializedName("sub_mchid") + public String subMchid; + + /** + *
+   * 字段名:用户在appid下的唯一标识
+   * 变量名:openid
+   * 必填:否
+   * 类型:string(128)
+   * 描述:openid与sub_openid二选一,传入openid时需要使用appid调起医保自费混合支付
+   * 
+ */ + @SerializedName("openid") + public String openid; + + /** + *
+   * 字段名:用户在sub_appid下的唯一标识
+   * 变量名:sub_openid
+   * 必填:否
+   * 类型:string(128)
+   * 描述:openid与sub_openid二选一,传入sub_openid时需要使用sub_appid调起医保自费混合支付
+   * 
+ */ + @SerializedName("sub_openid") + public String subOpenid; + + /** + *
+   * 字段名:是否代亲属支付
+   * 变量名:pay_for_relatives
+   * 必填:否
+   * 类型:boolean
+   * 描述:不传默认替本人支付
+   *   - true: 代亲属支付
+   *   - false: 本人支付
+   * 
+ */ + @SerializedName("pay_for_relatives") + public Boolean payForRelatives; + + /** + *
+   * 字段名:从业机构订单号
+   * 变量名:out_trade_no
+   * 必填:是
+   * 类型:string(64)
+   * 描述:从业机构/服务商需要调两次接口:从业机构/服务商向微信支付下单获取微信支付凭证,请求中会带上out_trade_no。下单成功后,从业机构/服务商调用混合下单的接口(即该接口),请求中也会带上out_trade_no。
+   * 
+ */ + @SerializedName("out_trade_no") + public String outTradeNo; + + /** + *
+   * 字段名:医疗机构订单号
+   * 变量名:serial_no
+   * 必填:是
+   * 类型:string(40)
+   * 描述:例如医院HIS系统订单号。传与费用明细上传中medOrgOrd字段一样的值,局端会校验,不一致将会返回错误
+   * 
+ */ + @SerializedName("serial_no") + public String serialNo; + + /** + *
+   * 字段名:支付订单号
+   * 变量名:pay_order_id
+   * 必填:否
+   * 类型:string
+   * 描述:支付订单号
+   * 
+ */ + @SerializedName("pay_order_id") + public String payOrderId; + + /** + *
+   * 字段名:支付授权号
+   * 变量名:pay_auth_no
+   * 必填:否
+   * 类型:string
+   * 描述:支付授权号
+   * 
+ */ + @SerializedName("pay_auth_no") + public String payAuthNo; + + /** + *
+   * 字段名:地理位置
+   * 变量名:geo_location
+   * 必填:否
+   * 类型:string
+   * 描述:地理位置
+   * 
+ */ + @SerializedName("geo_location") + public String geoLocation; + + /** + *
+   * 字段名:城市ID
+   * 变量名:city_id
+   * 必填:否
+   * 类型:string
+   * 描述:城市ID
+   * 
+ */ + @SerializedName("city_id") + public String cityId; + + /** + *
+   * 字段名:医疗机构名称
+   * 变量名:med_inst_name
+   * 必填:否
+   * 类型:string
+   * 描述:医疗机构名称
+   * 
+ */ + @SerializedName("med_inst_name") + public String medInstName; + + /** + *
+   * 字段名:医疗机构编号
+   * 变量名:med_inst_no
+   * 必填:否
+   * 类型:string
+   * 描述:医疗机构编号
+   * 
+ */ + @SerializedName("med_inst_no") + public String medInstNo; + + /** + *
+   * 字段名:医保订单创建时间
+   * 变量名:med_ins_order_create_time
+   * 必填:否
+   * 类型:string
+   * 描述:医保订单创建时间
+   * 
+ */ + @SerializedName("med_ins_order_create_time") + public String medInsOrderCreateTime; + + /** + *
+   * 字段名:总金额
+   * 变量名:total_fee
+   * 必填:否
+   * 类型:long
+   * 描述:总金额
+   * 
+ */ + @SerializedName("total_fee") + public Long totalFee; + + /** + *
+   * 字段名:医保统筹基金支付金额
+   * 变量名:med_ins_gov_fee
+   * 必填:否
+   * 类型:long
+   * 描述:医保统筹基金支付金额
+   * 
+ */ + @SerializedName("med_ins_gov_fee") + public Long medInsGovFee; + + /** + *
+   * 字段名:医保个人账户支付金额
+   * 变量名:med_ins_self_fee
+   * 必填:否
+   * 类型:long
+   * 描述:医保个人账户支付金额
+   * 
+ */ + @SerializedName("med_ins_self_fee") + public Long medInsSelfFee; + + /** + *
+   * 字段名:医保其他基金支付金额
+   * 变量名:med_ins_other_fee
+   * 必填:否
+   * 类型:long
+   * 描述:医保其他基金支付金额
+   * 
+ */ + @SerializedName("med_ins_other_fee") + public Long medInsOtherFee; + + /** + *
+   * 字段名:医保现金支付金额
+   * 变量名:med_ins_cash_fee
+   * 必填:否
+   * 类型:long
+   * 描述:医保现金支付金额
+   * 
+ */ + @SerializedName("med_ins_cash_fee") + public Long medInsCashFee; + + /** + *
+   * 字段名:微信支付现金支付金额
+   * 变量名:wechat_pay_cash_fee
+   * 必填:否
+   * 类型:long
+   * 描述:微信支付现金支付金额
+   * 
+ */ + @SerializedName("wechat_pay_cash_fee") + public Long wechatPayCashFee; + + /** + *
+   * 字段名:现金增加明细
+   * 变量名:cash_add_detail
+   * 必填:否
+   * 类型:list
+   * 描述:现金增加明细
+   * 
+ */ + @SerializedName("cash_add_detail") + public List cashAddDetail; + + /** + *
+   * 字段名:现金减少明细
+   * 变量名:cash_reduce_detail
+   * 必填:否
+   * 类型:list
+   * 描述:现金减少明细
+   * 
+ */ + @SerializedName("cash_reduce_detail") + public List cashReduceDetail; + + /** + *
+   * 字段名:回调URL
+   * 变量名:callback_url
+   * 必填:否
+   * 类型:string
+   * 描述:回调URL
+   * 
+ */ + @SerializedName("callback_url") + public String callbackUrl; + + /** + *
+   * 字段名:预支付交易会话标识
+   * 变量名:prepay_id
+   * 必填:否
+   * 类型:string
+   * 描述:预支付交易会话标识
+   * 
+ */ + @SerializedName("prepay_id") + public String prepayId; + + /** + *
+   * 字段名:透传请求内容
+   * 变量名:passthrough_request_content
+   * 必填:否
+   * 类型:string
+   * 描述:透传请求内容
+   * 
+ */ + @SerializedName("passthrough_request_content") + public String passthroughRequestContent; + + /** + *
+   * 字段名:扩展字段
+   * 变量名:extends
+   * 必填:否
+   * 类型:string
+   * 描述:扩展字段
+   * 
+ */ + @SerializedName("extends") + public String _extends; + + /** + *
+   * 字段名:附加数据
+   * 变量名:attach
+   * 必填:否
+   * 类型:string
+   * 描述:附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用
+   * 
+ */ + @SerializedName("attach") + public String attach; + + /** + *
+   * 字段名:渠道编号
+   * 变量名:channel_no
+   * 必填:否
+   * 类型:string
+   * 描述:渠道编号
+   * 
+ */ + @SerializedName("channel_no") + public String channelNo; + + /** + *
+   * 字段名:医保测试环境标识
+   * 变量名:med_ins_test_env
+   * 必填:否
+   * 类型:boolean
+   * 描述:医保测试环境标识
+   * 
+ */ + @SerializedName("med_ins_test_env") + public Boolean medInsTestEnv; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/MedInsRefundNotifyRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/MedInsRefundNotifyRequest.java new file mode 100644 index 0000000000..b6e15a3644 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/MedInsRefundNotifyRequest.java @@ -0,0 +1,116 @@ +package com.github.binarywang.wxpay.bean.mipay; + +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 医保退款通知请求 + *

+ * 从业机构调用该接口向微信医保后台通知医保订单的退款成功结果 + * 文档地址:https://pay.weixin.qq.com/doc/v3/partner/4012166534 + * @author xgl + * @date 2025/12/20 + */ +@Data +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor +public class MedInsRefundNotifyRequest { + + /** + *

+   * 字段名:医保自费混合订单号
+   * 必填:是
+   * 类型:string(32)
+   * 描述:医保自费混合订单号
+   * 
+ */ + private String mixTradeNo; + + /** + *
+   * 字段名:医疗机构的商户号
+   * 变量名:sub_mchid
+   * 必填:是
+   * 类型:string(32)
+   * 描述:医疗机构的商户号
+   * 
+ */ + @SerializedName("sub_mchid") + private String subMchid; + + /** + *
+   * 字段名:医保退款的总金额
+   * 变量名:med_refund_total_fee
+   * 必填:是
+   * 类型:integer
+   * 描述:单位分,医保退款的总金额。
+   * 
+ */ + @SerializedName("med_refund_total_fee") + private Integer medRefundTotalFee; + + /** + *
+   * 字段名:医保统筹退款金额
+   * 变量名:med_refund_gov_fee
+   * 必填:是
+   * 类型:integer
+   * 描述:单位分,医保统筹退款金额。
+   * 
+ */ + @SerializedName("med_refund_gov_fee") + private Integer medRefundGovFee; + + /** + *
+   * 字段名:医保个账退款金额
+   * 变量名:med_refund_self_fee
+   * 必填:是
+   * 类型:integer
+   * 描述:单位分,医保个账退款金额。
+   * 
+ */ + @SerializedName("med_refund_self_fee") + private Integer medRefundSelfFee; + + /** + *
+   * 字段名:医保其他退款金额
+   * 变量名:med_refund_other_fee
+   * 必填:是
+   * 类型:integer
+   * 描述:单位分,医保其他退款金额。
+   * 
+ */ + @SerializedName("med_refund_other_fee") + private Integer medRefundOtherFee; + + /** + *
+   * 字段名:医保退款成功时间
+   * 变量名:refund_time
+   * 必填:是
+   * 类型:string(64)
+   * 描述:遵循rfc3339标准格式,格式为yyyy-MM-DDTHH:mm:ss+TIMEZONE。
+   * 
+ */ + @SerializedName("refund_time") + private String refundTime; + + /** + *
+   * 字段名:从业机构\服务商退款单号
+   * 变量名:out_refund_no
+   * 必填:是
+   * 类型:string(64)
+   * 描述:有自费单时,从业机构\服务商应填与自费退款申请处一致的out_refund_no。否则从业机构透传医疗机构退款单号即可。
+   * 
+ */ + @SerializedName("out_refund_no") + private String outRefundNo; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/enums/CashAddTypeEnum.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/enums/CashAddTypeEnum.java new file mode 100644 index 0000000000..b935f20410 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/enums/CashAddTypeEnum.java @@ -0,0 +1,29 @@ +package com.github.binarywang.wxpay.bean.mipay.enums; + +import com.google.gson.annotations.SerializedName; + +/** + * 现金增加类型枚举 + *

+ * 描述医保自费混合支付中现金增加的类型 + * + * @author xgl + * @date 2025/12/20 + */ +public enum CashAddTypeEnum { + /** + * 默认增加类型 + */ + @SerializedName("DEFAULT_ADD_TYPE") + DEFAULT_ADD_TYPE, + /** + * 运费 + */ + @SerializedName("FREIGHT") + FREIGHT, + /** + * 其他医疗费用 + */ + @SerializedName("OTHER_MEDICAL_EXPENSES") + OTHER_MEDICAL_EXPENSES +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/enums/CashReduceTypeEnum.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/enums/CashReduceTypeEnum.java new file mode 100644 index 0000000000..4f90b8500a --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/enums/CashReduceTypeEnum.java @@ -0,0 +1,44 @@ +package com.github.binarywang.wxpay.bean.mipay.enums; + +import com.google.gson.annotations.SerializedName; + +/** + * 现金减少类型枚举 + *

+ * 描述医保自费混合支付中现金减少的类型 + * + * @author xgl + * @date 2025/12/20 + */ +public enum CashReduceTypeEnum { + /** + * 默认减少类型 + */ + @SerializedName("DEFAULT_REDUCE_TYPE") + DEFAULT_REDUCE_TYPE, + /** + * 医院减免 + */ + @SerializedName("HOSPITAL_REDUCE") + HOSPITAL_REDUCE, + /** + * 药店折扣 + */ + @SerializedName("PHARMACY_DISCOUNT") + PHARMACY_DISCOUNT, + /** + * 折扣优惠 + */ + @SerializedName("DISCOUNT") + DISCOUNT, + /** + * 预付费抵扣 + */ + @SerializedName("PRE_PAYMENT") + PRE_PAYMENT, + /** + * 押金扣除 + */ + @SerializedName("DEPOSIT_DEDUCTION") + DEPOSIT_DEDUCTION +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/enums/MedInsPayStatusEnum.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/enums/MedInsPayStatusEnum.java new file mode 100644 index 0000000000..324530f0ff --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/enums/MedInsPayStatusEnum.java @@ -0,0 +1,44 @@ +package com.github.binarywang.wxpay.bean.mipay.enums; + +import com.google.gson.annotations.SerializedName; + +/** + * 医保支付状态枚举 + *

+ * 描述医保自费混合支付中医保部分的支付状态 + * + * @author xgl + * @date 2025/12/20 + */ +public enum MedInsPayStatusEnum { + /** + * 未知的医保支付状态 + */ + @SerializedName("UNKNOWN_MED_INS_PAY_STATUS") + UNKNOWN_MED_INS_PAY_STATUS, + /** + * 医保支付已创建 + */ + @SerializedName("MED_INS_PAY_CREATED") + MED_INS_PAY_CREATED, + /** + * 医保支付成功 + */ + @SerializedName("MED_INS_PAY_SUCCESS") + MED_INS_PAY_SUCCESS, + /** + * 医保支付已退款 + */ + @SerializedName("MED_INS_PAY_REFUND") + MED_INS_PAY_REFUND, + /** + * 医保支付失败 + */ + @SerializedName("MED_INS_PAY_FAIL") + MED_INS_PAY_FAIL, + /** + * 无需医保支付 + */ + @SerializedName("NO_MED_INS_PAY") + NO_MED_INS_PAY +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/enums/MixPayStatusEnum.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/enums/MixPayStatusEnum.java new file mode 100644 index 0000000000..7360704986 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/enums/MixPayStatusEnum.java @@ -0,0 +1,39 @@ +package com.github.binarywang.wxpay.bean.mipay.enums; + +import com.google.gson.annotations.SerializedName; + +/** + * 混合支付状态枚举 + *

+ * 描述医保自费混合支付的整体状态 + * + * @author xgl + * @date 2025/12/20 + */ +public enum MixPayStatusEnum { + /** + * 未知的混合支付状态 + */ + @SerializedName("UNKNOWN_MIX_PAY_STATUS") + UNKNOWN_MIX_PAY_STATUS, + /** + * 混合支付已创建 + */ + @SerializedName("MIX_PAY_CREATED") + MIX_PAY_CREATED, + /** + * 混合支付成功 + */ + @SerializedName("MIX_PAY_SUCCESS") + MIX_PAY_SUCCESS, + /** + * 混合支付已退款 + */ + @SerializedName("MIX_PAY_REFUND") + MIX_PAY_REFUND, + /** + * 混合支付失败 + */ + @SerializedName("MIX_PAY_FAIL") + MIX_PAY_FAIL +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/enums/MixPayTypeEnum.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/enums/MixPayTypeEnum.java new file mode 100644 index 0000000000..ad62d50a66 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/enums/MixPayTypeEnum.java @@ -0,0 +1,41 @@ +package com.github.binarywang.wxpay.bean.mipay.enums; + +import com.google.gson.annotations.SerializedName; + +/** + * 混合支付类型枚举 + *

+ * 描述医保自费混合支付的类型 + * 文档地址:https://pay.weixin.qq.com/doc/v3/partner/4012503131 + * + * @author xgl + * @date 2025/12/20 09:21 + */ +public enum MixPayTypeEnum { + + /** + * 未知的混合支付类型,会被拦截。 + */ + @SerializedName("UNKNOWN_MIX_PAY_TYPE") + UNKNOWN_MIX_PAY_TYPE, + + /** + * 只向微信支付下单,没有向医保局下单。包括没有向医保局上传费用明细、预结算。 + */ + @SerializedName("CASH_ONLY") + CASH_ONLY, + + /** + * 只向医保局下单,没有向微信支付下单。如果医保局分账结果中有自费部份,但由于有减免抵扣,没有向微信支付下单,也是纯医保。 + */ + @SerializedName("INSURANCE_ONLY") + INSURANCE_ONLY, + + /** + * 向医保局下单,也向微信支付下单。如果医保预结算全部需自费,也属于混合类型。 + */ + @SerializedName("CASH_AND_INSURANCE") + CASH_AND_INSURANCE + + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/enums/OrderTypeEnum.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/enums/OrderTypeEnum.java new file mode 100644 index 0000000000..749b1276e7 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/enums/OrderTypeEnum.java @@ -0,0 +1,87 @@ +package com.github.binarywang.wxpay.bean.mipay.enums; + +import com.google.gson.annotations.SerializedName; + +/** + * 订单类型枚举 + *

+ * 描述医保自费混合支付的订单类型 + * 文档地址:https://pay.weixin.qq.com/doc/v3/partner/4012503131 + * + * @author xgl + * @date 2025/12/20 + */ +public enum OrderTypeEnum { + + /** + * 未知类型,会被拦截 + */ + @SerializedName("UNKNOWN_ORDER_TYPE") + UNKNOWN_ORDER_TYPE, + + /** + * 挂号支付 + */ + @SerializedName("REG_PAY") + REG_PAY, + + /** + * 诊间支付 + */ + @SerializedName("DIAG_PAY") + DIAG_PAY, + + /** + * 新冠检测费用(核酸) + */ + @SerializedName("COVID_EXAM_PAY") + COVID_EXAM_PAY, + + /** + * 住院费支付 + */ + @SerializedName("IN_HOSP_PAY") + IN_HOSP_PAY, + + /** + * 药店支付 + */ + @SerializedName("PHARMACY_PAY") + PHARMACY_PAY, + + /** + * 保险费支付 + */ + @SerializedName("INSURANCE_PAY") + INSURANCE_PAY, + + /** + * 互联网医院挂号支付 + */ + @SerializedName("INT_REG_PAY") + INT_REG_PAY, + + /** + * 互联网医院复诊支付 + */ + @SerializedName("INT_RE_DIAG_PAY") + INT_RE_DIAG_PAY, + + /** + * 互联网医院处方支付 + */ + @SerializedName("INT_RX_PAY") + INT_RX_PAY, + + /** + * 新冠抗原检测 + */ + @SerializedName("COVID_ANTIGEN_PAY") + COVID_ANTIGEN_PAY, + + /** + * 药费支付 + */ + @SerializedName("MED_PAY") + MED_PAY +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/enums/SelfPayStatusEnum.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/enums/SelfPayStatusEnum.java new file mode 100644 index 0000000000..a7014b9e13 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/enums/SelfPayStatusEnum.java @@ -0,0 +1,44 @@ +package com.github.binarywang.wxpay.bean.mipay.enums; + +import com.google.gson.annotations.SerializedName; + +/** + * 自费支付状态枚举 + *

+ * 描述医保自费混合支付中自费部分的支付状态 + * + * @author xgl + * @date 2025/12/20 + */ +public enum SelfPayStatusEnum { + /** + * 未知的自费支付状态 + */ + @SerializedName("UNKNOWN_SELF_PAY_STATUS") + UNKNOWN_SELF_PAY_STATUS, + /** + * 自费支付已创建 + */ + @SerializedName("SELF_PAY_CREATED") + SELF_PAY_CREATED, + /** + * 自费支付成功 + */ + @SerializedName("SELF_PAY_SUCCESS") + SELF_PAY_SUCCESS, + /** + * 自费支付已退款 + */ + @SerializedName("SELF_PAY_REFUND") + SELF_PAY_REFUND, + /** + * 自费支付失败 + */ + @SerializedName("SELF_PAY_FAIL") + SELF_PAY_FAIL, + /** + * 无需自费支付 + */ + @SerializedName("NO_SELF_PAY") + NO_SELF_PAY +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/enums/UserCardTypeEnum.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/enums/UserCardTypeEnum.java new file mode 100644 index 0000000000..1bf97b7628 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/enums/UserCardTypeEnum.java @@ -0,0 +1,54 @@ +package com.github.binarywang.wxpay.bean.mipay.enums; + +import com.google.gson.annotations.SerializedName; + +/** + * 用户证件类型枚举 + *

+ * 描述医保自费混合支付中用户的证件类型 + * + * @author xgl + * @date 2025/12/20 + */ +public enum UserCardTypeEnum { + /** + * 未知的用户证件类型 + */ + @SerializedName("UNKNOWN_USER_CARD_TYPE") + UNKNOWN_USER_CARD_TYPE, + /** + * 居民身份证 + */ + @SerializedName("ID_CARD") + ID_CARD, + /** + * 户口本 + */ + @SerializedName("HOUSEHOLD_REGISTRATION") + HOUSEHOLD_REGISTRATION, + /** + * 外国护照 + */ + @SerializedName("FOREIGNER_PASSPORT") + FOREIGNER_PASSPORT, + /** + * 台湾居民来往大陆通行证 + */ + @SerializedName("MAINLAND_TRAVEL_PERMIT_FOR_TW") + MAINLAND_TRAVEL_PERMIT_FOR_TW, + /** + * 澳门居民来往大陆通行证 + */ + @SerializedName("MAINLAND_TRAVEL_PERMIT_FOR_MO") + MAINLAND_TRAVEL_PERMIT_FOR_MO, + /** + * 香港居民来往大陆通行证 + */ + @SerializedName("MAINLAND_TRAVEL_PERMIT_FOR_HK") + MAINLAND_TRAVEL_PERMIT_FOR_HK, + /** + * 外国人永久居留身份证 + */ + @SerializedName("FOREIGN_PERMANENT_RESIDENT") + FOREIGN_PERMANENT_RESIDENT +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/MiPayNotifyV3Result.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/MiPayNotifyV3Result.java new file mode 100644 index 0000000000..a0641379fb --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/MiPayNotifyV3Result.java @@ -0,0 +1,265 @@ +package com.github.binarywang.wxpay.bean.notify; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + *

+ * 医保混合收款成功通知结果
+ * 文档地址:https://pay.weixin.qq.com/doc/v3/partner/4012165722
+ * 
+ * + * @author xgl + * @date 2025/12/20 + */ +@Data +@NoArgsConstructor +public class MiPayNotifyV3Result implements Serializable, WxPayBaseNotifyV3Result { + + /** + * 源数据 + */ + private OriginNotifyResponse rawData; + + /** + * 解密后的数据 + */ + private DecryptNotifyResult result; + + @Data + @NoArgsConstructor + public static class DecryptNotifyResult implements Serializable { + private static final long serialVersionUID = 1L; + + /** + *
+         * 字段名:应用ID
+         * 变量名:appid
+         * 是否必填:是
+         * 类型:string(32)
+         * 描述:
+         *   从业机构/服务商的公众号ID
+         * 
+ */ + @SerializedName(value = "appid") + private String appid; + + /** + *
+         * 字段名:医疗机构的公众号ID
+         * 变量名:sub_appid
+         * 是否必填:是
+         * 类型:string(32)
+         * 描述:
+         *   医疗机构的公众号ID
+         * 
+ */ + @SerializedName(value = "sub_appid") + private String subAppid; + + /** + *
+         * 字段名:医疗机构的商户号
+         * 变量名:sub_mchid
+         * 是否必填:是
+         * 类型:string(32)
+         * 描述:
+         *   医疗机构的商户号
+         * 
+ */ + @SerializedName(value = "sub_mchid") + private String subMchid; + + /** + *
+         * 字段名:从业机构订单号
+         * 变量名:out_trade_no
+         * 是否必填:是
+         * 类型:string(64)
+         * 描述:
+         *   从业机构/服务商订单号
+         * 
+ */ + @SerializedName(value = "out_trade_no") + private String outTradeNo; + + /** + *
+         * 字段名:医保自费混合订单号
+         * 变量名:mix_trade_no
+         * 是否必填:是
+         * 类型:string(32)
+         * 描述:
+         *   微信支付系统生成的医保自费混合订单号
+         * 
+ */ + @SerializedName(value = "mix_trade_no") + private String mixTradeNo; + + /** + *
+         * 字段名:微信支付订单号
+         * 变量名:transaction_id
+         * 是否必填:是
+         * 类型:string(32)
+         * 描述:
+         *   微信支付系统生成的订单号
+         * 
+ */ + @SerializedName(value = "transaction_id") + private String transactionId; + + /** + *
+         * 字段名:医保订单创建时间
+         * 变量名:med_ins_order_create_time
+         * 是否必填:是
+         * 类型:string(64)
+         * 描述:
+         *   医保订单创建时间,遵循rfc3339标准格式
+         * 
+ */ + @SerializedName(value = "med_ins_order_create_time") + private String medInsOrderCreateTime; + + /** + *
+         * 字段名:医保订单完成时间
+         * 变量名:med_ins_order_finish_time
+         * 是否必填:是
+         * 类型:string(64)
+         * 描述:
+         *   医保订单完成时间,遵循rfc3339标准格式
+         * 
+ */ + @SerializedName(value = "med_ins_order_finish_time") + private String medInsOrderFinishTime; + + /** + *
+         * 字段名:总金额
+         * 变量名:total_fee
+         * 是否必填:否
+         * 类型:long
+         * 描述:
+         *   总金额,单位为分
+         * 
+ */ + @SerializedName(value = "total_fee") + private Long totalFee; + + /** + *
+         * 字段名:医保统筹基金支付金额
+         * 变量名:med_ins_gov_fee
+         * 是否必填:否
+         * 类型:long
+         * 描述:
+         *   医保统筹基金支付金额,单位为分
+         * 
+ */ + @SerializedName(value = "med_ins_gov_fee") + private Long medInsGovFee; + + /** + *
+         * 字段名:医保个人账户支付金额
+         * 变量名:med_ins_self_fee
+         * 是否必填:否
+         * 类型:long
+         * 描述:
+         *   医保个人账户支付金额,单位为分
+         * 
+ */ + @SerializedName(value = "med_ins_self_fee") + private Long medInsSelfFee; + + /** + *
+         * 字段名:医保其他基金支付金额
+         * 变量名:med_ins_other_fee
+         * 是否必填:否
+         * 类型:long
+         * 描述:
+         *   医保其他基金支付金额,单位为分
+         * 
+ */ + @SerializedName(value = "med_ins_other_fee") + private Long medInsOtherFee; + + /** + *
+         * 字段名:医保现金支付金额
+         * 变量名:med_ins_cash_fee
+         * 是否必填:否
+         * 类型:long
+         * 描述:
+         *   医保现金支付金额,单位为分
+         * 
+ */ + @SerializedName(value = "med_ins_cash_fee") + private Long medInsCashFee; + + /** + *
+         * 字段名:微信支付现金支付金额
+         * 变量名:wechat_pay_cash_fee
+         * 是否必填:否
+         * 类型:long
+         * 描述:
+         *   微信支付现金支付金额,单位为分
+         * 
+ */ + @SerializedName(value = "wechat_pay_cash_fee") + private Long wechatPayCashFee; + + /** + *
+         * 字段名:附加数据
+         * 变量名:attach
+         * 是否必填:否
+         * 类型:string(128)
+         * 描述:
+         *   附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用
+         * 
+ */ + @SerializedName(value = "attach") + private String attach; + + /** + *
+         * 字段名:支付状态
+         * 变量名:trade_state
+         * 是否必填:是
+         * 类型:string(32)
+         * 描述:
+         *   交易状态,枚举值:
+         *   SUCCESS:支付成功
+         *   REFUND:转入退款
+         *   NOTPAY:未支付
+         *   CLOSED:已关闭
+         *   REVOKED:已撤销
+         *   USERPAYING:用户支付中
+         *   PAYERROR:支付失败
+         * 
+ */ + @SerializedName(value = "trade_state") + private String tradeState; + + /** + *
+         * 字段名:支付状态描述
+         * 变量名:trade_state_desc
+         * 是否必填:是
+         * 类型:string(256)
+         * 描述:
+         *   交易状态描述
+         * 
+ */ + @SerializedName(value = "trade_state_desc") + private String tradeStateDesc; + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/MiPayService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/MiPayService.java new file mode 100644 index 0000000000..83b75ad40c --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/MiPayService.java @@ -0,0 +1,94 @@ +package com.github.binarywang.wxpay.service; + +import com.github.binarywang.wxpay.bean.mipay.MedInsOrdersRequest; +import com.github.binarywang.wxpay.bean.mipay.MedInsOrdersResult; +import com.github.binarywang.wxpay.bean.mipay.MedInsRefundNotifyRequest; +import com.github.binarywang.wxpay.bean.notify.MiPayNotifyV3Result; +import com.github.binarywang.wxpay.bean.notify.SignatureHeader; +import com.github.binarywang.wxpay.exception.WxPayException; + +/** + * 医保相关接口 + * 医保相关接口 + * @author xgl + * @date 2025/12/20 + */ +public interface MiPayService { + + /** + *
+   * 医保自费混合收款下单
+   *
+   * 从业机构调用该接口向微信医保后台下单
+   *
+   * 文档地址:医保自费混合收款下单
+   * 
+ * + * @param request 下单参数 + * @return ReservationTransferNotifyResult 下单结果 + * @throws WxPayException the wx pay exception + */ + MedInsOrdersResult medInsOrders(MedInsOrdersRequest request) throws WxPayException; + + /** + *
+   * 使用医保自费混合订单号查看下单结果
+   *
+   * 从业机构使用混合下单订单号,通过该接口主动查询订单状态,完成下一步的业务逻辑。
+   *
+   * 文档地址:使用医保自费混合订单号查看下单结果
+   * 
+ * + * @param mixTradeNo 医保自费混合订单号 + * @param subMchid 医疗机构的商户号 + * @return MedInsOrdersResult 下单结果 + * @throws WxPayException the wx pay exception + */ + MedInsOrdersResult getMedInsOrderByMixTradeNo(String mixTradeNo, String subMchid) throws WxPayException; + + /** + *
+   * 使用从业机构订单号查看下单结果
+   *
+   * 从业机构使用从业机构订单号、医疗机构商户号,通过该接口主动查询订单状态,完成下一步的业务逻辑。
+   *
+   * 文档地址:使用从业机构订单号查看下单结果
+   * 
+ * + * @param outTradeNo 从业机构订单号 + * @param subMchid 医疗机构的商户号 + * @return MedInsOrdersResult 下单结果 + * @throws WxPayException the wx pay exception + */ + MedInsOrdersResult getMedInsOrderByOutTradeNo(String outTradeNo, String subMchid) throws WxPayException; + + /** + *
+   * 解析医保混合收款成功通知
+   *
+   * 微信支付会通过POST请求向商户设置的回调URL推送医保混合收款成功通知,商户需要接收处理该消息,并返回应答。
+   *
+   * 文档地址:医保混合收款成功通知
+   * 
+ * + * @param notifyData 通知数据字符串 + * @return MiPayNotifyV3Result 医保混合收款成功通知结果 + * @throws WxPayException the wx pay exception + */ + MiPayNotifyV3Result parseMiPayNotifyV3Result(String notifyData, SignatureHeader header) throws WxPayException; + + /** + *
+   * 医保退款通知
+   *
+   * 从业机构调用该接口向微信医保后台通知医保订单的退款成功结果
+   *
+   * 文档地址:医保退款通知
+   * 
+ * + * @param request 医保退款通知请求参数 + * @throws WxPayException the wx pay exception + */ + void medInsRefundNotify(MedInsRefundNotifyRequest request) throws WxPayException; + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java index 93da0d1332..cfb2479ae7 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 @@ -1706,4 +1706,12 @@ WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, Stri * @return the partner pay score sign plan service */ PartnerPayScoreSignPlanService getPartnerPayScoreSignPlanService(); + + /** + * 获取医保支付服务类 + * + * @return the merchant transfer service + */ + MiPayService getMiPayService(); + } 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 ba3dc37144..f2e343df22 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java @@ -1,17 +1,21 @@ package com.github.binarywang.wxpay.service.impl; -import com.github.binarywang.utils.qrcode.QrcodeUtils; -import com.github.binarywang.wxpay.bean.WxPayApiData; +import static com.github.binarywang.wxpay.constant.WxPayConstants.QUERY_COMMENT_DATE_FORMAT; +import static com.github.binarywang.wxpay.constant.WxPayConstants.TarType; import com.github.binarywang.wxpay.bean.coupon.*; import com.github.binarywang.wxpay.bean.notify.*; +import com.github.binarywang.wxpay.bean.request.*; +import com.github.binarywang.wxpay.bean.result.*; +import com.github.binarywang.wxpay.service.*; +import java.util.*; +import com.github.binarywang.wxpay.bean.result.enums.GlobalTradeTypeEnum; +import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum; +import com.github.binarywang.utils.qrcode.QrcodeUtils; +import com.github.binarywang.wxpay.bean.WxPayApiData; import com.github.binarywang.wxpay.bean.order.WxPayAppOrderResult; import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult; import com.github.binarywang.wxpay.bean.order.WxPayMwebOrderResult; import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult; -import com.github.binarywang.wxpay.bean.request.*; -import com.github.binarywang.wxpay.bean.result.*; -import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum; -import com.github.binarywang.wxpay.bean.result.enums.GlobalTradeTypeEnum; import com.github.binarywang.wxpay.bean.transfer.TransferBillsNotifyResult; import com.github.binarywang.wxpay.config.WxPayConfig; import com.github.binarywang.wxpay.config.WxPayConfigHolder; @@ -19,7 +23,6 @@ import com.github.binarywang.wxpay.constant.WxPayConstants.TradeType; import com.github.binarywang.wxpay.exception.WxPayException; import com.github.binarywang.wxpay.exception.WxSignTestException; -import com.github.binarywang.wxpay.service.*; import com.github.binarywang.wxpay.util.SignUtils; import com.github.binarywang.wxpay.util.XmlConfig; import com.github.binarywang.wxpay.util.ZipUtils; @@ -29,14 +32,6 @@ import com.google.common.collect.Maps; import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import lombok.Getter; -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; -import me.chanjar.weixin.common.error.WxRuntimeException; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.reflect.ConstructorUtils; -import org.apache.http.entity.ContentType; - import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -45,12 +40,15 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.security.GeneralSecurityException; -import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.zip.ZipException; - -import static com.github.binarywang.wxpay.constant.WxPayConstants.QUERY_COMMENT_DATE_FORMAT; -import static com.github.binarywang.wxpay.constant.WxPayConstants.TarType; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.error.WxRuntimeException; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.reflect.ConstructorUtils; +import org.apache.http.entity.ContentType; /** *
@@ -139,6 +137,9 @@ public abstract class BaseWxPayServiceImpl implements WxPayService {
   @Getter
   private final BusinessOperationTransferService businessOperationTransferService = new BusinessOperationTransferServiceImpl(this);
 
+  @Getter
+  private final MiPayService miPayService = new MiPayServiceImpl(this);
+
   protected Map configMap = new ConcurrentHashMap<>();
 
   @Override
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/MiPayServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/MiPayServiceImpl.java
new file mode 100644
index 0000000000..3063d7731e
--- /dev/null
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/MiPayServiceImpl.java
@@ -0,0 +1,68 @@
+package com.github.binarywang.wxpay.service.impl;
+
+import com.github.binarywang.wxpay.bean.mipay.MedInsOrdersRequest;
+import com.github.binarywang.wxpay.bean.mipay.MedInsOrdersResult;
+import com.github.binarywang.wxpay.bean.mipay.MedInsRefundNotifyRequest;
+import com.github.binarywang.wxpay.bean.notify.MiPayNotifyV3Result;
+import com.github.binarywang.wxpay.bean.notify.SignatureHeader;
+import com.github.binarywang.wxpay.exception.WxPayException;
+import com.github.binarywang.wxpay.service.MiPayService;
+import com.github.binarywang.wxpay.service.WxPayService;
+import com.github.binarywang.wxpay.v3.util.RsaCryptoUtil;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import java.security.cert.X509Certificate;
+import lombok.RequiredArgsConstructor;
+
+/**
+ * 医保相关接口
+ * 医保相关接口
+ * @author xgl
+ * @date 2025/12/20
+ */
+@RequiredArgsConstructor
+public class MiPayServiceImpl implements MiPayService {
+
+  private final WxPayService payService;
+  private static final Gson GSON = new GsonBuilder().create();
+
+
+  @Override
+  public MedInsOrdersResult medInsOrders(MedInsOrdersRequest request) throws WxPayException {
+
+    String url = String.format("%s/v3/med-ins/orders", this.payService.getPayBaseUrl());
+    X509Certificate validCertificate = this.payService.getConfig().getVerifier().getValidCertificate();
+
+    RsaCryptoUtil.encryptFields(request, validCertificate);
+
+    String result = this.payService.postV3WithWechatpaySerial(url, GSON.toJson(request));
+    return GSON.fromJson(result, MedInsOrdersResult.class);
+  }
+
+  @Override
+  public MedInsOrdersResult getMedInsOrderByMixTradeNo(String mixTradeNo, String subMchid) throws WxPayException {
+    String url = String.format("%s/v3/med-ins/orders/mix-trade-no/%s?sub_mchid=%s", this.payService.getPayBaseUrl(), mixTradeNo, subMchid);
+    String result = this.payService.getV3(url);
+    return GSON.fromJson(result, MedInsOrdersResult.class);
+  }
+
+  @Override
+  public MedInsOrdersResult getMedInsOrderByOutTradeNo(String outTradeNo, String subMchid) throws WxPayException {
+    String url = String.format("%s/v3/med-ins/orders/out-trade-no/%s?sub_mchid=%s", this.payService.getPayBaseUrl(), outTradeNo, subMchid);
+    String result = this.payService.getV3(url);
+    return GSON.fromJson(result, MedInsOrdersResult.class);
+  }
+
+  @Override
+  public MiPayNotifyV3Result parseMiPayNotifyV3Result(String notifyData, SignatureHeader header) throws WxPayException {
+    return this.payService.baseParseOrderNotifyV3Result(notifyData, header, MiPayNotifyV3Result.class, MiPayNotifyV3Result.DecryptNotifyResult.class);
+  }
+
+  @Override
+  public void medInsRefundNotify(MedInsRefundNotifyRequest request) throws WxPayException {
+    String url = String.format("%s/v3/med-ins/refunds/notify?mix_trade_no=%s", this.payService.getPayBaseUrl(), request.getMixTradeNo());
+    this.payService.postV3(url, GSON.toJson(request));
+  }
+
+
+}
diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/MiPayServiceImplTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/MiPayServiceImplTest.java
new file mode 100644
index 0000000000..23c3c56816
--- /dev/null
+++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/MiPayServiceImplTest.java
@@ -0,0 +1,148 @@
+package com.github.binarywang.wxpay.service.impl;
+
+import com.github.binarywang.wxpay.bean.mipay.MedInsOrdersRequest;
+import com.github.binarywang.wxpay.bean.mipay.MedInsOrdersResult;
+import com.github.binarywang.wxpay.bean.mipay.MedInsRefundNotifyRequest;
+import com.github.binarywang.wxpay.bean.notify.MiPayNotifyV3Result;
+import com.github.binarywang.wxpay.bean.notify.SignatureHeader;
+import com.github.binarywang.wxpay.exception.WxPayException;
+import com.github.binarywang.wxpay.service.MiPayService;
+import com.github.binarywang.wxpay.service.WxPayService;
+import com.github.binarywang.wxpay.testbase.ApiTestModule;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.inject.Inject;
+import lombok.extern.slf4j.Slf4j;
+import org.testng.annotations.Guice;
+import org.testng.annotations.Test;
+
+/**
+ * 医保接口测试
+ * @author xgl
+ * @date 2025/12/20 10:04
+ */
+@Slf4j
+@Test
+@Guice(modules = ApiTestModule.class)
+public class MiPayServiceImplTest {
+
+  @Inject
+  private WxPayService wxPayService;
+
+  private static final Gson GSON = new GsonBuilder().create();
+
+
+  /**
+   * 医保自费混合收款下单测试
+   * @throws WxPayException
+   */
+  @Test
+  public void medInsOrders() throws WxPayException {
+    String requestParamStr = "{\"mix_pay_type\":\"CASH_AND_INSURANCE\",\"order_type\":\"REG_PAY\",\"appid\":\"wxdace645e0bc2cXXX\",\"sub_appid\":\"wxdace645e0bc2cXXX\",\"sub_mchid\":\"1900008XXX\",\"openid\":\"o4GgauInH_RCEdvrrNGrntXDuXXX\",\"sub_openid\":\"o4GgauInH_RCEdvrrNGrntXDuXXX\",\"payer\":{\"name\":\"张三\",\"id_digest\":\"09eb26e839ff3a2e3980352ae45ef09e\",\"card_type\":\"ID_CARD\"},\"pay_for_relatives\":false,\"relative\":{\"name\":\"张三\",\"id_digest\":\"09eb26e839ff3a2e3980352ae45ef09e\",\"card_type\":\"ID_CARD\"},\"out_trade_no\":\"202204022005169952975171534816\",\"serial_no\":\"1217752501201\",\"pay_order_id\":\"ORD530100202204022006350000021\",\"pay_auth_no\":\"AUTH530100202204022006310000034\",\"geo_location\":\"102.682296,25.054260\",\"city_id\":\"530100\",\"med_inst_name\":\"北大医院\",\"med_inst_no\":\"1217752501201407033233368318\",\"med_ins_order_create_time\":\"2015-05-20T13:29:35+08:00\",\"total_fee\":202000,\"med_ins_gov_fee\":100000,\"med_ins_self_fee\":45000,\"med_ins_other_fee\":5000,\"med_ins_cash_fee\":50000,\"wechat_pay_cash_fee\":42000,\"cash_add_detail\":[{\"cash_add_fee\":2000,\"cash_add_type\":\"FREIGHT\"}],\"cash_reduce_detail\":[{\"cash_reduce_fee\":10000,\"cash_reduce_type\":\"DEFAULT_REDUCE_TYPE\"}],\"callback_url\":\"https://www.weixin.qq.com/wxpay/pay.php\",\"prepay_id\":\"wx201410272009395522657a690389285100\",\"passthrough_request_content\":\"{\\\"payAuthNo\\\":\\\"AUTH****\\\",\\\"payOrdId\\\":\\\"ORD****\\\",\\\"setlLatlnt\\\":\\\"118.096435,24.485407\\\"}\",\"extends\":\"{}\",\"attach\":\"{}\",\"channel_no\":\"AAGN9uhZc5EGyRdairKW7Qnu\",\"med_ins_test_env\":false}";
+
+    MedInsOrdersRequest request = GSON.fromJson(requestParamStr, MedInsOrdersRequest.class);
+
+    MiPayService miPayService = wxPayService.getMiPayService();
+
+    MedInsOrdersResult result = miPayService.medInsOrders(request);
+
+    log.info(result.toString());
+  }
+
+  /**
+   * 使用医保自费混合订单号查看下单结果测试
+   * @throws WxPayException
+   */
+  @Test
+  public void getMedInsOrderByMixTradeNo() throws WxPayException {
+    // 测试用的医保自费混合订单号和医疗机构商户号
+    String mixTradeNo = "202204022005169952975171534816";
+    String subMchid = "1900000109";
+
+    MiPayService miPayService = wxPayService.getMiPayService();
+
+    MedInsOrdersResult result = miPayService.getMedInsOrderByMixTradeNo(mixTradeNo, subMchid);
+
+    log.info(result.toString());
+  }
+
+  /**
+   * 使用从业机构订单号查看下单结果测试
+   * @throws WxPayException
+   */
+  @Test
+  public void getMedInsOrderByOutTradeNo() throws WxPayException {
+    // 测试用的从业机构订单号和医疗机构商户号
+    String outTradeNo = "202204022005169952975171534816";
+    String subMchid = "1900000109";
+
+    MiPayService miPayService = wxPayService.getMiPayService();
+
+    MedInsOrdersResult result = miPayService.getMedInsOrderByOutTradeNo(outTradeNo, subMchid);
+
+    log.info(result.toString());
+  }
+
+  /**
+   * 解析医保混合收款成功通知测试
+   * @throws WxPayException
+   */
+  @Test
+  public void parseMiPayNotifyV3Result() throws WxPayException {
+    // 模拟的医保混合收款成功通知数据
+    String notifyData = "{\"id\":\"EV-202401011234567890\",\"create_time\":\"2024-01-01T12:34:56+08:00\",\"event_type\":\"MEDICAL_INSURANCE.SUCCESS\",\"summary\":\"医保混合收款成功\",\"resource_type\":\"encrypt-resource\",\"resource\":{\"algorithm\":\"AEAD_AES_256_GCM\",\"ciphertext\":\"encrypted_data\",\"associated_data\":\"\",\"nonce\":\"random_string\"}}";
+
+    // 模拟的签名信息
+    String signature = "test_signature";
+    String timestamp = "1234567890";
+    String nonce = "test_nonce";
+    String serial = "test_serial";
+
+    MiPayService miPayService = wxPayService.getMiPayService();
+
+    SignatureHeader header = SignatureHeader.builder()
+      .signature(signature)
+      .timeStamp(timestamp)
+      .nonce(nonce)
+      .serial(serial)
+      .build();
+
+    try {
+      // 调用解析方法,预期会失败,因为是模拟数据
+      MiPayNotifyV3Result result = miPayService.parseMiPayNotifyV3Result(notifyData, header);
+      log.info("解析结果:{}", result);
+    } catch (WxPayException e) {
+      // 预期会抛出异常,因为是模拟数据,签名验证和解密都会失败
+      log.info("预期的异常:{}", e.getMessage());
+    }
+  }
+
+  /**
+   * 医保退款通知测试
+   * @throws WxPayException
+   */
+  @Test
+  public void medInsRefundNotify() throws WxPayException {
+    // 测试用的医保自费混合订单号
+    String mixTradeNo = "202204022005169952975171534816";
+
+    // 模拟的医保退款通知请求数据
+    String requestParamStr = "{\"sub_mchid\":\"1900008XXX\",\"med_refund_total_fee\":45000,\"med_refund_gov_fee\":45000,\"med_refund_self_fee\":45000,\"med_refund_other_fee\":45000,\"refund_time\":\"2015-05-20T13:29:35+08:00\",\"out_refund_no\":\"R202204022005169952975171534816\"}";
+
+    // 解析请求参数
+    MedInsRefundNotifyRequest request = GSON.fromJson(requestParamStr, MedInsRefundNotifyRequest.class);
+    request.setMixTradeNo(mixTradeNo);
+
+    MiPayService miPayService = wxPayService.getMiPayService();
+
+    try {
+      // 调用医保退款通知方法,预期会失败,因为是模拟数据
+      miPayService.medInsRefundNotify(request);
+      log.info("医保退款通知调用成功");
+    } catch (WxPayException e) {
+      // 预期会抛出异常,因为是模拟数据
+      log.info("预期的异常:{}", e.getMessage());
+    }
+  }
+
+}

From 3b8d0a8814e6e6891f82831393ce07f60d03f2ea Mon Sep 17 00:00:00 2001
From: Copilot <198982749+Copilot@users.noreply.github.com>
Date: Sat, 20 Dec 2025 17:23:55 +0800
Subject: [PATCH 14/46] =?UTF-8?q?:new:=20#3814=20=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=AE=9E?=
 =?UTF-8?q?=E5=90=8D=E9=AA=8C=E8=AF=81=E7=9A=84=E6=8E=A5=E5=8F=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 weixin-java-pay/REAL_NAME_USAGE.md            | 143 ++++++++++++++++++
 .../wxpay/bean/realname/RealNameRequest.java  |  50 ++++++
 .../wxpay/bean/realname/RealNameResult.java   |  91 +++++++++++
 .../wxpay/service/RealNameService.java        |  43 ++++++
 .../wxpay/service/WxPayService.java           |   7 +
 .../service/impl/BaseWxPayServiceImpl.java    |   3 +
 .../service/impl/RealNameServiceImpl.java     |  41 +++++
 .../service/impl/RealNameServiceImplTest.java |  54 +++++++
 8 files changed, 432 insertions(+)
 create mode 100644 weixin-java-pay/REAL_NAME_USAGE.md
 create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/realname/RealNameRequest.java
 create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/realname/RealNameResult.java
 create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/RealNameService.java
 create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/RealNameServiceImpl.java
 create mode 100644 weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/RealNameServiceImplTest.java

diff --git a/weixin-java-pay/REAL_NAME_USAGE.md b/weixin-java-pay/REAL_NAME_USAGE.md
new file mode 100644
index 0000000000..867bca9ce2
--- /dev/null
+++ b/weixin-java-pay/REAL_NAME_USAGE.md
@@ -0,0 +1,143 @@
+# 微信支付实名验证接口使用说明
+
+## 概述
+
+微信支付实名验证接口允许商户查询用户的实名认证状态,如果用户未实名认证,接口会返回引导用户进行实名认证的URL。
+
+## 官方文档
+
+https://pay.wechatpay.cn/doc/v2/merchant/4011987607
+
+## 接口说明
+
+### 查询用户实名认证信息
+
+- **接口地址**:`https://api.mch.weixin.qq.com/userinfo/realnameauth/query`
+- **请求方式**:POST(需要使用商户证书)
+- **请求参数**:
+  - `appid`:公众账号ID(自动填充)
+  - `mch_id`:商户号(自动填充)
+  - `openid`:用户在商户appid下的唯一标识
+  - `nonce_str`:随机字符串(自动生成)
+  - `sign`:签名(自动生成)
+
+- **返回参数**:
+  - `return_code`:返回状态码
+  - `return_msg`:返回信息
+  - `result_code`:业务结果
+  - `openid`:用户标识
+  - `is_certified`:实名认证状态(Y-已实名认证,N-未实名认证)
+  - `cert_info`:实名认证信息(加密,仅已实名时返回)
+  - `guide_url`:引导用户进行实名认证的URL(仅未实名时返回)
+
+## 使用示例
+
+### 1. 获取实名验证服务
+
+```java
+// 获取WxPayService实例
+WxPayService wxPayService = ... // 根据你的配置初始化
+
+// 获取实名验证服务
+RealNameService realNameService = wxPayService.getRealNameService();
+```
+
+### 2. 查询用户实名认证状态(完整方式)
+
+```java
+import com.github.binarywang.wxpay.bean.realname.RealNameRequest;
+import com.github.binarywang.wxpay.bean.realname.RealNameResult;
+import com.github.binarywang.wxpay.exception.WxPayException;
+
+try {
+    // 构建请求对象
+    RealNameRequest request = RealNameRequest.newBuilder()
+        .openid("oUpF8uMuAJO_M2pxb1Q9zNjWeS6o") // 用户的openid
+        .build();
+
+    // 调用查询接口
+    RealNameResult result = realNameService.queryRealName(request);
+
+    // 处理返回结果
+    if ("Y".equals(result.getIsCertified())) {
+        System.out.println("用户已实名认证");
+        System.out.println("认证信息:" + result.getCertInfo());
+    } else {
+        System.out.println("用户未实名认证");
+        System.out.println("引导链接:" + result.getGuideUrl());
+        // 可以将guide_url提供给用户,引导其完成实名认证
+    }
+} catch (WxPayException e) {
+    System.err.println("查询失败:" + e.getMessage());
+}
+```
+
+### 3. 查询用户实名认证状态(简化方式)
+
+```java
+import com.github.binarywang.wxpay.bean.realname.RealNameResult;
+import com.github.binarywang.wxpay.exception.WxPayException;
+
+try {
+    // 直接传入openid进行查询
+    RealNameResult result = realNameService.queryRealName("oUpF8uMuAJO_M2pxb1Q9zNjWeS6o");
+
+    // 处理返回结果
+    if ("Y".equals(result.getIsCertified())) {
+        System.out.println("用户已实名认证");
+    } else {
+        System.out.println("用户未实名认证,引导链接:" + result.getGuideUrl());
+    }
+} catch (WxPayException e) {
+    System.err.println("查询失败:" + e.getMessage());
+}
+```
+
+## 注意事项
+
+1. **证书要求**:本接口需要使用商户证书进行请求,请确保已正确配置商户证书。
+
+2. **OPENID获取**:openid是用户在商户appid下的唯一标识,需要通过微信公众平台或小程序获取。
+
+3. **认证信息**:`cert_info`字段返回的信息是加密的,需要使用相应的解密方法才能获取明文信息。
+
+4. **引导链接**:当用户未实名时,返回的`guide_url`可以用于引导用户完成实名认证,建议在小程序或H5页面中使用。
+
+5. **频率限制**:请注意接口调用频率限制,避免频繁查询同一用户的实名状态。
+
+## 业务场景
+
+- **转账前校验**:在进行企业付款到零钱等操作前,可以先查询用户的实名认证状态
+- **风控审核**:作为业务风控的一部分,确认用户已完成实名认证
+- **用户引导**:发现用户未实名时,引导用户完成实名认证以使用相关功能
+
+## 错误处理
+
+```java
+try {
+    RealNameResult result = realNameService.queryRealName(openid);
+    // 处理结果...
+} catch (WxPayException e) {
+    // 处理异常
+    String errorCode = e.getErrCode();
+    String errorMsg = e.getErrCodeDes();
+    
+    // 根据错误码进行相应处理
+    switch (errorCode) {
+        case "SYSTEMERROR":
+            // 系统错误,建议稍后重试
+            break;
+        case "PARAM_ERROR":
+            // 参数错误,检查openid是否正确
+            break;
+        default:
+            // 其他错误
+            break;
+    }
+}
+```
+
+## 相关链接
+
+- [微信支付官方文档](https://pay.wechatpay.cn/doc/v2/merchant/4011987607)
+- [WxJava项目主页](https://github.com/binarywang/WxJava)
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/realname/RealNameRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/realname/RealNameRequest.java
new file mode 100644
index 0000000000..a06318d613
--- /dev/null
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/realname/RealNameRequest.java
@@ -0,0 +1,50 @@
+package com.github.binarywang.wxpay.bean.realname;
+
+import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest;
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import lombok.*;
+import me.chanjar.weixin.common.annotation.Required;
+
+import java.util.Map;
+
+/**
+ * 
+ * 微信支付实名验证请求对象.
+ * 详见文档:https://pay.wechatpay.cn/doc/v2/merchant/4011987607
+ * 
+ * + * @author Binary Wang + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor +@XStreamAlias("xml") +public class RealNameRequest extends BaseWxPayRequest { + private static final long serialVersionUID = 1L; + + /** + *
+   * 字段名:用户标识
+   * 变量名:openid
+   * 是否必填:是
+   * 类型:String(128)
+   * 示例值:oUpF8uMuAJO_M2pxb1Q9zNjWeS6o
+   * 描述:用户在商户appid下的唯一标识
+   * 
+ */ + @Required + @XStreamAlias("openid") + private String openid; + + @Override + protected void checkConstraints() { + //do nothing + } + + @Override + protected void storeMap(Map map) { + map.put("openid", openid); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/realname/RealNameResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/realname/RealNameResult.java new file mode 100644 index 0000000000..0300cd88be --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/realname/RealNameResult.java @@ -0,0 +1,91 @@ +package com.github.binarywang.wxpay.bean.realname; + +import com.github.binarywang.wxpay.bean.result.BaseWxPayResult; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.w3c.dom.Document; + +import java.io.Serializable; + +/** + *
+ * 微信支付实名验证返回结果.
+ * 详见文档:https://pay.wechatpay.cn/doc/v2/merchant/4011987607
+ * 
+ * + * @author Binary Wang + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@XStreamAlias("xml") +public class RealNameResult extends BaseWxPayResult implements Serializable { + private static final long serialVersionUID = 1L; + + /** + *
+   * 字段名:用户标识
+   * 变量名:openid
+   * 是否必填:否
+   * 类型:String(128)
+   * 示例值:oUpF8uMuAJO_M2pxb1Q9zNjWeS6o
+   * 描述:用户在商户appid下的唯一标识
+   * 
+ */ + @XStreamAlias("openid") + private String openid; + + /** + *
+   * 字段名:实名认证状态
+   * 变量名:is_certified
+   * 是否必填:是
+   * 类型:String(1)
+   * 示例值:Y
+   * 描述:Y-已实名认证 N-未实名认证
+   * 
+ */ + @XStreamAlias("is_certified") + private String isCertified; + + /** + *
+   * 字段名:实名认证信息
+   * 变量名:cert_info
+   * 是否必填:否
+   * 类型:String(256)
+   * 示例值:
+   * 描述:实名认证的相关信息,如姓名等(加密)
+   * 
+ */ + @XStreamAlias("cert_info") + private String certInfo; + + /** + *
+   * 字段名:引导链接
+   * 变量名:guide_url
+   * 是否必填:否
+   * 类型:String(256)
+   * 示例值:
+   * 描述:未实名时,引导用户进行实名认证的URL
+   * 
+ */ + @XStreamAlias("guide_url") + private String guideUrl; + + /** + * 从XML结构中加载额外的属性 + * + * @param d Document + */ + @Override + protected void loadXml(Document d) { + openid = readXmlString(d, "openid"); + isCertified = readXmlString(d, "is_certified"); + certInfo = readXmlString(d, "cert_info"); + guideUrl = readXmlString(d, "guide_url"); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/RealNameService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/RealNameService.java new file mode 100644 index 0000000000..d69bda7d33 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/RealNameService.java @@ -0,0 +1,43 @@ +package com.github.binarywang.wxpay.service; + +import com.github.binarywang.wxpay.bean.realname.RealNameRequest; +import com.github.binarywang.wxpay.bean.realname.RealNameResult; +import com.github.binarywang.wxpay.exception.WxPayException; + +/** + *
+ * 微信支付实名验证相关服务类.
+ * 详见文档:https://pay.wechatpay.cn/doc/v2/merchant/4011987607
+ * 
+ * + * @author Binary Wang + */ +public interface RealNameService { + /** + *
+   * 获取用户实名认证信息API.
+   * 用于商户查询用户的实名认证状态,如果用户未实名认证,会返回引导用户实名认证的URL
+   * 文档详见:https://pay.wechatpay.cn/doc/v2/merchant/4011987607
+   * 接口链接:https://api.mch.weixin.qq.com/userinfo/realnameauth/query
+   * 
+ * + * @param request 请求对象 + * @return 实名认证查询结果 + * @throws WxPayException the wx pay exception + */ + RealNameResult queryRealName(RealNameRequest request) throws WxPayException; + + /** + *
+   * 获取用户实名认证信息API(简化方法).
+   * 用于商户查询用户的实名认证状态,如果用户未实名认证,会返回引导用户实名认证的URL
+   * 文档详见:https://pay.wechatpay.cn/doc/v2/merchant/4011987607
+   * 接口链接:https://api.mch.weixin.qq.com/userinfo/realnameauth/query
+   * 
+ * + * @param openid 用户openid + * @return 实名认证查询结果 + * @throws WxPayException the wx pay exception + */ + RealNameResult queryRealName(String openid) throws WxPayException; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java index cfb2479ae7..dab89a0142 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 @@ -1707,6 +1707,13 @@ WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, Stri */ PartnerPayScoreSignPlanService getPartnerPayScoreSignPlanService(); + /** + * 获取实名验证服务类 + * + * @return the real name service + */ + RealNameService getRealNameService(); + /** * 获取医保支付服务类 * 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 f2e343df22..b4c2b919a4 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 @@ -137,6 +137,9 @@ public abstract class BaseWxPayServiceImpl implements WxPayService { @Getter private final BusinessOperationTransferService businessOperationTransferService = new BusinessOperationTransferServiceImpl(this); + @Getter + private final RealNameService realNameService = new RealNameServiceImpl(this); + @Getter private final MiPayService miPayService = new MiPayServiceImpl(this); diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/RealNameServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/RealNameServiceImpl.java new file mode 100644 index 0000000000..9a1c57fe0f --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/RealNameServiceImpl.java @@ -0,0 +1,41 @@ +package com.github.binarywang.wxpay.service.impl; + +import com.github.binarywang.wxpay.bean.realname.RealNameRequest; +import com.github.binarywang.wxpay.bean.realname.RealNameResult; +import com.github.binarywang.wxpay.bean.result.BaseWxPayResult; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.RealNameService; +import com.github.binarywang.wxpay.service.WxPayService; +import lombok.RequiredArgsConstructor; + +/** + *
+ * 微信支付实名验证相关服务实现类.
+ * 详见文档:https://pay.wechatpay.cn/doc/v2/merchant/4011987607
+ * 
+ * + * @author Binary Wang + */ +@RequiredArgsConstructor +public class RealNameServiceImpl implements RealNameService { + private final WxPayService payService; + + @Override + public RealNameResult queryRealName(RealNameRequest request) throws WxPayException { + request.checkAndSign(this.payService.getConfig()); + String url = this.payService.getPayBaseUrl() + "/userinfo/realnameauth/query"; + + String responseContent = this.payService.post(url, request.toXML(), true); + RealNameResult result = BaseWxPayResult.fromXML(responseContent, RealNameResult.class); + result.checkResult(this.payService, request.getSignType(), true); + return result; + } + + @Override + public RealNameResult queryRealName(String openid) throws WxPayException { + RealNameRequest request = RealNameRequest.newBuilder() + .openid(openid) + .build(); + return this.queryRealName(request); + } +} diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/RealNameServiceImplTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/RealNameServiceImplTest.java new file mode 100644 index 0000000000..dda2371948 --- /dev/null +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/RealNameServiceImplTest.java @@ -0,0 +1,54 @@ +package com.github.binarywang.wxpay.service.impl; + +import com.github.binarywang.wxpay.bean.realname.RealNameRequest; +import com.github.binarywang.wxpay.bean.realname.RealNameResult; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.WxPayService; +import com.github.binarywang.wxpay.testbase.ApiTestModule; +import com.google.inject.Inject; +import lombok.extern.slf4j.Slf4j; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +/** + *
+ *  实名验证测试类.
+ * 
+ * + * @author Binary Wang + */ +@Test +@Guice(modules = ApiTestModule.class) +@Slf4j +public class RealNameServiceImplTest { + + @Inject + private WxPayService payService; + + /** + * 测试查询用户实名认证信息. + * + * @throws WxPayException the wx pay exception + */ + @Test + public void testQueryRealName() throws WxPayException { + RealNameRequest request = RealNameRequest.newBuilder() + .openid("oUpF8uMuAJO_M2pxb1Q9zNjWeS6o") + .build(); + + RealNameResult result = this.payService.getRealNameService().queryRealName(request); + log.info("实名认证查询结果:{}", result); + } + + /** + * 测试查询用户实名认证信息(简化方法). + * + * @throws WxPayException the wx pay exception + */ + @Test + public void testQueryRealNameSimple() throws WxPayException { + RealNameResult result = this.payService.getRealNameService() + .queryRealName("oUpF8uMuAJO_M2pxb1Q9zNjWeS6o"); + log.info("实名认证查询结果:{}", result); + } +} From 14537479c75261ffe6618380eb764f527fc2b685 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sat, 20 Dec 2025 20:07:04 +0800 Subject: [PATCH 15/46] =?UTF-8?q?:art:=20#3813=20=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=E5=A7=94?= =?UTF-8?q?=E6=89=98=E4=BB=A3=E6=89=A3=E5=8D=8F=E8=AE=AE=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E5=90=8D=E6=8B=BC=E5=86=99=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../binarywang/wxpay/bean/result/WxSignQueryResult.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxSignQueryResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxSignQueryResult.java index 808b9d7ddf..5241597194 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxSignQueryResult.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxSignQueryResult.java @@ -86,7 +86,7 @@ public class WxSignQueryResult extends BaseWxPayResult implements Serializable { * 协议解约方式 * 非必传 */ - @XStreamAlias("contract_terminated_mode") + @XStreamAlias("contract_termination_mode") private Integer contractTerminatedMode; /** @@ -114,7 +114,7 @@ protected void loadXml(Document d) { contractSignedTime = readXmlString(d, "contract_signed_time"); contractExpiredTime = readXmlString(d, "contrace_Expired_time"); contractTerminatedTime = readXmlString(d, "contract_terminated_time"); - contractTerminatedMode = readXmlInteger(d, "contract_terminate_mode"); + contractTerminatedMode = readXmlInteger(d, "contract_termination_mode"); contractTerminationRemark = readXmlString(d, "contract_termination_remark"); openId = readXmlString(d, "openid"); } From 58cdef951fff41a02ee4e72f41b5c2ce1dbda893 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sat, 20 Dec 2025 20:08:30 +0800 Subject: [PATCH 16/46] =?UTF-8?q?:new:=20#3811=20=E3=80=90=E5=B0=8F?= =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E3=80=91=E6=B7=BB=E5=8A=A0=E5=B0=8F=E6=B8=B8?= =?UTF-8?q?=E6=88=8F=E9=81=93=E5=85=B7=E7=9B=B4=E8=B4=AD=EF=BC=88present?= =?UTF-8?q?=5Fgoods=EF=BC=89API=E7=9A=84=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wx/miniapp/api/WxMaXPayService.java | 10 +++ .../miniapp/api/impl/WxMaXPayServiceImpl.java | 16 +++++ .../xpay/WxMaXPayPresentGoodsRequest.java | 63 +++++++++++++++++++ .../xpay/WxMaXPayPresentGoodsResponse.java | 34 ++++++++++ .../miniapp/constant/WxMaApiUrlConstants.java | 1 + 5 files changed, 124 insertions(+) create mode 100644 weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/xpay/WxMaXPayPresentGoodsRequest.java create mode 100644 weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/xpay/WxMaXPayPresentGoodsResponse.java diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaXPayService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaXPayService.java index a633c93de6..68d4dc0c97 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaXPayService.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaXPayService.java @@ -71,6 +71,16 @@ public interface WxMaXPayService { */ WxMaXPayPresentCurrencyResponse presentCurrency(WxMaXPayPresentCurrencyRequest request, WxMaXPaySigParams sigParams) throws WxErrorException; + /** + * 道具直购。 + * + * @param request 道具直购请求对象 + * @param sigParams 签名参数对象 + * @return 道具直购结果 + * @throws WxErrorException 直购失败时抛出 + */ + WxMaXPayPresentGoodsResponse presentGoods(WxMaXPayPresentGoodsRequest request, WxMaXPaySigParams sigParams) throws WxErrorException; + /** * 下载对账单。 * diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaXPayServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaXPayServiceImpl.java index 29a7c51a2c..37cf5e0d4e 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaXPayServiceImpl.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaXPayServiceImpl.java @@ -108,6 +108,22 @@ public WxMaXPayPresentCurrencyResponse presentCurrency(WxMaXPayPresentCurrencyRe return getDetailResponse; } + @Override + public WxMaXPayPresentGoodsResponse presentGoods(WxMaXPayPresentGoodsRequest request, WxMaXPaySigParams sigParams) throws WxErrorException { + final String postBody = request.toJson(); + final String uri = sigParams.signUriWithPay(PRESENT_GOODS_URL, postBody); + String responseContent = this.service.post(uri, postBody); + WxMaXPayPresentGoodsResponse getDetailResponse = WxMaGsonBuilder.create() + .fromJson(responseContent, WxMaXPayPresentGoodsResponse.class); + + if (getDetailResponse.getErrcode() != 0) { + throw new WxErrorException( + new WxError(getDetailResponse.getErrcode(), getDetailResponse.getErrmsg())); + } + + return getDetailResponse; + } + @Override public WxMaXPayDownloadBillResponse downloadBill(WxMaXPayDownloadBillRequest request, WxMaXPaySigParams sigParams) throws WxErrorException { final String postBody = request.toJson(); diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/xpay/WxMaXPayPresentGoodsRequest.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/xpay/WxMaXPayPresentGoodsRequest.java new file mode 100644 index 0000000000..ba9d7100da --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/xpay/WxMaXPayPresentGoodsRequest.java @@ -0,0 +1,63 @@ +package cn.binarywang.wx.miniapp.bean.xpay; + +import cn.binarywang.wx.miniapp.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 小游戏道具直购API请求. + * + * @author Binary Wang + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WxMaXPayPresentGoodsRequest implements Serializable { + private static final long serialVersionUID = 7495157056049312109L; + + /** + * 用户的openid. + */ + @SerializedName("openid") + private String openid; + + /** + * 环境。0-正式环境;1-沙箱环境. + */ + @SerializedName("env") + private Integer env; + + /** + * 商户订单号. + */ + @SerializedName("order_id") + private String orderId; + + /** + * 设备类型。0-安卓;1-iOS. + */ + @SerializedName("device_type") + private Integer deviceType; + + /** + * 道具id. + */ + @SerializedName("goods_id") + private String goodsId; + + /** + * 道具数量. + */ + @SerializedName("goods_number") + private Integer goodsNumber; + + public String toJson() { + return WxMaGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/xpay/WxMaXPayPresentGoodsResponse.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/xpay/WxMaXPayPresentGoodsResponse.java new file mode 100644 index 0000000000..ed3ea8d7f0 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/xpay/WxMaXPayPresentGoodsResponse.java @@ -0,0 +1,34 @@ +package cn.binarywang.wx.miniapp.bean.xpay; + +import cn.binarywang.wx.miniapp.bean.WxMaBaseResponse; +import cn.binarywang.wx.miniapp.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 小游戏道具直购API响应. + * + * @author Binary Wang + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WxMaXPayPresentGoodsResponse extends WxMaBaseResponse implements Serializable { + private static final long serialVersionUID = 7495157056049312110L; + + /** + * 商户订单号. + */ + @SerializedName("order_id") + private String orderId; + + public String toJson() { + return WxMaGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaApiUrlConstants.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaApiUrlConstants.java index 45e1219659..3963286394 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaApiUrlConstants.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaApiUrlConstants.java @@ -875,6 +875,7 @@ public interface XPay { String NOTIFY_PROVIDE_GOODS_URL = "https://api.weixin.qq.com/xpay/notify_provide_goods?pay_sig=%s"; String PRESENT_CURRENCY_URL = "https://api.weixin.qq.com/xpay/present_currency?pay_sig=%s"; + String PRESENT_GOODS_URL = "https://api.weixin.qq.com/xpay/present_goods?pay_sig=%s"; String DOWNLOAD_BILL_URL = "https://api.weixin.qq.com/xpay/download_bill?pay_sig=%s"; String REFUND_ORDER_URL = "https://api.weixin.qq.com/xpay/refund_order?pay_sig=%s"; String CREATE_WITHDRAW_ORDER_URL = From 621b8dc0e457302484054d3b1a997bc7da75b220 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sun, 21 Dec 2025 17:31:53 +0800 Subject: [PATCH 17/46] =?UTF-8?q?:new:=20#3812=20=E3=80=90=E5=B0=8F?= =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E3=80=91=E5=AE=9E=E7=8E=B0=E7=94=A8=E5=B7=A5?= =?UTF-8?q?=E5=85=B3=E7=B3=BBAPI=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/WxMaEmployeeRelationService.java | 41 +++++++++++++ .../wx/miniapp/api/WxMaService.java | 9 +++ .../miniapp/api/impl/BaseWxMaServiceImpl.java | 7 +++ .../impl/WxMaEmployeeRelationServiceImpl.java | 32 ++++++++++ .../employee/WxMaSendEmployeeMsgRequest.java | 61 +++++++++++++++++++ .../employee/WxMaUnbindEmployeeRequest.java | 51 ++++++++++++++++ .../miniapp/constant/WxMaApiUrlConstants.java | 8 +++ 7 files changed, 209 insertions(+) create mode 100644 weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaEmployeeRelationService.java create mode 100644 weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaEmployeeRelationServiceImpl.java create mode 100644 weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/employee/WxMaSendEmployeeMsgRequest.java create mode 100644 weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/employee/WxMaUnbindEmployeeRequest.java diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaEmployeeRelationService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaEmployeeRelationService.java new file mode 100644 index 0000000000..4c0a74b2ce --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaEmployeeRelationService.java @@ -0,0 +1,41 @@ +package cn.binarywang.wx.miniapp.api; + +import cn.binarywang.wx.miniapp.bean.employee.WxMaSendEmployeeMsgRequest; +import cn.binarywang.wx.miniapp.bean.employee.WxMaUnbindEmployeeRequest; +import me.chanjar.weixin.common.error.WxErrorException; + +/** + * 小程序用工关系相关操作接口 + *

+ * 文档地址:用工关系简介 + *

+ * + * @author Binary Wang + * created on 2025-12-19 + */ +public interface WxMaEmployeeRelationService { + + /** + * 解绑用工关系 + *

+ * 企业可以调用该接口解除和用户的B2C用工关系 + *

+ * 文档地址:解绑用工关系 + * + * @param request 解绑请求参数 + * @throws WxErrorException 调用微信接口失败时抛出 + */ + void unbindEmployee(WxMaUnbindEmployeeRequest request) throws WxErrorException; + + /** + * 推送用工消息 + *

+ * 企业可以调用该接口向用户推送用工相关消息 + *

+ * 文档地址:推送用工消息 + * + * @param request 推送消息请求参数 + * @throws WxErrorException 调用微信接口失败时抛出 + */ + void sendEmployeeMsg(WxMaSendEmployeeMsgRequest request) throws WxErrorException; +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaService.java index 26ced8bedd..dc7425fa6e 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 @@ -621,4 +621,13 @@ WxMaApiResponse execute( * @return 交易投诉服务对象WxMaComplaintService */ WxMaComplaintService getComplaintService(); + + /** + * 获取用工关系服务对象。 + *
+ * 文档:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/laboruse/intro.html + * + * @return 用工关系服务对象WxMaEmployeeRelationService + */ + WxMaEmployeeRelationService getEmployeeRelationService(); } 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 93bb2656e6..c0e1ff4a4e 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 @@ -167,6 +167,8 @@ public abstract class BaseWxMaServiceImpl implements WxMaService, RequestH private final WxMaPromotionService wxMaPromotionService = new WxMaPromotionServiceImpl(this); private final WxMaIntracityService intracityService = new WxMaIntracityServiceImpl(this); private final WxMaComplaintService complaintService = new WxMaComplaintServiceImpl(this); + private final WxMaEmployeeRelationService employeeRelationService = + new WxMaEmployeeRelationServiceImpl(this); private Map configMap = new HashMap<>(); private int retrySleepMillis = 1000; @@ -1048,4 +1050,9 @@ public WxMaIntracityService getIntracityService() { public WxMaComplaintService getComplaintService() { return this.complaintService; } + + @Override + public WxMaEmployeeRelationService getEmployeeRelationService() { + return this.employeeRelationService; + } } diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaEmployeeRelationServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaEmployeeRelationServiceImpl.java new file mode 100644 index 0000000000..8f240e9151 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaEmployeeRelationServiceImpl.java @@ -0,0 +1,32 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaEmployeeRelationService; +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.employee.WxMaSendEmployeeMsgRequest; +import cn.binarywang.wx.miniapp.bean.employee.WxMaUnbindEmployeeRequest; +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.error.WxErrorException; + +import static cn.binarywang.wx.miniapp.constant.WxMaApiUrlConstants.Employee.SEND_EMPLOYEE_MSG_URL; +import static cn.binarywang.wx.miniapp.constant.WxMaApiUrlConstants.Employee.UNBIND_EMPLOYEE_URL; + +/** + * 小程序用工关系相关操作接口实现 + * + * @author Binary Wang + * created on 2025-12-19 + */ +@RequiredArgsConstructor +public class WxMaEmployeeRelationServiceImpl implements WxMaEmployeeRelationService { + private final WxMaService service; + + @Override + public void unbindEmployee(WxMaUnbindEmployeeRequest request) throws WxErrorException { + this.service.post(UNBIND_EMPLOYEE_URL, request.toJson()); + } + + @Override + public void sendEmployeeMsg(WxMaSendEmployeeMsgRequest request) throws WxErrorException { + this.service.post(SEND_EMPLOYEE_MSG_URL, request.toJson()); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/employee/WxMaSendEmployeeMsgRequest.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/employee/WxMaSendEmployeeMsgRequest.java new file mode 100644 index 0000000000..2d50479817 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/employee/WxMaSendEmployeeMsgRequest.java @@ -0,0 +1,61 @@ +package cn.binarywang.wx.miniapp.bean.employee; + +import cn.binarywang.wx.miniapp.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 小程序推送用工消息请求实体 + *

+ * 文档地址:推送用工消息 + *

+ * + * @author Binary Wang + * created on 2025-12-19 + */ +@Data +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor +public class WxMaSendEmployeeMsgRequest implements Serializable { + private static final long serialVersionUID = 1L; + + /** + *
+   * 字段名:用户openid
+   * 是否必填:是
+   * 描述:需要接收消息的用户openid
+   * 
+ */ + @SerializedName("openid") + private String openid; + + /** + *
+   * 字段名:企业id
+   * 是否必填:是
+   * 描述:企业id,小程序管理员在微信开放平台配置
+   * 
+ */ + @SerializedName("corp_id") + private String corpId; + + /** + *
+   * 字段名:消息内容
+   * 是否必填:是
+   * 描述:推送的消息内容,文本格式,最长不超过200个字符
+   * 
+ */ + @SerializedName("msg") + private String msg; + + public String toJson() { + return WxMaGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/employee/WxMaUnbindEmployeeRequest.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/employee/WxMaUnbindEmployeeRequest.java new file mode 100644 index 0000000000..e56d84670c --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/employee/WxMaUnbindEmployeeRequest.java @@ -0,0 +1,51 @@ +package cn.binarywang.wx.miniapp.bean.employee; + +import cn.binarywang.wx.miniapp.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 小程序解绑用工关系请求实体 + *

+ * 文档地址:解绑用工关系 + *

+ * + * @author Binary Wang + * created on 2025-12-19 + */ +@Data +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor +public class WxMaUnbindEmployeeRequest implements Serializable { + private static final long serialVersionUID = 1L; + + /** + *
+   * 字段名:用户openid
+   * 是否必填:是
+   * 描述:需要解绑的用户openid
+   * 
+ */ + @SerializedName("openid") + private String openid; + + /** + *
+   * 字段名:企业id
+   * 是否必填:是
+   * 描述:企业id,小程序管理员在微信开放平台配置
+   * 
+ */ + @SerializedName("corp_id") + private String corpId; + + public String toJson() { + return WxMaGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaApiUrlConstants.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaApiUrlConstants.java index 3963286394..76625334f4 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaApiUrlConstants.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaApiUrlConstants.java @@ -997,4 +997,12 @@ public interface Complaint { /** 上传反馈图片 */ String UPLOAD_RESPONSE_IMAGE_URL = "https://api.weixin.qq.com/cgi-bin/miniapp/complaint/upload"; } + + /** 用工关系 */ + public interface Employee { + /** 解绑用工关系 */ + String UNBIND_EMPLOYEE_URL = "https://api.weixin.qq.com/wxa/unbinduserb2cauthinfo"; + /** 推送用工消息 */ + String SEND_EMPLOYEE_MSG_URL = "https://api.weixin.qq.com/wxa/sendemployeerelationmsg"; + } } From b0d9c6cc7679688fbdb3f29559d16755a13671f9 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sun, 21 Dec 2025 17:49:11 +0800 Subject: [PATCH 18/46] =?UTF-8?q?:new:=20#3717=20=E3=80=90=E5=BE=AE?= =?UTF-8?q?=E4=BF=A1=E6=94=AF=E4=BB=98=E3=80=91=E5=95=86=E6=88=B7=E8=BD=AC?= =?UTF-8?q?=E8=B4=A6=E6=96=B0=E5=A2=9E=E5=85=8D=E7=A1=AE=E8=AE=A4=E6=94=B6?= =?UTF-8?q?=E6=AC=BE=E6=8E=88=E6=9D=83=E6=A8=A1=E5=BC=8F=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/NEW_TRANSFER_API_SUPPORT.md | 25 ++++ docs/NEW_TRANSFER_API_USAGE.md | 94 +++++++++++++++ .../bean/transfer/TransferBillsRequest.java | 20 ++++ .../wxpay/constant/WxPayConstants.java | 21 ++++ .../wxpay/example/NewTransferApiExample.java | 112 +++++++++++++++++- 5 files changed, 268 insertions(+), 4 deletions(-) diff --git a/docs/NEW_TRANSFER_API_SUPPORT.md b/docs/NEW_TRANSFER_API_SUPPORT.md index c7e9eaf490..835ff7d518 100644 --- a/docs/NEW_TRANSFER_API_SUPPORT.md +++ b/docs/NEW_TRANSFER_API_SUPPORT.md @@ -17,6 +17,7 @@ | **转账方式** | 批量转账 | 单笔转账 | | **场景支持** | 基础场景 | 丰富场景(如佣金报酬等) | | **撤销功能** | ❌ 不支持 | ✅ 支持 | +| **授权模式** | 仅需确认模式 | ✅ 支持免确认授权模式 | | **适用范围** | 所有商户 | **新开通商户必须使用** | ### 2. 新版API功能列表 @@ -27,6 +28,30 @@ ✅ **回调通知** - `parseTransferBillsNotifyResult()` ✅ **RSA加密** - 自动处理用户姓名加密 ✅ **场景支持** - 支持多种转账场景ID +✅ **授权模式** - 支持免确认收款授权模式 + +### 3. 收款授权模式支持 + +**新增功能:免确认收款授权模式** + +- **需确认收款授权模式**(默认):用户需要手动确认才能收款 +- **免确认收款授权模式**:用户授权后,收款无需确认,转账直接到账 + +#### 使用方法 + +```java +// 免确认授权模式 - 提升用户体验 +TransferBillsRequest request = TransferBillsRequest.newBuilder() + .receiptAuthorizationMode(WxPayConstants.ReceiptAuthorizationMode.NO_CONFIRM_RECEIPT_AUTHORIZATION) + // 其他参数... + .build(); + +// 需确认授权模式(默认) +TransferBillsRequest request2 = TransferBillsRequest.newBuilder() + .receiptAuthorizationMode(WxPayConstants.ReceiptAuthorizationMode.CONFIRM_RECEIPT_AUTHORIZATION) + // 其他参数... + .build(); +``` ## 快速开始 diff --git a/docs/NEW_TRANSFER_API_USAGE.md b/docs/NEW_TRANSFER_API_USAGE.md index 9d1ac8254a..7b1a8da4ea 100644 --- a/docs/NEW_TRANSFER_API_USAGE.md +++ b/docs/NEW_TRANSFER_API_USAGE.md @@ -16,6 +16,100 @@ - **API前缀**: `/v3/fund-app/mch-transfer/transfer-bills` - **特点**: 单笔转账,支持更丰富的转账场景 +## 收款授权模式功能 + +### 授权模式说明 + +微信支付转账支持两种收款授权模式: + +#### 1. 需确认收款授权模式(默认) +- **常量**: `WxPayConstants.ReceiptAuthorizationMode.CONFIRM_RECEIPT_AUTHORIZATION` +- **特点**: 用户收到转账后需要手动点击确认才能到账 +- **适用场景**: 一般的转账场景 +- **用户体验**: 安全性高,但需要额外操作 + +#### 2. 免确认收款授权模式 +- **常量**: `WxPayConstants.ReceiptAuthorizationMode.NO_CONFIRM_RECEIPT_AUTHORIZATION` +- **特点**: 用户事先授权后,转账直接到账,无需确认 +- **适用场景**: 高频转账场景,如佣金发放、返现等 +- **用户体验**: 体验流畅,无需额外操作 +- **前提条件**: 需要用户事先进行授权 + +### 使用示例 + +#### 免确认授权模式转账 + +```java +TransferBillsRequest request = TransferBillsRequest.newBuilder() + .appid("your_appid") + .outBillNo("NO_CONFIRM_" + System.currentTimeMillis()) + .transferSceneId("1005") // 佣金报酬场景 + .openid("user_openid") + .transferAmount(200) // 2元 + .transferRemark("免确认收款转账") + .receiptAuthorizationMode(WxPayConstants.ReceiptAuthorizationMode.NO_CONFIRM_RECEIPT_AUTHORIZATION) + .userRecvPerception("Y") + .build(); + +try { + TransferBillsResult result = transferService.transferBills(request); + System.out.println("转账成功,直接到账:" + result.getTransferBillNo()); +} catch (WxPayException e) { + if ("USER_NOT_AUTHORIZED".equals(e.getErrCode())) { + System.err.println("用户未授权免确认收款,请先引导用户进行授权"); + } +} +``` + +#### 需确认授权模式转账(默认) + +```java +TransferBillsRequest request = TransferBillsRequest.newBuilder() + .appid("your_appid") + .outBillNo("CONFIRM_" + System.currentTimeMillis()) + .transferSceneId("1005") + .openid("user_openid") + .transferAmount(150) // 1.5元 + .transferRemark("需确认收款转账") + // .receiptAuthorizationMode(...) // 不设置时使用默认的确认模式 + .userRecvPerception("Y") + .build(); + +TransferBillsResult result = transferService.transferBills(request); +System.out.println("转账发起成功,等待用户确认:" + result.getPackageInfo()); +``` + +### 错误处理 + +使用免确认授权模式时,需要处理以下可能的错误: + +```java +try { + TransferBillsResult result = transferService.transferBills(request); +} catch (WxPayException e) { + switch (e.getErrCode()) { + case "USER_NOT_AUTHORIZED": + // 用户未授权免确认收款 + System.err.println("请先引导用户进行免确认收款授权"); + // 可以引导用户到授权页面 + break; + case "AUTHORIZATION_EXPIRED": + // 授权已过期 + System.err.println("用户授权已过期,请重新授权"); + break; + default: + System.err.println("转账失败:" + e.getMessage()); + } +} +``` + +### 使用建议 + +1. **高频转账场景**推荐使用免确认模式,提升用户体验 +2. **首次使用**需引导用户进行授权 +3. **处理异常**妥善处理授权相关异常,提供友好的错误提示 +4. **场景选择**根据业务场景选择合适的授权模式 + ## 使用新版转账API ### 1. 获取服务实例 diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferBillsRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferBillsRequest.java index 230e564e4b..2ac4b08c93 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferBillsRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferBillsRequest.java @@ -87,6 +87,26 @@ public class TransferBillsRequest implements Serializable { @SerializedName("transfer_scene_report_infos") private List transferSceneReportInfos; + /** + * 收款授权模式 + *
+   * 字段名:收款授权模式
+   * 变量名:receipt_authorization_mode
+   * 是否必填:否
+   * 类型:string
+   * 描述:
+   *  控制收款方式的授权模式,可选值:
+   *  - CONFIRM_RECEIPT_AUTHORIZATION:需确认收款授权模式(默认值)
+   *  - NO_CONFIRM_RECEIPT_AUTHORIZATION:免确认收款授权模式(需要用户事先授权)
+   *  为空时,默认为需确认收款授权模式
+   * 示例值:NO_CONFIRM_RECEIPT_AUTHORIZATION
+   * 
+ * + * @see com.github.binarywang.wxpay.constant.WxPayConstants.ReceiptAuthorizationMode + */ + @SerializedName("receipt_authorization_mode") + private String receiptAuthorizationMode; + @Data @Builder(builderMethodName = "newBuilder") diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/constant/WxPayConstants.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/constant/WxPayConstants.java index ec9e14ff2d..2b736691b7 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/constant/WxPayConstants.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/constant/WxPayConstants.java @@ -524,4 +524,25 @@ public static class CASH_MARKETING { } } + + /** + * 收款授权模式 + * + * @see 官方文档 + */ + @UtilityClass + public static class ReceiptAuthorizationMode { + /** + * 需确认收款授权模式(默认值) + * 用户需要手动确认才能收款 + */ + public static final String CONFIRM_RECEIPT_AUTHORIZATION = "CONFIRM_RECEIPT_AUTHORIZATION"; + + /** + * 免确认收款授权模式 + * 用户授权后,收款不需要确认,转账直接到账 + */ + public static final String NO_CONFIRM_RECEIPT_AUTHORIZATION = "NO_CONFIRM_RECEIPT_AUTHORIZATION"; + } + } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/example/NewTransferApiExample.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/example/NewTransferApiExample.java index 8d74e5a4ef..228234d589 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/example/NewTransferApiExample.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/example/NewTransferApiExample.java @@ -3,6 +3,7 @@ import com.github.binarywang.wxpay.bean.notify.SignatureHeader; import com.github.binarywang.wxpay.bean.transfer.*; import com.github.binarywang.wxpay.config.WxPayConfig; +import com.github.binarywang.wxpay.constant.WxPayConstants; import com.github.binarywang.wxpay.exception.WxPayException; import com.github.binarywang.wxpay.service.TransferService; import com.github.binarywang.wxpay.service.WxPayService; @@ -215,6 +216,100 @@ public void batchTransferExample() { } } + /** + * 使用免确认收款授权模式进行转账示例 + * 注意:使用此模式前,用户需要先进行授权 + */ + public void transferWithNoConfirmAuthModeExample() { + try { + // 构建转账请求,使用免确认收款授权模式 + TransferBillsRequest request = TransferBillsRequest.newBuilder() + .appid("wx1234567890123456") + .outBillNo("NO_CONFIRM_" + System.currentTimeMillis()) // 商户转账单号 + .transferSceneId("1005") // 转账场景ID(佣金报酬) + .openid("oUpF8uMuAJO_M2pxb1Q9zNjWeS6o") // 收款用户的openid + .transferAmount(200) // 转账金额,单位:分(此处为2元) + .transferRemark("免确认收款转账") // 转账备注 + .receiptAuthorizationMode(WxPayConstants.ReceiptAuthorizationMode.NO_CONFIRM_RECEIPT_AUTHORIZATION) + .userRecvPerception("Y") // 用户收款感知 + .build(); + + // 发起转账 + TransferBillsResult result = transferService.transferBills(request); + + System.out.println("=== 免确认授权模式转账成功 ==="); + System.out.println("商户单号: " + result.getOutBillNo()); + System.out.println("微信转账单号: " + result.getTransferBillNo()); + System.out.println("状态: " + result.getState()); + System.out.println("说明: 使用免确认授权模式,转账直接到账,无需用户确认"); + + } catch (WxPayException e) { + System.err.println("免确认授权转账失败: " + e.getMessage()); + System.err.println("错误代码: " + e.getErrCode()); + + // 可能的错误原因 + if ("USER_NOT_AUTHORIZED".equals(e.getErrCode())) { + System.err.println("用户未授权免确认收款,请先引导用户进行授权"); + } + } + } + + /** + * 使用需确认收款授权模式进行转账示例(默认模式) + */ + public void transferWithConfirmAuthModeExample() { + try { + // 构建转账请求,显式设置为需确认收款授权模式 + TransferBillsRequest request = TransferBillsRequest.newBuilder() + .appid("wx1234567890123456") + .outBillNo("CONFIRM_" + System.currentTimeMillis()) // 商户转账单号 + .transferSceneId("1005") // 转账场景ID + .openid("oUpF8uMuAJO_M2pxb1Q9zNjWeS6o") // 收款用户的openid + .transferAmount(150) // 转账金额,单位:分(此处为1.5元) + .transferRemark("需确认收款转账") // 转账备注 + .receiptAuthorizationMode(WxPayConstants.ReceiptAuthorizationMode.CONFIRM_RECEIPT_AUTHORIZATION) + .userRecvPerception("Y") // 用户收款感知 + .build(); + + // 发起转账 + TransferBillsResult result = transferService.transferBills(request); + + System.out.println("=== 需确认授权模式转账成功 ==="); + System.out.println("商户单号: " + result.getOutBillNo()); + System.out.println("微信转账单号: " + result.getTransferBillNo()); + System.out.println("状态: " + result.getState()); + System.out.println("packageInfo: " + result.getPackageInfo()); + System.out.println("说明: 使用需确认授权模式,用户需要手动确认才能收款"); + + } catch (WxPayException e) { + System.err.println("需确认授权转账失败: " + e.getMessage()); + } + } + + /** + * 权限模式对比示例 + * 展示两种权限模式的区别和使用场景 + */ + public void authModeComparisonExample() { + System.out.println("\n=== 收款授权模式对比 ==="); + System.out.println("1. 需确认收款授权模式 (CONFIRM_RECEIPT_AUTHORIZATION):"); + System.out.println(" - 这是默认模式"); + System.out.println(" - 用户收到转账后需要手动点击确认才能到账"); + System.out.println(" - 适用于一般的转账场景"); + System.out.println(" - 转账状态可能包含 WAIT_USER_CONFIRM 等待确认状态"); + + System.out.println("\n2. 免确认收款授权模式 (NO_CONFIRM_RECEIPT_AUTHORIZATION):"); + System.out.println(" - 用户事先授权后,转账直接到账,无需确认"); + System.out.println(" - 提升用户体验,减少操作步骤"); + System.out.println(" - 适用于高频转账场景,如佣金发放等"); + System.out.println(" - 需要用户先进行授权,否则会返回授权错误"); + + System.out.println("\n使用建议:"); + System.out.println("- 高频业务场景推荐使用免确认模式,提升用户体验"); + System.out.println("- 首次使用需引导用户进行授权"); + System.out.println("- 处理授权相关异常,提供友好的错误提示"); + } + /** * 使用配置示例 */ @@ -230,20 +325,29 @@ public static void main(String[] args) { // 创建示例实例 NewTransferApiExample example = new NewTransferApiExample(config); + // 权限模式对比说明 + example.authModeComparisonExample(); + // 运行示例 System.out.println("新版商户转账API使用示例"); System.out.println("==============================="); - // 1. 发起单笔转账 + // 1. 发起转账(使用免确认授权模式) + // example.transferWithNoConfirmAuthModeExample(); + + // 2. 发起转账(使用需确认授权模式) + // example.transferWithConfirmAuthModeExample(); + + // 3. 发起单笔转账(默认模式) example.transferExample(); - // 2. 查询转账结果 + // 4. 查询转账结果 // example.queryByOutBillNoExample(); - // 3. 撤销转账 + // 5. 撤销转账 // example.cancelTransferExample(); - // 4. 批量转账(传统API) + // 6. 批量转账(传统API) // example.batchTransferExample(); } } \ No newline at end of file From f249450dbe7fa079aa6d012ef5da9571be9fb074 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sun, 21 Dec 2025 18:03:36 +0800 Subject: [PATCH 19/46] =?UTF-8?q?:art:=20#3798=20=E3=80=90=E5=BE=AE?= =?UTF-8?q?=E4=BF=A1=E6=94=AF=E4=BB=98=E3=80=91=E5=BE=AE=E4=BF=A1=E6=94=AF?= =?UTF-8?q?=E4=BB=98=E4=B8=8B=E5=8D=95=E6=8E=A5=E5=8F=A3=E7=BB=93=E6=9E=9C?= =?UTF-8?q?JsapiResult=E7=B1=BB=E4=B8=AD=E6=B7=BB=E5=8A=A0prepayId?= =?UTF-8?q?=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../result/WxPayUnifiedOrderV3Result.java | 138 +++++++-- .../result/WxPayUnifiedOrderV3ResultTest.java | 267 ++++++++++++++++++ 2 files changed, 389 insertions(+), 16 deletions(-) create mode 100644 weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayUnifiedOrderV3ResultTest.java diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayUnifiedOrderV3Result.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayUnifiedOrderV3Result.java index 309fb8e752..5b60f3b520 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayUnifiedOrderV3Result.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayUnifiedOrderV3Result.java @@ -58,7 +58,7 @@ public class WxPayUnifiedOrderV3Result implements Serializable { /** *
    * 字段名:二维码链接(NATIVE支付 会返回)
-   * 变量名:h5_url
+   * 变量名:code_url
    * 是否必填:是
    * 类型:string[1,512]
    * 描述:
@@ -81,6 +81,19 @@ public static class JsapiResult implements Serializable {
     private String packageValue;
     private String signType;
     private String paySign;
+    /**
+     * 
+     * 字段名:预支付交易会话标识
+     * 变量名:prepay_id
+     * 是否必填:否(用户可选存储)
+     * 类型:string[1,64]
+     * 描述:
+     *  预支付交易会话标识。用于后续接口调用中使用,该值有效期为2小时
+     *  此字段用于支持用户存储prepay_id,以便复用和重新生成支付签名
+     *  示例值:wx201410272009395522657a690389285100
+     * 
+ */ + private String prepayId; private String getSignStr() { return String.format("%s\n%s\n%s\n%s\n", appId, timeStamp, nonceStr, packageValue); @@ -106,30 +119,123 @@ private String getSignStr() { } public T getPayInfo(TradeTypeEnum tradeType, String appId, String mchId, PrivateKey privateKey) { - String timestamp = String.valueOf(System.currentTimeMillis() / 1000); - String nonceStr = SignUtils.genRandomStr(); switch (tradeType) { case JSAPI: - JsapiResult jsapiResult = new JsapiResult(); - jsapiResult.setAppId(appId).setTimeStamp(timestamp) - .setPackageValue("prepay_id=" + this.prepayId).setNonceStr(nonceStr) - //签名类型,默认为RSA,仅支持RSA。 - .setSignType("RSA").setPaySign(SignUtils.sign(jsapiResult.getSignStr(), privateKey)); - return (T) jsapiResult; + return (T) buildJsapiResult(this.prepayId, appId, privateKey); case H5: return (T) this.h5Url; case APP: - AppResult appResult = new AppResult(); - appResult.setAppid(appId).setPrepayId(this.prepayId).setPartnerId(mchId) - .setNoncestr(nonceStr).setTimestamp(timestamp) - //暂填写固定值Sign=WXPay - .setPackageValue("Sign=WXPay") - .setSign(SignUtils.sign(appResult.getSignStr(), privateKey)); - return (T) appResult; + return (T) buildAppResult(this.prepayId, appId, mchId, privateKey); case NATIVE: return (T) this.codeUrl; default: throw new WxRuntimeException("不支持的支付类型"); } } + + /** + *
+   * 根据已有的prepay_id生成JSAPI支付所需的参数对象(解耦版本)
+   * 应用场景:
+   * 1. 用户已经通过createPartnerOrderV3或unifiedPartnerOrderV3获取了prepay_id
+   * 2. 用户希望存储prepay_id用于后续复用
+   * 3. 支付失败后,使用存储的prepay_id重新生成支付签名信息
+   * 
+   * 使用示例:
+   * // 步骤1:创建订单并获取prepay_id
+   * WxPayUnifiedOrderV3Result result = wxPayService.unifiedPartnerOrderV3(TradeTypeEnum.JSAPI, request);
+   * String prepayId = result.getPrepayId();
+   * // 存储prepayId到数据库...
+   * 
+   * // 步骤2:需要支付时,使用存储的prepay_id生成支付信息
+   * WxPayUnifiedOrderV3Result.JsapiResult payInfo = WxPayUnifiedOrderV3Result.getJsapiPayInfo(
+   *   prepayId, appId, wxPayService.getConfig().getPrivateKey()
+   * );
+   * 
+ * + * @param prepayId 预支付交易会话标识 + * @param appId 应用ID + * @param privateKey 商户私钥,用于签名 + * @return JSAPI支付所需的参数对象 + */ + public static JsapiResult getJsapiPayInfo(String prepayId, String appId, PrivateKey privateKey) { + if (prepayId == null || appId == null || privateKey == null) { + throw new IllegalArgumentException("prepayId, appId 和 privateKey 不能为空"); + } + return buildJsapiResult(prepayId, appId, privateKey); + } + + /** + *
+   * 根据已有的prepay_id生成APP支付所需的参数对象(解耦版本)
+   * 应用场景:
+   * 1. 用户已经通过createPartnerOrderV3或unifiedPartnerOrderV3获取了prepay_id
+   * 2. 用户希望存储prepay_id用于后续复用
+   * 3. 支付失败后,使用存储的prepay_id重新生成支付签名信息
+   * 
+   * 使用示例:
+   * // 步骤1:创建订单并获取prepay_id
+   * WxPayUnifiedOrderV3Result result = wxPayService.unifiedPartnerOrderV3(TradeTypeEnum.APP, request);
+   * String prepayId = result.getPrepayId();
+   * // 存储prepayId到数据库...
+   * 
+   * // 步骤2:需要支付时,使用存储的prepay_id生成支付信息
+   * WxPayUnifiedOrderV3Result.AppResult payInfo = WxPayUnifiedOrderV3Result.getAppPayInfo(
+   *   prepayId, appId, mchId, wxPayService.getConfig().getPrivateKey()
+   * );
+   * 
+ * + * @param prepayId 预支付交易会话标识 + * @param appId 应用ID + * @param mchId 商户号 + * @param privateKey 商户私钥,用于签名 + * @return APP支付所需的参数对象 + */ + public static AppResult getAppPayInfo(String prepayId, String appId, String mchId, PrivateKey privateKey) { + if (prepayId == null || appId == null || mchId == null || privateKey == null) { + throw new IllegalArgumentException("prepayId, appId, mchId 和 privateKey 不能为空"); + } + return buildAppResult(prepayId, appId, mchId, privateKey); + } + + /** + * 构建JSAPI支付结果对象 + * + * @param prepayId 预支付交易会话标识 + * @param appId 应用ID + * @param privateKey 商户私钥,用于签名 + * @return JSAPI支付所需的参数对象 + */ + private static JsapiResult buildJsapiResult(String prepayId, String appId, PrivateKey privateKey) { + String timestamp = String.valueOf(System.currentTimeMillis() / 1000); + String nonceStr = SignUtils.genRandomStr(); + JsapiResult jsapiResult = new JsapiResult(); + jsapiResult.setAppId(appId).setTimeStamp(timestamp) + .setPackageValue("prepay_id=" + prepayId).setNonceStr(nonceStr) + .setPrepayId(prepayId) + //签名类型,默认为RSA,仅支持RSA。 + .setSignType("RSA").setPaySign(SignUtils.sign(jsapiResult.getSignStr(), privateKey)); + return jsapiResult; + } + + /** + * 构建APP支付结果对象 + * + * @param prepayId 预支付交易会话标识 + * @param appId 应用ID + * @param mchId 商户号 + * @param privateKey 商户私钥,用于签名 + * @return APP支付所需的参数对象 + */ + private static AppResult buildAppResult(String prepayId, String appId, String mchId, PrivateKey privateKey) { + String timestamp = String.valueOf(System.currentTimeMillis() / 1000); + String nonceStr = SignUtils.genRandomStr(); + AppResult appResult = new AppResult(); + appResult.setAppid(appId).setPrepayId(prepayId).setPartnerId(mchId) + .setNoncestr(nonceStr).setTimestamp(timestamp) + //暂填写固定值Sign=WXPay + .setPackageValue("Sign=WXPay") + .setSign(SignUtils.sign(appResult.getSignStr(), privateKey)); + return appResult; + } } diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayUnifiedOrderV3ResultTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayUnifiedOrderV3ResultTest.java new file mode 100644 index 0000000000..2e824b0e00 --- /dev/null +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayUnifiedOrderV3ResultTest.java @@ -0,0 +1,267 @@ +package com.github.binarywang.wxpay.bean.result; + +import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum; +import com.github.binarywang.wxpay.v3.util.SignUtils; +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; + +/** + *
+ * WxPayUnifiedOrderV3Result 测试类
+ * 主要测试prepayId字段和静态工厂方法的解耦功能
+ * 
+ * + * @author copilot + */ +public class WxPayUnifiedOrderV3ResultTest { + + /** + * 生成测试用的RSA密钥对 + */ + private KeyPair generateKeyPair() throws Exception { + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + keyPairGenerator.initialize(2048); + return keyPairGenerator.generateKeyPair(); + } + + /** + * 测试JsapiResult中的prepayId字段是否正确设置 + */ + @Test + public void testJsapiResultWithPrepayId() throws Exception { + // 准备测试数据 + String testPrepayId = "wx201410272009395522657a690389285100"; + String testAppId = "wx8888888888888888"; + KeyPair keyPair = generateKeyPair(); + PrivateKey privateKey = keyPair.getPrivate(); + + // 创建WxPayUnifiedOrderV3Result对象 + WxPayUnifiedOrderV3Result result = new WxPayUnifiedOrderV3Result(); + result.setPrepayId(testPrepayId); + + // 调用getPayInfo生成JsapiResult + WxPayUnifiedOrderV3Result.JsapiResult jsapiResult = + result.getPayInfo(TradeTypeEnum.JSAPI, testAppId, null, privateKey); + + // 验证prepayId字段是否正确设置 + Assert.assertNotNull(jsapiResult.getPrepayId(), "prepayId不应为null"); + Assert.assertEquals(jsapiResult.getPrepayId(), testPrepayId, "prepayId应该与设置的值相同"); + + // 验证其他字段 + Assert.assertEquals(jsapiResult.getAppId(), testAppId); + Assert.assertNotNull(jsapiResult.getTimeStamp()); + Assert.assertNotNull(jsapiResult.getNonceStr()); + Assert.assertEquals(jsapiResult.getPackageValue(), "prepay_id=" + testPrepayId); + Assert.assertEquals(jsapiResult.getSignType(), "RSA"); + Assert.assertNotNull(jsapiResult.getPaySign()); + } + + /** + * 测试使用静态工厂方法生成JsapiResult(解耦场景) + */ + @Test + public void testGetJsapiPayInfoStaticMethod() throws Exception { + // 准备测试数据 + String testPrepayId = "wx201410272009395522657a690389285100"; + String testAppId = "wx8888888888888888"; + KeyPair keyPair = generateKeyPair(); + PrivateKey privateKey = keyPair.getPrivate(); + + // 使用静态工厂方法生成JsapiResult + WxPayUnifiedOrderV3Result.JsapiResult jsapiResult = + WxPayUnifiedOrderV3Result.getJsapiPayInfo(testPrepayId, testAppId, privateKey); + + // 验证prepayId字段 + Assert.assertNotNull(jsapiResult.getPrepayId(), "prepayId不应为null"); + Assert.assertEquals(jsapiResult.getPrepayId(), testPrepayId, "prepayId应该与输入的值相同"); + + // 验证其他字段 + Assert.assertEquals(jsapiResult.getAppId(), testAppId); + Assert.assertNotNull(jsapiResult.getTimeStamp()); + Assert.assertNotNull(jsapiResult.getNonceStr()); + Assert.assertEquals(jsapiResult.getPackageValue(), "prepay_id=" + testPrepayId); + Assert.assertEquals(jsapiResult.getSignType(), "RSA"); + Assert.assertNotNull(jsapiResult.getPaySign()); + } + + /** + * 测试使用静态工厂方法生成AppResult(解耦场景) + */ + @Test + public void testGetAppPayInfoStaticMethod() throws Exception { + // 准备测试数据 + String testPrepayId = "wx201410272009395522657a690389285100"; + String testAppId = "wx8888888888888888"; + String testMchId = "1900000109"; + KeyPair keyPair = generateKeyPair(); + PrivateKey privateKey = keyPair.getPrivate(); + + // 使用静态工厂方法生成AppResult + WxPayUnifiedOrderV3Result.AppResult appResult = + WxPayUnifiedOrderV3Result.getAppPayInfo(testPrepayId, testAppId, testMchId, privateKey); + + // 验证prepayId字段 + Assert.assertNotNull(appResult.getPrepayId(), "prepayId不应为null"); + Assert.assertEquals(appResult.getPrepayId(), testPrepayId, "prepayId应该与输入的值相同"); + + // 验证其他字段 + Assert.assertEquals(appResult.getAppid(), testAppId); + Assert.assertEquals(appResult.getPartnerId(), testMchId); + Assert.assertNotNull(appResult.getTimestamp()); + Assert.assertNotNull(appResult.getNoncestr()); + Assert.assertEquals(appResult.getPackageValue(), "Sign=WXPay"); + Assert.assertNotNull(appResult.getSign()); + } + + /** + * 测试解耦场景:先获取prepayId,后续再生成支付信息 + */ + @Test + public void testDecoupledScenario() throws Exception { + // 模拟场景:先创建订单获取prepayId + String testPrepayId = "wx201410272009395522657a690389285100"; + String testAppId = "wx8888888888888888"; + KeyPair keyPair = generateKeyPair(); + PrivateKey privateKey = keyPair.getPrivate(); + + // 步骤1:模拟从创建订单接口获取prepayId + WxPayUnifiedOrderV3Result orderResult = new WxPayUnifiedOrderV3Result(); + orderResult.setPrepayId(testPrepayId); + + // 获取prepayId用于存储 + String storedPrepayId = orderResult.getPrepayId(); + Assert.assertEquals(storedPrepayId, testPrepayId); + + // 步骤2:后续支付失败时,使用存储的prepayId重新生成支付信息 + WxPayUnifiedOrderV3Result.JsapiResult newPayInfo = + WxPayUnifiedOrderV3Result.getJsapiPayInfo(storedPrepayId, testAppId, privateKey); + + // 验证重新生成的支付信息 + Assert.assertEquals(newPayInfo.getPrepayId(), storedPrepayId); + Assert.assertEquals(newPayInfo.getPackageValue(), "prepay_id=" + storedPrepayId); + Assert.assertNotNull(newPayInfo.getPaySign()); + } + + /** + * 测试多次生成支付信息,签名应该不同(因为timestamp和nonceStr每次都不同) + */ + @Test + public void testMultipleGenerationsHaveDifferentSignatures() throws Exception { + String testPrepayId = "wx201410272009395522657a690389285100"; + String testAppId = "wx8888888888888888"; + KeyPair keyPair = generateKeyPair(); + PrivateKey privateKey = keyPair.getPrivate(); + + // 生成第一次支付信息 + WxPayUnifiedOrderV3Result.JsapiResult result1 = + WxPayUnifiedOrderV3Result.getJsapiPayInfo(testPrepayId, testAppId, privateKey); + + // 等待一秒确保timestamp不同 + Thread.sleep(1000); + + // 生成第二次支付信息 + WxPayUnifiedOrderV3Result.JsapiResult result2 = + WxPayUnifiedOrderV3Result.getJsapiPayInfo(testPrepayId, testAppId, privateKey); + + // prepayId应该相同 + Assert.assertEquals(result1.getPrepayId(), result2.getPrepayId()); + + // 但是timestamp、nonceStr和签名应该不同 + Assert.assertNotEquals(result1.getTimeStamp(), result2.getTimeStamp(), "timestamp应该不同"); + Assert.assertNotEquals(result1.getNonceStr(), result2.getNonceStr(), "nonceStr应该不同"); + Assert.assertNotEquals(result1.getPaySign(), result2.getPaySign(), "签名应该不同"); + } + + /** + * 测试AppResult中的prepayId字段 + */ + @Test + public void testAppResultWithPrepayId() throws Exception { + String testPrepayId = "wx201410272009395522657a690389285100"; + String testAppId = "wx8888888888888888"; + String testMchId = "1900000109"; + KeyPair keyPair = generateKeyPair(); + PrivateKey privateKey = keyPair.getPrivate(); + + WxPayUnifiedOrderV3Result result = new WxPayUnifiedOrderV3Result(); + result.setPrepayId(testPrepayId); + + // 调用getPayInfo生成AppResult + WxPayUnifiedOrderV3Result.AppResult appResult = + result.getPayInfo(TradeTypeEnum.APP, testAppId, testMchId, privateKey); + + // 验证prepayId字段 + Assert.assertNotNull(appResult.getPrepayId(), "prepayId不应为null"); + Assert.assertEquals(appResult.getPrepayId(), testPrepayId, "prepayId应该与设置的值相同"); + } + + /** + * 测试getJsapiPayInfo方法的空值验证 + */ + @Test(expectedExceptions = IllegalArgumentException.class, + expectedExceptionsMessageRegExp = "prepayId, appId 和 privateKey 不能为空") + public void testGetJsapiPayInfoWithNullPrepayId() { + WxPayUnifiedOrderV3Result.getJsapiPayInfo(null, "appId", null); + } + + /** + * 测试getJsapiPayInfo方法的空值验证 - appId为null + */ + @Test(expectedExceptions = IllegalArgumentException.class, + expectedExceptionsMessageRegExp = "prepayId, appId 和 privateKey 不能为空") + public void testGetJsapiPayInfoWithNullAppId() throws Exception { + KeyPair keyPair = generateKeyPair(); + WxPayUnifiedOrderV3Result.getJsapiPayInfo("prepayId", null, keyPair.getPrivate()); + } + + /** + * 测试getJsapiPayInfo方法的空值验证 - privateKey为null + */ + @Test(expectedExceptions = IllegalArgumentException.class, + expectedExceptionsMessageRegExp = "prepayId, appId 和 privateKey 不能为空") + public void testGetJsapiPayInfoWithNullPrivateKey() { + WxPayUnifiedOrderV3Result.getJsapiPayInfo("prepayId", "appId", null); + } + + /** + * 测试getAppPayInfo方法的空值验证 - prepayId为null + */ + @Test(expectedExceptions = IllegalArgumentException.class, + expectedExceptionsMessageRegExp = "prepayId, appId, mchId 和 privateKey 不能为空") + public void testGetAppPayInfoWithNullPrepayId() { + WxPayUnifiedOrderV3Result.getAppPayInfo(null, "appId", "mchId", null); + } + + /** + * 测试getAppPayInfo方法的空值验证 - appId为null + */ + @Test(expectedExceptions = IllegalArgumentException.class, + expectedExceptionsMessageRegExp = "prepayId, appId, mchId 和 privateKey 不能为空") + public void testGetAppPayInfoWithNullAppId() throws Exception { + KeyPair keyPair = generateKeyPair(); + WxPayUnifiedOrderV3Result.getAppPayInfo("prepayId", null, "mchId", keyPair.getPrivate()); + } + + /** + * 测试getAppPayInfo方法的空值验证 - mchId为null + */ + @Test(expectedExceptions = IllegalArgumentException.class, + expectedExceptionsMessageRegExp = "prepayId, appId, mchId 和 privateKey 不能为空") + public void testGetAppPayInfoWithNullMchId() throws Exception { + KeyPair keyPair = generateKeyPair(); + WxPayUnifiedOrderV3Result.getAppPayInfo("prepayId", "appId", null, keyPair.getPrivate()); + } + + /** + * 测试getAppPayInfo方法的空值验证 - privateKey为null + */ + @Test(expectedExceptions = IllegalArgumentException.class, + expectedExceptionsMessageRegExp = "prepayId, appId, mchId 和 privateKey 不能为空") + public void testGetAppPayInfoWithNullPrivateKey() { + WxPayUnifiedOrderV3Result.getAppPayInfo("prepayId", "appId", "mchId", null); + } +} From e3463a0adfe0cb8d4111f751c5ff68b18f68fca8 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sun, 21 Dec 2025 20:27:44 +0800 Subject: [PATCH 20/46] =?UTF-8?q?:art:=20#3686=20=E3=80=90=E5=BE=AE?= =?UTF-8?q?=E4=BF=A1=E6=94=AF=E4=BB=98=E3=80=91=E4=BB=98=E6=AC=BE=E7=A0=81?= =?UTF-8?q?=E6=94=AF=E4=BB=98=E6=8E=A5=E5=8F=A3=E6=B7=BB=E5=8A=A0=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E5=95=86=E6=A8=A1=E5=BC=8F=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bean/request/WxPayCodepayRequest.java | 52 +++++++++++++++++++ .../service/impl/BaseWxPayServiceImpl.java | 41 ++++++++++++--- 2 files changed, 85 insertions(+), 8 deletions(-) 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 index ecfa614a16..632561075a 100644 --- 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 @@ -45,6 +45,58 @@ public class WxPayCodepayRequest implements Serializable { */ @SerializedName(value = "mchid") protected String mchid; + /** + *
+   * 字段名:服务商应用ID
+   * 变量名:sp_appid
+   * 是否必填:否
+   * 类型:string[1,32]
+   * 描述:
+   *  服务商模式下使用,由微信生成的应用ID,全局唯一。
+   *  示例值:wxd678efh567hg6787
+   * 
+ */ + @SerializedName(value = "sp_appid") + protected String spAppid; + /** + *
+   * 字段名:服务商商户号
+   * 变量名:sp_mchid
+   * 是否必填:否
+   * 类型:string[1,32]
+   * 描述:
+   *  服务商模式下使用,服务商商户号,由微信支付生成并下发。
+   *  示例值:1230000109
+   * 
+ */ + @SerializedName(value = "sp_mchid") + protected String spMchid; + /** + *
+   * 字段名:子商户应用ID
+   * 变量名:sub_appid
+   * 是否必填:否
+   * 类型:string[1,32]
+   * 描述:
+   *  服务商模式下使用,由微信生成的应用ID,全局唯一。
+   *  示例值:wxd678efh567hg6787
+   * 
+ */ + @SerializedName(value = "sub_appid") + protected String subAppid; + /** + *
+   * 字段名:子商户商户号
+   * 变量名:sub_mchid
+   * 是否必填:否
+   * 类型:string[1,32]
+   * 描述:
+   *  服务商模式下使用,子商户商户号,由微信支付生成并下发。
+   *  示例值:1230000109
+   * 
+ */ + @SerializedName(value = "sub_mchid") + protected String subMchid; /** *
    * 字段名:商品描述
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 b4c2b919a4..484e191544 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
@@ -1201,15 +1201,40 @@ public WxPayMicropayResult micropay(WxPayMicropayRequest request) throws WxPayEx
 
   @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());
+    // 判断是否为服务商模式:如果设置了sp_appid或sp_mchid或sub_mchid中的任何一个,则认为是服务商模式
+    boolean isPartnerMode = StringUtils.isNotBlank(request.getSpAppid()) 
+        || StringUtils.isNotBlank(request.getSpMchid()) 
+        || StringUtils.isNotBlank(request.getSubMchid());
+
+    if (isPartnerMode) {
+      // 服务商模式
+      if (StringUtils.isBlank(request.getSpAppid())) {
+        request.setSpAppid(this.getConfig().getAppId());
+      }
+      if (StringUtils.isBlank(request.getSpMchid())) {
+        request.setSpMchid(this.getConfig().getMchId());
+      }
+      if (StringUtils.isBlank(request.getSubAppid())) {
+        request.setSubAppid(this.getConfig().getSubAppId());
+      }
+      if (StringUtils.isBlank(request.getSubMchid())) {
+        request.setSubMchid(this.getConfig().getSubMchId());
+      }
+      String url = String.format("%s/v3/pay/partner/transactions/codepay", this.getPayBaseUrl());
+      String body = this.postV3WithWechatpaySerial(url, GSON.toJson(request));
+      return GSON.fromJson(body, WxPayCodepayResult.class);
+    } else {
+      // 直连商户模式
+      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.postV3WithWechatpaySerial(url, GSON.toJson(request));
+      return GSON.fromJson(body, WxPayCodepayResult.class);
     }
-    String url = String.format("%s/v3/pay/transactions/codepay", this.getPayBaseUrl());
-    String body = this.postV3WithWechatpaySerial(url, GSON.toJson(request));
-    return GSON.fromJson(body, WxPayCodepayResult.class);
   }
 
   @Override

From 40428bc7cf2127acbae91bb4d2685d249e6cde83 Mon Sep 17 00:00:00 2001
From: Copilot <198982749+Copilot@users.noreply.github.com>
Date: Sun, 21 Dec 2025 21:54:43 +0800
Subject: [PATCH 21/46] =?UTF-8?q?:memo:=20=E6=B7=BB=E5=8A=A0=E5=BE=AE?=
 =?UTF-8?q?=E4=BF=A1=E5=BC=80=E6=94=BE=E5=B9=B3=E5=8F=B0=E5=B0=8F=E7=A8=8B?=
 =?UTF-8?q?=E5=BA=8F=E5=AE=A1=E6=A0=B8=E9=A2=9D=E5=BA=A6=E7=AE=A1=E7=90=86?=
 =?UTF-8?q?=E6=8C=87=E5=8D=97?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../AUDIT_QUOTA_MANAGEMENT_GUIDE.md           | 321 ++++++++++++++++++
 weixin-java-open/README.md                    |  30 ++
 .../weixin/open/api/WxOpenMaService.java      |  68 +++-
 .../message/WxOpenMaSubmitAuditMessage.java   |  31 ++
 .../bean/result/WxOpenMaQueryQuotaResult.java |  38 ++-
 .../api/impl/WxOpenMaServiceImplTest.java     | 135 ++++++++
 6 files changed, 617 insertions(+), 6 deletions(-)
 create mode 100644 weixin-java-open/AUDIT_QUOTA_MANAGEMENT_GUIDE.md

diff --git a/weixin-java-open/AUDIT_QUOTA_MANAGEMENT_GUIDE.md b/weixin-java-open/AUDIT_QUOTA_MANAGEMENT_GUIDE.md
new file mode 100644
index 0000000000..d84932034a
--- /dev/null
+++ b/weixin-java-open/AUDIT_QUOTA_MANAGEMENT_GUIDE.md
@@ -0,0 +1,321 @@
+# 微信开放平台小程序审核额度管理最佳实践
+
+## 问题背景
+
+在使用微信开放平台第三方服务为多个授权小程序提交审核时,需要注意以下重要限制:
+
+### 审核额度限制
+
+- **默认额度**: 每个第三方平台账号每月默认有 **20 个** 审核额度
+- **消耗规则**: 每次调用 `submitAudit()` 提交一个小程序审核,会消耗 **1 个** 审核额度
+- **重置周期**: 额度每月初自动重置
+- **不可返还**: 审核撤回(undoCodeAudit)不会返还已消耗的额度
+- **增加额度**: 如需更多额度,需要联系微信开放平台客服申请
+
+### 常见问题
+
+**问题**: 开放平台是每个 appId 都要这样提交审核吗?额度不够吧?
+
+**回答**: 是的,每个授权的小程序(appId)都需要单独调用 `submitAudit()` 提交审核,每次提交会消耗 1 个审核额度。默认的 20 个额度对于管理大量小程序的第三方平台来说可能不够用,建议:
+1. 提交审核前先查询剩余额度
+2. 合理规划审核计划,避免重复提交审核
+3. 联系微信开放平台申请增加额度
+
+## API 使用说明
+
+### 1. 查询审核额度
+
+```java
+// 查询当前审核额度
+WxOpenMaQueryQuotaResult quota = wxOpenMaService.queryQuota();
+
+System.out.println("当月剩余提交审核次数: " + quota.getRest());         // 剩余额度
+System.out.println("当月提交审核额度上限: " + quota.getLimit());        // 总额度
+System.out.println("剩余加急次数: " + quota.getSpeedupRest());      // 剩余加急次数
+System.out.println("加急额度上限: " + quota.getSpeedupLimit());     // 加急额度上限
+```
+
+**返回字段说明**:
+- `rest`: 当月剩余提交审核次数
+- `limit`: 当月提交审核额度上限(默认 20)
+- `speedupRest`: 剩余加急次数
+- `speedupLimit`: 加急额度上限
+
+### 2. 提交审核
+
+```java
+// 构建审核项
+WxMaCodeSubmitAuditItem item = new WxMaCodeSubmitAuditItem();
+item.setAddress("index");              // 页面路径
+item.setTag("工具");                   // 标签
+item.setFirstClass("工具");            // 一级类目
+item.setSecondClass("效率");           // 二级类目
+item.setTitle("首页");                 // 页面标题
+
+// 构建提交审核消息
+WxOpenMaSubmitAuditMessage message = new WxOpenMaSubmitAuditMessage();
+message.setItemList(Collections.singletonList(item));
+message.setVersionDesc("版本描述");
+
+// 提交审核
+WxOpenMaSubmitAuditResult result = wxOpenMaService.submitAudit(message);
+System.out.println("审核ID: " + result.getAuditId());
+```
+
+## 最佳实践
+
+### 方案一:单个小程序提交前检查额度
+
+这是最基本的做法,适用于偶尔提交审核的场景。
+
+```java
+import me.chanjar.weixin.open.api.WxOpenMaService;
+import me.chanjar.weixin.open.bean.message.WxOpenMaSubmitAuditMessage;
+import me.chanjar.weixin.open.bean.result.WxOpenMaQueryQuotaResult;
+import me.chanjar.weixin.open.bean.result.WxOpenMaSubmitAuditResult;
+import me.chanjar.weixin.common.error.WxErrorException;
+
+public class AuditSubmitter {
+  
+  /**
+   * 提交审核前检查额度
+   */
+  public WxOpenMaSubmitAuditResult submitWithQuotaCheck(
+      WxOpenMaService wxOpenMaService,
+      WxOpenMaSubmitAuditMessage message) throws WxErrorException {
+    
+    // 1. 检查审核额度
+    WxOpenMaQueryQuotaResult quota = wxOpenMaService.queryQuota();
+    System.out.println("当前剩余审核额度: " + quota.getRest());
+    
+    if (quota.getRest() <= 0) {
+      throw new RuntimeException("审核额度不足,无法提交审核。剩余额度: " + quota.getRest());
+    }
+    
+    // 2. 提交审核
+    WxOpenMaSubmitAuditResult result = wxOpenMaService.submitAudit(message);
+    System.out.println("提交审核成功,审核ID: " + result.getAuditId());
+    
+    // 3. 再次查询额度(可选)
+    quota = wxOpenMaService.queryQuota();
+    System.out.println("提交后剩余审核额度: " + quota.getRest());
+    
+    return result;
+  }
+}
+```
+
+### 方案二:批量提交审核的额度管理
+
+适用于需要同时为多个小程序提交审核的场景。
+
+```java
+import me.chanjar.weixin.open.api.WxOpenComponentService;
+import me.chanjar.weixin.open.api.WxOpenMaService;
+import me.chanjar.weixin.open.bean.message.WxOpenMaSubmitAuditMessage;
+import me.chanjar.weixin.open.bean.result.WxOpenMaQueryQuotaResult;
+import me.chanjar.weixin.open.bean.result.WxOpenMaSubmitAuditResult;
+import me.chanjar.weixin.common.error.WxErrorException;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class BatchAuditSubmitter {
+  
+  /**
+   * 批量提交审核结果
+   */
+  public static class BatchSubmitResult {
+    private int successCount;      // 成功数量
+    private int failCount;         // 失败数量
+    private int skipCount;         // 跳过数量(额度不足)
+    private List failedAppIds;  // 失败的 appId
+    
+    // getters and setters...
+  }
+  
+  /**
+   * 批量提交审核,带额度检查
+   */
+  public BatchSubmitResult batchSubmitWithQuotaCheck(
+      WxOpenComponentService wxOpenComponentService,
+      Map appIdToMessageMap) {
+    
+    BatchSubmitResult result = new BatchSubmitResult();
+    result.setFailedAppIds(new ArrayList<>());
+    
+    // 基本参数校验:避免空指针和空集合导致的 NoSuchElementException
+    if (appIdToMessageMap == null || appIdToMessageMap.isEmpty()) {
+      System.err.println("错误:待提交的小程序列表为空,未执行任何审核提交");
+      return result;
+    }
+    
+    try {
+      // 1. 检查总体额度是否充足
+      // 使用任意一个已授权的小程序 appId 获取 WxMaService 来查询审核额度。
+      // 注意:审核额度是以第三方平台维度统计的,因此这里选择任意一个 appId 即可。
+      WxOpenMaQueryQuotaResult quota = wxOpenComponentService
+          .getWxMaServiceByAppid(appIdToMessageMap.keySet().iterator().next())
+          .queryQuota();
+      
+      System.out.println("=== 批量提交审核开始 ===");
+      System.out.println("待提交数量: " + appIdToMessageMap.size());
+      System.out.println("当前剩余审核额度: " + quota.getRest());
+      
+      if (quota.getRest() < appIdToMessageMap.size()) {
+        System.err.println("警告:审核额度不足!");
+        System.err.println("  需要提交: " + appIdToMessageMap.size() + " 个");
+        System.err.println("  剩余额度: " + quota.getRest());
+        System.err.println("  缺少额度: " + (appIdToMessageMap.size() - quota.getRest()));
+        System.err.println("将仅提交前 " + quota.getRest() + " 个小程序");
+      }
+      
+      // 2. 依次提交审核
+      int count = 0;
+      for (Map.Entry entry : appIdToMessageMap.entrySet()) {
+        String appId = entry.getKey();
+        WxOpenMaSubmitAuditMessage message = entry.getValue();
+        
+        // 检查是否还有额度
+        if (count >= quota.getRest()) {
+          System.out.println("AppId: " + appId + " 跳过(额度不足)");
+          result.setSkipCount(result.getSkipCount() + 1);
+          continue;
+        }
+        
+        try {
+          WxOpenMaService maService = wxOpenComponentService.getWxMaServiceByAppid(appId);
+          WxOpenMaSubmitAuditResult submitResult = maService.submitAudit(message);
+          
+          System.out.println("AppId: " + appId + " 提交成功,审核ID: " + submitResult.getAuditId());
+          result.setSuccessCount(result.getSuccessCount() + 1);
+          count++;
+          
+        } catch (WxErrorException e) {
+          System.err.println("AppId: " + appId + " 提交失败: " + e.getMessage());
+          result.setFailCount(result.getFailCount() + 1);
+          result.getFailedAppIds().add(appId);
+          count++;
+        }
+      }
+      
+      // 3. 输出统计信息
+      System.out.println("=== 批量提交审核完成 ===");
+      System.out.println("成功: " + result.getSuccessCount());
+      System.out.println("失败: " + result.getFailCount());
+      System.out.println("跳过: " + result.getSkipCount());
+      
+      // 4. 查询剩余额度
+      quota = wxOpenComponentService
+          .getWxMaServiceByAppid(appIdToMessageMap.keySet().iterator().next())
+          .queryQuota();
+      System.out.println("剩余额度: " + quota.getRest());
+      
+    } catch (WxErrorException e) {
+      System.err.println("批量提交审核失败: " + e.getMessage());
+      e.printStackTrace();
+    }
+    
+    return result;
+  }
+}
+```
+
+### 方案三:审核额度监控和告警
+
+建议实现一个审核额度监控机制,及时发现额度不足的情况。
+
+```java
+import me.chanjar.weixin.open.api.WxOpenMaService;
+import me.chanjar.weixin.open.bean.result.WxOpenMaQueryQuotaResult;
+import me.chanjar.weixin.common.error.WxErrorException;
+
+public class QuotaMonitor {
+  
+  /**
+   * 检查审核额度并发出告警
+   */
+  public void checkAndAlert(WxOpenMaService wxOpenMaService) {
+    try {
+      WxOpenMaQueryQuotaResult quota = wxOpenMaService.queryQuota();
+      
+      int rest = quota.getRest();
+      int limit = quota.getLimit();
+      double percentage = (double) rest / limit * 100;
+      
+      System.out.println("审核额度状态:");
+      System.out.println("  剩余: " + rest + " / " + limit);
+      System.out.println("  使用率: " + String.format("%.1f", 100 - percentage) + "%");
+      
+      // 根据剩余额度发出不同级别的告警
+      if (rest <= 0) {
+        sendCriticalAlert("审核额度已用尽!无法提交新的审核。");
+      } else if (rest <= 3) {
+        sendWarningAlert("审核额度严重不足!剩余额度: " + rest);
+      } else if (percentage < 30) {
+        sendInfoAlert("审核额度偏低,剩余: " + rest + " (" + String.format("%.1f", percentage) + "%)");
+      }
+      
+    } catch (WxErrorException e) {
+      System.err.println("查询审核额度失败: " + e.getMessage());
+    }
+  }
+  
+  private void sendCriticalAlert(String message) {
+    // 发送紧急告警(如:发送邮件、短信、钉钉消息等)
+    System.err.println("[严重] " + message);
+  }
+  
+  private void sendWarningAlert(String message) {
+    // 发送警告(如:发送邮件、企业微信消息等)
+    System.out.println("[警告] " + message);
+  }
+  
+  private void sendInfoAlert(String message) {
+    // 发送普通提示
+    System.out.println("[提示] " + message);
+  }
+}
+```
+
+## 常见问题 FAQ
+
+### Q1: 审核额度什么时候重置?
+A: 审核额度在每月初自动重置为默认值(通常是 20 个)。
+
+### Q2: 审核撤回会返还额度吗?
+A: 不会。调用 `undoCodeAudit()` 撤回审核不会返还已消耗的额度。
+
+### Q3: 如何增加审核额度?
+A: 需要联系微信开放平台客服申请增加额度。具体联系方式请参考微信开放平台官方文档。
+
+### Q4: 审核失败会消耗额度吗?
+A: 会。只要调用了 `submitAudit()` 接口提交审核,无论审核是否通过,都会消耗 1 个额度。
+
+### Q5: 加急审核会额外消耗额度吗?
+A: 加急审核(`speedAudit()`)使用的是单独的加急额度(`speedupRest`),不会消耗普通审核额度。但加急审核的前提是已经提交了审核,所以提交审核时仍会消耗 1 个普通审核额度。
+
+### Q6: 多个小程序共享审核额度吗?
+A: 是的。同一个第三方平台账号下,所有授权的小程序共享审核额度。每提交一个小程序审核,都会消耗该第三方平台的 1 个审核额度。
+
+### Q7: 如何避免审核额度不足?
+A: 建议采取以下措施:
+- 在批量提交审核前,先调用 `queryQuota()` 检查剩余额度
+- 实现审核额度监控和告警机制
+- 合理规划审核计划,避免不必要的重复提交审核
+- 提高代码质量,减少审核不通过的情况
+- 联系微信开放平台申请增加额度
+
+### Q8: 能否查询历史审核额度使用情况?
+A: 微信开放平台 API 目前只提供当前剩余额度查询,不提供历史使用记录。如需统计历史使用情况,需要自行记录每次调用 `submitAudit()` 的时间和次数。
+
+## 相关文档
+
+- [微信开放平台官方文档 - 查询额度](https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/Mini_Programs/code/query_quota.html)
+- [微信开放平台官方文档 - 提交审核](https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/Mini_Programs/code/submit_audit.html)
+- [微信开放平台官方文档 - 加急审核](https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/Mini_Programs/code/speedup_audit.html)
+
+## 技术支持
+
+如有问题,请提交 Issue 到 [WxJava GitHub 仓库](https://github.com/binarywang/WxJava/issues)。
diff --git a/weixin-java-open/README.md b/weixin-java-open/README.md
index 6ca65dfef3..ad7f3d0109 100644
--- a/weixin-java-open/README.md
+++ b/weixin-java-open/README.md
@@ -31,6 +31,36 @@
 
 ---
 
+## 重要提示:小程序审核额度限制
+
+**在使用第三方平台代小程序提交审核时,请注意以下限制:**
+
+### 审核额度说明
+
+- **默认额度**: 每个第三方平台账号每月默认有 **20 个** 审核额度
+- **消耗规则**: 每次调用 `submitAudit()` 提交一个小程序审核,会消耗 **1 个** 审核额度
+- **重置周期**: 额度每月初自动重置
+- **额度查询**: 使用 `queryQuota()` 方法查询剩余额度
+
+### 最佳实践
+
+```java
+// 1. 先查询剩余额度
+WxOpenMaQueryQuotaResult quota = wxOpenMaService.queryQuota();
+if (quota.getRest() <= 0) {
+  throw new RuntimeException("审核额度不足,剩余:" + quota.getRest());
+}
+
+// 2. 提交审核
+WxOpenMaSubmitAuditMessage message = new WxOpenMaSubmitAuditMessage();
+message.setItemList(itemList);
+WxOpenMaSubmitAuditResult result = wxOpenMaService.submitAudit(message);
+```
+
+**详细说明**: 请参考 [AUDIT_QUOTA_MANAGEMENT_GUIDE.md](AUDIT_QUOTA_MANAGEMENT_GUIDE.md)
+
+---
+
 ## 代码示例
 
 消息机制未实现,下面为通知回调中设置的代码部分
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 ab229ba537..1033b57a16 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
@@ -539,10 +539,36 @@ WxOpenMaDomainResult modifyDomainDirectly(String action, List requestDom
 
   /**
    * 将第三方提交的代码包提交审核(仅供第三方开发者代小程序调用)
+   * 

+ * 重要提示:审核额度限制 + *

+ *
    + *
  • 每个第三方平台账号每月有审核额度限制(默认20次,可通过 {@link #queryQuota()} 查询)
  • + *
  • 每次调用 submitAudit 提交一个小程序审核时,会消耗1个审核额度
  • + *
  • 建议在提交审核前,先调用 {@link #queryQuota()} 检查剩余额度
  • + *
  • 如需增加额度,请联系微信开放平台客服
  • + *
+ *

+ * 最佳实践: + *

+ *
{@code
+   * // 1. 先查询剩余额度
+   * WxOpenMaQueryQuotaResult quota = wxOpenMaService.queryQuota();
+   * if (quota.getRest() <= 0) {
+   *   throw new RuntimeException("审核额度不足,剩余:" + quota.getRest());
+   * }
+   *
+   * // 2. 提交审核
+   * WxOpenMaSubmitAuditMessage message = new WxOpenMaSubmitAuditMessage();
+   * message.setItemList(itemList);
+   * WxOpenMaSubmitAuditResult result = wxOpenMaService.submitAudit(message);
+   * }
* * @param submitAuditMessage the submit audit message * @return the wx open ma submit audit result * @throws WxErrorException the wx error exception + * @see #queryQuota() 查询审核额度 + * @see #speedAudit(Long) 加急审核 */ WxOpenMaSubmitAuditResult submitAudit(WxOpenMaSubmitAuditMessage submitAuditMessage) throws WxErrorException; @@ -690,11 +716,43 @@ WxOpenMaDomainResult modifyDomainDirectly(String action, List requestDom WxOpenMaGetCodePrivacyInfoResult getCodePrivacyInfo() throws WxErrorException; /** - * 查询服务商的当月提审限额和加急次数(Quota) - * https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/Mini_Programs/code/query_quota.html - * - * @return the wx open ma query quota result - * @throws WxErrorException the wx error exception + * 查询服务商的当月提交审核限额和加急次数(Quota) + *

+ * 文档地址: + * 查询额度 + *

+ *

+ * 返回字段说明: + *

+ *
    + *
  • rest: 当月剩余提交审核次数
  • + *
  • limit: 当月提交审核额度上限(默认20次)
  • + *
  • speedup_rest: 剩余加急次数
  • + *
  • speedup_limit: 加急额度上限
  • + *
+ *

+ * 重要说明: + *

+ *
    + *
  • 每个第三方平台账号每月初会重置审核额度
  • + *
  • 每次调用 {@link #submitAudit} 提交审核会消耗1个额度
  • + *
  • 审核撤回不会返还额度
  • + *
  • 建议在批量提交审核前,先调用此接口检查额度是否充足
  • + *
+ *

+ * 使用示例: + *

+ *
{@code
+   * WxOpenMaQueryQuotaResult quota = wxOpenMaService.queryQuota();
+   * System.out.println("剩余审核次数:" + quota.getRest());
+   * System.out.println("审核额度上限:" + quota.getLimit());
+   * System.out.println("剩余加急次数:" + quota.getSpeedupRest());
+   * }
+ * + * @return 审核额度信息 + * @throws WxErrorException 调用微信接口失败时抛出 + * @see #submitAudit(WxOpenMaSubmitAuditMessage) 提交审核 + * @see #speedAudit(Long) 加急审核 */ WxOpenMaQueryQuotaResult queryQuota() throws WxErrorException; diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/message/WxOpenMaSubmitAuditMessage.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/message/WxOpenMaSubmitAuditMessage.java index d8c1461d67..79400aa968 100644 --- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/message/WxOpenMaSubmitAuditMessage.java +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/message/WxOpenMaSubmitAuditMessage.java @@ -10,8 +10,39 @@ /** * 微信小程序代码包提交审核(仅供第三方开发者代小程序调用) + *

+ * 重要提示:审核额度限制 + *

+ *
    + *
  • 每个第三方平台账号每月有审核额度限制(默认20次)
  • + *
  • 每次调用 submitAudit 提交审核会消耗1个额度,无论审核是否通过
  • + *
  • 建议在提交前先调用 queryQuota 检查剩余额度
  • + *
+ *

+ * 使用示例: + *

+ *
{@code
+ * // 1. 构建审核项
+ * WxMaCodeSubmitAuditItem item = new WxMaCodeSubmitAuditItem();
+ * item.setAddress("index");
+ * item.setTag("游戏");
+ * item.setFirstClass("游戏");
+ * item.setSecondClass("休闲游戏");
+ * item.setTitle("首页");
+ *
+ * // 2. 构建提交审核消息
+ * WxOpenMaSubmitAuditMessage message = new WxOpenMaSubmitAuditMessage();
+ * message.setItemList(Collections.singletonList(item));
+ * message.setVersionDesc("版本描述");
+ *
+ * // 3. 提交审核
+ * WxOpenMaSubmitAuditResult result = wxOpenMaService.submitAudit(message);
+ * System.out.println("审核ID: " + result.getAuditId());
+ * }
* * @author yqx + * @see me.chanjar.weixin.open.api.WxOpenMaService#submitAudit(WxOpenMaSubmitAuditMessage) + * @see me.chanjar.weixin.open.api.WxOpenMaService#queryQuota() * created on 2018/9/13 */ @Data diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaQueryQuotaResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaQueryQuotaResult.java index 3b02906242..0f964b5f44 100644 --- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaQueryQuotaResult.java +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaQueryQuotaResult.java @@ -6,7 +6,31 @@ import me.chanjar.weixin.open.util.json.WxOpenGsonBuilder; /** - * 微信开放平台小程序当前分阶段发布详情 + * 微信开放平台小程序提交审核额度查询结果 + *

+ * 用于查询第三方平台服务商的当月提交审核限额和加急次数 + *

+ *

+ * 字段说明: + *

+ *
    + *
  • rest: 当月剩余提交审核次数
  • + *
  • limit: 当月提交审核额度上限(默认20次,可联系微信开放平台增加)
  • + *
  • speedupRest: 剩余加急次数
  • + *
  • speedupLimit: 加急额度上限
  • + *
+ *

+ * 重要提示: + *

+ *
    + *
  • 每次调用 submitAudit 提交小程序审核,会消耗1个审核额度
  • + *
  • 额度每月初自动重置
  • + *
  • 审核撤回不会返还已消耗的额度
  • + *
  • 建议在批量提交审核前,先检查剩余额度是否充足
  • + *
+ * + * @see me.chanjar.weixin.open.api.WxOpenMaService#queryQuota() + * @see me.chanjar.weixin.open.api.WxOpenMaService#submitAudit(me.chanjar.weixin.open.bean.message.WxOpenMaSubmitAuditMessage) */ @Data @EqualsAndHashCode(callSuper = true) @@ -14,15 +38,27 @@ public class WxOpenMaQueryQuotaResult extends WxOpenResult { private static final long serialVersionUID = 5915265985261653007L; + /** + * 当月剩余提交审核次数 + */ @SerializedName("rest") private Integer rest; + /** + * 当月提交审核额度上限 + */ @SerializedName("limit") private Integer limit; + /** + * 剩余加急次数 + */ @SerializedName("speedup_rest") private Integer speedupRest; + /** + * 加急额度上限 + */ @SerializedName("speedup_limit") private Integer speedupLimit; diff --git a/weixin-java-open/src/test/java/me/chanjar/weixin/open/api/impl/WxOpenMaServiceImplTest.java b/weixin-java-open/src/test/java/me/chanjar/weixin/open/api/impl/WxOpenMaServiceImplTest.java index 4d8e41b59e..1de6ffe2d6 100644 --- a/weixin-java-open/src/test/java/me/chanjar/weixin/open/api/impl/WxOpenMaServiceImplTest.java +++ b/weixin-java-open/src/test/java/me/chanjar/weixin/open/api/impl/WxOpenMaServiceImplTest.java @@ -306,6 +306,141 @@ public void testGetGrayReleasePlan() { @Test public void testQueryQuota() { + // 此测试方法演示如何使用审核额度查询功能 + // 注意:实际运行需要真实的微信 API 凭据 + /* + try { + // 查询当前审核额度 + WxOpenMaQueryQuotaResult quota = wxOpenMaService.queryQuota(); + + System.out.println("审核额度信息:"); + System.out.println(" 当月剩余提交审核次数: " + quota.getRest()); + System.out.println(" 当月提交审核额度上限: " + quota.getLimit()); + System.out.println(" 剩余加急次数: " + quota.getSpeedupRest()); + System.out.println(" 加急额度上限: " + quota.getSpeedupLimit()); + + // 检查额度是否充足 + if (quota.getRest() <= 0) { + System.err.println("警告:审核额度已用尽!"); + } else if (quota.getRest() <= 5) { + System.out.println("提示:审核额度即将用尽,请注意!"); + } + } catch (WxErrorException e) { + e.printStackTrace(); + } + */ + } + + /** + * 演示提交审核前检查额度的最佳实践 + *

+ * 这是一个完整的示例,展示如何在提交审核前检查额度,避免额度不足导致的失败 + *

+ */ + @Test + public void testSubmitAuditWithQuotaCheck() { + // 此测试方法演示提交审核前的额度检查最佳实践 + // 注意:实际运行需要真实的微信 API 凭据 + /* + try { + // 步骤1:检查审核额度 + WxOpenMaQueryQuotaResult quota = wxOpenMaService.queryQuota(); + System.out.println("当前剩余审核额度: " + quota.getRest()); + + if (quota.getRest() <= 0) { + throw new RuntimeException("审核额度不足,无法提交审核。剩余额度: " + quota.getRest()); + } + + // 步骤2:准备审核数据 + WxMaCodeSubmitAuditItem item = new WxMaCodeSubmitAuditItem(); + item.setAddress("index"); + item.setTag("工具"); + item.setFirstClass("工具"); + item.setSecondClass("效率"); + item.setTitle("首页"); + + WxOpenMaSubmitAuditMessage message = new WxOpenMaSubmitAuditMessage(); + message.setItemList(Collections.singletonList(item)); + message.setVersionDesc("修复若干已知问题,优化用户体验"); + + // 步骤3:提交审核 + WxOpenMaSubmitAuditResult result = wxOpenMaService.submitAudit(message); + System.out.println("提交审核成功,审核ID: " + result.getAuditId()); + + // 步骤4:再次查询额度,确认已消耗 + quota = wxOpenMaService.queryQuota(); + System.out.println("提交后剩余审核额度: " + quota.getRest()); + + } catch (WxErrorException e) { + System.err.println("提交审核失败: " + e.getMessage()); + e.printStackTrace(); + } + */ + } + + /** + * 演示批量提交审核时的额度管理策略 + *

+ * 当需要为多个小程序提交审核时,应该先统一检查额度是否充足 + *

+ */ + @Test + public void testBatchSubmitAuditWithQuotaManagement() { + // 此测试方法演示批量提交审核时的额度管理策略 + // 注意:实际运行需要真实的微信 API 凭据,以及 WxOpenComponentService 实例 + /* + // 假设已经初始化了 wxOpenComponentService + // WxOpenComponentService wxOpenComponentService = ...; + + try { + // 假设需要为多个小程序提交审核 + List appIds = Arrays.asList("appid1", "appid2", "appid3"); + + // 步骤1:通过任意一个小程序服务查询总体额度 + // 注意:审核额度是第三方平台级别的,所有授权小程序共享 + WxOpenMaService firstMaService = wxOpenComponentService.getWxMaServiceByAppid(appIds.get(0)); + WxOpenMaQueryQuotaResult quota = firstMaService.queryQuota(); + System.out.println("当前剩余审核额度: " + quota.getRest()); + + if (quota.getRest() < appIds.size()) { + System.err.println("警告:审核额度不足!"); + System.err.println(" 需要提交: " + appIds.size() + " 个"); + System.err.println(" 剩余额度: " + quota.getRest()); + System.err.println(" 缺少额度: " + (appIds.size() - quota.getRest())); + return; + } + + // 步骤2:依次提交审核 + int successCount = 0; + for (String appId : appIds) { + try { + WxOpenMaService maService = wxOpenComponentService.getWxMaServiceByAppid(appId); + + WxOpenMaSubmitAuditMessage message = new WxOpenMaSubmitAuditMessage(); + // ... 设置审核信息 + + WxOpenMaSubmitAuditResult result = maService.submitAudit(message); + System.out.println("AppId: " + appId + " 提交成功,审核ID: " + result.getAuditId()); + successCount++; + + } catch (WxErrorException e) { + System.err.println("AppId: " + appId + " 提交失败: " + e.getMessage()); + } + } + + // 步骤3:输出统计信息 + System.out.println("批量提交完成:"); + System.out.println(" 成功: " + successCount); + System.out.println(" 失败: " + (appIds.size() - successCount)); + + // 步骤4:查询剩余额度 + quota = firstMaService.queryQuota(); + System.out.println(" 剩余额度: " + quota.getRest()); + + } catch (Exception e) { + e.printStackTrace(); + } + */ } @Test From b601b557f90053979412d152d8e3f836c7913513 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sun, 21 Dec 2025 21:57:27 +0800 Subject: [PATCH 22/46] =?UTF-8?q?:art:=20=E6=94=AF=E4=BB=98=E5=9B=9E?= =?UTF-8?q?=E8=B0=83=E7=BB=93=E6=9E=9C=E8=A7=A3=E6=9E=90=E6=97=B6=E6=8F=90?= =?UTF-8?q?=E4=BE=9B=E5=BF=85=E8=A6=81=E7=9A=84=E6=A3=80=E6=B5=8B=E6=8A=A5?= =?UTF-8?q?=E9=94=99=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/impl/BaseWxPayServiceImpl.java | 7 ++++ .../impl/BaseWxPayServiceImplTest.java | 36 +++++++++++++++++++ 2 files changed, 43 insertions(+) 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 484e191544..2e896cda7e 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 @@ -344,6 +344,13 @@ public WxPayOrderNotifyResult parseOrderNotifyResult(String xmlData) throws WxPa public WxPayOrderNotifyResult parseOrderNotifyResult(String xmlData, String signType) throws WxPayException { try { log.debug("微信支付异步通知请求参数:{}", xmlData); + + // 检测数据格式并给出适当的处理建议 + if (xmlData != null && xmlData.trim().startsWith("{")) { + throw new WxPayException("检测到V3版本的JSON格式通知数据,请使用parseOrderNotifyV3Result方法解析。" + + " V3 API需要传入SignatureHeader参数进行签名验证。"); + } + WxPayOrderNotifyResult result = WxPayOrderNotifyResult.fromXML(xmlData); if (signType == null) { this.switchover(result.getMchId(), result.getAppid()); 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 bd24f188d0..7dff396de5 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 @@ -627,6 +627,42 @@ public void testParseOrderNotifyResult() throws Exception { } + /** + * Test parse order notify result with JSON format should give helpful error. + * 测试当传入V3版本的JSON格式通知数据时,应该抛出清晰的错误提示 + * + * @throws Exception the exception + */ + @Test + public void testParseOrderNotifyResultWithJsonShouldGiveHelpfulError() throws Exception { + String jsonString = "{\n" + + " \"id\": \"EV-2018022511223320873\",\n" + + " \"create_time\": \"2015-05-20T13:29:35+08:00\",\n" + + " \"resource_type\": \"encrypt-resource\",\n" + + " \"event_type\": \"TRANSACTION.SUCCESS\",\n" + + " \"summary\": \"支付成功\",\n" + + " \"resource\": {\n" + + " \"algorithm\": \"AEAD_AES_256_GCM\",\n" + + " \"ciphertext\": \"test\",\n" + + " \"associated_data\": \"transaction\",\n" + + " \"nonce\": \"test\"\n" + + " }\n" + + "}"; + + try { + this.payService.parseOrderNotifyResult(jsonString); + fail("Expected WxPayException for JSON input"); + } catch (WxPayException e) { + // 验证错误消息包含V3版本和parseOrderNotifyV3Result方法的指导信息 + String message = e.getMessage(); + assertTrue(message.contains("V3版本"), "错误消息应包含'V3版本'"); + assertTrue(message.contains("JSON格式"), "错误消息应包含'JSON格式'"); + assertTrue(message.contains("parseOrderNotifyV3Result"), "错误消息应包含'parseOrderNotifyV3Result'方法名"); + assertTrue(message.contains("SignatureHeader"), "错误消息应包含'SignatureHeader'"); + log.info("JSON格式检测正常,错误提示: {}", message); + } + } + /** * Test get wx api data. * From 8e760a972ddbe9e79ca29d3b3d0499de36a293bf Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Mon, 22 Dec 2025 09:37:25 +0800 Subject: [PATCH 23/46] =?UTF-8?q?:art:=20#3654=20=E3=80=90=E5=BC=80?= =?UTF-8?q?=E6=94=BE=E5=B9=B3=E5=8F=B0=E3=80=91=E6=B6=88=E6=81=AF=E8=B7=AF?= =?UTF-8?q?=E7=94=B1=E5=A4=84=E7=90=86=E7=A8=8B=E5=BA=8F=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E5=AF=B9=E5=B0=8F=E7=A8=8B=E5=BA=8F=E7=9A=84=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../open/api/impl/WxOpenMessageRouter.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMessageRouter.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMessageRouter.java index 7314bfd694..3c2c3058f4 100644 --- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMessageRouter.java +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMessageRouter.java @@ -23,4 +23,29 @@ public WxMpXmlOutMessage route(final WxMpXmlMessage wxMessage, String appId) { public WxMpXmlOutMessage route(final WxMpXmlMessage wxMessage, final Map context, String appId) { return route(wxMessage, context, wxOpenService.getWxOpenComponentService().getWxMpServiceByAppid(appId)); } + + /** + * 路由微信消息到小程序服务 (Route WeChat message to Mini App service) + * + * @param wxMessage the wx message + * @param appId the app id + * @return the wx mp xml out message + */ + public WxMpXmlOutMessage routeForMa(final WxMpXmlMessage wxMessage, String appId) { + return routeForMa(wxMessage, new HashMap<>(), appId); + } + + /** + * 路由微信消息到小程序服务 (Route WeChat message to Mini App service) + * + * @param wxMessage the wx message + * @param context the context + * @param appId the app id + * @return the wx mp xml out message + */ + public WxMpXmlOutMessage routeForMa(final WxMpXmlMessage wxMessage, final Map context, String appId) { + // 将小程序服务放入上下文中,以便处理器可以访问 (Put Mini App service in context so handlers can access it) + context.put("wxOpenMaService", wxOpenService.getWxOpenComponentService().getWxMaServiceByAppid(appId)); + return route(wxMessage, context, wxOpenService.getWxOpenComponentService().getWxMpServiceByAppid(appId)); + } } From c2d0cf8d4e45969c167d8f34db24656409068e38 Mon Sep 17 00:00:00 2001 From: yanglegetuo <57779135+yanglegetuo@users.noreply.github.com> Date: Wed, 24 Dec 2025 10:05:07 +0800 Subject: [PATCH 24/46] =?UTF-8?q?:new:=20#3818=20=E3=80=90=E5=B0=8F?= =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E3=80=91=20=E6=96=B0=E5=A2=9E=E8=AE=BE?= =?UTF-8?q?=E5=A4=87=E7=BB=84=E7=9B=B8=E5=85=B3=E7=9A=84=20API=20=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/WxMaDeviceSubscribeService.java | 55 ++++++++++++++++++- .../impl/WxMaDeviceSubscribeServiceImpl.java | 54 +++++++++++++++++- .../device/WxMaCreateIotGroupIdRequest.java | 38 +++++++++++++ .../device/WxMaGetIotGroupInfoRequest.java | 33 +++++++++++ .../WxMaIotGroupDeviceInfoResponse.java | 51 +++++++++++++++++ .../device/WxMaIotGroupDeviceRequest.java | 44 +++++++++++++++ .../miniapp/constant/WxMaApiUrlConstants.java | 8 +++ .../WxMaDeviceSubscribeServiceImplTest.java | 55 +++++++++++++++++-- 8 files changed, 329 insertions(+), 9 deletions(-) create mode 100644 weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/device/WxMaCreateIotGroupIdRequest.java create mode 100644 weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/device/WxMaGetIotGroupInfoRequest.java create mode 100644 weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/device/WxMaIotGroupDeviceInfoResponse.java create mode 100644 weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/device/WxMaIotGroupDeviceRequest.java diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaDeviceSubscribeService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaDeviceSubscribeService.java index f44f64e48d..b45bc058a6 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaDeviceSubscribeService.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaDeviceSubscribeService.java @@ -1,9 +1,10 @@ package cn.binarywang.wx.miniapp.api; -import cn.binarywang.wx.miniapp.bean.device.WxMaDeviceSubscribeMessageRequest; -import cn.binarywang.wx.miniapp.bean.device.WxMaDeviceTicketRequest; +import cn.binarywang.wx.miniapp.bean.device.*; import me.chanjar.weixin.common.error.WxErrorException; +import java.util.List; + /** * 小程序设备订阅消息相关 API * 文档: @@ -21,6 +22,7 @@ public interface WxMaDeviceSubscribeService { * 注意: * 设备ticket有效时间为5分钟 *
+ * * @param deviceTicketRequest * @return * @throws WxErrorException @@ -37,4 +39,53 @@ public interface WxMaDeviceSubscribeService { */ void sendDeviceSubscribeMsg(WxMaDeviceSubscribeMessageRequest deviceSubscribeMessageRequest) throws WxErrorException; + /** + *
+   * 创建设备组
+   * 详情请见:https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/hardware-device/createIotGroupId.html
+   * 
+ * + * @param createIotGroupIdRequest 请求参数 + * @return 设备组的唯一标识 + * @throws WxErrorException + */ + String createIotGroupId(WxMaCreateIotGroupIdRequest createIotGroupIdRequest) throws WxErrorException; + + /** + *
+   * 查询设备组信息
+   * 详情请见:https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/hardware-device/getIotGroupInfo.html
+   * 
+ * + * @param getIotGroupInfoRequest 请求参数 + * @return 设备组信息 + * @throws WxErrorException + */ + WxMaIotGroupDeviceInfoResponse getIotGroupInfo(WxMaGetIotGroupInfoRequest getIotGroupInfoRequest) throws WxErrorException; + + /** + *
+   * 设备组添加设备
+   * 一个设备组最多添加 50 个设备。 一个设备同一时间只能被添加到一个设备组中。
+   * 详情请见:https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/hardware-device/addIotGroupDevice.html
+   * 
+ * + * @param request 请求参数 + * @return 成功添加的设备信息 + * @throws WxErrorException + */ + List addIotGroupDevice(WxMaIotGroupDeviceRequest request) throws WxErrorException; + + /** + *
+   * 设备组删除设备
+   * 详情请见:https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/hardware-device/removeIotGroupDevice.html
+   * 
+ * + * @param request 请求参数 + * @return 成功删除的设备信息 + * @throws WxErrorException + */ + List removeIotGroupDevice(WxMaIotGroupDeviceRequest request) throws WxErrorException; + } diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaDeviceSubscribeServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaDeviceSubscribeServiceImpl.java index 7f8dce1df8..632fe7bd94 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaDeviceSubscribeServiceImpl.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaDeviceSubscribeServiceImpl.java @@ -2,18 +2,26 @@ import cn.binarywang.wx.miniapp.api.WxMaDeviceSubscribeService; import cn.binarywang.wx.miniapp.api.WxMaService; -import cn.binarywang.wx.miniapp.bean.device.WxMaDeviceSubscribeMessageRequest; -import cn.binarywang.wx.miniapp.bean.device.WxMaDeviceTicketRequest; +import cn.binarywang.wx.miniapp.bean.device.*; +import cn.binarywang.wx.miniapp.json.WxMaGsonBuilder; import com.google.gson.JsonObject; +import com.google.gson.reflect.TypeToken; import lombok.RequiredArgsConstructor; import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.common.enums.WxType; import me.chanjar.weixin.common.error.WxError; import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.util.json.GsonParser; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +import java.util.List; import static cn.binarywang.wx.miniapp.constant.WxMaApiUrlConstants.DeviceSubscribe.GET_SN_TICKET_URL; import static cn.binarywang.wx.miniapp.constant.WxMaApiUrlConstants.DeviceSubscribe.SEND_DEVICE_SUBSCRIBE_MSG_URL; +import static cn.binarywang.wx.miniapp.constant.WxMaApiUrlConstants.DeviceSubscribe.CREATE_IOT_GROUP_ID_URL; +import static cn.binarywang.wx.miniapp.constant.WxMaApiUrlConstants.DeviceSubscribe.GET_IOT_GROUP_INFO_URL; +import static cn.binarywang.wx.miniapp.constant.WxMaApiUrlConstants.DeviceSubscribe.ADD_IOT_GROUP_DEVICE_URL; +import static cn.binarywang.wx.miniapp.constant.WxMaApiUrlConstants.DeviceSubscribe.REMOVE_IOT_GROUP_DEVICE_URL; /** * 小程序设备订阅消息相关 API @@ -47,4 +55,46 @@ public void sendDeviceSubscribeMsg(WxMaDeviceSubscribeMessageRequest deviceSubsc throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp)); } } + + @Override + public String createIotGroupId(WxMaCreateIotGroupIdRequest createIotGroupIdRequest) throws WxErrorException { + String responseContent = this.service.post(CREATE_IOT_GROUP_ID_URL, createIotGroupIdRequest.toJson()); + JsonObject jsonObject = GsonParser.parse(responseContent); + if (jsonObject.get(WxConsts.ERR_CODE).getAsInt() != 0) { + throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp)); + } + return jsonObject.get("group_id").getAsString(); + } + + @Override + public WxMaIotGroupDeviceInfoResponse getIotGroupInfo(WxMaGetIotGroupInfoRequest getIotGroupInfoRequest) throws WxErrorException { + String responseContent = this.service.post(GET_IOT_GROUP_INFO_URL, getIotGroupInfoRequest.toJson()); + JsonObject jsonObject = GsonParser.parse(responseContent); + if (jsonObject.get(WxConsts.ERR_CODE).getAsInt() != 0) { + throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp)); + } + return WxGsonBuilder.create().fromJson(responseContent, WxMaIotGroupDeviceInfoResponse.class); + } + + @Override + public List addIotGroupDevice(WxMaIotGroupDeviceRequest request) throws WxErrorException { + String responseContent = this.service.post(ADD_IOT_GROUP_DEVICE_URL, request.toJson()); + JsonObject jsonObject = GsonParser.parse(responseContent); + if (jsonObject.get(WxConsts.ERR_CODE).getAsInt() != 0) { + throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp)); + } + return WxMaGsonBuilder.create().fromJson(jsonObject.getAsJsonArray("device_list"), new TypeToken>() { + }.getType()); + } + + @Override + public List removeIotGroupDevice(WxMaIotGroupDeviceRequest request) throws WxErrorException { + String responseContent = this.service.post(REMOVE_IOT_GROUP_DEVICE_URL, request.toJson()); + JsonObject jsonObject = GsonParser.parse(responseContent); + if (jsonObject.get(WxConsts.ERR_CODE).getAsInt() != 0) { + throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp)); + } + return WxMaGsonBuilder.create().fromJson(jsonObject.getAsJsonArray("device_list"), new TypeToken>() { + }.getType()); + } } diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/device/WxMaCreateIotGroupIdRequest.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/device/WxMaCreateIotGroupIdRequest.java new file mode 100644 index 0000000000..20a0a146ae --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/device/WxMaCreateIotGroupIdRequest.java @@ -0,0 +1,38 @@ +package cn.binarywang.wx.miniapp.bean.device; + +import cn.binarywang.wx.miniapp.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * @Author: yanglegetuo + * @Date: 2025/12/22 8:51 + * @Description: 创建设备组 请求参数 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WxMaCreateIotGroupIdRequest implements Serializable { + private static final long serialVersionUID = 1827809470414413390L; + /** + * 设备型号id。通过注册设备获得(必填) + */ + @SerializedName("model_id") + private String modelId; + /** + * 设备组的名称(创建时决定,无法修改) + */ + @SerializedName("group_name") + private String groupName; + + + public String toJson() { + return WxMaGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/device/WxMaGetIotGroupInfoRequest.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/device/WxMaGetIotGroupInfoRequest.java new file mode 100644 index 0000000000..47ebc2be44 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/device/WxMaGetIotGroupInfoRequest.java @@ -0,0 +1,33 @@ +package cn.binarywang.wx.miniapp.bean.device; + +import cn.binarywang.wx.miniapp.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * @Author: yanglegetuo + * @Date: 2025/12/22 8:51 + * @Description: 查询设备组信息 请求参数 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WxMaGetIotGroupInfoRequest implements Serializable { + + private static final long serialVersionUID = 4913375114243384968L; + /** + * 设备组的唯一标识(必填) + */ + @SerializedName("group_id") + private String groupId; + + public String toJson() { + return WxMaGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/device/WxMaIotGroupDeviceInfoResponse.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/device/WxMaIotGroupDeviceInfoResponse.java new file mode 100644 index 0000000000..e7bd92edf8 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/device/WxMaIotGroupDeviceInfoResponse.java @@ -0,0 +1,51 @@ +package cn.binarywang.wx.miniapp.bean.device; + +import cn.binarywang.wx.miniapp.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +/** + * @Author: yanglegetuo + * @Date: 2025/12/22 8:51 + * @Description: 设备组信息 响应参数 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WxMaIotGroupDeviceInfoResponse implements Serializable { + + private static final long serialVersionUID = 6615660801230308048L; + /** + * 设备组名称 + */ + @SerializedName("group_name") + private String groupName; + /** + * 设备列表 + */ + @SerializedName("device_list") + private List deviceList; + + /** + * 设备型号id。通过注册设备获得(必填) + */ + @SerializedName("model_id") + private String modelId; + /** + * 设备类型 + */ + @SerializedName("model_type") + private String modelType; + + + public String toJson() { + return WxMaGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/device/WxMaIotGroupDeviceRequest.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/device/WxMaIotGroupDeviceRequest.java new file mode 100644 index 0000000000..02b87d6f02 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/device/WxMaIotGroupDeviceRequest.java @@ -0,0 +1,44 @@ +package cn.binarywang.wx.miniapp.bean.device; + +import cn.binarywang.wx.miniapp.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +/** + * @Author: yanglegetuo + * @Date: 2025/12/22 8:51 + * @Description: 设备组 添加/移除 设备 请求参数 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WxMaIotGroupDeviceRequest implements Serializable { + private static final long serialVersionUID = -5648997758678588138L; + + /** + * 设备组的唯一标识(必填) + */ + @SerializedName("group_id") + private String groupId; + /** + * 设备列表 + */ + @SerializedName("device_list") + private List deviceList; + /** + * 是否强制更新设备列表,等于 true 时将已存在其它设备组中的设备移除并添加到当前设备组,慎用。 + */ + @SerializedName("force_add") + private Boolean forceAdd; + + public String toJson() { + return WxMaGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaApiUrlConstants.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaApiUrlConstants.java index 76625334f4..2a7496e06e 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaApiUrlConstants.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaApiUrlConstants.java @@ -569,6 +569,14 @@ public interface DeviceSubscribe { /** 发送设备订阅消息 */ String SEND_DEVICE_SUBSCRIBE_MSG_URL = "https://api.weixin.qq.com/cgi-bin/message/device/subscribe/send"; + /** 创建设备组 */ + String CREATE_IOT_GROUP_ID_URL = "https://api.weixin.qq.com/wxa/business/group/createid"; + /** 设备组添加设备 */ + String ADD_IOT_GROUP_DEVICE_URL = "https://api.weixin.qq.com/wxa/business/group/adddevice"; + /** 设备组删除设备 */ + String REMOVE_IOT_GROUP_DEVICE_URL = "https://api.weixin.qq.com/wxa/business/group/removedevice"; + /** 查询设备组信息 */ + String GET_IOT_GROUP_INFO_URL = "https://api.weixin.qq.com/wxa/business/group/getinfo"; } /** diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaDeviceSubscribeServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaDeviceSubscribeServiceImplTest.java index e1c4390549..838713868a 100644 --- a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaDeviceSubscribeServiceImplTest.java +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaDeviceSubscribeServiceImplTest.java @@ -1,24 +1,27 @@ package cn.binarywang.wx.miniapp.api.impl; import cn.binarywang.wx.miniapp.api.WxMaService; -import cn.binarywang.wx.miniapp.bean.device.WxMaDeviceSubscribeMessageRequest; -import cn.binarywang.wx.miniapp.bean.device.WxMaDeviceTicketRequest; +import cn.binarywang.wx.miniapp.bean.device.*; import cn.binarywang.wx.miniapp.test.ApiTestModule; import com.google.common.collect.Lists; -import com.google.gson.GsonBuilder; import com.google.gson.JsonObject; import com.google.inject.Inject; +import lombok.extern.slf4j.Slf4j; import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.util.json.GsonParser; import org.testng.annotations.Guice; import org.testng.annotations.Test; +import java.util.Collections; +import java.util.List; + /** * 小程序设备订阅消息相关 测试类 * * @author JCLee * @since 2021-12-16 17:13:35 */ +@Slf4j @Test @Guice(modules = ApiTestModule.class) public class WxMaDeviceSubscribeServiceImplTest { @@ -27,7 +30,7 @@ public class WxMaDeviceSubscribeServiceImplTest { protected WxMaService wxService; @Test - public void testGetSnTicket() throws WxErrorException{ + public void testGetSnTicket() throws WxErrorException { WxMaDeviceTicketRequest wxMaDeviceTicketRequest = new WxMaDeviceTicketRequest(); wxMaDeviceTicketRequest.setModelId("11111"); wxMaDeviceTicketRequest.setSn("11111"); @@ -36,7 +39,7 @@ public void testGetSnTicket() throws WxErrorException{ } @Test - public void sendDeviceSubscribeMsg() throws WxErrorException{ + public void sendDeviceSubscribeMsg() throws WxErrorException { WxMaDeviceSubscribeMessageRequest wxMaDeviceSubscribeMessageRequest = new WxMaDeviceSubscribeMessageRequest(); wxMaDeviceSubscribeMessageRequest.setToOpenidList(Lists.newArrayList("1", "2")); wxMaDeviceSubscribeMessageRequest.setPage("pages/index/index"); @@ -56,4 +59,46 @@ public void sendDeviceSubscribeMsg() throws WxErrorException{ wxMaDeviceSubscribeMessageRequest.setData(data); this.wxService.getDeviceSubscribeService().sendDeviceSubscribeMsg(wxMaDeviceSubscribeMessageRequest); } + + @Test + public void testCreateIotGroupId() throws WxErrorException { + WxMaCreateIotGroupIdRequest request = new WxMaCreateIotGroupIdRequest(); + request.setModelId("11111"); + request.setGroupName("测试设备组"); + String groupId = this.wxService.getDeviceSubscribeService().createIotGroupId(request); + System.out.println(groupId); + } + + @Test + public void testGetIotGroupInfo() throws WxErrorException { + WxMaGetIotGroupInfoRequest request = new WxMaGetIotGroupInfoRequest(); + request.setGroupId("12313123"); + WxMaIotGroupDeviceInfoResponse response = this.wxService.getDeviceSubscribeService().getIotGroupInfo(request); + log.info("testGetIotGroupInfo = {}", response); + } + + @Test + public void testAddIotGroupDevice() throws WxErrorException { + WxMaDeviceTicketRequest deviceTicketRequest = new WxMaDeviceTicketRequest(); + deviceTicketRequest.setSn("2222222"); + deviceTicketRequest.setModelId("sdfeweee"); + WxMaIotGroupDeviceRequest request = new WxMaIotGroupDeviceRequest(); + request.setGroupId("12313123"); + request.setDeviceList(Collections.singletonList(deviceTicketRequest)); + request.setForceAdd(true); + List response = this.wxService.getDeviceSubscribeService().addIotGroupDevice(request); + log.info("testAddIotGroupDevice = {}", response); + } + + @Test + public void testRemoveIotGroupDevice() throws WxErrorException { + WxMaDeviceTicketRequest deviceTicketRequest = new WxMaDeviceTicketRequest(); + deviceTicketRequest.setSn("2222222"); + deviceTicketRequest.setModelId("sdfeweee"); + WxMaIotGroupDeviceRequest request = new WxMaIotGroupDeviceRequest(); + request.setGroupId("12313123"); + request.setDeviceList(Collections.singletonList(deviceTicketRequest)); + List response = this.wxService.getDeviceSubscribeService().removeIotGroupDevice(request); + log.info("testRemoveIotGroupDevice = {}", response); + } } From f00a71965a435e5de2f9e5ee6e1a989f46a7033e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Charon-=E5=BC=A0=E5=90=8C=E5=AD=A6?= <38038850+xgl6@users.noreply.github.com> Date: Wed, 24 Dec 2025 15:47:54 +0800 Subject: [PATCH 25/46] =?UTF-8?q?:art:=20=E4=BF=AE=E6=94=B9=E5=BE=AE?= =?UTF-8?q?=E4=BF=A1=E5=8C=BB=E4=BF=9D=E6=94=AF=E4=BB=98=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E7=9A=84=E8=A7=84=E8=8C=83=E6=80=A7=E9=97=AE=E9=A2=98=EF=BC=8C?= =?UTF-8?q?=E4=B8=BB=E8=A6=81=E6=B6=89=E5=8F=8A=E5=AD=97=E6=AE=B5=E5=91=BD?= =?UTF-8?q?=E5=90=8D=E6=A0=87=E5=87=86=E5=8C=96=E3=80=81=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E8=B0=83=E6=95=B4=E5=92=8C=20API=20=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E7=AD=BE=E5=90=8D=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wxpay/bean/mipay/MedInsOrdersRequest.java | 49 +++++++++++++------ .../wxpay/bean/mipay/MedInsOrdersResult.java | 26 +++++----- .../bean/mipay/MedInsRefundNotifyRequest.java | 10 ---- .../wxpay/service/MiPayService.java | 3 +- .../wxpay/service/impl/MiPayServiceImpl.java | 4 +- .../service/impl/MiPayServiceImplTest.java | 3 +- 6 files changed, 51 insertions(+), 44 deletions(-) diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/MedInsOrdersRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/MedInsOrdersRequest.java index 1819b328c8..b651100a59 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/MedInsOrdersRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/MedInsOrdersRequest.java @@ -12,6 +12,7 @@ import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; /** * 医保自费混合收款下单请求 @@ -25,6 +26,7 @@ @Builder(builderMethodName = "newBuilder") @NoArgsConstructor @AllArgsConstructor +@Accessors(chain = true) public class MedInsOrdersRequest { /** @@ -282,72 +284,72 @@ public class MedInsOrdersRequest { * 字段名:总金额 * 变量名:total_fee * 必填:否 - * 类型:long + * 类型:Integer * 描述:总金额 *
*/ @SerializedName("total_fee") - public Long totalFee; + public Integer totalFee; /** *
    * 字段名:医保统筹基金支付金额
    * 变量名:med_ins_gov_fee
    * 必填:否
-   * 类型:long
+   * 类型:Integer
    * 描述:医保统筹基金支付金额
    * 
*/ @SerializedName("med_ins_gov_fee") - public Long medInsGovFee; + public Integer medInsGovFee; /** *
    * 字段名:医保个人账户支付金额
    * 变量名:med_ins_self_fee
    * 必填:否
-   * 类型:long
+   * 类型:Integer
    * 描述:医保个人账户支付金额
    * 
*/ @SerializedName("med_ins_self_fee") - public Long medInsSelfFee; + public Integer medInsSelfFee; /** *
    * 字段名:医保其他基金支付金额
    * 变量名:med_ins_other_fee
    * 必填:否
-   * 类型:long
+   * 类型:Integer
    * 描述:医保其他基金支付金额
    * 
*/ @SerializedName("med_ins_other_fee") - public Long medInsOtherFee; + public Integer medInsOtherFee; /** *
    * 字段名:医保现金支付金额
    * 变量名:med_ins_cash_fee
    * 必填:否
-   * 类型:long
+   * 类型:Integer
    * 描述:医保现金支付金额
    * 
*/ @SerializedName("med_ins_cash_fee") - public Long medInsCashFee; + public Integer medInsCashFee; /** *
    * 字段名:微信支付现金支付金额
    * 变量名:wechat_pay_cash_fee
    * 必填:否
-   * 类型:long
+   * 类型:Integer
    * 描述:微信支付现金支付金额
    * 
*/ @SerializedName("wechat_pay_cash_fee") - public Long wechatPayCashFee; + public Integer wechatPayCashFee; /** *
@@ -462,6 +464,11 @@ public class MedInsOrdersRequest {
    * 支付人身份信息
    * 
*/ + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + @Accessors(chain = true) public static class PersonIdentification { /** *
@@ -507,18 +514,23 @@ public static class PersonIdentification {
    * 现金增加明细实体
    * 
*/ + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + @Accessors(chain = true) public static class CashAddEntity { /** *
      * 字段名:现金增加金额
      * 变量名:cash_add_fee
      * 必填:是
-     * 类型:long
+     * 类型:Integer
      * 描述:现金增加金额
      * 
*/ @SerializedName("cash_add_fee") - public Long cashAddFee; + public Integer cashAddFee; /** *
@@ -538,18 +550,23 @@ public static class CashAddEntity {
    * 现金减少明细实体
    * 
*/ + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + @Accessors(chain = true) public static class CashReduceEntity { /** *
      * 字段名:现金减少金额
      * 变量名:cash_reduce_fee
      * 必填:是
-     * 类型:long
+     * 类型:Integer
      * 描述:现金减少金额
      * 
*/ @SerializedName("cash_reduce_fee") - public Long cashReduceFee; + public Integer cashReduceFee; /** *
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/MedInsOrdersResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/MedInsOrdersResult.java
index 4fc68e279f..9a119d8723 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/MedInsOrdersResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/MedInsOrdersResult.java
@@ -65,7 +65,7 @@ public class MedInsOrdersResult {
    * 
*/ @SerializedName("med_ins_pay_status") - public MedInsPayStatusEnum medInsPayStatusEnum; + public MedInsPayStatusEnum medInsPayStatus; /** *
@@ -320,72 +320,72 @@ public class MedInsOrdersResult {
    * 字段名:总金额
    * 变量名:total_fee
    * 必填:否
-   * 类型:long
+   * 类型:Integer
    * 描述:总金额
    * 
*/ @SerializedName("total_fee") - public Long totalFee; + public Integer totalFee; /** *
    * 字段名:医保统筹基金支付金额
    * 变量名:med_ins_gov_fee
    * 必填:否
-   * 类型:long
+   * 类型:Integer
    * 描述:医保统筹基金支付金额
    * 
*/ @SerializedName("med_ins_gov_fee") - public Long medInsGovFee; + public Integer medInsGovFee; /** *
    * 字段名:医保个人账户支付金额
    * 变量名:med_ins_self_fee
    * 必填:否
-   * 类型:long
+   * 类型:Integer
    * 描述:医保个人账户支付金额
    * 
*/ @SerializedName("med_ins_self_fee") - public Long medInsSelfFee; + public Integer medInsSelfFee; /** *
    * 字段名:医保其他基金支付金额
    * 变量名:med_ins_other_fee
    * 必填:否
-   * 类型:long
+   * 类型:Integer
    * 描述:医保其他基金支付金额
    * 
*/ @SerializedName("med_ins_other_fee") - public Long medInsOtherFee; + public Integer medInsOtherFee; /** *
    * 字段名:医保现金支付金额
    * 变量名:med_ins_cash_fee
    * 必填:否
-   * 类型:long
+   * 类型:Integer
    * 描述:医保现金支付金额
    * 
*/ @SerializedName("med_ins_cash_fee") - public Long medInsCashFee; + public Integer medInsCashFee; /** *
    * 字段名:微信支付现金支付金额
    * 变量名:wechat_pay_cash_fee
    * 必填:否
-   * 类型:long
+   * 类型:Integer
    * 描述:微信支付现金支付金额
    * 
*/ @SerializedName("wechat_pay_cash_fee") - public Long wechatPayCashFee; + public Integer wechatPayCashFee; /** *
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/MedInsRefundNotifyRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/MedInsRefundNotifyRequest.java
index b6e15a3644..cb935b52cd 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/MedInsRefundNotifyRequest.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/mipay/MedInsRefundNotifyRequest.java
@@ -20,16 +20,6 @@
 @AllArgsConstructor
 public class MedInsRefundNotifyRequest {
 
-  /**
-   * 
-   * 字段名:医保自费混合订单号
-   * 必填:是
-   * 类型:string(32)
-   * 描述:医保自费混合订单号
-   * 
- */ - private String mixTradeNo; - /** *
    * 字段名:医疗机构的商户号
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/MiPayService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/MiPayService.java
index 83b75ad40c..5e2f678c16 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/MiPayService.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/MiPayService.java
@@ -87,8 +87,9 @@ public interface MiPayService {
    * 
* * @param request 医保退款通知请求参数 + * @param mixTradeNo 【医保自费混合订单号】 医保自费混合订单号 * @throws WxPayException the wx pay exception */ - void medInsRefundNotify(MedInsRefundNotifyRequest request) throws WxPayException; + void medInsRefundNotify(MedInsRefundNotifyRequest request, String mixTradeNo) throws WxPayException; } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/MiPayServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/MiPayServiceImpl.java index 3063d7731e..769b789fa3 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/MiPayServiceImpl.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/MiPayServiceImpl.java @@ -59,8 +59,8 @@ public MiPayNotifyV3Result parseMiPayNotifyV3Result(String notifyData, Signature } @Override - public void medInsRefundNotify(MedInsRefundNotifyRequest request) throws WxPayException { - String url = String.format("%s/v3/med-ins/refunds/notify?mix_trade_no=%s", this.payService.getPayBaseUrl(), request.getMixTradeNo()); + public void medInsRefundNotify(MedInsRefundNotifyRequest request, String mixTradeNo) throws WxPayException { + String url = String.format("%s/v3/med-ins/refunds/notify?mix_trade_no=%s", this.payService.getPayBaseUrl(), mixTradeNo); this.payService.postV3(url, GSON.toJson(request)); } diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/MiPayServiceImplTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/MiPayServiceImplTest.java index 23c3c56816..095d355bd4 100644 --- a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/MiPayServiceImplTest.java +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/MiPayServiceImplTest.java @@ -131,13 +131,12 @@ public void medInsRefundNotify() throws WxPayException { // 解析请求参数 MedInsRefundNotifyRequest request = GSON.fromJson(requestParamStr, MedInsRefundNotifyRequest.class); - request.setMixTradeNo(mixTradeNo); MiPayService miPayService = wxPayService.getMiPayService(); try { // 调用医保退款通知方法,预期会失败,因为是模拟数据 - miPayService.medInsRefundNotify(request); + miPayService.medInsRefundNotify(request,mixTradeNo); log.info("医保退款通知调用成功"); } catch (WxPayException e) { // 预期会抛出异常,因为是模拟数据 From 912ba354e992afc162b124e97e1ef9fcaf1fb4cd Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Wed, 24 Dec 2025 16:12:56 +0800 Subject: [PATCH 26/46] =?UTF-8?q?:art:=20#3821=20=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=20WxCpRe?= =?UTF-8?q?disConfigImpl.getWebhookKey()=20=E6=96=B9=E6=B3=95=E6=97=A0?= =?UTF-8?q?=E9=99=90=E9=80=92=E5=BD=92=E8=B0=83=E7=94=A8=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 --- .../cp/config/impl/WxCpRedisConfigImpl.java | 2 +- .../config/impl/WxCpRedisConfigImplTest.java | 48 +++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 weixin-java-cp/src/test/java/me/chanjar/weixin/cp/config/impl/WxCpRedisConfigImplTest.java diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpRedisConfigImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpRedisConfigImpl.java index 15c0ff6727..49cd7c4559 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpRedisConfigImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpRedisConfigImpl.java @@ -468,7 +468,7 @@ public boolean autoRefreshToken() { @Override public String getWebhookKey() { - return this.getWebhookKey(); + return this.webhookKey; } @Override diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/config/impl/WxCpRedisConfigImplTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/config/impl/WxCpRedisConfigImplTest.java new file mode 100644 index 0000000000..1a7fdccbd1 --- /dev/null +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/config/impl/WxCpRedisConfigImplTest.java @@ -0,0 +1,48 @@ +package me.chanjar.weixin.cp.config.impl; + +import org.mockito.Mockito; +import org.testng.Assert; +import org.testng.annotations.Test; +import redis.clients.jedis.JedisPool; + +/** + * WxCpRedisConfigImpl 测试类 + */ +public class WxCpRedisConfigImplTest { + + /** + * 测试 getWebhookKey 方法不会导致无限递归 + * 这个测试验证了 #issue 中提到的无限递归问题已被修复 + */ + @Test + public void testGetWebhookKeyNoInfiniteRecursion() { + // 使用 Mockito 创建 mock JedisPool,避免真实连接 + JedisPool jedisPool = Mockito.mock(JedisPool.class); + + WxCpRedisConfigImpl config = new WxCpRedisConfigImpl(jedisPool); + + // 测试1: webhookKey 为 null 时应该返回 null,而不是抛出 StackOverflowError + String webhookKey = config.getWebhookKey(); + Assert.assertNull(webhookKey, "未设置 webhookKey 时应返回 null"); + + // 测试2: 通过反射设置 webhookKey,然后验证能够正确获取 + // 注意:由于 WxCpRedisConfigImpl 没有提供 setWebhookKey 方法, + // 我们通过反射来设置这个字段以测试 getter 的正确性 + try { + java.lang.reflect.Field field = WxCpRedisConfigImpl.class.getDeclaredField("webhookKey"); + boolean originalAccessible = field.isAccessible(); + field.setAccessible(true); + try { + String testWebhookKey = "test-webhook-key-123"; + field.set(config, testWebhookKey); + + String retrievedKey = config.getWebhookKey(); + Assert.assertEquals(retrievedKey, testWebhookKey, "应该返回设置的 webhookKey 值"); + } finally { + field.setAccessible(originalAccessible); + } + } catch (NoSuchFieldException | IllegalAccessException e) { + Assert.fail("反射设置 webhookKey 失败: " + e.getMessage()); + } + } +} From 6504f5d82cf6ee87f6839b42b697201eda7f443b Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Wed, 24 Dec 2025 16:14:20 +0800 Subject: [PATCH 27/46] =?UTF-8?q?:new:=20#3823=20=E3=80=90=E4=BC=81?= =?UTF-8?q?=E4=B8=9A=E5=BE=AE=E4=BF=A1=E3=80=91=E6=B7=BB=E5=8A=A0=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E5=BA=94=E7=94=A8=E7=AE=A1=E7=90=86=E5=91=98=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E7=9A=84=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../weixin/cp/api/WxCpAgentService.java | 15 +++++ .../cp/api/impl/WxCpAgentServiceImpl.java | 18 ++++++ .../weixin/cp/constant/WxCpApiPathConsts.java | 4 ++ .../cp/api/impl/WxCpAgentServiceImplTest.java | 62 +++++++++++++++++++ 4 files changed, 99 insertions(+) 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 9eddc0f507..05f06f1da9 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 @@ -2,6 +2,7 @@ import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.cp.bean.WxCpAgent; +import me.chanjar.weixin.cp.bean.WxCpTpAdmin; import java.util.List; @@ -52,4 +53,18 @@ public interface WxCpAgentService { */ List list() throws WxErrorException; + /** + *
+   * 获取应用管理员列表
+   * 第三方服务商可以用此接口获取授权企业中某个第三方应用或者代开发应用的管理员列表(不包括外部管理员),
+   * 以便服务商在用户进入应用主页之后根据是否管理员身份做权限的区分。
+   * 详情请见: 文档
+   * 
+ * + * @param agentId 应用id + * @return admin list + * @throws WxErrorException the wx error exception + */ + WxCpTpAdmin getAdminList(Integer agentId) throws WxErrorException; + } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpAgentServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpAgentServiceImpl.java index 81628fed82..cc08d33bb1 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpAgentServiceImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpAgentServiceImpl.java @@ -11,6 +11,7 @@ import me.chanjar.weixin.cp.api.WxCpAgentService; import me.chanjar.weixin.cp.api.WxCpService; import me.chanjar.weixin.cp.bean.WxCpAgent; +import me.chanjar.weixin.cp.bean.WxCpTpAdmin; import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; import java.util.List; @@ -65,4 +66,21 @@ public List list() throws WxErrorException { }.getType()); } + @Override + public WxCpTpAdmin getAdminList(Integer agentId) throws WxErrorException { + if (agentId == null) { + throw new IllegalArgumentException("缺少agentid参数"); + } + + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("agentid", agentId); + String url = this.mainService.getWxCpConfigStorage().getApiUrl(AGENT_GET_ADMIN_LIST); + String responseContent = this.mainService.post(url, jsonObject.toString()); + JsonObject respObj = GsonParser.parse(responseContent); + if (respObj.get(WxConsts.ERR_CODE).getAsInt() != 0) { + throw new WxErrorException(WxError.fromJson(responseContent, WxType.CP)); + } + return WxCpTpAdmin.fromJson(responseContent); + } + } 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 ad4d4f33f2..ade813ef5c 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 @@ -112,6 +112,10 @@ interface Agent { * The constant AGENT_LIST. */ String AGENT_LIST = "/cgi-bin/agent/list"; + /** + * The constant AGENT_GET_ADMIN_LIST. + */ + String AGENT_GET_ADMIN_LIST = "/cgi-bin/agent/get_admin_list"; } /** diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpAgentServiceImplTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpAgentServiceImplTest.java index 07438056c3..cbd947b925 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpAgentServiceImplTest.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpAgentServiceImplTest.java @@ -1,11 +1,14 @@ package me.chanjar.weixin.cp.api.impl; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; import com.google.inject.Inject; import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.cp.api.ApiTestModule; import me.chanjar.weixin.cp.api.WxCpAgentService; import me.chanjar.weixin.cp.api.WxCpService; import me.chanjar.weixin.cp.bean.WxCpAgent; +import me.chanjar.weixin.cp.bean.WxCpTpAdmin; import me.chanjar.weixin.cp.config.WxCpConfigStorage; import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl; import me.chanjar.weixin.cp.constant.WxCpApiPathConsts; @@ -82,6 +85,20 @@ public void testList() throws WxErrorException { assertThat(list.get(0).getSquareLogoUrl()).isNotEmpty(); } + /** + * Test get admin list. + * + * @throws WxErrorException the wx error exception + */ + @Test + public void testGetAdminList() throws WxErrorException { + final Integer agentId = this.wxCpService.getWxCpConfigStorage().getAgentId(); + WxCpTpAdmin adminList = this.wxCpService.getAgentService().getAdminList(agentId); + + assertThat(adminList).isNotNull(); + assertThat(adminList.getErrcode()).isEqualTo(0L); + } + /** * The type Mock test. */ @@ -118,6 +135,51 @@ public void testGet() throws Exception { } + /** + * Test get admin list. + * + * @throws Exception the exception + */ + @Test + public void testGetAdminList() throws Exception { + // 构建响应JSON + JsonObject admin1 = new JsonObject(); + admin1.addProperty("userid", "zhangsan"); + admin1.addProperty("open_userid", "woAJ2GCAAAXtWyujaWJHDDGi0mACH71w"); + admin1.addProperty("auth_type", 1); + + JsonObject admin2 = new JsonObject(); + admin2.addProperty("userid", "lisi"); + admin2.addProperty("open_userid", "woAJ2GCAAAXtWyujaWJHDDGi0mACH72w"); + admin2.addProperty("auth_type", 2); + + JsonArray adminArray = new JsonArray(); + adminArray.add(admin1); + adminArray.add(admin2); + + JsonObject returnJsonObj = new JsonObject(); + returnJsonObj.addProperty("errcode", 0); + returnJsonObj.addProperty("errmsg", "ok"); + returnJsonObj.add("admin", adminArray); + String returnJson = returnJsonObj.toString(); + + JsonObject requestJson = new JsonObject(); + requestJson.addProperty("agentid", 9); + final WxCpConfigStorage configStorage = new WxCpDefaultConfigImpl(); + when(wxService.getWxCpConfigStorage()).thenReturn(configStorage); + when(wxService.post(configStorage.getApiUrl(WxCpApiPathConsts.Agent.AGENT_GET_ADMIN_LIST), requestJson.toString())).thenReturn(returnJson); + when(wxService.getAgentService()).thenReturn(new WxCpAgentServiceImpl(wxService)); + + WxCpAgentService wxAgentService = this.wxService.getAgentService(); + WxCpTpAdmin adminList = wxAgentService.getAdminList(9); + + assertEquals(0, adminList.getErrcode().intValue()); + assertEquals(2, adminList.getAdmin().size()); + assertEquals("zhangsan", adminList.getAdmin().get(0).getUserId()); + assertEquals("woAJ2GCAAAXtWyujaWJHDDGi0mACH71w", adminList.getAdmin().get(0).getOpenUserId()); + assertEquals(1, adminList.getAdmin().get(0).getAuthType().intValue()); + } + } } From db15aec8de330c457f33b25562021237e345fb40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=8F=E6=B2=AB=E6=B5=85=E5=BE=AE=E5=87=89?= <71374855+limingAlex1314@users.noreply.github.com> Date: Sat, 27 Dec 2025 21:10:39 +0800 Subject: [PATCH 28/46] =?UTF-8?q?:bug:=20#3825=20=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=E5=88=9D?= =?UTF-8?q?=E5=A7=8B=E5=8C=96api=20v3=E8=AF=B7=E6=B1=82=E6=97=B6=E8=AF=81?= =?UTF-8?q?=E4=B9=A6=E5=BA=8F=E5=88=97=E5=8F=B7=E5=80=BC=E7=94=9F=E6=88=90?= =?UTF-8?q?=E6=9D=A1=E4=BB=B6=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 --- .../java/com/github/binarywang/wxpay/config/WxPayConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 f4a1c3d008..a3a9dc7a92 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 @@ -343,7 +343,7 @@ public CloseableHttpClient initApiV3HttpClient() throws WxPayException { certificate = (X509Certificate) objects[1]; this.certSerialNo = certificate.getSerialNumber().toString(16).toUpperCase(); } - if (certificate == null && StringUtils.isBlank(this.getCertSerialNo()) && (StringUtils.isNotBlank(this.getPrivateCertPath()) || StringUtils.isNotBlank(this.getPrivateCertString())) || this.getPrivateCertContent() != null) { + if (certificate == null && StringUtils.isBlank(this.getCertSerialNo()) && (StringUtils.isNotBlank(this.getPrivateCertPath()) || StringUtils.isNotBlank(this.getPrivateCertString()) || this.getPrivateCertContent() != null)) { try (InputStream certInputStream = this.loadConfigInputStream(this.getPrivateCertString(), this.getPrivateCertPath(), this.privateCertContent, "privateCertPath")) { certificate = PemUtils.loadCertificate(certInputStream); From 7c6dbf15e44dcd521703f1a8c2f6b2d381b9759b Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sat, 3 Jan 2026 16:45:03 +0800 Subject: [PATCH 29/46] =?UTF-8?q?:art:=20#3827=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=E7=BE=A4=E8=81=8A?= =?UTF-8?q?=E5=8F=98=E6=9B=B4=E4=BA=8B=E4=BB=B6MemChangeList=E5=AD=97?= =?UTF-8?q?=E6=AE=B5=E5=91=BD=E5=90=8D=E8=A7=84=E8=8C=83=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cp/bean/message/WxCpXmlMessage.java | 2 +- .../cp/bean/message/WxCpXmlMessageTest.java | 74 +++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) 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 d15eda8826..4001c7d0e4 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 @@ -157,7 +157,7 @@ public class WxCpXmlMessage implements Serializable { @XStreamAlias("MemChangeList") @XStreamConverter(value = XStreamCDataConverter.class) - private String MemChangeList; + private String memChangeList; @XStreamAlias("LastMemVer") @XStreamConverter(value = XStreamCDataConverter.class) 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 94874519c0..0b2324a5f5 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 @@ -497,4 +497,78 @@ public void testIntelligentRobotMessage() { assertEquals(wxMessage.getRobotId(), "robot_id_123"); assertEquals(wxMessage.getSessionId(), "session_id_456"); } + + /** + * Test external chat change event + * 测试企业微信群聊变更事件解析 - 群成员变更场景 + */ + public void testExternalChatChangeEvent() { + // 测试群成员加入事件 + String xmlAddMember = "" + + "" + + "" + + "1403610513" + + "" + + "" + + "" + + "" + + "" + + "1" + + "2" + + "" + + ""; + WxCpXmlMessage wxMessage = WxCpXmlMessage.fromXml(xmlAddMember); + assertEquals(wxMessage.getToUserName(), "toUser"); + assertEquals(wxMessage.getFromUserName(), "sys"); + assertEquals(wxMessage.getCreateTime(), Long.valueOf(1403610513L)); + assertEquals(wxMessage.getMsgType(), WxConsts.XmlMsgType.EVENT); + assertEquals(wxMessage.getEvent(), WxCpConsts.EventType.CHANGE_EXTERNAL_CHAT); + assertEquals(wxMessage.getChangeType(), "update"); + assertEquals(wxMessage.getChatId(), "wrOgQhDgAAMYQiS5ol9G7gK9JVAAAA"); + assertEquals(wxMessage.getUpdateDetail(), "add_member"); + assertEquals(wxMessage.getJoinScene(), "1"); + assertEquals(wxMessage.getMemChangeCnt(), "2"); + assertEquals(wxMessage.getMemChangeList(), "wmEJiCwAAA9KG2qlSq6rKwASSgAAAA,wmEJiCwAAA9KG2qlSq6rKwBBBBBBB"); + + // 测试群成员退出事件 + String xmlDelMember = "" + + "" + + "" + + "1403610513" + + "" + + "" + + "" + + "" + + "" + + "1" + + "1" + + "" + + ""; + WxCpXmlMessage wxMessage2 = WxCpXmlMessage.fromXml(xmlDelMember); + assertEquals(wxMessage2.getEvent(), WxCpConsts.EventType.CHANGE_EXTERNAL_CHAT); + assertEquals(wxMessage2.getChangeType(), "update"); + assertEquals(wxMessage2.getChatId(), "wrOgQhDgAAMYQiS5ol9G7gK9JVAAAA"); + assertEquals(wxMessage2.getUpdateDetail(), "del_member"); + assertEquals(wxMessage2.getQuitScene(), "1"); + assertEquals(wxMessage2.getMemChangeCnt(), "1"); + assertEquals(wxMessage2.getMemChangeList(), "wmEJiCwAAA9KG2qlSq6rKwASSgAAAA"); + + // 测试空MemChangeList场景(某些情况下可能没有成员变更列表) + String xmlNoMemChangeList = "" + + "" + + "" + + "1403610513" + + "" + + "" + + "" + + "" + + "" + + ""; + WxCpXmlMessage wxMessage3 = WxCpXmlMessage.fromXml(xmlNoMemChangeList); + assertEquals(wxMessage3.getEvent(), WxCpConsts.EventType.CHANGE_EXTERNAL_CHAT); + assertEquals(wxMessage3.getChangeType(), "update"); + assertEquals(wxMessage3.getUpdateDetail(), "change_name"); + // 当XML中没有MemChangeList元素时,字段应该为null而不是空字符串 + assertThat(wxMessage3.getMemChangeList()).isNull(); + } } From 42f727cd412be46a86015c34de49256d7377c87e Mon Sep 17 00:00:00 2001 From: Binary Wang Date: Sat, 3 Jan 2026 23:30:31 +0800 Subject: [PATCH 30/46] =?UTF-8?q?:bookmark:=20=E5=8F=91=E5=B8=83=204.8.0?= =?UTF-8?q?=20=E6=AD=A3=E5=BC=8F=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/maven-publish.yml | 17 ++++++++++++++++- pom.xml | 2 +- solon-plugins/pom.xml | 2 +- .../wx-java-channel-multi-solon-plugin/pom.xml | 2 +- .../wx-java-channel-solon-plugin/pom.xml | 2 +- .../wx-java-cp-multi-solon-plugin/pom.xml | 2 +- solon-plugins/wx-java-cp-solon-plugin/pom.xml | 2 +- .../wx-java-miniapp-multi-solon-plugin/pom.xml | 2 +- .../wx-java-miniapp-solon-plugin/pom.xml | 2 +- .../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 +- .../wx-java-qidian-solon-plugin/pom.xml | 2 +- spring-boot-starters/pom.xml | 2 +- .../pom.xml | 2 +- .../wx-java-channel-spring-boot-starter/pom.xml | 2 +- .../pom.xml | 2 +- .../wx-java-cp-spring-boot-starter/pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../wx-java-miniapp-spring-boot-starter/pom.xml | 2 +- .../pom.xml | 2 +- .../wx-java-mp-spring-boot-starter/pom.xml | 2 +- .../wx-java-open-spring-boot-starter/pom.xml | 2 +- .../wx-java-pay-spring-boot-starter/pom.xml | 2 +- .../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 +- 36 files changed, 51 insertions(+), 36 deletions(-) diff --git a/.github/workflows/maven-publish.yml b/.github/workflows/maven-publish.yml index de68370ae1..a12c20b112 100644 --- a/.github/workflows/maven-publish.yml +++ b/.github/workflows/maven-publish.yml @@ -33,7 +33,22 @@ jobs: VERSION="${BASE_VER}.B" TAG="v${BASE_VER}" IS_RELEASE="true" - echo "Matched release commit: VERSION=$VERSION, TAG=$TAG" + echo "Matched test release commit: VERSION=$VERSION, TAG=$TAG" + # 检查并打tag + if git tag | grep -q "^$TAG$"; then + echo "Tag $TAG already exists." + else + git config user.name "Binary Wang" + git config user.email "a@binarywang.com" + git tag -a "$TAG" -m "Release $TAG" + git push origin "$TAG" + echo "Tag $TAG created and pushed." + fi + elif [[ "$COMMIT_MSG" =~ ^:bookmark:\ 发布\ ([0-9]+\.[0-9]+\.[0-9]+)\ 正式版本 ]]; then + VERSION="${BASH_REMATCH[1]}" + TAG="v${VERSION}" + IS_RELEASE="true" + echo "Matched formal release commit: VERSION=$VERSION, TAG=$TAG" # 检查并打tag if git tag | grep -q "^$TAG$"; then echo "Tag $TAG already exists." diff --git a/pom.xml b/pom.xml index eba8e083a7..0a33fbdf16 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.github.binarywang wx-java - 4.7.9.B + 4.8.0 pom WxJava - Weixin/Wechat Java SDK 微信开发Java SDK diff --git a/solon-plugins/pom.xml b/solon-plugins/pom.xml index 971fc184ab..d0ca564c24 100644 --- a/solon-plugins/pom.xml +++ b/solon-plugins/pom.xml @@ -6,7 +6,7 @@ com.github.binarywang wx-java - 4.7.9.B + 4.8.0 pom wx-java-solon-plugins diff --git a/solon-plugins/wx-java-channel-multi-solon-plugin/pom.xml b/solon-plugins/wx-java-channel-multi-solon-plugin/pom.xml index df721c03a3..995ecbd532 100644 --- a/solon-plugins/wx-java-channel-multi-solon-plugin/pom.xml +++ b/solon-plugins/wx-java-channel-multi-solon-plugin/pom.xml @@ -5,7 +5,7 @@ wx-java-solon-plugins com.github.binarywang - 4.7.9.B + 4.8.0 4.0.0 diff --git a/solon-plugins/wx-java-channel-solon-plugin/pom.xml b/solon-plugins/wx-java-channel-solon-plugin/pom.xml index 5e497a4c46..b2ca356692 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.7.9.B + 4.8.0 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 c39b84dc23..17e24bfe2d 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.7.9.B + 4.8.0 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 412db6ea98..7e6f2f8164 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.7.9.B + 4.8.0 4.0.0 diff --git a/solon-plugins/wx-java-miniapp-multi-solon-plugin/pom.xml b/solon-plugins/wx-java-miniapp-multi-solon-plugin/pom.xml index a1cab06d60..932f9244ce 100644 --- a/solon-plugins/wx-java-miniapp-multi-solon-plugin/pom.xml +++ b/solon-plugins/wx-java-miniapp-multi-solon-plugin/pom.xml @@ -5,7 +5,7 @@ wx-java-solon-plugins com.github.binarywang - 4.7.9.B + 4.8.0 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 513de54cd7..5ad8da85e6 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.7.9.B + 4.8.0 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 b65c3e4945..7c02acdfef 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.7.9.B + 4.8.0 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 b504caf7d8..d2507cc0db 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.7.9.B + 4.8.0 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 368d4a6258..0f0527183f 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.7.9.B + 4.8.0 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 baace7b37b..7c1cb4e850 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.7.9.B + 4.8.0 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 f1c7c2f26b..724bdf4ac5 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.7.9.B + 4.8.0 4.0.0 diff --git a/spring-boot-starters/pom.xml b/spring-boot-starters/pom.xml index fda5172752..1f4881e9dd 100644 --- a/spring-boot-starters/pom.xml +++ b/spring-boot-starters/pom.xml @@ -6,7 +6,7 @@ com.github.binarywang wx-java - 4.7.9.B + 4.8.0 pom wx-java-spring-boot-starters 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 index e0a53f8313..b44f597d22 100644 --- 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 @@ -5,7 +5,7 @@ wx-java-spring-boot-starters com.github.binarywang - 4.7.9.B + 4.8.0 4.0.0 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 63b74d4763..95021e2d22 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.7.9.B + 4.8.0 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 b1806a3476..550a14d2ad 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.7.9.B + 4.8.0 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 5b8419bf23..81f68274c5 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.7.9.B + 4.8.0 4.0.0 diff --git a/spring-boot-starters/wx-java-cp-tp-multi-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-cp-tp-multi-spring-boot-starter/pom.xml index 097fc7e07a..f1cc1fba13 100644 --- a/spring-boot-starters/wx-java-cp-tp-multi-spring-boot-starter/pom.xml +++ b/spring-boot-starters/wx-java-cp-tp-multi-spring-boot-starter/pom.xml @@ -4,7 +4,7 @@ wx-java-spring-boot-starters com.github.binarywang - 4.7.9.B + 4.8.0 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 e265218a37..8c8854067f 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.7.9.B + 4.8.0 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 a6f0fc2a38..bcc61b0309 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.7.9.B + 4.8.0 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 1ded1a4e16..6323ae4b6a 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.7.9.B + 4.8.0 4.0.0 diff --git a/spring-boot-starters/wx-java-mp-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-mp-spring-boot-starter/pom.xml index 2b83d966ac..273364c9a7 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.7.9.B + 4.8.0 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 8afd1b83a9..9a25cd89d7 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.7.9.B + 4.8.0 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 ff1d6b84b1..8b67ade1ea 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.7.9.B + 4.8.0 4.0.0 diff --git a/spring-boot-starters/wx-java-qidian-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-qidian-spring-boot-starter/pom.xml index 2257f8253e..a0fc329434 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.7.9.B + 4.8.0 4.0.0 diff --git a/weixin-graal/pom.xml b/weixin-graal/pom.xml index 8e91201f38..3a220b2888 100644 --- a/weixin-graal/pom.xml +++ b/weixin-graal/pom.xml @@ -6,7 +6,7 @@ com.github.binarywang wx-java - 4.7.9.B + 4.8.0 weixin-graal diff --git a/weixin-java-channel/pom.xml b/weixin-java-channel/pom.xml index b18759d728..13dd26fa98 100644 --- a/weixin-java-channel/pom.xml +++ b/weixin-java-channel/pom.xml @@ -6,7 +6,7 @@ com.github.binarywang wx-java - 4.7.9.B + 4.8.0 weixin-java-channel diff --git a/weixin-java-common/pom.xml b/weixin-java-common/pom.xml index d3496f923a..e5427942c4 100644 --- a/weixin-java-common/pom.xml +++ b/weixin-java-common/pom.xml @@ -6,7 +6,7 @@ com.github.binarywang wx-java - 4.7.9.B + 4.8.0 weixin-java-common diff --git a/weixin-java-cp/pom.xml b/weixin-java-cp/pom.xml index 00a6b2d06c..6c47176afc 100644 --- a/weixin-java-cp/pom.xml +++ b/weixin-java-cp/pom.xml @@ -7,7 +7,7 @@ com.github.binarywang wx-java - 4.7.9.B + 4.8.0 weixin-java-cp diff --git a/weixin-java-miniapp/pom.xml b/weixin-java-miniapp/pom.xml index ac308a4164..340a15e58e 100644 --- a/weixin-java-miniapp/pom.xml +++ b/weixin-java-miniapp/pom.xml @@ -7,7 +7,7 @@ com.github.binarywang wx-java - 4.7.9.B + 4.8.0 weixin-java-miniapp diff --git a/weixin-java-mp/pom.xml b/weixin-java-mp/pom.xml index 2b2e8e4210..c33aaeab86 100644 --- a/weixin-java-mp/pom.xml +++ b/weixin-java-mp/pom.xml @@ -7,7 +7,7 @@ com.github.binarywang wx-java - 4.7.9.B + 4.8.0 weixin-java-mp diff --git a/weixin-java-open/pom.xml b/weixin-java-open/pom.xml index f354cb16f8..4c34786dad 100644 --- a/weixin-java-open/pom.xml +++ b/weixin-java-open/pom.xml @@ -7,7 +7,7 @@ com.github.binarywang wx-java - 4.7.9.B + 4.8.0 weixin-java-open diff --git a/weixin-java-pay/pom.xml b/weixin-java-pay/pom.xml index 1902f01892..9bf6d49a7a 100644 --- a/weixin-java-pay/pom.xml +++ b/weixin-java-pay/pom.xml @@ -5,7 +5,7 @@ com.github.binarywang wx-java - 4.7.9.B + 4.8.0 4.0.0 diff --git a/weixin-java-qidian/pom.xml b/weixin-java-qidian/pom.xml index 5135dea3c8..293a4655cf 100644 --- a/weixin-java-qidian/pom.xml +++ b/weixin-java-qidian/pom.xml @@ -7,7 +7,7 @@ com.github.binarywang wx-java - 4.7.9.B + 4.8.0 weixin-java-qidian From 84b5c4d2d0774f800237634e5d0336f53c004fe3 Mon Sep 17 00:00:00 2001 From: Binary Wang Date: Sun, 4 Jan 2026 09:44:40 +0800 Subject: [PATCH 31/46] =?UTF-8?q?:memo:=20=E6=9B=B4=E6=96=B0=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E4=BF=A1=E6=81=AF?= 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 f1cccac4b3..79a19f10b1 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ ### 重要信息 1. [`WxJava` 荣获 `GitCode` 2024年度十大开源社区奖项](https://mp.weixin.qq.com/s/wM_UlMsDm3IZ1CPPDvcvQw)。 2. 项目合作洽谈请联系微信`binary0000`(在微信里自行搜索并添加好友,请注明来意,如有关于SDK问题需讨论请参考下文入群讨论,不要加此微信)。 -3. **2024-12-30 发布 [【4.7.0正式版】](https://mp.weixin.qq.com/s/_7k-XLYBqeJJhvHWCsdT0A)**! +3. **2026-01-03 发布 [【4.8.0正式版】](https://mp.weixin.qq.com/s/mJoFtGc25pXCn3uZRh6Q-w)**! 5. 贡献源码可以参考视频:[【贡献源码全过程(上集)】](https://mp.weixin.qq.com/s/3xUZSATWwHR_gZZm207h7Q)、[【贡献源码全过程(下集)】](https://mp.weixin.qq.com/s/nyzJwVVoYSJ4hSbwyvTx9A) ,友情提供:[程序员小山与Bug](https://space.bilibili.com/473631007) 6. 新手重要提示:本项目仅是一个SDK开发工具包,未提供Web实现,建议使用 `maven` 或 `gradle` 引用本项目即可使用本SDK提供的各种功能,详情可参考 **[【Demo项目】](demo.md)** 或本项目中的部分单元测试代码; 7. 微信开发新手请务必阅读【开发文档】([Gitee Wiki](https://gitee.com/binary/weixin-java-tools/wikis/Home) 或者 [Github Wiki](https://github.com/binarywang/WxJava/wiki))的常见问题部分,可以少走很多弯路,节省不少时间。 @@ -95,7 +95,7 @@ com.github.binarywang (不同模块参考下文) - 4.7.0 + 4.8.0 ``` From 521d46d95706960cf5a73f40f472001489b52edc Mon Sep 17 00:00:00 2001 From: HeCG95 <52144646+HeCG95@users.noreply.github.com> Date: Mon, 5 Jan 2026 19:33:02 +0800 Subject: [PATCH 32/46] =?UTF-8?q?:new:=20#3828=20=E3=80=90=E5=BE=AE?= =?UTF-8?q?=E4=BF=A1=E6=94=AF=E4=BB=98=E3=80=91=E6=9B=B4=E6=96=B0=E5=95=86?= =?UTF-8?q?=E5=AE=B6=E8=BD=AC=E8=B4=A6=20API=20=E7=9A=84=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E5=92=8C=E5=93=8D=E5=BA=94=E5=AD=97=E6=AE=B5=EF=BC=8C=E5=90=8C?= =?UTF-8?q?=E6=AD=A5=E5=AE=98=E6=96=B9=E6=96=87=E6=A1=A3=E7=9A=84=E5=AD=97?= =?UTF-8?q?=E6=AE=B5=E8=B0=83=E6=95=B4=EF=BC=8C=E6=96=B0=E5=A2=9E=E8=BD=AC?= =?UTF-8?q?=E8=B4=A6=E5=9C=BA=E6=99=AF=E6=8A=A5=E5=A4=87=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BusinessOperationTransferRequest.java | 44 ++++++++++++++++--- .../BusinessOperationTransferResult.java | 28 ++++++++---- .../BusinessOperationTransferExample.java | 12 ++++- .../BusinessOperationTransferServiceTest.java | 19 ++++++-- 4 files changed, 84 insertions(+), 19 deletions(-) diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/BusinessOperationTransferRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/BusinessOperationTransferRequest.java index 91d9438833..60b8edaf40 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/BusinessOperationTransferRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/BusinessOperationTransferRequest.java @@ -6,8 +6,10 @@ import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; import java.io.Serializable; +import java.util.List; /** * 运营工具-商家转账请求参数 @@ -37,11 +39,11 @@ public class BusinessOperationTransferRequest implements Serializable { private String outBillNo; /** - * 运营工具转账场景ID - * 必须,用于标识运营工具转账的具体业务场景 + * 转账场景ID + * 必须,该笔转账使用的转账场景,可前往“商户平台-产品中心-商家转账”中申请。如:1000(现金营销),1006(企业报销)等 */ - @SerializedName("operation_scene_id") - private String operationSceneId; + @SerializedName("transfer_scene_id") + private String transferSceneId; /** * 用户在直连商户应用下的用户标示 @@ -86,4 +88,36 @@ public class BusinessOperationTransferRequest implements Serializable { */ @SerializedName("notify_url") private String notifyUrl; -} \ No newline at end of file + + /** + * 转账场景报备信息 + * 必须,需按转账场景准确填写报备信息,参考 转账场景报备信息字段说明 + */ + @SerializedName("transfer_scene_report_infos") + private List transferSceneReportInfos; + + /** + * 转账场景报备信息 + */ + @Data + @Accessors(chain = true) + public static class TransferSceneReportInfo implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 信息类型 + * 必须,不能超过15个字符,商户所属转账场景下的信息类型,此字段内容为固定值,需严格按照 转账场景报备信息字段说明 传参。 + */ + @SerializedName("info_type") + private String infoType; + + /** + * 信息内容 + * 必须,不能超过32个字符,商户所属转账场景下的信息内容,商户可按实际业务场景自定义传参,需严格按照 转账场景报备信息字段说明 传参。 + */ + @SerializedName("info_content") + private String infoContent; + + } + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/BusinessOperationTransferResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/BusinessOperationTransferResult.java index a380d6133e..91771b43e1 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/BusinessOperationTransferResult.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/BusinessOperationTransferResult.java @@ -31,15 +31,27 @@ public class BusinessOperationTransferResult implements Serializable { private String transferBillNo; /** - * 转账状态 - * WAIT_PAY:等待确认 - * PROCESSING:转账中 - * SUCCESS:转账成功 - * FAIL:转账失败 - * REFUND:已退款 + * 单据状态 + * 商家转账订单状态 + * ACCEPTED:转账已受理,可原单重试(非终态)。 + * PROCESSING: 转账锁定资金中。如果一直停留在该状态,建议检查账户余额是否足够,如余额不足,可充值后再原单重试(非终态)。 + * WAIT_USER_CONFIRM: 待收款用户确认,当前转账单据资金已锁定,可拉起微信收款确认页面进行收款确认(非终态)。 + * TRANSFERING: 转账中,可拉起微信收款确认页面再次重试确认收款(非终态)。 + * SUCCESS: 转账成功,表示转账单据已成功(终态)。 + * FAIL: 转账失败,表示该笔转账单据已失败。若需重新向用户转账,请重新生成单据并再次发起(终态)。 + * CANCELING: 转账撤销中,商户撤销请求受理成功,该笔转账正在撤销中,需查单确认撤销的转账单据状态(非终态)。 + * CANCELLED: 转账撤销完成,代表转账单据已撤销成功(终态)。 */ - @SerializedName("transfer_state") - private String transferState; + @SerializedName("state") + private String state; + + /** + * 跳转领取页面的package信息 + * 跳转微信支付收款页的package信息, APP调起用户确认收款 或者 JSAPI调起用户确认收款 时需要使用的参数。仅当转账单据状态为WAIT_USER_CONFIRM时返回。
+ * 单据创建后,用户24小时内不领取将过期关闭,建议拉起用户确认收款页面前,先查单据状态:如单据状态为WAIT_USER_CONFIRM,可用之前的package信息拉起;单据到终态时需更换单号重新发起转账。 + */ + @SerializedName("package_info") + private String packageInfo; /** * 发起转账的时间 diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/example/BusinessOperationTransferExample.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/example/BusinessOperationTransferExample.java index d11738816b..117395ba62 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/example/BusinessOperationTransferExample.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/example/BusinessOperationTransferExample.java @@ -8,6 +8,8 @@ import com.github.binarywang.wxpay.service.WxPayService; import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl; +import java.util.Arrays; + /** * 运营工具-商家转账API使用示例 * @@ -41,10 +43,15 @@ public void init() { public void createOperationTransferExample() { try { // 构建转账请求 + BusinessOperationTransferRequest.TransferSceneReportInfo reportInfo = new BusinessOperationTransferRequest.TransferSceneReportInfo(); + reportInfo.setInfoType("活动名称"); + reportInfo.setInfoContent("新会员有礼"); + BusinessOperationTransferRequest request = BusinessOperationTransferRequest.newBuilder() .appid("your_app_id") // 应用ID .outBillNo("OT" + System.currentTimeMillis()) // 商户转账单号 - .operationSceneId(WxPayConstants.OperationSceneId.OPERATION_CASH_MARKETING) // 运营工具转账场景ID + .transferSceneId(WxPayConstants.OperationSceneId.OPERATION_CASH_MARKETING) // 运营工具转账场景ID + .transferSceneReportInfos(Arrays.asList(reportInfo)) // 转账场景报备信息 .openid("user_openid") // 用户openid .userName("张三") // 用户姓名(可选) .transferAmount(100) // 转账金额,单位分 @@ -59,7 +66,8 @@ public void createOperationTransferExample() { System.out.println("转账成功!"); System.out.println("商户单号: " + result.getOutBillNo()); System.out.println("微信转账单号: " + result.getTransferBillNo()); - System.out.println("转账状态: " + result.getTransferState()); + System.out.println("单据状态: " + result.getState()); + System.out.println("跳转领取页面的package信息: " + result.getPackageInfo()); System.out.println("创建时间: " + result.getCreateTime()); } catch (WxPayException e) { diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/BusinessOperationTransferServiceTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/BusinessOperationTransferServiceTest.java index 4107be4347..672483f96b 100644 --- a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/BusinessOperationTransferServiceTest.java +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/BusinessOperationTransferServiceTest.java @@ -7,6 +7,8 @@ import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; +import java.util.Arrays; + import static org.assertj.core.api.Assertions.assertThat; /** @@ -36,10 +38,17 @@ public void testServiceInitialization() { @Test public void testRequestBuilder() { + + // 构建转账请求 + BusinessOperationTransferRequest.TransferSceneReportInfo reportInfo = new BusinessOperationTransferRequest.TransferSceneReportInfo(); + reportInfo.setInfoType("test_info_type"); + reportInfo.setInfoContent("test_info_content"); + BusinessOperationTransferRequest request = BusinessOperationTransferRequest.newBuilder() .appid("test_app_id") .outBillNo("OT" + System.currentTimeMillis()) - .operationSceneId(WxPayConstants.OperationSceneId.OPERATION_CASH_MARKETING) + .transferSceneId(WxPayConstants.OperationSceneId.OPERATION_CASH_MARKETING) + .transferSceneReportInfos(Arrays.asList(reportInfo)) .openid("test_openid") .transferAmount(100) .transferRemark("测试转账") @@ -47,7 +56,7 @@ public void testRequestBuilder() { .build(); assertThat(request.getAppid()).isEqualTo("test_app_id"); - assertThat(request.getOperationSceneId()).isEqualTo(WxPayConstants.OperationSceneId.OPERATION_CASH_MARKETING); + assertThat(request.getTransferSceneId()).isEqualTo(WxPayConstants.OperationSceneId.OPERATION_CASH_MARKETING); assertThat(request.getTransferAmount()).isEqualTo(100); assertThat(request.getTransferRemark()).isEqualTo("测试转账"); } @@ -77,11 +86,13 @@ public void testResultClasses() { BusinessOperationTransferResult result = new BusinessOperationTransferResult(); result.setOutBillNo("test_out_bill_no"); result.setTransferBillNo("test_transfer_bill_no"); - result.setTransferState("SUCCESS"); + result.setState("SUCCESS"); + result.setPackageInfo("test_package_info"); assertThat(result.getOutBillNo()).isEqualTo("test_out_bill_no"); assertThat(result.getTransferBillNo()).isEqualTo("test_transfer_bill_no"); - assertThat(result.getTransferState()).isEqualTo("SUCCESS"); + assertThat(result.getState()).isEqualTo("SUCCESS"); + assertThat(result.getPackageInfo()).isEqualTo("test_package_info"); BusinessOperationTransferQueryResult queryResult = new BusinessOperationTransferQueryResult(); queryResult.setOperationSceneId("2001"); From 987001214ddf126d748853445d0b8859abd34258 Mon Sep 17 00:00:00 2001 From: buaazyl Date: Tue, 6 Jan 2026 11:19:28 +0800 Subject: [PATCH 33/46] =?UTF-8?q?:art:=20#3830=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=E4=BD=BF?= =?UTF-8?q?=E7=94=A8HttpComponents=E6=97=B6=E4=B8=8D=E9=85=8D=E7=BD=AEprox?= =?UTF-8?q?y=20password=E5=90=AF=E5=8A=A8=E6=8A=A5=E9=94=99=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/impl/WxChannelServiceHttpComponentsImpl.java | 3 +-- .../weixin/cp/api/impl/WxCpServiceHttpComponentsImpl.java | 3 ++- .../service/impl/WxCpCgServiceHttpComponentsImpl.java | 3 ++- .../tp/service/impl/WxCpTpServiceHttpComponentsImpl.java | 8 ++++---- .../weixin/mp/api/impl/WxMpServiceHttpComponentsImpl.java | 3 ++- .../api/impl/WxQidianServiceHttpComponentsImpl.java | 6 ++++-- 6 files changed, 15 insertions(+), 11 deletions(-) diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/impl/WxChannelServiceHttpComponentsImpl.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/impl/WxChannelServiceHttpComponentsImpl.java index 6cf2d38503..f4cbb04755 100644 --- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/impl/WxChannelServiceHttpComponentsImpl.java +++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/impl/WxChannelServiceHttpComponentsImpl.java @@ -5,7 +5,6 @@ import me.chanjar.weixin.channel.config.WxChannelConfig; import me.chanjar.weixin.channel.util.JsonUtils; import me.chanjar.weixin.common.util.http.HttpClientType; -import me.chanjar.weixin.common.util.http.apache.ApacheBasicResponseHandler; import me.chanjar.weixin.common.util.http.hc.BasicResponseHandler; import me.chanjar.weixin.common.util.http.hc.DefaultHttpComponentsClientBuilder; import me.chanjar.weixin.common.util.http.hc.HttpComponentsClientBuilder; @@ -41,7 +40,7 @@ public void initHttp() { apacheHttpClientBuilder.httpProxyHost(config.getHttpProxyHost()) .httpProxyPort(config.getHttpProxyPort()) .httpProxyUsername(config.getHttpProxyUsername()) - .httpProxyPassword(config.getHttpProxyPassword().toCharArray()); + .httpProxyPassword(config.getHttpProxyPassword() == null ? null : config.getHttpProxyPassword().toCharArray()); if (config.getHttpProxyHost() != null && config.getHttpProxyPort() > 0) { this.httpProxy = new HttpHost(config.getHttpProxyHost(), config.getHttpProxyPort()); diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceHttpComponentsImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceHttpComponentsImpl.java index 92fd2dbd9b..4b6a1e36ff 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceHttpComponentsImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceHttpComponentsImpl.java @@ -82,7 +82,8 @@ public void initHttp() { apacheHttpClientBuilder.httpProxyHost(this.configStorage.getHttpProxyHost()) .httpProxyPort(this.configStorage.getHttpProxyPort()) .httpProxyUsername(this.configStorage.getHttpProxyUsername()) - .httpProxyPassword(this.configStorage.getHttpProxyPassword().toCharArray()); + .httpProxyPassword(this.configStorage.getHttpProxyPassword() == null ? null : + this.configStorage.getHttpProxyPassword().toCharArray()); if (this.configStorage.getHttpProxyHost() != null && this.configStorage.getHttpProxyPort() > 0) { this.httpProxy = new HttpHost(this.configStorage.getHttpProxyHost(), this.configStorage.getHttpProxyPort()); diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/corpgroup/service/impl/WxCpCgServiceHttpComponentsImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/corpgroup/service/impl/WxCpCgServiceHttpComponentsImpl.java index d5c60ad037..4c8fa5d131 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/corpgroup/service/impl/WxCpCgServiceHttpComponentsImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/corpgroup/service/impl/WxCpCgServiceHttpComponentsImpl.java @@ -35,7 +35,8 @@ public void initHttp() { apacheHttpClientBuilder.httpProxyHost(this.configStorage.getHttpProxyHost()) .httpProxyPort(this.configStorage.getHttpProxyPort()) .httpProxyUsername(this.configStorage.getHttpProxyUsername()) - .httpProxyPassword(this.configStorage.getHttpProxyPassword().toCharArray()); + .httpProxyPassword(this.configStorage.getHttpProxyPassword() == null ? null : + this.configStorage.getHttpProxyPassword().toCharArray()); if (this.configStorage.getHttpProxyHost() != null && this.configStorage.getHttpProxyPort() > 0) { this.httpProxy = new HttpHost(this.configStorage.getHttpProxyHost(), this.configStorage.getHttpProxyPort()); diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/impl/WxCpTpServiceHttpComponentsImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/impl/WxCpTpServiceHttpComponentsImpl.java index bba597a3ee..44b5fd8693 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/impl/WxCpTpServiceHttpComponentsImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/impl/WxCpTpServiceHttpComponentsImpl.java @@ -10,7 +10,6 @@ import me.chanjar.weixin.common.util.http.hc.DefaultHttpComponentsClientBuilder; import me.chanjar.weixin.common.util.http.hc.HttpComponentsClientBuilder; import me.chanjar.weixin.common.util.json.GsonParser; -import me.chanjar.weixin.cp.config.WxCpTpConfigStorage; import me.chanjar.weixin.cp.constant.WxCpApiPathConsts; import org.apache.hc.client5.http.classic.methods.HttpPost; import org.apache.hc.client5.http.config.RequestConfig; @@ -87,9 +86,10 @@ public void initHttp() { HttpComponentsClientBuilder apacheHttpClientBuilder = DefaultHttpComponentsClientBuilder.get(); apacheHttpClientBuilder.httpProxyHost(this.configStorage.getHttpProxyHost()) - .httpProxyPort(this.configStorage.getHttpProxyPort()) - .httpProxyUsername(this.configStorage.getHttpProxyUsername()) - .httpProxyPassword(this.configStorage.getHttpProxyPassword().toCharArray()); + .httpProxyPort(this.configStorage.getHttpProxyPort()) + .httpProxyUsername(this.configStorage.getHttpProxyUsername()) + .httpProxyPassword(this.configStorage.getHttpProxyPassword() == null ? null : + this.configStorage.getHttpProxyPassword().toCharArray()); if (this.configStorage.getHttpProxyHost() != null && this.configStorage.getHttpProxyPort() > 0) { this.httpProxy = new HttpHost(this.configStorage.getHttpProxyHost(), this.configStorage.getHttpProxyPort()); diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceHttpComponentsImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceHttpComponentsImpl.java index bbf065acfc..c54202ad2f 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceHttpComponentsImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceHttpComponentsImpl.java @@ -51,7 +51,8 @@ public void initHttp() { apacheHttpClientBuilder.httpProxyHost(configStorage.getHttpProxyHost()) .httpProxyPort(configStorage.getHttpProxyPort()) .httpProxyUsername(configStorage.getHttpProxyUsername()) - .httpProxyPassword(configStorage.getHttpProxyPassword().toCharArray()); + .httpProxyPassword(configStorage.getHttpProxyPassword() == null ? null : + configStorage.getHttpProxyPassword().toCharArray()); if (configStorage.getHttpProxyHost() != null && configStorage.getHttpProxyPort() > 0) { this.httpProxy = new HttpHost(configStorage.getHttpProxyHost(), configStorage.getHttpProxyPort()); diff --git a/weixin-java-qidian/src/main/java/me/chanjar/weixin/qidian/api/impl/WxQidianServiceHttpComponentsImpl.java b/weixin-java-qidian/src/main/java/me/chanjar/weixin/qidian/api/impl/WxQidianServiceHttpComponentsImpl.java index a5cc23f0a2..90486efc8f 100644 --- a/weixin-java-qidian/src/main/java/me/chanjar/weixin/qidian/api/impl/WxQidianServiceHttpComponentsImpl.java +++ b/weixin-java-qidian/src/main/java/me/chanjar/weixin/qidian/api/impl/WxQidianServiceHttpComponentsImpl.java @@ -48,8 +48,10 @@ public void initHttp() { HttpComponentsClientBuilder apacheHttpClientBuilder = DefaultHttpComponentsClientBuilder.get(); apacheHttpClientBuilder.httpProxyHost(configStorage.getHttpProxyHost()) - .httpProxyPort(configStorage.getHttpProxyPort()).httpProxyUsername(configStorage.getHttpProxyUsername()) - .httpProxyPassword(configStorage.getHttpProxyPassword().toCharArray()); + .httpProxyPort(configStorage.getHttpProxyPort()) + .httpProxyUsername(configStorage.getHttpProxyUsername()) + .httpProxyPassword(configStorage.getHttpProxyPassword() == null ? null : + configStorage.getHttpProxyPassword().toCharArray()); if (configStorage.getHttpProxyHost() != null && configStorage.getHttpProxyPort() > 0) { this.httpProxy = new HttpHost(configStorage.getHttpProxyHost(), configStorage.getHttpProxyPort()); From e46da01cc802d0d925a89db6427c415dd9347e56 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Tue, 6 Jan 2026 11:20:58 +0800 Subject: [PATCH 34/46] =?UTF-8?q?:art:=20#3824=20=E3=80=90=E5=9F=BA?= =?UTF-8?q?=E7=A1=80=E6=9E=B6=E6=9E=84=E3=80=91=E5=8D=87=E7=BA=A7=E5=88=B0?= =?UTF-8?q?=20Apache=20HttpClient=205.x=20=E4=BD=9C=E4=B8=BA=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=20HTTP=20=E5=AE=A2=E6=88=B7=E7=AB=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 46 ++++ docs/HTTPCLIENT_UPGRADE_GUIDE.md | 203 ++++++++++++++++++ pom.xml | 6 +- .../wxjava/channel/enums/HttpClientType.java | 8 +- .../properties/WxChannelProperties.java | 2 +- .../wxjava/miniapp/enums/HttpClientType.java | 4 + .../solon/wxjava/mp/enums/HttpClientType.java | 4 + .../wxjava/mp/properties/WxMpProperties.java | 2 +- .../wxjava/qidian/enums/HttpClientType.java | 4 + .../qidian/properties/WxQidianProperties.java | 2 +- .../AbstractWxChannelConfiguration.java | 4 + .../wxjava/channel/enums/HttpClientType.java | 6 +- .../wxjava/channel/enums/HttpClientType.java | 8 +- .../properties/WxChannelProperties.java | 2 +- .../wxjava/miniapp/enums/HttpClientType.java | 6 +- .../wx-java-mp-spring-boot-starter/README.md | 2 +- .../wxjava/mp/properties/WxMpProperties.java | 2 +- .../wxjava/qidian/enums/HttpClientType.java | 4 + .../qidian/properties/WxQidianProperties.java | 2 +- weixin-java-common/pom.xml | 20 +- 20 files changed, 313 insertions(+), 24 deletions(-) create mode 100644 docs/HTTPCLIENT_UPGRADE_GUIDE.md diff --git a/README.md b/README.md index 79a19f10b1..54af600beb 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,52 @@ - **微信开放平台**(`weixin-java-open`)主要用于第三方平台,代公众号或小程序进行开发和管理 +--------------------------------- +### HTTP 客户端支持 + +本项目同时支持多种 HTTP 客户端实现,默认推荐使用 **Apache HttpClient 5.x**(最新稳定版本)。 + +#### 支持的 HTTP 客户端类型 + +| HTTP 客户端 | 说明 | 配置值 | 推荐程度 | +|------------|------|--------|---------| +| Apache HttpClient 5.x | Apache HttpComponents Client 5.x,最新版本 | `HttpComponents` | ⭐⭐⭐⭐⭐ 推荐 | +| Apache HttpClient 4.x | Apache HttpClient 4.x,向后兼容 | `HttpClient` | ⭐⭐⭐⭐ 兼容 | +| OkHttp | Square OkHttp 客户端 | `OkHttp` | ⭐⭐⭐ 可选 | +| Jodd-http | Jodd 轻量级 HTTP 客户端 | `JoddHttp` | ⭐⭐ 可选 | + +#### 配置方式 + +**Spring Boot 配置示例:** + +```properties +# 使用 HttpClient 5.x(推荐,MP/CP/Channel/QiDian 模块默认) +wx.mp.config-storage.http-client-type=HttpComponents + +# 使用 HttpClient 4.x(兼容模式,MiniApp 模块默认) +wx.mp.config-storage.http-client-type=HttpClient + +# 使用 OkHttp +wx.mp.config-storage.http-client-type=OkHttp + +# 使用 Jodd-http +wx.mp.config-storage.http-client-type=JoddHttp +``` + +**注意**:如果使用 Multi-Starter(如 `wx-java-mp-multi-spring-boot-starter`),枚举值需使用大写下划线格式: +```properties +# Multi-Starter 配置格式 +wx.mp.config-storage.http-client-type=HTTP_COMPONENTS # 注意使用大写下划线 +``` + +**注意事项:** +1. **MiniApp 模块**已提供 `HttpComponents`(HttpClient 5.x)类型的配置选项,但当前默认仍为 HttpClient 4.x;如需启用 HttpClient 5.x,请确保所使用的集成模块(如 `wx-java-miniapp-spring-boot-starter`、`wx-java-miniapp-solon-plugin`)版本已支持该选项 +2. **MP、Channel、QiDian 模块**已完整支持 HttpClient 5.x,默认推荐使用 +3. **CP 模块**的支持情况取决于具体使用的 Starter 版本,请参考对应模块文档 +4. 如需使用 OkHttp 或 Jodd-http,需在项目中添加对应的依赖(scope为provided) +5. HttpClient 4.x 和 HttpClient 5.x 可以共存,按需配置即可 + + --------------------------------- ### 版本说明 diff --git a/docs/HTTPCLIENT_UPGRADE_GUIDE.md b/docs/HTTPCLIENT_UPGRADE_GUIDE.md new file mode 100644 index 0000000000..08726fa03f --- /dev/null +++ b/docs/HTTPCLIENT_UPGRADE_GUIDE.md @@ -0,0 +1,203 @@ +# HttpClient 升级指南 + +## 概述 + +从 WxJava 4.7.x 版本开始,项目开始支持并推荐使用 **Apache HttpClient 5.x**(HttpComponents Client 5),同时保持对 HttpClient 4.x 的向后兼容。 + +## 为什么升级? + +1. **Apache HttpClient 5.x 是最新稳定版本**:提供更好的性能和更多的功能 +2. **HttpClient 4.x 已经进入维护模式**:不再积极开发新功能 +3. **更好的安全性**:HttpClient 5.x 包含最新的安全更新和改进 +4. **向前兼容**:为未来的开发做好准备 + +## 支持的 HTTP 客户端 + +| HTTP 客户端 | 版本 | 配置值 | 状态 | 说明 | +|------------|------|--------|------|------| +| Apache HttpClient 5.x | 5.5 | `HttpComponents` | ⭐ 推荐 | 最新稳定版本 | +| Apache HttpClient 4.x | 4.5.13 | `HttpClient` | ✅ 支持 | 向后兼容 | +| OkHttp | 4.12.0 | `OkHttp` | ✅ 支持 | 需自行添加依赖 | +| Jodd-http | 6.3.0 | `JoddHttp` | ✅ 支持 | 需自行添加依赖 | + +## 模块支持情况 + +| 模块 | HttpClient 5.x 支持 | 默认客户端 | +|------|-------------------|-----------| +| weixin-java-mp(公众号) | ✅ 是 | HttpComponents (5.x) | +| weixin-java-cp(企业微信) | ⚠️ 视集成方式而定 | 参考对应 starter 配置 | +| weixin-java-channel(视频号) | ✅ 是 | HttpComponents (5.x) | +| weixin-java-qidian(企点) | ✅ 是 | HttpComponents (5.x) | +| weixin-java-miniapp(小程序) | ✅ 是 | HttpClient (4.x) | +| weixin-java-pay(支付) | ✅ 是 | HttpComponents (5.x) | +| weixin-java-open(开放平台) | ✅ 是 | HttpComponents (5.x) | + +**注意**: +- **weixin-java-miniapp 模块**已在核心 SDK 中提供 HttpClient 5.x(`HttpComponents`)支持,但默认仍使用 HttpClient 4.x(`HttpClient`)。如需启用 HttpClient 5.x,可通过配置 `http-client-type=HttpComponents` 显式指定。 +- **weixin-java-cp 模块**的支持情况取决于具体使用的 Starter 版本,请参考对应模块文档。 + +## 对现有项目的影响 + +### 对新项目 +- **无需任何修改**,直接使用最新版本即可 +- 支持 HttpClient 5.x 的模块会自动使用 HttpComponents (5.x) + +### 对现有项目 +- **向后兼容**:不需要修改任何代码 +- HttpClient 4.x 依然可用,项目同时包含两个版本的依赖 +- 如果希望继续使用 HttpClient 4.x,只需在配置中显式指定 + +## 迁移步骤 + +### 1. 更新 WxJava 版本 + +在 `pom.xml` 中更新版本: + +```xml + + com.github.binarywang + weixin-java-mp + 最新版本 + +``` + +### 2. 检查配置(可选) + +#### Spring Boot 项目 + +在 `application.properties` 或 `application.yml` 中: + +```properties +# 使用 HttpClient 5.x(推荐,无需配置,已经是默认值) +wx.mp.config-storage.http-client-type=HttpComponents + +# 或者继续使用 HttpClient 4.x +wx.mp.config-storage.http-client-type=HttpClient +``` + +#### 纯 Java 项目 + +```java +// 使用 HttpClient 5.x(推荐) +WxMpService wxMpService = new WxMpServiceHttpComponentsImpl(); + +// 或者继续使用 HttpClient 4.x +WxMpService wxMpService = new WxMpServiceHttpClientImpl(); +``` + +### 3. 测试应用 + +升级后,建议进行全面测试以确保一切正常工作。 + +## 常见问题 + +### Q: 升级后会不会破坏现有代码? +A: 不会。项目保持完全向后兼容,HttpClient 4.x 的所有实现都保持不变。 + +### Q: 我需要修改代码吗? +A: 大多数情况下不需要。如果希望继续使用 HttpClient 4.x,只需在配置中指定 `http-client-type=HttpClient` 即可。 + +### Q: MiniApp 模块支持 HttpClient 5.x 吗? +A: 支持。MiniApp 模块在核心 SDK 中已经提供了基于 HttpClient 5.x(`HttpComponents`)的支持,但默认仍会使用 HttpClient 4.x(`HttpClient`)以保持向后兼容。如果你使用的是框架集成(例如 Spring Boot Starter 或 Solon Plugin),可以通过显式配置 `http-client-type=HttpComponents` 来启用 HttpClient 5.x。 + +### Q: 我可以在同一个项目中同时使用两个版本吗? +A: 可以。不同的模块可以配置使用不同的 HTTP 客户端。例如,MP 模块使用 HttpClient 5.x,MiniApp 模块默认使用 HttpClient 4.x,但也可以按需配置为 HttpClient 5.x。 + +### Q: 如何排除不需要的依赖? +A: 如果只想使用一个版本,可以在 `pom.xml` 中排除另一个: + +```xml + + com.github.binarywang + weixin-java-mp + 最新版本 + + + + org.apache.httpcomponents + httpclient + + + org.apache.httpcomponents + httpmime + + + +``` + +## 配置参考 + +### Spring Boot 完整配置示例 + +```properties +# 公众号配置 +wx.mp.app-id=your_app_id +wx.mp.secret=your_secret +wx.mp.token=your_token +wx.mp.aes-key=your_aes_key + +# HTTP 客户端配置 +wx.mp.config-storage.http-client-type=HttpComponents # HttpComponents, HttpClient, OkHttp, JoddHttp + +# HTTP 代理配置(可选) +wx.mp.config-storage.http-proxy-host=proxy.example.com +wx.mp.config-storage.http-proxy-port=8080 +wx.mp.config-storage.http-proxy-username=proxy_user +wx.mp.config-storage.http-proxy-password=proxy_pass + +# 超时配置(可选) +wx.mp.config-storage.connection-timeout=5000 +wx.mp.config-storage.so-timeout=5000 +wx.mp.config-storage.connection-request-timeout=5000 +``` + +## 技术细节 + +### HttpClient 4.x 与 5.x 的主要区别 + +1. **包名变更**: + - HttpClient 4.x: `org.apache.http.*` + - HttpClient 5.x: `org.apache.hc.client5.*`, `org.apache.hc.core5.*` + +2. **API 改进**: + - HttpClient 5.x 提供更现代的 API 设计 + - 更好的异步支持 + - 改进的连接池管理 + +3. **性能优化**: + - HttpClient 5.x 包含多项性能优化 + - 更好的资源管理 + +### 项目中的实现 + +WxJava 项目通过策略模式支持多种 HTTP 客户端: + +``` +weixin-java-common/ +├── util/http/ +│ ├── apache/ # HttpClient 4.x 实现 +│ ├── hc/ # HttpClient 5.x (HttpComponents) 实现 +│ ├── okhttp/ # OkHttp 实现 +│ └── jodd/ # Jodd-http 实现 +``` + +每个模块都有对应的 Service 实现类: +- `*ServiceHttpClientImpl` - 使用 HttpClient 4.x +- `*ServiceHttpComponentsImpl` - 使用 HttpClient 5.x +- `*ServiceOkHttpImpl` - 使用 OkHttp +- `*ServiceJoddHttpImpl` - 使用 Jodd-http + +## 反馈与支持 + +如果在升级过程中遇到问题,请: + +1. 查看 [项目 Wiki](https://github.com/binarywang/WxJava/wiki) +2. 在 [GitHub Issues](https://github.com/binarywang/WxJava/issues) 中搜索或提交问题 +3. 加入技术交流群(见 README.md) + +## 总结 + +- ✅ **推荐使用 HttpClient 5.x**:性能更好,功能更强 +- ✅ **向后兼容**:可以继续使用 HttpClient 4.x +- ✅ **灵活配置**:支持多种 HTTP 客户端,按需选择 +- ✅ **平滑迁移**:无需修改代码,仅需配置即可 diff --git a/pom.xml b/pom.xml index 0a33fbdf16..5740a27790 100644 --- a/pom.xml +++ b/pom.xml @@ -136,6 +136,7 @@ UTF-8 4.5.13 + 5.5 9.4.57.v20241219 @@ -157,13 +158,14 @@ 4.12.0 provided + org.apache.httpcomponents.client5 httpclient5 - 5.5 - provided + ${httpclient5.version} + org.apache.httpcomponents httpclient 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 index 0c00dbcaa7..5614f63e86 100644 --- 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 @@ -7,7 +7,11 @@ */ public enum HttpClientType { /** - * HttpClient + * HttpClient. */ - HttpClient + HttpClient, + /** + * HttpComponents. + */ + HttpComponents, } 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 index 6562a02e9d..89b81b7d9f 100644 --- 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 @@ -73,7 +73,7 @@ public static class ConfigStorage { /** * http客户端类型 */ - private HttpClientType httpClientType = HttpClientType.HttpClient; + private HttpClientType httpClientType = HttpClientType.HttpComponents; /** * http代理主机 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 index a4475a02c7..d116a30cf6 100644 --- 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 @@ -19,4 +19,8 @@ public enum HttpClientType { * JoddHttp. */ JoddHttp, + /** + * HttpComponents. + */ + HttpComponents, } 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 index 9b1a8ccbf4..2858d77e43 100644 --- 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 @@ -19,4 +19,8 @@ public enum HttpClientType { * JoddHttp. */ JoddHttp, + /** + * HttpComponents. + */ + HttpComponents, } 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 index cda0aa88e7..0dcc6b41d3 100644 --- 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 @@ -79,7 +79,7 @@ public static class ConfigStorage implements Serializable { /** * http客户端类型. */ - private HttpClientType httpClientType = HttpClientType.HttpClient; + private HttpClientType httpClientType = HttpClientType.HttpComponents; /** * http代理主机. 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 index 0b94821da7..5729ab8fda 100644 --- 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 @@ -19,4 +19,8 @@ public enum HttpClientType { * JoddHttp. */ JoddHttp, + /** + * HttpComponents. + */ + HttpComponents, } 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 index 67c4dba543..e99f882e6f 100644 --- 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 @@ -74,7 +74,7 @@ public static class ConfigStorage implements Serializable { /** * http客户端类型. */ - private HttpClientType httpClientType = HttpClientType.HttpClient; + private HttpClientType httpClientType = HttpClientType.HttpComponents; /** * http代理主机. 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 index 3462bbc25e..e2f9f87f5a 100644 --- 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 @@ -9,6 +9,7 @@ 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.WxChannelServiceHttpComponentsImpl; import me.chanjar.weixin.channel.api.impl.WxChannelServiceImpl; import me.chanjar.weixin.channel.config.WxChannelConfig; import me.chanjar.weixin.channel.config.impl.WxChannelDefaultConfigImpl; @@ -84,6 +85,9 @@ public WxChannelService wxChannelService(WxChannelConfig wxChannelConfig, WxChan case HTTP_CLIENT: wxChannelService = new WxChannelServiceHttpClientImpl(); break; + case HTTP_COMPONENTS: + wxChannelService = new WxChannelServiceHttpComponentsImpl(); + break; default: wxChannelService = new WxChannelServiceImpl(); break; 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 index 6ca09354a3..adc8a2b52b 100644 --- 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 @@ -8,7 +8,7 @@ */ public enum HttpClientType { /** - * HttpClient + * HttpClient. */ HTTP_CLIENT, // WxChannelServiceOkHttpImpl 实现经测试无法正常完成业务固暂不支持OK_HTTP方式 @@ -16,4 +16,8 @@ public enum HttpClientType { // * OkHttp. // */ // OK_HTTP, + /** + * HttpComponents. + */ + HTTP_COMPONENTS, } diff --git a/spring-boot-starters/wx-java-channel-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/enums/HttpClientType.java b/spring-boot-starters/wx-java-channel-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/enums/HttpClientType.java index 63a7bf0c24..e4b3f3ad16 100644 --- a/spring-boot-starters/wx-java-channel-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/enums/HttpClientType.java +++ b/spring-boot-starters/wx-java-channel-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/enums/HttpClientType.java @@ -7,7 +7,11 @@ */ public enum HttpClientType { /** - * HttpClient + * HttpClient. */ - HttpClient + HttpClient, + /** + * HttpComponents. + */ + HttpComponents, } diff --git a/spring-boot-starters/wx-java-channel-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/properties/WxChannelProperties.java b/spring-boot-starters/wx-java-channel-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/properties/WxChannelProperties.java index 43c35fbd1d..f43d297e0b 100644 --- a/spring-boot-starters/wx-java-channel-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/properties/WxChannelProperties.java +++ b/spring-boot-starters/wx-java-channel-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/channel/properties/WxChannelProperties.java @@ -85,7 +85,7 @@ public static class ConfigStorage { /** * http客户端类型 */ - private HttpClientType httpClientType = HttpClientType.HttpClient; + private HttpClientType httpClientType = HttpClientType.HttpComponents; /** * http代理主机 diff --git a/spring-boot-starters/wx-java-miniapp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/enums/HttpClientType.java b/spring-boot-starters/wx-java-miniapp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/enums/HttpClientType.java index b3e4b464fe..48549e4399 100644 --- a/spring-boot-starters/wx-java-miniapp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/enums/HttpClientType.java +++ b/spring-boot-starters/wx-java-miniapp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/enums/HttpClientType.java @@ -8,7 +8,7 @@ */ public enum HttpClientType { /** - * HttpClient. + * HttpClient (Apache HttpClient 4.x). */ HttpClient, /** @@ -19,4 +19,8 @@ public enum HttpClientType { * JoddHttp. */ JoddHttp, + /** + * HttpComponents (Apache HttpClient 5.x). + */ + HttpComponents, } diff --git a/spring-boot-starters/wx-java-mp-spring-boot-starter/README.md b/spring-boot-starters/wx-java-mp-spring-boot-starter/README.md index 3e14f499d9..091912cfad 100644 --- a/spring-boot-starters/wx-java-mp-spring-boot-starter/README.md +++ b/spring-boot-starters/wx-java-mp-spring-boot-starter/README.md @@ -27,7 +27,7 @@ #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-client-type=HttpComponents # http客户端类型: HttpComponents(Apache HttpClient 5.x,推荐), HttpClient(Apache HttpClient 4.x), OkHttp, JoddHttp wx.mp.config-storage.http-proxy-host= wx.mp.config-storage.http-proxy-port= wx.mp.config-storage.http-proxy-username= 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 377fb5b6ab..a6c6e3b6bd 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 @@ -80,7 +80,7 @@ public static class ConfigStorage implements Serializable { /** * http客户端类型. */ - private HttpClientType httpClientType = HttpClientType.HttpClient; + private HttpClientType httpClientType = HttpClientType.HttpComponents; /** * http代理主机. diff --git a/spring-boot-starters/wx-java-qidian-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/qidian/enums/HttpClientType.java b/spring-boot-starters/wx-java-qidian-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/qidian/enums/HttpClientType.java index 1a927211cc..04589a911b 100644 --- a/spring-boot-starters/wx-java-qidian-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/qidian/enums/HttpClientType.java +++ b/spring-boot-starters/wx-java-qidian-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/qidian/enums/HttpClientType.java @@ -19,4 +19,8 @@ public enum HttpClientType { * JoddHttp. */ JoddHttp, + /** + * HttpComponents. + */ + HttpComponents, } diff --git a/spring-boot-starters/wx-java-qidian-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/qidian/properties/WxQidianProperties.java b/spring-boot-starters/wx-java-qidian-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/qidian/properties/WxQidianProperties.java index ddecefb7e2..bec5dfcce0 100644 --- a/spring-boot-starters/wx-java-qidian-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/qidian/properties/WxQidianProperties.java +++ b/spring-boot-starters/wx-java-qidian-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/qidian/properties/WxQidianProperties.java @@ -72,7 +72,7 @@ public static class ConfigStorage implements Serializable { /** * http客户端类型. */ - private HttpClientType httpClientType = HttpClientType.HttpClient; + private HttpClientType httpClientType = HttpClientType.HttpComponents; /** * http代理主机. diff --git a/weixin-java-common/pom.xml b/weixin-java-common/pom.xml index e5427942c4..9032af97da 100644 --- a/weixin-java-common/pom.xml +++ b/weixin-java-common/pom.xml @@ -24,20 +24,13 @@ okhttp provided + org.apache.httpcomponents.client5 httpclient5 - provided - - org.slf4j - slf4j-api - - - com.thoughtworks.xstream - xstream - + org.apache.httpcomponents httpclient @@ -52,6 +45,15 @@ org.apache.httpcomponents httpmime + + + org.slf4j + slf4j-api + + + com.thoughtworks.xstream + xstream + org.slf4j jcl-over-slf4j From 37c2db9672f9b84ba33c4618571d088717d91373 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Tue, 6 Jan 2026 13:01:40 +0800 Subject: [PATCH 35/46] =?UTF-8?q?:art:=20#3832=20=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=20WxSign?= =?UTF-8?q?QueryResult=20=E4=B8=AD=20contract=5Fexpired=5Ftime=20=E5=AD=97?= =?UTF-8?q?=E6=AE=B5=E8=A7=A3=E6=9E=90=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wxpay/bean/result/WxSignQueryResult.java | 2 +- .../bean/result/WxSignQueryResultTest.java | 125 ++++++++++++++++++ 2 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxSignQueryResultTest.java diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxSignQueryResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxSignQueryResult.java index 5241597194..af19aec60a 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxSignQueryResult.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxSignQueryResult.java @@ -112,7 +112,7 @@ protected void loadXml(Document d) { contractDisplayAccount = readXmlString(d, "contract_display_account"); contractState = readXmlInteger(d, "contract_state"); contractSignedTime = readXmlString(d, "contract_signed_time"); - contractExpiredTime = readXmlString(d, "contrace_Expired_time"); + contractExpiredTime = readXmlString(d, "contract_expired_time"); contractTerminatedTime = readXmlString(d, "contract_terminated_time"); contractTerminatedMode = readXmlInteger(d, "contract_termination_mode"); contractTerminationRemark = readXmlString(d, "contract_termination_remark"); diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxSignQueryResultTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxSignQueryResultTest.java new file mode 100644 index 0000000000..52df2b6e2b --- /dev/null +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxSignQueryResultTest.java @@ -0,0 +1,125 @@ +package com.github.binarywang.wxpay.bean.result; + +import com.github.binarywang.wxpay.util.XmlConfig; +import org.testng.Assert; +import org.testng.annotations.Test; + +/** + * WxSignQueryResult 单元测试 + * + * @author Binary Wang + */ +public class WxSignQueryResultTest { + + /** + * 测试 XML 解析,特别是 contract_expired_time 字段 + */ + @Test + public void testFromXML() { + /* + * xml样例字符串来自于官方文档 + * https://pay.weixin.qq.com/doc/v2/merchant/4011987640 + */ + String xmlString = "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " 203\n" + + " 66\n" + + " \n" + + " 123\n" + + " \n" + + " \n" + + " 1\n" + + " 2015-07-01 10:00:00\n" + + " 2015-07-01 10:00:00\n" + + " 2015-07-01 10:00:00\n" + + " 3\n" + + " \n" + + " 0\n" + + " \n" + + " \n" + + ""; + + // 启用 fastMode 以覆盖 WxSignQueryResult#loadXml 分支 + XmlConfig.fastMode = true; + try { + WxSignQueryResult result = WxSignQueryResult.fromXML(xmlString, WxSignQueryResult.class); + + // 验证基本字段 + Assert.assertEquals(result.getReturnCode(), "SUCCESS"); + Assert.assertEquals(result.getResultCode(), "SUCCESS"); + Assert.assertEquals(result.getMchId(), "80000000"); + Assert.assertEquals(result.getAppid(), "wx426b3015555b46be"); + + // 验证签约相关字段 + Assert.assertEquals(result.getContractId(), "203"); + Assert.assertEquals(result.getPlanId(), "66"); + Assert.assertEquals(result.getOpenId(), "oHZx6uMbIG46UXQ3SKxVYEgw1LZs"); + Assert.assertEquals(result.getRequestSerial().longValue(), 123L); + Assert.assertEquals(result.getContractCode(), "1005"); + Assert.assertEquals(result.getContractDisplayAccount(), "test"); + Assert.assertEquals(result.getContractState().intValue(), 1); + + // 重点测试时间字段,特别是 contract_expired_time + Assert.assertEquals(result.getContractSignedTime(), "2015-07-01 10:00:00"); + Assert.assertEquals(result.getContractExpiredTime(), "2015-07-01 10:00:00"); + Assert.assertEquals(result.getContractTerminatedTime(), "2015-07-01 10:00:00"); + + // 验证其他字段 + Assert.assertEquals(result.getContractTerminatedMode().intValue(), 3); + Assert.assertEquals(result.getContractTerminationRemark(), "delete ...."); + } finally { + // 恢复默认值 + XmlConfig.fastMode = false; + } + } + + /** + * 测试 XML 解析 - 只包含必填字段 + */ + @Test + public void testFromXML_RequiredFieldsOnly() { + String xmlString = "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " Wx15463511252015071056489715\n" + + " 123\n" + + " 1695\n" + + " \n" + + " \n" + + " 0\n" + + " 2015-07-01 10:00:00\n" + + " 2016-07-01 10:00:00\n" + + " \n" + + " \n" + + ""; + + // 启用 fastMode 以覆盖 WxSignQueryResult#loadXml 分支 + XmlConfig.fastMode = true; + try { + WxSignQueryResult result = WxSignQueryResult.fromXML(xmlString, WxSignQueryResult.class); + + // 验证必填字段 + Assert.assertEquals(result.getReturnCode(), "SUCCESS"); + Assert.assertEquals(result.getResultCode(), "SUCCESS"); + Assert.assertEquals(result.getContractId(), "Wx15463511252015071056489715"); + Assert.assertEquals(result.getPlanId(), "123"); + Assert.assertEquals(result.getContractState().intValue(), 0); + + // 验证 contract_expired_time 字段能正确解析 + Assert.assertEquals(result.getContractExpiredTime(), "2016-07-01 10:00:00"); + + // 验证非必填字段为 null + Assert.assertNull(result.getContractTerminatedTime()); + Assert.assertNull(result.getContractTerminatedMode()); + Assert.assertNull(result.getContractTerminationRemark()); + } finally { + // 恢复默认值 + XmlConfig.fastMode = false; + } + } +} From 51d2ed7a3c707d6269c686dab15fcf0a27183c80 Mon Sep 17 00:00:00 2001 From: Binary Wang Date: Tue, 6 Jan 2026 16:51:30 +0800 Subject: [PATCH 36/46] =?UTF-8?q?:art:=20=E6=89=B9=E9=87=8F=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E9=83=A8=E5=88=86javadoc=E9=87=8C=E7=9A=84=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E6=A0=87=E7=AD=BE=E6=88=96=E4=B8=8D=E8=A7=84=E8=8C=83?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chanjar/weixin/common/api/WxConsts.java | 2 +- .../bean/oauth2/WxOAuth2AccessToken.java | 9 +- .../weixin/common/error/WxCpErrorMsgEnum.java | 4 +- .../chanjar/weixin/common/error/WxError.java | 4 +- .../weixin/common/error/WxMaErrorMsgEnum.java | 47 +++---- .../common/error/WxOpenErrorMsgEnum.java | 24 ++-- .../weixin/common/service/WxOcrService.java | 4 +- .../session/InternalSessionManager.java | 5 +- .../chanjar/weixin/common/util/SignUtils.java | 1 + .../weixin/common/util/crypto/SHA1.java | 5 +- .../common/util/crypto/WxCryptUtil.java | 1 + .../common/util/http/InputStreamData.java | 3 +- .../http/apache/ApacheHttpClientBuilder.java | 26 +++- .../common/util/json/WxGsonBuilder.java | 5 + .../common/util/xml/XStreamInitializer.java | 5 + .../weixin/cp/api/WxCpCorpGroupService.java | 2 +- .../weixin/cp/api/WxCpExportService.java | 2 +- .../cp/api/WxCpExternalContactService.java | 129 ++++++++++-------- .../weixin/cp/api/WxCpGroupRobotService.java | 7 +- .../chanjar/weixin/cp/api/WxCpKfService.java | 4 +- .../weixin/cp/api/WxCpLivingService.java | 2 +- .../weixin/cp/api/WxCpMediaService.java | 4 +- .../weixin/cp/api/WxCpMessageService.java | 3 +- .../weixin/cp/api/WxCpOAuth2Service.java | 13 +- .../cp/api/WxCpOaMeetingRoomService.java | 11 +- .../chanjar/weixin/cp/api/WxCpOaService.java | 5 +- .../weixin/cp/api/WxCpSchoolService.java | 5 +- .../weixin/cp/api/WxCpSchoolUserService.java | 45 +++--- .../me/chanjar/weixin/cp/api/WxCpService.java | 2 +- .../weixin/cp/api/WxCpUserService.java | 4 +- .../cp/api/impl/WxCpCorpGroupServiceImpl.java | 2 +- .../impl/WxCpExternalContactServiceImpl.java | 2 +- .../chanjar/weixin/cp/bean/WxCpBaseResp.java | 3 +- .../weixin/cp/bean/WxCpTpAuthInfo.java | 2 +- .../cp/bean/WxCpTpPermanentCodeInfo.java | 2 +- .../me/chanjar/weixin/cp/bean/WxCpTpTag.java | 2 +- .../bean/WxCpTpTagAddOrRemoveUsersResult.java | 2 +- .../weixin/cp/bean/WxCpTpTagGetResult.java | 2 +- .../cp/bean/external/WxCpMsgTemplate.java | 5 +- .../WxCpUserExternalUnassignList.java | 3 +- .../WxCpCustomerAcquisitionStatistic.java | 2 +- .../interceptrule/WxCpInterceptRuleInfo.java | 7 +- .../interceptrule/WxCpInterceptRuleList.java | 7 +- .../cp/bean/external/msg/Attachment.java | 5 + .../weixin/cp/bean/kf/WxCpKfAccountLink.java | 4 +- .../cp/bean/media/MediaUploadByUrlReq.java | 3 +- .../cp/bean/media/MediaUploadByUrlResult.java | 3 +- .../bean/message/WxCpLinkedCorpMessage.java | 14 +- .../cp/bean/message/WxCpXmlMessage.java | 24 ++-- .../messagebuilder/TemplateCardBuilder.java | 3 +- .../cp/bean/oa/WxCpCheckinGroupBase.java | 4 +- .../weixin/cp/bean/oa/WxCpOaSchedule.java | 2 +- .../WxCpOaMeetingRoomBookResult.java | 2 +- .../cp/bean/oa/templatedata/TemplateTips.java | 2 +- .../oa/templatedata/TemplateTipsContent.java | 2 +- .../oa/templatedata/TemplateTipsSubText.java | 2 +- .../TemplateTipsSubTextContent.java | 2 +- .../TemplateTipsSubTextContentLink.java | 2 +- .../TemplateTipsSubTextContentPlainText.java | 6 +- .../oa/templatedata/TemplateTipsText.java | 6 +- .../cp/config/WxCpCorpGroupConfigStorage.java | 23 ++-- .../weixin/cp/config/WxCpTpConfigStorage.java | 2 +- .../impl/AbstractWxCpTpInRedisConfigImpl.java | 4 +- .../config/impl/WxCpTpDefaultConfigImpl.java | 6 +- .../weixin/cp/constant/WxCpConsts.java | 2 +- .../cp/corpgroup/service/WxCpCgService.java | 38 ++++-- .../service/impl/BaseWxCpCgServiceImpl.java | 4 + .../cp/tp/service/WxCpTpEditionService.java | 2 +- .../cp/tp/service/WxCpTpIdConvertService.java | 32 +++-- .../weixin/cp/tp/service/WxCpTpOAService.java | 3 +- .../cp/tp/service/WxCpTpOrderService.java | 4 +- .../weixin/cp/tp/service/WxCpTpService.java | 14 +- .../cp/tp/service/WxCpTpTagService.java | 2 +- .../impl/WxCpTpEditionServiceImpl.java | 2 +- .../service/impl/WxCpTpOrderServiceImpl.java | 4 +- .../tp/service/impl/WxCpTpTagServiceImpl.java | 2 +- .../cp/util/xml/XStreamTransformer.java | 4 +- .../wx/miniapp/config/WxMaConfig.java | 26 ++++ .../wx/miniapp/util/crypt/WxMaCryptUtils.java | 2 + .../miniapp/util/xml/XStreamTransformer.java | 22 ++- .../weixin/mp/api/WxMpCardService.java | 4 +- .../weixin/mp/api/WxMpCommentService.java | 4 +- .../weixin/mp/api/WxMpDataCubeService.java | 98 +++++++++---- .../me/chanjar/weixin/mp/api/WxMpService.java | 86 ++++++------ .../mp/api/impl/BaseWxMpServiceImpl.java | 5 +- .../mp/bean/WxMpMassOpenIdsMessage.java | 12 +- .../mp/bean/WxMpMassPreviewMessage.java | 10 +- .../weixin/mp/bean/WxMpMassTagMessage.java | 10 +- .../chanjar/weixin/mp/bean/WxMpUserQuery.java | 5 +- .../card/WxMpCardMpnewsGethtmlResult.java | 4 +- .../MemberCardActivateUserFormRequest.java | 8 +- .../card/membercard/MemberCardUserForm.java | 3 + .../bean/card/membercard/NotifyOptional.java | 9 +- .../WxMpMemberCardUpdateResult.java | 6 +- .../WxMpMemberCardUserInfoResult.java | 7 +- .../weixin/mp/bean/kefu/WxMpKefuMessage.java | 31 ++++- .../mp/bean/message/WxMpXmlMessage.java | 31 +++-- .../mp/bean/message/WxMpXmlOutMessage.java | 27 ++++ .../mp/bean/result/WxMpMassGetResult.java | 3 +- .../weixin/mp/config/WxMpConfigStorage.java | 6 +- .../mp/config/impl/WxMpRedisConfigImpl.java | 2 + .../config/impl/WxMpRedissonConfigImpl.java | 2 + .../mp/constant/WxMpEventConstants.java | 2 +- .../weixin/mp/util/crypto/WxMpCryptUtil.java | 2 +- .../mp/util/xml/XStreamTransformer.java | 22 ++- .../bean/notify/MiPayNotifyV3Result.java | 2 +- .../bean/notify/WxPayBaseNotifyV3Result.java | 13 +- .../bean/notify/WxPayNotifyV3Response.java | 6 +- .../binarywang/wxpay/config/WxPayConfig.java | 5 +- .../binarywang/wxpay/util/RequestUtils.java | 2 +- .../binarywang/wxpay/util/ResourcesUtils.java | 11 ++ .../binarywang/wxpay/util/SignUtils.java | 24 +++- 112 files changed, 751 insertions(+), 419 deletions(-) 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 70c4e47933..d7e8936e62 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 @@ -12,7 +12,7 @@ /** * 微信开发所使用到的常量类. * - * @author Daniel Qian & binarywang & Wang_Wong + * @author Daniel Qian, binarywang, Wang_Wong */ @UtilityClass public class WxConsts { diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/oauth2/WxOAuth2AccessToken.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/oauth2/WxOAuth2AccessToken.java index c08a49063d..b339844ad6 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/oauth2/WxOAuth2AccessToken.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/oauth2/WxOAuth2AccessToken.java @@ -7,7 +7,10 @@ import java.io.Serializable; /** - * https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842 + * OAuth2 AccessToken + *

+ * 参考:{@code https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842} + *

* * @author Daniel Qian */ @@ -36,8 +39,10 @@ public class WxOAuth2AccessToken implements Serializable { private Integer snapshotUser; /** - * https://mp.weixin.qq.com/cgi-bin/announce?action=getannouncement&announce_id=11513156443eZYea&version=&lang=zh_CN. * 本接口在scope参数为snsapi_base时不再提供unionID字段。 + *

+ * 参考:{@code https://mp.weixin.qq.com/cgi-bin/announce?action=getannouncement&announce_id=11513156443eZYea&version=&lang=zh_CN} + *

*/ @SerializedName("unionid") private String unionId; diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxCpErrorMsgEnum.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxCpErrorMsgEnum.java index ea1e9e7c68..356d1dbbf9 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxCpErrorMsgEnum.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxCpErrorMsgEnum.java @@ -453,7 +453,7 @@ public enum WxCpErrorMsgEnum { */ CODE_60008(60008, "部门已存在;部门ID或者部门名称已存在"), /** - * 部门名称含有非法字符;不能含有 \\:?*“< >| 等字符. + * {@code 部门名称含有非法字符;不能含有 \\:?*"< >| 等字符.} */ CODE_60009(60009, "部门名称含有非法字符;不能含有 \\ :?*“< >| 等字符"), /** @@ -521,7 +521,7 @@ public enum WxCpErrorMsgEnum { */ CODE_60124(60124, "无效的父部门id;父部门不存在通讯录中"), /** - * 非法部门名字;不能为空,且不能超过64字节,且不能含有\\:*?”< >|等字符. + * {@code 非法部门名字;不能为空,且不能超过64字节,且不能含有\\:*?"< >|等字符.} */ CODE_60125(60125, "非法部门名字;不能为空,且不能超过64字节,且不能含有\\:*?”< >|等字符"), /** diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxError.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxError.java index b45fba3411..1aab7f1f20 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxError.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxError.java @@ -12,11 +12,13 @@ /** * 微信错误码. + *

* 请阅读: * 公众平台:全局返回码说明 * 企业微信:全局错误码 + *

* - * @author Daniel Qian & Binary Wang + * @author Daniel Qian, Binary Wang */ @Data @NoArgsConstructor diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxMaErrorMsgEnum.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxMaErrorMsgEnum.java index 1bb3f6472b..ffe9b5e3ea 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxMaErrorMsgEnum.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxMaErrorMsgEnum.java @@ -46,23 +46,23 @@ public enum WxMaErrorMsgEnum { */ CODE_40003(40003, "openid 不正确"), /** - *
    * 无效媒体文件类型
-   * 对应操作:uploadTempMedia
+   * 

+ * 对应操作:{@code uploadTempMedia} * 对应地址: - * POST https://api.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=TYPE + * {@code POST https://api.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=TYPE} * 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/customer-message/uploadTempMedia.html - *

+ *

*/ CODE_40004(40004, "无效媒体文件类型"), /** - *
    * 无效媒体文件 ID.
-   * 对应操作:getTempMedia
+   * 

+ * 对应操作:{@code getTempMedia} * 对应地址: - * GET https://api.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_ID + * {@code GET https://api.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_ID} * 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/customer-message/getTempMedia.html - *

+ *

*/ CODE_40007(40007, "无效媒体文件 ID"), /** @@ -99,29 +99,29 @@ public enum WxMaErrorMsgEnum { */ CODE_41028(41028, "form_id 不正确,或者过期"), /** - *
    * code 或 template_id 不正确.
-   * 对应操作:code2Session, sendUniformMessage, sendTemplateMessage
+   * 

+ * 对应操作:{@code code2Session}, {@code sendUniformMessage}, {@code sendTemplateMessage} * 对应地址: - * GET https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code + * {@code GET https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code} * POST https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token=ACCESS_TOKEN * POST https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=ACCESS_TOKEN * 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/login/code2Session.html * https://developers.weixin.qq.com/miniprogram/dev/api/open-api/uniform-message/sendUniformMessage.html * https://developers.weixin.qq.com/miniprogram/dev/api/open-api/template-message/sendTemplateMessage.html - *

+ *

*/ CODE_41029(41029, "请求的参数不正确"), /** - *
    * form_id 已被使用,或者所传page页面不存在,或者小程序没有发布
-   * 对应操作:sendUniformMessage, getWXACodeUnlimit
+   * 

+ * 对应操作:{@code sendUniformMessage}, {@code getWXACodeUnlimit} * 对应地址: * POST https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token=ACCESS_TOKEN * POST https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=ACCESS_TOKEN * 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/uniform-message/sendUniformMessage.html - * https://developers.weixin.qq.com/miniprogram/dev/api/open-api/qr-code/getWXACodeUnlimit.html - *

+ * https://developers.weixin.qq.com/miniprogram/dev/api/open-api/qr-code/getWXACodeUnlimit.html + *

*/ CODE_41030(41030, "请求的参数不正确"), /** @@ -138,13 +138,13 @@ public enum WxMaErrorMsgEnum { */ CODE_45009(45009, "调用分钟频率受限"), /** - *
    * 频率限制,每个用户每分钟100次.
-   * 对应操作:code2Session
+   * 

+ * 对应操作:{@code code2Session} * 对应地址: - * GET https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code + * {@code GET https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code} * 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/login/code2Session.html - *

+ *

*/ CODE_45011(45011, "频率限制,每个用户每分钟100次"), /** @@ -190,12 +190,13 @@ public enum WxMaErrorMsgEnum { */ CODE_45072(45072, "command字段取值不对"), /** - *
    * 下发输入状态,需要之前30秒内跟用户有过消息交互.
-   * 对应操作:customerTyping
+   * 

+ * 对应操作:{@code customerTyping} * 对应地址: * POST https://api.weixin.qq.com/cgi-bin/message/custom/typing?access_token=ACCESS_TOKEN * 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/customer-message/customerTyping.html + *

*/ CODE_45080(45080, "下发输入状态,需要之前30秒内跟用户有过消息交互"), /** @@ -686,7 +687,7 @@ public enum WxMaErrorMsgEnum { /** * 89252 - * 法人&企业信息一致性校验中 front checking + * {@code 法人&企业信息一致性校验中 front checking} */ CODE_89252(89252, "法人&企业信息一致性校验中"), diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxOpenErrorMsgEnum.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxOpenErrorMsgEnum.java index 28fb5de8ad..ba910e988b 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxOpenErrorMsgEnum.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxOpenErrorMsgEnum.java @@ -527,7 +527,7 @@ public enum WxOpenErrorMsgEnum { CODE_40099(40099, "invalid code, this code has consumed."), /** - * invalid DateInfo, Make Sure OldDateInfoType==NewDateInfoType && NewBeginTime<=OldBeginTime && OldEndTime<= NewEndTime + * {@code invalid DateInfo, Make Sure OldDateInfoType==NewDateInfoType && NewBeginTime<=OldBeginTime && OldEndTime<= NewEndTime} */ CODE_40100(40100, "invalid DateInfo, Make Sure OldDateInfoType==NewDateInfoType && NewBeginTime<=OldBeginTime && OldEndTime<= NewEndTime"), @@ -572,7 +572,7 @@ public enum WxOpenErrorMsgEnum { CODE_40108(40108, "invalid client version"), /** - * too many code size, must <= 100 + * {@code too many code size, must <= 100} */ CODE_40109(40109, "too many code size, must <= 100"), @@ -702,7 +702,7 @@ public enum WxOpenErrorMsgEnum { CODE_40135(40135, "invalid not supply bonus, can not change card_id which supply bonus to be not supply"), /** - * invalid use DepositCodeMode, make sure sku.quantity>DepositCode.quantity + * {@code invalid use DepositCodeMode, make sure sku.quantity>DepositCode.quantity} */ CODE_40136(40136, "invalid use DepositCodeMode, make sure sku.quantity>DepositCode.quantity"), @@ -1082,7 +1082,7 @@ public enum WxOpenErrorMsgEnum { CODE_40211(40211, "invalid scope_data"), /** - * paegs 当中存在不合法的query,query格式遵循URL标准,即k1=v1&k2=v2 invalid query + * {@code paegs 当中存在不合法的query,query格式遵循URL标准,即k1=v1&k2=v2 invalid query} */ CODE_40212(40212, "paegs 当中存在不合法的query,query格式遵循URL标准,即k1=v1&k2=v2"), @@ -4242,7 +4242,7 @@ public enum WxOpenErrorMsgEnum { CODE_71005(71005, "limit exe count"), /** - * limit coin count, 1 <= coin_count <= 100000 + * {@code limit coin count, 1 <= coin_count <= 100000} */ CODE_71006(71006, "limit coin count, 1 <= coin_count <= 100000"), @@ -4347,7 +4347,7 @@ public enum WxOpenErrorMsgEnum { CODE_72018(72018, "duplicate order id, invoice had inserted to user"), /** - * limit msg operation card list size, must <= 5 + * {@code limit msg operation card list size, must <= 5} */ CODE_72019(72019, "limit msg operation card list size, must <= 5"), @@ -6432,7 +6432,7 @@ public enum WxOpenErrorMsgEnum { CODE_88009(88009, "reply is not exists"), /** - * count range error. cout <= 0 or count > 50 + * {@code count range error. cout <= 0 or count > 50} */ CODE_88010(88010, "count range error. cout <= 0 or count > 50"), @@ -6682,7 +6682,7 @@ public enum WxOpenErrorMsgEnum { CODE_89251(89251, "模板消息已下发,待法人人脸核身校验"), /** - * 法人&企业信息一致性校验中 front checking + * {@code 法人&企业信息一致性校验中 front checking} */ CODE_89253(89253, "法人&企业信息一致性校验中"), @@ -7257,7 +7257,7 @@ public enum WxOpenErrorMsgEnum { CODE_200021(200021, "场景描述 sceneDesc 参数错误"), /** - * 禁止创建/更新商品(如商品创建功能被封禁) 或 禁止编辑&更新房间 + * {@code 禁止创建/更新商品(如商品创建功能被封禁) 或 禁止编辑&更新房间} */ CODE_300001(300001, "禁止创建/更新商品(如商品创建功能被封禁) 或 禁止编辑&更新房间"), @@ -8382,7 +8382,7 @@ public enum WxOpenErrorMsgEnum { CODE_9300003(9300003, "begin_time must less than end_time"), /** - * end_time - begin_time > 1year + * {@code end_time - begin_time > 1year} */ CODE_9300004(9300004, "end_time - begin_time > 1year"), @@ -8397,7 +8397,7 @@ public enum WxOpenErrorMsgEnum { CODE_9300006(9300006, "invalid activity status"), /** - * gift_num must >0 and <=15 + * {@code gift_num must >0 and <=15} */ CODE_9300007(9300007, "gift_num must >0 and <=15"), @@ -8412,7 +8412,7 @@ public enum WxOpenErrorMsgEnum { CODE_9300009(9300009, "activity can not finish"), /** - * card_info_list must >= 2 + * {@code card_info_list must >= 2} */ CODE_9300010(9300010, "card_info_list must >= 2"), diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/service/WxOcrService.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/service/WxOcrService.java index 39a8a93754..d0aeef8491 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/service/WxOcrService.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/service/WxOcrService.java @@ -12,7 +12,9 @@ /** * 基于小程序或 H5 的身份证、银行卡、行驶证 OCR 识别. - * https://mp.weixin.qq.com/wiki?t=resource/res_main&id=21516712284rHWMX + *

+ * 参考:{@code https://mp.weixin.qq.com/wiki?t=resource/res_main&id=21516712284rHWMX} + *

* * @author Binary Wang * created on 2019-06-22 diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/InternalSessionManager.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/InternalSessionManager.java index e3d9ab8351..24ea58ef38 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/InternalSessionManager.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/InternalSessionManager.java @@ -7,13 +7,12 @@ public interface InternalSessionManager { /** * Return the active Session, associated with this Manager, with the - * specified session id (if any); otherwise return null. + * specified session id (if any); otherwise return {@code null}. * * @param id The session id for the session to be returned + * @return the session or null * @throws IllegalStateException if a new session cannot be * instantiated for any reason - * @throws java.io.IOException if an input/output error occurs while - * processing this request */ InternalSession findSession(String id); diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/SignUtils.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/SignUtils.java index fc3579d45c..1886209f98 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/SignUtils.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/SignUtils.java @@ -25,6 +25,7 @@ public class SignUtils { * * @param message 签名数据 * @param key 签名密钥 + * @return 签名结果 */ public static String createHmacSha256Sign(String message, String key) { try { diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/SHA1.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/SHA1.java index 9b9f776768..43cc54b43d 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/SHA1.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/SHA1.java @@ -29,7 +29,10 @@ public static String gen(String... arr) { } /** - * 用&串接arr参数,生成sha1 digest. + * {@code 用&串接arr参数,生成sha1 digest.} + * + * @param arr 参数数组 + * @return sha1摘要 */ public static String genWithAmple(String... arr) { if (StringUtils.isAnyEmpty(arr)) { diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/WxCryptUtil.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/WxCryptUtil.java index 0b0590b1e6..50362636fc 100755 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/WxCryptUtil.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/WxCryptUtil.java @@ -197,6 +197,7 @@ public EncryptContext encryptContext(String plainText) { /** * 对明文进行加密. * + * @param randomStr 随机字符串 * @param plainText 需要加密的明文 * @return 加密后base64编码的字符串 */ diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/InputStreamData.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/InputStreamData.java index d07873f3c4..f03932984f 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/InputStreamData.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/InputStreamData.java @@ -10,8 +10,9 @@ /** * 输入流数据. - *

+ *

* InputStreamData + *

* * @author zichuan.zhou91@gmail.com * created on 2022/2/15 diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheHttpClientBuilder.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheHttpClientBuilder.java index de34ca5bd1..5b13e7cc17 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheHttpClientBuilder.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheHttpClientBuilder.java @@ -21,42 +21,66 @@ public interface ApacheHttpClientBuilder { /** * 代理服务器地址. + * + * @param httpProxyHost 代理服务器地址 + * @return ApacheHttpClientBuilder */ ApacheHttpClientBuilder httpProxyHost(String httpProxyHost); /** * 代理服务器端口. + * + * @param httpProxyPort 代理服务器端口 + * @return ApacheHttpClientBuilder */ ApacheHttpClientBuilder httpProxyPort(int httpProxyPort); /** * 代理服务器用户名. + * + * @param httpProxyUsername 代理服务器用户名 + * @return ApacheHttpClientBuilder */ ApacheHttpClientBuilder httpProxyUsername(String httpProxyUsername); /** * 代理服务器密码. + * + * @param httpProxyPassword 代理服务器密码 + * @return ApacheHttpClientBuilder */ ApacheHttpClientBuilder httpProxyPassword(String httpProxyPassword); /** * 重试策略. + * + * @param httpRequestRetryHandler 重试处理器 + * @return ApacheHttpClientBuilder */ - ApacheHttpClientBuilder httpRequestRetryHandler(HttpRequestRetryHandler httpRequestRetryHandler ); + ApacheHttpClientBuilder httpRequestRetryHandler(HttpRequestRetryHandler httpRequestRetryHandler); /** * 超时时间. + * + * @param keepAliveStrategy 保持连接策略 + * @return ApacheHttpClientBuilder */ ApacheHttpClientBuilder keepAliveStrategy(ConnectionKeepAliveStrategy keepAliveStrategy); /** * ssl连接socket工厂. + * + * @param sslConnectionSocketFactory SSL连接Socket工厂 + * @return ApacheHttpClientBuilder */ ApacheHttpClientBuilder sslConnectionSocketFactory(SSLConnectionSocketFactory sslConnectionSocketFactory); /** * 支持的TLS协议版本. * Supported TLS protocol versions. + * + * @param supportedProtocols 支持的协议版本数组 + * @return ApacheHttpClientBuilder */ ApacheHttpClientBuilder supportedProtocols(String[] supportedProtocols); } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxGsonBuilder.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxGsonBuilder.java index 6ea269f7e4..8f3dafe48a 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxGsonBuilder.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxGsonBuilder.java @@ -43,6 +43,11 @@ public boolean shouldSkipClass(Class aClass) { }); } + /** + * 创建Gson实例 + * + * @return Gson实例 + */ public static Gson create() { if (Objects.isNull(GSON_INSTANCE)) { synchronized (INSTANCE) { diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/xml/XStreamInitializer.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/xml/XStreamInitializer.java index 3fa91fa70e..51cd1e980c 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/xml/XStreamInitializer.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/xml/XStreamInitializer.java @@ -22,6 +22,11 @@ public class XStreamInitializer { public static ClassLoader classLoader; + /** + * 设置类加载器 + * + * @param classLoaderInfo 类加载器 + */ public static void setClassLoader(ClassLoader classLoaderInfo) { classLoader = classLoaderInfo; } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpCorpGroupService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpCorpGroupService.java index 4da13d3fde..69aea4bca7 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpCorpGroupService.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpCorpGroupService.java @@ -9,7 +9,7 @@ * 企业互联相关接口 * * @author libo <422423229@qq.com> - * Created on 27/2/2023 9:57 PM + * @since 2023-02-27 9:57 PM */ public interface WxCpCorpGroupService { /** diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpExportService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpExportService.java index 24c6ea9dc1..a2c7adabea 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpExportService.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpExportService.java @@ -85,7 +85,7 @@ public interface WxCpExportService { * 获取导出结果 * * 请求方式:GET(HTTPS) - * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/export/get_result?access_token=ACCESS_TOKEN&jobid=jobid_xxxxxxxxxxxxxxx + * {@code 请求地址:https://qyapi.weixin.qq.com/cgi-bin/export/get_result?access_token=ACCESS_TOKEN&jobid=jobid_xxxxxxxxxxxxxxx} * * 文档地址:https://developer.work.weixin.qq.com/document/path/94854 * 返回的url文件下载解密可参考 CSDN diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpExternalContactService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpExternalContactService.java index 7f3cdeab7c..6de9f9226d 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpExternalContactService.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpExternalContactService.java @@ -146,7 +146,7 @@ public interface WxCpExternalContactService { * 企业可通过此接口,根据外部联系人的userid(如何获取?),拉取客户详情。 * * 请求方式:GET(HTTPS) - * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/externalcontact/get?access_token=ACCESS_TOKEN&external_userid=EXTERNAL_USERID + * {@code 请求地址:https://qyapi.weixin.qq.com/cgi-bin/externalcontact/get?access_token=ACCESS_TOKEN&external_userid=EXTERNAL_USERID} * * 权限说明: * @@ -252,9 +252,9 @@ public interface WxCpExternalContactService { * * 权限说明: * - * 该企业授权了该服务商第三方应用,且授权的第三方应用具备“企业客户权限->客户基础信息”权限 + * {@code 该企业授权了该服务商第三方应用,且授权的第三方应用具备“企业客户权限->客户基础信息”权限} * 该客户的跟进人必须在应用的可见范围之内 - * 应用需具备“企业客户权限->客户基础信息”权限 + * {@code 应用需具备“企业客户权限->客户基础信息”权限} *
* * @param externalUserid 代开发自建应用获取到的外部联系人ID @@ -276,8 +276,8 @@ public interface WxCpExternalContactService { * * @param externalUserid 服务商主体的external_userid,必须是source_agentid对应的应用所获取 * @param sourceAgentId 企业授权的代开发自建应用或第三方应用的agentid - * @return - * @throws WxErrorException + * @return 企业的external_userid + * @throws WxErrorException 微信错误异常 */ String fromServiceExternalUserid(String externalUserid, String sourceAgentId) throws WxErrorException; @@ -362,7 +362,7 @@ public interface WxCpExternalContactService { * 权限说明: * * 企业需要使用“客户联系”secret或配置到“可调用应用”列表中的自建应用secret所获取的accesstoken来调用(accesstoken如何获取?) - * 第三方应用需具有“企业客户权限->客户基础信息”权限 + * {@code 第三方应用需具有“企业客户权限->客户基础信息”权限} * 对于第三方/自建应用,群主必须在应用的可见范围 * 仅支持企业服务人员创建的客户群 * 仅可转换出自己企业下的客户群chat_id @@ -410,11 +410,12 @@ WxCpExternalContactBatchInfo getContactDetailBatch(String[] userIdList, String c * 文档地址: https://developer.work.weixin.qq.com/document/path/99434 *
* + * 注意:企业可通过外部联系人临时ID排除重复数据,外部联系人临时ID有效期为4小时。 + * * @param cursor the cursor * @param limit the limit * @return 已服务的外部联系人列表 * @throws WxErrorException . - * @apiNote 企业可通过外部联系人临时ID排除重复数据,外部联系人临时ID有效期为4小时。 */ WxCpExternalContactListInfo getContactList(String cursor, Integer limit) throws WxErrorException; @@ -438,7 +439,7 @@ WxCpExternalContactBatchInfo getContactDetailBatch(String[] userIdList, String c * 企业可通过此接口获取指定成员添加的客户列表。客户是指配置了客户联系功能的成员所添加的外部联系人。没有配置客户联系功能的成员,所添加的外部联系人将不会作为客户返回。 * * 请求方式:GET(HTTPS) - * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/externalcontact/list?access_token=ACCESS_TOKEN&userid=USERID + * {@code 请求地址:https://qyapi.weixin.qq.com/cgi-bin/externalcontact/list?access_token=ACCESS_TOKEN&userid=USERID} * * 权限说明: * @@ -469,7 +470,8 @@ WxCpExternalContactBatchInfo getContactDetailBatch(String[] userIdList, String c /** * 获取待分配的离职成员列表 * 企业和第三方可通过此接口,获取所有离职成员的客户列表,并可进一步调用分配离职成员的客户接口将这些客户重新分配给其他企业成员。 - *

+ + * * 请求方式:POST(HTTPS) * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/externalcontact/get_unassigned_list?access_token=ACCESS_TOKEN * @@ -496,17 +498,17 @@ WxCpExternalContactBatchInfo getContactDetailBatch(String[] userIdList, String c /** * 企业可通过此接口,转接在职成员的客户给其他成员。 - * + *

    * external_userid必须是handover_userid的客户(即配置了客户联系功能的成员所添加的联系人)。
    * 在职成员的每位客户最多被分配2次。客户被转接成功后,将有90个自然日的服务关系保护期,保护期内的客户无法再次被分配。
-   * 

+ * * 权限说明: - * * 企业需要使用“客户联系”secret或配置到“可调用应用”列表中的自建应用secret所获取的accesstoken来调用(accesstoken如何获取?)。 - * 第三方应用需拥有“企业客户权限->客户联系->在职继承”权限 + * 企业需要使用“客户联系”secret或配置到“可调用应用”列表中的自建应用secret所获取的accesstoken来调用(accesstoken如何获取?)。 + * {@code 第三方应用需拥有“企业客户权限->客户联系->在职继承”权限} * 接替成员必须在此第三方应用或自建应用的可见范围内。 * 接替成员需要配置了客户联系功能。 * 接替成员需要在企业微信激活且已经过实名认证。 - * + *

* * @param req 转接在职成员的客户给其他成员请求实体 * @return wx cp base resp @@ -516,13 +518,13 @@ WxCpExternalContactBatchInfo getContactDetailBatch(String[] userIdList, String c /** * 企业和第三方可通过此接口查询在职成员的客户转接情况。 - * + *
    * 权限说明:
-   * 

+ * * 企业需要使用“客户联系”secret或配置到“可调用应用”列表中的自建应用secret所获取的accesstoken来调用(accesstoken如何获取?)。 - * 第三方应用需拥有“企业客户权限->客户联系->在职继承”权限 + * {@code 第三方应用需拥有“企业客户权限->客户联系->在职继承”权限} * 接替成员必须在此第三方应用或自建应用的可见范围内。 - * + *

* * @param handOverUserid 原添加成员的userid * @param takeOverUserid 接替成员的userid @@ -535,19 +537,21 @@ WxCpUserTransferResultResp transferResult(String handOverUserid, String takeOver /** * 企业可通过此接口,分配离职成员的客户给其他成员。 - * + *
    * handover_userid必须是已离职用户。
    * external_userid必须是handover_userid的客户(即配置了客户联系功能的成员所添加的联系人)。
    * 在职成员的每位客户最多被分配2次。客户被转接成功后,将有90个自然日的服务关系保护期,保护期内的客户无法再次被分配。
-   * 

+ + * * 权限说明: - *

+ + * * 企业需要使用“客户联系”secret或配置到“可调用应用”列表中的自建应用secret所获取的accesstoken来调用(accesstoken如何获取?)。 - * 第三方应用需拥有“企业客户权限->客户联系->离职分配”权限 + * {@code 第三方应用需拥有“企业客户权限->客户联系->离职分配”权限} * 接替成员必须在此第三方应用或自建应用的可见范围内。 * 接替成员需要配置了客户联系功能。 * 接替成员需要在企业微信激活且已经过实名认证。 - * + *

* * @param req 转接在职成员的客户给其他成员请求实体 * @return wx cp base resp @@ -557,13 +561,14 @@ WxCpUserTransferResultResp transferResult(String handOverUserid, String takeOver /** * 企业和第三方可通过此接口查询离职成员的客户分配情况。 - * + *
    * 权限说明:
-   * 

+ + * * 企业需要使用“客户联系”secret或配置到“可调用应用”列表中的自建应用secret所获取的accesstoken来调用(accesstoken如何获取?)。 - * 第三方应用需拥有“企业客户权限->客户联系->在职继承”权限 + * {@code 第三方应用需拥有“企业客户权限->客户联系->在职继承”权限} * 接替成员必须在此第三方应用或自建应用的可见范围内。 - * + *

* * @param handOverUserid 原添加成员的userid * @param takeOverUserid 接替成员的userid @@ -630,21 +635,24 @@ WxCpUserExternalGroupChatList listGroupChat(Integer pageIndex, Integer pageSize, /** * 企业可通过此接口,将已离职成员为群主的群,分配给另一个客服成员。 * - * + *
    * 注意::
-   * 

+ + * * 群主离职了的客户群,才可继承 * 继承给的新群主,必须是配置了客户联系功能的成员 * 继承给的新群主,必须有设置实名 * 继承给的新群主,必须有激活企业微信 * 同一个人的群,限制每天最多分配300个给新群主 - *

+ + * * 权限说明: - *

+ + * * 企业需要使用“客户联系”secret或配置到“可调用应用”列表中的自建应用secret所获取的accesstoken来调用(accesstoken如何获取?)。 - * 第三方应用需拥有“企业客户权限->客户联系->分配离职成员的客户群”权限 + * {@code 第三方应用需拥有“企业客户权限->客户联系->分配离职成员的客户群”权限} * 对于第三方/自建应用,群主必须在应用的可见范围。 - * + *

* * @param chatIds 需要转群主的客户群ID列表。取值范围: 1 ~ 100 * @param newOwner 新群主ID @@ -656,7 +664,7 @@ WxCpUserExternalGroupChatList listGroupChat(Integer pageIndex, Integer pageSize, /** * 企业可通过此接口,将在职成员为群主的群,分配给另一个客服成员。 - * + *
    * 注意:
    * 继承给的新群主,必须是配置了客户联系功能的成员
    * 继承给的新群主,必须有设置实名
@@ -716,11 +724,14 @@ WxCpUserExternalGroupChatStatistic getGroupChatStatistic(Date startTime, Integer
    * 企业可通过此接口添加企业群发消息的任务并通知客服人员发送给相关客户或客户群。(注:企业微信终端需升级到2.7.5版本及以上)
    * 注意:调用该接口并不会直接发送消息给客户/客户群,需要相关的客服人员操作以后才会实际发送(客服人员的企业微信需要升级到2.7.5及以上版本)
    * 同一个企业每个自然月内仅可针对一个客户/客户群发送4条消息,超过限制的用户将会被忽略。
-   * 

+ + * * 请求方式: POST(HTTP) - *

+ + * * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/externalcontact/add_msg_template?access_token=ACCESS_TOKEN - *

+ + * * 文档地址 * * @param wxCpMsgTemplate the wx cp msg template @@ -733,15 +744,18 @@ WxCpUserExternalGroupChatStatistic getGroupChatStatistic(Date startTime, Integer /** * 提醒成员群发 * 企业和第三方应用可调用此接口,重新触发群发通知,提醒成员完成群发任务,24小时内每个群发最多触发三次提醒。 - *

+ + * * 请求方式: POST(HTTPS) - *

+ + * * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/externalcontact/remind_groupmsg_send?access_token=ACCESS_TOKEN - *

+ * * 文档地址 * * @param msgId 群发消息的id,通过获取群发记录列表接口返回 * @return the wx cp msg template add result + * @throws WxErrorException 微信错误异常 */ WxCpBaseResp remindGroupMsgSend(String msgId) throws WxErrorException; @@ -753,11 +767,12 @@ WxCpUserExternalGroupChatStatistic getGroupChatStatistic(Date startTime, Integer * 请求方式: POST(HTTPS) *

* 请求地址:https://qyapi.weixin.qq.com/cgi-bin/externalcontact/cancel_groupmsg_send?access_token=ACCESS_TOKEN - *

+ * * 文档地址 * * @param msgId 群发消息的id,通过获取群发记录列表接口返回 * @return the wx cp msg template add result + * @throws WxErrorException 微信错误异常 */ WxCpBaseResp cancelGroupMsgSend(String msgId) throws WxErrorException; @@ -1002,7 +1017,7 @@ WxCpGroupMsgListResult getGroupMsgListV2(String chatType, Date startTime, Date e /** *

    * 企业和第三方应用可通过此接口获取企业与成员的群发记录。
-   * 获取企业群发成员执行结果
+   * 文档地址:https://work.weixin.qq.com/api/doc/90000/90135/93338
    * 
* * @param msgid 群发消息的id,通过获取群发记录列表接口返回 @@ -1031,7 +1046,7 @@ WxCpGroupMsgListResult getGroupMsgListV2(String chatType, Date startTime, Date e /** *
    * 获取群发成员发送任务列表。
-   * 获取群发成员发送任务列表
+   * 文档地址:https://work.weixin.qq.com/api/doc/90000/90135/93338
    * 
* * @param msgid 群发消息的id,通过获取群发记录列表接口返回 @@ -1045,7 +1060,7 @@ WxCpGroupMsgListResult getGroupMsgListV2(String chatType, Date startTime, Date e /** *
    * 添加入群欢迎语素材。
-   * 添加入群欢迎语素材
+   * 文档地址:https://open.work.weixin.qq.com/api/doc/90000/90135/92366
    * 
* * @param template 素材内容 @@ -1057,7 +1072,7 @@ WxCpGroupMsgListResult getGroupMsgListV2(String chatType, Date startTime, Date e /** *
    * 编辑入群欢迎语素材。
-   * 编辑入群欢迎语素材
+   * 文档地址:https://open.work.weixin.qq.com/api/doc/90000/90135/92366
    * 
* * @param template the template @@ -1069,7 +1084,7 @@ WxCpGroupMsgListResult getGroupMsgListV2(String chatType, Date startTime, Date e /** *
    * 获取入群欢迎语素材。
-   * 获取入群欢迎语素材
+   * 文档地址:https://open.work.weixin.qq.com/api/doc/90000/90135/92366
    * 
* * @param templateId 群欢迎语的素材id @@ -1082,7 +1097,7 @@ WxCpGroupMsgListResult getGroupMsgListV2(String chatType, Date startTime, Date e *
    * 删除入群欢迎语素材。
    * 企业可通过此API删除入群欢迎语素材,且仅能删除调用方自己创建的入群欢迎语素材。
-   * 删除入群欢迎语素材
+   * 文档地址:https://open.work.weixin.qq.com/api/doc/90000/90135/92366
    * 
* * @param templateId 群欢迎语的素材id @@ -1094,8 +1109,8 @@ WxCpGroupMsgListResult getGroupMsgListV2(String chatType, Date startTime, Date e /** *
-   * 获取商品图册
-   * 获取商品图册列表
+   * 获取商品图册列表
+   * 文档地址:https://work.weixin.qq.com/api/doc/90000/90135/95096
    * 
* * @param limit 返回的最大记录数,整型,最大值100,默认值50,超过最大值时取默认值 @@ -1108,7 +1123,7 @@ WxCpGroupMsgListResult getGroupMsgListV2(String chatType, Date startTime, Date e /** *
    * 获取商品图册
-   * 获取商品图册
+   * 文档地址:https://work.weixin.qq.com/api/doc/90000/90135/95096
    * 
* * @param productId 商品id @@ -1155,7 +1170,7 @@ WxMediaUploadResult uploadAttachment(String mediaType, Integer attachmentType, F * 企业和第三方应用可以通过此接口新建敏感词规则 * 请求方式:POST(HTTPS) * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/externalcontact/add_intercept_rule?access_token=ACCESS_TOKEN - *
+   * 
* @param ruleAddRequest the rule add request * @return 规则id * @throws WxErrorException the wx error exception @@ -1169,7 +1184,7 @@ WxMediaUploadResult uploadAttachment(String mediaType, Integer attachmentType, F * 企业和第三方应用可以通过此接口修改敏感词规则 * 请求方式:POST(HTTPS) * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/externalcontact/update_intercept_rule?access_token=ACCESS_TOKEN - *
+   * 
* @param interceptRule the rule * @throws WxErrorException the wx error exception */ @@ -1181,7 +1196,7 @@ WxMediaUploadResult uploadAttachment(String mediaType, Integer attachmentType, F * 企业和第三方应用可以通过此接口修改敏感词规则 * 请求方式:POST(HTTPS) * 请求地址 - *
+   * 
* @param ruleId 规则id * @throws WxErrorException the wx error exception */ @@ -1220,7 +1235,7 @@ WxMediaUploadResult uploadAttachment(String mediaType, Integer attachmentType, F * 请求地址: * https://qyapi.weixin.qq.com/cgi-bin/externalcontact/add_product_album?access_token=ACCESS_TOKEN * 文档地址 - *
+   * 
* @param wxCpProductAlbumInfo 商品图册信息 * @return 商品id string * @throws WxErrorException the wx error exception @@ -1235,7 +1250,7 @@ WxMediaUploadResult uploadAttachment(String mediaType, Integer attachmentType, F * 请求地址: * https://qyapi.weixin.qq.com/cgi-bin/externalcontact/update_product_album?access_token=ACCESS_TOKEN * 文档地址 - *
+   * 
* @param wxCpProductAlbumInfo 商品图册信息 * @throws WxErrorException the wx error exception */ @@ -1250,7 +1265,7 @@ WxMediaUploadResult uploadAttachment(String mediaType, Integer attachmentType, F * https://qyapi.weixin.qq.com/cgi-bin/externalcontact/delete_product_album?access_token=ACCESS_TOKEN * * 文档地址 - *
+   * 
* @param productId 商品id * @throws WxErrorException the wx error exception */ @@ -1379,7 +1394,7 @@ WxMediaUploadResult uploadAttachment(String mediaType, Integer attachmentType, F * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/externalcontact/customer_acquisition/statistic?access_token=ACCESS_TOKEN * * @author Hugo - * @date 2023/12/5 14:34 + * @since 2023/12/5 14:34 * @param linkId 获客链接的id * @param startTime 统计起始时间 * @param endTime 统计结束时间 diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpGroupRobotService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpGroupRobotService.java index c1a8d56255..b8ccea5e50 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpGroupRobotService.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpGroupRobotService.java @@ -126,9 +126,10 @@ public interface WxCpGroupRobotService { /** * 发送模板卡片消息 - * @param webhookUrl - * @param wxCpGroupRobotMessage - * @throws WxErrorException + * + * @param webhookUrl webhook地址 + * @param wxCpGroupRobotMessage 群机器人消息 + * @throws WxErrorException 异常 */ void sendTemplateCardMessage(String webhookUrl, WxCpGroupRobotMessage wxCpGroupRobotMessage) throws WxErrorException; diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpKfService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpKfService.java index 5a53829dc0..046cfbc5bb 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpKfService.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpKfService.java @@ -222,7 +222,7 @@ WxCpKfCustomerBatchGetResp customerBatchGet(List externalUserIdList) * https://qyapi.weixin.qq.com/cgi-bin/kf/get_corp_statistic?access_token=ACCESS_TOKEN * 文档地址: * https://developer.work.weixin.qq.com/document/path/95489 - *
+   * 
* @param request 查询参数 * @return 客户数据统计 -企业汇总数据 * @throws WxErrorException the wx error exception @@ -238,7 +238,7 @@ WxCpKfCustomerBatchGetResp customerBatchGet(List externalUserIdList) * https://qyapi.weixin.qq.com/cgi-bin/kf/get_servicer_statistic?access_token=ACCESS_TOKEN * 文档地址: * https://developer.work.weixin.qq.com/document/path/95490 - *
+   * 
* @param request 查询参数 * @return 客户数据统计 -企业汇总数据 * @throws WxErrorException the wx error exception diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpLivingService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpLivingService.java index a2e2344190..63fabad7a1 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpLivingService.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpLivingService.java @@ -27,7 +27,7 @@ public interface WxCpLivingService { /** * 获取直播详情 * 请求方式:GET(HTTPS) - * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/living/get_living_info?access_token=ACCESS_TOKEN&livingid=LIVINGID + * {@code 请求地址:https://qyapi.weixin.qq.com/cgi-bin/living/get_living_info?access_token=ACCESS_TOKEN&livingid=LIVINGID} * * @param livingId 直播id * @return 获取的直播详情 living info 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 e874b26f42..dd5ce594b2 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 @@ -110,9 +110,9 @@ WxMediaUploadResult upload(String mediaType, String filename, String url) * 获取高清语音素材. * 可以使用本接口获取从JSSDK的uploadVoice接口上传的临时语音素材,格式为speex,16K采样率。该音频比上文的临时素材获取接口(格式为amr,8K采样率)更加清晰,适合用作语音识别等对音质要求较高的业务。 * 请求方式:GET(HTTPS) - * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/media/get/jssdk?access_token=ACCESS_TOKEN&media_id=MEDIA_ID + * {@code 请求地址:https://qyapi.weixin.qq.com/cgi-bin/media/get/jssdk?access_token=ACCESS_TOKEN&media_id=MEDIA_ID} * 仅企业微信2.4及以上版本支持。 - * 文档地址:https://work.weixin.qq.com/api/doc#90000/90135/90255 + * 文档地址:https://work.weixin.qq.com/api/doc/90000/90135/90255 *
* * @param mediaId 媒体id diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageService.java index e49a36ba50..534cc89b36 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageService.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageService.java @@ -72,8 +72,9 @@ public interface WxCpMessageService { * 请求地址: https://qyapi.weixin.qq.com/cgi-bin/message/recall?access_token=ACCESS_TOKEN * 文档地址: https://developer.work.weixin.qq.com/document/path/94867 *
+ * * @param msgId 消息id - * @throws WxErrorException + * @throws WxErrorException 异常 */ void recall(String msgId) throws WxErrorException; diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOAuth2Service.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOAuth2Service.java index b7a44047aa..1824196720 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOAuth2Service.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOAuth2Service.java @@ -90,9 +90,10 @@ public interface WxCpOAuth2Service { /** * 获取家校访问用户身份 * 该接口用于根据code获取家长或者学生信息 - *

+ *

    * 请求方式:GET(HTTPS)
-   * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/school/getuserinfo?access_token=ACCESS_TOKEN&code=CODE
+   * {@code 请求地址:https://qyapi.weixin.qq.com/cgi-bin/school/getuserinfo?access_token=ACCESS_TOKEN&code=CODE}
+   * 
* * @param code the code * @return school user info @@ -123,7 +124,7 @@ public interface WxCpOAuth2Service { /** *
    * 获取用户登录身份
-   * https://qyapi.weixin.qq.com/cgi-bin/auth/getuserinfo?access_token=ACCESS_TOKEN&code=CODE
+   * {@code https://qyapi.weixin.qq.com/cgi-bin/auth/getuserinfo?access_token=ACCESS_TOKEN&code=CODE}
    * 该接口可使用用户登录成功颁发的code来获取成员信息,适用于自建应用与代开发应用
    *
    * 注意: 旧的/user/getuserinfo 接口的url已变更为auth/getuserinfo,不过旧接口依旧可以使用,建议是关注新接口即可
@@ -140,13 +141,15 @@ public interface WxCpOAuth2Service {
 
   /**
    * 获取用户二次验证信息
-   * 

+ *

    * api: https://qyapi.weixin.qq.com/cgi-bin/auth/get_tfa_info?access_token=ACCESS_TOKEN
    * 权限说明:仅『通讯录同步』或者自建应用可调用,如用自建应用调用,用户需要在二次验证范围和应用可见范围内。
    * 并发限制:20
+   * 
* * @param code 用户进入二次验证页面时,企业微信颁发的code,每次成员授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期 - * @return me.chanjar.weixin.cp.bean.workbench.WxCpSecondVerificationInfo 二次验证授权码,开发者可以调用通过二次验证接口,解锁企业微信终端.tfa_code有效期五分钟,且只能使用一次。 + * @return 二次验证授权码,开发者可以调用通过二次验证接口,解锁企业微信终端.tfa_code有效期五分钟,且只能使用一次。 + * @throws WxErrorException 微信错误异常 */ WxCpSecondVerificationInfo getTfaInfo(String code) throws WxErrorException; } 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 c2e6c5c872..cc039fd9f5 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 @@ -84,6 +84,7 @@ public interface WxCpOaMeetingRoomService { *
* * @param wxCpOaMeetingRoomBookingInfoRequest 会议室预定信息查询对象 + * @return 会议室预定信息 * @throws WxErrorException . */ WxCpOaMeetingRoomBookingInfoResult getMeetingRoomBookingInfo(WxCpOaMeetingRoomBookingInfoRequest wxCpOaMeetingRoomBookingInfoRequest) throws WxErrorException; @@ -99,6 +100,7 @@ public interface WxCpOaMeetingRoomService { * * * @param wxCpOaMeetingRoomBookRequest 会议室预定对象 + * @return 预定结果 * @throws WxErrorException . */ WxCpOaMeetingRoomBookResult bookingMeetingRoom(WxCpOaMeetingRoomBookRequest wxCpOaMeetingRoomBookRequest) throws WxErrorException; @@ -114,6 +116,7 @@ public interface WxCpOaMeetingRoomService { * * * @param wxCpOaMeetingRoomBookByScheduleRequest 会议室预定对象 + * @return 预定结果 * @throws WxErrorException . */ WxCpOaMeetingRoomBookResult bookingMeetingRoomBySchedule(WxCpOaMeetingRoomBookByScheduleRequest wxCpOaMeetingRoomBookByScheduleRequest) throws WxErrorException; @@ -129,6 +132,7 @@ public interface WxCpOaMeetingRoomService { * * * @param wxCpOaMeetingRoomBookByMeetingRequest 会议室预定对象 + * @return 预定结果 * @throws WxErrorException . */ WxCpOaMeetingRoomBookResult bookingMeetingRoomByMeeting(WxCpOaMeetingRoomBookByMeetingRequest wxCpOaMeetingRoomBookByMeetingRequest) throws WxErrorException; @@ -147,10 +151,10 @@ public interface WxCpOaMeetingRoomService { * @param wxCpOaMeetingRoomCancelBookRequest 取消预定会议室对象 * @throws WxErrorException . */ - void cancelBookMeetingRoom(WxCpOaMeetingRoomCancelBookRequest wxCpOaMeetingRoomCancelBookRequest) throws WxErrorException; + void cancelBookMeetingRoom(WxCpOaMeetingRoomCancelBookRequest wxCpOaMeetingRoomCancelBookRequest) throws WxErrorException; - /** + /** * 根据会议室预定ID查询预定详情. *
    * 企业可通过此接口根据预定id查询相关会议室的预定情况
@@ -161,8 +165,9 @@ public interface WxCpOaMeetingRoomService {
    * 
* * @param wxCpOaMeetingRoomBookingInfoByBookingIdRequest 根据会议室预定ID查询预定详情对象 + * @return 预定详情 * @throws WxErrorException . */ - WxCpOaMeetingRoomBookingInfoByBookingIdResult getBookingInfoByBookingId(WxCpOaMeetingRoomBookingInfoByBookingIdRequest wxCpOaMeetingRoomBookingInfoByBookingIdRequest) throws WxErrorException; + WxCpOaMeetingRoomBookingInfoByBookingIdResult getBookingInfoByBookingId(WxCpOaMeetingRoomBookingInfoByBookingIdRequest wxCpOaMeetingRoomBookingInfoByBookingIdRequest) throws WxErrorException; } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOaService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOaService.java index ee57107b5c..3494dcfa4e 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOaService.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOaService.java @@ -11,7 +11,8 @@ /** * 企业微信OA相关接口. * - * @author Element & Wang_Wong created on 2019-04-06 10:52 + * @author Element, Wang_Wong + * @since 2019-04-06 10:52 */ public interface WxCpOaService { @@ -331,7 +332,7 @@ List getDialRecord(Date startTime, Date endTime, Integer offset, * https://qyapi.weixin.qq.com/cgi-bin/checkin/addcheckinuserface?access_token=ACCESS_TOKEN * 文档地址: * https://developer.work.weixin.qq.com/document/path/93378 - *
+   * 
* @param userId 需要录入的用户id * @param userFace 需要录入的人脸图片数据,需要将图片数据base64处理后填入,对已录入的人脸会进行更新处理 * @throws WxErrorException the wx error exception diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpSchoolService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpSchoolService.java index 56687c9cb1..5f1d41c197 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpSchoolService.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpSchoolService.java @@ -80,9 +80,10 @@ public interface WxCpSchoolService { /** * 获取直播详情 + *
    * 请求方式:GET(HTTPS)
-   * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/school/living/get_living_info?access_token=ACCESS_TOKEN&livingid
-   * =LIVINGID
+   * {@code 请求地址:https://qyapi.weixin.qq.com/cgi-bin/school/living/get_living_info?access_token=ACCESS_TOKEN&livingid=LIVINGID}
+   * 
* * @param livingId the living id * @return living info diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpSchoolUserService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpSchoolUserService.java index a92bfcc100..d004ca8aa5 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpSchoolUserService.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpSchoolUserService.java @@ -19,9 +19,10 @@ public interface WxCpSchoolUserService { /** * 获取访问用户身份 * 该接口用于根据code获取成员信息 - *

+ *

    * 请求方式:GET(HTTPS)
-   * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?access_token=ACCESS_TOKEN&code=CODE
+   * {@code 请求地址:https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?access_token=ACCESS_TOKEN&code=CODE}
+   * 
* * @param code the code * @return user info @@ -32,9 +33,10 @@ public interface WxCpSchoolUserService { /** * 获取家校访问用户身份 * 该接口用于根据code获取家长或者学生信息 - *

+ *

    * 请求方式:GET(HTTPS)
-   * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/school/getuserinfo?access_token=ACCESS_TOKEN&code=CODE
+   * {@code 请求地址:https://qyapi.weixin.qq.com/cgi-bin/school/getuserinfo?access_token=ACCESS_TOKEN&code=CODE}
+   * 
* * @param code the code * @return school user info @@ -90,8 +92,10 @@ public interface WxCpSchoolUserService { /** * 删除学生 + *
    * 请求方式:GET(HTTPS)
-   * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/school/user/delete_student?access_token=ACCESS_TOKEN&userid=USERID
+   * {@code 请求地址:https://qyapi.weixin.qq.com/cgi-bin/school/user/delete_student?access_token=ACCESS_TOKEN&userid=USERID}
+   * 
* * @param studentUserId the student user id * @return wx cp base resp @@ -160,8 +164,10 @@ WxCpBaseResp updateStudent(@NonNull String studentUserId, String newStudentUserI /** * 读取学生或家长 + *
    * 请求方式:GET(HTTPS)
-   * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/school/user/get?access_token=ACCESS_TOKEN&userid=USERID
+   * {@code 请求地址:https://qyapi.weixin.qq.com/cgi-bin/school/user/get?access_token=ACCESS_TOKEN&userid=USERID}
+   * 
* * @param userId the user id * @return user @@ -171,9 +177,10 @@ WxCpBaseResp updateStudent(@NonNull String studentUserId, String newStudentUserI /** * 获取部门成员详情 + *
    * 请求方式:GET(HTTPS)
-   * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/school/user/list?access_token=ACCESS_TOKEN&department_id=DEPARTMENT_ID
-   * &fetch_child=FETCH_CHILD
+   * {@code 请求地址:https://qyapi.weixin.qq.com/cgi-bin/school/user/list?access_token=ACCESS_TOKEN&department_id=DEPARTMENT_ID&fetch_child=FETCH_CHILD}
+   * 
* * @param departmentId 获取的部门id * @param fetchChild 1/0:是否递归获取子部门下面的成员 @@ -184,9 +191,10 @@ WxCpBaseResp updateStudent(@NonNull String studentUserId, String newStudentUserI /** * 获取部门家长详情 + *
    * 请求方式:GET(HTTPS)
-   * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/school/user/list_parent?access_token=ACCESS_TOKEN&department_id
-   * =DEPARTMENT_ID
+   * {@code 请求地址:https://qyapi.weixin.qq.com/cgi-bin/school/user/list_parent?access_token=ACCESS_TOKEN&department_id=DEPARTMENT_ID}
+   * 
* * @param departmentId 获取的部门id * @return user list parent @@ -207,8 +215,10 @@ WxCpBaseResp updateStudent(@NonNull String studentUserId, String newStudentUserI /** * 删除家长 + *
    * 请求方式:GET(HTTPS)
-   * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/school/user/delete_parent?access_token=ACCESS_TOKEN&userid=USERID
+   * {@code 请求地址:https://qyapi.weixin.qq.com/cgi-bin/school/user/delete_parent?access_token=ACCESS_TOKEN&userid=USERID}
+   * 
* * @param userId the user id * @return wx cp base resp @@ -256,7 +266,7 @@ WxCpBaseResp updateStudent(@NonNull String studentUserId, String newStudentUserI /** * 删除部门 * 请求方式:GET(HTTPS) - * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/school/department/delete?access_token=ACCESS_TOKEN&id=ID + * {@code 请求地址:https://qyapi.weixin.qq.com/cgi-bin/school/department/delete?access_token=ACCESS_TOKEN&id=ID} * * @param id the id * @return wx cp base resp @@ -292,10 +302,9 @@ WxCpBaseResp updateStudent(@NonNull String studentUserId, String newStudentUserI /** * 获取外部联系人详情 * 学校可通过此接口,根据外部联系人的userid(如何获取?),拉取外部联系人详情。 - *

+ * * 请求方式:GET(HTTPS) - * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/externalcontact/get?access_token=ACCESS_TOKEN&external_userid - * =EXTERNAL_USERID + * {@code 请求地址:https://qyapi.weixin.qq.com/cgi-bin/externalcontact/get?access_token=ACCESS_TOKEN&external_userid=EXTERNAL_USERID} * * @param externalUserId 外部联系人的userid,注意不是学校成员的帐号 * @return external contact @@ -306,9 +315,9 @@ WxCpBaseResp updateStudent(@NonNull String studentUserId, String newStudentUserI /** * 获取可使用的家长范围 * 获取可在微信「学校通知-学校应用」使用该应用的家长范围,以学生或部门列表的形式返回。应用只能给该列表下的家长发送「学校通知」。注意该范围只能由学校的系统管理员在「管理端-家校沟通-配置」配置。 - *

+ * * 请求方式:GET(HTTPS) - * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/school/agent/get_allow_scope?access_token=ACCESS_TOKEN&agentid=AGENTID + * {@code 请求地址:https://qyapi.weixin.qq.com/cgi-bin/school/agent/get_allow_scope?access_token=ACCESS_TOKEN&agentid=AGENTID} * * @param agentId the agent id * @return allow scope @@ -332,7 +341,7 @@ WxCpBaseResp updateStudent(@NonNull String studentUserId, String newStudentUserI /** * 获取部门列表 * 请求方式:GET(HTTPS) - * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/school/department/list?access_token=ACCESS_TOKEN&id=ID + * {@code 请求地址:https://qyapi.weixin.qq.com/cgi-bin/school/department/list?access_token=ACCESS_TOKEN&id=ID} * * @param id 部门id。获取指定部门及其下的子部门。 如果不填,默认获取全量组织架构 * @return wx cp department list diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java index 0b601ca502..76012a2812 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java @@ -584,7 +584,7 @@ public interface WxCpService extends WxService { /** * 企业互联的服务类对象 * - * @return + * @return 企业互联服务对象 */ WxCpCorpGroupService getCorpGroupService(); diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpUserService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpUserService.java index 2368386b23..7a7b5f40a8 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpUserService.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpUserService.java @@ -38,7 +38,7 @@ public interface WxCpUserService { *

    * 获取部门成员详情
    * 请求方式:GET(HTTPS)
-   * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/user/list?access_token=ACCESS_TOKEN&department_id=DEPARTMENT_ID&fetch_child=FETCH_CHILD
+   * {@code 请求地址:https://qyapi.weixin.qq.com/cgi-bin/user/list?access_token=ACCESS_TOKEN&department_id=DEPARTMENT_ID&fetch_child=FETCH_CHILD}
    *
    * 文档地址:https://work.weixin.qq.com/api/doc/90000/90135/90201
    * 
@@ -213,7 +213,7 @@ public interface WxCpUserService { * 获取加入企业二维码。 * * 请求方式:GET(HTTPS) - * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/corp/get_join_qrcode?access_token=ACCESS_TOKEN&size_type=SIZE_TYPE + * {@code 请求地址:https://qyapi.weixin.qq.com/cgi-bin/corp/get_join_qrcode?access_token=ACCESS_TOKEN&size_type=SIZE_TYPE} * * 文档地址:https://work.weixin.qq.com/api/doc/90000/90135/91714 * diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpCorpGroupServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpCorpGroupServiceImpl.java index 48bd952a83..e3dc1cbe1c 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpCorpGroupServiceImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpCorpGroupServiceImpl.java @@ -18,7 +18,7 @@ * 企业互联相关接口实现类 * * @author libo <422423229@qq.com> - * Created on 27/2/2023 9:57 PM + * @since 2023-02-27 9:57 PM */ @RequiredArgsConstructor public class WxCpCorpGroupServiceImpl implements WxCpCorpGroupService { diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpExternalContactServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpExternalContactServiceImpl.java index 8e3a8d7b95..d43589595f 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpExternalContactServiceImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpExternalContactServiceImpl.java @@ -38,7 +38,7 @@ /** * The type Wx cp external contact service. * - * @author 曹祖鹏 & yuanqixun & Mr.Pan & Wang_Wong + * @author 曹祖鹏, yuanqixun, Mr.Pan, Wang_Wong */ @RequiredArgsConstructor public class WxCpExternalContactServiceImpl implements WxCpExternalContactService { diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpBaseResp.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpBaseResp.java index 6bf9a30aeb..a895c38a8f 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpBaseResp.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpBaseResp.java @@ -10,7 +10,8 @@ /** * 返回结果 * - * @author yqx & WangWong created on 2020/3/16 + * @author yqx, WangWong + * @since 2020/3/16 */ @Getter @Setter diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpAuthInfo.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpAuthInfo.java index fa50216153..9919fd72b8 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpAuthInfo.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpAuthInfo.java @@ -216,7 +216,7 @@ public static class Agent implements Serializable { /** * 付费状态 - *
+ *
*
    *
  • 0-没有付费;
  • *
  • 1-限时试用;
  • diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpPermanentCodeInfo.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpPermanentCodeInfo.java index 522e606a20..5330194abe 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpPermanentCodeInfo.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpPermanentCodeInfo.java @@ -225,7 +225,7 @@ public static class Agent implements Serializable { /** * 付费状态 - *
    + *
    *
      *
    • 0-没有付费;
    • *
    • 1-限时试用;
    • diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpTag.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpTag.java index 74e1fec3f8..a73ec171b4 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpTag.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpTag.java @@ -10,7 +10,7 @@ * The type Wx cp tp tag. * * @author zhangq - * @since 2021 -02-14 16:15 16:15 + * @since 2021-02-14 16:15 */ @Data public class WxCpTpTag implements Serializable { diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpTagAddOrRemoveUsersResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpTagAddOrRemoveUsersResult.java index dfbf250480..565cbb408c 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpTagAddOrRemoveUsersResult.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpTagAddOrRemoveUsersResult.java @@ -6,7 +6,7 @@ * 企业微信第三方开发-增加标签成员成员api响应体 * * @author zhangq - * @since 2021 /2/14 16:44 + * @since 2021/2/14 16:44 */ public class WxCpTpTagAddOrRemoveUsersResult extends WxCpTagAddOrRemoveUsersResult { private static final long serialVersionUID = 3490401800490702052L; diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpTagGetResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpTagGetResult.java index 162030c956..134656e438 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpTagGetResult.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTpTagGetResult.java @@ -6,7 +6,7 @@ * 获取标签成员接口响应体 * * @author zhangq - * @since 2021 /2/14 16:28 + * @since 2021/2/14 16:28 */ public class WxCpTpTagGetResult extends WxCpTagGetResult { private static final long serialVersionUID = 9051748686315562400L; diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpMsgTemplate.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpMsgTemplate.java index 6c546daa83..42e51afbb8 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpMsgTemplate.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpMsgTemplate.java @@ -14,10 +14,9 @@ /** * 企业群发消息任务 - *

      - * Created by songfan on 2020/7/14. * - * @author songfan & Mr.Pan + * @author songfan, Mr.Pan + * @since 2020/7/14 */ @Data @Builder diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalUnassignList.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalUnassignList.java index 8605760fa7..f3fdd96ce7 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalUnassignList.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpUserExternalUnassignList.java @@ -12,7 +12,8 @@ /** * 离职员工外部联系人列表 * - * @author yqx & Wang_Wong created on 2020/3/15 + * @author yqx, Wang_Wong + * @since 2020/3/15 */ @Getter @Setter diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/acquisition/WxCpCustomerAcquisitionStatistic.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/acquisition/WxCpCustomerAcquisitionStatistic.java index bb02b039bd..87e3d5580a 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/acquisition/WxCpCustomerAcquisitionStatistic.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/acquisition/WxCpCustomerAcquisitionStatistic.java @@ -10,7 +10,7 @@ * 获客链接的使用详情 * * @author Hugo - * @date 2023/12/11 10:31 + * @since 2023/12/11 10:31 */ @Data @EqualsAndHashCode(callSuper = true) diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/interceptrule/WxCpInterceptRuleInfo.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/interceptrule/WxCpInterceptRuleInfo.java index 20d6b32442..23bb70a240 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/interceptrule/WxCpInterceptRuleInfo.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/interceptrule/WxCpInterceptRuleInfo.java @@ -10,9 +10,10 @@ import java.util.List; /** - * @Date: 2024-03-07 17:02 - * @Author: shenliuming - * @return: + * 防骚扰规则详情 + * + * @author shenliuming + * @since 2024-03-07 17:02 */ @Data @EqualsAndHashCode(callSuper = true) diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/interceptrule/WxCpInterceptRuleList.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/interceptrule/WxCpInterceptRuleList.java index 6826413e13..543d32fcb9 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/interceptrule/WxCpInterceptRuleList.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/interceptrule/WxCpInterceptRuleList.java @@ -9,9 +9,10 @@ import java.util.List; /** - * @Date: 2024-03-07 15:54 - * @Author: shenliuming - * @return: + * 防骚扰规则列表 + * + * @author shenliuming + * @since 2024-03-07 15:54 */ @Data @EqualsAndHashCode(callSuper = true) diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/msg/Attachment.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/msg/Attachment.java index be9dcc9dd0..1fff457f97 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/msg/Attachment.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/msg/Attachment.java @@ -33,6 +33,7 @@ public class Attachment implements Serializable { * Sets image. * * @param image the image + * @return this */ public Attachment setImage(Image image) { this.image = image; @@ -44,6 +45,7 @@ public Attachment setImage(Image image) { * Sets link. * * @param link the link + * @return this */ public Attachment setLink(Link link) { this.link = link; @@ -55,6 +57,7 @@ public Attachment setLink(Link link) { * Sets mini program. * * @param miniProgram the mini program + * @return this */ public Attachment setMiniProgram(MiniProgram miniProgram) { this.miniProgram = miniProgram; @@ -66,6 +69,7 @@ public Attachment setMiniProgram(MiniProgram miniProgram) { * Sets video. * * @param video the video + * @return this */ public Attachment setVideo(Video video) { this.video = video; @@ -77,6 +81,7 @@ public Attachment setVideo(Video video) { * Sets file. * * @param file the file + * @return this */ public Attachment setFile(File file) { this.file = file; diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfAccountLink.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfAccountLink.java index a903d0fa54..38d25e61cd 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfAccountLink.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/kf/WxCpKfAccountLink.java @@ -27,10 +27,10 @@ public class WxCpKfAccountLink implements Serializable { * 场景值,字符串类型,由开发者自定义。 * 不多于32字节 * 字符串取值范围(正则表达式):[0-9a-zA-Z_-]* - *

      + * * 1. 若scene非空,返回的客服链接开发者可拼接scene_param=SCENE_PARAM参数使用,用户进入会话事件会将SCENE_PARAM原样返回。 * 其中SCENE_PARAM需要urlencode,且长度不能超过128字节。 - * 如 https://work.weixin.qq.com/kf/kfcbf8f8d07ac7215f?enc_scene=ENCGFSDF567DF&scene_param=a%3D1%26b%3D2 + * {@code 如 https://work.weixin.qq.com/kf/kfcbf8f8d07ac7215f?enc_scene=ENCGFSDF567DF&scene_param=a%3D1%26b%3D2} * 2. 历史调用接口返回的客服链接(包含encScene=XXX参数),不支持scene_param参数。 * 3. 返回的客服链接,不能修改或复制参数到其他链接使用。否则进入会话事件参数校验不通过,导致无法回调。 */ diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/media/MediaUploadByUrlReq.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/media/MediaUploadByUrlReq.java index c5cb21bde5..0149a426f6 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/media/MediaUploadByUrlReq.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/media/MediaUploadByUrlReq.java @@ -5,8 +5,9 @@ /** * 生成异步上传任务 + * * @author imyzt - * @date 2025/04/27 + * @since 2025/04/27 */ @Data public class MediaUploadByUrlReq { diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/media/MediaUploadByUrlResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/media/MediaUploadByUrlResult.java index cc931eed39..595f85dffa 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/media/MediaUploadByUrlResult.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/media/MediaUploadByUrlResult.java @@ -10,8 +10,9 @@ /** * 异步上传企微素材 + * * @author imyzt - * @date 2025/4/27 + * @since 2025/4/27 */ @EqualsAndHashCode(callSuper = true) @Data diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpLinkedCorpMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpLinkedCorpMessage.java index 92209fd4e5..7e777384eb 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpLinkedCorpMessage.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpLinkedCorpMessage.java @@ -85,13 +85,13 @@ public class WxCpLinkedCorpMessage implements Serializable { /** *

          * 请使用.
      -   * {@link LinkedCorpMsgType#TEXT}
      -   * {@link LinkedCorpMsgType#IMAGE}
      -   * {@link LinkedCorpMsgType#VIDEO}
      -   * {@link LinkedCorpMsgType#NEWS}
      -   * {@link LinkedCorpMsgType#MPNEWS}
      -   * {@link LinkedCorpMsgType#MARKDOWN}
      -   * {@link LinkedCorpMsgType#MINIPROGRAM_NOTICE}
      +   * {@link me.chanjar.weixin.cp.constant.WxCpConsts.LinkedCorpMsgType#TEXT}
      +   * {@link me.chanjar.weixin.cp.constant.WxCpConsts.LinkedCorpMsgType#IMAGE}
      +   * {@link me.chanjar.weixin.cp.constant.WxCpConsts.LinkedCorpMsgType#VIDEO}
      +   * {@link me.chanjar.weixin.cp.constant.WxCpConsts.LinkedCorpMsgType#NEWS}
      +   * {@link me.chanjar.weixin.cp.constant.WxCpConsts.LinkedCorpMsgType#MPNEWS}
      +   * {@link me.chanjar.weixin.cp.constant.WxCpConsts.LinkedCorpMsgType#MARKDOWN}
      +   * {@link me.chanjar.weixin.cp.constant.WxCpConsts.LinkedCorpMsgType#MINIPROGRAM_NOTICE}
          * 
      * * @param msgType 消息类型 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 4001c7d0e4..08a0936317 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 @@ -68,19 +68,19 @@ public class WxCpXmlMessage implements Serializable { /** *
          * 当接受用户消息时,可能会获得以下值:
      -   * {@link WxConsts.XmlMsgType#TEXT}
      -   * {@link WxConsts.XmlMsgType#IMAGE}
      -   * {@link WxConsts.XmlMsgType#VOICE}
      -   * {@link WxConsts.XmlMsgType#VIDEO}
      -   * {@link WxConsts.XmlMsgType#LOCATION}
      -   * {@link WxConsts.XmlMsgType#LINK}
      -   * {@link WxConsts.XmlMsgType#EVENT}
      +   * {@link me.chanjar.weixin.common.api.WxConsts.XmlMsgType#TEXT}
      +   * {@link me.chanjar.weixin.common.api.WxConsts.XmlMsgType#IMAGE}
      +   * {@link me.chanjar.weixin.common.api.WxConsts.XmlMsgType#VOICE}
      +   * {@link me.chanjar.weixin.common.api.WxConsts.XmlMsgType#VIDEO}
      +   * {@link me.chanjar.weixin.common.api.WxConsts.XmlMsgType#LOCATION}
      +   * {@link me.chanjar.weixin.common.api.WxConsts.XmlMsgType#LINK}
      +   * {@link me.chanjar.weixin.common.api.WxConsts.XmlMsgType#EVENT}
          * 当发送消息的时候使用:
      -   * {@link WxConsts.XmlMsgType#TEXT}
      -   * {@link WxConsts.XmlMsgType#IMAGE}
      -   * {@link WxConsts.XmlMsgType#VOICE}
      -   * {@link WxConsts.XmlMsgType#VIDEO}
      -   * {@link WxConsts.XmlMsgType#NEWS}
      +   * {@link me.chanjar.weixin.common.api.WxConsts.XmlMsgType#TEXT}
      +   * {@link me.chanjar.weixin.common.api.WxConsts.XmlMsgType#IMAGE}
      +   * {@link me.chanjar.weixin.common.api.WxConsts.XmlMsgType#VOICE}
      +   * {@link me.chanjar.weixin.common.api.WxConsts.XmlMsgType#VIDEO}
      +   * {@link me.chanjar.weixin.common.api.WxConsts.XmlMsgType#NEWS}
          * 
      */ @XStreamAlias("MsgType") diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/TemplateCardBuilder.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/TemplateCardBuilder.java index d3cbb89a3d..1ab92630a9 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/TemplateCardBuilder.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/TemplateCardBuilder.java @@ -12,7 +12,8 @@ * 用法: WxCustomMessage m = WxCustomMessage.TEMPLATECARD().title(...)....toUser(...).build(); * * - * @author yzts created on 2019-05-16 + * @author yzts + * @since 2019-05-16 */ public class TemplateCardBuilder extends BaseBuilder { /** diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpCheckinGroupBase.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpCheckinGroupBase.java index 3bc542ccd0..81e5dcc329 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpCheckinGroupBase.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpCheckinGroupBase.java @@ -10,8 +10,8 @@ * 打卡规则基础信息 * * @author zhongjun96 - * @date 2023/7/7 - **/ + * @since 2023/7/7 + */ @Data public class WxCpCheckinGroupBase implements Serializable { 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 20b1f45e20..9836a3291a 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 @@ -157,7 +157,7 @@ public static class Reminder implements Serializable { /** * 提醒时间与日程开始时间(start_time)的差值,当is_remind为1时有效。例如:-300表示日程开始前5分钟提醒。 * 特殊情况:企业微信终端设置的“全天”类型的日程,由于start_time是0点时间戳,提醒如果设置了当天9点,则会出现正数32400。 - *
      + *
      * 取值范围:-604800 ~ 86399 */ @SerializedName("remind_time_diffs") 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 index 16cf32fa5c..4f74bab79c 100644 --- 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 @@ -39,7 +39,7 @@ public static WxCpOaMeetingRoomBookResult fromJson(String json) { private String schedule_id; /** * 通过会议预定会议室 和 通过日程预定会议室 接口返回 - *
      + *
      * 会议室冲突日期列表,为当天0点的时间戳;使用重复会议预定会议室,部分日期与会议室预定情况冲突时返回 */ @SerializedName("conflict_date") diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTips.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTips.java index 58daeb007c..eada2ad100 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTips.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTips.java @@ -8,7 +8,7 @@ /** * @author mrsiu@msn.com * @version 1.0 - * @date 2025/1/16 09:40 + * @since 2025/1/16 09:40 */ @Data public class TemplateTips { diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTipsContent.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTipsContent.java index 939e6819a0..0af8be8be5 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTipsContent.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTipsContent.java @@ -5,7 +5,7 @@ /** * @author mrsiu@msn.com * @version 1.0 - * @date 2025/1/16 09:42 + * @since 2025/1/16 09:42 */ @Data public class TemplateTipsContent { diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTipsSubText.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTipsSubText.java index ac4681038c..78e88562e8 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTipsSubText.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTipsSubText.java @@ -5,7 +5,7 @@ /** * @author mrsiu@msn.com * @version 1.0 - * @date 2025/1/16 09:45 + * @since 2025/1/16 09:45 */ @Data public class TemplateTipsSubText { diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTipsSubTextContent.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTipsSubTextContent.java index 9c99b2688e..f33c31d054 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTipsSubTextContent.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTipsSubTextContent.java @@ -6,7 +6,7 @@ /** * @author mrsiu@msn.com * @version 1.0 - * @date 2025/1/16 09:46 + * @since 2025/1/16 09:46 */ @Data public class TemplateTipsSubTextContent { diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTipsSubTextContentLink.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTipsSubTextContentLink.java index 4cd198409a..fd4db2c18f 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTipsSubTextContentLink.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTipsSubTextContentLink.java @@ -5,7 +5,7 @@ /** * @author mrsiu@msn.com * @version 1.0 - * @date 2025/1/16 09:49 + * @since 2025/1/16 09:49 */ @Data public class TemplateTipsSubTextContentLink { diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTipsSubTextContentPlainText.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTipsSubTextContentPlainText.java index 12969cdcdb..0a2e792edb 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTipsSubTextContentPlainText.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTipsSubTextContentPlainText.java @@ -3,9 +3,9 @@ import lombok.Data; /** - * @author mrsiu@msn.com - * @date 2025/1/16 09:47 - * @version 1.0 + * @author mrsiu@msn.com + * @version 1.0 + * @since 2025/1/16 09:47 */ @Data public class TemplateTipsSubTextContentPlainText { diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTipsText.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTipsText.java index 100c5bb137..c9dbe68abf 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTipsText.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTipsText.java @@ -6,9 +6,9 @@ import java.util.List; /** - * @author mrsiu@msn.com - * @date 2025/1/16 09:43 - * @version 1.0 + * @author mrsiu@msn.com + * @version 1.0 + * @since 2025/1/16 09:43 */ @Data public class TemplateTipsText { diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpCorpGroupConfigStorage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpCorpGroupConfigStorage.java index 07acb189a8..df758ac3a2 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpCorpGroupConfigStorage.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpCorpGroupConfigStorage.java @@ -31,8 +31,8 @@ public interface WxCpCorpGroupConfigStorage { /** * Update corp access token. * - * @param corpId - * @param agentId + * @param corpId 企业ID + * @param agentId 应用ID * @param corpAccessToken the corp access token * @param expiresInSeconds the expires in seconds */ @@ -41,8 +41,8 @@ public interface WxCpCorpGroupConfigStorage { /** * 授权企业的access token相关 * - * @param corpId the corp id - * @param agentId + * @param corpId 企业ID + * @param agentId 应用ID * @return the access token */ String getCorpAccessToken(String corpId, Integer agentId); @@ -50,8 +50,8 @@ public interface WxCpCorpGroupConfigStorage { /** * Gets access token entity. * - * @param corpId the corp id - * @param agentId + * @param corpId 企业ID + * @param agentId 应用ID * @return the access token entity */ WxAccessToken getCorpAccessTokenEntity(String corpId, Integer agentId); @@ -59,8 +59,8 @@ public interface WxCpCorpGroupConfigStorage { /** * Is access token expired boolean. * - * @param corpId the corp id - * @param agentId + * @param corpId 企业ID + * @param agentId 应用ID * @return the boolean */ boolean isCorpAccessTokenExpired(String corpId, Integer agentId); @@ -68,8 +68,8 @@ public interface WxCpCorpGroupConfigStorage { /** * Expire access token. * - * @param corpId the corp id - * @param agentId + * @param corpId 企业ID + * @param agentId 应用ID */ void expireCorpAccessToken(String corpId, Integer agentId); @@ -118,7 +118,8 @@ public interface WxCpCorpGroupConfigStorage { /** * Gets access token lock. * - * @param corpId the corp id + * @param corpId 企业ID + * @param agentId 应用ID * @return the access token lock */ Lock getCorpAccessTokenLock(String corpId, Integer agentId); diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpTpConfigStorage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpTpConfigStorage.java index 2cd37821b5..0a2ed0e76a 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpTpConfigStorage.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpTpConfigStorage.java @@ -133,7 +133,7 @@ public interface WxCpTpConfigStorage { String getEncodingAESKey(); /** - * 企微服务商企业ID & 企业secret + * {@code 企微服务商企业ID & 企业secret} * * @return the corp id */ diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/AbstractWxCpTpInRedisConfigImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/AbstractWxCpTpInRedisConfigImpl.java index 08eed33c16..6d1260fe68 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/AbstractWxCpTpInRedisConfigImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/AbstractWxCpTpInRedisConfigImpl.java @@ -187,7 +187,9 @@ public String getEncodingAESKey() { /** - * 企微服务商企业ID & 企业secret, 来自于企微配置 + * {@code 企微服务商企业ID & 企业secret, 来自于企微配置} + * + * @return 企业ID */ @Override public String getCorpId() { diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpTpDefaultConfigImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpTpDefaultConfigImpl.java index fc124251f5..7fbbc9ccce 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpTpDefaultConfigImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpTpDefaultConfigImpl.java @@ -29,7 +29,7 @@ public class WxCpTpDefaultConfigImpl implements WxCpTpConfigStorage, Serializabl private final transient Map authCorpJsapiTicketLocker = new ConcurrentHashMap<>(); private final transient Map authSuiteJsapiTicketLocker = new ConcurrentHashMap<>(); /** - * 企微服务商企业ID & 企业secret,来自于企微配置 + * {@code 企微服务商企业ID & 企业secret,来自于企微配置} */ protected volatile String corpId; /** @@ -198,7 +198,7 @@ public String getSuiteId() { /** * Sets suite id. * - * @param suiteId + * @param suiteId 套件ID */ public void setSuiteId(String suiteId) { this.suiteId = suiteId; @@ -211,6 +211,8 @@ public String getSuiteSecret() { /** * Sets suite secret. + * + * @param corpSecret 套件密钥 */ public void setSuiteSecret(String corpSecret) { this.suiteSecret = corpSecret; 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 ff3f8e0e6c..df85249013 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 @@ -124,7 +124,7 @@ public static class EventType { public static final String REOPEN_INACTIVE_AGENT = "reopen_inactive_agent"; /** - * 企业成员添加外部联系人事件推送 & 会话存档客户同意进行聊天内容存档事件回调事件 + * {@code 企业成员添加外部联系人事件推送 & 会话存档客户同意进行聊天内容存档事件回调事件} */ public static final String CHANGE_EXTERNAL_CONTACT = "change_external_contact"; diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/corpgroup/service/WxCpCgService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/corpgroup/service/WxCpCgService.java index b9b2c5d774..f94c14a4f1 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/corpgroup/service/WxCpCgService.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/corpgroup/service/WxCpCgService.java @@ -33,28 +33,31 @@ public interface WxCpCgService { /** * 授权企业的access token相关 * - * @param corpId the corp id - * @param agentId - * @param businessType + * @param corpId 企业ID + * @param agentId 应用ID + * @param businessType 业务类型 * @return the access token + * @throws WxErrorException 微信错误异常 */ WxAccessToken getCorpAccessTokenEntity(String corpId, Integer agentId, Integer businessType) throws WxErrorException; /** * Gets access token entity. * - * @param corpId the corp id - * @param agentId - * @param businessType + * @param corpId 企业ID + * @param agentId 应用ID + * @param businessType 业务类型 + * @param forceRefresh 是否强制刷新 * @return the access token entity + * @throws WxErrorException 微信错误异常 */ WxAccessToken getCorpAccessTokenEntity(String corpId, Integer agentId, Integer businessType, boolean forceRefresh) throws WxErrorException; /** * Is access token expired boolean. * - * @param corpId the corp id - * @param agentId + * @param corpId 企业ID + * @param agentId 应用ID * @return the boolean */ boolean isCorpAccessTokenExpired(String corpId, Integer agentId); @@ -62,8 +65,8 @@ public interface WxCpCgService { /** * Expire access token. * - * @param corpId the corp id - * @param agentId + * @param corpId 企业ID + * @param agentId 应用ID */ void expireCorpAccessToken(String corpId, Integer agentId); @@ -72,6 +75,7 @@ public interface WxCpCgService { * * @param url 接口地址 * @param queryParam 请求参数 + * @param req 获取token请求参数 * @return the string * @throws WxErrorException the wx error exception */ @@ -83,6 +87,7 @@ public interface WxCpCgService { * @param url 接口地址 * @param queryParam 请求参数 * @param withoutCorpAccessToken 请求是否忽略CorpAccessToken 默认不忽略-false + * @param req 获取token请求参数 * @return the string * @throws WxErrorException the wx error exception */ @@ -93,6 +98,7 @@ public interface WxCpCgService { * * @param url 接口地址 * @param postData 请求body字符串 + * @param req 获取token请求参数 * @return the string * @throws WxErrorException the wx error exception */ @@ -110,6 +116,7 @@ public interface WxCpCgService { * @param executor 执行器 * @param uri 请求地址 * @param data 参数 + * @param req 获取token请求参数 * @return the t * @throws WxErrorException the wx error exception */ @@ -156,7 +163,7 @@ public interface WxCpCgService { /** * 互联企业的服务类对象 * - * @return + * @return 互联企业服务对象 */ WxCpLinkedCorpService getLinkedCorpService(); @@ -164,10 +171,11 @@ public interface WxCpCgService { * 获取下级/下游企业小程序session * https://developer.work.weixin.qq.com/document/path/93355 * - * @param userId - * @param sessionKey - * @return - * @throws WxErrorException + * @param userId 用户ID + * @param sessionKey 会话密钥 + * @param req 请求参数 + * @return 小程序session结果 + * @throws WxErrorException 微信错误异常 */ WxCpMaTransferSession getCorpTransferSession(String userId, String sessionKey, WxCpCorpGroupCorpGetTokenReq req) throws WxErrorException; } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/corpgroup/service/impl/BaseWxCpCgServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/corpgroup/service/impl/BaseWxCpCgServiceImpl.java index 9991073739..e4fe2a686a 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/corpgroup/service/impl/BaseWxCpCgServiceImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/corpgroup/service/impl/BaseWxCpCgServiceImpl.java @@ -112,6 +112,7 @@ public String post(String url, String postData, WxCpCorpGroupCorpGetTokenReq req * @param url the url * @param postData the post data * @param withoutCorpAccessToken the without Corp access token + * @param req 获取token请求参数 * @return the string * @throws WxErrorException the wx error exception */ @@ -136,6 +137,7 @@ public T execute(RequestExecutor executor, String uri, E data, WxCp * @param uri the uri * @param data the data * @param withoutCorpAccessToken the without Corp access token + * @param req 获取token请求参数 * @return the t * @throws WxErrorException the wx error exception */ @@ -181,6 +183,7 @@ public T execute(RequestExecutor executor, String uri, E data, bool * @param executor the executor * @param uri the uri * @param data the data + * @param req 获取token请求参数 * @return the t * @throws WxErrorException the wx error exception */ @@ -197,6 +200,7 @@ protected T executeInternal(RequestExecutor executor, String uri, E * @param uri the uri * @param data the data * @param withoutCorpAccessToken the without Corp access token + * @param req 获取token请求参数 * @return the t * @throws WxErrorException the wx error exception */ diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/WxCpTpEditionService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/WxCpTpEditionService.java index 2c2f11628b..d539f03bca 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/WxCpTpEditionService.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/WxCpTpEditionService.java @@ -15,7 +15,7 @@ public interface WxCpTpEditionService { * 延长试用期 *

      * 文档地址 - *

      + *

      * 注意: *
        *
      • 一个应用可以多次延长试用,但是试用总天数不能超过60天
      • diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/WxCpTpIdConvertService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/WxCpTpIdConvertService.java index 10268bcb31..83dd6fe36b 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/WxCpTpIdConvertService.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/WxCpTpIdConvertService.java @@ -20,9 +20,11 @@ public interface WxCpTpIdConvertService { * unionid与external_userid的关联 * 查看文档 * + * @param cropId 企业ID * @param unionid 微信客户的unionid * @param openid 微信客户的openid * @param subjectType 程序或公众号的主体类型: 0表示主体名称是企业的,1表示主体名称是服务商的 + * @return 转换结果 * @throws WxErrorException 。 */ WxCpTpUnionidToExternalUseridResult unionidToExternalUserid(String cropId, String unionid, String openid, @@ -31,29 +33,33 @@ WxCpTpUnionidToExternalUseridResult unionidToExternalUserid(String cropId, Strin /** * 将企业主体下的客户标签ID转换成服务商主体下的客户标签ID - * @param corpId 企业微信 ID - * @param externalTagIdList 企业主体下的客户标签ID列表,最多不超过1000个 - * @return 客户标签转换结果 + * + * @param corpId 企业微信 ID + * @param externalTagIdList 企业主体下的客户标签ID列表,最多不超过1000个 + * @return 客户标签转换结果 * @throws WxErrorException . */ WxCpTpTagIdListConvertResult externalTagId(String corpId, String... externalTagIdList) throws WxErrorException; /** * 将企业主体下的微信客服ID转换成服务商主体下的微信客服ID - * @param corpId 企业微信 ID - * @param openKfIdList 微信客服ID列表,最多不超过1000个 - * @return 微信客服ID转换结果 - * @throws WxErrorException . + * + * @param corpId 企业微信 ID + * @param openKfIdList 微信客服ID列表,最多不超过1000个 + * @return 微信客服ID转换结果 + * @throws WxErrorException . */ - WxCpTpOpenKfIdConvertResult ConvertOpenKfId (String corpId, String... openKfIdList ) throws WxErrorException; + WxCpTpOpenKfIdConvertResult ConvertOpenKfId(String corpId, String... openKfIdList) throws WxErrorException; /** * 将应用获取的外部用户临时idtmp_external_userid,转换为external_userid - * @param corpId 企业微信Id - * @param businessType 业务类型。1-会议 2-收集表 - * @param userType 转换的目标用户类型。1-客户 2-企业互联 3-上下游 4-互联企业(圈子) - * @param tmpExternalUserIdList 外部用户临时id,最多不超过100个 - * @return 转换成功的结果列表 + * + * @param corpId 企业微信Id + * @param businessType 业务类型。1-会议 2-收集表 + * @param userType 转换的目标用户类型。1-客户 2-企业互联 3-上下游 4-互联企业(圈子) + * @param tmpExternalUserIdList 外部用户临时id,最多不超过100个 + * @return 转换成功的结果列表 + * @throws WxErrorException . */ WxCpTpConvertTmpExternalUserIdResult convertTmpExternalUserId(String corpId, int businessType, int userType, String... tmpExternalUserIdList) throws WxErrorException; } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/WxCpTpOAService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/WxCpTpOAService.java index 85321213ab..4aed96b7b5 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/WxCpTpOAService.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/WxCpTpOAService.java @@ -51,8 +51,7 @@ public interface WxCpTpOAService { String copyTemplate(@NonNull String openTemplateId, String corpId) throws WxErrorException; /** - *
        -   *   获取审批申请详情
        +   * 获取审批申请详情
            *
            * @param spNo 审批单编号。
            * @param corpId the corp id
        diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/WxCpTpOrderService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/WxCpTpOrderService.java
        index 3aff90bb56..6e0acb7dee 100644
        --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/WxCpTpOrderService.java
        +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/WxCpTpOrderService.java
        @@ -18,7 +18,7 @@ public interface WxCpTpOrderService {
            * 获取订单详情
            * 

        * 文档地址 - *

        + *

        * * @param orderId 订单号 * @return the order @@ -31,7 +31,7 @@ public interface WxCpTpOrderService { * 获取订单列表 *

        * 文档地址 - *

        + *

        * * @param startTime 起始时间 * @param endTime 终止时间 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 b24be535da..93855d1a48 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 @@ -199,7 +199,7 @@ public interface WxCpTpService { * @return permanent code info * @throws WxErrorException the wx error exception * @author yuan - * @since 2020 -03-18 + * @since 2020-03-18 */ WxCpTpPermanentCodeInfo getPermanentCodeInfo(String authCode) throws WxErrorException; @@ -227,7 +227,7 @@ public interface WxCpTpService { * @param authType 授权类型:0 正式授权, 1 测试授权。 * @return pre auth url * @throws WxErrorException the wx error exception - * @link https ://work.weixin.qq.com/api/doc/90001/90143/90602 + * @see 文档地址 */ String getPreAuthUrl(String redirectUri, String state, int authType) throws WxErrorException; @@ -558,12 +558,12 @@ WxCpTpXmlMessage fromEncryptedXml(String encryptedXml, WxCpTpAppQrcode getAppQrcode(String suiteId, String appId, String state, Integer style, Integer resultType) throws WxErrorException ; /** - * * 明文corpid转换为加密corpid 为更好地保护企业与用户的数据,第三方应用获取的corpid不再是明文的corpid,将升级为第三方服务商级别的加密corpid。文档说明 * 第三方可以将已有的明文corpid转换为第三方的加密corpid。 - * @param corpId - * @return - * @throws WxErrorException + * + * @param corpId 企业ID + * @return 加密的企业ID + * @throws WxErrorException 微信错误异常 */ WxCpTpCorpId2OpenCorpId corpId2OpenCorpId(String corpId) throws WxErrorException; @@ -655,6 +655,8 @@ WxCpTpXmlMessage fromEncryptedXml(String encryptedXml, /** * 构造第三方应用oauth2链接 + * + * @return OAuth2服务 */ WxCpTpOAuth2Service getWxCpTpOAuth2Service(); diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/WxCpTpTagService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/WxCpTpTagService.java index b508df59a1..df60041865 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/WxCpTpTagService.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/WxCpTpTagService.java @@ -13,7 +13,7 @@ *
        * * @author zhangq - * @since 2021 -02-14 16:02 + * @since 2021-02-14 16:02 */ public interface WxCpTpTagService { /** diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/impl/WxCpTpEditionServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/impl/WxCpTpEditionServiceImpl.java index 34ca852c3f..b77fb924c2 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/impl/WxCpTpEditionServiceImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/impl/WxCpTpEditionServiceImpl.java @@ -26,7 +26,7 @@ public class WxCpTpEditionServiceImpl implements WxCpTpEditionService { * 延长试用期 *

        * 文档地址 - *

        + *

        *
          *
        • 一个应用可以多次延长试用,但是试用总天数不能超过60天
        • *
        • 仅限时试用或试用过期状态下的应用可以延长试用期
        • diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/impl/WxCpTpOrderServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/impl/WxCpTpOrderServiceImpl.java index b8aff54d39..7b2d45b5c6 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/impl/WxCpTpOrderServiceImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/impl/WxCpTpOrderServiceImpl.java @@ -30,7 +30,7 @@ public class WxCpTpOrderServiceImpl implements WxCpTpOrderService { * 获取订单详情 *

          * 文档地址 - *

          + *

          * * @param orderId 订单号 * @return the order @@ -49,7 +49,7 @@ public WxCpTpOrderDetails getOrder(String orderId) throws WxErrorException { * 获取订单列表 *

          * 文档地址 - *

          + *

          * * @param startTime 起始时间 * @param endTime 终止时间 diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/impl/WxCpTpTagServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/impl/WxCpTpTagServiceImpl.java index b81760e72c..1b03f18c79 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/impl/WxCpTpTagServiceImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/tp/service/impl/WxCpTpTagServiceImpl.java @@ -25,7 +25,7 @@ * * * @author zhangq - * @since 2021 -02-14 16:02 + * @since 2021-02-14 16:02 */ @RequiredArgsConstructor public class WxCpTpTagServiceImpl implements WxCpTpTagService { 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 098a781c64..09c6ce8287 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 @@ -20,7 +20,7 @@ public class XStreamTransformer { protected static final Map, XStream> CLASS_2_XSTREAM_INSTANCE = configXStreamInstance(); /** - * xml -> pojo + * {@code xml -> pojo} * * @param the type parameter * @param clazz the clazz @@ -58,7 +58,7 @@ public static void register(Class clz, XStream xStream) { } /** - * pojo -> xml. + * {@code pojo -> xml.} * * @param the type parameter * @param clazz the clazz diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/WxMaConfig.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/WxMaConfig.java index 8164d48346..e39836c061 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/WxMaConfig.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/WxMaConfig.java @@ -13,6 +13,11 @@ */ public interface WxMaConfig { + /** + * 设置更新access_token之前的回调 + * + * @param updateAccessTokenBefore 回调函数 + */ default void setUpdateAccessTokenBefore(Consumer updateAccessTokenBefore) {} /** @@ -23,8 +28,18 @@ default void setUpdateAccessTokenBefore(Consumer updateAcce String getAccessToken(); // region 稳定版access token + /** + * 是否使用稳定版access_token + * + * @return 是否使用稳定版access_token + */ boolean isStableAccessToken(); + /** + * 设置是否使用稳定版access_token + * + * @param useStableAccessToken 是否使用稳定版access_token + */ void useStableAccessToken(boolean useStableAccessToken); // endregion @@ -65,6 +80,12 @@ default void updateAccessToken(WxAccessToken accessToken) { */ void updateAccessToken(String accessToken, int expiresInSeconds); + /** + * 更新access_token处理器 + * + * @param accessToken 新的 access_token 值 + * @param expiresInSeconds 过期时间,单位:秒 + */ default void updateAccessTokenProcessor(String accessToken, int expiresInSeconds) { WxAccessTokenEntity wxAccessTokenEntity = new WxAccessTokenEntity(); wxAccessTokenEntity.setAppid(getAppid()); @@ -74,6 +95,11 @@ default void updateAccessTokenProcessor(String accessToken, int expiresInSeconds updateAccessToken(accessToken, expiresInSeconds); } + /** + * 更新access_token之前的回调 + * + * @param wxAccessTokenEntity access_token实体 + */ default void updateAccessTokenBefore(WxAccessTokenEntity wxAccessTokenEntity) {} /** diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/crypt/WxMaCryptUtils.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/crypt/WxMaCryptUtils.java index 08346dbbb8..2343634bfc 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/crypt/WxMaCryptUtils.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/crypt/WxMaCryptUtils.java @@ -36,6 +36,7 @@ public WxMaCryptUtils(WxMaConfig config) { * @param sessionKey session_key * @param encryptedData 消息密文 * @param ivStr iv字符串 + * @return 解密后的字符串 */ public static String decrypt(String sessionKey, String encryptedData, String ivStr) { try { @@ -58,6 +59,7 @@ public static String decrypt(String sessionKey, String encryptedData, String ivS * @param sessionKey session_key * @param encryptedData 消息密文 * @param ivStr iv字符串 + * @return 解密后的字符串 */ public static String decryptAnotherWay(String sessionKey, String encryptedData, String ivStr) { byte[] keyBytes = Base64.decodeBase64(sessionKey.getBytes(UTF_8)); diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/xml/XStreamTransformer.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/xml/XStreamTransformer.java index f36d8c8fbd..b9e80d7341 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/xml/XStreamTransformer.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/xml/XStreamTransformer.java @@ -24,7 +24,12 @@ public class XStreamTransformer { } /** - * xml -> pojo. + * {@code xml -> pojo.} + * + * @param 返回类型 + * @param clazz 类型 + * @param xml xml字符串 + * @return 转换后的对象 */ @SuppressWarnings("unchecked") public static T fromXml(Class clazz, String xml) { @@ -32,6 +37,14 @@ public static T fromXml(Class clazz, String xml) { return object; } + /** + * {@code xml -> pojo.} + * + * @param 返回类型 + * @param clazz 类型 + * @param is 输入流 + * @return 转换后的对象 + */ @SuppressWarnings("unchecked") public static T fromXml(Class clazz, InputStream is) { T object = (T) CLASS_2_XSTREAM_INSTANCE.get(clazz).fromXML(is); @@ -39,7 +52,12 @@ public static T fromXml(Class clazz, InputStream is) { } /** - * pojo -> xml. + * {@code pojo -> xml.} + * + * @param 类型参数 + * @param clazz 类型 + * @param object 对象 + * @return xml字符串 */ public static String toXml(Class clazz, T object) { return CLASS_2_XSTREAM_INSTANCE.get(clazz).toXML(object); diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpCardService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpCardService.java index 188e4be78b..ddce68e765 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpCardService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpCardService.java @@ -255,11 +255,9 @@ public interface WxMpCardService { String addTestWhiteList(String openid) throws WxErrorException; /** - *
                * 创建卡券
          -     * 
          * - * @param cardCreateRequest 卡券创建请求对象 + * @param cardCreateMessage 卡券创建请求对象 * @return 卡券创建结果对象 * @throws WxErrorException 微信API调用异常,可能包括: *
            diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpCommentService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpCommentService.java index 25463061fe..738c1ea656 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpCommentService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpCommentService.java @@ -37,8 +37,8 @@ public interface WxMpCommentService { * @param msgDataId 群发返回的msg_data_id * @param index 多图文时,用来指定第几篇图文,从0开始,不带默认操作该msg_data_id的第一篇图文 * @param begin 起始位置 - * @param count 获取数目(>=50会被拒绝) - * @param type type=0 普通评论&精选评论 type=1 普通评论 type=2 精选评论 + * @param count 获取数目(大于等于50会被拒绝) + * @param type type=0 普通评论和精选评论 type=1 普通评论 type=2 精选评论 * @return 评论列表数据 wx mp comment list vo * @throws WxErrorException 异常 */ diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpDataCubeService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpDataCubeService.java index d107444e21..83dd2342ec 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpDataCubeService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpDataCubeService.java @@ -60,10 +60,13 @@ public interface WxMpDataCubeService { List getArticleSummary(Date beginDate, Date endDate) throws WxErrorException; /** - *
                  * 获取图文群发总数据(getarticletotal)
            -     * 详情请见文档:图文分析数据接口
            +     * 

            + * {@code 详情请见文档:图文分析数据接口} + *

            + *

            * 接口url格式:https://api.weixin.qq.com/datacube/getarticletotal?access_token=ACCESS_TOKEN + *

            * * @param beginDate 开始时间 * @param endDate 最大时间跨度1天,endDate不能早于begingDate @@ -73,10 +76,13 @@ public interface WxMpDataCubeService { List getArticleTotal(Date beginDate, Date endDate) throws WxErrorException; /** - *
                  * 获取图文统计数据(getuserread)
            -     * 详情请见文档:图文分析数据接口
            +     * 

            + * {@code 详情请见文档:图文分析数据接口} + *

            + *

            * 接口url格式:https://api.weixin.qq.com/datacube/getuserread?access_token=ACCESS_TOKEN + *

            * * @param beginDate 开始时间 * @param endDate 最大时间跨度3天,endDate不能早于begingDate @@ -86,10 +92,13 @@ public interface WxMpDataCubeService { List getUserRead(Date beginDate, Date endDate) throws WxErrorException; /** - *
                  * 获取图文统计分时数据(getuserreadhour)
            -     * 详情请见文档:图文分析数据接口
            +     * 

            + * {@code 详情请见文档:图文分析数据接口} + *

            + *

            * 接口url格式:https://api.weixin.qq.com/datacube/getuserreadhour?access_token=ACCESS_TOKEN + *

            * * @param beginDate 开始时间 * @param endDate 最大时间跨度1天,endDate不能早于begingDate @@ -99,10 +108,13 @@ public interface WxMpDataCubeService { List getUserReadHour(Date beginDate, Date endDate) throws WxErrorException; /** - *
                  * 获取图文分享转发数据(getusershare)
            -     * 详情请见文档:图文分析数据接口
            +     * 

            + * {@code 详情请见文档:图文分析数据接口} + *

            + *

            * 接口url格式:https://api.weixin.qq.com/datacube/getusershare?access_token=ACCESS_TOKEN + *

            * * @param beginDate 开始时间 * @param endDate 最大时间跨度7天,endDate不能早于begingDate @@ -112,10 +124,13 @@ public interface WxMpDataCubeService { List getUserShare(Date beginDate, Date endDate) throws WxErrorException; /** - *
                  * 获取图文分享转发分时数据(getusersharehour)
            -     * 详情请见文档:图文分析数据接口
            +     * 

            + * {@code 详情请见文档:图文分析数据接口} + *

            + *

            * 接口url格式:https://api.weixin.qq.com/datacube/getusersharehour?access_token=ACCESS_TOKEN + *

            * * @param beginDate 开始时间 * @param endDate 最大时间跨度1天,endDate不能早于begingDate @@ -127,10 +142,13 @@ public interface WxMpDataCubeService { //*******************消息分析数据接口***********************// /** - *
                  * 获取消息发送概况数据(getupstreammsg)
            -     * 详情请见文档:消息分析数据接口
            +     * 

            + * {@code 详情请见文档:消息分析数据接口} + *

            + *

            * 接口url格式:https://api.weixin.qq.com/datacube/getupstreammsg?access_token=ACCESS_TOKEN + *

            * * @param beginDate 开始时间 * @param endDate 最大时间跨度7天,endDate不能早于begingDate @@ -140,10 +158,13 @@ public interface WxMpDataCubeService { List getUpstreamMsg(Date beginDate, Date endDate) throws WxErrorException; /** - *
                  * 获取消息分送分时数据(getupstreammsghour)
            -     * 详情请见文档:消息分析数据接口
            +     * 

            + * {@code 详情请见文档:消息分析数据接口} + *

            + *

            * 接口url格式:https://api.weixin.qq.com/datacube/getupstreammsghour?access_token=ACCESS_TOKEN + *

            * * @param beginDate 开始时间 * @param endDate 最大时间跨度1天,endDate不能早于begingDate @@ -153,10 +174,13 @@ public interface WxMpDataCubeService { List getUpstreamMsgHour(Date beginDate, Date endDate) throws WxErrorException; /** - *
                  * 获取消息发送周数据(getupstreammsgweek)
            -     * 详情请见文档:消息分析数据接口
            +     * 

            + * {@code 详情请见文档:消息分析数据接口} + *

            + *

            * 接口url格式:https://api.weixin.qq.com/datacube/getupstreammsgweek?access_token=ACCESS_TOKEN + *

            * * @param beginDate 开始时间 * @param endDate 最大时间跨度30天,endDate不能早于begingDate @@ -166,10 +190,13 @@ public interface WxMpDataCubeService { List getUpstreamMsgWeek(Date beginDate, Date endDate) throws WxErrorException; /** - *
                  * 获取消息发送月数据(getupstreammsgmonth)
            -     * 详情请见文档:消息分析数据接口
            +     * 

            + * {@code 详情请见文档:消息分析数据接口} + *

            + *

            * 接口url格式:https://api.weixin.qq.com/datacube/getupstreammsgmonth?access_token=ACCESS_TOKEN + *

            * * @param beginDate 开始时间 * @param endDate 最大时间跨度30天,endDate不能早于begingDate @@ -179,10 +206,13 @@ public interface WxMpDataCubeService { List getUpstreamMsgMonth(Date beginDate, Date endDate) throws WxErrorException; /** - *
                  * 获取消息发送分布数据(getupstreammsgdist)
            -     * 详情请见文档:消息分析数据接口
            +     * 

            + * {@code 详情请见文档:消息分析数据接口} + *

            + *

            * 接口url格式:https://api.weixin.qq.com/datacube/getupstreammsgdist?access_token=ACCESS_TOKEN + *

            * * @param beginDate 开始时间 * @param endDate 最大时间跨度15天,endDate不能早于begingDate @@ -192,10 +222,13 @@ public interface WxMpDataCubeService { List getUpstreamMsgDist(Date beginDate, Date endDate) throws WxErrorException; /** - *
                  * 获取消息发送分布周数据(getupstreammsgdistweek)
            -     * 详情请见文档:消息分析数据接口
            +     * 

            + * {@code 详情请见文档:消息分析数据接口} + *

            + *

            * 接口url格式:https://api.weixin.qq.com/datacube/getupstreammsgdistweek?access_token=ACCESS_TOKEN + *

            * * @param beginDate 开始时间 * @param endDate 最大时间跨度30天,endDate不能早于begingDate @@ -205,10 +238,13 @@ public interface WxMpDataCubeService { List getUpstreamMsgDistWeek(Date beginDate, Date endDate) throws WxErrorException; /** - *
                  * 获取消息发送分布月数据(getupstreammsgdistmonth)
            -     * 详情请见文档:消息分析数据接口
            +     * 

            + * {@code 详情请见文档:消息分析数据接口} + *

            + *

            * 接口url格式:https://api.weixin.qq.com/datacube/getupstreammsgdistmonth?access_token=ACCESS_TOKEN + *

            * * @param beginDate 开始时间 * @param endDate 最大时间跨度30天,endDate不能早于begingDate @@ -220,10 +256,13 @@ public interface WxMpDataCubeService { //*******************接口分析数据接口***********************// /** - *
                  * 获取接口分析数据(getinterfacesummary)
            -     * 详情请见文档:接口分析数据接口
            +     * 

            + * {@code 详情请见文档:接口分析数据接口} + *

            + *

            * 接口url格式:https://api.weixin.qq.com/datacube/getinterfacesummary?access_token=ACCESS_TOKEN + *

            * * @param beginDate 开始时间 * @param endDate 最大时间跨度30天,endDate不能早于begingDate @@ -233,10 +272,13 @@ public interface WxMpDataCubeService { List getInterfaceSummary(Date beginDate, Date endDate) throws WxErrorException; /** - *
                  * 获取接口分析分时数据(getinterfacesummaryhour)
            -     * 详情请见文档:接口分析数据接口
            +     * 

            + * {@code 详情请见文档:接口分析数据接口} + *

            + *

            * 接口url格式:https://api.weixin.qq.com/datacube/getinterfacesummaryhour?access_token=ACCESS_TOKEN + *

            * * @param beginDate 开始时间 * @param endDate 最大时间跨度1天,endDate不能早于begingDate diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java index 468dced138..2d965bf8de 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java @@ -54,10 +54,10 @@ public interface WxMpService extends WxService { WxMpShortKeyResult fetchShorten(String shortKey) throws WxErrorException; /** - *
                * 验证消息的确来自微信服务器.
            -   * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421135319&token=&lang=zh_CN
            -   * 
            + *

            + * {@code 详情请见: 接入指南} + *

            * * @param timestamp 时间戳,字符串格式 * @param nonce 随机串,字符串格式 @@ -76,16 +76,19 @@ public interface WxMpService extends WxService { String getAccessToken() throws WxErrorException; /** - *
                * 获取access_token,本方法线程安全.
            +   * 

            * 且在多线程同时刷新时只刷新一次,避免超出2000次/日的调用次数上限 - * + *

            + *

            * 另:本service的所有方法都会在access_token过期时调用此方法 - * + *

            + *

            * 程序员在非必要情况下尽量不要主动调用此方法 - * - * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140183&token=&lang=zh_CN - *

            + *

            + *

            + * {@code 详情请见: 获取access_token} + *

            * * @param forceRefresh 是否强制刷新,true表示强制刷新,false表示使用缓存 * @return token access token,字符串格式 @@ -126,12 +129,13 @@ public interface WxMpService extends WxService { String getJsapiTicket() throws WxErrorException; /** - *
                * 获得jsapi_ticket.
            +   * 

            * 获得时会检查jsapiToken是否过期,如果过期了,那么就刷新一下,否则就什么都不干 - * - * 详情请见:http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115&token=&lang=zh_CN - *

            + *

            + *

            + * {@code 详情请见:JS-SDK使用权限签名算法} + *

            * * @param forceRefresh 强制刷新,true表示强制刷新,false表示使用缓存 * @return jsapi ticket,字符串格式 @@ -140,11 +144,10 @@ public interface WxMpService extends WxService { String getJsapiTicket(boolean forceRefresh) throws WxErrorException; /** - *
                * 创建调用jsapi时所需要的签名.
            -   *
            -   * 详情请见:http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115&token=&lang=zh_CN
            -   * 
            + *

            + * {@code 详情请见:JS-SDK使用权限签名算法} + *

            * * @param url 当前网页的URL,不包括#及其后面部分 * @return 生成的签名对象,包含签名、时间戳、随机串等信息 @@ -153,10 +156,10 @@ public interface WxMpService extends WxService { WxJsapiSignature createJsapiSignature(String url) throws WxErrorException; /** - *
                * 长链接转短链接接口.
            -   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=长链接转短链接接口
            -   * 
            + *

            + * 详情请见: 长链接转短链接接口 + *

            * * @param longUrl 长url,需要转换的原始URL * @return 生成的短地址,字符串格式 @@ -167,10 +170,10 @@ public interface WxMpService extends WxService { String shortUrl(String longUrl) throws WxErrorException; /** - *
                * 语义查询接口.
            -   * 详情请见:http://mp.weixin.qq.com/wiki/index.php?title=语义理解
            -   * 
            + *

            + * 详情请见:语义理解 + *

            * * @param semanticQuery 查询条件,包含查询内容、类型等信息 * @return 查询结果,包含语义理解的结果和建议回复 @@ -179,11 +182,13 @@ public interface WxMpService extends WxService { WxMpSemanticQueryResult semanticQuery(WxMpSemanticQuery semanticQuery) throws WxErrorException; /** - *
                * 构造第三方使用网站应用授权登录的url.
            -   * 详情请见: 网站应用微信登录开发指南
            -   * URL格式为:https://open.weixin.qq.com/connect/qrconnect?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
            -   * 
            + *

            + * {@code 详情请见: 网站应用微信登录开发指南} + *

            + *

            + * {@code URL格式为:https://open.weixin.qq.com/connect/qrconnect?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect} + *

            * * @param redirectUri 用户授权完成后的重定向链接,无需urlencode, 方法内会进行encode * @param scope 应用授权作用域,拥有多个作用域用逗号(,)分隔,网页应用目前仅填写snsapi_login即可 @@ -193,10 +198,7 @@ public interface WxMpService extends WxService { String buildQrConnectUrl(String redirectUri, String scope, String state); /** - *
                * 获取微信服务器IP地址
            -   * http://mp.weixin.qq.com/wiki/0/2ad4b6bfd29f30f71d39616c2a0fcedc.html
            -   * 
            * * @return 微信服务器ip地址数组,包含所有微信服务器IP地址 * @throws WxErrorException 微信API调用异常 @@ -204,11 +206,10 @@ public interface WxMpService extends WxService { String[] getCallbackIP() throws WxErrorException; /** - *
            -   *  网络检测
            -   *  https://mp.weixin.qq.com/wiki?t=resource/res_main&id=21541575776DtsuT
            -   *  为了帮助开发者排查回调连接失败的问题,提供这个网络检测的API。它可以对开发者URL做域名解析,然后对所有IP进行一次ping操作,得到丢包率和耗时。
            -   * 
            + * 网络检测 + *

            + * 为了帮助开发者排查回调连接失败的问题,提供这个网络检测的API。它可以对开发者URL做域名解析,然后对所有IP进行一次ping操作,得到丢包率和耗时。 + *

            * * @param action 执行的检测动作,可选值:all(全部检测)、dns(仅域名解析)、ping(仅网络连通性检测) * @param operator 指定平台从某个运营商进行检测,可选值:CHINANET(中国电信)、UNICOM(中国联通)、CAP(中国联通)、CUCC(中国联通) @@ -239,12 +240,13 @@ public interface WxMpService extends WxService { WxMpCurrentAutoReplyInfo getCurrentAutoReplyInfo() throws WxErrorException; /** - *
            -   *  公众号调用或第三方平台帮公众号调用对公众号的所有api调用(包括第三方帮其调用)次数进行清零:
            -   *  HTTP调用:https://api.weixin.qq.com/cgi-bin/clear_quota?access_token=ACCESS_TOKEN
            -   *  接口文档地址:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433744592
            -   *
            -   * 
            + * 公众号调用或第三方平台帮公众号调用对公众号的所有api调用(包括第三方帮其调用)次数进行清零. + *

            + * HTTP调用:https://api.weixin.qq.com/cgi-bin/clear_quota?access_token=ACCESS_TOKEN + *

            + *

            + * {@code 接口文档地址:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433744592} + *

            * * @param appid 公众号的APPID,需要清零调用的公众号的appid * @throws WxErrorException 微信API调用异常 @@ -252,11 +254,9 @@ public interface WxMpService extends WxService { void clearQuota(String appid) throws WxErrorException; /** - *
                * Service没有实现某个API的时候,可以用这个,
                * 比{@link #get}和{@link #post}方法更灵活,可以自己构造RequestExecutor用来处理不同的参数和不同的返回类型。
                * 可以参考,{@link MediaUploadRequestExecutor}的实现方法
            -   * 
            * * @param 返回值类型 * @param 参数类型 diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/BaseWxMpServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/BaseWxMpServiceImpl.java index 63ca608eba..76ab466157 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/BaseWxMpServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/BaseWxMpServiceImpl.java @@ -304,8 +304,9 @@ public String getAccessToken(boolean forceRefresh) throws WxErrorException { /** * 通过网络请求获取稳定版接口调用凭据 * - * @return . - * @throws IOException . + * @param forceRefresh 是否强制刷新 + * @return access_token字符串 + * @throws IOException IO异常 */ protected abstract String doGetStableAccessTokenRequest(boolean forceRefresh) throws IOException; diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMassOpenIdsMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMassOpenIdsMessage.java index 80e1658c16..936996ef69 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMassOpenIdsMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMassOpenIdsMessage.java @@ -25,11 +25,11 @@ public class WxMpMassOpenIdsMessage implements Serializable { /** *
                * 请使用
            -   * {@link WxConsts.MassMsgType#IMAGE}
            -   * {@link WxConsts.MassMsgType#MPNEWS}
            -   * {@link WxConsts.MassMsgType#TEXT}
            -   * {@link WxConsts.MassMsgType#MPVIDEO}
            -   * {@link WxConsts.MassMsgType#VOICE}
            +   * {@link me.chanjar.weixin.common.api.WxConsts.MassMsgType#IMAGE}
            +   * {@link me.chanjar.weixin.common.api.WxConsts.MassMsgType#MPNEWS}
            +   * {@link me.chanjar.weixin.common.api.WxConsts.MassMsgType#TEXT}
            +   * {@link me.chanjar.weixin.common.api.WxConsts.MassMsgType#MPVIDEO}
            +   * {@link me.chanjar.weixin.common.api.WxConsts.MassMsgType#VOICE}
                * 如果msgtype和media_id不匹配的话,会返回系统繁忙的错误
                * 
            */ @@ -60,6 +60,8 @@ public String toJson() { /** * 添加openid,最多支持10,000个 + * + * @param openid 用户openid */ public void addUser(String openid) { this.toUsers.add(openid); diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMassPreviewMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMassPreviewMessage.java index dca743c9c3..57b34d352a 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMassPreviewMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMassPreviewMessage.java @@ -19,11 +19,11 @@ public class WxMpMassPreviewMessage implements Serializable { *
                * 消息类型
                * 请使用
            -   * {@link WxConsts.MassMsgType#IMAGE}
            -   * {@link WxConsts.MassMsgType#MPNEWS}
            -   * {@link WxConsts.MassMsgType#TEXT}
            -   * {@link WxConsts.MassMsgType#MPVIDEO}
            -   * {@link WxConsts.MassMsgType#VOICE}
            +   * {@link me.chanjar.weixin.common.api.WxConsts.MassMsgType#IMAGE}
            +   * {@link me.chanjar.weixin.common.api.WxConsts.MassMsgType#MPNEWS}
            +   * {@link me.chanjar.weixin.common.api.WxConsts.MassMsgType#TEXT}
            +   * {@link me.chanjar.weixin.common.api.WxConsts.MassMsgType#MPVIDEO}
            +   * {@link me.chanjar.weixin.common.api.WxConsts.MassMsgType#VOICE}
                * 如果msgtype和media_id不匹配的话,会返回系统繁忙的错误
                * 
            */ diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMassTagMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMassTagMessage.java index 598e5754f1..466ef8d96f 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMassTagMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMassTagMessage.java @@ -24,11 +24,11 @@ public class WxMpMassTagMessage implements Serializable { *
                * 消息类型.
                * 请使用
            -   * {@link WxConsts.MassMsgType#IMAGE}
            -   * {@link WxConsts.MassMsgType#MPNEWS}
            -   * {@link WxConsts.MassMsgType#TEXT}
            -   * {@link WxConsts.MassMsgType#MPVIDEO}
            -   * {@link WxConsts.MassMsgType#VOICE}
            +   * {@link me.chanjar.weixin.common.api.WxConsts.MassMsgType#IMAGE}
            +   * {@link me.chanjar.weixin.common.api.WxConsts.MassMsgType#MPNEWS}
            +   * {@link me.chanjar.weixin.common.api.WxConsts.MassMsgType#TEXT}
            +   * {@link me.chanjar.weixin.common.api.WxConsts.MassMsgType#MPVIDEO}
            +   * {@link me.chanjar.weixin.common.api.WxConsts.MassMsgType#VOICE}
                * 如果msgtype和media_id不匹配的话,会返回系统繁忙的错误
                * 
            */ diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpUserQuery.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpUserQuery.java index 9e73b46159..ac4a596dd8 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpUserQuery.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpUserQuery.java @@ -63,9 +63,8 @@ public WxMpUserQuery add(String openid, String lang) { /** * 添加一个OpenId到列表中,并返回本对象 *

            - *

                * 该方法默认lang = zh_CN
            -   * 
            + *

            * * @param openid openid * @return {@link WxMpUserQuery} @@ -100,6 +99,8 @@ public WxMpUserQuery remove(String openid, String lang) { /** * 获取查询参数列表 + * + * @return 查询参数列表 */ public List getQueryParamList() { return this.queryParamList; diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardMpnewsGethtmlResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardMpnewsGethtmlResult.java index 6d7dde1ad6..34a9c56b99 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardMpnewsGethtmlResult.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardMpnewsGethtmlResult.java @@ -8,7 +8,9 @@ /** - * @author S + * 卡券图文消息HTML结果 + * + * @author S (sshzh90@gmail.com) */ @Data public class WxMpCardMpnewsGethtmlResult implements Serializable { diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/MemberCardActivateUserFormRequest.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/MemberCardActivateUserFormRequest.java index d8634cfa3c..0adb413869 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/MemberCardActivateUserFormRequest.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/MemberCardActivateUserFormRequest.java @@ -39,8 +39,8 @@ public class MemberCardActivateUserFormRequest implements Serializable { /** * 绑定老会员卡信息 * - * @param name - * @param url + * @param name 名称 + * @param url 链接地址 */ public void setBindOldCard(String name, String url) { if (StringUtils.isAnyEmpty(name, url)) { @@ -56,8 +56,8 @@ public void setBindOldCard(String name, String url) { /** * 设置服务声明,用于放置商户会员卡守则 * - * @param name - * @param url + * @param name 名称 + * @param url 链接地址 */ public void setServiceStatement(String name, String url) { if (StringUtils.isAnyEmpty(name, url)) { diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/MemberCardUserForm.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/MemberCardUserForm.java index 0c0fae3e2b..b3b0c9be5e 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/MemberCardUserForm.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/MemberCardUserForm.java @@ -50,6 +50,7 @@ public class MemberCardUserForm implements Serializable { /** * 添加富文本类型字段 * + * @param field 富文本字段 */ public void addRichField(MemberCardUserFormRichField field) { if (field == null) { @@ -64,6 +65,7 @@ public void addRichField(MemberCardUserFormRichField field) { /** * 添加微信选项类型字段 * + * @param fieldType 微信字段类型 */ public void addWechatField(CardWechatFieldType fieldType) { if (fieldType == null) { @@ -78,6 +80,7 @@ public void addWechatField(CardWechatFieldType fieldType) { /** * 添加文本类型字段 * + * @param field 文本字段 */ public void addCustomField(String field) { if (StringUtils.isBlank(field)) { diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/NotifyOptional.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/NotifyOptional.java index 139db68557..1ba8a0e60c 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/NotifyOptional.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/NotifyOptional.java @@ -6,12 +6,13 @@ import java.io.Serializable; /** - *
              * 控制原生消息结构体,包含各字段的消息控制字段。
            - *
            + * 

            * 用于 `7 更新会员信息` 的接口参数调用 - * https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1451025283 - *

            + *

            + *

            + * {@code 参考:会员卡接口} + *

            * * @author YuJian(mgcnrx11@gmail.com) * @version 2017/7/15 diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/WxMpMemberCardUpdateResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/WxMpMemberCardUpdateResult.java index 663fe1f1e5..b4ad8eb139 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/WxMpMemberCardUpdateResult.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/WxMpMemberCardUpdateResult.java @@ -6,10 +6,10 @@ import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; /** - *
              * 用于 `7 更新会员信息` 的接口调用后的返回结果
            - * https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1451025283
            - * 
            + *

            + * {@code 参考:会员卡接口} + *

            * * @author YuJian(mgcnrx11@gmail.com) * @version 2017/7/15 diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/WxMpMemberCardUserInfoResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/WxMpMemberCardUserInfoResult.java index 8fad40ccf8..9a2b47f5bf 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/WxMpMemberCardUserInfoResult.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/membercard/WxMpMemberCardUserInfoResult.java @@ -6,11 +6,10 @@ import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; /** - *
              * 拉取会员信息返回的结果
            - *
            - * 字段格式参考https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1451025283  6.2.1小节的步骤5
            - * 
            + *

            + * {@code 字段格式参考:会员卡接口 6.2.1小节的步骤5} + *

            * * @author YuJian * @version 2017/7/9 diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/WxMpKefuMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/WxMpKefuMessage.java index f066c1d934..01be3c08d2 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/WxMpKefuMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/WxMpKefuMessage.java @@ -45,6 +45,8 @@ public class WxMpKefuMessage implements Serializable { /** * 获得文本消息builder. + * + * @return 文本消息builder */ public static TextBuilder TEXT() { return new TextBuilder(); @@ -52,6 +54,8 @@ public static TextBuilder TEXT() { /** * 获得图片消息builder. + * + * @return 图片消息builder */ public static ImageBuilder IMAGE() { return new ImageBuilder(); @@ -59,6 +63,8 @@ public static ImageBuilder IMAGE() { /** * 获得语音消息builder. + * + * @return 语音消息builder */ public static VoiceBuilder VOICE() { return new VoiceBuilder(); @@ -66,6 +72,8 @@ public static VoiceBuilder VOICE() { /** * 获得视频消息builder. + * + * @return 视频消息builder */ public static VideoBuilder VIDEO() { return new VideoBuilder(); @@ -73,6 +81,8 @@ public static VideoBuilder VIDEO() { /** * 获得音乐消息builder. + * + * @return 音乐消息builder */ public static MusicBuilder MUSIC() { return new MusicBuilder(); @@ -80,6 +90,8 @@ public static MusicBuilder MUSIC() { /** * 获得图文消息(点击跳转到外链)builder. + * + * @return 图文消息builder */ public static NewsBuilder NEWS() { return new NewsBuilder(); @@ -87,6 +99,8 @@ public static NewsBuilder NEWS() { /** * 获得图文消息(点击跳转到图文消息页面)builder. + * + * @return 图文消息builder */ public static MpNewsBuilder MPNEWS() { return new MpNewsBuilder(); @@ -94,6 +108,8 @@ public static MpNewsBuilder MPNEWS() { /** * 获得卡券消息builder. + * + * @return 卡券消息builder */ public static WxCardBuilder WXCARD() { return new WxCardBuilder(); @@ -101,6 +117,8 @@ public static WxCardBuilder WXCARD() { /** * 获得菜单消息builder. + * + * @return 菜单消息builder */ public static WxMsgMenuBuilder MSGMENU() { return new WxMsgMenuBuilder(); @@ -108,20 +126,25 @@ public static WxMsgMenuBuilder MSGMENU() { /** * 小程序卡片. + * + * @return 小程序卡片builder */ public static MiniProgramPageBuilder MINIPROGRAMPAGE() { return new MiniProgramPageBuilder(); } /** - * 发送图文消息(点击跳转到图文消息页面)使用通过 “发布” 系列接口得到的 article_id(草稿箱功能上线后不再支持客服接口中带 media_id 的 mpnews 类型的图文消息) + * 发送图文消息(点击跳转到图文消息页面)使用通过 “发布” 系列接口得到的 article_id + * + * @return 图文消息builder */ public static MpNewsArticleBuilder MPNEWSARTICLE() { return new MpNewsArticleBuilder(); } /** - *
            +   * 设置消息类型
            +   * 

            * 请使用 * {@link me.chanjar.weixin.common.api.WxConsts.KefuMsgType#TEXT} * {@link me.chanjar.weixin.common.api.WxConsts.KefuMsgType#IMAGE} @@ -135,7 +158,9 @@ public static MpNewsArticleBuilder MPNEWSARTICLE() { * {@link me.chanjar.weixin.common.api.WxConsts.KefuMsgType#TASKCARD} * {@link me.chanjar.weixin.common.api.WxConsts.KefuMsgType#MSGMENU} * {@link me.chanjar.weixin.common.api.WxConsts.KefuMsgType#MP_NEWS_ARTICLE} - *

            + *

            + * + * @param msgType 消息类型 */ public void setMsgType(String msgType) { this.msgType = msgType; diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java index 3d5f4ac3a0..dfc88ab13b 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java @@ -927,6 +927,7 @@ public static WxMpXmlMessage fromXml(InputStream is) { * @param timestamp 时间戳 * @param nonce 随机串 * @param msgSignature 签名串 + * @return 解密后的消息对象 */ public static WxMpXmlMessage fromEncryptedXml(String encryptedXml, WxMpConfigStorage wxMpConfigStorage, String timestamp, String nonce, String msgSignature) { @@ -956,14 +957,16 @@ public WxMpXmlMessage decryptField(WxMpConfigStorage wxMpConfigStorage, /** *
                * 当接受用户消息时,可能会获得以下值:
            -   * {@link WxConsts.XmlMsgType#TEXT}
            -   * {@link WxConsts.XmlMsgType#IMAGE}
            -   * {@link WxConsts.XmlMsgType#VOICE}
            -   * {@link WxConsts.XmlMsgType#VIDEO}
            -   * {@link WxConsts.XmlMsgType#LOCATION}
            -   * {@link WxConsts.XmlMsgType#LINK}
            -   * {@link WxConsts.XmlMsgType#EVENT}
            +   * {@link me.chanjar.weixin.common.api.WxConsts.XmlMsgType#TEXT}
            +   * {@link me.chanjar.weixin.common.api.WxConsts.XmlMsgType#IMAGE}
            +   * {@link me.chanjar.weixin.common.api.WxConsts.XmlMsgType#VOICE}
            +   * {@link me.chanjar.weixin.common.api.WxConsts.XmlMsgType#VIDEO}
            +   * {@link me.chanjar.weixin.common.api.WxConsts.XmlMsgType#LOCATION}
            +   * {@link me.chanjar.weixin.common.api.WxConsts.XmlMsgType#LINK}
            +   * {@link me.chanjar.weixin.common.api.WxConsts.XmlMsgType#EVENT}
                * 
            + * + * @return 消息类型 */ public String getMsgType() { return this.msgType; @@ -972,13 +975,15 @@ public String getMsgType() { /** *
                * 当发送消息的时候使用:
            -   * {@link WxConsts.XmlMsgType#TEXT}
            -   * {@link WxConsts.XmlMsgType#IMAGE}
            -   * {@link WxConsts.XmlMsgType#VOICE}
            -   * {@link WxConsts.XmlMsgType#VIDEO}
            -   * {@link WxConsts.XmlMsgType#NEWS}
            -   * {@link WxConsts.XmlMsgType#MUSIC}
            +   * {@link me.chanjar.weixin.common.api.WxConsts.XmlMsgType#TEXT}
            +   * {@link me.chanjar.weixin.common.api.WxConsts.XmlMsgType#IMAGE}
            +   * {@link me.chanjar.weixin.common.api.WxConsts.XmlMsgType#VOICE}
            +   * {@link me.chanjar.weixin.common.api.WxConsts.XmlMsgType#VIDEO}
            +   * {@link me.chanjar.weixin.common.api.WxConsts.XmlMsgType#NEWS}
            +   * {@link me.chanjar.weixin.common.api.WxConsts.XmlMsgType#MUSIC}
                * 
            + * + * @param msgType 消息类型 */ public void setMsgType(String msgType) { this.msgType = msgType; diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutMessage.java index a44aea740c..1f3143df7e 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutMessage.java @@ -70,6 +70,8 @@ public abstract class WxMpXmlOutMessage implements Serializable { /** * 获得文本消息builder + * + * @return 文本消息构建器 */ public static TextBuilder TEXT() { return new TextBuilder(); @@ -77,6 +79,8 @@ public static TextBuilder TEXT() { /** * 获得图片消息builder + * + * @return 图片消息构建器 */ public static ImageBuilder IMAGE() { return new ImageBuilder(); @@ -84,6 +88,8 @@ public static ImageBuilder IMAGE() { /** * 获得语音消息builder + * + * @return 语音消息构建器 */ public static VoiceBuilder VOICE() { return new VoiceBuilder(); @@ -91,6 +97,8 @@ public static VoiceBuilder VOICE() { /** * 获得视频消息builder + * + * @return 视频消息构建器 */ public static VideoBuilder VIDEO() { return new VideoBuilder(); @@ -98,6 +106,8 @@ public static VideoBuilder VIDEO() { /** * 获得音乐消息builder + * + * @return 音乐消息构建器 */ public static MusicBuilder MUSIC() { return new MusicBuilder(); @@ -105,6 +115,8 @@ public static MusicBuilder MUSIC() { /** * 获得图文消息builder + * + * @return 图文消息构建器 */ public static NewsBuilder NEWS() { return new NewsBuilder(); @@ -112,6 +124,8 @@ public static NewsBuilder NEWS() { /** * 获得客服消息builder + * + * @return 客服消息构建器 */ public static TransferCustomerServiceBuilder TRANSFER_CUSTOMER_SERVICE() { return new TransferCustomerServiceBuilder(); @@ -119,11 +133,18 @@ public static TransferCustomerServiceBuilder TRANSFER_CUSTOMER_SERVICE() { /** * 获得设备消息builder + * + * @return 设备消息builder */ public static DeviceBuilder DEVICE() { return new DeviceBuilder(); } + /** + * 转换成xml格式 + * + * @return xml格式字符串 + */ @SuppressWarnings("unchecked") public String toXml() { return XStreamTransformer.toXml((Class) this.getClass(), this); @@ -131,6 +152,9 @@ public String toXml() { /** * 转换成加密的结果 + * + * @param wxMpConfigStorage 公众号配置 + * @return 加密后的消息对象 */ public WxMpXmlOutMessage toEncrypted(WxMpConfigStorage wxMpConfigStorage) { String plainXml = toXml(); @@ -146,6 +170,9 @@ public WxMpXmlOutMessage toEncrypted(WxMpConfigStorage wxMpConfigStorage) { /** * 转换成加密的xml格式 + * + * @param wxMpConfigStorage 公众号配置 + * @return 加密后的xml格式字符串 */ public String toEncryptedXml(WxMpConfigStorage wxMpConfigStorage) { String plainXml = toXml(); diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpMassGetResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpMassGetResult.java index fe8f6e4043..58f2ea2693 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpMassGetResult.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpMassGetResult.java @@ -9,7 +9,8 @@ /** *
              * 查询群发消息发送状态【订阅号与服务号认证后均可用】
            - * https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Batch_Sends_and_Originality_Checks.html#%E6%9F%A5%E8%AF%A2%E7%BE%A4%E5%8F%91%E6%B6%88%E6%81%AF%E5%8F%91%E9%80%81%E7%8A%B6%E6%80%81%E3%80%90%E8%AE%A2%E9%98%85%E5%8F%B7%E4%B8%8E%E6%9C%8D%E5%8A%A1%E5%8F%B7%E8%AE%A4%E8%AF%81%E5%90%8E%E5%9D%87%E5%8F%AF%E7%94%A8%E3%80%91
            + * 文档地址:https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Batch_Sends_and_Originality_Checks.html
            + * 
            * @author S */ @Data diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/config/WxMpConfigStorage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/config/WxMpConfigStorage.java index 11aeef6124..1bebe86885 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/config/WxMpConfigStorage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/config/WxMpConfigStorage.java @@ -24,7 +24,7 @@ public interface WxMpConfigStorage { * Is use stable access token api * * @return the boolean - * @link https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/getStableAccessToken.html + * @see 文档 */ boolean isStableAccessToken(); @@ -211,6 +211,8 @@ public interface WxMpConfigStorage { *
                *   {@link me.chanjar.weixin.mp.api.impl.BaseWxMpServiceImpl#setRetrySleepMillis(int)}
                * 
            + * + * @return 重试间隔毫秒数 */ int getRetrySleepMillis(); @@ -219,6 +221,8 @@ public interface WxMpConfigStorage { *
                *   {@link me.chanjar.weixin.mp.api.impl.BaseWxMpServiceImpl#setMaxRetryTimes(int)}
                * 
            + * + * @return 最大重试次数 */ int getMaxRetryTimes(); diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/config/impl/WxMpRedisConfigImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/config/impl/WxMpRedisConfigImpl.java index 870fa1e276..7939d57a18 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/config/impl/WxMpRedisConfigImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/config/impl/WxMpRedisConfigImpl.java @@ -39,6 +39,8 @@ public WxMpRedisConfigImpl(WxRedisOps redisOps, String keyPrefix) { /** * 每个公众号生成独有的存储key. + * + * @param appId 公众号appId */ @Override public void setAppId(String appId) { diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/config/impl/WxMpRedissonConfigImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/config/impl/WxMpRedissonConfigImpl.java index e0d9e92af1..4982336f8a 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/config/impl/WxMpRedissonConfigImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/config/impl/WxMpRedissonConfigImpl.java @@ -42,6 +42,8 @@ private WxMpRedissonConfigImpl(@NonNull WxRedisOps redisOps, String keyPrefix) { /** * 每个公众号生成独有的存储key. + * + * @param appId 公众号appId */ @Override public void setAppId(String appId) { diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/constant/WxMpEventConstants.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/constant/WxMpEventConstants.java index b2e984b0f9..4dfee6295e 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/constant/WxMpEventConstants.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/constant/WxMpEventConstants.java @@ -20,7 +20,7 @@ public class WxMpEventConstants { public static final String SUBMIT_MEMBERCARD_USER_INFO = "submit_membercard_user_info"; /** - * 微信摇一摇周边>>摇一摇事件通知. + * 微信摇一摇周边-摇一摇事件通知. */ public static final String SHAKEAROUND_USER_SHAKE = "ShakearoundUserShake"; diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/crypto/WxMpCryptUtil.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/crypto/WxMpCryptUtil.java index 99d759f32f..7757ad78bf 100755 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/crypto/WxMpCryptUtil.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/crypto/WxMpCryptUtil.java @@ -27,7 +27,7 @@ public class WxMpCryptUtil extends me.chanjar.weixin.common.util.crypto.WxCryptU /** * 构造函数 * - * @param wxMpConfigStorage + * @param wxMpConfigStorage 公众号配置存储 */ public WxMpCryptUtil(WxMpConfigStorage wxMpConfigStorage) { /* diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/XStreamTransformer.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/XStreamTransformer.java index ace711a236..55e92700de 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/XStreamTransformer.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/XStreamTransformer.java @@ -33,7 +33,12 @@ public class XStreamTransformer { } /** - * xml -> pojo. + * {@code xml -> pojo.} + * + * @param 返回类型 + * @param clazz 类型 + * @param xml xml字符串 + * @return 转换后的对象 */ @SuppressWarnings("unchecked") public static T fromXml(Class clazz, String xml) { @@ -41,6 +46,14 @@ public static T fromXml(Class clazz, String xml) { return object; } + /** + * {@code xml -> pojo.} + * + * @param 返回类型 + * @param clazz 类型 + * @param is 输入流 + * @return 转换后的对象 + */ @SuppressWarnings("unchecked") public static T fromXml(Class clazz, InputStream is) { T object = (T) CLASS_2_XSTREAM_INSTANCE.get(clazz).fromXML(is); @@ -48,7 +61,12 @@ public static T fromXml(Class clazz, InputStream is) { } /** - * pojo -> xml. + * {@code pojo -> xml.} + * + * @param 类型参数 + * @param clazz 类型 + * @param object 对象 + * @return xml字符串 */ public static String toXml(Class clazz, T object) { return CLASS_2_XSTREAM_INSTANCE.get(clazz).toXML(object); diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/MiPayNotifyV3Result.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/MiPayNotifyV3Result.java index a0641379fb..346d427fc4 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/MiPayNotifyV3Result.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/MiPayNotifyV3Result.java @@ -13,7 +13,7 @@ *
            * * @author xgl - * @date 2025/12/20 + * @since 2025/12/20 */ @Data @NoArgsConstructor diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayBaseNotifyV3Result.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayBaseNotifyV3Result.java index 86915d0956..364c9080d8 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayBaseNotifyV3Result.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayBaseNotifyV3Result.java @@ -5,7 +5,8 @@ * * @author Pursuer * @version 1.0 - * @date 2023/6/15 + * @since 2023/6/15 + * @param 解密后的数据类型 */ public interface WxPayBaseNotifyV3Result { /** @@ -13,9 +14,8 @@ public interface WxPayBaseNotifyV3Result { * * @param rawData 原始数据 * @author Pursuer - * @date 2023/6/15 - * @since 1.0 - **/ + * @since 2023/6/15 + */ void setRawData(OriginNotifyResponse rawData); /** @@ -23,8 +23,7 @@ public interface WxPayBaseNotifyV3Result { * * @param data 解密后的数据 * @author Pursuer - * @date 2023/6/15 - * @since 1.0 - **/ + * @since 2023/6/15 + */ void setResult(T data); } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayNotifyV3Response.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayNotifyV3Response.java index b9d7f4d9f6..eac5f7c9de 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayNotifyV3Response.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayNotifyV3Response.java @@ -28,8 +28,8 @@ public class WxPayNotifyV3Response { /** * 返回成功 * - * @param msg - * @return + * @param msg 返回消息 + * @return 成功响应的JSON字符串 */ public static String success(String msg) { WxPayNotifyV3Response response = new WxPayNotifyV3Response(SUCCESS, msg); @@ -40,7 +40,7 @@ public static String success(String msg) { * 返回失败 * * @param msg 返回信息,如非空,为错误原因 - * @return + * @return 失败响应的JSON字符串 */ public static String fail(String msg) { WxPayNotifyV3Response response = new WxPayNotifyV3Response(FAIL, msg); 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 a3a9dc7a92..b42451a666 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 @@ -325,7 +325,8 @@ public SSLContext initSSLContext() throws WxPayException { * * @return org.apache.http.impl.client.CloseableHttpClient * @author doger.wang - **/ + * @throws WxPayException 微信支付异常 + */ public CloseableHttpClient initApiV3HttpClient() throws WxPayException { if (StringUtils.isBlank(this.getApiV3Key())) { throw new WxPayException("请确保apiV3Key值已设置"); @@ -663,6 +664,8 @@ public CloseableHttpClient initSslHttpClient() throws WxPayException { /** * 配置HTTP代理 + * + * @param httpClientBuilder HttpClient构建器 */ private void configureProxy(org.apache.http.impl.client.HttpClientBuilder httpClientBuilder) { if (StringUtils.isNotBlank(this.getHttpProxyHost()) && this.getHttpProxyPort() > 0) { diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/RequestUtils.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/RequestUtils.java index b641cbe537..c4ad966415 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/RequestUtils.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/RequestUtils.java @@ -17,7 +17,7 @@ public class RequestUtils { /** * 获取请求头数据,微信V3版本回调使用 * - * @param request + * @param request HTTP请求对象 * @return 字符串 */ public static String readData(HttpServletRequest request) { diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/ResourcesUtils.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/ResourcesUtils.java index ac68b00bb4..51dd8fbbb6 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/ResourcesUtils.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/ResourcesUtils.java @@ -23,6 +23,10 @@ public class ResourcesUtils { *
          • {@link Class#getClassLoader() ClassLoaderUtil.class.getClassLoader()}
          • *
          • if callingClass is provided: {@link Class#getClassLoader() callingClass.getClassLoader()}
          • *
          + * + * @param resourceName 资源名称 + * @param classLoader 类加载器 + * @return 资源URL */ public static URL getResourceUrl(String resourceName, final ClassLoader classLoader) { @@ -64,6 +68,9 @@ public static URL getResourceUrl(String resourceName, final ClassLoader classLoa /** * Opens a resource of the specified name for reading. * + * @param resourceName 资源名称 + * @return 输入流 + * @throws IOException IO异常 * @see #getResourceAsStream(String, ClassLoader) */ public static InputStream getResourceAsStream(final String resourceName) throws IOException { @@ -73,6 +80,10 @@ public static InputStream getResourceAsStream(final String resourceName) throws /** * Opens a resource of the specified name for reading. * + * @param resourceName 资源名称 + * @param callingClass 类加载器 + * @return 输入流 + * @throws IOException IO异常 * @see #getResourceUrl(String, ClassLoader) */ public static InputStream getResourceAsStream(final String resourceName, final ClassLoader callingClass) throws IOException { diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/SignUtils.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/SignUtils.java index 6c0009fd18..9d4a9b0237 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/SignUtils.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/SignUtils.java @@ -112,7 +112,16 @@ public static String createSign(Map params, String signType, Str /** * 企业微信签名 * - * @param signType md5 目前接口要求使用的加密类型 + * @param actName 活动名称 + * @param mchBillNo 商户订单号 + * @param mchId 商户号 + * @param nonceStr 随机字符串 + * @param reOpenid 用户openid + * @param totalAmount 金额 + * @param wxAppId 微信appid + * @param signKey 签名密钥 + * @param signType md5 目前接口要求使用的加密类型 + * @return 签名字符串 */ public static String createEntSign(String actName, String mchBillNo, String mchId, String nonceStr, String reOpenid, Integer totalAmount, String wxAppId, String signKey, @@ -131,7 +140,18 @@ public static String createEntSign(String actName, String mchBillNo, String mchI /** * 企业微信签名 - * @param signType md5 目前接口要求使用的加密类型 + * + * @param totalAmount 金额 + * @param appId 应用ID + * @param description 描述 + * @param mchId 商户号 + * @param nonceStr 随机字符串 + * @param openid 用户openid + * @param partnerTradeNo 商户订单号 + * @param wwMsgType 消息类型 + * @param signKey 签名密钥 + * @param signType md5 目前接口要求使用的加密类型 + * @return 签名字符串 */ public static String createEntSign(Integer totalAmount, String appId, String description, String mchId, String nonceStr, String openid, String partnerTradeNo, String wwMsgType, From e572ddfba61e7ada2cb0a4a8915a6afb08118d4b Mon Sep 17 00:00:00 2001 From: buaazyl Date: Wed, 7 Jan 2026 09:52:21 +0800 Subject: [PATCH 37/46] =?UTF-8?q?:art:=20#3833=20=E3=80=90=E5=85=AC?= =?UTF-8?q?=E4=BC=97=E5=8F=B7=E3=80=91=E9=87=8D=E6=9E=84Starter=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=E9=85=8D=E7=BD=AE=E5=AD=98=E5=82=A8=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E5=8A=9F=E8=83=BD=EF=BC=8C=E6=8C=89=E5=AD=98?= =?UTF-8?q?=E5=82=A8=E7=B1=BB=E5=9E=8B=E6=8B=86=E5=88=86=E4=B8=BA=E7=8B=AC?= =?UTF-8?q?=E7=AB=8B=E9=85=8D=E7=BD=AE=E7=B1=BB=EF=BC=8C=E5=B9=B6=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E5=8F=AF=E9=80=89=E7=9A=84=20Redisson=20=E5=AD=98?= =?UTF-8?q?=E5=82=A8=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- solon-plugins/wx-java-mp-solon-plugin/pom.xml | 7 +- .../config/WxMpStorageAutoConfiguration.java | 128 ------------- ...bstractWxMpConfigStorageConfiguration.java | 27 +++ ...WxMpInJedisConfigStorageConfiguration.java | 76 ++++++++ ...xMpInMemoryConfigStorageConfiguration.java | 29 +++ ...pInRedissonConfigStorageConfiguration.java | 65 +++++++ .../wxjava/mp/integration/WxMpPluginImpl.java | 15 +- .../wx-java-mp-spring-boot-starter/pom.xml | 7 +- .../config/WxMpStorageAutoConfiguration.java | 171 ++---------------- ...bstractWxMpConfigStorageConfiguration.java | 54 ++++++ ...WxMpInJedisConfigStorageConfiguration.java | 80 ++++++++ ...xMpInMemoryConfigStorageConfiguration.java | 33 ++++ ...disTemplateConfigStorageConfiguration.java | 46 +++++ ...pInRedissonConfigStorageConfiguration.java | 69 +++++++ 14 files changed, 514 insertions(+), 293 deletions(-) delete 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/config/storage/AbstractWxMpConfigStorageConfiguration.java create mode 100644 solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/config/storage/WxMpInJedisConfigStorageConfiguration.java create mode 100644 solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/config/storage/WxMpInMemoryConfigStorageConfiguration.java create mode 100644 solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/config/storage/WxMpInRedissonConfigStorageConfiguration.java create mode 100644 spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/storage/AbstractWxMpConfigStorageConfiguration.java create mode 100644 spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/storage/WxMpInJedisConfigStorageConfiguration.java create mode 100644 spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/storage/WxMpInMemoryConfigStorageConfiguration.java create mode 100644 spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/storage/WxMpInRedisTemplateConfigStorageConfiguration.java create mode 100644 spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/storage/WxMpInRedissonConfigStorageConfiguration.java diff --git a/solon-plugins/wx-java-mp-solon-plugin/pom.xml b/solon-plugins/wx-java-mp-solon-plugin/pom.xml index d2507cc0db..d72a5f7fc4 100644 --- a/solon-plugins/wx-java-mp-solon-plugin/pom.xml +++ b/solon-plugins/wx-java-mp-solon-plugin/pom.xml @@ -22,7 +22,12 @@ redis.clients jedis - compile + provided + + + org.redisson + redisson + provided org.jodd 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 deleted file mode 100644 index ac995dd1ec..0000000000 --- a/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/config/WxMpStorageAutoConfiguration.java +++ /dev/null @@ -1,128 +0,0 @@ -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/config/storage/AbstractWxMpConfigStorageConfiguration.java b/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/config/storage/AbstractWxMpConfigStorageConfiguration.java new file mode 100644 index 0000000000..663bb13340 --- /dev/null +++ b/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/config/storage/AbstractWxMpConfigStorageConfiguration.java @@ -0,0 +1,27 @@ +package com.binarywang.solon.wxjava.mp.config.storage; + +import com.binarywang.solon.wxjava.mp.properties.WxMpProperties; +import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl; + +/** + * @author zhangyl + */ +public abstract class AbstractWxMpConfigStorageConfiguration { + + protected WxMpDefaultConfigImpl config(WxMpDefaultConfigImpl config, WxMpProperties properties) { + config.setAppId(properties.getAppId()); + config.setSecret(properties.getSecret()); + config.setToken(properties.getToken()); + config.setAesKey(properties.getAesKey()); + config.setUseStableAccessToken(properties.isUseStableAccessToken()); + + WxMpProperties.ConfigStorage configStorageProperties = properties.getConfigStorage(); + config.setHttpProxyHost(configStorageProperties.getHttpProxyHost()); + config.setHttpProxyUsername(configStorageProperties.getHttpProxyUsername()); + config.setHttpProxyPassword(configStorageProperties.getHttpProxyPassword()); + if (configStorageProperties.getHttpProxyPort() != null) { + config.setHttpProxyPort(configStorageProperties.getHttpProxyPort()); + } + return config; + } +} diff --git a/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/config/storage/WxMpInJedisConfigStorageConfiguration.java b/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/config/storage/WxMpInJedisConfigStorageConfiguration.java new file mode 100644 index 0000000000..a949ccfaca --- /dev/null +++ b/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/config/storage/WxMpInJedisConfigStorageConfiguration.java @@ -0,0 +1,76 @@ +package com.binarywang.solon.wxjava.mp.config.storage; + +import com.binarywang.solon.wxjava.mp.properties.RedisProperties; +import com.binarywang.solon.wxjava.mp.properties.WxMpProperties; +import lombok.RequiredArgsConstructor; +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.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; + +/** + * @author zhangyl + */ +@Configuration +@Condition( + onProperty = "${" + WxMpProperties.PREFIX + ".config-storage.type} = jedis", + onClass = Jedis.class +) +@RequiredArgsConstructor +public class WxMpInJedisConfigStorageConfiguration extends AbstractWxMpConfigStorageConfiguration { + private final WxMpProperties properties; + private final AppContext applicationContext; + + @Bean + @Condition(onMissingBean = WxMpConfigStorage.class) + public WxMpConfigStorage wxMpConfigStorage() { + WxMpRedisConfigImpl config = getWxMpRedisConfigImpl(); + return this.config(config, properties); + } + + private WxMpRedisConfigImpl getWxMpRedisConfigImpl() { + RedisProperties redisProperties = properties.getConfigStorage().getRedis(); + JedisPool jedisPool; + if (redisProperties != null && StringUtils.isNotEmpty(redisProperties.getHost())) { + jedisPool = applicationContext.getBean("wxMpJedisPool"); + } else { + jedisPool = applicationContext.getBean(JedisPool.class); + } + WxRedisOps redisOps = new JedisWxRedisOps(jedisPool); + return new WxMpRedisConfigImpl(redisOps, properties.getConfigStorage().getKeyPrefix()); + } + + @Bean + @Condition(onProperty = "${" + WxMpProperties.PREFIX + ".config-storage.redis.host}") + public JedisPool wxMpJedisPool() { + WxMpProperties.ConfigStorage storage = properties.getConfigStorage(); + RedisProperties redis = storage.getRedis(); + + JedisPoolConfig config = new JedisPoolConfig(); + if (redis.getMaxActive() != null) { + config.setMaxTotal(redis.getMaxActive()); + } + if (redis.getMaxIdle() != null) { + config.setMaxIdle(redis.getMaxIdle()); + } + if (redis.getMaxWaitMillis() != null) { + config.setMaxWaitMillis(redis.getMaxWaitMillis()); + } + if (redis.getMinIdle() != null) { + config.setMinIdle(redis.getMinIdle()); + } + config.setTestOnBorrow(true); + config.setTestWhileIdle(true); + + 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/config/storage/WxMpInMemoryConfigStorageConfiguration.java b/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/config/storage/WxMpInMemoryConfigStorageConfiguration.java new file mode 100644 index 0000000000..88994fcf42 --- /dev/null +++ b/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/config/storage/WxMpInMemoryConfigStorageConfiguration.java @@ -0,0 +1,29 @@ +package com.binarywang.solon.wxjava.mp.config.storage; + +import com.binarywang.solon.wxjava.mp.properties.WxMpProperties; +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.mp.config.WxMpConfigStorage; +import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl; +import org.noear.solon.annotation.Bean; +import org.noear.solon.annotation.Condition; +import org.noear.solon.annotation.Configuration; + +/** + * @author zhangyl + */ +@Configuration +@Condition( + onProperty = "${" + WxMpProperties.PREFIX + ".config-storage.type:memory} = memory" +) +@RequiredArgsConstructor +public class WxMpInMemoryConfigStorageConfiguration extends AbstractWxMpConfigStorageConfiguration { + private final WxMpProperties properties; + + @Bean + @Condition(onMissingBean = WxMpConfigStorage.class) + public WxMpConfigStorage wxMpConfigStorage() { + WxMpDefaultConfigImpl config = new WxMpDefaultConfigImpl(); + config(config, properties); + return config; + } +} diff --git a/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/config/storage/WxMpInRedissonConfigStorageConfiguration.java b/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/config/storage/WxMpInRedissonConfigStorageConfiguration.java new file mode 100644 index 0000000000..c1f5ebf0f3 --- /dev/null +++ b/solon-plugins/wx-java-mp-solon-plugin/src/main/java/com/binarywang/solon/wxjava/mp/config/storage/WxMpInRedissonConfigStorageConfiguration.java @@ -0,0 +1,65 @@ +package com.binarywang.solon.wxjava.mp.config.storage; + +import com.binarywang.solon.wxjava.mp.properties.RedisProperties; +import com.binarywang.solon.wxjava.mp.properties.WxMpProperties; +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.mp.config.WxMpConfigStorage; +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; + +/** + * @author zhangyl + */ +@Configuration +@Condition( + onProperty = "${" + WxMpProperties.PREFIX + ".config-storage.type} = redisson", + onClass = Redisson.class +) +@RequiredArgsConstructor +public class WxMpInRedissonConfigStorageConfiguration extends AbstractWxMpConfigStorageConfiguration { + private final WxMpProperties properties; + private final AppContext applicationContext; + + @Bean + @Condition(onMissingBean = WxMpConfigStorage.class) + public WxMpConfigStorage wxMaConfig() { + WxMpRedissonConfigImpl config = getWxMpInRedissonConfigStorage(); + return this.config(config, properties); + } + + private WxMpRedissonConfigImpl getWxMpInRedissonConfigStorage() { + RedisProperties redisProperties = properties.getConfigStorage().getRedis(); + RedissonClient redissonClient; + if (redisProperties != null && StringUtils.isNotEmpty(redisProperties.getHost())) { + redissonClient = applicationContext.getBean("wxMpRedissonClient"); + } else { + redissonClient = applicationContext.getBean(RedissonClient.class); + } + return new WxMpRedissonConfigImpl(redissonClient, properties.getConfigStorage().getKeyPrefix()); + } + + @Bean + @Condition(onProperty = "${" + WxMpProperties.PREFIX + ".config-storage.redis.host}") + public RedissonClient wxMpRedissonClient() { + WxMpProperties.ConfigStorage storage = properties.getConfigStorage(); + RedisProperties redis = storage.getRedis(); + + Config config = new Config(); + config.useSingleServer() + .setAddress("redis://" + redis.getHost() + ":" + redis.getPort()) + .setDatabase(redis.getDatabase()); + if (StringUtils.isNotBlank(redis.getPassword())) { + config.useSingleServer().setPassword(redis.getPassword()); + } + config.setTransportMode(TransportMode.NIO); + return Redisson.create(config); + } +} 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 index 3368d34269..285d871f25 100644 --- 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 @@ -1,10 +1,13 @@ 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.config.storage.WxMpInJedisConfigStorageConfiguration; +import com.binarywang.solon.wxjava.mp.config.storage.WxMpInMemoryConfigStorageConfiguration; +import com.binarywang.solon.wxjava.mp.config.storage.WxMpInRedissonConfigStorageConfiguration; import com.binarywang.solon.wxjava.mp.properties.WxMpProperties; import org.noear.solon.core.AppContext; import org.noear.solon.core.Plugin; +import org.noear.solon.core.util.ClassUtil; /** * @author noear 2024/9/2 created @@ -13,8 +16,14 @@ 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); + + context.beanMake(WxMpInMemoryConfigStorageConfiguration.class); + if (ClassUtil.loadClass("redis.clients.jedis.Jedis") != null) { + context.beanMake(WxMpInJedisConfigStorageConfiguration.class); + } + if (ClassUtil.loadClass("org.redisson.api.RedissonClient") != null) { + context.beanMake(WxMpInRedissonConfigStorageConfiguration.class); + } } } 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 273364c9a7..38e484b450 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 @@ -22,7 +22,7 @@ redis.clients jedis - compile + provided org.springframework.data @@ -44,6 +44,11 @@ httpclient5 provided + + org.redisson + redisson + provided + diff --git a/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/WxMpStorageAutoConfiguration.java b/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/WxMpStorageAutoConfiguration.java index 4c0938454a..cab3cb17b2 100644 --- a/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/WxMpStorageAutoConfiguration.java +++ b/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/WxMpStorageAutoConfiguration.java @@ -1,175 +1,26 @@ package com.binarywang.spring.starter.wxjava.mp.config; -import com.binarywang.spring.starter.wxjava.mp.enums.StorageType; -import com.binarywang.spring.starter.wxjava.mp.properties.RedisProperties; -import com.binarywang.spring.starter.wxjava.mp.properties.WxMpProperties; -import com.google.common.collect.Sets; +import com.binarywang.spring.starter.wxjava.mp.config.storage.WxMpInJedisConfigStorageConfiguration; +import com.binarywang.spring.starter.wxjava.mp.config.storage.WxMpInMemoryConfigStorageConfiguration; +import com.binarywang.spring.starter.wxjava.mp.config.storage.WxMpInRedisTemplateConfigStorageConfiguration; +import com.binarywang.spring.starter.wxjava.mp.config.storage.WxMpInRedissonConfigStorageConfiguration; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import me.chanjar.weixin.common.redis.JedisWxRedisOps; -import me.chanjar.weixin.common.redis.RedisTemplateWxRedisOps; -import me.chanjar.weixin.common.redis.WxRedisOps; -import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; -import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder; -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.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.data.redis.core.StringRedisTemplate; -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; +import org.springframework.context.annotation.Import; /** * 微信公众号存储策略自动配置. * * @author Luo */ -@Slf4j @Configuration +@Import({ + WxMpInMemoryConfigStorageConfiguration.class, + WxMpInJedisConfigStorageConfiguration.class, + WxMpInRedisTemplateConfigStorageConfiguration.class, + WxMpInRedissonConfigStorageConfiguration.class +}) @RequiredArgsConstructor public class WxMpStorageAutoConfiguration { - private final ApplicationContext applicationContext; - - private final WxMpProperties wxMpProperties; - - @Bean - @ConditionalOnMissingBean(WxMpConfigStorage.class) - public WxMpConfigStorage wxMpConfigStorage() { - StorageType type = wxMpProperties.getConfigStorage().getType(); - WxMpConfigStorage config; - switch (type) { - case Jedis: - config = jedisConfigStorage(); - break; - case RedisTemplate: - config = redisTemplateConfigStorage(); - 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 WxMpConfigStorage redisTemplateConfigStorage() { - StringRedisTemplate redisTemplate = null; - try { - redisTemplate = applicationContext.getBean(StringRedisTemplate.class); - } catch (Exception e) { - log.error(e.getMessage(), e); - } - try { - if (null == redisTemplate) { - redisTemplate = (StringRedisTemplate) applicationContext.getBean("stringRedisTemplate"); - } - } catch (Exception e) { - log.error(e.getMessage(), e); - } - - if (null == redisTemplate) { - redisTemplate = (StringRedisTemplate) applicationContext.getBean("redisTemplate"); - } - - WxRedisOps redisOps = new RedisTemplateWxRedisOps(redisTemplate); - 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()); - WxMpProperties.ConfigStorage storage = properties.getConfigStorage(); - // 设置自定义的HttpClient超时配置 - ApacheHttpClientBuilder clientBuilder = config.getApacheHttpClientBuilder(); - if (clientBuilder == null) { - clientBuilder = DefaultApacheHttpClientBuilder.get(); - } - if (clientBuilder instanceof DefaultApacheHttpClientBuilder) { - DefaultApacheHttpClientBuilder defaultBuilder = (DefaultApacheHttpClientBuilder) clientBuilder; - defaultBuilder.setConnectionTimeout(storage.getConnectionTimeout()); - defaultBuilder.setSoTimeout(storage.getSoTimeout()); - defaultBuilder.setConnectionRequestTimeout(storage.getConnectionRequestTimeout()); - config.setApacheHttpClientBuilder(defaultBuilder); - } - 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/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/storage/AbstractWxMpConfigStorageConfiguration.java b/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/storage/AbstractWxMpConfigStorageConfiguration.java new file mode 100644 index 0000000000..e39a8bf4d9 --- /dev/null +++ b/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/storage/AbstractWxMpConfigStorageConfiguration.java @@ -0,0 +1,54 @@ +package com.binarywang.spring.starter.wxjava.mp.config.storage; + +import com.binarywang.spring.starter.wxjava.mp.properties.WxMpProperties; +import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; +import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder; +import me.chanjar.weixin.mp.config.WxMpHostConfig; +import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl; +import org.apache.commons.lang3.StringUtils; + +/** + * @author zhangyl + */ +public abstract class AbstractWxMpConfigStorageConfiguration { + + protected WxMpDefaultConfigImpl config(WxMpDefaultConfigImpl config, WxMpProperties properties) { + config.setAppId(properties.getAppId()); + config.setSecret(properties.getSecret()); + config.setToken(properties.getToken()); + config.setAesKey(properties.getAesKey()); + config.setUseStableAccessToken(properties.isUseStableAccessToken()); + + WxMpProperties.ConfigStorage configStorageProperties = properties.getConfigStorage(); + config.setHttpProxyHost(configStorageProperties.getHttpProxyHost()); + config.setHttpProxyUsername(configStorageProperties.getHttpProxyUsername()); + config.setHttpProxyPassword(configStorageProperties.getHttpProxyPassword()); + if (configStorageProperties.getHttpProxyPort() != null) { + config.setHttpProxyPort(configStorageProperties.getHttpProxyPort()); + } + + // 设置自定义的 HttpClient 超时配置 + ApacheHttpClientBuilder clientBuilder = config.getApacheHttpClientBuilder(); + if (clientBuilder == null) { + clientBuilder = DefaultApacheHttpClientBuilder.get(); + } + if (clientBuilder instanceof DefaultApacheHttpClientBuilder) { + DefaultApacheHttpClientBuilder defaultBuilder = (DefaultApacheHttpClientBuilder) clientBuilder; + defaultBuilder.setConnectionTimeout(configStorageProperties.getConnectionTimeout()); + defaultBuilder.setSoTimeout(configStorageProperties.getSoTimeout()); + defaultBuilder.setConnectionRequestTimeout(configStorageProperties.getConnectionRequestTimeout()); + config.setApacheHttpClientBuilder(defaultBuilder); + } + + // wx host config + if (null != properties.getHosts() && StringUtils.isNotEmpty(properties.getHosts().getApiHost())) { + WxMpHostConfig hostConfig = new WxMpHostConfig(); + hostConfig.setApiHost(properties.getHosts().getApiHost()); + hostConfig.setOpenHost(properties.getHosts().getOpenHost()); + hostConfig.setMpHost(properties.getHosts().getMpHost()); + config.setHostConfig(hostConfig); + } + + return config; + } +} diff --git a/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/storage/WxMpInJedisConfigStorageConfiguration.java b/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/storage/WxMpInJedisConfigStorageConfiguration.java new file mode 100644 index 0000000000..c21418a6f6 --- /dev/null +++ b/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/storage/WxMpInJedisConfigStorageConfiguration.java @@ -0,0 +1,80 @@ +package com.binarywang.spring.starter.wxjava.mp.config.storage; + +import com.binarywang.spring.starter.wxjava.mp.properties.RedisProperties; +import com.binarywang.spring.starter.wxjava.mp.properties.WxMpProperties; +import lombok.RequiredArgsConstructor; +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.impl.WxMpRedisConfigImpl; +import org.apache.commons.lang3.StringUtils; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +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.Jedis; +import redis.clients.jedis.JedisPool; +import redis.clients.jedis.JedisPoolConfig; + +/** + * @author zhangyl + */ +@Configuration +@ConditionalOnProperty( + prefix = WxMpProperties.PREFIX + ".config-storage", + name = "type", + havingValue = "jedis" +) +@ConditionalOnClass(Jedis.class) +@RequiredArgsConstructor +public class WxMpInJedisConfigStorageConfiguration extends AbstractWxMpConfigStorageConfiguration { + private final WxMpProperties properties; + private final ApplicationContext applicationContext; + + @Bean + @ConditionalOnMissingBean(WxMpConfigStorage.class) + public WxMpConfigStorage wxMpConfigStorage() { + WxMpRedisConfigImpl config = getWxMpRedisConfigImpl(); + return this.config(config, properties); + } + + private WxMpRedisConfigImpl getWxMpRedisConfigImpl() { + RedisProperties redisProperties = properties.getConfigStorage().getRedis(); + JedisPool jedisPool; + if (redisProperties != null && StringUtils.isNotEmpty(redisProperties.getHost())) { + jedisPool = applicationContext.getBean("wxMpJedisPool", JedisPool.class); + } else { + jedisPool = applicationContext.getBean(JedisPool.class); + } + WxRedisOps redisOps = new JedisWxRedisOps(jedisPool); + return new WxMpRedisConfigImpl(redisOps, properties.getConfigStorage().getKeyPrefix()); + } + + @Bean + @ConditionalOnProperty(prefix = WxMpProperties.PREFIX + ".config-storage.redis", name = "host") + public JedisPool wxMpJedisPool() { + WxMpProperties.ConfigStorage storage = properties.getConfigStorage(); + RedisProperties redis = storage.getRedis(); + + JedisPoolConfig config = new JedisPoolConfig(); + if (redis.getMaxActive() != null) { + config.setMaxTotal(redis.getMaxActive()); + } + if (redis.getMaxIdle() != null) { + config.setMaxIdle(redis.getMaxIdle()); + } + if (redis.getMaxWaitMillis() != null) { + config.setMaxWaitMillis(redis.getMaxWaitMillis()); + } + if (redis.getMinIdle() != null) { + config.setMinIdle(redis.getMinIdle()); + } + config.setTestOnBorrow(true); + config.setTestWhileIdle(true); + + return new JedisPool(config, redis.getHost(), redis.getPort(), redis.getTimeout(), redis.getPassword(), + redis.getDatabase()); + } +} diff --git a/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/storage/WxMpInMemoryConfigStorageConfiguration.java b/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/storage/WxMpInMemoryConfigStorageConfiguration.java new file mode 100644 index 0000000000..16eada73ae --- /dev/null +++ b/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/storage/WxMpInMemoryConfigStorageConfiguration.java @@ -0,0 +1,33 @@ +package com.binarywang.spring.starter.wxjava.mp.config.storage; + +import com.binarywang.spring.starter.wxjava.mp.properties.WxMpProperties; +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.mp.config.WxMpConfigStorage; +import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author zhangyl + */ +@Configuration +@ConditionalOnProperty( + prefix = WxMpProperties.PREFIX + ".config-storage", + name = "type", + havingValue = "memory", + matchIfMissing = true +) +@RequiredArgsConstructor +public class WxMpInMemoryConfigStorageConfiguration extends AbstractWxMpConfigStorageConfiguration { + private final WxMpProperties properties; + + @Bean + @ConditionalOnMissingBean(WxMpConfigStorage.class) + public WxMpConfigStorage wxMpConfigStorage() { + WxMpDefaultConfigImpl config = new WxMpDefaultConfigImpl(); + config(config, properties); + return config; + } +} diff --git a/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/storage/WxMpInRedisTemplateConfigStorageConfiguration.java b/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/storage/WxMpInRedisTemplateConfigStorageConfiguration.java new file mode 100644 index 0000000000..0305ca4f8e --- /dev/null +++ b/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/storage/WxMpInRedisTemplateConfigStorageConfiguration.java @@ -0,0 +1,46 @@ +package com.binarywang.spring.starter.wxjava.mp.config.storage; + +import com.binarywang.spring.starter.wxjava.mp.properties.WxMpProperties; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.redis.RedisTemplateWxRedisOps; +import me.chanjar.weixin.common.redis.WxRedisOps; +import me.chanjar.weixin.mp.config.WxMpConfigStorage; +import me.chanjar.weixin.mp.config.impl.WxMpRedisConfigImpl; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +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; + +/** + * @author zhangyl + */ +@Slf4j +@Configuration +@ConditionalOnProperty( + prefix = WxMpProperties.PREFIX + ".config-storage", + name = "type", + havingValue = "redistemplate" +) +@ConditionalOnClass(StringRedisTemplate.class) +@RequiredArgsConstructor +public class WxMpInRedisTemplateConfigStorageConfiguration extends AbstractWxMpConfigStorageConfiguration { + private final WxMpProperties properties; + private final ApplicationContext applicationContext; + + @Bean + @ConditionalOnMissingBean(WxMpConfigStorage.class) + public WxMpConfigStorage wxMpConfigStorage() { + WxMpRedisConfigImpl config = getWxMpInRedisTemplateConfigStorage(); + return this.config(config, properties); + } + + private WxMpRedisConfigImpl getWxMpInRedisTemplateConfigStorage() { + StringRedisTemplate redisTemplate = applicationContext.getBean(StringRedisTemplate.class); + WxRedisOps redisOps = new RedisTemplateWxRedisOps(redisTemplate); + return new WxMpRedisConfigImpl(redisOps, properties.getConfigStorage().getKeyPrefix()); + } +} diff --git a/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/storage/WxMpInRedissonConfigStorageConfiguration.java b/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/storage/WxMpInRedissonConfigStorageConfiguration.java new file mode 100644 index 0000000000..75b736f53f --- /dev/null +++ b/spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/config/storage/WxMpInRedissonConfigStorageConfiguration.java @@ -0,0 +1,69 @@ +package com.binarywang.spring.starter.wxjava.mp.config.storage; + +import com.binarywang.spring.starter.wxjava.mp.properties.RedisProperties; +import com.binarywang.spring.starter.wxjava.mp.properties.WxMpProperties; +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.mp.config.WxMpConfigStorage; +import me.chanjar.weixin.mp.config.impl.WxMpRedissonConfigImpl; +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.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author zhangyl + */ +@Configuration +@ConditionalOnProperty( + prefix = WxMpProperties.PREFIX + ".config-storage", + name = "type", + havingValue = "redisson" +) +@ConditionalOnClass({Redisson.class, RedissonClient.class}) +@RequiredArgsConstructor +public class WxMpInRedissonConfigStorageConfiguration extends AbstractWxMpConfigStorageConfiguration { + private final WxMpProperties properties; + private final ApplicationContext applicationContext; + + @Bean + @ConditionalOnMissingBean(WxMpConfigStorage.class) + public WxMpConfigStorage wxMpConfigStorage() { + WxMpRedissonConfigImpl config = getWxMpInRedissonConfigStorage(); + return this.config(config, properties); + } + + private WxMpRedissonConfigImpl getWxMpInRedissonConfigStorage() { + RedisProperties redisProperties = properties.getConfigStorage().getRedis(); + RedissonClient redissonClient; + if (redisProperties != null && StringUtils.isNotEmpty(redisProperties.getHost())) { + redissonClient = applicationContext.getBean("wxMpRedissonClient", RedissonClient.class); + } else { + redissonClient = applicationContext.getBean(RedissonClient.class); + } + return new WxMpRedissonConfigImpl(redissonClient, properties.getConfigStorage().getKeyPrefix()); + } + + @Bean + @ConditionalOnProperty(prefix = WxMpProperties.PREFIX + ".config-storage.redis", name = "host") + public RedissonClient wxMpRedissonClient() { + WxMpProperties.ConfigStorage storage = properties.getConfigStorage(); + RedisProperties redis = storage.getRedis(); + + Config config = new Config(); + config.useSingleServer() + .setAddress("redis://" + redis.getHost() + ":" + redis.getPort()) + .setDatabase(redis.getDatabase()); + if (StringUtils.isNotBlank(redis.getPassword())) { + config.useSingleServer().setPassword(redis.getPassword()); + } + config.setTransportMode(TransportMode.NIO); + return Redisson.create(config); + } +} From 2c5a33917e7991543a1469ef08bcf64a88cae243 Mon Sep 17 00:00:00 2001 From: buaazyl Date: Thu, 8 Jan 2026 15:08:58 +0800 Subject: [PATCH 38/46] =?UTF-8?q?:art:=20#3834=20=E3=80=90=E5=9F=BA?= =?UTF-8?q?=E7=A1=80=E6=9E=B6=E6=9E=84=E3=80=91=E5=B0=8F=E7=A8=8B=E5=BA=8F?= =?UTF-8?q?=E5=92=8C=E5=BC=80=E6=94=BE=E5=B9=B3=E5=8F=B0=E4=B8=A4=E4=B8=AA?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=E8=A1=A5=E5=85=85=E4=BA=86=20Apache=20HttpCl?= =?UTF-8?q?ient=205.x=20=E5=AE=9E=E7=8E=B0=EF=BC=8C=E5=90=8C=E6=97=B6?= =?UTF-8?q?=E5=B9=B6=E5=B0=86=E5=A4=9A=E4=B8=AA=E6=A8=A1=E5=9D=97=E7=9A=84?= =?UTF-8?q?=E9=BB=98=E8=AE=A4=E6=9C=8D=E5=8A=A1=E5=AE=9E=E7=8E=B0=E8=BF=81?= =?UTF-8?q?=E7=A7=BB=E8=87=B3=20Apache=20HttpClient=205.x=20=E5=AE=A2?= =?UTF-8?q?=E6=88=B7=E7=AB=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/HTTPCLIENT_UPGRADE_GUIDE.md | 6 +- .../properties/WxChannelMultiProperties.java | 2 +- .../properties/WxCpMultiProperties.java | 2 +- .../services/AbstractWxMaConfiguration.java | 4 + .../properties/WxMaMultiProperties.java | 8 +- .../config/WxMaServiceAutoConfiguration.java | 4 + .../miniapp/properties/WxMaProperties.java | 2 +- .../services/AbstractWxMpConfiguration.java | 4 + .../properties/WxMpMultiProperties.java | 8 +- .../config/WxMpServiceAutoConfiguration.java | 8 ++ .../WxQidianServiceAutoConfiguration.java | 8 ++ .../config/WxMaServiceAutoConfiguration.java | 4 + .../miniapp/properties/WxMaProperties.java | 2 +- .../api/impl/WxChannelServiceImpl.java | 2 +- .../impl/WxMaServiceHttpComponentsImpl.java | 101 ++++++++++++++++++ .../wx/miniapp/api/impl/WxMaServiceImpl.java | 2 +- .../weixin/mp/api/impl/WxMpServiceImpl.java | 4 +- .../impl/WxOpenServiceHttpComponentsImpl.java | 74 +++++++++++++ .../open/api/impl/WxOpenServiceImpl.java | 2 +- .../wxpay/service/impl/WxPayServiceImpl.java | 4 +- .../qidian/api/impl/WxQidianServiceImpl.java | 4 +- 21 files changed, 233 insertions(+), 22 deletions(-) create mode 100644 weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceHttpComponentsImpl.java create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenServiceHttpComponentsImpl.java diff --git a/docs/HTTPCLIENT_UPGRADE_GUIDE.md b/docs/HTTPCLIENT_UPGRADE_GUIDE.md index 08726fa03f..945341966e 100644 --- a/docs/HTTPCLIENT_UPGRADE_GUIDE.md +++ b/docs/HTTPCLIENT_UPGRADE_GUIDE.md @@ -28,12 +28,11 @@ | weixin-java-cp(企业微信) | ⚠️ 视集成方式而定 | 参考对应 starter 配置 | | weixin-java-channel(视频号) | ✅ 是 | HttpComponents (5.x) | | weixin-java-qidian(企点) | ✅ 是 | HttpComponents (5.x) | -| weixin-java-miniapp(小程序) | ✅ 是 | HttpClient (4.x) | +| weixin-java-miniapp(小程序) | ✅ 是 | HttpComponents (5.x) | | weixin-java-pay(支付) | ✅ 是 | HttpComponents (5.x) | | weixin-java-open(开放平台) | ✅ 是 | HttpComponents (5.x) | **注意**: -- **weixin-java-miniapp 模块**已在核心 SDK 中提供 HttpClient 5.x(`HttpComponents`)支持,但默认仍使用 HttpClient 4.x(`HttpClient`)。如需启用 HttpClient 5.x,可通过配置 `http-client-type=HttpComponents` 显式指定。 - **weixin-java-cp 模块**的支持情况取决于具体使用的 Starter 版本,请参考对应模块文档。 ## 对现有项目的影响 @@ -97,9 +96,6 @@ A: 不会。项目保持完全向后兼容,HttpClient 4.x 的所有实现都 ### Q: 我需要修改代码吗? A: 大多数情况下不需要。如果希望继续使用 HttpClient 4.x,只需在配置中指定 `http-client-type=HttpClient` 即可。 -### Q: MiniApp 模块支持 HttpClient 5.x 吗? -A: 支持。MiniApp 模块在核心 SDK 中已经提供了基于 HttpClient 5.x(`HttpComponents`)的支持,但默认仍会使用 HttpClient 4.x(`HttpClient`)以保持向后兼容。如果你使用的是框架集成(例如 Spring Boot Starter 或 Solon Plugin),可以通过显式配置 `http-client-type=HttpComponents` 来启用 HttpClient 5.x。 - ### Q: 我可以在同一个项目中同时使用两个版本吗? A: 可以。不同的模块可以配置使用不同的 HTTP 客户端。例如,MP 模块使用 HttpClient 5.x,MiniApp 模块默认使用 HttpClient 4.x,但也可以按需配置为 HttpClient 5.x。 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 index 2e2da1add7..ca99e522b9 100644 --- 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 @@ -55,7 +55,7 @@ public static class ConfigStorage implements Serializable { /** * http客户端类型. */ - private HttpClientType httpClientType = HttpClientType.HTTP_CLIENT; + private HttpClientType httpClientType = HttpClientType.HTTP_COMPONENTS; /** * http代理主机. 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 index 821f885f98..2d4bffae66 100644 --- 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 @@ -52,7 +52,7 @@ public static class ConfigStorage implements Serializable { /** * http客户端类型. */ - private HttpClientType httpClientType = HttpClientType.HTTP_CLIENT; + private HttpClientType httpClientType = HttpClientType.HTTP_COMPONENTS; /** * http代理主机 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 index fd94200e58..8ad85c96b8 100644 --- 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 @@ -2,6 +2,7 @@ import cn.binarywang.wx.miniapp.api.WxMaService; import cn.binarywang.wx.miniapp.api.impl.WxMaServiceHttpClientImpl; +import cn.binarywang.wx.miniapp.api.impl.WxMaServiceHttpComponentsImpl; import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl; import cn.binarywang.wx.miniapp.api.impl.WxMaServiceJoddHttpImpl; import cn.binarywang.wx.miniapp.api.impl.WxMaServiceOkHttpImpl; @@ -89,6 +90,9 @@ public WxMaService wxMaService(WxMaConfig wxMaConfig, WxMaMultiProperties wxMaMu case HTTP_CLIENT: wxMaService = new WxMaServiceHttpClientImpl(); break; + case HTTP_COMPONENTS: + wxMaService = new WxMaServiceHttpComponentsImpl(); + break; default: wxMaService = new WxMaServiceImpl(); break; 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 index 87fcd42f03..f99d6280ec 100644 --- 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 @@ -77,7 +77,7 @@ public static class ConfigStorage implements Serializable { /** * http客户端类型. */ - private HttpClientType httpClientType = HttpClientType.HTTP_CLIENT; + private HttpClientType httpClientType = HttpClientType.HTTP_COMPONENTS; /** * http代理主机. @@ -149,6 +149,10 @@ public enum HttpClientType { /** * JoddHttp */ - JODD_HTTP + JODD_HTTP, + /** + * HttpComponents + */ + HTTP_COMPONENTS } } 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 index 5463ec08e9..78f95380b2 100644 --- 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 @@ -2,6 +2,7 @@ import cn.binarywang.wx.miniapp.api.WxMaService; import cn.binarywang.wx.miniapp.api.impl.WxMaServiceHttpClientImpl; +import cn.binarywang.wx.miniapp.api.impl.WxMaServiceHttpComponentsImpl; import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl; import cn.binarywang.wx.miniapp.api.impl.WxMaServiceJoddHttpImpl; import cn.binarywang.wx.miniapp.api.impl.WxMaServiceOkHttpImpl; @@ -44,6 +45,9 @@ public WxMaService wxMaService(WxMaConfig wxMaConfig) { case HttpClient: wxMaService = new WxMaServiceHttpClientImpl(); break; + case HttpComponents: + wxMaService = new WxMaServiceHttpComponentsImpl(); + break; default: wxMaService = new WxMaServiceImpl(); break; 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 index 1c3e495f4e..4493b6aec5 100644 --- 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 @@ -76,7 +76,7 @@ public static class ConfigStorage { /** * http客户端类型. */ - private HttpClientType httpClientType = HttpClientType.HttpClient; + private HttpClientType httpClientType = HttpClientType.HttpComponents; /** * http代理主机. 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 index d534b98746..a51c6eaaea 100644 --- 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 @@ -8,6 +8,7 @@ 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.WxMpServiceHttpComponentsImpl; import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl; import me.chanjar.weixin.mp.api.impl.WxMpServiceJoddHttpImpl; import me.chanjar.weixin.mp.api.impl.WxMpServiceOkHttpImpl; @@ -91,6 +92,9 @@ public WxMpService wxMpService(WxMpConfigStorage configStorage, WxMpMultiPropert case HTTP_CLIENT: wxMpService = new WxMpServiceHttpClientImpl(); break; + case HTTP_COMPONENTS: + wxMpService = new WxMpServiceHttpComponentsImpl(); + break; default: wxMpService = new WxMpServiceImpl(); break; 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 index 1929e92607..3d47f71381 100644 --- 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 @@ -77,7 +77,7 @@ public static class ConfigStorage implements Serializable { /** * http客户端类型. */ - private HttpClientType httpClientType = HttpClientType.HTTP_CLIENT; + private HttpClientType httpClientType = HttpClientType.HTTP_COMPONENTS; /** * http代理主机. @@ -149,6 +149,10 @@ public enum HttpClientType { /** * JoddHttp */ - JODD_HTTP + JODD_HTTP, + /** + * HttpComponents + */ + HTTP_COMPONENTS } } 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 index 3e7a598494..334ccf7abe 100644 --- 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 @@ -4,6 +4,7 @@ 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.WxMpServiceHttpComponentsImpl; import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl; import me.chanjar.weixin.mp.api.impl.WxMpServiceJoddHttpImpl; import me.chanjar.weixin.mp.api.impl.WxMpServiceOkHttpImpl; @@ -35,6 +36,9 @@ public WxMpService wxMpService(WxMpConfigStorage configStorage, WxMpProperties w case HttpClient: wxMpService = newWxMpServiceHttpClientImpl(); break; + case HttpComponents: + wxMpService = newWxMpServiceHttpComponentsImpl(); + break; default: wxMpService = newWxMpServiceImpl(); break; @@ -60,4 +64,8 @@ private WxMpService newWxMpServiceJoddHttpImpl() { return new WxMpServiceJoddHttpImpl(); } + private WxMpService newWxMpServiceHttpComponentsImpl() { + return new WxMpServiceHttpComponentsImpl(); + } + } 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 index f3dce59a73..02ec06cd25 100644 --- 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 @@ -4,6 +4,7 @@ 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.WxQidianServiceHttpComponentsImpl; import me.chanjar.weixin.qidian.api.impl.WxQidianServiceImpl; import me.chanjar.weixin.qidian.api.impl.WxQidianServiceJoddHttpImpl; import me.chanjar.weixin.qidian.api.impl.WxQidianServiceOkHttpImpl; @@ -35,6 +36,9 @@ public WxQidianService wxQidianService(WxQidianConfigStorage configStorage, WxQi case HttpClient: wxQidianService = newWxQidianServiceHttpClientImpl(); break; + case HttpComponents: + wxQidianService = newWxQidianServiceHttpComponentsImpl(); + break; default: wxQidianService = newWxQidianServiceImpl(); break; @@ -60,4 +64,8 @@ private WxQidianService newWxQidianServiceJoddHttpImpl() { return new WxQidianServiceJoddHttpImpl(); } + private WxQidianService newWxQidianServiceHttpComponentsImpl() { + return new WxQidianServiceHttpComponentsImpl(); + } + } diff --git a/spring-boot-starters/wx-java-miniapp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/config/WxMaServiceAutoConfiguration.java b/spring-boot-starters/wx-java-miniapp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/config/WxMaServiceAutoConfiguration.java index 79c16fb053..f03d3f1493 100644 --- a/spring-boot-starters/wx-java-miniapp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/config/WxMaServiceAutoConfiguration.java +++ b/spring-boot-starters/wx-java-miniapp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/config/WxMaServiceAutoConfiguration.java @@ -2,6 +2,7 @@ import cn.binarywang.wx.miniapp.api.WxMaService; import cn.binarywang.wx.miniapp.api.impl.WxMaServiceHttpClientImpl; +import cn.binarywang.wx.miniapp.api.impl.WxMaServiceHttpComponentsImpl; import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl; import cn.binarywang.wx.miniapp.api.impl.WxMaServiceJoddHttpImpl; import cn.binarywang.wx.miniapp.api.impl.WxMaServiceOkHttpImpl; @@ -46,6 +47,9 @@ public WxMaService wxMaService(WxMaConfig wxMaConfig) { case HttpClient: wxMaService = new WxMaServiceHttpClientImpl(); break; + case HttpComponents: + wxMaService = new WxMaServiceHttpComponentsImpl(); + break; default: wxMaService = new WxMaServiceImpl(); break; diff --git a/spring-boot-starters/wx-java-miniapp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/properties/WxMaProperties.java b/spring-boot-starters/wx-java-miniapp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/properties/WxMaProperties.java index 7e88db904f..82f1500941 100644 --- a/spring-boot-starters/wx-java-miniapp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/properties/WxMaProperties.java +++ b/spring-boot-starters/wx-java-miniapp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/miniapp/properties/WxMaProperties.java @@ -88,7 +88,7 @@ public static class ConfigStorage { /** * http客户端类型. */ - private HttpClientType httpClientType = HttpClientType.HttpClient; + private HttpClientType httpClientType = HttpClientType.HttpComponents; /** * http代理主机. diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/impl/WxChannelServiceImpl.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/impl/WxChannelServiceImpl.java index 6f2c349f3f..ccd4eafc79 100644 --- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/impl/WxChannelServiceImpl.java +++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/impl/WxChannelServiceImpl.java @@ -8,7 +8,7 @@ * @author Zeyes */ @Slf4j -public class WxChannelServiceImpl extends WxChannelServiceHttpClientImpl { +public class WxChannelServiceImpl extends WxChannelServiceHttpComponentsImpl { public WxChannelServiceImpl() { } diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceHttpComponentsImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceHttpComponentsImpl.java new file mode 100644 index 0000000000..526e1ec4e7 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceHttpComponentsImpl.java @@ -0,0 +1,101 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.bean.WxMaStableAccessTokenRequest; +import cn.binarywang.wx.miniapp.config.WxMaConfig; +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.util.http.HttpClientType; +import me.chanjar.weixin.common.util.http.hc.BasicResponseHandler; +import me.chanjar.weixin.common.util.http.hc.DefaultHttpComponentsClientBuilder; +import me.chanjar.weixin.common.util.http.hc.HttpComponentsClientBuilder; +import org.apache.commons.lang3.StringUtils; +import org.apache.hc.client5.http.classic.methods.HttpGet; +import org.apache.hc.client5.http.classic.methods.HttpPost; +import org.apache.hc.client5.http.config.RequestConfig; +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.apache.hc.core5.http.ContentType; +import org.apache.hc.core5.http.HttpHost; +import org.apache.hc.core5.http.io.entity.StringEntity; + +import java.io.IOException; + +/** + * Apache Http client5 方式实现 + * + * @author zhangyl + */ +@Slf4j +public class WxMaServiceHttpComponentsImpl extends BaseWxMaServiceImpl { + private CloseableHttpClient httpClient; + private HttpHost httpProxy; + + @Override + public void initHttp() { + WxMaConfig configStorage = this.getWxMaConfig(); + HttpComponentsClientBuilder apacheHttpClientBuilder = DefaultHttpComponentsClientBuilder.get(); + + apacheHttpClientBuilder.httpProxyHost(configStorage.getHttpProxyHost()) + .httpProxyPort(configStorage.getHttpProxyPort()) + .httpProxyUsername(configStorage.getHttpProxyUsername()) + .httpProxyPassword(configStorage.getHttpProxyPassword() == null ? null : + configStorage.getHttpProxyPassword().toCharArray()); + + if (configStorage.getHttpProxyHost() != null && configStorage.getHttpProxyPort() > 0) { + this.httpProxy = new HttpHost(configStorage.getHttpProxyHost(), configStorage.getHttpProxyPort()); + } + + this.httpClient = apacheHttpClientBuilder.build(); + } + + @Override + public CloseableHttpClient getRequestHttpClient() { + return httpClient; + } + + @Override + public HttpHost getRequestHttpProxy() { + return httpProxy; + } + + @Override + public HttpClientType getRequestType() { + return HttpClientType.HTTP_COMPONENTS; + } + + @Override + protected String doGetAccessTokenRequest() throws IOException { + String url = StringUtils.isNotEmpty(this.getWxMaConfig().getAccessTokenUrl()) ? + this.getWxMaConfig().getAccessTokenUrl() : + GET_ACCESS_TOKEN_URL.replace(WxMaConfig.DEFAULT_API_HOST_URL, this.getWxMaConfig().getEffectiveApiHostUrl()); + + url = String.format(url, this.getWxMaConfig().getAppid(), this.getWxMaConfig().getSecret()); + + HttpGet httpGet = new HttpGet(url); + if (this.getRequestHttpProxy() != null) { + RequestConfig config = RequestConfig.custom().setProxy(this.getRequestHttpProxy()).build(); + httpGet.setConfig(config); + } + return getRequestHttpClient().execute(httpGet, BasicResponseHandler.INSTANCE); + } + + @Override + protected String doGetStableAccessTokenRequest(boolean forceRefresh) throws IOException { + String url = StringUtils.isNotEmpty(this.getWxMaConfig().getAccessTokenUrl()) ? + this.getWxMaConfig().getAccessTokenUrl() : + GET_STABLE_ACCESS_TOKEN.replace( + WxMaConfig.DEFAULT_API_HOST_URL, this.getWxMaConfig().getEffectiveApiHostUrl()); + + HttpPost httpPost = new HttpPost(url); + if (this.getRequestHttpProxy() != null) { + RequestConfig config = RequestConfig.custom().setProxy(this.getRequestHttpProxy()).build(); + httpPost.setConfig(config); + } + WxMaStableAccessTokenRequest wxMaAccessTokenRequest = new WxMaStableAccessTokenRequest(); + wxMaAccessTokenRequest.setAppid(this.getWxMaConfig().getAppid()); + wxMaAccessTokenRequest.setSecret(this.getWxMaConfig().getSecret()); + wxMaAccessTokenRequest.setGrantType("client_credential"); + wxMaAccessTokenRequest.setForceRefresh(forceRefresh); + + httpPost.setEntity(new StringEntity(wxMaAccessTokenRequest.toJson(), ContentType.APPLICATION_JSON)); + return getRequestHttpClient().execute(httpPost, BasicResponseHandler.INSTANCE); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImpl.java index 16478f8411..d0820a0c2e 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImpl.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImpl.java @@ -6,6 +6,6 @@ * @author Binary Wang */ @Slf4j -public class WxMaServiceImpl extends WxMaServiceHttpClientImpl { +public class WxMaServiceImpl extends WxMaServiceHttpComponentsImpl { } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImpl.java index 79c3fad266..7cef64e576 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImpl.java @@ -2,11 +2,11 @@ /** *
          - * 默认接口实现类,使用apache httpclient实现
          + * 默认接口实现类,使用apache httpClient 5实现
            * Created by Binary Wang on 2017-5-27.
            * 
          * * @author Binary Wang */ -public class WxMpServiceImpl extends WxMpServiceHttpClientImpl { +public class WxMpServiceImpl extends WxMpServiceHttpComponentsImpl { } diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenServiceHttpComponentsImpl.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenServiceHttpComponentsImpl.java new file mode 100644 index 0000000000..913c1971cb --- /dev/null +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenServiceHttpComponentsImpl.java @@ -0,0 +1,74 @@ +package me.chanjar.weixin.open.api.impl; + +import me.chanjar.weixin.common.bean.result.WxMinishopImageUploadResult; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.HttpClientType; +import me.chanjar.weixin.common.util.http.MinishopUploadRequestExecutor; +import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; +import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor; +import me.chanjar.weixin.common.util.http.hc.DefaultHttpComponentsClientBuilder; +import me.chanjar.weixin.common.util.http.hc.HttpComponentsClientBuilder; +import me.chanjar.weixin.open.api.WxOpenConfigStorage; +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.apache.hc.core5.http.HttpHost; + +import java.io.File; + +/** + * httpclient 5 实现 + * + * @author zhangyl + */ +public class WxOpenServiceHttpComponentsImpl extends WxOpenServiceAbstractImpl { + private CloseableHttpClient httpClient; + private HttpHost httpProxy; + + @Override + public void initHttp() { + WxOpenConfigStorage configStorage = this.getWxOpenConfigStorage(); + HttpComponentsClientBuilder apacheHttpClientBuilder = DefaultHttpComponentsClientBuilder.get(); + + apacheHttpClientBuilder.httpProxyHost(configStorage.getHttpProxyHost()) + .httpProxyPort(configStorage.getHttpProxyPort()) + .httpProxyUsername(configStorage.getHttpProxyUsername()) + .httpProxyPassword(configStorage.getHttpProxyPassword() == null ? null : + configStorage.getHttpProxyPassword().toCharArray()); + + if (configStorage.getHttpProxyHost() != null && configStorage.getHttpProxyPort() > 0) { + this.httpProxy = new HttpHost(configStorage.getHttpProxyHost(), configStorage.getHttpProxyPort()); + } + + this.httpClient = apacheHttpClientBuilder.build(); + + } + + @Override + public CloseableHttpClient getRequestHttpClient() { + return httpClient; + } + + @Override + public HttpHost getRequestHttpProxy() { + return httpProxy; + } + + @Override + public HttpClientType getRequestType() { + return HttpClientType.HTTP_COMPONENTS; + } + + @Override + public String get(String url, String queryParam) throws WxErrorException { + return execute(SimpleGetRequestExecutor.create(this), url, queryParam); + } + + @Override + public String post(String url, String postData) throws WxErrorException { + return execute(SimplePostRequestExecutor.create(this), url, postData); + } + + @Override + public WxMinishopImageUploadResult uploadMinishopMediaFile(String url, File file) throws WxErrorException { + return execute(MinishopUploadRequestExecutor.create(this), url, file); + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenServiceImpl.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenServiceImpl.java index c807ccdf99..83e831df70 100644 --- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenServiceImpl.java +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenServiceImpl.java @@ -3,6 +3,6 @@ /** * @author 007 */ -public class WxOpenServiceImpl extends WxOpenServiceApacheHttpClientImpl { +public class WxOpenServiceImpl extends WxOpenServiceHttpComponentsImpl { } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImpl.java index 8e795966f4..4316bafa40 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImpl.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImpl.java @@ -2,11 +2,11 @@ /** *
          - * 微信支付接口请求实现类,默认使用Apache HttpClient实现
          + * 微信支付接口请求实现类,默认使用Apache HttpClient 5实现
            * Created by Binary Wang on 2017-7-8.
            * 
          * * @author Binary Wang */ -public class WxPayServiceImpl extends WxPayServiceApacheHttpImpl { +public class WxPayServiceImpl extends WxPayServiceHttpComponentsImpl { } diff --git a/weixin-java-qidian/src/main/java/me/chanjar/weixin/qidian/api/impl/WxQidianServiceImpl.java b/weixin-java-qidian/src/main/java/me/chanjar/weixin/qidian/api/impl/WxQidianServiceImpl.java index 45e87204cb..2e1314b3b1 100644 --- a/weixin-java-qidian/src/main/java/me/chanjar/weixin/qidian/api/impl/WxQidianServiceImpl.java +++ b/weixin-java-qidian/src/main/java/me/chanjar/weixin/qidian/api/impl/WxQidianServiceImpl.java @@ -2,11 +2,11 @@ /** *
          - * 默认接口实现类,使用apache httpclient实现
          + * 默认接口实现类,使用apache httpClient 5实现
            * Created by Binary Wang on 2017-5-27.
            * 
          * * @author Binary Wang */ -public class WxQidianServiceImpl extends WxQidianServiceHttpClientImpl { +public class WxQidianServiceImpl extends WxQidianServiceHttpComponentsImpl { } From 72fa3b3301967c29031fabdad1574951a19b1c48 Mon Sep 17 00:00:00 2001 From: buaazyl Date: Fri, 9 Jan 2026 14:16:06 +0800 Subject: [PATCH 39/46] =?UTF-8?q?:art:=20#3838=20=E3=80=90=E5=BE=AE?= =?UTF-8?q?=E4=BF=A1=E6=94=AF=E4=BB=98=E3=80=91=E4=B8=BAstarter=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E4=BA=86=E9=80=80=E6=AC=BE=E7=BB=93=E6=9E=9C=E5=9B=9E?= =?UTF-8?q?=E8=B0=83=20URL=20=E7=9A=84=E9=85=8D=E7=BD=AE=E6=94=AF=E6=8C=81?= =?UTF-8?q?=EF=BC=8C=E5=85=81=E8=AE=B8=E5=BC=80=E5=8F=91=E8=80=85=E5=9C=A8?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6=E4=B8=AD=E7=BB=9F=E4=B8=80?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E9=80=80=E6=AC=BE=E9=80=9A=E7=9F=A5=E5=9C=B0?= =?UTF-8?q?=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 13 ++++++------- .../wxjava/pay/config/WxPayAutoConfiguration.java | 1 + .../wxjava/pay/properties/WxPayProperties.java | 5 +++++ .../wxjava/pay/config/WxPayAutoConfiguration.java | 1 + .../wxjava/pay/properties/WxPayProperties.java | 5 +++++ .../wxpay/bean/request/WxPayRefundRequest.java | 4 +++- .../github/binarywang/wxpay/config/WxPayConfig.java | 6 +++++- .../wxpay/service/impl/BaseWxPayServiceImpl.java | 6 ++++++ 8 files changed, 32 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 54af600beb..080b831d1e 100644 --- a/README.md +++ b/README.md @@ -133,10 +133,10 @@ **Spring Boot 配置示例:** ```properties -# 使用 HttpClient 5.x(推荐,MP/CP/Channel/QiDian 模块默认) +# 使用 HttpClient 5.x(推荐,MP/MiniApp/CP/Channel/QiDian 模块默认) wx.mp.config-storage.http-client-type=HttpComponents -# 使用 HttpClient 4.x(兼容模式,MiniApp 模块默认) +# 使用 HttpClient 4.x(兼容模式) wx.mp.config-storage.http-client-type=HttpClient # 使用 OkHttp @@ -153,11 +153,10 @@ wx.mp.config-storage.http-client-type=HTTP_COMPONENTS # 注意使用大写下 ``` **注意事项:** -1. **MiniApp 模块**已提供 `HttpComponents`(HttpClient 5.x)类型的配置选项,但当前默认仍为 HttpClient 4.x;如需启用 HttpClient 5.x,请确保所使用的集成模块(如 `wx-java-miniapp-spring-boot-starter`、`wx-java-miniapp-solon-plugin`)版本已支持该选项 -2. **MP、Channel、QiDian 模块**已完整支持 HttpClient 5.x,默认推荐使用 -3. **CP 模块**的支持情况取决于具体使用的 Starter 版本,请参考对应模块文档 -4. 如需使用 OkHttp 或 Jodd-http,需在项目中添加对应的依赖(scope为provided) -5. HttpClient 4.x 和 HttpClient 5.x 可以共存,按需配置即可 +1. **MP、MiniApp、Channel、QiDian 模块**已完整支持 HttpClient 5.x,默认推荐使用 +2. **CP 模块**的支持情况取决于具体使用的 Starter 版本,请参考对应模块文档 +3. 如需使用 OkHttp 或 Jodd-http,需在项目中添加对应的依赖(scope为provided) +4. HttpClient 4.x 和 HttpClient 5.x 可以共存,按需配置即可 --------------------------------- 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 index 94112c7d9d..3ef7456daa 100644 --- 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 @@ -47,6 +47,7 @@ public WxPayService wxPayService() { payConfig.setKeyPath(StringUtils.trimToNull(this.properties.getKeyPath())); payConfig.setUseSandboxEnv(this.properties.isUseSandboxEnv()); payConfig.setNotifyUrl(StringUtils.trimToNull(this.properties.getNotifyUrl())); + payConfig.setRefundNotifyUrl(StringUtils.trimToNull(this.properties.getRefundNotifyUrl())); //以下是apiv3以及支付分相关 payConfig.setServiceId(StringUtils.trimToNull(this.properties.getServiceId())); payConfig.setPayScoreNotifyUrl(StringUtils.trimToNull(this.properties.getPayScoreNotifyUrl())); 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 index 0b035e983e..d394fefbd1 100644 --- 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 @@ -87,6 +87,11 @@ public class WxPayProperties { */ private String notifyUrl; + /** + * 退款结果异步回调地址,通知url必须为直接可访问的url,不能携带参数. + */ + private String refundNotifyUrl; + /** * 微信支付分授权回调地址 */ 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 5a794de7e8..758fd929a1 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 @@ -51,6 +51,7 @@ public WxPayService wxPayService() { payConfig.setKeyPath(StringUtils.trimToNull(this.properties.getKeyPath())); payConfig.setUseSandboxEnv(this.properties.isUseSandboxEnv()); payConfig.setNotifyUrl(StringUtils.trimToNull(this.properties.getNotifyUrl())); + payConfig.setRefundNotifyUrl(StringUtils.trimToNull(this.properties.getRefundNotifyUrl())); //以下是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 8212e3b013..25f7d7c02e 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 @@ -64,6 +64,11 @@ public class WxPayProperties { */ private String notifyUrl; + /** + * 退款结果异步回调地址,通知url必须为直接可访问的url,不能携带参数 + */ + private String refundNotifyUrl; + /** * 微信支付分回调地址 */ diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundRequest.java index e145644d91..b0cbcf4e70 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundRequest.java @@ -230,7 +230,9 @@ public void checkAndSign(WxPayConfig config) throws WxPayException { if (StringUtils.isBlank(this.getOpUserId())) { this.setOpUserId(config.getMchId()); } - + if (StringUtils.isBlank(this.getNotifyUrl())) { + this.setNotifyUrl(config.getRefundNotifyUrl()); + } super.checkAndSign(config); } 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 b42451a666..88e544e675 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 @@ -97,9 +97,13 @@ public class WxPayConfig { */ private String subMchId; /** - * 微信支付异步回掉地址,通知url必须为直接可访问的url,不能携带参数. + * 微信支付异步回调地址,通知url必须为直接可访问的url,不能携带参数. */ private String notifyUrl; + /** + * 退款结果异步回调地址,通知url必须为直接可访问的url,不能携带参数. + */ + private String refundNotifyUrl; /** * 交易类型. *
          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 2e896cda7e..5347099a0b 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
          @@ -263,6 +263,9 @@ public WxPayRefundResult refundV2(WxPayRefundRequest request) throws WxPayExcept
           
             @Override
             public WxPayRefundV3Result refundV3(WxPayRefundV3Request request) throws WxPayException {
          +    if (StringUtils.isBlank(request.getNotifyUrl())) {
          +      request.setNotifyUrl(this.getConfig().getRefundNotifyUrl());
          +    }
               String url = String.format("%s/v3/refund/domestic/refunds", this.getPayBaseUrl());
               String response = this.postV3WithWechatpaySerial(url, GSON.toJson(request));
               return GSON.fromJson(response, WxPayRefundV3Result.class);
          @@ -270,6 +273,9 @@ public WxPayRefundV3Result refundV3(WxPayRefundV3Request request) throws WxPayEx
           
             @Override
             public WxPayRefundV3Result partnerRefundV3(WxPayPartnerRefundV3Request request) throws WxPayException {
          +    if (StringUtils.isBlank(request.getNotifyUrl())) {
          +      request.setNotifyUrl(this.getConfig().getRefundNotifyUrl());
          +    }
               if (StringUtils.isBlank(request.getSubMchid())) {
                 request.setSubMchid(this.getConfig().getSubMchId());
               }
          
          From e27325f58090db4e8801dfb433795668f1fe23bd Mon Sep 17 00:00:00 2001
          From: Noctis_nkt 
          Date: Fri, 9 Jan 2026 14:18:23 +0800
          Subject: [PATCH 40/46] =?UTF-8?q?=E3=80=90=E4=BC=81=E4=B8=9A=E5=BE=AE?=
           =?UTF-8?q?=E4=BF=A1=E3=80=91=E6=96=B0=E5=A2=9E=E7=AE=A1=E7=90=86=E8=A1=A8?=
           =?UTF-8?q?=E6=A0=BC=E5=86=85=E5=AE=B9=E7=9B=B8:new:=20#3837=20=E3=80=90?=
           =?UTF-8?q?=E4=BC=81=E4=B8=9A=E5=BE=AE=E4=BF=A1=E3=80=91=E5=BE=AE=E6=96=87?=
           =?UTF-8?q?=E6=A1=A3=EF=BC=88WeDoc=EF=BC=89=E6=9C=8D=E5=8A=A1=E6=96=B0?=
           =?UTF-8?q?=E5=A2=9E=E4=BA=86=E4=B8=89=E4=B8=AA=E7=AE=A1=E7=90=86=E8=A1=A8?=
           =?UTF-8?q?=E6=A0=BC=E5=86=85=E5=AE=B9=E7=9A=84=20API=20=E6=8E=A5=E5=8F=A3?=
           =?UTF-8?q?=EF=BC=8C=E5=8C=85=E6=8B=AC=E6=89=B9=E9=87=8F=E6=9B=B4=E6=96=B0?=
           =?UTF-8?q?=E8=A1=A8=E6=A0=BC=E5=86=85=E5=AE=B9=E3=80=81=E8=8E=B7=E5=8F=96?=
           =?UTF-8?q?=E8=A1=A8=E6=A0=BC=E8=A1=8C=E5=88=97=E4=BF=A1=E6=81=AF=E4=BB=A5?=
           =?UTF-8?q?=E5=8F=8A=E8=8E=B7=E5=8F=96=E6=8C=87=E5=AE=9A=E8=8C=83=E5=9B=B4?=
           =?UTF-8?q?=E7=9A=84=E8=A1=A8=E6=A0=BC=E6=95=B0=E6=8D=AE?=
          MIME-Version: 1.0
          Content-Type: text/plain; charset=UTF-8
          Content-Transfer-Encoding: 8bit
          
          ---
           .../weixin/cp/api/WxCpOaWeDocService.java     |  49 ++++
           .../cp/api/impl/WxCpOaWeDocServiceImpl.java   |  23 ++
           .../doc/WxCpDocSheetBatchUpdateRequest.java   | 199 ++++++++++++++
           .../doc/WxCpDocSheetBatchUpdateResponse.java  | 126 +++++++++
           .../cp/bean/oa/doc/WxCpDocSheetData.java      | 260 ++++++++++++++++++
           .../oa/doc/WxCpDocSheetGetDataRequest.java    |  64 +++++
           .../bean/oa/doc/WxCpDocSheetProperties.java   |  79 ++++++
           .../weixin/cp/constant/WxCpApiPathConsts.java |  15 +
           8 files changed, 815 insertions(+)
           create mode 100644 weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/doc/WxCpDocSheetBatchUpdateRequest.java
           create mode 100644 weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/doc/WxCpDocSheetBatchUpdateResponse.java
           create mode 100644 weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/doc/WxCpDocSheetData.java
           create mode 100644 weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/doc/WxCpDocSheetGetDataRequest.java
           create mode 100644 weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/doc/WxCpDocSheetProperties.java
          
          diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOaWeDocService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOaWeDocService.java
          index 1356c839b2..d63d32694a 100644
          --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOaWeDocService.java
          +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOaWeDocService.java
          @@ -78,4 +78,53 @@ public interface WxCpOaWeDocService {
              * @throws WxErrorException the wx error exception
              */
             WxCpDocShare docShare(@NonNull String docId) throws WxErrorException;
          +
          +  /**
          +   * 编辑表格内容
          +   * 该接口可以对一个在线表格批量执行多个更新操作
          +   * 

          + * 注意: + * 1.批量更新请求中的各个操作会逐个按顺序执行,直到全部执行完成则请求返回,或者其中一个操作报错则不再继续执行后续的操作 + * 2.每一个更新操作在执行之前都会做请求校验(包括权限校验、参数校验等等),如果校验未通过则该更新操作会报错并返回,不再执行后续操作 + * 3.单次批量更新请求的操作数量 <= 5 + *

          + * 请求方式:POST(HTTPS) + * 请求地址: https://qyapi.weixin.qq.com/cgi-bin/wedoc/spreadsheet/batch_update?access_token=ACCESS_TOKEN + * + * @param request 编辑表格内容请求参数 + * @return 编辑表格内容批量更新的响应结果 + * @throws WxErrorException the wx error exception + */ + WxCpDocSheetBatchUpdateResponse docBatchUpdate(@NonNull WxCpDocSheetBatchUpdateRequest request) throws WxErrorException; + + /** + * 获取表格行列信息 + * 该接口用于获取在线表格的工作表、行数、列数等。 + *

          + * 请求方式:POST(HTTPS) + * 请求地址: https://qyapi.weixin.qq.com/cgi-bin/wedoc/spreadsheet/get_sheet_properties?access_token=ACCESS_TOKEN + * + * @param docId 在线表格的docid + * @return 返回表格行列信息 + * @throws WxErrorException + */ + WxCpDocSheetProperties getSheetProperties(@NonNull String docId) throws WxErrorException; + + + /** + * 本接口用于获取指定范围内的在线表格信息,单次查询的范围大小需满足以下限制: + *

          + * 查询范围行数 <=1000 + * 查询范围列数 <=200 + * 范围内的总单元格数量 <=10000 + *

          + * 请求方式:POST(HTTPS) + * 请求地址: https://qyapi.weixin.qq.com/cgi-bin/wedoc/spreadsheet/get_sheet_range_data?access_token=ACCESS_TOKEN + * + * @param request 获取指定范围内的在线表格信息请求参数 + * @return 返回指定范围内的在线表格信息 + * @throws WxErrorException + */ + WxCpDocSheetData getSheetRangeData(@NonNull WxCpDocSheetGetDataRequest request) throws WxErrorException; + } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpOaWeDocServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpOaWeDocServiceImpl.java index 81de32453d..fc5379dc73 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpOaWeDocServiceImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpOaWeDocServiceImpl.java @@ -63,4 +63,27 @@ public WxCpDocShare docShare(@NonNull String docId) throws WxErrorException { String responseContent = this.cpService.post(apiUrl, jsonObject.toString()); return WxCpDocShare.fromJson(responseContent); } + + @Override + public WxCpDocSheetBatchUpdateResponse docBatchUpdate(@NonNull WxCpDocSheetBatchUpdateRequest request) throws WxErrorException { + String apiUrl = this.cpService.getWxCpConfigStorage().getApiUrl(WEDOC_SPREADSHEET_BATCH_UPDATE); + String responseContent = this.cpService.post(apiUrl, request.toJson()); + return WxCpDocSheetBatchUpdateResponse.fromJson(responseContent); + } + + @Override + public WxCpDocSheetProperties getSheetProperties(@NonNull String docId) throws WxErrorException { + String apiUrl = this.cpService.getWxCpConfigStorage().getApiUrl(WEDOC_SPREADSHEET_GET_SHEET_PROPERTIES); + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("docid", docId); + String responseContent = this.cpService.post(apiUrl, jsonObject.toString()); + return WxCpDocSheetProperties.fromJson(responseContent); + } + + @Override + public WxCpDocSheetData getSheetRangeData(@NonNull WxCpDocSheetGetDataRequest request) throws WxErrorException { + String apiUrl = this.cpService.getWxCpConfigStorage().getApiUrl(WEDOC_SPREADSHEET_GET_SHEET_RANGE_DATA); + String responseContent = this.cpService.post(apiUrl, request.toJson()); + return WxCpDocSheetData.fromJson(responseContent); + } } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/doc/WxCpDocSheetBatchUpdateRequest.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/doc/WxCpDocSheetBatchUpdateRequest.java new file mode 100644 index 0000000000..37c42717e7 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/doc/WxCpDocSheetBatchUpdateRequest.java @@ -0,0 +1,199 @@ +package me.chanjar.weixin.cp.bean.oa.doc; + +import java.io.Serializable; +import java.util.List; + +import com.google.gson.annotations.SerializedName; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.experimental.Accessors; +import me.chanjar.weixin.cp.bean.oa.doc.WxCpDocSheetData.GridData; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +/** + * 编辑表格内容请求 + * + * @author zhongying + * @since 2026-01-07 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +public class WxCpDocSheetBatchUpdateRequest implements Serializable { + private static final long serialVersionUID = 584565591133421347L; + + /** + * 文档的docid.必填 + */ + @SerializedName("docid") + private String docId; + + /** + * 更新操作列表.必填 + */ + @SerializedName("requests") + private List requests; + + /** + * From json wx cp doc sheet batch update request. + * + * @param json the json + * @return the wx cp doc sheet batch update request + */ + public static WxCpDocSheetBatchUpdateRequest fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpDocSheetBatchUpdateRequest.class); + } + + /** + * To json string. + * + * @return the string + */ + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + /** + * 更新操作 + */ + @Getter + @Setter + public static class Request implements Serializable { + private static final long serialVersionUID = 253933038745894628L; + + /** + * 新增工作表 + */ + @SerializedName("add_sheet_request") + private AddSheetRequest addSheetRequest; + + /** + * 删除工作表请求 + */ + @SerializedName("delete_sheet_request") + private DeleteSheetRequest deleteSheetRequest; + + /** + * 更新范围内单元格内容 + */ + @SerializedName("update_range_request") + private UpdateRangeRequest updateRangeRequest; + + /** + * 删除表格连续的行或列 + */ + @SerializedName("delete_dimension_request") + private DeleteDimensionRequest deleteDimensionRequest; + + /** + * 新增工作表,新增需满足以下限制 + * 范围列数 <=200 + * 范围内的总单元格数量 <=10000 + */ + @Getter + @Setter + public static class AddSheetRequest implements Serializable { + private static final long serialVersionUID = 523704967699486288L; + + /** + * 工作表名称 + */ + @SerializedName("title") + private String title; + + /** + * 新增工作表的初始行数 + */ + @SerializedName("row_count") + private int rowCount; + + /** + * 新增工作表的初始列数 + */ + @SerializedName("column_count") + private int columnCount; + } + + /** + * 删除工作表请求 + */ + @Getter + @Setter + public static class DeleteSheetRequest implements Serializable { + private static final long serialVersionUID = 767974765396168274L; + + /** + * 工作表唯一标识 + */ + @SerializedName("sheet_id") + private String sheetId; + } + + /** + * 更新范围内单元格内容 + */ + @Getter + @Setter + public static class UpdateRangeRequest implements Serializable { + private static final long serialVersionUID = 433859595039061888L; + + /** + * 工作表唯一标识 + */ + @SerializedName("sheet_id") + private String sheetId; + + /** + * 表格数据 + */ + @SerializedName("grid_data") + private GridData gridData; + } + + /** + * 删除表格连续的行或列 + * 注意: + * 1.该操作会导致表格缩表 + * 2.删除的范围遵循 左闭右开 ———— [start_index,end_index) ,如果 end_index <= start_index + * 则该请求报错。 + */ + @Getter + @Setter + public static class DeleteDimensionRequest implements Serializable { + private static final long serialVersionUID = 107245521502978033L; + + /** + * 工作表唯一标识 + */ + @SerializedName("sheet_id") + private String sheetId; + + /** + * 删除的维度类型. + * ROW:行 + * COLUMN:列 + */ + @SerializedName("dimension") + private String dimension; + + /** + * 删除行列的起始序号(从1开始) + */ + @SerializedName("start_index") + private int startIndex; + + /** + * 删除行列的终止序号(从1开始) + */ + @SerializedName("end_index") + private int endIndex; + } + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/doc/WxCpDocSheetBatchUpdateResponse.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/doc/WxCpDocSheetBatchUpdateResponse.java new file mode 100644 index 0000000000..8e31e9022f --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/doc/WxCpDocSheetBatchUpdateResponse.java @@ -0,0 +1,126 @@ +package me.chanjar.weixin.cp.bean.oa.doc; + +import java.io.Serializable; + +import com.google.gson.annotations.SerializedName; + +import lombok.Data; +import lombok.Getter; +import lombok.Setter; +import me.chanjar.weixin.cp.bean.oa.doc.WxCpDocSheetProperties.Properties; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +/** + * 更新操作(UpdateRequest])对应的响应结构体类型 + * + * @author zhongying + * @since 2026-01-08 + */ +@Data +public class WxCpDocSheetBatchUpdateResponse implements Serializable { + private static final long serialVersionUID = 781694717017131015L; + + /** + * 新增工作表响应 + */ + @SerializedName("add_sheet_response") + private AddSheetResponse addSheetResponse; + + /** + * 删除工作表响应 + */ + @SerializedName("delete_sheet_response") + private DeleteSheetResponse deleteSheetResponse; + + /** + * 更新范围内单元格内容响应 + */ + @SerializedName("update_range_response") + private UpdateRangeResponse updateRangeResponse; + + /** + * 删除表格连续的行或列响应 + */ + @SerializedName("delete_dimension_response") + private DeleteDimensionResponse deleteDimensionResponse; + + /** + * 从 JSON 字符串反序列化为 WxCpDocSheetBatchUpdateResponse 对象。 + * + * @param json the json + * @return 反序列化得到的 WxCpDocSheetBatchUpdateResponse 对象 + */ + public static WxCpDocSheetBatchUpdateResponse fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpDocSheetBatchUpdateResponse.class); + } + + /** + * To json string. + * + * @return the string + */ + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + /** + * 新增工作表响应 + */ + @Getter + @Setter + public static class AddSheetResponse implements Serializable { + private static final long serialVersionUID = 618814209485621336L; + + /** + * 新增子表的属性 + */ + @SerializedName("properties") + private Properties properties; + } + + /** + * 删除工作表响应 + */ + @Getter + @Setter + public static class DeleteSheetResponse implements Serializable { + private static final long serialVersionUID = 625927337093938411L; + + /** + * 被删除的工作表的唯一标识 + */ + @SerializedName("sheet_id") + private String sheetId; + } + + /** + * 更新范围内单元格内容响应 + */ + @Getter + @Setter + public static class UpdateRangeResponse implements Serializable { + private static final long serialVersionUID = 180842641209787414L; + + /** + * 数据更新的成功的单元格数量 + */ + @SerializedName("updated_cells") + private int updatedCells; + } + + /** + * 删除表格连续的行(或列),请求响应体结构 + */ + @Getter + @Setter + public static class DeleteDimensionResponse implements Serializable { + private static final long serialVersionUID = 107245521502978033L; + + /** + * 被删除的行数(或列数) + */ + @SerializedName("deleted") + private int deleted; + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/doc/WxCpDocSheetData.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/doc/WxCpDocSheetData.java new file mode 100644 index 0000000000..15e76430b7 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/doc/WxCpDocSheetData.java @@ -0,0 +1,260 @@ +package me.chanjar.weixin.cp.bean.oa.doc; + +import java.io.Serializable; +import java.util.List; + +import com.google.gson.annotations.SerializedName; + +import lombok.Data; +import lombok.Getter; +import lombok.Setter; +import me.chanjar.weixin.cp.bean.WxCpBaseResp; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +/** + * 获取表格数据 + * + * @author zhongying + * @since 2026-01-07 + */ +@Data +public class WxCpDocSheetData extends WxCpBaseResp implements Serializable { + private static final long serialVersionUID = 498054945993671723L; + + /** + * 返回data + */ + @SerializedName("grid_data") + private GridData gridData; + + /** + * From json sheet data. + * + * @param json the json + * @return the sheet data + */ + public static WxCpDocSheetData fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpDocSheetData.class); + } + + /** + * To json string. + * + * @return the string + */ + @Override + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + /** + * 表格数据 + */ + @Getter + @Setter + public static class GridData implements Serializable { + private static final long serialVersionUID = 389887488098521821L; + + /** + * 起始行编号 (从0开始计算) + */ + @SerializedName("start_row") + private int startRow; + + /** + * 起始列编号 (从0开始计算) + */ + @SerializedName("start_column") + private int startColumn; + + /** + * 各行的数据 + */ + @SerializedName("rows") + private List rows; + + /** + * 行数据 + */ + @Getter + @Setter + public static class RowData implements Serializable { + private static final long serialVersionUID = 225648868492722337L; + + /** + * 各个单元格的数据内容 + */ + @SerializedName("values") + private List values; + + /** + * 单元格的信息 + */ + @Getter + @Setter + public static class CellData implements Serializable { + private static final long serialVersionUID = 656471192719707304L; + + /** + * 单元格的数据内容 + */ + @SerializedName("cell_value") + private CellValue cellValue; + + /** + * 单元格的样式信息 + */ + @SerializedName("cell_format") + private CellFormat cellFormat; + + /** + * 单元格的数据内容,暂时只支持文本、链接,一个CellValue中只能选填一个字段 + */ + @Getter + @Setter + public static class CellValue implements Serializable { + private static final long serialVersionUID = 656471192719707304L; + + /** + * 文本 + */ + @SerializedName("text") + private String text; + + /** + * 链接 + */ + @SerializedName("link") + private Link link; + + /** + * 链接 + */ + @Getter + @Setter + public static class Link implements Serializable { + private static final long serialVersionUID = 912896452879347178L; + + /** + * 链接url + */ + @SerializedName("url") + private String url; + + /** + * 链接标题 + */ + @SerializedName("text") + private String text; + } + } + + /** + * 单元格的样式信息 + */ + @Getter + @Setter + public static class CellFormat implements Serializable { + private static final long serialVersionUID = 656471192719707304L; + + /** + * 文字样式 + */ + + @SerializedName("text_format") + private TextFormat textFormat; + + /** + * 文字样式 + */ + @Getter + @Setter + public static class TextFormat implements Serializable { + private static final long serialVersionUID = 184358104921122206L; + + /** + * 字体 + */ + @SerializedName("font") + private String font; + + /** + * 字体大小,最大72 + */ + @SerializedName("font_size") + private int fontSize; + + /** + * 字体加粗 + */ + @SerializedName("bold") + private boolean bold; + + /** + * 斜体 + */ + @SerializedName("italic") + private boolean italic; + + /** + * 字体删除线 + */ + @SerializedName("strikethrough") + private boolean strikethrough; + + /** + * 字体下划线 + */ + @SerializedName("underline") + private boolean underline; + + /** + * 字体颜色 + */ + @SerializedName("color") + private Color color; + + /** + * 字体颜色 + */ + @Getter + @Setter + public static class Color implements Serializable { + private static final long serialVersionUID = 140418085882147315L; + + /** + * 红色,取值范围:[0,255] + */ + @SerializedName("red") + private int red; + + /** + * 绿色,取值范围:[0,255] + */ + @SerializedName("green") + private int green; + + /** + * 蓝色,取值范围:[0,255] + */ + @SerializedName("blue") + private int blue; + + /** + * alpha通道,取值范围:[0,255],默认值为255完全不透明 + */ + @SerializedName("alpha") + private int alpha; + + } + + } + } + + } + + } + + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/doc/WxCpDocSheetGetDataRequest.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/doc/WxCpDocSheetGetDataRequest.java new file mode 100644 index 0000000000..df2907aa31 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/doc/WxCpDocSheetGetDataRequest.java @@ -0,0 +1,64 @@ +package me.chanjar.weixin.cp.bean.oa.doc; + +import java.io.Serializable; + +import com.google.gson.annotations.SerializedName; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +/** + * 获取表格数据请求 + * + * @author zhongying + * @since 2026-01-07 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +public class WxCpDocSheetGetDataRequest implements Serializable { + private static final long serialVersionUID = 827718590347822812L; + + /** + * 在线表格唯一标识.必填 + */ + @SerializedName("docid") + private String docId; + + /** + * 工作表ID,工作表的唯一标识.必填 + */ + @SerializedName("sheet_id") + private String sheetId; + + /** + * 查询的范围,格式为"A1:B2",表示获取A1到B2单元格的数据.必填 + */ + @SerializedName("range") + private String range; + + /** + * From json to {@link WxCpDocSheetGetDataRequest}. + * + * @param json the json string representing {@link WxCpDocSheetGetDataRequest} + * @return the {@link WxCpDocSheetGetDataRequest} object + */ + public static WxCpDocSheetGetDataRequest fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpDocSheetGetDataRequest.class); + } + + /** + * To json string. + * + * @return the string + */ + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/doc/WxCpDocSheetProperties.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/doc/WxCpDocSheetProperties.java new file mode 100644 index 0000000000..6e67965fe0 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/doc/WxCpDocSheetProperties.java @@ -0,0 +1,79 @@ +package me.chanjar.weixin.cp.bean.oa.doc; + +import java.io.Serializable; +import java.util.List; + +import com.google.gson.annotations.SerializedName; + +import lombok.Data; +import lombok.Getter; +import lombok.Setter; +import me.chanjar.weixin.cp.bean.WxCpBaseResp; +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +/** + * 获取表格行列信息 + * + * @author zhongying + * @since 2026-01-07 + */ +@Data +public class WxCpDocSheetProperties extends WxCpBaseResp implements Serializable { + private static final long serialVersionUID = 666707252457243065L; + + @SerializedName("properties") + private List properties; + + /** + * From json sheet properties. + * + * @param json the json + * @return the sheet properties + */ + public static WxCpDocSheetProperties fromJson(String json) { + return WxCpGsonBuilder.create().fromJson(json, WxCpDocSheetProperties.class); + } + + /** + * To json string. + * + * @return the string + */ + public String toJson() { + return WxCpGsonBuilder.create().toJson(this); + } + + /** + * 工作表属性 + */ + @Getter + @Setter + public static class Properties implements Serializable { + private static final long serialVersionUID = 640301090538839892L; + + /** + * 工作表ID,工作表的唯一标识 + */ + @SerializedName("sheet_id") + private String sheetId; + + /** + * 工作表名称 + */ + @SerializedName("title") + private String title; + + /** + * 表格的总行数 + */ + @SerializedName("row_count") + private int rowCount; + + /** + * 表格的总列数 + */ + @SerializedName("column_count") + private int columnCount; + + } +} 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 ade813ef5c..82bb811b3c 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 @@ -575,6 +575,21 @@ interface Oa { */ String WEDOC_DOC_SHARE = "/cgi-bin/wedoc/doc_share"; + /** + * The constant WEDOC_SPREADSHEET_BATCH_UPDATE. + */ + String WEDOC_SPREADSHEET_BATCH_UPDATE = "/cgi-bin/wedoc/spreadsheet/batch_update"; + + /** + * The constant WEDOC_SPREADSHEET_GET_SHEET_PROPERTIES. + */ + String WEDOC_SPREADSHEET_GET_SHEET_PROPERTIES = "/cgi-bin/wedoc/spreadsheet/get_sheet_properties"; + + /** + * The constant WEDOC_SPREADSHEET_GET_SHEET_RANGE_DATA. + */ + String WEDOC_SPREADSHEET_GET_SHEET_RANGE_DATA = "/cgi-bin/wedoc/spreadsheet/get_sheet_range_data"; + /** * 邮件 * https://developer.work.weixin.qq.com/document/path/95486 From 397bbe04c4376ab6f73717acd10a2558c6728906 Mon Sep 17 00:00:00 2001 From: buaazyl Date: Sat, 10 Jan 2026 14:23:38 +0800 Subject: [PATCH 41/46] =?UTF-8?q?:art:=20#3844=20=E3=80=90=E5=9F=BA?= =?UTF-8?q?=E7=A1=80=E6=9E=B6=E6=9E=84=E3=80=91=E5=B0=86=20httpclient4=20?= =?UTF-8?q?=E4=BE=9D=E8=B5=96=E7=9A=84=20scope=20=E4=BB=8E=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E7=9A=84=20compile=20=E6=94=B9=E4=B8=BA=20provided?= =?UTF-8?q?=EF=BC=8C=E4=BB=A5=E4=BE=BF=E8=AE=A9=E4=BD=BF=E7=94=A8=E8=80=85?= =?UTF-8?q?=E5=8F=AF=E4=BB=A5=E6=9B=B4=E7=81=B5=E6=B4=BB=E5=9C=B0=E9=80=89?= =?UTF-8?q?=E6=8B=A9=20HTTP=20=E5=AE=A2=E6=88=B7=E7=AB=AF=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/HTTPCLIENT_UPGRADE_GUIDE.md | 10 +++++----- pom.xml | 2 +- weixin-java-channel/pom.xml | 10 ++++++++++ weixin-java-common/pom.xml | 2 ++ weixin-java-cp/pom.xml | 5 +++++ weixin-java-miniapp/pom.xml | 10 ++++++++++ weixin-java-mp/pom.xml | 10 ++++++++++ weixin-java-open/pom.xml | 10 ++++++++++ weixin-java-pay/pom.xml | 8 ++++++++ weixin-java-qidian/pom.xml | 5 +++++ 10 files changed, 66 insertions(+), 6 deletions(-) diff --git a/docs/HTTPCLIENT_UPGRADE_GUIDE.md b/docs/HTTPCLIENT_UPGRADE_GUIDE.md index 945341966e..5cabb10674 100644 --- a/docs/HTTPCLIENT_UPGRADE_GUIDE.md +++ b/docs/HTTPCLIENT_UPGRADE_GUIDE.md @@ -43,8 +43,8 @@ ### 对现有项目 - **向后兼容**:不需要修改任何代码 -- HttpClient 4.x 依然可用,项目同时包含两个版本的依赖 -- 如果希望继续使用 HttpClient 4.x,只需在配置中显式指定 +- 如果希望继续使用 HttpClient 4.x,只需在配置中显式指定,pay 模块会自动包含 httpclient4 依赖(因为某些接口必须使用 httpclient4) + 其他模块(mp、miniapp、cp、open、channel、qidian)如果需要使用 httpclient4,必须显式在项目中添加 httpclient4 依赖 ## 迁移步骤 @@ -94,10 +94,10 @@ WxMpService wxMpService = new WxMpServiceHttpClientImpl(); A: 不会。项目保持完全向后兼容,HttpClient 4.x 的所有实现都保持不变。 ### Q: 我需要修改代码吗? -A: 大多数情况下不需要。如果希望继续使用 HttpClient 4.x,只需在配置中指定 `http-client-type=HttpClient` 即可。 +A: 大多数情况下不需要。如果希望继续使用 HttpClient 4.x,只需在配置中指定 `http-client-type=HttpClient` ,并引入 HttpClient 4.x 依赖即可。 ### Q: 我可以在同一个项目中同时使用两个版本吗? -A: 可以。不同的模块可以配置使用不同的 HTTP 客户端。例如,MP 模块使用 HttpClient 5.x,MiniApp 模块默认使用 HttpClient 4.x,但也可以按需配置为 HttpClient 5.x。 +A: 可以。不同的模块可以配置使用不同的 HTTP 客户端。例如,MP 模块使用 HttpClient 5.x,pay 模块部分接口仍使用 HttpClient 4.x,但也可以按需配置为 HttpClient 5.x。 ### Q: 如何排除不需要的依赖? A: 如果只想使用一个版本,可以在 `pom.xml` 中排除另一个: @@ -196,4 +196,4 @@ weixin-java-common/ - ✅ **推荐使用 HttpClient 5.x**:性能更好,功能更强 - ✅ **向后兼容**:可以继续使用 HttpClient 4.x - ✅ **灵活配置**:支持多种 HTTP 客户端,按需选择 -- ✅ **平滑迁移**:无需修改代码,仅需配置即可 +- ✅ **平滑迁移**:无需修改代码,仅需配置,若不使用 HttpClient 5.x ,引入其他依赖即可 diff --git a/pom.xml b/pom.xml index 5740a27790..d58a677b28 100644 --- a/pom.xml +++ b/pom.xml @@ -136,7 +136,7 @@ UTF-8 4.5.13 - 5.5 + 5.5.2 9.4.57.v20241219 diff --git a/weixin-java-channel/pom.xml b/weixin-java-channel/pom.xml index 13dd26fa98..c15ea302e9 100644 --- a/weixin-java-channel/pom.xml +++ b/weixin-java-channel/pom.xml @@ -29,6 +29,16 @@ jodd-http provided + + org.apache.httpcomponents + httpclient + provided + + + org.apache.httpcomponents + httpmime + provided + org.apache.httpcomponents.client5 httpclient5 diff --git a/weixin-java-common/pom.xml b/weixin-java-common/pom.xml index 9032af97da..68826ca2d3 100644 --- a/weixin-java-common/pom.xml +++ b/weixin-java-common/pom.xml @@ -34,6 +34,7 @@ org.apache.httpcomponents httpclient + provided commons-logging @@ -44,6 +45,7 @@ org.apache.httpcomponents httpmime + provided diff --git a/weixin-java-cp/pom.xml b/weixin-java-cp/pom.xml index 6c47176afc..6f8ab756a0 100644 --- a/weixin-java-cp/pom.xml +++ b/weixin-java-cp/pom.xml @@ -30,6 +30,11 @@ okhttp provided + + org.apache.httpcomponents + httpclient + provided + org.apache.httpcomponents.client5 httpclient5 diff --git a/weixin-java-miniapp/pom.xml b/weixin-java-miniapp/pom.xml index 340a15e58e..4f5378727e 100644 --- a/weixin-java-miniapp/pom.xml +++ b/weixin-java-miniapp/pom.xml @@ -31,6 +31,16 @@ okhttp provided + + org.apache.httpcomponents + httpclient + provided + + + org.apache.httpcomponents + httpmime + provided + org.apache.httpcomponents.client5 httpclient5 diff --git a/weixin-java-mp/pom.xml b/weixin-java-mp/pom.xml index c33aaeab86..755695d0c1 100644 --- a/weixin-java-mp/pom.xml +++ b/weixin-java-mp/pom.xml @@ -31,6 +31,16 @@ okhttp provided + + org.apache.httpcomponents + httpclient + provided + + + org.apache.httpcomponents + httpmime + provided + org.apache.httpcomponents.client5 httpclient5 diff --git a/weixin-java-open/pom.xml b/weixin-java-open/pom.xml index 4c34786dad..6720cd2b30 100644 --- a/weixin-java-open/pom.xml +++ b/weixin-java-open/pom.xml @@ -48,6 +48,16 @@ okhttp provided + + org.apache.httpcomponents + httpclient + provided + + + org.apache.httpcomponents + httpmime + provided + org.apache.httpcomponents.client5 httpclient5 diff --git a/weixin-java-pay/pom.xml b/weixin-java-pay/pom.xml index 9bf6d49a7a..43851df85d 100644 --- a/weixin-java-pay/pom.xml +++ b/weixin-java-pay/pom.xml @@ -34,6 +34,14 @@ jodd-util 6.1.0 + + org.apache.httpcomponents + httpclient + + + org.apache.httpcomponents + httpmime + org.apache.httpcomponents.client5 httpclient5 diff --git a/weixin-java-qidian/pom.xml b/weixin-java-qidian/pom.xml index 293a4655cf..0752ce69d0 100644 --- a/weixin-java-qidian/pom.xml +++ b/weixin-java-qidian/pom.xml @@ -31,6 +31,11 @@ okhttp provided + + org.apache.httpcomponents + httpclient + provided + org.apache.httpcomponents.client5 httpclient5 From 5947f2480585acdb5e735de10c4263534a732b36 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Tue, 13 Jan 2026 20:08:02 +0800 Subject: [PATCH 42/46] =?UTF-8?q?:art:=20=E6=9B=B4=E6=AD=A3BusinessOperati?= =?UTF-8?q?onTransferRequest=E4=B8=ADtransferSceneId=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E7=9A=84=E6=96=87=E6=A1=A3=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wxpay/bean/transfer/BusinessOperationTransferRequest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/BusinessOperationTransferRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/BusinessOperationTransferRequest.java index 60b8edaf40..0129798ed9 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/BusinessOperationTransferRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/BusinessOperationTransferRequest.java @@ -40,7 +40,9 @@ public class BusinessOperationTransferRequest implements Serializable { /** * 转账场景ID - * 必须,该笔转账使用的转账场景,可前往“商户平台-产品中心-商家转账”中申请。如:1000(现金营销),1006(企业报销)等 + * 必须,该笔转账使用的转账场景,可前往"商户平台-产品中心-商家转账"中申请。 + * 运营工具场景ID如:2001(现金营销)、2002(佣金报酬)、2003(推广奖励)等 + * 可使用 {@link com.github.binarywang.wxpay.constant.WxPayConstants.OperationSceneId} 中定义的常量 */ @SerializedName("transfer_scene_id") private String transferSceneId; From 3b5ead664cc6e7ef71c1890e71b539aa684a818c Mon Sep 17 00:00:00 2001 From: Binary Wang Date: Wed, 14 Jan 2026 13:56:50 +0800 Subject: [PATCH 43/46] =?UTF-8?q?:new:=20#3850=20=E3=80=90=E8=A7=86?= =?UTF-8?q?=E9=A2=91=E5=8F=B7=E3=80=91=E5=B0=8F=E5=BA=97=20SDK=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E7=B1=BB=E7=9B=AE=E6=9D=83=E9=99=90=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E5=92=8C=E5=95=86=E5=93=81=20SPU=20=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=E6=89=A9=E5=B1=95=EF=BC=88by=20cchengg=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../channel/api/WxChannelCategoryService.java | 18 +++++--- .../impl/WxChannelCategoryServiceImpl.java | 27 ++++++------ .../bean/category/CategoryDetailResult.java | 3 ++ .../bean/category/RelationCategoryItem.java | 41 +++++++++++++++++++ .../category/RelationCategoryRequest.java | 28 +++++++++++++ .../category/RelationCategoryResponse.java | 25 +++++++++++ .../channel/bean/product/SkuFastInfo.java | 8 ++++ .../weixin/channel/bean/product/SkuInfo.java | 4 ++ .../channel/bean/product/SpuFastInfo.java | 24 +++++++++++ .../weixin/channel/bean/product/SpuInfo.java | 4 ++ .../bean/product/TimingOnSaleInfo.java | 36 ++++++++++++++++ .../constant/WxChannelApiUrlConstants.java | 2 + .../WxChannelCategoryServiceImplTest.java | 10 +++++ 13 files changed, 212 insertions(+), 18 deletions(-) create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/category/RelationCategoryItem.java create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/category/RelationCategoryRequest.java create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/category/RelationCategoryResponse.java create mode 100644 weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/product/TimingOnSaleInfo.java diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/WxChannelCategoryService.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/WxChannelCategoryService.java index 0b357a5d1c..ad86697614 100644 --- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/WxChannelCategoryService.java +++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/WxChannelCategoryService.java @@ -6,11 +6,7 @@ import me.chanjar.weixin.channel.bean.audit.AuditResponse; import me.chanjar.weixin.channel.bean.audit.CategoryAuditInfo; import me.chanjar.weixin.channel.bean.base.WxChannelBaseResponse; -import me.chanjar.weixin.channel.bean.category.CategoryDetailResult; -import me.chanjar.weixin.channel.bean.category.CategoryQualificationResponse; -import me.chanjar.weixin.channel.bean.category.PassCategoryResponse; -import me.chanjar.weixin.channel.bean.category.ShopCategory; -import me.chanjar.weixin.channel.bean.category.ShopCategoryResponse; +import me.chanjar.weixin.channel.bean.category.*; import me.chanjar.weixin.common.error.WxErrorException; /** @@ -121,4 +117,16 @@ AuditApplyResponse addCategory(String level1, String level2, String level3, List * @throws WxErrorException 异常 */ PassCategoryResponse listPassCategory() throws WxErrorException; + + /** + * 获取店铺的类目权限列表 + * + * @param isFilterStatus 是否按状态筛选 + * @param status 类目状态(当 isFilterStatus 为 true 时有效) + * @return 类目权限列表 + * + * @throws WxErrorException 异常 + */ + RelationCategoryResponse listRelationCategory(Boolean isFilterStatus, Integer status) throws WxErrorException; + } diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/impl/WxChannelCategoryServiceImpl.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/impl/WxChannelCategoryServiceImpl.java index 23cd839848..7070ab9298 100644 --- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/impl/WxChannelCategoryServiceImpl.java +++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/impl/WxChannelCategoryServiceImpl.java @@ -1,13 +1,5 @@ package me.chanjar.weixin.channel.api.impl; -import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.Category.ADD_CATEGORY_URL; -import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.Category.AVAILABLE_CATEGORY_URL; -import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.Category.CANCEL_CATEGORY_AUDIT_URL; -import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.Category.GET_CATEGORY_AUDIT_URL; -import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.Category.GET_CATEGORY_DETAIL_URL; -import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.Category.LIST_ALL_CATEGORY_URL; -import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.Category.LIST_PASS_CATEGORY_URL; - import java.util.Collections; import java.util.List; import lombok.extern.slf4j.Slf4j; @@ -17,17 +9,15 @@ import me.chanjar.weixin.channel.bean.audit.CategoryAuditInfo; import me.chanjar.weixin.channel.bean.audit.CategoryAuditRequest; import me.chanjar.weixin.channel.bean.base.WxChannelBaseResponse; -import me.chanjar.weixin.channel.bean.category.CategoryDetailResult; -import me.chanjar.weixin.channel.bean.category.CategoryQualificationResponse; -import me.chanjar.weixin.channel.bean.category.PassCategoryResponse; -import me.chanjar.weixin.channel.bean.category.ShopCategory; -import me.chanjar.weixin.channel.bean.category.ShopCategoryResponse; +import me.chanjar.weixin.channel.bean.category.*; import me.chanjar.weixin.channel.util.JsonUtils; import me.chanjar.weixin.channel.util.ResponseUtils; import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor; +import static me.chanjar.weixin.channel.constant.WxChannelApiUrlConstants.Category.*; + /** * 视频号小店 商品类目相关接口 * @@ -135,4 +125,15 @@ public PassCategoryResponse listPassCategory() throws WxErrorException { return ResponseUtils.decode(resJson, PassCategoryResponse.class); } + @Override + public RelationCategoryResponse listRelationCategory(Boolean isFilterStatus, Integer status) throws WxErrorException { + RelationCategoryRequest request = new RelationCategoryRequest( + isFilterStatus != null ? isFilterStatus : false, + status != null ? status : 0 + ); + String reqJson = JsonUtils.encode(request); + String resJson = shopService.post(LIST_RELATION_CATEGORY_URL, reqJson); + return ResponseUtils.decode(resJson, RelationCategoryResponse.class); + } + } diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/category/CategoryDetailResult.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/category/CategoryDetailResult.java index 32313b7e34..3188bd3820 100644 --- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/category/CategoryDetailResult.java +++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/category/CategoryDetailResult.java @@ -22,6 +22,9 @@ public class CategoryDetailResult extends WxChannelBaseResponse { @JsonProperty("attr") private Attr attr; + @JsonProperty("product_qua_list") + private List productQuaList; + @Data @NoArgsConstructor diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/category/RelationCategoryItem.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/category/RelationCategoryItem.java new file mode 100644 index 0000000000..8e0bd1b0b5 --- /dev/null +++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/category/RelationCategoryItem.java @@ -0,0 +1,41 @@ +package me.chanjar.weixin.channel.bean.category; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 店铺类目权限列表项 + * + * @author chucheng + */ +@Data +@NoArgsConstructor +public class RelationCategoryItem implements Serializable { + + /** 类目id */ + @JsonProperty("id") + private Long id; + + /** 类目状态, 1生效中,2已失效 */ + @JsonProperty("status") + private Integer status; + + /** 失效原因 */ + @JsonProperty("uneffective_reason") + private String uneffectiveReason; + + /** 生效时间 */ + @JsonProperty("effective_time") + private Long effectiveTime; + + /** 失效时间 */ + @JsonProperty("uneffective_time") + private Long uneffectiveTime; + + /** 类目资质id */ + @JsonProperty("qua_id") + private Long quaId; +} diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/category/RelationCategoryRequest.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/category/RelationCategoryRequest.java new file mode 100644 index 0000000000..c514e7d9ca --- /dev/null +++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/category/RelationCategoryRequest.java @@ -0,0 +1,28 @@ +package me.chanjar.weixin.channel.bean.category; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.io.Serializable; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 类目权限列表请求参数 + * + * @author Zeyes + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class RelationCategoryRequest implements Serializable { + + private static final long serialVersionUID = -8765432109876543210L; + + /** 是否按状态筛选 */ + @JsonProperty("is_filter_status") + private Boolean isFilterStatus; + + /** 类目状态(当 isFilterStatus 为 true 时有效) */ + @JsonProperty("status") + private Integer status; +} diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/category/RelationCategoryResponse.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/category/RelationCategoryResponse.java new file mode 100644 index 0000000000..4bd1ea96d4 --- /dev/null +++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/category/RelationCategoryResponse.java @@ -0,0 +1,25 @@ +package me.chanjar.weixin.channel.bean.category; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import me.chanjar.weixin.channel.bean.base.WxChannelBaseResponse; + +/** + * 店铺的类目权限列表响应 + * + * @author chucheng + */ +@Data +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class RelationCategoryResponse extends WxChannelBaseResponse { + + private static final long serialVersionUID = -8473920857463918245L; + + @JsonProperty("list") + private List list; + +} diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/product/SkuFastInfo.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/product/SkuFastInfo.java index a461e6d952..b37dfe472c 100644 --- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/product/SkuFastInfo.java +++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/product/SkuFastInfo.java @@ -35,6 +35,14 @@ public class SkuFastInfo implements Serializable { @JsonProperty("is_delete") private Boolean delete; + /** 商品sku编码 */ + @JsonProperty("sku_code") + private String skuCode; + + /** 更新sku状态 0-默认值;5-上架;11-下架 */ + @JsonProperty("status") + private Integer status; + @Data @NoArgsConstructor diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/product/SkuInfo.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/product/SkuInfo.java index 22e75d7afc..956b188c22 100644 --- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/product/SkuInfo.java +++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/product/SkuInfo.java @@ -56,6 +56,10 @@ public class SkuInfo implements Serializable { @JsonProperty("sku_id") private String skuId; + /** sku条形码 */ + @JsonProperty("bar_code") + private String barCode; + public SkuInfo() { } diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/product/SpuFastInfo.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/product/SpuFastInfo.java index 05e107779b..23b1135ba5 100644 --- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/product/SpuFastInfo.java +++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/product/SpuFastInfo.java @@ -25,4 +25,28 @@ public class SpuFastInfo implements Serializable { @JsonProperty("skus") protected List skus; + /** 商品编码 */ + @JsonProperty("spu_code") + protected String spuCode; + + /** 限购信息 */ + @JsonProperty("limit_info") + protected LimitInfo limitInfo; + + /** 运费信息 */ + @JsonProperty("express_info") + protected ExpressInfo expressInfo; + + /** 额外服务 */ + @JsonProperty("extra_service") + protected ExtraServiceInfo extraService; + + /** 发货方式:0-快递发货;1-无需快递,手机号发货;3-无需快递,可选发货账号类型,默认为0,若为无需快递,则无需填写运费模版id */ + @JsonProperty("deliver_method") + private Integer deliverMethod; + + /** 商品待开售信息 */ + @JsonProperty("timing_onsale_info") + private TimingOnSaleInfo timingOnSaleInfo; + } diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/product/SpuInfo.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/product/SpuInfo.java index 155148c178..9b2224db94 100644 --- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/product/SpuInfo.java +++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/product/SpuInfo.java @@ -151,4 +151,8 @@ public class SpuInfo extends SpuSimpleInfo { /** 发布模式,0: 普通模式;1: 极简模式 */ @JsonProperty("release_mode") private Integer releaseMode; + + /** 商品待开售信息 */ + @JsonProperty("timing_onsale_info") + private TimingOnSaleInfo timingOnSaleInfo; } diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/product/TimingOnSaleInfo.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/product/TimingOnSaleInfo.java new file mode 100644 index 0000000000..29270d426c --- /dev/null +++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/product/TimingOnSaleInfo.java @@ -0,0 +1,36 @@ +package me.chanjar.weixin.channel.bean.product; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 商品待开售信息 + * + * @author chu + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class TimingOnSaleInfo implements Serializable { + + /** 状态枚举 0-没有待开售;1-待开售 */ + @JsonProperty("status") + private Integer status; + + /** 开售时间,秒级时间戳,0为未配置时间 */ + @JsonProperty("onsale_time") + private Long onSaleTime; + + /** 是否隐藏价格 0-不隐藏;1-隐藏 */ + @JsonProperty("is_hide_price") + private Integer isHidePrice; + + /** 待开售任务ID,可用于请求立即开售 */ + @JsonProperty("task_id") + private Integer taskId; + +} 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 2d9aa84f84..4859b723fb 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 @@ -53,6 +53,8 @@ public interface Category { String CANCEL_CATEGORY_AUDIT_URL = "https://api.weixin.qq.com/channels/ec/category/audit/cancel"; /** 获取账号申请通过的类目和资质信息 */ String LIST_PASS_CATEGORY_URL = "https://api.weixin.qq.com/channels/ec/category/list/get"; + /** 获取店铺的类目权限列表 */ + String LIST_RELATION_CATEGORY_URL = "https://api.weixin.qq.com/shop/ec/category/get_category_relation_list"; } /** 主页管理相关接口 */ diff --git a/weixin-java-channel/src/test/java/me/chanjar/weixin/channel/api/impl/WxChannelCategoryServiceImplTest.java b/weixin-java-channel/src/test/java/me/chanjar/weixin/channel/api/impl/WxChannelCategoryServiceImplTest.java index 125e061cd8..06afde2993 100644 --- a/weixin-java-channel/src/test/java/me/chanjar/weixin/channel/api/impl/WxChannelCategoryServiceImplTest.java +++ b/weixin-java-channel/src/test/java/me/chanjar/weixin/channel/api/impl/WxChannelCategoryServiceImplTest.java @@ -158,4 +158,14 @@ public void testListPassCategory() throws WxErrorException { assertTrue(response.isSuccess()); System.out.println(response); } + + @Test + public void testListRelationCategory() throws WxErrorException { + WxChannelCategoryService categoryService = channelService.getCategoryService(); + me.chanjar.weixin.channel.bean.category.RelationCategoryResponse response = + categoryService.listRelationCategory(true, 1); + assertNotNull(response); + assertTrue(response.isSuccess()); + System.out.println(response); + } } From 16d5cf9afd8dd03ff89b6537c7c01b1d8e75306f Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Wed, 14 Jan 2026 17:05:39 +0800 Subject: [PATCH 44/46] =?UTF-8?q?:art:=20#3843=20=E3=80=90=E5=9F=BA?= =?UTF-8?q?=E7=A1=80=E6=9E=B6=E6=9E=84=E3=80=91=E4=BF=AE=E5=A4=8D=20Spring?= =?UTF-8?q?=20Boot=203=EF=BC=88Spring=20Data=20Redis=203.x=EF=BC=89?= =?UTF-8?q?=E4=B8=8B=20RedisTemplate.getExpire=20=E8=BF=94=E5=9B=9E?= =?UTF-8?q?=E5=80=BC=E8=AF=AD=E4=B9=89=E5=8F=98=E5=8C=96=E5=AF=BC=E8=87=B4?= =?UTF-8?q?=E7=9A=84=E8=BF=87=E6=9C=9F=E5=88=A4=E6=96=AD/=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E5=A4=B1=E6=95=88=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../weixin/common/redis/RedisTemplateWxRedisOps.java | 2 +- .../weixin/common/redis/CommonWxRedisOpsTest.java | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/redis/RedisTemplateWxRedisOps.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/redis/RedisTemplateWxRedisOps.java index 19d4046c92..d531a2a307 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/redis/RedisTemplateWxRedisOps.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/redis/RedisTemplateWxRedisOps.java @@ -29,7 +29,7 @@ public void setValue(String key, String value, int expire, TimeUnit timeUnit) { @Override public Long getExpire(String key) { - return redisTemplate.getExpire(key); + return redisTemplate.getExpire(key, TimeUnit.SECONDS); } @Override diff --git a/weixin-java-common/src/test/java/me/chanjar/weixin/common/redis/CommonWxRedisOpsTest.java b/weixin-java-common/src/test/java/me/chanjar/weixin/common/redis/CommonWxRedisOpsTest.java index 96ba20ba2b..fb53c8c4b6 100644 --- a/weixin-java-common/src/test/java/me/chanjar/weixin/common/redis/CommonWxRedisOpsTest.java +++ b/weixin-java-common/src/test/java/me/chanjar/weixin/common/redis/CommonWxRedisOpsTest.java @@ -35,6 +35,17 @@ public void testGetExpire() { Assert.assertTrue(expireSeconds <= 4 && expireSeconds >= 0); } + @Test + public void testGetExpireForNonExistentKey() { + String nonExistentKey = "non_existent_key_" + System.currentTimeMillis(); + Long expire = wxRedisOps.getExpire(nonExistentKey); + // 对于不存在的 key,底层使用 getExpire(key, TimeUnit.SECONDS) 时应返回负值 + // Spring Data Redis 2.x 和 3.x 约定:-2 表示 key 不存在,-1 表示 key 没有过期时间 + // 因此这里不应返回 null,而应返回一个小于 0 的值 + Assert.assertNotNull(expire, "Non-existent key should not have null expiration"); + Assert.assertTrue(expire < 0, "Non-existent key should have negative expiration"); + } + @Test public void testExpire() { String key = "access_token", value = String.valueOf(System.currentTimeMillis()); From 777f4e518a75753e99aaf9bed3d8c5e86814d055 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Thu, 15 Jan 2026 10:57:55 +0800 Subject: [PATCH 45/46] =?UTF-8?q?:art:=20#3841=20=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=20RsaCry?= =?UTF-8?q?ptoUtil=20=E6=97=A0=E6=B3=95=E5=8A=A0=E5=AF=86=E7=BB=A7?= =?UTF-8?q?=E6=89=BF=E5=AD=97=E6=AE=B5=E5=92=8C=E5=B5=8C=E5=A5=97=E5=AF=B9?= =?UTF-8?q?=E8=B1=A1=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wxpay/v3/util/RsaCryptoUtil.java | 23 ++- .../wxpay/v3/util/RsaCryptoUtilTest.java | 179 ++++++++++++++++++ 2 files changed, 200 insertions(+), 2 deletions(-) create mode 100644 weixin-java-pay/src/test/java/com/github/binarywang/wxpay/v3/util/RsaCryptoUtilTest.java diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/util/RsaCryptoUtil.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/util/RsaCryptoUtil.java index 8c3e2ace53..0143022ece 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/util/RsaCryptoUtil.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/util/RsaCryptoUtil.java @@ -14,8 +14,11 @@ import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.cert.X509Certificate; +import java.util.ArrayList; import java.util.Base64; import java.util.Collection; +import java.util.Collections; +import java.util.List; /** * 微信支付敏感信息加密 @@ -36,10 +39,26 @@ public static void encryptFields(Object encryptObject, X509Certificate certifica } } + /** + * 递归获取类的所有字段,包括父类中的字段 + * + * @param clazz 要获取字段的类 + * @return 所有字段的列表 + */ + private static List getAllFields(Class clazz) { + List fields = new ArrayList<>(); + while (clazz != null && clazz != Object.class) { + Field[] declaredFields = clazz.getDeclaredFields(); + Collections.addAll(fields, declaredFields); + clazz = clazz.getSuperclass(); + } + return fields; + } + private static void encryptField(Object encryptObject, X509Certificate certificate) throws IllegalAccessException, IllegalBlockSizeException { Class infoClass = encryptObject.getClass(); - Field[] infoFieldArray = infoClass.getDeclaredFields(); - for (Field field : infoFieldArray) { + List infoFieldList = getAllFields(infoClass); + for (Field field : infoFieldList) { if (field.isAnnotationPresent(SpecEncrypt.class)) { //字段使用了@SpecEncrypt进行标识 if (field.getType().getTypeName().equals(JAVA_LANG_STRING)) { diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/v3/util/RsaCryptoUtilTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/v3/util/RsaCryptoUtilTest.java new file mode 100644 index 0000000000..18f46c687f --- /dev/null +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/v3/util/RsaCryptoUtilTest.java @@ -0,0 +1,179 @@ +package com.github.binarywang.wxpay.v3.util; + +import com.github.binarywang.wxpay.bean.profitsharing.request.ProfitSharingReceiverV3Request; +import com.github.binarywang.wxpay.bean.profitsharing.request.ProfitSharingV3Request; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.v3.SpecEncrypt; +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import org.testng.annotations.Test; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + +import static org.testng.Assert.*; + +/** + * RsaCryptoUtil 测试类 + */ +public class RsaCryptoUtilTest { + + /** + * 测试反射能否找到嵌套类中的 @SpecEncrypt 注解字段 + */ + @Test + public void testFindAnnotatedFieldsInNestedClass() { + // 创建 Receiver 对象 + ProfitSharingV3Request.Receiver receiver = new ProfitSharingV3Request.Receiver(); + receiver.setName("测试姓名"); + + // 使用反射查找带有 @SpecEncrypt 注解的字段 + Class receiverClass = receiver.getClass(); + Field[] fields = receiverClass.getDeclaredFields(); + + boolean foundNameField = false; + boolean nameFieldHasAnnotation = false; + + for (Field field : fields) { + if (field.getName().equals("name")) { + foundNameField = true; + if (field.isAnnotationPresent(SpecEncrypt.class)) { + nameFieldHasAnnotation = true; + } + } + } + + // 验证能够找到 name 字段并且它有 @SpecEncrypt 注解 + assertTrue(foundNameField, "应该能找到 name 字段"); + assertTrue(nameFieldHasAnnotation, "name 字段应该有 @SpecEncrypt 注解"); + } + + /** + * 测试嵌套对象中的字段加密 + * 验证 List 中每个 Receiver 对象的 name 字段是否能被正确找到和处理 + */ + @Test + public void testEncryptFieldsWithNestedObjects() { + // 创建测试对象 + ProfitSharingV3Request request = ProfitSharingV3Request.newBuilder() + .appid("test-appid") + .subMchId("test-submchid") + .transactionId("test-transaction") + .outOrderNo("test-order-no") + .unfreezeUnsplit(true) + .build(); + + List receivers = new ArrayList<>(); + ProfitSharingV3Request.Receiver receiver = new ProfitSharingV3Request.Receiver(); + receiver.setName("张三"); // 设置需要加密的字段 + receiver.setAccount("test-account"); + receiver.setType("PERSONAL_OPENID"); + receiver.setAmount(100); + receiver.setRelationType("STORE"); + receiver.setDescription("测试分账"); + + receivers.add(receiver); + request.setReceivers(receivers); + + // 验证 receivers 字段有 @SpecEncrypt 注解 + try { + Field receiversField = ProfitSharingV3Request.class.getDeclaredField("receivers"); + boolean hasAnnotation = receiversField.isAnnotationPresent(SpecEncrypt.class); + assertTrue(hasAnnotation, "receivers 字段应该有 @SpecEncrypt 注解"); + } catch (NoSuchFieldException e) { + fail("应该能找到 receivers 字段"); + } + + // 验证name字段不为null + assertNotNull(receiver.getName()); + assertEquals(receiver.getName(), "张三"); + } + + /** + * 测试单个对象中的字段加密 + * 验证直接在对象上的 @SpecEncrypt 字段是否能被正确找到 + */ + @Test + public void testEncryptFieldsWithDirectField() { + // 创建测试对象 + ProfitSharingReceiverV3Request request = ProfitSharingReceiverV3Request.newBuilder() + .appid("test-appid") + .subMchId("test-submchid") + .type("PERSONAL_OPENID") + .account("test-account") + .name("李四") + .relationType("STORE") + .build(); + + // 验证 name 字段有 @SpecEncrypt 注解 + try { + Field nameField = ProfitSharingReceiverV3Request.class.getDeclaredField("name"); + boolean hasAnnotation = nameField.isAnnotationPresent(SpecEncrypt.class); + assertTrue(hasAnnotation, "name 字段应该有 @SpecEncrypt 注解"); + } catch (NoSuchFieldException e) { + fail("应该能找到 name 字段"); + } + + // 验证name字段不为null + assertNotNull(request.getName()); + assertEquals(request.getName(), "李四"); + } + + /** + * 测试类继承场景下的字段加密 + * 验证父类中带 @SpecEncrypt 注解的字段是否能被正确找到和加密 + */ + @Test + public void testEncryptFieldsWithInheritance() { + // 定义测试用的父类和子类 + @Data + class ParentRequest { + @SpecEncrypt + @SerializedName("parent_name") + private String parentName; + } + + @Data + @lombok.EqualsAndHashCode(callSuper = false) + class ChildRequest extends ParentRequest { + @SpecEncrypt + @SerializedName("child_name") + private String childName; + + @Override + protected boolean canEqual(final Object other) { + return other instanceof ChildRequest; + } + } + + // 创建子类实例 + ChildRequest request = new ChildRequest(); + request.setParentName("父类字段"); + request.setChildName("子类字段"); + + // 验证能够找到父类和子类的字段 + // 使用 getDeclaredFields 只能找到子类字段 + Field[] childFields = ChildRequest.class.getDeclaredFields(); + + // 使用反射调用 RsaCryptoUtil 的私有 getAllFields 方法 + int annotatedFieldCount = 0; + try { + java.lang.reflect.Method getAllFieldsMethod = RsaCryptoUtil.class.getDeclaredMethod("getAllFields", Class.class); + getAllFieldsMethod.setAccessible(true); + @SuppressWarnings("unchecked") + List allFields = (List) getAllFieldsMethod.invoke(null, ChildRequest.class); + + for (Field field : allFields) { + if (field.isAnnotationPresent(SpecEncrypt.class)) { + annotatedFieldCount++; + } + } + } catch (Exception e) { + fail("无法调用 getAllFields 方法: " + e.getMessage()); + } + + // 应该找到2个带注解的字段(parentName 和 childName) + assertTrue(annotatedFieldCount >= 2, "应该能找到至少2个带 @SpecEncrypt 注解的字段"); + } +} From 6a9852f7e3bfef2b1090abebae86865b3b449735 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Thu, 15 Jan 2026 11:07:03 +0800 Subject: [PATCH 46/46] =?UTF-8?q?:new:=20#3847=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=20wx-jav?= =?UTF-8?q?a-open-multi-spring-boot-starter=20=E6=94=AF=E6=8C=81=E5=A4=9A?= =?UTF-8?q?=E5=BC=80=E6=94=BE=E5=B9=B3=E5=8F=B0=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spring-boot-starters/pom.xml | 1 + .../README.md | 98 +++++++++++ .../pom.xml | 62 +++++++ .../WxOpenMultiAutoConfiguration.java | 15 ++ .../WxOpenMultiServiceConfiguration.java | 26 +++ .../services/AbstractWxOpenConfiguration.java | 153 ++++++++++++++++++ .../services/WxOpenInJedisConfiguration.java | 78 +++++++++ .../services/WxOpenInMemoryConfiguration.java | 33 ++++ .../WxOpenInRedisTemplateConfiguration.java | 44 +++++ .../WxOpenInRedissonConfiguration.java | 68 ++++++++ .../properties/WxOpenMultiProperties.java | 125 ++++++++++++++ .../WxOpenMultiRedisProperties.java | 57 +++++++ .../properties/WxOpenSingleProperties.java | 49 ++++++ .../open/service/WxOpenMultiServices.java | 26 +++ .../open/service/WxOpenMultiServicesImpl.java | 35 ++++ .../main/resources/META-INF/spring.factories | 2 + ...ot.autoconfigure.AutoConfiguration.imports | 1 + 17 files changed, 873 insertions(+) create mode 100644 spring-boot-starters/wx-java-open-multi-spring-boot-starter/README.md create mode 100644 spring-boot-starters/wx-java-open-multi-spring-boot-starter/pom.xml create mode 100644 spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/autoconfigure/WxOpenMultiAutoConfiguration.java create mode 100644 spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/configuration/WxOpenMultiServiceConfiguration.java create mode 100644 spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/configuration/services/AbstractWxOpenConfiguration.java create mode 100644 spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/configuration/services/WxOpenInJedisConfiguration.java create mode 100644 spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/configuration/services/WxOpenInMemoryConfiguration.java create mode 100644 spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/configuration/services/WxOpenInRedisTemplateConfiguration.java create mode 100644 spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/configuration/services/WxOpenInRedissonConfiguration.java create mode 100644 spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/properties/WxOpenMultiProperties.java create mode 100644 spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/properties/WxOpenMultiRedisProperties.java create mode 100644 spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/properties/WxOpenSingleProperties.java create mode 100644 spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/service/WxOpenMultiServices.java create mode 100644 spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/service/WxOpenMultiServicesImpl.java create mode 100644 spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/resources/META-INF/spring.factories create mode 100644 spring-boot-starters/wx-java-open-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 1f4881e9dd..e145e5fd66 100644 --- a/spring-boot-starters/pom.xml +++ b/spring-boot-starters/pom.xml @@ -23,6 +23,7 @@ wx-java-mp-multi-spring-boot-starter wx-java-mp-spring-boot-starter wx-java-pay-spring-boot-starter + wx-java-open-multi-spring-boot-starter wx-java-open-spring-boot-starter wx-java-qidian-spring-boot-starter wx-java-cp-multi-spring-boot-starter diff --git a/spring-boot-starters/wx-java-open-multi-spring-boot-starter/README.md b/spring-boot-starters/wx-java-open-multi-spring-boot-starter/README.md new file mode 100644 index 0000000000..ab5afa5449 --- /dev/null +++ b/spring-boot-starters/wx-java-open-multi-spring-boot-starter/README.md @@ -0,0 +1,98 @@ +# wx-java-open-multi-spring-boot-starter + +## 快速开始 + +1. 引入依赖 + ```xml + + com.github.binarywang + wx-java-open-multi-spring-boot-starter + ${version} + + ``` +2. 添加配置(application.properties) + ```properties + # 开放平台配置 + ## 应用 1 配置(必填) + wx.open.apps.tenantId1.app-id=appId + wx.open.apps.tenantId1.secret=@secret + ## 选填 + wx.open.apps.tenantId1.token=@token + wx.open.apps.tenantId1.aes-key=@aesKey + wx.open.apps.tenantId1.api-host-url=@apiHostUrl + wx.open.apps.tenantId1.access-token-url=@accessTokenUrl + ## 应用 2 配置(必填) + wx.open.apps.tenantId2.app-id=@appId + wx.open.apps.tenantId2.secret=@secret + ## 选填 + wx.open.apps.tenantId2.token=@token + wx.open.apps.tenantId2.aes-key=@aesKey + wx.open.apps.tenantId2.api-host-url=@apiHostUrl + wx.open.apps.tenantId2.access-token-url=@accessTokenUrl + + # ConfigStorage 配置(选填) + ## 配置类型: memory(默认), jedis, redisson, redistemplate + wx.open.config-storage.type=memory + ## 相关redis前缀配置: wx:open:multi(默认) + wx.open.config-storage.key-prefix=wx:open:multi + wx.open.config-storage.redis.host=127.0.0.1 + wx.open.config-storage.redis.port=6379 + ## 注意:当前版本暂不支持 sentinel 配置,以下配置仅作为预留 + # wx.open.config-storage.redis.sentinel-ips=127.0.0.1:16379,127.0.0.1:26379 + # wx.open.config-storage.redis.sentinel-name=mymaster + + # http 客户端配置(选填) + 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 + ## 连接超时时间,单位毫秒,默认:5000 + wx.open.config-storage.connection-timeout=5000 + ## 读数据超时时间,即socketTimeout,单位毫秒,默认:5000 + wx.open.config-storage.so-timeout=5000 + ## 从连接池获取链接的超时时间,单位毫秒,默认:5000 + wx.open.config-storage.connection-request-timeout=5000 + ``` +3. 自动注入的类型:`WxOpenMultiServices` + +4. 使用样例 + +```java +import com.binarywang.spring.starter.wxjava.open.service.WxOpenMultiServices; +import me.chanjar.weixin.open.api.WxOpenService; +import me.chanjar.weixin.open.api.WxOpenComponentService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class DemoService { + @Autowired + private WxOpenMultiServices wxOpenMultiServices; + + public void test() { + // 应用 1 的 WxOpenService + WxOpenService wxOpenService1 = wxOpenMultiServices.getWxOpenService("tenantId1"); + WxOpenComponentService componentService1 = wxOpenService1.getWxOpenComponentService(); + // todo ... + + // 应用 2 的 WxOpenService + WxOpenService wxOpenService2 = wxOpenMultiServices.getWxOpenService("tenantId2"); + WxOpenComponentService componentService2 = wxOpenService2.getWxOpenComponentService(); + // todo ... + + // 应用 3 的 WxOpenService + WxOpenService wxOpenService3 = wxOpenMultiServices.getWxOpenService("tenantId3"); + // 判断是否为空 + if (wxOpenService3 == null) { + // todo wxOpenService3 为空,请先配置 tenantId3 微信开放平台应用参数 + return; + } + WxOpenComponentService componentService3 = wxOpenService3.getWxOpenComponentService(); + // todo ... + } +} +``` diff --git a/spring-boot-starters/wx-java-open-multi-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-open-multi-spring-boot-starter/pom.xml new file mode 100644 index 0000000000..1ad7a5e8e1 --- /dev/null +++ b/spring-boot-starters/wx-java-open-multi-spring-boot-starter/pom.xml @@ -0,0 +1,62 @@ + + + + wx-java-spring-boot-starters + com.github.binarywang + 4.8.0 + + 4.0.0 + + wx-java-open-multi-spring-boot-starter + WxJava - Spring Boot Starter for WxOpen::支持多账号配置 + 微信开放平台开发的 Spring Boot Starter::支持多账号配置 + + + + com.github.binarywang + weixin-java-open + ${project.version} + + + redis.clients + jedis + provided + + + org.redisson + redisson + provided + + + org.springframework.data + spring-data-redis + 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-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/autoconfigure/WxOpenMultiAutoConfiguration.java b/spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/autoconfigure/WxOpenMultiAutoConfiguration.java new file mode 100644 index 0000000000..749130f517 --- /dev/null +++ b/spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/autoconfigure/WxOpenMultiAutoConfiguration.java @@ -0,0 +1,15 @@ +package com.binarywang.spring.starter.wxjava.open.autoconfigure; + +import com.binarywang.spring.starter.wxjava.open.configuration.WxOpenMultiServiceConfiguration; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +/** + * 微信开放平台多账号自动配置 + * + * @author Binary Wang + */ +@Configuration +@Import(WxOpenMultiServiceConfiguration.class) +public class WxOpenMultiAutoConfiguration { +} diff --git a/spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/configuration/WxOpenMultiServiceConfiguration.java b/spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/configuration/WxOpenMultiServiceConfiguration.java new file mode 100644 index 0000000000..e858185e30 --- /dev/null +++ b/spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/configuration/WxOpenMultiServiceConfiguration.java @@ -0,0 +1,26 @@ +package com.binarywang.spring.starter.wxjava.open.configuration; + +import com.binarywang.spring.starter.wxjava.open.configuration.services.WxOpenInJedisConfiguration; +import com.binarywang.spring.starter.wxjava.open.configuration.services.WxOpenInMemoryConfiguration; +import com.binarywang.spring.starter.wxjava.open.configuration.services.WxOpenInRedisTemplateConfiguration; +import com.binarywang.spring.starter.wxjava.open.configuration.services.WxOpenInRedissonConfiguration; +import com.binarywang.spring.starter.wxjava.open.properties.WxOpenMultiProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +/** + * 微信开放平台相关服务自动注册 + * + * @author Binary Wang + */ +@Configuration +@EnableConfigurationProperties(WxOpenMultiProperties.class) +@Import({ + WxOpenInJedisConfiguration.class, + WxOpenInMemoryConfiguration.class, + WxOpenInRedissonConfiguration.class, + WxOpenInRedisTemplateConfiguration.class +}) +public class WxOpenMultiServiceConfiguration { +} diff --git a/spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/configuration/services/AbstractWxOpenConfiguration.java b/spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/configuration/services/AbstractWxOpenConfiguration.java new file mode 100644 index 0000000000..0c63878783 --- /dev/null +++ b/spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/configuration/services/AbstractWxOpenConfiguration.java @@ -0,0 +1,153 @@ +package com.binarywang.spring.starter.wxjava.open.configuration.services; + +import com.binarywang.spring.starter.wxjava.open.properties.WxOpenMultiProperties; +import com.binarywang.spring.starter.wxjava.open.properties.WxOpenSingleProperties; +import com.binarywang.spring.starter.wxjava.open.service.WxOpenMultiServices; +import com.binarywang.spring.starter.wxjava.open.service.WxOpenMultiServicesImpl; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; +import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder; +import me.chanjar.weixin.open.api.WxOpenConfigStorage; +import me.chanjar.weixin.open.api.WxOpenService; +import me.chanjar.weixin.open.api.impl.WxOpenInMemoryConfigStorage; +import me.chanjar.weixin.open.api.impl.WxOpenServiceImpl; +import org.apache.commons.lang3.StringUtils; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * WxOpenConfigStorage 抽象配置类 + * + * @author Binary Wang + */ +@RequiredArgsConstructor +@Slf4j +public abstract class AbstractWxOpenConfiguration { + + protected WxOpenMultiServices wxOpenMultiServices(WxOpenMultiProperties wxOpenMultiProperties) { + Map appsMap = wxOpenMultiProperties.getApps(); + if (appsMap == null || appsMap.isEmpty()) { + log.warn("微信开放平台应用参数未配置,通过 WxOpenMultiServices#getWxOpenService(\"tenantId\")获取实例将返回空"); + return new WxOpenMultiServicesImpl(); + } + /** + * 校验 appId 是否唯一,避免使用 redis 缓存 token、ticket 时错乱。 + */ + Collection apps = appsMap.values(); + if (apps.size() > 1) { + // 校验 appId 是否唯一 + String nullAppIdPlaceholder = "__NULL_APP_ID__"; + boolean multi = apps.stream() + // 没有 appId,如果不判断是否为空,这里会报 NPE 异常 + .collect(Collectors.groupingBy(c -> c.getAppId() == null ? nullAppIdPlaceholder : c.getAppId(), Collectors.counting())) + .entrySet().stream().anyMatch(e -> e.getValue() > 1); + if (multi) { + throw new RuntimeException("请确保微信开放平台配置 appId 的唯一性"); + } + } + WxOpenMultiServicesImpl services = new WxOpenMultiServicesImpl(); + + Set> entries = appsMap.entrySet(); + for (Map.Entry entry : entries) { + String tenantId = entry.getKey(); + WxOpenSingleProperties wxOpenSingleProperties = entry.getValue(); + WxOpenInMemoryConfigStorage storage = this.wxOpenConfigStorage(wxOpenMultiProperties); + this.configApp(storage, wxOpenSingleProperties); + this.configHttp(storage, wxOpenMultiProperties.getConfigStorage()); + WxOpenService wxOpenService = this.wxOpenService(storage, wxOpenMultiProperties); + services.addWxOpenService(tenantId, wxOpenService); + } + return services; + } + + /** + * 配置 WxOpenInMemoryConfigStorage + * + * @param wxOpenMultiProperties 参数 + * @return WxOpenInMemoryConfigStorage + */ + protected abstract WxOpenInMemoryConfigStorage wxOpenConfigStorage(WxOpenMultiProperties wxOpenMultiProperties); + + public WxOpenService wxOpenService(WxOpenConfigStorage configStorage, WxOpenMultiProperties wxOpenMultiProperties) { + WxOpenService wxOpenService = new WxOpenServiceImpl(); + wxOpenService.setWxOpenConfigStorage(configStorage); + return wxOpenService; + } + + private void configApp(WxOpenInMemoryConfigStorage config, WxOpenSingleProperties appProperties) { + String appId = appProperties.getAppId(); + String secret = appProperties.getSecret(); + String token = appProperties.getToken(); + String aesKey = appProperties.getAesKey(); + String apiHostUrl = appProperties.getApiHostUrl(); + String accessTokenUrl = appProperties.getAccessTokenUrl(); + + // appId 和 secret 是必需的 + if (StringUtils.isBlank(appId)) { + throw new IllegalArgumentException("微信开放平台 appId 不能为空"); + } + if (StringUtils.isBlank(secret)) { + throw new IllegalArgumentException("微信开放平台 secret 不能为空"); + } + + config.setComponentAppId(appId); + config.setComponentAppSecret(secret); + if (StringUtils.isNotBlank(token)) { + config.setComponentToken(token); + } + if (StringUtils.isNotBlank(aesKey)) { + config.setComponentAesKey(aesKey); + } + // 设置URL配置 + config.setApiHostUrl(StringUtils.trimToNull(apiHostUrl)); + config.setAccessTokenUrl(StringUtils.trimToNull(accessTokenUrl)); + } + + private void configHttp(WxOpenInMemoryConfigStorage config, WxOpenMultiProperties.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); + } + } + + // 设置重试配置 + int maxRetryTimes = storage.getMaxRetryTimes(); + if (maxRetryTimes < 0) { + maxRetryTimes = 0; + } + int retrySleepMillis = storage.getRetrySleepMillis(); + if (retrySleepMillis < 0) { + retrySleepMillis = 1000; + } + config.setRetrySleepMillis(retrySleepMillis); + config.setMaxRetryTimes(maxRetryTimes); + + // 设置自定义的HttpClient超时配置 + ApacheHttpClientBuilder clientBuilder = config.getApacheHttpClientBuilder(); + if (clientBuilder == null) { + clientBuilder = DefaultApacheHttpClientBuilder.get(); + } + if (clientBuilder instanceof DefaultApacheHttpClientBuilder) { + DefaultApacheHttpClientBuilder defaultBuilder = (DefaultApacheHttpClientBuilder) clientBuilder; + defaultBuilder.setConnectionTimeout(storage.getConnectionTimeout()); + defaultBuilder.setSoTimeout(storage.getSoTimeout()); + defaultBuilder.setConnectionRequestTimeout(storage.getConnectionRequestTimeout()); + config.setApacheHttpClientBuilder(defaultBuilder); + } + } +} diff --git a/spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/configuration/services/WxOpenInJedisConfiguration.java b/spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/configuration/services/WxOpenInJedisConfiguration.java new file mode 100644 index 0000000000..bb9577b99b --- /dev/null +++ b/spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/configuration/services/WxOpenInJedisConfiguration.java @@ -0,0 +1,78 @@ +package com.binarywang.spring.starter.wxjava.open.configuration.services; + +import com.binarywang.spring.starter.wxjava.open.properties.WxOpenMultiProperties; +import com.binarywang.spring.starter.wxjava.open.properties.WxOpenMultiRedisProperties; +import com.binarywang.spring.starter.wxjava.open.service.WxOpenMultiServices; +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.open.api.impl.WxOpenInMemoryConfigStorage; +import me.chanjar.weixin.open.api.impl.WxOpenInRedisConfigStorage; +import org.apache.commons.lang3.StringUtils; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +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 Binary Wang + */ +@Configuration +@ConditionalOnProperty( + prefix = WxOpenMultiProperties.PREFIX + ".config-storage", name = "type", havingValue = "JEDIS" +) +@ConditionalOnClass({JedisPool.class, JedisPoolConfig.class}) +@RequiredArgsConstructor +public class WxOpenInJedisConfiguration extends AbstractWxOpenConfiguration { + private final WxOpenMultiProperties wxOpenMultiProperties; + private final ApplicationContext applicationContext; + + @Bean + public WxOpenMultiServices wxOpenMultiServices() { + return this.wxOpenMultiServices(wxOpenMultiProperties); + } + + @Override + protected WxOpenInMemoryConfigStorage wxOpenConfigStorage(WxOpenMultiProperties wxOpenMultiProperties) { + return this.configJedis(wxOpenMultiProperties); + } + + private WxOpenInRedisConfigStorage configJedis(WxOpenMultiProperties wxOpenMultiProperties) { + WxOpenMultiRedisProperties redisProperties = wxOpenMultiProperties.getConfigStorage().getRedis(); + JedisPool jedisPool; + if (redisProperties != null && StringUtils.isNotEmpty(redisProperties.getHost())) { + jedisPool = getJedisPool(wxOpenMultiProperties); + } else { + jedisPool = applicationContext.getBean(JedisPool.class); + } + return new WxOpenInRedisConfigStorage(jedisPool, wxOpenMultiProperties.getConfigStorage().getKeyPrefix()); + } + + private JedisPool getJedisPool(WxOpenMultiProperties wxOpenMultiProperties) { + WxOpenMultiProperties.ConfigStorage storage = wxOpenMultiProperties.getConfigStorage(); + WxOpenMultiRedisProperties 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-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/configuration/services/WxOpenInMemoryConfiguration.java b/spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/configuration/services/WxOpenInMemoryConfiguration.java new file mode 100644 index 0000000000..f7448a0875 --- /dev/null +++ b/spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/configuration/services/WxOpenInMemoryConfiguration.java @@ -0,0 +1,33 @@ +package com.binarywang.spring.starter.wxjava.open.configuration.services; + +import com.binarywang.spring.starter.wxjava.open.properties.WxOpenMultiProperties; +import com.binarywang.spring.starter.wxjava.open.service.WxOpenMultiServices; +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.open.api.impl.WxOpenInMemoryConfigStorage; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * 自动装配基于内存策略配置 + * + * @author someone + */ +@Configuration +@ConditionalOnProperty( + prefix = WxOpenMultiProperties.PREFIX + ".config-storage", name = "type", havingValue = "MEMORY", matchIfMissing = true +) +@RequiredArgsConstructor +public class WxOpenInMemoryConfiguration extends AbstractWxOpenConfiguration { + private final WxOpenMultiProperties wxOpenMultiProperties; + + @Bean + public WxOpenMultiServices wxOpenMultiServices() { + return this.wxOpenMultiServices(wxOpenMultiProperties); + } + + @Override + protected WxOpenInMemoryConfigStorage wxOpenConfigStorage(WxOpenMultiProperties wxOpenMultiProperties) { + return new WxOpenInMemoryConfigStorage(); + } +} diff --git a/spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/configuration/services/WxOpenInRedisTemplateConfiguration.java b/spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/configuration/services/WxOpenInRedisTemplateConfiguration.java new file mode 100644 index 0000000000..6208c90fe5 --- /dev/null +++ b/spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/configuration/services/WxOpenInRedisTemplateConfiguration.java @@ -0,0 +1,44 @@ +package com.binarywang.spring.starter.wxjava.open.configuration.services; + +import com.binarywang.spring.starter.wxjava.open.properties.WxOpenMultiProperties; +import com.binarywang.spring.starter.wxjava.open.service.WxOpenMultiServices; +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.open.api.impl.WxOpenInMemoryConfigStorage; +import me.chanjar.weixin.open.api.impl.WxOpenInRedisTemplateConfigStorage; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +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; + +/** + * 自动装配基于 redis template 策略配置 + * + * @author Binary Wang + */ +@Configuration +@ConditionalOnProperty( + prefix = WxOpenMultiProperties.PREFIX + ".config-storage", name = "type", havingValue = "redistemplate" +) +@ConditionalOnClass(StringRedisTemplate.class) +@RequiredArgsConstructor +public class WxOpenInRedisTemplateConfiguration extends AbstractWxOpenConfiguration { + private final WxOpenMultiProperties wxOpenMultiProperties; + private final ApplicationContext applicationContext; + + @Bean + public WxOpenMultiServices wxOpenMultiServices() { + return this.wxOpenMultiServices(wxOpenMultiProperties); + } + + @Override + protected WxOpenInMemoryConfigStorage wxOpenConfigStorage(WxOpenMultiProperties wxOpenMultiProperties) { + return this.configRedisTemplate(); + } + + private WxOpenInRedisTemplateConfigStorage configRedisTemplate() { + StringRedisTemplate redisTemplate = applicationContext.getBean(StringRedisTemplate.class); + return new WxOpenInRedisTemplateConfigStorage(redisTemplate, wxOpenMultiProperties.getConfigStorage().getKeyPrefix()); + } +} diff --git a/spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/configuration/services/WxOpenInRedissonConfiguration.java b/spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/configuration/services/WxOpenInRedissonConfiguration.java new file mode 100644 index 0000000000..97569f3baf --- /dev/null +++ b/spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/configuration/services/WxOpenInRedissonConfiguration.java @@ -0,0 +1,68 @@ +package com.binarywang.spring.starter.wxjava.open.configuration.services; + +import com.binarywang.spring.starter.wxjava.open.properties.WxOpenMultiProperties; +import com.binarywang.spring.starter.wxjava.open.properties.WxOpenMultiRedisProperties; +import com.binarywang.spring.starter.wxjava.open.service.WxOpenMultiServices; +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.open.api.impl.WxOpenInMemoryConfigStorage; +import me.chanjar.weixin.open.api.impl.WxOpenInRedissonConfigStorage; +import org.apache.commons.lang3.StringUtils; +import org.redisson.Redisson; +import org.redisson.api.RedissonClient; +import org.redisson.config.Config; +import org.redisson.config.TransportMode; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +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 Binary Wang + */ +@Configuration +@ConditionalOnProperty( + prefix = WxOpenMultiProperties.PREFIX + ".config-storage", name = "type", havingValue = "redisson" +) +@ConditionalOnClass({Redisson.class, RedissonClient.class}) +@RequiredArgsConstructor +public class WxOpenInRedissonConfiguration extends AbstractWxOpenConfiguration { + private final WxOpenMultiProperties wxOpenMultiProperties; + private final ApplicationContext applicationContext; + + @Bean + public WxOpenMultiServices wxOpenMultiServices() { + return this.wxOpenMultiServices(wxOpenMultiProperties); + } + + @Override + protected WxOpenInMemoryConfigStorage wxOpenConfigStorage(WxOpenMultiProperties wxOpenMultiProperties) { + return this.configRedisson(wxOpenMultiProperties); + } + + private WxOpenInRedissonConfigStorage configRedisson(WxOpenMultiProperties wxOpenMultiProperties) { + WxOpenMultiRedisProperties redisProperties = wxOpenMultiProperties.getConfigStorage().getRedis(); + RedissonClient redissonClient; + if (redisProperties != null && StringUtils.isNotEmpty(redisProperties.getHost())) { + redissonClient = getRedissonClient(wxOpenMultiProperties); + } else { + redissonClient = applicationContext.getBean(RedissonClient.class); + } + return new WxOpenInRedissonConfigStorage(redissonClient, wxOpenMultiProperties.getConfigStorage().getKeyPrefix()); + } + + private RedissonClient getRedissonClient(WxOpenMultiProperties wxOpenMultiProperties) { + WxOpenMultiProperties.ConfigStorage storage = wxOpenMultiProperties.getConfigStorage(); + WxOpenMultiRedisProperties 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-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/properties/WxOpenMultiProperties.java b/spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/properties/WxOpenMultiProperties.java new file mode 100644 index 0000000000..95e5b66712 --- /dev/null +++ b/spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/properties/WxOpenMultiProperties.java @@ -0,0 +1,125 @@ +package com.binarywang.spring.starter.wxjava.open.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 Binary Wang + */ +@Data +@NoArgsConstructor +@ConfigurationProperties(WxOpenMultiProperties.PREFIX) +public class WxOpenMultiProperties implements Serializable { + private static final long serialVersionUID = -5358245184407791011L; + public static final String PREFIX = "wx.open"; + + private Map apps = new HashMap<>(); + + /** + * 存储策略 + */ + private final 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:open:multi"; + + /** + * redis连接配置. + */ + @NestedConfigurationProperty + private final WxOpenMultiRedisProperties redis = new WxOpenMultiRedisProperties(); + + /** + * 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#setMaxRetryTimes(int)}
          +     *   {@link cn.binarywang.wx.miniapp.api.impl.BaseWxMaServiceImpl#setMaxRetryTimes(int)}
          +     * 
          + */ + private int maxRetryTimes = 5; + + /** + * 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; + + /** + * 连接超时时间,单位毫秒 + */ + private int connectionTimeout = 5000; + + /** + * 读数据超时时间,即socketTimeout,单位毫秒 + */ + private int soTimeout = 5000; + + /** + * 从连接池获取链接的超时时间,单位毫秒 + */ + private int connectionRequestTimeout = 5000; + } + + public enum StorageType { + /** + * 内存 + */ + memory, + /** + * jedis + */ + jedis, + /** + * redisson + */ + redisson, + /** + * redisTemplate + */ + redistemplate + } + +} diff --git a/spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/properties/WxOpenMultiRedisProperties.java b/spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/properties/WxOpenMultiRedisProperties.java new file mode 100644 index 0000000000..ae6d5368d7 --- /dev/null +++ b/spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/properties/WxOpenMultiRedisProperties.java @@ -0,0 +1,57 @@ +package com.binarywang.spring.starter.wxjava.open.properties; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 微信开放平台多账号Redis配置. + * + * @author Binary Wang + */ +@Data +@NoArgsConstructor +public class WxOpenMultiRedisProperties 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-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/properties/WxOpenSingleProperties.java b/spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/properties/WxOpenSingleProperties.java new file mode 100644 index 0000000000..116da323dc --- /dev/null +++ b/spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/properties/WxOpenSingleProperties.java @@ -0,0 +1,49 @@ +package com.binarywang.spring.starter.wxjava.open.properties; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 微信开放平台单个应用配置. + * + * @author Binary Wang + */ +@Data +@NoArgsConstructor +public class WxOpenSingleProperties implements Serializable { + private static final long serialVersionUID = 1980986361098922525L; + + /** + * 设置微信开放平台的appid. + */ + private String appId; + + /** + * 设置微信开放平台的app secret. + */ + private String secret; + + /** + * 设置微信开放平台的token. + */ + private String token; + + /** + * 设置微信开放平台的EncodingAESKey. + */ + private String aesKey; + + /** + * 自定义API主机地址,用于替换默认的 https://api.weixin.qq.com + * 例如:http://proxy.company.com:8080 + */ + private String apiHostUrl; + + /** + * 自定义获取AccessToken地址,用于向自定义统一服务获取AccessToken + * 例如:http://proxy.company.com:8080/oauth/token + */ + private String accessTokenUrl; +} diff --git a/spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/service/WxOpenMultiServices.java b/spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/service/WxOpenMultiServices.java new file mode 100644 index 0000000000..9228071a10 --- /dev/null +++ b/spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/service/WxOpenMultiServices.java @@ -0,0 +1,26 @@ +package com.binarywang.spring.starter.wxjava.open.service; + + +import me.chanjar.weixin.open.api.WxOpenService; + +/** + * 微信开放平台 {@link WxOpenService} 所有实例存放类. + * + * @author binarywang + */ +public interface WxOpenMultiServices { + /** + * 通过租户 Id 获取 WxOpenService + * + * @param tenantId 租户 Id + * @return WxOpenService + */ + WxOpenService getWxOpenService(String tenantId); + + /** + * 根据租户 Id,从列表中移除一个 WxOpenService 实例 + * + * @param tenantId 租户 Id + */ + void removeWxOpenService(String tenantId); +} diff --git a/spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/service/WxOpenMultiServicesImpl.java b/spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/service/WxOpenMultiServicesImpl.java new file mode 100644 index 0000000000..76fb139e6c --- /dev/null +++ b/spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/open/service/WxOpenMultiServicesImpl.java @@ -0,0 +1,35 @@ +package com.binarywang.spring.starter.wxjava.open.service; + +import me.chanjar.weixin.open.api.WxOpenService; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 微信开放平台 {@link WxOpenMultiServices} 默认实现 + * + * @author Binary Wang + */ +public class WxOpenMultiServicesImpl implements WxOpenMultiServices { + private final Map services = new ConcurrentHashMap<>(); + + @Override + public WxOpenService getWxOpenService(String tenantId) { + return this.services.get(tenantId); + } + + /** + * 根据租户 Id,添加一个 WxOpenService 到列表 + * + * @param tenantId 租户 Id + * @param wxOpenService WxOpenService 实例 + */ + public void addWxOpenService(String tenantId, WxOpenService wxOpenService) { + this.services.put(tenantId, wxOpenService); + } + + @Override + public void removeWxOpenService(String tenantId) { + this.services.remove(tenantId); + } +} diff --git a/spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/resources/META-INF/spring.factories b/spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000000..a61d0018db --- /dev/null +++ b/spring-boot-starters/wx-java-open-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.open.autoconfigure.WxOpenMultiAutoConfiguration \ No newline at end of file diff --git a/spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000000..ddc66af02c --- /dev/null +++ b/spring-boot-starters/wx-java-open-multi-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.binarywang.spring.starter.wxjava.open.autoconfigure.WxOpenMultiAutoConfiguration \ No newline at end of file