// // SharedBusinessModels.swift // suixinkan // // Created by Codex on 2026/6/24. // import Foundation /// 可关联订单实体,表示任务、排班等业务表单中可选择的订单。 struct AvailableOrderResponse: Decodable, Identifiable, Hashable { var id: String { orderNumber } let projectName: String let orderNumber: String let orderStatus: Int let orderStatusLabel: String let payTime: String let userPhone: String /// 可关联订单字段映射,兼容后端下划线命名。 enum CodingKeys: String, CodingKey { case projectName = "project_name" case orderNumber = "order_number" case orderStatus = "order_status" case orderStatusLabel = "order_status_label" case payTime = "pay_time" case userPhone = "user_phone" } /// 宽松解码可关联订单字段。 init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) projectName = try container.decodeSharedLossyString(forKey: .projectName) orderNumber = try container.decodeSharedLossyString(forKey: .orderNumber) orderStatus = try container.decodeSharedLossyInt(forKey: .orderStatus) ?? 0 orderStatusLabel = try container.decodeSharedLossyString(forKey: .orderStatusLabel) payTime = try container.decodeSharedLossyString(forKey: .payTime) userPhone = try container.decodeSharedLossyString(forKey: .userPhone) } } /// 摄影项目列表项实体,表示项目管理列表和样片上传时可关联的项目。 struct PhotographerProjectItem: Decodable, Identifiable, Hashable { let id: Int let type: Int let typeName: String let status: Int let statusName: String let name: String let coverProject: String let coverVideo: String let price: String let otPrice: String let priceDeposit: String let attrLabel: [String] let label: String /// 创建摄影项目列表项,主要用于详情页摘要和测试替身。 init( id: Int, type: Int = 0, typeName: String = "", status: Int = 0, statusName: String = "", name: String, coverProject: String = "", coverVideo: String = "", price: String = "", otPrice: String = "", priceDeposit: String = "", attrLabel: [String] = [], label: String = "" ) { self.id = id self.type = type self.typeName = typeName self.status = status self.statusName = statusName self.name = name self.coverProject = coverProject self.coverVideo = coverVideo self.price = price self.otPrice = otPrice self.priceDeposit = priceDeposit self.attrLabel = attrLabel self.label = label } /// 字段映射,兼容后端下划线命名。 enum CodingKeys: String, CodingKey { case id case type case typeName = "type_name" case status case statusName = "status_name" case name case coverProject = "cover_project" case coverVideo = "cover_video" case price case otPrice = "ot_price" case priceDeposit = "price_deposit" case attrLabel = "attr_label" case label } /// 宽松解码项目列表项。 init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) id = try container.decodeSharedLossyInt(forKey: .id) ?? 0 type = try container.decodeSharedLossyInt(forKey: .type) ?? 0 typeName = try container.decodeSharedLossyString(forKey: .typeName) status = try container.decodeSharedLossyInt(forKey: .status) ?? 0 statusName = try container.decodeSharedLossyString(forKey: .statusName) name = try container.decodeSharedLossyString(forKey: .name) coverProject = try container.decodeSharedLossyString(forKey: .coverProject) coverVideo = try container.decodeSharedLossyString(forKey: .coverVideo) price = try container.decodeSharedLossyString(forKey: .price) otPrice = try container.decodeSharedLossyString(forKey: .otPrice) priceDeposit = try container.decodeSharedLossyString(forKey: .priceDeposit) label = try container.decodeSharedLossyString(forKey: .label) if let labels = try? container.decodeIfPresent([String].self, forKey: .attrLabel) { attrLabel = labels } else if let text = try? container.decodeIfPresent(String.self, forKey: .attrLabel) { attrLabel = text .components(separatedBy: CharacterSet(charactersIn: ",,")) .map { $0.trimmingCharacters(in: .whitespacesAndNewlines) } .filter { !$0.isEmpty } } else { attrLabel = [] } } } private extension KeyedDecodingContainer { /// 将 String、数字和 Bool 宽松解码为字符串。 func decodeSharedLossyString(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 "" } /// 将 String、Double 和 Int 宽松解码为 Int。 func decodeSharedLossyInt(forKey key: Key) throws -> Int? { if let value = try? decodeIfPresent(Int.self, forKey: key) { return value } if let value = try? decodeIfPresent(Double.self, forKey: key) { return Int(value) } if let value = try? decodeIfPresent(String.self, forKey: key) { let text = value.trimmingCharacters(in: .whitespacesAndNewlines) return Int(text) ?? Double(text).map(Int.init) } return nil } }