98b7600797
对标安卓正式版、对接 app-server(8770)的 SwiftUI 客户端。iOS 无障碍受限、无比价核心,做自成一体:美团 CPS 导购 + 福利(签到/任务/金币)+ 查看(比价记录/省钱战绩)。 功能完整 M0-M5:脚手架、短信登录(JWT+Keychain+401 自动刷新)、首页券 feed 导购、比价记录+省钱战绩、福利、我的(资料/反馈/设置/注销)。 单 target、0 三方依赖、XcodeGen 生成工程;Debug/Release 双 plist 分流 ATS。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
138 lines
3.7 KiB
Swift
138 lines
3.7 KiB
Swift
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?
|
|
}
|