@ -1,15 +1,21 @@
import Foundation
import SQLite3
import os . log
/* *
* ChatStorageHelper - 聊 天 数 据 存 储 助 手
* 负 责 处 理 SQLite数据库操作 , 提 供 消 息 存 储 和 检 索 功 能
* 单 例 模 式 确 保 数 据 库 连 接 的 一 致 性
*/
class ChatStorageHelper {
public class ChatStorageHelper {
// 单 例 实 例
public static let shared = ChatStorageHelper ( )
private var db : OpaquePointer ?
private let dbPath : String
private let logger = OSLog ( subsystem : " com.yunqiinnovation.chat_storage " , category : " ChatStorageHelper " )
init ( ) {
private init ( ) {
// 获 取 文 档 目 录 路 径
let fileURL = try ! FileManager . default
. url ( for : . documentDirectory , in : . userDomainMask , appropriateFor : nil , create : false )
@ -19,7 +25,8 @@ class ChatStorageHelper {
// 打 开 数 据 库
if sqlite3_open ( dbPath , & db ) != SQLITE_OK {
print ( " 无法打开数据库 " )
let errmsg = String ( cString : sqlite3_errmsg ( db ) ! )
os_log ( " 无法打开数据库: %{public}@ " , log : logger , type : . error , errmsg )
return
}
@ -39,7 +46,7 @@ class ChatStorageHelper {
if sqlite3_exec ( db , createTableString , nil , nil , nil ) != SQLITE_OK {
let errmsg = String ( cString : sqlite3_errmsg ( db ) ! )
print ( " 创建表失败: \( errmsg ) " )
os_log ( " 创建表失败: %{public}@ " , log : logger , type : . error , errmsg )
}
}
@ -57,7 +64,7 @@ class ChatStorageHelper {
* @ param metadata 可 选 的 元 数 据
* @ return 插 入 的 消 息 ID , 失 败 则 返 回 - 1
*/
func saveMessage ( sessionId : String , message : String , sender : String , metadata : String ? ) -> Int64 {
public func saveMessage ( sessionId : String , message : String , sender : String , metadata : String ? ) -> Int64 {
let insertStatementString = " INSERT INTO messages (session_id, message, sender, metadata, created_at) VALUES (?, ?, ?, ?, ?); "
var insertStatement : OpaquePointer ?
@ -80,12 +87,14 @@ class ChatStorageHelper {
sqlite3_finalize ( insertStatement )
return id
} else {
print ( " 插入消息失败: \( String ( cString : sqlite3_errmsg ( db ) ! ) ) " )
let errmsg = String ( cString : sqlite3_errmsg ( db ) ! )
os_log ( " 插入消息失败: %{public}@ " , log : logger , type : . error , errmsg )
}
sqlite3_finalize ( insertStatement )
} else {
print ( " 插入消息语句准备失败: \( String ( cString : sqlite3_errmsg ( db ) ! ) ) " )
let errmsg = String ( cString : sqlite3_errmsg ( db ) ! )
os_log ( " 插入消息语句准备失败: %{public}@ " , log : logger , type : . error , errmsg )
}
return - 1
@ -98,10 +107,26 @@ class ChatStorageHelper {
* @ param pageSize 每 页 消 息 数 量
* @ return 消 息 列 表 的 JSON字符串
*/
func getMessages ( sessionId : String , page : Int , pageSize : Int ) -> String {
public func getMessages ( sessionId : String , page : Int , pageSize : Int ) -> String {
let offset = ( page - 1 ) * pageSize
var messagesArray : [ [ String : Any ] ] = [ ]
// 首 先 获 取 总 记 录 数
let countQuery = " SELECT COUNT(*) FROM messages WHERE session_id = ? "
var countStatement : OpaquePointer ?
var totalCount = 0
if sqlite3_prepare_v2 ( db , countQuery , - 1 , & countStatement , nil ) = = SQLITE_OK {
sqlite3_bind_text ( countStatement , 1 , ( sessionId as NSString ) . utf8String , - 1 , nil )
if sqlite3_step ( countStatement ) = = SQLITE_ROW {
totalCount = Int ( sqlite3_column_int ( countStatement , 0 ) )
}
sqlite3_finalize ( countStatement )
}
// 计 算 总 页 数
let totalPages = totalCount = = 0 ? 0 : ( totalCount + pageSize - 1 ) / pageSize
let queryString = " " "
SELECT id , message , sender , metadata , created_at
FROM messages
@ -131,11 +156,17 @@ class ChatStorageHelper {
let createdAt = sqlite3_column_int ( queryStatement , 4 )
// 将 时 间 戳 转 换 为 I S O 8 6 0 1 格 式 的 字 符 串
let date = Date ( timeIntervalSince1970 : TimeInterval ( createdAt ) )
let formatter = ISO8601DateFormatter ( )
let timestamp = formatter . string ( from : date )
var messageDict : [ String : Any ] = [
" id " : id ,
" sessionId " : sessionId , // 添 加 s e s s i o n I d 字 段
" message " : message ,
" sender " : sender ,
" created_at " : createdAt
" timestamp " : timestamp // 使 用 t i m e s t a m p 而 不 是 c r e a t e d _ a t
]
if let metadata = metadata {
@ -147,22 +178,48 @@ class ChatStorageHelper {
sqlite3_finalize ( queryStatement )
// 构 建 与 A n d r o i d 版 本 一 致 的 返 回 格 式
let result : [ String : Any ] = [
" messages " : messagesArray ,
" page " : page ,
" pageSize " : pageSize ,
" totalCount " : totalCount ,
" totalPages " : totalPages
]
do {
let jsonData = try JSONSerialization . data ( withJSONObject : messagesArray , options : [ ] )
let jsonData = try JSONSerialization . data ( withJSONObject : result , options : [ ] )
if let jsonString = String ( data : jsonData , encoding : . utf8 ) {
return jsonString
}
} catch {
print ( " 转换消息为JSON失败: \( error . localizedDescription ) " )
os_log ( " getMessages: JSON转换失败: %{public}@ " , log : logger , type : . error , error . localizedDescription )
}
} else {
print ( " 查询消息失败: \( String ( cString : sqlite3_errmsg ( db ) ! ) ) " )
let errmsg = String ( cString : sqlite3_errmsg ( db ) ! )
os_log ( " getMessages: SQL准备失败: %{public}@ " , log : logger , type : . error , errmsg )
}
return " [] "
}
// 返 回 空 结 果 , 但 保 持 格 式 一 致
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 " { \" messages \" :[], \" page \" : \( page ) , \" pageSize \" : \( pageSize ) , \" totalCount \" :0, \" totalPages \" :0} "
}
/* *
* 删 除 消 息
@ -170,7 +227,7 @@ class ChatStorageHelper {
* @ param messageIds 可 选 的 消 息 ID列表 , 指 定 则 删 除 特 定 消 息
* @ return 是 否 删 除 成 功
*/
func deleteMessages ( sessionId : String ? , messageIds : [ Int ] ? ) -> Bool {
public func deleteMessages ( sessionId : String ? , messageIds : [ Int ] ? ) -> Bool {
if let sessionId = sessionId {
let deleteString = " DELETE FROM messages WHERE session_id = ?; "
var deleteStatement : OpaquePointer ?
@ -182,12 +239,14 @@ class ChatStorageHelper {
sqlite3_finalize ( deleteStatement )
return true
} else {
print ( " 删除会话消息失败: \( String ( cString : sqlite3_errmsg ( db ) ! ) ) " )
let errmsg = String ( cString : sqlite3_errmsg ( db ) ! )
os_log ( " 删除会话消息失败: %{public}@ " , log : logger , type : . error , errmsg )
}
sqlite3_finalize ( deleteStatement )
} else {
print ( " 删除会话消息语句准备失败: \( String ( cString : sqlite3_errmsg ( db ) ! ) ) " )
let errmsg = String ( cString : sqlite3_errmsg ( db ) ! )
os_log ( " 删除会话消息语句准备失败: %{public}@ " , log : logger , type : . error , errmsg )
}
} else if let messageIds = messageIds , ! messageIds . isEmpty {
// 构 建 参 数 占 位 符
@ -204,14 +263,17 @@ class ChatStorageHelper {
sqlite3_finalize ( deleteStatement )
return true
} else {
print ( " 删除特定消息失败: \( String ( cString : sqlite3_errmsg ( db ) ! ) ) " )
let errmsg = String ( cString : sqlite3_errmsg ( db ) ! )
os_log ( " 删除特定消息失败: %{public}@ " , log : logger , type : . error , errmsg )
}
sqlite3_finalize ( deleteStatement )
} else {
print ( " 删除特定消息语句准备失败: \( String ( cString : sqlite3_errmsg ( db ) ! ) ) " )
let errmsg = String ( cString : sqlite3_errmsg ( db ) ! )
os_log ( " 删除特定消息语句准备失败: %{public}@ " , log : logger , type : . error , errmsg )
}
} else {
os_log ( " 删除消息参数无效 - sessionId和messageIds都为空 " , log : logger , type : . error )
return false
}
@ -222,16 +284,90 @@ class ChatStorageHelper {
* 清 空 数 据 库
* @ return 是 否 清 空 成 功
*/
func clearDatabase ( ) -> Bool {
public func clearDatabase ( ) -> Bool {
let deleteString = " DELETE FROM messages; "
if sqlite3_exec ( db , deleteString , nil , nil , nil ) = = SQLITE_OK {
return true
} else {
print ( " 清空数据库失败: \( String ( cString : sqlite3_errmsg ( db ) ! ) ) " )
let errmsg = String ( cString : sqlite3_errmsg ( db ) ! )
os_log ( " 清空数据库失败: %{public}@ " , log : logger , type : . error , errmsg )
return false
}
}
/* *
* 获 取 指 定 会 话 的 最 近 N条消息
* @ param sessionId 会 话 ID
* @ param limit 限 制 数 量
* @ return 消 息 列 表 , 按 时 间 正 序 排 列
*/
public func getRecentMessages ( sessionId : String , limit : Int ) -> [ [ String : Any ] ] {
var messages : [ [ String : Any ] ] = [ ]
// 首 先 检 查 数 据 库 中 是 否 有 该 会 话 的 消 息
let countQuery = " SELECT COUNT(*) FROM messages WHERE session_id = ? "
var countStatement : OpaquePointer ?
if sqlite3_prepare_v2 ( db , countQuery , - 1 , & countStatement , nil ) = = SQLITE_OK {
sqlite3_bind_text ( countStatement , 1 , ( sessionId as NSString ) . utf8String , - 1 , nil )
if sqlite3_step ( countStatement ) = = SQLITE_ROW {
_ = Int ( sqlite3_column_int ( countStatement , 0 ) )
}
sqlite3_finalize ( countStatement )
}
// 构 建 查 询 语 句 - 按 时 间 倒 序 获 取 最 近 的 N 条 , 然 后 在 结 果 中 再 按 时 间 正 序
let queryString = " " "
SELECT * FROM (
SELECT id , message , sender , metadata , created_at
FROM messages
WHERE session_id = ?
ORDER BY created_at DESC
LIMIT ?
) tmp ORDER BY created_at ASC
" " "
var queryStatement : OpaquePointer ?
if sqlite3_prepare_v2 ( db , queryString , - 1 , & queryStatement , nil ) = = SQLITE_OK {
sqlite3_bind_text ( queryStatement , 1 , ( sessionId 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 )
let messagePtr = sqlite3_column_text ( queryStatement , 1 )
let message = messagePtr != nil ? String ( cString : messagePtr ! ) : " "
let senderPtr = sqlite3_column_text ( queryStatement , 2 )
let sender = senderPtr != nil ? String ( cString : senderPtr ! ) : " "
let metadataPtr = sqlite3_column_text ( queryStatement , 3 )
let metadata = metadataPtr != nil ? String ( cString : metadataPtr ! ) : nil
let createdAt = sqlite3_column_int ( queryStatement , 4 )
var messageDict : [ String : Any ] = [
" id " : id ,
" message " : message ,
" sender " : sender ,
" created_at " : createdAt
]
if let metadata = metadata {
messageDict [ " metadata " ] = metadata
}
messages . append ( messageDict )
}
sqlite3_finalize ( queryStatement )
} else {
let errmsg = String ( cString : sqlite3_errmsg ( db ) ! )
os_log ( " 查询最近消息失败: %{public}@ " , log : logger , type : . error , errmsg )
}
return messages
}
}