Forráskód Böngészése

feat:添加了 后台生成的队列类

100Years 2 hónapja
szülő
commit
a684678700

+ 32 - 0
TSLiveWallpaper.xcodeproj/project.pbxproj

@@ -200,6 +200,10 @@
 		A8EE92C02DFFC3B50077DFFD /* TSImageProComparisonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8EE92BF2DFFC39C0077DFFD /* TSImageProComparisonView.swift */; };
 		A8EE92C22DFFC54C0077DFFD /* TSBootModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8EE92C12DFFC54A0077DFFD /* TSBootModel.swift */; };
 		A8EE92C42DFFC5830077DFFD /* TSBootCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8EE92C32DFFC5820077DFFD /* TSBootCell.swift */; };
+		A8EE92CD2DFFF2860077DFFD /* TSBaseOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8EE92CB2DFFF2860077DFFD /* TSBaseOperation.swift */; };
+		A8EE92CE2DFFF2860077DFFD /* TSGenerateBaseOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8EE92C72DFFF2860077DFFD /* TSGenerateBaseOperation.swift */; };
+		A8EE92CF2DFFF2860077DFFD /* TSGenerateBasePhotoOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8EE92C82DFFF2860077DFFD /* TSGenerateBasePhotoOperation.swift */; };
+		A8EE92D02DFFF2860077DFFD /* TSBaseOperationQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8EE92CA2DFFF2860077DFFD /* TSBaseOperationQueue.swift */; };
 		A8F76C422D350A9600AA6E93 /* TSPurchaseManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F76C3E2D350A9600AA6E93 /* TSPurchaseManager.swift */; };
 		A8F76C4D2D3747B400AA6E93 /* TSPurchaseVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F76C4C2D3747AB00AA6E93 /* TSPurchaseVC.swift */; };
 		A8F778AE2D1AC12400BF55D5 /* TSRandomWallpaperBrowseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F778AD2D1AC12100BF55D5 /* TSRandomWallpaperBrowseView.swift */; };
@@ -427,6 +431,10 @@
 		A8EE92BF2DFFC39C0077DFFD /* TSImageProComparisonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSImageProComparisonView.swift; sourceTree = "<group>"; };
 		A8EE92C12DFFC54A0077DFFD /* TSBootModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSBootModel.swift; sourceTree = "<group>"; };
 		A8EE92C32DFFC5820077DFFD /* TSBootCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSBootCell.swift; sourceTree = "<group>"; };
+		A8EE92C72DFFF2860077DFFD /* TSGenerateBaseOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSGenerateBaseOperation.swift; sourceTree = "<group>"; };
+		A8EE92C82DFFF2860077DFFD /* TSGenerateBasePhotoOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSGenerateBasePhotoOperation.swift; sourceTree = "<group>"; };
+		A8EE92CA2DFFF2860077DFFD /* TSBaseOperationQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSBaseOperationQueue.swift; sourceTree = "<group>"; };
+		A8EE92CB2DFFF2860077DFFD /* TSBaseOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSBaseOperation.swift; sourceTree = "<group>"; };
 		A8F76C3E2D350A9600AA6E93 /* TSPurchaseManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSPurchaseManager.swift; sourceTree = "<group>"; };
 		A8F76C4C2D3747AB00AA6E93 /* TSPurchaseVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSPurchaseVC.swift; sourceTree = "<group>"; };
 		A8F778AD2D1AC12100BF55D5 /* TSRandomWallpaperBrowseView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSRandomWallpaperBrowseView.swift; sourceTree = "<group>"; };
@@ -1091,6 +1099,7 @@
 		A86857802DF81AC20089D222 /* Data */ = {
 			isa = PBXGroup;
 			children = (
+				A8EE92CC2DFFF2860077DFFD /* OperationQueue */,
 				A868578B2DF843F90089D222 /* TSRealmManager */,
 				A868577F2DF81AB90089D222 /* Model */,
 				A868577A2DF819AA0089D222 /* TSDBManager */,
@@ -1301,6 +1310,25 @@
 			path = TSBootVC;
 			sourceTree = "<group>";
 		};
+		A8EE92C92DFFF2860077DFFD /* TSGenerateBaseOperation */ = {
+			isa = PBXGroup;
+			children = (
+				A8EE92C72DFFF2860077DFFD /* TSGenerateBaseOperation.swift */,
+				A8EE92C82DFFF2860077DFFD /* TSGenerateBasePhotoOperation.swift */,
+			);
+			path = TSGenerateBaseOperation;
+			sourceTree = "<group>";
+		};
+		A8EE92CC2DFFF2860077DFFD /* OperationQueue */ = {
+			isa = PBXGroup;
+			children = (
+				A8EE92C92DFFF2860077DFFD /* TSGenerateBaseOperation */,
+				A8EE92CA2DFFF2860077DFFD /* TSBaseOperationQueue.swift */,
+				A8EE92CB2DFFF2860077DFFD /* TSBaseOperation.swift */,
+			);
+			path = OperationQueue;
+			sourceTree = "<group>";
+		};
 		A8F76C3A2D35022300AA6E93 /* TSPurchaseMembershipVC */ = {
 			isa = PBXGroup;
 			children = (
@@ -1497,6 +1525,10 @@
 				A81CA4832D157F5C00A3AAC8 /* UIImageView+Ex.swift in Sources */,
 				60F82C112D43298800FFB08D /* MusicContainerViewModel.swift in Sources */,
 				A81F5B492D1956EA00740085 /* UIScreen.swift in Sources */,
+				A8EE92CD2DFFF2860077DFFD /* TSBaseOperation.swift in Sources */,
+				A8EE92CE2DFFF2860077DFFD /* TSGenerateBaseOperation.swift in Sources */,
+				A8EE92CF2DFFF2860077DFFD /* TSGenerateBasePhotoOperation.swift in Sources */,
+				A8EE92D02DFFF2860077DFFD /* TSBaseOperationQueue.swift in Sources */,
 				A8FD8F412DFC138A008CAACF /* TYCycleImageComparisonView.swift in Sources */,
 				A8C4C0E62D268D02003C46FC /* LivePhotoCreater.swift in Sources */,
 				A8C4C0E72D268D02003C46FC /* VideoRecorder.swift in Sources */,

+ 2 - 2
TSLiveWallpaper/AppDelegate.swift

@@ -42,8 +42,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
         if AppDelegate.isFirstInstallApp() {
             let bootPageVC = TSBootVC { [weak self] in
                 guard let self = self else { return }
-//                UserDefaults.standard.set("1", forKey: "isFirstInstallApp")
-//                UserDefaults.standard.synchronize()
+                UserDefaults.standard.set("1", forKey: "isFirstInstallApp")
+                UserDefaults.standard.synchronize()
                 goToTab()
             }
             let navi = TSBaseNavigationC(rootViewController: bootPageVC)

+ 21 - 3
TSLiveWallpaper/Business/TSAIListVC/TSAIListHistoryVC/TSAIListHistoryVC.swift

@@ -10,6 +10,19 @@ import RealmSwift
 class TSAIListHistoryVC: TSBaseVC {
 
     var listModelArray:List<TSDBActionInfoModel> = List<TSDBActionInfoModel>()
+    
+    lazy var navBarView: TSBaseNavContentBarView = {
+       let navBarView = TSBaseNavContentBarView()
+       
+       let titleImageView = UIImageView.createImageView(imageName: "nav_title_ailist",contentMode: .scaleToFill)
+       navBarView.barView.addSubview(titleImageView)
+       titleImageView.snp.makeConstraints { make in
+           make.center.equalToSuperview()
+       }
+
+       return navBarView
+   }()
+    
     //###################################### 集合视图 ######################################
     let collectionViewBtootm:CGFloat = 80
 
@@ -53,9 +66,14 @@ class TSAIListHistoryVC: TSBaseVC {
     override func createView() {
         addNormalNavBarView()
         
-        setPageTitle("History".localized)
-        navRightBtn = setNavigationItem("", imageName: "ai_delete", direction: .right, action: #selector(clickNavRight))
-   
+//        setPageTitle("History".localized)
+//        navRightBtn = setNavigationItem("", imageName: "ai_delete", direction: .right, action: #selector(clickNavRight))
+        
+        navBarContentView.addSubview(navBarView)
+        navBarView.snp.makeConstraints { make in
+            make.edges.equalToSuperview()
+        }
+        
         contentView.addSubview(pageNullView)
         contentView.addSubview(collectionView)
         collectionView.snp.makeConstraints { make in

+ 6 - 2
TSLiveWallpaper/Business/TSAIListVC/TSAIPhotoGeneratorVC/TSAIListPhotoGeneratorVC.swift

@@ -6,7 +6,7 @@
 //
 
 import Kingfisher
-struct TSAIListPhotoGeneratorModel {
+class TSAIListPhotoGeneratorModel {
     var upLoadImage:UIImage
     var generatorStyle:TSGeneratorImageStyle
     var expandEdge:UIEdgeInsets
@@ -16,7 +16,6 @@ struct TSAIListPhotoGeneratorModel {
     //预测宝宝
     var upLoadImages:[UIImage]?
     
-    
     init(upLoadImage: UIImage,
          generatorStyle: TSGeneratorImageStyle,
          expandEdge:UIEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0),
@@ -31,6 +30,11 @@ struct TSAIListPhotoGeneratorModel {
         self.additionalPrompt = additionalPrompt
         self.upLoadImages = upLoadImages
     }
+    
+    var prompt:String = ""
+    var inputText:String = ""
+    var upLoadImageUrl:String?
+    var model:String = ""   //决定生图的模型
 }
 
 class TSAIListPhotoGeneratorVC: TSAIPhotoDetailsVC {

+ 4 - 7
TSLiveWallpaper/Business/TSTabBarController/TSTabBarController.swift

@@ -47,20 +47,17 @@ class TSTabBarController: UITabBarController {
     @objc private func setUpData() {
         viewControllerArray = [
             "TSAIListVC",
-            "MusicHomeContainerViewController",
-            "MusicPlaylistContainerViewController",
+            "TSAIListHistoryVC",
             "TSMineVC"]
         
         selectedImageArray = [
             "tabbar_select_ailist",
-            "tabbar_select_music",
-            "tabbar_select_playlist",
+            "tabbar_select_ailist",
             "tabbar_select_mine",
         ]
         unselectedImageArray = [
             "tabbar_unSelect_allist",
-            "tabbar_unSelect_music",
-            "tabbar_unSelect_playlist",
+            "tabbar_unSelect_allist",
             "tabbar_unSelect_mine",
         ]
 
@@ -163,7 +160,7 @@ extension TSTabBarController: UITabBarControllerDelegate {
     }
     
     func updateMiniBarHidden(){
-        PlayerManager.shared.miniBar.isHidden = (selectedIndex == 0 || selectedIndex == 3)
+        PlayerManager.shared.miniBar.isHidden = true
     }
 }
 

+ 4 - 4
TSLiveWallpaper/Data/Model/TSActionInfoModel.swift

@@ -105,8 +105,9 @@ class TSActionRequestModel : TSBaseModel {
     
     var imageUrl:String = ""
     var imageUrlTimestamp:Int = 0
-    var style:String = ""
-    var advance:Bool = false//决定生图的模型
+    var generatorStyle:TSGeneratorImageStyle = .enhance
+    
+    //暂时用不到,以后再说
     var model:String = ""   //决定生图的模型
     
     override func mapping(map: ObjectMapper.Map) {
@@ -117,8 +118,7 @@ class TSActionRequestModel : TSBaseModel {
         
         imageUrl            <- map["imageUrl"]
         imageUrlTimestamp   <- map["imageUrlTimestamp"]
-        style               <- map["style"]
-        advance             <- map["advance"]
+        generatorStyle      <- map["generatorStyle"]
         model               <- map["model"]
     }
 }

+ 129 - 0
TSLiveWallpaper/Data/OperationQueue/TSBaseOperation.swift

@@ -0,0 +1,129 @@
+//
+//  TSBaseOperation.swift
+//  AIRingtone
+//
+//  Created by 100Years on 2025/3/20.
+//
+
+import Foundation
+
+class TSBaseOperation: Operation , @unchecked Sendable{
+    
+    let uuid:String
+    
+    enum TSBaseOperationState {
+        case executing(Bool)
+        case finished(Bool)
+        case cancelled(Bool)
+    }
+    
+    @Published var operationStatePblished:TSBaseOperationState = .executing(false)
+    
+    private var _executing = false
+    private var _finished = false
+    private var _cancelled = false
+    private var _error: Error?
+    
+    required init(uuid:String) {
+        self.uuid = uuid
+        super.init()
+    }
+    
+    // MARK: - State Management
+    
+    override var isExecuting: Bool {
+        return _executing
+    }
+    
+    override var isFinished: Bool {
+        return _finished
+    }
+    
+    override var isCancelled: Bool {
+        return _cancelled
+    }
+    
+    override var isAsynchronous: Bool {
+        return true
+    }
+    
+    override func start() {
+        if isCancelled {
+            finished()
+            return
+        }
+        setExecutingValue(value: true)
+    }
+    
+    override func cancel() {
+        debugPrint("TSBaseOperation cancel")
+        cancelCleanContent()
+        setCancelValue(value: true)
+        if isExecuting {
+            finished()
+        }
+    }
+    
+    func finished() {
+        if _executing {
+            setExecutingValue(value: false)
+        }
+        
+        if !_finished {
+            setFinishedValue(value: true)
+        }
+    }
+    
+    func setCancelValue(value:Bool){
+        willChangeValue(forKey: "isCancelled")
+        _cancelled = value
+        didChangeValue(forKey: "isCancelled")
+        operationStatePblished = .cancelled(value)
+    }
+    
+    func setExecutingValue(value:Bool){
+        willChangeValue(forKey: "isExecuting")
+        _executing = value
+        didChangeValue(forKey: "isExecuting")
+        operationStatePblished = .executing(value)
+    }
+    
+    func setFinishedValue(value:Bool){
+        willChangeValue(forKey: "isFinished")
+        _finished = value
+        didChangeValue(forKey: "isFinished")
+        operationStatePblished = .finished(value)
+    }
+    
+    // MARK: - Error Handling
+    
+    var error: Error? {
+        return _error
+    }
+    
+    // MARK: - Convenience Methods
+    
+    override func waitUntilFinished() {
+        while !isFinished {
+            RunLoop.current.run(mode: .default, before: .distantFuture)
+        }
+    }
+    
+    func addDependency(_ operation: TSBaseOperation) {
+        super.addDependency(operation)
+    }
+    
+    func removeDependency(_ operation: TSBaseOperation) {
+        super.removeDependency(operation)
+    }
+    
+    //子类重写,退出清理内容
+    func cancelCleanContent() {
+        fatalError("必须重写 cancelCleanContent")
+    }
+    
+    
+    deinit {
+        debugPrint("♻️♻️♻️ \(type(of: self)) deinit ♻️♻️♻️")
+    }
+}

+ 110 - 0
TSLiveWallpaper/Data/OperationQueue/TSBaseOperationQueue.swift

@@ -0,0 +1,110 @@
+//
+//  TSBaseOperationQueue.swift
+//  AIRingtone
+//
+//  Created by 100Years on 2025/3/20.
+//
+
+import Foundation
+import Combine
+
+
+
+class TSBaseOperationQueue {
+    
+    private(set) var queue: OperationQueue = OperationQueue()
+    
+    private var activeOperations:[String:TSBaseOperation] = [:]
+    // 存储每个操作的 AnyCancellable
+    private var cancellables: [String: AnyCancellable] = [:]
+    // 存储 KVO 观察者
+    private var operationCountObservation: NSKeyValueObservation?
+    
+    var isAvailability:Bool{
+        if queue.operationCount < queue.maxConcurrentOperationCount {
+            return true
+        }
+        return false
+    }
+
+    init(maxConcurrentOperationCount: Int = 1) {
+        var maxCount = maxConcurrentOperationCount
+//#if DEBUG
+//        maxCount = 6
+//#endif
+        queue.maxConcurrentOperationCount = maxCount
+        dePrint("TSBaseOperationQueue operationCountObservation")
+        // 监听 operationCount 的变化
+           operationCountObservation = queue.observe(\.operationCount, options: [.new]) { [weak self] (queue, change) in
+               guard let _ = self else { return }
+               if let _ = change.newValue {
+                   NotificationCenter.default.post(name: .kBaseOperationQueueCountChanged, object: nil, userInfo: nil)
+               }
+           }
+    }
+
+    func creatOperation(uuid: String, type: TSBaseOperation.Type) -> TSBaseOperation {
+        if let existingOperation = activeOperations[uuid] {
+            return existingOperation
+        } else {
+            // 使用 type 参数创建操作
+            let operation = type.init(uuid: uuid)
+            activeOperations[uuid] = operation
+            queue.addOperation(operation)
+            dePrint("TSBaseOperationQueue addOperation \(operation)")
+            cancellables[uuid] = operation.$operationStatePblished.sink { [weak self] state in
+                guard let self = self else { return }
+                
+                switch state {
+                case .finished(let finished):
+                    if finished == true {
+                        clearOperationsData(uuid: uuid)
+                    }
+                case .cancelled(let cancelled):
+                    if cancelled == true {
+                        clearOperationsData(uuid: uuid)
+                    }
+                default: break
+                }
+                
+                dePrint("TSBaseOperationQueue $operationStatePblished =\(state)")
+            }
+            return operation
+        }
+    }
+    
+    func findOperation<T: TSBaseOperation>(uuid: String) -> T? {
+        if let existingOperation = activeOperations[uuid] as? T {
+            return existingOperation
+        }
+        return nil
+    }
+    
+    /// 清理所有下载任务
+    func cancelAllOperations() {
+        queue.cancelAllOperations()
+        activeOperations = [:]
+    }
+    
+    /// 清理某个下载任务
+    /// - Parameter url: 下载任务的 URL
+    func cancelOperations(uuid: String) {
+        if let operation = activeOperations[uuid] {
+            operation.cancel()
+            clearOperationsData(uuid: uuid)
+        }
+    }
+
+    func clearOperationsData(uuid: String) {
+        dePrint("TSBaseOperationQueue cancelOperations activeOperations 前=\(activeOperations)")
+        dePrint("TSBaseOperationQueue cancelOperations cancellables 前=\(cancellables)")
+        activeOperations.removeValue(forKey: uuid)
+        cancellables.removeValue(forKey: uuid)
+        dePrint("TSBaseOperationQueue cancelOperations activeOperations 后=\(activeOperations)")
+        dePrint("TSBaseOperationQueue cancelOperations cancellables 后=\(cancellables)")
+    }
+    func waitUntilAllOperationsAreFinished() {
+        queue.waitUntilAllOperationsAreFinished()
+    }
+
+}

+ 216 - 0
TSLiveWallpaper/Data/OperationQueue/TSGenerateBaseOperation/TSGenerateBaseOperation.swift

@@ -0,0 +1,216 @@
+//
+//  TSGenerateBaseOperation.swift
+//  AIRingtone
+//
+//  Created by 100Years on 2025/3/24.
+//
+
+import Combine
+import Alamofire
+
+
+extension Notification.Name {
+    static let kBaseOperationQueueCountChanged = Notification.Name("kBaseOperationQueueCountChanged") //任务数量放生变化
+    static let kGenerateBasePhotoOperation = Notification.Name("TSGenerateBasePhotoOperation") //生成图生图任务发生变化
+}
+
+
+class TSGenerateBaseOperationQueue: TSBaseOperationQueue {
+    // 存储每个操作的 AnyCancellable
+    var stateables: [String: AnyCancellable] = [:]
+    
+    var generateOperationStateChanged:((String)->Void)?
+    
+    
+    override func cancelOperations(uuid: String) {
+        super.cancelOperations(uuid: uuid)
+        stateables.removeValue(forKey: uuid)
+    }
+
+    func handleStateDatauPblished(uuid:String,generateOperation: TSGenerateBaseOperation,notificationName:Notification.Name) {
+        stateables[uuid] = generateOperation.$stateDatauPblished.sink { [weak self] state in
+            guard let self = self else { return }
+            DispatchQueue.main.async {
+                self.generateOperationStateChanged?(uuid)
+                
+                let uuidData = self.getUUIDData(uuid: uuid)
+                NotificationCenter.default.post(
+                    name: notificationName,
+                    object: nil,
+                    userInfo: [
+                        "uuid": uuid,
+                        "count":self.queue.maxConcurrentOperationCount,
+                        "state":uuidData.0,
+                        "actionInfo":uuidData.1,
+                    ])
+            }
+        }
+    }
+    
+    func getUUIDData(uuid:String)->(TSProgressState,TSActionInfoModel?){
+        return (.none,TSActionInfoModel())
+    }
+    
+}
+
+class TSGenerateBaseOperation: TSBaseOperation , @unchecked Sendable{
+    
+    var actionInfoDict:[String:Any]{
+        return [:]
+    }
+    
+    @Published var stateDatauPblished:(TSProgressState,TSActionInfoModel?) = (TSProgressState.none,nil){
+        didSet{
+            dePrint("TSBaseOperation stateDatauPblished didSet = \(stateDatauPblished)")
+            if case .start = stateDatauPblished.0 {
+                start()
+            }else if stateDatauPblished.0.isResult {
+                DispatchQueue.main.asyncAfter(deadline: .now()+0.3){//稍微延迟,让通知报成功状态发送出去
+                    self.finished()
+                }
+            }
+        }
+    }
+    
+    var queryRequest:Request?
+    var stopNetwork = false
+    var generatingProgress = 0
+    var action_id:Int = 0
+    var isSaveDB:Bool = false //是否保存到数据库
+    var currentActionInfoModelChanged:((TSActionInfoModel)->Void)?
+    @Published var currentActionInfoModel: TSActionInfoModel = TSActionInfoModel()
+    
+    func initializeActionInfoModel(oldModel:TSActionInfoModel) {
+        currentActionInfoModel = oldModel
+        replaceSaveInfoModel(model: currentActionInfoModel)
+        stateDatauPblished = (.start,currentActionInfoModel)
+    }
+    
+    func replaceSaveInfoModel(model:TSActionInfoModel){ }
+    
+    func handleGenerateSuccess(){
+        
+    }
+
+    func handleFailInfoModel(errorString:String?,code:Int = 0){
+        self.currentActionInfoModel.actionStatus = .failed
+        self.currentActionInfoModel.status = "failed"
+        generatingProgress = 0
+        self.replaceSaveInfoModel(model: self.currentActionInfoModel)
+        self.stateDatauPblished = (TSProgressState.getFailed(errorString ?? "",code),self.currentActionInfoModel)
+    }
+    
+    func getActionInfo(oldModel:TSActionInfoModel) {
+        currentActionInfoModel = oldModel
+        self.getActionInfo(action_id:oldModel.id)
+    }
+
+    func getActionInfo(action_id:Int){
+        self.action_id = action_id
+        queryRequest = TSNetworkShared.get(urlType: .actionInfo,parameters: ["action_id":action_id]) { [weak self] data,error in
+            guard let self = self else { return }
+            
+            if stopNetwork == true {
+                return
+            }
+            
+            if let error = error {
+                debugPrint("getActionInfo error error = \(error)")
+                handleFailInfoModel(errorString: error.tsDesc,code: error.tsCode)
+                return
+            }
+            
+            if let result = kNetWorkResultSuccess(data: data) {
+                if let genmojiModel = TSActionInfoModel(JSON: result) {
+                    
+                    if genmojiModel.actionStatus != .success {
+                        self.replaceSaveInfoModel(model: genmojiModel)
+                    }
+                    
+                    switch genmojiModel.actionStatus {
+                    case .success:
+                        
+                        let successBlock = { [weak self]  in
+                            guard let self = self else { return }
+                            self.replaceSaveInfoModel(model: genmojiModel)
+                            self.stateDatauPblished = (.success(nil),genmojiModel)
+                            generatingProgress = 0
+                            self.handleGenerateSuccess()
+                        }
+                        
+                        if URL(string:genmojiModel.response.resultUrl) != nil {
+                            UIImageView.downloadImageWithProgress(urlString: genmojiModel.response.resultUrl) { [weak self]  progress in
+                                guard let self = self else { return }
+                                let progressInt = Int(progress*10.0)
+                                let progressString = "Generating".localized + " \(90 + progressInt)%"
+                                stateDatauPblished = (.progressString(progressString),currentActionInfoModel)
+                                dePrint("生成后图片下载进度 \(progress)")
+                            } completion: { image in
+                                successBlock()
+                            }
+
+                        }else{
+                            successBlock()
+                        }
+                    case .failed:
+                        debugPrint("getActionInfo error failed")
+                        handleFailInfoModel(errorString:genmojiModel.response.codeErrorMsg,code: genmojiModel.response.code)
+                    default:
+                        stateDatauPblished = (.progressString(generating(progress: genmojiModel.percent)),currentActionInfoModel)
+                        if stopNetwork == false {
+                            kDelayOnMainThread(2.0) {
+                                self.getActionInfo(action_id: action_id)
+                            }
+                        }
+                    }
+                    
+                    return
+                }
+            }
+            debugPrint("getActionInfo error nil")
+            handleFailInfoModel(errorString: nil)
+            
+        }
+    }
+     func generating(progress:Float) -> String {
+
+         //Generating 0%-100%
+         var progressInt = Int(progress*100)
+
+         if generatingProgress >= progressInt{
+             return getGeneratingProgressText()
+         }
+
+         if progressInt > 99 {
+             progressInt = 99
+         }
+         
+         generatingProgress = progressInt
+         return getGeneratingProgressText()
+     }
+     
+     
+    func getGeneratingProgressText()->String{
+        return "Working on your ringtone \(generatingProgress)%..."
+    }
+     
+
+    override func cancelCleanContent() {
+        debugPrint("cancelCleanContent")
+        stopNetwork = true
+        queryRequest?.cancel()
+    }
+}
+ var kRandomBoolLastResult:Bool = true
+func kRandomBool() -> Bool {
+    if !kRandomBoolLastResult {
+        // 如果上一次是 false,这次必须返回 true
+        kRandomBoolLastResult = true
+        return true
+    } else {
+        // 如果上一次是 true,随机返回 true 或 false
+        let randomResult = Bool.random()
+        kRandomBoolLastResult = randomResult
+        return randomResult
+    }
+}

+ 259 - 0
TSLiveWallpaper/Data/OperationQueue/TSGenerateBaseOperation/TSGenerateBasePhotoOperation.swift

@@ -0,0 +1,259 @@
+//
+//  TSGenerateBasePhotoOperation.swift
+//  AIRingtone
+//
+//  Created by 100Years on 2025/3/24.
+//
+
+import Combine
+import Alamofire
+import ObjectMapper
+import Kingfisher
+class TSGenerateBasePhotoOperationQueue: TSGenerateBaseOperationQueue {
+    static let shared:TSGenerateBasePhotoOperationQueue = TSGenerateBasePhotoOperationQueue()
+
+    func creatOperation(uuid: String) -> TSGenerateBasePhotoOperation {
+        let operation = super.creatOperation(uuid: uuid, type: TSGenerateBasePhotoOperation.self)
+        handleStateDatauPblished(uuid: uuid, generateOperation: operation as! TSGenerateBaseOperation, notificationName: .kGenerateBasePhotoOperation)
+        return operation as! TSGenerateBasePhotoOperation
+    }
+    
+    override func getUUIDData(uuid:String)->(TSProgressState,TSActionInfoModel?){
+        if let PosterOperation = TSGenerateBasePhotoOperationQueue.shared.findOperation(uuid: uuid) as? TSGenerateBasePhotoOperation {
+            dePrint("TSBaseOperation stateDatauPblished 发送 = \(PosterOperation.stateDatauPblished)")
+            return (PosterOperation.stateDatauPblished.0,PosterOperation.currentActionInfoModel)
+        }
+        return (.none,TSActionInfoModel())
+    }
+    
+}
+
+class TSGenerateBasePhotoOperation: TSGenerateBaseOperation , @unchecked Sendable{
+    
+    //是否展示成功后的 View 提醒
+    public var isShowSuccessView:Bool = false
+    
+    override var actionInfoDict:[String:Any]{
+        return [
+            "actionType":"image_create",
+            "comments": "Success",
+            "costTime":9,
+            "createdTimestamp":1742183242,
+            "id":2449,
+            "percent":1,
+            "request":"{\"prompt\": \"Traditional Chinese ink painting style phoenix soaring through misty mountain peaks, dynamic black brushstrokes with crimson accents on rice paper texture, Googie architecture space station, 1950s atomic age aesthetic, curved aluminum panels, starburst patterns, glass dome observatory --edge_threshold 0.5 --atomic_age_style 0.8 --chrome_reflection 1.2\", \"width\": 800, \"height\": 1440, \"countryCode\": \"FR\"}",
+            "response": "{\"resultUrl\": \"https://be-aigc.s3-accelerate.amazonaws.com/4c946f78-b1e7-4ffe-ba18-fff26b10178c.png\"}",
+            "status":"success"
+        ]
+    }
+
+    override func replaceSaveInfoModel(model:TSActionInfoModel){
+        model.uuid = uuid
+        model.request.imageUrlTimestamp = currentActionInfoModel.request.imageUrlTimestamp
+        if isSaveDB {
+            TSRMShared.aiListDB.updateData(model,id: currentActionInfoModel.id)
+        }
+        currentActionInfoModel = model
+        dePrint("model actionStatus 发出=\(model.actionStatus)")
+        currentActionInfoModelChanged?(currentActionInfoModel)
+    }
+
+    override func handleGenerateSuccess() {
+//        kPurchaseBusiness.useOnceForFree(type: .general)
+        TSRMShared.aiListDB.updateData(currentActionInfoModel,id: currentActionInfoModel.id)
+        if isShowSuccessView == false {
+            return
+        }
+        
+        
+        guard let window = WindowHelper.getKeyWindow() else {
+            debugPrint("getKeyWindow nil")
+            return
+        }
+        
+        guard let rootVC = WindowHelper.topViewController() else {
+            debugPrint("handleGenerateSuccess topViewController nil")
+            return
+        }
+        
+        let copyModel = self.currentActionInfoModel.copy()
+        if let cyModel = copyModel as? TSActionInfoModel {
+            let topY = k_Nav_Height+10
+            debugPrint("topY=\(topY)")
+            kSaveSuccesswShared.show(atView: window,text: "Successfully generated".localized,deadline: 5.0,bottom: kSaveSuccesswShared.getBottom(topY: topY)) {
+//                let gennerateVC = TSPTPGeneratorVC(generateStyleModel: TSAIListPhotoGeneratorModel(),infoModel: cyModel) { model in }
+//                gennerateVC.modalPresentationStyle = .overFullScreen
+//                gennerateVC.modalTransitionStyle = .crossDissolve
+//                rootVC.present(gennerateVC, animated: true)
+            }
+        }else{
+            debugPrint("copyModel as? TSActionInfoModel error")
+        }
+    }
+    
+
+    /**
+        1.用户上传图片
+        2.调用生成接口
+        3.后台返回查询 id,根据 id 不断查询进度和结果.(此时才允许进度后台)
+     
+        利用3后台返回的TSActionRequestModel中存着关键的请求接口数据,请求数据
+     
+     */
+    
+    private var uploadRequest:Request?
+    private var creatRequest:Request?
+    
+    private func createActionInfoModel(generateStyleModel:TSAIListPhotoGeneratorModel) -> TSActionInfoModel? {
+
+        guard let upLoadImageUrl = generateStyleModel.upLoadImageUrl else { return nil }
+
+        let infoModel = TSActionInfoModel()
+        infoModel.id = Date.timestampInt
+        infoModel.request = TSActionRequestModel()
+        infoModel.request.imageUrl = upLoadImageUrl
+        infoModel.request.imageUrlTimestamp = Date.timestampInt
+        
+        infoModel.request.prompt = generateStyleModel.prompt
+        infoModel.request.inputText = generateStyleModel.inputText
+        infoModel.request.generatorStyle = generateStyleModel.generatorStyle
+        
+        return infoModel
+    }
+    
+    func uploadImage(generateStyleModel:TSAIListPhotoGeneratorModel,complete:@escaping (TSActionInfoModel?)->Void) {
+        let upLoadImage = generateStyleModel.upLoadImage
+        if let imageUrl = generateStyleModel.upLoadImageUrl,imageUrl.contains("http") {
+            complete(createActionInfoModel(generateStyleModel: generateStyleModel))
+            return
+        }
+        
+        stopNetwork = false
+        stateDatauPblished = (.start,nil)
+        
+        stateDatauPblished = (.progressString(uploadingPhoto(progress: 0.0)),currentActionInfoModel)
+        uploadRequest = TSNetworkShared.uploadImage(upLoadImage: upLoadImage, maxKb: imageMaxKb) { [weak self]  progress in
+            guard let self = self else { return }
+            if generatingProgress == 0 {
+                stateDatauPblished = (.progressString(uploadingPhoto(progress: progress)),currentActionInfoModel)
+            }
+        } completion: { [weak self]  data, error in
+            guard let self = self else { return }
+            if stopNetwork == true { return }
+            if let error = error {
+                generateStyleModel.upLoadImageUrl = nil
+                self.stateDatauPblished = (TSProgressState.getFailed(error.tsDesc,error.tsCode),nil)
+                complete(nil)
+            }else{
+                if let string = data as? String {
+                    generateStyleModel.upLoadImageUrl = string
+                    if let url = URL(string: string){//上传成功后,就将图片缓存到本地
+                        ImageCache.default.store(upLoadImage, forKey: url.cacheKey)
+                    }
+                    
+                    complete(createActionInfoModel(generateStyleModel: generateStyleModel))
+                }else{
+                    complete(nil)
+                }
+            }
+        }
+    }
+    
+    func creatImage(oldModel:TSActionInfoModel) {
+        
+        initializeActionInfoModel(oldModel: oldModel)
+        if oldModel.upImageURLExpired { return  }
+
+        generatingProgress = 0
+        stopNetwork = false
+        stateDatauPblished = (.start,nil)
+
+        currentActionInfoModel.status = "running"
+        currentActionInfoModel.actionStatus = .running
+        currentActionInfoModel.percent = 0
+        replaceSaveInfoModel(model: currentActionInfoModel)
+        
+        stateDatauPblished = (.progressString(generating(progress: 0.0)),currentActionInfoModel)
+ 
+        
+        let request = currentActionInfoModel.request
+        
+        
+        var urlType:TSNeURLType = .imageRewrite
+        var postDict:[String:Any] = [
+            "device":getUserInfoJsonString(),
+            "imageUrl":request.imageUrl,
+            "model":request.generatorStyle.aiModel
+        ]
+        switch request.generatorStyle {
+        case .enhance:
+            urlType = .imageRewrite
+            postDict["prompt"] = "把图片变清晰"
+        case .colorize:
+            urlType = .imageRewrite
+            postDict["prompt"] = "Add suitable colors to photos"
+        case .descratch:
+            urlType = .imageRewrite
+            postDict["prompt"] = "Remove the photo's scratches and dirt"
+        case .enlighten:
+            urlType = .imageRewrite
+            postDict["prompt"] = "Adjust the light and darkness of the photo to make the overall look coordinated"
+            
+        case .recreate:
+            urlType = .imageRewrite
+            postDict["prompt"] = "Recreate damaged portraits and added suitable color for photo"
+        }
+
+        creatRequest = TSNetworkShared.post(urlType: urlType,parameters: postDict) { [weak self] data,error in
+            guard let self = self else { return }
+            if stopNetwork == true { return }
+            
+            if let error = error {
+                handleFailInfoModel(errorString: error.tsDesc,code: error.tsCode)
+            }else{
+                if let dataDict = kNetWorkCodeSuccess(data: data),
+                   let actionId = dataDict["actionId"] as? Int{
+                    if stopNetwork == false {
+                        self.stateDatauPblished = (.pending,nil) //通知首页进行更新
+                        self.getActionInfo(action_id:actionId)
+                    }
+                }else{
+                    handleFailInfoModel(errorString: "",code: 0)
+                }
+            }
+        }
+    }
+    override func generating(progress: Float) -> String {
+        let progress = Float(progress)*(0.9) // 预留 10% 进度给图片下载
+        //Generating 0%-100%
+        var progressInt = Int(progress*100)
+
+        if progressInt > 99 {
+            progressInt = 99
+        }
+        
+        generatingProgress = progressInt
+        return "Generating".localized + " \(progressInt)%"
+    }
+
+    var imageMaxKb:Int{
+        return 10*1024
+    }
+    
+    func uploadingPhoto(progress:Float) -> String {
+        //Uploading Photo 0%-100%
+        var progressInt = Int(progress*100)
+        if progressInt > 99 {
+            progressInt = 99
+        }
+        return "Uploading Photo".localized + " \(progressInt)%"
+    }
+    
+    
+    override func cancelCleanContent() {
+        super.cancelCleanContent()
+        debugPrint("cancelCleanContent")
+        uploadRequest?.cancel()
+        creatRequest?.cancel()
+    }
+}

+ 9 - 6
TSLiveWallpaper/Data/TSDBManager/TSDBActionInfoModel.swift

@@ -114,8 +114,9 @@ class TSDBActionRequestModel : Object {
     
     @Persisted var imageUrl:String = ""
     @Persisted var imageUrlTimestamp:Int = 0
-    @Persisted var style:String = ""
-    @Persisted var advance:Bool = false
+    
+    @Persisted var model:String = ""
+    @Persisted var generatorStyle:String = ""
     
     static func createDBModel(requestModel:TSActionRequestModel) -> TSDBActionRequestModel{
         let dbModel = TSDBActionRequestModel()
@@ -131,8 +132,9 @@ class TSDBActionRequestModel : Object {
         
         self.imageUrl = requestModel.imageUrl
         self.imageUrlTimestamp = requestModel.imageUrlTimestamp
-        self.style = requestModel.style
-        self.advance = requestModel.advance
+
+        self.model = requestModel.model
+        self.generatorStyle = requestModel.generatorStyle.rawValue
     }
     
     
@@ -145,8 +147,9 @@ class TSDBActionRequestModel : Object {
         
         model.imageUrl = self.imageUrl
         model.imageUrlTimestamp = self.imageUrlTimestamp
-        model.style = self.style
-        model.advance = self.advance
+ 
+        model.model = self.model
+        model.generatorStyle = TSGeneratorImageStyle(rawValue: self.generatorStyle) ?? .enhance
         
         return model
     }

+ 6 - 1
TSLiveWallpaper/LaunchVC/TSLaunchVC.swift

@@ -31,9 +31,14 @@ class TSLaunchVC: UIViewController {
     override func viewDidLoad() {
         super.viewDidLoad()
         setupLaunchScreenView()
-        startTimer()
         kPurchaseBusiness.launchPrchase()
         addNotifiy()
+        if AppDelegate.isFirstInstallApp() {//首次安装需要等待网络权限弹窗
+            startTimer()
+        }else{
+            remindTimeInterval = 1.0
+            startTimer()
+        }
     }