You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

231 lines
8.6 KiB

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> = 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<NSFetchRequestResult> = 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> = 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> = 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> = 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> = 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 []
}
}
}