import Foundation import QCloudCOSXML import ComposableArchitecture // MARK: - COS 核心数据模型 /// 腾讯云 COS Token 数据模型 public struct TcTokenData: Codable, Equatable, Sendable { /// 存储桶名称 public let bucket: String /// 临时会话令牌 public let sessionToken: String /// 地域 public let region: String /// 自定义域名 public let customDomain: String /// 是否启用加速 public let accelerate: Bool /// 应用 ID public let appId: String /// 临时密钥 public let secretKey: String /// 过期时间戳 public let expireTime: Int64 /// 开始时间戳 public let startTime: Int64 /// 临时密钥 ID public let secretId: String public init( bucket: String, sessionToken: String, region: String, customDomain: String, accelerate: Bool, appId: String, secretKey: String, expireTime: Int64, startTime: Int64, secretId: String ) { self.bucket = bucket self.sessionToken = sessionToken self.region = region self.customDomain = customDomain self.accelerate = accelerate self.appId = appId self.secretKey = secretKey self.expireTime = expireTime self.startTime = startTime self.secretId = secretId } } /// Token 请求模型 struct TcTokenRequest: APIRequestProtocol { typealias Response = TcTokenResponse let endpoint: String = APIEndpoint.tcToken.path let method: HTTPMethod = .GET let queryParameters: [String: String]? = nil var bodyParameters: [String: Any]? { nil } let timeout: TimeInterval = 30.0 let includeBaseParameters: Bool = true let shouldShowLoading: Bool = false // 不显示 loading,避免影响用户体验 let shouldShowError: Bool = false // 不显示错误,静默处理 } /// Token 响应模型 public struct TcTokenResponse: Codable, Equatable, Sendable { public let code: Int public let message: String public let data: TcTokenData? public let timestamp: Int64 public init(code: Int, message: String, data: TcTokenData?, timestamp: Int64) { self.code = code self.message = message self.data = data self.timestamp = timestamp } } // MARK: - 上传相关模型 /// 上传状态枚举 public enum UploadStatus: Equatable, Sendable { case idle case uploading(progress: Double) case success(url: String) case failure(error: String) } /// 上传任务模型 public struct UploadTask: Equatable, Identifiable, Sendable { public let id: UUID public let imageData: Data public let fileName: String public let status: UploadStatus public let createdAt: Date public init( id: UUID = UUID(), imageData: Data, fileName: String, status: UploadStatus = .idle, createdAt: Date = Date() ) { self.id = id self.imageData = imageData self.fileName = fileName self.status = status self.createdAt = createdAt } } /// 上传进度模型 public struct UploadProgress: Equatable, Sendable { public let bytesSent: Int64 public let totalBytesSent: Int64 public let totalBytesExpectedToSend: Int64 public var progress: Double { guard totalBytesExpectedToSend > 0 else { return 0.0 } return Double(totalBytesSent) / Double(totalBytesExpectedToSend) } public init( bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64 ) { self.bytesSent = bytesSent self.totalBytesSent = totalBytesSent self.totalBytesExpectedToSend = totalBytesExpectedToSend } } // MARK: - 配置相关模型 /// COS 配置模型 public struct COSConfiguration: Equatable, Sendable { public let region: String public let bucket: String public let accelerate: Bool public let customDomain: String public let useHTTPS: Bool public init( region: String, bucket: String, accelerate: Bool = false, customDomain: String = "", useHTTPS: Bool = true ) { self.region = region self.bucket = bucket self.accelerate = accelerate self.customDomain = customDomain self.useHTTPS = useHTTPS } } /// COS 服务状态 public enum COSServiceStatus: Equatable, Sendable { case notInitialized case initializing case initialized(configuration: COSConfiguration) case failed(error: String) } // MARK: - 错误模型 /// COS 错误类型 public enum COSError: Equatable, Sendable, LocalizedError { case tokenExpired case tokenInvalid case serviceNotInitialized case uploadFailed(String) case configurationFailed(String) case networkError(String) case unknown(String) public var errorDescription: String? { switch self { case .tokenExpired: return "Token已过期" case .tokenInvalid: return "Token无效" case .serviceNotInitialized: return "服务未初始化" case .uploadFailed(let message): return "上传失败: \(message)" case .configurationFailed(let message): return "配置失败: \(message)" case .networkError(let message): return "网络错误: \(message)" case .unknown(let message): return "未知错误: \(message)" } } } // MARK: - 扩展 extension TcTokenData { /// 检查 Token 是否已过期 public var isExpired: Bool { let currentTime = Int64(Date().timeIntervalSince1970) return currentTime >= expireTime } /// 获取过期时间 public var expirationDate: Date { return Date(timeIntervalSince1970: TimeInterval(expireTime)) } /// 获取开始时间 public var startDate: Date { return Date(timeIntervalSince1970: TimeInterval(startTime)) } /// 获取剩余有效时间(秒) public var remainingTime: Int64 { let currentTime = Int64(Date().timeIntervalSince1970) return max(0, expireTime - currentTime) } /// 检查Token是否有效 public var isValid: Bool { return !isExpired } /// 获取剩余有效时间(TimeInterval) public var remainingValidTime: TimeInterval { return max(0, expirationDate.timeIntervalSinceNow) } /// 构建云存储URL public func buildCloudURL(for key: String) -> String { let domain = customDomain.isEmpty ? "\(bucket).cos.\(region).myqcloud.com" : customDomain let prefix = domain.hasPrefix("http") ? "" : "https://" return "\(prefix)\(domain)/\(key)" } } extension UploadTask { /// 更新上传状态 public func updatingStatus(_ newStatus: UploadStatus) -> UploadTask { return UploadTask( id: id, imageData: imageData, fileName: fileName, status: newStatus, createdAt: createdAt ) } }