Files
汉秋 abcac9bfdf 新增打卡点与位置上报模块,并接入首页路由
将打卡点管理与位置上报从占位页迁移为完整 MVVM 流程,包含前台定位支持与单元测试。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-24 11:08:41 +08:00

253 lines
8.5 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// PunchPointModels.swift
// suixinkan
//
// Created by Codex on 2026/6/24.
//
import Foundation
///
enum PunchPointFilter: Int, CaseIterable, Identifiable {
case all = 0
case operating = 1
case paused = 2
case pendingReview = 3
case rejected = 4
var id: Int { rawValue }
///
var title: String {
switch self {
case .all: "全部"
case .operating: "运营中"
case .paused: "已暂停"
case .pendingReview: "待审核"
case .rejected: "已驳回"
}
}
}
///
struct PunchPointRegion: Codable, Hashable {
var lat: Double
var lot: Double
var address: String
var scenicSpotStr: String?
enum CodingKeys: String, CodingKey {
case lat
case lot
case address
case scenicSpotStr = "scenic_spot_str"
}
///
init(lat: Double, lot: Double, address: String, scenicSpotStr: String? = nil) {
self.lat = lat
self.lot = lot
self.address = address
self.scenicSpotStr = scenicSpotStr
}
///
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
lat = try container.decodeLossyDouble(forKey: .lat) ?? 0
lot = try container.decodeLossyDouble(forKey: .lot) ?? 0
address = try container.decodeLossyString(forKey: .address)
scenicSpotStr = try container.decodeIfPresent(String.self, forKey: .scenicSpotStr)
}
}
///
struct PunchPointItem: Decodable, Hashable, Identifiable {
let id: Int
let scenicAreaId: Int
let name: String
let status: Int
let statusLabel: String
let description: String
let region: PunchPointRegion?
let scenicSpotStr: String
let guideImages: [String]
let mpQrcode: String
let createdAt: String
let creator: String
let creatorPhone: String
let auditor: String
let auditTime: String
let auditRemark: String
enum CodingKeys: String, CodingKey {
case id
case scenicAreaId = "scenic_area_id"
case name
case status
case statusLabel = "status_label"
case description
case region
case scenicSpotStr = "scenic_spot_str"
case guideImages = "guide_imgs"
case mpQrcode = "mp_qrcode"
case createdAt = "created_at"
case creator
case creatorPhone = "creator_phone"
case auditor
case auditTime = "audit_time"
case auditRemark = "audit_remark"
}
///
init(
id: Int,
scenicAreaId: Int = 0,
name: String,
status: Int = 0,
statusLabel: String = "",
description: String = "",
region: PunchPointRegion? = nil,
scenicSpotStr: String = "",
guideImages: [String] = [],
mpQrcode: String = "",
createdAt: String = "",
creator: String = "",
creatorPhone: String = "",
auditor: String = "",
auditTime: String = "",
auditRemark: String = ""
) {
self.id = id
self.scenicAreaId = scenicAreaId
self.name = name
self.status = status
self.statusLabel = statusLabel
self.description = description
self.region = region
self.scenicSpotStr = scenicSpotStr
self.guideImages = guideImages
self.mpQrcode = mpQrcode
self.createdAt = createdAt
self.creator = creator
self.creatorPhone = creatorPhone
self.auditor = auditor
self.auditTime = auditTime
self.auditRemark = auditRemark
}
///
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decodeLossyInt(forKey: .id) ?? 0
scenicAreaId = try container.decodeLossyInt(forKey: .scenicAreaId) ?? 0
name = try container.decodeLossyString(forKey: .name)
status = try container.decodeLossyInt(forKey: .status) ?? 0
statusLabel = try container.decodeLossyString(forKey: .statusLabel)
description = try container.decodeLossyString(forKey: .description)
region = try container.decodeIfPresent(PunchPointRegion.self, forKey: .region)
scenicSpotStr = try container.decodeLossyString(forKey: .scenicSpotStr)
guideImages = (try? container.decodeIfPresent([String].self, forKey: .guideImages)) ?? []
mpQrcode = try container.decodeLossyString(forKey: .mpQrcode)
createdAt = try container.decodeLossyString(forKey: .createdAt)
creator = try container.decodeLossyString(forKey: .creator)
creatorPhone = try container.decodeLossyString(forKey: .creatorPhone)
auditor = try container.decodeLossyString(forKey: .auditor)
auditTime = try container.decodeLossyString(forKey: .auditTime)
auditRemark = try container.decodeLossyString(forKey: .auditRemark)
}
}
///
struct AddPunchPointRequest: Encodable, Equatable {
let scenicAreaId: String
let name: String
let description: String
let region: PunchPointRegion
let scenicSpotStr: String
let guideImages: [String]
enum CodingKeys: String, CodingKey {
case scenicAreaId = "scenic_area_id"
case name
case description
case region
case scenicSpotStr = "scenic_spot_str"
case guideImages = "guide_imgs"
}
}
///
struct EditPunchPointRequest: Encodable, Equatable {
let id: Int
let scenicAreaId: String
let name: String
let description: String
let region: PunchPointRegion
let scenicSpotStr: String
let guideImages: [String]
enum CodingKeys: String, CodingKey {
case id
case scenicAreaId = "scenic_area_id"
case name
case description
case region
case scenicSpotStr = "scenic_spot_str"
case guideImages = "guide_imgs"
}
}
/// ID
struct PunchPointDeleteRequest: Encodable, Equatable {
let id: Int
}
/// PhotosPicker
struct PunchPointLocalImage: Identifiable, Equatable {
let id = UUID()
let data: Data
let fileName: String
var remoteURL: String?
var progress: Int
///
init(data: Data, fileName: String, remoteURL: String? = nil, progress: Int = 0) {
self.data = data
self.fileName = fileName
self.remoteURL = remoteURL
self.progress = progress
}
}
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 ""
}
/// StringDouble Int 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) {
return Int(value.trimmingCharacters(in: .whitespacesAndNewlines))
}
return nil
}
/// StringDouble Int Double
func decodeLossyDouble(forKey key: Key) throws -> Double? {
if let value = try? decodeIfPresent(Double.self, forKey: key) { return value }
if let value = try? decodeIfPresent(Int.self, forKey: key) { return Double(value) }
if let value = try? decodeIfPresent(String.self, forKey: key) {
return Double(value.trimmingCharacters(in: .whitespacesAndNewlines))
}
return nil
}
}