import Redis from 'ioredis' ; import { compact } from 'lodash' ; import IgnoreChatInfo from '../types/IgnoreChatInfo' ; import Message from '../types/Message' ; import Room from '../types/Room' ; import StorageProvider from '../types/StorageProvider' ; export default class RedisStorageProvider implements StorageProvider { qid : string ; connStr : string ; redis : Redis . Redis ; /** `constructor` 方法。 */ constructor ( connStr , id ) { this . connStr = connStr ; this . qid = `eqq:${ id }` ; } /** `connect` 方法。在这里与数据库建立连接。 */ connect ( ) { this . redis = new Redis ( this . connStr ) ; return Promise . resolve ( ) ; } /** 实现  StorageProvider 类的 `getIgnoredChats` 方法，
     * 是对 `ignoredChats` 的“查所有”操作。
     *
     * 在用户查询忽略聊天列表时被调用。
      async getIgnoredChats ( ) { const infoStrings = await this . redis . hvals ( `${ this . qid }:rooms:ignored` ) ; return infoStrings . map ( ( infoString ) => JSON . parse ( infoString ) ) ; } /** 实现  StorageProvider 类的 `isChatIgnored` 方法，
     * 是对 `ignoredChats` 的自定义查询操作。返回一个**布尔**值。
     *
     * 在收到消息时被调用。
      async isChatIgnored ( id ) { const existance = await this . redis . hexists ( `${ this . qid }:rooms:ignored` , `${ id }` ) ; return existance === 1 ; } /** 实现  StorageProvider 类的 `addIgnoredChat` 方法，
     * 是对 `ignoredChats` 的“增”操作。
     *
     * 在忽略聊天时被调用。
      async addIgnoredChat ( info ) { await this . redis . hset ( `${ this . qid }:rooms:ignored` , `${ info . id }` , JSON . stringify ( info ) ) ; } /** 实现  StorageProvider 类的 `removeIgnoredChat` 方法，
     * 是对 `ignoredChats` 的“删”操作。
     *
     * 在取消忽略聊天时被调用。
      async removeIgnoredChat ( roomId ) { await this . redis . hdel ( `${ this . qid }:rooms:ignored` , `${ roomId }` ) ; } /** 实现  StorageProvider 类的 `getRoom` 方法，
     * 对应 room 的“查单个”操作。
     *
     * 在进入房间后被调用。
      async getRoom ( roomId ) { const room = await this . redis . hget ( `${ this . qid }:rooms:rooms` , `${ roomId }` ) ; const pRoom = JSON . parse ( room ) as Room ; if ( ! pRoom ) { return null ; } return { ... pRoom , roomId : Number ( pRoom . roomId ) } ; } /** 实现  StorageProvider 类的 `updateRoom` 方法，
     * 对应 room 的“改”操作。
     *
     * 在“收到新消息”等引起房间信息变化的事件时调用。
      async updateRoom ( roomId , room ) { const roomInDB = JSON . parse ( await this . redis . hget ( `${ this . qid }:rooms:rooms` , `${ roomId }` ) ) ; const roomToUpdate = { ... roomInDB , ... room } ; await this . redis . hset ( `${ this . qid }:rooms:rooms` , `${ roomId }` , JSON . stringify ( roomToUpdate ) ) ; await this . redis . zadd ( `${ this . qid }:rooms:keyList` , roomToUpdate . utime , `${ roomId }` ) ; await this . redis . zadd ( `${ this . qid }:rooms:priority` , roomToUpdate . priority , `${ roomId }` ) ; } /** 实现  StorageProvider 类的 `addRoom` 方法，
     * 对应 room 的“增”操作。
     *
     * 在“新房间收到新消息”等需要新增房间的事件时被调用。
      async addRoom ( room ) { await this . redis . hset ( `${ this . qid }:rooms:rooms` , `${ room . roomId }` , JSON . stringify ( room ) ) ; if ( room . utime ) await this . redis . zadd ( `${ this . qid }:rooms:keyList` , room . utime , `${ room . roomId }` ) ; if ( room . priority ) { await this . redis . zadd ( `${ this . qid }:rooms:priority` , room . priority , `${ room . roomId }` ) ; } } /** 实现  StorageProvider 类的 `removeRoom` 方法，
     * 对应 room 的“删”操作。
     *
     * 在删除聊天时调用。
      async removeRoom ( roomId ) { await this . redis . hdel ( `${ this . qid }:rooms:rooms` , `${ roomId }` ) ; await this . redis . zrem ( `${ this . qid }:rooms:keyList` , `${ roomId }` ) ; await this . redis . zrem ( `${ this . qid }:rooms:priority` , `${ roomId }` ) ; } /** 实现  StorageProvider 类的 `getAllRooms` 方法，
     * 对应 room 的“查所有”操作。
     *
     * 在登录成功后调用。
      async getAllRooms ( ) { const roomKeys = await this . redis . zrevrange ( `${ this . qid }:rooms:keyList` , 0 , - 1 ) ; const roomsPAry = roomKeys . map ( async ( key ) => { const room = await this . redis . hget ( `${ this . qid }:rooms:rooms` , key ) ; const pRoom = JSON . parse ( room ) as Room ; return { ... pRoom , roomId : Number ( pRoom . roomId ) } ; } ) ; const rooms = ( await Promise . all ( roomsPAry ) ) as Room [ ] ; return rooms ; } /** 实现  StorageProvider 类的 `addRoom` 方法，
     * 对应 room 的“增”操作。
     *
     * 在“新房间收到新消息”等需要新增房间的事件时被调用。
      async addMessage ( roomId , message ) { await this . redis . hset ( `${ this . qid }:msg${ roomId }:messages` , `${ message . _id }` , JSON . stringify ( message ) ) ; await this . redis . zadd ( `${ this . qid }:msg${ roomId }:msgIdList` , message . time , message . _id ) ; } /** 实现  StorageProvider 类的 `updateMessage` 方法，
     * 是对 `msg${roomId}` 的“改”操作。
     *
     * 在“用户撤回消息”等需要改动消息内容的事件中被调用。
      async updateMessage ( roomId , messageId , message ) { const msgInDB = JSON . parse ( await this . redis . hget ( `${ this . qid }:msg${ roomId }:messages` , `${ messageId }` ) ) ; const msgToUpdate = { ... msgInDB , ... message } ; return this . redis . hset ( `${ this . qid }:msg${ roomId }:messages` , `${ messageId }` , JSON . stringify ( msgToUpdate ) ) ; } /** 实现  StorageProvider 类的 `fetchMessage` 方法，
     * 是对 `msg${roomId}` 的“查多个”操作。
     *
     * 在进入房间时，该方法被调用。
      async fetchMessages ( roomId , skip , limit ) { const msgKeys = await this . redis . zrevrange ( `${ this . qid }:msg${ roomId }:msgIdList` , skip , skip + limit - 1 ) ; const messagesPAry = msgKeys . map ( async ( key ) => { const msg = await this . redis . hget ( `${ this . qid }:msg${ roomId }:messages` , key ) ; return JSON . parse ( msg ) as Message ; } ) ; const messages = ( await Promise . all ( messagesPAry ) ) as Message [ ] ; messages . sort ( ( a , b ) => a . time - b . time ) ; return messages ; } /** 实现  StorageProvider 类的 `getMessage` 方法，
     * 是对 `msg${roomId}` 的“查”操作。
     *
     * 在获取聊天历史消息时，该方法被调用。
      async getMessage ( roomId , messageId ) { const msgString = await this . redis . hget ( `${ this . qid }:msg${ roomId }:messages` , `${ messageId }` ) ; return JSON . parse ( msgString ) ; } /** 实现  StorageProvider 类的 `addMessages` 方法，
     * 是对 `msg${roomId}` 的自定义增操作。用于向数据库内增加多条消息。
     *
     * 在获取聊天历史消息时，该方法被调用。
      addMessages ( roomId , messages ) { messages . forEach ( ( message ) => { this . redis . hset ( `${ this . qid }:msg${ roomId }:messages` , `${ message . _id }` , JSON . stringify ( message ) ) ; this . redis . zadd ( `${ this . qid }:msg${ roomId }:msgIdList` , message . time , message . _id ) ; } ) ; return Promise . resolve ( ) ; } /** 实现  StorageProvider 类的 `getUnreadCount` 方法，
     * 是对 room 的自定义查询方法。查询有未读消息的大于指定通知优先级的房间数。
     *
     * 在登录成功与每次收到消息后调用。
      async getUnreadCount ( priority ) { const keyAry = await this . redis . zrangebyscore ( `${ this . qid }:rooms:priority` , priority , priority ) ; const roomsUnreadAry = await Promise . all ( keyAry . map ( async ( key ) => { const room = await this . redis . hget ( `${ this . qid }:rooms:rooms` , key ) ; const pRoom = JSON . parse ( room ) as Room ; return pRoom . unreadCount ; } ) ) ; return compact ( roomsUnreadAry ) . length ; } /** 实现  StorageProvider 类的 `getFirstUnreadRoom` 方法，
     * 是对 room 的自定义查询方法。
     *
     * 调用情况未知。
      async getFirstUnreadRoom ( priority ) { const keyAry = await this . redis . zrangebyscore ( `${ this . qid }:rooms:priority` , priority , priority ) ; if ( keyAry . length === 0 ) { return undefined ; } const room = await this . redis . hget ( `${ this . qid }:rooms:rooms` , keyAry [ 0 ] ) ; const pRoom = JSON . parse ( room ) as Room ; return pRoom ; } }