|
|
@ -15,6 +15,8 @@ public class ChatStorageHelper { |
|
|
private let dbPath: String |
|
|
private let dbPath: String |
|
|
private let logger = OSLog(subsystem: "com.yunqiinnovation.chat_storage", category: "ChatStorageHelper") |
|
|
private let logger = OSLog(subsystem: "com.yunqiinnovation.chat_storage", category: "ChatStorageHelper") |
|
|
|
|
|
|
|
|
|
|
|
private let dbQueue = DispatchQueue(label: "com.yunqiinnovation.chat_storage.dbQueue") |
|
|
|
|
|
|
|
|
private init() { |
|
|
private init() { |
|
|
// 获取文档目录路径(不变) |
|
|
// 获取文档目录路径(不变) |
|
|
let fileURL = try! FileManager.default |
|
|
let fileURL = try! FileManager.default |
|
|
@ -23,6 +25,7 @@ public class ChatStorageHelper { |
|
|
|
|
|
|
|
|
dbPath = fileURL.path |
|
|
dbPath = fileURL.path |
|
|
|
|
|
|
|
|
|
|
|
dbQueue.sync { |
|
|
// 打开数据库(不变) |
|
|
// 打开数据库(不变) |
|
|
if sqlite3_open(dbPath, &db) != SQLITE_OK { |
|
|
if sqlite3_open(dbPath, &db) != SQLITE_OK { |
|
|
let errmsg = String(cString: sqlite3_errmsg(db)!) |
|
|
let errmsg = String(cString: sqlite3_errmsg(db)!) |
|
|
@ -52,12 +55,15 @@ public class ChatStorageHelper { |
|
|
os_log("创建表失败: %{public}@", log: logger, type: .error, errmsg) |
|
|
os_log("创建表失败: %{public}@", log: logger, type: .error, errmsg) |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
deinit { |
|
|
deinit { |
|
|
|
|
|
dbQueue.sync { |
|
|
if db != nil { |
|
|
if db != nil { |
|
|
sqlite3_close(db) |
|
|
sqlite3_close(db) |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 保存消息到数据库 |
|
|
* 保存消息到数据库 |
|
|
@ -68,6 +74,8 @@ public class ChatStorageHelper { |
|
|
* @return 插入的消息ID,失败则返回-1 |
|
|
* @return 插入的消息ID,失败则返回-1 |
|
|
*/ |
|
|
*/ |
|
|
public func saveMessage(agentId: String, sessionId: String, message: String, sender: String, metadata: String?) -> Int64 { |
|
|
public func saveMessage(agentId: String, sessionId: String, message: String, sender: String, metadata: String?) -> Int64 { |
|
|
|
|
|
var result: Int64 = -1 |
|
|
|
|
|
dbQueue.sync { |
|
|
// 关键修改:用 INSERT OR REPLACE 替换 INSERT,支持冲突时更新 |
|
|
// 关键修改:用 INSERT OR REPLACE 替换 INSERT,支持冲突时更新 |
|
|
let insertStatementString = """ |
|
|
let insertStatementString = """ |
|
|
INSERT OR REPLACE INTO messages |
|
|
INSERT OR REPLACE INTO messages |
|
|
@ -95,21 +103,19 @@ public class ChatStorageHelper { |
|
|
// 执行语句(冲突时会自动替换,返回新的 rowid) |
|
|
// 执行语句(冲突时会自动替换,返回新的 rowid) |
|
|
if sqlite3_step(insertStatement) == SQLITE_DONE { |
|
|
if sqlite3_step(insertStatement) == SQLITE_DONE { |
|
|
let id = sqlite3_last_insert_rowid(db) // 替换后返回新的 id(原 id 会被删除) |
|
|
let id = sqlite3_last_insert_rowid(db) // 替换后返回新的 id(原 id 会被删除) |
|
|
sqlite3_finalize(insertStatement) |
|
|
|
|
|
os_log("消息保存成功(新增/更新),id: %{public}lld", log: logger, type: .info, id) |
|
|
os_log("消息保存成功(新增/更新),id: %{public}lld", log: logger, type: .info, id) |
|
|
return id |
|
|
result = id |
|
|
} else { |
|
|
} else { |
|
|
let errmsg = String(cString: sqlite3_errmsg(db)!) |
|
|
let errmsg = String(cString: sqlite3_errmsg(db)!) |
|
|
os_log("插入/更新消息失败: %{public}@", log: logger, type: .error, errmsg) |
|
|
os_log("插入/更新消息失败: %{public}@", log: logger, type: .error, errmsg) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
sqlite3_finalize(insertStatement) |
|
|
sqlite3_finalize(insertStatement) |
|
|
} else { |
|
|
} else { |
|
|
let errmsg = String(cString: sqlite3_errmsg(db)!) |
|
|
let errmsg = String(cString: sqlite3_errmsg(db)!) |
|
|
os_log("插入/更新消息语句准备失败: %{public}@", log: logger, type: .error, errmsg) |
|
|
os_log("插入/更新消息语句准备失败: %{public}@", log: logger, type: .error, errmsg) |
|
|
} |
|
|
} |
|
|
|
|
|
} |
|
|
return -1 |
|
|
return result |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
@ -120,6 +126,8 @@ public class ChatStorageHelper { |
|
|
* @return 消息列表的JSON字符串 |
|
|
* @return 消息列表的JSON字符串 |
|
|
*/ |
|
|
*/ |
|
|
public func getMessages(agentId: String, page: Int, pageSize: Int) -> String { |
|
|
public func getMessages(agentId: String, page: Int, pageSize: Int) -> String { |
|
|
|
|
|
var result = "{\"messages\":[],\"page\":\(page),\"pageSize\":\(pageSize),\"totalCount\":0,\"totalPages\":0}" |
|
|
|
|
|
dbQueue.sync { |
|
|
let offset = (page - 1) * pageSize |
|
|
let offset = (page - 1) * pageSize |
|
|
var messagesArray: [[String: Any]] = [] |
|
|
var messagesArray: [[String: Any]] = [] |
|
|
|
|
|
|
|
|
@ -196,7 +204,7 @@ public class ChatStorageHelper { |
|
|
sqlite3_finalize(queryStatement) |
|
|
sqlite3_finalize(queryStatement) |
|
|
|
|
|
|
|
|
// 构建与Android版本一致的返回格式 |
|
|
// 构建与Android版本一致的返回格式 |
|
|
let result: [String: Any] = [ |
|
|
let resultDict: [String: Any] = [ |
|
|
"messages": messagesArray, |
|
|
"messages": messagesArray, |
|
|
"page": page, |
|
|
"page": page, |
|
|
"pageSize": pageSize, |
|
|
"pageSize": pageSize, |
|
|
@ -205,9 +213,9 @@ public class ChatStorageHelper { |
|
|
] |
|
|
] |
|
|
os_log("getMessages: 读取历史记录: %{public}@", log: logger, type: .error, messagesArray) |
|
|
os_log("getMessages: 读取历史记录: %{public}@", log: logger, type: .error, messagesArray) |
|
|
do { |
|
|
do { |
|
|
let jsonData = try JSONSerialization.data(withJSONObject: result, options: []) |
|
|
let jsonData = try JSONSerialization.data(withJSONObject: resultDict, options: []) |
|
|
if let jsonString = String(data: jsonData, encoding: .utf8) { |
|
|
if let jsonString = String(data: jsonData, encoding: .utf8) { |
|
|
return jsonString |
|
|
result = jsonString |
|
|
} |
|
|
} |
|
|
} catch { |
|
|
} catch { |
|
|
os_log("getMessages: JSON转换失败: %{public}@", log: logger, type: .error, error.localizedDescription) |
|
|
os_log("getMessages: JSON转换失败: %{public}@", log: logger, type: .error, error.localizedDescription) |
|
|
@ -216,26 +224,8 @@ public class ChatStorageHelper { |
|
|
let errmsg = String(cString: sqlite3_errmsg(db)!) |
|
|
let errmsg = String(cString: sqlite3_errmsg(db)!) |
|
|
os_log("getMessages: SQL准备失败: %{public}@", log: logger, type: .error, errmsg) |
|
|
os_log("getMessages: SQL准备失败: %{public}@", log: logger, type: .error, errmsg) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 返回空结果,但保持格式一致 |
|
|
|
|
|
let emptyResult: [String: Any] = [ |
|
|
|
|
|
"messages": [], |
|
|
|
|
|
"page": page, |
|
|
|
|
|
"pageSize": pageSize, |
|
|
|
|
|
"totalCount": 0, |
|
|
|
|
|
"totalPages": 0 |
|
|
|
|
|
] |
|
|
|
|
|
|
|
|
|
|
|
do { |
|
|
|
|
|
let jsonData = try JSONSerialization.data(withJSONObject: emptyResult, options: []) |
|
|
|
|
|
if let jsonString = String(data: jsonData, encoding: .utf8) { |
|
|
|
|
|
return jsonString |
|
|
|
|
|
} |
|
|
|
|
|
} catch { |
|
|
|
|
|
os_log("getMessages: 空结果JSON转换失败: %{public}@", log: logger, type: .error, error.localizedDescription) |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
return result |
|
|
return "{\"messages\":[],\"page\":\(page),\"pageSize\":\(pageSize),\"totalCount\":0,\"totalPages\":0}" |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
@ -245,6 +235,8 @@ public class ChatStorageHelper { |
|
|
* @return 是否删除成功 |
|
|
* @return 是否删除成功 |
|
|
*/ |
|
|
*/ |
|
|
public func deleteMessages(agentId: String?, messageIds: [Int]?) -> Bool { |
|
|
public func deleteMessages(agentId: String?, messageIds: [Int]?) -> Bool { |
|
|
|
|
|
var result = false |
|
|
|
|
|
dbQueue.sync { |
|
|
if let agentId = agentId { |
|
|
if let agentId = agentId { |
|
|
let deleteString = "DELETE FROM messages WHERE agent_id = ?;" |
|
|
let deleteString = "DELETE FROM messages WHERE agent_id = ?;" |
|
|
var deleteStatement: OpaquePointer? |
|
|
var deleteStatement: OpaquePointer? |
|
|
@ -254,7 +246,7 @@ public class ChatStorageHelper { |
|
|
|
|
|
|
|
|
if sqlite3_step(deleteStatement) == SQLITE_DONE { |
|
|
if sqlite3_step(deleteStatement) == SQLITE_DONE { |
|
|
sqlite3_finalize(deleteStatement) |
|
|
sqlite3_finalize(deleteStatement) |
|
|
return true |
|
|
result = true |
|
|
} else { |
|
|
} else { |
|
|
let errmsg = String(cString: sqlite3_errmsg(db)!) |
|
|
let errmsg = String(cString: sqlite3_errmsg(db)!) |
|
|
os_log("删除会话消息失败: %{public}@", log: logger, type: .error, errmsg) |
|
|
os_log("删除会话消息失败: %{public}@", log: logger, type: .error, errmsg) |
|
|
@ -277,13 +269,11 @@ public class ChatStorageHelper { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if sqlite3_step(deleteStatement) == SQLITE_DONE { |
|
|
if sqlite3_step(deleteStatement) == SQLITE_DONE { |
|
|
sqlite3_finalize(deleteStatement) |
|
|
result = true |
|
|
return true |
|
|
|
|
|
} else { |
|
|
} else { |
|
|
let errmsg = String(cString: sqlite3_errmsg(db)!) |
|
|
let errmsg = String(cString: sqlite3_errmsg(db)!) |
|
|
os_log("删除特定消息失败: %{public}@", log: logger, type: .error, errmsg) |
|
|
os_log("删除特定消息失败: %{public}@", log: logger, type: .error, errmsg) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
sqlite3_finalize(deleteStatement) |
|
|
sqlite3_finalize(deleteStatement) |
|
|
} else { |
|
|
} else { |
|
|
let errmsg = String(cString: sqlite3_errmsg(db)!) |
|
|
let errmsg = String(cString: sqlite3_errmsg(db)!) |
|
|
@ -291,10 +281,9 @@ public class ChatStorageHelper { |
|
|
} |
|
|
} |
|
|
} else { |
|
|
} else { |
|
|
os_log("删除消息参数无效 - agentId和messageIds都为空", log: logger, type: .error) |
|
|
os_log("删除消息参数无效 - agentId和messageIds都为空", log: logger, type: .error) |
|
|
return false |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
} |
|
|
return false |
|
|
return result |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
@ -302,16 +291,19 @@ public class ChatStorageHelper { |
|
|
* @return 是否清空成功 |
|
|
* @return 是否清空成功 |
|
|
*/ |
|
|
*/ |
|
|
public func clearDatabase() -> Bool { |
|
|
public func clearDatabase() -> Bool { |
|
|
|
|
|
var result = false |
|
|
|
|
|
dbQueue.sync { |
|
|
let deleteString = "DELETE FROM messages;" |
|
|
let deleteString = "DELETE FROM messages;" |
|
|
|
|
|
|
|
|
if sqlite3_exec(db, deleteString, nil, nil, nil) == SQLITE_OK { |
|
|
if sqlite3_exec(db, deleteString, nil, nil, nil) == SQLITE_OK { |
|
|
return true |
|
|
result = true |
|
|
} else { |
|
|
} else { |
|
|
let errmsg = String(cString: sqlite3_errmsg(db)!) |
|
|
let errmsg = String(cString: sqlite3_errmsg(db)!) |
|
|
os_log("清空数据库失败: %{public}@", log: logger, type: .error, errmsg) |
|
|
os_log("清空数据库失败: %{public}@", log: logger, type: .error, errmsg) |
|
|
return false |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
return result |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 获取指定会话的最近N条消息 |
|
|
* 获取指定会话的最近N条消息 |
|
|
@ -321,7 +313,7 @@ public class ChatStorageHelper { |
|
|
*/ |
|
|
*/ |
|
|
public func getRecentMessages(agentId: String, limit: Int) -> [[String: Any]] { |
|
|
public func getRecentMessages(agentId: String, limit: Int) -> [[String: Any]] { |
|
|
var messages: [[String: Any]] = [] |
|
|
var messages: [[String: Any]] = [] |
|
|
|
|
|
dbQueue.sync { |
|
|
// 首先检查数据库中是否有该会话的消息 |
|
|
// 首先检查数据库中是否有该会话的消息 |
|
|
let countQuery = "SELECT COUNT(*) FROM messages WHERE agent_id = ?" |
|
|
let countQuery = "SELECT COUNT(*) FROM messages WHERE agent_id = ?" |
|
|
var countStatement: OpaquePointer? |
|
|
var countStatement: OpaquePointer? |
|
|
@ -389,7 +381,7 @@ public class ChatStorageHelper { |
|
|
let errmsg = String(cString: sqlite3_errmsg(db)!) |
|
|
let errmsg = String(cString: sqlite3_errmsg(db)!) |
|
|
os_log("查询最近消息失败: %{public}@", log: logger, type: .error, errmsg) |
|
|
os_log("查询最近消息失败: %{public}@", log: logger, type: .error, errmsg) |
|
|
} |
|
|
} |
|
|
|
|
|
} |
|
|
return messages |
|
|
return messages |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|