🐛 修复未配置 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 io.minio.MinioClient;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
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.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 凭证图片 MinIO 客户端配置。
|
* 凭证图片 MinIO 客户端配置。
|
||||||
|
* <p>
|
||||||
* 连接参数由 Nacos 的 file.storage.minio 配置提供。
|
* 连接参数由 Nacos 的 file.storage.minio 配置提供。
|
||||||
|
* 未配置 {@code file.storage.minio.endpoint} 时不注册该客户端,
|
||||||
|
* 以免凭证上传功能缺失配置导致整个 blade-transport 服务无法启动;
|
||||||
|
* 此时凭证相关接口会在调用时给出明确提示,而非启动即失败。
|
||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
|
@ConditionalOnProperty(prefix = "file.storage.minio", name = "endpoint")
|
||||||
public class VoucherMinioConfig {
|
public class VoucherMinioConfig {
|
||||||
|
|
||||||
@Bean
|
@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.BusinessRemoveResultVO;
|
||||||
import org.springblade.transport.pojo.vo.ProcessConfigVO;
|
import org.springblade.transport.pojo.vo.ProcessConfigVO;
|
||||||
import org.springblade.transport.service.IProcessConfigService;
|
import org.springblade.transport.service.IProcessConfigService;
|
||||||
|
import org.springframework.beans.factory.ObjectProvider;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
@@ -83,20 +84,34 @@ public class ProcessConfigController extends BladeController {
|
|||||||
private final VoucherFileMapper voucherFileMapper;
|
private final VoucherFileMapper voucherFileMapper;
|
||||||
private final VoucherImageMapper voucherImageMapper;
|
private final VoucherImageMapper voucherImageMapper;
|
||||||
private final VoucherManageMapper voucherManageMapper;
|
private final VoucherManageMapper voucherManageMapper;
|
||||||
private final MinioClient minioClient;
|
private final ObjectProvider<MinioClient> minioClientProvider;
|
||||||
@Value("${file.storage.minio.bucket-name:${minio.bucket-name:}}")
|
@Value("${file.storage.minio.bucket-name:${minio.bucket-name:}}")
|
||||||
private String minioBucketName;
|
private String minioBucketName;
|
||||||
|
|
||||||
public ProcessConfigController(IProcessConfigService processConfigService, WaybillMapper waybillMapper,
|
public ProcessConfigController(IProcessConfigService processConfigService, WaybillMapper waybillMapper,
|
||||||
VoucherFileMapper voucherFileMapper, VoucherImageMapper voucherImageMapper,
|
VoucherFileMapper voucherFileMapper, VoucherImageMapper voucherImageMapper,
|
||||||
VoucherManageMapper voucherManageMapper,
|
VoucherManageMapper voucherManageMapper,
|
||||||
MinioClient minioClient) {
|
ObjectProvider<MinioClient> minioClientProvider) {
|
||||||
this.processConfigService = processConfigService;
|
this.processConfigService = processConfigService;
|
||||||
this.waybillMapper = waybillMapper;
|
this.waybillMapper = waybillMapper;
|
||||||
this.voucherFileMapper = voucherFileMapper;
|
this.voucherFileMapper = voucherFileMapper;
|
||||||
this.voucherImageMapper = voucherImageMapper;
|
this.voucherImageMapper = voucherImageMapper;
|
||||||
this.voucherManageMapper = voucherManageMapper;
|
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")
|
@GetMapping("/detail")
|
||||||
@@ -147,7 +162,7 @@ public class ProcessConfigController extends BladeController {
|
|||||||
result.put("waybillNo", image.getWaybillNo());
|
result.put("waybillNo", image.getWaybillNo());
|
||||||
result.put("objectKey", image.getObjectKey());
|
result.put("objectKey", image.getObjectKey());
|
||||||
try {
|
try {
|
||||||
result.put("url", minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
|
result.put("url", minioClient().getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
|
||||||
.method(Method.GET).bucket(minioBucketName).object(image.getObjectKey())
|
.method(Method.GET).bucket(minioBucketName).object(image.getObjectKey())
|
||||||
.expiry(1, TimeUnit.HOURS).build()));
|
.expiry(1, TimeUnit.HOURS).build()));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -270,7 +285,7 @@ public class ProcessConfigController extends BladeController {
|
|||||||
result.put("objectKey", image.objectKey());
|
result.put("objectKey", image.objectKey());
|
||||||
result.put("matched", image.matched());
|
result.put("matched", image.matched());
|
||||||
try {
|
try {
|
||||||
result.put("url", minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
|
result.put("url", minioClient().getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
|
||||||
.method(Method.GET).bucket(minioBucketName).object(image.objectKey())
|
.method(Method.GET).bucket(minioBucketName).object(image.objectKey())
|
||||||
.expiry(1, TimeUnit.HOURS).build()));
|
.expiry(1, TimeUnit.HOURS).build()));
|
||||||
} catch (Exception exception) {
|
} catch (Exception exception) {
|
||||||
|
|||||||
+25
-10
@@ -42,6 +42,7 @@ import io.minio.PutObjectArgs;
|
|||||||
import io.minio.RemoveObjectArgs;
|
import io.minio.RemoveObjectArgs;
|
||||||
import io.minio.http.Method;
|
import io.minio.http.Method;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.ObjectProvider;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
@@ -79,14 +80,14 @@ public class VoucherManageServiceImpl extends BaseServiceImpl<VoucherManageMappe
|
|||||||
private final VoucherImageMapper voucherImageMapper;
|
private final VoucherImageMapper voucherImageMapper;
|
||||||
private final VoucherFileMapper voucherFileMapper;
|
private final VoucherFileMapper voucherFileMapper;
|
||||||
private final ApplicationEventPublisher eventPublisher;
|
private final ApplicationEventPublisher eventPublisher;
|
||||||
private final MinioClient minioClient;
|
private final ObjectProvider<MinioClient> minioClientProvider;
|
||||||
private final String minioBucketName;
|
private final String minioBucketName;
|
||||||
private final String minioRootDirectory;
|
private final String minioRootDirectory;
|
||||||
|
|
||||||
public VoucherManageServiceImpl(VoucherWaybillBatchMapper voucherWaybillBatchMapper, IProjectApplyService projectApplyService,
|
public VoucherManageServiceImpl(VoucherWaybillBatchMapper voucherWaybillBatchMapper, IProjectApplyService projectApplyService,
|
||||||
IWaybillService waybillService,
|
IWaybillService waybillService,
|
||||||
VoucherImageMapper voucherImageMapper, VoucherFileMapper voucherFileMapper,
|
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.bucket-name:${minio.bucket-name:}}") String minioBucketName,
|
||||||
@Value("${file.storage.minio.root-directory:${minio.root-directory:}}") String minioRootDirectory) {
|
@Value("${file.storage.minio.root-directory:${minio.root-directory:}}") String minioRootDirectory) {
|
||||||
this.voucherWaybillBatchMapper = voucherWaybillBatchMapper;
|
this.voucherWaybillBatchMapper = voucherWaybillBatchMapper;
|
||||||
@@ -95,7 +96,7 @@ public class VoucherManageServiceImpl extends BaseServiceImpl<VoucherManageMappe
|
|||||||
this.voucherImageMapper = voucherImageMapper;
|
this.voucherImageMapper = voucherImageMapper;
|
||||||
this.voucherFileMapper = voucherFileMapper;
|
this.voucherFileMapper = voucherFileMapper;
|
||||||
this.eventPublisher = eventPublisher;
|
this.eventPublisher = eventPublisher;
|
||||||
this.minioClient = minioClient;
|
this.minioClientProvider = minioClientProvider;
|
||||||
this.minioBucketName = minioBucketName;
|
this.minioBucketName = minioBucketName;
|
||||||
this.minioRootDirectory = minioRootDirectory;
|
this.minioRootDirectory = minioRootDirectory;
|
||||||
}
|
}
|
||||||
@@ -251,7 +252,7 @@ public class VoucherManageServiceImpl extends BaseServiceImpl<VoucherManageMappe
|
|||||||
List<String> candidateObjectKeys = resolvedObjectKey.equals(objectKey)
|
List<String> candidateObjectKeys = resolvedObjectKey.equals(objectKey)
|
||||||
? List.of(objectKey) : List.of(resolvedObjectKey, objectKey);
|
? List.of(objectKey) : List.of(resolvedObjectKey, objectKey);
|
||||||
for (String candidateObjectKey : candidateObjectKeys) {
|
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)) {
|
OutputStream target = Files.newOutputStream(archivePath)) {
|
||||||
source.transferTo(target);
|
source.transferTo(target);
|
||||||
return archivePath;
|
return archivePath;
|
||||||
@@ -427,7 +428,7 @@ public class VoucherManageServiceImpl extends BaseServiceImpl<VoucherManageMappe
|
|||||||
String waybillNo = matchedWaybill == null ? "unmatched" : safePathPart(matchedWaybill.getWaybillNo());
|
String waybillNo = matchedWaybill == null ? "unmatched" : safePathPart(matchedWaybill.getWaybillNo());
|
||||||
String objectKey = buildObjectKey(voucher.getId(), waybillNo, plateNo, entryName);
|
String objectKey = buildObjectKey(voucher.getId(), waybillNo, plateNo, entryName);
|
||||||
String contentType = contentType(fileName);
|
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)
|
.stream(zipInputStream, entry.getSize(), 10 * 1024 * 1024)
|
||||||
.contentType(contentType).build());
|
.contentType(contentType).build());
|
||||||
VoucherFile file = new VoucherFile();
|
VoucherFile file = new VoucherFile();
|
||||||
@@ -490,7 +491,7 @@ public class VoucherManageServiceImpl extends BaseServiceImpl<VoucherManageMappe
|
|||||||
String waybillNo = matchedWaybill == null ? "unmatched" : safePathPart(matchedWaybill.getWaybillNo());
|
String waybillNo = matchedWaybill == null ? "unmatched" : safePathPart(matchedWaybill.getWaybillNo());
|
||||||
String objectKey = buildObjectKey(voucher.getId(), waybillNo, plateNo, entryName);
|
String objectKey = buildObjectKey(voucher.getId(), waybillNo, plateNo, entryName);
|
||||||
String contentType = contentType(fileName);
|
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)
|
.stream(zipInputStream, entry.getSize(), 10 * 1024 * 1024)
|
||||||
.contentType(contentType).build());
|
.contentType(contentType).build());
|
||||||
VoucherFile file = new VoucherFile();
|
VoucherFile file = new VoucherFile();
|
||||||
@@ -537,7 +538,7 @@ public class VoucherManageServiceImpl extends BaseServiceImpl<VoucherManageMappe
|
|||||||
String entryName = plateNo + "/" + System.currentTimeMillis() + "_" + fileName;
|
String entryName = plateNo + "/" + System.currentTimeMillis() + "_" + fileName;
|
||||||
String objectKey = buildObjectKey(voucher.getId(), matchedWaybill == null ? "unmatched" : safePathPart(matchedWaybill.getWaybillNo()), plateNo, entryName);
|
String objectKey = buildObjectKey(voucher.getId(), matchedWaybill == null ? "unmatched" : safePathPart(matchedWaybill.getWaybillNo()), plateNo, entryName);
|
||||||
try (InputStream inputStream = imageFile.getInputStream()) {
|
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)
|
.stream(inputStream, imageFile.getSize(), 10 * 1024 * 1024)
|
||||||
.contentType(Func.isEmpty(imageFile.getContentType()) ? contentType(fileName) : imageFile.getContentType()).build());
|
.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;
|
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
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
@@ -838,7 +839,7 @@ public class VoucherManageServiceImpl extends BaseServiceImpl<VoucherManageMappe
|
|||||||
String objectKey = buildObjectKey(voucher.getId(), waybillNo,
|
String objectKey = buildObjectKey(voucher.getId(), waybillNo,
|
||||||
Func.isEmpty(plateNo) ? "root" : plateNo, entryName);
|
Func.isEmpty(plateNo) ? "root" : plateNo, entryName);
|
||||||
String contentType = contentType(fileName);
|
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)
|
.stream(zipInputStream, entry.getSize(), 10 * 1024 * 1024)
|
||||||
.contentType(contentType).build());
|
.contentType(contentType).build());
|
||||||
VoucherFile file = new VoucherFile();
|
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) {
|
private String buildObjectKey(Long voucherId, String waybillNo, String plateNo, String entryName) {
|
||||||
String objectKey = voucherId + "/" + waybillNo + "/" + safePathPart(plateNo) + "/" + safeArchivePath(entryName);
|
String objectKey = voucherId + "/" + waybillNo + "/" + safePathPart(plateNo) + "/" + safeArchivePath(entryName);
|
||||||
if (Func.isEmpty(minioRootDirectory)) {
|
if (Func.isEmpty(minioRootDirectory)) {
|
||||||
@@ -1127,7 +1142,7 @@ public class VoucherManageServiceImpl extends BaseServiceImpl<VoucherManageMappe
|
|||||||
|
|
||||||
private String buildVoucherFileUrl(Long fileId, String objectKey) {
|
private String buildVoucherFileUrl(Long fileId, String objectKey) {
|
||||||
try {
|
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());
|
.method(Method.GET).bucket(minioBucketName).object(objectKey).expiry(1, java.util.concurrent.TimeUnit.HOURS).build());
|
||||||
} catch (Exception exception) {
|
} catch (Exception exception) {
|
||||||
log.warn("生成凭证文件预览地址失败 voucherFileId={}, objectKey={}", fileId, objectKey, exception);
|
log.warn("生成凭证文件预览地址失败 voucherFileId={}, objectKey={}", fileId, objectKey, exception);
|
||||||
|
|||||||
Reference in New Issue
Block a user