🐛 修复未配置 MinIO 时 transport 服务无法启动的问题
凭证图片 MinIO 客户端此前为无条件装配,缺少 file.storage.minio.endpoint 配置时会在启动阶段直接抛 IllegalArgumentException,导致整个 blade-transport 服务起不来,并在关闭失败上下文时连带抛出 nacosGracefulShutdownDelegate 的 BeanCreationNotAllowedException, 掩盖了真正的失败原因。 - VoucherMinioConfig 增加 @ConditionalOnProperty,未配置 endpoint 时不注册客户端 - ProcessConfigController、VoucherManageServiceImpl 改用 ObjectProvider 可选注入,避免构造器注入因 bean 缺失而启动失败 - 新增 minioClient() 访问器,在真正调用时给出"未配置"的明确提示, 与已有 validateMinioConfig() 的提示风格保持一致 未配置 MinIO 时服务可正常启动,凭证相关接口仅在使用时提示配置缺失; 已配置 MinIO 时行为不变。
This commit is contained in:
+6
@@ -6,14 +6,20 @@ package org.springblade.transport.config;
|
||||
|
||||
import io.minio.MinioClient;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* 凭证图片 MinIO 客户端配置。
|
||||
* <p>
|
||||
* 连接参数由 Nacos 的 file.storage.minio 配置提供。
|
||||
* 未配置 {@code file.storage.minio.endpoint} 时不注册该客户端,
|
||||
* 以免凭证上传功能缺失配置导致整个 blade-transport 服务无法启动;
|
||||
* 此时凭证相关接口会在调用时给出明确提示,而非启动即失败。
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnProperty(prefix = "file.storage.minio", name = "endpoint")
|
||||
public class VoucherMinioConfig {
|
||||
|
||||
@Bean
|
||||
|
||||
+20
-5
@@ -52,6 +52,7 @@ import org.springblade.transport.pojo.entity.Waybill;
|
||||
import org.springblade.transport.pojo.vo.BusinessRemoveResultVO;
|
||||
import org.springblade.transport.pojo.vo.ProcessConfigVO;
|
||||
import org.springblade.transport.service.IProcessConfigService;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
@@ -83,20 +84,34 @@ public class ProcessConfigController extends BladeController {
|
||||
private final VoucherFileMapper voucherFileMapper;
|
||||
private final VoucherImageMapper voucherImageMapper;
|
||||
private final VoucherManageMapper voucherManageMapper;
|
||||
private final MinioClient minioClient;
|
||||
private final ObjectProvider<MinioClient> minioClientProvider;
|
||||
@Value("${file.storage.minio.bucket-name:${minio.bucket-name:}}")
|
||||
private String minioBucketName;
|
||||
|
||||
public ProcessConfigController(IProcessConfigService processConfigService, WaybillMapper waybillMapper,
|
||||
VoucherFileMapper voucherFileMapper, VoucherImageMapper voucherImageMapper,
|
||||
VoucherManageMapper voucherManageMapper,
|
||||
MinioClient minioClient) {
|
||||
ObjectProvider<MinioClient> minioClientProvider) {
|
||||
this.processConfigService = processConfigService;
|
||||
this.waybillMapper = waybillMapper;
|
||||
this.voucherFileMapper = voucherFileMapper;
|
||||
this.voucherImageMapper = voucherImageMapper;
|
||||
this.voucherManageMapper = voucherManageMapper;
|
||||
this.minioClient = minioClient;
|
||||
this.minioClientProvider = minioClientProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 MinIO 客户端。
|
||||
* <p>
|
||||
* 未配置 file.storage.minio.endpoint 时该客户端不会被注册,
|
||||
* 此时凭证预览地址无法生成,抛出明确提示而非启动即失败。
|
||||
*/
|
||||
private MinioClient minioClient() {
|
||||
MinioClient minioClient = minioClientProvider.getIfAvailable();
|
||||
if (minioClient == null) {
|
||||
throw new IllegalStateException("Nacos 未配置 file.storage.minio.endpoint,凭证文件功能不可用");
|
||||
}
|
||||
return minioClient;
|
||||
}
|
||||
|
||||
@GetMapping("/detail")
|
||||
@@ -147,7 +162,7 @@ public class ProcessConfigController extends BladeController {
|
||||
result.put("waybillNo", image.getWaybillNo());
|
||||
result.put("objectKey", image.getObjectKey());
|
||||
try {
|
||||
result.put("url", minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
|
||||
result.put("url", minioClient().getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
|
||||
.method(Method.GET).bucket(minioBucketName).object(image.getObjectKey())
|
||||
.expiry(1, TimeUnit.HOURS).build()));
|
||||
} catch (Exception e) {
|
||||
@@ -270,7 +285,7 @@ public class ProcessConfigController extends BladeController {
|
||||
result.put("objectKey", image.objectKey());
|
||||
result.put("matched", image.matched());
|
||||
try {
|
||||
result.put("url", minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
|
||||
result.put("url", minioClient().getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
|
||||
.method(Method.GET).bucket(minioBucketName).object(image.objectKey())
|
||||
.expiry(1, TimeUnit.HOURS).build()));
|
||||
} catch (Exception exception) {
|
||||
|
||||
+25
-10
@@ -42,6 +42,7 @@ import io.minio.PutObjectArgs;
|
||||
import io.minio.RemoveObjectArgs;
|
||||
import io.minio.http.Method;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
|
||||
import java.time.format.DateTimeFormatter;
|
||||
@@ -79,14 +80,14 @@ public class VoucherManageServiceImpl extends BaseServiceImpl<VoucherManageMappe
|
||||
private final VoucherImageMapper voucherImageMapper;
|
||||
private final VoucherFileMapper voucherFileMapper;
|
||||
private final ApplicationEventPublisher eventPublisher;
|
||||
private final MinioClient minioClient;
|
||||
private final ObjectProvider<MinioClient> minioClientProvider;
|
||||
private final String minioBucketName;
|
||||
private final String minioRootDirectory;
|
||||
|
||||
public VoucherManageServiceImpl(VoucherWaybillBatchMapper voucherWaybillBatchMapper, IProjectApplyService projectApplyService,
|
||||
IWaybillService waybillService,
|
||||
VoucherImageMapper voucherImageMapper, VoucherFileMapper voucherFileMapper,
|
||||
ApplicationEventPublisher eventPublisher, MinioClient minioClient,
|
||||
ApplicationEventPublisher eventPublisher, ObjectProvider<MinioClient> minioClientProvider,
|
||||
@Value("${file.storage.minio.bucket-name:${minio.bucket-name:}}") String minioBucketName,
|
||||
@Value("${file.storage.minio.root-directory:${minio.root-directory:}}") String minioRootDirectory) {
|
||||
this.voucherWaybillBatchMapper = voucherWaybillBatchMapper;
|
||||
@@ -95,7 +96,7 @@ public class VoucherManageServiceImpl extends BaseServiceImpl<VoucherManageMappe
|
||||
this.voucherImageMapper = voucherImageMapper;
|
||||
this.voucherFileMapper = voucherFileMapper;
|
||||
this.eventPublisher = eventPublisher;
|
||||
this.minioClient = minioClient;
|
||||
this.minioClientProvider = minioClientProvider;
|
||||
this.minioBucketName = minioBucketName;
|
||||
this.minioRootDirectory = minioRootDirectory;
|
||||
}
|
||||
@@ -251,7 +252,7 @@ public class VoucherManageServiceImpl extends BaseServiceImpl<VoucherManageMappe
|
||||
List<String> candidateObjectKeys = resolvedObjectKey.equals(objectKey)
|
||||
? List.of(objectKey) : List.of(resolvedObjectKey, objectKey);
|
||||
for (String candidateObjectKey : candidateObjectKeys) {
|
||||
try (InputStream source = minioClient.getObject(GetObjectArgs.builder().bucket(minioBucketName).object(candidateObjectKey).build());
|
||||
try (InputStream source = minioClient().getObject(GetObjectArgs.builder().bucket(minioBucketName).object(candidateObjectKey).build());
|
||||
OutputStream target = Files.newOutputStream(archivePath)) {
|
||||
source.transferTo(target);
|
||||
return archivePath;
|
||||
@@ -427,7 +428,7 @@ public class VoucherManageServiceImpl extends BaseServiceImpl<VoucherManageMappe
|
||||
String waybillNo = matchedWaybill == null ? "unmatched" : safePathPart(matchedWaybill.getWaybillNo());
|
||||
String objectKey = buildObjectKey(voucher.getId(), waybillNo, plateNo, entryName);
|
||||
String contentType = contentType(fileName);
|
||||
minioClient.putObject(PutObjectArgs.builder().bucket(minioBucketName).object(objectKey)
|
||||
minioClient().putObject(PutObjectArgs.builder().bucket(minioBucketName).object(objectKey)
|
||||
.stream(zipInputStream, entry.getSize(), 10 * 1024 * 1024)
|
||||
.contentType(contentType).build());
|
||||
VoucherFile file = new VoucherFile();
|
||||
@@ -490,7 +491,7 @@ public class VoucherManageServiceImpl extends BaseServiceImpl<VoucherManageMappe
|
||||
String waybillNo = matchedWaybill == null ? "unmatched" : safePathPart(matchedWaybill.getWaybillNo());
|
||||
String objectKey = buildObjectKey(voucher.getId(), waybillNo, plateNo, entryName);
|
||||
String contentType = contentType(fileName);
|
||||
minioClient.putObject(PutObjectArgs.builder().bucket(minioBucketName).object(objectKey)
|
||||
minioClient().putObject(PutObjectArgs.builder().bucket(minioBucketName).object(objectKey)
|
||||
.stream(zipInputStream, entry.getSize(), 10 * 1024 * 1024)
|
||||
.contentType(contentType).build());
|
||||
VoucherFile file = new VoucherFile();
|
||||
@@ -537,7 +538,7 @@ public class VoucherManageServiceImpl extends BaseServiceImpl<VoucherManageMappe
|
||||
String entryName = plateNo + "/" + System.currentTimeMillis() + "_" + fileName;
|
||||
String objectKey = buildObjectKey(voucher.getId(), matchedWaybill == null ? "unmatched" : safePathPart(matchedWaybill.getWaybillNo()), plateNo, entryName);
|
||||
try (InputStream inputStream = imageFile.getInputStream()) {
|
||||
minioClient.putObject(PutObjectArgs.builder().bucket(minioBucketName).object(objectKey)
|
||||
minioClient().putObject(PutObjectArgs.builder().bucket(minioBucketName).object(objectKey)
|
||||
.stream(inputStream, imageFile.getSize(), 10 * 1024 * 1024)
|
||||
.contentType(Func.isEmpty(imageFile.getContentType()) ? contentType(fileName) : imageFile.getContentType()).build());
|
||||
}
|
||||
@@ -606,7 +607,7 @@ public class VoucherManageServiceImpl extends BaseServiceImpl<VoucherManageMappe
|
||||
return Func.isEmpty(normalizedPlateNo) ? "未识别车牌" : normalizedPlateNo;
|
||||
}
|
||||
|
||||
private void deleteObjectQuietly(String objectKey) { try { minioClient.removeObject(RemoveObjectArgs.builder().bucket(minioBucketName).object(objectKey).build()); } catch (Exception exception) { log.warn("删除凭证对象失败 objectKey={}", objectKey, exception); } }
|
||||
private void deleteObjectQuietly(String objectKey) { try { minioClient().removeObject(RemoveObjectArgs.builder().bucket(minioBucketName).object(objectKey).build()); } catch (Exception exception) { log.warn("删除凭证对象失败 objectKey={}", objectKey, exception); } }
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@@ -838,7 +839,7 @@ public class VoucherManageServiceImpl extends BaseServiceImpl<VoucherManageMappe
|
||||
String objectKey = buildObjectKey(voucher.getId(), waybillNo,
|
||||
Func.isEmpty(plateNo) ? "root" : plateNo, entryName);
|
||||
String contentType = contentType(fileName);
|
||||
minioClient.putObject(PutObjectArgs.builder().bucket(minioBucketName).object(objectKey)
|
||||
minioClient().putObject(PutObjectArgs.builder().bucket(minioBucketName).object(objectKey)
|
||||
.stream(zipInputStream, entry.getSize(), 10 * 1024 * 1024)
|
||||
.contentType(contentType).build());
|
||||
VoucherFile file = new VoucherFile();
|
||||
@@ -1052,6 +1053,20 @@ public class VoucherManageServiceImpl extends BaseServiceImpl<VoucherManageMappe
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 MinIO 客户端。
|
||||
* <p>
|
||||
* 未配置 file.storage.minio.endpoint 时该客户端不会被注册,
|
||||
* 此处给出与 {@link #validateMinioConfig()} 一致的明确提示。
|
||||
*/
|
||||
private MinioClient minioClient() {
|
||||
MinioClient minioClient = minioClientProvider.getIfAvailable();
|
||||
if (minioClient == null) {
|
||||
throw new ServiceException("Nacos 未配置 file.storage.minio.endpoint,凭证文件功能不可用");
|
||||
}
|
||||
return minioClient;
|
||||
}
|
||||
|
||||
private String buildObjectKey(Long voucherId, String waybillNo, String plateNo, String entryName) {
|
||||
String objectKey = voucherId + "/" + waybillNo + "/" + safePathPart(plateNo) + "/" + safeArchivePath(entryName);
|
||||
if (Func.isEmpty(minioRootDirectory)) {
|
||||
@@ -1127,7 +1142,7 @@ public class VoucherManageServiceImpl extends BaseServiceImpl<VoucherManageMappe
|
||||
|
||||
private String buildVoucherFileUrl(Long fileId, String objectKey) {
|
||||
try {
|
||||
return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
|
||||
return minioClient().getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
|
||||
.method(Method.GET).bucket(minioBucketName).object(objectKey).expiry(1, java.util.concurrent.TimeUnit.HOURS).build());
|
||||
} catch (Exception exception) {
|
||||
log.warn("生成凭证文件预览地址失败 voucherFileId={}, objectKey={}", fileId, objectKey, exception);
|
||||
|
||||
Reference in New Issue
Block a user