|
|
|
@ -15,6 +15,8 @@ public class ChatStorageHelper { |
|
|
|
private let dbPath: String |
|
|
|
private let logger = OSLog(subsystem: "com.yunqiinnovation.chat_storage", category: "ChatStorageHelper") |
|
|
|
|
|
|
|
private let dbQueue = DispatchQueue(label: "com.yunqiinnovation.chat_storage.dbQueue") |
|
|
|
|
|
|
|
private init() { |
|
|
|
// 获取文档目录路径(不变) |
|
|
|
let fileURL = try! FileManager.default |
|
|
|
@ -23,39 +25,43 @@ public class ChatStorageHelper { |
|
|
|
|
|
|
|
dbPath = fileURL.path |
|
|
|
|
|
|
|
// 打开数据库(不变) |
|
|
|
if sqlite3_open(dbPath, &db) != SQLITE_OK { |
|
|
|
let errmsg = String(cString: sqlite3_errmsg(db)!) |
|
|
|
os_log("无法打开数据库: %{public}@", log: logger, type: .error, errmsg) |
|
|
|
return |
|
|
|
} |
|
|
|
dbQueue.sync { |
|
|
|
// 打开数据库(不变) |
|
|
|
if sqlite3_open(dbPath, &db) != SQLITE_OK { |
|
|
|
let errmsg = String(cString: sqlite3_errmsg(db)!) |
|
|
|
os_log("无法打开数据库: %{public}@", log: logger, type: .error, errmsg) |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
// 关键修改:新增 UNIQUE (agent_id, session_id, sender) 联合唯一索引 |
|
|
|
let createTableString = """ |
|
|
|
CREATE TABLE IF NOT EXISTS messages ( |
|
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT, |
|
|
|
agent_id TEXT NOT NULL, |
|
|
|
session_id TEXT NOT NULL, |
|
|
|
message TEXT NOT NULL, |
|
|
|
sender TEXT NOT NULL, |
|
|
|
metadata TEXT, |
|
|
|
created_at INTEGER NOT NULL, |
|
|
|
UNIQUE (agent_id, session_id, sender) ON CONFLICT REPLACE |
|
|
|
); |
|
|
|
CREATE INDEX IF NOT EXISTS idx_agent_id ON messages (agent_id); |
|
|
|
CREATE INDEX IF NOT EXISTS idx_session_id ON messages (session_id); |
|
|
|
CREATE INDEX IF NOT EXISTS idx_created_at ON messages (created_at); |
|
|
|
""" |
|
|
|
|
|
|
|
if sqlite3_exec(db, createTableString, nil, nil, nil) != SQLITE_OK { |
|
|
|
let errmsg = String(cString: sqlite3_errmsg(db)!) |
|
|
|
os_log("创建表失败: %{public}@", log: logger, type: .error, errmsg) |
|
|
|
// 关键修改:新增 UNIQUE (agent_id, session_id, sender) 联合唯一索引 |
|
|
|
let createTableString = """ |
|
|
|
CREATE TABLE IF NOT EXISTS messages ( |
|
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT, |
|
|
|
agent_id TEXT NOT NULL, |
|
|
|
session_id TEXT NOT NULL, |
|
|
|
message TEXT NOT NULL, |
|
|
|
sender TEXT NOT NULL, |
|
|
|
metadata TEXT, |
|
|
|
created_at INTEGER NOT NULL, |
|
|
|
UNIQUE (agent_id, session_id, sender) ON CONFLICT REPLACE |
|
|
|
); |
|
|
|
CREATE INDEX IF NOT EXISTS idx_agent_id ON messages (agent_id); |
|
|
|
CREATE INDEX IF NOT EXISTS idx_session_id ON messages (session_id); |
|
|
|
CREATE INDEX IF NOT EXISTS idx_created_at ON messages (created_at); |
|
|
|
""" |
|
|
|
|
|
|
|
if sqlite3_exec(db, createTableString, nil, nil, nil) != SQLITE_OK { |
|
|
|
let errmsg = String(cString: sqlite3_errmsg(db)!) |
|
|
|
os_log("创建表失败: %{public}@", log: logger, type: .error, errmsg) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
deinit { |
|
|
|
if db != nil { |
|
|
|
sqlite3_close(db) |
|
|
|
dbQueue.sync { |
|
|
|
if db != nil { |
|
|
|
sqlite3_close(db) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@ -68,48 +74,48 @@ public class ChatStorageHelper { |
|
|
|
* @return 插入的消息ID,失败则返回-1 |
|
|
|
*/ |
|
|
|
public func saveMessage(agentId: String, sessionId: String, message: String, sender: String, metadata: String?) -> Int64 { |
|
|
|
// 关键修改:用 INSERT OR REPLACE 替换 INSERT,支持冲突时更新 |
|
|
|
let insertStatementString = """ |
|
|
|
INSERT OR REPLACE INTO messages |
|
|
|
(agent_id, session_id, message, sender, metadata, created_at) |
|
|
|
VALUES (?, ?, ?, ?, ?, ?); |
|
|
|
""" |
|
|
|
var insertStatement: OpaquePointer? |
|
|
|
|
|
|
|
if sqlite3_prepare_v2(db, insertStatementString, -1, &insertStatement, nil) == SQLITE_OK { |
|
|
|
// 绑定参数(逻辑不变,确保三个唯一字段正确传入) |
|
|
|
sqlite3_bind_text(insertStatement, 1, (agentId as NSString).utf8String, -1, nil) |
|
|
|
sqlite3_bind_text(insertStatement, 2, (sessionId as NSString).utf8String, -1, nil) |
|
|
|
sqlite3_bind_text(insertStatement, 3, (message as NSString).utf8String, -1, nil) |
|
|
|
sqlite3_bind_text(insertStatement, 4, (sender as NSString).utf8String, -1, nil) |
|
|
|
|
|
|
|
if let metadata = metadata { |
|
|
|
sqlite3_bind_text(insertStatement, 5, (metadata as NSString).utf8String, -1, nil) |
|
|
|
} else { |
|
|
|
sqlite3_bind_null(insertStatement, 5) |
|
|
|
} |
|
|
|
var result: Int64 = -1 |
|
|
|
dbQueue.sync { |
|
|
|
// 关键修改:用 INSERT OR REPLACE 替换 INSERT,支持冲突时更新 |
|
|
|
let insertStatementString = """ |
|
|
|
INSERT OR REPLACE INTO messages |
|
|
|
(agent_id, session_id, message, sender, metadata, created_at) |
|
|
|
VALUES (?, ?, ?, ?, ?, ?); |
|
|
|
""" |
|
|
|
var insertStatement: OpaquePointer? |
|
|
|
|
|
|
|
if sqlite3_prepare_v2(db, insertStatementString, -1, &insertStatement, nil) == SQLITE_OK { |
|
|
|
// 绑定参数(逻辑不变,确保三个唯一字段正确传入) |
|
|
|
sqlite3_bind_text(insertStatement, 1, (agentId as NSString).utf8String, -1, nil) |
|
|
|
sqlite3_bind_text(insertStatement, 2, (sessionId as NSString).utf8String, -1, nil) |
|
|
|
sqlite3_bind_text(insertStatement, 3, (message as NSString).utf8String, -1, nil) |
|
|
|
sqlite3_bind_text(insertStatement, 4, (sender as NSString).utf8String, -1, nil) |
|
|
|
|
|
|
|
if let metadata = metadata { |
|
|
|
sqlite3_bind_text(insertStatement, 5, (metadata as NSString).utf8String, -1, nil) |
|
|
|
} else { |
|
|
|
sqlite3_bind_null(insertStatement, 5) |
|
|
|
} |
|
|
|
|
|
|
|
let currentTime = Int(Date().timeIntervalSince1970) |
|
|
|
sqlite3_bind_int(insertStatement, 6, Int32(currentTime)) |
|
|
|
let currentTime = Int(Date().timeIntervalSince1970) |
|
|
|
sqlite3_bind_int(insertStatement, 6, Int32(currentTime)) |
|
|
|
|
|
|
|
// 执行语句(冲突时会自动替换,返回新的 rowid) |
|
|
|
if sqlite3_step(insertStatement) == SQLITE_DONE { |
|
|
|
let id = sqlite3_last_insert_rowid(db) // 替换后返回新的 id(原 id 会被删除) |
|
|
|
// 执行语句(冲突时会自动替换,返回新的 rowid) |
|
|
|
if sqlite3_step(insertStatement) == SQLITE_DONE { |
|
|
|
let id = sqlite3_last_insert_rowid(db) // 替换后返回新的 id(原 id 会被删除) |
|
|
|
os_log("消息保存成功(新增/更新),id: %{public}lld", log: logger, type: .info, id) |
|
|
|
result = id |
|
|
|
} else { |
|
|
|
let errmsg = String(cString: sqlite3_errmsg(db)!) |
|
|
|
os_log("插入/更新消息失败: %{public}@", log: logger, type: .error, errmsg) |
|
|
|
} |
|
|
|
sqlite3_finalize(insertStatement) |
|
|
|
os_log("消息保存成功(新增/更新),id: %{public}lld", log: logger, type: .info, id) |
|
|
|
return id |
|
|
|
} else { |
|
|
|
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) |
|
|
|
} else { |
|
|
|
let errmsg = String(cString: sqlite3_errmsg(db)!) |
|
|
|
os_log("插入/更新消息语句准备失败: %{public}@", log: logger, type: .error, errmsg) |
|
|
|
} |
|
|
|
|
|
|
|
return -1 |
|
|
|
return result |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
@ -120,122 +126,106 @@ public class ChatStorageHelper { |
|
|
|
* @return 消息列表的JSON字符串 |
|
|
|
*/ |
|
|
|
public func getMessages(agentId: String, page: Int, pageSize: Int) -> String { |
|
|
|
let offset = (page - 1) * pageSize |
|
|
|
var messagesArray: [[String: Any]] = [] |
|
|
|
|
|
|
|
// 首先获取总记录数 |
|
|
|
let countQuery = "SELECT COUNT(*) FROM messages WHERE agent_id = ?" |
|
|
|
var countStatement: OpaquePointer? |
|
|
|
var totalCount = 0 |
|
|
|
|
|
|
|
if sqlite3_prepare_v2(db, countQuery, -1, &countStatement, nil) == SQLITE_OK { |
|
|
|
sqlite3_bind_text(countStatement, 1, (agentId as NSString).utf8String, -1, nil) |
|
|
|
if sqlite3_step(countStatement) == SQLITE_ROW { |
|
|
|
totalCount = Int(sqlite3_column_int(countStatement, 0)) |
|
|
|
var result = "{\"messages\":[],\"page\":\(page),\"pageSize\":\(pageSize),\"totalCount\":0,\"totalPages\":0}" |
|
|
|
dbQueue.sync { |
|
|
|
let offset = (page - 1) * pageSize |
|
|
|
var messagesArray: [[String: Any]] = [] |
|
|
|
|
|
|
|
// 首先获取总记录数 |
|
|
|
let countQuery = "SELECT COUNT(*) FROM messages WHERE agent_id = ?" |
|
|
|
var countStatement: OpaquePointer? |
|
|
|
var totalCount = 0 |
|
|
|
|
|
|
|
if sqlite3_prepare_v2(db, countQuery, -1, &countStatement, nil) == SQLITE_OK { |
|
|
|
sqlite3_bind_text(countStatement, 1, (agentId as NSString).utf8String, -1, nil) |
|
|
|
if sqlite3_step(countStatement) == SQLITE_ROW { |
|
|
|
totalCount = Int(sqlite3_column_int(countStatement, 0)) |
|
|
|
} |
|
|
|
sqlite3_finalize(countStatement) |
|
|
|
} |
|
|
|
sqlite3_finalize(countStatement) |
|
|
|
} |
|
|
|
|
|
|
|
// 计算总页数 |
|
|
|
let totalPages = totalCount == 0 ? 0 : (totalCount + pageSize - 1) / pageSize |
|
|
|
// 计算总页数 |
|
|
|
let totalPages = totalCount == 0 ? 0 : (totalCount + pageSize - 1) / pageSize |
|
|
|
|
|
|
|
let queryString = """ |
|
|
|
SELECT id, session_id, message, sender, metadata, created_at |
|
|
|
FROM messages |
|
|
|
WHERE agent_id = ? |
|
|
|
ORDER BY created_at DESC |
|
|
|
LIMIT ? OFFSET ? |
|
|
|
""" |
|
|
|
|
|
|
|
let queryString = """ |
|
|
|
SELECT id, session_id, message, sender, metadata, created_at |
|
|
|
FROM messages |
|
|
|
WHERE agent_id = ? |
|
|
|
ORDER BY created_at DESC |
|
|
|
LIMIT ? OFFSET ? |
|
|
|
""" |
|
|
|
var queryStatement: OpaquePointer? |
|
|
|
|
|
|
|
var queryStatement: OpaquePointer? |
|
|
|
if sqlite3_prepare_v2(db, queryString, -1, &queryStatement, nil) == SQLITE_OK { |
|
|
|
sqlite3_bind_text(queryStatement, 1, (agentId as NSString).utf8String, -1, nil) |
|
|
|
sqlite3_bind_int(queryStatement, 2, Int32(pageSize)) |
|
|
|
sqlite3_bind_int(queryStatement, 3, Int32(offset)) |
|
|
|
|
|
|
|
if sqlite3_prepare_v2(db, queryString, -1, &queryStatement, nil) == SQLITE_OK { |
|
|
|
sqlite3_bind_text(queryStatement, 1, (agentId as NSString).utf8String, -1, nil) |
|
|
|
sqlite3_bind_int(queryStatement, 2, Int32(pageSize)) |
|
|
|
sqlite3_bind_int(queryStatement, 3, Int32(offset)) |
|
|
|
while sqlite3_step(queryStatement) == SQLITE_ROW { |
|
|
|
let id = sqlite3_column_int(queryStatement, 0) |
|
|
|
|
|
|
|
while sqlite3_step(queryStatement) == SQLITE_ROW { |
|
|
|
let id = sqlite3_column_int(queryStatement, 0) |
|
|
|
let sessionIdPtr = sqlite3_column_text(queryStatement, 1) |
|
|
|
let sessionId = sessionIdPtr != nil ? String(cString: sessionIdPtr!) : "" |
|
|
|
|
|
|
|
let sessionIdPtr = sqlite3_column_text(queryStatement, 1) |
|
|
|
let sessionId = sessionIdPtr != nil ? String(cString: sessionIdPtr!) : "" |
|
|
|
let messagePtr = sqlite3_column_text(queryStatement, 2) |
|
|
|
let message = messagePtr != nil ? String(cString: messagePtr!) : "" |
|
|
|
|
|
|
|
let messagePtr = sqlite3_column_text(queryStatement, 2) |
|
|
|
let message = messagePtr != nil ? String(cString: messagePtr!) : "" |
|
|
|
let senderPtr = sqlite3_column_text(queryStatement, 3) |
|
|
|
let sender = senderPtr != nil ? String(cString: senderPtr!) : "" |
|
|
|
|
|
|
|
let senderPtr = sqlite3_column_text(queryStatement, 3) |
|
|
|
let sender = senderPtr != nil ? String(cString: senderPtr!) : "" |
|
|
|
let metadataPtr = sqlite3_column_text(queryStatement, 4) |
|
|
|
let metadata = metadataPtr != nil ? String(cString: metadataPtr!) : nil |
|
|
|
|
|
|
|
let metadataPtr = sqlite3_column_text(queryStatement, 4) |
|
|
|
let metadata = metadataPtr != nil ? String(cString: metadataPtr!) : nil |
|
|
|
let createdAt = sqlite3_column_int(queryStatement, 5) |
|
|
|
|
|
|
|
let createdAt = sqlite3_column_int(queryStatement, 5) |
|
|
|
// 将时间戳转换为ISO 8601格式的字符串 |
|
|
|
let date = Date(timeIntervalSince1970: TimeInterval(createdAt)) |
|
|
|
let formatter = ISO8601DateFormatter() |
|
|
|
let timestamp = formatter.string(from: date) |
|
|
|
os_log("getMessages: 读取历史记录: %{public}@", log: logger, type: .info, timestamp) |
|
|
|
|
|
|
|
// 将时间戳转换为ISO 8601格式的字符串 |
|
|
|
let date = Date(timeIntervalSince1970: TimeInterval(createdAt)) |
|
|
|
let formatter = ISO8601DateFormatter() |
|
|
|
let timestamp = formatter.string(from: date) |
|
|
|
os_log("getMessages: 读取历史记录: %{public}@", log: logger, type: .info, timestamp) |
|
|
|
var messageDict: [String: Any] = [ |
|
|
|
"id": id, |
|
|
|
"agentId": agentId, // 添加agentId字段 |
|
|
|
"sessionId": sessionId, // 添加sessionId字段 |
|
|
|
"message": message, |
|
|
|
"sender": sender, |
|
|
|
"timestamp": timestamp // 使用timestamp而不是created_at |
|
|
|
] |
|
|
|
|
|
|
|
var messageDict: [String: Any] = [ |
|
|
|
"id": id, |
|
|
|
"agentId": agentId, // 添加agentId字段 |
|
|
|
"sessionId": sessionId, // 添加sessionId字段 |
|
|
|
"message": message, |
|
|
|
"sender": sender, |
|
|
|
"timestamp": timestamp // 使用timestamp而不是created_at |
|
|
|
] |
|
|
|
if let metadata = metadata { |
|
|
|
messageDict["metadata"] = metadata |
|
|
|
} |
|
|
|
|
|
|
|
if let metadata = metadata { |
|
|
|
messageDict["metadata"] = metadata |
|
|
|
messagesArray.append(messageDict) |
|
|
|
} |
|
|
|
|
|
|
|
messagesArray.append(messageDict) |
|
|
|
} |
|
|
|
sqlite3_finalize(queryStatement) |
|
|
|
|
|
|
|
sqlite3_finalize(queryStatement) |
|
|
|
|
|
|
|
// 构建与Android版本一致的返回格式 |
|
|
|
let result: [String: Any] = [ |
|
|
|
"messages": messagesArray, |
|
|
|
"page": page, |
|
|
|
"pageSize": pageSize, |
|
|
|
"totalCount": totalCount, |
|
|
|
"totalPages": totalPages |
|
|
|
] |
|
|
|
os_log("getMessages: 读取历史记录: %{public}@", log: logger, type: .error, messagesArray) |
|
|
|
do { |
|
|
|
let jsonData = try JSONSerialization.data(withJSONObject: result, options: []) |
|
|
|
if let jsonString = String(data: jsonData, encoding: .utf8) { |
|
|
|
return jsonString |
|
|
|
// 构建与Android版本一致的返回格式 |
|
|
|
let resultDict: [String: Any] = [ |
|
|
|
"messages": messagesArray, |
|
|
|
"page": page, |
|
|
|
"pageSize": pageSize, |
|
|
|
"totalCount": totalCount, |
|
|
|
"totalPages": totalPages |
|
|
|
] |
|
|
|
os_log("getMessages: 读取历史记录: %{public}@", log: logger, type: .error, messagesArray) |
|
|
|
do { |
|
|
|
let jsonData = try JSONSerialization.data(withJSONObject: resultDict, options: []) |
|
|
|
if let jsonString = String(data: jsonData, encoding: .utf8) { |
|
|
|
result = jsonString |
|
|
|
} |
|
|
|
} catch { |
|
|
|
os_log("getMessages: JSON转换失败: %{public}@", log: logger, type: .error, error.localizedDescription) |
|
|
|
} |
|
|
|
} catch { |
|
|
|
os_log("getMessages: JSON转换失败: %{public}@", log: logger, type: .error, error.localizedDescription) |
|
|
|
} |
|
|
|
} else { |
|
|
|
let errmsg = String(cString: sqlite3_errmsg(db)!) |
|
|
|
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 |
|
|
|
} else { |
|
|
|
let errmsg = String(cString: sqlite3_errmsg(db)!) |
|
|
|
os_log("getMessages: SQL准备失败: %{public}@", log: logger, type: .error, errmsg) |
|
|
|
} |
|
|
|
} catch { |
|
|
|
os_log("getMessages: 空结果JSON转换失败: %{public}@", log: logger, type: .error, error.localizedDescription) |
|
|
|
} |
|
|
|
|
|
|
|
return "{\"messages\":[],\"page\":\(page),\"pageSize\":\(pageSize),\"totalCount\":0,\"totalPages\":0}" |
|
|
|
return result |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
@ -245,56 +235,55 @@ public class ChatStorageHelper { |
|
|
|
* @return 是否删除成功 |
|
|
|
*/ |
|
|
|
public func deleteMessages(agentId: String?, messageIds: [Int]?) -> Bool { |
|
|
|
if let agentId = agentId { |
|
|
|
let deleteString = "DELETE FROM messages WHERE agent_id = ?;" |
|
|
|
var deleteStatement: OpaquePointer? |
|
|
|
|
|
|
|
if sqlite3_prepare_v2(db, deleteString, -1, &deleteStatement, nil) == SQLITE_OK { |
|
|
|
sqlite3_bind_text(deleteStatement, 1, (agentId as NSString).utf8String, -1, nil) |
|
|
|
var result = false |
|
|
|
dbQueue.sync { |
|
|
|
if let agentId = agentId { |
|
|
|
let deleteString = "DELETE FROM messages WHERE agent_id = ?;" |
|
|
|
var deleteStatement: OpaquePointer? |
|
|
|
|
|
|
|
if sqlite3_prepare_v2(db, deleteString, -1, &deleteStatement, nil) == SQLITE_OK { |
|
|
|
sqlite3_bind_text(deleteStatement, 1, (agentId as NSString).utf8String, -1, nil) |
|
|
|
|
|
|
|
if sqlite3_step(deleteStatement) == SQLITE_DONE { |
|
|
|
sqlite3_finalize(deleteStatement) |
|
|
|
result = true |
|
|
|
} else { |
|
|
|
let errmsg = String(cString: sqlite3_errmsg(db)!) |
|
|
|
os_log("删除会话消息失败: %{public}@", log: logger, type: .error, errmsg) |
|
|
|
} |
|
|
|
|
|
|
|
if sqlite3_step(deleteStatement) == SQLITE_DONE { |
|
|
|
sqlite3_finalize(deleteStatement) |
|
|
|
return true |
|
|
|
} else { |
|
|
|
let errmsg = String(cString: sqlite3_errmsg(db)!) |
|
|
|
os_log("删除会话消息失败: %{public}@", log: logger, type: .error, errmsg) |
|
|
|
} |
|
|
|
|
|
|
|
sqlite3_finalize(deleteStatement) |
|
|
|
} else { |
|
|
|
let errmsg = String(cString: sqlite3_errmsg(db)!) |
|
|
|
os_log("删除会话消息语句准备失败: %{public}@", log: logger, type: .error, errmsg) |
|
|
|
} |
|
|
|
} else if let messageIds = messageIds, !messageIds.isEmpty { |
|
|
|
// 构建参数占位符 |
|
|
|
let placeholders = String(repeating: "?,", count: messageIds.count).dropLast() |
|
|
|
let deleteString = "DELETE FROM messages WHERE id IN (\(placeholders));" |
|
|
|
var deleteStatement: OpaquePointer? |
|
|
|
|
|
|
|
if sqlite3_prepare_v2(db, deleteString, -1, &deleteStatement, nil) == SQLITE_OK { |
|
|
|
for (index, id) in messageIds.enumerated() { |
|
|
|
sqlite3_bind_int(deleteStatement, Int32(index + 1), Int32(id)) |
|
|
|
os_log("删除会话消息语句准备失败: %{public}@", log: logger, type: .error, errmsg) |
|
|
|
} |
|
|
|
|
|
|
|
if sqlite3_step(deleteStatement) == SQLITE_DONE { |
|
|
|
} else if let messageIds = messageIds, !messageIds.isEmpty { |
|
|
|
// 构建参数占位符 |
|
|
|
let placeholders = String(repeating: "?,", count: messageIds.count).dropLast() |
|
|
|
let deleteString = "DELETE FROM messages WHERE id IN (\(placeholders));" |
|
|
|
var deleteStatement: OpaquePointer? |
|
|
|
|
|
|
|
if sqlite3_prepare_v2(db, deleteString, -1, &deleteStatement, nil) == SQLITE_OK { |
|
|
|
for (index, id) in messageIds.enumerated() { |
|
|
|
sqlite3_bind_int(deleteStatement, Int32(index + 1), Int32(id)) |
|
|
|
} |
|
|
|
|
|
|
|
if sqlite3_step(deleteStatement) == SQLITE_DONE { |
|
|
|
result = true |
|
|
|
} else { |
|
|
|
let errmsg = String(cString: sqlite3_errmsg(db)!) |
|
|
|
os_log("删除特定消息失败: %{public}@", log: logger, type: .error, errmsg) |
|
|
|
} |
|
|
|
sqlite3_finalize(deleteStatement) |
|
|
|
return true |
|
|
|
} else { |
|
|
|
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) |
|
|
|
} else { |
|
|
|
let errmsg = String(cString: sqlite3_errmsg(db)!) |
|
|
|
os_log("删除特定消息语句准备失败: %{public}@", log: logger, type: .error, errmsg) |
|
|
|
os_log("删除消息参数无效 - agentId和messageIds都为空", log: logger, type: .error) |
|
|
|
} |
|
|
|
} else { |
|
|
|
os_log("删除消息参数无效 - agentId和messageIds都为空", log: logger, type: .error) |
|
|
|
return false |
|
|
|
} |
|
|
|
|
|
|
|
return false |
|
|
|
return result |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
@ -302,15 +291,18 @@ public class ChatStorageHelper { |
|
|
|
* @return 是否清空成功 |
|
|
|
*/ |
|
|
|
public func clearDatabase() -> Bool { |
|
|
|
let deleteString = "DELETE FROM messages;" |
|
|
|
|
|
|
|
if sqlite3_exec(db, deleteString, nil, nil, nil) == SQLITE_OK { |
|
|
|
return true |
|
|
|
} else { |
|
|
|
let errmsg = String(cString: sqlite3_errmsg(db)!) |
|
|
|
os_log("清空数据库失败: %{public}@", log: logger, type: .error, errmsg) |
|
|
|
return false |
|
|
|
var result = false |
|
|
|
dbQueue.sync { |
|
|
|
let deleteString = "DELETE FROM messages;" |
|
|
|
|
|
|
|
if sqlite3_exec(db, deleteString, nil, nil, nil) == SQLITE_OK { |
|
|
|
result = true |
|
|
|
} else { |
|
|
|
let errmsg = String(cString: sqlite3_errmsg(db)!) |
|
|
|
os_log("清空数据库失败: %{public}@", log: logger, type: .error, errmsg) |
|
|
|
} |
|
|
|
} |
|
|
|
return result |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
@ -321,75 +313,75 @@ public class ChatStorageHelper { |
|
|
|
*/ |
|
|
|
public func getRecentMessages(agentId: String, limit: Int) -> [[String: Any]] { |
|
|
|
var messages: [[String: Any]] = [] |
|
|
|
|
|
|
|
// 首先检查数据库中是否有该会话的消息 |
|
|
|
let countQuery = "SELECT COUNT(*) FROM messages WHERE agent_id = ?" |
|
|
|
var countStatement: OpaquePointer? |
|
|
|
|
|
|
|
if sqlite3_prepare_v2(db, countQuery, -1, &countStatement, nil) == SQLITE_OK { |
|
|
|
sqlite3_bind_text(countStatement, 1, (agentId as NSString).utf8String, -1, nil) |
|
|
|
if sqlite3_step(countStatement) == SQLITE_ROW { |
|
|
|
_ = Int(sqlite3_column_int(countStatement, 0)) |
|
|
|
dbQueue.sync { |
|
|
|
// 首先检查数据库中是否有该会话的消息 |
|
|
|
let countQuery = "SELECT COUNT(*) FROM messages WHERE agent_id = ?" |
|
|
|
var countStatement: OpaquePointer? |
|
|
|
|
|
|
|
if sqlite3_prepare_v2(db, countQuery, -1, &countStatement, nil) == SQLITE_OK { |
|
|
|
sqlite3_bind_text(countStatement, 1, (agentId as NSString).utf8String, -1, nil) |
|
|
|
if sqlite3_step(countStatement) == SQLITE_ROW { |
|
|
|
_ = Int(sqlite3_column_int(countStatement, 0)) |
|
|
|
} |
|
|
|
sqlite3_finalize(countStatement) |
|
|
|
} |
|
|
|
sqlite3_finalize(countStatement) |
|
|
|
} |
|
|
|
|
|
|
|
// 构建查询语句 - 按时间倒序获取最近的N条,然后在结果中再按时间正序 |
|
|
|
let queryString = """ |
|
|
|
SELECT * FROM ( |
|
|
|
SELECT id, session_id, message, sender, metadata, created_at |
|
|
|
FROM messages |
|
|
|
WHERE agent_id = ? |
|
|
|
ORDER BY created_at DESC |
|
|
|
LIMIT ? |
|
|
|
) tmp ORDER BY created_at ASC |
|
|
|
""" |
|
|
|
// 构建查询语句 - 按时间倒序获取最近的N条,然后在结果中再按时间正序 |
|
|
|
let queryString = """ |
|
|
|
SELECT * FROM ( |
|
|
|
SELECT id, session_id, message, sender, metadata, created_at |
|
|
|
FROM messages |
|
|
|
WHERE agent_id = ? |
|
|
|
ORDER BY created_at DESC |
|
|
|
LIMIT ? |
|
|
|
) tmp ORDER BY created_at ASC |
|
|
|
""" |
|
|
|
|
|
|
|
var queryStatement: OpaquePointer? |
|
|
|
var queryStatement: OpaquePointer? |
|
|
|
|
|
|
|
if sqlite3_prepare_v2(db, queryString, -1, &queryStatement, nil) == SQLITE_OK { |
|
|
|
sqlite3_bind_text(queryStatement, 1, (agentId as NSString).utf8String, -1, nil) |
|
|
|
sqlite3_bind_int(queryStatement, 2, Int32(limit)) |
|
|
|
if sqlite3_prepare_v2(db, queryString, -1, &queryStatement, nil) == SQLITE_OK { |
|
|
|
sqlite3_bind_text(queryStatement, 1, (agentId as NSString).utf8String, -1, nil) |
|
|
|
sqlite3_bind_int(queryStatement, 2, Int32(limit)) |
|
|
|
|
|
|
|
while sqlite3_step(queryStatement) == SQLITE_ROW { |
|
|
|
let id = sqlite3_column_int(queryStatement, 0) |
|
|
|
while sqlite3_step(queryStatement) == SQLITE_ROW { |
|
|
|
let id = sqlite3_column_int(queryStatement, 0) |
|
|
|
|
|
|
|
let sessionIdPtr = sqlite3_column_text(queryStatement, 1) |
|
|
|
let sessionId = sessionIdPtr != nil ? String(cString: sessionIdPtr!) : "" |
|
|
|
let sessionIdPtr = sqlite3_column_text(queryStatement, 1) |
|
|
|
let sessionId = sessionIdPtr != nil ? String(cString: sessionIdPtr!) : "" |
|
|
|
|
|
|
|
|
|
|
|
let messagePtr = sqlite3_column_text(queryStatement, 2) |
|
|
|
let message = messagePtr != nil ? String(cString: messagePtr!) : "" |
|
|
|
let messagePtr = sqlite3_column_text(queryStatement, 2) |
|
|
|
let message = messagePtr != nil ? String(cString: messagePtr!) : "" |
|
|
|
|
|
|
|
let senderPtr = sqlite3_column_text(queryStatement, 3) |
|
|
|
let sender = senderPtr != nil ? String(cString: senderPtr!) : "" |
|
|
|
let senderPtr = sqlite3_column_text(queryStatement, 3) |
|
|
|
let sender = senderPtr != nil ? String(cString: senderPtr!) : "" |
|
|
|
|
|
|
|
let metadataPtr = sqlite3_column_text(queryStatement, 4) |
|
|
|
let metadata = metadataPtr != nil ? String(cString: metadataPtr!) : nil |
|
|
|
let metadataPtr = sqlite3_column_text(queryStatement, 4) |
|
|
|
let metadata = metadataPtr != nil ? String(cString: metadataPtr!) : nil |
|
|
|
|
|
|
|
let createdAt = sqlite3_column_int(queryStatement, 5) |
|
|
|
let createdAt = sqlite3_column_int(queryStatement, 5) |
|
|
|
|
|
|
|
var messageDict: [String: Any] = [ |
|
|
|
"id": id, |
|
|
|
"sessionId":sessionId, |
|
|
|
"message": message, |
|
|
|
"sender": sender, |
|
|
|
"created_at": createdAt |
|
|
|
] |
|
|
|
var messageDict: [String: Any] = [ |
|
|
|
"id": id, |
|
|
|
"sessionId":sessionId, |
|
|
|
"message": message, |
|
|
|
"sender": sender, |
|
|
|
"created_at": createdAt |
|
|
|
] |
|
|
|
|
|
|
|
if let metadata = metadata { |
|
|
|
messageDict["metadata"] = metadata |
|
|
|
if let metadata = metadata { |
|
|
|
messageDict["metadata"] = metadata |
|
|
|
} |
|
|
|
|
|
|
|
messages.append(messageDict) |
|
|
|
} |
|
|
|
|
|
|
|
messages.append(messageDict) |
|
|
|
sqlite3_finalize(queryStatement) |
|
|
|
} else { |
|
|
|
let errmsg = String(cString: sqlite3_errmsg(db)!) |
|
|
|
os_log("查询最近消息失败: %{public}@", log: logger, type: .error, errmsg) |
|
|
|
} |
|
|
|
|
|
|
|
sqlite3_finalize(queryStatement) |
|
|
|
} else { |
|
|
|
let errmsg = String(cString: sqlite3_errmsg(db)!) |
|
|
|
os_log("查询最近消息失败: %{public}@", log: logger, type: .error, errmsg) |
|
|
|
} |
|
|
|
|
|
|
|
return messages |
|
|
|
} |
|
|
|
} |
|
|
|
|