最近和其餘部門合做項目,固然我是負責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,功能至此實現了。