import Foundation struct FirmwareInfo: Codable, Equatable { let version: String let fileUrl: String let notes: String? } enum FirmwareServiceError: LocalizedError { case invalidBrand case invalidResponse case http(Int) case network(String) var errorDescription: String? { switch self { case .invalidBrand: return "Invalid brand" case .invalidResponse: return "Invalid firmware info response" case .http(let code): return "HTTP error: \(code)" case .network(let msg): return msg } } } enum FirmwareUpdateService { // 可根据后端实际接口调整 private static let base = NetworkConfig.apiHost private static let latestPath = "/api/auth/firmware/latest" /// 获取指定品牌的最新固件信息。 /// - Parameters: /// - brand: 设备品牌,如 Bowong。 /// - status: 固件状态筛选,默认 `PUBLISHED`。 /// - Returns: 最新固件信息(版本、下载地址、备注)。 /// - Throws: 参数不合法、网络/HTTP 错误或响应解析失败。 static func fetchLatest(brand: String, status: String = "PUBLISHED") async throws -> FirmwareInfo { guard !brand.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { throw FirmwareServiceError.invalidBrand } var comps = URLComponents(url: base.appendingPathComponent(latestPath), resolvingAgainstBaseURL: false)! comps.queryItems = [ URLQueryItem(name: "brand", value: brand), URLQueryItem(name: "status", value: status) ] guard let url = comps.url else { throw FirmwareServiceError.invalidResponse } var req = URLRequest(url: url) req.httpMethod = "GET" req.setValue(NetworkConfig.apiKey, forHTTPHeaderField: "x-api-key") req.setValue("application/json", forHTTPHeaderField: "accept") do { let (data, resp) = try await URLSession.shared.data(for: req) guard let http = resp as? HTTPURLResponse else { throw FirmwareServiceError.invalidResponse } guard (200...299).contains(http.statusCode) else { throw FirmwareServiceError.http(http.statusCode) } // 期望返回 { version: String, fileUrl: String, notes?: String } let info = try JSONDecoder().decode(FirmwareInfo.self, from: data) return info } catch let e as FirmwareServiceError { throw e } catch { throw FirmwareServiceError.network(error.localizedDescription) } } }