// // ProfileModels.swift // suixinkan // import Foundation /// 用户资料响应实体,表示“我的”页面展示的账号基础信息。 struct UserInfoResponse: Decodable, Equatable { let avatar: String let realName: String let phone: String let nickname: String let roleName: String let status: Int let statusName: String enum CodingKeys: String, CodingKey { case avatar case realName = "real_name" case phone case nickname case roleName = "role_name" case status case statusName = "status_name" } init( avatar: String = "", realName: String = "", phone: String = "", nickname: String = "", roleName: String = "", status: Int = 0, statusName: String = "" ) { self.avatar = avatar self.realName = realName self.phone = phone self.nickname = nickname self.roleName = roleName self.status = status self.statusName = statusName } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) avatar = try container.decodeLossyString(forKey: .avatar) realName = try container.decodeLossyString(forKey: .realName) phone = try container.decodeLossyString(forKey: .phone) nickname = try container.decodeLossyString(forKey: .nickname) roleName = try container.decodeLossyString(forKey: .roleName) status = try container.decodeLossyInt(forKey: .status) ?? 0 statusName = try container.decodeLossyString(forKey: .statusName) } } /// 用户资料更新请求实体,表示昵称、密码和头像修改参数。 struct UpdateInfoRequest: Encodable { let nickname: String? let password: String? let avatar: String? } /// 实名认证响应实体,包裹当前用户的认证详情。 struct RealNameInfoResponse: Decodable, Equatable { let realNameInfo: RealNameInfo? enum CodingKeys: String, CodingKey { case realNameInfo = "real_name_info" } } /// 实名认证信息实体,表示当前用户实名审核状态和失败原因。 struct RealNameInfo: Decodable, Equatable { let realName: String let idCardNo: String let auditStatus: Int let auditStatusText: String? let rejectReason: String? let startDate: String? let endDate: String? let isLongValid: Bool let frontUrl: String? let backUrl: String? let auditorId: Int? let auditor: RealNameAuditor? let auditAt: String? var verified: Bool { auditStatus == 2 } enum CodingKeys: String, CodingKey { case realName = "real_name" case idCardNo = "id_card_no" case auditStatus = "audit_status" case auditStatusText = "audit_status_text" case rejectReason = "reject_reason" case startDate = "start_date" case endDate = "end_date" case isLongValid = "is_long_valid" case frontUrl = "front_url" case backUrl = "back_url" case auditorId = "auditor_id" case auditor case auditAt = "audit_at" } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) realName = try container.decodeLossyString(forKey: .realName) idCardNo = try container.decodeLossyString(forKey: .idCardNo) auditStatus = try container.decodeLossyInt(forKey: .auditStatus) ?? 0 auditStatusText = try container.decodeIfPresent(String.self, forKey: .auditStatusText) rejectReason = try container.decodeIfPresent(String.self, forKey: .rejectReason) startDate = try container.decodeIfPresent(String.self, forKey: .startDate) endDate = try container.decodeIfPresent(String.self, forKey: .endDate) isLongValid = (try container.decodeLossyInt(forKey: .isLongValid) ?? 0) == 1 frontUrl = try container.decodeIfPresent(String.self, forKey: .frontUrl) backUrl = try container.decodeIfPresent(String.self, forKey: .backUrl) auditorId = try container.decodeLossyInt(forKey: .auditorId) auditor = try container.decodeIfPresent(RealNameAuditor.self, forKey: .auditor) auditAt = try container.decodeIfPresent(String.self, forKey: .auditAt) } } /// 实名认证审核人实体,表示后台审核人的基础展示信息。 struct RealNameAuditor: Decodable, Equatable { let id: Int let name: String enum CodingKeys: String, CodingKey { case id case name } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) id = try container.decodeLossyInt(forKey: .id) ?? 0 name = try container.decodeLossyString(forKey: .name) } } /// 实名认证提交请求实体,表示用户填写的姓名、证件号、短信和证件图片。 struct RealNameAuthRequest: Encodable, Equatable { let realName: String let idCardNo: String let smsVerifyCode: String let startDate: String let endDate: String let isLongValid: Int let frontUrl: String let backUrl: String enum CodingKeys: String, CodingKey { case realName = "real_name" case idCardNo = "id_card_no" case smsVerifyCode = "sms_verify_code" case startDate = "start_date" case endDate = "end_date" case isLongValid = "is_long_valid" case frontUrl = "front_url" case backUrl = "back_url" } } /// 银行卡信息响应实体。 struct BankCardInfoResponse: Decodable, Equatable { let bankCard: BankCardInfo? enum CodingKeys: String, CodingKey { case bankCard = "bank_card" } } /// 银行卡信息实体,表示提现设置与审核状态。 struct BankCardInfo: Decodable, Equatable { let realName: String let bankName: String let branchName: String let cardNumber: String let frontUrl: String? let backUrl: String? let provinceCode: String? let cityCode: String? let rejectReason: String let auditStatus: Int let auditStatusLabel: String let auditAt: String? let auditorId: Int? let auditor: RealNameAuditor? enum CodingKeys: String, CodingKey { case realName = "real_name" case bankName = "bank_name" case branchName = "branch_name" case cardNumber = "card_number" case frontUrl = "front_url" case backUrl = "back_url" case provinceCode = "province_code" case cityCode = "city_code" case rejectReason = "reject_reason" case auditStatus = "audit_status" case auditStatusLabel = "audit_status_label" case auditAt = "audit_at" case auditorId = "auditor_id" case auditor } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) realName = try container.decodeLossyString(forKey: .realName) bankName = try container.decodeLossyString(forKey: .bankName) branchName = try container.decodeLossyString(forKey: .branchName) cardNumber = try container.decodeLossyString(forKey: .cardNumber) frontUrl = try container.decodeIfPresent(String.self, forKey: .frontUrl) backUrl = try container.decodeIfPresent(String.self, forKey: .backUrl) provinceCode = try container.decodeIfPresent(String.self, forKey: .provinceCode) cityCode = try container.decodeIfPresent(String.self, forKey: .cityCode) rejectReason = try container.decodeLossyString(forKey: .rejectReason) auditStatus = try container.decodeLossyInt(forKey: .auditStatus) ?? 0 auditStatusLabel = try container.decodeLossyString(forKey: .auditStatusLabel) auditAt = try container.decodeIfPresent(String.self, forKey: .auditAt) auditorId = try container.decodeLossyInt(forKey: .auditorId) auditor = try container.decodeIfPresent(RealNameAuditor.self, forKey: .auditor) } } /// 银行列表响应实体。 struct BankListResponse: Decodable, Equatable { let banks: [String] enum CodingKeys: String, CodingKey { case banks } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) banks = try container.decodeIfPresent([String].self, forKey: .banks) ?? [] } } /// 省市区响应实体。 struct AreasResponse: Decodable, Equatable, Identifiable { let id: Int let code: String let name: String let children: [AreasResponse] enum CodingKeys: String, CodingKey { case id case code case name case children } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) id = try container.decodeLossyInt(forKey: .id) ?? container.decodeLossyInt(forKey: .code) ?? 0 let decodedCode = try container.decodeLossyString(forKey: .code) .trimmingCharacters(in: .whitespacesAndNewlines) code = decodedCode.isEmpty ? "\(id)" : decodedCode name = try container.decodeLossyString(forKey: .name) children = try container.decodeIfPresent([AreasResponse].self, forKey: .children) ?? [] } init(id: Int, code: String, name: String, children: [AreasResponse] = []) { self.id = id self.code = code self.name = name self.children = children } } /// 更新银行卡信息请求实体。 struct UpdateBankInfoRequest: Encodable, Equatable { let realName: String let cardNumber: String let bankName: String let branchName: String let frontUrl: String let backUrl: String let provinceCode: String let cityCode: String let smsVerifyCode: String enum CodingKeys: String, CodingKey { case realName = "real_name" case cardNumber = "card_number" case bankName = "bank_name" case branchName = "branch_name" case frontUrl = "front_url" case backUrl = "back_url" case provinceCode = "province_code" case cityCode = "city_code" case smsVerifyCode = "sms_verify_code" } } /// 个人信息页校验错误实体。 enum ProfileValidationError: LocalizedError, Equatable { case emptyNickname case shortPassword var errorDescription: String? { switch self { case .emptyNickname: "请输入昵称" case .shortPassword: "密码至少 6 位" } } } 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 } }