import Foundation /// 固件信息 public struct FirmwareInfo: Codable, Equatable { public let version: String public let fileUrl: String public let description: String? public let fileSize: String? public let fileMd5: String? public let identifier: String? public let status: String? } struct FirmwareResponse: Codable { let success: Bool let data: FirmwareInfo? } /// 固件服务内部实现 final class FirmwareService { private let config: DuooomiBleConfig private let latestPath = "api/auth/loomart/firmware/latest-published" init(config: DuooomiBleConfig) { self.config = config } func fetchLatest( identifier: String? = nil, status: String? = nil, completion: @escaping (Result) -> Void ) { let id = (identifier ?? config.firmwareIdentifier).trimmingCharacters(in: .whitespacesAndNewlines) let st = status ?? config.firmwareStatus guard !id.isEmpty else { completion(.failure(DuooomiBleError.transferFailed("Invalid firmware identifier"))) return } let urlString = "\(config.apiHost)/\(latestPath)" guard var comps = URLComponents(string: urlString) else { completion(.failure(DuooomiBleError.transferFailed("Invalid firmware URL"))) return } comps.queryItems = [ URLQueryItem(name: "identifier", value: id), URLQueryItem(name: "status", value: st) ] guard let url = comps.url else { completion(.failure(DuooomiBleError.transferFailed("Invalid firmware URL"))) return } var req = URLRequest(url: url) req.httpMethod = "GET" req.setValue(config.apiKey, forHTTPHeaderField: "x-api-key") req.setValue("application/json", forHTTPHeaderField: "accept") URLSession.shared.dataTask(with: req) { data, resp, error in if let error = error { completion(.failure(error)) return } guard let http = resp as? HTTPURLResponse else { completion(.failure(DuooomiBleError.transferFailed("Invalid response"))) return } guard (200...299).contains(http.statusCode) else { completion(.failure(DuooomiBleError.transferFailed("HTTP \(http.statusCode)"))) return } guard let data = data else { completion(.failure(DuooomiBleError.transferFailed("Empty response"))) return } do { let decoded = try JSONDecoder().decode(FirmwareResponse.self, from: data) completion(.success(decoded.data)) } catch { completion(.failure(error)) } }.resume() } }