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("Core Data 加载失败: \(error.localizedDescription)") // 如果是架构不匹配错误,删除数据库文件并重新创建 if let nsError = error as NSError?, nsError.domain == NSCocoaErrorDomain && (nsError.code == 134030 || nsError.code == 134140) { print("🔄 检测到架构不匹配,删除现有数据库文件") self.deleteDatabaseFiles() // 重新加载持久化存储 self.container.loadPersistentStores { _, reloadError in if let reloadError = reloadError { print("❌ 重新加载Core Data失败: \(reloadError.localizedDescription)") } else { print("✅ Core Data重新加载成功") } } } } } // 启用自动合并更改 container.viewContext.automaticallyMergesChangesFromParent = true container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy } // 保存上下文 func save() { let context = container.viewContext if context.hasChanges { do { try context.save() print("✅ Core Data保存成功") } catch { print("❌ Core Data保存失败: \(error.localizedDescription)") print("❌ 错误详情: \(error)") // 如果是NSError,打印更多信息 if let nsError = error as NSError? { print("❌ 错误域: \(nsError.domain)") print("❌ 错误代码: \(nsError.code)") print("❌ 用户信息: \(nsError.userInfo)") // 检查是否是Transformable属性错误 if nsError.domain == NSCocoaErrorDomain && nsError.code == 134030 { print("❌ 可能是Transformable属性编码错误") } } } } else { print("ℹ️ 没有更改需要保存") } } // 获取历史记录 func fetchHistoryItems() -> [HistoryItem] { let request: NSFetchRequest = HistoryItem.fetchRequest() request.sortDescriptors = [NSSortDescriptor(keyPath: \HistoryItem.createdAt, ascending: false)] do { return try container.viewContext.fetch(request) } catch { print("获取历史记录失败: \(error.localizedDescription)") return [] } } // 添加历史记录 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("清空历史记录失败: \(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 [] } } }