diff --git a/common/src/main/java/com/sdm/common/common/ThreadLocalContext.java b/common/src/main/java/com/sdm/common/common/ThreadLocalContext.java index 7aad77b3..0965b8e5 100644 --- a/common/src/main/java/com/sdm/common/common/ThreadLocalContext.java +++ b/common/src/main/java/com/sdm/common/common/ThreadLocalContext.java @@ -84,6 +84,10 @@ public class ThreadLocalContext { current.get().setTenantId(tenantId); } + public static void clear() { + current.remove(); + } + // userName public static String getUserName() { return current.get().getUserName(); diff --git a/system/src/main/java/com/sdm/system/model/req/tenant/TenantListReq.java b/common/src/main/java/com/sdm/common/entity/req/data/TenantListReq.java similarity index 94% rename from system/src/main/java/com/sdm/system/model/req/tenant/TenantListReq.java rename to common/src/main/java/com/sdm/common/entity/req/data/TenantListReq.java index ff6ab0f8..000e722e 100644 --- a/system/src/main/java/com/sdm/system/model/req/tenant/TenantListReq.java +++ b/common/src/main/java/com/sdm/common/entity/req/data/TenantListReq.java @@ -1,7 +1,6 @@ -package com.sdm.system.model.req.tenant; +package com.sdm.common.entity.req.data; import com.fasterxml.jackson.annotation.JsonFormat; -import com.sdm.system.model.req.BaseReq; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/system/src/main/java/com/sdm/system/model/resp/TenantResp.java b/common/src/main/java/com/sdm/common/entity/resp/system/TenantResp.java similarity index 97% rename from system/src/main/java/com/sdm/system/model/resp/TenantResp.java rename to common/src/main/java/com/sdm/common/entity/resp/system/TenantResp.java index e244696b..ff0418f1 100644 --- a/system/src/main/java/com/sdm/system/model/resp/TenantResp.java +++ b/common/src/main/java/com/sdm/common/entity/resp/system/TenantResp.java @@ -1,4 +1,4 @@ -package com.sdm.system.model.resp; +package com.sdm.common.entity.resp.system; import com.fasterxml.jackson.annotation.JsonFormat; import io.swagger.annotations.ApiModel; diff --git a/common/src/main/java/com/sdm/common/feign/impl/system/SysTenantFeignClientImpl.java b/common/src/main/java/com/sdm/common/feign/impl/system/SysTenantFeignClientImpl.java new file mode 100644 index 00000000..6f99feaa --- /dev/null +++ b/common/src/main/java/com/sdm/common/feign/impl/system/SysTenantFeignClientImpl.java @@ -0,0 +1,34 @@ +package com.sdm.common.feign.impl.system; + +import com.sdm.common.common.SdmResponse; +import com.sdm.common.entity.req.data.TenantListReq; +import com.sdm.common.entity.resp.PageDataResp; +import com.sdm.common.entity.resp.system.TenantResp; +import com.sdm.common.feign.inter.system.ISysTenantFeignClient; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ObjectUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Slf4j +@Component +public class SysTenantFeignClientImpl implements ISysTenantFeignClient { + @Autowired + ISysTenantFeignClient sysTenantClient; + + public SdmResponse>> listTenant(TenantListReq tenant) { + SdmResponse>> sdmResponse; + try { + sdmResponse = sysTenantClient.listTenant(tenant); + if (!sdmResponse.isSuccess() || ObjectUtils.isEmpty(sdmResponse.getData())) { + return SdmResponse.failed("查询租户信息失败"); + } + } catch (Exception e) { + log.error("查询租户信息失败", e); + return SdmResponse.failed("查询租户信息 户信息失败"); + } + return sdmResponse; + } +} diff --git a/common/src/main/java/com/sdm/common/feign/inter/system/ISysTenantFeignClient.java b/common/src/main/java/com/sdm/common/feign/inter/system/ISysTenantFeignClient.java new file mode 100644 index 00000000..feade18f --- /dev/null +++ b/common/src/main/java/com/sdm/common/feign/inter/system/ISysTenantFeignClient.java @@ -0,0 +1,19 @@ +package com.sdm.common.feign.inter.system; + +import com.sdm.common.common.SdmResponse; +import com.sdm.common.entity.req.data.TenantListReq; +import com.sdm.common.entity.resp.PageDataResp; +import com.sdm.common.entity.resp.system.TenantResp; +import io.swagger.v3.oas.annotations.Operation; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +import java.util.List; + +@FeignClient(name = "tenant",contextId = "sysTenantFeignClient") +public interface ISysTenantFeignClient { + @PostMapping("/tenant/list") + SdmResponse>> listTenant(@RequestBody TenantListReq tenant); +} diff --git a/data/src/main/java/com/sdm/data/controller/DataFileController.java b/data/src/main/java/com/sdm/data/controller/DataFileController.java index 46f81f57..b69abeca 100644 --- a/data/src/main/java/com/sdm/data/controller/DataFileController.java +++ b/data/src/main/java/com/sdm/data/controller/DataFileController.java @@ -538,4 +538,20 @@ public class DataFileController implements IDataFeignClient { return ResponseEntity.ok("SUCCESS:" + targetFile.toString()); } + /** + * 供 System 服务调用:新租户初始化 + */ + @PostMapping("/initNewTenant") + public SdmResponse initNewTenant(@RequestParam Long tenantId) { + if (tenantId == null) { + return SdmResponse.failed("TenantId 不能为空"); + } + try { + boolean success = IDataFileService.initTenantData(tenantId); + return success ? SdmResponse.success() : SdmResponse.failed("初始化失败"); + } catch (Exception e) { + return SdmResponse.failed("初始化异常: " + e.getMessage()); + } + } + } \ No newline at end of file diff --git a/data/src/main/java/com/sdm/data/schedule/InitSystemDirectory.java b/data/src/main/java/com/sdm/data/schedule/InitSystemDirectory.java index 794a1b8e..d43cbe31 100644 --- a/data/src/main/java/com/sdm/data/schedule/InitSystemDirectory.java +++ b/data/src/main/java/com/sdm/data/schedule/InitSystemDirectory.java @@ -19,27 +19,12 @@ import javax.annotation.Resource; @Component public class InitSystemDirectory { - @Resource - private SpringUtils springUtils; - @Autowired private IDataFileService IDataFileService; @Resource private SystemMapper systemMapper; - @PostConstruct public void postConstruct() { - /* List companyList = systemMapper.getCompanyList(0); - for (SysCompany company : companyList) { - log.info(company.getCompany() + "初始化系统文件..."); - boolean result = IDataService.initSystemDirectory(company.getCompany()); - if (result) { - systemMapper.updateCompanyInitDir(company.getCompany(), 1); - } - log.info(company.getCompany() + "初始化系统文件完成!"); - }*/ - - IDataFileService.initSystemDirectory(null); } } \ No newline at end of file diff --git a/data/src/main/java/com/sdm/data/service/IDataFileService.java b/data/src/main/java/com/sdm/data/service/IDataFileService.java index 56d61157..bb8f404a 100644 --- a/data/src/main/java/com/sdm/data/service/IDataFileService.java +++ b/data/src/main/java/com/sdm/data/service/IDataFileService.java @@ -140,6 +140,10 @@ public interface IDataFileService { boolean initSystemDirectory(String company); + default boolean initTenantData(Long tenantId){ + return true; + } + /** * 下载文件 diff --git a/data/src/main/java/com/sdm/data/service/IMinioService.java b/data/src/main/java/com/sdm/data/service/IMinioService.java index 7ddeef5d..82f7292d 100644 --- a/data/src/main/java/com/sdm/data/service/IMinioService.java +++ b/data/src/main/java/com/sdm/data/service/IMinioService.java @@ -8,28 +8,22 @@ import org.springframework.web.multipart.MultipartFile; import java.io.InputStream; import java.util.List; import java.util.Map; +import java.util.Set; public interface IMinioService { - /** - * 获取MinIO的存储桶名称 - * - * @return 存储桶名称 - */ - public String getBucketName(); - + + Set preloadBucketCache(); + + void createBucketIfAbsent(String bucketName); + /** * 获取MinIO的保密业务存储桶名称 * * @return 保密业务存储桶名称 */ - public String getSecretBusinessBucketName(); + String getSecretBusinessBucketName(); - /** - * 创建目录 - * - * @param objectKey 绝对路径目录名 - */ - public void createDirectoryByObjectKey(String objectKey); + String getCurrentTenantBucketName(); /** * 创建目录 @@ -37,52 +31,31 @@ public interface IMinioService { * @param objectKey 绝对路径目录名 * @param bucketName 桶名称 */ - public void createDirectoryByObjectKey(String objectKey, String bucketName); + void createDirectoryByObjectKey(String objectKey, String bucketName); /** * 递归删除指定目录下的所有对象。 * * @param directoryName 目录名称(objectKey) + * @param bucketName 桶名称 */ - public void deleteDirectoryRecursively(String directoryName); - - public void deleteDirectoryRecursively2(String directoryName,String bucketName); + void deleteDirectoryRecursively(String directoryName, String bucketName); /** * 递归获取目录下的所有对象。 * * @param directoryName 目录名称(objectKey) + * @param bucketName */ - public Iterable> listObjects(String directoryName); - /** - * 递归删除指定目录下的所有对象。 - * - * @param directoryName 目录名称(objectKey) - * @param bucketName 桶名称 - */ - public void deleteDirectoryRecursively(String directoryName, String bucketName); - - /** - * 从MinIO删除文件 - * @param minioObjectKey 文件名(objectKey) - */ - public void deleteFile(String minioObjectKey); + Iterable> listObjects(String directoryName, String bucketName); /** * 从MinIO删除文件 * @param minioObjectKey 文件名(objectKey) * @param bucketName 桶名称 */ - public void deleteFile(String minioObjectKey, String bucketName); + void deleteFile(String minioObjectKey, String bucketName); - /** - * 上传文件到MinIO - * @param file 文件对象 - * @param objectName 文件名(objectKey) - * @param tags 文件标签 - */ - public void uploadFile(MultipartFile file, String objectName, Map tags); - /** * 上传文件到MinIO * @param file 文件对象 @@ -90,16 +63,7 @@ public interface IMinioService { * @param tags 文件标签 * @param bucketName 桶名称 */ - public void uploadFile(MultipartFile file, String objectName, Map tags, String bucketName); - - /** - * 重命名MinIO中的文件 - * - * @param oldObjectName 原文件名(objectKey) - * @param newObjectName 新文件名(objectKey) - * @throws Exception 异常信息 - */ - public void renameFile(String oldObjectName, String newObjectName); + void uploadFile(MultipartFile file, String objectName, Map tags, String bucketName); /** * 重命名MinIO中的文件 @@ -109,21 +73,9 @@ public interface IMinioService { * @param bucketName 桶名称 * @throws Exception 异常信息 */ - public void renameFile(String oldObjectName, String newObjectName, String bucketName); + void renameFile(String oldObjectName, String newObjectName, String bucketName); - /** - * 从MinIO下载文件 - * @param objectName 文件名(objectKey) - */ - byte[] downloadFile(String objectName) throws Exception; - - /** - * 从MinIO下载文件到response - * @param objectName 文件名(objectKey) - */ - void downloadFile(String objectName, HttpServletResponse response,String encodedFileName,Boolean preview,String contentType) throws Exception; - /** * 从MinIO下载文件 * @param objectName 文件名(objectKey) @@ -131,24 +83,24 @@ public interface IMinioService { */ byte[] downloadFile(String objectName, String bucketName) throws Exception; - InputStream getMinioInputStream(String objectName); + /** + * 从MinIO下载文件到response + * @param objectName 文件名(objectKey) + */ + void downloadFile(String objectName, String bucketName, HttpServletResponse response,String encodedFileName,Boolean preview, String contentType) throws Exception; + InputStream getMinioInputStream(String objectName, String bucketName); - /** - * 根据标签查询文件 - */ - List queryFilesByTags(Map tags) throws Exception; - /** * 根据标签查询文件 */ List queryFilesByTags(Map tags, String bucketName) throws Exception; - String getMinioPresignedUrl(String objectKey); String getMinioPresignedUrl(String objectKey, String bucketName); Boolean chunkUpload(String bucketName, MultipartFile file, String fileName,Map tags); Boolean merge(String tempBucketName,String tempFilePath,String mergeBucketName,String fileName); + } \ No newline at end of file diff --git a/data/src/main/java/com/sdm/data/service/impl/DataAnalysisServiceImpl.java b/data/src/main/java/com/sdm/data/service/impl/DataAnalysisServiceImpl.java index 608818af..4ad71db5 100644 --- a/data/src/main/java/com/sdm/data/service/impl/DataAnalysisServiceImpl.java +++ b/data/src/main/java/com/sdm/data/service/impl/DataAnalysisServiceImpl.java @@ -102,7 +102,7 @@ public class DataAnalysisServiceImpl implements IDataAnalysisService { for (Long fileId : fileIds) { FileMetadataInfo csvFile = fileMetadataInfoService.getById(fileId); String objectKey = csvFile.getObjectKey(); - byte[] fileBytes = MinIOService.downloadFile(objectKey); + byte[] fileBytes = MinIOService.downloadFile(objectKey,csvFile.getBucketName()); // 将字节数组转换为 BufferedReader 以便读取 CSV 内容 ByteArrayInputStream bis = new ByteArrayInputStream(fileBytes); diff --git a/data/src/main/java/com/sdm/data/service/impl/MinioFileIDataFileServiceImpl.java b/data/src/main/java/com/sdm/data/service/impl/MinioFileIDataFileServiceImpl.java index 83555923..e4008c80 100644 --- a/data/src/main/java/com/sdm/data/service/impl/MinioFileIDataFileServiceImpl.java +++ b/data/src/main/java/com/sdm/data/service/impl/MinioFileIDataFileServiceImpl.java @@ -100,9 +100,6 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { NumberConstants.FOUR ); - @Value("${fileSystem.chunkBucket:spdm}") - private String chunkBucket; - @Autowired private IFileMetadataInfoService fileMetadataInfoService; @@ -184,6 +181,774 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { } } + + /** + * 创建根目录 + * + * @param req 创建目录请求 + * @return 创建结果 + */ + public SdmResponse createRootDir(CreateDirReq req) { + Long tenantId = ThreadLocalContext.getTenantId(); + if(ObjectUtils.isEmpty(req.getDirType())){ + log.error("请选择目录类型:1 知识库文件夹,2 项目节点文件夹,3 头像库文件夹,4 仿真参数库文件夹,5 训练模型文件夹"); + return SdmResponse.failed("请选择目录类型:1 知识库文件夹,2 项目节点文件夹,3 头像库文件夹,4 仿真参数库文件夹,5 训练模型文件夹"); + } + + DirTypeEnum dirTypeByValue = DirTypeEnum.getDirTypeByValue(req.getDirType()); + if (dirTypeByValue == null) { + log.error("请选择正确的目录类型:1 知识库文件夹,2 项目节点文件夹,3 头像库文件夹,4 仿真参数库文件夹,5 训练模型文件夹"); + return SdmResponse.failed("请选择正确的目录类型:1 知识库文件夹,2 项目节点文件夹,3 头像库文件夹,4 仿真参数库文件夹,5 训练模型文件夹"); + } + + // 先检查根目录是否已存在 + String rootDirMinioObjectKey = getDirMinioObjectKey(dirTypeByValue.getDirName()); + Optional fileMetadataInfoByObjectKey = getFileMetadataInfoByObjectKey(rootDirMinioObjectKey, null); + // 检查目录是否已存在 + if (!fileMetadataInfoByObjectKey.isPresent()) { + log.error("知识库、项目根目录不存在,等待initSystemDirectory 初始化完成"); + return SdmResponse.failed("知识库、项目根目录不存在,等待initSystemDirectory 初始化完成"); + } + + // 获取根目录的 id + FileMetadataInfo rootDirMetadataInfo = fileMetadataInfoByObjectKey.get(); + Long rootDirId = rootDirMetadataInfo.getId(); + + String dirName = req.getDirName(); + String dirMinioObjectKey = getDirMinioObjectKey(rootDirMetadataInfo.getObjectKey() + dirName); + String uuId = req.getUuId(); + String uuIdOwnType = req.getUuIdOwnType(); + + try { + // 检查目录是否已存在 + if (getFileMetadataInfoByObjectKey(dirMinioObjectKey,tenantId).isPresent()) { + log.error("目录已存在"); + return SdmResponse.failed("目录已存在"); + } + + // 在MinIO中创建目录 + minioService.createDirectoryByObjectKey(dirMinioObjectKey,null); + + // 创建目录元数据并保存到数据库 + FileMetadataInfo dirInfo = createDirectoryMetadata(dirMinioObjectKey, dirName, false, rootDirId, uuId, uuIdOwnType, req.getDirType()); + fileMetadataInfoService.save(dirInfo); + + // 创建默认权限记录 + createDirectoryPermission(dirInfo.getId()); + + log.info("根目录创建成功"); + return SdmResponse.success(dirInfo.getId()); + } catch (Exception dbException) { + log.error("创建根目录失败", dbException); + + // 补偿操作:如果 MinIO 创建成功但数据库失败,删除 MinIO 目录 + minioService.deleteFile(dirMinioObjectKey, null); + + // 重新抛出异常,确保事务回滚 + throw new RuntimeException("创建根目录失败: " + dbException.getMessage(), dbException); + } + } + + + /** + * 创建子目录 + * + * @param req 创建目录请求 + * @return 创建结果 + */ + public SdmResponse createChildDir(CreateDirReq req) { + Long tenantId = ThreadLocalContext.getTenantId(); + Long parDirId = null; + if (req.getParentUuId() != null) { + FileMetadataInfo node = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getRelatedResourceUuid, req.getParentUuId()).one(); + if (ObjectUtils.isEmpty(node)) { + log.error("上级节点目录不存在"); + return SdmResponse.failed("上级节点目录不存在"); + } + parDirId = node.getId(); + } else { + parDirId = req.getParDirId(); + } + + FileMetadataInfo parDirInfo = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getId, parDirId).one(); + if (ObjectUtils.isEmpty(parDirInfo)) { + log.error("上级目录不存在"); + return SdmResponse.failed("上级目录不存在"); + } + + // 构造子目录完整路径 + String childDirMinioObjectKey = getDirMinioObjectKey(parDirInfo.getObjectKey() + req.getDirName()); + + // 检查子目录是否已存在 + if (getFileMetadataInfoByObjectKey(childDirMinioObjectKey, tenantId).isPresent()) { + log.error("目录已存在"); + return SdmResponse.failed("目录已存在"); + } + + boolean hasWritePermission = fileUserPermissionService.hasFilePermission(parDirId, ThreadLocalContext.getUserId(), FilePermissionEnum.WRITE); + if (!hasWritePermission) { + log.error("没有写入权限"); + return SdmResponse.failed("没有写入权限"); + } + + try { + // 在MinIO中创建子目录 + minioService.createDirectoryByObjectKey(childDirMinioObjectKey,null); + + // 创建子目录元数据并保存到数据库 + FileMetadataInfo dirInfo = createDirectoryMetadata(childDirMinioObjectKey, req.getDirName(), false, parDirId, req.getUuId(), req.getUuIdOwnType(), parDirInfo.getDirType()); + fileMetadataInfoService.save(dirInfo); + + // 创建默认权限记录 + createDirectoryPermission(dirInfo.getId()); + + log.info("子目录创建成功"); + return SdmResponse.success(dirInfo.getId()); + + } catch (Exception dbException) { + log.error("创建子目录失败", dbException); + + // 补偿操作:如果 MinIO 创建成功但数据库失败,删除 MinIO 目录 + minioService.deleteFile(childDirMinioObjectKey, null); + + // 重新抛出异常,确保事务回滚 + throw new RuntimeException("创建子目录失败: " + dbException.getMessage(), dbException); + } + } + + + /** + * 创建目录元数据 + * + * @param minioObjectKey 目录路径 + * @param originalName 原始名称 + * @param isRoot 是否为根目录 + * @param parentId 父目录ID(根目录为null) + * @param uuid 节点ID + * @param uuIdOwnType + * @return 目录元数据对象 + */ + private FileMetadataInfo createDirectoryMetadata(String minioObjectKey, String originalName, + boolean isRoot, Long parentId, String uuid, + String uuIdOwnType, Integer dirType) { + FileMetadataInfo dirInfo = new FileMetadataInfo(); + dirInfo.setObjectKey(minioObjectKey); + dirInfo.setBucketName(minioService.getCurrentTenantBucketName()); + dirInfo.setOriginalName(originalName); + dirInfo.setDataType(DataTypeEnum.DIRECTORY.getValue()); + dirInfo.setIsRoot(isRoot); + dirInfo.setDirType(dirType); + dirInfo.setCreatorId(ThreadLocalContext.getUserId()); + dirInfo.setTenantId(ThreadLocalContext.getTenantId()); + if (!isRoot && parentId != null) { + dirInfo.setParentId(parentId); + } + if (uuid != null) { + dirInfo.setRelatedResourceUuid(uuid); + } + if (uuIdOwnType != null) { + dirInfo.setRelatedResourceUuidOwnType(uuIdOwnType); + } + return dirInfo; + } + + /** + * 创建文件元数据信息 + * + * @param filePath 文件路径 + * @param fileName 文件名 + * @param fileType 文件类型 + * @param projectId 项目ID + * @param analysisDirectionId 分析方向ID + * @param remarks 备注信息 + * @param parentId 父目录ID + * @param fileSize 文件大小 + * @return 创建后的文件元数据信息 + */ + private FileMetadataInfo createFileMetadata(String filePath, String fileName, Integer fileType + , String projectId, String analysisDirectionId, String remarks, Long parentId, Long fileSize + ) { + FileMetadataInfo fileInfo = new FileMetadataInfo(); + fileInfo.setObjectKey(filePath); + fileInfo.setBucketName(minioService.getCurrentTenantBucketName()); + fileInfo.setOriginalName(fileName); + fileInfo.setProjectId(projectId); + fileInfo.setAnalysisDirectionId(analysisDirectionId); + fileInfo.setRemarks(remarks); + fileInfo.setDataType(DataTypeEnum.FILE.getValue()); + fileInfo.setFileType(fileType); + fileInfo.setParentId(parentId); + fileInfo.setIsRoot(false); + fileInfo.setCreatorId(ThreadLocalContext.getUserId()); + fileInfo.setTenantId(ThreadLocalContext.getTenantId()); + fileInfo.setFileSize(fileSize); + return fileInfo; + } + + /** + * 创建目录权限记录 + * + * @param directoryId 目录ID + */ + private void createDirectoryPermission(Long directoryId) { + FileUserPermission permission = new FileUserPermission(); + permission.setTFilemetaId(directoryId); + permission.setPermission(FilePermissionEnum.ALL.getValue()); + permission.setUserId(ThreadLocalContext.getUserId()); + permission.setTenantId(ThreadLocalContext.getTenantId()); + fileUserPermissionService.save(permission); + } + + /** + * 删除文件夹权限 + * @param directoryId + */ + private void deleteDirectoryPermission(Long directoryId) { + fileUserPermissionService.lambdaUpdate().eq(FileUserPermission::getTFilemetaId, directoryId).remove(); + } + + /** + * 创建文件权限信息 + * + * @param fileId 文件ID + * @return + */ + private void createFilePermission(Long fileId) { + FileUserPermission permission = new FileUserPermission(); + permission.setTFilemetaId(fileId); + permission.setPermission(FilePermissionEnum.ALL.getValue()); + permission.setUserId(ThreadLocalContext.getUserId()); + permission.setTenantId(ThreadLocalContext.getTenantId()); + fileUserPermissionService.save(permission); + } + + /** + * 删除文件权限 + * @param fileId + * @return + */ + private void deleteFilePermission(Long fileId) { + fileUserPermissionService.lambdaUpdate().eq(FileUserPermission::getTFilemetaId, fileId).remove(); + } + + private Optional getFileMetadataInfoByObjectKey(String minioObjectKey, Long tenantId) { + if (!StringUtils.hasText(minioObjectKey)) { + return Optional.empty(); + } + String currentTenantBucketName = minioService.getCurrentTenantBucketName(); + minioObjectKey = getDirMinioObjectKey(minioObjectKey); + return Optional.ofNullable(fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getBucketName, currentTenantBucketName).eq(FileMetadataInfo::getObjectKey, minioObjectKey).eq(ObjectUtils.isNotEmpty(tenantId), FileMetadataInfo::getTenantId, tenantId).one()); + } + + @NotNull + private static String getDirMinioObjectKey(String dirPath) { + if (dirPath.startsWith("/")) { + dirPath = dirPath.substring(1); + } + if (!dirPath.endsWith("/")) { + dirPath += "/"; + } + return dirPath; + } + + private static String getFileMinioObjectKey(String filePath) { + if (filePath.startsWith("/")) { + filePath = filePath.substring(1); + } + + if (filePath.endsWith("/")) { + filePath = filePath.substring(0, filePath.length() - 1); + } + return filePath; + + } + + @Override + @Transactional(rollbackFor = Exception.class) + public SdmResponse delDir(DelDirReq req) { + try { + FileMetadataInfo deleteDirMetadataInfo = null; + if (req.getDelUuid() != null) { + deleteDirMetadataInfo = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getRelatedResourceUuid, req.getDelUuid()).one(); + } else { + deleteDirMetadataInfo = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getId, req.getDelDirId()).one(); + } + + if (ObjectUtils.isEmpty(deleteDirMetadataInfo)) { + return SdmResponse.failed("目录不存在"); + } + + String dirMinioObjectKey = deleteDirMetadataInfo.getObjectKey(); + Long deleteDirId = deleteDirMetadataInfo.getId(); + + boolean hasDeletePermission = fileUserPermissionService.hasFilePermission(deleteDirId, ThreadLocalContext.getUserId(), FilePermissionEnum.DELETE); + if (!hasDeletePermission) { + return SdmResponse.failed("没有删除权限"); + } + + // 数据库批量删除文件信息 + LambdaQueryWrapper fileMetaDataQuery = new LambdaQueryWrapper<>(); + fileMetaDataQuery.select(FileStorage::getFileId).eq(FileStorage::getDirId, deleteDirId); + List deleteFileIdList = fileStorageService.list(fileMetaDataQuery).stream().map(FileStorage::getFileId).collect(Collectors.toList()); + + deleteFileIdList.add(deleteDirId); + + if(ObjectUtils.isNotEmpty(deleteFileIdList)){ + fileMetadataInfoService.removeByIds(deleteFileIdList); + fileMetadataExtensionService.lambdaUpdate().in(FileMetadataExtension::getTFilemetaId, deleteFileIdList).remove(); + fileStorageService.lambdaUpdate().in(FileStorage::getFileId, deleteFileIdList).remove(); + fileUserPermissionService.lambdaUpdate().in(FileUserPermission::getTFilemetaId, deleteFileIdList).remove(); + fileSimulationMappingService.lambdaUpdate().in(FileSimulationMapping::getFileId, deleteFileIdList).remove(); + } + + fileStorageService.lambdaUpdate().eq(FileStorage::getDirId, deleteDirId).remove(); + //删除文件夹权限 + deleteDirectoryPermission(deleteDirId); + + + // minio文件系统批量删除文件 + minioService.deleteDirectoryRecursively(dirMinioObjectKey,deleteDirMetadataInfo.getBucketName()); + log.info("删除目录文件元数据和Minio文件成功: " + dirMinioObjectKey); + + + + // 子文件夹一并删除 + List childDirs = fileMetadataInfoService.lambdaQuery() + .eq(FileMetadataInfo::getParentId, deleteDirId) + .eq(FileMetadataInfo::getDataType, DataTypeEnum.DIRECTORY.getValue()) + .list(); + if (CollectionUtils.isNotEmpty(childDirs)) { + for (FileMetadataInfo childDir : childDirs) { + DelDirReq delDirReq = new DelDirReq(); + delDirReq.setDelDirId(childDir.getId()); + delDir(delDirReq); + } + } + } catch (Exception e) { + log.error("删除目录失败", e); + // 重新抛出异常,确保事务回滚 + throw new RuntimeException("删除目录失败: " + e.getMessage(), e); + } + return SdmResponse.success("删除成功"); + } + + @Override + public boolean reverseDeleteDataFiles(File file) { + return false; + } + + @Override + @Transactional(rollbackFor = Exception.class) + @PermissionCheckAspect.FilePermissionCheck(value = FilePermissionEnum.DELETE, fileIdExpression = "#req.delFileId") + public SdmResponse delFile(DelFileReq req) { + try { + Long delFileId = req.getDelFileId(); + FileMetadataInfo deleteFileMetadataInfo = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getId, delFileId).one(); + if (ObjectUtils.isEmpty(deleteFileMetadataInfo)) { + return SdmResponse.failed("文件不存在"); + } + + // 文件夹 + FileMetadataInfo dirMetadataInfo = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getId, deleteFileMetadataInfo.getParentId()).eq(FileMetadataInfo::getDataType, DataTypeEnum.DIRECTORY.getValue()).one(); + + String deleteFileMinioObejctKey = deleteFileMetadataInfo.getObjectKey(); + + /*boolean hasDeletePermission = fileUserPermissionService.hasFilePermission(deleteFileMetadataInfo.getId(), ThreadLocalContext.getUserId(), FilePermissionEnum.DELETE); + if (!hasDeletePermission) { + return SdmResponse.failed("没有删除权限"); + }*/ + + // 知识库 + if(dirMetadataInfo!=null&&Objects.equals(DirTypeEnum.KNOWLEDGE_BASE_DIR.getValue(), dirMetadataInfo.getDirType())){ + // 发送审批电子流,成功继续西面操作,失败直接返回 + // String templateId, String templateName,String approveContents,int approveAction:1:新增 2:修改 3:删除 + String approveContents = getApproveContents(List.of(delFileId), "知识库文件删除", NumberConstants.THREE, List.of(deleteFileMetadataInfo), null); + Pair approvePair = launchFileDataApprove(req.getTemplateId(), req.getTemplateName(), approveContents, NumberConstants.THREE, ApproveTypeEnum.KNOWLEDGE_APPROVE); + if(!approvePair.getLeft()|| org.apache.commons.lang3.StringUtils.isBlank(approvePair.getRight())) { + log.error("delFile approveInit failed, params :{}", JSONObject.toJSONString(req)); + throw new RuntimeException("文件删除,创建审批流失败,cidFlowId:"+approvePair.getRight()); + } + deleteFileMetadataInfo.setApprovalStatus(ApprovalFileDataStatusEnum.PENDING.getKey()); + deleteFileMetadataInfo.setApproveType(ApproveFileDataTypeEnum.DELETE_REVIEWING.getCode()); + deleteFileMetadataInfo.setCidFlowId(approvePair.getRight()); + fileMetadataInfoService.updateById(deleteFileMetadataInfo); + return SdmResponse.success("操作成功,删除文件待审批"); + }else{ + // 删除MinIO文件 + minioService.deleteFile(deleteFileMetadataInfo.getObjectKey(), deleteFileMetadataInfo.getBucketName()); + // 删除数据库记录 + fileMetadataInfoService.removeById(deleteFileMetadataInfo.getId()); + fileStorageService.remove(new LambdaQueryWrapper().eq(FileStorage::getFileId, deleteFileMetadataInfo.getId())); + fileMetadataExtensionService.remove(new LambdaQueryWrapper().eq(FileMetadataExtension::getTFilemetaId, deleteFileMetadataInfo.getId())); + fileUserPermissionService.remove(new LambdaQueryWrapper().eq(FileUserPermission::getTFilemetaId, deleteFileMetadataInfo.getId())); + return SdmResponse.success("操作成功"); + } + } catch (Exception e) { + log.error("删除文件失败", e); + throw new RuntimeException("删除文件失败: " + e.getMessage(), e); + } + } + + @Override + public SdmResponse fileSearch(FileSearchReq minioFileSearchReq) { + QueryBigFileReq queryBigFileReq = new QueryBigFileReq(); + + FileMetadataInfo fileMetadataInfo = null; + if (ObjectUtils.isNotEmpty(minioFileSearchReq.getParentUuid())) { + // 项目节点下搜索文件 + fileMetadataInfo = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getRelatedResourceUuid, minioFileSearchReq.getParentUuid()).one(); + } else if (ObjectUtils.isNotEmpty(minioFileSearchReq.getParentDirId())) { + // 知识库的文件查询 + fileMetadataInfo = fileMetadataInfoService.getById(minioFileSearchReq.getParentDirId()); + } + + BeanUtils.copyProperties(minioFileSearchReq, queryBigFileReq); + if (ObjectUtils.isNotEmpty(fileMetadataInfo)) { + queryBigFileReq.setDirId(fileMetadataInfo.getId()); + queryBigFileReq.setCurrent(minioFileSearchReq.getCurrent()); + queryBigFileReq.setSize(minioFileSearchReq.getSize()); + } + // 这里是知识库文件:排除新增在审批的文件 + queryBigFileReq.setApproveTypeList(fileDatdList); + queryBigFileReq.setIsLatest(true); + + List creatorIds = org.apache.commons.lang3.StringUtils.isBlank(minioFileSearchReq.getUploadUserId()) + ? new ArrayList<>() + : Arrays.stream(minioFileSearchReq.getUploadUserId().split(",")) + .filter(org.apache.commons.lang3.StringUtils::isNotBlank) + .map(Long::valueOf) + .collect(Collectors.toList()); + queryBigFileReq.setUploadUserId(creatorIds); + + List fileIdList =dataStorageAnalysis.getListBigFileId(queryBigFileReq); + if(CollectionUtils.isEmpty(fileIdList)){ + return SdmResponse.success(); + } + List files = fileMetadataInfoService + .lambdaQuery() + .in(FileMetadataInfo::getId, fileIdList) + .list(); + setCreatorNames(files); + setCidInfos(files); + setProjectName(files); + setAnalysisDirectionName(files); + setSimulationPoolAndTaskInfo(files); + PageInfo page = new PageInfo<>(files); + return PageUtils.getJsonObjectSdmResponse(files, page); + } + + @Override + public SdmResponse fileSearchStop() { + return null; + } + + @Override + public SdmResponse getFileBaseInfo(GetFileBaseInfoReq req) { + FileMetadataInfo fileMetadataInfo = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getId, req.getFileId()).one(); + if (fileMetadataInfo == null) { + return SdmResponse.failed("文件不存在"); + } + FileMetadataInfoResp fileMetadataInfoResp = new FileMetadataInfoResp(); + BeanUtils.copyProperties(fileMetadataInfo, fileMetadataInfoResp); + + return SdmResponse.success(fileMetadataInfoResp); + } + + @Override + public SdmResponse>> queryDir(QueryDirReq req) { + Long parentId; + if (ObjectUtils.isNotEmpty(req.getFileId())) { + // 知识库的文件查询 + parentId = req.getFileId(); + } else if (ObjectUtils.isNotEmpty(req.getUuid())) { + // 项目节点文件查询 + FileMetadataInfo nodeMetadataInfo = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getRelatedResourceUuid, req.getUuid()).one(); + if (nodeMetadataInfo == null) { + // 项目节点未创建文件夹,返回空 + return SdmResponse.success(); + } + parentId = nodeMetadataInfo.getId(); + } else { + return SdmResponse.success(); + } + List creatorIds = org.apache.commons.lang3.StringUtils.isBlank(req.getUploadUserId()) + ? new ArrayList<>() + : Arrays.stream(req.getUploadUserId().split(",")) + .filter(org.apache.commons.lang3.StringUtils::isNotBlank) + .map(Long::valueOf) + .collect(Collectors.toList()); + PageHelper.startPage(req.getCurrent(), req.getSize()); + List list = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getParentId, parentId) + .in(org.apache.commons.collections4.CollectionUtils.isNotEmpty(creatorIds), FileMetadataInfo::getCreatorId, creatorIds) + .eq(ObjectUtils.isNotEmpty(req.getQueryTarget()), FileMetadataInfo::getDataType, req.getQueryTarget()) + .like(ObjectUtils.isNotEmpty(req.getFileName()), FileMetadataInfo::getOriginalName, req.getFileName()) + .eq(FileMetadataInfo::getIsLatest, FileIsLastEnum.YES.getValue()) + // 审核完成 ,元数据修改审核中,文件修改审核中,删除文件审核中 + .in(FileMetadataInfo::getApproveType,fileDatdList) + .list(); + // 创建人赋值 + setCreatorNames(list); + setCidInfos(list); + setProjectName(list); + setAnalysisDirectionName(list); + setSimulationPoolAndTaskInfo(list); + PageInfo page = new PageInfo<>(list); + long total = page.getTotal(); + List dtoList = list.stream().map(entity -> { + FileMetadataInfoResp dto = new FileMetadataInfoResp(); + BeanUtils.copyProperties(entity, dto); + return dto; + }).collect(Collectors.toList()); + PageInfo page1 = new PageInfo<>(dtoList); + page1.setTotal(total); + return PageUtils.getJsonObjectSdmResponse(dtoList, page1); + } + + @Override + public SdmResponse queryFilePermValue(QueryFilePermValueReq req) { + return null; + } + + @Override + public SdmResponse queryPermission(QueryPermissionReq req) { + FileMetadataInfo fileMetadataInfo = fileMetadataInfoService.getById(req.getFileId()); + if (fileMetadataInfo == null) { + return SdmResponse.failed("文件不存在"); + } + + PageDataResp> cidUserResp; + + UserListReq userListReq = new UserListReq(); + BeanUtils.copyProperties(req, userListReq); + userListReq.setUserId(req.getUserId()); + userListReq.setNickname(req.getUserName()); + + SdmResponse>> sdmResponse = sysUserFeignClient.listUser(userListReq); + if (!sdmResponse.isSuccess()) { + return SdmResponse.failed("人员信息未查到"); + } + + cidUserResp = sdmResponse.getData(); + + + List users = cidUserResp.getData(); + List userIds = users.stream().map(CIDUserResp::getUserId).toList(); + + + Map userPermissionMap = fileUserPermissionService.lambdaQuery() + .eq(FileUserPermission::getTFilemetaId, fileMetadataInfo.getId()) + .in(FileUserPermission::getUserId, userIds).list().stream() + .collect(Collectors.toMap(FileUserPermission::getUserId, FileUserPermission::getPermission)); + + users.forEach(user -> { + user.setPermission(userPermissionMap.getOrDefault(user.getUserId(), FilePermissionEnum.ZERO.getValue())); + }); + return SdmResponse.success(cidUserResp); + } + + @Override + public SdmResponse queryUserFilePermission(QueryUserFilePermissionReq req) { + return null; + } + + @Override + @Transactional(rollbackFor = Exception.class) + @PermissionCheckAspect.FilePermissionCheck(value = FilePermissionEnum.WRITE, fileIdExpression = "#req.fileId") + public SdmResponse renameFile(RenameFileReq req) { + String oldObjectKey = null; + String newObjectKey = null; + String bucketName = null; + Long fileId = req.getFileId(); + try { + FileMetadataInfo oldFile = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getId, fileId).one(); + bucketName = oldFile.getBucketName(); + if (ObjectUtils.isEmpty(oldFile)) { + return SdmResponse.failed("文件不存在"); + } + /*boolean hasDeletePermission = fileUserPermissionService.hasFilePermission(fileId, ThreadLocalContext.getUserId(), FilePermissionEnum.WRITE); + if (!hasDeletePermission){ + return SdmResponse.failed("没有修改权限"); + }*/ + String newName = req.getNewName(); + oldObjectKey = oldFile.getObjectKey(); + newObjectKey = oldObjectKey.substring(0, oldObjectKey.lastIndexOf("/") + 1) + newName; + + minioService.renameFile(oldObjectKey, newObjectKey,bucketName); + fileMetadataInfoService.lambdaUpdate().set(FileMetadataInfo::getObjectKey, newObjectKey).set(FileMetadataInfo::getOriginalName, newName).eq(FileMetadataInfo::getId, fileId).update(); + return SdmResponse.success("重命名成功"); + } catch (Exception e) { + log.error("重命名文件失败", e); + minioService.renameFile(newObjectKey, oldObjectKey,bucketName); + throw new RuntimeException("重命名文件失败: " + e.getMessage(), e); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public SdmResponse updatePermission(UpdatePermissionReq req) { + Map userPermissions = req.getUserPermissions(); + if (ObjectUtils.isEmpty(userPermissions)) return SdmResponse.failed("参数错误,缺少userPermissions参数"); + + Long fileId = req.getFileId(); + FileMetadataInfo fileMetadataInfo = null; + if(ObjectUtils.isNotEmpty(fileId)){ + fileMetadataInfo = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getId, fileId).one(); + }else if (ObjectUtils.isNotEmpty(req.getUuid())) { + fileMetadataInfo = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getRelatedResourceUuid, req.getUuid()).one(); + fileId = fileMetadataInfo.getId(); + } + + if (fileMetadataInfo == null) { + return SdmResponse.failed("文件不存在"); + } + + Long finalFileId = fileId; + userPermissions.forEach((userId, permission) -> { + FileUserPermission one = fileUserPermissionService.lambdaQuery() + .eq(FileUserPermission::getTFilemetaId, finalFileId) + .eq(FileUserPermission::getUserId, userId) + .one(); + if (one != null) { + //更新权限 + fileUserPermissionService.lambdaUpdate() + .set(FileUserPermission::getPermission, permission) + .eq(FileUserPermission::getUserId, userId) + .eq(FileUserPermission::getTFilemetaId, finalFileId) + .update(); + } else { + // 新增权限 + FileUserPermission fileUserPermission = new FileUserPermission(); + fileUserPermission.setTFilemetaId(finalFileId); + fileUserPermission.setUserId(userId); + fileUserPermission.setTenantId(ThreadLocalContext.getTenantId()); + fileUserPermission.setPermission(permission); + fileUserPermissionService.save(fileUserPermission); + } + }); + return SdmResponse.success("更新权限成功"); + } + + @Override + public SdmResponse fileExists(String path) { + String filePath = getDirMinioObjectKey(path); + FileMetadataInfo fileMetadataInfo = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getObjectKey, filePath).eq(FileMetadataInfo::getTenantId, ThreadLocalContext.getTenantId()).one(); + if (fileMetadataInfo == null) { + return SdmResponse.failed("文件不存在"); + } + return SdmResponse.success("文件已存在"); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean initSystemDirectory(String company) { + return true; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean initTenantData(Long tenantId) { + // 1. 强制切换当前线程的租户上下文(非常重要,否则后续 DAO 和 MinIO 操作找不到人) + ThreadLocalContext.setTenantId(tenantId); + + try { + // 获取该租户的桶名 + String bucketName = minioService.getCurrentTenantBucketName(); // 这里会触发 MinioService 的懒加载创建桶 + + for (DirTypeEnum dirType : DirTypeEnum.getInitSpmdDir()) { + // 目录路径,如 "knowledge/" + String dirPath = getDirMinioObjectKey(dirType.getDirName()); + + // 2. 检查 DB 是否已存在 (加上租户隔离条件) + boolean exists = fileMetadataInfoService.lambdaQuery() + .eq(FileMetadataInfo::getTenantId, tenantId) + .eq(FileMetadataInfo::getObjectKey, dirPath) + .exists(); + + if (exists) { + continue; // 已初始化过,跳过 + } + + // 3. MinIO 物理创建 (确保桶里也有这个文件夹占位符) + // 注意:这里一定要显式传 bucketName,因为我们已经处于多桶模式 + minioService.createDirectoryByObjectKey(dirPath, bucketName); + + // 4. DB 入库 + FileMetadataInfo dirInfo = createDirectoryMetadata( + dirPath, + dirType.getDirName(), + true, // isRoot + null, // parentId + null, + null, + dirType.getValue() + ); + // 显式设置关键字段 + dirInfo.setTenantId(tenantId); + dirInfo.setCreatorId(null); // 0代表系统自动创建 + dirInfo.setBucketName(bucketName); // 【关键】存入正确的桶名 + + fileMetadataInfoService.save(dirInfo); + } + return true; + } catch (Exception e) { + log.error("初始化租户[{}]数据失败", tenantId, e); + throw new RuntimeException("租户空间初始化失败", e); + } finally { + // 【务必】清理上下文,防止线程污染 + ThreadLocalContext.clear(); + } + } + + @Override + public SdmResponse copyFile(CopyFileReq req) { + return null; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public SdmResponse renameDir(String filePath, String newName, Integer type) { + return null; + } + + @Override + public SdmResponse renameDirNew(RenameDirReq req) { + FileMetadataInfo dirMetadataInfo = null; + if (ObjectUtils.isNotEmpty(req.getUuid()) ) { + dirMetadataInfo = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getRelatedResourceUuid, req.getUuid()).one(); + } else { + dirMetadataInfo = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getId, req.getDirId()).one(); + } + + if (ObjectUtils.isEmpty(dirMetadataInfo)) { + return SdmResponse.failed("文件夹不存在"); + } + + String oldDirMinioObjectKey = dirMetadataInfo.getObjectKey(); + + boolean hasWritePermission = fileUserPermissionService.hasFilePermission(dirMetadataInfo.getId(), ThreadLocalContext.getUserId(), FilePermissionEnum.WRITE); + if (!hasWritePermission) { + return SdmResponse.failed("没有写入权限"); + } + // 修正路径处理逻辑 + String newDirMinioObjectKey = getDirMinioObjectKey(oldDirMinioObjectKey.substring(0, oldDirMinioObjectKey.lastIndexOf(dirMetadataInfo.getOriginalName())) + req.getNewName()); + + try { + minioService.renameFile(oldDirMinioObjectKey, newDirMinioObjectKey,dirMetadataInfo.getBucketName()); + fileMetadataInfoService.lambdaUpdate().set(FileMetadataInfo::getObjectKey, newDirMinioObjectKey).set(FileMetadataInfo::getOriginalName, req.getNewName()).eq(FileMetadataInfo::getId, dirMetadataInfo.getId()).update(); + return SdmResponse.success("重命名目录成功"); + } catch (Exception e) { + log.error("重命名目录失败", e); + minioService.renameFile(newDirMinioObjectKey, oldDirMinioObjectKey,dirMetadataInfo.getBucketName()); + throw new RuntimeException("重命名目录失败: " + e.getMessage(), e); + } + } + + @Override + public SdmResponse zipFiles(ZipFilesReq req) { + return null; + } + + @Override public SdmResponse approveDataFile(LaunchApproveReq req) { // 审批状态 @@ -249,6 +1014,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { @Override public SdmResponse chunkUploadToMinio(ChunkUploadMinioFileReq req) { + String chunkBucket = minioService.getCurrentTenantBucketName(); ChunkUploadMinioFileResp resp = new ChunkUploadMinioFileResp(); // 基础路径配置 // -2. 参数校验 @@ -507,762 +1273,6 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { } - /** - * 创建根目录 - * - * @param req 创建目录请求 - * @return 创建结果 - */ - public SdmResponse createRootDir(CreateDirReq req) { - Long tenantId = ThreadLocalContext.getTenantId(); - if(ObjectUtils.isEmpty(req.getDirType())){ - log.error("请选择目录类型:1 知识库文件夹,2 项目节点文件夹,3 头像库文件夹,4 仿真参数库文件夹,5 训练模型文件夹"); - return SdmResponse.failed("请选择目录类型:1 知识库文件夹,2 项目节点文件夹,3 头像库文件夹,4 仿真参数库文件夹,5 训练模型文件夹"); - } - - DirTypeEnum dirTypeByValue = DirTypeEnum.getDirTypeByValue(req.getDirType()); - if (dirTypeByValue == null) { - log.error("请选择正确的目录类型:1 知识库文件夹,2 项目节点文件夹,3 头像库文件夹,4 仿真参数库文件夹,5 训练模型文件夹"); - return SdmResponse.failed("请选择正确的目录类型:1 知识库文件夹,2 项目节点文件夹,3 头像库文件夹,4 仿真参数库文件夹,5 训练模型文件夹"); - } - - // 先检查根目录是否已存在 - String rootDirMinioObjectKey = getDirMinioObjectKey(dirTypeByValue.getDirName()); - Optional fileMetadataInfoByObjectKey = getFileMetadataInfoByObjectKey(rootDirMinioObjectKey, null); - // 检查目录是否已存在 - if (!fileMetadataInfoByObjectKey.isPresent()) { - log.error("知识库、项目根目录不存在,等待initSystemDirectory 初始化完成"); - return SdmResponse.failed("知识库、项目根目录不存在,等待initSystemDirectory 初始化完成"); - } - - // 获取根目录的 id - FileMetadataInfo rootDirMetadataInfo = fileMetadataInfoByObjectKey.get(); - Long rootDirId = rootDirMetadataInfo.getId(); - - String dirName = req.getDirName(); - String dirMinioObjectKey = getDirMinioObjectKey(rootDirMetadataInfo.getObjectKey() + dirName); - String uuId = req.getUuId(); - String uuIdOwnType = req.getUuIdOwnType(); - - try { - // 检查目录是否已存在 - if (getFileMetadataInfoByObjectKey(dirMinioObjectKey,tenantId).isPresent()) { - log.error("目录已存在"); - return SdmResponse.failed("目录已存在"); - } - - // 在MinIO中创建目录 - minioService.createDirectoryByObjectKey(dirMinioObjectKey); - - // 创建目录元数据并保存到数据库 - FileMetadataInfo dirInfo = createDirectoryMetadata(dirMinioObjectKey, dirName, false, rootDirId, uuId, uuIdOwnType, req.getDirType()); - fileMetadataInfoService.save(dirInfo); - - // 创建默认权限记录 - createDirectoryPermission(dirInfo.getId()); - - log.info("根目录创建成功"); - return SdmResponse.success(dirInfo.getId()); - } catch (Exception dbException) { - log.error("创建根目录失败", dbException); - - // 补偿操作:如果 MinIO 创建成功但数据库失败,删除 MinIO 目录 - minioService.deleteFile(dirMinioObjectKey); - - // 重新抛出异常,确保事务回滚 - throw new RuntimeException("创建根目录失败: " + dbException.getMessage(), dbException); - } - } - - - /** - * 创建子目录 - * - * @param req 创建目录请求 - * @return 创建结果 - */ - public SdmResponse createChildDir(CreateDirReq req) { - Long tenantId = ThreadLocalContext.getTenantId(); - Long parDirId = null; - if (req.getParentUuId() != null) { - FileMetadataInfo node = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getRelatedResourceUuid, req.getParentUuId()).one(); - if (ObjectUtils.isEmpty(node)) { - log.error("上级节点目录不存在"); - return SdmResponse.failed("上级节点目录不存在"); - } - parDirId = node.getId(); - } else { - parDirId = req.getParDirId(); - } - - FileMetadataInfo parDirInfo = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getId, parDirId).one(); - if (ObjectUtils.isEmpty(parDirInfo)) { - log.error("上级目录不存在"); - return SdmResponse.failed("上级目录不存在"); - } - - // 构造子目录完整路径 - String childDirMinioObjectKey = getDirMinioObjectKey(parDirInfo.getObjectKey() + req.getDirName()); - - // 检查子目录是否已存在 - if (getFileMetadataInfoByObjectKey(childDirMinioObjectKey, tenantId).isPresent()) { - log.error("目录已存在"); - return SdmResponse.failed("目录已存在"); - } - - boolean hasWritePermission = fileUserPermissionService.hasFilePermission(parDirId, ThreadLocalContext.getUserId(), FilePermissionEnum.WRITE); - if (!hasWritePermission) { - log.error("没有写入权限"); - return SdmResponse.failed("没有写入权限"); - } - - try { - // 在MinIO中创建子目录 - minioService.createDirectoryByObjectKey(childDirMinioObjectKey); - - // 创建子目录元数据并保存到数据库 - FileMetadataInfo dirInfo = createDirectoryMetadata(childDirMinioObjectKey, req.getDirName(), false, parDirId, req.getUuId(), req.getUuIdOwnType(), parDirInfo.getDirType()); - fileMetadataInfoService.save(dirInfo); - - // 创建默认权限记录 - createDirectoryPermission(dirInfo.getId()); - - log.info("子目录创建成功"); - return SdmResponse.success(dirInfo.getId()); - - } catch (Exception dbException) { - log.error("创建子目录失败", dbException); - - // 补偿操作:如果 MinIO 创建成功但数据库失败,删除 MinIO 目录 - minioService.deleteFile(childDirMinioObjectKey); - - // 重新抛出异常,确保事务回滚 - throw new RuntimeException("创建子目录失败: " + dbException.getMessage(), dbException); - } - } - - - /** - * 创建目录元数据 - * - * @param minioObjectKey 目录路径 - * @param originalName 原始名称 - * @param isRoot 是否为根目录 - * @param parentId 父目录ID(根目录为null) - * @param uuid 节点ID - * @param uuIdOwnType - * @return 目录元数据对象 - */ - private FileMetadataInfo createDirectoryMetadata(String minioObjectKey, String originalName, - boolean isRoot, Long parentId, String uuid, - String uuIdOwnType, Integer dirType) { - FileMetadataInfo dirInfo = new FileMetadataInfo(); - dirInfo.setObjectKey(minioObjectKey); - dirInfo.setBucketName(minioService.getBucketName()); - dirInfo.setOriginalName(originalName); - dirInfo.setDataType(DataTypeEnum.DIRECTORY.getValue()); - dirInfo.setIsRoot(isRoot); - dirInfo.setDirType(dirType); - dirInfo.setCreatorId(ThreadLocalContext.getUserId()); - dirInfo.setTenantId(ThreadLocalContext.getTenantId()); - if (!isRoot && parentId != null) { - dirInfo.setParentId(parentId); - } - if (uuid != null) { - dirInfo.setRelatedResourceUuid(uuid); - } - if (uuIdOwnType != null) { - dirInfo.setRelatedResourceUuidOwnType(uuIdOwnType); - } - return dirInfo; - } - - /** - * 创建文件元数据信息 - * - * @param filePath 文件路径 - * @param fileName 文件名 - * @param fileType 文件类型 - * @param projectId 项目ID - * @param analysisDirectionId 分析方向ID - * @param remarks 备注信息 - * @param parentId 父目录ID - * @param fileSize 文件大小 - * @return 创建后的文件元数据信息 - */ - private FileMetadataInfo createFileMetadata(String filePath, String fileName, Integer fileType - , String projectId, String analysisDirectionId, String remarks, Long parentId, Long fileSize - ) { - FileMetadataInfo fileInfo = new FileMetadataInfo(); - fileInfo.setObjectKey(filePath); - fileInfo.setBucketName(minioService.getBucketName()); - fileInfo.setOriginalName(fileName); - fileInfo.setProjectId(projectId); - fileInfo.setAnalysisDirectionId(analysisDirectionId); - fileInfo.setRemarks(remarks); - fileInfo.setDataType(DataTypeEnum.FILE.getValue()); - fileInfo.setFileType(fileType); - fileInfo.setParentId(parentId); - fileInfo.setIsRoot(false); - fileInfo.setCreatorId(ThreadLocalContext.getUserId()); - fileInfo.setTenantId(ThreadLocalContext.getTenantId()); - fileInfo.setFileSize(fileSize); - return fileInfo; - } - - /** - * 创建目录权限记录 - * - * @param directoryId 目录ID - */ - private void createDirectoryPermission(Long directoryId) { - FileUserPermission permission = new FileUserPermission(); - permission.setTFilemetaId(directoryId); - permission.setPermission(FilePermissionEnum.ALL.getValue()); - permission.setUserId(ThreadLocalContext.getUserId()); - permission.setTenantId(ThreadLocalContext.getTenantId()); - fileUserPermissionService.save(permission); - } - - /** - * 删除文件夹权限 - * @param directoryId - */ - private void deleteDirectoryPermission(Long directoryId) { - fileUserPermissionService.lambdaUpdate().eq(FileUserPermission::getTFilemetaId, directoryId).remove(); - } - - /** - * 创建文件权限信息 - * - * @param fileId 文件ID - * @return - */ - private void createFilePermission(Long fileId) { - FileUserPermission permission = new FileUserPermission(); - permission.setTFilemetaId(fileId); - permission.setPermission(FilePermissionEnum.ALL.getValue()); - permission.setUserId(ThreadLocalContext.getUserId()); - permission.setTenantId(ThreadLocalContext.getTenantId()); - fileUserPermissionService.save(permission); - } - - /** - * 删除文件权限 - * @param fileId - * @return - */ - private void deleteFilePermission(Long fileId) { - fileUserPermissionService.lambdaUpdate().eq(FileUserPermission::getTFilemetaId, fileId).remove(); - } - - private Optional getFileMetadataInfoByObjectKey(String minioObjectKey, Long tenantId) { - if (!StringUtils.hasText(minioObjectKey)) { - return Optional.empty(); - } - minioObjectKey = getDirMinioObjectKey(minioObjectKey); - return Optional.ofNullable(fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getObjectKey, minioObjectKey).eq(ObjectUtils.isNotEmpty(tenantId), FileMetadataInfo::getTenantId, tenantId).one()); - } - - @NotNull - private static String getDirMinioObjectKey(String dirPath) { - if (dirPath.startsWith("/")) { - dirPath = dirPath.substring(1); - } - if (!dirPath.endsWith("/")) { - dirPath += "/"; - } - return dirPath; - } - - private static String getFileMinioObjectKey(String filePath) { - if (filePath.startsWith("/")) { - filePath = filePath.substring(1); - } - - if (filePath.endsWith("/")) { - filePath = filePath.substring(0, filePath.length() - 1); - } - return filePath; - - } - - @Override - @Transactional(rollbackFor = Exception.class) - public SdmResponse delDir(DelDirReq req) { - try { - FileMetadataInfo deleteDirMetadataInfo = null; - if (req.getDelUuid() != null) { - deleteDirMetadataInfo = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getRelatedResourceUuid, req.getDelUuid()).one(); - } else { - deleteDirMetadataInfo = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getId, req.getDelDirId()).one(); - } - - if (ObjectUtils.isEmpty(deleteDirMetadataInfo)) { - return SdmResponse.failed("目录不存在"); - } - - String dirMinioObjectKey = deleteDirMetadataInfo.getObjectKey(); - Long deleteDirId = deleteDirMetadataInfo.getId(); - - boolean hasDeletePermission = fileUserPermissionService.hasFilePermission(deleteDirId, ThreadLocalContext.getUserId(), FilePermissionEnum.DELETE); - if (!hasDeletePermission) { - return SdmResponse.failed("没有删除权限"); - } - - // 数据库批量删除文件信息 - LambdaQueryWrapper fileMetaDataQuery = new LambdaQueryWrapper<>(); - fileMetaDataQuery.select(FileStorage::getFileId).eq(FileStorage::getDirId, deleteDirId); - List deleteFileIdList = fileStorageService.list(fileMetaDataQuery).stream().map(FileStorage::getFileId).collect(Collectors.toList()); - - deleteFileIdList.add(deleteDirId); - - if(ObjectUtils.isNotEmpty(deleteFileIdList)){ - fileMetadataInfoService.removeByIds(deleteFileIdList); - fileMetadataExtensionService.lambdaUpdate().in(FileMetadataExtension::getTFilemetaId, deleteFileIdList).remove(); - fileStorageService.lambdaUpdate().in(FileStorage::getFileId, deleteFileIdList).remove(); - fileUserPermissionService.lambdaUpdate().in(FileUserPermission::getTFilemetaId, deleteFileIdList).remove(); - fileSimulationMappingService.lambdaUpdate().in(FileSimulationMapping::getFileId, deleteFileIdList).remove(); - } - - fileStorageService.lambdaUpdate().eq(FileStorage::getDirId, deleteDirId).remove(); - //删除文件夹权限 - deleteDirectoryPermission(deleteDirId); - - - // minio文件系统批量删除文件 - minioService.deleteDirectoryRecursively(dirMinioObjectKey); - log.info("删除目录文件元数据和Minio文件成功: " + dirMinioObjectKey); - - - - // 子文件夹一并删除 - List childDirs = fileMetadataInfoService.lambdaQuery() - .eq(FileMetadataInfo::getParentId, deleteDirId) - .eq(FileMetadataInfo::getDataType, DataTypeEnum.DIRECTORY.getValue()) - .list(); - if (CollectionUtils.isNotEmpty(childDirs)) { - for (FileMetadataInfo childDir : childDirs) { - DelDirReq delDirReq = new DelDirReq(); - delDirReq.setDelDirId(childDir.getId()); - delDir(delDirReq); - } - } - } catch (Exception e) { - log.error("删除目录失败", e); - // 重新抛出异常,确保事务回滚 - throw new RuntimeException("删除目录失败: " + e.getMessage(), e); - } - return SdmResponse.success("删除成功"); - } - - @Override - public boolean reverseDeleteDataFiles(File file) { - return false; - } - - @Override - @Transactional(rollbackFor = Exception.class) - @PermissionCheckAspect.FilePermissionCheck(value = FilePermissionEnum.DELETE, fileIdExpression = "#req.delFileId") - public SdmResponse delFile(DelFileReq req) { - try { - Long delFileId = req.getDelFileId(); - FileMetadataInfo deleteFileMetadataInfo = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getId, delFileId).one(); - if (ObjectUtils.isEmpty(deleteFileMetadataInfo)) { - return SdmResponse.failed("文件不存在"); - } - - // 文件夹 - FileMetadataInfo dirMetadataInfo = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getId, deleteFileMetadataInfo.getParentId()).eq(FileMetadataInfo::getDataType, DataTypeEnum.DIRECTORY.getValue()).one(); - - String deleteFileMinioObejctKey = deleteFileMetadataInfo.getObjectKey(); - - /*boolean hasDeletePermission = fileUserPermissionService.hasFilePermission(deleteFileMetadataInfo.getId(), ThreadLocalContext.getUserId(), FilePermissionEnum.DELETE); - if (!hasDeletePermission) { - return SdmResponse.failed("没有删除权限"); - }*/ - - // 知识库 - if(dirMetadataInfo!=null&&Objects.equals(DirTypeEnum.KNOWLEDGE_BASE_DIR.getValue(), dirMetadataInfo.getDirType())){ - // 发送审批电子流,成功继续西面操作,失败直接返回 - // String templateId, String templateName,String approveContents,int approveAction:1:新增 2:修改 3:删除 - String approveContents = getApproveContents(List.of(delFileId), "知识库文件删除", NumberConstants.THREE, List.of(deleteFileMetadataInfo), null); - Pair approvePair = launchFileDataApprove(req.getTemplateId(), req.getTemplateName(), approveContents, NumberConstants.THREE, ApproveTypeEnum.KNOWLEDGE_APPROVE); - if(!approvePair.getLeft()|| org.apache.commons.lang3.StringUtils.isBlank(approvePair.getRight())) { - log.error("delFile approveInit failed, params :{}", JSONObject.toJSONString(req)); - throw new RuntimeException("文件删除,创建审批流失败,cidFlowId:"+approvePair.getRight()); - } - deleteFileMetadataInfo.setApprovalStatus(ApprovalFileDataStatusEnum.PENDING.getKey()); - deleteFileMetadataInfo.setApproveType(ApproveFileDataTypeEnum.DELETE_REVIEWING.getCode()); - deleteFileMetadataInfo.setCidFlowId(approvePair.getRight()); - fileMetadataInfoService.updateById(deleteFileMetadataInfo); - return SdmResponse.success("操作成功,删除文件待审批"); - }else{ - // 删除MinIO文件 - minioService.deleteFile(deleteFileMetadataInfo.getObjectKey()); - // 删除数据库记录 - fileMetadataInfoService.removeById(deleteFileMetadataInfo.getId()); - fileStorageService.remove(new LambdaQueryWrapper().eq(FileStorage::getFileId, deleteFileMetadataInfo.getId())); - fileMetadataExtensionService.remove(new LambdaQueryWrapper().eq(FileMetadataExtension::getTFilemetaId, deleteFileMetadataInfo.getId())); - fileUserPermissionService.remove(new LambdaQueryWrapper().eq(FileUserPermission::getTFilemetaId, deleteFileMetadataInfo.getId())); - return SdmResponse.success("操作成功"); - } - } catch (Exception e) { - log.error("删除文件失败", e); - throw new RuntimeException("删除文件失败: " + e.getMessage(), e); - } - } - - @Override - public SdmResponse fileSearch(FileSearchReq minioFileSearchReq) { - QueryBigFileReq queryBigFileReq = new QueryBigFileReq(); - - FileMetadataInfo fileMetadataInfo = null; - if (ObjectUtils.isNotEmpty(minioFileSearchReq.getParentUuid())) { - // 项目节点下搜索文件 - fileMetadataInfo = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getRelatedResourceUuid, minioFileSearchReq.getParentUuid()).one(); - } else if (ObjectUtils.isNotEmpty(minioFileSearchReq.getParentDirId())) { - // 知识库的文件查询 - fileMetadataInfo = fileMetadataInfoService.getById(minioFileSearchReq.getParentDirId()); - } - - BeanUtils.copyProperties(minioFileSearchReq, queryBigFileReq); - if (ObjectUtils.isNotEmpty(fileMetadataInfo)) { - queryBigFileReq.setDirId(fileMetadataInfo.getId()); - queryBigFileReq.setCurrent(minioFileSearchReq.getCurrent()); - queryBigFileReq.setSize(minioFileSearchReq.getSize()); - } - // 这里是知识库文件:排除新增在审批的文件 - queryBigFileReq.setApproveTypeList(fileDatdList); - queryBigFileReq.setIsLatest(true); - - List creatorIds = org.apache.commons.lang3.StringUtils.isBlank(minioFileSearchReq.getUploadUserId()) - ? new ArrayList<>() - : Arrays.stream(minioFileSearchReq.getUploadUserId().split(",")) - .filter(org.apache.commons.lang3.StringUtils::isNotBlank) - .map(Long::valueOf) - .collect(Collectors.toList()); - queryBigFileReq.setUploadUserId(creatorIds); - - List fileIdList =dataStorageAnalysis.getListBigFileId(queryBigFileReq); - if(CollectionUtils.isEmpty(fileIdList)){ - return SdmResponse.success(); - } - List files = fileMetadataInfoService - .lambdaQuery() - .in(FileMetadataInfo::getId, fileIdList) - .list(); - setCreatorNames(files); - setCidInfos(files); - setProjectName(files); - setAnalysisDirectionName(files); - setSimulationPoolAndTaskInfo(files); - PageInfo page = new PageInfo<>(files); - return PageUtils.getJsonObjectSdmResponse(files, page); - } - - @Override - public SdmResponse fileSearchStop() { - return null; - } - - @Override - public SdmResponse getFileBaseInfo(GetFileBaseInfoReq req) { - FileMetadataInfo fileMetadataInfo = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getId, req.getFileId()).one(); - if (fileMetadataInfo == null) { - return SdmResponse.failed("文件不存在"); - } - FileMetadataInfoResp fileMetadataInfoResp = new FileMetadataInfoResp(); - BeanUtils.copyProperties(fileMetadataInfo, fileMetadataInfoResp); - - return SdmResponse.success(fileMetadataInfoResp); - } - - @Override - public SdmResponse>> queryDir(QueryDirReq req) { - Long parentId; - if (ObjectUtils.isNotEmpty(req.getFileId())) { - // 知识库的文件查询 - parentId = req.getFileId(); - } else if (ObjectUtils.isNotEmpty(req.getUuid())) { - // 项目节点文件查询 - FileMetadataInfo nodeMetadataInfo = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getRelatedResourceUuid, req.getUuid()).one(); - if (nodeMetadataInfo == null) { - // 项目节点未创建文件夹,返回空 - return SdmResponse.success(); - } - parentId = nodeMetadataInfo.getId(); - } else { - return SdmResponse.success(); - } - List creatorIds = org.apache.commons.lang3.StringUtils.isBlank(req.getUploadUserId()) - ? new ArrayList<>() - : Arrays.stream(req.getUploadUserId().split(",")) - .filter(org.apache.commons.lang3.StringUtils::isNotBlank) - .map(Long::valueOf) - .collect(Collectors.toList()); - PageHelper.startPage(req.getCurrent(), req.getSize()); - List list = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getParentId, parentId) - .in(org.apache.commons.collections4.CollectionUtils.isNotEmpty(creatorIds), FileMetadataInfo::getCreatorId, creatorIds) - .eq(ObjectUtils.isNotEmpty(req.getQueryTarget()), FileMetadataInfo::getDataType, req.getQueryTarget()) - .like(ObjectUtils.isNotEmpty(req.getFileName()), FileMetadataInfo::getOriginalName, req.getFileName()) - .eq(FileMetadataInfo::getIsLatest, FileIsLastEnum.YES.getValue()) - // 审核完成 ,元数据修改审核中,文件修改审核中,删除文件审核中 - .in(FileMetadataInfo::getApproveType,fileDatdList) - .list(); - // 创建人赋值 - setCreatorNames(list); - setCidInfos(list); - setProjectName(list); - setAnalysisDirectionName(list); - setSimulationPoolAndTaskInfo(list); - PageInfo page = new PageInfo<>(list); - long total = page.getTotal(); - List dtoList = list.stream().map(entity -> { - FileMetadataInfoResp dto = new FileMetadataInfoResp(); - BeanUtils.copyProperties(entity, dto); - return dto; - }).collect(Collectors.toList()); - PageInfo page1 = new PageInfo<>(dtoList); - page1.setTotal(total); - return PageUtils.getJsonObjectSdmResponse(dtoList, page1); - } - - @Override - public SdmResponse queryFilePermValue(QueryFilePermValueReq req) { - return null; - } - - @Override - public SdmResponse queryPermission(QueryPermissionReq req) { - FileMetadataInfo fileMetadataInfo = fileMetadataInfoService.getById(req.getFileId()); - if (fileMetadataInfo == null) { - return SdmResponse.failed("文件不存在"); - } - - PageDataResp> cidUserResp; - - UserListReq userListReq = new UserListReq(); - BeanUtils.copyProperties(req, userListReq); - userListReq.setUserId(req.getUserId()); - userListReq.setNickname(req.getUserName()); - - SdmResponse>> sdmResponse = sysUserFeignClient.listUser(userListReq); - if (!sdmResponse.isSuccess()) { - return SdmResponse.failed("人员信息未查到"); - } - - cidUserResp = sdmResponse.getData(); - - - List users = cidUserResp.getData(); - List userIds = users.stream().map(CIDUserResp::getUserId).toList(); - - - Map userPermissionMap = fileUserPermissionService.lambdaQuery() - .eq(FileUserPermission::getTFilemetaId, fileMetadataInfo.getId()) - .in(FileUserPermission::getUserId, userIds).list().stream() - .collect(Collectors.toMap(FileUserPermission::getUserId, FileUserPermission::getPermission)); - - users.forEach(user -> { - user.setPermission(userPermissionMap.getOrDefault(user.getUserId(), FilePermissionEnum.ZERO.getValue())); - }); - return SdmResponse.success(cidUserResp); - } - - @Override - public SdmResponse queryUserFilePermission(QueryUserFilePermissionReq req) { - return null; - } - - @Override - @Transactional(rollbackFor = Exception.class) - @PermissionCheckAspect.FilePermissionCheck(value = FilePermissionEnum.WRITE, fileIdExpression = "#req.fileId") - public SdmResponse renameFile(RenameFileReq req) { - String oldObjectKey = null; - String newObjectKey = null; - Long fileId = req.getFileId(); - try { - FileMetadataInfo oldFile = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getId, fileId).one(); - if (ObjectUtils.isEmpty(oldFile)) { - return SdmResponse.failed("文件不存在"); - } - /*boolean hasDeletePermission = fileUserPermissionService.hasFilePermission(fileId, ThreadLocalContext.getUserId(), FilePermissionEnum.WRITE); - if (!hasDeletePermission){ - return SdmResponse.failed("没有修改权限"); - }*/ - String newName = req.getNewName(); - oldObjectKey = oldFile.getObjectKey(); - newObjectKey = oldObjectKey.substring(0, oldObjectKey.lastIndexOf("/") + 1) + newName; - - minioService.renameFile(oldObjectKey, newObjectKey); - fileMetadataInfoService.lambdaUpdate().set(FileMetadataInfo::getObjectKey, newObjectKey).set(FileMetadataInfo::getOriginalName, newName).eq(FileMetadataInfo::getId, fileId).update(); - return SdmResponse.success("重命名成功"); - } catch (Exception e) { - log.error("重命名文件失败", e); - minioService.renameFile(newObjectKey, oldObjectKey); - throw new RuntimeException("重命名文件失败: " + e.getMessage(), e); - } - } - - @Override - @Transactional(rollbackFor = Exception.class) - public SdmResponse updatePermission(UpdatePermissionReq req) { - Map userPermissions = req.getUserPermissions(); - if (ObjectUtils.isEmpty(userPermissions)) return SdmResponse.failed("参数错误,缺少userPermissions参数"); - - Long fileId = req.getFileId(); - FileMetadataInfo fileMetadataInfo = null; - if(ObjectUtils.isNotEmpty(fileId)){ - fileMetadataInfo = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getId, fileId).one(); - }else if (ObjectUtils.isNotEmpty(req.getUuid())) { - fileMetadataInfo = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getRelatedResourceUuid, req.getUuid()).one(); - fileId = fileMetadataInfo.getId(); - } - - if (fileMetadataInfo == null) { - return SdmResponse.failed("文件不存在"); - } - - Long finalFileId = fileId; - userPermissions.forEach((userId, permission) -> { - FileUserPermission one = fileUserPermissionService.lambdaQuery() - .eq(FileUserPermission::getTFilemetaId, finalFileId) - .eq(FileUserPermission::getUserId, userId) - .one(); - if (one != null) { - //更新权限 - fileUserPermissionService.lambdaUpdate() - .set(FileUserPermission::getPermission, permission) - .eq(FileUserPermission::getUserId, userId) - .eq(FileUserPermission::getTFilemetaId, finalFileId) - .update(); - } else { - // 新增权限 - FileUserPermission fileUserPermission = new FileUserPermission(); - fileUserPermission.setTFilemetaId(finalFileId); - fileUserPermission.setUserId(userId); - fileUserPermission.setTenantId(ThreadLocalContext.getTenantId()); - fileUserPermission.setPermission(permission); - fileUserPermissionService.save(fileUserPermission); - } - }); - return SdmResponse.success("更新权限成功"); - } - - @Override - public SdmResponse fileExists(String path) { - String filePath = getDirMinioObjectKey(path); - FileMetadataInfo fileMetadataInfo = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getObjectKey, filePath).eq(FileMetadataInfo::getTenantId, ThreadLocalContext.getTenantId()).one(); - if (fileMetadataInfo == null) { - return SdmResponse.failed("文件不存在"); - } - return SdmResponse.success("文件已存在"); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public boolean initSystemDirectory(String company) { - for (DirTypeEnum dirType : DirTypeEnum.getInitSpmdDir()) { - String dirMinioObjectKey = getDirMinioObjectKey(dirType.getDirName()); - try { - // 检查目录是否已存在 - if (getFileMetadataInfoByObjectKey(dirMinioObjectKey, null).isPresent()) { - continue; - } - - // 在MinIO中创建目录 - minioService.createDirectoryByObjectKey(dirMinioObjectKey); - - // 创建目录元数据并保存到数据库 - FileMetadataInfo dirInfo = createDirectoryMetadata(dirMinioObjectKey, dirType.getDirName(), true, null, null, null, dirType.getValue()); - // 顶层目录的创建者ID和租户ID设置为null,表示这是系统级别的根目录 - dirInfo.setCreatorId(null); - dirInfo.setTenantId(null); - fileMetadataInfoService.save(dirInfo); - } catch (Exception dbException) { - log.error("创建根目录失败", dbException); - - // 补偿操作:如果 MinIO 创建成功但数据库失败,删除 MinIO 目录 - minioService.deleteFile(dirMinioObjectKey); - - // 重新抛出异常,确保事务回滚 - throw new RuntimeException("创建根目录失败: " + dbException.getMessage(), dbException); - } - } - return true; - } - - @Override - public SdmResponse copyFile(CopyFileReq req) { - return null; - } - - @Override - @Transactional(rollbackFor = Exception.class) - public SdmResponse renameDir(String filePath, String newName, Integer type) { - String oldPath = getDirMinioObjectKey(filePath); - FileMetadataInfo fileMetadataInfo = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getObjectKey, oldPath).eq(FileMetadataInfo::getTenantId, ThreadLocalContext.getTenantId()).one(); - if (fileMetadataInfo == null) { - return SdmResponse.failed("文件不存在"); - } - - boolean hasWritePermission = fileUserPermissionService.hasFilePermission(fileMetadataInfo.getId(), ThreadLocalContext.getUserId(), FilePermissionEnum.WRITE); - if (!hasWritePermission) { - return SdmResponse.failed("没有写入权限"); - } - // 修正路径处理逻辑 - String newPath = getDirMinioObjectKey(oldPath.substring(0, oldPath.lastIndexOf('/') + 1) + newName); - - try { - minioService.renameFile(oldPath, newPath); - fileMetadataInfoService.lambdaUpdate().set(FileMetadataInfo::getObjectKey, newPath).eq(FileMetadataInfo::getId, fileMetadataInfo.getId()).update(); - return SdmResponse.success("重命名目录成功"); - } catch (Exception e) { - log.error("重命名目录失败", e); - minioService.renameFile(newPath, oldPath); - throw new RuntimeException("重命名目录失败: " + e.getMessage(), e); - } - } - - @Override - public SdmResponse renameDirNew(RenameDirReq req) { - FileMetadataInfo dirMetadataInfo = null; - if (ObjectUtils.isNotEmpty(req.getUuid()) ) { - dirMetadataInfo = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getRelatedResourceUuid, req.getUuid()).one(); - } else { - dirMetadataInfo = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getId, req.getDirId()).one(); - } - - if (ObjectUtils.isEmpty(dirMetadataInfo)) { - return SdmResponse.failed("文件夹不存在"); - } - - String oldDirMinioObjectKey = dirMetadataInfo.getObjectKey(); - - boolean hasWritePermission = fileUserPermissionService.hasFilePermission(dirMetadataInfo.getId(), ThreadLocalContext.getUserId(), FilePermissionEnum.WRITE); - if (!hasWritePermission) { - return SdmResponse.failed("没有写入权限"); - } - // 修正路径处理逻辑 - String newDirMinioObjectKey = getDirMinioObjectKey(oldDirMinioObjectKey.substring(0, oldDirMinioObjectKey.lastIndexOf(dirMetadataInfo.getOriginalName())) + req.getNewName()); - - try { - minioService.renameFile(oldDirMinioObjectKey, newDirMinioObjectKey); - fileMetadataInfoService.lambdaUpdate().set(FileMetadataInfo::getObjectKey, newDirMinioObjectKey).set(FileMetadataInfo::getOriginalName, req.getNewName()).eq(FileMetadataInfo::getId, dirMetadataInfo.getId()).update(); - return SdmResponse.success("重命名目录成功"); - } catch (Exception e) { - log.error("重命名目录失败", e); - minioService.renameFile(newDirMinioObjectKey, oldDirMinioObjectKey); - throw new RuntimeException("重命名目录失败: " + e.getMessage(), e); - } - } - - @Override - public SdmResponse zipFiles(ZipFilesReq req) { - return null; - } - @Override @PermissionCheckAspect.FilePermissionCheck(value = FilePermissionEnum.DOWNLOAD, fileIdExpression = "#req.fileId") public void downloadFile(DownloadFileReq req, HttpServletResponse response) { @@ -1281,7 +1291,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { }*/ // 从MinIO下载文件 String encodedFileName = URLEncoder.encode(fileMetadataInfo.getOriginalName(), StandardCharsets.UTF_8); - minioService.downloadFile(fileObjectKey,response,encodedFileName,false,""); + minioService.downloadFile(fileObjectKey,fileMetadataInfo.getBucketName(),response,encodedFileName,false,""); } catch (Exception e) { log.error("下载文件失败", e); try { @@ -1302,7 +1312,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { String fileObjectKey = fileMetadataInfo.getObjectKey(); // 从MinIO下载文件InputStream - return minioService.getMinioInputStream(fileObjectKey); + return minioService.getMinioInputStream(fileObjectKey,fileMetadataInfo.getBucketName()); } @Override @@ -1346,7 +1356,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { return SdmResponse.failed("没有上传权限"); } try { - minioService.uploadFile(req.getFile(), fileMinioObjectKey, null); + minioService.uploadFile(req.getFile(), fileMinioObjectKey, null,dirMetadataInfo.getBucketName()); // 创建目录元数据并保存到数据库 FileMetadataInfo fileInfo = createFileMetadata(fileMinioObjectKey, originalName, req.getFileType(), @@ -1444,7 +1454,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { jsonObject.put("fileId", fileInfo.getId()); return SdmResponse.success(jsonObject); } catch (Exception e) { - minioService.deleteFile(fileMinioObjectKey); + minioService.deleteFile(fileMinioObjectKey, dirMetadataInfo.getBucketName()); log.error("上传文件失败", e); throw new RuntimeException("上传文件失败: " + e.getMessage(), e); } @@ -1609,7 +1619,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { // 处理历史版本objectkey= 1/test1_V1.txt originalName=test1.txt 替换为 newFileMinioObjectKey= 1/test1_V2.txt newFileMinioObjectKey = getFileMinioObjectKey(oldFileMinioObjectKey.substring(0, oldFileMinioObjectKey.lastIndexOf(fileMetadataInfo.getOriginalName().substring(0, fileMetadataInfo.getOriginalName().lastIndexOf('.')))) + modifiedFileName); - minioService.uploadFile(req.getFile(), newFileMinioObjectKey, null); + minioService.uploadFile(req.getFile(), newFileMinioObjectKey, null,fileMetadataInfo.getBucketName()); // 创建目录元数据并保存到数据库 FileMetadataInfo fileInfo = createFileMetadata(newFileMinioObjectKey, req.getFileName(), req.getFileType(), req.getProjectId(), req.getAnalysisDirectionId(), req.getRemarks(), fileMetadataInfo.getParentId(), req.getFile().getSize() @@ -1704,7 +1714,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { } } catch (Exception e) { - minioService.deleteFile(newFileMinioObjectKey); + minioService.deleteFile(newFileMinioObjectKey, fileMetadataInfo.getBucketName()); log.error("更新文件失败", e); throw new RuntimeException("更新文件失败: " + e.getMessage(), e); } @@ -1715,7 +1725,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { public SdmResponse uploadAvatar(MultipartFile avatar) { // 先创建 avatar 目录 String dirMinioObjectKey = getDirMinioObjectKey(DirTypeEnum.AVATAR_DIR.getDirName()); - Optional fileMetadataInfoByObjectKey = getFileMetadataInfoByObjectKey(dirMinioObjectKey,null ); + Optional fileMetadataInfoByObjectKey = getFileMetadataInfoByObjectKey(dirMinioObjectKey,ThreadLocalContext.getTenantId() ); // 检查目录是否已存在 if (!fileMetadataInfoByObjectKey.isPresent()) { return SdmResponse.failed("avatar 目录不存在,等待initSystemDirectory 初始化完成"); @@ -1755,7 +1765,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { String avatarMinioObjectKey = getFileMinioObjectKey(avatarDirMetadataInfo.getObjectKey() + newFilename); try { - minioService.uploadFile(avatar, avatarMinioObjectKey, null); + minioService.uploadFile(avatar, avatarMinioObjectKey, null,avatarDirMetadataInfo.getBucketName()); // 创建目录元数据并保存到数据库 FileMetadataInfo fileInfo = createFileMetadata(avatarMinioObjectKey, newFilename, null, @@ -1765,7 +1775,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { jsonObject.put("avatarId", fileInfo.getId()); return SdmResponse.success(jsonObject); } catch (Exception e) { - minioService.deleteFile(avatarMinioObjectKey); + minioService.deleteFile(avatarMinioObjectKey,avatarDirMetadataInfo.getBucketName() ); log.error("上传头像失败", e); throw new RuntimeException("上传头像失败: " + e.getMessage(), e); } @@ -1776,7 +1786,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { public SdmResponse uploadSimulationParamFile(MultipartFile paramFile) { // 先创建 simulationParameter 目录 String dirMinioObjectKey = getDirMinioObjectKey(DirTypeEnum.SIMULATION_PARAMETER_DIR.getDirName()); - Optional fileMetadataInfoByObjectKey = getFileMetadataInfoByObjectKey(dirMinioObjectKey,null ); + Optional fileMetadataInfoByObjectKey = getFileMetadataInfoByObjectKey(dirMinioObjectKey,ThreadLocalContext.getTenantId() ); // 检查目录是否已存在 if (!fileMetadataInfoByObjectKey.isPresent()) { return SdmResponse.failed("仿真参数库文件夹 目录不存在,等待initSystemDirectory 初始化完成"); @@ -1816,7 +1826,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { } try { - minioService.uploadFile(paramFile, simulationParamMinioObjectKey, null); + minioService.uploadFile(paramFile, simulationParamMinioObjectKey, null,simulationParamDirMetadataInfo.getBucketName()); // 创建目录元数据并保存到数据库 FileMetadataInfo fileInfo = createFileMetadata(simulationParamMinioObjectKey, originalFilename, null, @@ -1827,7 +1837,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { fileMetadataInfoService.save(fileInfo); return SdmResponse.success(fileInfo.getId()); } catch (Exception e) { - minioService.deleteFile(simulationParamMinioObjectKey); + minioService.deleteFile(simulationParamMinioObjectKey, simulationParamDirMetadataInfo.getBucketName()); log.error("上传仿真参数文件", e); throw new RuntimeException("上传仿真参数文件: " + e.getMessage(), e); } @@ -1838,7 +1848,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { public SdmResponse updateSimulationParamFile(MultipartFile paramFile) { // 先创建 simulationParameter 目录 String dirMinioObjectKey = getDirMinioObjectKey(DirTypeEnum.SIMULATION_PARAMETER_DIR.getDirName()); - Optional fileMetadataInfoByObjectKey = getFileMetadataInfoByObjectKey(dirMinioObjectKey,null); + Optional fileMetadataInfoByObjectKey = getFileMetadataInfoByObjectKey(dirMinioObjectKey,ThreadLocalContext.getTenantId()); // 检查目录是否已存在 if (!fileMetadataInfoByObjectKey.isPresent()) { return SdmResponse.failed("仿真参数库文件夹 目录不存在,等待initSystemDirectory 初始化完成"); @@ -1871,10 +1881,10 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { // 上传仿真参数文件 try { - minioService.uploadFile(paramFile, fileMinioObjectKey, null); + minioService.uploadFile(paramFile, fileMinioObjectKey, null,simulationParamDirMetadataInfo.getBucketName()); return SdmResponse.success(); } catch (Exception e) { - minioService.deleteFile(fileMinioObjectKey); + minioService.deleteFile(fileMinioObjectKey, simulationParamDirMetadataInfo.getBucketName()); log.error("更新仿真参数文件", e); throw new RuntimeException("更新仿真参数文件: " + e.getMessage(), e); } @@ -1885,7 +1895,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { public SdmResponse uploadTrainModelDataFile(MultipartFile trainModelFile) { // 先创建 trainModel 目录 String dirMinioObjectKey = getDirMinioObjectKey(DirTypeEnum.TRAIN_MODEL_DIR.getDirName()); - Optional fileMetadataInfoByObjectKey = getFileMetadataInfoByObjectKey(dirMinioObjectKey, null); + Optional fileMetadataInfoByObjectKey = getFileMetadataInfoByObjectKey(dirMinioObjectKey, ThreadLocalContext.getTenantId()); // 检查目录是否已存在 if (!fileMetadataInfoByObjectKey.isPresent()) { return SdmResponse.failed("trainModel 目录不存在,等待initSystemDirectory 初始化完成"); @@ -1912,7 +1922,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { String trainingMinioObjectKey = getFileMinioObjectKey(trainModelDirMetadataInfo.getObjectKey() + newFilename); try { - minioService.uploadFile(trainModelFile, trainingMinioObjectKey, null); + minioService.uploadFile(trainModelFile, trainingMinioObjectKey, null,trainModelDirMetadataInfo.getBucketName()); // 创建目录元数据并保存到数据库 FileMetadataInfo fileInfo = createFileMetadata(trainingMinioObjectKey, originalFilename, null, @@ -1920,7 +1930,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { fileMetadataInfoService.save(fileInfo); return SdmResponse.success(fileInfo.getId()); } catch (Exception e) { - minioService.deleteFile(trainingMinioObjectKey); + minioService.deleteFile(trainingMinioObjectKey, trainModelDirMetadataInfo.getBucketName()); log.error("上传训练模型文件失败", e); throw new RuntimeException("上传训练模型文件失败: " + e.getMessage(), e); } @@ -1930,7 +1940,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { public SdmResponse uploadScriptFile(MultipartFile scriptFile) { // 先创建 script 目录 String dirMinioObjectKey = getDirMinioObjectKey(DirTypeEnum.SCRIPT_DIR.getDirName()); - Optional fileMetadataInfoByObjectKey = getFileMetadataInfoByObjectKey(dirMinioObjectKey, null); + Optional fileMetadataInfoByObjectKey = getFileMetadataInfoByObjectKey(dirMinioObjectKey, ThreadLocalContext.getTenantId()); // 检查目录是否已存在 if (!fileMetadataInfoByObjectKey.isPresent()) { return SdmResponse.failed("script 目录不存在,等待initSystemDirectory 初始化完成"); @@ -1957,7 +1967,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { String scriptFileMinioObjectKey = getFileMinioObjectKey(scriptDirMetadataInfo.getObjectKey() + newFilename); try { - minioService.uploadFile(scriptFile, scriptFileMinioObjectKey, null); + minioService.uploadFile(scriptFile, scriptFileMinioObjectKey, null,scriptDirMetadataInfo.getBucketName()); // 创建目录元数据并保存到数据库 FileMetadataInfo fileInfo = createFileMetadata(scriptFileMinioObjectKey, originalFilename, null, @@ -1965,7 +1975,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { fileMetadataInfoService.save(fileInfo); return SdmResponse.success(fileInfo.getId()); } catch (Exception e) { - minioService.deleteFile(scriptFileMinioObjectKey); + minioService.deleteFile(scriptFileMinioObjectKey, scriptDirMetadataInfo.getBucketName() ); log.error("上传传脚本文件", e); throw new RuntimeException("上传传脚本文件: " + e.getMessage(), e); } @@ -1975,7 +1985,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { public SdmResponse updateScriptFile(UpdateScriptReq req) { // 先创建 script 目录 String dirMinioObjectKey = getDirMinioObjectKey(DirTypeEnum.SCRIPT_DIR.getDirName()); - Optional scriptDirInfo = getFileMetadataInfoByObjectKey(dirMinioObjectKey,null ); + Optional scriptDirInfo = getFileMetadataInfoByObjectKey(dirMinioObjectKey,ThreadLocalContext.getTenantId() ); // 检查目录是否已存在 if (!scriptDirInfo.isPresent()) { return SdmResponse.failed("script 目录不存在,等待initSystemDirectory 初始化完成"); @@ -2003,7 +2013,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { String scriptFileMinioObjectKey = getFileMinioObjectKey(scriptDirMetadataInfo.getObjectKey() + newFilename); try { - minioService.uploadFile(updateFile, scriptFileMinioObjectKey, null); + minioService.uploadFile(updateFile, scriptFileMinioObjectKey, null,scriptDirMetadataInfo.getBucketName()); // 创建目录元数据并保存到数据库 fileMetadataInfoService.lambdaUpdate() @@ -2014,7 +2024,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { .update(); return SdmResponse.success(req.getScriptFileId()); } catch (Exception e) { - minioService.deleteFile(scriptFileMinioObjectKey); + minioService.deleteFile(scriptFileMinioObjectKey, scriptDirMetadataInfo.getBucketName()); log.error("更新传脚本文件", e); throw new RuntimeException("更新传脚本文件: " + e.getMessage(), e); } @@ -2039,7 +2049,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { String originalName = fileMetadataInfo.getOriginalName(); // 由于FileMetadataInfo中没有fileType字段,我们从originalName推断MIME类型 String contentType = getContentTypeByFileName(originalName); - minioService.downloadFile(objectKey,response,originalName,true,contentType); + minioService.downloadFile(objectKey,fileMetadataInfo.getBucketName(),response,originalName,true,contentType); } catch (Exception e) { log.error("预览图片失败: fileId={}", fileId, e); try { @@ -2085,7 +2095,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { } String objectKey = fileMetadataInfo.getObjectKey(); - String minioPresignedUrl = minioService.getMinioPresignedUrl(objectKey); + String minioPresignedUrl = minioService.getMinioPresignedUrl(objectKey,fileMetadataInfo.getBucketName()); String encodeKKFileViewURL = buildUrlWithTime(minioPresignedUrl, DateUtil.now()); KKFileViewURLFromMinioResp kkFileViewURLFromMinioResp = new KKFileViewURLFromMinioResp(minioPresignedUrl, encodeKKFileViewURL); // kkFileView已经二次开发,需要拼接 &lastModified=2025-10-13%2016:12:12 @@ -2501,7 +2511,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { // 分片上传,异常的时候删除临时目录,假如后面断点续传,某些场景的删除要最后确定失败再删除 private void deleteTempFileAfterFailed(String tempFilePath,String finalChunkBucket) { CompletableFuture.runAsync(() -> { - minioService.deleteDirectoryRecursively2(tempFilePath, finalChunkBucket); + minioService.deleteDirectoryRecursively(tempFilePath, finalChunkBucket); }, nonSensitiveTaskPool); } @@ -2683,7 +2693,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { } // 3. 列出 MinIO 中该前缀下的所有对象(递归) - Iterable> results = minioService.listObjects(folderObjectKey); + Iterable> results = minioService.listObjects(folderObjectKey, folderInfo.getBucketName()); // 编译正则表达式(如果提供) Pattern pattern = null; @@ -2737,7 +2747,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { Files.createDirectories(localFilePath.getParent()); // 流式下载 - try (InputStream inputStream = minioService.getMinioInputStream(objectKey); + try (InputStream inputStream = minioService.getMinioInputStream(objectKey,folderInfo.getBucketName()); FileOutputStream outputStream = new FileOutputStream(localFilePath.toFile())) { byte[] buffer = new byte[8192]; @@ -2884,7 +2894,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { } catch (final IOException e) { } MultipartFile file = new MockMultipartFile(fileMetadataInfo.getOriginalName(), fileMetadataInfo.getOriginalName(), contentType, content); - minioService.uploadFile(file, fileMetadataInfo.getObjectKey(), null); + minioService.uploadFile(file, fileMetadataInfo.getObjectKey(), null,fileMetadataInfo.getBucketName()); return true; } @@ -2903,7 +2913,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { return SdmResponse.failed("用户对该文件无下载权限"); } // 从MinIO下载文件 - byte[] fileData = minioService.downloadFile(fileObjectKey); + byte[] fileData = minioService.downloadFile(fileObjectKey,fileMetadataInfo.getBucketName()); // 写入响应流 File folder = new File(path); folder.mkdir(); diff --git a/data/src/main/java/com/sdm/data/service/impl/dataFileHandle/DeleteApproveStrategy.java b/data/src/main/java/com/sdm/data/service/impl/dataFileHandle/DeleteApproveStrategy.java index a4d9e58d..9cc34e9e 100644 --- a/data/src/main/java/com/sdm/data/service/impl/dataFileHandle/DeleteApproveStrategy.java +++ b/data/src/main/java/com/sdm/data/service/impl/dataFileHandle/DeleteApproveStrategy.java @@ -31,7 +31,7 @@ public class DeleteApproveStrategy implements ApproveStrategy { // 审批通过 if (NumberConstants.TWO == status) { // 删除MinIO文件 - minioService.deleteFile(metadata.getObjectKey()); + minioService.deleteFile(metadata.getObjectKey(),metadata.getBucketName() ); // 删除数据库记录 service.removeById(metadata.getId()); fileStorageService.remove(new LambdaQueryWrapper().eq(FileStorage::getFileId, metadata.getId())); diff --git a/data/src/main/java/com/sdm/data/service/impl/dataFileHandle/ModifyFileApproveStrategy.java b/data/src/main/java/com/sdm/data/service/impl/dataFileHandle/ModifyFileApproveStrategy.java index 8fd71076..ca34fa46 100644 --- a/data/src/main/java/com/sdm/data/service/impl/dataFileHandle/ModifyFileApproveStrategy.java +++ b/data/src/main/java/com/sdm/data/service/impl/dataFileHandle/ModifyFileApproveStrategy.java @@ -53,7 +53,7 @@ public class ModifyFileApproveStrategy implements ApproveStrategy { // 审批不通过 if (NumberConstants.THREE == status) { // 删除MinIO中修改的文件 - minioService.deleteFile(metadata.getObjectKey()); + minioService.deleteFile(metadata.getObjectKey(), metadata.getBucketName()); // 删除修改产生的新记录 service.removeById(metadata.getId()); fileMetadataExtensionService.remove(new LambdaQueryWrapper().eq(FileMetadataExtension::getTFilemetaId, metadata.getId())); diff --git a/data/src/main/java/com/sdm/data/service/impl/dataFileHandle/UploadApproveStrategy.java b/data/src/main/java/com/sdm/data/service/impl/dataFileHandle/UploadApproveStrategy.java index 89b14801..6e860017 100644 --- a/data/src/main/java/com/sdm/data/service/impl/dataFileHandle/UploadApproveStrategy.java +++ b/data/src/main/java/com/sdm/data/service/impl/dataFileHandle/UploadApproveStrategy.java @@ -43,7 +43,7 @@ public class UploadApproveStrategy implements ApproveStrategy { // 删除MinIO文件 ListremoveIds = new ArrayList<>(); approveMetadataInfos.forEach(metadata -> { - minioService.deleteFile(metadata.getObjectKey()); + minioService.deleteFile(metadata.getObjectKey(), metadata.getBucketName()); removeIds.add(metadata.getId()); }); // 删除数据库记录 diff --git a/data/src/main/java/com/sdm/data/service/minio/MinioService.java b/data/src/main/java/com/sdm/data/service/minio/MinioService.java index 75b980c7..404a35f2 100644 --- a/data/src/main/java/com/sdm/data/service/minio/MinioService.java +++ b/data/src/main/java/com/sdm/data/service/minio/MinioService.java @@ -2,6 +2,7 @@ package com.sdm.data.service.minio; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; +import com.sdm.common.common.ThreadLocalContext; import com.sdm.common.entity.constants.NumberConstants; import com.sdm.common.log.CoreLogger; import com.sdm.data.config.MinioConfig; @@ -31,10 +32,8 @@ import java.nio.channels.WritableByteChannel; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Map; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -47,6 +46,10 @@ public class MinioService implements IMinioService { // 可配置缓冲区大小) 16KB private static final int DEFAULT_BUFFER_SIZE = 16384; + // 1. 本地缓存:记录已经确认存在的桶,避免每次上传都请求 MinIO 网络 + // 使用 ConcurrentHashMap.newKeySet() 保证高并发下的线程安全 + private final Set existingBucketsCache = ConcurrentHashMap.newKeySet(); + @Autowired public MinioService(MinioClient minioClient, MinioConfig minioConfig) { this.minioClient = minioClient; @@ -58,13 +61,6 @@ public class MinioService implements IMinioService { */ @PostConstruct public void initializeBucket() { - try { - createBucketIfNotExists(minioConfig.getSpdmBucket()); - log.info("桶{}初始化完成", minioConfig.getSpdmBucket()); - } catch (Exception e) { - log.error("桶{}初始化失败", minioConfig.getSpdmBucket(), e); - } - try { createBucketIfNotExists(minioConfig.getSecretBusinessBucket()); log.info("桶{}初始化完成", minioConfig.getSecretBusinessBucket()); @@ -75,11 +71,97 @@ public class MinioService implements IMinioService { } + /** + * 【初始化专用】从 MinIO 拉取现有桶列表(缓存预热) + * 该方法由 Initializer 调用,只消耗一次网络请求 + */ + public Set preloadBucketCache() { + log.info("正在从 MinIO 拉取现有桶列表进行缓存预热..."); + Set currentMinioBuckets = new HashSet<>(); + try { + List buckets = minioClient.listBuckets(); + String baseName = minioConfig.getSpdmBucket(); - public String getBucketName() { - return minioConfig.getSpdmBucket(); + for (Bucket bucket : buckets) { + // 只要是以 spdm 开头的,都认为是系统的业务桶,加入缓存 + if (bucket.name().startsWith(baseName)) { + existingBucketsCache.add(bucket.name()); + currentMinioBuckets.add(bucket.name()); + } + } + log.info("MinIO 桶缓存预热完成,共加载 {} 个桶。", existingBucketsCache.size()); + } catch (Exception e) { + log.error("预热缓存失败(不影响核心功能,将降级为惰性检查)", e); + } + return currentMinioBuckets; } + /** + * 【初始化专用】创建指定桶(如果缓存里没有) + * 供初始化器批量补齐使用 + */ + public void createBucketIfAbsent(String bucketName) { + if (existingBucketsCache.contains(bucketName)) { + return; + } + try { + boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build()); + if (!found) { + log.info("系统初始化:创建缺失的租户桶 -> {}", bucketName); + minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); + // TODO: 如果需要,可以在这里设置生命周期策略 (Lifecycle Config) + } + existingBucketsCache.add(bucketName); + } catch (Exception e) { + log.error("创建桶失败: {}", bucketName, e); + } + } + + /** + * 【核心方法】获取当前租户的桶名(带 缓存+懒加载+双重锁 保障) + * 供业务层上传时调用 + */ + public String getCurrentTenantBucketName() { + Long tenantId = ThreadLocalContext.getTenantId(); + + if (tenantId == null) { + log.error("严重错误:尝试进行 MinIO 操作但缺失租户上下文!"); + throw new RuntimeException("系统内部错误:租户上下文缺失,操作被拒绝。"); + } + + // 2. 拼接目标桶名,例如: spdm-1001 + String bucketName = minioConfig.getSpdmBucket() + "-" + tenantId; + + // 3. 第一层保障:查本地缓存 (性能极高,无网络IO) + if (existingBucketsCache.contains(bucketName)) { + return bucketName; + } + + // 4. 第二层保障:惰性创建 (防止初始化失败或运行时新增租户) + // 使用 intern() 锁住字符串,防止同一个租户并发创建,但不阻塞其他租户 + synchronized (bucketName.intern()) { + // 双重检查 + if (existingBucketsCache.contains(bucketName)) { + return bucketName; + } + try { + boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build()); + if (!found) { + log.info("运行时检测到新租户桶不存在,正在创建: {}", bucketName); + minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); + } + existingBucketsCache.add(bucketName); + } catch (Exception e) { + log.error("运行时初始化租户桶失败: {}", bucketName, e); + throw new RuntimeException("文件存储初始化失败,请联系管理员", e); + } + } + return bucketName; + } + + + + public String getSecretBusinessBucketName() { return minioConfig.getSecretBusinessBucket(); } @@ -99,7 +181,7 @@ public class MinioService implements IMinioService { // 使用空内容创建目录对象 ObjectWriteResponse objectWriteResponse = minioClient.putObject( PutObjectArgs.builder() - .bucket(bucketName) + .bucket(getBucketName(bucketName)) .object(objectKey) .stream(new ByteArrayInputStream(new byte[0]), 0, -1) .build()); @@ -108,14 +190,6 @@ public class MinioService implements IMinioService { } } - /** - * 创建目录(默认使用spdmBucket) - * - * @param objectKey 绝对路径目录名 - */ - public void createDirectoryByObjectKey(String objectKey) { - createDirectoryByObjectKey(objectKey, minioConfig.getSpdmBucket()); - } private static String dealDirPath(String dirPath) { if (dirPath.startsWith("/")) { @@ -143,7 +217,7 @@ public class MinioService implements IMinioService { // 列出目录下的所有对象 Iterable> results = minioClient.listObjects( ListObjectsArgs.builder() - .bucket(bucketName) + .bucket(getBucketName(bucketName)) .prefix(directoryName) .recursive(true) .build()); @@ -163,7 +237,7 @@ public class MinioService implements IMinioService { Iterable> deleteErrors = minioClient.removeObjects( RemoveObjectsArgs.builder() - .bucket(bucketName) + .bucket(getBucketName(bucketName)) .objects(deleteObjects) .build()); @@ -179,31 +253,19 @@ public class MinioService implements IMinioService { } } - /** - * 递归删除指定目录下的所有对象(默认使用spdmBucket)。 - * - * @param directoryName 目录名称 - */ - public void deleteDirectoryRecursively(String directoryName) { - deleteDirectoryRecursively(directoryName, minioConfig.getSpdmBucket()); - } - - public void deleteDirectoryRecursively2(String directoryName,String bucketName) { - deleteDirectoryRecursively(directoryName, bucketName); - } - @Override - public Iterable> listObjects(String directoryName) { + public Iterable> listObjects(String directoryName, String bucketName) { // 列出目录下的所有对象 return minioClient.listObjects( ListObjectsArgs.builder() - .bucket(minioConfig.getSpdmBucket()) + .bucket(getBucketName(bucketName)) .prefix(directoryName) .recursive(true) .build()); } + /** * 从MinIO删除文件 * @param minioObjectKey 文件名 @@ -215,7 +277,7 @@ public class MinioService implements IMinioService { minioObjectKey = dealDirPath(minioObjectKey); minioClient.removeObject( RemoveObjectArgs.builder() - .bucket(bucketName) + .bucket(getBucketName(bucketName)) .object(minioObjectKey) .build()); } catch (Exception e) { @@ -223,13 +285,6 @@ public class MinioService implements IMinioService { } } - /** - * 从MinIO删除文件(默认使用spdmBucket) - */ - public void deleteFile(String minioObjectKey) { - deleteFile(minioObjectKey, minioConfig.getSpdmBucket()); - } - /** * 上传文件到MinIO * @param file 文件对象 @@ -240,7 +295,7 @@ public class MinioService implements IMinioService { public void uploadFile(MultipartFile file, String objectName, Map tags, String bucketName) { try { // 检查存储桶是否存在,不存在则创建(为了向后兼容,仍然保留此处检查) - createBucketIfNotExists(bucketName); + createBucketIfNotExists(getBucketName(bucketName)); // 转换标签为MinIO格式 Map tagMap = tags != null ? tags : Map.of(); @@ -249,7 +304,7 @@ public class MinioService implements IMinioService { try (InputStream inputStream = file.getInputStream()) { minioClient.putObject( PutObjectArgs.builder() - .bucket(bucketName) + .bucket(getBucketName(bucketName)) .object(objectName) .stream(inputStream, file.getSize(), -1) .contentType(file.getContentType()) @@ -262,13 +317,6 @@ public class MinioService implements IMinioService { } } - /** - * 上传文件到MinIO(默认使用spdmBucket) - */ - public void uploadFile(MultipartFile file, String objectName, Map tags) { - uploadFile(file, objectName, tags, minioConfig.getSpdmBucket()); - } - /** * 重命名MinIO中的文件 * @@ -279,6 +327,7 @@ public class MinioService implements IMinioService { */ public void renameFile(String oldObjectName, String newObjectName, String bucketName) { try { + bucketName =getBucketName(bucketName); // 参数校验 if (!StringUtils.hasText(oldObjectName)) { throw new IllegalArgumentException("原文件名不能为空"); @@ -319,17 +368,6 @@ public class MinioService implements IMinioService { } } - /** - * 重命名MinIO中的文件(默认使用spdmBucket) - * - * @param oldObjectName 原文件名 - * @param newObjectName 新文件名 - * @throws Exception 异常信息 - */ - public void renameFile(String oldObjectName, String newObjectName) { - renameFile(oldObjectName, newObjectName, minioConfig.getSpdmBucket()); - } - /** * 从MinIO下载文件 @@ -340,38 +378,26 @@ public class MinioService implements IMinioService { try (InputStream stream = minioClient.getObject( GetObjectArgs.builder() - .bucket(bucketName) + .bucket(getBucketName(bucketName)) .object(objectName) .build())) { return IOUtils.toByteArray(stream); } } - /** - * 从MinIO下载文件(默认使用spdmBucket) - */ - public byte[] downloadFile(String objectName) throws Exception { - return downloadFile(objectName, minioConfig.getSpdmBucket()); - } - - @Override - public void downloadFile(String objectName, HttpServletResponse response,String encodedFileName,Boolean preview,String contentType) throws Exception { - downloadFile(objectName, minioConfig.getSpdmBucket(),response,encodedFileName,preview,contentType); - } - // 流式响应的返回 public void downloadFile(String objectName, String bucketName, HttpServletResponse response,String encodedFileName,Boolean preview, String contentType) throws Exception { // 1. 获取文件元数据(大小) StatObjectResponse stat = minioClient.statObject( StatObjectArgs.builder() - .bucket(bucketName) + .bucket(getBucketName(bucketName)) .object(objectName) .build()); long fileSize = stat.size(); // 1. 获取MinIO的流式数据(使用try-with-resources确保资源释放) try (InputStream stream = minioClient.getObject( GetObjectArgs.builder() - .bucket(bucketName) + .bucket(getBucketName(bucketName)) .object(objectName) .build()); ReadableByteChannel inChannel = Channels.newChannel(stream); @@ -402,16 +428,11 @@ public class MinioService implements IMinioService { } - @Override - public InputStream getMinioInputStream(String objectName) { - return getMinioInputStream(objectName, minioConfig.getSpdmBucket()); - } - public InputStream getMinioInputStream(String objectName, String bucketName) { try { return minioClient.getObject( GetObjectArgs.builder() - .bucket(bucketName) + .bucket(getBucketName(bucketName)) .object(objectName) .build()); } catch (Exception e) { @@ -429,7 +450,7 @@ public class MinioService implements IMinioService { List result = new ArrayList<>(); Iterable> results = minioClient.listObjects( ListObjectsArgs.builder() - .bucket(bucketName) + .bucket(getBucketName(bucketName)) .build()); for (Result itemResult : results) { @@ -441,12 +462,6 @@ public class MinioService implements IMinioService { return result; } - /** - * 根据标签查询文件(默认使用spdmBucket) - */ - public List queryFilesByTags(Map tags) throws Exception { - return queryFilesByTags(tags, minioConfig.getSpdmBucket()); - } /** * 检查文件标签是否匹配查询条件 @@ -556,18 +571,12 @@ public class MinioService implements IMinioService { } } - - - @Override - public String getMinioPresignedUrl(String objectKey) { - return getMinioPresignedUrl(objectKey, minioConfig.getSpdmBucket()); - } public String getMinioPresignedUrl(String objectKey, String bucketName) { String presignedUrl = null; try { // 生成预签名URL - GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder().method(Method.GET).bucket(bucketName).object(objectKey).expiry(3600, TimeUnit.SECONDS).build(); + GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder().method(Method.GET).bucket(getBucketName(bucketName)).object(objectKey).expiry(3600, TimeUnit.SECONDS).build(); presignedUrl = minioClient.getPresignedObjectUrl(args); } catch (Exception e) { log.error("获取文件预览URL失败: " + objectKey, e); @@ -591,7 +600,7 @@ public class MinioService implements IMinioService { try { inputStream = file.getInputStream(); minioClient.putObject(PutObjectArgs.builder() - .bucket(bucketName) + .bucket(getBucketName(bucketName)) .object(fileName) .stream(inputStream, file.getSize(), -1) .build()); @@ -624,7 +633,7 @@ public class MinioService implements IMinioService { public Boolean merge(String tempBucketName,String tempFilePath,String mergeBucketName,String fileName) { try { List sourceObjectList = new ArrayList(); - List folderList = getFolderList(tempBucketName,tempFilePath); + List folderList = getFolderList(getBucketName(tempBucketName),tempFilePath); List fileNames = new ArrayList<>(); if (!folderList.isEmpty()) { for (Object value : folderList) { @@ -640,12 +649,12 @@ public class MinioService implements IMinioService { return Integer.compare(num1, num2); }); for (String name : fileNames) { - sourceObjectList.add(ComposeSource.builder().bucket(tempBucketName).object(name).build()); + sourceObjectList.add(ComposeSource.builder().bucket(getBucketName(tempBucketName)).object(name).build()); } } minioClient.composeObject( ComposeObjectArgs.builder() - .bucket(mergeBucketName) + .bucket(getBucketName(mergeBucketName)) .object(fileName) .sources(sourceObjectList) .build()); @@ -674,7 +683,7 @@ public class MinioService implements IMinioService { // 2. 调用 listObjects,使用规范化后的 prefix Iterable> results = minioClient.listObjects( ListObjectsArgs.builder() - .bucket(bucketName) + .bucket(getBucketName(bucketName)) .prefix(prefix) // 关键:使用规范化后的前缀 .recursive(false) // 只查当前前缀的直接子对象(即“当前目录”下的文件) .build() @@ -739,5 +748,10 @@ public class MinioService implements IMinioService { } + private String getBucketName(String bucketName) { + return StringUtils.hasText(bucketName) ? bucketName : getCurrentTenantBucketName(); + } + + } \ No newline at end of file diff --git a/data/src/main/java/com/sdm/data/service/minio/MinioTenantInitializer.java b/data/src/main/java/com/sdm/data/service/minio/MinioTenantInitializer.java new file mode 100644 index 00000000..c5403159 --- /dev/null +++ b/data/src/main/java/com/sdm/data/service/minio/MinioTenantInitializer.java @@ -0,0 +1,130 @@ +package com.sdm.data.service.minio; + +import com.sdm.common.common.SdmResponse; +import com.sdm.common.entity.req.data.TenantListReq; +import com.sdm.common.entity.resp.PageDataResp; +import com.sdm.common.entity.resp.system.TenantResp; +import com.sdm.common.feign.inter.system.ISysTenantFeignClient; +import com.sdm.common.feign.inter.system.ISysUserFeignClient; +import com.sdm.data.config.MinioConfig; +import com.sdm.data.service.IDataFileService; +import com.sdm.data.service.IMinioService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +@Component +@Slf4j +public class MinioTenantInitializer implements CommandLineRunner { + @Autowired + private IMinioService minioService; + @Autowired + private MinioConfig minioConfig; + @Autowired + private ISysTenantFeignClient sysTenantFeignClient; + + @Autowired + private IDataFileService dataFileService; + + // 配置重试策略 + private static final int MAX_RETRIES = 12; // 最多重试12次 + private static final int RETRY_DELAY_SECONDS = 5; // 每次间隔5秒,共覆盖60秒启动延迟 + + @Override + public void run(String... args) { + // 【关键】使用异步线程执行,绝对不要阻塞 Data 服务的主线程启动 + CompletableFuture.runAsync(this::initBuckets); + } + + private void initBuckets() { + log.info(">>> [MinIO Bucket Init] 开始异步初始化租户桶..."); + + List allTenantIds = null; + int attempt = 0; + + // 1. 循环重试获取租户信息 (解决 Feign 调用失败问题) + while (attempt < MAX_RETRIES) { + try { + // 调用 Feign 接口 (建议 System 服务提供一个只查 ID 的轻量接口) + TenantListReq req = new TenantListReq(); + req.setCurrent(1); + req.setSize(1000); + SdmResponse>> response = sysTenantFeignClient.listTenant(req); + log.info(">>> [MinIO Bucket Init] 获取租户列表成功,共有 {} 个租户", response.getData().getTotal()); + + if (response != null && response.isSuccess() && response.getData() != null && response.getData().getData() != null) { + allTenantIds = response.getData().getData().stream() + .map(TenantResp::getTenantId) + .toList(); + log.info(">>> [MinIO Bucket Init] 成功连接 System 服务,获取到 {} 个租户", allTenantIds.size()); + break; + } else { + log.warn(">>> [MinIO Bucket Init] System 服务返回数据为空或异常"); + } + } catch (Exception e) { + attempt++; + log.warn(">>> [MinIO Bucket Init] 连接 System 服务失败 (第 {}/{} 次),{} 秒后重试...", + attempt, MAX_RETRIES, RETRY_DELAY_SECONDS); + try { + TimeUnit.SECONDS.sleep(RETRY_DELAY_SECONDS); + } catch (InterruptedException ignored) { + Thread.currentThread().interrupt(); + return; + } + } + } + + // 2. 如果 System 服务彻底挂了,放弃初始化,依靠 MinioService 的运行时懒加载兜底 + if (allTenantIds == null) { + log.error(">>> [MinIO Bucket Init] 放弃初始化。系统将依赖 '运行时懒加载' 机制自动创建缺失的桶。"); + // 这里仅仅是预热失败,不抛异常,不影响 Data 服务运行 + return; + } + + long start = System.currentTimeMillis(); + + // 3. 预热缓存并获取 MinIO 现有桶 + Set existingBuckets = minioService.preloadBucketCache(); + + // 4. 计算预期应该存在的桶 (spdm-1001, spdm-1002...) + String baseBucket = minioConfig.getSpdmBucket(); + Set requiredBuckets = allTenantIds.stream() + .map(id -> baseBucket + "-" + id) + .collect(Collectors.toSet()); + + // 5. 求差集:需要创建 = 理论存在 - 实际存在 + requiredBuckets.removeAll(existingBuckets); + + // 6. 补齐缺失 (使用并行流加快速度) + if (!requiredBuckets.isEmpty()) { + log.info(">>> [MinIO Bucket Init] 发现 {} 个缺失的租户桶,开始补齐...", requiredBuckets.size()); + requiredBuckets.parallelStream().forEach(bucketName -> { + minioService.createBucketIfAbsent(bucketName); + }); + } else { + log.info(">>> [MinIO Bucket Init] 桶状态一致,无需补齐。"); + } + + // 7. 确保每个租户的 DB 和 MinIO 目录结构都已初始化 + log.info(">>> [Tenant Init] 开始检查 {} 个租户的基础目录结构...", allTenantIds.size()); + + // 使用并行流加速(如果有几千个租户,串行会很慢) + allTenantIds.parallelStream().forEach(tenantId -> { + try { + dataFileService.initTenantData(tenantId); + } catch (Exception e) { + log.error(">>> [Tenant Init] 租户 {} 初始化失败", tenantId, e); + } + }); + + log.info(">>> [MinIO Bucket Init] 初始化流程结束,耗时: {} ms", System.currentTimeMillis() - start); + } + +} diff --git a/system/src/main/java/com/sdm/system/controller/SysTenantController.java b/system/src/main/java/com/sdm/system/controller/SysTenantController.java index f3a858a9..90a8108b 100644 --- a/system/src/main/java/com/sdm/system/controller/SysTenantController.java +++ b/system/src/main/java/com/sdm/system/controller/SysTenantController.java @@ -1,10 +1,12 @@ package com.sdm.system.controller; import com.sdm.common.common.SdmResponse; -import com.sdm.common.entity.ExportExcelFormat; +import com.sdm.common.entity.resp.PageDataResp; +import com.sdm.common.feign.inter.system.ISysTenantFeignClient; import com.sdm.system.model.req.tenant.ExportExcelReq; -import com.sdm.system.model.req.tenant.TenantListReq; +import com.sdm.common.entity.req.data.TenantListReq; import com.sdm.system.model.req.tenant.TenantReq; +import com.sdm.common.entity.resp.system.TenantResp; import com.sdm.system.service.ISysTenantService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -27,7 +29,7 @@ import java.util.List; @RestController @RequestMapping("/tenant") @Tag(name = "租户信息管理") -public class SysTenantController { +public class SysTenantController implements ISysTenantFeignClient { @Autowired @Qualifier("tenantService") @@ -74,7 +76,7 @@ public class SysTenantController { */ @PostMapping("/list") @Operation(summary = "查询租户列表") - public SdmResponse listTenant(@RequestBody @Validated TenantListReq tenant) { + public SdmResponse>> listTenant(@RequestBody @Validated TenantListReq tenant) { return tenantService.listTenant(tenant); } diff --git a/system/src/main/java/com/sdm/system/model/req/tenant/ExportExcelReq.java b/system/src/main/java/com/sdm/system/model/req/tenant/ExportExcelReq.java index 12411667..4d4434aa 100644 --- a/system/src/main/java/com/sdm/system/model/req/tenant/ExportExcelReq.java +++ b/system/src/main/java/com/sdm/system/model/req/tenant/ExportExcelReq.java @@ -1,6 +1,7 @@ package com.sdm.system.model.req.tenant; import com.sdm.common.entity.ExportExcelFormat; +import com.sdm.common.entity.req.data.TenantListReq; import io.swagger.v3.oas.annotations.media.Schema; import java.util.ArrayList; diff --git a/system/src/main/java/com/sdm/system/service/ISysTenantService.java b/system/src/main/java/com/sdm/system/service/ISysTenantService.java index 902c4d59..40bf82a6 100644 --- a/system/src/main/java/com/sdm/system/service/ISysTenantService.java +++ b/system/src/main/java/com/sdm/system/service/ISysTenantService.java @@ -2,11 +2,12 @@ package com.sdm.system.service; import com.baomidou.mybatisplus.extension.service.IService; import com.sdm.common.common.SdmResponse; -import com.sdm.common.entity.ExportExcelFormat; +import com.sdm.common.entity.resp.PageDataResp; import com.sdm.system.model.entity.SysTenant; import com.sdm.system.model.req.tenant.ExportExcelReq; -import com.sdm.system.model.req.tenant.TenantListReq; +import com.sdm.common.entity.req.data.TenantListReq; import com.sdm.system.model.req.tenant.TenantReq; +import com.sdm.common.entity.resp.system.TenantResp; import jakarta.servlet.http.HttpServletResponse; import java.util.List; @@ -44,7 +45,7 @@ public interface ISysTenantService extends IService { * @param tenant 租户信息 * @return */ - SdmResponse listTenant(TenantListReq tenant); + SdmResponse>> listTenant(TenantListReq tenant); /** * 导出租户列表 diff --git a/system/src/main/java/com/sdm/system/service/impl/CID/CIDtenantServiceImpl.java b/system/src/main/java/com/sdm/system/service/impl/CID/CIDtenantServiceImpl.java index d62d5bf0..f86c21aa 100644 --- a/system/src/main/java/com/sdm/system/service/impl/CID/CIDtenantServiceImpl.java +++ b/system/src/main/java/com/sdm/system/service/impl/CID/CIDtenantServiceImpl.java @@ -6,16 +6,14 @@ import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONObject; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.sdm.common.common.SdmResponse; -import com.sdm.common.entity.ExportExcelFormat; import com.sdm.common.entity.resp.PageDataResp; -import com.sdm.common.utils.excel.ExcelSheet; import com.sdm.common.utils.excel.ExcelUtil; import com.sdm.system.dao.SysTenantMapper; import com.sdm.system.model.entity.SysTenant; import com.sdm.system.model.req.tenant.ExportExcelReq; -import com.sdm.system.model.req.tenant.TenantListReq; +import com.sdm.common.entity.req.data.TenantListReq; import com.sdm.system.model.req.tenant.TenantReq; -import com.sdm.system.model.resp.TenantResp; +import com.sdm.common.entity.resp.system.TenantResp; import com.sdm.system.service.ISysTenantService; import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Value; @@ -62,7 +60,7 @@ public class CIDtenantServiceImpl extends ServiceImpl>> listTenant(TenantListReq tenant) { try { log.info("正在向CID租户列表API发送请求: {},参数: {}", cidUrl + listTenant, JSON.toJSONString(tenant)); String CidTenantResp = HttpUtil.post(cidUrl + listTenant,JSON.toJSONString( tenant)); diff --git a/system/src/main/java/com/sdm/system/service/impl/LocalSysTenantServiceImpl.java b/system/src/main/java/com/sdm/system/service/impl/LocalSysTenantServiceImpl.java index f810374c..7dccfcf8 100644 --- a/system/src/main/java/com/sdm/system/service/impl/LocalSysTenantServiceImpl.java +++ b/system/src/main/java/com/sdm/system/service/impl/LocalSysTenantServiceImpl.java @@ -5,14 +5,13 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; import com.sdm.common.common.SdmResponse; -import com.sdm.common.entity.ExportExcelFormat; import com.sdm.common.utils.PageUtils; import com.sdm.system.dao.SysTenantMapper; import com.sdm.system.model.entity.SysTenant; import com.sdm.system.model.req.tenant.ExportExcelReq; -import com.sdm.system.model.req.tenant.TenantListReq; +import com.sdm.common.entity.req.data.TenantListReq; import com.sdm.system.model.req.tenant.TenantReq; -import com.sdm.system.model.resp.TenantResp; +import com.sdm.common.entity.resp.system.TenantResp; import com.sdm.system.service.ISysTenantService; import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.BeanUtils;