// // ScenicQueueAPI.swift // suixinkan // import Foundation @MainActor /// 排队管理 API 协议,便于 ViewModel 单元测试注入替身。 protocol ScenicQueueAPIProtocol { func saveSetting(_ request: ScenicQueueSaveSettingRequest) async throws func call(recordId: Int64) async throws -> ScenicQueueCallData func pass(recordId: Int64) async throws -> ScenicQueuePassData func finish(recordId: Int64) async throws -> ScenicQueueFinishData func userMark(_ request: ScenicQueueUserMarkRequest) async throws func requeueInsertBefore(recordId: Int64, operatorId: Int) async throws func shootQueueQrcode(scenicId: Int64, scenicSpotId: Int64) async throws -> ScenicQueueShootQueueQrcodeData func setting(scenicId: Int64?, scenicSpotId: Int64?) async throws -> ScenicQueueSettingData func stats(scenicId: Int64, scenicSpotId: Int64) async throws -> ScenicQueueStatsData func home(scenicId: Int64, scenicSpotId: Int64, type: Int, page: Int, pageSize: Int) async throws -> ScenicQueueHomeData func settingChangeLog(scenicId: Int64, scenicSpotId: Int64?, page: Int, pageSize: Int) async throws -> ScenicQueueSettingChangeLogData func scenicSpotListAll(scenicId: String) async throws -> ScenicSpotListResponse func socketToken() async throws -> SocketTokenResponse func aliyunSTS(bucket: String) async throws -> AliyunOSSResponse } @MainActor /// 排队管理 API,封装 Android `NetworkApi` 中 scenic-queue 相关接口。 final class ScenicQueueAPI: ScenicQueueAPIProtocol { private let client: APIClient init(client: APIClient) { self.client = client } /// 保存排队设置。 func saveSetting(_ request: ScenicQueueSaveSettingRequest) async throws { let _: EmptyPayload = try await client.send( APIRequest(method: .post, path: "/api/app/scenic-queue/save-setting", body: request) ) } /// 叫号。 func call(recordId: Int64) async throws -> ScenicQueueCallData { try await client.send( APIRequest(method: .post, path: "/api/app/scenic-queue/call", body: ScenicQueueIdRequest(id: recordId)) ) } /// 过号。 func pass(recordId: Int64) async throws -> ScenicQueuePassData { try await client.send( APIRequest(method: .post, path: "/api/app/scenic-queue/pass", body: ScenicQueueIdRequest(id: recordId)) ) } /// 完成拍摄。 func finish(recordId: Int64) async throws -> ScenicQueueFinishData { try await client.send( APIRequest(method: .post, path: "/api/app/scenic-queue/finish", body: ScenicQueueIdRequest(id: recordId)) ) } /// 标记用户。 func userMark(_ request: ScenicQueueUserMarkRequest) async throws { let _: EmptyPayload = try await client.send( APIRequest(method: .post, path: "/api/app/scenic-queue/user-mark", body: request) ) } /// 将过号记录插入队首后指定位置。 func requeueInsertBefore(recordId: Int64, operatorId: Int) async throws { let _: EmptyPayload = try await client.send( APIRequest( method: .post, path: "/api/app/scenic-queue/requeue-insert-before", body: ScenicQueueRequeueInsertBeforeRequest(recordId: recordId, operatorId: operatorId) ) ) } /// 获取打卡点排队二维码。 func shootQueueQrcode(scenicId: Int64, scenicSpotId: Int64) async throws -> ScenicQueueShootQueueQrcodeData { try await client.send( APIRequest( method: .get, path: "/api/app/scenic-queue/shoot-queue-qrcode", queryItems: [ URLQueryItem(name: "scenic_id", value: String(scenicId)), URLQueryItem(name: "scenic_spot_id", value: String(scenicSpotId)), ] ) ) } /// 获取指定打卡点设置。 func setting(scenicId: Int64?, scenicSpotId: Int64?) async throws -> ScenicQueueSettingData { var items: [URLQueryItem] = [] if let scenicId { items.append(URLQueryItem(name: "scenic_id", value: String(scenicId))) } if let scenicSpotId { items.append(URLQueryItem(name: "scenic_spot_id", value: String(scenicSpotId))) } return try await client.send( APIRequest(method: .get, path: "/api/app/scenic-queue/setting", queryItems: items) ) } /// 获取排队统计。 func stats(scenicId: Int64, scenicSpotId: Int64) async throws -> ScenicQueueStatsData { try await client.send( APIRequest( method: .get, path: "/api/app/scenic-queue/queue-stats", queryItems: [ URLQueryItem(name: "scenic_id", value: String(scenicId)), URLQueryItem(name: "scenic_spot_id", value: String(scenicSpotId)), ] ) ) } /// 获取排队首页列表。 func home(scenicId: Int64, scenicSpotId: Int64, type: Int, page: Int, pageSize: Int) async throws -> ScenicQueueHomeData { try await client.send( APIRequest( method: .get, path: "/api/app/scenic-queue/home", queryItems: [ URLQueryItem(name: "scenic_id", value: String(scenicId)), URLQueryItem(name: "scenic_spot_id", value: String(scenicSpotId)), URLQueryItem(name: "type", value: String(type)), URLQueryItem(name: "page", value: String(page)), URLQueryItem(name: "page_size", value: String(pageSize)), ] ) ) } /// 获取配置变更日志。 func settingChangeLog(scenicId: Int64, scenicSpotId: Int64?, page: Int, pageSize: Int) async throws -> ScenicQueueSettingChangeLogData { var items = [ URLQueryItem(name: "scenic_id", value: String(scenicId)), URLQueryItem(name: "page", value: String(page)), URLQueryItem(name: "page_size", value: String(pageSize)), ] if let scenicSpotId { items.append(URLQueryItem(name: "scenic_spot_id", value: String(scenicSpotId))) } return try await client.send( APIRequest(method: .get, path: "/api/app/scenic-queue/setting-change-log", queryItems: items) ) } /// 获取当前景区全部打卡点。 func scenicSpotListAll(scenicId: String) async throws -> ScenicSpotListResponse { try await client.send( APIRequest( method: .get, path: "/api/yf-handset-app/photog/scenic-spot/list-all", queryItems: [URLQueryItem(name: "scenic_id", value: scenicId)] ) ) } /// 获取业务 WebSocket token。 func socketToken() async throws -> SocketTokenResponse { try await client.send(APIRequest(method: .get, path: "/api/app/socket-token")) } /// 获取阿里云 STS 临时凭证。 func aliyunSTS(bucket: String = "vipsky") async throws -> AliyunOSSResponse { try await client.send( APIRequest( method: .get, path: "/api/app/config/get-sts-token", queryItems: [URLQueryItem(name: "bucket", value: bucket)] ) ) } } /// 仅包含 id 的排队操作请求体。 struct ScenicQueueIdRequest: Encodable, Equatable { let id: Int64 } /// 过号重排请求体。 struct ScenicQueueRequeueInsertBeforeRequest: Encodable, Equatable { let recordId: Int64 let operatorId: Int enum CodingKeys: String, CodingKey { case recordId = "record_id" case operatorId = "operator_id" } } /// 用户标记请求体。 struct ScenicQueueUserMarkRequest: Encodable, Equatable { let uid: Int64 let scenicId: Int64 let markAsFreelancePhotog: Int let queueBanDays: Int? let operatorId: Int? enum CodingKeys: String, CodingKey { case uid case scenicId = "scenic_id" case markAsFreelancePhotog = "mark_as_freelance_photog" case queueBanDays = "queue_ban_days" case operatorId = "operator_id" } }