封装
This commit is contained in:
@@ -3,12 +3,16 @@ package com.czg;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.czg.dto.req.*;
|
||||
import com.czg.dto.resp.BankBranchDto;
|
||||
import com.czg.dto.resp.EntryRespDto;
|
||||
import com.czg.exception.CzgException;
|
||||
import com.czg.third.alipay.AlipayEntryManager;
|
||||
import com.czg.third.wechat.WechatEntryManager;
|
||||
import com.czg.utils.AssertUtil;
|
||||
import com.czg.utils.AsyncTaskExecutor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* 进件管理
|
||||
@@ -34,101 +38,136 @@ public class EntryManager {
|
||||
* 请先执行:
|
||||
* 1. {@link com.czg.EntryManager#verifyEntryParam(AggregateMerchantDto)} 验证进件参数
|
||||
* 2. {@link com.czg.EntryManager#uploadParamImage(AggregateMerchantDto)} 上传图片至第三方
|
||||
*
|
||||
* @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)} 验证进件参数
|
||||
*
|
||||
* @param reqDto 进件参数
|
||||
*/
|
||||
public static void uploadParamImage(AggregateMerchantDto reqDto) {
|
||||
List<Supplier<Void>> tasks = new ArrayList<>();
|
||||
|
||||
MerchantBaseInfoDto baseInfo = reqDto.getMerchantBaseInfo();
|
||||
// 联系人身份证反面
|
||||
if (baseInfo.getContactIdCardBackPic() != null) {
|
||||
tasks.add(() -> {
|
||||
uploadImageToThird(baseInfo.getContactIdCardBackPic());
|
||||
}
|
||||
return null;
|
||||
});
|
||||
// 联系人身份证正面
|
||||
if (baseInfo.getContactIdCardFrontPic() != null) {
|
||||
tasks.add(() -> {
|
||||
uploadImageToThird(baseInfo.getContactIdCardFrontPic());
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
LegalPersonInfoDto legalPersonInfo = reqDto.getLegalPersonInfo();
|
||||
// 法人身份证反面
|
||||
if (legalPersonInfo.getIdCardBackPic() != null) {
|
||||
tasks.add(() -> {
|
||||
uploadImageToThird(legalPersonInfo.getIdCardBackPic());
|
||||
}
|
||||
return null;
|
||||
});
|
||||
// 法人身份证正面
|
||||
if (legalPersonInfo.getIdCardFrontPic() != null) {
|
||||
tasks.add(() -> {
|
||||
uploadImageToThird(legalPersonInfo.getIdCardFrontPic());
|
||||
}
|
||||
return null;
|
||||
});
|
||||
// 法人手持身份证
|
||||
if (legalPersonInfo.getIdCardHandPic() != null) {
|
||||
tasks.add(() -> {
|
||||
uploadImageToThird(legalPersonInfo.getIdCardHandPic());
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
BusinessLicenceInfoDto businessLicenceInfo = reqDto.getBusinessLicenceInfo();
|
||||
// 营业执照
|
||||
if (businessLicenceInfo.getLicensePic() != null) {
|
||||
tasks.add(() -> {
|
||||
uploadImageToThird(businessLicenceInfo.getLicensePic());
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
SettlementInfoDto settlementInfo = reqDto.getSettlementInfo();
|
||||
// 银行卡背面
|
||||
if (settlementInfo.getBankCardBackPic() != null) {
|
||||
tasks.add(() -> {
|
||||
uploadImageToThird(settlementInfo.getBankCardBackPic());
|
||||
}
|
||||
return null;
|
||||
});
|
||||
// 银行卡正面
|
||||
if (settlementInfo.getBankCardFrontPic() != null) {
|
||||
tasks.add(() -> {
|
||||
uploadImageToThird(settlementInfo.getBankCardFrontPic());
|
||||
}
|
||||
return null;
|
||||
});
|
||||
// 开户许可证
|
||||
if (settlementInfo.getOpenAccountLicencePic() != null) {
|
||||
tasks.add(() -> {
|
||||
uploadImageToThird(settlementInfo.getOpenAccountLicencePic());
|
||||
}
|
||||
return null;
|
||||
});
|
||||
// 非法人手持授权函
|
||||
if (settlementInfo.getNoLegalHandSettleAuthPic() != null) {
|
||||
tasks.add(() -> {
|
||||
uploadImageToThird(settlementInfo.getNoLegalHandSettleAuthPic());
|
||||
}
|
||||
return null;
|
||||
});
|
||||
// 非法人授权函
|
||||
if (settlementInfo.getNoLegalSettleAuthPic() != null) {
|
||||
tasks.add(() -> {
|
||||
uploadImageToThird(settlementInfo.getNoLegalSettleAuthPic());
|
||||
}
|
||||
return null;
|
||||
});
|
||||
// 非法人身份证反面
|
||||
if (settlementInfo.getNoLegalIdCardBackPic() != null) {
|
||||
tasks.add(() -> {
|
||||
uploadImageToThird(settlementInfo.getNoLegalIdCardBackPic());
|
||||
}
|
||||
return null;
|
||||
});
|
||||
// 非法人身份证正面
|
||||
if (settlementInfo.getNoLegalIdCardFrontPic() != null) {
|
||||
tasks.add(() -> {
|
||||
uploadImageToThird(settlementInfo.getNoLegalIdCardFrontPic());
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
StoreInfoDto storeInfo = reqDto.getStoreInfo();
|
||||
// 店内图片
|
||||
if (storeInfo.getInsidePic() != null) {
|
||||
tasks.add(() -> {
|
||||
uploadImageToThird(storeInfo.getInsidePic());
|
||||
}
|
||||
return null;
|
||||
});
|
||||
// 门店门头图片
|
||||
if (storeInfo.getDoorPic() != null) {
|
||||
tasks.add(() -> {
|
||||
uploadImageToThird(storeInfo.getDoorPic());
|
||||
}
|
||||
return null;
|
||||
});
|
||||
// 收银台图片
|
||||
if (storeInfo.getCashierDeskPic() != null) {
|
||||
tasks.add(() -> {
|
||||
uploadImageToThird(storeInfo.getCashierDeskPic());
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
// 执行所有任务
|
||||
AsyncTaskExecutor.executeAll(tasks);
|
||||
}
|
||||
|
||||
private static void uploadImageToThird(ImageDto dto) {
|
||||
if (StrUtil.isBlank(dto.getWechatId())) {
|
||||
String image = WechatEntryManager.uploadImage(dto.getUrl());
|
||||
dto.setWechatId(image);
|
||||
}
|
||||
if (StrUtil.isBlank(dto.getAlipayId())) {
|
||||
String image = AlipayEntryManager.uploadImage(dto.getUrl());
|
||||
dto.setAlipayId(image);
|
||||
if (dto != null && StrUtil.isNotBlank(dto.getUrl())) {
|
||||
if (StrUtil.isBlank(dto.getWechatId())) {
|
||||
String image = WechatEntryManager.uploadImage(null, dto.getUrl());
|
||||
dto.setWechatId(image);
|
||||
}
|
||||
if (StrUtil.isBlank(dto.getAlipayId())) {
|
||||
String image = AlipayEntryManager.uploadImage(null, dto.getUrl());
|
||||
dto.setAlipayId(image);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,6 +306,7 @@ public class EntryManager {
|
||||
AggregateMerchantDto merchantDto = new AggregateMerchantDto();
|
||||
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.AlipayConfig;
|
||||
import com.alipay.api.DefaultAlipayClient;
|
||||
import com.alipay.v3.ApiClient;
|
||||
import com.alipay.v3.Configuration;
|
||||
import com.czg.third.alipay.dto.config.AlipayConfigDto;
|
||||
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) {
|
||||
if (configDto == null) {
|
||||
configDto = AlipayConfigDto.getDefaultConfig();
|
||||
|
||||
@@ -12,8 +12,11 @@ import com.alipay.v3.api.AlipayOpenAgentApi;
|
||||
import com.alipay.v3.api.AntMerchantExpandIndirectImageApi;
|
||||
import com.alipay.v3.model.*;
|
||||
import com.alipay.v3.util.model.AlipayConfig;
|
||||
import com.czg.dto.req.AggregateMerchantDto;
|
||||
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 java.io.File;
|
||||
@@ -33,9 +36,29 @@ import java.util.List;
|
||||
@Slf4j
|
||||
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 {
|
||||
byte[] bytes = CommonUtil.downloadImage(url);
|
||||
byte[] bytes = UploadFileUtil.downloadImage(url);
|
||||
|
||||
Path tempFile = Files.createTempFile("image_", ".png");
|
||||
|
||||
@@ -45,10 +68,10 @@ public class AlipayEntryManager {
|
||||
File file = tempFile.toFile();
|
||||
|
||||
AntMerchantExpandIndirectImageApi imageApi = new AntMerchantExpandIndirectImageApi();
|
||||
|
||||
imageApi.setApiClient(AlipayClient.getApiClient(configDto));
|
||||
AntMerchantExpandIndirectImageUploadModel model = new AntMerchantExpandIndirectImageUploadModel();
|
||||
// 从 url 中获取图片 后缀
|
||||
model.setImageType(CommonUtil.extractImageExtension(url));
|
||||
model.setImageType(UploadFileUtil.extractImageExtension(url));
|
||||
AntMerchantExpandIndirectImageUploadResponseModel upload = imageApi.upload(model, file);
|
||||
return upload.getImageId();
|
||||
} catch (Exception e) {
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
package com.czg.third.wechat;
|
||||
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.alibaba.fastjson2.JSONWriter;
|
||||
import com.czg.third.wechat.dto.req.entry.WechatEntryReqDto;
|
||||
import com.czg.utils.CommonUtil;
|
||||
import com.czg.dto.req.AggregateMerchantDto;
|
||||
import com.czg.dto.resp.EntryRespDto;
|
||||
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.model.FileUploadResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -26,6 +23,16 @@ import java.util.Map;
|
||||
@Slf4j
|
||||
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) {
|
||||
String resp = WechatReqUtils.getReq(configDto, "/v3/capital/capitallhh/banks/corporate-banking", Map.of("offset", offset, "limit", limit));
|
||||
log.info("查询银行列表:{}", resp);
|
||||
@@ -67,8 +74,10 @@ public class WechatEntryManager {
|
||||
* @param url 图片URL
|
||||
* @return 图片ID
|
||||
*/
|
||||
public static String uploadImage(String url) {
|
||||
WechatPayConfigDto configDto = WechatPayConfigDto.getDefaultConfig();
|
||||
public static String uploadImage(WechatPayConfigDto configDto, String url) {
|
||||
if (configDto == null) {
|
||||
configDto = WechatPayConfigDto.getDefaultConfig();
|
||||
}
|
||||
// 校验入参
|
||||
if (url == null || url.trim().isEmpty()) {
|
||||
log.error("上传图片失败:URL参数为空");
|
||||
@@ -79,7 +88,7 @@ public class WechatEntryManager {
|
||||
|
||||
try {
|
||||
// 获取图片字节数组
|
||||
byte[] bytes = CommonUtil.downloadImage(url);
|
||||
byte[] bytes = UploadFileUtil.downloadImage(url);
|
||||
if (bytes.length == 0) {
|
||||
log.error("下载的图片内容为空,URL:{}", url);
|
||||
return "";
|
||||
@@ -92,7 +101,7 @@ public class WechatEntryManager {
|
||||
JSONObject meta = new JSONObject();
|
||||
meta.put("sha256", sha256Hex);
|
||||
// 从URL提取文件名,若提取失败则使用默认名
|
||||
String fileName = extractFileNameFromUrl(url);
|
||||
String fileName = UploadFileUtil.extractFileNameFromUrl(url);
|
||||
meta.put("filename", fileName);
|
||||
|
||||
// 4. 上传图片到微信接口
|
||||
@@ -110,32 +119,6 @@ public class WechatEntryManager {
|
||||
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 {
|
||||
WechatPayConfigDto dto = new WechatPayConfigDto()
|
||||
.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
|
||||
*/
|
||||
@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);
|
||||
@@ -58,4 +58,30 @@ public class CommonUtil {
|
||||
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