import uniIm from '@/uni_modules/uni-im/lib/main.js'; import md5 from '@/uni_modules/uni-im/common/md5' const db = uniCloud.database(); const dbCmd = db.command export default class Message { constructor(currentConversation) { // #ifdef APP this.sqlite = getApp().globalData.sqlite // #endif // console.log('currentConversation',currentConversation); // currentConversation = currentConversation//await uniIm.conversation.get(conversation_id) this.conversation_id = currentConversation.id Object.defineProperty(this, 'msgList', { get() { // 未init当[],防止本地发送失败的数据 影响查询线上数据结果 if(currentConversation.isInit){ return currentConversation.msgList }else{ return [] } }, set(data) { currentConversation.msgList = data } }) // #ifdef H5 Object.defineProperty(this, 'indexDB', { get() { return uniIm.indexDB } }) // #endif // 初始化 storage中的maxTime // #ifdef MP this.localMsg.key = 'uni-im-msg-' + this.conversation_id // #endif } indexDB = false isInit = false msgList = [] async sleep(t) { return await new Promise((resolve, rejece) => { setTimeout(resolve, t) }) } async localMsgMaxTime() { // 拿到【本地】数据库中,当前会话聊天记录的最大值 if (this.localMsg.maxTime === false) { let lastLocalDatas = await this.localMsg.get({limit:1,orderBy:{"create_time":"desc"}}) // console.log('lastLocalDatas------',lastLocalDatas); let [lastLocalData] =lastLocalDatas if (lastLocalData) { this.localMsg.maxTime = lastLocalData.create_time } else { this.localMsg.maxTime = 0 } // console.log('init localMsgMaxTime',this.localMsg.maxTime); } return this.localMsg.maxTime } msgListMinTime(){ let item = this.msgList[0] if(item){ return item.create_time }else{ return 0 } } getMore = async () => { // console.log('getMore'); if (this.cloudMsg.hasAfterStorage) { let minTime = await this.localMsgMaxTime(), maxTime = this.msgListMinTime() // 1. 拉取云端最新数据(大于本地storage的部分)直到没有 let data = await this.cloudMsg.get({minTime,maxTime}) if (data.length) { // console.error('【+++】cloudMsg:请求到时间大于storeage中的云端数据', minTime, data.length, data); return data } else { this.cloudMsg.hasAfterStorage = false // console.error('cloudMsg已无:时间大于storeage中的云端数据'); return this.getMore() } } else if (this.localMsg.hasBeforeList) { // 2. 拉取storage中的数据(小于已经拉取的数据)直到没有 let maxTime = this.msgListMinTime() let data = await this.localMsg.get({ maxTime }) if (data.length) { // console.error('【+++】localMsg:请求到时间小于列表的本地数据', data.length, data, maxTime); return data } else { this.localMsg.hasBeforeList = false // console.error('localMsg:已无时间小于列表的本地数据', maxTime); return this.getMore() } } // 3. 拉取云端(小于已经拉取的数据)的数据直到没有 if (this.cloudMsg.hasBeforeStorage) { // await this.sleep(3000) let maxTime = this.msgList[0] ? this.msgList[0].create_time : false // console.log('*--**--*', maxTime, JSON.stringify(this.msgList)); let data = await this.cloudMsg.get({ maxTime }) if (data.length) { // console.error('【+++】cloudMsg:请求到时间小于列表的云端数据', data.length, data, maxTime); return data } else { this.cloudMsg.hasBeforeStorage = false // console.error('cloudMsg已无:时间小于列表的云端数据', maxTime); return [] } } } cloudMsg = { hasAfterStorage: true, hasBeforeStorage: true, get: async ({ minTime = 0, maxTime = false, limit = 30 } = {}) => { // console.log(1111,minTime,maxTime); //console.log('this',this); // let where = `"conversation_id" == "${this.conversation_id}"` let where = { "conversation_id": this.conversation_id } if (minTime && maxTime) { where.create_time = dbCmd.and([ dbCmd.gt(minTime), dbCmd.lt(maxTime) ]) } else { if (minTime) { // where += `&& "create_time" > ${minTime}` where.create_time = dbCmd.gt(minTime) } if (maxTime) { // where += `&& "create_time" < ${maxTime}` where.create_time = dbCmd.lt(maxTime) } } const msgTable = db.collection('uni-im-msg') let data; try { let res = await msgTable.where(where) .limit(limit) .orderBy('create_time', 'desc') .get() data = res.result.data.reverse() // console.error('where', where,{minTime,maxTime},data); } catch (e) { // console.error(e); // 如果断网的话,会请求不到直接返回空即可 data = [] } // console.error('where', where, data); if (data.length) { //存到本地 this.localMsg.add(data, minTime === 0 ? 'unshift' : 'push') //console.error(996666699955,[...data], minTime != 0); } return data } } localMsg = { maxTime: false, hasBeforeList: true, get: async ({ minTime = 0, maxTime = false, limit = 30, orderBy = {"create_time":"asc"} //asc 升序,desc 降序 } = {}) => { // #ifdef APP let sql = `select * from msg WHERE conversation_id = "${this.conversation_id}" ` //注意结尾要留空格,下一段语句连接 if(maxTime || minTime){ if(maxTime){ sql += `AND create_time < ${maxTime} ` } if(minTime){ sql += `AND create_time > ${minTime} ` } } // 注意传入的orderBy是查询结果不是过程,im场景下都是从大到小查询 sql += `ORDER BY "create_time" DESC ` if(limit){ sql += `LIMIT ${limit} ` } let datas = [] try{ datas = await this.sqlite.selectSql(sql) }catch(e){ // 错误时,仍然返回空数组,提高 高可用性 console.error(e) } // console.error('sql:',sql,datas); return datas.map(data=>{ try{ let mapData = { """:'"', "'":"'", "<":'<', ">":'>', "&":'&' } Object.keys(mapData).forEach(key=>{ data.body = data.body.replace(new RegExp(key,'g'), mapData[key]); }) // console.log('data.body',data.body) data.body = JSON.parse(data.body) }catch(e){ console.error(e) } return data }).sort((a,b)=>{ if(orderBy.create_time == 'asc'){ return a.create_time - b.create_time }else{ return b.create_time - a.create_time } }) // #endif // #ifdef H5 let datas = await new Promise((resolve, reject) => { let datas = [],index = 0 if (!maxTime) { maxTime = Date.now() } try{ // 设置查询索引 // console.log('kkkkkkkk',[this.conversation_id, minTime], [this.conversation_id,maxTime]); let range = IDBKeyRange.bound([this.conversation_id, minTime], [this.conversation_id,maxTime]) // 传入的 prev 表示是降序遍历游标,默认是next表示升序; // indexDB 在im的场景下,查始终是降序遍历游标 orderBy指的是查询结果的排序方式 let sort = "prev" // console.log('sortsortsortsortsortsortsort',sort); let task = this.indexDB.transaction("uni-im-msg") .objectStore("uni-im-msg") .index("conversation_id-create_time") .openCursor(range, sort) task.onsuccess = function(event) { let cursor = event.target.result; if (cursor) { // console.log('cursor',cursor.value); // 排除边界值 if (![minTime, maxTime].includes(cursor.value.create_time)) { datas.push(cursor.value) } index ++ if(limit && index === limit){ resolve(datas) }else{ cursor.continue(); } } else { resolve(datas) } } task.onerror = function(err) { console.error(err); resolve([]) } }catch(e){ console.error(e); resolve([]) //TODO handle the exception } }) return datas.sort((a,b)=>{ if(orderBy.create_time == 'asc'){ return a.create_time - b.create_time }else{ return b.create_time - a.create_time } }) // #endif // #ifdef MP let data = uni.getStorageSync(this.localMsg.key) || [] // console.error(111,data,minTime,maxTime) data = data.filter(item => { if (minTime && (item.create_time < minTime || item.create_time == minTime)) { return false } if (maxTime && (item.create_time > maxTime || item.create_time == maxTime)) { return false } return true }) // console.error(222,data) data = data.sort((a,b)=>{ if(orderBy.create_time == 'asc'){ return a.create_time - b.create_time }else{ return b.create_time - a.create_time } }) if(limit){ data = data.slice(0,limit) } return data // #endif }, add: async (datas, action = 'push') => { // console.log('localMsg.add',action,datas); if(!Array.isArray(datas)){ datas = [datas] } datas.forEach(async data=>{ data.unique_id = md5(JSON.stringify(data) + Math.random()) }) // #ifdef APP let sql = [] datas.forEach(async data=>{ let keys = Object.keys(data) let str = keys.reduce((sum,key)=>{ if(key == 'body'){ let body = JSON.stringify(data.body) try{ body = escapeHtml(body) // console.log('bodybodybody',body) }catch(e){ console.error(e) } sum += `"${body}",` }else if(typeof data[key] == 'string'){ sum += `"${data[key]}",` } else if(typeof data[key] == 'undefined'){ sum += `${null},` }else{ sum += `${data[key]},` } return sum },'').slice(0,-1); sql.push(`insert into msg("${keys.join('","')}") values (${str})`) }) if(sql.length){ try{ await this.sqlite.executeSql(sql) }catch(e){ console.log(e) } } // console.log('executeSql:',sql); // #endif // #ifdef H5 let res = await new Promise((resolve, reject) => { // console.log('datas', datas[0]); // 事务对象 指定表格名称和操作模式("只读"或"读写") let transaction = this.indexDB.transaction('uni-im-msg', 'readwrite') let objectStore = transaction.objectStore('uni-im-msg') // 仓库对象 let index = 0 let length = datas.length datas.forEach(data => { let res = objectStore.add(data) res.onsuccess = (e) => { // console.log('resolve',e, index, length); index++ if (index == length) { resolve() } } res.onerror = (e) => { // console.error('add - failed', e); // console.log('reject', e); reject() } }) }) // #endif // #ifdef MP // mp 端把所有storage查出来 let _datas = await this.localMsg.get() if(_datas.length > 20){ let tipText = "提示:当前会话的离线(存到storage)的聊天记录已经超过20条," if(action == 'push'){ console.log(tipText + '将“自动删除”旧数据后再添加新数据。如果你有其他策略可以自己修改此逻辑') _datas = _datas.slice(-1*datas.length) }else{ return console.log(tipText + '不再存储更多') } } _datas[action](...datas) uni.setStorageSync(this.localMsg.key,_datas) // #endif // 更新最大值 let maxTime = datas.map(i=>i.create_time).sort((a,b)=>b-a)[0] let localMsgMaxTime = await this.localMsgMaxTime() if(maxTime > await localMsgMaxTime){ // console.error('更新最大值 maxTime----------------',maxTime); this.localMsg.maxTime = maxTime } }, update: async(unique_id,data)=> { // console.log('localMsg update',data); data = Object.assign({},data) // #ifdef APP let dataStr = '' for (let key in data) { dataStr += `"${key}" = ` if(key == 'body'){ let body = JSON.stringify(data.body) try{ body = escapeHtml(body) // console.log('bodybodybody',body) }catch(e){ console.error(e) } dataStr += `"${body}",` }else if(typeof data[key] == 'string'){ dataStr += `"${data[key]}",` }else if(typeof data[key] == 'undefined'){ dataStr += `${null},` }else{ dataStr += `${data[key]},` } } let sql = `UPDATE msg SET ${dataStr.slice(0,-1)} WHERE unique_id = "${unique_id}"` try{ await this.sqlite.executeSql(sql) }catch(e){ console.log(e) } // console.log('executeSql:',sql); // #endif // #ifdef H5 let datas = await new Promise((resolve, reject) => { let request = this.indexDB.transaction(['uni-im-msg'], 'readwrite') .objectStore("uni-im-msg") .put(data) request.onsuccess = function(event) { // console.log('event---',event); resolve(event) } request.onerror = function(event) { console.error(event); reject(event) }; }) // #endif // #ifdef MP // mp 端把所有storage查出来 let _datas = await this.localMsg.get() let index = _datas.findIndex(i=>i.unique_id == unique_id) _datas[index] = data uni.setStorageSync(this.localMsg.key,_datas) // #endif } } } // #ifdef APP var matchHtmlRegExp = /["'&<>]/ function escapeHtml (string) { var str = '' + string var match = matchHtmlRegExp.exec(str) if (!match) { return str } var escape var html = '' var index = 0 var lastIndex = 0 for (index = match.index; index < str.length; index++) { switch (str.charCodeAt(index)) { case 34: // " escape = '"' break case 38: // & escape = '&' break case 39: // ' escape = ''' break case 60: // < escape = '<' break case 62: // > escape = '>' break default: continue } if (lastIndex !== index) { html += str.substring(lastIndex, index) } lastIndex = index + 1 html += escape } return lastIndex !== index ? html + str.substring(lastIndex, index) : html } // #endif