// // OrderModels.swift // suixinkan // // Created by Codex on 2026/6/22. // import Foundation /// 订单 Tab 内的子入口,表示订单管理或核销订单。 enum OrdersEntry: Hashable { case storeOrders case verificationOrders } /// 订单状态筛选实体,表示订单管理列表顶部的状态过滤项。 struct OrderStatusFilter: Identifiable, Hashable { let id: Int let title: String } /// 订单筛选常量,集中维护旧工程同步过来的订单状态。 enum OrderFilters { static let statusFilters: [OrderStatusFilter] = [ .init(id: 0, title: "全部"), .init(id: 18, title: "已付定金"), .init(id: 30, title: "已完成"), .init(id: 50, title: "已退款"), .init(id: -1, title: "需要精修"), .init(id: -2, title: "无需精修") ] } /// 订单管理列表实体,承载订单卡片展示和筛选判断所需字段。 struct OrderEntity: Decodable, Identifiable, Equatable { var id: String { orderNumber } 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 storeId: Int? let userId: Int let projectId: Int let phone: String let orderAmount: String let actualPayAmount: String let actualRefundAmount: String let refundAmount: String let payTypeName: String let payType: Int let projectName: String let orderType: Int let orderTypeLabel: String let depositPayTime: String let createdAt: String let photoTravel: OrderPhotoTravel? let isNeedEdit: Bool let isRefined: Int 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 storeId = "store_id" 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 refundAmount = "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 isNeedEdit case isRefined = "is_refined" } /// 宽松解码订单字段,兼容后端数字和字符串混用。 init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) photogUid = try container.decodeLossyInt(forKey: .photogUid) ?? 0 orderNumber = try container.decodeLossyString(forKey: .orderNumber) remark = try container.decodeLossyString(forKey: .remark) scenicAreaId = try container.decodeLossyInt(forKey: .scenicAreaId) ?? 0 payTime = try container.decodeLossyString(forKey: .payTime) completeTime = try container.decodeLossyString(forKey: .completeTime) orderStatus = try container.decodeLossyInt(forKey: .orderStatus) ?? 0 orderStatusName = try container.decodeLossyString(forKey: .orderStatusName) storeId = try container.decodeLossyInt(forKey: .storeId) userId = try container.decodeLossyInt(forKey: .userId) ?? 0 projectId = try container.decodeLossyInt(forKey: .projectId) ?? 0 phone = try container.decodeLossyString(forKey: .phone) orderAmount = try container.decodeLossyString(forKey: .orderAmount) actualPayAmount = try container.decodeLossyString(forKey: .actualPayAmount) actualRefundAmount = try container.decodeLossyString(forKey: .actualRefundAmount) refundAmount = try container.decodeLossyString(forKey: .refundAmount) payTypeName = try container.decodeLossyString(forKey: .payTypeName) payType = try container.decodeLossyInt(forKey: .payType) ?? 0 projectName = try container.decodeLossyString(forKey: .projectName) orderType = try container.decodeLossyInt(forKey: .orderType) ?? 0 orderTypeLabel = try container.decodeLossyString(forKey: .orderTypeLabel) depositPayTime = try container.decodeLossyString(forKey: .depositPayTime) createdAt = try container.decodeLossyString(forKey: .createdAt) photoTravel = try container.decodeIfPresent(OrderPhotoTravel.self, forKey: .photoTravel) isNeedEdit = try container.decodeLossyBool(forKey: .isNeedEdit) ?? true isRefined = try container.decodeLossyInt(forKey: .isRefined) ?? 0 } } /// 旅拍订单附加信息实体,表示修图、视频和核销相关数量。 struct OrderPhotoTravel: Decodable, Equatable { let orderPhotoNum: Int let orderVideoNum: Int let checkInTime: String let retouchGiftPhotoNum: Int let retouchGiftVideoNum: Int enum CodingKeys: String, CodingKey { case orderPhotoNum = "order_photo_num" case orderVideoNum = "order_video_num" case checkInTime = "check_in_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.decodeLossyInt(forKey: .orderPhotoNum) ?? 0 orderVideoNum = try container.decodeLossyInt(forKey: .orderVideoNum) ?? 0 checkInTime = try container.decodeLossyString(forKey: .checkInTime) retouchGiftPhotoNum = try container.decodeLossyInt(forKey: .retouchGiftPhotoNum) ?? 0 retouchGiftVideoNum = try container.decodeLossyInt(forKey: .retouchGiftVideoNum) ?? 0 } } /// 核销订单列表实体,表示可核销订单卡片的展示和操作状态。 struct WriteOffOrderItem: Decodable, Identifiable, Equatable { var id: String { orderNumber } let orderNumber: String let orderVerificationStatus: String let payTime: String let projectName: String let userPhone: String let orderAmount: String let orderStatusName: String let orderVerificationTime: String enum CodingKeys: String, CodingKey { case orderNumber = "order_number" case orderVerificationStatus = "order_verification_status" case payTime = "pay_time" case projectName = "project_name" case userPhone = "user_phone" case orderAmount = "order_amount" case orderStatusName = "order_status_name" case orderVerificationTime = "order_verification_time" } /// 宽松解码核销订单字段。 init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) orderNumber = try container.decodeLossyString(forKey: .orderNumber) orderVerificationStatus = try container.decodeLossyString(forKey: .orderVerificationStatus) payTime = try container.decodeLossyString(forKey: .payTime) projectName = try container.decodeLossyString(forKey: .projectName) userPhone = try container.decodeLossyString(forKey: .userPhone) orderAmount = try container.decodeLossyString(forKey: .orderAmount) orderStatusName = try container.decodeLossyString(forKey: .orderStatusName) orderVerificationTime = try container.decodeLossyString(forKey: .orderVerificationTime) } } /// 核销订单请求实体,提交手动输入的订单号。 struct WriteOffRequest: Encodable { let orderNumber: String enum CodingKeys: String, CodingKey { case orderNumber = "order_number" } } private extension KeyedDecodingContainer { /// 将任意常见 JSON 值宽松解码成字符串。 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 ? "1" : "0" } return "" } /// 将 Int、Double、Bool 或数字字符串宽松解码成整数。 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) } if let value = try? decodeIfPresent(Bool.self, forKey: key) { return value ? 1 : 0 } return nil } /// 将 Bool、Int 或字符串宽松解码成布尔值。 func decodeLossyBool(forKey key: Key) throws -> Bool? { if let value = try? decodeIfPresent(Bool.self, forKey: key) { return value } if let value = try? decodeIfPresent(Int.self, forKey: key) { return value != 0 } if let value = try? decodeIfPresent(String.self, forKey: key) { switch value.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() { case "1", "true", "yes": return true case "0", "false", "no": return false default: return nil } } return nil } }