262 lines
10 KiB
Swift
262 lines
10 KiB
Swift
//
|
||
// 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
|
||
}
|
||
}
|