// // SettingModels.swift // suixinkan // import Foundation /// 最新版本响应实体,对齐 Android `VersionResponse`。 struct VersionResponse: Decodable, Equatable { let id: Int let type: Int let version: String let operatorName: String let description: String let status: Int let createdAt: String let updatedAt: String let deletedAt: String? let versionCode: Int let apkURL: String let isForceUpdate: Int let approveNotes: String enum CodingKeys: String, CodingKey { case id case type case version case operatorName = "operator" case description case status case createdAt = "created_at" case updatedAt = "updated_at" case deletedAt = "deleted_at" case versionCode = "version_code" case apkURL = "apk_url" case isForceUpdate = "is_force_update" case approveNotes = "approve_notes" } init( id: Int = 0, type: Int = 0, version: String = "", operatorName: String = "", description: String = "", status: Int = 0, createdAt: String = "", updatedAt: String = "", deletedAt: String? = nil, versionCode: Int = 0, apkURL: String = "", isForceUpdate: Int = 0, approveNotes: String = "" ) { self.id = id self.type = type self.version = version self.operatorName = operatorName self.description = description self.status = status self.createdAt = createdAt self.updatedAt = updatedAt self.deletedAt = deletedAt self.versionCode = versionCode self.apkURL = apkURL self.isForceUpdate = isForceUpdate self.approveNotes = approveNotes } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) id = try container.decodeLossyInt(forKey: .id) ?? 0 type = try container.decodeLossyInt(forKey: .type) ?? 0 version = try container.decodeLossyString(forKey: .version) operatorName = try container.decodeLossyString(forKey: .operatorName) description = try container.decodeLossyString(forKey: .description) status = try container.decodeLossyInt(forKey: .status) ?? 0 createdAt = try container.decodeLossyString(forKey: .createdAt) updatedAt = try container.decodeLossyString(forKey: .updatedAt) deletedAt = try container.decodeIfPresent(String.self, forKey: .deletedAt) versionCode = try container.decodeLossyInt(forKey: .versionCode) ?? 0 apkURL = try container.decodeLossyString(forKey: .apkURL) isForceUpdate = try container.decodeLossyInt(forKey: .isForceUpdate) ?? 0 approveNotes = try container.decodeLossyString(forKey: .approveNotes) } } /// 设置页协议入口实体,表示标题和对应 H5 地址。 struct SettingAgreementDestination: Equatable { let title: String let url: URL } /// 设置页协议类型。 enum SettingAgreementKind: CaseIterable { case aboutUs case userAgreement case privacyPolicy } private extension KeyedDecodingContainer { func decodeLossyString(forKey key: Key) throws -> String { if let value = try? decodeIfPresent(String.self, forKey: key) { return value } if let value = try? decodeIfPresent(Int.self, forKey: key) { return String(value) } if let value = try? decodeIfPresent(Double.self, forKey: key) { return String(value) } if let value = try? decodeIfPresent(Bool.self, forKey: key) { return value ? "true" : "false" } return "" } func decodeLossyInt(forKey key: Key) throws -> Int? { if let value = try? decodeIfPresent(Int.self, forKey: key) { return value } if let value = try? decodeIfPresent(String.self, forKey: key) { let text = value.trimmingCharacters(in: .whitespacesAndNewlines) if let intValue = Int(text) { return intValue } if let doubleValue = Double(text) { return Int(doubleValue) } } if let value = try? decodeIfPresent(Double.self, forKey: key) { return Int(value) } return nil } }