php實現題目搶答、商品秒殺等類型的需求

最近和其餘部門合做項目,固然我是負責php接口方面的工做,
get到一些東西,因此來分享記錄一下。php

項目需求:html

題目將經過主持人ipad投射至大屏幕,選手按‘搶答’
按鈕進行搶答。搶答成功,選手所在組,以及大屏幕上廣播搶答成功者的ipad屏幕,
搶答失敗選手,返回搶答失敗界面。laravel

需求分析:redis

這裏搶答,其實就是和秒殺活動機制同樣了,不過這裏場景可能稍微複雜點,
須要用到強弱鏈接,實時廣播,你們能夠去看看GatewayWordker固然,今天咱們只是單純
討論搶答機制是如何實現。那麼既然搶答,就要考慮高併發問題了。數據庫

思路分析:json

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,功能至此實現了。

相關文章
相關標籤/搜索