/** * 发放红包 * @param $money //金额 * @param $num //数量 * @param $packet //群组id确保key的唯一性 * @param $user_id //发放人 * @return array */ public function deliver($num,$packet,$money,$user_id){ //业务逻辑 $user = User::get($user_id); if($user['money'] < $money){ $this->error('金额不足'); } //获取金额 $money_arr = $this->redAlgorithm($money,$num); if(!$money_arr){ $this->error('单个红包金额不可低于0.01'); } //发放红包 入库操作 $key = 'packet'.$packet; //防止对已经设置过的商品库存进行覆盖 if(!empty($this->redis->llen($key))){ $this->error('改红包已建立'); } // 将商品存入Redis链表中 $this->redis->lPush($key,0); foreach ($money_arr as $k=>$v){ $this->redis->lPush($key,$k+1); //记录num个红包的金额 } //扣除金额 //加入流水 // 延时队列,24小时没有抢则退还 86400 // todo 未测时效性(redis不行可切mysql) // Queue::later('86390','app\job\Robexpire',['id'=>$id],'Robexpire'); $this->success('发放成功'); }
/** * 抢红包 * @param $user_id //抢红包id * @param $packet //群组id,获取key * @param $boid //红包id,用来获取已分好金额 * @return array */ public function rob($user_id,$packet,$boid) { $key = 'packet'.$packet; //已抢购用户队列 $userBuyKey = 'user_buy'.$packet; //该用户id是否在抢购用户集合中 $userBuyStatus = $this->redis->sismember($userBuyKey,$user_id); if($userBuyStatus){ //该用户已抢过 $this->error('您已抢过'); } // 从链表的头部删除一个元素,返回删除的元素,因为pop操作是原子性,即使很多用户同时到达,也是依次执行 //$count为第几个 $count = $this->redis->lpop($key); if(!$count){ //已抢完 $this->error('红包已抢完'); } //加入已抢 将用户id添加到抢购成功用户集合中 $this->redis->sadd($userBuyKey,$user_id); //抢成功, 操作数据库 //增加金额 //加入流水 $this->success('抢成功'); }
/** * 获取红包金额 * @param $money //金额 * @param $count //数量 * @return array */ function redAlgorithm($money, $count) { // 参数校验 if ($count * 0.01 > $money) { $this->error('单个红包金额不可低于0.01'); } // 存放随机红包 $redpack = []; // 未分配的金额 $surplus = $money; for ($i = 1; $i <= $count; $i++) { // 安全金额 $safeMoney = $surplus - ($count - $i) * 0.01; // 平均金额 $avg = $i == $count ? $safeMoney : bcdiv($safeMoney, ($count - $i), 2); // 随机红包 $rand = $avg > 0.01 ? mt_rand(1, $avg * 100) / 100 : 0.01; // 剩余红包 $surplus = bcsub($surplus, $rand, 2); $redpack[] = $rand; } // 平分剩余红包 $avg = bcdiv($surplus, $count, 2); for ($n = 0; $n < count($redpack); $n++) { $redpack[$n] = bcadd($redpack[$n], $avg, 2); $surplus = bcsub($surplus, $avg, 2); } // 如果还有红包没有分配完时继续分配 if ($surplus > 0) { // 随机抽取分配好的红包,将剩余金额分配进去 $keys = array_rand($redpack, $surplus * 100); // array_rand 第二个参数为 1 时返回的是下标而不是数组 $keys = is_array($keys) ? $keys : [$keys]; foreach ($keys as $key) { $redpack[$key] = bcadd($redpack[$key], 0.01, 2); $surplus = bcsub($surplus, 0.01, 2); } } // 红包分配结果 return $redpack; }
标签:红包,surplus,队列,money,抢红包,redis,user,key,id From: https://www.cnblogs.com/jwyq/p/17756215.html