Initial commit: suixinkan_ios UIKit rewrite project.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
140
suixinkan_ios/Features/Schedule/Models/ScheduleModels.swift
Normal file
140
suixinkan_ios/Features/Schedule/Models/ScheduleModels.swift
Normal file
@ -0,0 +1,140 @@
|
||||
//
|
||||
// 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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user