Initial commit: suixinkan_ios UIKit rewrite project.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
48
suixinkan_ios/Features/Profile/Models/AgreementPage.swift
Normal file
48
suixinkan_ios/Features/Profile/Models/AgreementPage.swift
Normal file
@ -0,0 +1,48 @@
|
||||
//
|
||||
// AgreementPage.swift
|
||||
// suixinkan
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// 协议和说明页面枚举,描述设置中心可打开的 H5 页面。
|
||||
enum AgreementPage: Hashable, Identifiable {
|
||||
case about
|
||||
case userAgreement
|
||||
case privacyPolicy
|
||||
case walletUserNotice
|
||||
case walletPrivacy
|
||||
|
||||
var id: String { title }
|
||||
|
||||
/// 页面导航标题。
|
||||
var title: String {
|
||||
switch self {
|
||||
case .about: "关于我们"
|
||||
case .userAgreement: "用户协议"
|
||||
case .privacyPolicy: "隐私政策"
|
||||
case .walletUserNotice: "钱包用户须知"
|
||||
case .walletPrivacy: "钱包隐私政策"
|
||||
}
|
||||
}
|
||||
|
||||
/// 页面对应的 H5 地址。
|
||||
var url: URL {
|
||||
let path = switch self {
|
||||
case .about: "/h5/app/about-us"
|
||||
case .userAgreement: "/h5/app/user-agreement"
|
||||
case .privacyPolicy: "/h5/app/privacy-policy"
|
||||
case .walletUserNotice: "/h5/app/wallet-user-notice"
|
||||
case .walletPrivacy: "/h5/app/wallet-privacy"
|
||||
}
|
||||
return APIEnvironment.current.baseURL.appending(path: path)
|
||||
}
|
||||
}
|
||||
|
||||
/// 设置展示策略,集中处理版本号等纯展示逻辑。
|
||||
enum SettingsDisplayPolicy {
|
||||
/// 生成页面显示的版本号。
|
||||
nonisolated static func versionText(infoDictionary: [String: Any]? = Bundle.main.infoDictionary) -> String {
|
||||
AppClientInfo.appVersion(infoDictionary: infoDictionary)
|
||||
}
|
||||
}
|
||||
217
suixinkan_ios/Features/Profile/Models/ProfileModels.swift
Normal file
217
suixinkan_ios/Features/Profile/Models/ProfileModels.swift
Normal file
@ -0,0 +1,217 @@
|
||||
//
|
||||
// ProfileModels.swift
|
||||
// suixinkan
|
||||
//
|
||||
// Created by Codex on 2026/6/20.
|
||||
//
|
||||
|
||||
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
|
||||
|
||||
/// 用户资料响应的 JSON 字段映射。
|
||||
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?
|
||||
|
||||
/// 实名认证响应的 JSON 字段映射。
|
||||
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
|
||||
}
|
||||
|
||||
/// 实名认证信息的 JSON 字段映射。
|
||||
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
|
||||
|
||||
/// 审核人 JSON 字段映射。
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case id
|
||||
case name
|
||||
}
|
||||
|
||||
/// 自定义解码,兼容审核人 ID 类型不稳定。
|
||||
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
|
||||
|
||||
/// 实名认证提交请求 JSON 字段映射。
|
||||
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"
|
||||
}
|
||||
}
|
||||
|
||||
private extension KeyedDecodingContainer {
|
||||
/// 将 String、数字和 Bool 宽松解码为字符串。
|
||||
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 ""
|
||||
}
|
||||
|
||||
/// 将 Int、Double 或数字字符串宽松解码为整数。
|
||||
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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user