php教程系列四——评论系统设计

评论架构设计图

评论系统如下图所示,具有登录系统,注册系统,站点管理,贴子管理,游客系统,评论管理,评论发表,评论删除,评论回复,评论点赞等等。这是一个简单的模型,作为三方评论系统的基本结构。
php教程系列四——评论系统设计_第1张图片

注册系统

用户数据表在上一篇已经介绍过了,这里不再重复介绍。注册使用的基本信息为用户名,密码,邮箱。与其他数据表关联时,不使用自增id,而是生成唯一id,这样在数据库迁移时也可以把数据很好的对应上,同样的对每一条评论数据,也要单独生成uuid。另外,为方便用户,我们提供了游客账号,稍后讲解。相关代码如下:

public function doRegisterComment()
    {
        $user_validate = new \app\index\validate\CommentUser;
        if(!$user_validate->check($this->request->param('', null, 'urldecode'))){
            dump($user_validate->getError());
        }
        $jsoncallback = $this->get('jsoncallback');
        $account = $this->get('account');
        $newPwd = $this->get('newPwd');
        $confirmPwd = $this->get('confirmPwd');
        $email = $this->get('email');
        $securityCode = $this->get('securityCode');
        $admin = $this->get('admin1234');
        if(Db::name('user')->where('account', $account)->find()){
            return $this->create_res([
                "info"=>"用户名[$account]已存在"
            ]);
        }
        $uniqid = uniqid();
        //create user, usertype:0 normal member, 1 admin, 2 guest
        $id = Db::name('user')->insertGetId([
            'uuid'=>$uniqid,
            'user_type' => $admin ? 1 : 0,
            'account'=>$account,
            'password'=>md5($confirmPwd),
            'email'=>$email,
            'regtime'=>time(),
            'lastlogintime'=>time(),
            'islogin'=>1,
            'head'=>''
        ]);
        //注册成功后
        $second = md5($email);
        $third = md5(time());
        cookie('cookieUserLoginEntityInfos', $uniqid.'_'.$second.'_'.$third);
        $user_data = [
            'id'=>$id,
            'uuid'=>$uniqid,
            'account'=>$account,
            'password'=>md5($confirmPwd),
            'email'=>$email,
            'user_type'=>$admin ? 1 : 0,
            'head'=>'',
            'lastlogintime'=>time()
        ];
        cache($uniqid.'_'.$second.'_'.$third, json_encode($user_data), 24*3600);
        Db::name('user')->where('id', $id)->update(['cache_key'=>$uniqid.'_'.$second.'_'.$third]);
        return $this->create_res(['info'=>'保存成功']);
    }

登录系统

用户填写完表单之后,后台验证用户账号,检查授权信息(接入评论系统必要步骤),验证成功之后,对客户端要设置登录凭证(cookie)。对于用户数据操作,为减少数据库读写,在登录之后,需要把用户数据提取到缓存。否则,数据读写会成为网站负载的瓶颈。代码如下:

public function doLoginComment(){
        if(!$this->check_callback()){
            return $this->return_info("invalid parameter");
        }
        $account = $this->get('account');
        $password = $this->get('pwd');
        $securityCode = $this->get('securityCode');
        $captcha = new Captcha(config('app.varify_config'));
        if(!$captcha->check($securityCode)){
            return $this->create_res(['info'=>'验证码输入错误']);
        }
        $user_entity = Db::name('user')->where('account', $account)->find();
        if(!$user_entity){
            return $this->create_res([
                'info'=>'用户名或密码错误'
            ]);
        }
        if($user_entity['password'] != md5($password)){
            return $this->create_res([
                'info'=>'用户名或密码错误'
            ]);
        }
        $uniqid = $user_entity['uuid'];
        $id = $user_entity['id'];
        $second = md5($user_entity['email']);
        $third = md5(time());
        if($user_entity['cache_key']){
            cache($user_entity['cache_key'], null);
        }
        cookie('cookieUserLoginEntityInfos', $uniqid.'_'.$second.'_'.$third);
        $user_data = [
            'id'=>$id,
            'uuid'=>$uniqid,
            'account'=>$user_entity['account'],
            'password'=>$user_entity['password'],
            'email'=>$user_entity['email'],
            'user_type'=>$user_entity['user_type'],
            'head'=>$user_entity['head']
        ];
        cache($uniqid.'_'.$second.'_'.$third, json_encode($user_data), 24*3600);
        Db::name('user')->where('id', $id)->update(['cache_key'=>$uniqid.'_'.$second.'_'.$third]);
        return $this->create_res(['info'=>'登录成功']);
    }

站点管理及贴子管理

这部分不是本篇讨论的重点,不再详述。

评论点赞

需要注意的是用户必须登录之后才可以对评论进行操作,而且每个用户对每条评论只能操作一次,实现方式有以下几种:

  1. 建立数据表记录每个用户操作过的评论,可以用户id+评论id存储一行,可以用户id+评论列表一行,也可以用户id+评论id作数据,在数据量不太大的情况下都是可以的。
  2. 写入缓存,这种方式查询效率较高,不需要查询Mysql,可以使用memcache, redis做缓存服务器
    我们在这里使用了redis作为记录库,以用户uuid+评论uuid为记录值。代码如下:
if(!$this->check_callback()){
   return $this->return_info("invalid request");
   }
   if(!$this->check_login()){
       return $this->return_info("need login");
   }
   $comment_id = $this->get('commentUuid');
   if(!$comment_id){
       return $this->return_info("commentid not found");
   }
   $user_data = json_decode(cache(cookie('cookieUserLoginEntityInfos')), true);
   $key = $user_data['uuid'].'_'.$comment_id;
   if(cache($key) == 1){
       return $this->create_res(["info"=>"您已经操作过了哦"]);
   }
   cache($key, 1, 0);//this will produce many keys, can be improved by only store userid->data
   Db::name('comment')->where('uuid', $comment_id)->inc('top');
   return $this->create_res([
       "info"=>"顶成功"
   ]);

评论发表与评论回复

评论的发表与评论的回复是同一个处理过程,因为回复评论在我们的系统中,也做成了一条正常的评论。评论回复系统,在数据存储上使用了回复评论列表的形式,可以根据id的先后顺序实现评论回复的级别,因为回复只能针对一条评论进行。

if(!$this->check_login()){
            return $this->return_info("need login");
        }
        $config = Db::name('config')->where('option_name', 'save_switch')->find();
        if($config && !$config['option_value']){
            return $this->return_info("评论系统已经关闭");
        }
        $siteKey = $this->post('siteKey');
        $threadKey = $this->post('threadKey');
        $commentContent = $this->post('commentContent');
        $user_uuid = $this->post('doIframeCommentUuid');
        $commentUuid = $this->post('commentUuid');
        $comment_uuid = get_unique_id();
        $site = Db::name('site')->where('site_key', $siteKey)->find();
        $thread = Db::name('thread')->where('thread_key', $threadKey)->find();
        $site_id = 0;
        $thread_id = 0;
        if(!$site){
            if(config('varify_site')){
                return $this->return_info('invalid parameter, site or thread not regist1');    
            }else{
                $site_id = $this->addSite($siteKey);
            }
        }else{
            $site_id = $site['id'];
        }
        if(!$thread){
            if(config('verify_site')){
                return $this->return_info('invalid parameter, site or thread not regist2');
            }else{
                $thread_id = $this->addThread($threadKey);
            }
        }else{
            $thread_id = $thread['id'];
        }
        $user_data = $this->getLoginUserData();
        if(!$user_data){
            return $this->return_info("login data error, pls contact manager");
        }
        $ret = false;
        if($commentUuid)
        {
            //评论回复
            $comment = Db::name('comment')->where('uuid', $commentUuid)->find();
            if(!$comment){
                return $this->create_res(['info'=>'评论不存在']);
            }else{
                $reply_list = $comment['reply_uuids'];
                if($reply_list)
                    $reply_list = $reply_list . ',' . $comment['uuid'];
                else
                    $reply_list = $comment['uuid'];
                $ret = Db::name("comment")->insert([
                    'uuid' => $comment_uuid,
                    'content' => $commentContent,
                    'posttime' => floor(microtime(true)*1000),
                    'site_id' => $site_id,
                    'thread_id' => $thread_id,
                    'user_uuid' => $user_data['uuid'],
                    'reply_uuids' => $reply_list
                ]);
            }
        }
        else
        {
            $ret = Db::name("comment")->insert([
                'uuid' => $comment_uuid,
                'content' => $commentContent,
                'posttime' => floor(microtime(true)*1000),
                'site_id' => $site_id,
                'thread_id' => $thread_id,
                'user_uuid' => $user_data['uuid'],
                'is_top' => ($user_data['user_type'] == 1 ? 1 : 0)
            ]);
        }
        if($user_data['user_type'] == 1){
            $key = $this->select_top_key . $site_id . $thread_id;
            cache($key, null);
            $key = $this->select_top_cnt . $site_id . $thread_id;
            cache($key, null);
        }else{
            //clear comment caches
            $key = $this->select_key . $site_id . $thread_id;
            cache($key, null);
            $page_idx = 1;
            $order_type = [1,2,3];
            foreach($order_type as $type){
                $page_idx = 1;
                while(true){
                    $cahce_key_check = "?".$this->select_key.$site_id.$thread_id.$type.$page_idx;
                    $cache_key = $this->select_key.$site_id.$thread_id.$type.$page_idx;
                    if(cache($cahce_key_check, '')){
                        cache($cache_key, null);
                        $page_idx += 1;
                    }else{
                        break;
                    }
                }
            }
            $key = $this->count_key . $site_id . $thread_id;
            cache($key, null);
        }
        
        if($ret){
            return $this->create_res([
                'info'=>'发布成功'
            ]);
        }
        return $this->create_res([
            'info'=>'数据操作错误'
        ]);

评论删除与评论管理

这两个部分放一起是因为用户系统可以是统一的,用户分为管理员,注册用户与游客(也是普通用户),对于注册用户或游客只可以删除自己发过的评论,而对于管理员可以删除任何评论,包括自己的评论。这部分实现比较简单,验证权限之后操作就可以了。

结语

评论系统就介绍到这里,欢迎在下方评论。

你可能感兴趣的:(PHP)