gitee地址

     https://gitee.com/wyqgg/iblog.git
概述:

这里我新增了道具表、背包道具表和背包道具流水表是为了字符兑换活动可以兑换一些可以使用的道具到我的背包中,获得道具或者消耗道具时,在背包道具流水表中都有记录。盲盒字符兑换流水表是为了将用户兑换各种奖励记录下来,因为每种道具的兑换是有限制的。

字符兑换道具基本逻辑:定义可以兑换的道具的id、num、以及需要的字符index数组,在兑换道具时需要传递用户id和需要兑换的道具的index,在兑换操作时具体操作

1、获得字符可兑换的道具数组,通过传递的index获取到当前兑换的道具,在从redis中获取用户全部的字符即数量,若字符不足则返回失败

2、查看用户是否已经达到兑换限制:在盲盒字符兑换流水表中获取兑换的次数

3、上锁防止多次点击、在redis中扣除用户需要消耗的字符数量

4、添加道具到用户背包道具表,然后记录背包道具流水。

5、增加盲盒字符兑换流水记录,将本次兑换的记录存到数据表中

兑换金币的话,就不需要判断兑换限制,以及记录背包流水,只需要修改用户表数据即可。

数据表sql
CREATE TABLE `prop_log` (`id` int unsigned NOT NULL AUTO_INCREMENT,`uid` int unsigned NOT NULL COMMENT '用户id',`prid` int unsigned NOT NULL COMMENT '道具编号',`num` int NOT NULL DEFAULT '1' COMMENT '数量',`desc` varchar(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '描述信息',`init_time` int unsigned NOT NULL COMMENT '记录创建时间',`last_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录上次修改时间',PRIMARY KEY (`id`),KEY `uid` (`uid`),KEY `init_time` (`init_time`),KEY `ik_desctxt` (`desc`),
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='背包道具获得记录表';CREATE TABLE `rucksack` (`id` int unsigned NOT NULL AUTO_INCREMENT,`uid` int unsigned NOT NULL COMMENT '用户id',`prid` int unsigned NOT NULL COMMENT '道具编号',`num` int unsigned NOT NULL DEFAULT '1' COMMENT '道具数量',`init_time` int unsigned NOT NULL COMMENT '记录创建时间',`last_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录上次修改时间',PRIMARY KEY (`id`),UNIQUE KEY `uidprid` (`uid`,`prid`),KEY `uid` (`uid`),KEY `init_time` (`init_time`),KEY `idx_prid_nums` (`prid`,`num`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb3 COMMENT='我的背包表';CREATE TABLE `blind_box_char_exchange` (`id` int unsigned NOT NULL AUTO_INCREMENT,`uid` int unsigned NOT NULL COMMENT 'uid',`use_index` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '使用的字符序号 0-3 分别为W Y Q G, 逗号连接',`award_index` tinyint unsigned NOT NULL COMMENT '奖品序号',`init_time` int unsigned NOT NULL COMMENT '记录创建时间',`last_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录上次修改时间',PRIMARY KEY (`id`),KEY `uid` (`uid`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='盲盒字符兑换流水';CREATE TABLE `prop` (`id` int unsigned NOT NULL AUTO_INCREMENT,`prop_name` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '道具名称',`desc` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '道具介绍',`status` int DEFAULT '1' COMMENT '道具状态 0不使用 1使用 ',`image` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '道具展示图片',`init_time` int DEFAULT NULL,`last_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,PRIMARY KEY (`id`),KEY `time_idx` (`init_time`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin COMMENT='道具表';
controller层代码
// 字符兑换操作public function charExchange(){$data = $this->input->post();if (empty($data['uid']) || $data['index'] < 0) {fail(400, '参数错误!');}$this->Activity_service->charExchange($data['uid'], $data['index']);}//用户字符已兑换奖励列表public function charExchangeAward(){$data = $this->input->post();if (empty($data['uid'])){fail(200,'参数错误!');}$this->Activity_service->charExchangeAward($data['uid']);}//用户字符兑换奖励页面public function charExchangeAwardIndex(){$data = $this->input->post();if (empty($data['uid'])){fail(400,'参数错误!');}$this->Activity_service->charExchangeAwardIndex($data['uid']);}
service层代码
 //字符兑换列表public function charExchangeList(){return [['propId' => 0, //一个金币'useChar' => [],'num' => -1, //无上限],['propId' => '1',    //道具id'useChar' => [0, 1], //需要的字符'num' => 10,  //兑换上限],['propId' => '2','useChar' => [0, 1, 2],'num' => 5,],['propId' => '3','useChar' => [0, 1, 2],'num' => 5,],['propId' => '4','useChar' => [0, 1, 2, 3],'num' => 1],];}/**字符兑换道具* @param $uid* @param $index*/public function charExchange($uid, $index){$awardList = $this->charExchangeList();$awardListItem = $awardList[$index]; //通过index找到当前用户兑换的道具if (empty($awardListItem)) {fail(400, '配置错误!');}//查看用户字符是否足够兑换该道具$charList = $this->Activity_model->getChars($uid);if (empty($charList)) {fail(400, '字符数量不够!');}foreach ($awardListItem['useChar'] as $k => $v) {if (empty($charList[$k]) || $charList[$k] < 1) {fail(400, '字符数量不够!');}};//到这里说明字符数量足够//上锁$lockKey = "charExchange_" . $uid;if (!$this->conn->setNxExpire($lockKey, 1, 3)) {fail(400, '系统繁忙,稍后重试!');}//非金币兑换if ($awardListItem['propId']) {//通过查询兑换记录查看用户是否已经达到兑换限制$usedNum = $this->Activity_model->getExchangePropNum($uid, $index);if ($usedNum[0]['num'] >= $awardListItem['num']) {fail(400, '该道具兑换已达上限!');}//首先扣除字符道具foreach ($awardListItem['useChar'] as $k => $v) {$res = $this->Activity_model->changeCharNum($uid, $k, -1);if (!$res){//扣除失败,移除锁$this->conn->del($lockKey);fail(400,'字符数量不够!');}}//获取兑换的道具名$prop_name = $this->Activity_model->getPropNameById($awardListItem['propId']);//将道具增加到用户的背包中$this->Activity_model->addRucksack($uid, $awardListItem['propId'], 1, '字符兑换道具' . $prop_name[0]['prop_name']);$useIndex = implode(',', $awardListItem['useChar']);} else {//金币兑换,使用最多的字符兑换金币$max = 0;foreach ($charList as $k => $v) {if ($v > $max) {$max = $v;$useChar = $k;}}//扣除字符$res = $this->Activity_model->changeCharNum($uid, $useChar, -1);if (!$res) {$this->conn->del($lockKey);fail(200, '字符数量不够!');}//向用户账户增加金币$this->Activity_model->changeCoins($uid, 1);$useIndex = $useChar;}$this->Activity_model->addExchangeLog($uid, $useIndex, $index, time());success();}//用户字符已兑换奖励列表public function charExchangeAward($uid){if (!$this->Users_model->checkUser($uid)) {fail(400, '参数错误!');}//从兑换记录流水中获取到我的兑换记录$exchangeAward = $this->Activity_model->getExchangePropLog($uid);//字符兑换的全部奖励$exchangeList = $this->charExchangeList();$exchangeAward = array_column($exchangeAward, null, 'award_index');//通过字符兑换全部奖励得到兑换的道具奖励prop_idforeach ($exchangeList as $k => $v) {foreach ($exchangeAward as $k1 => $v1) {if ($k == $k1) {$exchangeAward[$k1]['propId'] = $v['propId'];//通过prop_id得到道具的名称$prop_name = $this->Activity_model->getPropNameById($v['propId']);if (!empty($prop_name[0]['prop_name'])) {$data[] = ['prop_id' => $v['propId'],'num' => $exchangeAward[$k1]['num'],'prop_name' => $prop_name[0]['prop_name']];} else {$data[] = ['prop_id' => $v['propId'],'num' => $exchangeAward[$k1]['num'],'prop_name' => "一个金币"];}}}}success($data);}//用户字符兑换字符首页public function charExchangeAwardIndex($uid){if (!$this->Users_model->checkUser($uid)) {fail(400, '没有该用户!');}//获取字符兑换的奖励列表$charExchangeList = $this->charExchangeList();//获取我已经兑换的奖励列表$charExchangedList = $this->Activity_model->getExchangePropLog($uid);$charExchangedList = array_column($charExchangedList, null, 'award_index');//获取我的字符列表$charList = $this->Activity_model->getChars($uid);//按index排序ksort($charList);foreach ($charExchangeList as $k => $v) {foreach ($charExchangedList as $k1 => $v1) {$propName = $this->Activity_model->getPropNameById($charExchangeList[$k]['propId']);$used = 0;if ($k == $k1) {$used = $charExchangedList[$k1]['num'];}}$list[] = ['prop_id' => $charExchangeList[$k]['propId'],'prop_name' => $charExchangeList[$k]['propId'] ? $propName[0]['prop_name'] : "一个金币",'num' => $charExchangeList[$k]['num'],'used' => $used,'needChar' => $charExchangeList[$k]['useChar'],];}$data['awardList'] = $list;$data["hasCharList"] = $charList;success($data);}
model层代码
    public function getExchangePropNum($uid, $index){return $this->commonQuery('count(id) num', ['uid' => $uid, 'award_index' => $index], 'blind_box_char_exchange');}public function getChars($uid){$key = $uid . "_charNum";return $this->conn->hGetAll($key);}/*** @param $uid 用户id* @param $propId 道具id* @param $num 获得数量 + 为获得 - 为消耗* @param $desc 记录背包道具流水描述信息*/public function addRucksack($uid, $propId, $num, $desc){if ($num > 0) {$value = "num+" . $num;} else {$value = "num-" . $num;}//先做修改操作$rowData = ['uid' => $uid,'prid' => $propId,'num' => $num,'init_time' => time()];$this->db->set('num', $value, false)->where(array('prid' => $propId, 'uid' => $uid))->limit(1)->update('rucksack');//若修改失败,则说明该用户没有该道具if (!$this->db->affected_rows()) {$sql = $this->db->insert_string('rucksack', $rowData) . ' ON DUPLICATE KEY UPDATE `num`=' . $value;$this->db->query($sql);if (!$this->db->affected_rows()) {return false;}}$rowData['desc'] = $desc;//增加流水$this->commonInsert('prop_log', $rowData);}public function getPropNameById($id){return $this->commonQuery('prop_name', ['id' => $id], 'prop');}public function addExchangeLog($uid, $useIndex, $awardIndex, $initTime){$data = ['uid' => $uid,'use_index' => $useIndex,'award_index' => $awardIndex,'init_time' => $initTime];$this->commonInsert('blind_box_char_exchange', $data);}public function changeCharNum($uid, $index, $num){$charKey = $uid . '_charNum';$after_num = $this->conn->hIncrBy($charKey, $index, $num);if ($num < 0 && $after_num < 0) {//扣除道具数量不够,先将数量加回去,返回错误$this->conn->hIncrBy($charKey, $index, -$num);return false;}return true;}public function changeCoins($uid, $coins){$coins = 'coin+' . $coins;$this->db->set('coin', $coins,false)->where('id', $uid)->update('users');return $this->db->affected_rows() ? true : false;}public function getExchangePropLog($uid){$query = $this->db->select('award_index,count(id) num')->where(['uid' => $uid])->group_by('award_index')->get('blind_box_char_exchange');return $query->num_rows() ? $query->result_array() : [];}
接口描述
字符兑换道具奖励接口

请求地址: /activity/charExchange

请求参数: uid(用户id) 、index(兑换的道具index,0为金币,1为鲜花,2为双倍经验卡,3为双倍金币卡)

请求方式: post

返回示例:

{"code":200,"msg":"success","data":[]}
用户字符已兑换奖励列表

请求地址: /activity/charExchangeAward

请求参数: uid(用户id)

请求方式: post

返回示例:

{"code": 200,"msg": "success","data": [{"prop_id": 0,              //道具id 0为金币"num": "3",                  //兑换的次数"prop_name": "一个金币"      //兑换的奖励描述},{"prop_id": "4","num": "1","prop_name": "直升令牌"}]
}
用户字符兑换奖励首页

请求地址: /activity/charExchangeAwardIndex

请求参数: uid(用户id)

请求方式: post

返回示例:

{"code": 200,"msg": "success","data": {"awardList": [{"prop_id": 0,              //道具id"prop_name": "一个金币",   //道具名"num": -1,                  //可兑换数量-1为不限制"used": "3",               //已经使用的数量"needChar": []               //需要的字符列表,兑换金币使用的是最多的字符},{"prop_id": "1","prop_name": "鲜花","num": 10,"used": 0,"needChar": [0,     //定义的开启盲盒获得的字符数组的index1]},{"prop_id": "2","prop_name": "双倍经验卡","num": 5,"used": 0,"needChar": [0,1,2]},{"prop_id": "3","prop_name": "双倍金币卡","num": 5,"used": 0,"needChar": [0,1,2]},{"prop_id": "4","prop_name": "直升令牌","num": 1,"used": 0,"needChar": [0,1,2,3]}],"hasCharList": [             //用户拥有的字符数量95,  //0号字符(W)的数量96, //1号字符(Y)的数量95, //2号字符(Q)的数量33  //3号字符(G)的数量]}
}

php+redis 盲盒字符兑换道具相关推荐

  1. 小程序源码:修复登录大河盲盒小程序源码,实现运营“玩法自由”,超多功能的盲盒型抽奖挖矿程序源码下载

    程序介绍 应用支持哪些类型的商品? 1.实物需邮寄商品,用户领取时填写收货信息,后台发货. 2.虚拟商品,如:教程.课程.图文.图片.下载链接等等. 3.卡密商品,后台添加卡密商品,填写使用方法.批量 ...

  2. 最新版抽奖盲盒运营版

    先附个下载地址:立即下载 超多功能的盲盒型抽奖应用 我们想让运营者有更多发挥的空间,实现运营"玩法自由". 应用支持哪些类型的商品? 1.实物需邮寄商品,用户领取时填写收货信息,后 ...

  3. 盲盒防沉迷——订单限购的设计与实现

    目录 盲盒是什么 防沉迷维度 用户身份 时间维度 频率 订单统计 订单额度统计 有效订单统计 盲盒是什么 盲盒(Blind Box):就是装着不同的手办或玩偶的一个严密盒子,消费者购买前无法通过外观分 ...

  4. 盲盒APP想要做好需要具备什么呢?

    首先在做好一个东西时一定要充分了解这个东西,它所具备得功能,它容易出现什么问题,如何避免这些问题.想要开发一个软件,就要去充分的了解这款软件,去了解市面上做的比较好的此类型得软件,了解它为什么会那么火 ...

  5. 《看聊天记录都学不会C语言?太菜了吧》(13)(9*9 乘法表)寻找电脑中的盲盒彩蛋

    好消息2020年4月13日晚7.30我在CSDN开播,等你来聊天 预约连接:https://live.csdn.net/room/A757291228/MJWK0Gem 本系列文章将会以通俗易懂的对话 ...

  6. 盲盒拓客小程序商家联盟红包分销裂变抽奖小程序源码

    介绍: 盲盒拓客,集五种玩法于一身!.盲盒一刺激!好奇心!.商家联盟一多商家推广,扩人传播覆盖群体.红包拓客一促进分销裂变.抽奖一概率事件,随机中奖.言舌动分销裂变平台! 什么版本的不知道,自行研究, ...

  7. 66元机票盲盒,去哪随机、日期随机:要不起!

    来源 | 中新经纬 ID | jwview 文 | 张燕征 4月20日,飞猪推出66元机票盲盒:21日,去哪儿88元旅行盲盒上线:22日,携程99元"隐藏款"机票类盲盒也开售--五 ...

  8. 小孟5w接了个盲盒小程序,三周开发完毕

    现在的年轻人是真的会玩,越来越新的东西出来,越来越好玩的东西流行. 就像最近很火的地摊盲盒. 讲真的,这之前我都不知道盲盒是啥. 前面有个粉丝让我开发盲盒小程序,为此我还去地摊上找摊位买了几个盲盒玩玩 ...

  9. 数字藏品盲盒系统功能开发H5源码搭建

    数字藏品盲盒系统功 数字 藏品 盲盒营销,是当下火热的营销方式之一 , 也是当前市场热门模式之一 .数字藏品拥有着独 √ 一无 √ 二性.可证明稀缺性.可交 Y 性.可流动性并且能证明拥者是谁的极高价 ...

最新文章

  1. 对C#下函数,委托,事件的一点理解!
  2. php fatal class ziparchive not found,php中Fatal error: Class ZipArchive not found的解决办法
  3. Android 三种方式实现自定义圆形页面加载中效果的进度条
  4. 这5个要点让你看清“Salesforce+AWS”
  5. AT2161-[ARC065D]シャッフル/Shuffling【dp】
  6. 《MySQL——关于grant赋权以及flush privileges》
  7. *【51nod - 1459】迷宫游戏(记录双向权值的Dijkstra单源最短路)
  8. 重置mariadb密码
  9. jquery 自动完成 Autocomplete
  10. Java爬虫系列二:使用HttpClient抓取页面HTML
  11. c# json 汉字乱码_json.net中文乱码问题
  12. Kubernetes的调度机制
  13. STM32 CubeMX 串口通信
  14. 跳转微信公众号首页方式
  15. 直播APP制作时即时聊天功能实现
  16. emc re 整改 超标_RE102测试中单点超标且高频有杂散如何整改?
  17. javaEye上对于南京软件公司的讨论
  18. 百度搜索之site 使用
  19. java获取一定距离以内的经纬度值_java 根据经纬度计算两地间的距离
  20. 哈佛商业评论: 从商战到反恐,如何建立指挥系统内外的“网络”

热门文章

  1. 惠普打印机网络驱动安装方法
  2. google earth 离线安装和运行在从未连接过网络的电脑上的设置方法
  3. JSON 三级联动 高校专业分类
  4. 3M Design和Matteo Thun Partners在2019年米兰设计周中开展合作
  5. 如何完成期刊论文的高清图片
  6. ETH Geth节点配置参数
  7. astropy常用命令 python天文绘图
  8. 大视频上传服务器,支持HTML5断点续传,支持4GB以上大视频文件上传
  9. 支持通话/音量加减/接听功能TypeC线控耳机方案开发
  10. nvidia GPU