diff --git a/wake/ContentView.swift b/wake/ContentView.swift index 87a66fe..1a893f3 100644 --- a/wake/ContentView.swift +++ b/wake/ContentView.swift @@ -285,6 +285,23 @@ struct BlindBoxView: View { } } } + + // 盲盒列表 + NetworkService.shared.postWithToken( + path: "/blind_box/generate/mock", + parameters: ["box_type": "Image"] + ) { (result: Result, NetworkError>) in + DispatchQueue.main.async { + switch result { + case .success(let response): + self.blindList = response.data ?? [] + print("✅✅✅✅✅✅✅✅✅ 成功获取 \(self.blindList.count) 个盲盒") + case .failure(let error): + self.blindList = [] + print("❌❌ ❌ ❌ ❌ ❌ ❌ 获取盲盒列表失败:", error.localizedDescription) + } + } + } } } diff --git a/wake/Utils/NetworkService.swift b/wake/Utils/NetworkService.swift index 118c605..bec1089 100644 --- a/wake/Utils/NetworkService.swift +++ b/wake/Utils/NetworkService.swift @@ -104,6 +104,7 @@ public enum NetworkError: Error { case other(Error) case networkError(Error) case unknownError(Error) + case invalidParameters public var localizedDescription: String { switch self { @@ -123,6 +124,8 @@ public enum NetworkError: Error { return "网络错误: \(error.localizedDescription)" case .unknownError(let error): return "未知错误: \(error.localizedDescription)" + case .invalidParameters: + return "无效的参数" } } } @@ -145,7 +148,7 @@ class NetworkService { private func request( _ method: String, path: String, - parameters: [String: Any]? = nil, + parameters: Any? = nil, headers: [String: String]? = nil, completion: @escaping (Result) -> Void ) { @@ -184,7 +187,13 @@ class NetworkService { // 设置请求体(如果是POST/PUT请求) if let parameters = parameters, (method == "POST" || method == "PUT") { do { - request.httpBody = try JSONSerialization.data(withJSONObject: parameters) + if JSONSerialization.isValidJSONObject(parameters) { + request.httpBody = try JSONSerialization.data(withJSONObject: parameters) + } else { + print("❌ [Network][#\(requestId)][\(method) \(path)] 参数不是有效的JSON对象") + completion(.failure(.invalidParameters)) + return + } } catch { print("❌ [Network][#\(requestId)][\(method) \(path)] 参数序列化失败: \(error.localizedDescription)") completion(.failure(.other(error))) @@ -442,17 +451,29 @@ class NetworkService { /// POST 请求 func post( path: String, - parameters: [String: Any]? = nil, + parameters: Any? = nil, headers: [String: String]? = nil, completion: @escaping (Result) -> Void ) { - request("POST", path: path, parameters: parameters, headers: headers, completion: completion) + var params: Any? + if let parameters = parameters { + if let dict = parameters as? [String: Any] { + params = dict + } else if let array = parameters as? [Any] { + params = array + } else { + print("❌ [Network] POST 请求参数类型不支持") + completion(.failure(.invalidParameters)) + return + } + } + request("POST", path: path, parameters: params, headers: headers, completion: completion) } /// POST 请求(带Token) func postWithToken( path: String, - parameters: [String: Any]? = nil, + parameters: Any? = nil, headers: [String: String]? = nil, completion: @escaping (Result) -> Void ) { diff --git a/wake/View/Components/Upload/MediaUpload.swift b/wake/View/Components/Upload/MediaUpload.swift index 4927b6f..8acd520 100644 --- a/wake/View/Components/Upload/MediaUpload.swift +++ b/wake/View/Components/Upload/MediaUpload.swift @@ -59,8 +59,10 @@ public class MediaUploadManager: ObservableObject { @Published public private(set) var selectedMedia: [MediaType] = [] /// 上传状态 @Published public private(set) var uploadStatus: [String: MediaUploadStatus] = [:] + /// 上传结果 + @Published public private(set) var uploadResults: [String: String] = [:] // Store fileId as String - private let uploader = ImageUploadService() + private let uploader = ImageUploadService() // Use ImageUploadService private let logger = Logger(subsystem: "com.yourapp.media", category: "MediaUploadManager") public init() {} @@ -93,6 +95,7 @@ public class MediaUploadManager: ObservableObject { Task { @MainActor in self.selectedMedia.removeAll { $0.id == id } self.uploadStatus.removeValue(forKey: id) + self.uploadResults.removeValue(forKey: id) } } @@ -100,6 +103,7 @@ public class MediaUploadManager: ObservableObject { public func clearAllMedia() { selectedMedia.removeAll() uploadStatus.removeAll() + uploadResults.removeAll() } /// 开始上传所有选中的媒体 @@ -112,6 +116,9 @@ public class MediaUploadManager: ObservableObject { return !status.isCompleted && !status.isUploading } + // 清空之前的上传结果 + self.uploadResults.removeAll() + for media in mediaToUpload { self.uploadMedia(media) } @@ -146,43 +153,73 @@ public class MediaUploadManager: ObservableObject { updateStatus(for: media.id, status: .uploading(progress: 0)) // 转换媒体类型 - let uploadMediaType: ImageUploadService.MediaType + let uploadMedia: ImageUploadService.MediaType switch media { - case .image(let image): - uploadMediaType = .image(image) + case .image(let uiImage): + uploadMedia = .image(uiImage) case .video(let url, let thumbnail): - uploadMediaType = .video(url, thumbnail) + uploadMedia = .video(url as URL, thumbnail) } - // 开始上传 - uploader.uploadMedia( - uploadMediaType, - progress: { [weak self] progress in - guard let self = self else { return } - Task { @MainActor in - self.updateStatus(for: media.id, status: .uploading(progress: progress.progress)) - } - }, - completion: { [weak self] result in - guard let self = self else { return } - Task { @MainActor in - switch result { - case .success(let uploadResult): - self.logger.info("✅ 上传成功 (\(media.id)): \(uploadResult.fileId)") - self.updateStatus(for: media.id, status: .completed(fileId: uploadResult.fileId)) - case .failure(let error): - self.logger.error("❌ 上传失败 (\(media.id)): \(error.localizedDescription)") - self.updateStatus(for: media.id, status: .failed(error)) - } - } - } - ) + // 上传媒体文件 + uploader.uploadMedia(uploadMedia, + progress: { progress in + // 更新上传进度 + Task { @MainActor in + self.updateStatus(for: media.id, status: .uploading(progress: progress.progress)) + } + }, + completion: { [weak self] result in + guard let self = self else { return } + + Task { @MainActor in + switch result { + case .success(let uploadResult): + let fileId = uploadResult.fileId + self.logger.info("✅ 上传成功 (\(media.id)): \(fileId)") + self.uploadResults[media.id] = fileId + self.updateStatus(for: media.id, status: .completed(fileId: fileId)) + + // 打印上传结果 + if self.isAllUploaded { + self.printUploadResults() + } + + case .failure(let error): + self.logger.error("❌ 上传失败 (\(media.id)): \(error.localizedDescription)") + self.updateStatus(for: media.id, status: .failed(error)) + } + } + }) } @MainActor private func updateStatus(for mediaId: String, status: MediaUploadStatus) { uploadStatus[mediaId] = status } + + // MARK: - Upload Results + + /// 打印上传结果 + private func printUploadResults() { + let results = self.selectedMedia.compactMap { media -> [String: String]? in + guard let result = self.uploadResults[media.id] else { return nil } + return [ + "file_id": result, + "preview_file_id": result + ] + } + + do { + let jsonData = try JSONSerialization.data(withJSONObject: results, options: [.prettyPrinted, .sortedKeys]) + if let jsonString = String(data: jsonData, encoding: .utf8) { + print("📦 上传完成,文件ID列表:") + print(jsonString) + } + } catch { + print("❌ 无法序列化上传结果: \(error)") + } + } } // MARK: - Preview Helper diff --git a/wake/View/Login/Login.swift b/wake/View/Login/Login.swift index 276b154..d14d05f 100644 --- a/wake/View/Login/Login.swift +++ b/wake/View/Login/Login.swift @@ -288,12 +288,15 @@ struct LoginView: View { case .networkError(let error): print(" → 网络错误: \(error.localizedDescription)") errorMessage = "网络连接失败,请检查网络" + case .other(let error): + print(" → 其他错误: \(error.localizedDescription)") + errorMessage = "发生未知错误" case .unknownError(let error): print(" → 未知错误: \(error.localizedDescription)") errorMessage = "发生未知错误" - case .other(let error): - print(" → 其他错误: \(error.localizedDescription)") - errorMessage = "发生错误: \(error.localizedDescription)" + case .invalidParameters: + print(" → 无效的参数") + errorMessage = "请求参数错误,请重试" } self.errorMessage = errorMessage diff --git a/wake/View/Upload/MediaUploadView.swift b/wake/View/Upload/MediaUploadView.swift index 633e2e5..75ff363 100644 --- a/wake/View/Upload/MediaUploadView.swift +++ b/wake/View/Upload/MediaUploadView.swift @@ -19,6 +19,10 @@ struct MediaUploadView: View { /// 当前选中的媒体索引集合 @State private var selectedIndices: Set = [] @State private var mediaPickerSelection: [MediaType] = [] // 添加这个状态变量 + /// 上传完成状态 + @State private var uploadComplete = false + /// 上传完成的文件ID列表 + @State private var uploadedFileIds: [[String: String]] = [] // MARK: - 视图主体 @@ -41,6 +45,32 @@ struct MediaUploadView: View { Spacer() + // // 上传结果展示 + // if uploadComplete && !uploadedFileIds.isEmpty { + // VStack(alignment: .leading) { + // Text("上传完成!") + // .font(.headline) + + // ScrollView { + // ForEach(Array(uploadedFileIds.enumerated()), id: \.offset) { index, fileInfo in + // VStack(alignment: .leading) { + // Text("文件 \(index + 1):") + // .font(.subheadline) + // Text("ID: \(fileInfo["file_id"] ?? "")") + // .font(.caption) + // .foregroundColor(.gray) + // } + // .padding() + // .frame(maxWidth: .infinity, alignment: .leading) + // .background(Color.gray.opacity(0.1)) + // .cornerRadius(8) + // } + // } + // .frame(height: 200) + // } + // .padding() + // } + // 继续按钮 continueButton .padding(.bottom, 24) @@ -52,6 +82,9 @@ struct MediaUploadView: View { // 媒体选择器 mediaPickerView } + .onChange(of: uploadManager.uploadResults) { newResults in + handleUploadCompletion(results: newResults) + } } // MARK: - 子视图 @@ -108,10 +141,7 @@ struct MediaUploadView: View { /// 继续按钮 private var continueButton: some View { - Button(action: { - // 处理继续操作 - Router.shared.navigate(to: .blindBox(mediaType: .video)) - }) { + Button(action: handleContinue) { Text("Continue") .font(.headline) .foregroundColor(uploadManager.selectedMedia.isEmpty ? Color.themeTextMessage : Color.themeTextMessageMain) @@ -264,6 +294,56 @@ struct MediaUploadView: View { return false } } + + /// 处理上传完成 + private func handleUploadCompletion(results: [String: String]) { + uploadedFileIds = results.map { ["file_id": $0.value, "preview_file_id": $0.value] } + uploadComplete = !uploadedFileIds.isEmpty + + // 打印结果到控制台 + if let jsonData = try? JSONSerialization.data(withJSONObject: uploadedFileIds, options: .prettyPrinted), + let jsonString = String(data: jsonData, encoding: .utf8) { + print("📦 上传完成,文件ID列表:") + print(jsonString) + } + } + + /// 处理继续按钮点击 + private func handleContinue() { + // 获取所有已上传文件的ID + let fileIds = uploadManager.uploadResults.map { $0.value } + + guard !fileIds.isEmpty else { + print("⚠️ 没有可用的文件ID") + return + } + + // 准备请求参数 + let files = fileIds.map { fileId -> [String: String] in + return [ + "file_id": fileId, + "preview_file_id": fileId + ] + } + + // 发送POST请求到/material接口 + NetworkService.shared.postWithToken( + path: "/material", + parameters: files + ) { (result: Result) in + switch result { + case .success: + print("✅ 素材提交成功") + // 跳转到盲盒页面 + DispatchQueue.main.async { + Router.shared.navigate(to: .blindBox(mediaType: .video)) + } + case .failure(let error): + print("❌ 素材提交失败: \(error.localizedDescription)") + // 这里可以添加错误处理逻辑,比如显示错误提示 + } + } + } } // MARK: - 主上传区域 @@ -595,6 +675,13 @@ private func getVideoDuration(url: URL) -> String { let seconds = Int(durationInSeconds) % 60 return String(format: "%d:%02d", minutes, seconds) } + +// MARK: - Response Types + +private struct EmptyResponse: Decodable { + // Empty response type for endpoints that don't return data +} + // MARK: - 扩展 /// 扩展 MediaType 以支持 Identifiable 协议