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:
浅念
2026-09-28 17:06:19 +08:00
parent 4f89c25bad
commit 29f3bde9c2
21 changed files with 522 additions and 19 deletions
+1
View File
@@ -293,6 +293,7 @@
"errorAlbumUploadDisabled": "Album uploads are disabled",
"errorUploadSessionConflict": "Upload session conflict. Please retry",
"errorUploadVerifyFailed": "Upload verification failed. Will retry",
"errorPhotoDuplicate": "Possible duplicate: an identical photo already exists in this album",
"captureTitle": "Continuous capture",
"captureCategoryDefault": "Default category",
"captureSwitchHint": "Switching only affects photos captured afterwards",