fix:优化回收站全量删除
This commit is contained in:
@@ -788,7 +788,13 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService {
|
||||
|
||||
String oldKey = deleteFileMetadataInfo.getObjectKey();
|
||||
String suffix = "_del_" + System.currentTimeMillis();
|
||||
String newKey = oldKey + suffix;
|
||||
String newKey;
|
||||
int dotIndex = oldKey.lastIndexOf('.');
|
||||
if (dotIndex > -1) {
|
||||
newKey = oldKey.substring(0, dotIndex) + suffix + oldKey.substring(dotIndex);
|
||||
} else {
|
||||
newKey = oldKey + suffix;
|
||||
}
|
||||
String bucketName = deleteFileMetadataInfo.getBucketName();
|
||||
|
||||
// 1. MinIO 重命名
|
||||
@@ -1539,7 +1545,12 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService {
|
||||
public void updatePathRecursively(String oldPrefix, String newPrefix, String bucketName, LocalDateTime deletedAt, LocalDateTime expireAt, boolean updateStatus) {
|
||||
// 1. MinIO 移动 (如果路径不同)
|
||||
if (!Objects.equals(oldPrefix, newPrefix)) {
|
||||
minioService.renameDirectoryRecursively(oldPrefix, newPrefix, bucketName);
|
||||
// 根据路径特征决定是递归目录还是单文件重命名
|
||||
if (oldPrefix.endsWith("/")) {
|
||||
minioService.renameDirectoryRecursively(oldPrefix, newPrefix, bucketName);
|
||||
} else {
|
||||
minioService.renameFile(oldPrefix, newPrefix, bucketName);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 数据库批量更新 Metadata
|
||||
@@ -1568,7 +1579,15 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService {
|
||||
}
|
||||
|
||||
if (CollectionUtils.isNotEmpty(updates)) {
|
||||
fileMetadataInfoService.updateBatchById(updates);
|
||||
for (FileMetadataInfo child : updates) {
|
||||
fileMetadataInfoService.lambdaUpdate()
|
||||
.eq(FileMetadataInfo::getId, child.getId())
|
||||
.set(FileMetadataInfo::getObjectKey, child.getObjectKey())
|
||||
.set(updateStatus, FileMetadataInfo::getDeletedAt, deletedAt)
|
||||
.set(updateStatus, FileMetadataInfo::getRecycleExpireAt, expireAt)
|
||||
.set(FileMetadataInfo::getUpdateTime, child.getUpdateTime())
|
||||
.update();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4664,7 +4683,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService {
|
||||
.eq(ObjectUtils.isNotEmpty(req.getDirType()), FileMetadataInfo::getDirType, req.getDirType())
|
||||
.eq(ObjectUtils.isNotEmpty(req.getDataType()), FileMetadataInfo::getDataType, req.getDataType())
|
||||
// 仅显示顶层删除项:即其父级未被删除(或者无父级)
|
||||
.apply("(parentId IS NULL OR NOT EXISTS (SELECT 1 FROM file_metadata_info p WHERE p.id = parentId AND p.deletedAt IS NOT NULL))")
|
||||
.apply("(parentId IS NULL OR EXISTS (SELECT 1 FROM file_metadata_info p WHERE p.id = file_metadata_info.parentId AND p.deletedAt IS NULL))")
|
||||
.orderByDesc(FileMetadataInfo::getDeletedAt)
|
||||
.list();
|
||||
|
||||
@@ -4738,45 +4757,25 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService {
|
||||
String originalName = metadata.getOriginalName();
|
||||
String bucketName = metadata.getBucketName();
|
||||
|
||||
// 2. 冲突检测与自动重命名
|
||||
// 循环检测 parentId 下是否存在同名文件(未删除的)
|
||||
// 2. 计算还原用的 objectKey:从回收站 key 移除 "_del_时间戳" 后缀(文件在扩展名前插入,目录在末尾插入)
|
||||
boolean isDirectory = Objects.equals(metadata.getDataType(), DataTypeEnum.DIRECTORY.getValue());
|
||||
String restoreName = originalName;
|
||||
String restoreKey;
|
||||
String restoreKey = removeRecycleDeleteSuffix(oldKey, isDirectory);
|
||||
|
||||
// 3. 同名冲突:originalName(不带版本号)改名为 xxx(1).png;objectKey 保持版本号不变,改成 xxx(1)_V1.png
|
||||
int counter = 1;
|
||||
while (true) {
|
||||
boolean exists = fileMetadataInfoService.lambdaQuery()
|
||||
.eq(FileMetadataInfo::getParentId, metadata.getParentId())
|
||||
.eq(FileMetadataInfo::getOriginalName, restoreName)
|
||||
.eq(FileMetadataInfo::getTenantId, ThreadLocalContext.getTenantId())
|
||||
.isNull(FileMetadataInfo::getDeletedAt)
|
||||
.exists();
|
||||
while (fileMetadataInfoService.lambdaQuery()
|
||||
.eq(FileMetadataInfo::getParentId, metadata.getParentId())
|
||||
.eq(FileMetadataInfo::getOriginalName, restoreName)
|
||||
.eq(FileMetadataInfo::getTenantId, ThreadLocalContext.getTenantId())
|
||||
.isNull(FileMetadataInfo::getDeletedAt)
|
||||
.exists()) {
|
||||
|
||||
if (!exists) {
|
||||
break;
|
||||
}
|
||||
|
||||
// 自动重命名: name(1).txt 或 folder(1)
|
||||
if (Objects.equals(metadata.getDataType(), DataTypeEnum.DIRECTORY.getValue())) {
|
||||
restoreName = originalName + "(" + counter + ")";
|
||||
} else {
|
||||
int dotIndex = originalName.lastIndexOf('.');
|
||||
if (dotIndex > -1) {
|
||||
restoreName = originalName.substring(0, dotIndex) + "(" + counter + ")" + originalName.substring(dotIndex);
|
||||
} else {
|
||||
restoreName = originalName + "(" + counter + ")";
|
||||
}
|
||||
}
|
||||
restoreName = buildOriginalNameWithCounter(originalName, isDirectory, counter);
|
||||
restoreKey = buildObjectKeyWithCounterKeepingVersion(restoreKey, isDirectory, counter);
|
||||
counter++;
|
||||
}
|
||||
|
||||
// 3. 构建新的 ObjectKey
|
||||
if (Objects.equals(metadata.getDataType(), DataTypeEnum.DIRECTORY.getValue())) {
|
||||
restoreKey = getDirMinioObjectKey(parentPath + restoreName);
|
||||
} else {
|
||||
restoreKey = getFileMinioObjectKey(parentPath + restoreName);
|
||||
}
|
||||
|
||||
try {
|
||||
// 4. 执行恢复(MinIO Rename + DB Recursive Update + Status Update)
|
||||
// 传递 null 给 deletedAt 和 expireAt 表示清除删除状态
|
||||
@@ -4816,7 +4815,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService {
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public SdmResponse permanentDeleteFromRecycle(PermanentDeleteFromRecycleReq req) {
|
||||
List<Long> ids = req.getIds();
|
||||
if (ObjectUtils.isEmpty(ids)) {
|
||||
if (CollectionUtils.isEmpty(ids)) {
|
||||
Long userId = ThreadLocalContext.getUserId();
|
||||
// 查询回收站中的顶层节点(避免对子节点重复触发递归删除)
|
||||
List<FileMetadataInfo> list = fileMetadataInfoService.lambdaQuery()
|
||||
@@ -4839,6 +4838,56 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService {
|
||||
return SdmResponse.success("彻底删除成功");
|
||||
}
|
||||
|
||||
private static String removeRecycleDeleteSuffix(String objectKey, boolean isDirectory) {
|
||||
if (!StringUtils.hasText(objectKey)) return objectKey;
|
||||
|
||||
// 移除 _del_123456789 后缀
|
||||
int delIndex = objectKey.lastIndexOf("_del_");
|
||||
if (delIndex == -1) return objectKey;
|
||||
|
||||
if (isDirectory) {
|
||||
// 目录:.../folder_del_timestamp/ -> .../folder/
|
||||
return objectKey.substring(0, delIndex) + "/";
|
||||
} else {
|
||||
// 文件:.../name_V1_del_timestamp.png -> .../name_V1.png
|
||||
int dotIndex = objectKey.lastIndexOf('.');
|
||||
if (dotIndex > delIndex) {
|
||||
return objectKey.substring(0, delIndex) + objectKey.substring(dotIndex);
|
||||
}
|
||||
return objectKey.substring(0, delIndex);
|
||||
}
|
||||
}
|
||||
|
||||
private static String buildOriginalNameWithCounter(String originalName, boolean isDirectory, int counter) {
|
||||
if (isDirectory) return originalName + "(" + counter + ")";
|
||||
int dotIndex = originalName.lastIndexOf('.');
|
||||
if (dotIndex > -1) {
|
||||
return originalName.substring(0, dotIndex) + "(" + counter + ")" + originalName.substring(dotIndex);
|
||||
}
|
||||
return originalName + "(" + counter + ")";
|
||||
}
|
||||
|
||||
private static String buildObjectKeyWithCounterKeepingVersion(String restoreKey, boolean isDirectory, int counter) {
|
||||
if (isDirectory) {
|
||||
String key = restoreKey.endsWith("/") ? restoreKey.substring(0, restoreKey.length() - 1) : restoreKey;
|
||||
return key + "(" + counter + ")/";
|
||||
}
|
||||
|
||||
// 文件处理:在 _Vn 之前插入 (n)
|
||||
// restoreKey 此时已移除 _del_,格式为 .../name_V1.png
|
||||
int versionIdx = restoreKey.lastIndexOf("_V");
|
||||
if (versionIdx > -1) {
|
||||
return restoreKey.substring(0, versionIdx) + "(" + counter + ")" + restoreKey.substring(versionIdx);
|
||||
}
|
||||
|
||||
// 兜底(如果不带 _V):按扩展名插
|
||||
int dotIndex = restoreKey.lastIndexOf('.');
|
||||
if (dotIndex > -1) {
|
||||
return restoreKey.substring(0, dotIndex) + "(" + counter + ")" + restoreKey.substring(dotIndex);
|
||||
}
|
||||
return restoreKey + "(" + counter + ")";
|
||||
}
|
||||
|
||||
private SdmResponse permanentDeleteSingleFile(Long id) {
|
||||
FileMetadataInfo metadata = fileMetadataInfoService.lambdaQuery()
|
||||
.eq(FileMetadataInfo::getId, id)
|
||||
|
||||
Reference in New Issue
Block a user