From 774579186cf0bf0de87a7b41af5b194aa2b4b3e6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=A4=A9=E6=9C=9D=E7=BA=A2=E9=9B=A8?=
 <44485373+tianchaohongyu@users.noreply.github.com>
Date: Fri, 12 Jan 2024 20:01:42 +0800
Subject: [PATCH] =?UTF-8?q?:new:=20=E3=80=90=E5=BC=80=E6=94=BE=E5=B9=B3?=
 =?UTF-8?q?=E5=8F=B0=E3=80=91=E6=8E=A5=E5=85=A5=E5=B0=8F=E7=A8=8B=E5=BA=8F?=
 =?UTF-8?q?=E8=AE=A4=E8=AF=81=EF=BC=88=E5=B9=B4=E5=AE=A1=EF=BC=89=E7=9B=B8?=
 =?UTF-8?q?=E5=85=B3=E6=8E=A5=E5=8F=A3=EF=BC=8C=E5=90=8C=E6=97=B6=E5=A2=9E?=
 =?UTF-8?q?=E5=8A=A0=E5=85=AC=E5=85=B1=E7=9A=84=E6=96=87=E4=BB=B6=E4=B8=8A?=
 =?UTF-8?q?=E4=BC=A0=E6=96=B9=E6=B3=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../api/impl/BaseWxChannelServiceImpl.java    |  39 +++----
 .../weixin/common/bean/CommonUploadData.java  |  76 ++++++++++++
 .../weixin/common/bean/CommonUploadParam.java |  65 +++++++++++
 .../executor/CommonUploadRequestExecutor.java |  50 ++++++++
 ...CommonUploadRequestExecutorApacheImpl.java |  83 +++++++++++++
 ...mmonUploadRequestExecutorJoddHttpImpl.java |  91 +++++++++++++++
 ...CommonUploadRequestExecutorOkHttpImpl.java |  91 +++++++++++++++
 .../weixin/common/service/WxService.java      |  11 ++
 .../util/http/MediaUploadRequestExecutor.java |  14 ++-
 .../cp/api/impl/BaseWxCpServiceImpl.java      |  10 +-
 .../miniapp/api/impl/BaseWxMaServiceImpl.java |  12 +-
 .../api/impl/WxMaMediaServiceImpl.java        |   6 +-
 ...ApacheAuditMediaUploadRequestExecutor.java |  58 ---------
 .../AuditMediaUploadRequestExecutor.java      |  47 --------
 ...ddHttpAuditMediaUploadRequestExecutor.java |  45 -------
 ...OkHttpAuditMediaUploadRequestExecutor.java |  49 --------
 .../impl/WxMaLiveGoodsServiceImplTest.java    |   3 +-
 .../miniapp/api/impl/WxMaServiceImplTest.java |  11 ++
 .../mp/api/impl/BaseWxMpServiceImpl.java      |  14 ++-
 .../weixin/open/api/WxOpenMaAuthService.java  |  82 +++++++++++++
 .../weixin/open/api/WxOpenMaService.java      |   9 +-
 .../api/impl/WxOpenMaAuthServiceImpl.java     |  56 +++++++++
 .../open/api/impl/WxOpenMaServiceImpl.java    |   8 +-
 .../auth/MaAuthQueryIdentityTreeResult.java   |  29 +++++
 ...thQueryIdentityTreeResultIdentityLeaf.java |  20 ++++
 ...thQueryIdentityTreeResultIdentityNode.java |  47 ++++++++
 .../open/bean/auth/MaAuthQueryResult.java     |  64 ++++++++++
 .../auth/MaAuthQueryResultDispatchInfo.java   |  36 ++++++
 .../open/bean/auth/MaAuthResubmitParam.java   |  22 ++++
 .../auth/MaAuthResubmitParamAuthData.java     |  24 ++++
 .../open/bean/auth/MaAuthSubmitParam.java     |  27 +++++
 .../bean/auth/MaAuthSubmitParamAuthData.java  | 110 ++++++++++++++++++
 .../auth/MaAuthSubmitParamContactInfo.java    |  28 +++++
 .../MaAuthSubmitParamInvoiceElectronic.java   |  29 +++++
 .../auth/MaAuthSubmitParamInvoiceInfo.java    |  44 +++++++
 .../auth/MaAuthSubmitParamInvoiceVat.java     | 102 ++++++++++++++++
 .../open/bean/auth/MaAuthSubmitResult.java    |  34 ++++++
 .../open/bean/auth/MaAuthUploadResult.java    |  27 +++++
 .../api/impl/BaseWxQidianServiceImpl.java     |  50 ++++----
 39 files changed, 1353 insertions(+), 270 deletions(-)
 create mode 100644 weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/CommonUploadData.java
 create mode 100644 weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/CommonUploadParam.java
 create mode 100644 weixin-java-common/src/main/java/me/chanjar/weixin/common/executor/CommonUploadRequestExecutor.java
 create mode 100644 weixin-java-common/src/main/java/me/chanjar/weixin/common/executor/CommonUploadRequestExecutorApacheImpl.java
 create mode 100644 weixin-java-common/src/main/java/me/chanjar/weixin/common/executor/CommonUploadRequestExecutorJoddHttpImpl.java
 create mode 100644 weixin-java-common/src/main/java/me/chanjar/weixin/common/executor/CommonUploadRequestExecutorOkHttpImpl.java
 delete mode 100644 weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/executor/ApacheAuditMediaUploadRequestExecutor.java
 delete mode 100644 weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/executor/AuditMediaUploadRequestExecutor.java
 delete mode 100644 weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/executor/JoddHttpAuditMediaUploadRequestExecutor.java
 delete mode 100644 weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/executor/OkHttpAuditMediaUploadRequestExecutor.java
 create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenMaAuthService.java
 create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMaAuthServiceImpl.java
 create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthQueryIdentityTreeResult.java
 create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthQueryIdentityTreeResultIdentityLeaf.java
 create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthQueryIdentityTreeResultIdentityNode.java
 create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthQueryResult.java
 create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthQueryResultDispatchInfo.java
 create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthResubmitParam.java
 create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthResubmitParamAuthData.java
 create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthSubmitParam.java
 create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthSubmitParamAuthData.java
 create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthSubmitParamContactInfo.java
 create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthSubmitParamInvoiceElectronic.java
 create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthSubmitParamInvoiceInfo.java
 create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthSubmitParamInvoiceVat.java
 create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthSubmitResult.java
 create mode 100644 weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthUploadResult.java

diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/impl/BaseWxChannelServiceImpl.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/impl/BaseWxChannelServiceImpl.java
index 6dd12a5b5..6eb07981f 100644
--- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/impl/BaseWxChannelServiceImpl.java
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/api/impl/BaseWxChannelServiceImpl.java
@@ -2,36 +2,19 @@ package me.chanjar.weixin.channel.api.impl;
 
 
 import com.google.gson.JsonObject;
-import java.io.IOException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.locks.Lock;
 import lombok.extern.slf4j.Slf4j;
-import me.chanjar.weixin.channel.api.WxChannelAddressService;
-import me.chanjar.weixin.channel.api.WxChannelAfterSaleService;
-import me.chanjar.weixin.channel.api.WxChannelBasicService;
-import me.chanjar.weixin.channel.api.WxChannelBrandService;
-import me.chanjar.weixin.channel.api.WxChannelCategoryService;
-import me.chanjar.weixin.channel.api.WxChannelCouponService;
-import me.chanjar.weixin.channel.api.WxChannelFreightTemplateService;
-import me.chanjar.weixin.channel.api.WxChannelFundService;
-import me.chanjar.weixin.channel.api.WxChannelOrderService;
-import me.chanjar.weixin.channel.api.WxChannelProductService;
-import me.chanjar.weixin.channel.api.WxChannelService;
-import me.chanjar.weixin.channel.api.WxChannelSharerService;
-import me.chanjar.weixin.channel.api.WxChannelWarehouseService;
-import me.chanjar.weixin.channel.api.WxLeagueProductService;
-import me.chanjar.weixin.channel.api.WxLeaguePromoterService;
-import me.chanjar.weixin.channel.api.WxLeagueSupplierService;
-import me.chanjar.weixin.channel.api.WxLeagueWindowService;
+import me.chanjar.weixin.channel.api.*;
 import me.chanjar.weixin.channel.config.WxChannelConfig;
 import me.chanjar.weixin.channel.util.JsonUtils;
 import me.chanjar.weixin.common.api.WxConsts;
+import me.chanjar.weixin.common.bean.CommonUploadParam;
 import me.chanjar.weixin.common.bean.ToJson;
 import me.chanjar.weixin.common.bean.WxAccessToken;
 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.error.WxRuntimeException;
+import me.chanjar.weixin.common.executor.CommonUploadRequestExecutor;
 import me.chanjar.weixin.common.util.DataUtils;
 import me.chanjar.weixin.common.util.crypto.SHA1;
 import me.chanjar.weixin.common.util.http.RequestExecutor;
@@ -40,6 +23,10 @@ import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor;
 import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor;
 import org.apache.commons.lang3.StringUtils;
 
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Lock;
+
 /**
  * @author <a href="https://github.com/lixize">Zeyes</a>
  * @see #doGetAccessTokenRequest
@@ -119,7 +106,6 @@ public abstract class BaseWxChannelServiceImpl<H, P> implements WxChannelService
    * 通过网络请求获取AccessToken
    *
    * @return .
-   *
    * @throws IOException .
    */
   protected abstract String doGetAccessTokenRequest() throws IOException;
@@ -145,6 +131,12 @@ public abstract class BaseWxChannelServiceImpl<H, P> implements WxChannelService
     return this.post(url, obj.toJson());
   }
 
+  @Override
+  public String upload(String url, CommonUploadParam param) throws WxErrorException {
+    RequestExecutor<String, CommonUploadParam> executor = CommonUploadRequestExecutor.create(getRequestHttp());
+    return this.execute(executor, url, param);
+  }
+
   @Override
   public String post(String url, JsonObject jsonObject) throws WxErrorException {
     return this.post(url, jsonObject.toString());
@@ -200,7 +192,7 @@ public abstract class BaseWxChannelServiceImpl<H, P> implements WxChannelService
   }
 
   protected <T, E> T executeInternal(RequestExecutor<T, E> executor, String uri, E data, boolean doNotAutoRefreshToken,
-    boolean printResult) throws WxErrorException {
+                                     boolean printResult) throws WxErrorException {
     E dataForLog = DataUtils.handleDataWithSecret(data);
 
     if (uri.contains("access_token=")) {
@@ -259,7 +251,6 @@ public abstract class BaseWxChannelServiceImpl<H, P> implements WxChannelService
    *
    * @param resultContent 响应内容
    * @return access token
-   *
    * @throws WxErrorException 异常
    */
   protected String extractAccessToken(String resultContent) throws WxErrorException {
@@ -372,7 +363,7 @@ public abstract class BaseWxChannelServiceImpl<H, P> implements WxChannelService
   }
 
   @Override
-  public synchronized  WxLeaguePromoterService getLeaguePromoterService() {
+  public synchronized WxLeaguePromoterService getLeaguePromoterService() {
     if (leaguePromoterService == null) {
       leaguePromoterService = new WxLeaguePromoterServiceImpl(this);
     }
diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/CommonUploadData.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/CommonUploadData.java
new file mode 100644
index 000000000..ea76137f6
--- /dev/null
+++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/CommonUploadData.java
@@ -0,0 +1,76 @@
+package me.chanjar.weixin.common.bean;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.jetbrains.annotations.NotNull;
+import org.springframework.lang.Nullable;
+
+import java.io.*;
+import java.nio.file.Files;
+
+/**
+ * 通用文件上传数据
+ *
+ * @author <a href="https://www.sacoc.cn">广州跨界</a>
+ * created on  2024/01/11
+ */
+@Slf4j
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class CommonUploadData implements Serializable {
+
+  /**
+   * 文件名,如:1.jpg
+   */
+  @Nullable
+  private String fileName;
+
+  /**
+   * 文件内容
+   *
+   * @see FileInputStream 文件输入流
+   * @see ByteArrayInputStream 字节输入流
+   */
+  @NotNull
+  private InputStream inputStream;
+
+  /**
+   * 文件内容长度(字节数)
+   */
+  private long length;
+
+  /**
+   * 从文件构造
+   *
+   * @param file 文件
+   * @return 通用文件上传数据
+   */
+  @SneakyThrows
+  public static CommonUploadData fromFile(File file) {
+    return new CommonUploadData(file.getName(), Files.newInputStream(file.toPath()), file.length());
+  }
+
+
+  /**
+   * 读取所有字节,此方法会关闭输入流
+   *
+   * @return 字节数组
+   */
+  @SneakyThrows
+  public byte[] readAllBytes() {
+    byte[] bytes = new byte[(int) length];
+    //noinspection ResultOfMethodCallIgnored
+    inputStream.read(bytes);
+    inputStream.close();
+    return bytes;
+  }
+
+  @Override
+  public String toString() {
+    return String.format("{fileName:%s, length:%s}", fileName, length);
+  }
+}
diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/CommonUploadParam.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/CommonUploadParam.java
new file mode 100644
index 000000000..3a9872fc9
--- /dev/null
+++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/CommonUploadParam.java
@@ -0,0 +1,65 @@
+package me.chanjar.weixin.common.bean;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.SneakyThrows;
+import org.jetbrains.annotations.NotNull;
+import org.springframework.lang.Nullable;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.Serializable;
+
+/**
+ * 通用文件上传参数
+ *
+ * @author <a href="https://www.sacoc.cn">广州跨界</a>
+ * created on  2024/01/11
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class CommonUploadParam implements Serializable {
+
+  /**
+   * 文件对应的接口参数名称(非文件名),如:media
+   */
+  @NotNull
+  private String name;
+
+  /**
+   * 上传数据
+   */
+  @NotNull
+  private CommonUploadData data;
+
+  /**
+   * 从文件构造
+   *
+   * @param name 参数名,如:media
+   * @param file 文件
+   * @return 文件上传参数对象
+   */
+  @SneakyThrows
+  public static CommonUploadParam fromFile(String name, File file) {
+    return new CommonUploadParam(name, CommonUploadData.fromFile(file));
+  }
+
+  /**
+   * 从字节数组构造
+   *
+   * @param name  参数名,如:media
+   * @param bytes 字节数组
+   * @return 文件上传参数对象
+   */
+  @SneakyThrows
+  public static CommonUploadParam fromBytes(String name, @Nullable String fileName, byte[] bytes) {
+    return new CommonUploadParam(name, new CommonUploadData(fileName, new ByteArrayInputStream(bytes), bytes.length));
+  }
+
+  @Override
+  public String toString() {
+    return String.format("{name:%s, data:%s}", name, data);
+  }
+}
diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/executor/CommonUploadRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/executor/CommonUploadRequestExecutor.java
new file mode 100644
index 000000000..2c9a4d752
--- /dev/null
+++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/executor/CommonUploadRequestExecutor.java
@@ -0,0 +1,50 @@
+package me.chanjar.weixin.common.executor;
+
+import me.chanjar.weixin.common.bean.CommonUploadParam;
+import me.chanjar.weixin.common.enums.WxType;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.common.util.http.RequestExecutor;
+import me.chanjar.weixin.common.util.http.RequestHttp;
+import me.chanjar.weixin.common.util.http.ResponseHandler;
+
+import java.io.IOException;
+
+/**
+ * 通用文件上传执行器
+ *
+ * @author <a href="https://www.sacoc.cn">广州跨界</a>
+ * created on  2024/01/11
+ */
+public abstract class CommonUploadRequestExecutor<H, P> implements RequestExecutor<String, CommonUploadParam> {
+
+  protected RequestHttp<H, P> requestHttp;
+
+  public CommonUploadRequestExecutor(RequestHttp<H, P> requestHttp) {
+    this.requestHttp = requestHttp;
+  }
+
+  @Override
+  public void execute(String uri, CommonUploadParam data, ResponseHandler<String> handler, WxType wxType) throws WxErrorException, IOException {
+    handler.handle(this.execute(uri, data, wxType));
+  }
+
+  /**
+   * 构造通用文件上传执行器
+   *
+   * @param requestHttp 请求信息
+   * @return 执行器
+   */
+  @SuppressWarnings({"rawtypes", "unchecked"})
+  public static RequestExecutor<String, CommonUploadParam> create(RequestHttp requestHttp) {
+    switch (requestHttp.getRequestType()) {
+      case APACHE_HTTP:
+        return new CommonUploadRequestExecutorApacheImpl(requestHttp);
+      case JODD_HTTP:
+        return new CommonUploadRequestExecutorJoddHttpImpl(requestHttp);
+      case OK_HTTP:
+        return new CommonUploadRequestExecutorOkHttpImpl(requestHttp);
+      default:
+        throw new IllegalArgumentException("不支持的http执行器类型:" + requestHttp.getRequestType());
+    }
+  }
+}
diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/executor/CommonUploadRequestExecutorApacheImpl.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/executor/CommonUploadRequestExecutorApacheImpl.java
new file mode 100644
index 000000000..6a3c05dd2
--- /dev/null
+++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/executor/CommonUploadRequestExecutorApacheImpl.java
@@ -0,0 +1,83 @@
+package me.chanjar.weixin.common.executor;
+
+import lombok.Getter;
+import me.chanjar.weixin.common.bean.CommonUploadData;
+import me.chanjar.weixin.common.bean.CommonUploadParam;
+import me.chanjar.weixin.common.enums.WxType;
+import me.chanjar.weixin.common.error.WxError;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.common.util.http.RequestHttp;
+import me.chanjar.weixin.common.util.http.apache.Utf8ResponseHandler;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpHost;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.mime.HttpMultipartMode;
+import org.apache.http.entity.mime.MultipartEntityBuilder;
+import org.apache.http.entity.mime.content.InputStreamBody;
+import org.apache.http.impl.client.CloseableHttpClient;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Apache HttpClient 通用文件上传器
+ *
+ * @author <a href="https://www.sacoc.cn">广州跨界</a>
+ * created on  2024/01/11
+ */
+public class CommonUploadRequestExecutorApacheImpl
+  extends CommonUploadRequestExecutor<CloseableHttpClient, HttpHost> {
+
+  public CommonUploadRequestExecutorApacheImpl(RequestHttp<CloseableHttpClient, HttpHost> requestHttp) {
+    super(requestHttp);
+  }
+
+  @Override
+  public String execute(String uri, CommonUploadParam param, WxType wxType) throws WxErrorException, IOException {
+    HttpPost httpPost = new HttpPost(uri);
+    if (requestHttp.getRequestHttpProxy() != null) {
+      RequestConfig config = RequestConfig.custom().setProxy(requestHttp.getRequestHttpProxy()).build();
+      httpPost.setConfig(config);
+    }
+    if (param != null) {
+      CommonUploadData data = param.getData();
+      InnerStreamBody part = new InnerStreamBody(data.getInputStream(), ContentType.DEFAULT_BINARY, data.getFileName(), data.getLength());
+      HttpEntity entity = MultipartEntityBuilder
+        .create()
+        .addPart(param.getName(), part)
+        .setMode(HttpMultipartMode.RFC6532)
+        .build();
+      httpPost.setEntity(entity);
+    }
+    try (CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpPost)) {
+      String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response);
+      if (responseContent == null || responseContent.isEmpty()) {
+        throw new WxErrorException(String.format("上传失败,服务器响应空 url:%s param:%s", uri, param));
+      }
+      WxError error = WxError.fromJson(responseContent, wxType);
+      if (error.getErrorCode() != 0) {
+        throw new WxErrorException(error);
+      }
+      return responseContent;
+    } finally {
+      httpPost.releaseConnection();
+    }
+  }
+
+  /**
+   * 内部流 请求体
+   */
+  @Getter
+  public static class InnerStreamBody extends InputStreamBody {
+
+    private final long contentLength;
+
+    public InnerStreamBody(final InputStream in, final ContentType contentType, final String filename, long contentLength) {
+      super(in, contentType, filename);
+      this.contentLength = contentLength;
+    }
+  }
+}
diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/executor/CommonUploadRequestExecutorJoddHttpImpl.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/executor/CommonUploadRequestExecutorJoddHttpImpl.java
new file mode 100644
index 000000000..36e8660f7
--- /dev/null
+++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/executor/CommonUploadRequestExecutorJoddHttpImpl.java
@@ -0,0 +1,91 @@
+package me.chanjar.weixin.common.executor;
+
+import jodd.http.HttpConnectionProvider;
+import jodd.http.HttpRequest;
+import jodd.http.HttpResponse;
+import jodd.http.ProxyInfo;
+import jodd.http.upload.Uploadable;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.SneakyThrows;
+import me.chanjar.weixin.common.bean.CommonUploadData;
+import me.chanjar.weixin.common.bean.CommonUploadParam;
+import me.chanjar.weixin.common.enums.WxType;
+import me.chanjar.weixin.common.error.WxError;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.common.util.http.RequestHttp;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * JoddHttp 通用文件上传器
+ *
+ * @author <a href="https://www.sacoc.cn">广州跨界</a>
+ * created on  2024/01/11
+ */
+public class CommonUploadRequestExecutorJoddHttpImpl extends CommonUploadRequestExecutor<HttpConnectionProvider, ProxyInfo> {
+
+  public CommonUploadRequestExecutorJoddHttpImpl(RequestHttp<HttpConnectionProvider, ProxyInfo> requestHttp) {
+    super(requestHttp);
+  }
+
+  @Override
+  public String execute(String uri, CommonUploadParam param, WxType wxType) throws WxErrorException, IOException {
+    HttpRequest request = HttpRequest.post(uri);
+    if (requestHttp.getRequestHttpProxy() != null) {
+      requestHttp.getRequestHttpClient().useProxy(requestHttp.getRequestHttpProxy());
+    }
+    request.withConnectionProvider(requestHttp.getRequestHttpClient());
+    request.form(param.getName(), new CommonUploadParamToUploadableAdapter(param.getData()));
+    HttpResponse response = request.send();
+    response.charset(StandardCharsets.UTF_8.name());
+    String responseContent = response.bodyText();
+    if (responseContent.isEmpty()) {
+      throw new WxErrorException(String.format("上传失败,服务器响应空 url:%s param:%s", uri, param));
+    }
+    WxError error = WxError.fromJson(responseContent, wxType);
+    if (error.getErrorCode() != 0) {
+      throw new WxErrorException(error);
+    }
+    return responseContent;
+  }
+
+  /**
+   * 通用上传参数 到 Uploadable 的适配器
+   */
+  @Getter
+  @AllArgsConstructor
+  public static class CommonUploadParamToUploadableAdapter implements Uploadable<CommonUploadData> {
+
+    private CommonUploadData content;
+
+    @SneakyThrows
+    @Override
+    public byte[] getBytes() {
+      return content.readAllBytes();
+    }
+
+    @Override
+    public String getFileName() {
+      return content.getFileName();
+    }
+
+    @Override
+    public String getMimeType() {
+      return null;
+    }
+
+    @SneakyThrows
+    @Override
+    public int getSize() {
+      return (int) content.getLength();
+    }
+
+    @Override
+    public InputStream openInputStream() {
+      return content.getInputStream();
+    }
+  }
+}
diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/executor/CommonUploadRequestExecutorOkHttpImpl.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/executor/CommonUploadRequestExecutorOkHttpImpl.java
new file mode 100644
index 000000000..40a4622b8
--- /dev/null
+++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/executor/CommonUploadRequestExecutorOkHttpImpl.java
@@ -0,0 +1,91 @@
+package me.chanjar.weixin.common.executor;
+
+import lombok.AllArgsConstructor;
+import me.chanjar.weixin.common.bean.CommonUploadData;
+import me.chanjar.weixin.common.bean.CommonUploadParam;
+import me.chanjar.weixin.common.enums.WxType;
+import me.chanjar.weixin.common.error.WxError;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.common.util.http.RequestHttp;
+import me.chanjar.weixin.common.util.http.okhttp.OkHttpProxyInfo;
+import okhttp3.*;
+import okio.BufferedSink;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * OkHttp 通用文件上传器
+ *
+ * @author <a href="https://www.sacoc.cn">广州跨界</a>
+ * created on  2024/01/11
+ */
+public class CommonUploadRequestExecutorOkHttpImpl extends CommonUploadRequestExecutor<OkHttpClient, OkHttpProxyInfo> {
+
+  public CommonUploadRequestExecutorOkHttpImpl(RequestHttp<OkHttpClient, OkHttpProxyInfo> requestHttp) {
+    super(requestHttp);
+  }
+
+  @Override
+  public String execute(String uri, CommonUploadParam param, WxType wxType) throws WxErrorException, IOException {
+    RequestBody requestBody = new CommonUpdateDataToRequestBodyAdapter(param.getData());
+    RequestBody body = new MultipartBody.Builder()
+      .setType(MediaType.get("multipart/form-data"))
+      .addFormDataPart(param.getName(), param.getData().getFileName(), requestBody)
+      .build();
+    Request request = new Request.Builder().url(uri).post(body).build();
+
+    try (Response response = requestHttp.getRequestHttpClient().newCall(request).execute()) {
+      ResponseBody responseBody = response.body();
+      String responseContent = responseBody == null ? "" : responseBody.string();
+      if (responseContent.isEmpty()) {
+        throw new WxErrorException(String.format("上传失败,服务器响应空 url:%s param:%s", uri, param));
+      }
+      WxError error = WxError.fromJson(responseContent, wxType);
+      if (error.getErrorCode() != 0) {
+        throw new WxErrorException(error);
+      }
+      return responseContent;
+    }
+  }
+
+  /**
+   * 通用上传输入 到 OkHttp 请求提 适配器
+   */
+  @AllArgsConstructor
+  public static class CommonUpdateDataToRequestBodyAdapter extends RequestBody {
+
+    private static final MediaType CONTENT_TYPE = MediaType.get("application/octet-stream");
+
+    private CommonUploadData data;
+
+    @Override
+    public long contentLength() {
+      return data.getLength();
+    }
+
+    @Nullable
+    @Override
+    public MediaType contentType() {
+      return CONTENT_TYPE;
+    }
+
+    @Override
+    public void writeTo(@NotNull BufferedSink bufferedSink) throws IOException {
+      InputStream inputStream = data.getInputStream();
+      int count;
+      byte[] buffer = new byte[4096];
+      while ((count = inputStream.read(buffer)) != -1) {
+        bufferedSink.write(buffer, 0, count);
+      }
+      inputStream.close();
+    }
+
+    @Override
+    public boolean isOneShot() {
+      return true;
+    }
+  }
+}
diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/service/WxService.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/service/WxService.java
index 497c1c054..f894cba44 100644
--- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/service/WxService.java
+++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/service/WxService.java
@@ -1,6 +1,7 @@
 package me.chanjar.weixin.common.service;
 
 import com.google.gson.JsonObject;
+import me.chanjar.weixin.common.bean.CommonUploadParam;
 import me.chanjar.weixin.common.bean.ToJson;
 import me.chanjar.weixin.common.error.WxErrorException;
 
@@ -60,4 +61,14 @@ public interface WxService {
    * @throws WxErrorException 异常
    */
   String post(String url, ToJson obj) throws WxErrorException;
+
+  /**
+   * 当本Service没有实现某个上传API的时候,可以用这个,针对所有微信API中的POST文件上传请求
+   *
+   * @param url   请求接口地址
+   * @param param 文件上传对象
+   * @return 接口响应字符串
+   * @throws WxErrorException 异常
+   */
+  String upload(String url, CommonUploadParam param) throws WxErrorException;
 }
diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaUploadRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaUploadRequestExecutor.java
index 14724412f..83d0c099b 100644
--- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaUploadRequestExecutor.java
+++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaUploadRequestExecutor.java
@@ -1,21 +1,27 @@
 package me.chanjar.weixin.common.util.http;
 
-import java.io.File;
-import java.io.IOException;
-
-import me.chanjar.weixin.common.enums.WxType;
+import me.chanjar.weixin.common.bean.CommonUploadParam;
 import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
+import me.chanjar.weixin.common.enums.WxType;
 import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.common.service.WxService;
 import me.chanjar.weixin.common.util.http.apache.ApacheMediaUploadRequestExecutor;
 import me.chanjar.weixin.common.util.http.jodd.JoddHttpMediaUploadRequestExecutor;
 import me.chanjar.weixin.common.util.http.okhttp.OkHttpMediaUploadRequestExecutor;
 
+import java.io.File;
+import java.io.IOException;
+
 /**
  * 上传媒体文件请求执行器.
  * 请求的参数是File, 返回的结果是String
  *
  * @author Daniel Qian
+ * @see WxService#upload(String, CommonUploadParam) 通用的上传,封装接口是推荐调用此方法
+ * @see CommonUploadParam 通用的上传参数
+ * @deprecated 不应该继续使用执行器的方式上传文件,封装上传接口时应调用通用的文件上传,而旧代码也应该逐步迁移为新的上传方式
  */
+@Deprecated
 public abstract class MediaUploadRequestExecutor<H, P> implements RequestExecutor<WxMediaUploadResult, File> {
   protected RequestHttp<H, P> requestHttp;
 
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImpl.java
index 0d4314548..a66b059c5 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImpl.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImpl.java
@@ -5,12 +5,14 @@ import com.google.gson.JsonArray;
 import com.google.gson.JsonObject;
 import lombok.extern.slf4j.Slf4j;
 import me.chanjar.weixin.common.api.WxConsts;
+import me.chanjar.weixin.common.bean.CommonUploadParam;
 import me.chanjar.weixin.common.bean.ToJson;
 import me.chanjar.weixin.common.bean.WxJsapiSignature;
 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.error.WxRuntimeException;
+import me.chanjar.weixin.common.executor.CommonUploadRequestExecutor;
 import me.chanjar.weixin.common.session.StandardSessionManager;
 import me.chanjar.weixin.common.session.WxSession;
 import me.chanjar.weixin.common.session.WxSessionManager;
@@ -24,8 +26,6 @@ import me.chanjar.weixin.cp.bean.WxCpAgentJsapiSignature;
 import me.chanjar.weixin.cp.bean.WxCpMaJsCode2SessionResult;
 import me.chanjar.weixin.cp.bean.WxCpProviderToken;
 import me.chanjar.weixin.cp.config.WxCpConfigStorage;
-import me.chanjar.weixin.cp.corpgroup.service.WxCpLinkedCorpService;
-import me.chanjar.weixin.cp.corpgroup.service.impl.WxCpLinkedCorpServiceImpl;
 import org.apache.commons.lang3.StringUtils;
 
 import java.io.File;
@@ -268,6 +268,12 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH
     return this.post(url, obj.toJson());
   }
 
+  @Override
+  public String upload(String url, CommonUploadParam param) throws WxErrorException {
+    RequestExecutor<String, CommonUploadParam> executor = CommonUploadRequestExecutor.create(getRequestHttp());
+    return this.execute(executor, url, param);
+  }
+
   @Override
   public String post(String url, Object obj) throws WxErrorException {
     return this.post(url, obj.toString());
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 37a25db14..7f2bf53ff 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
@@ -12,12 +12,14 @@ import com.google.gson.Gson;
 import com.google.gson.JsonObject;
 import lombok.extern.slf4j.Slf4j;
 import me.chanjar.weixin.common.api.WxConsts;
+import me.chanjar.weixin.common.bean.CommonUploadParam;
 import me.chanjar.weixin.common.bean.ToJson;
 import me.chanjar.weixin.common.bean.WxAccessToken;
 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.error.WxRuntimeException;
+import me.chanjar.weixin.common.executor.CommonUploadRequestExecutor;
 import me.chanjar.weixin.common.service.WxImgProcService;
 import me.chanjar.weixin.common.service.WxOcrService;
 import me.chanjar.weixin.common.util.DataUtils;
@@ -237,6 +239,12 @@ public abstract class BaseWxMaServiceImpl<H, P> implements WxMaService, RequestH
     return this.post(url, obj.toJson());
   }
 
+  @Override
+  public String upload(String url, CommonUploadParam param) throws WxErrorException {
+    RequestExecutor<String, CommonUploadParam> executor = CommonUploadRequestExecutor.create(getRequestHttp());
+    return this.execute(executor, url, param);
+  }
+
   @Override
   public String post(String url, JsonObject jsonObject) throws WxErrorException {
     return this.post(url, jsonObject.toString());
@@ -378,7 +386,7 @@ public abstract class BaseWxMaServiceImpl<H, P> implements WxMaService, RequestH
   @JsonDeserialize
   public void setMultiConfigs(Map<String, WxMaConfig> configs, String defaultMiniappId) {
     // 防止覆盖配置
-    if(this.configMap != null) {
+    if (this.configMap != null) {
       this.configMap.putAll(configs);
     } else {
       this.configMap = Maps.newHashMap(configs);
@@ -689,7 +697,7 @@ public abstract class BaseWxMaServiceImpl<H, P> implements WxMaService, RequestH
   }
 
   @Override
-  public WxMaExpressDeliveryReturnService getWxMaExpressDeliveryReturnService(){
+  public WxMaExpressDeliveryReturnService getWxMaExpressDeliveryReturnService() {
     return this.wxMaExpressDeliveryReturnService;
   }
 }
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaMediaServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaMediaServiceImpl.java
index d362d0183..0310cd099 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaMediaServiceImpl.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaMediaServiceImpl.java
@@ -3,12 +3,12 @@ package cn.binarywang.wx.miniapp.api.impl;
 import cn.binarywang.wx.miniapp.api.WxMaMediaService;
 import cn.binarywang.wx.miniapp.api.WxMaService;
 import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.common.bean.CommonUploadParam;
 import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
 import me.chanjar.weixin.common.error.WxError;
 import me.chanjar.weixin.common.error.WxErrorException;
 import me.chanjar.weixin.common.util.fs.FileUtils;
 import me.chanjar.weixin.common.util.http.BaseMediaDownloadRequestExecutor;
-import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor;
 import me.chanjar.weixin.common.util.http.RequestExecutor;
 
 import java.io.File;
@@ -38,8 +38,10 @@ public class WxMaMediaServiceImpl implements WxMaMediaService {
 
   @Override
   public WxMediaUploadResult uploadMedia(String mediaType, File file) throws WxErrorException {
+//    return this.wxMaService.execute(MediaUploadRequestExecutor.create(this.wxMaService.getRequestHttp()), url, file);
     String url = String.format(MEDIA_UPLOAD_URL, mediaType);
-    return this.wxMaService.execute(MediaUploadRequestExecutor.create(this.wxMaService.getRequestHttp()), url, file);
+    String result = wxMaService.upload(url, CommonUploadParam.fromFile("media", file));
+    return WxMediaUploadResult.fromJson(result);
   }
 
   @Override
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/executor/ApacheAuditMediaUploadRequestExecutor.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/executor/ApacheAuditMediaUploadRequestExecutor.java
deleted file mode 100644
index 782dc46f2..000000000
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/executor/ApacheAuditMediaUploadRequestExecutor.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package cn.binarywang.wx.miniapp.executor;
-
-import java.io.File;
-import java.io.IOException;
-
-import me.chanjar.weixin.common.enums.WxType;
-import me.chanjar.weixin.common.error.WxError;
-import me.chanjar.weixin.common.error.WxErrorException;
-import me.chanjar.weixin.common.util.http.RequestHttp;
-
-import me.chanjar.weixin.common.util.http.apache.Utf8ResponseHandler;
-import cn.binarywang.wx.miniapp.bean.WxMaAuditMediaUploadResult;
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpHost;
-import org.apache.http.client.config.RequestConfig;
-import org.apache.http.client.methods.CloseableHttpResponse;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.entity.mime.HttpMultipartMode;
-import org.apache.http.entity.mime.MultipartEntityBuilder;
-import org.apache.http.impl.client.CloseableHttpClient;
-
-/**
- * @author yangyh22
- * @since 2020/11/14
- */
-public class ApacheAuditMediaUploadRequestExecutor extends AuditMediaUploadRequestExecutor<CloseableHttpClient, HttpHost> {
-
-  public ApacheAuditMediaUploadRequestExecutor(RequestHttp requestHttp) {
-    super(requestHttp);
-  }
-
-  @Override
-  public WxMaAuditMediaUploadResult execute(String uri, File file, WxType wxType) throws WxErrorException, IOException {
-    HttpPost httpPost = new HttpPost(uri);
-    if (requestHttp.getRequestHttpProxy() != null) {
-      RequestConfig config = RequestConfig.custom().setProxy(requestHttp.getRequestHttpProxy()).build();
-      httpPost.setConfig(config);
-    }
-    if (file != null) {
-      HttpEntity entity = MultipartEntityBuilder
-        .create()
-        .addBinaryBody("media", file)
-        .setMode(HttpMultipartMode.RFC6532)
-        .build();
-      httpPost.setEntity(entity);
-    }
-    try (CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpPost)) {
-      String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response);
-      WxError error = WxError.fromJson(responseContent, wxType);
-      if (error.getErrorCode() != 0) {
-        throw new WxErrorException(error);
-      }
-      return WxMaAuditMediaUploadResult.fromJson(responseContent);
-    } finally {
-      httpPost.releaseConnection();
-    }
-  }
-}
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/executor/AuditMediaUploadRequestExecutor.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/executor/AuditMediaUploadRequestExecutor.java
deleted file mode 100644
index 6aad5cfdc..000000000
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/executor/AuditMediaUploadRequestExecutor.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package cn.binarywang.wx.miniapp.executor;
-
-import java.io.File;
-import java.io.IOException;
-
-import me.chanjar.weixin.common.util.http.RequestExecutor;
-import me.chanjar.weixin.common.util.http.RequestHttp;
-import me.chanjar.weixin.common.util.http.ResponseHandler;
-import me.chanjar.weixin.common.enums.WxType;
-import me.chanjar.weixin.common.error.WxErrorException;
-import cn.binarywang.wx.miniapp.bean.WxMaAuditMediaUploadResult;
-
-/**
- * 小程序 提审素材上传接口
- * 上传媒体文件请求执行器.
- * 请求的参数是File, 返回的结果是String
- *
- * @author yangyh22
- * @since 2020/11/14
- */
-public abstract class AuditMediaUploadRequestExecutor<H, P> implements RequestExecutor<WxMaAuditMediaUploadResult, File> {
-
-  protected RequestHttp<H, P> requestHttp;
-
-  public AuditMediaUploadRequestExecutor(RequestHttp requestHttp) {
-    this.requestHttp = requestHttp;
-  }
-
-  @Override
-  public void execute(String uri, File data, ResponseHandler<WxMaAuditMediaUploadResult> handler, WxType wxType) throws WxErrorException, IOException {
-    handler.handle(this.execute(uri, data, wxType));
-  }
-
-  public static RequestExecutor<WxMaAuditMediaUploadResult, File> create(RequestHttp requestHttp) {
-    switch (requestHttp.getRequestType()) {
-      case APACHE_HTTP:
-        return new ApacheAuditMediaUploadRequestExecutor(requestHttp);
-      case JODD_HTTP:
-        return new JoddHttpAuditMediaUploadRequestExecutor(requestHttp);
-      case OK_HTTP:
-        return new OkHttpAuditMediaUploadRequestExecutor(requestHttp);
-      default:
-        return null;
-    }
-  }
-
-}
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/executor/JoddHttpAuditMediaUploadRequestExecutor.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/executor/JoddHttpAuditMediaUploadRequestExecutor.java
deleted file mode 100644
index cce799098..000000000
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/executor/JoddHttpAuditMediaUploadRequestExecutor.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package cn.binarywang.wx.miniapp.executor;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-
-import jodd.http.HttpConnectionProvider;
-import jodd.http.HttpRequest;
-import jodd.http.HttpResponse;
-import jodd.http.ProxyInfo;
-import me.chanjar.weixin.common.enums.WxType;
-import me.chanjar.weixin.common.error.WxError;
-import me.chanjar.weixin.common.error.WxErrorException;
-import me.chanjar.weixin.common.util.http.RequestHttp;
-import cn.binarywang.wx.miniapp.bean.WxMaAuditMediaUploadResult;
-
-/**
- * @author yangyh22
- * @since 2020/11/14
- */
-public class JoddHttpAuditMediaUploadRequestExecutor extends AuditMediaUploadRequestExecutor<HttpConnectionProvider, ProxyInfo> {
-
-  public JoddHttpAuditMediaUploadRequestExecutor(RequestHttp requestHttp) {
-    super(requestHttp);
-  }
-
-  @Override
-  public WxMaAuditMediaUploadResult execute(String uri, File file, WxType wxType) throws WxErrorException, IOException {
-    HttpRequest request = HttpRequest.post(uri);
-    if (requestHttp.getRequestHttpProxy() != null) {
-      requestHttp.getRequestHttpClient().useProxy(requestHttp.getRequestHttpProxy());
-    }
-    request.withConnectionProvider(requestHttp.getRequestHttpClient());
-    request.form("media", file);
-    HttpResponse response = request.send();
-    response.charset(StandardCharsets.UTF_8.name());
-
-    String responseContent = response.bodyText();
-    WxError error = WxError.fromJson(responseContent, wxType);
-    if (error.getErrorCode() != 0) {
-      throw new WxErrorException(error);
-    }
-    return WxMaAuditMediaUploadResult.fromJson(responseContent);
-  }
-}
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/executor/OkHttpAuditMediaUploadRequestExecutor.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/executor/OkHttpAuditMediaUploadRequestExecutor.java
deleted file mode 100644
index 808f16d83..000000000
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/executor/OkHttpAuditMediaUploadRequestExecutor.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package cn.binarywang.wx.miniapp.executor;
-
-import java.io.File;
-import java.io.IOException;
-
-import me.chanjar.weixin.common.enums.WxType;
-import me.chanjar.weixin.common.error.WxError;
-import me.chanjar.weixin.common.error.WxErrorException;
-import me.chanjar.weixin.common.util.http.RequestHttp;
-import cn.binarywang.wx.miniapp.bean.WxMaAuditMediaUploadResult;
-import me.chanjar.weixin.common.util.http.okhttp.OkHttpProxyInfo;
-import okhttp3.MediaType;
-import okhttp3.MultipartBody;
-import okhttp3.OkHttpClient;
-import okhttp3.Request;
-import okhttp3.RequestBody;
-import okhttp3.Response;
-
-/**
- * @author yangyh22
- * @since 2020/11/14
- */
-public class OkHttpAuditMediaUploadRequestExecutor extends AuditMediaUploadRequestExecutor<OkHttpClient, OkHttpProxyInfo> {
-
-  public OkHttpAuditMediaUploadRequestExecutor(RequestHttp requestHttp) {
-    super(requestHttp);
-  }
-
-  @Override
-  public WxMaAuditMediaUploadResult execute(String uri, File file, WxType wxType) throws WxErrorException, IOException {
-
-    RequestBody body = new MultipartBody.Builder()
-      .setType(MediaType.parse("multipart/form-data"))
-      .addFormDataPart("media",
-        file.getName(),
-        RequestBody.create(MediaType.parse("application/octet-stream"), file))
-      .build();
-    Request request = new Request.Builder().url(uri).post(body).build();
-
-    Response response = requestHttp.getRequestHttpClient().newCall(request).execute();
-    String responseContent = response.body().string();
-    WxError error = WxError.fromJson(responseContent, wxType);
-    if (error.getErrorCode() != 0) {
-      throw new WxErrorException(error);
-    }
-    return WxMaAuditMediaUploadResult.fromJson(responseContent);
-  }
-
-}
diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaLiveGoodsServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaLiveGoodsServiceImplTest.java
index 5ea3b11ea..1cbdd6974 100644
--- a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaLiveGoodsServiceImplTest.java
+++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaLiveGoodsServiceImplTest.java
@@ -30,7 +30,8 @@ public class WxMaLiveGoodsServiceImplTest {
   @Test
   public void addGoods() throws Exception {
     //上传临时素材
-    WxMediaUploadResult mediaUpload = this.wxService.getMediaService().uploadMedia("image", new File("E:\\1.png"));
+    WxMediaUploadResult mediaUpload = this.wxService.getMediaService()
+      .uploadMedia("image", new File("./static/temp.jpg"));
 
     WxMaLiveGoodInfo goods = new WxMaLiveGoodInfo();
     goods.setCoverImgUrl(mediaUpload.getMediaId());
diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImplTest.java
index b31dea2dd..afb7f7212 100644
--- a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImplTest.java
+++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImplTest.java
@@ -5,6 +5,8 @@ import cn.binarywang.wx.miniapp.config.WxMaConfig;
 import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
 import cn.binarywang.wx.miniapp.test.ApiTestModule;
 import com.google.inject.Inject;
+import lombok.SneakyThrows;
+import me.chanjar.weixin.common.bean.CommonUploadParam;
 import me.chanjar.weixin.common.bean.WxAccessTokenEntity;
 import me.chanjar.weixin.common.error.WxError;
 import me.chanjar.weixin.common.error.WxErrorException;
@@ -16,6 +18,7 @@ import org.testng.Assert;
 import org.testng.annotations.Guice;
 import org.testng.annotations.Test;
 
+import java.io.File;
 import java.io.IOException;
 import java.util.HashMap;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -184,6 +187,14 @@ public class WxMaServiceImplTest {
     }
   }
 
+  @SneakyThrows
+  @Test
+  public void upload() {
+    CommonUploadParam param = CommonUploadParam.fromFile("media", new File("./static/1.jpg"));
+    String result = wxService.upload("https://api.weixin.qq.com/wxa/sec/uploadauthmaterial", param);
+    System.out.println(result);
+  }
+
   @Test
   public void testGetWxMaConfig() {
   }
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 d7c0f53ab..901a6637b 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
@@ -8,15 +8,13 @@ import lombok.Getter;
 import lombok.Setter;
 import lombok.extern.slf4j.Slf4j;
 import me.chanjar.weixin.common.api.WxConsts;
-import me.chanjar.weixin.common.bean.ToJson;
-import me.chanjar.weixin.common.bean.WxAccessToken;
-import me.chanjar.weixin.common.bean.WxJsapiSignature;
-import me.chanjar.weixin.common.bean.WxNetCheckResult;
+import me.chanjar.weixin.common.bean.*;
 import me.chanjar.weixin.common.enums.TicketType;
 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.error.WxRuntimeException;
+import me.chanjar.weixin.common.executor.CommonUploadRequestExecutor;
 import me.chanjar.weixin.common.service.WxImgProcService;
 import me.chanjar.weixin.common.service.WxOAuth2Service;
 import me.chanjar.weixin.common.service.WxOcrService;
@@ -401,6 +399,12 @@ public abstract class BaseWxMpServiceImpl<H, P> implements WxMpService, RequestH
     return this.post(url, obj.toJson());
   }
 
+  @Override
+  public String upload(String url, CommonUploadParam param) throws WxErrorException {
+    RequestExecutor<String, CommonUploadParam> executor = CommonUploadRequestExecutor.create(getRequestHttp());
+    return this.execute(executor, url, param);
+  }
+
   @Override
   public String post(String url, JsonObject jsonObject) throws WxErrorException {
     return this.post(url, jsonObject.toString());
@@ -543,7 +547,7 @@ public abstract class BaseWxMpServiceImpl<H, P> implements WxMpService, RequestH
   @Override
   public void setMultiConfigStorages(Map<String, WxMpConfigStorage> configStorages, String defaultMpId) {
     // 防止覆盖配置
-    if(this.configStorageMap != null) {
+    if (this.configStorageMap != null) {
       this.configStorageMap.putAll(configStorages);
     } else {
       this.configStorageMap = Maps.newHashMap(configStorages);
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenMaAuthService.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenMaAuthService.java
new file mode 100644
index 000000000..c59929d81
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenMaAuthService.java
@@ -0,0 +1,82 @@
+package me.chanjar.weixin.open.api;
+
+import me.chanjar.weixin.common.bean.CommonUploadData;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.open.bean.auth.*;
+
+/**
+ * 微信第三方平台 小程序认证接口 (年审)
+ * https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/product/weapp_wxverify.html
+ *
+ * @author <a href="https://www.sacoc.cn">广州跨界</a>
+ * created on 2024/01/11
+ */
+public interface WxOpenMaAuthService {
+
+  /**
+   * 1 小程程序认证
+   */
+  String OPEN_MA_AUTH_SUBMIT = "https://api.weixin.qq.com/wxa/sec/wxaauth";
+
+  /**
+   * 2 小程程序认证任务进度查询.
+   */
+  String OPEN_MA_AUTH_QUERY = "https://api.weixin.qq.com/wxa/sec/queryauth";
+
+  /**
+   * 3 小程序认证上传补充材料.
+   */
+  String OPEN_MA_AUTH_UPLOAD = "https://api.weixin.qq.com/wxa/sec/uploadauthmaterial";
+
+  /**
+   * 4 小程序认证重新提审.
+   */
+  String OPEN_MA_AUTH_RESUBMIT = "https://api.weixin.qq.com/wxa/sec/reauth";
+
+  /**
+   * 5 查询个人认证身份选项列表.
+   */
+  String OPEN_MA_AUTH_IDENTITY = "https://api.weixin.qq.com/wxa/sec/authidentitytree";
+
+
+  /**
+   * 小程序认证(提审)
+   *
+   * @param param 参数
+   * @return 提交结果,须保存任务ID 和 授权链接
+   */
+  MaAuthSubmitResult submit(MaAuthSubmitParam param) throws WxErrorException;
+
+
+  /**
+   * 进度查询
+   *
+   * @param taskId 任务ID,提交任务时返回
+   */
+  MaAuthQueryResult query(String taskId) throws WxErrorException;
+
+
+  /**
+   * 上传补充材料
+   *
+   * @param data 上传数据,仅支持png\jpeg\jpg\gif格式,文件后缀名如果填写不对会导致上传失败,建议写死1.jpg
+   */
+  MaAuthUploadResult upload(CommonUploadData data) throws WxErrorException;
+
+
+  /**
+   * 重新提审
+   *
+   * @param param 参数
+   * @return 提交结果
+   */
+  MaAuthSubmitResult resubmit(MaAuthResubmitParam param) throws WxErrorException;
+
+
+  /**
+   * 查询个人认证身份选项列表
+   *
+   * @return 职业身份认证树
+   */
+  MaAuthQueryIdentityTreeResult queryIdentityTree() throws WxErrorException;
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenMaService.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenMaService.java
index b54df7841..deb6098c3 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
@@ -702,6 +702,13 @@ public interface WxOpenMaService extends WxMaService {
    */
   WxOpenMaBasicService getBasicService();
 
+  /**
+   * 小程序认证(年审)服务
+   *
+   * @return 小程序认证(年审)服务
+   */
+  WxOpenMaAuthService getAuthService();
+
   /**
    * 小程序用户隐私保护指引服务
    *
@@ -719,7 +726,7 @@ public interface WxOpenMaService extends WxMaService {
   /**
    * 小程序审核 提审素材上传接口
    *
-   * @return
+   * @return 结果
    */
   WxMaAuditMediaUploadResult uploadMedia(File file) throws WxErrorException;
 
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMaAuthServiceImpl.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMaAuthServiceImpl.java
new file mode 100644
index 000000000..eae12acae
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMaAuthServiceImpl.java
@@ -0,0 +1,56 @@
+package me.chanjar.weixin.open.api.impl;
+
+import cn.binarywang.wx.miniapp.api.WxMaService;
+import cn.binarywang.wx.miniapp.json.WxMaGsonBuilder;
+import kotlin.Pair;
+import kotlin.collections.MapsKt;
+import me.chanjar.weixin.common.bean.CommonUploadData;
+import me.chanjar.weixin.common.bean.CommonUploadParam;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.open.api.WxOpenMaAuthService;
+import me.chanjar.weixin.open.bean.auth.*;
+
+/**
+ * 微信第三方平台 小程序认证接口 (年审)
+ *
+ * @author <a href="https://www.sacoc.cn">广州跨界</a>
+ * Created on 2024/01/11
+ */
+public class WxOpenMaAuthServiceImpl implements WxOpenMaAuthService {
+
+  private final WxMaService wxMaService;
+
+  public WxOpenMaAuthServiceImpl(WxMaService wxMaService) {
+    this.wxMaService = wxMaService;
+  }
+
+  @Override
+  public MaAuthSubmitResult submit(MaAuthSubmitParam param) throws WxErrorException {
+    String response = wxMaService.post(OPEN_MA_AUTH_SUBMIT, param);
+    return WxMaGsonBuilder.create().fromJson(response, MaAuthSubmitResult.class);
+  }
+
+  @Override
+  public MaAuthQueryResult query(String taskId) throws WxErrorException {
+    String response = wxMaService.post(OPEN_MA_AUTH_QUERY, MapsKt.mapOf(new Pair<>("taskid", taskId)));
+    return WxMaGsonBuilder.create().fromJson(response, MaAuthQueryResult.class);
+  }
+
+  @Override
+  public MaAuthUploadResult upload(CommonUploadData data) throws WxErrorException {
+    String response = wxMaService.upload(OPEN_MA_AUTH_UPLOAD, new CommonUploadParam("media", data));
+    return WxMaGsonBuilder.create().fromJson(response, MaAuthUploadResult.class);
+  }
+
+  @Override
+  public MaAuthSubmitResult resubmit(MaAuthResubmitParam param) throws WxErrorException {
+    String response = wxMaService.post(OPEN_MA_AUTH_RESUBMIT, param);
+    return WxMaGsonBuilder.create().fromJson(response, MaAuthSubmitResult.class);
+  }
+
+  @Override
+  public MaAuthQueryIdentityTreeResult queryIdentityTree() throws WxErrorException {
+    String response = wxMaService.get(OPEN_MA_AUTH_IDENTITY, null);
+    return WxMaGsonBuilder.create().fromJson(response, MaAuthQueryIdentityTreeResult.class);
+  }
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMaServiceImpl.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMaServiceImpl.java
index 5cc8e677a..c56264028 100644
--- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMaServiceImpl.java
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMaServiceImpl.java
@@ -14,6 +14,7 @@ import com.google.gson.GsonBuilder;
 import com.google.gson.JsonArray;
 import com.google.gson.JsonObject;
 import lombok.Getter;
+import me.chanjar.weixin.common.bean.CommonUploadParam;
 import me.chanjar.weixin.common.error.WxErrorException;
 import me.chanjar.weixin.open.api.*;
 import me.chanjar.weixin.open.bean.ma.WxMaPrefetchDomain;
@@ -47,6 +48,8 @@ public class WxOpenMaServiceImpl extends WxMaServiceImpl implements WxOpenMaServ
   @Getter
   private final WxOpenMaBasicService basicService;
   @Getter
+  private final WxOpenMaAuthService authService;
+  @Getter
   private final WxOpenMaPrivacyService privacyService;
   @Getter
   private final WxOpenMaShoppingOrdersService shoppingOrdersService;
@@ -56,6 +59,7 @@ public class WxOpenMaServiceImpl extends WxMaServiceImpl implements WxOpenMaServ
     this.appId = appId;
     this.wxMaConfig = wxMaConfig;
     this.basicService = new WxOpenMaBasicServiceImpl(this);
+    this.authService = new WxOpenMaAuthServiceImpl(this);
     this.privacyService = new WxOpenMaPrivacyServiceImpl(this);
     this.shoppingOrdersService = new WxOpenMaShoppingOrdersServiceImpl(this);
     initHttp();
@@ -429,7 +433,9 @@ public class WxOpenMaServiceImpl extends WxMaServiceImpl implements WxOpenMaServ
 
   @Override
   public WxMaAuditMediaUploadResult uploadMedia(File file) throws WxErrorException {
-    return (WxMaAuditMediaUploadResult) this.execute(AuditMediaUploadRequestExecutor.create(getRequestHttp()), API_AUDIT_UPLOAD_MEDIA, file);
+    CommonUploadParam param = CommonUploadParam.fromFile("media", file);
+    String result = upload(API_AUDIT_UPLOAD_MEDIA, param);
+    return WxMaAuditMediaUploadResult.fromJson(result);
   }
 
   private JsonArray toJsonArray(List<String> strList) {
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthQueryIdentityTreeResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthQueryIdentityTreeResult.java
new file mode 100644
index 000000000..c3960a2b5
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthQueryIdentityTreeResult.java
@@ -0,0 +1,29 @@
+package me.chanjar.weixin.open.bean.auth;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import me.chanjar.weixin.open.bean.result.WxOpenResult;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+/**
+ * 小程序认证 查询个人认证身份选项列表 响应
+ *
+ * @author <a href="https://www.sacoc.cn">广州跨界</a>
+ * created on 2024/01/11
+ */
+@Getter
+@Setter
+@NoArgsConstructor
+public class MaAuthQueryIdentityTreeResult extends WxOpenResult {
+
+  /**
+   * 子节点信息 非叶子节点必有
+   */
+  @Nullable
+  @SerializedName("node_list")
+  private List<MaAuthQueryIdentityTreeResultIdentityNode> nodeList;
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthQueryIdentityTreeResultIdentityLeaf.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthQueryIdentityTreeResultIdentityLeaf.java
new file mode 100644
index 000000000..48a247827
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthQueryIdentityTreeResultIdentityLeaf.java
@@ -0,0 +1,20 @@
+package me.chanjar.weixin.open.bean.auth;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 职业身份叶子信息
+ *
+ * @author <a href="https://www.sacoc.cn">广州跨界</a>
+ * created on 2024/01/11
+ */
+@Data
+@NoArgsConstructor
+public class MaAuthQueryIdentityTreeResultIdentityLeaf {
+
+  /**
+   * 要求说明
+   */
+  private String requirement;
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthQueryIdentityTreeResultIdentityNode.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthQueryIdentityTreeResultIdentityNode.java
new file mode 100644
index 000000000..ed4298aa4
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthQueryIdentityTreeResultIdentityNode.java
@@ -0,0 +1,47 @@
+package me.chanjar.weixin.open.bean.auth;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+/**
+ * 职业身份 节点信息
+ *
+ * @author <a href="https://www.sacoc.cn">广州跨界</a>
+ * created on 2024/01/11
+ */
+@Data
+@NoArgsConstructor
+public class MaAuthQueryIdentityTreeResultIdentityNode {
+
+  /**
+   * 职业身份名
+   */
+  @NotNull
+  private String name;
+
+  /**
+   * 职业身份节点ID
+   */
+  @NotNull
+  @SerializedName("node_id")
+  private Integer nodeId;
+
+  /**
+   * 要求信息 叶子节点特有
+   */
+  @Nullable
+  @SerializedName("leaf_info")
+  private MaAuthQueryIdentityTreeResultIdentityLeaf leafInfo;
+
+  /**
+   * 子节点信息 非叶子节点必有
+   */
+  @Nullable
+  @SerializedName("node_list")
+  private List<MaAuthQueryIdentityTreeResultIdentityNode> nodeList;
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthQueryResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthQueryResult.java
new file mode 100644
index 000000000..806490f72
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthQueryResult.java
@@ -0,0 +1,64 @@
+package me.chanjar.weixin.open.bean.auth;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Getter;
+import lombok.Setter;
+import me.chanjar.weixin.open.bean.result.WxOpenResult;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * 小程序认证 查询操作 响应
+ *
+ * @author <a href="https://www.sacoc.cn">广州跨界</a>
+ * created on 2024/01/11
+ */
+@Getter
+@Setter
+public class MaAuthQueryResult extends WxOpenResult {
+
+  /**
+   * 小程序ID
+   */
+  @NotNull
+  @SerializedName("appid")
+  private String appId;
+
+  /**
+   * 状态 0初始 1超24小时 2用户拒绝 3用户同意 4发起人脸 5人脸失败 6人脸ok 7人脸认证后手机验证码 8手机验证失败 9手机验证成功 11创建审核单失败 12创建审核单成功 14验证失败 15等待支付
+   */
+  @NotNull
+  @SerializedName("task_status")
+  private Integer taskStatus;
+
+  /**
+   * 授权链接
+   */
+  @NotNull
+  @SerializedName("auth_url")
+  private String authUrl;
+
+  /**
+   * 审核单状态,创建审核单成功后有效 0审核单不存在 1待支付 2审核中 3打回重填 4认证通过 5认证最终失败(不能再修改)
+   */
+  @SerializedName("apply_status")
+  private Integer applyStatus;
+
+  /**
+   * 小程序后台展示的认证订单号
+   */
+  @SerializedName("orderid")
+  private String orderId;
+
+  /**
+   * 当审核单被打回重填(apply_status=3)时有效
+   */
+  @SerializedName("refill_reason")
+  private String refillReason;
+
+  /**
+   * 审核最终失败的原因(apply_status=5)时有效
+   */
+  @SerializedName("fail_reason")
+  private String failReason;
+
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthQueryResultDispatchInfo.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthQueryResultDispatchInfo.java
new file mode 100644
index 000000000..022e47cd2
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthQueryResultDispatchInfo.java
@@ -0,0 +1,36 @@
+package me.chanjar.weixin.open.bean.auth;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * 小程序认证 查询操作 响应数据
+ *
+ * @author <a href="https://www.sacoc.cn">广州跨界</a>
+ * created on 2024/01/11
+ */
+@Data
+@NoArgsConstructor
+public class MaAuthQueryResultDispatchInfo {
+
+  /**
+   * 提供商,如:上海倍通企业信用征信有限公司
+   */
+  @NotNull
+  private String provider;
+
+  /**
+   * 联系方式,如:咨询电话:0411-84947888,咨询时间:周一至周五(工作日)8:30-17:30
+   */
+  @NotNull
+  private String contact;
+
+  /**
+   * 派遣时间戳(秒),如:1704952913
+   */
+  @NotNull
+  @SerializedName("dispatch_time")
+  private Integer dispatchTime;
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthResubmitParam.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthResubmitParam.java
new file mode 100644
index 000000000..5f28a2a68
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthResubmitParam.java
@@ -0,0 +1,22 @@
+package me.chanjar.weixin.open.bean.auth;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * 小程序认证 重新提交操作 参数
+ *
+ * @author <a href="https://www.sacoc.cn">广州跨界</a>
+ * created on 2024/01/11
+ */
+@Data
+public class MaAuthResubmitParam {
+
+  /**
+   * 认证信息
+   */
+  @NotNull
+  @SerializedName("auth_data")
+  private MaAuthResubmitParamAuthData authData;
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthResubmitParamAuthData.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthResubmitParamAuthData.java
new file mode 100644
index 000000000..9073dfdfe
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthResubmitParamAuthData.java
@@ -0,0 +1,24 @@
+package me.chanjar.weixin.open.bean.auth;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Getter;
+import lombok.Setter;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * 小程序认证 重新提交操作 认证参数
+ *
+ * @author <a href="https://www.sacoc.cn">广州跨界</a>
+ * created on 2024/01/11
+ */
+@Getter
+@Setter
+public class MaAuthResubmitParamAuthData extends MaAuthSubmitParamAuthData {
+
+  /**
+   * 认证任务id
+   */
+  @NotNull
+  @SerializedName("taskid")
+  private String taskId;
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthSubmitParam.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthSubmitParam.java
new file mode 100644
index 000000000..fd1218525
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthSubmitParam.java
@@ -0,0 +1,27 @@
+package me.chanjar.weixin.open.bean.auth;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * 小程序认证 提交操作 参数
+ *
+ * @author <a href="https://www.sacoc.cn">广州跨界</a>
+ * created on 2024/01/11
+ */
+@Data
+@NoArgsConstructor
+public class MaAuthSubmitParam {
+
+  /**
+   * 认证信息
+   *
+   * @author <a href="https://www.sacoc.cn">广州跨界</a>
+   * created on 2024/01/11
+   */
+  @NotNull
+  @SerializedName("auth_data")
+  private MaAuthSubmitParamAuthData authData;
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthSubmitParamAuthData.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthSubmitParamAuthData.java
new file mode 100644
index 000000000..9063ca543
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthSubmitParamAuthData.java
@@ -0,0 +1,110 @@
+package me.chanjar.weixin.open.bean.auth;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.jetbrains.annotations.NotNull;
+import org.springframework.lang.Nullable;
+
+import java.util.List;
+
+/**
+ * 小程序认证 提交操作 参数 数据
+ *
+ * @author <a href="https://www.sacoc.cn">广州跨界</a>
+ * created on 2024/01/11
+ */
+@Data
+@NoArgsConstructor
+public class MaAuthSubmitParamAuthData {
+
+  /**
+   * 1企业 12个体户 15个人 参考:https://developers.weixin.qq.com/doc/oplatform/openApi/OpenApiDoc/miniprogram-management/basic-info-management/getAccountBasicInfo.html#realname-status-%E5%AE%9E%E5%90%8D%E8%AE%A4%E8%AF%81%E7%8A%B6%E6%80%81%E6%9E%9A%E4%B8%BE%E5%80%BC
+   */
+  @NotNull
+  @SerializedName("customer_type")
+  private String customerType;
+
+  /**
+   * 联系人信息
+   */
+  @NotNull
+  @SerializedName("contact_info")
+  private MaAuthSubmitParamContactInfo contactInfo;
+
+  /**
+   * 发票信息,如果是服务商代缴模式,不需要改参数
+   */
+  @Nullable
+  @SerializedName("invoice_info")
+  private MaAuthSubmitParamInvoiceInfo invoiceInfo;
+
+  /**
+   * 非个人类型必填。主体资质材料 media_id 支持jpg,jpeg .bmp.gif .png格式,仅支持一张图片
+   */
+  @Nullable
+  private String qualification;
+
+  /**
+   * 主体资质其他证明材料 media_id 支持jpg,jpeg .bmp.gif .png格式,最多上传10张图片
+   */
+  @Nullable
+  @SerializedName("qualification_other")
+  private List<String> qualificationOther;
+
+  /**
+   * 小程序账号名称
+   */
+  @NotNull
+  @SerializedName("account_name")
+  private String accountName;
+
+  /**
+   * 小程序账号名称命名类型 1:基于自选词汇命名 2:基于商标命名
+   */
+  @NotNull
+  @SerializedName("account_name_type")
+  private Integer accountNameType;
+
+  /**
+   * 名称命中关键词-补充材料 media_id 支持jpg,jpeg .bmp.gif .png格式,支持上传多张图片
+   */
+  @Nullable
+  @SerializedName("account_supplemental")
+  private List<String> accountSupplemental;
+
+  /**
+   * 支付方式 1:消耗服务商预购包 2:小程序开发者自行支付
+   */
+  @NotNull
+  @SerializedName("pay_type")
+  private String payType;
+
+  /**
+   * 认证类型为个人类型时可以选择要认证的身份,从/wxa/sec/authidentitytree 里获取,填叶节点的name
+   */
+  @Nullable
+  @SerializedName("auth_identification")
+  private String authIdentification;
+
+  /**
+   * 填了auth_identification则必填。身份证明材料 media_id (1)基于不同认证身份上传不同的材料;(2)认证类型=1时选填,支持上传10张图片(3)支持jpg,jpeg .bmp.gif .png格式
+   */
+  @Nullable
+  @SerializedName("auth_ident_material")
+  private String authIdentMaterial;
+
+  /**
+   * 第三方联系电话
+   */
+  @NotNull
+  @SerializedName("third_party_phone")
+  private String thirdPartyPhone;
+
+  /**
+   * 选择服务商代缴模式时必填。服务市场appid
+   */
+  @Nullable
+  @SerializedName("service_appid")
+  private String serviceAppid;
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthSubmitParamContactInfo.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthSubmitParamContactInfo.java
new file mode 100644
index 000000000..02c162bb9
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthSubmitParamContactInfo.java
@@ -0,0 +1,28 @@
+package me.chanjar.weixin.open.bean.auth;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * 联系人信息
+ *
+ * @author <a href="https://www.sacoc.cn">广州跨界</a>
+ * created on 2024/01/11
+ */
+@Data
+@NoArgsConstructor
+public class MaAuthSubmitParamContactInfo {
+
+  /**
+   * 姓名
+   */
+  @NotNull
+  private String name;
+
+  /**
+   * 邮箱
+   */
+  @NotNull
+  private String email;
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthSubmitParamInvoiceElectronic.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthSubmitParamInvoiceElectronic.java
new file mode 100644
index 000000000..825878fcd
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthSubmitParamInvoiceElectronic.java
@@ -0,0 +1,29 @@
+package me.chanjar.weixin.open.bean.auth;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.jetbrains.annotations.NotNull;
+import org.springframework.lang.Nullable;
+
+/**
+ * 发票 - 电子发票
+ *
+ * @author <a href="https://www.sacoc.cn">广州跨界</a>
+ * created on 2024/01/11
+ */
+@Data
+@NoArgsConstructor
+public class MaAuthSubmitParamInvoiceElectronic {
+
+  /**
+   * 纳税识别号(15位、17、18或20位)
+   */
+  @NotNull
+  private String id;
+
+  /**
+   * 发票备注(选填)
+   */
+  @Nullable
+  private String desc;
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthSubmitParamInvoiceInfo.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthSubmitParamInvoiceInfo.java
new file mode 100644
index 000000000..36020a8eb
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthSubmitParamInvoiceInfo.java
@@ -0,0 +1,44 @@
+package me.chanjar.weixin.open.bean.auth;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.jetbrains.annotations.NotNull;
+import org.springframework.lang.Nullable;
+
+/**
+ * 发票信息
+ *
+ * @author <a href="https://www.sacoc.cn">广州跨界</a>
+ * created on 2024/01/11
+ */
+@Data
+@NoArgsConstructor
+public class MaAuthSubmitParamInvoiceInfo {
+
+  /**
+   * 发票类型 1: 不开发票 2: 电子发票 3: 增值税专票
+   */
+  @NotNull
+  @SerializedName("invoice_type")
+  private Integer invoiceType;
+
+  /**
+   * 发票类型=2时必填 电子发票开票信息
+   */
+  @Nullable
+  private MaAuthSubmitParamInvoiceElectronic electronic;
+
+  /**
+   * 发票类型=3时必填 增值税专票开票信息
+   */
+  @Nullable
+  private MaAuthSubmitParamInvoiceVat vat;
+
+  /**
+   * 发票抬头,发票类型!=1时必填 需要和认证主体名称一样
+   */
+  @Nullable
+  @SerializedName("invoice_title")
+  private String invoiceTitle;
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthSubmitParamInvoiceVat.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthSubmitParamInvoiceVat.java
new file mode 100644
index 000000000..33bb8fb60
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthSubmitParamInvoiceVat.java
@@ -0,0 +1,102 @@
+package me.chanjar.weixin.open.bean.auth;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.jetbrains.annotations.NotNull;
+import org.springframework.lang.Nullable;
+
+/**
+ * 发票 - 增值税专票
+ *
+ * @author <a href="https://www.sacoc.cn">广州跨界</a>
+ * created on 2024/01/11
+ */
+@Data
+@NoArgsConstructor
+public class MaAuthSubmitParamInvoiceVat {
+
+
+  /**
+   * 企业电话
+   */
+  @NotNull
+  @SerializedName("enterprise_phone")
+  private String enterprisePhone;
+
+  /**
+   * 纳税识别号(15位、17、18或20位)
+   */
+  @NotNull
+  private String id;
+
+  /**
+   * 企业注册地址
+   */
+  @NotNull
+  @SerializedName("enterprise_address")
+  private String enterpriseAddress;
+
+  /**
+   * 企业开户银行
+   */
+  @NotNull
+  @SerializedName("bank_name")
+  private String bankName;
+
+  /**
+   * 企业银行账号
+   */
+  @NotNull
+  @SerializedName("bank_account")
+  private String bankAccount;
+
+  /**
+   * 发票邮寄地址邮编
+   */
+  @NotNull
+  @SerializedName("mailing_address")
+  private String mailingAddress;
+
+  /**
+   * 街道地址
+   */
+  @NotNull
+  private String address;
+
+  /**
+   * 联系人
+   */
+  @NotNull
+  private String name;
+
+  /**
+   * 联系电话
+   */
+  @NotNull
+  private String phone;
+
+  /**
+   * 省份
+   */
+  @NotNull
+  private String province;
+
+  /**
+   * 城市
+   */
+  @NotNull
+  private String city;
+
+  /**
+   * 县区
+   */
+  @NotNull
+  private String district;
+
+  /**
+   * 发票备注(选填)
+   */
+  @Nullable
+  private String desc;
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthSubmitResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthSubmitResult.java
new file mode 100644
index 000000000..ddc681ba5
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthSubmitResult.java
@@ -0,0 +1,34 @@
+package me.chanjar.weixin.open.bean.auth;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import me.chanjar.weixin.open.bean.result.WxOpenResult;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * 小程序认证 提交操作 响应
+ *
+ * @author <a href="https://www.sacoc.cn">广州跨界</a>
+ * created on 2024/01/11
+ */
+@Getter
+@Setter
+@NoArgsConstructor
+public class MaAuthSubmitResult extends WxOpenResult {
+
+  /**
+   * 任务ID
+   */
+  @NotNull
+  @SerializedName("taskid")
+  private String taskId;
+
+  /**
+   * 小程序管理员授权链接
+   */
+  @NotNull
+  @SerializedName("auth_url")
+  private String authUrl;
+}
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthUploadResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthUploadResult.java
new file mode 100644
index 000000000..0e0c511a2
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/auth/MaAuthUploadResult.java
@@ -0,0 +1,27 @@
+package me.chanjar.weixin.open.bean.auth;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import me.chanjar.weixin.open.bean.result.WxOpenResult;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * 小程序认证 上传补充材料操作 响应
+ *
+ * @author <a href="https://www.sacoc.cn">广州跨界</a>
+ * created on 2024/01/11
+ */
+@Getter
+@Setter
+@NoArgsConstructor
+public class MaAuthUploadResult extends WxOpenResult {
+
+  /**
+   * 媒体ID
+   */
+  @NotNull
+  @SerializedName("mediaid")
+  private String mediaId;
+}
diff --git a/weixin-java-qidian/src/main/java/me/chanjar/weixin/qidian/api/impl/BaseWxQidianServiceImpl.java b/weixin-java-qidian/src/main/java/me/chanjar/weixin/qidian/api/impl/BaseWxQidianServiceImpl.java
index 0bc089608..2b7c7057a 100644
--- a/weixin-java-qidian/src/main/java/me/chanjar/weixin/qidian/api/impl/BaseWxQidianServiceImpl.java
+++ b/weixin-java-qidian/src/main/java/me/chanjar/weixin/qidian/api/impl/BaseWxQidianServiceImpl.java
@@ -1,44 +1,23 @@
 package me.chanjar.weixin.qidian.api.impl;
 
-import static me.chanjar.weixin.qidian.enums.WxQidianApiUrl.Other.CLEAR_QUOTA_URL;
-import static me.chanjar.weixin.qidian.enums.WxQidianApiUrl.Other.GET_CALLBACK_IP_URL;
-import static me.chanjar.weixin.qidian.enums.WxQidianApiUrl.Other.GET_CURRENT_AUTOREPLY_INFO_URL;
-import static me.chanjar.weixin.qidian.enums.WxQidianApiUrl.Other.GET_TICKET_URL;
-import static me.chanjar.weixin.qidian.enums.WxQidianApiUrl.Other.NETCHECK_URL;
-import static me.chanjar.weixin.qidian.enums.WxQidianApiUrl.Other.QRCONNECT_URL;
-import static me.chanjar.weixin.qidian.enums.WxQidianApiUrl.Other.SHORTURL_API_URL;
-
-import java.io.IOException;
-import java.util.Map;
-import java.util.concurrent.locks.Lock;
-
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Maps;
 import com.google.gson.JsonArray;
 import com.google.gson.JsonObject;
-
-import org.apache.commons.lang3.StringUtils;
-
 import lombok.Getter;
 import lombok.extern.slf4j.Slf4j;
 import me.chanjar.weixin.common.api.WxConsts;
-import me.chanjar.weixin.common.bean.ToJson;
-import me.chanjar.weixin.common.bean.WxAccessToken;
-import me.chanjar.weixin.common.bean.WxJsapiSignature;
-import me.chanjar.weixin.common.bean.WxNetCheckResult;
+import me.chanjar.weixin.common.bean.*;
 import me.chanjar.weixin.common.enums.TicketType;
 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.error.WxRuntimeException;
+import me.chanjar.weixin.common.executor.CommonUploadRequestExecutor;
 import me.chanjar.weixin.common.util.DataUtils;
 import me.chanjar.weixin.common.util.RandomUtils;
 import me.chanjar.weixin.common.util.crypto.SHA1;
-import me.chanjar.weixin.common.util.http.RequestExecutor;
-import me.chanjar.weixin.common.util.http.RequestHttp;
-import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor;
-import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor;
-import me.chanjar.weixin.common.util.http.URIUtil;
+import me.chanjar.weixin.common.util.http.*;
 import me.chanjar.weixin.common.util.json.GsonParser;
 import me.chanjar.weixin.common.util.json.WxGsonBuilder;
 import me.chanjar.weixin.qidian.api.WxQidianCallDataService;
@@ -47,6 +26,13 @@ import me.chanjar.weixin.qidian.api.WxQidianService;
 import me.chanjar.weixin.qidian.config.WxQidianConfigStorage;
 import me.chanjar.weixin.qidian.enums.WxQidianApiUrl;
 import me.chanjar.weixin.qidian.util.WxQidianConfigStorageHolder;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.concurrent.locks.Lock;
+
+import static me.chanjar.weixin.qidian.enums.WxQidianApiUrl.Other.*;
 
 /**
  * 基础实现类.
@@ -56,9 +42,9 @@ import me.chanjar.weixin.qidian.util.WxQidianConfigStorageHolder;
 @Slf4j
 public abstract class BaseWxQidianServiceImpl<H, P> implements WxQidianService, RequestHttp<H, P> {
   @Getter
-  private WxQidianDialService dialService = new WxQidianDialServiceImpl(this);
+  private final WxQidianDialService dialService = new WxQidianDialServiceImpl(this);
   @Getter
-  private WxQidianCallDataService callDataService = new WxQidianCallDataServiceImpl(this);
+  private final WxQidianCallDataService callDataService = new WxQidianCallDataServiceImpl(this);
 
   private Map<String, WxQidianConfigStorage> configStorageMap;
 
@@ -93,7 +79,7 @@ public abstract class BaseWxQidianServiceImpl<H, P> implements WxQidianService,
       try {
         if (this.getWxMpConfigStorage().isTicketExpired(type)) {
           String responseContent = execute(SimpleGetRequestExecutor.create(this),
-              GET_TICKET_URL.getUrl(this.getWxMpConfigStorage()) + type.getCode(), null);
+            GET_TICKET_URL.getUrl(this.getWxMpConfigStorage()) + type.getCode(), null);
           JsonObject tmpJsonObject = GsonParser.parse(responseContent);
           String jsapiTicket = tmpJsonObject.get("ticket").getAsString();
           int expiresInSeconds = tmpJsonObject.get("expires_in").getAsInt();
@@ -123,7 +109,7 @@ public abstract class BaseWxQidianServiceImpl<H, P> implements WxQidianService,
     String randomStr = RandomUtils.getRandomStr();
     String jsapiTicket = getJsapiTicket(false);
     String signature = SHA1.genWithAmple("jsapi_ticket=" + jsapiTicket, "noncestr=" + randomStr,
-        "timestamp=" + timestamp, "url=" + url);
+      "timestamp=" + timestamp, "url=" + url);
     WxJsapiSignature jsapiSignature = new WxJsapiSignature();
     jsapiSignature.setAppId(this.getWxMpConfigStorage().getAppId());
     jsapiSignature.setTimestamp(timestamp);
@@ -154,7 +140,7 @@ public abstract class BaseWxQidianServiceImpl<H, P> implements WxQidianService,
   @Override
   public String buildQrConnectUrl(String redirectUri, String scope, String state) {
     return String.format(QRCONNECT_URL.getUrl(this.getWxMpConfigStorage()), this.getWxMpConfigStorage().getAppId(),
-        URIUtil.encodeURIComponent(redirectUri), scope, StringUtils.trimToEmpty(state));
+      URIUtil.encodeURIComponent(redirectUri), scope, StringUtils.trimToEmpty(state));
   }
 
   @Override
@@ -215,6 +201,12 @@ public abstract class BaseWxQidianServiceImpl<H, P> implements WxQidianService,
     return this.post(url, obj.toJson());
   }
 
+  @Override
+  public String upload(String url, CommonUploadParam param) throws WxErrorException {
+    RequestExecutor<String, CommonUploadParam> executor = CommonUploadRequestExecutor.create(getRequestHttp());
+    return this.execute(executor, url, param);
+  }
+
   @Override
   public String post(String url, JsonObject jsonObject) throws WxErrorException {
     return this.post(url, jsonObject.toString());