import Foundation // 福利模块(钱包/签到/任务/省钱)。金额:金币=个数,现金=分。客户端把"分"换算成"元"展示。 // 提现 / 看广告 / 金币兑现金 首版不做,故不含 Withdraw / Ad / Exchange / Cash 相关 DTO。 // ===== 钱包(金币只进不出:展示余额 + 流水)===== struct CoinAccountDTO: Codable { let coinBalance: Int let cashBalanceCents: Int let totalCoinEarned: Int } struct CoinTransactionDTO: Codable, Identifiable, Hashable { let id: Int let amount: Int let balanceAfter: Int let bizType: String let refId: String? let remark: String? let createdAt: String } struct CoinTransactionPageDTO: Codable { let items: [CoinTransactionDTO] let nextCursor: Int? } // ===== 签到 ===== struct SigninStepDTO: Codable, Hashable { let day: Int let coin: Int let status: String // claimed / today / locked } struct SigninStatusDTO: Codable { let todaySigned: Bool let consecutiveDays: Int let todayCycleDay: Int let todayCoin: Int let canClaim: Bool let steps: [SigninStepDTO] } struct SigninResultDTO: Codable { let coinAwarded: Int let cycleDay: Int let streak: Int let coinBalance: Int } // ===== 任务 ===== struct TaskDTO: Codable, Identifiable, Hashable { var id: String { taskKey } let taskKey: String let coin: Int let claimed: Bool } struct TaskListDTO: Codable { let items: [TaskDTO] } struct TaskClaimResultDTO: Codable { let taskKey: String let coinAwarded: Int let coinBalance: Int } // ===== 比价战绩里程碑(福利页「记录比价战绩」)===== struct MilestoneStateDTO: Codable, Hashable { let milestone: Int let coin: Int let state: String // claimed / active / locked } struct MilestoneStatusDTO: Codable { let successCount: Int let claimableCount: Int let milestones: [MilestoneStateDTO] } // ===== 省钱(累计/战绩/明细)===== struct SavingsSummaryDTO: Codable { let totalSavedCents: Int let orderCount: Int let avgSavedCents: Int } struct SavingsBattleDTO: Codable { let weekSavedCents: Int let beatPercent: Int let streakDays: Int } struct SavingsRecordDTO: Codable, Identifiable, Hashable { let id: Int let orderAmountCents: Int let savedAmountCents: Int let platform: String? let title: String? let shopName: String? var dishes: [String] = [] let payChannel: String? let sourcePlatformName: String? let originalPriceCents: Int? let createdAt: String enum CodingKeys: String, CodingKey { case id, orderAmountCents, savedAmountCents, platform, title case shopName, dishes, payChannel, sourcePlatformName, originalPriceCents, createdAt } init(from decoder: Decoder) throws { let c = try decoder.container(keyedBy: CodingKeys.self) id = try c.decode(Int.self, forKey: .id) orderAmountCents = try c.decode(Int.self, forKey: .orderAmountCents) savedAmountCents = try c.decode(Int.self, forKey: .savedAmountCents) platform = try? c.decode(String.self, forKey: .platform) title = try? c.decode(String.self, forKey: .title) shopName = try? c.decode(String.self, forKey: .shopName) dishes = (try? c.decode([String].self, forKey: .dishes)) ?? [] payChannel = try? c.decode(String.self, forKey: .payChannel) sourcePlatformName = try? c.decode(String.self, forKey: .sourcePlatformName) originalPriceCents = try? c.decode(Int.self, forKey: .originalPriceCents) createdAt = try c.decode(String.self, forKey: .createdAt) } } struct SavingsRecordPageDTO: Codable { let items: [SavingsRecordDTO] let nextCursor: Int? }