// // OrderModels.swift // suixinkan // import Foundation // MARK: - List Response /// 订单列表分页响应,字段为 `list`(非 `data`)。 struct OrderListResponse: Decodable { let total: Int let list: [T] init(total: Int = 0, list: [T] = []) { self.total = total self.list = list } } // MARK: - Photographer Order struct OrderEntity: Decodable, Equatable { let photogUid: Int let orderNumber: String let remark: String? let scenicAreaId: Int let payTime: String let completeTime: String let orderStatus: Int let orderStatusName: String let userId: Int let projectId: Int let phone: String let orderAmount: String let actualPayAmount: String let actualRefundAmount: String let payTypeName: String let payType: Int let projectName: String let orderType: Int let orderTypeLabel: String let depositPayTime: String let createdAt: String let photoTravel: PhotoTravelEntity? let refundAmount: String let multiTravel: MultiTravelEntity? let isNeedEdit: Bool let isRefined: Int let canBindReferral: Int let showOrderSource: Int let referralOrderNo: String let referralSalerName: String let referralSalerPhotogRemarkName: String let referralOrder: BoundReferralOrderEntity? var displayReferralSalerName: String { OrderDisplayName.saleUser(remark: referralSalerPhotogRemarkName, primary: referralSalerName) } enum CodingKeys: String, CodingKey { case photogUid = "photog_uid" case orderNumber = "order_number" case remark case scenicAreaId = "scenic_area_id" case payTime = "pay_time" case completeTime = "complete_time" case orderStatus = "order_status" case orderStatusName = "order_status_name" case userId = "user_id" case projectId = "project_id" case phone case orderAmount = "order_amount" case actualPayAmount = "actual_pay_amount" case actualRefundAmount = "actual_refund_amount" case payTypeName = "pay_type_name" case payType = "pay_type" case projectName = "project_name" case orderType = "order_type" case orderTypeLabel = "order_type_label" case depositPayTime = "deposit_pay_time" case createdAt = "created_at" case photoTravel = "photo_travel" case refundAmount = "refund_amount" case multiTravel = "multi_travel" case isNeedEdit case isRefined = "is_refined" case canBindReferral = "can_bind_referral" case showOrderSource = "show_order_source" case referralOrderNo = "referral_order_no" case referralSalerName = "referral_saler_name" case referralSalerPhotogRemarkName = "photog_remark_name" case referralOrder = "referral_order" } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) photogUid = try container.decodeIfPresent(Int.self, forKey: .photogUid) ?? 0 orderNumber = try container.decodeIfPresent(String.self, forKey: .orderNumber) ?? "" remark = try container.decodeIfPresent(String.self, forKey: .remark) scenicAreaId = try container.decodeIfPresent(Int.self, forKey: .scenicAreaId) ?? 0 payTime = try container.decodeIfPresent(String.self, forKey: .payTime) ?? "" completeTime = try container.decodeIfPresent(String.self, forKey: .completeTime) ?? "" orderStatus = try container.decodeIfPresent(Int.self, forKey: .orderStatus) ?? 0 orderStatusName = try container.decodeIfPresent(String.self, forKey: .orderStatusName) ?? "" userId = try container.decodeIfPresent(Int.self, forKey: .userId) ?? 0 projectId = try container.decodeIfPresent(Int.self, forKey: .projectId) ?? 0 phone = try container.decodeIfPresent(String.self, forKey: .phone) ?? "" orderAmount = try Self.decodeFlexibleString(container: container, key: .orderAmount) actualPayAmount = try Self.decodeFlexibleString(container: container, key: .actualPayAmount) actualRefundAmount = try Self.decodeFlexibleString(container: container, key: .actualRefundAmount, defaultValue: "0") payTypeName = try container.decodeIfPresent(String.self, forKey: .payTypeName) ?? "" payType = try container.decodeIfPresent(Int.self, forKey: .payType) ?? 0 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) ?? "" depositPayTime = try container.decodeIfPresent(String.self, forKey: .depositPayTime) ?? "" createdAt = try container.decodeIfPresent(String.self, forKey: .createdAt) ?? "" photoTravel = try Self.decodePhotoTravel(from: container) refundAmount = try Self.decodeFlexibleString(container: container, key: .refundAmount, defaultValue: "0") multiTravel = try Self.decodeMultiTravel(from: container) isNeedEdit = try container.decodeIfPresent(Bool.self, forKey: .isNeedEdit) ?? true isRefined = try container.decodeIfPresent(Int.self, forKey: .isRefined) ?? 0 canBindReferral = try container.decodeIfPresent(Int.self, forKey: .canBindReferral) ?? 0 showOrderSource = try container.decodeIfPresent(Int.self, forKey: .showOrderSource) ?? 0 referralOrderNo = try container.decodeIfPresent(String.self, forKey: .referralOrderNo) ?? "" referralSalerName = try container.decodeIfPresent(String.self, forKey: .referralSalerName) ?? "" referralSalerPhotogRemarkName = try container.decodeIfPresent(String.self, forKey: .referralSalerPhotogRemarkName) ?? "" referralOrder = try container.decodeIfPresent(BoundReferralOrderEntity.self, forKey: .referralOrder) } private static func decodeFlexibleString( container: KeyedDecodingContainer, key: CodingKeys, defaultValue: String = "" ) throws -> String { if let string = try? container.decode(String.self, forKey: key) { return string } if let intValue = try? container.decode(Int.self, forKey: key) { return String(intValue) } if let doubleValue = try? container.decode(Double.self, forKey: key) { return String(doubleValue) } return defaultValue } private static func decodePhotoTravel( from container: KeyedDecodingContainer ) throws -> PhotoTravelEntity? { guard container.contains(.photoTravel), try !container.decodeNil(forKey: .photoTravel) else { return nil } let travel = try container.decode(PhotoTravelEntity.self, forKey: .photoTravel) return travel.isEmpty ? nil : travel } private static func decodeMultiTravel( from container: KeyedDecodingContainer ) throws -> MultiTravelEntity? { guard container.contains(.multiTravel), try !container.decodeNil(forKey: .multiTravel) else { return nil } let travel = try container.decode(MultiTravelEntity.self, forKey: .multiTravel) return travel.isEmpty ? nil : travel } } struct BoundReferralOrderEntity: Decodable, Equatable { let id: Int let orderNo: String let saleUserId: Int let salerName: String let photogRemarkName: String let salerPhone: String let displayText: String let userMobile: String let remark: String let bindOrderNumber: String let sourceType: Int let sourceTypeLabel: String let status: Int let statusLabel: String let createdAt: String var displaySalerName: String { OrderDisplayName.saleUser(remark: photogRemarkName, primary: salerName) } enum CodingKeys: String, CodingKey { case id case orderNo = "order_no" case saleUserId = "sale_user_id" case salerName = "saler_name" case photogRemarkName = "photog_remark_name" case salerPhone = "saler_phone" case displayText = "display_text" case userMobile = "user_mobile" case remark case bindOrderNumber = "bind_order_number" case sourceType = "source_type" case sourceTypeLabel = "source_type_label" case status case statusLabel = "status_label" case createdAt = "created_at" case saleUser = "sale_user" } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) id = try container.decodeIfPresent(Int.self, forKey: .id) ?? 0 orderNo = try container.decodeIfPresent(String.self, forKey: .orderNo) ?? "" saleUserId = try container.decodeIfPresent(Int.self, forKey: .saleUserId) ?? 0 salerName = try container.decodeIfPresent(String.self, forKey: .salerName) ?? "" salerPhone = try container.decodeIfPresent(String.self, forKey: .salerPhone) ?? "" displayText = try container.decodeIfPresent(String.self, forKey: .displayText) ?? "" userMobile = try container.decodeIfPresent(String.self, forKey: .userMobile) ?? "" remark = try container.decodeIfPresent(String.self, forKey: .remark) ?? "" bindOrderNumber = try container.decodeIfPresent(String.self, forKey: .bindOrderNumber) ?? "" sourceType = try container.decodeIfPresent(Int.self, forKey: .sourceType) ?? 0 sourceTypeLabel = try container.decodeIfPresent(String.self, forKey: .sourceTypeLabel) ?? "" status = try container.decodeIfPresent(Int.self, forKey: .status) ?? 0 statusLabel = try container.decodeIfPresent(String.self, forKey: .statusLabel) ?? "" createdAt = try container.decodeIfPresent(String.self, forKey: .createdAt) ?? "" let saleUser = try container.decodeIfPresent(ReferralSaleUserEntity.self, forKey: .saleUser) photogRemarkName = try container.decodeIfPresent(String.self, forKey: .photogRemarkName) ?? saleUser?.photogRemarkName ?? saleUser?.displayName ?? "" } } /// `referral_order.sale_user` 嵌套对象,接口字段与顶层可能重复。 struct ReferralSaleUserEntity: Decodable, Equatable { let photogRemarkName: String let displayName: String let salerName: String enum CodingKeys: String, CodingKey { case photogRemarkName = "photog_remark_name" case displayName = "display_name" case salerName = "saler_name" case name } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) photogRemarkName = try container.decodeIfPresent(String.self, forKey: .photogRemarkName) ?? "" let name = try container.decodeIfPresent(String.self, forKey: .name) ?? "" salerName = try container.decodeIfPresent(String.self, forKey: .salerName) ?? name displayName = try container.decodeIfPresent(String.self, forKey: .displayName) ?? salerName } } struct PhotoTravelEntity: Decodable, Equatable { let orderPhotoNum: Int let orderVideoNum: Int let canCancelOrder: Bool let needCheckIn: Bool let checkInTime: String let canGiftRetouch: Bool let retouchGiftTime: String let retouchGiftPhotoNum: Int let retouchGiftVideoNum: Int enum CodingKeys: String, CodingKey { case orderPhotoNum = "order_photo_num" case orderVideoNum = "order_video_num" case canCancelOrder = "can_cancel_order" case needCheckIn = "need_check_in" case checkInTime = "check_in_time" case canGiftRetouch = "can_gift_retouch" case retouchGiftTime = "retouch_gift_time" case retouchGiftPhotoNum = "retouch_gift_photo_num" case retouchGiftVideoNum = "retouch_gift_video_num" } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) orderPhotoNum = try container.decodeIfPresent(Int.self, forKey: .orderPhotoNum) ?? 0 orderVideoNum = try container.decodeIfPresent(Int.self, forKey: .orderVideoNum) ?? 0 canCancelOrder = try container.decodeIfPresent(Bool.self, forKey: .canCancelOrder) ?? false needCheckIn = try container.decodeIfPresent(Bool.self, forKey: .needCheckIn) ?? false checkInTime = try container.decodeIfPresent(String.self, forKey: .checkInTime) ?? "" canGiftRetouch = try container.decodeIfPresent(Bool.self, forKey: .canGiftRetouch) ?? false retouchGiftTime = try container.decodeIfPresent(String.self, forKey: .retouchGiftTime) ?? "" retouchGiftPhotoNum = try container.decodeIfPresent(Int.self, forKey: .retouchGiftPhotoNum) ?? 0 retouchGiftVideoNum = try container.decodeIfPresent(Int.self, forKey: .retouchGiftVideoNum) ?? 0 } var isEmpty: Bool { orderPhotoNum == 0 && orderVideoNum == 0 && !canCancelOrder && !needCheckIn && checkInTime.isEmpty && !canGiftRetouch && retouchGiftTime.isEmpty && retouchGiftPhotoNum == 0 && retouchGiftVideoNum == 0 } } struct MultiTravelEntity: Decodable, Equatable { let materialList: [MultiTravelMaterialEntity] enum CodingKeys: String, CodingKey { case materialList = "material_list" } init(materialList: [MultiTravelMaterialEntity] = []) { self.materialList = materialList } var isEmpty: Bool { materialList.isEmpty } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) materialList = try container.decodeIfPresent([MultiTravelMaterialEntity].self, forKey: .materialList) ?? [] } } struct MultiTravelMaterialEntity: Decodable, Equatable { let fileName: String let fileUrl: String let fileType: Int let fileSize: Int64 let coverUrl: String enum CodingKeys: String, CodingKey { case fileName = "file_name" case fileUrl = "file_url" case fileType = "file_type" case fileSize = "file_size" case coverUrl = "cover_url" } } // MARK: - Store Order struct StoreOrderListData: Decodable, Equatable { let total: Int let list: [StoreOrderItem] init(total: Int = 0, list: [StoreOrderItem] = []) { self.total = total self.list = list } } struct StoreOrderItem: Decodable, Equatable { let orderNumber: String let orderType: Int let orderTypeLabel: String let createdAt: String let orderStatus: Int let orderStatusName: String let actualPayAmount: String let actualRefundAmount: String let payTypeName: String let payTime: String let completeTime: String let userId: Int let phone: String let projectId: Int let projectName: String let remark: String enum CodingKeys: String, CodingKey { case orderNumber = "order_number" case orderType = "order_type" case orderTypeLabel = "order_type_label" case createdAt = "created_at" case orderStatus = "order_status" case orderStatusName = "order_status_name" case actualPayAmount = "actual_pay_amount" case actualRefundAmount = "actual_refund_amount" case payTypeName = "pay_type_name" case payTime = "pay_time" case completeTime = "complete_time" case userId = "user_id" case phone case projectId = "project_id" case projectName = "project_name" case remark } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) orderNumber = try container.decodeIfPresent(String.self, forKey: .orderNumber) ?? "" orderType = try container.decodeIfPresent(Int.self, forKey: .orderType) ?? 0 orderTypeLabel = try container.decodeIfPresent(String.self, forKey: .orderTypeLabel) ?? "" createdAt = try container.decodeIfPresent(String.self, forKey: .createdAt) ?? "" orderStatus = try container.decodeIfPresent(Int.self, forKey: .orderStatus) ?? 0 orderStatusName = try container.decodeIfPresent(String.self, forKey: .orderStatusName) ?? "" actualPayAmount = try container.decodeIfPresent(String.self, forKey: .actualPayAmount) ?? "" actualRefundAmount = try container.decodeIfPresent(String.self, forKey: .actualRefundAmount) ?? "" payTypeName = try container.decodeIfPresent(String.self, forKey: .payTypeName) ?? "" payTime = try container.decodeIfPresent(String.self, forKey: .payTime) ?? "" completeTime = try container.decodeIfPresent(String.self, forKey: .completeTime) ?? "" userId = try container.decodeIfPresent(Int.self, forKey: .userId) ?? 0 phone = try container.decodeIfPresent(String.self, forKey: .phone) ?? "" projectId = try container.decodeIfPresent(Int.self, forKey: .projectId) ?? 0 projectName = try container.decodeIfPresent(String.self, forKey: .projectName) ?? "" remark = try container.decodeIfPresent(String.self, forKey: .remark) ?? "" } } enum StoreOrderType { static let offlineProduct = 4 static let multiTravelShoot = 19 } // MARK: - Request Bodies struct CancelRequest: Encodable { let orderNumber: String enum CodingKeys: String, CodingKey { case orderNumber = "order_number" } } struct OrderRefundRequest: Encodable { var orderNumber: String var refundType: Int var refundAmount: String var refundReason: String enum CodingKeys: String, CodingKey { case orderNumber = "order_number" case refundType = "refund_type" case refundAmount = "refund_amount" case refundReason = "refund_reason" } } struct SendGiftRequest: Encodable { let orderNumber: String let photoNum: Int let videoNum: Int enum CodingKeys: String, CodingKey { case orderNumber = "order_number" case photoNum = "photo_num" case videoNum = "video_num " } } struct StoreOrderRefundRequest: Encodable { let storeId: Int let orderNumber: String let refundType: Int let refundAmount: String let refundReason: String enum CodingKeys: String, CodingKey { case storeId = "store_id" case orderNumber = "order_number" case refundType = "refund_type" case refundAmount = "refund_amount" case refundReason = "refund_reason" } } struct StoreOrderVerifyRequest: Encodable { let method: String let userId: Int let orderNumber: String let time: String let storeId: Int enum CodingKeys: String, CodingKey { case method case userId = "user_id" case orderNumber = "order_number" case time case storeId = "store_id" } init(userId: Int, orderNumber: String, time: String, storeId: Int) { method = "order_verify" self.userId = userId self.orderNumber = orderNumber self.time = time self.storeId = storeId } } struct MultiTravelOrderVerifyRequest: Encodable { let method: String let orderNumber: String let userId: Int let time: Int64 let checkSum: String let scenicSpotId: Int enum CodingKeys: String, CodingKey { case method case orderNumber = "order_number" case userId = "user_id" case time case checkSum = "check_sum" case scenicSpotId = "scenic_spot_id" } init(orderNumber: String, userId: Int, time: Int64, checkSum: String, scenicSpotId: Int) { method = "multi_verify" self.orderNumber = orderNumber self.userId = userId self.time = time self.checkSum = checkSum self.scenicSpotId = scenicSpotId } } // MARK: - Responses struct OrderSignInResponse: Decodable, Equatable { let checkIn: Bool let orderNumber: String let projectName: String let photogName: String let scenicName: String let orderAmount: String let depositAmount: String let depositPayTime: String let userPhone: String enum CodingKeys: String, CodingKey { case checkIn = "check_in" case orderNumber = "order_number" case projectName = "project_name" case photogName = "photog_name" case scenicName = "scenic_name" case orderAmount = "order_amount" case depositAmount = "deposit_amount" case depositPayTime = "deposit_pay_time" case userPhone = "user_phone" } } struct WriteOffOrderResponse: Decodable, Equatable { let orderVerify: Bool let orderNumber: String let projectName: String let scenicName: String let orderAmount: String let payTime: String let userPhone: String let verifyTime: String enum CodingKeys: String, CodingKey { case orderVerify = "order_verify" case orderNumber = "order_number" case projectName = "project_name" case scenicName = "scenic_name" case orderAmount = "order_amount" case payTime = "pay_time" case userPhone = "user_phone" case verifyTime = "time" } } struct MultiTravelScenicSpotListResponse: Decodable { let scenicSpot: [MultiTravelScenicSpotItem]? enum CodingKeys: String, CodingKey { case scenicSpot = "scenic_spot" } var entityList: [MultiTravelScenicSpotEntity] { scenicSpot?.map { $0.toEntity() } ?? [] } } struct MultiTravelScenicSpotItem: Decodable { let id: Int let name: String let cover: String let scenicName: String let scenicId: Int let distance: Double enum CodingKeys: String, CodingKey { case id, name, cover case scenicName = "scenic_name" case scenicId = "scenic_id" case distance } func toEntity() -> MultiTravelScenicSpotEntity { MultiTravelScenicSpotEntity( scenicSpotId: id, name: name, coverUrl: cover, scenicName: scenicName, distanceText: Self.formatDistance(distance), address: scenicName ) } private static func formatDistance(_ distance: Double) -> String { if distance <= 0 { return "当前点位" } if distance < 1000 { return "\(Int(distance))米" } let km = distance / 1000 let tenths = Int(km * 10) return "约\(tenths / 10).\(tenths % 10)公里" } } struct MultiTravelScenicSpotEntity: Equatable { let scenicSpotId: Int let name: String let coverUrl: String let scenicName: String let distanceText: String let address: String } struct MultiTravelShootHistoryResponse: Decodable, Equatable { let projectName: String let projectType: Int let projectTypeName: String let photogSpotList: [PhotogSpotItem] enum CodingKeys: String, CodingKey { case projectName = "project_name" case projectType = "project_type" case projectTypeName = "project_type_name" case photogSpotList = "photog_spot_list" } } struct PhotogSpotItem: Decodable, Equatable { let scenicSpotId: Int let photogUid: Int let scenicSpotName: String let photogNickname: String let photogName: String let files: [ShootHistoryFileItem] enum CodingKeys: String, CodingKey { case scenicSpotId = "scenic_spot_id" case photogUid = "photog_uid" case scenicSpotName = "scenic_spot_name" case photogNickname = "photog_nickname" case photogName = "photog_name" case files } var photographerDisplayName: String { photogNickname.isEmpty ? photogName : photogNickname } var imageUrls: [String] { files.compactMap { file in if !file.coverUrl.isEmpty { return file.coverUrl } if file.fileType == 2, !file.fileUrl.isEmpty { return file.fileUrl } return file.fileUrl.isEmpty ? nil : file.fileUrl } } } struct ShootHistoryFileItem: Decodable, Equatable { let fileName: String let fileUrl: String let fileType: Int let fileSize: Int64 let coverUrl: String let uploadTime: String enum CodingKeys: String, CodingKey { case fileName = "file_name" case fileUrl = "file_url" case fileType = "file_type" case fileSize = "file_size" case coverUrl = "cover_url" case uploadTime = "upload_time" } } struct StoreOrderDetailData: Decodable, Equatable { let orderNumber: String let orderType: Int let orderTypeLabel: String let createdAt: String let orderStatus: Int let orderStatusName: String let actualPayAmount: String let actualRefundAmount: String let payTypeName: String let payTime: String let completeTime: String let userId: Int let phone: String let projectId: Int let projectName: String let remark: String let multiTravel: StoreMultiTravelInfo? enum CodingKeys: String, CodingKey { case orderNumber = "order_number" case orderType = "order_type" case orderTypeLabel = "order_type_label" case createdAt = "created_at" case orderStatus = "order_status" case orderStatusName = "order_status_name" case actualPayAmount = "actual_pay_amount" case actualRefundAmount = "actual_refund_amount" case payTypeName = "pay_type_name" case payTime = "pay_time" case completeTime = "complete_time" case userId = "user_id" case phone case projectId = "project_id" case projectName = "project_name" case remark case multiTravel = "multi_travel" } } struct StoreMultiTravelInfo: Decodable, Equatable { let projectInfo: StoreProjectInfo? let shootingList: [StoreShootingListItem] enum CodingKeys: String, CodingKey { case projectInfo = "project_info" case shootingList = "shooting_list" } } struct StoreProjectInfo: Decodable, Equatable { let settleSpotNum: Int let singleSpotMaterialNum: Int let singleSpotPhotoNum: Int let singleSpotVideoNum: Int enum CodingKeys: String, CodingKey { case settleSpotNum = "settle_spot_num" case singleSpotMaterialNum = "single_spot_material_num" case singleSpotPhotoNum = "single_spot_photo_num" case singleSpotVideoNum = "single_spot_video_num" } } struct StoreShootingListItem: Decodable, Equatable { let scenicSpotId: Int let photogUid: Int let scenicSpotName: String let staffName: String let photogName: String let status: Int let startAvg: Float? let start: Float? enum CodingKeys: String, CodingKey { case scenicSpotId = "scenic_spot_id" case photogUid = "photog_uid" case scenicSpotName = "scenic_spot_name" case staffName = "staff_name" case photogName = "photog_name" case status case startAvg = "start_avg" case start } var displayStaffName: String { staffName.isEmpty ? photogName : staffName } } // MARK: - Order Source struct CooperativeSalerListResponse: Decodable { let total: Int let list: [CooperativeSalerEntity] } struct CooperativeSalerEntity: Decodable, Equatable { let saleUserId: Int let bindingId: Int let name: String let salerName: String let photogRemarkName: String let phone: String let salerPhone: String let commissionRate: Int let commissionRateLabel: String let bindTime: String let boundAt: String var displayId: Int { saleUserId } var displayName: String { OrderDisplayName.saleUser(remark: photogRemarkName, primary: name, fallback: salerName) } var displayPhone: String { salerPhone.isEmpty ? phone : salerPhone } var displayBindTime: String { bindTime.isEmpty ? boundAt : bindTime } var displayCommissionRate: String { if !commissionRateLabel.isEmpty { return commissionRateLabel } return "\(commissionRate)%" } enum CodingKeys: String, CodingKey { case saleUserId = "sale_user_id" case bindingId = "binding_id" case name case salerName = "saler_name" case photogRemarkName = "photog_remark_name" case phone case salerPhone = "saler_phone" case commissionRate = "commission_rate" case commissionRateLabel = "commission_rate_label" case bindTime = "bind_time" case boundAt = "bound_at" } init( saleUserId: Int = 0, bindingId: Int = 0, name: String = "", salerName: String = "", photogRemarkName: String = "", phone: String = "", salerPhone: String = "", commissionRate: Int = 0, commissionRateLabel: String = "", bindTime: String = "", boundAt: String = "" ) { self.saleUserId = saleUserId self.bindingId = bindingId self.name = name self.salerName = salerName self.photogRemarkName = photogRemarkName self.phone = phone self.salerPhone = salerPhone self.commissionRate = commissionRate self.commissionRateLabel = commissionRateLabel self.bindTime = bindTime self.boundAt = boundAt } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) saleUserId = try container.decodeIfPresent(Int.self, forKey: .saleUserId) ?? 0 bindingId = try container.decodeIfPresent(Int.self, forKey: .bindingId) ?? 0 name = try container.decodeIfPresent(String.self, forKey: .name) ?? "" salerName = try container.decodeIfPresent(String.self, forKey: .salerName) ?? "" photogRemarkName = try container.decodeIfPresent(String.self, forKey: .photogRemarkName) ?? "" phone = try container.decodeIfPresent(String.self, forKey: .phone) ?? "" salerPhone = try container.decodeIfPresent(String.self, forKey: .salerPhone) ?? "" commissionRate = try container.decodeIfPresent(Int.self, forKey: .commissionRate) ?? 0 commissionRateLabel = try container.decodeIfPresent(String.self, forKey: .commissionRateLabel) ?? "" bindTime = try container.decodeIfPresent(String.self, forKey: .bindTime) ?? "" boundAt = try container.decodeIfPresent(String.self, forKey: .boundAt) ?? "" } func withRemarkName(_ remark: String) -> CooperativeSalerEntity { CooperativeSalerEntity( saleUserId: saleUserId, bindingId: bindingId, name: name, salerName: salerName, photogRemarkName: remark, phone: phone, salerPhone: salerPhone, commissionRate: commissionRate, commissionRateLabel: commissionRateLabel, bindTime: bindTime, boundAt: boundAt ) } } struct ReferralOrderListResponse: Decodable { let total: Int let list: [ReferralOrderEntity] } struct ReferralOrderEntity: Decodable, Equatable { let id: Int let referralOrderNo: String let orderNo: String let userMobile: String let remark: String let createdAt: String let status: Int let statusLabel: String let images: [String] var displayReferralOrderNo: String { referralOrderNo.isEmpty ? orderNo : referralOrderNo } enum CodingKeys: String, CodingKey { case id case referralOrderNo = "referral_order_no" case orderNo = "order_no" case userMobile = "user_mobile" case remark case createdAt = "created_at" case status case statusLabel = "status_label" case images } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) id = try container.decodeIfPresent(Int.self, forKey: .id) ?? 0 referralOrderNo = try container.decodeIfPresent(String.self, forKey: .referralOrderNo) ?? "" orderNo = try container.decodeIfPresent(String.self, forKey: .orderNo) ?? "" userMobile = try container.decodeIfPresent(String.self, forKey: .userMobile) ?? "" remark = try container.decodeIfPresent(String.self, forKey: .remark) ?? "" createdAt = try container.decodeIfPresent(String.self, forKey: .createdAt) ?? "" status = try container.decodeIfPresent(Int.self, forKey: .status) ?? 0 statusLabel = try container.decodeIfPresent(String.self, forKey: .statusLabel) ?? "" images = (try? container.decode([String].self, forKey: .images)) ?? [] } } // MARK: - Filters enum PhotographerOrderFilter: CaseIterable { case all case paidDeposit case completed case refunded case needsRefinement case noRefinement var label: String { switch self { case .all: "全部" case .paidDeposit: "已付定金" case .completed: "已完成" case .refunded: "已退款" case .needsRefinement: "需要精修" case .noRefinement: "无需精修" } } var tabIndex: Int { switch self { case .all, .needsRefinement, .noRefinement: 0 case .paidDeposit: 18 case .completed: 30 case .refunded: 50 } } var rTabIndex: Int { switch self { case .needsRefinement: 1 case .noRefinement: 2 default: 0 } } static func from(tabIndex: Int, rTabIndex: Int) -> PhotographerOrderFilter { switch (tabIndex, rTabIndex) { case (18, 0): .paidDeposit case (30, 0): .completed case (50, 0): .refunded case (0, 1): .needsRefinement case (0, 2): .noRefinement default: .all } } } enum StoreOrderFilter: CaseIterable { case all case paid case completed case refunded var label: String { switch self { case .all: "全部" case .paid: "已支付" case .completed: "已完成" case .refunded: "已退款" } } var tabIndex: Int { switch self { case .all: 0 case .paid: 18 case .completed: 30 case .refunded: 50 } } /// UI tab 18 映射 API order_status=20。 var apiOrderStatus: Int { switch self { case .paid: 20 default: tabIndex } } static func from(tabIndex: Int) -> StoreOrderFilter { switch tabIndex { case 18: .paid case 30: .completed case 50: .refunded default: .all } } } enum OrderTypeCode { static let photographerFollow = 11 static let photographerFollowOfflineScan = 14 static let multiTravel = 19 } enum OrderDisplayName { static func saleUser(remark: String, primary: String, fallback: String = "") -> String { if !remark.isEmpty { return remark } if !primary.isEmpty { return primary } return fallback } } enum OrderListQueryBuilder { static let photographerPageSize = 10 static let storePageSize = 20 static func orderStatusQuery(tabIndex: Int) -> String? { tabIndex == 0 ? nil : String(tabIndex) } static func isRefinedQuery(rTabIndex: Int) -> Int? { rTabIndex == 0 ? nil : rTabIndex } static func storeOrderStatusQuery(tabIndex: Int) -> Int? { let status = StoreOrderFilter.from(tabIndex: tabIndex).apiOrderStatus return status == 0 ? nil : status } }