141 lines
4.9 KiB
Swift
141 lines
4.9 KiB
Swift
//
|
||
// ScheduleModels.swift
|
||
// suixinkan
|
||
//
|
||
// Created by Codex on 2026/6/24.
|
||
//
|
||
|
||
import Foundation
|
||
|
||
/// 排班日程实体,表示某一天的一条工作安排。
|
||
struct ScheduleItem: Decodable, Identifiable, Hashable {
|
||
let id: Int
|
||
let name: String
|
||
let remark: String
|
||
let startTime: String
|
||
let endTime: String
|
||
let scheduleDate: String
|
||
let orderNumber: String?
|
||
let userNickname: String?
|
||
let userPhone: String?
|
||
let orderStatus: Int?
|
||
let orderStatusName: String?
|
||
|
||
/// 字段映射,兼容后端下划线命名。
|
||
enum CodingKeys: String, CodingKey {
|
||
case id
|
||
case name
|
||
case remark
|
||
case startTime = "start_time"
|
||
case endTime = "end_time"
|
||
case scheduleDate = "schedule_date"
|
||
case orderNumber = "order_number"
|
||
case userNickname = "user_nickname"
|
||
case userPhone = "user_phone"
|
||
case orderStatus = "order_status"
|
||
case orderStatusName = "order_status_name"
|
||
}
|
||
|
||
/// 宽松解码排班字段。
|
||
init(from decoder: Decoder) throws {
|
||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||
id = try container.decodeScheduleLossyInt(forKey: .id) ?? 0
|
||
name = try container.decodeScheduleLossyString(forKey: .name)
|
||
remark = try container.decodeScheduleLossyString(forKey: .remark)
|
||
startTime = try container.decodeScheduleLossyString(forKey: .startTime)
|
||
endTime = try container.decodeScheduleLossyString(forKey: .endTime)
|
||
scheduleDate = try container.decodeScheduleLossyString(forKey: .scheduleDate)
|
||
orderNumber = container.decodeScheduleLossyOptionalString(forKey: .orderNumber)
|
||
userNickname = container.decodeScheduleLossyOptionalString(forKey: .userNickname)
|
||
userPhone = container.decodeScheduleLossyOptionalString(forKey: .userPhone)
|
||
orderStatus = try container.decodeScheduleLossyInt(forKey: .orderStatus)
|
||
orderStatusName = container.decodeScheduleLossyOptionalString(forKey: .orderStatusName)
|
||
}
|
||
}
|
||
|
||
/// 新增排班请求实体。
|
||
struct AddScheduleRequest: Encodable, Equatable {
|
||
let scenicId: String
|
||
let name: String
|
||
let remark: String
|
||
let startTime: String
|
||
let endTime: String
|
||
let scheduleDate: String
|
||
let orderNumber: String?
|
||
|
||
/// 请求字段映射,兼容后端下划线命名。
|
||
enum CodingKeys: String, CodingKey {
|
||
case scenicId = "scenic_id"
|
||
case name
|
||
case remark
|
||
case startTime = "start_time"
|
||
case endTime = "end_time"
|
||
case scheduleDate = "schedule_date"
|
||
case orderNumber = "order_number"
|
||
}
|
||
}
|
||
|
||
/// 删除排班请求实体。
|
||
struct ScheduleDeleteRequest: Encodable, Equatable {
|
||
let id: Int
|
||
}
|
||
|
||
/// 排班编辑表单实体,用于 ViewModel 校验和生成请求。
|
||
struct ScheduleDraft: Equatable {
|
||
var name = ""
|
||
var remark = ""
|
||
var scheduleDate = ""
|
||
var startTime = ""
|
||
var endTime = ""
|
||
var manualOrderNumber = ""
|
||
var selectedOrder: AvailableOrderResponse?
|
||
|
||
/// 计算最终订单号,优先使用选择的订单,手工输入作为兜底。
|
||
func finalOrderNumber() -> String? {
|
||
let picked = selectedOrder?.orderNumber.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
||
let manual = manualOrderNumber.trimmingCharacters(in: .whitespacesAndNewlines)
|
||
let value = picked.isEmpty ? manual : picked
|
||
return value.isEmpty ? nil : value
|
||
}
|
||
}
|
||
|
||
private extension KeyedDecodingContainer {
|
||
/// 将 String、数字和 Bool 宽松解码为字符串。
|
||
func decodeScheduleLossyString(forKey key: Key) throws -> String {
|
||
decodeScheduleLossyOptionalString(forKey: key) ?? ""
|
||
}
|
||
|
||
/// 将 String、数字和 Bool 宽松解码为可选字符串。
|
||
func decodeScheduleLossyOptionalString(forKey key: Key) -> String? {
|
||
if let value = try? decodeIfPresent(String.self, forKey: key) {
|
||
let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines)
|
||
return trimmed.isEmpty ? nil : 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 nil
|
||
}
|
||
|
||
/// 将 String、Double 和 Int 宽松解码为 Int。
|
||
func decodeScheduleLossyInt(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
|
||
}
|
||
}
|