封装
This commit is contained in:
@@ -3,12 +3,16 @@ package com.czg;
|
|||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.czg.dto.req.*;
|
import com.czg.dto.req.*;
|
||||||
import com.czg.dto.resp.BankBranchDto;
|
import com.czg.dto.resp.BankBranchDto;
|
||||||
|
import com.czg.dto.resp.EntryRespDto;
|
||||||
import com.czg.exception.CzgException;
|
import com.czg.exception.CzgException;
|
||||||
import com.czg.third.alipay.AlipayEntryManager;
|
import com.czg.third.alipay.AlipayEntryManager;
|
||||||
import com.czg.third.wechat.WechatEntryManager;
|
import com.czg.third.wechat.WechatEntryManager;
|
||||||
import com.czg.utils.AssertUtil;
|
import com.czg.utils.AssertUtil;
|
||||||
|
import com.czg.utils.AsyncTaskExecutor;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 进件管理
|
* 进件管理
|
||||||
@@ -34,101 +38,136 @@ public class EntryManager {
|
|||||||
* 请先执行:
|
* 请先执行:
|
||||||
* 1. {@link com.czg.EntryManager#verifyEntryParam(AggregateMerchantDto)} 验证进件参数
|
* 1. {@link com.czg.EntryManager#verifyEntryParam(AggregateMerchantDto)} 验证进件参数
|
||||||
* 2. {@link com.czg.EntryManager#uploadParamImage(AggregateMerchantDto)} 上传图片至第三方
|
* 2. {@link com.czg.EntryManager#uploadParamImage(AggregateMerchantDto)} 上传图片至第三方
|
||||||
|
*
|
||||||
* @param reqDto 进件参数
|
* @param reqDto 进件参数
|
||||||
*/
|
*/
|
||||||
public static void entryMerchant(AggregateMerchantDto reqDto) {
|
public static EntryRespDto entryMerchant(AggregateMerchantDto reqDto) {
|
||||||
|
List<Supplier<EntryRespDto>> tasks = new ArrayList<>();
|
||||||
|
tasks.add(() -> WechatEntryManager.entryMerchant(null, reqDto));
|
||||||
|
tasks.add(() -> AlipayEntryManager.entryMerchant(null, reqDto));
|
||||||
|
|
||||||
|
// 执行所有任务
|
||||||
|
List<AsyncTaskExecutor.TaskResult<EntryRespDto>> results = AsyncTaskExecutor.executeAll(tasks);
|
||||||
|
for (AsyncTaskExecutor.TaskResult<EntryRespDto> result : results) {
|
||||||
|
// 合并两个进件结果
|
||||||
|
}
|
||||||
|
|
||||||
|
return new EntryRespDto();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 上传图片至第三方
|
* 上传图片至第三方
|
||||||
* 请先执行 {@link com.czg.EntryManager#verifyEntryParam(AggregateMerchantDto)} 验证进件参数
|
* 请先执行 {@link com.czg.EntryManager#verifyEntryParam(AggregateMerchantDto)} 验证进件参数
|
||||||
|
*
|
||||||
* @param reqDto 进件参数
|
* @param reqDto 进件参数
|
||||||
*/
|
*/
|
||||||
public static void uploadParamImage(AggregateMerchantDto reqDto) {
|
public static void uploadParamImage(AggregateMerchantDto reqDto) {
|
||||||
|
List<Supplier<Void>> tasks = new ArrayList<>();
|
||||||
|
|
||||||
MerchantBaseInfoDto baseInfo = reqDto.getMerchantBaseInfo();
|
MerchantBaseInfoDto baseInfo = reqDto.getMerchantBaseInfo();
|
||||||
// 联系人身份证反面
|
// 联系人身份证反面
|
||||||
if (baseInfo.getContactIdCardBackPic() != null) {
|
tasks.add(() -> {
|
||||||
uploadImageToThird(baseInfo.getContactIdCardBackPic());
|
uploadImageToThird(baseInfo.getContactIdCardBackPic());
|
||||||
}
|
return null;
|
||||||
|
});
|
||||||
// 联系人身份证正面
|
// 联系人身份证正面
|
||||||
if (baseInfo.getContactIdCardFrontPic() != null) {
|
tasks.add(() -> {
|
||||||
uploadImageToThird(baseInfo.getContactIdCardFrontPic());
|
uploadImageToThird(baseInfo.getContactIdCardFrontPic());
|
||||||
}
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
LegalPersonInfoDto legalPersonInfo = reqDto.getLegalPersonInfo();
|
LegalPersonInfoDto legalPersonInfo = reqDto.getLegalPersonInfo();
|
||||||
// 法人身份证反面
|
// 法人身份证反面
|
||||||
if (legalPersonInfo.getIdCardBackPic() != null) {
|
tasks.add(() -> {
|
||||||
uploadImageToThird(legalPersonInfo.getIdCardBackPic());
|
uploadImageToThird(legalPersonInfo.getIdCardBackPic());
|
||||||
}
|
return null;
|
||||||
|
});
|
||||||
// 法人身份证正面
|
// 法人身份证正面
|
||||||
if (legalPersonInfo.getIdCardFrontPic() != null) {
|
tasks.add(() -> {
|
||||||
uploadImageToThird(legalPersonInfo.getIdCardFrontPic());
|
uploadImageToThird(legalPersonInfo.getIdCardFrontPic());
|
||||||
}
|
return null;
|
||||||
|
});
|
||||||
// 法人手持身份证
|
// 法人手持身份证
|
||||||
if (legalPersonInfo.getIdCardHandPic() != null) {
|
tasks.add(() -> {
|
||||||
uploadImageToThird(legalPersonInfo.getIdCardHandPic());
|
uploadImageToThird(legalPersonInfo.getIdCardHandPic());
|
||||||
}
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
BusinessLicenceInfoDto businessLicenceInfo = reqDto.getBusinessLicenceInfo();
|
BusinessLicenceInfoDto businessLicenceInfo = reqDto.getBusinessLicenceInfo();
|
||||||
// 营业执照
|
// 营业执照
|
||||||
if (businessLicenceInfo.getLicensePic() != null) {
|
tasks.add(() -> {
|
||||||
uploadImageToThird(businessLicenceInfo.getLicensePic());
|
uploadImageToThird(businessLicenceInfo.getLicensePic());
|
||||||
}
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
SettlementInfoDto settlementInfo = reqDto.getSettlementInfo();
|
SettlementInfoDto settlementInfo = reqDto.getSettlementInfo();
|
||||||
// 银行卡背面
|
// 银行卡背面
|
||||||
if (settlementInfo.getBankCardBackPic() != null) {
|
tasks.add(() -> {
|
||||||
uploadImageToThird(settlementInfo.getBankCardBackPic());
|
uploadImageToThird(settlementInfo.getBankCardBackPic());
|
||||||
}
|
return null;
|
||||||
|
});
|
||||||
// 银行卡正面
|
// 银行卡正面
|
||||||
if (settlementInfo.getBankCardFrontPic() != null) {
|
tasks.add(() -> {
|
||||||
uploadImageToThird(settlementInfo.getBankCardFrontPic());
|
uploadImageToThird(settlementInfo.getBankCardFrontPic());
|
||||||
}
|
return null;
|
||||||
|
});
|
||||||
// 开户许可证
|
// 开户许可证
|
||||||
if (settlementInfo.getOpenAccountLicencePic() != null) {
|
tasks.add(() -> {
|
||||||
uploadImageToThird(settlementInfo.getOpenAccountLicencePic());
|
uploadImageToThird(settlementInfo.getOpenAccountLicencePic());
|
||||||
}
|
return null;
|
||||||
|
});
|
||||||
// 非法人手持授权函
|
// 非法人手持授权函
|
||||||
if (settlementInfo.getNoLegalHandSettleAuthPic() != null) {
|
tasks.add(() -> {
|
||||||
uploadImageToThird(settlementInfo.getNoLegalHandSettleAuthPic());
|
uploadImageToThird(settlementInfo.getNoLegalHandSettleAuthPic());
|
||||||
}
|
return null;
|
||||||
|
});
|
||||||
// 非法人授权函
|
// 非法人授权函
|
||||||
if (settlementInfo.getNoLegalSettleAuthPic() != null) {
|
tasks.add(() -> {
|
||||||
uploadImageToThird(settlementInfo.getNoLegalSettleAuthPic());
|
uploadImageToThird(settlementInfo.getNoLegalSettleAuthPic());
|
||||||
}
|
return null;
|
||||||
|
});
|
||||||
// 非法人身份证反面
|
// 非法人身份证反面
|
||||||
if (settlementInfo.getNoLegalIdCardBackPic() != null) {
|
tasks.add(() -> {
|
||||||
uploadImageToThird(settlementInfo.getNoLegalIdCardBackPic());
|
uploadImageToThird(settlementInfo.getNoLegalIdCardBackPic());
|
||||||
}
|
return null;
|
||||||
|
});
|
||||||
// 非法人身份证正面
|
// 非法人身份证正面
|
||||||
if (settlementInfo.getNoLegalIdCardFrontPic() != null) {
|
tasks.add(() -> {
|
||||||
uploadImageToThird(settlementInfo.getNoLegalIdCardFrontPic());
|
uploadImageToThird(settlementInfo.getNoLegalIdCardFrontPic());
|
||||||
}
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
StoreInfoDto storeInfo = reqDto.getStoreInfo();
|
StoreInfoDto storeInfo = reqDto.getStoreInfo();
|
||||||
// 店内图片
|
// 店内图片
|
||||||
if (storeInfo.getInsidePic() != null) {
|
tasks.add(() -> {
|
||||||
uploadImageToThird(storeInfo.getInsidePic());
|
uploadImageToThird(storeInfo.getInsidePic());
|
||||||
}
|
return null;
|
||||||
|
});
|
||||||
// 门店门头图片
|
// 门店门头图片
|
||||||
if (storeInfo.getDoorPic() != null) {
|
tasks.add(() -> {
|
||||||
uploadImageToThird(storeInfo.getDoorPic());
|
uploadImageToThird(storeInfo.getDoorPic());
|
||||||
}
|
return null;
|
||||||
|
});
|
||||||
// 收银台图片
|
// 收银台图片
|
||||||
if (storeInfo.getCashierDeskPic() != null) {
|
tasks.add(() -> {
|
||||||
uploadImageToThird(storeInfo.getCashierDeskPic());
|
uploadImageToThird(storeInfo.getCashierDeskPic());
|
||||||
}
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 执行所有任务
|
||||||
|
AsyncTaskExecutor.executeAll(tasks);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void uploadImageToThird(ImageDto dto) {
|
private static void uploadImageToThird(ImageDto dto) {
|
||||||
if (StrUtil.isBlank(dto.getWechatId())) {
|
if (dto != null && StrUtil.isNotBlank(dto.getUrl())) {
|
||||||
String image = WechatEntryManager.uploadImage(dto.getUrl());
|
if (StrUtil.isBlank(dto.getWechatId())) {
|
||||||
dto.setWechatId(image);
|
String image = WechatEntryManager.uploadImage(null, dto.getUrl());
|
||||||
}
|
dto.setWechatId(image);
|
||||||
if (StrUtil.isBlank(dto.getAlipayId())) {
|
}
|
||||||
String image = AlipayEntryManager.uploadImage(dto.getUrl());
|
if (StrUtil.isBlank(dto.getAlipayId())) {
|
||||||
dto.setAlipayId(image);
|
String image = AlipayEntryManager.uploadImage(null, dto.getUrl());
|
||||||
|
dto.setAlipayId(image);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,6 +306,7 @@ public class EntryManager {
|
|||||||
AggregateMerchantDto merchantDto = new AggregateMerchantDto();
|
AggregateMerchantDto merchantDto = new AggregateMerchantDto();
|
||||||
merchantDto.setMerchantCode("20220106000000000001");
|
merchantDto.setMerchantCode("20220106000000000001");
|
||||||
|
|
||||||
verifyEntryParam(merchantDto);
|
// verifyEntryParam(merchantDto);
|
||||||
|
uploadParamImage(merchantDto);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package com.czg.dto.resp;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 进件相应结果
|
||||||
|
* @author yjjie
|
||||||
|
* @date 2026/1/6 18:15
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class EntryRespDto {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信申请 Id
|
||||||
|
*/
|
||||||
|
private String wechatApplyId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信状态
|
||||||
|
*/
|
||||||
|
private String wechatStatus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付宝订单 Id
|
||||||
|
*/
|
||||||
|
private String alipayOrderId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付宝状态
|
||||||
|
*/
|
||||||
|
private String alipayStatus;
|
||||||
|
}
|
||||||
@@ -3,6 +3,8 @@ package com.czg.third.alipay;
|
|||||||
import com.alipay.api.AlipayApiException;
|
import com.alipay.api.AlipayApiException;
|
||||||
import com.alipay.api.AlipayConfig;
|
import com.alipay.api.AlipayConfig;
|
||||||
import com.alipay.api.DefaultAlipayClient;
|
import com.alipay.api.DefaultAlipayClient;
|
||||||
|
import com.alipay.v3.ApiClient;
|
||||||
|
import com.alipay.v3.Configuration;
|
||||||
import com.czg.third.alipay.dto.config.AlipayConfigDto;
|
import com.czg.third.alipay.dto.config.AlipayConfigDto;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
@@ -22,6 +24,23 @@ public class AlipayClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ApiClient getApiClient(AlipayConfigDto configDto) {
|
||||||
|
try {
|
||||||
|
ApiClient defaultClient = Configuration.getDefaultApiClient();
|
||||||
|
// 初始化alipay参数(全局设置一次)
|
||||||
|
com.alipay.v3.util.model.AlipayConfig alipayConfig = new com.alipay.v3.util.model.AlipayConfig();
|
||||||
|
alipayConfig.setServerUrl(configDto.getDomain());
|
||||||
|
alipayConfig.setAppId(configDto.getAppId());
|
||||||
|
alipayConfig.setAlipayPublicKey(configDto.getAlipayPublicKey());
|
||||||
|
alipayConfig.setPrivateKey(configDto.getPrivateKey());
|
||||||
|
defaultClient.setAlipayConfig(alipayConfig);
|
||||||
|
return defaultClient;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("创建支付宝客户端失败", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static AlipayConfig getAlipayConfig(AlipayConfigDto configDto) {
|
public static AlipayConfig getAlipayConfig(AlipayConfigDto configDto) {
|
||||||
if (configDto == null) {
|
if (configDto == null) {
|
||||||
configDto = AlipayConfigDto.getDefaultConfig();
|
configDto = AlipayConfigDto.getDefaultConfig();
|
||||||
|
|||||||
@@ -12,8 +12,11 @@ import com.alipay.v3.api.AlipayOpenAgentApi;
|
|||||||
import com.alipay.v3.api.AntMerchantExpandIndirectImageApi;
|
import com.alipay.v3.api.AntMerchantExpandIndirectImageApi;
|
||||||
import com.alipay.v3.model.*;
|
import com.alipay.v3.model.*;
|
||||||
import com.alipay.v3.util.model.AlipayConfig;
|
import com.alipay.v3.util.model.AlipayConfig;
|
||||||
|
import com.czg.dto.req.AggregateMerchantDto;
|
||||||
import com.czg.dto.resp.BankBranchDto;
|
import com.czg.dto.resp.BankBranchDto;
|
||||||
import com.czg.utils.CommonUtil;
|
import com.czg.dto.resp.EntryRespDto;
|
||||||
|
import com.czg.third.alipay.dto.config.AlipayConfigDto;
|
||||||
|
import com.czg.utils.UploadFileUtil;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -33,9 +36,29 @@ import java.util.List;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class AlipayEntryManager {
|
public class AlipayEntryManager {
|
||||||
|
|
||||||
public static String uploadImage(String url) {
|
/**
|
||||||
|
* 进件商户
|
||||||
|
*
|
||||||
|
* @param configDto 配置信息
|
||||||
|
* @param reqDto 请求信息
|
||||||
|
*/
|
||||||
|
public static EntryRespDto entryMerchant(AlipayConfigDto configDto, AggregateMerchantDto reqDto) {
|
||||||
|
return new EntryRespDto();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传图片
|
||||||
|
*
|
||||||
|
* @param configDto 配置信息
|
||||||
|
* @param url 图片地址
|
||||||
|
* @return 图片ID
|
||||||
|
*/
|
||||||
|
public static String uploadImage(AlipayConfigDto configDto, String url) {
|
||||||
|
if (configDto == null) {
|
||||||
|
configDto = AlipayConfigDto.getDefaultConfig();
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
byte[] bytes = CommonUtil.downloadImage(url);
|
byte[] bytes = UploadFileUtil.downloadImage(url);
|
||||||
|
|
||||||
Path tempFile = Files.createTempFile("image_", ".png");
|
Path tempFile = Files.createTempFile("image_", ".png");
|
||||||
|
|
||||||
@@ -45,10 +68,10 @@ public class AlipayEntryManager {
|
|||||||
File file = tempFile.toFile();
|
File file = tempFile.toFile();
|
||||||
|
|
||||||
AntMerchantExpandIndirectImageApi imageApi = new AntMerchantExpandIndirectImageApi();
|
AntMerchantExpandIndirectImageApi imageApi = new AntMerchantExpandIndirectImageApi();
|
||||||
|
imageApi.setApiClient(AlipayClient.getApiClient(configDto));
|
||||||
AntMerchantExpandIndirectImageUploadModel model = new AntMerchantExpandIndirectImageUploadModel();
|
AntMerchantExpandIndirectImageUploadModel model = new AntMerchantExpandIndirectImageUploadModel();
|
||||||
// 从 url 中获取图片 后缀
|
// 从 url 中获取图片 后缀
|
||||||
model.setImageType(CommonUtil.extractImageExtension(url));
|
model.setImageType(UploadFileUtil.extractImageExtension(url));
|
||||||
AntMerchantExpandIndirectImageUploadResponseModel upload = imageApi.upload(model, file);
|
AntMerchantExpandIndirectImageUploadResponseModel upload = imageApi.upload(model, file);
|
||||||
return upload.getImageId();
|
return upload.getImageId();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|||||||
@@ -1,20 +1,17 @@
|
|||||||
package com.czg.third.wechat;
|
package com.czg.third.wechat;
|
||||||
|
|
||||||
import com.alibaba.fastjson2.JSONArray;
|
|
||||||
import com.alibaba.fastjson2.JSONObject;
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
import com.alibaba.fastjson2.JSONWriter;
|
import com.alibaba.fastjson2.JSONWriter;
|
||||||
import com.czg.third.wechat.dto.req.entry.WechatEntryReqDto;
|
import com.czg.dto.req.AggregateMerchantDto;
|
||||||
import com.czg.utils.CommonUtil;
|
import com.czg.dto.resp.EntryRespDto;
|
||||||
import com.czg.third.wechat.dto.config.WechatPayConfigDto;
|
import com.czg.third.wechat.dto.config.WechatPayConfigDto;
|
||||||
|
import com.czg.third.wechat.dto.req.entry.WechatEntryReqDto;
|
||||||
|
import com.czg.utils.UploadFileUtil;
|
||||||
import com.wechat.pay.java.service.file.FileUploadService;
|
import com.wechat.pay.java.service.file.FileUploadService;
|
||||||
import com.wechat.pay.java.service.file.model.FileUploadResponse;
|
import com.wechat.pay.java.service.file.model.FileUploadResponse;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -26,6 +23,16 @@ import java.util.Map;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class WechatEntryManager {
|
public class WechatEntryManager {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 进件商户
|
||||||
|
*
|
||||||
|
* @param configDto 配置信息
|
||||||
|
* @param reqDto 请求信息
|
||||||
|
*/
|
||||||
|
public static EntryRespDto entryMerchant(WechatPayConfigDto configDto, AggregateMerchantDto reqDto) {
|
||||||
|
return new EntryRespDto();
|
||||||
|
}
|
||||||
|
|
||||||
public static JSONObject queryBankList(WechatPayConfigDto configDto, Integer offset, Integer limit) {
|
public static JSONObject queryBankList(WechatPayConfigDto configDto, Integer offset, Integer limit) {
|
||||||
String resp = WechatReqUtils.getReq(configDto, "/v3/capital/capitallhh/banks/corporate-banking", Map.of("offset", offset, "limit", limit));
|
String resp = WechatReqUtils.getReq(configDto, "/v3/capital/capitallhh/banks/corporate-banking", Map.of("offset", offset, "limit", limit));
|
||||||
log.info("查询银行列表:{}", resp);
|
log.info("查询银行列表:{}", resp);
|
||||||
@@ -67,8 +74,10 @@ public class WechatEntryManager {
|
|||||||
* @param url 图片URL
|
* @param url 图片URL
|
||||||
* @return 图片ID
|
* @return 图片ID
|
||||||
*/
|
*/
|
||||||
public static String uploadImage(String url) {
|
public static String uploadImage(WechatPayConfigDto configDto, String url) {
|
||||||
WechatPayConfigDto configDto = WechatPayConfigDto.getDefaultConfig();
|
if (configDto == null) {
|
||||||
|
configDto = WechatPayConfigDto.getDefaultConfig();
|
||||||
|
}
|
||||||
// 校验入参
|
// 校验入参
|
||||||
if (url == null || url.trim().isEmpty()) {
|
if (url == null || url.trim().isEmpty()) {
|
||||||
log.error("上传图片失败:URL参数为空");
|
log.error("上传图片失败:URL参数为空");
|
||||||
@@ -79,7 +88,7 @@ public class WechatEntryManager {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// 获取图片字节数组
|
// 获取图片字节数组
|
||||||
byte[] bytes = CommonUtil.downloadImage(url);
|
byte[] bytes = UploadFileUtil.downloadImage(url);
|
||||||
if (bytes.length == 0) {
|
if (bytes.length == 0) {
|
||||||
log.error("下载的图片内容为空,URL:{}", url);
|
log.error("下载的图片内容为空,URL:{}", url);
|
||||||
return "";
|
return "";
|
||||||
@@ -92,7 +101,7 @@ public class WechatEntryManager {
|
|||||||
JSONObject meta = new JSONObject();
|
JSONObject meta = new JSONObject();
|
||||||
meta.put("sha256", sha256Hex);
|
meta.put("sha256", sha256Hex);
|
||||||
// 从URL提取文件名,若提取失败则使用默认名
|
// 从URL提取文件名,若提取失败则使用默认名
|
||||||
String fileName = extractFileNameFromUrl(url);
|
String fileName = UploadFileUtil.extractFileNameFromUrl(url);
|
||||||
meta.put("filename", fileName);
|
meta.put("filename", fileName);
|
||||||
|
|
||||||
// 4. 上传图片到微信接口
|
// 4. 上传图片到微信接口
|
||||||
@@ -110,32 +119,6 @@ public class WechatEntryManager {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 从URL中提取文件名
|
|
||||||
*
|
|
||||||
* @param url 图片URL
|
|
||||||
* @return 提取的文件名,失败则返回默认名
|
|
||||||
*/
|
|
||||||
private static String extractFileNameFromUrl(String url) {
|
|
||||||
try {
|
|
||||||
if (url.contains("/")) {
|
|
||||||
String fileName = url.substring(url.lastIndexOf("/") + 1);
|
|
||||||
// 如果文件名包含参数,截取?之前的部分
|
|
||||||
if (fileName.contains("?")) {
|
|
||||||
fileName = fileName.substring(0, fileName.indexOf("?"));
|
|
||||||
}
|
|
||||||
// 如果提取的文件名有效,直接返回
|
|
||||||
if (fileName.contains(".")) {
|
|
||||||
return fileName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.warn("提取文件名失败,使用默认文件名", e);
|
|
||||||
}
|
|
||||||
// 默认文件名
|
|
||||||
return "upload_" + System.currentTimeMillis() + ".png";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException {
|
public static void main(String[] args) throws IOException {
|
||||||
WechatPayConfigDto dto = new WechatPayConfigDto()
|
WechatPayConfigDto dto = new WechatPayConfigDto()
|
||||||
.setMerchantId("1643779408")
|
.setMerchantId("1643779408")
|
||||||
|
|||||||
@@ -0,0 +1,157 @@
|
|||||||
|
package com.czg.utils;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java 21 异步多任务执行工具类
|
||||||
|
* 功能:异步执行多个任务,等待所有任务完成后统一返回结果(包含成功/失败信息)
|
||||||
|
* 特性:基于虚拟线程、支持泛型、完善的异常处理、可自定义线程池
|
||||||
|
*
|
||||||
|
* @author yjjie
|
||||||
|
* @date 2026/1/6 18:21
|
||||||
|
*/
|
||||||
|
public class AsyncTaskExecutor {
|
||||||
|
|
||||||
|
// 默认线程池:Java 21 虚拟线程池(轻量级、高并发)
|
||||||
|
private static final ExecutorService DEFAULT_EXECUTOR = Executors.newVirtualThreadPerTaskExecutor();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行多个异步任务,等待所有任务完成后统一返回结果
|
||||||
|
*
|
||||||
|
* @param tasks 任务列表(每个任务是一个 Supplier 函数式接口,封装具体业务逻辑)
|
||||||
|
* @param <T> 任务返回值类型
|
||||||
|
* @return 所有任务的执行结果(包含成功/失败信息)
|
||||||
|
*/
|
||||||
|
public static <T> List<TaskResult<T>> executeAll(List<Supplier<T>> tasks) {
|
||||||
|
return executeAll(tasks, DEFAULT_EXECUTOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行多个异步任务(自定义线程池),等待所有任务完成后统一返回结果
|
||||||
|
*
|
||||||
|
* @param tasks 任务列表
|
||||||
|
* @param executor 自定义线程池(如需要使用传统线程池可传入)
|
||||||
|
* @param <T> 任务返回值类型
|
||||||
|
* @return 所有任务的执行结果
|
||||||
|
*/
|
||||||
|
public static <T> List<TaskResult<T>> executeAll(List<Supplier<T>> tasks, ExecutorService executor) {
|
||||||
|
// 校验入参
|
||||||
|
if (tasks == null || tasks.isEmpty()) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
if (executor == null) {
|
||||||
|
throw new IllegalArgumentException("线程池不能为null");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. 提交所有异步任务,获取CompletableFuture列表
|
||||||
|
List<CompletableFuture<TaskResult<T>>> futureList = tasks.stream()
|
||||||
|
.map(task -> CompletableFuture.supplyAsync(
|
||||||
|
() -> executeSingleTask(task),
|
||||||
|
executor
|
||||||
|
))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
// 2. 等待所有任务完成(无超时)
|
||||||
|
CompletableFuture<Void> allFutures = CompletableFuture.allOf(
|
||||||
|
futureList.toArray(new CompletableFuture[0])
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 阻塞等待所有任务完成(可根据业务需求添加超时,如 allFutures.get(10, TimeUnit.SECONDS))
|
||||||
|
allFutures.get();
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 全局等待异常(如超时、中断),标记所有未完成的任务为失败
|
||||||
|
handleGlobalException(futureList, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 收集所有任务结果
|
||||||
|
return futureList.stream()
|
||||||
|
// 显式指定泛型类型,让编译器明确知道map的返回类型是TaskResult<T>
|
||||||
|
.<TaskResult<T>>map(future -> {
|
||||||
|
try {
|
||||||
|
// 这里强制指定泛型,避免类型推断模糊
|
||||||
|
return future.get();
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 理论上不会走到这里,因为singleTask已捕获异常,allOf已等待完成
|
||||||
|
return new TaskResult<>(null, false, "结果收集异常:" + e.getMessage());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行单个任务,捕获任务执行过程中的异常
|
||||||
|
*/
|
||||||
|
private static <T> TaskResult<T> executeSingleTask(Supplier<T> task) {
|
||||||
|
try {
|
||||||
|
T result = task.get();
|
||||||
|
return new TaskResult<>(result, true, null);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 捕获单个任务的所有异常,封装为失败结果
|
||||||
|
return new TaskResult<>(null, false, "任务执行失败:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理全局等待过程中的异常(如超时、中断),标记未完成任务为失败
|
||||||
|
*/
|
||||||
|
private static <T> void handleGlobalException(List<CompletableFuture<TaskResult<T>>> futureList, Exception e) {
|
||||||
|
String errorMsg = "全局等待异常:" + e.getMessage();
|
||||||
|
futureList.forEach(future -> {
|
||||||
|
if (!future.isDone()) {
|
||||||
|
// 取消未完成的任务,并标记为失败
|
||||||
|
future.complete(new TaskResult<>(null, false, errorMsg));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 恢复线程中断状态(如果是中断异常)
|
||||||
|
if (e instanceof InterruptedException) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务结果封装类
|
||||||
|
* 包含:返回值、是否成功、失败信息
|
||||||
|
*
|
||||||
|
* @param <T> 结果类型
|
||||||
|
* @param result Getter 方法 任务返回值(成功时非null)
|
||||||
|
* @param success 是否执行成功
|
||||||
|
* @param errorMsg 失败信息(失败时非null)
|
||||||
|
*/
|
||||||
|
public record TaskResult<T>(T result, boolean success, String errorMsg) {
|
||||||
|
|
||||||
|
// 重写toString,方便打印结果
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (success) {
|
||||||
|
return "TaskResult{success=true, result=" + result + "}";
|
||||||
|
} else {
|
||||||
|
return "TaskResult{success=false, errorMsg='" + errorMsg + "'}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭默认线程池(可选,如应用退出时调用)
|
||||||
|
*/
|
||||||
|
public static void shutdownDefaultExecutor() {
|
||||||
|
DEFAULT_EXECUTOR.shutdown();
|
||||||
|
try {
|
||||||
|
if (!DEFAULT_EXECUTOR.awaitTermination(5, TimeUnit.SECONDS)) {
|
||||||
|
DEFAULT_EXECUTOR.shutdownNow();
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
DEFAULT_EXECUTOR.shutdownNow();
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@ import java.util.regex.Pattern;
|
|||||||
* @date 2026/1/4 13:58
|
* @date 2026/1/4 13:58
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class CommonUtil {
|
public class UploadFileUtil {
|
||||||
|
|
||||||
// 匹配常见的图片扩展名
|
// 匹配常见的图片扩展名
|
||||||
private static final Pattern PATTERN = Pattern.compile("\\.(jpg|jpeg|png|gif|bmp|webp|svg|ico)(?:[\\?#]|$)", Pattern.CASE_INSENSITIVE);
|
private static final Pattern PATTERN = Pattern.compile("\\.(jpg|jpeg|png|gif|bmp|webp|svg|ico)(?:[\\?#]|$)", Pattern.CASE_INSENSITIVE);
|
||||||
@@ -58,4 +58,30 @@ public class CommonUtil {
|
|||||||
return new byte[0];
|
return new byte[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从URL中提取文件名
|
||||||
|
*
|
||||||
|
* @param url 图片URL
|
||||||
|
* @return 提取的文件名,失败则返回默认名
|
||||||
|
*/
|
||||||
|
public static String extractFileNameFromUrl(String url) {
|
||||||
|
try {
|
||||||
|
if (url.contains("/")) {
|
||||||
|
String fileName = url.substring(url.lastIndexOf("/") + 1);
|
||||||
|
// 如果文件名包含参数,截取?之前的部分
|
||||||
|
if (fileName.contains("?")) {
|
||||||
|
fileName = fileName.substring(0, fileName.indexOf("?"));
|
||||||
|
}
|
||||||
|
// 如果提取的文件名有效,直接返回
|
||||||
|
if (fileName.contains(".")) {
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("提取文件名失败,使用默认文件名", e);
|
||||||
|
}
|
||||||
|
// 默认文件名
|
||||||
|
return "upload_" + System.currentTimeMillis() + ".png";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user