Files
suixinkan_ios_new/suixinkan/Features/PilotCertification/Models/PilotCertificationModels.swift
汉秋 41dda3cc9b Add OperatingArea and PilotCertification modules with live push readiness.
Migrate operating area and pilot certification from home placeholders, extend Live with playback and push readiness flows, and add pilot certificate OSS upload.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-25 18:15:59 +08:00

311 lines
11 KiB
Swift

//
// PilotCertificationModels.swift
// suixinkan
//
// Created by Codex on 2026/6/25.
//
import Foundation
///
enum PilotCertType: Int, CaseIterable, Identifiable {
case idCard = 1
case caac = 2
var id: Int { rawValue }
var title: String {
switch self {
case .idCard:
"身份证"
case .caac:
"民用无人机驾驶员执照"
}
}
}
///
struct FlyerDetailResponse: Decodable, Equatable {
let id: Int
let name: String
let accountId: String
let realnameStatus: Int
let status: Int
let statusName: String
let submitTime: String
let updatedAt: String
let auditPerson: String
let auditTime: String
let auditNote: String
let certificateType: Int
let certificateNo: String
let certificateStartDate: String
let certificateEndDate: String
let certificateImage: String
let droneModel: String
let droneSn: String
let contactPhone: String
let realnameStatusText: String
let certificationLogs: [FlyerCertificationLogItem]
enum CodingKeys: String, CodingKey {
case id
case name = "flyer_nickname"
case accountId = "account_id"
case realnameStatus = "realname_status"
case status
case statusName = "status_text"
case submitTime = "created_at"
case updatedAt = "updated_at"
case auditPerson = "reviewer"
case auditTime = "review_time"
case auditNote = "reject_reason"
case certificateType = "certificate_type"
case certificateNo = "certificate_no"
case certificateStartDate = "certificate_start_date"
case certificateEndDate = "certificate_end_date"
case certificateImage = "certificate_image"
case droneModel = "drone_model"
case droneSn = "drone_sn"
case contactPhone = "contact_phone"
case realnameStatusText = "realname_status_text"
case certificationLogs = "flyers_certification_logs"
}
init(
id: Int = 0,
name: String = "",
accountId: String = "",
realnameStatus: Int = 2,
status: Int = 0,
statusName: String = "",
submitTime: String = "",
updatedAt: String = "",
auditPerson: String = "",
auditTime: String = "",
auditNote: String = "",
certificateType: Int = 2,
certificateNo: String = "",
certificateStartDate: String = "",
certificateEndDate: String = "",
certificateImage: String = "",
droneModel: String = "",
droneSn: String = "",
contactPhone: String = "",
realnameStatusText: String = "",
certificationLogs: [FlyerCertificationLogItem] = []
) {
self.id = id
self.name = name
self.accountId = accountId
self.realnameStatus = realnameStatus
self.status = status
self.statusName = statusName
self.submitTime = submitTime
self.updatedAt = updatedAt
self.auditPerson = auditPerson
self.auditTime = auditTime
self.auditNote = auditNote
self.certificateType = certificateType
self.certificateNo = certificateNo
self.certificateStartDate = certificateStartDate
self.certificateEndDate = certificateEndDate
self.certificateImage = certificateImage
self.droneModel = droneModel
self.droneSn = droneSn
self.contactPhone = contactPhone
self.realnameStatusText = realnameStatusText
self.certificationLogs = certificationLogs
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.pilotDecodeLossyInt(forKey: .id) ?? 0
name = try container.pilotDecodeLossyString(forKey: .name)
accountId = try container.pilotDecodeLossyString(forKey: .accountId)
realnameStatus = try container.pilotDecodeLossyInt(forKey: .realnameStatus) ?? 2
status = try container.pilotDecodeLossyInt(forKey: .status) ?? 0
statusName = try container.pilotDecodeLossyString(forKey: .statusName)
submitTime = try container.pilotDecodeLossyString(forKey: .submitTime)
updatedAt = try container.pilotDecodeLossyString(forKey: .updatedAt)
auditPerson = try container.pilotDecodeLossyString(forKey: .auditPerson)
auditTime = try container.pilotDecodeLossyString(forKey: .auditTime)
auditNote = try container.pilotDecodeLossyString(forKey: .auditNote)
certificateType = try container.pilotDecodeLossyInt(forKey: .certificateType) ?? 2
certificateNo = try container.pilotDecodeLossyString(forKey: .certificateNo)
certificateStartDate = try container.pilotDecodeLossyString(forKey: .certificateStartDate)
certificateEndDate = try container.pilotDecodeLossyString(forKey: .certificateEndDate)
certificateImage = try container.pilotDecodeLossyString(forKey: .certificateImage)
droneModel = try container.pilotDecodeLossyString(forKey: .droneModel)
droneSn = try container.pilotDecodeLossyString(forKey: .droneSn)
contactPhone = try container.pilotDecodeLossyString(forKey: .contactPhone)
realnameStatusText = try container.pilotDecodeLossyString(forKey: .realnameStatusText)
certificationLogs = try container.decodeIfPresent([FlyerCertificationLogItem].self, forKey: .certificationLogs) ?? []
}
}
///
struct FlyerCertificationLogItem: Decodable, Identifiable, Equatable {
let id: Int
let flyerId: Int
let operatorName: String
let action: Int
let actionText: String
let rejectReason: String
let remark: String
let createdAt: String
enum CodingKeys: String, CodingKey {
case id
case flyerId = "flyer_id"
case operatorName = "operator"
case action
case actionText = "action_text"
case rejectReason = "reject_reason"
case remark
case createdAt = "created_at"
}
init(
id: Int = 0,
flyerId: Int = 0,
operatorName: String = "",
action: Int = 0,
actionText: String = "",
rejectReason: String = "",
remark: String = "",
createdAt: String = ""
) {
self.id = id
self.flyerId = flyerId
self.operatorName = operatorName
self.action = action
self.actionText = actionText
self.rejectReason = rejectReason
self.remark = remark
self.createdAt = createdAt
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.pilotDecodeLossyInt(forKey: .id) ?? 0
flyerId = try container.pilotDecodeLossyInt(forKey: .flyerId) ?? 0
operatorName = try container.pilotDecodeLossyString(forKey: .operatorName)
action = try container.pilotDecodeLossyInt(forKey: .action) ?? 0
actionText = try container.pilotDecodeLossyString(forKey: .actionText)
rejectReason = try container.pilotDecodeLossyString(forKey: .rejectReason)
remark = try container.pilotDecodeLossyString(forKey: .remark)
createdAt = try container.pilotDecodeLossyString(forKey: .createdAt)
}
}
///
struct FlyerSendCodeRequest: Encodable, Equatable {
let phone: String
}
///
struct FlyerApplyRequest: Encodable, Equatable {
let name: String
let realnameStatus: Int
let certificateType: Int
let certificateNo: String
let certificateStartDate: String
let certificateEndDate: String
let certificateImage: String
let droneModel: String
let droneSn: String
let contactPhone: String
let code: String
enum CodingKeys: String, CodingKey {
case name
case realnameStatus = "realname_status"
case certificateType = "certificate_type"
case certificateNo = "certificate_no"
case certificateStartDate = "certificate_start_date"
case certificateEndDate = "certificate_end_date"
case certificateImage = "certificate_image"
case droneModel = "drone_model"
case droneSn = "drone_sn"
case contactPhone = "contact_phone"
case code
}
}
///
struct FlyerEditRequest: Encodable, Equatable {
let id: Int
let name: String
let realnameStatus: Int
let certificateType: Int
let certificateNo: String
let certificateStartDate: String
let certificateEndDate: String
let certificateImage: String
let droneModel: String
let droneSn: String
let contactPhone: String
let code: String
enum CodingKeys: String, CodingKey {
case id
case name
case realnameStatus = "realname_status"
case certificateType = "certificate_type"
case certificateNo = "certificate_no"
case certificateStartDate = "certificate_start_date"
case certificateEndDate = "certificate_end_date"
case certificateImage = "certificate_image"
case droneModel = "drone_model"
case droneSn = "drone_sn"
case contactPhone = "contact_phone"
case code
}
}
///
enum PilotCertificationValidationError: LocalizedError, Equatable {
case message(String)
var errorDescription: String? {
switch self {
case .message(let message):
message
}
}
}
private extension KeyedDecodingContainer {
func pilotDecodeLossyString(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 ? "true" : "false"
}
return ""
}
func pilotDecodeLossyInt(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)
}
return nil
}
}