活动投骰子
代碼git地址:https://gitee.com/wyqgg/iblog.git
描述
首先獲得骰子
? 每天(首次登錄、簽到)之后都可以獲得一個骰子
擲骰子邏輯
獲取前進步數(每次使用全部的骰子,每個骰子可以前進1-6步),每個步數都有獎勵(前進一步可以獲得10積分,每到達特定步數可以獲得指定金幣獎勵)
?
實現:
首先設置redis緩存
key
| date(‘Y-m-d’, time()) . “__” . $uid ."_Login_dice" | string | 86400 | 登陸鎖,來指定首次登陸 |
| date(‘Y-m-d’, time()) . “__” . $uid ."_signIn_dice" | string | 86400 | 簽到鎖,來指定首次登陸 |
| $uid . ‘_diceNum’ | string | 30*86400 | 用戶骰子的總數 |
| $uid . ‘_diceStep’ | string | 30*86400 | 用戶擲骰子走的步數 |
| $uid._‘rollDiceLock’ | string | 3 | redis防止并發點擊鎖 |
| stepRank | zset | 30*86400 | 用戶步數排行榜 |
數據表
骰子活動記錄表
CREATE TABLE `activity_score` (`id` int(11) unsigned NOT NULL AUTO_INCREMENT,`uid` int(11) DEFAULT NULL COMMENT '用戶id',`step` int(5) DEFAULT NULL COMMENT '前進的步數',`after_step` int(5) DEFAULT NULL COMMENT '使用之后的步數',`score` int(11) DEFAULT NULL COMMENT '獲取的積分',`coin` int(11) DEFAULT '0' COMMENT '額外金幣獎勵,未獲得則為零',`use_num` int(5) DEFAULT NULL COMMENT '使用的骰子數量',`init_time` int(11) DEFAULT NULL,`last_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,PRIMARY KEY (`id`),KEY `uid_index` (`uid`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=gbk COMMENT='骰子活動記錄表';邏輯
? 每次登陸、和簽到獲得骰子放在指定的鍵中,每天首次登陸和簽到自增骰子($uid . ‘_diceNum’)的數量。
? 每次登陸簽到設置鎖,用來判斷用戶是否可以獲得骰子
? 擲骰子的時候獲取用戶的骰子總數減去骰子記錄表中的該用戶指定時間小號的骰子數量就可以獲得剩余的骰子數量,然后計算出用戶可以前進的步數,增加到用戶步數中($uid . ‘_diceStep’),然后將用戶和步數放到用戶步數排行(stepRank)中,將本次骰子的記錄放到骰子記錄表中,將獲得的獎勵存到用戶賬戶中,即可。
使用ci框架在iblog項目中實現具體代碼為:
控制器代碼:
(將邏輯處理全部放到service層代碼,這里只做參數獲取)
class Activity extends Base_Controller {public function __construct(){parent::__construct();$this->load->service('Activity_service');$this->load->model('Activity_model');}//擲骰子操作public function rollDice(){$data = $this->input->post();$uid = empty($data['uid']) ? 0 : $data['uid'];$this->Activity_service->rollDice($uid);}//獲取用戶步數排行榜public function stepRank(){$data = $this->input->post();$uid = empty($data['uid']) ? 0 : $data['uid'];$this->Activity_service->stepRank($uid);}//擲骰子活動首頁public function diceIndex(){$data = $this->input->post();$uid = empty($data['uid']) ? 0 : $data['uid'];$this->Activity_service->diceIndex($uid);} }登錄和簽到中添加增加骰子方法
/** 用戶簽到 User控制器*/ public function signIn() {$data = $this->input->post();$rules = [['field' => 'user_id','label' => '用戶id','rules' => 'required','errors' => ['require' => '用戶id不能為空',]],];$res = $this->validate($data, $rules);if (!$res) {fail(400, '參數錯誤!');}$data['init_date'] = date('Y-m-d', time());$isSignIn = $this->users_model->isSignIn($data);if ($isSignIn) {fail(400, '已簽到,不可重復簽到!');}//獲取當前用戶昨天簽到的keep_time+1,若沒有則為1;$time = date('Y-m-d', strtotime('-1 days'));$data['keep_time'] = $this->users_model->getSignInKeep($data['user_id'], $time);//簽到操作$data1 = $this->users_model->signIn($data);if (!$data1) {fail(400, '簽到失敗!');}//簽到成功增加一個骰子$this->Activity_service->addDice($data['user_id'],'signIn');success($data, '簽到成功!'); }//登錄 Login控制器 public function login() {//獲取數據$post = $this->input->post();//查詢是否有該用戶名$data = $this->users_model->findByUsername($post['username']);if ($data) {if (encrypt($post['password']) == $data['password']) {//記錄登錄日志$sqlData = ['ip' => $_SERVER['SERVER_ADDR'],'init_time' => time(),'user_name' => $post['username'],'uid' => $data['id']];$data['init_time'] = time();//登錄日志$this->logger_model->userLoginLog($sqlData);//用戶首次登錄成功送一個骰子$this->Activity_service->addDice($data['id'],'Login');$data['image'] = "http://cakephp.com/" . $data['image'];success($data, '登陸成功!');} else {fail(501, '密碼錯誤!');}} else {fail(500, '沒有該用戶!');} }service代碼:
<?phpclass Activity_service extends MY_Service {//redis連接private $conn;public function __construct(){parent::__construct();$this->load->model('Activity_model');$this->conn = getRedis();//活動的數據放在三庫$this->conn->selectDb(3);}//擲骰子操作public function rollDice($uid){if ($uid == 0) {fail(400, '參數錯誤');}$lock = $uid . '_rollDiceLock'; //redis防止并發點擊鎖if (!$this->conn->setNxExpire($lock, 1, 3)) {fail(400, '點擊太快了,稍后再試');}$num = $this->getDiceNum($uid);if (!$num) {$this->conn->del($lock); //移除鎖fail(400, '骰子數量不足');}//執行擲骰子操作(有多少骰子,全部擲出)$step = 0;for ($i = 1; $i <= $num; $i++) {$step += rand(1, 6);}if ($step <= 0) {$this->conn->del($lock); //移除鎖fail(400, '前進步數計算錯誤');}//更新用戶已經走的步數$step_key = $uid . '_diceStep';$this->conn->incrBy($step_key, $step);//更新步數排行榜 (向有序set中指定id加指定的步長)$step_rank_key = 'stepRank';$after_step = $this->conn->zIncrBy($step_rank_key, $step, $uid);//獲取到的積分獎勵(每一步獲取10積分)$score = $step * 10;//額外金幣獎勵$coin = $this->getAward($step, $after_step);//將獲得的全部獎勵記錄到本次骰子活動的記錄表中$this->Activity_model->addScore($uid, $step, $score, $coin, $after_step, $num);//將積分獎勵增加到用戶賬戶上$this->Activity_model->updateScore($uid, $score, $coin);$res = ['step' => $step,'score' => $score,'use_num' => $num,'coin' => $coin,];$this->conn->del($lock);//移除鎖success($res);}/** 獲得骰子(首次登錄、每日簽到)*/public function addDice($uid, $action){//將當天日期上鎖$lock_key = date('Y-m-d', time()) . '_' . $uid . "_{$action}_" . '_dice';$status = $this->conn->setNx($lock_key, 1);$this->conn->expire($lock_key, 86400);//只有首次進來才加骰子if ($status) {$dice_key = $uid . '_diceNum';$this->conn->expire($dice_key, 86400 * 7);$this->conn->incr($dice_key);}}/** 擲骰子的額外獎勵*/public function getAwardList(){return [['step' => 10,'coin' => 100],['step' => 20,'coin' => 200,],['step' => 50,'coin' => 500,],['step' => 100,'coin' => 1000,],['step' => 200,'coin' => 2000,],['step' => 500,'coin' => 10000,],];}/** 獲取用戶可以使用的骰子數量*/public function getDiceNum($uid){//獲取用戶的骰子數$key = $uid . '_diceNum';//從redis中獲取用戶全部的骰子數量$allNum = $this->conn->get($key);//獲取用戶已經消耗的骰子數量$useNum = $this->Activity_model->getUseNum($uid);//返回剩下的骰子數量,不夠的話返回falsereturn ($allNum - $useNum >= 0) ? $allNum - $useNum : false;}/** 獲取用戶可以獲得的額外金幣獎勵*/public function getAward($step, $after_step){//獲取用戶本次擲骰子獲取的額外獎勵$list = $this->getAwardList();$list = array_column($list, null, 'step');$award = 0;//獲取用戶可以獲得的額外獎勵foreach ($list as $k => $v) {if ($after_step >= $v['step'] && $after_step - $step < $v['step']) {$award += $v['coin'];}}return $award;}//活動首頁public function diceIndex($uid){if (!$uid) {fail(400, '參數錯誤!');}//全部榜單$list = $this->userStepRank();//用戶距離榜單的數據$userInfo = $this->stepDistance($list, $uid);//用戶剩下的骰子數量$num = $this->getDiceNum($uid);$userInfo['num'] = $num;//用戶走過的全部步數$key = $uid . "_diceStep";$userStep = $this->conn->get($key);$award = $this->getAwardList();foreach ($award as $k => $v) {if ($userStep >= $v['step']) {unset($award[$k]);}}$data = ['userInfo' => $userInfo,'award' => $award,];success($data);}//榜單頁面public function stepRank($uid){//獲取全部榜單$list = $this->userStepRank();//取榜單前三$userRank3 = array_slice($list, 0, 3);//取前三名用戶的詳細信息$userInfo3 = $this->Activity_model->stepRank($userRank3);foreach ($userInfo3 as $k => $v) {foreach ($userRank3 as $kk => $vv) {if ($v['id'] == $vv['uid']) {$userRank3[$kk]['nickname'] = $v['nickname'] ? $v['nickname'] : '';$userRank3[$kk]['image'] = $v['image'] ? $v['image'] : '';}$userRank3[$kk]['award'] = $this->rankAward()[$vv['rank']];}}//獲取用戶的步數信息$userRank = $this->stepDistance($list, $uid);$userInfo = $this->Activity_model->commonQuery('nickname,image', ['id' => $uid], 'users');$userRank['nickname'] = $userInfo[0]['nickname'];$userRank['image'] = $userInfo[0]['image'];$data = ['rankList' => $userRank3,'userInfo' => $userRank];success($data);}//獲取完成的用戶排行榜單public function userStepRank(){//獲取榜單的前3名數據$step_rank_key = 'stepRank';//獲取全部的榜單$rank = $this->conn->zRevRange($step_rank_key, 0, -1, true);$rank1 = 1;foreach ($rank as $k => $v) {$res[] = ['uid' => $k,'step' => $v,'rank' => $rank1];$rank1++;}return $res;}//獲取用戶在榜單的位置以及距離上一名需要的步數public function stepDistance($list, $uid){$uidKeysArray = array_column($list, null, 'uid');//前三名$uidKeysArray3 = array_slice($uidKeysArray, 0, 3, true);//用戶在榜單中if (array_key_exists($uid, $uidKeysArray3)) {foreach ($list as $k => $v) {if ($v['uid'] == $uid) {if ($v['rank'] == 1) {$distance = 0;} else {$distance = $list[$k - 1]['step'] - $v['step'];}$res = ['uid' => $uid,'rank' => $v['rank'],'step' => $v['step'],'distance' => $distance];}}} else {//用戶不在榜單中,則距離榜單最后一名需要的步數$end = end($uidKeysArray3);$res = ['uid' => $uid,'rank' => $uidKeysArray[$uid]['rank'],'step' => $uidKeysArray[$uid]['step'],'distance' => $end['step'] - $uidKeysArray[$uid]['step']];}return $res;}//榜單前三名活動結束之后可以獲得的獎勵列表public function rankAward(){return [1 => '100000金幣',2 => '50000金幣',3 => '10000金幣',];} }model層代碼:
這里我定義了一個Base_model里面定義了幾個比較基礎的通用增刪改查方法
<?php /*** Created by PhpStorm.* User: wyq* Date: 2021/10/29* Time: 13:35*/class Activity_model extends Base_Model {public function __construct(){parent::__construct();}public function addScore($uid, $step, $score, $coin, $after_step, $num){$data = ['uid' => $uid,'step' => $step,'score' => $score,'coin' => $coin,'after_step' => $after_step,'use_num' => $num,'init_time' => time()];return $this->commonInsert('activity_score', $data);}public function getUseNum($uid){$res = $this->commonQuery('sum(use_num) num','uid='.$uid,'activity_score');return $res[0]['num'];}public function updateScore($uid, $score,$coin){$before = $this->commonQuery('score,coin', ['id' => $uid], 'users');$score += $before[0]['score'];$coin += $before[0]['coin'];return $this->commonUpdate('users', ['score' => $score,'coin' => $coin], ['id' => $uid]);}public function stepRank($rank){$uid = array_column($rank, 'uid');$res = $this->db->select('id,nickname,image')->where_in('id', $uid)->get('users');return $res->num_rows() ? $res->result_array() : false;} }Base_model代碼
<?phpclass Base_Model extends CI_Model {public function __construct(){parent::__construct();$this->load->database();}/** 通用的簡單檢索*/public function commonQuery($fields, $where, $table){$query = $this->db->select($fields)->where($where)->get($table);return $query->num_rows() ? $query->result_array() : [];}/** 通用的簡單刪除*/public function commonDelete($table, $where){$this->db->delete($table, $where);return $this->db->affected_rows() ? true : false;}/** 通用的新增*/public function commonInsert($table, $value){$this->db->insert($table, $value);return $this->db->affected_rows() ? true : false;}/** 通用的修改*/public function commonUpdate($table, $value, $where){$this->db->update($table, $value, $where);return $this->db->affected_rows() ? true : false;} }這里我使用的接口編程(只能本地訪問)
接口返回
擲骰子操作
請求地址:(ci.com/activity/rollDice)
請求類型: POST
請求參數: uid(用戶id)
成功返回示例:
{"code": 200,"msg": "success","data": {"step": 299,"score": 2990,"use_num": 92,"coin": 3500} }失敗示例:
{"code": 400,"msg": "骰子數量不足","data": [] }返回參數
| step | int | 本次前進的步數 |
| score | int | 獲得的積分 |
| use_num | int | 本次使用的骰子數量 |
| coin | int | 本次獲得的額外金幣獎勵 |
獲取榜單
請求地址:(ci.com/activity/stepRank)
請求類型: POST
請求參數: uid(用戶id)
返回示例:
{"code": 200,"msg": "success","data": {"rankList": [{"uid": 2,"step": 337,"rank": 1,"nickname": "王永清1","image": "static/img/2021-11-01/16357612048552.png","award": "100000金幣"},{"uid": 3,"step": 11,"rank": 2,"award": "50000金幣","nickname": "石趙玉","image": "static/img/2021-11-01/16357612074427.png"},{"uid": 4,"step": 6,"rank": 3,"award": "10000金幣","nickname": "住我心,免房租1","image": "static/img/2021-11-01/16357612109097.png"}],"userInfo": {"uid": "2","rank": 1,"step": 337,"distance": 0,"nickname": "王永清1","image": "static/img/2021-11-01/16357612048552.png"}} }返回參數列表
| rankList | json | 榜單信息 這里只取前三名 |
| rankList.nickname | string | 榜單用戶昵稱 |
| rankList.step | int | 榜單用戶前進的步數 |
| rankList.rank | int | 榜單用戶排名 |
| rankList.award | string | 該排名可以獲得的獎勵 |
| rankList.image | string | 榜單用戶頭像 |
| userInfo | json | 當前登錄用戶信息 |
| userInfo.distance | int | 距上一名的步數差,若為沒上榜則距離上榜的步數差 |
活動首頁
請求地址:(ci.com/activity/diceIndex)
請求類型: POST
請求參數: uid(用戶id)
返回示例:
{"code": 200,"msg": "success","data": {"userInfo": {"uid": "3","rank": 2,"step": 11,"distance": 326,"num": 0},"award": {"1": {"step": 20,"coin": 200},"2": {"step": 50,"coin": 500},"3": {"step": 100,"coin": 1000},"4": {"step": 200,"coin": 2000},"5": {"step": 500,"coin": 10000}}} }返回參數列表
| award | json | 用戶未獲得的額外獎勵列表 |
| award.step | string | 額外獎勵獲得需要的步數 |
| award.coin | int | 額外獎勵的金幣數量 |
| userInfo | int | 當前登錄用戶信息 |
| userInfo.step | string | 用戶的步數 |
| userInfo.image | string | 榜單用戶頭像 |
| userInfo.rank | json | 用戶排名 |
| userInfo.distance | int | 距上一名的步數差,若為沒上榜則距離上榜的步數差 |
| userInfo.num | int | 當前用戶可以使用的骰子數 |
這里因為本系統中沒有額外的道具,所以獎勵只能有用戶的積分,和金幣。若其他系統額外的獎勵可以是其他的道具,基本功能可以實現,但是功能還不是很完善。
總結
- 上一篇: Tile based Rendering
- 下一篇: 目标检测(后处理):从 NMS 到 So