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,功能至此实现了。

posted @ 2017-04-28 10:29  十月桂花香十里  阅读(2282)  评论(0编辑  收藏  举报
我一辈子走过许多地方的路, 行过许多地方的桥, 看过许多次数的云, 喝过许多种类的酒, 如今却只爱一个正当最好年纪的人。