|
|
|
@ -16,21 +16,21 @@ public class ChatStorageHelper { |
|
|
|
private let logger = OSLog(subsystem: "com.yunqiinnovation.chat_storage", category: "ChatStorageHelper") |
|
|
|
|
|
|
|
private init() { |
|
|
|
// 获取文档目录路径 |
|
|
|
// 获取文档目录路径(不变) |
|
|
|
let fileURL = try! FileManager.default |
|
|
|
.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false) |
|
|
|
.appendingPathComponent("chat_storage.db") |
|
|
|
|
|
|
|
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 |
|
|
|
} |
|
|
|
|
|
|
|
// 创建消息表 |
|
|
|
// 关键修改:新增 UNIQUE (agent_id, session_id, sender) 联合唯一索引 |
|
|
|
let createTableString = """ |
|
|
|
CREATE TABLE IF NOT EXISTS messages ( |
|
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT, |
|
|
|
@ -39,9 +39,10 @@ public class ChatStorageHelper { |
|
|
|
message TEXT NOT NULL, |
|
|
|
sender TEXT NOT NULL, |
|
|
|
metadata TEXT, |
|
|
|
created_at INTEGER NOT NULL |
|
|
|
created_at INTEGER NOT NULL, |
|
|
|
UNIQUE (agent_id, session_id, sender) ON CONFLICT REPLACE |
|
|
|
); |
|
|
|
CREATE INDEX IF NOT EXISTS idx_agent_id ON messages (session_id); |
|
|
|
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); |
|
|
|
""" |
|
|
|
@ -66,38 +67,46 @@ public class ChatStorageHelper { |
|
|
|
* @param metadata 可选的元数据 |
|
|
|
* @return 插入的消息ID,失败则返回-1 |
|
|
|
*/ |
|
|
|
public func saveMessage(agentId:String,sessionId: String, message: String, sender: String, metadata: String?) -> Int64 { |
|
|
|
let insertStatementString = "INSERT INTO messages (agent_id,session_id, message, sender, metadata, created_at) VALUES (?, ?, ?, ?, ?, ?);" |
|
|
|
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, 1, (sessionId as NSString).utf8String, -1, nil) |
|
|
|
sqlite3_bind_text(insertStatement, 2, (message as NSString).utf8String, -1, nil) |
|
|
|
sqlite3_bind_text(insertStatement, 3, (sender 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, 4, (metadata as NSString).utf8String, -1, nil) |
|
|
|
sqlite3_bind_text(insertStatement, 5, (metadata as NSString).utf8String, -1, nil) |
|
|
|
} else { |
|
|
|
sqlite3_bind_null(insertStatement, 4) |
|
|
|
sqlite3_bind_null(insertStatement, 5) |
|
|
|
} |
|
|
|
|
|
|
|
let currentTime = Int(Date().timeIntervalSince1970) |
|
|
|
sqlite3_bind_int(insertStatement, 5, Int32(currentTime)) |
|
|
|
sqlite3_bind_int(insertStatement, 6, Int32(currentTime)) |
|
|
|
|
|
|
|
// 执行语句(冲突时会自动替换,返回新的 rowid) |
|
|
|
if sqlite3_step(insertStatement) == SQLITE_DONE { |
|
|
|
let id = sqlite3_last_insert_rowid(db) |
|
|
|
let id = sqlite3_last_insert_rowid(db) // 替换后返回新的 id(原 id 会被删除) |
|
|
|
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) |
|
|
|
os_log("插入/更新消息语句准备失败: %{public}@", log: logger, type: .error, errmsg) |
|
|
|
} |
|
|
|
|
|
|
|
return -1 |
|
|
|
@ -131,7 +140,7 @@ public class ChatStorageHelper { |
|
|
|
let totalPages = totalCount == 0 ? 0 : (totalCount + pageSize - 1) / pageSize |
|
|
|
|
|
|
|
let queryString = """ |
|
|
|
SELECT id, message, sender, metadata, created_at |
|
|
|
SELECT id, session_id, message, sender, metadata, created_at |
|
|
|
FROM messages |
|
|
|
WHERE agent_id = ? |
|
|
|
ORDER BY created_at DESC |
|
|
|
@ -148,16 +157,19 @@ public class ChatStorageHelper { |
|
|
|
while sqlite3_step(queryStatement) == SQLITE_ROW { |
|
|
|
let id = sqlite3_column_int(queryStatement, 0) |
|
|
|
|
|
|
|
let messagePtr = sqlite3_column_text(queryStatement, 1) |
|
|
|
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 senderPtr = sqlite3_column_text(queryStatement, 2) |
|
|
|
let senderPtr = sqlite3_column_text(queryStatement, 3) |
|
|
|
let sender = senderPtr != nil ? String(cString: senderPtr!) : "" |
|
|
|
|
|
|
|
let metadataPtr = sqlite3_column_text(queryStatement, 3) |
|
|
|
let metadataPtr = sqlite3_column_text(queryStatement, 4) |
|
|
|
let metadata = metadataPtr != nil ? String(cString: metadataPtr!) : nil |
|
|
|
|
|
|
|
let createdAt = sqlite3_column_int(queryStatement, 4) |
|
|
|
let createdAt = sqlite3_column_int(queryStatement, 5) |
|
|
|
|
|
|
|
// 将时间戳转换为ISO 8601格式的字符串 |
|
|
|
let date = Date(timeIntervalSince1970: TimeInterval(createdAt)) |
|
|
|
@ -167,6 +179,7 @@ public class ChatStorageHelper { |
|
|
|
var messageDict: [String: Any] = [ |
|
|
|
"id": id, |
|
|
|
"agentId": agentId, // 添加agentId字段 |
|
|
|
"sessionId": sessionId, // 添加sessionId字段 |
|
|
|
"message": message, |
|
|
|
"sender": sender, |
|
|
|
"timestamp": timestamp // 使用timestamp而不是created_at |
|
|
|
@ -323,7 +336,7 @@ public class ChatStorageHelper { |
|
|
|
// 构建查询语句 - 按时间倒序获取最近的N条,然后在结果中再按时间正序 |
|
|
|
let queryString = """ |
|
|
|
SELECT * FROM ( |
|
|
|
SELECT id, message, sender, metadata, created_at |
|
|
|
SELECT id, session_id, message, sender, metadata, created_at |
|
|
|
FROM messages |
|
|
|
WHERE agent_id = ? |
|
|
|
ORDER BY created_at DESC |
|
|
|
@ -340,19 +353,24 @@ public class ChatStorageHelper { |
|
|
|
while sqlite3_step(queryStatement) == SQLITE_ROW { |
|
|
|
let id = sqlite3_column_int(queryStatement, 0) |
|
|
|
|
|
|
|
let messagePtr = sqlite3_column_text(queryStatement, 1) |
|
|
|
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 senderPtr = sqlite3_column_text(queryStatement, 2) |
|
|
|
let senderPtr = sqlite3_column_text(queryStatement, 3) |
|
|
|
let sender = senderPtr != nil ? String(cString: senderPtr!) : "" |
|
|
|
|
|
|
|
let metadataPtr = sqlite3_column_text(queryStatement, 3) |
|
|
|
let metadataPtr = sqlite3_column_text(queryStatement, 4) |
|
|
|
let metadata = metadataPtr != nil ? String(cString: metadataPtr!) : nil |
|
|
|
|
|
|
|
let createdAt = sqlite3_column_int(queryStatement, 4) |
|
|
|
let createdAt = sqlite3_column_int(queryStatement, 5) |
|
|
|
|
|
|
|
var messageDict: [String: Any] = [ |
|
|
|
"id": id, |
|
|
|
"sessionId":sessionId, |
|
|
|
"message": message, |
|
|
|
"sender": sender, |
|
|
|
"created_at": createdAt |
|
|
|
|