import Foundation import CoreData import SwiftUI import Combine class CoreDataManager: ObservableObject { static let shared = CoreDataManager() let container: NSPersistentContainer init() { container = NSPersistentContainer(name: "MyQrCode") container.loadPersistentStores { description, error in if let error = error { print(String(format: "core_data_load_failed".localized, error.localizedDescription)) // 如果是架构不匹配错误,删除数据库文件并重新创建 if let nsError = error as NSError?, nsError.domain == NSCocoaErrorDomain && (nsError.code == 134030 || nsError.code == 134140) { print("architecture_mismatch_detected".localized) self.deleteDatabaseFiles() // 重新加载持久化存储 self.container.loadPersistentStores { _, reloadError in if let reloadError = reloadError { print(String(format: "core_data_reload_failed".localized, reloadError.localizedDescription)) } else { print("core_data_reload_success".localized) } } } } } // 启用自动合并更改 container.viewContext.automaticallyMergesChangesFromParent = true container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy } // 保存上下文 func save() { let context = container.viewContext if context.hasChanges { do { try context.save() print("core_data_save_success".localized) } catch { print(String(format: "core_data_save_failed".localized, error.localizedDescription)) print(String(format: "error_details".localized, "\(error)")) // 如果是NSError,打印更多信息 if let nsError = error as NSError? { print(String(format: "error_domain".localized, nsError.domain)) print("❌ Error code: \(nsError.code)") print("❌ User info: \(nsError.userInfo)") // 检查是否是Transformable属性错误 if nsError.domain == NSCocoaErrorDomain && nsError.code == 134030 { print("❌ May be Transformable property encoding error") } } } } else { print("ℹ️ No changes to save") } } // 分页大小 private let pageSize = 20 // 获取历史记录(分页加载) func fetchHistoryItems(page: Int = 0) -> [HistoryItem] { let request: NSFetchRequest = HistoryItem.fetchRequest() request.sortDescriptors = [NSSortDescriptor(keyPath: \HistoryItem.createdAt, ascending: false)] request.fetchLimit = pageSize request.fetchOffset = page * pageSize do { return try container.viewContext.fetch(request) } catch { print("Failed to fetch history: \(error.localizedDescription)") return [] } } // 获取所有历史记录(用于兼容性) func fetchAllHistoryItems() -> [HistoryItem] { let request: NSFetchRequest = HistoryItem.fetchRequest() request.sortDescriptors = [NSSortDescriptor(keyPath: \HistoryItem.createdAt, ascending: false)] do { return try container.viewContext.fetch(request) } catch { print("Failed to fetch all history: \(error.localizedDescription)") return [] } } // 获取历史记录总数 func fetchHistoryItemsCount() -> Int { let request: NSFetchRequest = HistoryItem.fetchRequest() do { return try container.viewContext.count(for: request) } catch { print("Failed to count history: \(error.localizedDescription)") return 0 } } // 添加历史记录 func addHistoryItem(_ item: HistoryItem) { container.viewContext.insert(item) save() } // 删除历史记录 func deleteHistoryItem(_ item: HistoryItem) { container.viewContext.delete(item) save() } // 清空所有历史记录 func clearAllHistory() { let request: NSFetchRequest = HistoryItem.fetchRequest() let deleteRequest = NSBatchDeleteRequest(fetchRequest: request) do { try container.viewContext.execute(deleteRequest) save() } catch { print("Failed to clear history: \(error.localizedDescription)") } } // 删除数据库文件 private func deleteDatabaseFiles() { let fileManager = FileManager.default // 获取应用支持目录 guard let appSupportURL = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first else { print("❌ 无法获取应用支持目录") return } // 删除MyQrCode.sqlite及其相关文件 let databaseName = "MyQrCode" let possibleFiles = [ appSupportURL.appendingPathComponent("\(databaseName).sqlite"), appSupportURL.appendingPathComponent("\(databaseName).sqlite-shm"), appSupportURL.appendingPathComponent("\(databaseName).sqlite-wal") ] for fileURL in possibleFiles { if fileManager.fileExists(atPath: fileURL.path) { do { try fileManager.removeItem(at: fileURL) print("✅ 删除数据库文件: \(fileURL.lastPathComponent)") } catch { print("❌ 删除数据库文件失败: \(error)") } } } // 也检查文档目录 guard let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first else { return } let documentFiles = [ documentsURL.appendingPathComponent("\(databaseName).sqlite"), documentsURL.appendingPathComponent("\(databaseName).sqlite-shm"), documentsURL.appendingPathComponent("\(databaseName).sqlite-wal") ] for fileURL in documentFiles { if fileManager.fileExists(atPath: fileURL.path) { do { try fileManager.removeItem(at: fileURL) print("✅ 删除数据库文件: \(fileURL.lastPathComponent)") } catch { print("❌ 删除数据库文件失败: \(error)") } } } } // 搜索历史记录 func searchHistoryItems(query: String) -> [HistoryItem] { let request: NSFetchRequest = HistoryItem.fetchRequest() if !query.isEmpty { let contentPredicate = NSPredicate(format: "content CONTAINS[cd] %@", query) let barcodeTypePredicate = NSPredicate(format: "barcodeType CONTAINS[cd] %@", query) let qrCodeTypePredicate = NSPredicate(format: "qrCodeType CONTAINS[cd] %@", query) let compoundPredicate = NSCompoundPredicate( orPredicateWithSubpredicates: [ contentPredicate, barcodeTypePredicate, qrCodeTypePredicate ] ) request.predicate = compoundPredicate } request.sortDescriptors = [NSSortDescriptor(keyPath: \HistoryItem.createdAt, ascending: false)] do { return try container.viewContext.fetch(request) } catch { print("搜索历史记录失败: \(error.localizedDescription)") return [] } } // 按类型过滤 func filterByType(_ type: DataType) -> [HistoryItem] { let request: NSFetchRequest = HistoryItem.fetchRequest() request.predicate = NSPredicate(format: "dataType == %@", type.rawValue) request.sortDescriptors = [NSSortDescriptor(keyPath: \HistoryItem.createdAt, ascending: false)] do { return try container.viewContext.fetch(request) } catch { print("按类型过滤失败: \(error.localizedDescription)") return [] } } // 按来源过滤 func filterBySource(_ source: DataSource) -> [HistoryItem] { let request: NSFetchRequest = HistoryItem.fetchRequest() request.predicate = NSPredicate(format: "dataSource == %@", source.rawValue) request.sortDescriptors = [NSSortDescriptor(keyPath: \HistoryItem.createdAt, ascending: false)] do { return try container.viewContext.fetch(request) } catch { print("按来源过滤失败: \(error.localizedDescription)") return [] } } // 获取收藏项目 func getFavoriteItems() -> [HistoryItem] { let request: NSFetchRequest = HistoryItem.fetchRequest() request.predicate = NSPredicate(format: "isFavorite == YES") request.sortDescriptors = [NSSortDescriptor(keyPath: \HistoryItem.createdAt, ascending: false)] do { return try container.viewContext.fetch(request) } catch { print("获取收藏项目失败: \(error.localizedDescription)") return [] } } }