// // PaymentModels.swift // suixinkan // import Foundation /// 收款页展示配置接口响应。 struct PayPageConfigResponse: Decodable, Sendable, Equatable { let background: String let logo: String let title: String let subtitle: String init(background: String, logo: String, title: String, subtitle: String) { self.background = background self.logo = logo self.title = title self.subtitle = subtitle } } /// 收款页最终可展示并可持久化的品牌配置。 struct PayPageConfig: Codable, Sendable, Equatable { let background: String let logo: String let title: String let subtitle: String /// 那拉提景区随包默认配置。 static let nalatiDefault = PayPageConfig( background: "", logo: "", title: "那拉提旅拍管理收费平台", subtitle: "那拉提景区" ) /// 将服务端非空有效字段合并到当前可用配置。 func merging(_ response: PayPageConfigResponse) -> PayPageConfig { PayPageConfig( background: Self.validRemoteImageURL(response.background) ?? background, logo: Self.validRemoteImageURL(response.logo) ?? logo, title: response.title.trimmedNonEmpty ?? title, subtitle: response.subtitle.trimmedNonEmpty ?? subtitle ) } private static func validRemoteImageURL(_ value: String) -> String? { let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines) guard let url = URL(string: trimmed), url.scheme?.lowercased() == "https", url.host != nil else { return nil } return trimmed } } /// 收款页品牌化景区判定策略。 enum PayPageBrandingPolicy { /// 那拉提景区正式 ID。 static let nalatiScenicId = 128 /// 判断当前景区是否启用品牌收款页。 static func isNalati(scenicId: Int) -> Bool { scenicId == nalatiScenicId } } /// 收款页当前账号信息的展示文本格式化规则。 enum PaymentAccountDisplayFormatter { /// 解析当前登录摄影师名称,昵称为空时使用实名,仍为空时显示占位符。 static func photographerName(userName: String, realName: String) -> String { userName.trimmedNonEmpty ?? realName.trimmedNonEmpty ?? "-" } /// 去除空值与重复项后,合并当前账号的全部店铺名称。 static func storeNames(_ names: [String]) -> String { var seen = Set() let resolved = names.compactMap(\.trimmedNonEmpty).filter { seen.insert($0).inserted } return resolved.isEmpty ? "-" : resolved.joined(separator: "、") } } /// 摄影师收款码响应,包含静态与动态支付 URL。 struct PayCodeResponse: Decodable, Sendable, Equatable { let staticPayURL: String let dynamicPayURL: String enum CodingKeys: String, CodingKey { case staticPayURL = "static_pay_url" case dynamicPayURL = "dynamic_pay_url" } init(staticPayURL: String, dynamicPayURL: String) { self.staticPayURL = staticPayURL self.dynamicPayURL = dynamicPayURL } } /// 收款记录响应,含按日汇总与明细列表。 struct RepaymentCollectionRecordResponse: Decodable, Sendable, Equatable { let analyse: [RepaymentCollectionRecordAnalyseItem] let list: [RepaymentCollectionRecordItem] init( analyse: [RepaymentCollectionRecordAnalyseItem] = [], list: [RepaymentCollectionRecordItem] = [] ) { self.analyse = analyse self.list = list } } /// 收款记录按日汇总项。 struct RepaymentCollectionRecordAnalyseItem: Decodable, Sendable, Equatable, Hashable { let date: String let orderCount: Int let orderAmountSum: String enum CodingKeys: String, CodingKey { case date case orderCount = "order_count" case orderAmountSum = "order_amount_sum" } init(date: String, orderCount: Int, orderAmountSum: String) { self.date = date self.orderCount = orderCount self.orderAmountSum = orderAmountSum } } /// 收款记录明细项。 struct RepaymentCollectionRecordItem: Decodable, Sendable, Equatable, Hashable { let orderNumber: String let userPhone: String let orderAmount: String let createDate: String let createTime: String enum CodingKeys: String, CodingKey { case orderNumber = "order_number" case userPhone = "user_phone" case orderAmount = "order_amount" case createDate = "create_date" case createTime = "create_time" } init( orderNumber: String, userPhone: String, orderAmount: String, createDate: String, createTime: String ) { self.orderNumber = orderNumber self.userPhone = userPhone self.orderAmount = orderAmount self.createDate = createDate self.createTime = createTime } } /// 按日分组的收款记录,对齐 Android `RepaymentCollectionRecordGroup`。 struct RepaymentCollectionRecordGroup: Sendable, Equatable, Hashable { let analyse: RepaymentCollectionRecordAnalyseItem let items: [RepaymentCollectionRecordItem] } /// 收款商户展示信息。 struct PaymentMerchantInfo: Sendable, Equatable { let name: String let company: String static let defaultInfo = PaymentMerchantInfo( name: "元智享", company: "扬州元智享网络科技有限公司" ) } private extension String { var trimmedNonEmpty: String? { let value = trimmingCharacters(in: .whitespacesAndNewlines) return value.isEmpty ? nil : value } }