为旅拍 OTG 上传增加 client_photo_id,并按服务端已登记 ID 同步本地上传状态。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-16 19:13:19 +08:00
parent 3d5ad8a614
commit b019e1e494
11 changed files with 323 additions and 115 deletions

View File

@ -77,6 +77,7 @@ final class WiredCameraTransferViewModel {
private let storage: TravelAlbumOTGPhotoStore
private let repository: TravelAlbumOTGPhotoRepository
private let uploader: any TravelAlbumOTGUploading
private let api: any TravelAlbumServing
private let appStore: AppStore
private let userDefaults: UserDefaults
@ -85,6 +86,7 @@ final class WiredCameraTransferViewModel {
private var isUploading = false
private var queuedAutoUploadPhotoIds: Set<String> = []
private var suppressSelectedTimeSlotNotification = false
private var serverStatusSyncTask: Task<Void, Never>?
@MainActor
init(
@ -95,6 +97,7 @@ final class WiredCameraTransferViewModel {
connectionManager: (any WiredCameraConnectionManaging)? = nil,
storage: TravelAlbumOTGPhotoStore = TravelAlbumOTGPhotoStore(),
uploader: (any TravelAlbumOTGUploading)? = nil,
api: (any TravelAlbumServing)? = nil,
appStore: AppStore = .shared,
userDefaults: UserDefaults = .standard
) {
@ -106,6 +109,7 @@ final class WiredCameraTransferViewModel {
self.storage = storage
self.repository = TravelAlbumOTGPhotoRepository(storage: storage)
self.uploader = uploader ?? TravelAlbumOTGUploader()
self.api = api ?? NetworkServices.shared.travelAlbumAPI
self.appStore = appStore
self.userDefaults = userDefaults
self.transferMode = Self.persistedTransferMode(in: userDefaults)
@ -225,11 +229,14 @@ final class WiredCameraTransferViewModel {
connectionManager.delegate = self
syncFromConnectionManager()
loadPersistedPhotos()
syncServerUploadStatuses()
connectionManager.start()
}
/// UI delegate
func stop() {
serverStatusSyncTask?.cancel()
serverStatusSyncTask = nil
connectionManager.unbindDelegate()
connectionManager.suspendLiveTransfer()
}
@ -483,6 +490,39 @@ final class WiredCameraTransferViewModel {
applyMergedPhotos()
}
private func syncServerUploadStatuses() {
serverStatusSyncTask?.cancel()
guard albumId > 0 else { return }
let baseline = persistedRecordsById
serverStatusSyncTask = Task { [weak self] in
guard let self else { return }
do {
let response = try await api.materialClientPhotoIds(userEquityTravelId: albumId)
try Task.checkCancellation()
let activeIds = Set(persistedRecordsById.values.compactMap { record in
record.status == .transferring || record.status == .uploading ? record.id : nil
})
let reconciled = TravelAlbumOTGServerStatusPolicy.reconcile(
records: Array(persistedRecordsById.values),
baselineRecordsById: baseline,
activePhotoIds: activeIds,
serverClientPhotoIds: response.clientPhotoIds,
now: Int64(Date().timeIntervalSince1970 * 1000)
)
let reconciledById = Dictionary(uniqueKeysWithValues: reconciled.map { ($0.id, $0) })
guard reconciledById != persistedRecordsById else { return }
persistedRecordsById = reconciledById
storage.save(reconciled, albumId: albumId)
applyMergedPhotos()
selectedPhotoIds = selectedPhotoIds.intersection(selectableVisiblePhotoIds)
} catch is CancellationError {
return
} catch {
OTGLog.error(.connection, "sync server upload statuses failed: \(error.localizedDescription)")
}
}
}
private func applyMergedPhotos() {
let persistedItems = persistedRecordsById.values
.map { $0.toPhotoItem(storage: storage, albumId: albumId) }