zoukankan      html  css  js  c++  java
  • php实现题目抢答、商品秒杀等类型的需求

    最近和其他部门合作项目,当然我是负责php接口方面的工作,
    get到一些东西,所以来分享记录一下。

    项目需求:

    题目将通过主持人ipad投射至大屏幕,选手按‘抢答’
    按钮进行抢答。抢答成功,选手所在组,以及大屏幕上广播抢答成功者的ipad屏幕,
    抢答失败选手,返回抢答失败界面。

    需求分析:

    这里抢答,其实就是和秒杀活动机制一样了,不过这里场景可能稍微复杂点,
    需要用到强弱连接,实时广播,大家可以去看看GatewayWordker当然,今天我们只是单纯
    讨论抢答机制是如何实现。那么既然抢答,就要考虑高并发问题了。

    思路分析:

    1.把题目的状态写在redis里面,比如题目还没有被抢状态为1,抢完状态为0
    2.选手进行抢答时,查询redis状态,为0,直接返回,题目已经被抢完;
    3.选手进行抢答时,查询redis状态,为1,进入下一步逻辑操作,修改redis状态为0,
    进入数据库查询改题目数据,运用行级琐机制。返回逻辑处理结果

    框架依然用的是laravel,开发模式用的是仓库模式,这用有利于项目后期的维护和升级。

    (数据字段涉及安全,暂时用test1等表示便好)

    /******首先进行题目获取,并把题目对应的redis编号改为1******/

     1     /**
     2      * @desc    随机获得抢答题题目
     3      * @date    2017/4/19 17:26
     4      * @param   [type]
     5      * @author  十月桂花香十里
     6      * @return  [bool or array]
     7      */
     8     public function getQuickQuestion(){
     9         //获取抢答题随机题号id
    10         $randNum = $this->getRandArray(config('test.start_quick_id'),config('test.end_quick_id'),config('djm.max_quick'));
    11         DB::transaction(function () use ($randNum){
    12             //将抢答题题号id写进redis,值为1题目可以进行抢答,0不可以进行抢答(为了便于后期维护,0和1都可以写进配置文件中)
    13             $redis = PRedis::connection('default');
    14             foreach($randNum as $k=>$v){
    15                 $redis->set("check_quick_id_".$v,1);
    16             }
    17             //将数据库中抢答题对应的id记录,test3状态改为可进行抢答
    18             //DjmQuestion::whereIn('id', $randNum)->update(['test3'=>1]);
    19         });
    20         $res = DjmQuestion::whereIn('id', $randNum)
    21          ->orderByRaw(DB::raw("FIELD(id, ".implode(',', $randNum).")"))
    22             ->get(['id','test1','test2','test3','test4']); 23 if($res){
    24       foreach($res as $k=>$v){ 25 $res[$k]['test6'] = json_decode($v['test6'],true); 26 } 27 //处理需要返回的数组 28 return $res; 29 }else return false;
    30 }

    /******利用php的array_slice函数实现编号的随机选择******/

     1     /*
     2      * function getTenNum( int $min, int $max, int $num)
     3      * 生成一定数量的随机数
     4      * $min 和 $max: 指定随机数的范围
     5      * $num: 指定生成数量
     6      */
     7     public function getRandArray($min,$max,$num){
     8         $array = range($min,$max);
     9         shuffle($array);
    10         $array = array_slice($array, -$num);
    11         return $array;
    12     }

    /******选手抢答题目逻辑的实现******/

     1     /**
     2      * @desc    选手进行题目的抢答
     3      * @date    2017/4/19 18:19
     4      * @param   [$id $uid]
     5      * @author  十月桂花香十里
     6      * @return  [bool or array]
     7      */
     8     public function userQuickAnswer($id='',$uid){
     9         //判断uid是否是答题选手
    10         if(!in_array($uid, config('test1.check_uid'))) return false;
    11         //判断$id是否存在
    12         if($id < config('test1.start_quick_id') || $id > config('test1.end_quick_id')) return false;
    13         //redis判断题目是否可以进行抢答
    14         $redis = PRedis::connection('default');
    15         $check_quick_status = $redis->get("check_quick_id_".$id);
    16         if($check_quick_status ==1){
    17             //运用事务,添加共享锁
    18             //DB::transaction(function () use ($redis,$id){
    19                 //将redis的状态和数据库field3改为0,变为不可抢答
    20                 //DjmQuestion::where('id',$id)->sharedLock()->update(['test1'=>0]);
    21             //});
    22             //将redis的状态改为0,变为不可抢答
    23             $redis->set("check_quick_id_".$id,0);
    24             //redis绑定此题和选手的关系
    25             $redis->set("check_quick_id_".$id."_".$uid,1);
    26             //获取本条数据记录
    28 return DjmQuestion::find($id)->toArray();
    29 }else return false; 30 }

    /******选手抢答题目回答的实现******/

     1     /**
     2      * @desc    选手进行抢答题的回答
     3      * @date    2017/4/20 10:09
     4      * @param   [$id,$uid]
     5      * @author  1245049149@qq.com
     6      * @return  [bool or array]
     7      */
     8     public function userQuickAnswerResult($id,$uid,$answer){
     9         //判断uid是否是答题选手
    10         if(!in_array($uid, config('djm.check_uid'))) return false;
    11         //判断$id是否存在
    12         if($id < config('djm.start_quick_id') || $id > config('djm.end_quick_id')) return false;
    13         //redis判断此题和选手是否绑定关系
    14         $redis = PRedis::connection('default');
    15         $check_user_quick_status = $redis->get("check_quick_id_".$id."_".$uid);
    16         if($check_user_quick_status == 1){
    17                //关系如果已经绑定,判断选手答题情况
    18                 $redis->set("check_quick_id_".$id."_".$uid,0);
    19                 $check_answer = config('djm.check_answer'); //题目编号答案对照(如果题目数量不多且固定的话,建议写进配置文件中,不用查询数据库)
    20                 if($check_answer[$id] == strtoupper($answer)){
    21                     //回答正确分数自加5
    22                     DjmQuestionScore::where('test10',$uid)
    23                         ->where('test11',config('djm.quick_test11'))
    24               ->increment('test12',5); 25 return array( 26 'msg' => '回答正确', 27 'status' => 1, 28 ); 29 }else{ 30 //回答错误分数自减5 32 DjmQuestionScore::where('test10',$uid) 33  ->where('test11',config('djm.quick_test11'))
    34               ->decrement('score',5); 35 return array( 36 'msg' => '回答错误,正确答案:'.$check_answer[$id], 37 'test12' => $check_answer[$id], 38 'status' => 0, 39 ); 40 } 41 }else return false; 42 }

    ok,功能至此实现了。

  • 相关阅读:
    1014 Waiting in Line (30)(30 point(s))
    1013 Battle Over Cities (25)(25 point(s))
    1012 The Best Rank (25)(25 point(s))
    1011 World Cup Betting (20)(20 point(s))
    1010 Radix (25)(25 point(s))
    1009 Product of Polynomials (25)(25 point(s))
    1008 Elevator (20)(20 point(s))
    1007 Maximum Subsequence Sum (25)(25 point(s))
    1006 Sign In and Sign Out (25)(25 point(s))
    1005 Spell It Right (20)(20 point(s))
  • 原文地址:https://www.cnblogs.com/qwgshare/p/6780465.html
Copyright © 2011-2022 走看看