feat(app): wire server dedup - force passthrough + PHOTO_DUPLICATE classification & l10n
Task 8: close the gap between server-side duplicate detection (90dcc58)
and the app.
- ApiClient.mapDioException: normalize 409 wire body reusing
VALIDATION_FAILED with message=PHOTO_DUPLICATE to
ErrorCodes.photoDuplicate (message-based; auto-compat if the contract
later adds a dedicated code)
- l10n zh/en errorPhotoDuplicate; error_l10n maps the code (queue page
inherits via fallback)
- UploadEngine._classifyFailure: PHOTO_DUPLICATE joins the no-auto-retry
bucket (retrying the same init body would 409 again); force to skip
dedup travels only through the enqueue path
- UploadTasks drift table +force column (schema v2, addColumn migration;
legacy rows default false); UploadTaskDraft/UploadTask/prepare carry it
- enqueueForUpload +forcePaths; duplicate dialog "全部上传" (capture page
+ album detail page) now returns DuplicateDialogChoice so confirmed
duplicates enqueue with force=true (server adjudicates owner/admin)
- engine init body carries force only when true (absent otherwise)
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -124,6 +124,26 @@ void main() {
|
||||
await p.prepare(accountId: 'alice', albumId: 'alb-1', sourcePath: src.path);
|
||||
expect(enqueued, 1);
|
||||
});
|
||||
|
||||
test('force 透传(Task 8):缺省 false,force=true 落到任务行', () async {
|
||||
final src = sourceFile('force.jpg', _jpgBytes);
|
||||
final p = preparer();
|
||||
|
||||
final normal = await p.prepare(
|
||||
accountId: 'alice',
|
||||
albumId: 'alb-1',
|
||||
sourcePath: src.path,
|
||||
);
|
||||
expect(normal.force, isFalse, reason: '缺省不强传');
|
||||
|
||||
final forced = await p.prepare(
|
||||
accountId: 'alice',
|
||||
albumId: 'alb-1',
|
||||
sourcePath: src.path,
|
||||
force: true,
|
||||
);
|
||||
expect(forced.force, isTrue, reason: '弹窗「全部上传」的强传标记落库');
|
||||
});
|
||||
});
|
||||
|
||||
group('prepare(预检失败 → preparationFailed)', () {
|
||||
|
||||
Reference in New Issue
Block a user