🆕 #3347 【开放平台】新增小程序备案相关接口

This commit is contained in:
Molzx 2024-08-15 22:07:20 +08:00 committed by Binary Wang
parent 8d108a0467
commit 606e932647
23 changed files with 2020 additions and 0 deletions

View File

@ -0,0 +1,207 @@
package me.chanjar.weixin.open.api;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.open.bean.icp.*;
import me.chanjar.weixin.open.bean.result.WxOpenResult;
import java.io.File;
/**
* @author xzh
* @Description 小程序备案
* https://developers.weixin.qq.com/doc/oplatform/openApi/OpenApiDoc/miniprogram-management/record/queryIcpVerifyTask.html
* @createTime 2024/08/14 10:52
*/
public interface WxOpenMaIcpService {
/**
* 查询人脸核身任务状态
* https://developers.weixin.qq.com/doc/oplatform/openApi/OpenApiDoc/miniprogram-management/record/queryIcpVerifyTask.html
*/
String QUERY_ICP_VERIFY_TASK = "https://api.weixin.qq.com/wxa/icp/query_icp_verifytask";
/**
* 发起小程序管理员人脸核身
* https://developers.weixin.qq.com/doc/oplatform/openApi/OpenApiDoc/miniprogram-management/record/createIcpVerifyTask.html
*/
String CREATE_ICP_VERIFY_TASK = "https://api.weixin.qq.com/wxa/icp/create_icp_verifytask";
/**
* 上传小程序备案媒体材料
* https://developers.weixin.qq.com/doc/oplatform/openApi/OpenApiDoc/miniprogram-management/record/uploadIcpMedia.html
*/
String UPLOAD_ICP_MEDIA = "https://api.weixin.qq.com/wxa/icp/upload_icp_media";
/**
* 撤回小程序备案申请
* https://developers.weixin.qq.com/doc/oplatform/openApi/OpenApiDoc/miniprogram-management/record/cancelApplyIcpFiling.html
*/
String CANCEL_APPLY_ICP_FILING = "https://api.weixin.qq.com/wxa/icp/cancel_apply_icp_filing";
/**
* 申请小程序备案
* https://developers.weixin.qq.com/doc/oplatform/openApi/OpenApiDoc/miniprogram-management/record/applyIcpFiling.html
*/
String APPLY_ICP_FILING = "https://api.weixin.qq.com/wxa/icp/apply_icp_filing";
/**
* 注销小程序备案
* https://developers.weixin.qq.com/doc/oplatform/openApi/OpenApiDoc/miniprogram-management/record/cancelIcpfiling.html
*/
String CANCEL_ICP_FILING = "https://api.weixin.qq.com/wxa/icp/cancel_icp_filing";
/**
* 获取小程序备案状态及驳回原因
* https://developers.weixin.qq.com/doc/oplatform/openApi/OpenApiDoc/miniprogram-management/record/getIcpEntranceInfo.html
*/
String GET_ICP_ENTRANCE_INFO = "https://api.weixin.qq.com/wxa/icp/get_icp_entrance_info";
/**
* 获取小程序已备案详情
* https://developers.weixin.qq.com/doc/oplatform/openApi/OpenApiDoc/miniprogram-management/record/getOnlineIcpOrder.html
*/
String GET_ONLINE_ICP_ORDER = "https://api.weixin.qq.com/wxa/icp/get_online_icp_order";
/**
* 获取小程序服务内容类型
* https://developers.weixin.qq.com/doc/oplatform/openApi/OpenApiDoc/miniprogram-management/record/queryIcpServiceContentTypes.html
*/
String QUERY_ICP_SERVICE_CONTENT_TYPES = "https://api.weixin.qq.com/wxa/icp/query_icp_service_content_types";
/**
* 获取证件类型
* https://developers.weixin.qq.com/doc/oplatform/openApi/OpenApiDoc/miniprogram-management/record/queryIcpCertificateTypes.html
*/
String QUERY_ICP_CERTIFICATE_TYPES = "https://api.weixin.qq.com/wxa/icp/query_icp_certificate_types";
/**
* 获取区域信息
* https://developers.weixin.qq.com/doc/oplatform/openApi/OpenApiDoc/miniprogram-management/record/queryIcpDistrictCode.html
*/
String QUERY_ICP_DISTRICT_CODE = "https://api.weixin.qq.com/wxa/icp/query_icp_district_code";
/**
* 获取前置审批项类型
* https://developers.weixin.qq.com/doc/oplatform/openApi/OpenApiDoc/miniprogram-management/record/queryIcpNrlxTypes.html
*/
String QUERY_ICP_NRLX_TYPES = "https://api.weixin.qq.com/wxa/icp/query_icp_nrlx_types";
/**
* 获取单位性质
* https://developers.weixin.qq.com/doc/oplatform/openApi/OpenApiDoc/miniprogram-management/record/queryIcpSubjectTypes.html
*/
String QUERY_ICP_SUBJECT_TYPES = "https://api.weixin.qq.com/wxa/icp/query_icp_subject_types";
/**
* 获取小程序备案媒体材料
* https://developers.weixin.qq.com/doc/oplatform/openApi/OpenApiDoc/miniprogram-management/record/getIcpMedia.html
*/
String GET_ICP_MEDIA = "https://api.weixin.qq.com/wxa/icp/get_icp_media";
/**
* 查询人脸核身任务状态
*
* @param taskId 任务id
* @return 人脸核身任务的状态和结果
* @throws WxErrorException e
*/
WxOpenIcpVerifyTaskResult queryIcpVerifyTask(String taskId) throws WxErrorException;
/**
* 发起小程序管理员人脸核身
*
* @return 人脸核验任务结果
* @throws WxErrorException e
*/
WxOpenIcpCreateIcpVerifyTaskResult createIcpVerifyTask() throws WxErrorException;
/**
* 上传小程序备案媒体材料
*
* @param param 备案媒体材料
* @return 备案媒体材料结果
* @throws WxErrorException e
*/
WxOpenUploadIcpMediaResult uploadIcpMedia(WxOpenUploadIcpMediaParam param) throws WxErrorException;
/**
* 撤回小程序备案申请
*
* @return r
* @throws WxErrorException e
*/
WxOpenResult cancelApplyIcpFiling() throws WxErrorException;
/**
* 申请小程序备案
*
* @param param 参数
* @return r
* @throws WxErrorException e
*/
WxOpenApplyIcpFilingResult applyIcpFiling(WxOpenApplyIcpFilingParam param) throws WxErrorException;
/**
* 注销小程序备案
* @param cancelType 注销类型1 -- 注销主体, 2 -- 注销小程序, 3 -- 注销微信小程序
* @return r
* @throws WxErrorException e
*/
WxOpenResult cancelIcpFiling(Integer cancelType) throws WxErrorException;
/**
* 获取小程序备案状态及驳回原因
* @return r
* @throws WxErrorException e
*/
WxOpenIcpEntranceInfoResult getIcpEntranceInfo() throws WxErrorException;
/**
* 获取小程序已备案详情
* @return 已备案详情
* @throws WxErrorException e
*/
WxOpenOnlineIcpOrderResult getOnlineIcpOrder() throws WxErrorException;
/**
* 获取小程序服务内容类型
* @return 小程序服务内容类型定义
* @throws WxErrorException e
*/
WxOpenQueryIcpServiceContentTypesResult queryIcpServiceContentTypes() throws WxErrorException;
/**
* 获取证件类型
* @return 证件类型定义
* @throws WxErrorException e
*/
WxOpenQueryIcpCertificateTypeResult queryIcpCertificateTypes() throws WxErrorException;
/**
* 获取区域信息
* @return 省市区的区域信息
* @throws WxErrorException e
*/
WxOpenQueryIcpDistrictCodeResult queryIcpDistrictCode() throws WxErrorException;
/**
* 获取前置审批项类型
* @return 小程序备案前置审批项类型定义
* @throws WxErrorException e
*/
WxOpenQueryIcpNrlxTypesResult queryIcpNrlxTypes() throws WxErrorException;
/**
* 获取单位性质
* @return 单位性质定义
* @throws WxErrorException e
*/
WxOpenQueryIcpSubjectTypeResult queryIcpSubjectTypes() throws WxErrorException;
/**
* 获取小程序备案媒体材料
* @param mediaId 上传小程序备案媒体材料接口返回的 media_id示例值4ahCGpd3CYkE6RpkNkUR5czt3LvG8xDnDdKAz6bBKttSfM8p4k5Rj6823HXugPwQBurgMezyib7
* @return 所上传的图片或视频媒体材料
* @throws WxErrorException e
*/
File getIcpMedia(String mediaId) throws WxErrorException;
}

View File

@ -724,6 +724,13 @@ public interface WxOpenMaService extends WxMaService {
*/
WxOpenMaAuthService getAuthService();
/**
* 小程序备案服务
*
* @return 小程序备案服务
*/
WxOpenMaIcpService getIcpService();
/**
* 小程序用户隐私保护指引服务
*

View File

@ -0,0 +1,215 @@
package me.chanjar.weixin.open.api.impl;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.json.WxMaGsonBuilder;
import com.google.gson.JsonObject;
import me.chanjar.weixin.open.bean.CommonUploadMultiParam;
import me.chanjar.weixin.common.error.WxError;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.open.executor.CommonUploadMultiRequestExecutor;
import me.chanjar.weixin.common.util.http.BaseMediaDownloadRequestExecutor;
import me.chanjar.weixin.common.util.http.RequestExecutor;
import me.chanjar.weixin.open.api.WxOpenMaIcpService;
import me.chanjar.weixin.open.bean.icp.*;
import me.chanjar.weixin.open.bean.result.WxOpenResult;
import me.chanjar.weixin.open.util.json.WxOpenGsonBuilder;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
/**
* @author xzh
* @Description
* @createTime 2024/08/14 14:48
*/
public class WxOpenMaIcpServiceImpl implements WxOpenMaIcpService {
private final WxMaService wxMaService;
public WxOpenMaIcpServiceImpl(WxMaService wxMaService) {
this.wxMaService = wxMaService;
}
/**
* 查询人脸核身任务状态
*
* @param taskId 任务id
* @return 人脸核身任务的状态和结果
* @throws WxErrorException e
*/
@Override
public WxOpenIcpVerifyTaskResult queryIcpVerifyTask(String taskId) throws WxErrorException {
JsonObject params = new JsonObject();
params.addProperty("task_id", taskId);
String response = wxMaService.post(QUERY_ICP_VERIFY_TASK, params);
return WxOpenGsonBuilder.create().fromJson(response, WxOpenIcpVerifyTaskResult.class);
}
/**
* 发起小程序管理员人脸核身
*
* @return 人脸核验任务结果
* @throws WxErrorException e
*/
@Override
public WxOpenIcpCreateIcpVerifyTaskResult createIcpVerifyTask() throws WxErrorException {
String response = wxMaService.post(CREATE_ICP_VERIFY_TASK, "");
return WxMaGsonBuilder.create().fromJson(response, WxOpenIcpCreateIcpVerifyTaskResult.class);
}
/**
* 上传小程序备案媒体材料
*
* @param param 备案媒体材料
* @return 备案媒体材料结果
* @throws WxErrorException e
*/
@Override
public WxOpenUploadIcpMediaResult uploadIcpMedia(WxOpenUploadIcpMediaParam param) throws WxErrorException {
RequestExecutor<String, CommonUploadMultiParam> executor = CommonUploadMultiRequestExecutor.create(wxMaService.getRequestHttp());
String response = wxMaService.execute(executor, UPLOAD_ICP_MEDIA, param.toCommonUploadMultiParam());
return WxMaGsonBuilder.create().fromJson(response, WxOpenUploadIcpMediaResult.class);
}
/**
* 撤回小程序备案申请
*
* @return r
* @throws WxErrorException e
*/
@Override
public WxOpenResult cancelApplyIcpFiling() throws WxErrorException {
String response = wxMaService.post(CANCEL_APPLY_ICP_FILING, "");
return WxMaGsonBuilder.create().fromJson(response, WxOpenResult.class);
}
/**
* 申请小程序备案
*
* @param param 参数
* @return r
* @throws WxErrorException e
*/
@Override
public WxOpenApplyIcpFilingResult applyIcpFiling(WxOpenApplyIcpFilingParam param) throws WxErrorException {
String response = wxMaService.post(APPLY_ICP_FILING, param);
return WxMaGsonBuilder.create().fromJson(response, WxOpenApplyIcpFilingResult.class);
}
/**
* 注销小程序备案
*
* @param cancelType 注销类型1 -- 注销主体, 2 -- 注销小程序, 3 -- 注销微信小程序
* @return r
* @throws WxErrorException e
*/
@Override
public WxOpenResult cancelIcpFiling(Integer cancelType) throws WxErrorException {
JsonObject params = new JsonObject();
params.addProperty("cancel_type", cancelType);
String response = wxMaService.post(CANCEL_ICP_FILING, params);
return WxOpenGsonBuilder.create().fromJson(response, WxOpenResult.class);
}
/**
* 获取小程序备案状态及驳回原因
*
* @return r
* @throws WxErrorException e
*/
@Override
public WxOpenIcpEntranceInfoResult getIcpEntranceInfo() throws WxErrorException {
String response = wxMaService.get(GET_ICP_ENTRANCE_INFO, null);
return WxMaGsonBuilder.create().fromJson(response, WxOpenIcpEntranceInfoResult.class);
}
/**
* 获取小程序已备案详情
*
* @return 已备案详情
* @throws WxErrorException e
*/
@Override
public WxOpenOnlineIcpOrderResult getOnlineIcpOrder() throws WxErrorException {
String response = wxMaService.get(GET_ONLINE_ICP_ORDER, null);
return WxMaGsonBuilder.create().fromJson(response, WxOpenOnlineIcpOrderResult.class);
}
/**
* 获取小程序服务内容类型
*
* @return 小程序服务内容类型定义
* @throws WxErrorException e
*/
@Override
public WxOpenQueryIcpServiceContentTypesResult queryIcpServiceContentTypes() throws WxErrorException {
String response = wxMaService.get(QUERY_ICP_SERVICE_CONTENT_TYPES, null);
return WxMaGsonBuilder.create().fromJson(response, WxOpenQueryIcpServiceContentTypesResult.class);
}
/**
* 获取证件类型
*
* @return 证件类型定义
* @throws WxErrorException e
*/
@Override
public WxOpenQueryIcpCertificateTypeResult queryIcpCertificateTypes() throws WxErrorException {
String response = wxMaService.get(QUERY_ICP_CERTIFICATE_TYPES, null);
return WxMaGsonBuilder.create().fromJson(response, WxOpenQueryIcpCertificateTypeResult.class);
}
/**
* 获取区域信息
*
* @return 省市区的区域信息
* @throws WxErrorException e
*/
@Override
public WxOpenQueryIcpDistrictCodeResult queryIcpDistrictCode() throws WxErrorException {
String response = wxMaService.get(QUERY_ICP_DISTRICT_CODE, null);
return WxMaGsonBuilder.create().fromJson(response, WxOpenQueryIcpDistrictCodeResult.class);
}
/**
* 获取前置审批项类型
*
* @return 小程序备案前置审批项类型定义
* @throws WxErrorException e
*/
@Override
public WxOpenQueryIcpNrlxTypesResult queryIcpNrlxTypes() throws WxErrorException {
String response = wxMaService.get(QUERY_ICP_NRLX_TYPES, null);
return WxMaGsonBuilder.create().fromJson(response, WxOpenQueryIcpNrlxTypesResult.class);
}
/**
* 获取单位性质
*
* @return 单位性质定义
* @throws WxErrorException e
*/
@Override
public WxOpenQueryIcpSubjectTypeResult queryIcpSubjectTypes() throws WxErrorException {
String response = wxMaService.get(QUERY_ICP_SUBJECT_TYPES, null);
return WxMaGsonBuilder.create().fromJson(response, WxOpenQueryIcpSubjectTypeResult.class);
}
/**
* 获取小程序备案媒体材料
* @param mediaId 上传小程序备案媒体材料接口返回的 media_id示例值4ahCGpd3CYkE6RpkNkUR5czt3LvG8xDnDdKAz6bBKttSfM8p4k5Rj6823HXugPwQBurgMezyib7
* @return 所上传的图片或视频媒体材料
* @throws WxErrorException e
*/
@Override
public File getIcpMedia(String mediaId) throws WxErrorException {
try {
RequestExecutor<File, String> executor = BaseMediaDownloadRequestExecutor
.create(this.wxMaService.getRequestHttp(), Files.createTempDirectory("wxma").toFile());
return this.wxMaService.execute(executor, GET_ICP_MEDIA, "media_id=" + mediaId);
} catch (IOException e) {
throw new WxErrorException(WxError.builder().errorMsg(e.getMessage()).build(), e);
}
}
}

View File

@ -49,6 +49,8 @@ public class WxOpenMaServiceImpl extends WxMaServiceImpl implements WxOpenMaServ
@Getter
private final WxOpenMaAuthService authService;
@Getter
private final WxOpenMaIcpService icpService;
@Getter
private final WxOpenMaPrivacyService privacyService;
@Getter
private final WxOpenMaShoppingOrdersService shoppingOrdersService;
@ -59,6 +61,7 @@ public class WxOpenMaServiceImpl extends WxMaServiceImpl implements WxOpenMaServ
this.wxMaConfig = wxMaConfig;
this.basicService = new WxOpenMaBasicServiceImpl(this);
this.authService = new WxOpenMaAuthServiceImpl(this);
this.icpService = new WxOpenMaIcpServiceImpl(this);
this.privacyService = new WxOpenMaPrivacyServiceImpl(this);
this.shoppingOrdersService = new WxOpenMaShoppingOrdersServiceImpl(this);
initHttp();

View File

@ -0,0 +1,39 @@
package me.chanjar.weixin.open.bean;
import lombok.Builder;
import lombok.Data;
import me.chanjar.weixin.common.bean.CommonUploadParam;
import java.io.Serializable;
import java.util.List;
/**
* @author xzh
* @Description
* @createTime 2024/08/14 16:20
*/
@Data
@Builder
public class CommonUploadMultiParam implements Serializable {
private static final long serialVersionUID = -7935687108401829869L;
private List<NormalParam> normalParams;
private CommonUploadParam uploadParam;
@Data
@Builder
public static class NormalParam implements Serializable {
private static final long serialVersionUID = 4345164516827726194L;
/**
* 参数名称(非文件名)type
*/
private String name;
/**
* 参数名称对应值image
*/
private String value;
}
}

View File

@ -0,0 +1,557 @@
package me.chanjar.weixin.open.bean.icp;
import com.google.gson.annotations.SerializedName;
import lombok.*;
import java.io.Serializable;
import java.util.List;
/**
* @author xzh
* @Description
* @createTime 2024/08/14 15:09
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class WxOpenApplyIcpFilingParam implements Serializable {
private static final long serialVersionUID = -1175687030580685304L;
/**
* 备案主体信息
*/
@SerializedName("icp_subject")
private Subject icpSubject;
/**
* 微信小程序信息
*/
@SerializedName("icp_applets")
private Applets icpApplets;
/**
* 其他备案媒体材料
*/
@SerializedName("icp_materials")
private Materials icpMaterials;
//region Subject define
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class Subject implements Serializable {
private static final long serialVersionUID = -3760060095514905158L;
/**
* 主体基本信息
*/
@SerializedName("base_info")
private SubjectBaseInfo baseInfo;
/**
* 个人主体额外信息
*/
@SerializedName("personal_info")
private SubjectPersonalInfo personalInfo;
/**
* 主体额外信息个人备案时如果存在与主体负责人信息相同的字段则填入相同的值
*/
@SerializedName("organize_info")
private SubjectOrganizeInfo organizeInfo;
/**
* 主体负责人信息
*/
@SerializedName("principal_info")
private SubjectPrincipalInfo principalInfo;
/**
* 法人信息非个人备案且主体负责人不是法人时必填
*/
@SerializedName("legal_person_info")
private SubjectLegaPersonInfo legalPersonInfo;
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class SubjectBaseInfo implements Serializable {
private static final long serialVersionUID = 6040247507213564709L;
/**
* 主体性质示例值5
*/
@SerializedName("type")
private Integer type;
/**
* 主办单位名称
*/
@SerializedName("name")
private String name;
/**
* 备案省份
* 使用省份代码示例值"110000"(参考获取区域信息接口)
*/
@SerializedName("province")
private String province;
/**
* 备案城市
* 使用城市代码示例值"110100"(参考获取区域信息接口)
*/
@SerializedName("city")
private String city;
/**
* 备案县区
* 使用县区代码示例值"110105"(参考获取区域信息接口)
*/
@SerializedName("district")
private String district;
/**
* 通讯地址必须属于备案省市区地址开头的省市区不用填入
* 例如通信地址为北京市朝阳区高碑店路181号1栋12345室
* 只需要填写 "高碑店路181号1栋12345室" 即可
*/
@SerializedName("address")
private String address;
/**
* 主体信息备注根据需要如实填写
*/
@SerializedName("comment")
private String comment;
/**
* 主体备案号示例值粤B2-20090059申请小程序备案时不用填写查询已备案详情时会返回
*/
@SerializedName("record_number")
private String recordNumber;
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class SubjectPersonalInfo implements Serializable {
private static final long serialVersionUID = 2453569107311102079L;
/**
* 临时居住证明照片 media_id个人备案且非本省人员需要提供居住证暂住证社保证明房产证等临时居住证明
*/
@SerializedName("residence_permit")
private String residencePermit;
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class SubjectOrganizeInfo implements Serializable {
private static final long serialVersionUID = 562578565445293345L;
/**
* 主体证件类型
* 示例值2(参考获取证件类型接口)
*/
@SerializedName("certificate_type")
private Integer certificateType;
/**
* 主体证件号码
* 示例值"110105199001011234"
*/
@SerializedName("certificate_number")
private String certificateNumber;
/**
* 主体证件住所
* 示例值"北京市朝阳区高碑店路181号1栋12345室"
*/
@SerializedName("certificate_address")
private String certificateAddress;
/**
* 主体证件照片 media_id
* 如果小程序主体为非个人类型则必填
*/
@SerializedName("certificate_photo")
private String certificatePhoto;
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class SubjectPrincipalInfo implements Serializable {
private static final long serialVersionUID = -6840245946309353916L;
/**
* 负责人姓名
*/
@SerializedName("name")
private String name;
/**
* 负责人联系方式
*/
@SerializedName("mobile")
private String mobile;
/**
* 负责人电子邮件
*/
@SerializedName("email")
private String email;
/**
* 负责人应急联系方式
*/
@SerializedName("emergency_contact")
private String emergencyContact;
/**
* 负责人证件类型
*/
@SerializedName("certificate_type")
private Integer certificateType;
/**
* 负责人证件号码
*/
@SerializedName("certificate_number")
private String certificateNumber;
/**
* 负责人证件有效期起始日期
* 格式为 YYYYmmdd示例值"20230815"
*/
@SerializedName("certificate_validity_date_start")
private String certificateValidityDateStart;
/**
* 负责人证件有效期终止日期
* 格式为 YYYYmmdd如证件长期有效请填写 "长期"示例值"20330815"
*/
@SerializedName("certificate_validity_date_end")
private String certificateValidityDateEnd;
/**
* 负责人证件正面照片 media_id身份证为人像面
*/
@SerializedName("certificate_photo_front")
private String certificatePhotoFront;
/**
* 负责人证件背面照片 media_id
*/
@SerializedName("certificate_photo_back")
private String certificatePhotoBack;
/**
* 授权书 media_id当主体负责人不是法人时需要主体负责人授权书
* 当小程序负责人不是法人时需要小程序负责人授权书
*/
@SerializedName("authorization_letter")
private String authorizationLetter;
/**
* 扫脸认证任务id(扫脸认证接口返回的task_id)
* 仅小程序负责人需要扫脸主体负责人无需扫脸
*/
@SerializedName("verify_task_id")
private String verifyTaskId;
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class SubjectLegaPersonInfo implements Serializable {
private static final long serialVersionUID = -7386716346559073571L;
/**
* 法人代表姓名
*/
@SerializedName("name")
private String name;
/**
* 法人证件号码
*/
@SerializedName("certificate_number")
private String certificateNumber;
}
//endregion Subject define
//region Applets define
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class Applets implements Serializable {
private static final long serialVersionUID = -2938469180388648595L;
/**
* 微信小程序基本信息
*/
@SerializedName("base_info")
private AppletsBaseInfo basInfo;
/**
* 小程序负责人信息
*/
@SerializedName("principal_info")
private AppletsPrincipalInfo principalInfo;
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class AppletsBaseInfo implements Serializable {
private static final long serialVersionUID = 8404017028547715919L;
/**
* 小程序ID不用填写后台自动拉取
*/
@SerializedName("appid")
private String appId;
/**
* 小程序名称不用填写后台自动拉取
*/
@SerializedName("name")
private String name;
/**
* 小程序服务内容类型只能填写二级服务内容类型最多5个
*/
@SerializedName("service_content_types")
private List<Integer> serviceContentTypes;
/**
* 前置审批项列表中不能存在重复的前置审批类型id如不涉及前置审批项也需要填以上都不涉及
*/
@SerializedName("nrlx_details")
private List<AppletsNrlxDetailItem> nrlxDetails;
/**
* 请具体描述小程序实际经营内容主要服务内容该信息为主管部门审核重要依据备注内容字数限制20-200字请认真填写
*/
@SerializedName("comment")
private String comment;
/**
* 小程序备案号示例值粤B2-20090059-1626X
* 申请小程序备案时不用填写查询已备案详情时会返回
*/
@SerializedName("record_number")
private String recordNumber;
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class AppletsNrlxDetailItem implements Serializable {
private static final long serialVersionUID = -9144721738792167000L;
/**
* 前置审批类型示例值2
* (参考获取前置审批项接口)
*/
@SerializedName("type")
private Integer type;
/**
* 前置审批号如果前置审批类型不是以上都不涉及
* 则必填示例值"粤-12345号
*/
@SerializedName("code")
private String code;
/**
* 前置审批媒体材料 media_id
*/
@SerializedName("media")
private String media;
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class AppletsPrincipalInfo implements Serializable {
private static final long serialVersionUID = 5088256283066784463L;
/**
* 负责人姓名
*/
@SerializedName("name")
private String name;
/**
* 负责人联系方式
*/
@SerializedName("mobile")
private String mobile;
/**
* 负责人电子邮件
*/
@SerializedName("email")
private String email;
/**
* 负责人应急联系方式
*/
@SerializedName("emergency_contact")
private String emergencyContact;
/**
* 负责人证件类型示例值2(参考获取证件类型接口此处只能填入单位性质属于个人的证件类型)
*/
@SerializedName("certificate_type")
private Integer certificateType;
/**
* 负责人证件号码
*/
@SerializedName("certificate_number")
private String certificateNumber;
/**
* 负责人证件有效期起始日期
* 格式为 YYYYmmdd示例值"20230815"
*/
@SerializedName("certificate_validity_date_start")
private String certificateValidityDateStart;
/**
* 负责人证件有效期终止日期
* 格式为 YYYYmmdd
* 如证件长期有效请填写 "长期"示例值"20330815"
*/
@SerializedName("certificate_validity_date_end")
private String certificateValidityDateEnd;
/**
* 负责人证件正面照片 media_id
* 身份证为人像面
*/
@SerializedName("certificate_photo_front")
private String certificatePhotoFront;
/**
* 负责人证件背面照片 media_id
* 身份证为国徽面
*/
@SerializedName("certificate_photo_back")
private String certificatePhotoBack;
/**
* 授权书 media_id
* 当主体负责人不是法人时需要主体负责人授权书
* 当小程序负责人不是法人时需要小程序负责人授权书
*/
@SerializedName("authorization_letter")
private String authorizationLetter;
/**
* 扫脸认证任务id(扫脸认证接口返回的task_id)
* 仅小程序负责人需要扫脸主体负责人无需扫脸
*/
@SerializedName("verify_task_id")
private String verifyTaskId;
}
//endregion Applets define
//region Materials define
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class Materials {
/**
* 互联网信息服务承诺书 media_id最多上传1个
*/
@SerializedName("commitment_letter")
private List<String> commitmentLetter;
/**
* 主体更名函 media_id(非个人类型且发生过更名时需要上传)最多上传1个
*/
@SerializedName("business_name_change_letter")
private List<String> businessNameChangeLetter;
/**
* 党建确认函 media_id最多上传1个
*/
@SerializedName("party_building_confirmation_letter")
private List<String> partyBuildingConfirmationLetter;
/**
* 承诺视频 media_id最多上传1个
*/
@SerializedName("promise_video")
private List<String> promiseVideo;
/**
* 网站备案信息真实性责任告知书 media_id最多上传1个
*/
@SerializedName("authenticity_responsibility_letter")
private List<String> authenticityResponsibilityLetter;
/**
* 小程序备案信息真实性承诺书 media_id最多上传1个
*/
@SerializedName("authenticity_commitment_letter")
private List<String> authenticityCommitmentLetter;
/**
* 小程序建设方案书 media_id最多上传1个
*/
@SerializedName("website_construction_proposal")
private List<String> websiteConstructionProposal;
/**
* 主体其它附件 media_id最多上传10个
*/
@SerializedName("subject_other_materials")
private List<String> subjectOtherMaterials;
/**
* 小程序其它附件 media_id最多上传10个
*/
@SerializedName("applets_other_materials")
private List<String> appletsOtherMaterials;
/**
* 手持证件照 media_id最多上传1个
*/
@SerializedName("holding_certificate_photo")
private List<String> holdingCertificatePhoto;
}
//endregion Materials define
}

View File

@ -0,0 +1,40 @@
package me.chanjar.weixin.open.bean.icp;
import java.util.List;
import com.google.gson.annotations.SerializedName;
import lombok.*;
import me.chanjar.weixin.open.bean.result.WxOpenResult;
/**
* @author xzh
* @Description 申请小程序备案
* @createTime 2024/08/14 10:52
*/
@Getter
@Setter
@NoArgsConstructor
public class WxOpenApplyIcpFilingResult extends WxOpenResult {
private static final long serialVersionUID = 9215343492997218227L;
/**
* 错误提示
*/
@SerializedName("hints")
private List<Hint> hints;
@Data
@EqualsAndHashCode(callSuper = true)
public static class Hint extends WxOpenResult {
private static final long serialVersionUID = 6585787444231217123L;
/**
* 校验失败的字段
*/
@SerializedName("err_field")
private String errField;
}
}

View File

@ -0,0 +1,28 @@
package me.chanjar.weixin.open.bean.icp;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import me.chanjar.weixin.open.bean.result.WxOpenResult;
/**
* @author xzh
* @Description 人脸核验任务结果
* @createTime 2024/08/14 10:52
*/
@Getter
@Setter
@NoArgsConstructor
public class WxOpenIcpCreateIcpVerifyTaskResult extends WxOpenResult {
private static final long serialVersionUID = -8960874090439615220L;
/**
* 人脸核验任务id
*/
@JsonProperty("task_id")
private String taskId;
}

View File

@ -0,0 +1,92 @@
package me.chanjar.weixin.open.bean.icp;
import com.google.gson.annotations.SerializedName;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import me.chanjar.weixin.open.bean.result.WxOpenResult;
import java.io.Serializable;
/**
* @author xzh
* @Description 获取小程序备案状态及驳回原因
* @createTime 2024/08/14 10:52
*/
@Getter
@Setter
@NoArgsConstructor
public class WxOpenIcpEntranceInfoResult extends WxOpenResult {
private static final long serialVersionUID = 4661275080517814216L;
/**
* 备案状态信息
*/
private Info info;
@Getter
@Setter
@NoArgsConstructor
public static class Info implements Serializable {
private static final long serialVersionUID = 879913578935521216L;
/**
* 备案状态取值参考备案状态枚举示例值1024
*/
@SerializedName("status")
private Integer status;
/**
* 是否正在注销备案
*/
@SerializedName("is_canceling")
private Boolean canceling;
/**
* 驳回原因备案不通过时返回
*/
@SerializedName("audit_data")
private AuditData auditData;
/**
* 备案入口是否对该小程序开放0不开放1开放特定情况下入口不会开放如小程序昵称包含某些关键词时管局系统不可用时当备案入口开放时才能提交备案申请
*/
@SerializedName("available")
private Integer available;
/**
* 管局短信核验状态仅当备案状态为 4管局审核中的时候才有效1等待核验中2核验完成3核验超时
*/
@SerializedName("sms_verify_status")
private Integer smsVerifyStatus;
}
@Getter
@Setter
@NoArgsConstructor
public static class AuditData implements Serializable {
private static final long serialVersionUID = 2217833539540191890L;
/**
* 审核不通过的字段中文名
*/
@SerializedName("key_name")
private String keyName;
/**
* 字段不通过的原因
*/
@SerializedName("error")
private String error;
/**
* 修改建议
*/
@SerializedName("suggest")
private String suggest;
}
}

View File

@ -0,0 +1,33 @@
package me.chanjar.weixin.open.bean.icp;
import com.google.gson.annotations.SerializedName;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import me.chanjar.weixin.open.bean.result.WxOpenResult;
/**
* @author xzh
* @Description 人脸核身任务的状态和结果
* @createTime 2024/08/14 10:52
*/
@Getter
@Setter
@NoArgsConstructor
public class WxOpenIcpVerifyTaskResult extends WxOpenResult {
private static final long serialVersionUID = 3134332406149779364L;
/**
* 人脸核身任务是否已完成
*/
@SerializedName("is_finish")
private Boolean finish;
/**
* 任务状态枚举0. 未开始1. 等待中2. 失败3. 成功返回的 is_finish 字段为 true face_status 才是最终状态
*/
@SerializedName("face_status")
private Integer faceStatus;
}

View File

@ -0,0 +1,36 @@
package me.chanjar.weixin.open.bean.icp;
import java.io.Serializable;
import java.util.List;
import com.google.gson.annotations.SerializedName;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* @author xzh
* @Description 已备案详情
* @createTime 2024/08/14 10:52
*/
@Getter
@Setter
@NoArgsConstructor
public class WxOpenOnlineIcpOrderResult implements Serializable {
private static final long serialVersionUID = -8670174116784375577L;
/**
* 备案主体信息
*/
@SerializedName("icp_subject")
private WxOpenApplyIcpFilingParam.Subject icpSubject;
/**
* 微信小程序信息
*/
@SerializedName("icp_applets")
private WxOpenApplyIcpFilingParam.Applets icpApplets;
}

View File

@ -0,0 +1,50 @@
package me.chanjar.weixin.open.bean.icp;
import java.io.Serializable;
import java.util.List;
import com.google.gson.annotations.SerializedName;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import me.chanjar.weixin.open.bean.result.WxOpenResult;
/**
* @author xzh
* @Description 证件类型定义
* @createTime 2024/08/14 10:52
*/
@Getter
@Setter
@NoArgsConstructor
public class WxOpenQueryIcpCertificateTypeResult extends WxOpenResult {
private static final long serialVersionUID = -6492653564753189104L;
/**
* 证件类型列表
*/
@SerializedName("items")
private List<Item> items;
@Getter
@Setter
@NoArgsConstructor
public static class Item implements Serializable {
private static final long serialVersionUID = -5353506906838811002L;
@SerializedName("type")
private Integer type;
@SerializedName("subject_type")
private Integer subjectType;
@SerializedName("name")
private String name;
@SerializedName("remark")
private String remark;
}
}

View File

@ -0,0 +1,58 @@
package me.chanjar.weixin.open.bean.icp;
import java.io.Serializable;
import java.util.List;
import com.google.gson.annotations.SerializedName;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import me.chanjar.weixin.open.bean.result.WxOpenResult;
/**
* @author xzh
* @Description 省市区的区域信息
* @createTime 2024/08/14 10:52
*/
@Getter
@Setter
@NoArgsConstructor
public class WxOpenQueryIcpDistrictCodeResult extends WxOpenResult {
private static final long serialVersionUID = -5087976503275542589L;
@SerializedName("items")
private List<Item> items;
@Getter
@Setter
@NoArgsConstructor
public static class Item implements Serializable {
private static final long serialVersionUID = -8598323103982035055L;
/**
* 区域类型: 1 -- 省份2 -- 3 --
*/
@SerializedName("type")
private Integer type;
/**
* 区域代码
*/
@SerializedName("code")
private Integer code;
/**
* 下级区域信息列表
*/
@SerializedName("name")
private String name;
/**
* 下级区域信息列表
*/
@SerializedName("sub_list")
private List<Item> subList;
}
}

View File

@ -0,0 +1,50 @@
package me.chanjar.weixin.open.bean.icp;
import java.io.Serializable;
import java.util.List;
import com.google.gson.annotations.SerializedName;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import me.chanjar.weixin.open.bean.result.WxOpenResult;
/**
* @author xzh
* @Description 小程序备案前置审批项类型定义
* @createTime 2024/08/14 10:52
*/
@Getter
@Setter
@NoArgsConstructor
public class WxOpenQueryIcpNrlxTypesResult extends WxOpenResult {
private static final long serialVersionUID = 4986067025882451072L;
/**
* 前置审批项类型列表
*/
@SerializedName("items")
private List<Item> items;
@Getter
@Setter
@NoArgsConstructor
public static class Item implements Serializable {
private static final long serialVersionUID = 9069126796573950000L;
/**
* 前置审批项类型id
*/
@SerializedName("type")
private Integer type;
/**
* 名称
*/
@SerializedName("name")
private String name;
}
}

View File

@ -0,0 +1,60 @@
package me.chanjar.weixin.open.bean.icp;
import java.io.Serializable;
import java.util.List;
import com.google.gson.annotations.SerializedName;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import me.chanjar.weixin.open.bean.result.WxOpenResult;
/**
* @author xzh
* @Description 小程序服务内容类型
* @createTime 2024/08/14 10:52
*/
@Getter
@Setter
@NoArgsConstructor
public class WxOpenQueryIcpServiceContentTypesResult extends WxOpenResult {
private static final long serialVersionUID = 2982244171428787389L;
/**
* 服务内容类型列表
*/
private List<Item> items;
@Getter
@Setter
@NoArgsConstructor
public static class Item implements Serializable {
private static final long serialVersionUID = 1432103347845426732L;
/**
* 服务内容类型id
*/
@SerializedName("type")
private Integer type;
/**
* 该服务内容类型的父类型id
*/
@SerializedName("parent_type")
private Integer parentType;
/**
* 名称
*/
@SerializedName("name")
private String name;
/**
* 备注
*/
@SerializedName("remark")
private String remark;
}
}

View File

@ -0,0 +1,53 @@
package me.chanjar.weixin.open.bean.icp;
import java.io.Serializable;
import java.util.List;
import com.google.gson.annotations.SerializedName;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* @author xzh
* @Description 单位性质定义
* @createTime 2024/08/14 10:52
*/
@Getter
@Setter
@NoArgsConstructor
public class WxOpenQueryIcpSubjectTypeResult implements Serializable {
private static final long serialVersionUID = -2090825609788654435L;
/**
* 单位性质列表
*/
private List<Item> items;
@Getter
@Setter
@NoArgsConstructor
public static class Item implements Serializable {
private static final long serialVersionUID = -1284830856404207970L;
/**
* 单位性质类型id
*/
@SerializedName("type")
private Integer type;
/**
* 名称
*/
@SerializedName("name")
private String name;
/**
* 备注
*/
@SerializedName("remark")
private String remark;
}
}

View File

@ -0,0 +1,64 @@
package me.chanjar.weixin.open.bean.icp;
import java.io.Serializable;
import java.util.*;
import com.google.gson.annotations.SerializedName;
import lombok.*;
import me.chanjar.weixin.common.bean.CommonUploadData;
import me.chanjar.weixin.open.bean.CommonUploadMultiParam;
import me.chanjar.weixin.common.bean.CommonUploadParam;
/**
* @author xzh
* @Description 文件上传
* @createTime 2024/08/14 10:52
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class WxOpenUploadIcpMediaParam implements Serializable {
private static final long serialVersionUID = -9082174509776922969L;
/**
* 媒体材料类型目前支持两种图片("image")和视频("video")示例值"image"
*/
@SerializedName("type")
private String type;
/**
* 证件类型(参考获取证件类型)如果上传的是证件媒体材料则必填示例值2
*/
@SerializedName("certificate_type")
private String certificateType;
/**
* 媒体材料所属的备案字段名(参考申请小程序备案接口)如要用于多个备案字段则填写其中一个字段名即可
* 例如要上传身份证头像面照片作为备案主体负责人和小程序负责人的证件照正面 就填写
* "icp_subject.principal_info.certificate_photo_front"
*/
@SerializedName("icp_order_field")
private String icpOrderField;
/**
* 待上传的图片或视频
*/
private CommonUploadData media;
public CommonUploadMultiParam toCommonUploadMultiParam() {
return CommonUploadMultiParam.builder()
.normalParams(Arrays.asList(
CommonUploadMultiParam.NormalParam.builder().name("type").value(type).build(),
CommonUploadMultiParam.NormalParam.builder().name("certificate_type").value(certificateType).build(),
CommonUploadMultiParam.NormalParam.builder().name("icp_order_field").value(icpOrderField).build()
))
.uploadParam(new CommonUploadParam("media", media))
.build();
}
}

View File

@ -0,0 +1,40 @@
package me.chanjar.weixin.open.bean.icp;
import com.google.gson.annotations.SerializedName;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder;
import me.chanjar.weixin.open.bean.result.WxOpenResult;
/**
* @author xzh
* @Description 上传小程序备案媒体材料结果
* @createTime 2024/08/14 10:52
*/
@Getter
@Setter
@NoArgsConstructor
public class WxOpenUploadIcpMediaResult extends WxOpenResult {
private static final long serialVersionUID = 6929154695595201734L;
/**
* 媒体材料类型目前支持两种图片("image")和视频("video")示例值"image"
*/
@SerializedName("type")
private String type;
/**
* 媒体id示例值"4ahCGpd3CYkE6RpkNkUR5czt3LvG8xDnDdKAz6bBKttSfM8p4k5Rj6823HXugPwQBurgMezyib7"
*/
@SerializedName("media_id")
private String mediaId;
/**
* 创建时间UNIX时间戳示例值1692883651
*/
@SerializedName("created_at")
private String createdAt;
}

View File

@ -123,6 +123,37 @@ public class WxOpenXmlMessage implements Serializable {
@XStreamAlias("expired")
private Long expired;
//region 以下为小程序管理员人脸核身完成事件 推送的消息 infoType=notify_icpfiling_verify_result
/**
* 人脸核验任务id
*/
@XStreamAlias("task_id")
private String IcpVerifyTaskId;
/**
* 小程序唯一id
*/
@XStreamAlias("verify_appid")
private String verifyAppId;
/**
* 人脸核验结果 2-核验失败3-核验成功
*/
@XStreamAlias("result")
private Integer result;
//endregion
//region 当备案审核被驳回或通过时会推送该事件 推送的消息 infoType=notify_apply_icpfiling_result
/**
* 小程序唯一id
*/
@XStreamAlias("authorizer_appid")
private String authorizerAppId;
/**
* 备案状态参考获取小程序备案状态及驳回原因接口的备案状态枚举¬
*/
@XStreamAlias("beian_status")
private Integer beianStatus;
//endregion
/**
* 快速创建的小程序appId已弃用未来将删除

View File

@ -0,0 +1,49 @@
package me.chanjar.weixin.open.executor;
import me.chanjar.weixin.open.bean.CommonUploadMultiParam;
import me.chanjar.weixin.common.enums.WxType;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.http.RequestExecutor;
import me.chanjar.weixin.common.util.http.RequestHttp;
import me.chanjar.weixin.common.util.http.ResponseHandler;
import java.io.IOException;
/**
* @author xzh
* @Description
* @createTime 2024/08/14 16:28
*/
public abstract class CommonUploadMultiRequestExecutor<H, P> implements RequestExecutor<String, CommonUploadMultiParam> {
protected RequestHttp<H, P> requestHttp;
public CommonUploadMultiRequestExecutor(RequestHttp<H, P> requestHttp) {
this.requestHttp = requestHttp;
}
@Override
public void execute(String uri, CommonUploadMultiParam 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, CommonUploadMultiParam> create(RequestHttp requestHttp) {
switch (requestHttp.getRequestType()) {
case APACHE_HTTP:
return new CommonUploadMultiRequestExecutorApacheImpl(requestHttp);
case JODD_HTTP:
return new CommonUploadMultiRequestExecutorJoddHttpImpl(requestHttp);
case OK_HTTP:
return new CommonUploadMultiRequestExecutorOkHttpImpl(requestHttp);
default:
throw new IllegalArgumentException("不支持的http执行器类型" + requestHttp.getRequestType());
}
}
}

View File

@ -0,0 +1,95 @@
package me.chanjar.weixin.open.executor;
import lombok.Getter;
import me.chanjar.weixin.common.bean.CommonUploadData;
import me.chanjar.weixin.open.bean.CommonUploadMultiParam;
import me.chanjar.weixin.common.bean.CommonUploadParam;
import me.chanjar.weixin.common.enums.WxType;
import me.chanjar.weixin.common.error.WxError;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.http.RequestHttp;
import me.chanjar.weixin.common.util.http.apache.Utf8ResponseHandler;
import org.apache.http.Consts;
import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.InputStreamBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.util.CollectionUtils;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
/**
* @author xzh
* @Description
* @createTime 2024/08/14 16:28
*/
public class CommonUploadMultiRequestExecutorApacheImpl extends CommonUploadMultiRequestExecutor<CloseableHttpClient, HttpHost> {
public CommonUploadMultiRequestExecutorApacheImpl(RequestHttp<CloseableHttpClient, HttpHost> requestHttp) {
super(requestHttp);
}
@Override
public String execute(String uri, CommonUploadMultiParam param, WxType wxType) throws WxErrorException, IOException {
HttpPost httpPost = new HttpPost(uri);
if (requestHttp.getRequestHttpProxy() != null) {
RequestConfig config = RequestConfig.custom().setProxy(requestHttp.getRequestHttpProxy()).build();
httpPost.setConfig(config);
}
if (param != null) {
MultipartEntityBuilder entity = MultipartEntityBuilder.create();
List<CommonUploadMultiParam.NormalParam> normalParams = param.getNormalParams();
if (!CollectionUtils.isEmpty(normalParams)) {
for (CommonUploadMultiParam.NormalParam normalParam : normalParams) {
entity.addPart(normalParam.getName(), new StringBody(normalParam.getValue(), ContentType.create("multipart/form-data", Consts.UTF_8)));
}
}
CommonUploadParam uploadParam = param.getUploadParam();
if (uploadParam != null) {
CommonUploadData data = uploadParam.getData();
InnerStreamBody part = new InnerStreamBody(data.getInputStream(), ContentType.DEFAULT_BINARY, data.getFileName(), data.getLength());
entity.addPart(uploadParam.getName(), part)
.setMode(HttpMultipartMode.RFC6532);
}
httpPost.setEntity(entity.build());
}
try (CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpPost)) {
String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response);
if (responseContent == null || responseContent.isEmpty()) {
throw new WxErrorException(String.format("上传失败,服务器响应空 url:%s param:%s", uri, param));
}
WxError error = WxError.fromJson(responseContent, wxType);
if (error.getErrorCode() != 0) {
throw new WxErrorException(error);
}
return responseContent;
} finally {
httpPost.releaseConnection();
}
}
/**
* 内部流 请求体
*/
@Getter
public static class InnerStreamBody extends InputStreamBody {
private final long contentLength;
public InnerStreamBody(final InputStream in, final ContentType contentType, final String filename, long contentLength) {
super(in, contentType, filename);
this.contentLength = contentLength;
}
}
}

View File

@ -0,0 +1,109 @@
package me.chanjar.weixin.open.executor;
import jodd.http.HttpConnectionProvider;
import jodd.http.HttpRequest;
import jodd.http.HttpResponse;
import jodd.http.ProxyInfo;
import jodd.http.upload.Uploadable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.SneakyThrows;
import me.chanjar.weixin.common.bean.CommonUploadData;
import me.chanjar.weixin.open.bean.CommonUploadMultiParam;
import me.chanjar.weixin.common.bean.CommonUploadParam;
import me.chanjar.weixin.common.enums.WxType;
import me.chanjar.weixin.common.error.WxError;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.http.RequestHttp;
import org.apache.http.Consts;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.content.StringBody;
import org.springframework.util.CollectionUtils;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
/**
* @author xzh
* @Description
* @createTime 2024/08/14 16:43
*/
public class CommonUploadMultiRequestExecutorJoddHttpImpl extends CommonUploadMultiRequestExecutor<HttpConnectionProvider, ProxyInfo> {
public CommonUploadMultiRequestExecutorJoddHttpImpl(RequestHttp<HttpConnectionProvider, ProxyInfo> requestHttp) {
super(requestHttp);
}
@Override
public String execute(String uri, CommonUploadMultiParam param, WxType wxType) throws WxErrorException, IOException {
HttpRequest request = HttpRequest.post(uri);
if (requestHttp.getRequestHttpProxy() != null) {
requestHttp.getRequestHttpClient().useProxy(requestHttp.getRequestHttpProxy());
}
request.withConnectionProvider(requestHttp.getRequestHttpClient());
List<CommonUploadMultiParam.NormalParam> normalParams = param.getNormalParams();
if (!CollectionUtils.isEmpty(normalParams)) {
for (CommonUploadMultiParam.NormalParam normalParam : normalParams) {
request.form(normalParam.getName(), new StringBody(normalParam.getValue(), ContentType.create("multipart/form-data", Consts.UTF_8)));
}
}
CommonUploadParam uploadParam = param.getUploadParam();
if (uploadParam != null) {
request.form(uploadParam.getName(), new CommonUploadParamToUploadableAdapter(uploadParam.getData()));
}
HttpResponse response = request.send();
response.charset(StandardCharsets.UTF_8.name());
String responseContent = response.bodyText();
if (responseContent.isEmpty()) {
throw new WxErrorException(String.format("上传失败,服务器响应空 url:%s param:%s", uri, param));
}
WxError error = WxError.fromJson(responseContent, wxType);
if (error.getErrorCode() != 0) {
throw new WxErrorException(error);
}
return responseContent;
}
/**
* 通用上传参数 Uploadable 的适配器
*/
@Getter
@AllArgsConstructor
public static class CommonUploadParamToUploadableAdapter implements Uploadable<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();
}
}
}

View File

@ -0,0 +1,104 @@
package me.chanjar.weixin.open.executor;
import lombok.AllArgsConstructor;
import me.chanjar.weixin.common.bean.CommonUploadData;
import me.chanjar.weixin.open.bean.CommonUploadMultiParam;
import me.chanjar.weixin.common.bean.CommonUploadParam;
import me.chanjar.weixin.common.enums.WxType;
import me.chanjar.weixin.common.error.WxError;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.http.RequestHttp;
import me.chanjar.weixin.common.util.http.okhttp.OkHttpProxyInfo;
import okhttp3.*;
import okio.BufferedSink;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.util.CollectionUtils;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
/**
* @author xzh
* @Description
* @createTime 2024/08/14 16:48
*/
public class CommonUploadMultiRequestExecutorOkHttpImpl extends CommonUploadMultiRequestExecutor<OkHttpClient, OkHttpProxyInfo> {
public CommonUploadMultiRequestExecutorOkHttpImpl(RequestHttp<OkHttpClient, OkHttpProxyInfo> requestHttp) {
super(requestHttp);
}
@Override
public String execute(String uri, CommonUploadMultiParam param, WxType wxType) throws WxErrorException, IOException {
MultipartBody.Builder builder = new MultipartBody.Builder()
.setType(MediaType.get("multipart/form-data"));
List<CommonUploadMultiParam.NormalParam> normalParams = param.getNormalParams();
if (!CollectionUtils.isEmpty(normalParams)) {
for (CommonUploadMultiParam.NormalParam normalParam : normalParams) {
builder.addFormDataPart(normalParam.getName(), normalParam.getValue());
}
}
CommonUploadParam uploadParam = param.getUploadParam();
if (uploadParam != null) {
RequestBody requestBody = new CommonUpdateDataToRequestBodyAdapter(uploadParam.getData());
builder.addFormDataPart(uploadParam.getName(), uploadParam.getData().getFileName(), requestBody);
}
Request request = new Request.Builder().url(uri).post(builder.build()).build();
try (Response response = requestHttp.getRequestHttpClient().newCall(request).execute()) {
ResponseBody responseBody = response.body();
String responseContent = responseBody == null ? "" : responseBody.string();
if (responseContent.isEmpty()) {
throw new WxErrorException(String.format("上传失败,服务器响应空 url:%s param:%s", uri, param));
}
WxError error = WxError.fromJson(responseContent, wxType);
if (error.getErrorCode() != 0) {
throw new WxErrorException(error);
}
return responseContent;
}
}
/**
* 通用上传输入 OkHttp 请求提 适配器
*/
@AllArgsConstructor
public static class CommonUpdateDataToRequestBodyAdapter extends RequestBody {
private static final MediaType CONTENT_TYPE = MediaType.get("application/octet-stream");
private CommonUploadData data;
@Override
public long contentLength() {
return data.getLength();
}
@Nullable
@Override
public MediaType contentType() {
return CONTENT_TYPE;
}
@Override
public void writeTo(@NotNull BufferedSink bufferedSink) throws IOException {
InputStream inputStream = data.getInputStream();
int count;
byte[] buffer = new byte[4096];
while ((count = inputStream.read(buffer)) != -1) {
bufferedSink.write(buffer, 0, count);
}
inputStream.close();
}
@Override
public boolean isOneShot() {
return true;
}
}
}