feat: 盲盒状态更新

This commit is contained in:
Junhui Chen 2025-09-06 17:45:52 +08:00
parent 2b300d29fe
commit 06c2de3764
5 changed files with 85 additions and 38 deletions

View File

@ -18,19 +18,34 @@ enum BlindBoxAnimationPhase {
// MARK: - Blind Box Data Models
// File info used by result_file / cover_file (named uniquely to avoid collision)
struct BlindFileInfo: Codable {
let id: String
let fileName: String?
let url: String?
let metadata: [String: String]?
enum CodingKeys: String, CodingKey {
case id
case fileName = "file_name"
case url
case metadata
}
}
struct BlindList: Codable, Identifiable {
let id: Int64
let id: String
let boxCode: String
let userId: Int64
let userId: String
let name: String
let boxType: String
let features: String?
let resultFileId: Int64?
let resultFile: BlindFileInfo?
let status: String
let workflowInstanceId: String?
let videoGenerateTime: String?
let createTime: String
let coverFileId: Int64?
let coverFile: BlindFileInfo?
let description: String
enum CodingKeys: String, CodingKey {
@ -40,14 +55,40 @@ struct BlindList: Codable, Identifiable {
case name
case boxType = "box_type"
case features
case resultFileId = "result_file_id"
case resultFile = "result_file"
case status
case workflowInstanceId = "workflow_instance_id"
case videoGenerateTime = "video_generate_time"
case createTime = "create_time"
case coverFileId = "cover_file_id"
case coverFile = "cover_file"
case description
}
// Tolerant decoding for id/userId which may be number or string
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
func decodeString(forKey key: CodingKeys) throws -> String {
if let s = try? container.decode(String.self, forKey: key) { return s }
if let i = try? container.decode(Int64.self, forKey: key) { return String(i) }
if let i = try? container.decode(Int.self, forKey: key) { return String(i) }
return ""
}
self.id = try decodeString(forKey: .id)
self.boxCode = (try? container.decode(String.self, forKey: .boxCode)) ?? ""
self.userId = try decodeString(forKey: .userId)
self.name = (try? container.decode(String.self, forKey: .name)) ?? ""
self.boxType = (try? container.decode(String.self, forKey: .boxType)) ?? ""
self.features = try? container.decodeIfPresent(String.self, forKey: .features)
self.resultFile = try? container.decodeIfPresent(BlindFileInfo.self, forKey: .resultFile)
self.status = (try? container.decode(String.self, forKey: .status)) ?? ""
self.workflowInstanceId = try? container.decodeIfPresent(String.self, forKey: .workflowInstanceId)
self.videoGenerateTime = try? container.decodeIfPresent(String.self, forKey: .videoGenerateTime)
self.createTime = (try? container.decode(String.self, forKey: .createTime)) ?? ""
self.coverFile = try? container.decodeIfPresent(BlindFileInfo.self, forKey: .coverFile)
self.description = (try? container.decode(String.self, forKey: .description)) ?? ""
}
}
struct BlindCount: Codable {
@ -59,9 +100,9 @@ struct BlindCount: Codable {
}
struct BlindBoxData: Codable {
let id: Int64
let id: String
let boxCode: String
let userId: Int64
let userId: String
let name: String
let boxType: String
let features: String?
@ -87,7 +128,7 @@ struct BlindBoxData: Codable {
case description
}
init(id: Int64, boxCode: String, userId: Int64, name: String, boxType: String, features: String?, url: String?, status: String, workflowInstanceId: String?, videoGenerateTime: String?, createTime: String, description: String?) {
init(id: String, boxCode: String, userId: String, name: String, boxType: String, features: String?, url: String?, status: String, workflowInstanceId: String?, videoGenerateTime: String?, createTime: String, description: String?) {
self.id = id
self.boxCode = boxCode
self.userId = userId
@ -110,7 +151,7 @@ struct BlindBoxData: Codable {
name: listItem.name,
boxType: listItem.boxType,
features: listItem.features,
url: nil,
url: listItem.resultFile?.url,
status: listItem.status,
workflowInstanceId: listItem.workflowInstanceId,
videoGenerateTime: listItem.videoGenerateTime,
@ -118,4 +159,28 @@ struct BlindBoxData: Codable {
description: listItem.description
)
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
func decodeString(forKey key: CodingKeys) -> String? {
if let s = try? container.decode(String.self, forKey: key) { return s }
if let i = try? container.decode(Int64.self, forKey: key) { return String(i) }
if let i = try? container.decode(Int.self, forKey: key) { return String(i) }
return nil
}
self.id = decodeString(forKey: .id) ?? ""
self.boxCode = (try? container.decode(String.self, forKey: .boxCode)) ?? ""
self.userId = decodeString(forKey: .userId) ?? ""
self.name = (try? container.decode(String.self, forKey: .name)) ?? ""
self.boxType = (try? container.decode(String.self, forKey: .boxType)) ?? ""
self.features = try? container.decodeIfPresent(String.self, forKey: .features)
self.url = try? container.decodeIfPresent(String.self, forKey: .url)
self.status = (try? container.decode(String.self, forKey: .status)) ?? ""
self.workflowInstanceId = try? container.decodeIfPresent(String.self, forKey: .workflowInstanceId)
self.videoGenerateTime = try? container.decodeIfPresent(String.self, forKey: .videoGenerateTime)
self.createTime = (try? container.decode(String.self, forKey: .createTime)) ?? ""
self.description = try? container.decodeIfPresent(String.self, forKey: .description)
}
}

View File

@ -69,7 +69,7 @@ struct BlindBoxView: View {
DispatchQueue.main.async {
switch result {
case .success(let response):
let list = response.data ?? []
let list = response.data
self.blindList = list
let lowered = list.map { ($0.boxType).lowercased() }
let statuses = list.map { $0.status }
@ -178,7 +178,7 @@ struct BlindBoxView: View {
DispatchQueue.main.async {
switch result {
case .success(let response):
self.blindList = response.data ?? []
self.blindList = response.data
// none
if self.blindList.isEmpty {
self.animationPhase = .none

View File

@ -53,7 +53,7 @@ struct GateView: View {
isLoading = false
switch result {
case .success(let response):
let list = response.data ?? []
let list = response.data
print("[Gate] Success, list count: \(list.count)")
let lowered = list.map { ($0.boxType).lowercased() }
let hasFirst = lowered.contains(where: { $0 == "first" })

View File

@ -348,31 +348,13 @@ extension UserInfo {
DispatchQueue.main.async {
switch result {
case .success(let response):
guard response.code == 0, let urlStr = response.data?.resultFile?.url, let url = URL(string: urlStr) else {
guard response.code == 0 else {
self.errorMessage = "Create first blind box failed: invalid response"
self.showError = true
return
}
//
URLSession.shared.dataTask(with: url) { data, _, error in
if let error = error {
DispatchQueue.main.async {
self.errorMessage = "Load result image failed: \(error.localizedDescription)"
self.showError = true
}
return
}
guard let data = data, let image = UIImage(data: data) else {
DispatchQueue.main.async {
self.errorMessage = "Invalid image data"
self.showError = true
}
return
}
DispatchQueue.main.async {
Router.shared.navigate(to: .blindOutcome(media: .image(image), time: nil, description: nil))
}
}.resume()
//
Router.shared.navigate(to: .blindBox(mediaType: .all))
case .failure(let error):
self.errorMessage = "Create first blind box failed: \(error.localizedDescription)"
self.showError = true

View File

@ -461,14 +461,14 @@ struct MediaUploadView: View {
DispatchQueue.main.async {
switch result {
case .success(let response):
guard response.code == 0, let id = response.data?.id else {
guard response.code == 0 else {
print("❌ 创建第二个盲盒失败:响应无效")
isGeneratingSecond = false
return
}
generatedSecondBoxId = id
//
startPollingSecondBox(id: id)
//
isGeneratingSecond = false
Router.shared.navigate(to: .blindBox(mediaType: .all))
case .failure(let error):
print("❌ 创建第二个盲盒失败: \(error.localizedDescription)")
isGeneratingSecond = false