Improve music processing robustness and workflow UX

Unify safe file-move behavior and richer progress semantics across backend tasks, while upgrading traditional-to-simplified conversion and refining the frontend multi-step panels for clearer execution feedback.
This commit is contained in:
2026-03-08 04:26:18 +08:00
parent 20a70270c7
commit 81977a157e
39 changed files with 2131 additions and 1511 deletions
@@ -1,5 +1,6 @@
package com.music.service;
import com.music.common.FileTransferUtils;
import com.music.dto.ProgressMessage;
import org.jaudiotagger.audio.AudioFile;
import org.jaudiotagger.audio.AudioFileIO;
@@ -109,6 +110,7 @@ public class DedupService {
AtomicInteger duplicateGroups = new AtomicInteger(0);
AtomicInteger moved = new AtomicInteger(0);
AtomicInteger failed = new AtomicInteger(0);
Set<Path> processedDuplicates = new HashSet<>();
sendProgress(taskId, total, 0, 0, 0,
"开始扫描音乐库...", false);
@@ -147,7 +149,7 @@ public class DedupService {
// 第二阶段:处理 MD5 去重结果(完全二进制重复)
if (useMd5) {
for (Map.Entry<String, List<Path>> entry : md5Groups.entrySet()) {
List<Path> group = entry.getValue();
List<Path> group = filterProcessableCandidates(entry.getValue(), processedDuplicates);
if (group.size() <= 1) {
continue;
}
@@ -158,15 +160,15 @@ public class DedupService {
List<Path> duplicates = new ArrayList<>(group);
duplicates.remove(keep);
moved.addAndGet(handleDuplicates(duplicates, keep, trashPath, mode, taskId, total,
scanned, duplicateGroups, failed));
handleDuplicates(duplicates, keep, trashPath, mode, taskId, total,
scanned, duplicateGroups, moved, failed, processedDuplicates);
}
}
if (useMetadata) {
// 第三阶段:处理元数据匹配去重结果
for (Map.Entry<MetadataKey, List<Path>> entry : metadataGroups.entrySet()) {
List<Path> group = entry.getValue();
List<Path> group = filterProcessableCandidates(entry.getValue(), processedDuplicates);
if (group.size() <= 1) {
continue;
}
@@ -177,8 +179,8 @@ public class DedupService {
List<Path> duplicates = new ArrayList<>(group);
duplicates.remove(keep);
moved.addAndGet(handleDuplicates(duplicates, keep, trashPath, mode, taskId, total,
scanned, duplicateGroups, failed));
handleDuplicates(duplicates, keep, trashPath, mode, taskId, total,
scanned, duplicateGroups, moved, failed, processedDuplicates);
}
}
@@ -362,30 +364,30 @@ public class DedupService {
/**
* 将重复文件移动/复制到回收站,并更新统计与进度
*
* @return 实际成功移动/复制的文件数量
*/
private int handleDuplicates(List<Path> duplicates,
Path keep,
Path trashPath,
String mode,
String taskId,
int total,
AtomicInteger scanned,
AtomicInteger duplicateGroups,
AtomicInteger failed) {
int movedCount = 0;
private void handleDuplicates(List<Path> duplicates,
Path keep,
Path trashPath,
String mode,
String taskId,
int total,
AtomicInteger scanned,
AtomicInteger duplicateGroups,
AtomicInteger moved,
AtomicInteger failed,
Set<Path> processedDuplicates) {
for (Path dup : duplicates) {
try {
Path target = resolveTargetFile(trashPath, dup.getFileName().toString());
if ("move".equalsIgnoreCase(mode)) {
Files.move(dup, target, StandardCopyOption.REPLACE_EXISTING);
FileTransferUtils.moveWithFallback(dup, target);
} else {
Files.copy(dup, target, StandardCopyOption.REPLACE_EXISTING);
}
movedCount++;
moved.incrementAndGet();
processedDuplicates.add(toNormalizedAbsolutePath(dup));
sendProgress(taskId, total, scanned.get(),
duplicateGroups.get(), movedCount,
duplicateGroups.get(), moved.get(),
String.format("重复文件: %s (保留: %s)",
dup.getFileName(), keep.getFileName()),
false);
@@ -394,7 +396,24 @@ public class DedupService {
log.warn("处理重复文件失败: {}", dup, e);
}
}
return movedCount;
}
private List<Path> filterProcessableCandidates(List<Path> group, Set<Path> processedDuplicates) {
List<Path> candidates = new ArrayList<>(group.size());
for (Path file : group) {
Path normalized = toNormalizedAbsolutePath(file);
if (processedDuplicates.contains(normalized)) {
continue;
}
if (Files.exists(file)) {
candidates.add(file);
}
}
return candidates;
}
private Path toNormalizedAbsolutePath(Path path) {
return path.toAbsolutePath().normalize();
}
/**
@@ -424,8 +443,8 @@ public class DedupService {
* 字段语义(供前端展示用):
* - total:扫描到的音频文件总数
* - processed:已扫描文件数
* - success:重复组数量
* - failed:移动/复制的重复文件数量
* - duplicateGroups / success:重复组数量
* - movedFiles / failed:移动/复制的重复文件数量
*
* 由于进度字段在不同任务中的含义略有差异,前端可根据 type === "dedup" 做专门映射。
*/
@@ -443,6 +462,8 @@ public class DedupService {
pm.setProcessed(processed);
pm.setSuccess(success);
pm.setFailed(failed);
pm.setDuplicateGroups(success);
pm.setMovedFiles(failed);
pm.setCurrentFile(null);
pm.setMessage(message);
pm.setCompleted(completed);
@@ -451,4 +472,3 @@ public class DedupService {
messagingTemplate.convertAndSend("/topic/progress/" + taskId, pm);
}
}