// Created by AI on 2025-10-11. import Foundation @objc class EPSDKManager: NSObject, QCloudSignatureProvider, QCloudCredentailFenceQueueDelegate { // MARK: - Singleton @objc static let shared = EPSDKManager() // MARK: - Properties private var qcloudConfig: EPQCloudConfig? private var isQCloudInitializing = false private var qcloudInitCallbacks: [(Bool, String?) -> Void] = [] private var credentialFenceQueue: QCloudCredentailFenceQueue? private let lock = NSLock() private let uploader = EPImageUploader() // MARK: - Initialization private override init() { super.init() } // MARK: - Public API (对外统一入口) @objc func uploadImages( _ images: [UIImage], progress: @escaping (Int, Int) -> Void, success: @escaping ([[String: Any]]) -> Void, failure: @escaping (String) -> Void ) { guard !images.isEmpty else { success([]) return } ensureQCloudReady { [weak self] isReady, errorMsg in guard let self = self, isReady else { DispatchQueue.main.async { failure(errorMsg ?? YMLocalizedString("error.qcloud_init_failed")) } return } self.uploader.performBatchUpload( images, bucket: self.qcloudConfig?.bucket ?? "", customDomain: self.qcloudConfig?.customDomain ?? "", progress: progress, success: success, failure: failure ) } } @objc func isQCloudReady() -> Bool { lock.lock() defer { lock.unlock() } guard let config = qcloudConfig else { return false } return !config.isExpired } // MARK: - Internal Methods private func ensureQCloudReady(completion: @escaping (Bool, String?) -> Void) { if isQCloudReady() { completion(true, nil) return } initializeQCloud(completion: completion) } private func initializeQCloud(completion: @escaping (Bool, String?) -> Void) { lock.lock() if isQCloudInitializing { qcloudInitCallbacks.append(completion) lock.unlock() return } if let config = qcloudConfig, !config.isExpired { lock.unlock() completion(true, nil) return } isQCloudInitializing = true qcloudInitCallbacks.append(completion) lock.unlock() Api.getQCloudInfo { [weak self] (data, code, msg) in guard let self = self else { return } self.lock.lock() if code == 200, let dict = data?.data as? [String: Any], let config = EPQCloudConfig(dictionary: dict) { self.qcloudConfig = config self.configureQCloudSDK(with: config) self.isQCloudInitializing = false let callbacks = self.qcloudInitCallbacks self.qcloudInitCallbacks.removeAll() self.lock.unlock() DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { callbacks.forEach { $0(true, nil) } } } else { self.isQCloudInitializing = false let callbacks = self.qcloudInitCallbacks self.qcloudInitCallbacks.removeAll() self.lock.unlock() let errorMsg = msg ?? YMLocalizedString("error.qcloud_config_failed") DispatchQueue.main.async { callbacks.forEach { $0(false, errorMsg) } } } } } private func configureQCloudSDK(with config: EPQCloudConfig) { let configuration = QCloudServiceConfiguration() configuration.appID = config.appId let endpoint = QCloudCOSXMLEndPoint() endpoint.regionName = config.region endpoint.useHTTPS = true if config.accelerate == 1 { endpoint.suffix = "cos.accelerate.myqcloud.com" } configuration.endpoint = endpoint configuration.signatureProvider = self QCloudCOSXMLService.registerDefaultCOSXML(with: configuration) QCloudCOSTransferMangerService.registerDefaultCOSTransferManger(with: configuration) credentialFenceQueue = QCloudCredentailFenceQueue() credentialFenceQueue?.delegate = self } // MARK: - QCloudSignatureProvider Protocol func signature( with fields: QCloudSignatureFields, request: QCloudBizHTTPRequest, urlRequest: NSMutableURLRequest, compelete: @escaping QCloudHTTPAuthentationContinueBlock ) { guard let config = qcloudConfig else { let error = NSError(domain: "com.yumi.qcloud", code: -1, userInfo: [NSLocalizedDescriptionKey: YMLocalizedString("error.qcloud_config_not_initialized")]) compelete(nil, error) return } let credential = QCloudCredential() credential.secretID = config.secretId credential.secretKey = config.secretKey credential.token = config.sessionToken credential.startDate = Date(timeIntervalSince1970: TimeInterval(config.startTime)) credential.expirationDate = Date(timeIntervalSince1970: TimeInterval(config.expireTime)) let creator = QCloudAuthentationV5Creator(credential: credential) let signature = creator?.signature(forData: urlRequest) compelete(signature, nil) } // MARK: - QCloudCredentailFenceQueueDelegate Protocol func fenceQueue( _ queue: QCloudCredentailFenceQueue, requestCreatorWithContinue continueBlock: @escaping QCloudCredentailFenceQueueContinue ) { guard let config = qcloudConfig else { let error = NSError(domain: "com.yumi.qcloud", code: -1, userInfo: [NSLocalizedDescriptionKey: YMLocalizedString("error.qcloud_config_not_initialized")]) continueBlock(nil, error) return } let credential = QCloudCredential() credential.secretID = config.secretId credential.secretKey = config.secretKey credential.token = config.sessionToken credential.startDate = Date(timeIntervalSince1970: TimeInterval(config.startTime)) credential.expirationDate = Date(timeIntervalSince1970: TimeInterval(config.expireTime)) let creator = QCloudAuthentationV5Creator(credential: credential) continueBlock(creator, nil) } }