新增订单长尾流程,并迁移排队、消息、结算与审核模块

将定金订单、历史拍摄与多行程上传接入 Orders,并以真实页面替换首页排队管理、消息中心、景区结算与提现审核占位入口。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-25 13:39:02 +08:00
parent 311a70d610
commit c39c3d3c75
63 changed files with 9823 additions and 58 deletions

View File

@ -0,0 +1,226 @@
//
// MessageCenterModels.swift
// suixinkan
//
// Created by Codex on 2026/6/25.
//
import Foundation
import SwiftUI
/// 使 last_id
struct MessageListResponse: Decodable, Equatable {
let hasMore: Bool
let lastId: Int
let items: [MessageEntity]
enum CodingKeys: String, CodingKey {
case hasMore = "has_more"
case lastId = "last_id"
case items
}
///
init(hasMore: Bool = false, lastId: Int = 0, items: [MessageEntity] = []) {
self.hasMore = hasMore
self.lastId = lastId
self.items = items
}
/// //
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
hasMore = try container.decodeLossyBool(forKey: .hasMore) ?? false
lastId = try container.decodeLossyInt(forKey: .lastId) ?? 0
items = try container.decodeIfPresent([MessageEntity].self, forKey: .items) ?? []
}
}
///
struct MessageEntity: Decodable, Equatable, Identifiable {
let id: Int
let type: Int
let typeName: String
let title: String
let content: String
let pushAt: String
let createdAt: String
let isRead: Bool
enum CodingKeys: String, CodingKey {
case id
case type
case typeName = "type_name"
case title
case content
case pushAt = "push_at"
case createdAt = "created_at"
case isRead = "is_read"
}
///
init(
id: Int,
type: Int,
typeName: String = "",
title: String = "",
content: String = "",
pushAt: String = "",
createdAt: String = "",
isRead: Bool = false
) {
self.id = id
self.type = type
self.typeName = typeName
self.title = title
self.content = content
self.pushAt = pushAt
self.createdAt = createdAt
self.isRead = isRead
}
///
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decodeLossyInt(forKey: .id) ?? 0
type = try container.decodeLossyInt(forKey: .type) ?? 0
typeName = try container.decodeLossyString(forKey: .typeName)
title = try container.decodeLossyString(forKey: .title)
content = try container.decodeLossyString(forKey: .content)
pushAt = try container.decodeLossyString(forKey: .pushAt)
createdAt = try container.decodeLossyString(forKey: .createdAt)
isRead = try container.decodeLossyBool(forKey: .isRead) ?? false
}
}
///
struct MessageDeleteRequest: Encodable {
let id: Int
}
///
enum MessageType: Equatable {
case order
case writeOff
case system
///
var iconName: String {
switch self {
case .order:
return "cart.fill"
case .writeOff:
return "qrcode.viewfinder"
case .system:
return "bell.badge.fill"
}
}
///
var title: String {
switch self {
case .order:
return "订单"
case .writeOff:
return "核销"
case .system:
return "系统"
}
}
///
var color: Color {
switch self {
case .order:
return AppDesign.primary
case .writeOff:
return AppDesign.success
case .system:
return AppDesign.warning
}
}
}
///
struct MessageItem: Identifiable, Equatable {
let id: String
let msgId: Int?
let title: String
let detail: String
let time: String
var isRead: Bool
let type: MessageType
}
///
enum MessageFilter: Int, CaseIterable, Identifiable {
case all = 0
case unread = 1
var id: Int { rawValue }
///
var title: String {
switch self {
case .all:
return "全部"
case .unread:
return "未读"
}
}
}
private extension KeyedDecodingContainer {
/// String Bool
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 ""
}
/// String Bool Int
func decodeLossyInt(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),
case let text = value.trimmingCharacters(in: .whitespacesAndNewlines),
!text.isEmpty {
return Int(text) ?? Int(Double(text) ?? 0)
}
if let value = try? decodeIfPresent(Bool.self, forKey: key) {
return value ? 1 : 0
}
return nil
}
/// String Bool Bool
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),
case let text = value.trimmingCharacters(in: .whitespacesAndNewlines).lowercased(),
!text.isEmpty {
if ["1", "true", "yes"].contains(text) { return true }
if ["0", "false", "no"].contains(text) { return false }
}
return nil
}
}