php+redis实现无mysql的最近联系人功能(好友+群)

正文开始

使用mysql的性能没有redis快。 利用redis的list结构做存储。 支持将之前的mysql结构转成redis 请勿转载个人代码,个人原创实现的逻辑。 上代码: 下载

<?php

namespace copyCommonCodes;

use think\Db;

class PubRecentRds
{
    protected static $table = 'im_user_recent';

    /**
    CREATE TABLE `im_user_recent` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `uid` int(11) NOT NULL DEFAULT '0' COMMENT '主uid',
    `targetId` int(11) NOT NULL DEFAULT '0' COMMENT '目标id',
    `isGroup` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0是人; 1是群',
    `lastTime` int(11) NOT NULL DEFAULT '0' COMMENT '最后聊天时间',
    `lastMsgId` char(20) NOT NULL DEFAULT '' COMMENT '最后一条消息id ',
    `isTop` int(11) NOT NULL DEFAULT '0' COMMENT '是否置顶 >1则降序排序',
    PRIMARY KEY (`id`) USING BTREE,
    UNIQUE KEY `uid` (`uid`,`isGroup`,`targetId`) USING BTREE,
    KEY `lastTime` (`lastTime`),
    KEY `uid_2` (`uid`,`isTop`)
    ) ENGINE=InnoDB AUTO_INCREMENT=1543 DEFAULT CHARSET=utf8mb4 COMMENT='用户的最近联系人表';

     */


    //群成员列表缓存名字
    protected static function _getRecentListCacheName($uid) {
        return 'chat.myRecentList2.'.$uid;
    }


    //单个聊天对象的缓存
    protected static $_recentValCacheName = 'chat.recentVal';
    //field 结构:myUid_targetId_isGroup
    protected static function _getUserTargetKey($myUid, $targetId, $isGroup) {
        return $myUid .'_'. $targetId .'_'. intval($isGroup);
    }

    //最近联系人 val
    protected static function _opt_val($operate = 'set/remove/set/get', $myUid = 0, $hisSid = 0, $isGroup = 0, $data=[])
    {
        $uKey = self::_getUserTargetKey($myUid, $hisSid, $isGroup);
        switch ($operate) {
            case 'set':// 设置val
                if(is_array($data)) $data = json_encode($data);
                RDS::hset(self::$_recentValCacheName, $uKey, $data);
                return $uKey;
            case 'remove': //移除单个缓存
                RDS::hdel(self::$_recentValCacheName, $uKey);
                break;
            case 'get': //读取
                $json = RDS::hget(self::$_recentValCacheName, $uKey);
                return $json ? json_decode($json, 1) : [];
        }
    }


    //检测我的群列表是否写入过缓存
    protected static function _checkMyRecentListCache($uid) {
        $zCacheName = self::_getRecentListCacheName($uid);
        //批量写入缓存
        $pushListCache = function ($zCacheName, $recList) use($uid) {
            foreach ($recList as $v_) {
                $uKey = self::_opt_val('set', $uid, $v_['targetId'], $v_['isGroup'], $v_);
                RDS::rpush($zCacheName, $uKey);
            }
        };
        if(!RDS::existsKey($zCacheName)) {
            $recList = self::_getMyRecenterBySql($uid);
            $pushListCache($zCacheName, $recList);
        } else {
//           print_r('已有缓存');
        }
    }


    //更新最近联系人的 时间 消息id 置顶
    //使用场景(2):
    //单聊的更新(群聊不需要频繁操作 有单独的缓存)
    //最近联系人的置顶操作

    // $data = ['lastMsgId'=> '', 'lastTime'=> '', 'isTop' => ''];
    protected static function _updateRecord($uid, $hisSid, $isGroup, $data) {
        $lastVal = self::_opt_val('get', $uid, $hisSid, $isGroup);
        if($lastVal) {
            $lastVal = array_merge($lastVal, $data);
        } else {
            if(!isset($data['targetId'])) $data['targetId'] = $hisSid;
            if(!isset($data['isGroup'])) $data['isGroup'] = $isGroup;
            if(!isset($data['lastTime'])) $data['lastTime'] = time();
            if(!isset($data['lastMsgId'])) $data['lastMsgId'] = '';
            if(!isset($data['isTop'])) $data['isTop'] = 0;
            $lastVal = $data;
        }
        self::_opt_val('set',$uid, $hisSid, $isGroup, json_encode($lastVal));
    }
    //过度版 最近联系人
    protected static function _getMyRecenterBySql($myUid)
    {
        //获取最近联系人
        $where_ = [
            'uid' => $myUid,
        ];
        return Db::table(self::$table)->field('isGroup,targetId,lastTime,isTop,lastMsgId')->where($where_)->order('lastTime', 'desc')->select();
    }



    //检测是否需要插入最近联系人 好友消息:每次都要检测; 群消息: 如果存在则不需要更新,直接更新群的最近消息即可
    protected static function _ifExistRecent($myUid = 0, $hisSid = 0, $isGroup = 0){
        $zCacheName = self::_getRecentListCacheName($myUid);
        $uKey = self::_getUserTargetKey($myUid, $hisSid, $isGroup);
        $removeNum = RDS::lrem($zCacheName, $uKey); //如果删除数量>0 则存在此key
        if($removeNum) {
            //删除成功 需要 恢复原key
            RDS::rpush($zCacheName, $uKey);
        }
        return $removeNum;
    }


 //=====================  公开接口 开始  =======================================


    //插入最近联系人
    //使用场景
    //好友的每一条新消息
    // 群聊的每一条新消息
    public static function addRecent($myUid, $hisSid, $isGroup, $msgId, $time){
        $exist = self::_ifExistRecent($myUid, $hisSid, $isGroup);
        if(!$exist) {
            //list写入
            $zCacheName = self::_getRecentListCacheName($myUid);
            $uKey = self::_getUserTargetKey($myUid, $hisSid, $isGroup);
            RDS::rpush($zCacheName, $uKey);
            //value更新
            self::_updateRecord($myUid, $hisSid, $isGroup, [
                'lastMsgId'=> $msgId, 'lastTime'=> $time, 'isTop' => 0,
            ]);
        } else {
            if(!$isGroup) {
                //好友单聊,每条消息都需要更新value
                self::_updateRecord($myUid, $hisSid, $isGroup, [
                    'lastMsgId'=> $msgId, 'lastTime'=> $time, 'isTop' => 0,
                ]);
                //群聊有单独的更新方法 optGroupLastInfo
            }
        }
    }


    //移除最近联系人
    public static function removeRecenter($myUid = 0, $hisSid = 0, $isGroup = 0)
    {
        Db::table(self::$table)->where([
            'uid' => $myUid,
            'isGroup' => intval($isGroup),
            'targetId' => $hisSid
        ])->delete();
        $recentValCacheName = 'chat.recentVal';
        $zCacheName = self::_getRecentListCacheName($myUid);
        $uKey = self::_getUserTargetKey($myUid, $hisSid,$isGroup);
        RDS::lrem($zCacheName, $uKey);
        RDS::hdel($recentValCacheName, $uKey);
        //删除多余缓存
        $isGroup = intval($isGroup);
        $key_ = "{$myUid}_{$hisSid}_{$isGroup}";
        RDS::hdel('chat.targetInMysql', $key_);
    }


    //我的最近联系人缓存查询
    public static function getMyRecentListInCache($myUid)
    {
        self::_checkMyRecentListCache($myUid);
        $zCacheName = self::_getRecentListCacheName($myUid);
        $keyList = RDS::lrange($zCacheName, 0, 2000);
        //最新的消息和时间在缓存里拿
        $res = RDS::hMGet(self::$_recentValCacheName, $keyList);
//        print_r('mGets'.PHP_EOL);
//        print_r($res);
//        exit;
        $backList = [];
        foreach ($keyList as $uKey_) {
            $cacheInfo = isset($res[$uKey_]) ? json_decode($res[$uKey_], 1) : [];
            $backList[] = $cacheInfo;
        }
        return $backList;
    }

    public static function setTop($myUid, $hisSid, $isGroup)
    {
        // top +1
        $lastVal = self::_opt_val('get', $myUid, $hisSid, $isGroup);
        $top_ = isset($lastVal['isTop']) ? $lastVal['isTop'] : 0;
        $top_ ++;
        self::_updateRecord($myUid, $hisSid, $isGroup, [
            'isTop' => $top_
        ]);
    }

    //取消置顶
    public static function unTop($myUid, $hisSid, $isGroup)
    {
        // top +1
        $lastVal = self::_opt_val('get', $myUid, $hisSid, $isGroup);
        $top_ = isset($lastVal['isTop']) ? $lastVal['isTop'] : 0;
        $top_ --;
        if($top_<0) $top_ = 0;
        self::_updateRecord($myUid, $hisSid, $isGroup, [
            'isTop' => $top_
        ]);
    }


    //操作群的最后一条消息
    public static function optGroupLastInfo($operate='set/get/del', $gid, $uniqueId='', $time='') {
        $cacheName = 'chat.groupLastMsg';
        switch ($operate) {
            case 'set'://插入最近联系人/群
                RDS::hset($cacheName, $gid, $uniqueId .'|'. $time);
                break;
            case 'exist':
                RDS::hexists($cacheName, $gid);
                break;
            case 'del':
                RDS::hdel($cacheName, $gid);
                break;
            case 'get':
                $val_ = RDS::hget($cacheName, $gid);
                if(!$val_) {
                    return ['', ''];
                }
                list($uniqueId, $time) = explode('|', $val_);
                return [$uniqueId, $time];
        }
    }


}

正文结束

PHP接口(interface)和抽象类(abstract) php 平衡组正则提取字符串中的 json 部分内容