将运营区域与飞手认证从首页占位页迁移为完整模块,扩展 Live 播放与推流就绪流程,并新增飞手证书 OSS 上传。 Co-authored-by: Cursor <cursoragent@cursor.com>
311 lines
11 KiB
Swift
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
|
|
}
|
|
}
|