feat: 接通图片预览 AI 修图流程

This commit is contained in:
2026-08-11 09:52:15 +08:00
parent 5704531f3a
commit 43c75c8e36
14 changed files with 814 additions and 169 deletions
@@ -129,6 +129,7 @@ struct TravelAlbumMaterial: Decodable, Sendable, Equatable, Hashable, Identifiab
let isPurchased: Bool
let aiRetouchStatus: Int
let aiRetouchStatusName: String
let aiRetouchBatchId: Int
let aiRefinedURL: String
let aiAtmosphereURL: String
let createdAt: String
@@ -148,6 +149,7 @@ struct TravelAlbumMaterial: Decodable, Sendable, Equatable, Hashable, Identifiab
case isPurchased = "is_purchased"
case aiRetouchStatus = "ai_retouch_status"
case aiRetouchStatusName = "ai_retouch_status_name"
case aiRetouchBatchId = "ai_retouch_batch_id"
case aiRefinedURL = "ai_refined_url"
case aiAtmosphereURL = "ai_atmosphere_url"
case createdAt = "created_at"
@@ -172,6 +174,7 @@ struct TravelAlbumMaterial: Decodable, Sendable, Equatable, Hashable, Identifiab
aiRetouchStatusName = (
try? container.decodeIfPresent(String.self, forKey: .aiRetouchStatusName)
) ?? ""
aiRetouchBatchId = (try? container.decodeIfPresent(Int.self, forKey: .aiRetouchBatchId)) ?? 0
aiRefinedURL = (try? container.decodeIfPresent(String.self, forKey: .aiRefinedURL)) ?? ""
aiAtmosphereURL = (
try? container.decodeIfPresent(String.self, forKey: .aiAtmosphereURL)
@@ -194,6 +197,7 @@ struct TravelAlbumMaterial: Decodable, Sendable, Equatable, Hashable, Identifiab
isPurchased: Bool = false,
aiRetouchStatus: Int = 0,
aiRetouchStatusName: String = "",
aiRetouchBatchId: Int = 0,
aiRefinedURL: String = "",
aiAtmosphereURL: String = "",
createdAt: String = "",
@@ -212,6 +216,7 @@ struct TravelAlbumMaterial: Decodable, Sendable, Equatable, Hashable, Identifiab
self.isPurchased = isPurchased
self.aiRetouchStatus = aiRetouchStatus
self.aiRetouchStatusName = aiRetouchStatusName
self.aiRetouchBatchId = aiRetouchBatchId
self.aiRefinedURL = aiRefinedURL
self.aiAtmosphereURL = aiAtmosphereURL
self.createdAt = createdAt
@@ -342,6 +347,62 @@ struct TravelAlbumMpCodeResponse: Decodable, Sendable, Equatable {
}
}
/// AI 修图模板类别,决定页面分组、选择规则和提交字段。
enum TravelAlbumAIRetouchTemplateCategory: Int, Sendable, Hashable {
case refined
case atmosphere
case cover
/// 模板分组展示标题。
var title: String {
switch self {
case .refined: "原图精修"
case .atmosphere: "氛围感修图"
case .cover: "封面风格模板"
}
}
}
/// AI 修图模板页工作流,明确区分首次批量修图与单张结果覆盖重修。
enum TravelAlbumAIRetouchWorkflow: Sendable, Equatable {
/// 对一个相册内的原始素材发起首次 AI 修图。
case initial(albumId: Int, materialIds: [Int])
/// 对单张素材的既有 AI 结果发起覆盖重修。
case reretouch(materialId: Int, batchId: Int, type: TravelAlbumAIReretouchType)
/// 当前页面需要展示的模板分组。
var visibleCategories: [TravelAlbumAIRetouchTemplateCategory] {
switch self {
case .initial(_, let materialIds):
var categories: [TravelAlbumAIRetouchTemplateCategory] = [.refined, .atmosphere]
if materialIds.count >= 4 { categories.append(.cover) }
return categories
case .reretouch(_, _, .refined):
return [.refined]
case .reretouch(_, _, .atmosphere):
return [.atmosphere]
case .reretouch(_, _, .all):
return [.refined, .atmosphere]
}
}
/// 当前分组是否允许不选择;仅首次修图的氛围感模板选填。
func isOptional(_ category: TravelAlbumAIRetouchTemplateCategory) -> Bool {
if case .initial = self, category == .atmosphere { return true }
return false
}
/// 工作流目标是否满足接口的最小参数要求。
var isValid: Bool {
switch self {
case .initial(let albumId, let materialIds):
return albumId > 0 && !materialIds.isEmpty
case .reretouch(let materialId, let batchId, _):
return materialId > 0 && batchId > 0
}
}
}
/// AI 修图模板,包含业务 ID、展示名称和预览图地址。
struct TravelAlbumAIRetouchTemplate: Decodable, Sendable, Equatable, Hashable, Identifiable {
let id: Int
@@ -392,14 +453,38 @@ struct TravelAlbumAIRetouchRequest: Encodable, Sendable, Equatable {
let materialIds: [Int]
let refinedTemplateId: Int
let atmosphereTemplateId: Int?
let aiPreviewTabs: Int?
let coverTemplateId: Int?
enum CodingKeys: String, CodingKey {
case userEquityTravelId = "user_equity_travel_id"
case materialIds = "material_ids"
case refinedTemplateId = "refined_template_id"
case atmosphereTemplateId = "atmosphere_template_id"
case aiPreviewTabs = "ai_preview_tabs"
case coverTemplateId = "cover_template_id"
}
}
/// 重新修图类型,决定后端覆盖的 AI 结果及必需模板字段。
enum TravelAlbumAIReretouchType: Int, Encodable, Sendable, Equatable {
case refined = 1
case atmosphere = 2
case all = 3
}
/// 提交单张素材重新修图的最小请求参数。
struct TravelAlbumAIReretouchRequest: Encodable, Sendable, Equatable {
let id: Int
let aiRetouchBatchId: Int
let type: TravelAlbumAIReretouchType
let refinedTemplateId: Int?
let atmosphereTemplateId: Int?
enum CodingKeys: String, CodingKey {
case id
case aiRetouchBatchId = "ai_retouch_batch_id"
case type
case refinedTemplateId = "refined_template_id"
case atmosphereTemplateId = "atmosphere_template_id"
}
}