fix:批量同步节点下的普通文件夹,保存uuid
This commit is contained in:
@@ -3827,118 +3827,208 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService {
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public SdmResponse<BatchCreateNormalDirResp> batchCreateNormalDirs(BatchCreateNormalDirReq req) {
|
||||
// 1. 基础校验
|
||||
if (req.getParentUUId() == null) {
|
||||
return SdmResponse.failed("父文件夹ID不能为空");
|
||||
log.info("开始执行批量创建普通文件夹,父目UUID: {}, folderItems数量: {}",
|
||||
req.getParentUUId(), req.getFolderItems() == null ? 0 : req.getFolderItems().size());
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
// 1. 参数校验
|
||||
SdmResponse<Void> validateResult = validateBatchCreateNormalDirRequest(req);
|
||||
if (!validateResult.isSuccess()) {
|
||||
log.error("批量创建普通文件夹参数校验失败: {}", validateResult.getMessage());
|
||||
return SdmResponse.failed(validateResult.getMessage());
|
||||
}
|
||||
if (CollectionUtils.isEmpty(req.getFolderNames())) {
|
||||
return SdmResponse.failed("文件夹名称列表不能为空");
|
||||
}
|
||||
|
||||
|
||||
// 2. 父目录校验与权限检查
|
||||
FileMetadataInfo parentDir = fileMetadataInfoService.lambdaQuery()
|
||||
.eq(FileMetadataInfo::getRelatedResourceUuid, req.getParentUUId())
|
||||
.one();
|
||||
if (parentDir == null) {
|
||||
return SdmResponse.failed("父文件夹不存在");
|
||||
SdmResponse<FileMetadataInfo> parentDirResult = validateParentDirAndPermission(req.getParentUUId());
|
||||
if (!parentDirResult.isSuccess()) {
|
||||
log.error("父目录校验失败: {}", parentDirResult.getMessage());
|
||||
return SdmResponse.failed(parentDirResult.getMessage());
|
||||
}
|
||||
|
||||
// 权限检查(需要写入权限)
|
||||
boolean hasWritePermission = fileUserPermissionService.hasFilePermission(parentDir.getId(), ThreadLocalContext.getUserId(), FilePermissionEnum.WRITE);
|
||||
if (!hasWritePermission) {
|
||||
return SdmResponse.failed("没有写入权限");
|
||||
}
|
||||
|
||||
FileMetadataInfo parentDir = parentDirResult.getData();
|
||||
|
||||
BatchCreateNormalDirResp resp = new BatchCreateNormalDirResp();
|
||||
// 去重并过滤空名
|
||||
List<String> validFolderNames = req.getFolderNames().stream()
|
||||
.filter(name -> name != null && !name.trim().isEmpty())
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (validFolderNames.isEmpty()) {
|
||||
return SdmResponse.failed("有效文件夹名称为空");
|
||||
|
||||
// 3. 过滤并验证文件夹项
|
||||
List<FolderItemReq> validFolderItems = filterAndValidateFolderItems(req.getFolderItems());
|
||||
if (validFolderItems.isEmpty()) {
|
||||
log.error("有效文件夹项为空");
|
||||
return SdmResponse.failed("有效文件夹项为空");
|
||||
}
|
||||
|
||||
// 3. 检查数据库中是否已存在同名目录
|
||||
List<FileMetadataInfo> existingFiles = fileMetadataInfoService.lambdaQuery()
|
||||
.eq(FileMetadataInfo::getParentId, parentDir.getId())
|
||||
.eq(FileMetadataInfo::getTenantId, ThreadLocalContext.getTenantId())
|
||||
.eq(FileMetadataInfo::getDataType, DataTypeEnum.DIRECTORY.getValue())
|
||||
.in(FileMetadataInfo::getOriginalName, validFolderNames)
|
||||
.list();
|
||||
|
||||
Set<String> existingNames = existingFiles.stream()
|
||||
.map(FileMetadataInfo::getOriginalName)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
List<String> toCreateNames = new ArrayList<>();
|
||||
for (String name : validFolderNames) {
|
||||
if (existingNames.contains(name)) {
|
||||
resp.addFailure(name, "文件夹已存在");
|
||||
} else {
|
||||
toCreateNames.add(name);
|
||||
}
|
||||
}
|
||||
|
||||
if (toCreateNames.isEmpty()) {
|
||||
|
||||
// 4. 检查已存在的文件夹,分离出需要创建的项
|
||||
List<FolderItemReq> toCreateItems = checkExistingFoldersAndFilter(
|
||||
validFolderItems, parentDir, resp);
|
||||
if (toCreateItems.isEmpty()) {
|
||||
log.info("所有文件夹均已存在,无需创建");
|
||||
return SdmResponse.success(resp);
|
||||
}
|
||||
|
||||
// 4. 准备创建数据
|
||||
|
||||
// 5. 准备创建数据(元数据对象 + MinIO Key)
|
||||
List<FileMetadataInfo> newDirs = new ArrayList<>();
|
||||
List<String> minioKeys = new ArrayList<>();
|
||||
String parentObjectKey = parentDir.getObjectKey();
|
||||
|
||||
// 确保parentObjectKey以/结尾
|
||||
if (!parentObjectKey.endsWith("/")) {
|
||||
parentObjectKey += "/";
|
||||
}
|
||||
|
||||
for (String name : toCreateNames) {
|
||||
// 构造对象Key
|
||||
String objectKey = getDirMinioObjectKey(parentObjectKey + name);
|
||||
|
||||
// 创建元数据对象 (复用现有的createDirectoryMetadata方法)
|
||||
FileMetadataInfo dirInfo = createDirectoryMetadata(
|
||||
objectKey, name, false, parentDir.getId(),
|
||||
null, null, parentDir.getDirType());
|
||||
|
||||
newDirs.add(dirInfo);
|
||||
minioKeys.add(objectKey);
|
||||
}
|
||||
|
||||
prepareBatchCreateData(toCreateItems, parentDir, newDirs, minioKeys);
|
||||
|
||||
try {
|
||||
// 5. MinIO批量创建
|
||||
// 6. 批量MinIO创建
|
||||
minioService.batchCreateDirectories(minioKeys, minioService.getCurrentTenantBucketName());
|
||||
|
||||
// 6. 数据库批量插入
|
||||
fileMetadataInfoService.saveBatch(newDirs);
|
||||
|
||||
// 7. 批量添加权限
|
||||
List<FileUserPermission> permissions = newDirs.stream().map(dir -> {
|
||||
FileUserPermission p = new FileUserPermission();
|
||||
p.setTFilemetaId(dir.getId());
|
||||
p.setPermission(FilePermissionEnum.ALL.getValue());
|
||||
p.setUserId(ThreadLocalContext.getUserId());
|
||||
p.setTenantId(ThreadLocalContext.getTenantId());
|
||||
return p;
|
||||
}).collect(Collectors.toList());
|
||||
fileUserPermissionService.saveBatch(permissions, 500);
|
||||
|
||||
// 填充成功列表
|
||||
for (FileMetadataInfo dir : newDirs) {
|
||||
resp.addSuccess(dir.getId(), dir.getOriginalName());
|
||||
}
|
||||
|
||||
log.info("批量MinIO创建完成,数量: {}", minioKeys.size());
|
||||
|
||||
// 7. 数据库批量插入
|
||||
fileMetadataInfoService.saveBatch(newDirs, 500);
|
||||
log.info("数据库批量插入完成,数量: {}", newDirs.size());
|
||||
|
||||
// 8. 批量添加权限
|
||||
createBatchPermissions(newDirs, ThreadLocalContext.getTenantId());
|
||||
|
||||
// 9. 填充成功列表
|
||||
fillSuccessResponse(newDirs, resp);
|
||||
|
||||
long duration = System.currentTimeMillis() - startTime;
|
||||
log.info("批量创建普通文件夹成功,耗时: {}ms, 共创建{}个目录", duration, newDirs.size());
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("批量创建普通文件夹失败", e);
|
||||
// 补偿删除MinIO目录
|
||||
compensateDeleteMinioKeys(minioKeys);
|
||||
throw new RuntimeException("批量创建失败: " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
|
||||
return SdmResponse.success(resp);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验批量创建普通目录请求参数
|
||||
*/
|
||||
private SdmResponse<Void> validateBatchCreateNormalDirRequest(BatchCreateNormalDirReq req) {
|
||||
if (req == null) {
|
||||
return SdmResponse.failed("请求参数不能为空");
|
||||
}
|
||||
if (req.getParentUUId() == null) {
|
||||
return SdmResponse.failed("父文件夹UUID不能为空");
|
||||
}
|
||||
if (CollectionUtils.isEmpty(req.getFolderItems())) {
|
||||
return SdmResponse.failed("文件夹项列表不能为空");
|
||||
}
|
||||
return SdmResponse.success(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证父目录并检查权限
|
||||
*/
|
||||
private SdmResponse<FileMetadataInfo> validateParentDirAndPermission(String parentUuid) {
|
||||
FileMetadataInfo parentDir = fileMetadataInfoService.lambdaQuery()
|
||||
.eq(FileMetadataInfo::getRelatedResourceUuid, parentUuid)
|
||||
.one();
|
||||
if (parentDir == null) {
|
||||
return SdmResponse.failed("父文件夹不存在");
|
||||
}
|
||||
|
||||
// 权限检查(需要写入权限)
|
||||
boolean hasWritePermission = fileUserPermissionService.hasFilePermission(
|
||||
parentDir.getId(), ThreadLocalContext.getUserId(), FilePermissionEnum.WRITE);
|
||||
if (!hasWritePermission) {
|
||||
return SdmResponse.failed("没有写入权限");
|
||||
}
|
||||
|
||||
return SdmResponse.success(parentDir);
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤并验证文件夹项:去重、过滤空名
|
||||
*/
|
||||
private List<FolderItemReq> filterAndValidateFolderItems(List<FolderItemReq> folderItems) {
|
||||
// 使用 folderName 作为唯一标识去重
|
||||
Map<String, FolderItemReq> uniqueMap = new LinkedHashMap<>();
|
||||
for (FolderItemReq item : folderItems) {
|
||||
if (item != null && item.getFolderName() != null && !item.getFolderName().trim().isEmpty()) {
|
||||
// 后面的同名项会覆盖前面的,保留最后一个
|
||||
uniqueMap.put(item.getFolderName().trim(), item);
|
||||
}
|
||||
}
|
||||
return new ArrayList<>(uniqueMap.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查已存在的文件夹,过滤出需要创建的项
|
||||
*/
|
||||
private List<FolderItemReq> checkExistingFoldersAndFilter(
|
||||
List<FolderItemReq> validFolderItems,
|
||||
FileMetadataInfo parentDir,
|
||||
BatchCreateNormalDirResp resp) {
|
||||
|
||||
// 提取所有文件夹名称
|
||||
List<String> folderNames = validFolderItems.stream()
|
||||
.map(FolderItemReq::getFolderName)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 查询数据库中是否已存在同名目录
|
||||
List<FileMetadataInfo> existingFiles = fileMetadataInfoService.lambdaQuery()
|
||||
.eq(FileMetadataInfo::getParentId, parentDir.getId())
|
||||
.eq(FileMetadataInfo::getTenantId, ThreadLocalContext.getTenantId())
|
||||
.eq(FileMetadataInfo::getDataType, DataTypeEnum.DIRECTORY.getValue())
|
||||
.in(FileMetadataInfo::getOriginalName, folderNames)
|
||||
.list();
|
||||
|
||||
// 构建已存在的文件夹名称集合
|
||||
Set<String> existingNames = existingFiles.stream()
|
||||
.map(FileMetadataInfo::getOriginalName)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// 过滤出需要创建的项
|
||||
List<FolderItemReq> toCreateItems = new ArrayList<>();
|
||||
for (FolderItemReq item : validFolderItems) {
|
||||
if (existingNames.contains(item.getFolderName())) {
|
||||
resp.addFailure(item.getFolderName(), "文件夹已存在");
|
||||
} else {
|
||||
toCreateItems.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
return toCreateItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* 准备批量创建数据:构建元数据对象和MinIO Key
|
||||
*/
|
||||
private void prepareBatchCreateData(
|
||||
List<FolderItemReq> toCreateItems,
|
||||
FileMetadataInfo parentDir,
|
||||
List<FileMetadataInfo> newDirs,
|
||||
List<String> minioKeys) {
|
||||
|
||||
String parentObjectKey = parentDir.getObjectKey();
|
||||
// 确保 parentObjectKey 以 / 结尾
|
||||
if (!parentObjectKey.endsWith("/")) {
|
||||
parentObjectKey += "/";
|
||||
}
|
||||
|
||||
for (FolderItemReq item : toCreateItems) {
|
||||
// 构造对象Key
|
||||
String objectKey = getDirMinioObjectKey(parentObjectKey + item.getFolderName());
|
||||
|
||||
// 创建元数据对象(复用现有的 createDirectoryMetadata 方法)
|
||||
// 传入 folderUuid 作为 relatedResourceUuid
|
||||
FileMetadataInfo dirInfo = createDirectoryMetadata(
|
||||
objectKey,
|
||||
item.getFolderName(),
|
||||
false,
|
||||
parentDir.getId(),
|
||||
item.getFolderUuid(), // 保存 UUID
|
||||
null,
|
||||
parentDir.getDirType());
|
||||
|
||||
newDirs.add(dirInfo);
|
||||
minioKeys.add(objectKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充成功响应结果
|
||||
*/
|
||||
private void fillSuccessResponse(List<FileMetadataInfo> newDirs, BatchCreateNormalDirResp resp) {
|
||||
for (FileMetadataInfo dir : newDirs) {
|
||||
resp.addSuccess(dir.getId(), dir.getOriginalName());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user