// // CooperationOrderModels.swift // suixinkan // import Foundation /// 合作订单功能常量与校验,对齐 Android `CooperationOrderFeature`。 enum CooperationOrderFeature { static let uri = "cooperation_order" static let remarkNameMaxLength = 10 static func remarkNameValidationMessage(_ remark: String) -> String? { let trimmed = remark.trimmingCharacters(in: .whitespacesAndNewlines) if trimmed.count > remarkNameMaxLength { return "备注名不能超过\(remarkNameMaxLength)个字符" } return nil } static func hasPermission(in permissions: [HomePermissionItem]) -> Bool { permissions.contains { $0.uri == uri } } } /// GET /photog/sale-user/referral-leads 获客线索。 struct ReferralLeadEntity: Decodable, Equatable { let id: Int let leadId: Int let displayName: String let photogRemarkName: String let name: String let salerName: String let userPhone: String let userMobileMasked: String let phone: String let remark: String let createdAt: String let imageUrls: [String] let images: [String] var displayId: String { String(leadId > 0 ? leadId : id) } var displayPhone: String { if !userMobileMasked.isEmpty { return userMobileMasked } if !userPhone.isEmpty { return userPhone } return phone } var displayImages: [String] { imageUrls.isEmpty ? images : imageUrls } var displaySalerName: String { if !displayName.isEmpty { return displayName } return OrderDisplayName.saleUser(remark: photogRemarkName, primary: name, fallback: salerName) } enum CodingKeys: String, CodingKey { case id case leadId = "lead_id" case displayName = "display_name" case photogRemarkName = "photog_remark_name" case name case salerName = "saler_name" case userPhone = "user_phone" case userMobileMasked = "user_mobile_masked" case phone case remark case createdAt = "created_at" case imageUrls = "image_urls" case images } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) id = try container.decodeIfPresent(Int.self, forKey: .id) ?? 0 leadId = try container.decodeIfPresent(Int.self, forKey: .leadId) ?? 0 displayName = try container.decodeIfPresent(String.self, forKey: .displayName) ?? "" photogRemarkName = try container.decodeIfPresent(String.self, forKey: .photogRemarkName) ?? "" name = try container.decodeIfPresent(String.self, forKey: .name) ?? "" salerName = try container.decodeIfPresent(String.self, forKey: .salerName) ?? "" userPhone = try container.decodeIfPresent(String.self, forKey: .userPhone) ?? "" userMobileMasked = try container.decodeIfPresent(String.self, forKey: .userMobileMasked) ?? "" phone = try container.decodeIfPresent(String.self, forKey: .phone) ?? "" remark = try container.decodeIfPresent(String.self, forKey: .remark) ?? "" createdAt = try container.decodeIfPresent(String.self, forKey: .createdAt) ?? "" imageUrls = try container.decodeIfPresent([String].self, forKey: .imageUrls) ?? [] images = try container.decodeIfPresent([String].self, forKey: .images) ?? [] } } /// GET /photog/sale-user/acquisition-orders 获客订单。 struct AcquisitionOrderEntity: Decodable, Equatable { let id: Int let orderNumber: String let projectName: String let orderType: Int let orderTypeLabel: String let orderAmount: String let amount: String let actualPayAmount: String let createdAt: String let customerPhone: String let userMobileMasked: String let phone: String let saleUserName: String let photogRemarkName: String let partnerName: String var displayAmount: String { if !orderAmount.isEmpty { return orderAmount } if !amount.isEmpty { return amount } return actualPayAmount } var displayCustomerPhone: String { if !userMobileMasked.isEmpty { return userMobileMasked } if !customerPhone.isEmpty { return customerPhone } return phone } var displayPartner: String { let salerDisplay = OrderDisplayName.saleUser(remark: photogRemarkName, primary: saleUserName) if !salerDisplay.isEmpty { return salerDisplay } return partnerName } enum CodingKeys: String, CodingKey { case id case orderNumber = "order_number" case projectName = "project_name" case orderType = "order_type" case orderTypeLabel = "order_type_label" case orderAmount = "order_amount" case amount case actualPayAmount = "actual_pay_amount" case createdAt = "created_at" case customerPhone = "customer_phone" case userMobileMasked = "user_mobile_masked" case phone case saleUserName = "sale_user_name" case photogRemarkName = "photog_remark_name" case partnerName = "partner_name" } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) id = try container.decodeIfPresent(Int.self, forKey: .id) ?? 0 orderNumber = try container.decodeIfPresent(String.self, forKey: .orderNumber) ?? "" projectName = try container.decodeIfPresent(String.self, forKey: .projectName) ?? "" orderType = try container.decodeIfPresent(Int.self, forKey: .orderType) ?? 0 orderTypeLabel = try container.decodeIfPresent(String.self, forKey: .orderTypeLabel) ?? "" orderAmount = try container.decodeIfPresent(String.self, forKey: .orderAmount) ?? "" amount = try container.decodeIfPresent(String.self, forKey: .amount) ?? "" actualPayAmount = try container.decodeIfPresent(String.self, forKey: .actualPayAmount) ?? "" createdAt = try container.decodeIfPresent(String.self, forKey: .createdAt) ?? "" customerPhone = try container.decodeIfPresent(String.self, forKey: .customerPhone) ?? "" userMobileMasked = try container.decodeIfPresent(String.self, forKey: .userMobileMasked) ?? "" phone = try container.decodeIfPresent(String.self, forKey: .phone) ?? "" saleUserName = try container.decodeIfPresent(String.self, forKey: .saleUserName) ?? "" photogRemarkName = try container.decodeIfPresent(String.self, forKey: .photogRemarkName) ?? "" partnerName = try container.decodeIfPresent(String.self, forKey: .partnerName) ?? "" } } /// GET /photog/sale-user/invite-info 扫码邀请信息。 struct SaleUserInviteInfoResponse: Decodable, Equatable { let saleUserId: Int let isBound: Bool let photographerName: String let photographerPhone: String let salerName: String let salerPhone: String let shareCode: String let subtitle: String let title: String enum CodingKeys: String, CodingKey { case saleUserId = "sale_user_id" case isBound = "is_bound" case photographerName = "photographer_name" case photographerPhone = "photographer_phone" case salerName = "saler_name" case salerPhone = "saler_phone" case shareCode = "share_code" case subtitle case title } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) saleUserId = try container.decodeIfPresent(Int.self, forKey: .saleUserId) ?? 0 isBound = try container.decodeIfPresent(Bool.self, forKey: .isBound) ?? false photographerName = try container.decodeIfPresent(String.self, forKey: .photographerName) ?? "" photographerPhone = try container.decodeIfPresent(String.self, forKey: .photographerPhone) ?? "" salerName = try container.decodeIfPresent(String.self, forKey: .salerName) ?? "" salerPhone = try container.decodeIfPresent(String.self, forKey: .salerPhone) ?? "" shareCode = try container.decodeIfPresent(String.self, forKey: .shareCode) ?? "" subtitle = try container.decodeIfPresent(String.self, forKey: .subtitle) ?? "" title = try container.decodeIfPresent(String.self, forKey: .title) ?? "" } init( saleUserId: Int = 0, isBound: Bool = false, photographerName: String = "", photographerPhone: String = "", salerName: String = "", salerPhone: String = "", shareCode: String = "", subtitle: String = "", title: String = "" ) { self.saleUserId = saleUserId self.isBound = isBound self.photographerName = photographerName self.photographerPhone = photographerPhone self.salerName = salerName self.salerPhone = salerPhone self.shareCode = shareCode self.subtitle = subtitle self.title = title } } struct SaleUserUpdateRemarkRequest: Encodable { let saleUserId: Int let photogRemarkName: String enum CodingKeys: String, CodingKey { case saleUserId = "sale_user_id" case photogRemarkName = "photog_remark_name" } } struct SaleUserCommissionRateSmsRequest: Encodable { let phone: String } struct SaleUserCommissionRateUpdateRequest: Encodable { let saleUserId: Int let commissionRate: Int let smsCode: String enum CodingKeys: String, CodingKey { case saleUserId = "sale_user_id" case commissionRate = "commission_rate" case smsCode = "sms_code" } }