feat(app): P0 batch 2+2.5 — local upload dedup with batch dialog, album switch check, file size guard
P0-1 层 1 本地查重:
- checkDuplicates:同账号+同相册+state=completed+filename+bytes 匹配
- EnqueueResult 返回 enqueued+duplicates+skipped
- capture_page + album_detail_page:批量单次弹窗(非逐张)三选一
[跳过重复上传非重复] [全部上传] [取消]
P0-8 相册上传开关预检:album_detail_page 入队前检查 uploadEnabled,
关闭时提示「该相册已关闭上传」+ 不入队
P1-8 文件大小预检:preparer 限单文件 ≤100MB(磁盘满由 on Object 兜底)
P0-7 更正:上传并发限制已有(kMaxParallelUploads=2),v2 分析误报
397/397 全绿、analyze 0
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -55,8 +55,8 @@ class UploadPreparer {
|
||||
Random? random,
|
||||
this.maxBytes = kMaxUploadBytes,
|
||||
this.onEnqueued,
|
||||
}) : _baseDirProvider = baseDirProvider ?? getApplicationSupportDirectory,
|
||||
_random = random ?? Random.secure();
|
||||
}) : _baseDirProvider = baseDirProvider ?? getApplicationSupportDirectory,
|
||||
_random = random ?? Random.secure();
|
||||
|
||||
final TaskStore _store;
|
||||
final Future<Directory> Function() _baseDirProvider;
|
||||
@@ -88,13 +88,15 @@ class UploadPreparer {
|
||||
required String sourcePath,
|
||||
String? filename,
|
||||
}) {
|
||||
return _serialize(() => _prepare(
|
||||
accountId: accountId,
|
||||
albumId: albumId,
|
||||
categoryId: categoryId,
|
||||
sourcePath: sourcePath,
|
||||
filename: filename,
|
||||
));
|
||||
return _serialize(
|
||||
() => _prepare(
|
||||
accountId: accountId,
|
||||
albumId: albumId,
|
||||
categoryId: categoryId,
|
||||
sourcePath: sourcePath,
|
||||
filename: filename,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 重试准备失败的任务(preparationFailed → preparing → 复制 → queued)。
|
||||
@@ -106,11 +108,10 @@ class UploadPreparer {
|
||||
required String taskId,
|
||||
required String sourcePath,
|
||||
}) {
|
||||
return _serialize(() => _retry(
|
||||
accountId: accountId,
|
||||
taskId: taskId,
|
||||
sourcePath: sourcePath,
|
||||
));
|
||||
return _serialize(
|
||||
() =>
|
||||
_retry(accountId: accountId, taskId: taskId, sourcePath: sourcePath),
|
||||
);
|
||||
}
|
||||
|
||||
/// 串行化:所有准备操作首尾相接(错误吞掉以不断链,错误经返回值上抛)。
|
||||
@@ -196,6 +197,11 @@ class UploadPreparer {
|
||||
if (expected <= 0) {
|
||||
throw StateError('empty source');
|
||||
}
|
||||
// P1-8:磁盘空间预检——超 100MB 直接拒绝(copy 阶段的磁盘满错误
|
||||
// 已被下方 on Object 兜底为 copyFailed;此上限防止单文件打满存储)。
|
||||
if (expected > 100 * 1024 * 1024) {
|
||||
throw StateError('source too large');
|
||||
}
|
||||
await target.parent.create(recursive: true);
|
||||
await source.copy(task.sourceFilePath);
|
||||
final copied = await target.length();
|
||||
|
||||
Reference in New Issue
Block a user