ソースを参照

feat:会员,数据库优化开发

100Years 2 ヶ月 前
コミット
1b4db05d54
22 ファイル変更2340 行追加2019 行削除
  1. 1 0
      Podfile
  2. 5 1
      Podfile.lock
  3. 52 4
      TSLiveWallpaper.xcodeproj/project.pbxproj
  4. 66 0
      TSLiveWallpaper/Business/BusinessView/TSDynamicBlurView.swift
  5. 133 0
      TSLiveWallpaper/Business/BusinessView/TSGeneratorloadingView/TSGeneratorErrorView.swift
  6. 154 0
      TSLiveWallpaper/Business/BusinessView/TSGeneratorloadingView/TSGeneratoringAnimationView.swift
  7. 192 0
      TSLiveWallpaper/Business/BusinessView/TSGeneratorloadingView/TSGeneratorloadingView.swift
  8. 2 2
      TSLiveWallpaper/Business/TSEditLiveVC/TSEditLiveEidtCell.swift
  9. 2 2
      TSLiveWallpaper/Business/TSEditLiveVC/TSEditLiveVC.swift
  10. 0 426
      TSLiveWallpaper/Business/TSPurchaseMembershipVC/TSPurchaseMembershipVC.swift
  11. 3 1
      TSLiveWallpaper/Business/TSPurchaseMembershipVC/TSViewTool/TSViewTool.swift
  12. 160 0
      TSLiveWallpaper/Common/Purchase/TSPurchaseBusiness.swift
  13. 105 0
      TSLiveWallpaper/Common/Purchase/TSPurchaseEnum.swift
  14. 5 105
      TSLiveWallpaper/Common/Purchase/TSPurchaseManager.swift
  15. 386 386
      TSLiveWallpaper/Common/TSNetWork/TSNetWork+Business.swift
  16. 80 80
      TSLiveWallpaper/Common/TSNetWork/TSNetworkManager+Loading.swift
  17. 398 398
      TSLiveWallpaper/Common/TSNetWork/TSNetworkManager.swift
  18. 135 130
      TSLiveWallpaper/Data/Model/TSActionInfoModel.swift
  19. 178 172
      TSLiveWallpaper/Data/TSDBManager/TSDBActionInfoModel.swift
  20. 162 311
      TSLiveWallpaper/Data/TSDBManager/TSDBManager.swift
  21. 120 0
      TSLiveWallpaper/Data/TSRealmManager/TSRealmManager.swift
  22. 1 1
      TSLiveWallpaper/LaunchVC/TSLaunchVC.swift

+ 1 - 0
Podfile

@@ -30,6 +30,7 @@ target 'TSLiveWallpaper' do
   pod 'ADManager', :path => '../TSCoacopods/ADManager'
   pod 'RealmSwift', '~>10'
   pod 'TSSmalCoacopods', :path => '../TSSmalCoacopods'
+  pod "DynamicBlurView"
 end
 
 

+ 5 - 1
Podfile.lock

@@ -18,6 +18,7 @@ PODS:
     - AFNetworking/NSURLSession
   - Alamofire (5.10.2)
   - BetterSegmentedControl (2.0.1)
+  - DynamicBlurView (5.0.3)
   - Google-Mobile-Ads-SDK (11.12.0):
     - GoogleUserMessagingPlatform (>= 1.1)
   - GoogleUserMessagingPlatform (2.7.0)
@@ -127,6 +128,7 @@ DEPENDENCIES:
   - ADManager (from `../TSCoacopods/ADManager`)
   - Alamofire
   - BetterSegmentedControl (~> 2.0)
+  - DynamicBlurView
   - Google-Mobile-Ads-SDK
   - IJKMediaPlayerKit (from `https://github.com/debugly/ijkplayer/releases/download/k0.11.8/IJKMediaPlayerKit.spec.json`)
   - Kingfisher (= 7.10.0)
@@ -149,6 +151,7 @@ SPEC REPOS:
     - AFNetworking
     - Alamofire
     - BetterSegmentedControl
+    - DynamicBlurView
     - Google-Mobile-Ads-SDK
     - GoogleUserMessagingPlatform
     - Kingfisher
@@ -199,6 +202,7 @@ SPEC CHECKSUMS:
   AFNetworking: 3bd23d814e976cd148d7d44c3ab78017b744cd58
   Alamofire: 7193b3b92c74a07f85569e1a6c4f4237291e7496
   BetterSegmentedControl: 09607b27861d49cbce48b7673b74f9150a3d371a
+  DynamicBlurView: b57e2f6aa33f85b2bcca272265162a3c7c5cc499
   Google-Mobile-Ads-SDK: 3a76704456669fbed2057efc8bdb99050ad0e3fb
   GoogleUserMessagingPlatform: a8b56893477f67212fbc8411c139e61d463349f5
   IJKMediaPlayerKit: 3444702ec61bc649e7e81a9c0e19fda661bef1ce
@@ -223,6 +227,6 @@ SPEC CHECKSUMS:
   TZImagePickerController: d084a7b97c82d387e7669dd86dc9a9057500aacf
   YYModel: 2a7fdd96aaa4b86a824e26d0c517de8928c04b30
 
-PODFILE CHECKSUM: 92404e787675f7e5d22aded1f99acddf441c6801
+PODFILE CHECKSUM: b7c5415af3c54aa76934a2ac2f496e7d2666cd06
 
 COCOAPODS: 1.16.2

+ 52 - 4
TSLiveWallpaper.xcodeproj/project.pbxproj

@@ -164,6 +164,13 @@
 		A86857872DF81B660089D222 /* TSNetWork+Business.swift in Sources */ = {isa = PBXBuildFile; fileRef = A86857852DF81B660089D222 /* TSNetWork+Business.swift */; };
 		A86857882DF81B660089D222 /* TSNetworkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A86857842DF81B660089D222 /* TSNetworkManager.swift */; };
 		A86857892DF81B660089D222 /* TSNetworkManager+Loading.swift in Sources */ = {isa = PBXBuildFile; fileRef = A86857862DF81B660089D222 /* TSNetworkManager+Loading.swift */; };
+		A868578C2DF843F90089D222 /* TSRealmManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A868578A2DF843F90089D222 /* TSRealmManager.swift */; };
+		A86857922DF845F60089D222 /* TSGeneratoringAnimationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A86857902DF845F60089D222 /* TSGeneratoringAnimationView.swift */; };
+		A86857932DF845F60089D222 /* TSGeneratorErrorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A86857912DF845F60089D222 /* TSGeneratorErrorView.swift */; };
+		A86857942DF845F60089D222 /* TSGeneratorloadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A868578F2DF845F60089D222 /* TSGeneratorloadingView.swift */; };
+		A86857982DF846FE0089D222 /* TSDynamicBlurView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A86857972DF846FB0089D222 /* TSDynamicBlurView.swift */; };
+		A868579A2DF915950089D222 /* TSPurchaseEnum.swift in Sources */ = {isa = PBXBuildFile; fileRef = A86857992DF9158C0089D222 /* TSPurchaseEnum.swift */; };
+		A868579E2DF915E90089D222 /* TSPurchaseBusiness.swift in Sources */ = {isa = PBXBuildFile; fileRef = A868579D2DF915DD0089D222 /* TSPurchaseBusiness.swift */; };
 		A8C4C0982D242154003C46FC /* LivePhoto.swift in Sources */ = {isa = PBXBuildFile; fileRef = A858EE162D1CF49B004B680F /* LivePhoto.swift */; };
 		A8C4C0A42D24218A003C46FC /* metadata.mov in Resources */ = {isa = PBXBuildFile; fileRef = A8C4C09E2D24218A003C46FC /* metadata.mov */; };
 		A8C4C0A52D24218A003C46FC /* Converter4Video.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8C4C09B2D24218A003C46FC /* Converter4Video.swift */; };
@@ -177,7 +184,6 @@
 		A8E56BF62D1520EC003C54AF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8E56BEC2D1520EC003C54AF /* AppDelegate.swift */; };
 		A8E56BF92D1520EC003C54AF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A8E56BED2D1520EC003C54AF /* Assets.xcassets */; };
 		A8E56BFB2D1520EC003C54AF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A8E56BF02D1520EC003C54AF /* LaunchScreen.storyboard */; };
-		A8F76C3C2D35026200AA6E93 /* TSPurchaseMembershipVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F76C3B2D35026100AA6E93 /* TSPurchaseMembershipVC.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 */; };
@@ -357,6 +363,13 @@
 		A86857842DF81B660089D222 /* TSNetworkManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSNetworkManager.swift; sourceTree = "<group>"; };
 		A86857852DF81B660089D222 /* TSNetWork+Business.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TSNetWork+Business.swift"; sourceTree = "<group>"; };
 		A86857862DF81B660089D222 /* TSNetworkManager+Loading.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TSNetworkManager+Loading.swift"; sourceTree = "<group>"; };
+		A868578A2DF843F90089D222 /* TSRealmManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSRealmManager.swift; sourceTree = "<group>"; };
+		A868578F2DF845F60089D222 /* TSGeneratorloadingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSGeneratorloadingView.swift; sourceTree = "<group>"; };
+		A86857902DF845F60089D222 /* TSGeneratoringAnimationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSGeneratoringAnimationView.swift; sourceTree = "<group>"; };
+		A86857912DF845F60089D222 /* TSGeneratorErrorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSGeneratorErrorView.swift; sourceTree = "<group>"; };
+		A86857972DF846FB0089D222 /* TSDynamicBlurView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSDynamicBlurView.swift; sourceTree = "<group>"; };
+		A86857992DF9158C0089D222 /* TSPurchaseEnum.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSPurchaseEnum.swift; sourceTree = "<group>"; };
+		A868579D2DF915DD0089D222 /* TSPurchaseBusiness.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSPurchaseBusiness.swift; sourceTree = "<group>"; };
 		A8C4C0992D24218A003C46FC /* AVAssetExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVAssetExtension.swift; sourceTree = "<group>"; };
 		A8C4C09A2D24218A003C46FC /* Converter4Image.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Converter4Image.swift; sourceTree = "<group>"; };
 		A8C4C09B2D24218A003C46FC /* Converter4Video.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Converter4Video.swift; sourceTree = "<group>"; };
@@ -373,7 +386,6 @@
 		A8E56BED2D1520EC003C54AF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
 		A8E56BEE2D1520EC003C54AF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		A8E56BEF2D1520EC003C54AF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
-		A8F76C3B2D35026100AA6E93 /* TSPurchaseMembershipVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSPurchaseMembershipVC.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>"; };
@@ -809,6 +821,7 @@
 		A81CA48C2D15855300A3AAC8 /* Business */ = {
 			isa = PBXGroup;
 			children = (
+				A868578D2DF845D00089D222 /* BusinessView */,
 				606372DB2D549998005C82CF /* AdMob */,
 				60553F022D3B4DF800BAAD7F /* TSMusic */,
 				A8F76C3A2D35022300AA6E93 /* TSPurchaseMembershipVC */,
@@ -1040,6 +1053,7 @@
 		A86857802DF81AC20089D222 /* Data */ = {
 			isa = PBXGroup;
 			children = (
+				A868578B2DF843F90089D222 /* TSRealmManager */,
 				A868577F2DF81AB90089D222 /* Model */,
 				A868577A2DF819AA0089D222 /* TSDBManager */,
 			);
@@ -1056,6 +1070,33 @@
 			path = TSNetWork;
 			sourceTree = "<group>";
 		};
+		A868578B2DF843F90089D222 /* TSRealmManager */ = {
+			isa = PBXGroup;
+			children = (
+				A868578A2DF843F90089D222 /* TSRealmManager.swift */,
+			);
+			path = TSRealmManager;
+			sourceTree = "<group>";
+		};
+		A868578D2DF845D00089D222 /* BusinessView */ = {
+			isa = PBXGroup;
+			children = (
+				A86857972DF846FB0089D222 /* TSDynamicBlurView.swift */,
+				A868578E2DF845E40089D222 /* TSGeneratorloadingView */,
+			);
+			path = BusinessView;
+			sourceTree = "<group>";
+		};
+		A868578E2DF845E40089D222 /* TSGeneratorloadingView */ = {
+			isa = PBXGroup;
+			children = (
+				A868578F2DF845F60089D222 /* TSGeneratorloadingView.swift */,
+				A86857902DF845F60089D222 /* TSGeneratoringAnimationView.swift */,
+				A86857912DF845F60089D222 /* TSGeneratorErrorView.swift */,
+			);
+			path = TSGeneratorloadingView;
+			sourceTree = "<group>";
+		};
 		A8C4C0A12D24218A003C46FC /* Util */ = {
 			isa = PBXGroup;
 			children = (
@@ -1111,7 +1152,6 @@
 			isa = PBXGroup;
 			children = (
 				A8F76C4C2D3747AB00AA6E93 /* TSPurchaseVC.swift */,
-				A8F76C3B2D35026100AA6E93 /* TSPurchaseMembershipVC.swift */,
 				A81CA4A22D16747900A3AAC8 /* TSViewTool */,
 			);
 			path = TSPurchaseMembershipVC;
@@ -1120,7 +1160,9 @@
 		A8F76C402D350A9600AA6E93 /* Purchase */ = {
 			isa = PBXGroup;
 			children = (
+				A86857992DF9158C0089D222 /* TSPurchaseEnum.swift */,
 				A8F76C3E2D350A9600AA6E93 /* TSPurchaseManager.swift */,
+				A868579D2DF915DD0089D222 /* TSPurchaseBusiness.swift */,
 			);
 			path = Purchase;
 			sourceTree = "<group>";
@@ -1297,7 +1339,6 @@
 				60553FDB2D3B7CC600BAAD7F /* FitManager.swift in Sources */,
 				A81CA4992D1652C400A3AAC8 /* TSMineVC.swift in Sources */,
 				A8C4C0EF2D27BFF7003C46FC /* TSNetworkTool.swift in Sources */,
-				A8F76C3C2D35026200AA6E93 /* TSPurchaseMembershipVC.swift in Sources */,
 				A81CA46E2D156C7000A3AAC8 /* GlobalImports.swift in Sources */,
 				606372E52D55BAB8005C82CF /* GuideBubbleView.swift in Sources */,
 				A81CA4832D157F5C00A3AAC8 /* UIImageView+Ex.swift in Sources */,
@@ -1323,6 +1364,7 @@
 				60553FD72D3B54A400BAAD7F /* GradientText.swift in Sources */,
 				60553FD82D3B54A400BAAD7F /* LWBaseNavigationController.swift in Sources */,
 				60553FD92D3B54A400BAAD7F /* GradientBackgroundModifier.swift in Sources */,
+				A868579E2DF915E90089D222 /* TSPurchaseBusiness.swift in Sources */,
 				606372DD2D54999C005C82CF /* ADScene.swift in Sources */,
 				A81F5B5D2D1A906C00740085 /* TSHomeDataModel.swift in Sources */,
 				A81CA49B2D1652CA00A3AAC8 /* TSHomeVC.swift in Sources */,
@@ -1400,6 +1442,7 @@
 				60553F9F2D3B528A00BAAD7F /* FilterBarView.swift in Sources */,
 				60553FA02D3B528A00BAAD7F /* SongListViewController+Target.swift in Sources */,
 				60553FA12D3B528A00BAAD7F /* ToastView.swift in Sources */,
+				A868579A2DF915950089D222 /* TSPurchaseEnum.swift in Sources */,
 				60553FA22D3B528A00BAAD7F /* SongListViewController.swift in Sources */,
 				60553FA32D3B528A00BAAD7F /* PlayListTopItemView.swift in Sources */,
 				60553FA42D3B528A00BAAD7F /* VipTagView.swift in Sources */,
@@ -1417,6 +1460,9 @@
 				60553FB02D3B528A00BAAD7F /* PlayMiniBar.swift in Sources */,
 				60553FB12D3B528A00BAAD7F /* SongDownloadCell.swift in Sources */,
 				60553FB22D3B528A00BAAD7F /* LocalSearchViewModel.swift in Sources */,
+				A86857922DF845F60089D222 /* TSGeneratoringAnimationView.swift in Sources */,
+				A86857932DF845F60089D222 /* TSGeneratorErrorView.swift in Sources */,
+				A86857942DF845F60089D222 /* TSGeneratorloadingView.swift in Sources */,
 				60553FB32D3B528A00BAAD7F /* SortMenuViewController.swift in Sources */,
 				60553FB42D3B528A00BAAD7F /* ImportFilesManager.swift in Sources */,
 				60553FB52D3B528A00BAAD7F /* PlayButtonView.swift in Sources */,
@@ -1428,6 +1474,7 @@
 				60553FBB2D3B528A00BAAD7F /* PlayListAddCell.swift in Sources */,
 				60553FBC2D3B528A00BAAD7F /* SearchOnlineViewController.swift in Sources */,
 				60553FBD2D3B528A00BAAD7F /* SpacedButton.swift in Sources */,
+				A868578C2DF843F90089D222 /* TSRealmManager.swift in Sources */,
 				60553FBE2D3B528A00BAAD7F /* BasePresentViewController.swift in Sources */,
 				60553FBF2D3B528A00BAAD7F /* PlayListCell.swift in Sources */,
 				60553FC12D3B528A00BAAD7F /* MusicSearchBar.swift in Sources */,
@@ -1440,6 +1487,7 @@
 				A839463C2D1D6E3600ABFF0D /* TSRandomWallpaperCopyrightVC.swift in Sources */,
 				A81CA4952D1652B500A3AAC8 /* TSEditLiveVC.swift in Sources */,
 				606372E12D54BC37005C82CF /* GoogleMobileAdsConsentManager.swift in Sources */,
+				A86857982DF846FE0089D222 /* TSDynamicBlurView.swift in Sources */,
 				A83946332D1D66A900ABFF0D /* TSPrivacyPolicyVC.swift in Sources */,
 				A839463A2D1D6E3000ABFF0D /* TSRandomWallpaperTutorialsVC.swift in Sources */,
 				A84C239F2D1E88CD00B61B55 /* TSFileManagerTool.swift in Sources */,

+ 66 - 0
TSLiveWallpaper/Business/BusinessView/TSDynamicBlurView.swift

@@ -0,0 +1,66 @@
+//
+//  TSDynamicBlurView.swift
+//  TSLiveWallpaper
+//
+//  Created by 100Years on 2025/6/10.
+//
+
+
+import DynamicBlurView  //  pod "DynamicBlurView"
+class TSDynamicBlurView: UIView {
+    
+    public override init(frame: CGRect) {
+        super.init(frame: frame)
+        creatUI()
+    }
+    
+    required public init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+
+    lazy var blurView: DynamicBlurView = {
+        let blurView = DynamicBlurView()
+        blurView.blurRadius = 18.0//16.0
+        blurView.trackingMode = .none // tracking滚动时候会很卡的
+        blurView.iterations = 10
+        blurView.isDeepRendering = true//ture 从 window 截图,false 从父视图截图
+        return blurView
+    }()
+    
+    func creatUI(){
+        self.backgroundColor = "#111111".uiColor.withAlphaComponent(0.5)
+        self.addSubview(blurView)
+        blurView.snp.makeConstraints { make in
+            make.edges.equalToSuperview()
+        }
+    }
+    
+    func refresh(){
+        blurView.refresh()
+    }
+}
+
+extension TSDynamicBlurView {
+    static func blurred(image:UIImage) -> UIImage? {
+        if let darkenedImage = imageWithBlackTransparency(image, color: "#111111".uiColor.withAlphaComponent(0.5)) {
+            return darkenedImage.blurred(radius: 16.0, iterations: 10, ratio: 1.0, blendColor: nil, blendMode: .plusLighter)
+        }
+        return image.blurred(radius: 16.0, iterations: 10, ratio: 1.0, blendColor: nil, blendMode: .plusLighter)
+    }
+    
+    static func imageWithBlackTransparency(_ image: UIImage, color: UIColor) -> UIImage? {
+        let rect = CGRect(origin: .zero, size: image.size)
+        
+        UIGraphicsBeginImageContextWithOptions(image.size, false, image.scale)
+        defer { UIGraphicsEndImageContext() }
+        
+        // 先绘制原图
+        image.draw(in: rect)
+        
+        // 再绘制黑色透明层
+        color.setFill()
+        UIRectFillUsingBlendMode(rect, .multiply)
+        
+        return UIGraphicsGetImageFromCurrentImageContext()
+    }
+}

+ 133 - 0
TSLiveWallpaper/Business/BusinessView/TSGeneratorloadingView/TSGeneratorErrorView.swift

@@ -0,0 +1,133 @@
+//
+//  TSGeneratorView.swift
+//  AIEmoji
+//
+//  Created by 100Years on 2025/5/12.
+//
+
+class TSGeneratorErrorView: TSBaseView {
+    
+    var style:TSGeneratorView.Style = .generalError{
+        didSet{
+            debugPrint("TSGeneratorErrorView style = \(style)")
+            switch style {
+            case .sensitiveError:
+                sensitiveErrorView()
+            case .netWorkError:
+                netWorkErrorView()
+            case .generateTooMuch:
+                generateTooMuchView()
+            default:
+                generalErrorView()
+            }
+        }
+    }
+    var isUploadImage:Bool = true
+    lazy var cusStackView: UIStackView = {
+        let cusStackView = UIStackView()
+        cusStackView.axis = .vertical
+        cusStackView.distribution = .fill
+        cusStackView.alignment = .center
+        return cusStackView
+    }()
+    
+    lazy var errorImageView: UIImageView = {
+        let errorImageView = UIImageView.createImageView(imageName: "yellow_warning")
+        return errorImageView
+    }()
+    
+    lazy var textLabel: UILabel = {
+        let textLabel = UILabel.createLabel(font: .font(size: 14),textColor: .white,textAlignment: .center,numberOfLines: 0)
+        return textLabel
+    }()
+    
+    lazy var submitBtn: UIButton = {
+        let btn = UIButton.createButton(title: "Generate in the background".localized,backgroundImage: kSubmitBtnNormalbg,font: .font(size: 16),titleColor: "#111111".uiColor,corner: 24)
+        btn.titleLabel?.adjustsFontSizeToFitWidth = true
+        return btn
+    }()
+    
+    override func creatUI() {
+        
+        contentView.addSubview(cusStackView)
+        cusStackView.snp.makeConstraints { make in
+            make.edges.equalToSuperview()
+        }
+        
+        cusStackView.addArrangedSubview(errorImageView)
+        errorImageView.snp.makeConstraints { make in
+            make.top.equalTo(0)
+            make.width.height.equalTo(56)
+        }
+        
+        cusStackView.addSpacing(length: 12.0)
+        
+        cusStackView.addArrangedSubview(textLabel)
+        textLabel.snp.makeConstraints { make in
+            make.leading.equalTo(24)
+            make.trailing.equalTo(-24)
+        }
+        
+        cusStackView.addSpacing(length: 28.0)
+        
+        cusStackView.addArrangedSubview(submitBtn)
+        submitBtn.snp.makeConstraints { make in
+            make.width.equalTo(250)
+            make.height.equalTo(48)
+            make.bottom.equalTo(0)
+        }
+    }
+}
+
+extension TSGeneratorErrorView {
+    
+//    func setErrorText(errorString:String) {
+//        textLabel.text = errorString
+//    }
+    
+    func sensitiveErrorView() {
+        submitBtn.setTitle(isUploadImage ? "Reselect photos".localized : "Got it".localized, for: .normal)
+        errorImageView.image = UIImage(named: "yellow_warning")
+        
+        errorImageView.snp.updateConstraints { make in
+            make.width.height.equalTo(56)
+        }
+
+        textLabel.text = "Your photo may contain nudity, gore or violence that does not comply with the health policy, please replace the photo and try again.".localized
+        
+    }
+    
+    func generalErrorView() {
+        submitBtn.setTitle("Got it".localized, for: .normal)
+        errorImageView.image = UIImage(named: "failed_big")
+        
+        errorImageView.snp.updateConstraints { make in
+            make.width.height.equalTo(120)
+        }
+        
+        textLabel.text = "Sorry there was a slight problem with the image processing, please try again later.".localized
+    }
+    
+    
+    func netWorkErrorView() {
+        submitBtn.setTitle("Try Again".localized, for: .normal)
+        errorImageView.image = UIImage(named: "network_error")
+        
+        errorImageView.snp.updateConstraints { make in
+            make.width.height.equalTo(120)
+        }
+        
+        textLabel.text = "No network, please check your network and try again.".localized
+    }
+    
+    func generateTooMuchView() {
+        submitBtn.setTitle("Got it".localized, for: .normal)
+        errorImageView.image = UIImage(named: "yellow_warning")
+        
+        errorImageView.snp.updateConstraints { make in
+            make.width.height.equalTo(56)
+        }
+
+        textLabel.text = "Your photo may contain nudity, gore or violence that does not comply with the health policy, please replace the photo and try again.".localized
+    }
+}

+ 154 - 0
TSLiveWallpaper/Business/BusinessView/TSGeneratorloadingView/TSGeneratoringAnimationView.swift

@@ -0,0 +1,154 @@
+//
+//  Untitled.swift
+//  AIEmoji
+//
+//  Created by 100Years on 2025/5/12.
+//
+
+import Kingfisher
+
+class TSGeneratoringAnimationView : TSBaseView {
+
+    
+    var isShowBackGeneration:Bool = false {
+        didSet{
+            self.backgroundGenerateBtn.isHidden = !self.isShowBackGeneration
+        }
+    }
+    
+    lazy var cusStackView: UIStackView = {
+        let cusStackView = UIStackView()
+        cusStackView.axis = .vertical
+        cusStackView.distribution = .fill
+        cusStackView.alignment = .center
+        return cusStackView
+    }()
+    
+    lazy var animatedImageView: AnimatedImageView = {
+        let animatedImageView = AnimatedImageView()
+        animatedImageView.autoPlayAnimatedImage = false
+        if let gifURL = Bundle.main.url(forResource: "rotatingAnimation_1", withExtension: "gif") {
+            animatedImageView.kf.setImage(with: gifURL, options: [.cacheOriginalImage]) { result in
+                switch result {
+                case .success(let value):
+                    print("GIF 加载成功: \(value.source.url?.absoluteString ?? "")")
+                case .failure(let error):
+                    print("GIF 加载失败: \(error.localizedDescription)")
+                }
+            }
+        }
+        
+        return animatedImageView
+    }()
+    
+    lazy var timeLabel: UILabel = {
+        let textLabel = UILabel.createLabel(font: .font(size: 18,weight: .semibold),textColor: .white,textAlignment: .center)
+        textLabel.isHidden = true
+        return textLabel
+    }()
+    
+    lazy var textLabel: UILabel = {
+        let textLabel = UILabel.createLabel(font: .font(size: 18),textColor: .white,textAlignment: .center,numberOfLines: 0)
+        textLabel.isHidden = true
+        return textLabel
+    }()
+    
+    lazy var infoLabel: UILabel = {
+        let textLabel = UILabel.createLabel(font: .font(size: 14),textColor: .white.withAlphaComponent(0.6),textAlignment: .center,numberOfLines: 0)
+        textLabel.isHidden = true
+        return textLabel
+    }()
+    
+    lazy var backgroundGenerateBtn: UIButton = {
+        let btn = UIButton.createButton(title: "Generate in the background".localized,font: .font(size: 16),titleColor: .themeColor,corner: 24)
+        btn.layer.borderColor = UIColor.themeColor.cgColor
+        btn.layer.borderWidth = 1.0
+        btn.titleLabel?.adjustsFontSizeToFitWidth = true
+        btn.isHidden = true
+        return btn
+    }()
+    
+    
+    override func creatUI() {
+        
+        contentView.addSubview(cusStackView)
+        cusStackView.snp.makeConstraints { make in
+            make.edges.equalToSuperview()
+        }
+        
+        setUpGeneratorContentView()
+    }
+    
+    func setUpGeneratorContentView(){
+        
+        cusStackView.addArrangedSubview(animatedImageView)
+        animatedImageView.snp.makeConstraints { make in
+            make.top.equalTo(0)
+            make.width.height.equalTo(120)
+        }
+        
+        cusStackView.addSpacing(length: 12.0)
+        
+        cusStackView.addArrangedSubview(timeLabel)
+        timeLabel.snp.makeConstraints { make in
+            make.leading.equalTo(kTextLeading)
+            make.trailing.equalTo(-kTextLeading)
+        }
+        
+        cusStackView.addSpacing(length: 16.0)
+        
+        cusStackView.addArrangedSubview(textLabel)
+        textLabel.snp.makeConstraints { make in
+            make.leading.equalTo(kTextLeading)
+            make.trailing.equalTo(-kTextLeading)
+        }
+        
+        cusStackView.addSpacing(length: 8.0)
+        
+        cusStackView.addArrangedSubview(infoLabel)
+        infoLabel.snp.makeConstraints { make in
+            make.leading.equalTo(kTextLeading)
+            make.trailing.equalTo(-kTextLeading)
+        }
+        
+        cusStackView.addSpacing(length: 20.0)
+        
+        cusStackView.addArrangedSubview(backgroundGenerateBtn)
+        backgroundGenerateBtn.snp.makeConstraints { make in
+            make.width.lessThanOrEqualTo(k_ScreenWidth - 64)
+            make.width.greaterThanOrEqualTo(250)
+            make.height.equalTo(48)
+            make.bottom.equalTo(0)
+        }
+
+    }
+
+}
+
+extension TSGeneratoringAnimationView {
+    
+    
+    func startAnimation() {
+        kDelayMainShort {
+            self.animatedImageView.startAnimating()
+        }
+    }
+
+    func stopAnimation() {
+        self.animatedImageView.stopAnimating()
+    }
+    
+    func setText(time:String,info:String) {
+        timeLabel.text = time
+        infoLabel.text = info
+        
+        timeLabel.isHidden = time.isEmpty
+        infoLabel.isHidden = info.isEmpty
+    }
+    
+    func setProgressText(text:String) {
+        textLabel.text = text
+        textLabel.isHidden = text.isEmpty
+    }
+
+}

+ 192 - 0
TSLiveWallpaper/Business/BusinessView/TSGeneratorloadingView/TSGeneratorloadingView.swift

@@ -0,0 +1,192 @@
+//
+//  TSGeneratorView.swift
+//  AIEmoji
+//
+//  Created by 100Years on 2025/4/2.
+//
+
+import Kingfisher
+
+
+
+
+let kTextLeading = 24.0
+
+class TSGeneratorView: TSBaseView {
+    
+    enum Style {
+        case loading    //loading
+        case generalError //通用错误
+        case sensitiveError   //敏感类的错误
+        case netWorkError //网络错误
+        case generateTooMuch //生成次数过多
+    }
+    
+    var style:Style = .generalError
+    var clickBackstageBlock:(()->Void)?
+    var clickErrorBlock:((Style)->Void)?
+    
+    var isUploadImage:Bool = true{
+        didSet {
+            errorView.isUploadImage = isUploadImage
+        }
+    }
+    lazy var animationView: TSGeneratoringAnimationView = {
+        let animationView = TSGeneratoringAnimationView()
+        animationView.backgroundGenerateBtn.addTarget(self, action: #selector(clickBackstageBtn), for: .touchUpInside)
+        return animationView
+    }()
+    
+    lazy var errorView: TSGeneratorErrorView = {
+        let errorView = TSGeneratorErrorView()
+        errorView.isUploadImage = isUploadImage
+        errorView.isHidden = true
+        errorView.submitBtn.addTarget(self, action: #selector(clickErrorBtn), for: .touchUpInside)
+        return errorView
+    }()
+
+    lazy var blurEffect: TSDynamicBlurView = {
+        return TSDynamicBlurView()
+    }()
+    
+    lazy var xBtn: UIButton = {
+        let xBtn = UIButton.createButton(image: UIImage(named: "close_gray")) { [weak self]  in
+            guard let self = self else { return }
+            self.isHidden = true
+        }
+        xBtn.isHidden = true
+        return xBtn
+    }()
+    
+
+    override func creatUI() {
+        contentView.addSubview(blurEffect)
+        blurEffect.snp.makeConstraints { make in
+            make.edges.equalToSuperview()
+        }
+
+        contentView.addSubview(animationView)
+        animationView.snp.makeConstraints { make in
+            make.leading.trailing.centerY.equalToSuperview()
+        }
+        
+        contentView.addSubview(errorView)
+        errorView.snp.makeConstraints { make in
+            make.leading.trailing.centerY.equalToSuperview()
+        }
+        
+
+        //关闭按钮
+        contentView.addSubview(xBtn)
+        xBtn.snp.makeConstraints { make in
+            make.top.equalTo(k_Height_StatusBar + 4)
+            make.leading.equalTo(16)
+            make.width.equalTo(36)
+            make.height.equalTo(36)
+        }
+    }
+    
+
+    override func dealThings() {
+        // 监听应用生命周期事件
+        NotificationCenter.default.addObserver(
+            self,
+            selector: #selector(handleAppDidEnterBackground),
+            name: UIApplication.didEnterBackgroundNotification,
+            object: nil
+        )
+        
+        NotificationCenter.default.addObserver(
+            self,
+            selector: #selector(handleAppWillEnterForeground),
+            name: UIApplication.willEnterForegroundNotification,
+            object: nil
+        )
+    }
+    
+    lazy var isRotating = false{
+        didSet{
+            if isRotating == true{
+                animationView.startAnimation()
+            }else{
+                animationView.stopAnimation()
+            }
+        }
+    }
+    
+    @objc private func handleAppDidEnterBackground() {
+        animationView.stopAnimation()
+    }
+
+    @objc private func handleAppWillEnterForeground() {
+        isRotating = isRotating
+    }
+    
+
+    
+    func showLoading(text:String){
+        isRotating = true
+        animationView.isHidden = false
+        errorView.isHidden = true
+        
+        animationView.setProgressText(text: text)
+    }
+    
+    func showError(text:String){
+        isRotating = false
+        animationView.isHidden = true
+        errorView.isHidden = false
+        errorView.style = style
+        errorView.textLabel.text = text
+    }
+
+    func setBackgroundColor(color:UIColor){
+        blurEffect.removeFromSuperview()
+        contentView.backgroundColor = color
+    }
+    
+    //后台生成
+    @objc func clickBackstageBtn() {
+        clickBackstageBlock?()
+    }
+    
+    @objc func clickErrorBtn() {
+        clickErrorBlock?(self.style)
+        errorView.submitBtn.preventMultipleTaps()
+    }
+    
+}
+
+
+extension TSGeneratorView{
+    
+    func updateShowProgress(text:String) {
+        isHidden = false
+        showLoading(text: text)
+        isRotating = true
+    }
+    
+    func updateShowLoading(text:String){
+        isHidden = false
+        showLoading(text: text)
+        isRotating = true
+    }
+    
+    func updateShowError(text:String,code:Int){
+        self.style = TSNetWorkCode.getGeneratorStyle(code: code)
+        isHidden = false
+        showError(text: text.isEmpty ? kGenerateFailed : text)
+        isRotating = false
+        setBackgroundGenerateBtnHidden(true)
+    }
+    
+    func updateShowSuccess(){
+        isHidden = true
+        isRotating = false
+        setBackgroundGenerateBtnHidden(true)
+    }
+    
+    func setBackgroundGenerateBtnHidden(_ isHidden:Bool){
+        self.animationView.isShowBackGeneration = !isHidden
+    }
+}

+ 2 - 2
TSLiveWallpaper/Business/TSEditLiveVC/TSEditLiveEidtCell.swift

@@ -113,10 +113,10 @@ class TSEditLiveEidtCell : TSBaseCollectionCell{
     
     
     func getVipText()->String{
-        if kPurchaseDefault.isVip {
+        if kPurchaseBusiness.isVip {
             return "DIY Live Wallpaper"
         }
-        return "DIY Live Wallpaper (\(kPurchaseDefault.freeNum))"
+        return "DIY Live Wallpaper (\(kPurchaseBusiness.freeNum(type: .general)))"
     }
 
     deinit {

+ 2 - 2
TSLiveWallpaper/Business/TSEditLiveVC/TSEditLiveVC.swift

@@ -37,7 +37,7 @@ class TSEditLiveVC: TSBaseVC, UINavigationControllerDelegate {
             guard let self = self else { return }
 
             // 判断 vip
-            if kPurchaseDefault.freeNumAvailable() == false {
+            if kPurchaseBusiness.freeNumAvailable(type: .general) == false {
                 TSPurchaseVC.show(target: self) { [weak self] in
                     guard let self = self else { return }
                     reloadView()
@@ -188,7 +188,7 @@ extension TSEditLiveVC {
                     LivePhotoConverter.saveToLibrary(videoURL: videoURL, imageURL: imageURL) { _ in
                         kSavePhotoSuccesswShared.show(atView: self.view, text: "DIY Successfully".localized)
                     }
-                    kPurchaseDefault.useOnceForFree()
+                    kPurchaseBusiness.useOnceForFree(type: .general)
 
                     let saveURL = TSFileManagerTool.saveLiveVideoPathURL
                     let timestampString = Date.timestampString

+ 0 - 426
TSLiveWallpaper/Business/TSPurchaseMembershipVC/TSPurchaseMembershipVC.swift

@@ -1,426 +0,0 @@
-//
-//  TSPurchaseMembershipVC.swift
-//  TSLiveWallpaper
-//
-//  Created by 100Years on 2025/1/13.
-//
-
-class TSPurchaseMembership111VC: TSBaseVC {
-    
-    var closePageBlock:(()->Void)?
-    var buyPeriod:PremiumPeriod = .year
-    lazy var purchaseManager: PurchaseManager = {
-        let purchaseManager = PurchaseManager.default
-        return purchaseManager
-    }()
-    
-    lazy var yearCell: TSPurchaseMembershipCell = {
-        let yearCell = TSPurchaseMembershipCell()
-        yearCell.titleLabel.text = "Year".localized
-        yearCell.priceLabel.text = purchaseManager.price(for: .year)
-        yearCell.selectedImageView.isHidden = false
-        yearCell.onTapd = { [weak self]  in
-            guard let self = self else { return }
-            monthCell.selectedImageView.isHidden = true
-            buyPeriod = .year
-        }
-        return yearCell
-    }()
-    
-    lazy var monthCell: TSPurchaseMembershipCell = {
-        let monthCell = TSPurchaseMembershipCell()
-        monthCell.titleLabel.text = "Month".localized
-        monthCell.priceLabel.text = purchaseManager.price(for: .month)
-        monthCell.onTapd = { [weak self]  in
-            guard let self = self else { return }
-            yearCell.selectedImageView.isHidden = true
-            buyPeriod = .month
-        }
-        return monthCell
-    }()
-    
-    lazy var submitBtn: UIButton = {
-        let submitBtn = TSViewTool.createNormalSubmitBtn(title: "Continue") { [weak self]  in
-            guard let self = self else { return }
-            
-            purchaseManager.pay(for: buyPeriod)
-            
-        }
-        submitBtn.cornerRadius = 24.0
-        return submitBtn
-    }()
-
-    lazy var textLabel: UILabel = {
-        let label = UILabel()
-        label.numberOfLines = 0
-        label.textAlignment = .center
-        label.font = .font(size: 8)
-        label.textColor = UIColor.fromHex("#FFFFFF",alpha: 0.4)
-        let fullText = "Recurring billing, cancel anytime. Payment will be charged to your iTunes account at confirmation of purchase. Subscriptions automatically renew for the same applicable term and price, unless auto-renew is turned off at least 24 hours before the end of the current period."
-        let highlightedText = "Recurring billing, cancel anytime."
-        let attributedString = NSMutableAttributedString(string: fullText)
-        if let range = fullText.range(of: highlightedText) {
-            let nsRange = NSRange(range, in: fullText)
-            attributedString.addAttributes([
-                .foregroundColor: UIColor.fromHex("#12FFF7"), // 高亮颜色
-            ], range: nsRange)
-        }
-        label.attributedText = attributedString
-        return label
-    }()
-    
-    override func createView() {
-        addNormalNavBarView()
-        _ = setNavigationItem("", imageName: "close_gray", direction: .left, action: #selector(closePage))
-    
-    
-        
-        let topView = creatTopView()
-        contentView.addSubview(topView)
-        topView.snp.makeConstraints { make in
-            make.leading.equalTo(0)
-            make.trailing.equalTo(0)
-            make.top.equalTo(20)
-        }
-        
-        
-        contentView.addSubview(yearCell)
-        contentView.addSubview(monthCell)
-        contentView.addSubview(submitBtn)
-        
-        yearCell.snp.makeConstraints { make in
-            make.leading.equalTo(16)
-            make.trailing.equalTo(-16)
-            make.height.equalTo(74)
-            make.top.equalTo(topView.snp.bottom).offset(72)
-        }
-        
-        monthCell.snp.makeConstraints { make in
-            make.leading.equalTo(16)
-            make.trailing.equalTo(-16)
-            make.height.equalTo(74)
-            make.top.equalTo(yearCell.snp.bottom).offset(12)
-        }
-        
-        submitBtn.snp.makeConstraints { make in
-            make.leading.equalTo(16)
-            make.trailing.equalTo(-16)
-            make.height.equalTo(48)
-            make.top.equalTo(monthCell.snp.bottom).offset(28)
-        }
-        
-        contentView.addSubview(textLabel)
-        textLabel.snp.makeConstraints { make in
-            make.leading.equalTo(16)
-            make.trailing.equalTo(-16)
-            make.height.equalTo(48)
-            make.top.equalTo(submitBtn.snp.bottom).offset(12)
-        }
-        
-        let bottomView = creatBottomLabel()
-        contentView.addSubview(bottomView)
-        bottomView.snp.makeConstraints { make in
-            make.height.equalTo(17)
-            make.centerX.equalToSuperview()
-            make.top.equalTo(textLabel.snp.bottom).offset(8)
-        }
-  
-    }
-    
-    override func dealThings() {
-        purchaseManager.onPurchaseStateChanged = { [weak self] manager,state,object in
-            guard let self = self else { return }
-        
-            DispatchQueue.main.async {
-                switch state {
-                case .none:
-                    break
-                case .loading:
-                    TSToastShared.showLoading(text: "Getting price".localized,containerView: self.view)
-                case .loadSuccess:
-                    TSToastShared.hideLoading()
-                case .loadFail:
-                    TSToastShared.hideLoading()
-                    let message = "Get price failure, Will automatically retry in 5 seconds".localized
-                    TSToastShared.showToast(text: message)
-                    DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
-                        PurchaseManager.default.requestProducts()
-                    }
-                case .paying:
-                    TSToastShared.showLoading(text: "Purchasing now".localized,containerView: self.view)
-                case .paySuccess:
-                    TSToastShared.hideLoading()
-                    let loadingText = manager.isVip ? "Congratulation you have become VIP".localized : "Finish".localized
-                    TSToastShared.showToast(text:loadingText)
-                    if manager.isVip {
-                        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
-                            self.closePage()
-                        }
-                    }
-
-                case .payFail:
-                    TSToastShared.hideLoading()
-                    if let str = object as? String {
-                        TSToastShared.showToast(text: str)
-                    }
-                    
-                case .restoreing:
-                    TSToastShared.showLoading(text: "Restoring now".localized,containerView: self.view)
-                case .restoreSuccess:
-                    TSToastShared.hideLoading()
-                    let loadingText = manager.isVip ? "Congratulation you have become VIP".localized : "Couldn't Restore Subscription".localized
-                    debugPrint(loadingText)
-                    TSToastShared.showToast(text:loadingText)
-                    if manager.isVip {
-                        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
-                            self.closePage()
-                        }
-                    }
-
-                case .restoreFail:
-                    TSToastShared.hideLoading()
-                    let loadingText = (object as? String) ?? "Failed to restore subscribe, please try again".localized
-                    debugPrint(loadingText)
-                    TSToastShared.showToast(text: loadingText)
-                case .verifying:
-                    #if DEBUG
-                    TSToastShared.showLoading(text: "Verifying receipt...".localized,containerView: self.view)
-                    #endif
-                case .verifySuccess:
-                    break
-                case .verifyFail:
-                #if DEBUG
-                    TSToastShared.hideLoading()
-                    let message = (object as? String) ?? "Verify receipt failed"
-                    TSToastShared.showToast(text:message)
-
-                #endif
-                }
-            }
-            debugPrint("PurchaseManager onPurchaseStateChanged=\(String(describing: state))")
-        }
-        
-
-    }
-    
-    @objc func closePage(){
-        closePageBlock?()
-        TSToastShared.hideLoading()
-        self.dismiss(animated: true)
-    }
-}
-
-
-extension TSPurchaseMembership111VC {
-    
-
-    func creatTopView() -> UIView {
-        
-        let topView = UIView()
-        
-        let vipImageView = UIImageView.createImageView(imageName: "vip_big_icon")
-        topView.addSubview(vipImageView)
-        
-        let liveImageView = UIImageView.createImageView(imageName: "Dmanager_pro")
-        topView.addSubview(liveImageView)
-        
-        let cell0 = creatCell(text: "All Premium Wallpapers")
-        topView.addSubview(cell0)
-        
-        let cell1 = creatCell(text: "Unlimited Video To Live")
-        topView.addSubview(cell1)
-        
-        let cell2 = creatCell(text: "100% No Ads")
-        topView.addSubview(cell2)
-        
-        
-        
-        vipImageView.snp.makeConstraints { make in
-            make.top.equalTo(0)
-            make.centerX.equalToSuperview()
-            make.width.equalTo(124)
-            make.height.equalTo(102)
-        }
-        
-        liveImageView.snp.makeConstraints { make in
-            make.top.equalTo(vipImageView.snp.bottom).offset(12)
-            make.centerX.equalToSuperview()
-        }
-        
-        cell0.snp.makeConstraints { make in
-            make.top.equalTo(liveImageView.snp.bottom).offset(40)
-            make.leading.equalTo(76)
-            make.trailing.equalTo(-10)
-            make.height.equalTo(24)
-        }
-        
-        cell1.snp.makeConstraints { make in
-            make.top.equalTo(cell0.snp.bottom).offset(18)
-            make.leading.equalTo(76)
-            make.trailing.equalTo(-10)
-            make.height.equalTo(24)
-        }
-        
-        cell2.snp.makeConstraints { make in
-            make.top.equalTo(cell1.snp.bottom).offset(18)
-            make.leading.equalTo(76)
-            make.trailing.equalTo(-10)
-            make.height.equalTo(24)
-            make.bottom.equalToSuperview()
-        }
-        
-        return topView
-    }
-    
- 
-    func creatCell(text:String) -> UIView {
-        let cellView = UIView()
-        
-        let vipImageView = UIImageView.createImageView(imageName: "check")
-        cellView.addSubview(vipImageView)
-        
-        
-        let label = UILabel.createLabel(text: text,font: .font(size: 16),textColor: .lesserText)
-        cellView.addSubview(label)
-        
-        vipImageView.snp.makeConstraints { make in
-            make.width.height.equalTo(24)
-            make.leading.top.bottom.equalTo(0)
-        }
-        
-        label.snp.makeConstraints { make in
-            make.trailing.top.bottom.equalTo(0)
-            make.leading.equalTo(vipImageView.snp.trailing).offset(8)
-        }
-        
-        return cellView
-    }
-    
-    
-    func creatBottomLabel() -> UIView {
-        let bgView = UIView()
-        
-        let termsLable = UILabel.createLabel(text: " Terms of us  ".localized,font: .font(size: 12),textColor: .textAssist)
-        termsLable.isUserInteractionEnabled = true
-        termsLable.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(clickTermsLabel)))
-        let privacyLable = UILabel.createLabel(text: "|  Privacy Policy  ".localized,font: .font(size: 12),textColor: .textAssist)
-        privacyLable.isUserInteractionEnabled = true
-        privacyLable.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(clickPrivacyLable)))
-        let restoreLable = UILabel.createLabel(text: "|  Restore   ".localized,font: .font(size: 12),textColor: .textAssist)
-        restoreLable.isUserInteractionEnabled = true
-        restoreLable.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(clickRestoreLable)))
-        
-        bgView.addSubview(termsLable)
-        termsLable.snp.makeConstraints { make in
-            make.leading.top.bottom.equalTo(0)
-        }
-        
-        bgView.addSubview(privacyLable)
-        privacyLable.snp.makeConstraints { make in
-            make.leading.equalTo(termsLable.snp.trailing)
-            make.top.bottom.equalTo(0)
-        }
-        
-        bgView.addSubview(restoreLable)
-        restoreLable.snp.makeConstraints { make in
-            make.leading.equalTo(privacyLable.snp.trailing)
-            make.top.bottom.trailing.equalTo(0)
-        }
-        
-        return bgView
-    }
-    
-    
-    @objc func clickTermsLabel() {
-        
-        let vc = TSBusinessWebVC(urlType: .terms)
-        vc.hidesBottomBarWhenPushed = true
-        kPresentModalVC(target: self, modelVC: vc)
-    }
-    
-    @objc func clickPrivacyLable() {
-        let vc = TSBusinessWebVC(urlType: .privacy)
-        vc.hidesBottomBarWhenPushed = true
-        kPresentModalVC(target: self, modelVC: vc)
-    }
-    
-    @objc func clickRestoreLable() {
-        purchaseManager.restorePremium()
-    }
-    
-}
-
-
-
-
-
-class TSPurchaseMembershipCell: TSBaseView {
-    
-    
-    var onTapd:(()->Void)?
-    
-    
-    lazy var titleLabel: UILabel = {
-        let titleLabel = UILabel.createLabel(font: .font(size: 14),textColor: .textAssist)
-        return titleLabel
-    }()
-    
-    lazy var priceLabel: UILabel = {
-        let titleLabel = UILabel.createLabel(font: .font(size: 18,weight: .medium),textColor: .mainText)
-        return titleLabel
-    }()
-    
-    
-    lazy var selectedImageView: UIImageView = {
-        let selectedImageView = UIImageView.createImageView(imageName: "radiobox_selected")
-        selectedImageView.isHidden = true
-        return selectedImageView
-    }()
-    
-    override func creatUI() {
-        contentView.backgroundColor = .fromHex("FFFFFF", alpha: 0.1)
-        contentView.layer.cornerRadius = 16.0
-        contentView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(clickBg)))
-        contentView.addSubview(titleLabel)
-        contentView.addSubview(priceLabel)
-        contentView.addSubview(selectedImageView)
-        
-        titleLabel.snp.makeConstraints { make in
-            make.leading.equalTo(16)
-            make.top.equalTo(15)
-            make.height.equalTo(14)
-        }
-        
-        priceLabel.snp.makeConstraints { make in
-            make.leading.equalTo(16)
-            make.bottom.equalTo(-15)
-            make.height.equalTo(18)
-        }
-        
-        selectedImageView.snp.makeConstraints { make in
-            make.trailing.equalTo(-16)
-            make.centerY.equalToSuperview()
-            make.width.height.equalTo(24)
-        }
-    }
-    
-    @objc func clickBg(){
-        selectedImageView.isHidden = false
-        onTapd?()
-    }
-    
-}
-
-extension TSPurchaseMembership111VC{
-    
-    static func show(target:UIViewController,closePageBlock:(()->Void)?){
-//        let vc = TSPurchaseMembershipVC()
-//        vc.closePageBlock = closePageBlock
-//        let navi = TSBaseNavigationC(rootViewController: vc)
-//        navi.modalPresentationStyle = .overFullScreen
-//        target.present(navi, animated: true)
-    }
-
-}
-
-

+ 3 - 1
TSLiveWallpaper/Business/TSPurchaseMembershipVC/TSViewTool/TSViewTool.swift

@@ -56,7 +56,9 @@ class TSViewTool: UIView {
 }
 
 
-
+let kSubmitBtnbg = UIImage(named: "submit_btn_bg")
+let kSubmitBtnSmallBg = UIImage(named: "submit_btn_small_bg")
+let kSubmitBtnNormalbg = UIImage(named: "submit_btn_normal_bg")
 let kWapppaperPlaceholderImage = UIImage(named: "wapppaper_placeholder")
 let KIconLiveImage = UIImage(named: "icon_live")
 

+ 160 - 0
TSLiveWallpaper/Common/Purchase/TSPurchaseBusiness.swift

@@ -0,0 +1,160 @@
+//
+//  TSPUrChaseTool.swift
+//  TSLiveWallpaper
+//
+//  Created by 100Years on 2025/6/10.
+//
+
+private let kFreeNumKey = "kFreeNumKey"
+private let kTotalUseNumKey = "kTotalUseNumKey"
+
+public enum VipFreeNumType: String, CaseIterable {
+    case none = "kNone"
+    case general = "kGeneral" //通用的 vip
+}
+
+let kPurchaseBusiness = TSPurchaseBusiness.shared
+class TSPurchaseBusiness {
+    static let shared = TSPurchaseBusiness()
+    public var totalUsedTimes: Int = 0
+    // 免费使用会员的次数
+    var freeDict:[String:Int] = [:]
+    
+    
+    init() {
+        initializeForFree()
+    }
+    
+    var isVip:Bool{
+        return PurchaseManager.default.isVip
+    }
+    
+    var vipType:PremiumPeriod{
+        return PurchaseManager.default.vipType
+    }
+    
+    public var isOverTotalTimes: Bool {
+        if isVip {
+            loadTotalUse()
+            #if DEBUG
+                    return false
+            #endif
+            return totalUsedTimes >= vipType.freeNumber
+        }
+        return false
+    }
+    
+    /// 使用一次免费次数
+    func useOnceForFree(type:VipFreeNumType){
+        var freeNum = freeDict[type.rawValue] ?? 0
+        if freeNum > 0 {
+            freeNum-=1
+        }
+        
+        if freeNum < 0 {
+            freeNum = 0
+        }
+        
+        freeDict[type.rawValue] = freeNum
+        saveForFree()
+        
+        NotificationCenter.default.post(name: .kVipFreeNumChanged, object: nil, userInfo: ["VipFreeNumType": type])
+    }
+    
+    func freeNum(type:VipFreeNumType) -> Int{
+        let freeNum = freeDict[type.rawValue] ?? 0
+        return freeNum
+    }
+    
+    func saveForFree(){
+        UserDefaults.standard.set(freeDict, forKey: kFreeNumKey)
+        UserDefaults.standard.synchronize()
+    }
+    
+    func loadTotalUse() {
+        // 当天没记录,设置默认次数
+        guard let dict = UserDefaults.standard.dictionary(forKey: kTotalUseNumKey),
+              dict.safeString(forKey: "date") == Date().dateDayString else {
+            totalUsedTimes = 0
+            return
+        }
+        // 有记录,设置已经使用次数
+        totalUsedTimes = dict.safeInt(forKey: "times")
+    }
+    
+    func initializeForFree(){
+        if let dict = UserDefaults.standard.dictionary(forKey: kFreeNumKey) as? [String:Int]{
+            freeDict = dict
+        }else{
+            freeDict = [
+                VipFreeNumType.general.rawValue:3
+            ]
+            saveForFree()
+        }
+    }
+    
+    /// 免费次数是否可用
+    func freeNumAvailable(type:VipFreeNumType) -> Bool{
+        if isVip == true {
+            return true
+        }else{
+            if let freeNum = freeDict[type.rawValue],freeNum > 0 {
+                return true
+            }
+        }
+        return false
+    }
+    
+    /// 是否展示生成类的会员图标
+    func generateVipShow(type:VipFreeNumType) -> Bool{
+        if isVip == false, freeNum(type: type) > 0 {
+            return false
+        }
+        return true
+    }
+}
+extension TSPurchaseBusiness{
+    
+    
+    func launchPrchase() {
+        PurchaseManager.default.password = "155c8104e2b041c0abae43ace199124c"
+        PurchaseManager.default.purchaseProducts = [
+            PurchaseProduct(productId: "1001", period:.month),
+            PurchaseProduct(productId: "1002", period: .year),
+            PurchaseProduct(productId: "003", period: .lifetime)
+        ]
+        
+        PurchaseManager.default.requestProducts()
+        
+    }
+    
+}
+
+extension TSPurchaseBusiness{
+    func kJudgeVipFreeType(vipFreeNumType:VipFreeNumType,
+                   vc:UIViewController? = nil,
+                   closePageBlock:(()->Void)? = nil) -> Bool {
+        //判断 vip
+        return kJudgeVip(externalBool: kPurchaseBusiness.freeNumAvailable(type: vipFreeNumType) == false, vc: vc,closePageBlock: closePageBlock)
+    }
+
+
+    func kJudgeVip(externalBool:Bool,
+                   vc:UIViewController? = nil,
+                   closePageBlock:(()->Void)? = nil) -> Bool {
+        //判断 vip
+        if externalBool,
+           self.isVip == false
+        {
+            if let vc = vc {
+                TSPurchaseVC.show(target: vc,closePageBlock: nil)
+            }else if let rootVC = WindowHelper.getRootViewController() {
+                TSPurchaseVC.show(target: rootVC,closePageBlock: nil)
+            }
+            
+            return true
+        }
+        return false
+    }
+}
+

+ 105 - 0
TSLiveWallpaper/Common/Purchase/TSPurchaseEnum.swift

@@ -0,0 +1,105 @@
+//
+//  TSPurchaseEnum.swift
+//  TSLiveWallpaper
+//
+//  Created by 100Years on 2025/6/10.
+//
+
+
+public enum PremiumPeriod: String, CaseIterable {
+    case none           = ""
+    case week           = "Week"
+    case month          = "Monthly"
+    case year           = "Yearly"
+    case lifetime       = "Lifetime"
+    
+    /// 对应vip类型,可以免费使用次数
+    var freeNumber: Int {
+        switch self {
+        case .week:
+            return 30
+        case .month:
+            return 30
+        case .year:
+            return 60
+        case .none:
+            return 0
+        default:
+            return 30
+        }
+    }
+
+    var saveString: String {
+        switch self {
+        case .none:
+            return "80%"//"40%" 增加月付费
+        default:
+            return "80%"
+        }
+    }
+    
+    /*
+    1. 一年(非闰年)
+    ​365 天​ = 365 × 24 × 60 × 60 × 1000 = ​31,536,000,000 毫秒
+    (若闰年 366 天 = 31,622,400,000 毫秒)
+    ​2. 一个月(平均)
+    ​30.44 天​(按 365 天/12 个月计算)≈ 30.44 × 24 × 60 × 60 × 1000 ≈ ​2,629,746,000 毫秒
+    (实际月份天数不同,如 28/30/31 天需单独计算)
+    ​3. 一周
+    ​7 天​ = 7 × 24 × 60 × 60 × 1000 = ​604,800,000 毫秒
+    */
+    var milliseconds:Int {
+        switch self {
+        case .year:
+            return  365 * 24 * 60 * 60 * 1000
+        case .month:
+            return 30 * 24 * 60 * 60 * 1000
+        case .week:
+            return 7 * 24 * 60 * 60 * 1000
+        default:
+            return 0
+        }
+    }
+}
+
+public struct PurchaseProduct {
+    public let productId: String
+    public let period: PremiumPeriod
+    
+    public init(productId: String, period: PremiumPeriod) {
+        self.productId = productId
+        self.period = period
+    }
+}
+
+public enum PremiumRequestState {
+    case none
+    
+    case loading
+    case loadSuccess
+    case loadFail
+    
+    case paying
+    case paySuccess
+    case payFail
+    
+    case restoreing
+    case restoreSuccess
+    case restoreFail
+    
+    case verifying
+    case verifySuccess
+    case verifyFail
+}
+
+
+public extension Notification.Name {
+    static let kPurchasePrepared = Self.init("kPurchaseProductPrepared")
+    static let kPurchaseDidChanged = Self.init("kPurchaseDidChanged")
+    static let kVipFreeNumChanged = Notification.Name("kVipFreeNumChanged")   //Vip免费次数发生变化
+    
+}
+
+
+
+

+ 5 - 105
TSLiveWallpaper/Common/Purchase/TSPurchaseManager.swift

@@ -8,69 +8,17 @@
 import Foundation
 import StoreKit
 
-public enum PremiumPeriod: String, CaseIterable {
-    case none           = ""
-    case month          = "Monthly"
-    case year           = "Yearly"
-    case lifetime       = "Lifetime"
-}
-
-public struct PurchaseProduct {
-    public let productId: String
-    public let period: PremiumPeriod
-    
-    public init(productId: String, period: PremiumPeriod) {
-        self.productId = productId
-        self.period = period
-    }
-}
-
-public enum PremiumRequestState {
-    case none
-    
-    case loading
-    case loadSuccess
-    case loadFail
-    
-    case paying
-    case paySuccess
-    case payFail
-    
-    case restoreing
-    case restoreSuccess
-    case restoreFail
-    
-    case verifying
-    case verifySuccess
-    case verifyFail
-}
-
-
-public extension Notification.Name {
-    static let kPurchasePrepared = Self.init("kPurchaseProductPrepared")
-    static let kPurchaseDidChanged = Self.init("kPurchaseDidChanged")
-}
-
-private let kFreeNumKey = "kFreeNumKey"
-private let kPremiumExpiredInfoKey = "premiumExpiredInfoKey"
-
-
 typealias PurchaseStateChangeHandler = (_ manager: PurchaseManager, _ state: PremiumRequestState, _ object: Any?) -> Void
+private let kPremiumExpiredInfoKey = "premiumExpiredInfoKey"
 
-let kPurchaseDefault = PurchaseManager.default
 public class PurchaseManager: NSObject {
     @objc public static let `default` = PurchaseManager()
 
     //苹果共享密钥
-    private let AppleSharedKey:String = "155c8104e2b041c0abae43ace199124c"
+    var password:String = "155c8104e2b041c0abae43ace199124c"
     
     //商品信息
-    public lazy var purchaseProducts:[PurchaseProduct] = {
-        return [PurchaseProduct(productId: "1001", period:.month),
-                PurchaseProduct(productId: "1002", period: .year),
-                PurchaseProduct(productId: "003", period: .lifetime),
-        ]
-    }()
+    public var purchaseProducts:[PurchaseProduct] = []
 
     struct Config {
         static let verifyUrl = "https://buy.itunes.apple.com/verifyReceipt"
@@ -83,14 +31,9 @@ public class PurchaseManager: NSObject {
 
     // 会员信息
     var vipInformation: [String: Any] = [:]
-
-    
-    // 免费使用会员转 livew的次数
-    var freeNum:Int = 0
-    
     //原始订单交易id dict
     var originalTransactionIdentifierDict:[String:String] = [:]
-    
+
     override init() {
         super.init()
 
@@ -99,8 +42,6 @@ public class PurchaseManager: NSObject {
         if let info = UserDefaults.standard.object(forKey: kPremiumExpiredInfoKey) as? [String: Any] {
             vipInformation = info
         }
-        
-        initializeForFree()
     }
 
     public var expiredDate: Date? {
@@ -394,7 +335,7 @@ extension PurchaseManager {
 
         let requestContents = [
             "receipt-data": receiptData.base64EncodedString(),
-            "password": AppleSharedKey,
+            "password": password,
         ]
         guard let requestData = try? JSONSerialization.data(withJSONObject: requestContents) else {
             purchase(self, didChaged: .verifyFail, object: "凭证文件为空")
@@ -498,47 +439,6 @@ public extension PurchaseManager {
 }
 
 
-/// 免费次数
-extension PurchaseManager {
-    /// 使用一次免费次数
-    func useOnceForFree(){
-        if freeNum > 0 {
-            freeNum-=1
-        }else{
-            freeNum=0
-        }
-        saveForFree()
-    }
-    
-    
-    func saveForFree(){
-        UserDefaults.standard.set(String(freeNum), forKey: kFreeNumKey)
-        UserDefaults.standard.synchronize()
-    }
-    
-    func initializeForFree(){
-        if let num = UserDefaults.standard.string(forKey: kFreeNumKey) {
-            freeNum = Int(num) ?? 3
-        }else{
-            freeNum = 3
-            saveForFree()
-        }
-    }
-    
-    /// 免费次数是否可用
-    func freeNumAvailable() -> Bool{
-        if isVip == true {
-            return true
-        }else{
-            if freeNum <= 0 {
-                return false
-            }else{
-                return true
-            }
-        }
-    }
-}
-
 /*
  
  首先,创建SKProductsRequest对象并使用init(productIdentifiers:)初始化,传入要查询的产品标识符。

+ 386 - 386
TSLiveWallpaper/Common/TSNetWork/TSNetWork+Business.swift

@@ -5,389 +5,389 @@
 //  Created by 100Years on 2025/1/16.
 //
 
-///// 基础 URL(根据需求修改)
-//private let baseURL = "http://ai.100yearslater.com"
-//private let baseChinaURL = "http://ai.100yearslater100.com"
-//import Alamofire
-//enum TSNeURLType:String {
-//    
-//    case imageEmoji = "/api/image/emoji"         //文生emoji
-//    case actionInfo = "/api/action/info"         //查询生成过程接口
-//    case chat = "/api/text/chat"                 //AI 对话接口
-//    case textPicCreate = "/api/image/create"     //文生图
-//    case upload = "/api/upload"                  //上传图片
-//    case imageRewrite = "/api/image/rewrite"     //图生图
-//    case imageToImageStyle = "/api/ops/aichat-img2img-config"     //图生图 风格列表
-//    case chatV2 = "/api/text/chat/v2"             //AI 对话接口V2,扩展了 DeepSeek 深度思考
-//    
-//    case config = "/api/ops/aichat-config"       //App配置
-//    
-//    case changeAge = "/api/image/change-age"       //换年龄
-//    case subscriptionApple = "/api/subscription/apple"       //苹果订阅
-//    case changeEmotion = "/api/image/change-emotion"       //更换表情
-//    case changeHair = "/api/image/change-hair"       //更换头发
-//    case imageRestore = "/api/image/restore"          //老照片修复
-//    case eyeOpen = "/api/image/eye-open"          //睁眼
-//    case pretty = "/api/image/pretty"          //美容
-//    case photoAnimation = "/api/image/animation"          //照片变活
-//    case photoExpand = "/api/image/outpaint"          //照片扩展
-//    
-//    case overResolution = "/api/image/over-resolution"          //图片超分辨率
-//    case changeClothes = "/api/image/change-clothes"          //换衣服
-//
-//    
-//    func getUrlString() -> String {
-//        if Locale.current.identifier.contains("_CN") {//中国区
-//            return baseChinaURL + self.rawValue
-//        }else{
-//            return baseURL + self.rawValue
-//        }
-//    }
-//    
-//    var needValidate : Bool {
-//        return validateURLTypeList.contains(self)
-//    }
-//    
-//    ///需要进行次数验证的接口
-//    var validateURLTypeList : [TSNeURLType] {
-//        [.textPicCreate,
-//         .upload,
-//         .imageRewrite,
-//         .changeAge,
-//         .subscriptionApple,
-//         .changeEmotion,
-//         .changeHair,
-//         .imageRestore,
-//         .eyeOpen,
-//         .pretty,
-//         .photoAnimation,
-//         .photoExpand,
-//         .overResolution,
-//         .changeClothes]
-//    }
-//}
-//
-//
-//
-////let kGenerateFailed:String = "Failed to generate, please try later".localized
-//let kGenerateFailed:String = "Sorry there was a slight problem with the image processing, please try again later.".localized
-//
-//enum TSNetWorkCode : Int {
-//    case success = 200
-//    case fail = 0   //通用错误
-//    case textSensitive = -10003    //文生图敏感错误
-//    case imageSensitive = -10004   //图生图敏感错误
-//    case networkError = -1005   //网络错误
-//    case generateTooMuch = -200   //单日生成次数过多
-//    
-//    var errorMsg:String {
-//        switch self {
-//        case .textSensitive,.imageSensitive:
-//            return "Your photo may contain copyright infringement, nudity, gore or violence that does not comply with the Health Policy, please replace the photo and try again.".localized
-//        case .networkError:
-//            return "No network, please check your network and try again.".localized
-//        case .generateTooMuch:
-//            return "We've detected unusually high generation activity. You may be a bot. Please try again tomorrow.".localized
-//        default:
-//            return "Sorry there was a slight problem with the image processing, please try again later.".localized
-//        }
-//        
-//    }
-//    
-//    //获取生成错误 code 对应的文案
-//    static func errorMsg(code:Int)->String{
-//        let netCode = TSNetWorkCode(rawValue: code) ?? .fail
-//        return netCode.errorMsg
-//    }
-//    
-//    //敏感错误
-//    static func sensitiveError(code:Int)->Bool{
-//        let netCode = TSNetWorkCode(rawValue: code)
-//        switch netCode {
-//        case .textSensitive,.imageSensitive:
-//            return true
-//        default:
-//            return false
-//        }
-//    }
-//    
-//    //网络错误错误
-//    static func networkError(code:Int)->Bool{
-//        let netCode = TSNetWorkCode(rawValue: code)
-//        switch netCode {
-//        case .networkError:
-//            return true
-//        default:
-//            return false
-//        }
-//    }
-//    
-//    //网络错误错误
-//    static func getErrorCode(_ error: Error) -> Int {
-//        if let urlError = error as? URLError {
-//            switch urlError.code {
-//            case .notConnectedToInternet, .networkConnectionLost:
-//                return TSNetWorkCode.networkError.rawValue
-//            default:
-//                return urlError.code.rawValue
-//            }
-//        }
-//        return 0
-//    }
-//    //获取生成错误 code 对应的文案
-//    static func getGeneratorStyle(code:Int)->TSGeneratorView.Style{
-//        let netCode = TSNetWorkCode(rawValue: code)
-//        switch netCode {
-//        case .textSensitive,.imageSensitive:
-//            return TSGeneratorView.Style.sensitiveError
-//        case .networkError:
-//            return TSGeneratorView.Style.netWorkError
-//        case .generateTooMuch:
-//            return TSGeneratorView.Style.generateTooMuch
-//        default:
-//            return TSGeneratorView.Style.generalError
-//        }
-//    }
-//}
-//
-//
-//extension Error {
-//    
-//    var tsCode:Int {
-//        if let error = self as? AFError, let underlyingError = error.underlyingError as? URLError {
-//            switch underlyingError.code {
-//            case .notConnectedToInternet, .networkConnectionLost,.timedOut:
-//                return TSNetWorkCode.networkError.rawValue
-//            default:
-//                return underlyingError.code.rawValue
-//            }
-//        }else
-//        if let urlError = self as? URLError {
-//            switch urlError.code {
-//            case .notConnectedToInternet, .networkConnectionLost,.timedOut:
-//                return TSNetWorkCode.networkError.rawValue
-//            default:
-//                return urlError.code.rawValue
-//            }
-//        }else
-//        if let error = self as? NSError {
-//            return error.code
-//        }
-//        return 0
-//    }
-//    
-//    var tsDesc:String {
-//        return TSNetWorkCode.errorMsg(code: tsCode)
-//    }
-//}
-//
-//func getUserInfoJsonString()->[String:Any] {
-//    let uuid: String
-//    let uuidUdKey = "my_UUID"
-//    if let saved = UserDefaults.standard.string(forKey: uuidUdKey),
-//       !saved.isEmpty {
-//        uuid = saved
-//    } else {
-//        let newUuid = UUID().uuidString
-//        UserDefaults.standard.set(newUuid, forKey: uuidUdKey)
-//        UserDefaults.standard.synchronize()
-//        uuid = newUuid
-//    }
-//    
-//    let dic:[String:Any] = [
-//        "device":UIDevice.current.modelName,
-//        "deviceId":uuid,
-//        "iosVersion":UIDevice.current.systemVersion,
-//        "appVersion":appShortVersion(),
-//        "subscriptionStatus":kPurchaseDefault.isVip ? "active" : "fallow",
-//    ]
-//
-//    return dic
-//}
-//
-//
-//func getLanguageCode()->String{
-//    if Locale.current.identifier.contains("zh-Hant"){
-//        return "zh-Hant"
-//    }
-//    if let languageCode = Locale.current.languageCode {
-////        print("当前设备语言简写: \(languageCode)") // 输出如 "en", "ja", "zh" 等
-//        return languageCode
-//    }
-//    return "en"
-//}
-//
-//
-//extension TSNetworkManager {
-//    
-//    /// 通用 get 请求
-//    func get<T: TSBaseModel>(
-//        urlType: TSNeURLType,
-//        parameters: [String: Any]? = nil,
-//        responseType: T.Type? = nil,
-//        completion: @escaping (Result<Any, Error>) -> Void
-//    ) -> Request  {
-//        let urlString = urlType.getUrlString()
-//        return request(method: .get, urlString: urlString, parameters:parameters) { result in
-//            completion(result)
-//        }
-//    }
-//
-//
-//    /// 通用 POST 请求
-//    /// - Parameters:
-//    ///   - endpoint: 接口路径
-//    ///   - parameters: 请求参数
-//    ///   - responseType: 响应数据模型(可选)
-//    ///   - completion: 请求完成的回调
-//    func post<T: TSBaseModel>(
-//        urlType: TSNeURLType,
-//        parameters: [String: Any]? = nil,
-//        responseType: T.Type? = nil,
-//        completion: @escaping (Result<Any, Error>) -> Void
-//    ) -> Request? {
-//        ///需要校验。且需要判断是否超过最大次数
-//        if urlType.needValidate,PurchaseManager.default.isOverTotalTimes {
-//            completion(.failure(NSError(domain: "", code: TSNetWorkCode.generateTooMuch.rawValue)))
-//            return nil
-//        }
-//        
-//        let urlString = urlType.getUrlString()
-//        return request(method: .post, urlString: urlString, parameters:parameters) { result in
-//            completion(result)
-//        }
-//    }
-//    
-//    /// 通用 POST Stream 请求
-//    /// - Parameters:
-//    ///   - endpoint: 接口路径
-//    ///   - parameters: 请求参数
-//    ///   - responseType: 响应数据模型(可选)
-//    ///   - completion: 请求完成的回调
-//    func postStream<T: TSBaseModel>(
-//        urlType: TSNeURLType,
-//        parameters: [String: Any]? = nil,
-//        responseType: T.Type? = nil,
-//        streamHandler:@escaping (String) -> Void,
-//        completion: @escaping (Result<Any, Error>) -> Void
-//    ) -> Request?{
-//        let urlString = urlType.getUrlString()
-//        let streamRequest = postStreamRequest(urlString: urlString, parameters: parameters, streamHandler: streamHandler, completion: completion)
-//        return streamRequest
-//    }
-//
-//    
-//    /// 上传多个 Data 数据
-//    /// - Parameters:
-//    ///   - urlType: TSNeURLType
-//    ///   - dataArray: Data 数组,每个元素是一个字典,包含 Data 和字段名
-//    ///   - parameters: 其他参数(可选)
-//    ///   - headers: 自定义请求头(可选)
-//    ///   - completion: 完成回调,返回结果或错误
-//    func uploadData<T: TSBaseModel>(
-//        urlType: TSNeURLType,
-//        dataArray: [[String: Any]], // Data 数组,每个元素包含 Data 和字段名
-//        parameters: [String: Any]? = nil,
-//        responseType: T.Type? = nil,
-//        progressHandler: @escaping (Float) -> Void, // 上传进度回调
-//        completion: @escaping (Result<Any, Error>) -> Void
-//    ) -> Request?{
-//        let urlString = urlType.getUrlString()
-//        let request = uploadData(urlString: urlString,dataArray:dataArray, parameters: parameters, progressHandler: { progress in
-//            progressHandler(Float(progress.fractionCompleted))
-//        },completion: completion)
-//        return request
-//    }
-//    
-//    func downloadFile(
-//        urlString: String,
-//        to destination: URL,
-//        progressHandler: ((Double) -> Void)? = nil,
-//        completion: @escaping (URL?, Error?) -> Void
-//    ) -> DownloadRequest? {
-//        let request = self.downloadFile(
-//            urlString: urlString,
-//            to: destination,
-//            progressHandler: { progress in
-//                print("下载进度: \(progress * 100)%")
-//                progressHandler?(progress)
-//            },
-//            completion: { result in
-//                switch result {
-//                case .success(let fileURL):
-//                    dePrint("下载完成,文件保存在: \(fileURL.path)")
-//                    completion(fileURL,nil)
-//                case .failure(let error):
-//                    dePrint("下载失败: \(error.localizedDescription)")
-//                    completion(nil,error)
-//                }
-//            }
-//        )
-//        return request
-//    }
-//
-//}
-//
-//extension TSNetworkManager {
-//    
-//    func uploadImage(
-//        upLoadImage:UIImage,
-//        maxKb:Int,
-//        progressHandler: @escaping (Float) -> Void, // 上传进度回调
-//        completion: @escaping (Any?, Error?) -> Void)
-//    -> Request?{
-//        
-//        ///需要校验。且需要判断是否超过最大次数
-//        if PurchaseManager.default.isOverTotalTimes {
-//            completion(nil,NSError(domain: "", code: TSNetWorkCode.generateTooMuch.rawValue))
-//            return nil
-//        }
-//        
-//        guard let imageData = TSImageCompress.compressImageToTargetSize(upLoadImage, targetSizeKB: maxKb, preserveTransparency: false) else {
-//            completion(nil,NSError(domain: "image nil", code: 0))
-//            return nil
-//        }
-//        
-//        var dataArray:[[String:Any]] = []
-//        if upLoadImage.hasAlphaChannel(){
-//            dataArray = [
-//                ["data": imageData,
-//                 "fieldName": "file", // 字段名
-//                 "fileName": "image.png", // 文件名
-//                 "mimeType": "image/png" // MIME 类型
-//                ]
-//            ]
-//        }else {
-//            dataArray = [
-//                ["data": imageData,
-//                 "fieldName": "file", // 字段名
-//                 "fileName": "image.jpeg", // 文件名
-//                 "mimeType": "image/jpeg" // MIME 类型
-//                ]
-//            ]
-//        }
-//
-//        return TSNetworkShared.uploadData(
-//            urlType: .upload,
-//            dataArray: dataArray,
-//            progressHandler: { progress in
-//                progressHandler(progress)
-//            },completion: { [weak self] result in
-//            guard let self = self else { return }
-//            switch result {
-//            case .success(let data):
-//                if let dataDict = kNetWorkCodeSuccess(data: data),
-//                   let picUrl = dataDict["result"] as? String{
-//                    completion(picUrl,nil)
-//                }else{
-//                    let error = NSError(domain: "Service exception", code: 0)
-//                    completion(nil,error)
-//                }
-//            case .failure(let error):
-//                completion(nil,error)
-//            }
-//        })
-//    }
-//    
-//}
-//
+/// 基础 URL(根据需求修改)
+private let baseURL = "http://ai.100yearslater.com"
+private let baseChinaURL = "http://ai.100yearslater100.com"
+import Alamofire
+enum TSNeURLType:String {
+    
+    case imageEmoji = "/api/image/emoji"         //文生emoji
+    case actionInfo = "/api/action/info"         //查询生成过程接口
+    case chat = "/api/text/chat"                 //AI 对话接口
+    case textPicCreate = "/api/image/create"     //文生图
+    case upload = "/api/upload"                  //上传图片
+    case imageRewrite = "/api/image/rewrite"     //图生图
+    case imageToImageStyle = "/api/ops/aichat-img2img-config"     //图生图 风格列表
+    case chatV2 = "/api/text/chat/v2"             //AI 对话接口V2,扩展了 DeepSeek 深度思考
+    
+    case config = "/api/ops/aichat-config"       //App配置
+    
+    case changeAge = "/api/image/change-age"       //换年龄
+    case subscriptionApple = "/api/subscription/apple"       //苹果订阅
+    case changeEmotion = "/api/image/change-emotion"       //更换表情
+    case changeHair = "/api/image/change-hair"       //更换头发
+    case imageRestore = "/api/image/restore"          //老照片修复
+    case eyeOpen = "/api/image/eye-open"          //睁眼
+    case pretty = "/api/image/pretty"          //美容
+    case photoAnimation = "/api/image/animation"          //照片变活
+    case photoExpand = "/api/image/outpaint"          //照片扩展
+    
+    case overResolution = "/api/image/over-resolution"          //图片超分辨率
+    case changeClothes = "/api/image/change-clothes"          //换衣服
+
+    
+    func getUrlString() -> String {
+        if Locale.current.identifier.contains("_CN") {//中国区
+            return baseChinaURL + self.rawValue
+        }else{
+            return baseURL + self.rawValue
+        }
+    }
+    
+    var needValidate : Bool {
+        return validateURLTypeList.contains(self)
+    }
+    
+    ///需要进行次数验证的接口
+    var validateURLTypeList : [TSNeURLType] {
+        [.textPicCreate,
+         .upload,
+         .imageRewrite,
+         .changeAge,
+         .subscriptionApple,
+         .changeEmotion,
+         .changeHair,
+         .imageRestore,
+         .eyeOpen,
+         .pretty,
+         .photoAnimation,
+         .photoExpand,
+         .overResolution,
+         .changeClothes]
+    }
+}
+
+
+
+//let kGenerateFailed:String = "Failed to generate, please try later".localized
+let kGenerateFailed:String = "Sorry there was a slight problem with the image processing, please try again later.".localized
+
+enum TSNetWorkCode : Int {
+    case success = 200
+    case fail = 0   //通用错误
+    case textSensitive = -10003    //文生图敏感错误
+    case imageSensitive = -10004   //图生图敏感错误
+    case networkError = -1005   //网络错误
+    case generateTooMuch = -200   //单日生成次数过多
+    
+    var errorMsg:String {
+        switch self {
+        case .textSensitive,.imageSensitive:
+            return "Your photo may contain copyright infringement, nudity, gore or violence that does not comply with the Health Policy, please replace the photo and try again.".localized
+        case .networkError:
+            return "No network, please check your network and try again.".localized
+        case .generateTooMuch:
+            return "We've detected unusually high generation activity. You may be a bot. Please try again tomorrow.".localized
+        default:
+            return "Sorry there was a slight problem with the image processing, please try again later.".localized
+        }
+        
+    }
+    
+    //获取生成错误 code 对应的文案
+    static func errorMsg(code:Int)->String{
+        let netCode = TSNetWorkCode(rawValue: code) ?? .fail
+        return netCode.errorMsg
+    }
+    
+    //敏感错误
+    static func sensitiveError(code:Int)->Bool{
+        let netCode = TSNetWorkCode(rawValue: code)
+        switch netCode {
+        case .textSensitive,.imageSensitive:
+            return true
+        default:
+            return false
+        }
+    }
+    
+    //网络错误错误
+    static func networkError(code:Int)->Bool{
+        let netCode = TSNetWorkCode(rawValue: code)
+        switch netCode {
+        case .networkError:
+            return true
+        default:
+            return false
+        }
+    }
+    
+    //网络错误错误
+    static func getErrorCode(_ error: Error) -> Int {
+        if let urlError = error as? URLError {
+            switch urlError.code {
+            case .notConnectedToInternet, .networkConnectionLost:
+                return TSNetWorkCode.networkError.rawValue
+            default:
+                return urlError.code.rawValue
+            }
+        }
+        return 0
+    }
+    //获取生成错误 code 对应的文案
+    static func getGeneratorStyle(code:Int)->TSGeneratorView.Style{
+        let netCode = TSNetWorkCode(rawValue: code)
+        switch netCode {
+        case .textSensitive,.imageSensitive:
+            return TSGeneratorView.Style.sensitiveError
+        case .networkError:
+            return TSGeneratorView.Style.netWorkError
+        case .generateTooMuch:
+            return TSGeneratorView.Style.generateTooMuch
+        default:
+            return TSGeneratorView.Style.generalError
+        }
+    }
+}
+
+
+extension Error {
+    
+    var tsCode:Int {
+        if let error = self as? AFError, let underlyingError = error.underlyingError as? URLError {
+            switch underlyingError.code {
+            case .notConnectedToInternet, .networkConnectionLost,.timedOut:
+                return TSNetWorkCode.networkError.rawValue
+            default:
+                return underlyingError.code.rawValue
+            }
+        }else
+        if let urlError = self as? URLError {
+            switch urlError.code {
+            case .notConnectedToInternet, .networkConnectionLost,.timedOut:
+                return TSNetWorkCode.networkError.rawValue
+            default:
+                return urlError.code.rawValue
+            }
+        }else
+        if let error = self as? NSError {
+            return error.code
+        }
+        return 0
+    }
+    
+    var tsDesc:String {
+        return TSNetWorkCode.errorMsg(code: tsCode)
+    }
+}
+
+func getUserInfoJsonString()->[String:Any] {
+    let uuid: String
+    let uuidUdKey = "my_UUID"
+    if let saved = UserDefaults.standard.string(forKey: uuidUdKey),
+       !saved.isEmpty {
+        uuid = saved
+    } else {
+        let newUuid = UUID().uuidString
+        UserDefaults.standard.set(newUuid, forKey: uuidUdKey)
+        UserDefaults.standard.synchronize()
+        uuid = newUuid
+    }
+    
+    let dic:[String:Any] = [
+        "device":UIDevice.current.modelName,
+        "deviceId":uuid,
+        "iosVersion":UIDevice.current.systemVersion,
+        "appVersion":appShortVersion(),
+        "subscriptionStatus":kPurchaseBusiness.isVip ? "active" : "fallow",
+    ]
+
+    return dic
+}
+
+
+func getLanguageCode()->String{
+    if Locale.current.identifier.contains("zh-Hant"){
+        return "zh-Hant"
+    }
+    if let languageCode = Locale.current.languageCode {
+//        print("当前设备语言简写: \(languageCode)") // 输出如 "en", "ja", "zh" 等
+        return languageCode
+    }
+    return "en"
+}
+
+
+extension TSNetworkManager {
+    
+    /// 通用 get 请求
+    func get<T: TSBaseModel>(
+        urlType: TSNeURLType,
+        parameters: [String: Any]? = nil,
+        responseType: T.Type? = nil,
+        completion: @escaping (Result<Any, Error>) -> Void
+    ) -> Request  {
+        let urlString = urlType.getUrlString()
+        return request(method: .get, urlString: urlString, parameters:parameters) { result in
+            completion(result)
+        }
+    }
+
+
+    /// 通用 POST 请求
+    /// - Parameters:
+    ///   - endpoint: 接口路径
+    ///   - parameters: 请求参数
+    ///   - responseType: 响应数据模型(可选)
+    ///   - completion: 请求完成的回调
+    func post<T: TSBaseModel>(
+        urlType: TSNeURLType,
+        parameters: [String: Any]? = nil,
+        responseType: T.Type? = nil,
+        completion: @escaping (Result<Any, Error>) -> Void
+    ) -> Request? {
+        ///需要校验。且需要判断是否超过最大次数
+        if urlType.needValidate,kPurchaseBusiness.isOverTotalTimes {
+            completion(.failure(NSError(domain: "", code: TSNetWorkCode.generateTooMuch.rawValue)))
+            return nil
+        }
+        
+        let urlString = urlType.getUrlString()
+        return request(method: .post, urlString: urlString, parameters:parameters) { result in
+            completion(result)
+        }
+    }
+    
+    /// 通用 POST Stream 请求
+    /// - Parameters:
+    ///   - endpoint: 接口路径
+    ///   - parameters: 请求参数
+    ///   - responseType: 响应数据模型(可选)
+    ///   - completion: 请求完成的回调
+    func postStream<T: TSBaseModel>(
+        urlType: TSNeURLType,
+        parameters: [String: Any]? = nil,
+        responseType: T.Type? = nil,
+        streamHandler:@escaping (String) -> Void,
+        completion: @escaping (Result<Any, Error>) -> Void
+    ) -> Request?{
+        let urlString = urlType.getUrlString()
+        let streamRequest = postStreamRequest(urlString: urlString, parameters: parameters, streamHandler: streamHandler, completion: completion)
+        return streamRequest
+    }
+
+    
+    /// 上传多个 Data 数据
+    /// - Parameters:
+    ///   - urlType: TSNeURLType
+    ///   - dataArray: Data 数组,每个元素是一个字典,包含 Data 和字段名
+    ///   - parameters: 其他参数(可选)
+    ///   - headers: 自定义请求头(可选)
+    ///   - completion: 完成回调,返回结果或错误
+    func uploadData<T: TSBaseModel>(
+        urlType: TSNeURLType,
+        dataArray: [[String: Any]], // Data 数组,每个元素包含 Data 和字段名
+        parameters: [String: Any]? = nil,
+        responseType: T.Type? = nil,
+        progressHandler: @escaping (Float) -> Void, // 上传进度回调
+        completion: @escaping (Result<Any, Error>) -> Void
+    ) -> Request?{
+        let urlString = urlType.getUrlString()
+        let request = uploadData(urlString: urlString,dataArray:dataArray, parameters: parameters, progressHandler: { progress in
+            progressHandler(Float(progress.fractionCompleted))
+        },completion: completion)
+        return request
+    }
+    
+    func downloadFile(
+        urlString: String,
+        to destination: URL,
+        progressHandler: ((Double) -> Void)? = nil,
+        completion: @escaping (URL?, Error?) -> Void
+    ) -> DownloadRequest? {
+        let request = self.downloadFile(
+            urlString: urlString,
+            to: destination,
+            progressHandler: { progress in
+                print("下载进度: \(progress * 100)%")
+                progressHandler?(progress)
+            },
+            completion: { result in
+                switch result {
+                case .success(let fileURL):
+                    dePrint("下载完成,文件保存在: \(fileURL.path)")
+                    completion(fileURL,nil)
+                case .failure(let error):
+                    dePrint("下载失败: \(error.localizedDescription)")
+                    completion(nil,error)
+                }
+            }
+        )
+        return request
+    }
+
+}
+
+extension TSNetworkManager {
+    
+    func uploadImage(
+        upLoadImage:UIImage,
+        maxKb:Int,
+        progressHandler: @escaping (Float) -> Void, // 上传进度回调
+        completion: @escaping (Any?, Error?) -> Void)
+    -> Request?{
+        
+        ///需要校验。且需要判断是否超过最大次数
+        if kPurchaseBusiness.isOverTotalTimes {
+            completion(nil,NSError(domain: "", code: TSNetWorkCode.generateTooMuch.rawValue))
+            return nil
+        }
+        
+        guard let imageData = TSImageCompress.compressImageToTargetSize(upLoadImage, targetSizeKB: maxKb, preserveTransparency: false) else {
+            completion(nil,NSError(domain: "image nil", code: 0))
+            return nil
+        }
+        
+        var dataArray:[[String:Any]] = []
+        if upLoadImage.hasAlphaChannel(){
+            dataArray = [
+                ["data": imageData,
+                 "fieldName": "file", // 字段名
+                 "fileName": "image.png", // 文件名
+                 "mimeType": "image/png" // MIME 类型
+                ]
+            ]
+        }else {
+            dataArray = [
+                ["data": imageData,
+                 "fieldName": "file", // 字段名
+                 "fileName": "image.jpeg", // 文件名
+                 "mimeType": "image/jpeg" // MIME 类型
+                ]
+            ]
+        }
+
+        return TSNetworkShared.uploadData(
+            urlType: .upload,
+            dataArray: dataArray,
+            progressHandler: { progress in
+                progressHandler(progress)
+            },completion: { [weak self] result in
+            guard let self = self else { return }
+            switch result {
+            case .success(let data):
+                if let dataDict = kNetWorkCodeSuccess(data: data),
+                   let picUrl = dataDict["result"] as? String{
+                    completion(picUrl,nil)
+                }else{
+                    let error = NSError(domain: "Service exception", code: 0)
+                    completion(nil,error)
+                }
+            case .failure(let error):
+                completion(nil,error)
+            }
+        })
+    }
+    
+}
+

+ 80 - 80
TSLiveWallpaper/Common/TSNetWork/TSNetworkManager+Loading.swift

@@ -4,83 +4,83 @@
 //
 //  Created by 100Years on 2025/1/16.
 //
-//import Alamofire
-//extension TSNetworkManager {
-//    
-//    /// 通用 get 请求
-//    func get<T: TSBaseModel>(
-//        urlType: TSNeURLType,
-//        parameters: [String: Any]? = nil,
-//        responseType: T.Type? = nil,
-//        animationView:UIView? = nil,
-//        completion: @escaping (Any?, Error?) -> Void
-//    ) -> Request {
-//        
-//        var isShowAnimation = false
-//        if animationView != nil {
-//            isShowAnimation = true
-//        }
-//        
-//        if isShowAnimation,let view = animationView {
-//            TSToastShared.showLoading(containerView: view)
-//        }
-//
-//        return get(urlType: urlType, parameters:parameters,responseType:responseType) { result in
-//            
-//            if isShowAnimation {
-//                TSToastShared.hideLoading()
-//            }
-//    
-//            switch result {
-//            case .success(let data):
-////                print("Response data: \(data)")
-//                completion(data,nil)
-//            case .failure(let error):
-////                print("Error: \(error)")
-//                completion(nil,error)
-//            }
-//        }
-//    }
-//
-//
-//    /// 通用 POST 请求
-//    /// - Parameters:
-//    ///   - endpoint: 接口路径
-//    ///   - parameters: 请求参数
-//    ///   - responseType: 响应数据模型(可选)
-//    ///   - completion: 请求完成的回调
-//    func post<T: TSBaseModel>(
-//        urlType: TSNeURLType,
-//        parameters: [String: Any]? = nil,
-//        responseType: T.Type? = nil,
-//        animationView:UIView? = nil,
-//        completion: @escaping (Any?, Error?) -> Void
-//    ) -> Request? {
-//        var isShowAnimation = false
-//        if animationView != nil {
-//            isShowAnimation = true
-//        }
-//        
-//        if isShowAnimation,let view = animationView {
-//            TSToastShared.showLoading(containerView: view)
-//        }
-//
-//        return post(urlType: urlType, parameters:parameters,responseType:responseType){ result in
-//            
-//            if isShowAnimation {
-//                TSToastShared.hideLoading()
-//            }
-//    
-//            switch result {
-//            case .success(let data):
-////                print("Response data: \(data)")
-//                completion(data,nil)
-//            case .failure(let error):
-////                print("Error: \(error)")
-//                completion(nil,error)
-//            }
-//        }
-//    
-//    }
-//    
-//}
+import Alamofire
+extension TSNetworkManager {
+    
+    /// 通用 get 请求
+    func get<T: TSBaseModel>(
+        urlType: TSNeURLType,
+        parameters: [String: Any]? = nil,
+        responseType: T.Type? = nil,
+        animationView:UIView? = nil,
+        completion: @escaping (Any?, Error?) -> Void
+    ) -> Request {
+        
+        var isShowAnimation = false
+        if animationView != nil {
+            isShowAnimation = true
+        }
+        
+        if isShowAnimation,let view = animationView {
+            TSToastShared.showLoading(containerView: view)
+        }
+
+        return get(urlType: urlType, parameters:parameters,responseType:responseType) { result in
+            
+            if isShowAnimation {
+                TSToastShared.hideLoading()
+            }
+    
+            switch result {
+            case .success(let data):
+//                print("Response data: \(data)")
+                completion(data,nil)
+            case .failure(let error):
+//                print("Error: \(error)")
+                completion(nil,error)
+            }
+        }
+    }
+
+
+    /// 通用 POST 请求
+    /// - Parameters:
+    ///   - endpoint: 接口路径
+    ///   - parameters: 请求参数
+    ///   - responseType: 响应数据模型(可选)
+    ///   - completion: 请求完成的回调
+    func post<T: TSBaseModel>(
+        urlType: TSNeURLType,
+        parameters: [String: Any]? = nil,
+        responseType: T.Type? = nil,
+        animationView:UIView? = nil,
+        completion: @escaping (Any?, Error?) -> Void
+    ) -> Request? {
+        var isShowAnimation = false
+        if animationView != nil {
+            isShowAnimation = true
+        }
+        
+        if isShowAnimation,let view = animationView {
+            TSToastShared.showLoading(containerView: view)
+        }
+
+        return post(urlType: urlType, parameters:parameters,responseType:responseType){ result in
+            
+            if isShowAnimation {
+                TSToastShared.hideLoading()
+            }
+    
+            switch result {
+            case .success(let data):
+//                print("Response data: \(data)")
+                completion(data,nil)
+            case .failure(let error):
+//                print("Error: \(error)")
+                completion(nil,error)
+            }
+        }
+    
+    }
+    
+}

+ 398 - 398
TSLiveWallpaper/Common/TSNetWork/TSNetworkManager.swift

@@ -9,409 +9,409 @@ import Alamofire
 import ObjectMapper
 
 
-//let TSNetworkShared = TSNetworkManager.shared
+let TSNetworkShared = TSNetworkManager.shared
 
-///// 网络工具类
-//class TSNetworkManager {
-//    
-//    static let shared = TSNetworkManager()
-//    private init() {}
-//    
-//    /// 通用 Headers
-//    private var defaultHeaders: HTTPHeaders {
-//        return ["Content-Type": "application/json",
-//                "accept": "application/json",
-////            "Authorization": "Bearer YOUR_ACCESS_TOKEN" // 按需修改
-//        ]
-//    }
-//    
-//    lazy var afSession: Session = {
-////        let configuration = URLSessionConfiguration.af.default
-////        configuration.timeoutIntervalForRequest = 15  // 请求超时时间(秒)
-////        configuration.timeoutIntervalForResource = 30 // 资源超时时间(秒)
-////        let session = Session(configuration: configuration)
-////        return session
-//        
-//        return AF
-//    }()
-//    lazy var encoder: JSONEncoding = {
-//        return JSONEncoding(options: .withoutEscapingSlashes)// 关键:禁用斜杠转义
-//    }()
-//
-//    func postStreamRequest(
-//        urlString: String,
-//        parameters: [String: Any]? = nil,
-//        streamHandler:@escaping (String) -> Void,
-//        completion: @escaping (Result<Any, Error>) -> Void
-//    )-> Request? {
-//        
-//        guard let url = URL(string: urlString) else {
-//            completion(.failure(NSError(domain: "url nil", code: 0)))
-//            return nil
-//        }
-//        
-//        // 1. 创建 URLRequest
-//        var urlRequest = URLRequest(url:URL(string: urlString)!)
-//        urlRequest.httpMethod = "POST"
-//        urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
-//        
-//        // 2. 将字典参数转换为 JSON 数据并设置为请求体
-//        do {
-//            let jsonData = try JSONSerialization.data(withJSONObject: parameters, options: [.withoutEscapingSlashes])//去掉转义
-//            urlRequest.httpBody = jsonData
-//        } catch {
-//            dePrint("Failed to encode parameters: \(error)")
-//            completion(.failure(error))
-//            return nil
-//        }
-//        
-//        // 3. 使用 streamRequest 发起流式请求
-//        let request = afSession.streamRequest(urlRequest)
-//        request.responseStreamString{ stream in
-//            switch stream.event {
-//            case .stream(let result):
-//                switch result {
-//                case .success(let string):
-//                    dePrint("🚰🚰🚰Stream Received string string: \(string)")
-//                    streamHandler(string)
-//                case .failure(let error):
-//                    dePrint("Stream error: \(error)")
-//                    completion(.failure(error))
-//                }
-//            case .complete(let cpl):
-//                if let error = cpl.error {
-//                    dePrint("Stream Request failed with error: \(error)")
-//                    completion(.failure(error))
-//                } else {
-//                    dePrint("Stream success")
-//                    completion(.success("Stream success"))
-//                }
-//            }
-//        }
-//        return request
-//    }
-//
-//    func request(
-//        method:HTTPMethod,
-//        urlString: String,
-//        parameters: [String: any Any & Sendable]? = nil,
-//        completion: @escaping (Result<Any, Error>) -> Void
-//    ) -> Request{
-//        dePrint("✈️✈️✈️网络请求:\(urlString)")
-//        dePrint("✈️✈️✈️参数:\(String(describing: parameters))")
-//        
-//        var encoding: ParameterEncoding = URLEncoding.default
-//        if method == .post {
-//            encoding = encoder
-//        }
-//        let request = afSession.request(urlString, method: method, parameters: parameters, encoding: encoding, headers: defaultHeaders, interceptor: nil)
-//        request.responseString { response in
-//                self.handleResponse(response, completion: completion)
-//            }
-//        return request
-//    }
-//    
-//    
-//
-//    
-//}
-//
-//extension TSNetworkManager {
-//    
-//    
-//    /*
-//     // Data 数组
-//     let dataArray: [[String: Any]] = [
-//         [
-//             "data": imageData,
-//             "fieldName": "file1", // 字段名
-//             "fileName": "App-Icon.png", // 文件名
-//             "mimeType": "image/png" // MIME 类型
-//         ],
-//         [
-//             "data": textData,
-//             "fieldName": "file2", // 字段名
-//             "fileName": "hello.txt", // 文件名
-//             "mimeType": "text/plain" // MIME 类型
-//         ]
-//     ]
-//     */
-//    
-//    /// 上传多个 Data 数据
-//    /// - Parameters:
-//    ///   - url: 上传地址
-//    ///   - dataArray: Data 数组,每个元素是一个字典,包含 Data 和字段名
-//    ///   - parameters: 其他参数(可选)
-//    ///   - headers: 自定义请求头(可选)
-//    ///   - completion: 完成回调,返回结果或错误
-//    func uploadData(
-//        urlString: String,
-//        dataArray: [[String: Any]], // Data 数组,每个元素包含 Data 和字段名
-//        parameters: [String: Any]? = nil, // 其他参数
-//        headers: HTTPHeaders? = nil, // 自定义请求头
-//        progressHandler: @escaping (Progress) -> Void, // 上传进度回调
-//        completion: @escaping (Result<Any, Error>) -> Void
-//    )-> Request? {
-//        
-//        guard let url = URL(string: urlString) else {
-//            completion(.failure(NSError(domain: "url nil", code: 0)))
-//            return nil
-//        }
-//        
-//        // 1. 设置默认请求头
-//        var defaultHeaders: HTTPHeaders = [
-//            "accept": "application/json",
-//            "Content-Type": "multipart/form-data"
-//        ]
-//        // 合并自定义请求头
-//        if let customHeaders = headers {
-//            customHeaders.forEach { defaultHeaders[$0.name] = $0.value }
-//        }
-//        
-//        dePrint("✈️✈️✈️网络请求:\(urlString)")
-//        dePrint("✈️✈️✈️dataArray:\(String(describing: dataArray))")
-//        dePrint("✈️✈️✈️参数:\(String(describing: parameters))")
-//        // 2. 使用 Alamofire 上传 Data
-//        let request = afSession.upload(
-//            multipartFormData: { multipartFormData in
-//                // 添加 Data
-//                for dataItem in dataArray {
-//                    if let data = dataItem["data"] as? Data,
-//                       let fieldName = dataItem["fieldName"] as? String,
-//                       let fileName = dataItem["fileName"] as? String,
-//                       let mimeType = dataItem["mimeType"] as? String {
-//                        multipartFormData.append(
-//                            data,
-//                            withName: fieldName,
-//                            fileName: fileName,
-//                            mimeType: mimeType
-//                        )
-//                    }
-//                }
-//
-//                // 添加其他参数
-//                if let parameters = parameters {
-//                    for (key, value) in parameters {
-//                        if let data = "\(value)".data(using: .utf8) {
-//                            multipartFormData.append(data, withName: key)
-//                        }
-//                    }
-//                }
-//            },
-//            to: url, // 上传地址
-//            headers: defaultHeaders // 请求头
-//        )
-//        
-//        request
-//            .uploadProgress{ progress in
-//                // 3. 上传进度回调
-//                // 上传进度回调
-//                dePrint("✈️✈️✈️进度: \(progress.fractionCompleted * 100)%")
-//                progressHandler(progress)
-//            }
-//            .responseString{ response in
-//                // 4. 处理响应
-//                self.handleResponse(response, completion: completion)
-//        }
-//        
-//        return request
-//    }
-//    
-//    
-//    /// 下载文件
-//    /// - Parameters:
-//    ///   - url: 下载URL
-//    ///   - destination: 目标保存路径 (可选,不传则使用临时目录)
-//    ///   - progressHandler: 进度回调 (0.0~1.0)
-//    ///   - completion: 完成回调 (返回文件URL或错误)
-//    func downloadFile(
-//        urlString: String,
-//        to destination: URL? = nil,
-//        progressHandler: ((Double) -> Void)? = nil,
-//        completion: @escaping (Result<URL, Error>) -> Void
-//    ) -> DownloadRequest? {
-//        
-//        
-//        guard let url = URL(string: urlString) else {
-//            completion(.failure(NSError(domain: "url nil", code: 0)))
-//            return nil
-//        }
-//        
-//        // 设置下载目标路径
-//        let destination: DownloadRequest.Destination = { temporaryURL, response in
-//            // 如果用户指定了目标路径
-//            if let destination = destination {
-//                // 确保目录存在
-//                let directory = destination.deletingLastPathComponent()
-//                try? FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true, attributes: nil)
-//                return (destination, [.removePreviousFile, .createIntermediateDirectories])
-//            }
-//            
-//            // 否则使用临时目录
-//            let documentsURL = FileManager.default.temporaryDirectory
-//            let suggestedFilename = response.suggestedFilename ?? url.lastPathComponent
-//            let fileURL = documentsURL.appendingPathComponent(suggestedFilename)
-//            
-//            return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
-//        }
-//        
-//        // 开始下载
-//        let request = afSession.download(url, to: destination)
-//            .downloadProgress { progress in
-//                // 主线程回调进度
-//                DispatchQueue.main.async {
-//                    progressHandler?(progress.fractionCompleted)
-//                }
-//            }
-//            .response { response in
-//                // 主线程回调结果
-//                DispatchQueue.main.async {
-//                    switch response.result {
-//                    case .success(let fileURL):
-//                        if let fileURL = fileURL {
-//                            completion(.success(fileURL))
-//                        } else {
-//                            completion(.failure(NSError(domain: "DownloadError", code: -1, userInfo: [NSLocalizedDescriptionKey: "文件路径无效"])))
-//                        }
-//                    case .failure(let error):
-//                        completion(.failure(error))
+/// 网络工具类
+class TSNetworkManager {
+    
+    static let shared = TSNetworkManager()
+    private init() {}
+    
+    /// 通用 Headers
+    private var defaultHeaders: HTTPHeaders {
+        return ["Content-Type": "application/json",
+                "accept": "application/json",
+//            "Authorization": "Bearer YOUR_ACCESS_TOKEN" // 按需修改
+        ]
+    }
+    
+    lazy var afSession: Session = {
+//        let configuration = URLSessionConfiguration.af.default
+//        configuration.timeoutIntervalForRequest = 15  // 请求超时时间(秒)
+//        configuration.timeoutIntervalForResource = 30 // 资源超时时间(秒)
+//        let session = Session(configuration: configuration)
+//        return session
+        
+        return AF
+    }()
+    lazy var encoder: JSONEncoding = {
+        return JSONEncoding(options: .withoutEscapingSlashes)// 关键:禁用斜杠转义
+    }()
+
+    func postStreamRequest(
+        urlString: String,
+        parameters: [String: Any]? = nil,
+        streamHandler:@escaping (String) -> Void,
+        completion: @escaping (Result<Any, Error>) -> Void
+    )-> Request? {
+        
+        guard let url = URL(string: urlString) else {
+            completion(.failure(NSError(domain: "url nil", code: 0)))
+            return nil
+        }
+        
+        // 1. 创建 URLRequest
+        var urlRequest = URLRequest(url:URL(string: urlString)!)
+        urlRequest.httpMethod = "POST"
+        urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
+        
+        // 2. 将字典参数转换为 JSON 数据并设置为请求体
+        do {
+            let jsonData = try JSONSerialization.data(withJSONObject: parameters, options: [.withoutEscapingSlashes])//去掉转义
+            urlRequest.httpBody = jsonData
+        } catch {
+            dePrint("Failed to encode parameters: \(error)")
+            completion(.failure(error))
+            return nil
+        }
+        
+        // 3. 使用 streamRequest 发起流式请求
+        let request = afSession.streamRequest(urlRequest)
+        request.responseStreamString{ stream in
+            switch stream.event {
+            case .stream(let result):
+                switch result {
+                case .success(let string):
+                    dePrint("🚰🚰🚰Stream Received string string: \(string)")
+                    streamHandler(string)
+                case .failure(let error):
+                    dePrint("Stream error: \(error)")
+                    completion(.failure(error))
+                }
+            case .complete(let cpl):
+                if let error = cpl.error {
+                    dePrint("Stream Request failed with error: \(error)")
+                    completion(.failure(error))
+                } else {
+                    dePrint("Stream success")
+                    completion(.success("Stream success"))
+                }
+            }
+        }
+        return request
+    }
+
+    func request(
+        method:HTTPMethod,
+        urlString: String,
+        parameters: [String: any Any & Sendable]? = nil,
+        completion: @escaping (Result<Any, Error>) -> Void
+    ) -> Request{
+        dePrint("✈️✈️✈️网络请求:\(urlString)")
+        dePrint("✈️✈️✈️参数:\(String(describing: parameters))")
+        
+        var encoding: ParameterEncoding = URLEncoding.default
+        if method == .post {
+            encoding = encoder
+        }
+        let request = afSession.request(urlString, method: method, parameters: parameters, encoding: encoding, headers: defaultHeaders, interceptor: nil)
+        request.responseString { response in
+                self.handleResponse(response, completion: completion)
+            }
+        return request
+    }
+    
+    
+
+    
+}
+
+extension TSNetworkManager {
+    
+    
+    /*
+     // Data 数组
+     let dataArray: [[String: Any]] = [
+         [
+             "data": imageData,
+             "fieldName": "file1", // 字段名
+             "fileName": "App-Icon.png", // 文件名
+             "mimeType": "image/png" // MIME 类型
+         ],
+         [
+             "data": textData,
+             "fieldName": "file2", // 字段名
+             "fileName": "hello.txt", // 文件名
+             "mimeType": "text/plain" // MIME 类型
+         ]
+     ]
+     */
+    
+    /// 上传多个 Data 数据
+    /// - Parameters:
+    ///   - url: 上传地址
+    ///   - dataArray: Data 数组,每个元素是一个字典,包含 Data 和字段名
+    ///   - parameters: 其他参数(可选)
+    ///   - headers: 自定义请求头(可选)
+    ///   - completion: 完成回调,返回结果或错误
+    func uploadData(
+        urlString: String,
+        dataArray: [[String: Any]], // Data 数组,每个元素包含 Data 和字段名
+        parameters: [String: Any]? = nil, // 其他参数
+        headers: HTTPHeaders? = nil, // 自定义请求头
+        progressHandler: @escaping (Progress) -> Void, // 上传进度回调
+        completion: @escaping (Result<Any, Error>) -> Void
+    )-> Request? {
+        
+        guard let url = URL(string: urlString) else {
+            completion(.failure(NSError(domain: "url nil", code: 0)))
+            return nil
+        }
+        
+        // 1. 设置默认请求头
+        var defaultHeaders: HTTPHeaders = [
+            "accept": "application/json",
+            "Content-Type": "multipart/form-data"
+        ]
+        // 合并自定义请求头
+        if let customHeaders = headers {
+            customHeaders.forEach { defaultHeaders[$0.name] = $0.value }
+        }
+        
+        dePrint("✈️✈️✈️网络请求:\(urlString)")
+        dePrint("✈️✈️✈️dataArray:\(String(describing: dataArray))")
+        dePrint("✈️✈️✈️参数:\(String(describing: parameters))")
+        // 2. 使用 Alamofire 上传 Data
+        let request = afSession.upload(
+            multipartFormData: { multipartFormData in
+                // 添加 Data
+                for dataItem in dataArray {
+                    if let data = dataItem["data"] as? Data,
+                       let fieldName = dataItem["fieldName"] as? String,
+                       let fileName = dataItem["fileName"] as? String,
+                       let mimeType = dataItem["mimeType"] as? String {
+                        multipartFormData.append(
+                            data,
+                            withName: fieldName,
+                            fileName: fileName,
+                            mimeType: mimeType
+                        )
+                    }
+                }
+
+                // 添加其他参数
+                if let parameters = parameters {
+                    for (key, value) in parameters {
+                        if let data = "\(value)".data(using: .utf8) {
+                            multipartFormData.append(data, withName: key)
+                        }
+                    }
+                }
+            },
+            to: url, // 上传地址
+            headers: defaultHeaders // 请求头
+        )
+        
+        request
+            .uploadProgress{ progress in
+                // 3. 上传进度回调
+                // 上传进度回调
+                dePrint("✈️✈️✈️进度: \(progress.fractionCompleted * 100)%")
+                progressHandler(progress)
+            }
+            .responseString{ response in
+                // 4. 处理响应
+                self.handleResponse(response, completion: completion)
+        }
+        
+        return request
+    }
+    
+    
+    /// 下载文件
+    /// - Parameters:
+    ///   - url: 下载URL
+    ///   - destination: 目标保存路径 (可选,不传则使用临时目录)
+    ///   - progressHandler: 进度回调 (0.0~1.0)
+    ///   - completion: 完成回调 (返回文件URL或错误)
+    func downloadFile(
+        urlString: String,
+        to destination: URL? = nil,
+        progressHandler: ((Double) -> Void)? = nil,
+        completion: @escaping (Result<URL, Error>) -> Void
+    ) -> DownloadRequest? {
+        
+        
+        guard let url = URL(string: urlString) else {
+            completion(.failure(NSError(domain: "url nil", code: 0)))
+            return nil
+        }
+        
+        // 设置下载目标路径
+        let destination: DownloadRequest.Destination = { temporaryURL, response in
+            // 如果用户指定了目标路径
+            if let destination = destination {
+                // 确保目录存在
+                let directory = destination.deletingLastPathComponent()
+                try? FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true, attributes: nil)
+                return (destination, [.removePreviousFile, .createIntermediateDirectories])
+            }
+            
+            // 否则使用临时目录
+            let documentsURL = FileManager.default.temporaryDirectory
+            let suggestedFilename = response.suggestedFilename ?? url.lastPathComponent
+            let fileURL = documentsURL.appendingPathComponent(suggestedFilename)
+            
+            return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
+        }
+        
+        // 开始下载
+        let request = afSession.download(url, to: destination)
+            .downloadProgress { progress in
+                // 主线程回调进度
+                DispatchQueue.main.async {
+                    progressHandler?(progress.fractionCompleted)
+                }
+            }
+            .response { response in
+                // 主线程回调结果
+                DispatchQueue.main.async {
+                    switch response.result {
+                    case .success(let fileURL):
+                        if let fileURL = fileURL {
+                            completion(.success(fileURL))
+                        } else {
+                            completion(.failure(NSError(domain: "DownloadError", code: -1, userInfo: [NSLocalizedDescriptionKey: "文件路径无效"])))
+                        }
+                    case .failure(let error):
+                        completion(.failure(error))
+                    }
+                }
+            }
+        
+        return request
+    }
+
+}
+
+extension TSNetworkManager {
+    
+    private func handleResponse(
+        _ response: AFDataResponse<String>,
+        completion: @escaping (Result<Any, Error>) -> Void
+    ) {
+        
+        switch response.result {
+        case .success(let value):
+            
+            if let resultDict = dataToJSONObject(data:response.data) as? [String:Any] {
+                
+//                let code = resultDict.safeInt(forKey: "code")
+//                if responseType != nil {
+//                    if let model = T(JSONString: value) {
+//                        handleSuccess(data: model, response: response, completion: completion)
+//                    } else {
+//                        let models = Mapper<T>().mapArray(JSONString: value)
+//                        handleSuccess(data: models ?? [], response: response, completion: completion)
 //                    }
+//                } else {
+                    handleSuccess(data: resultDict, response: response, completion: completion)
 //                }
-//            }
-//        
-//        return request
-//    }
-//
-//}
-//
-//extension TSNetworkManager {
-//    
-//    private func handleResponse(
-//        _ response: AFDataResponse<String>,
-//        completion: @escaping (Result<Any, Error>) -> Void
-//    ) {
-//        
-//        switch response.result {
-//        case .success(let value):
-//            
-//            if let resultDict = dataToJSONObject(data:response.data) as? [String:Any] {
-//                
-////                let code = resultDict.safeInt(forKey: "code")
-////                if responseType != nil {
-////                    if let model = T(JSONString: value) {
-////                        handleSuccess(data: model, response: response, completion: completion)
-////                    } else {
-////                        let models = Mapper<T>().mapArray(JSONString: value)
-////                        handleSuccess(data: models ?? [], response: response, completion: completion)
-////                    }
-////                } else {
-//                    handleSuccess(data: resultDict, response: response, completion: completion)
-////                }
-//                
-//            }else{
-//                handleFail(error: NSError(domain: "Unable to parse data", code: 0), response: response, completion: completion)
-//            }
-//
-//        case .failure(let error):
-//            handleFail(error: error, response: response, completion: completion)
-//        }
-//    }
-//    
-//    func handleSuccess(data:Any,
-//                       response: AFDataResponse<String>,
-//                       completion: @escaping (Result<Any, Error>) -> Void){
-//        dePrint("🚗🚗🚗网络请求成功:\(String(describing: response.request?.url?.absoluteString))")
-//        dePrint("🚗🚗🚗网络请求成功:\(response)")
-//        completion(.success(data))
-//       }
-//    
-//    func handleFail(error:Error,
-//                    response: AFDataResponse<String>,
-//                    completion: @escaping (Result<Any, Error>) -> Void){
-//        dePrint("🚗🚗🚗网络请求失败:\(String(describing: response.request?.url?.absoluteString))")
-//        dePrint("🚗🚗🚗网络请求失败:\(response)")
-//        completion(.failure(error))
-//    }
-//    
-//    func dataToJSONObject(data: Data?) -> Any? {
-//        guard let data = data else { return nil }
-//        do {
-//            let jsonObject = try JSONSerialization.jsonObject(with: data, options: [])
-//            return jsonObject
-//        } catch {
-//            print("Failed to convert Data to JSON object: \(error.localizedDescription)")
-//            return nil
-//        }
-//    }
-//    
-//}
-//
-//
-//
-//func kNetWorkCodeSuccess(data:Any?) -> [String:Any]? {
-//    guard let data = data else { return nil }
-//    if let dataDict = data as? [String:Any]{
-//        let code = dataDict.safeInt(forKey: "code")
-//        
-//        switch code {
-//        case 200://成功
-//            return dataDict
-////        case 400://机器人提示
-////            TSAbnormalPopUpAlertVC.showRobotWarning()
-////            return nil
-//        default:
+                
+            }else{
+                handleFail(error: NSError(domain: "Unable to parse data", code: 0), response: response, completion: completion)
+            }
+
+        case .failure(let error):
+            handleFail(error: error, response: response, completion: completion)
+        }
+    }
+    
+    func handleSuccess(data:Any,
+                       response: AFDataResponse<String>,
+                       completion: @escaping (Result<Any, Error>) -> Void){
+        dePrint("🚗🚗🚗网络请求成功:\(String(describing: response.request?.url?.absoluteString))")
+        dePrint("🚗🚗🚗网络请求成功:\(response)")
+        completion(.success(data))
+       }
+    
+    func handleFail(error:Error,
+                    response: AFDataResponse<String>,
+                    completion: @escaping (Result<Any, Error>) -> Void){
+        dePrint("🚗🚗🚗网络请求失败:\(String(describing: response.request?.url?.absoluteString))")
+        dePrint("🚗🚗🚗网络请求失败:\(response)")
+        completion(.failure(error))
+    }
+    
+    func dataToJSONObject(data: Data?) -> Any? {
+        guard let data = data else { return nil }
+        do {
+            let jsonObject = try JSONSerialization.jsonObject(with: data, options: [])
+            return jsonObject
+        } catch {
+            print("Failed to convert Data to JSON object: \(error.localizedDescription)")
+            return nil
+        }
+    }
+    
+}
+
+
+
+func kNetWorkCodeSuccess(data:Any?) -> [String:Any]? {
+    guard let data = data else { return nil }
+    if let dataDict = data as? [String:Any]{
+        let code = dataDict.safeInt(forKey: "code")
+        
+        switch code {
+        case 200://成功
+            return dataDict
+//        case 400://机器人提示
+//            TSAbnormalPopUpAlertVC.showRobotWarning()
 //            return nil
-//        }
-//    }
-//    return nil
-//}
-//
-//
-//
-//func kNetWorkResultSuccess(data:Any?) -> [String:Any]? {
-//    guard let dataDict = kNetWorkCodeSuccess(data: data) else { return nil }
-//    if let result = dataDict["result"] as? [String:Any]{
-//        return result
-//    }
-//    return nil
-//}
-//
-//func kNetWorkMessage(data:Any?) -> String? {
-//    guard let data = data else { return nil }
-//    if let dataDict = data as? [String:Any] ,
-//       let message = dataDict["message"] as? String{
-//        return message
-//    }
-//    return nil
-//}
-//
-//  
-//
-//class JsonStringTransform<T:Mappable>: TransformType {
-//    typealias Object = T
-//    typealias JSON = [String: Any]
-//    
-//    func transformFromJSON(_ value: Any?) -> T? {
-//    
-//        if let jsonString = value as? String {
-//            let obj = T(JSONString: jsonString)
-//            return obj
-//        }
-//        
-//        if let dict = value as? [String: Any] {
-//            let obj = T(JSON: dict)
-//            return obj
-//        }
-//
-//        return nil
-//    }
-//    
-//    func transformToJSON(_ value: T?) -> [String : Any]? {
-//        return value?.toJSON() as? [String: Any]
-//    }
-//}
+        default:
+            return nil
+        }
+    }
+    return nil
+}
 
-class TSNetworkManager {
+
+
+func kNetWorkResultSuccess(data:Any?) -> [String:Any]? {
+    guard let dataDict = kNetWorkCodeSuccess(data: data) else { return nil }
+    if let result = dataDict["result"] as? [String:Any]{
+        return result
+    }
+    return nil
+}
+
+func kNetWorkMessage(data:Any?) -> String? {
+    guard let data = data else { return nil }
+    if let dataDict = data as? [String:Any] ,
+       let message = dataDict["message"] as? String{
+        return message
+    }
+    return nil
+}
+
+  
+
+class JsonStringTransform<T:Mappable>: TransformType {
+    typealias Object = T
+    typealias JSON = [String: Any]
+    
+    func transformFromJSON(_ value: Any?) -> T? {
+    
+        if let jsonString = value as? String {
+            let obj = T(JSONString: jsonString)
+            return obj
+        }
+        
+        if let dict = value as? [String: Any] {
+            let obj = T(JSON: dict)
+            return obj
+        }
+
+        return nil
+    }
+    
+    func transformToJSON(_ value: T?) -> [String : Any]? {
+        return value?.toJSON() as? [String: Any]
+    }
+}
+
+extension TSNetworkManager {
 
     /// 发送 POST 请求
     /// - Parameters:

+ 135 - 130
TSLiveWallpaper/Data/Model/TSActionInfoModel.swift

@@ -5,135 +5,140 @@
 //  Created by 100Years on 2025/6/10.
 //
 
-//
-//import ObjectMapper
-//class TSActionInfoModel: TSBaseModel {
-//    
-//    enum ModelType:Int {
-//        case normal = 0
-//        case example
-//    }
-//    
-//    enum ActionStatus:String ,Equatable {
-//        case success = "success"//成功
-//        case pending = "pending"//等待
-//        case running = "running"//运行
-//        case failed = "failed"//失败
-//        
-//        static func from(_ string: String) -> ActionStatus {
-//            return ActionStatus(rawValue: string) ?? .failed
-//        }
-//    }
-//    var modelType:ModelType = .normal
-//    var id:Int = 0
-//    var actionType:String = ""
-//    var comments:String = ""
-//    var request:TSActionRequestModel = TSActionRequestModel()
-//    var response:TSActionResponseModel = TSActionResponseModel()
-//    var createdTimestamp:Int = 0
-//    var status:String = ""
-//    var costTime:Int = 0
-//    var percent:Float = 0.0
-//    var actionStatus:ActionStatus = .failed
-//    
-//    var videoThumbnailPath:String = ""
-//    var videoPath:String = ""
-//    
-//    var uuid:String = UUID().uuidString
-//    override func mapping(map: ObjectMapper.Map) {
-//        modelType           <- map["modelType"]
-//        id           <- map["id"]
-//        actionType   <- map["actionType"]
-//        comments     <- map["comments"]
-//        request      <- (map["request"],JsonStringTransform<TSActionRequestModel>())
-//        response           <- (map["response"],JsonStringTransform<TSActionResponseModel>())
-//        createdTimestamp   <- map["createdTimestamp"]
-//        status     <- map["status"]
-//        costTime      <- map["costTime"]
-//        percent     <- map["percent"]
-//        actionStatus      <- map["actionStatus"]
-//        actionStatus = ActionStatus.from(status)
-//        videoThumbnailPath      <- map["videoThumbnailPath"]
-//        videoPath      <- map["videoPath"]
-//        
-//        uuid      <- map["uuid"]
-//    }
-//}
-//
-//extension TSActionInfoModel {
-//    var isVideo:Bool{
-//        return videoThumbnailPath.count > 0
-//    }
-//
-//    var videoThumbnailURL: URL {
-//        return URL("")!//videoThumbnailPath//.fillDocumentURL
-//    }
-//    
-//    var videoURL: URL {
-//        return URL("")!//videoPath//.fillDocumentURL
-//    }
-//    
-//    
-//    var upImageURLExpired:Bool{
-//        if request.imageUrl.count == 0 {
-//            return true
-//        }
-//        
-//        if request.imageUrlTimestamp <= 0 {
+
+import ObjectMapper
+class TSActionInfoModel: TSBaseModel {
+    
+    enum ModelType:Int {
+        case normal = 0
+        case example
+    }
+    
+    enum ActionStatus:String ,Equatable {
+        case success = "success"//成功
+        case pending = "pending"//等待
+        case running = "running"//运行
+        case failed = "failed"//失败
+        
+        static func from(_ string: String) -> ActionStatus {
+            return ActionStatus(rawValue: string) ?? .failed
+        }
+    }
+    var modelType:ModelType = .normal
+    var id:Int = 0
+    var actionType:String = ""
+    var comments:String = ""
+    var request:TSActionRequestModel = TSActionRequestModel()
+    var response:TSActionResponseModel = TSActionResponseModel()
+    var createdTimestamp:Int = 0
+    var status:String = ""
+    var costTime:Int = 0
+    var percent:Float = 0.0
+    var actionStatus:ActionStatus = .failed
+    
+    var type:Int = 0
+    var videoThumbnailPath:String = ""
+    var videoPath:String = ""
+    
+    var uuid:String = UUID().uuidString
+    override func mapping(map: ObjectMapper.Map) {
+        modelType           <- map["modelType"]
+        id           <- map["id"]
+        actionType   <- map["actionType"]
+        comments     <- map["comments"]
+        request      <- (map["request"],JsonStringTransform<TSActionRequestModel>())
+        response           <- (map["response"],JsonStringTransform<TSActionResponseModel>())
+        createdTimestamp   <- map["createdTimestamp"]
+        status     <- map["status"]
+        costTime      <- map["costTime"]
+        percent     <- map["percent"]
+        actionStatus      <- map["actionStatus"]
+        actionStatus = ActionStatus.from(status)
+        videoThumbnailPath      <- map["videoThumbnailPath"]
+        videoPath      <- map["videoPath"]
+        
+        uuid      <- map["uuid"]
+    }
+}
+
+extension TSActionInfoModel {
+    var isVideo:Bool{
+        return videoThumbnailPath.count > 0
+    }
+
+    var videoThumbnailURL: URL {
+        return URL("")!//videoThumbnailPath//.fillDocumentURL
+    }
+    
+    var videoURL: URL {
+        return URL("")!//videoPath//.fillDocumentURL
+    }
+    
+    
+    var upImageURLExpired:Bool{
+        if request.imageUrl.count == 0 {
+            return true
+        }
+        
+        if request.imageUrlTimestamp <= 0 {
+            return true
+        }
+        //86400=24小时
+//        if Date.timestampInt - request.imageUrlTimestamp > 86400 {
 //            return true
 //        }
-//        //86400=24小时
-////        if Date.timestampInt - request.imageUrlTimestamp > 86400 {
-////            return true
-////        }
-//        
-//        return false
-//    }
-//}
-//
-//class TSActionRequestModel : TSBaseModel {
-//    var prompt:String = ""
-//    var promptSort:String = ""  //用户自己输入的内容
-//    var width:Int = 0
-//    var height:Int = 0
-//    
-//    var imageUrl:String = ""
-//    var imageUrlTimestamp:Int = 0
-//    var style:String = ""
-//    var advance:Bool = false//决定生图的模型
-//    var model:String = ""   //决定生图的模型
-//    
-//    override func mapping(map: ObjectMapper.Map) {
-//        prompt              <- map["prompt"]
-//        promptSort          <- map["promptSort"]
-//        width               <- map["width"]
-//        height              <- map["height"]
-//        
-//        imageUrl            <- map["imageUrl"]
-//        imageUrlTimestamp   <- map["imageUrlTimestamp"]
-//        style               <- map["style"]
-//        advance             <- map["advance"]
-//        model               <- map["model"]
-//    }
-//}
-//
-//class TSActionResponseModel : TSBaseModel {
-//    var resultUrl:String = ""
-//    var code:Int = 0
-//    var vip:Bool = false
-//    override func mapping(map: ObjectMapper.Map) {
-//        resultUrl           <- map["resultUrl"]
-//        vip                 <- map["vip"]
-//        code                <- map["code"]
-//    }
-//    
-//    //获取生成错误 code 对应的文案
-//    var codeErrorMsg:String {
-//        TSNetWorkCode.errorMsg(code: code)
-//    }
-//    //敏感错误
-//    var sensitiveError:Bool {
-//        return TSNetWorkCode.sensitiveError(code: code)
-//    }
-//}
-//
+        
+        return false
+    }
+}
+
+class TSActionRequestModel : TSBaseModel {
+    var prompt:String = ""
+    var inputText:String = ""  //用户自己输入的内容
+    var width:Int = 0
+    var height:Int = 0
+    
+    var imageUrl:String = ""
+    var imageUrlTimestamp:Int = 0
+    var style:String = ""
+    var advance:Bool = false//决定生图的模型
+    var model:String = ""   //决定生图的模型
+    
+    override func mapping(map: ObjectMapper.Map) {
+        prompt              <- map["prompt"]
+        inputText           <- map["inputText"]
+        width               <- map["width"]
+        height              <- map["height"]
+        
+        imageUrl            <- map["imageUrl"]
+        imageUrlTimestamp   <- map["imageUrlTimestamp"]
+        style               <- map["style"]
+        advance             <- map["advance"]
+        model               <- map["model"]
+    }
+}
+
+class TSActionResponseModel : TSBaseModel {
+    var resultUrl:String = ""
+    var originalPath:String = ""    //原始的本地路径
+
+    var code:Int = 0
+    var vip:Bool = false
+    
+    override func mapping(map: ObjectMapper.Map) {
+        resultUrl           <- map["resultUrl"]
+        originalPath           <- map["originalPath"]
+        vip                 <- map["vip"]
+        code                <- map["code"]
+    }
+    
+    //获取生成错误 code 对应的文案
+    var codeErrorMsg:String {
+        TSNetWorkCode.errorMsg(code: code)
+    }
+    //敏感错误
+    var sensitiveError:Bool {
+        return TSNetWorkCode.sensitiveError(code: code)
+    }
+}
+

+ 178 - 172
TSLiveWallpaper/Data/TSDBManager/TSDBActionInfoModel.swift

@@ -6,175 +6,181 @@
 //
 
 
-//import RealmSwift
-//
-//class TSDBActionInfoModel: Object {
-// 
-//    @Persisted(primaryKey: true) var primaryKey: String = UUID().uuidString
-//    @Persisted var createdAt: Date = Date()
-//    
-//    @Persisted var modelType:Int = 0
-//    @Persisted var id:Int = 0
-//    @Persisted var actionType:String = ""
-//    @Persisted var comments:String = ""
-//    @Persisted var request:TSDBActionRequestModel?
-//    @Persisted var response:TSDBActionResponseModel?
-//    @Persisted var createdTimestamp:Int = 0
-//    @Persisted var status:String = ""   //success,pending,running,failed
-//    @Persisted var costTime:Int = 0
-//    @Persisted var percent:Float = 0.0
-//    
-//    @Persisted var videoThumbnailPath:String = ""
-//    @Persisted var videoPath:String = ""
-//    @Persisted var uuid:String = UUID().uuidString
-//    
-//    static func createDBModel(actionInfoModel:TSActionInfoModel) -> TSDBActionInfoModel{
-//        let dbModel = TSDBActionInfoModel()
-//        dbModel.saveData(infoModel: actionInfoModel)
-//        return dbModel
-//    }
-//    
-//    func saveData(infoModel:TSActionInfoModel) {
-//        self.modelType = infoModel.modelType.rawValue
-//        self.id = infoModel.id
-//        self.actionType = infoModel.actionType
-//        self.comments = infoModel.comments
-//
-//        self.createdTimestamp = infoModel.createdTimestamp
-//        self.status = infoModel.status
-//        self.costTime = infoModel.costTime
-//        self.percent = infoModel.percent
-//        self.videoThumbnailPath = infoModel.videoThumbnailPath
-//        self.videoPath = infoModel.videoPath
-//        self.uuid = infoModel.uuid
-//        
-//        self.request = TSDBActionRequestModel.createDBModel(requestModel: infoModel.request)
-//        self.response = TSDBActionResponseModel.createDBModel(responseModel: infoModel.response)
-//        
-//    }
-//    
-//    func getModel()->TSActionInfoModel{
-//        let infoModel = TSActionInfoModel()
-//        
-//        infoModel.modelType = TSActionInfoModel.ModelType.init(rawValue: self.modelType) ?? .normal
-//        infoModel.id = self.id
-//        infoModel.actionType = self.actionType
-//        infoModel.comments = self.comments
-//
-//        infoModel.createdTimestamp = self.createdTimestamp
-//        infoModel.status = self.status
-//        infoModel.costTime = self.costTime
-//        infoModel.percent = self.percent
-//        infoModel.videoThumbnailPath = self.videoThumbnailPath
-//        infoModel.videoPath = self.videoPath
-//        infoModel.uuid = self.uuid
-//
-//        if let request = self.request {
-//            infoModel.request = request.getModel()
-//        }
-//        
-//        if let response = self.response {
-//            infoModel.response = response.getModel()
-//        }
-//        
-//        infoModel.actionStatus = TSActionInfoModel.ActionStatus.from(infoModel.status)
-//        return infoModel
-//    }
-//    
-//    
-//    var isResult:Bool {
-//        if status.count > 0 {
-//            if status == "pending" ||
-//               status == "running"
-//            {
-//                return false
-//            }
-//        }
-//        return true
-//    }
-//
-//}
-//
-//
-//
-//class TSDBActionRequestModel : Object {
-//    
-//    @Persisted(primaryKey: true) var primaryKey: String = UUID().uuidString
-//    @Persisted var createdAt: Date = Date()
-//    
-//    @Persisted var prompt:String = ""
-//    @Persisted var promptSort:String = ""  //用户自己输入的内容
-//    @Persisted var width:Int = 0
-//    @Persisted var height:Int = 0
-//    
-//    @Persisted var imageUrl:String = ""
-//    @Persisted var imageUrlTimestamp:Int = 0
-//    @Persisted var style:String = ""
-//    @Persisted var advance:Bool = false
-//    
-//    static func createDBModel(requestModel:TSActionRequestModel) -> TSDBActionRequestModel{
-//        let dbModel = TSDBActionRequestModel()
-//        dbModel.saveData(requestModel: requestModel)
-//        return dbModel
-//    }
-//    
-//    func saveData(requestModel:TSActionRequestModel) {
-//        self.prompt = requestModel.prompt
-//        self.promptSort = requestModel.promptSort
-//        self.width = requestModel.width
-//        self.height = requestModel.height
-//        
-//        self.imageUrl = requestModel.imageUrl
-//        self.imageUrlTimestamp = requestModel.imageUrlTimestamp
-//        self.style = requestModel.style
-//        self.advance = requestModel.advance
-//    }
-//    
-//    
-//    func getModel()->TSActionRequestModel{
-//        let model = TSActionRequestModel()
-//        model.prompt = self.prompt
-//        model.promptSort = self.promptSort
-//        model.width = self.width
-//        model.height = self.height
-//        
-//        model.imageUrl = self.imageUrl
-//        model.imageUrlTimestamp = self.imageUrlTimestamp
-//        model.style = self.style
-//        model.advance = self.advance
-//        
-//        return model
-//    }
-//
-//}
-//
-//class TSDBActionResponseModel : Object {
-//    @Persisted(primaryKey: true) var primaryKey: String = UUID().uuidString
-//    @Persisted var createdAt: Date = Date()
-//    
-//    @Persisted var resultUrl:String = ""
-//    @Persisted var code:Int = 0
-//    @Persisted var vip:Bool = false
-//    
-//    static func createDBModel(responseModel:TSActionResponseModel) -> TSDBActionResponseModel{
-//        let dbModel = TSDBActionResponseModel()
-//        dbModel.saveData(responseModel: responseModel)
-//        return dbModel
-//    }
-//    
-//    func saveData(responseModel:TSActionResponseModel) {
-//        self.resultUrl = responseModel.resultUrl
-//        self.code = responseModel.code
-//        self.vip = responseModel.vip
-//    }
-//    
-//    
-//    func getModel()->TSActionResponseModel{
-//        let model = TSActionResponseModel()
-//        model.resultUrl = self.resultUrl
-//        model.code = self.code
-//        model.vip = self.vip
-//        return model
-//    }
-//    
-//}
+import RealmSwift
+
+class TSDBActionInfoModel: Object {
+ 
+    @Persisted(primaryKey: true) var primaryKey: String = UUID().uuidString
+    @Persisted var createdAt: Date = Date()
+    
+    @Persisted var dbType:String    //储存的业务类型
+
+    @Persisted var modelType:Int = 0
+    @Persisted var id:Int = 0
+    @Persisted var actionType:String = ""
+    @Persisted var comments:String = ""
+    @Persisted var request:TSDBActionRequestModel?
+    @Persisted var response:TSDBActionResponseModel?
+    @Persisted var createdTimestamp:Int = 0
+    @Persisted var status:String = ""   //success,pending,running,failed
+    @Persisted var costTime:Int = 0
+    @Persisted var percent:Float = 0.0
+    
+    @Persisted var videoThumbnailPath:String = ""
+    @Persisted var videoPath:String = ""
+    @Persisted var uuid:String = UUID().uuidString
+    
+    static func createDBModel(actionInfoModel:TSActionInfoModel,dbType:String) -> TSDBActionInfoModel{
+        let dbModel = TSDBActionInfoModel()
+        dbModel.dbType = dbType
+        dbModel.saveData(infoModel: actionInfoModel)
+        return dbModel
+    }
+    
+    func saveData(infoModel:TSActionInfoModel) {
+        self.modelType = infoModel.modelType.rawValue
+        self.id = infoModel.id
+        self.actionType = infoModel.actionType
+        self.comments = infoModel.comments
+
+        self.createdTimestamp = infoModel.createdTimestamp
+        self.status = infoModel.status
+        self.costTime = infoModel.costTime
+        self.percent = infoModel.percent
+        self.videoThumbnailPath = infoModel.videoThumbnailPath
+        self.videoPath = infoModel.videoPath
+        self.uuid = infoModel.uuid
+        
+        self.request = TSDBActionRequestModel.createDBModel(requestModel: infoModel.request)
+        self.response = TSDBActionResponseModel.createDBModel(responseModel: infoModel.response)
+        
+    }
+    
+    func getModel()->TSActionInfoModel{
+        let infoModel = TSActionInfoModel()
+        
+        infoModel.modelType = TSActionInfoModel.ModelType.init(rawValue: self.modelType) ?? .normal
+        infoModel.id = self.id
+        infoModel.actionType = self.actionType
+        infoModel.comments = self.comments
+
+        infoModel.createdTimestamp = self.createdTimestamp
+        infoModel.status = self.status
+        infoModel.costTime = self.costTime
+        infoModel.percent = self.percent
+        infoModel.videoThumbnailPath = self.videoThumbnailPath
+        infoModel.videoPath = self.videoPath
+        infoModel.uuid = self.uuid
+
+        if let request = self.request {
+            infoModel.request = request.getModel()
+        }
+        
+        if let response = self.response {
+            infoModel.response = response.getModel()
+        }
+        
+        infoModel.actionStatus = TSActionInfoModel.ActionStatus.from(infoModel.status)
+        return infoModel
+    }
+    
+    
+    var isResult:Bool {
+        if status.count > 0 {
+            if status == "pending" ||
+               status == "running"
+            {
+                return false
+            }
+        }
+        return true
+    }
+
+}
+
+
+
+class TSDBActionRequestModel : Object {
+    
+    @Persisted(primaryKey: true) var primaryKey: String = UUID().uuidString
+    @Persisted var createdAt: Date = Date()
+    
+    @Persisted var prompt:String = ""
+    @Persisted var inputText:String = ""  //用户自己输入的内容
+    @Persisted var width:Int = 0
+    @Persisted var height:Int = 0
+    
+    @Persisted var imageUrl:String = ""
+    @Persisted var imageUrlTimestamp:Int = 0
+    @Persisted var style:String = ""
+    @Persisted var advance:Bool = false
+    
+    static func createDBModel(requestModel:TSActionRequestModel) -> TSDBActionRequestModel{
+        let dbModel = TSDBActionRequestModel()
+        dbModel.saveData(requestModel: requestModel)
+        return dbModel
+    }
+    
+    func saveData(requestModel:TSActionRequestModel) {
+        self.prompt = requestModel.prompt
+        self.inputText = requestModel.inputText
+        self.width = requestModel.width
+        self.height = requestModel.height
+        
+        self.imageUrl = requestModel.imageUrl
+        self.imageUrlTimestamp = requestModel.imageUrlTimestamp
+        self.style = requestModel.style
+        self.advance = requestModel.advance
+    }
+    
+    
+    func getModel()->TSActionRequestModel{
+        let model = TSActionRequestModel()
+        model.prompt = self.prompt
+        model.inputText = self.inputText
+        model.width = self.width
+        model.height = self.height
+        
+        model.imageUrl = self.imageUrl
+        model.imageUrlTimestamp = self.imageUrlTimestamp
+        model.style = self.style
+        model.advance = self.advance
+        
+        return model
+    }
+
+}
+
+class TSDBActionResponseModel : Object {
+    @Persisted(primaryKey: true) var primaryKey: String = UUID().uuidString
+    @Persisted var createdAt: Date = Date()
+    
+    @Persisted var resultUrl:String = ""
+    @Persisted var originalPath:String = ""
+    @Persisted var code:Int = 0
+    @Persisted var vip:Bool = false
+    
+    static func createDBModel(responseModel:TSActionResponseModel) -> TSDBActionResponseModel{
+        let dbModel = TSDBActionResponseModel()
+        dbModel.saveData(responseModel: responseModel)
+        return dbModel
+    }
+    
+    func saveData(responseModel:TSActionResponseModel) {
+        self.resultUrl = responseModel.resultUrl
+        self.originalPath = responseModel.originalPath
+        self.code = responseModel.code
+        self.vip = responseModel.vip
+    }
+    
+    
+    func getModel()->TSActionResponseModel{
+        let model = TSActionResponseModel()
+        model.resultUrl = self.resultUrl
+        model.originalPath = self.originalPath
+        model.code = self.code
+        model.vip = self.vip
+        return model
+    }
+    
+}

+ 162 - 311
TSLiveWallpaper/Data/TSDBManager/TSDBManager.swift

@@ -6,315 +6,166 @@
 //
 
 
-//import RealmSwift
-//import ObjectMapper
-//
-//// 1. 定义历史记录类型枚举
-//enum TSDBHistoryType: String,CaseIterable {
-//    case ptp = "photoToPhotoHistoryListString"  //图生图
-//    case ttp = "textPicHistoryListString"       //文生图
-//    case ttEnmoji = "enmojiHistoryListString"   //文生表情
-//
-//    case pretty = "kTSAIPhotoPrettyHistoryListString"   //美容
-//    case oldAge = "kTSChangeOldAgeHistoryListString"   //变老
-//    case babyAge = "kTSChangeBabyAgeHistoryListString"   //变年轻
-//    case oldPhoto = "kTSChangeOldPhotoHistoryListString"   //旧照片修复
-//    case openEyes = "kTSAIEyeOpenHistoryListString"   //睁眼
-//    case photoLive = "kTSAIPhotoLiveHistoryListString"   //活照片
-//    case photoExpand = "kTSAIPhotoExpandHistoryListString"   //照片扩展
-//    case photoQuality = "kTSAIPhotoQualityHistoryListString"   //照片变高清
-//    case motherDay = "kTSAIMotherDayHistoryListString"   //母亲节
-//    case catTohuman = "kTSAICatTohumanHistoryListString"   //猫变人
-//    case futureBaby = "kTSAIFutureBabyHistoryListString"   //预测宝宝
-//}
-//
-//
-////MARK: TSDBAIChatList - 用于存储会话及消息列表
-//class TSDBHistory: Object {
-//    @Persisted(primaryKey: true) var primaryKey: String = ""
-//    @Persisted var listModels = List<TSDBActionInfoModel>()
-//    
-//    var type: TSDBHistoryType {
-//        get { TSDBHistoryType(rawValue: primaryKey) ?? .ptp }
-//        set { primaryKey = newValue.rawValue }
-//    }
-//    
-//    convenience init(type: TSDBHistoryType) {
-//        self.init()
-//        self.primaryKey = type.rawValue
-//    }
-//    
-//
-//    func getModelList() -> [TSActionInfoModel] {
-//        var msgModel:[TSActionInfoModel] = []
-//        for msgDBModel in listModels {
-//            msgModel.append(msgDBModel.getModel())
-//        }
-//        return msgModel
-//    }
-//    
-//    
-//    func getModelList(completion:@escaping ([TSActionInfoModel])->Void){
-//        let frozenList = self.listModels.freeze()
-//        DispatchQueue.global(qos: .userInitiated).async {
-//            var msgModel:[TSActionInfoModel] = []
-//            for msgDBModel in frozenList {
-//                msgModel.append(msgDBModel.getModel())
-//            }
-//            DispatchQueue.main.async {
-//                completion(msgModel)
-//            }
-//        }
-//    }
-//    
-//    func getModelList(count:Int) -> [TSActionInfoModel] {
-//        let listModels = Array(listModels.prefix(count))
-//        var msgModel:[TSActionInfoModel] = []
-//        for msgDBModel in listModels {
-//            msgModel.append(msgDBModel.getModel())
-//        }
-//        return msgModel
-//    }
-//    
-//    func delete() {
-//        TSRMShared.delete(self)
-//    }
-//    
-//    func deleteListModel(id:Int) {
-//        TSRMShared.writeThread {
-//            if let index = listModels.firstIndex(where: { $0.id == id }) {
-//                listModels.remove(at: index)
-//                debugPrint("listModels.remove(at: \(index))")
-//            }
-//        }
-//    }
-//    
-//    static func deleteAll() {
-//        do {
-//            let realm = try Realm()
-//            try realm.write {
-//                let allPersons = realm.objects(TSDBHistory.self)
-//                realm.delete(allPersons)
-//            }
-//        } catch {
-//            debugPrint("删除 TSDBPTPHistory 模型数据时出错: \(error)")
-//        }
-//    }
-//    
-//    func updateData(_ actionInfoModel:TSActionInfoModel,id:Int? = nil){
-//            let dbModel = TSDBActionInfoModel.createDBModel(actionInfoModel: actionInfoModel)
-//            var replaceID = dbModel.id
-//            if let id = id {
-//                replaceID = id
-//            }
-//            
-//            let frozenList = self.listModels.freeze()
-//            if let index = frozenList.firstIndex(where: { $0.id == replaceID }) {
-//                TSRMShared.writeThread {
-//                    listModels[index] = dbModel// 如果找到,替换该元素
-//                }
-//            } else {
-//                print("Thread.current insert1=\(Thread.current)")
-//                TSRMShared.writeThread {
-//                    listModels.insert(dbModel, at: 0)// 如果没有找到,添加到末尾
-//                    print("Thread.current insert2=\(Thread.current)")
-//                }
-//            }
-//    }
-//    
-//    func updateDatas(_ actionInfoModels:[TSActionInfoModel]){
-//        for actionInfoModel in actionInfoModels {
-//            updateData(actionInfoModel)
-//        }
-//    }
-//
-//    
-//    func addDatas(_ actionInfoModels:[TSActionInfoModel]){
-//        for actionInfoModel in actionInfoModels {
-//            let dbModel = TSDBActionInfoModel.createDBModel(actionInfoModel: actionInfoModel)
-//            TSRMShared.writeThread {
-//                listModels.append(dbModel)
-//            }
-//        }
-//    }
-//}
-//
-//
-//extension TSDBHistory {
-//    //是否需要迁移历史记录
-//    static var isMigrationUserDefaultsHistory:Bool{
-//        for type in TSDBHistoryType.allCases {
-//            if let _ = UserDefaults.standard.string(forKey: type.rawValue){
-//                debugPrint("需要迁移\(type.rawValue)")
-//                return true
-//            }
-//        }
-//        return false
-//    }
-//
-//    static func migrationUserDefaultsHistory(complete:@escaping ()->Void){
-//        DispatchQueue.global(qos: .userInitiated).async {
-//            for type in TSDBHistoryType.allCases {
-//                if let historyString = UserDefaults.standard.string(forKey: type.rawValue){
-//                    if let models = Mapper<TSActionInfoModel>().mapArray(JSONString: historyString) {
-//                        debugPrint("TSDBHistory 需要迁移\(type.rawValue)\(models.count)条")
-//                        let dbHistory = TSRMShared.getDBHistory(type:type)
-//                        dbHistory.addDatas(models)
-//                        debugPrint("TSDBHistory 迁移完毕\(type.rawValue)")
-//                        UserDefaults.standard.set(nil, forKey: type.rawValue)
-//                        UserDefaults.standard.synchronize()
-//                    }
-//                }
-//            }
-//            DispatchQueue.main.async {
-//                complete()
-//            }
-//        }
-//    }
-//}
-//
-//
-//extension TSRealmManager {
-//    func getDBHistory(type:TSDBHistoryType) -> TSDBHistory {
-//        let predicate = NSPredicate(format: "primaryKey == %@",type.rawValue)
-//        let historys = TSRMShared.realm.objects(TSDBHistory.self).filter(predicate)
-//        if let history = historys.first {
-//            return history
-//        }else {
-//            let dbHistory = TSDBHistory(type: type)
-//            TSRMShared.update(dbHistory)
-//            return dbHistory
-//        }
-//    }
-//    
-//    func createExampleModel(id:Int,imageName:String)->TSActionInfoModel{
-//        let model = TSActionInfoModel()
-//        model.id = id
-//        model.modelType = .example
-//        model.request.prompt = "Example"
-//        model.request.promptSort = "Example"
-//        model.request.width = 330
-//        model.request.height = 440
-//        model.response.resultUrl = imageName
-//        model.status = "success"
-//        return model
-//    }
-//    
-//    //测试 5万条数据
-//    func textPtpDBHistory(){
-//        DispatchQueue.global(qos: .userInteractive).async {
-//            let id = Date.timestampInt
-//            var array:[TSDBActionInfoModel] = []
-//            debugPrint("创建 5万条数据 前")
-//            for i in 0...50 {
-//                let dbModel = TSDBActionInfoModel.createDBModel(actionInfoModel: self.createExampleModel(id:id+i, imageName: "ptp_example_image0"))
-//                array.append(dbModel)
-//            }
-//            debugPrint("创建 5万条数据 后")
-//            
-//            TSRMShared.writeThread {
-//                debugPrint("写入 5万条数据")
-////                TSRMShared.photoExpandDBHistory.listModels.append(objectsIn: array)
-//                TSRMShared.ptpDBHistory.listModels.append(objectsIn: array)
-//                debugPrint("写完 5万条数据")
-//            }
-//        }
-//    }
-//    
-//    //图生图
-//    var ptpDBHistory:TSDBHistory {
-//        let ptpHistory = getDBHistory(type: TSDBHistoryType.ptp)
-//        
-//        if ptpHistory.listModels.count == 0,UserDefaults.standard.string(forKey: "insertPTPExampleData") == nil {
-//            let id = Date.timestampInt
-//            ptpHistory.updateDatas([
-//                createExampleModel(id:id, imageName: "ptp_example_image0"),
-//                createExampleModel(id:id+1, imageName: "ptp_example_image1")
-//            ])
-//            UserDefaults.standard.set("1", forKey: "insertPTPExampleData")
-//            UserDefaults.standard.synchronize()
-//        }
-//        
-//        return ptpHistory
-//    }
-//    
-//    //文生图
-//    var ttpDBHistory:TSDBHistory {
-//
-//        let history = getDBHistory(type: TSDBHistoryType.ttp)
-//        
-//        if history.listModels.count == 0,UserDefaults.standard.string(forKey: "insertTTPExampleData") == nil {
-//            let id = Date.timestampInt
-//            history.updateDatas([
-//                createExampleModel(id:id, imageName: "ttp_example_image0"),
-//                createExampleModel(id:id+1, imageName: "ttp_example_image1")
-//            ])
-//            UserDefaults.standard.set("1", forKey: "insertTTPExampleData")
-//            UserDefaults.standard.synchronize()
-//        }
-//        
-//        return history
-//        
-//    }
-//
-//    //文生表情
-//    var ttEnmojiDBHistory:TSDBHistory {
-//        return getDBHistory(type: TSDBHistoryType.ttEnmoji)
-//    }
-//
-//    //美容
-//    var oldAgeDBHistory:TSDBHistory {
-//        return getDBHistory(type: TSDBHistoryType.oldAge)
-//    }
-//    
-//    //变老
-//    var prettyDBHistory:TSDBHistory {
-//        return getDBHistory(type: TSDBHistoryType.pretty)
-//    }
-//    
-//    //变年轻
-//    var babyAgeDBHistory:TSDBHistory {
-//        return getDBHistory(type: TSDBHistoryType.babyAge)
-//    }
-//    
-//    //老照片修复
-//    var oldPhotoDBHistory:TSDBHistory {
-//        return getDBHistory(type: TSDBHistoryType.oldPhoto)
-//    }
-//    
-//    //睁眼
-//    var openEyesDBHistory:TSDBHistory {
-//        return getDBHistory(type: TSDBHistoryType.openEyes)
-//    }
-//    
-//    //活照片
-//    var photoLiveDBHistory:TSDBHistory {
-//        return getDBHistory(type: TSDBHistoryType.photoLive)
-//    }
-//    
-//    //扩图
-//    var photoExpandDBHistory:TSDBHistory {
-//        return getDBHistory(type: TSDBHistoryType.photoExpand)
-//    }
-//    
-//    //变高清图
-//    var photoQualityDBHistory:TSDBHistory {
-//        return getDBHistory(type: TSDBHistoryType.photoQuality)
-//    }
-//    
-//    //母亲节
-//    var motherDayDBHistory:TSDBHistory {
-//        return getDBHistory(type: TSDBHistoryType.motherDay)
-//    }
-//    
-//    //猫变人
-//    var catTohumanDBHistory:TSDBHistory {
-//        return getDBHistory(type: TSDBHistoryType.catTohuman)
-//    }
-//    
-//    //预测宝宝
-//    var futureBabyDBHistory:TSDBHistory {
-//        return getDBHistory(type: TSDBHistoryType.futureBaby)
-//    }
-//}
+import RealmSwift
+import ObjectMapper
+
+// 1. 定义历史记录类型枚举
+enum TSDBHistoryType: String,CaseIterable {
+    case aiList = "kDBAIList"              //旧照片修复
+}
+
+
+//MARK: TSDBAIChatList - 用于存储会话及消息列表
+class TSDBHistory: Object {
+    @Persisted(primaryKey: true) var primaryKey: String = ""
+    @Persisted var listModels = List<TSDBActionInfoModel>()
+    
+    var type: TSDBHistoryType {
+        get { TSDBHistoryType(rawValue: primaryKey) ?? .aiList }
+        set { primaryKey = newValue.rawValue }
+    }
+    
+    convenience init(type: TSDBHistoryType) {
+        self.init()
+        self.primaryKey = type.rawValue
+    }
+    
+
+    func getModelList() -> [TSActionInfoModel] {
+        var msgModel:[TSActionInfoModel] = []
+        for msgDBModel in listModels {
+            msgModel.append(msgDBModel.getModel())
+        }
+        return msgModel
+    }
+    
+    
+    func getModelList(completion:@escaping ([TSActionInfoModel])->Void){
+        let frozenList = self.listModels.freeze()
+        DispatchQueue.global(qos: .userInitiated).async {
+            var msgModel:[TSActionInfoModel] = []
+            for msgDBModel in frozenList {
+                msgModel.append(msgDBModel.getModel())
+            }
+            DispatchQueue.main.async {
+                completion(msgModel)
+            }
+        }
+    }
+    
+    func getModelList(count:Int) -> [TSActionInfoModel] {
+        let listModels = Array(listModels.prefix(count))
+        var msgModel:[TSActionInfoModel] = []
+        for msgDBModel in listModels {
+            msgModel.append(msgDBModel.getModel())
+        }
+        return msgModel
+    }
+    
+    func delete() {
+        TSRMShared.delete(self)
+    }
+    
+    func deleteListModel(id:Int) {
+        TSRMShared.writeThread {
+            if let index = listModels.firstIndex(where: { $0.id == id }) {
+                listModels.remove(at: index)
+                debugPrint("listModels.remove(at: \(index))")
+            }
+        }
+    }
+    
+    static func deleteAll() {
+        do {
+            let realm = try Realm()
+            try realm.write {
+                let allPersons = realm.objects(TSDBHistory.self)
+                realm.delete(allPersons)
+            }
+        } catch {
+            debugPrint("删除 TSDBPTPHistory 模型数据时出错: \(error)")
+        }
+    }
+    
+    func updateData(_ actionInfoModel:TSActionInfoModel,id:Int? = nil){
+        let dbModel = TSDBActionInfoModel.createDBModel(actionInfoModel: actionInfoModel,dbType: self.primaryKey)
+            var replaceID = dbModel.id
+            if let id = id {
+                replaceID = id
+            }
+            
+            let frozenList = self.listModels.freeze()
+            if let index = frozenList.firstIndex(where: { $0.id == replaceID }) {
+                TSRMShared.writeThread {
+                    listModels[index] = dbModel// 如果找到,替换该元素
+                }
+            } else {
+                print("Thread.current insert1=\(Thread.current)")
+                TSRMShared.writeThread {
+                    listModels.insert(dbModel, at: 0)// 如果没有找到,添加到末尾
+                    print("Thread.current insert2=\(Thread.current)")
+                }
+            }
+    }
+    
+    func updateDatas(_ actionInfoModels:[TSActionInfoModel]){
+        for actionInfoModel in actionInfoModels {
+            updateData(actionInfoModel)
+        }
+    }
+
+    
+    func addDatas(_ actionInfoModels:[TSActionInfoModel]){
+        for actionInfoModel in actionInfoModels {
+            let dbModel = TSDBActionInfoModel.createDBModel(actionInfoModel: actionInfoModel,dbType: self.primaryKey)
+            TSRMShared.writeThread {
+                listModels.append(dbModel)
+            }
+        }
+    }
+}
+
+extension TSRealmManager {
+    func getDBHistory(type:TSDBHistoryType) -> TSDBHistory {
+        let predicate = NSPredicate(format: "primaryKey == %@",type.rawValue)
+        let historys = TSRMShared.realm.objects(TSDBHistory.self).filter(predicate)
+        if let history = historys.first {
+            return history
+        }else {
+            let dbHistory = TSDBHistory(type: type)
+            TSRMShared.update(dbHistory)
+            return dbHistory
+        }
+    }
+    
+    func createExampleModel(id:Int,imageName:String)->TSActionInfoModel{
+        let model = TSActionInfoModel()
+        model.id = id
+        model.modelType = .example
+        model.request.prompt = "Example"
+        model.request.inputText = "Example"
+        model.request.width = 330
+        model.request.height = 440
+        model.response.resultUrl = imageName
+        model.status = "success"
+        return model
+    }
+    
+    //老照片修复
+    var aiListDB:TSDBHistory {
+        let history = getDBHistory(type: TSDBHistoryType.aiList)
+        if history.listModels.count == 0,UserDefaults.standard.string(forKey: "insertAIListExampleData") == nil {
+            let id = Date.timestampInt
+            history.updateDatas([
+                createExampleModel(id:id, imageName: "ttp_example_image0"),
+                createExampleModel(id:id+1, imageName: "ttp_example_image1")
+            ])
+            UserDefaults.standard.set("1", forKey: "insertAIListExampleData")
+            UserDefaults.standard.synchronize()
+        }
+        
+        return history
+    }
+    
+}
 

+ 120 - 0
TSLiveWallpaper/Data/TSRealmManager/TSRealmManager.swift

@@ -0,0 +1,120 @@
+//
+//  TSRealmManager.swift
+//  AIEmoji
+//
+//  Created by 100Years on 2025/2/11.
+//
+
+import RealmSwift
+
+let TSRMShared = TSRealmManager.shared
+
+
+
+
+class TSRealmManager {
+    static let shared = TSRealmManager()
+    private init() {
+        /*设置新的版本号
+         1.0 ->1
+         1.9 ->2    //新增
+         2.1 ->3    //新增ai思考
+         3.6.1  ->4   //将UserDefaults 的历史记录迁移到数据库中
+         **/
+   
+        let newSchemaVersion: UInt64 = 4
+        // 获取默认配置
+        var config = Realm.Configuration.defaultConfiguration
+        // 设置新版本号
+        config.schemaVersion = newSchemaVersion
+        // 设置迁移块
+        config.migrationBlock = { migration, oldSchemaVersion in
+            if oldSchemaVersion < newSchemaVersion {
+                // 执行迁移操作
+            }
+        }
+        // 将修改后的配置设置为默认配置
+        Realm.Configuration.defaultConfiguration = config
+        debugPrint("Realm 数据库已成功打开,版本号: \(newSchemaVersion)")
+    }
+    
+    var realm:Realm {
+        return try! Realm()
+    }
+
+
+    // 创建数据
+    func create<T: Object>(_ object: T) {
+        do {
+            try realm.write {
+                realm.add(object,update: .modified)
+            }
+        } catch {
+            print("Failed to create object: \(error)")
+        }
+    }
+
+    // 读取数据
+    func read<T: Object>(_ type: T.Type) -> Results<T> {
+        return realm.objects(type)
+    }
+
+
+    func update<T: Object>(_ object: T) {
+        do {
+            try Realm().write {
+                try Realm().add(object,update: .modified)
+            }
+        } catch {
+            print("Failed to update object: \(error)")
+        }
+    }
+
+    // 删除数据
+    func delete<T: Object>(_ object: T) {
+        do {
+      
+            try Realm().write {
+                try Realm().delete(object)
+            }
+        } catch {
+            print("Failed to delete object: \(error)")
+        }
+    }
+    
+    
+    func writeThread(wt: ()->Void) {
+        do {
+            try Realm().write {
+                wt()
+            }
+        } catch {
+            print("Failed to writeThread: \(error)")
+        }
+        
+    }
+}
+
+
+/*
+// 创建数据
+let person = Person()
+person.name = "John"
+person.age = 30
+RealmManager.shared.create(person)
+// 读取数据
+let allPersons = RealmManager.shared.read(Person.self)
+for person in allPersons {
+    print("Name: \(person.name), Age: \(person.age)")
+}
+// 更新数据
+if let firstPerson = allPersons.first {
+    RealmManager.shared.update(firstPerson) { person in
+        person.age = 31
+    }
+}
+// 删除数据
+if let firstPerson = allPersons.first {
+    RealmManager.shared.delete(firstPerson)
+}
+*/

+ 1 - 1
TSLiveWallpaper/LaunchVC/TSLaunchVC.swift

@@ -46,7 +46,7 @@ class TSLaunchVC: UIViewController {
                 if !PurchaseManager.default.isVip {
                     ADManager.shared.showLaunchAd(scene: ADScene.launch, in: self)
                 }
-                PurchaseManager.default.requestProducts()
+                kPurchaseBusiness.launchPrchase()
                 self.initUmpProtocal()
                 manager?.stopListening()
             default: