redis = Cache::store('redis')->handler(); // 加载配置 $this->config = Db::name('bb_lottery_config')->column('value', 'key'); // 初始化Redis缓存(若Redis数据丢失,从数据库恢复) $this->initBigPrizeWeights(); $this->initRedisFromDb(); $this->checkRoundConstraint(); } private function initBigPrizeWeights() { $this->bigPrizeWeights = [ '60' => intval($this->config['big_prize_ratio_60_weight'] ?? 20), '70' => intval($this->config['big_prize_ratio_70_weight'] ?? 50), '80' => intval($this->config['big_prize_ratio_80_weight'] ?? 30) ]; $this->bigPrizeWeights['total'] = array_sum($this->bigPrizeWeights); } private function checkRoundConstraint() { $small_round = intval($this->redis->get('lottery:small_pool:round') ?: 1); $big_round = intval($this->redis->get('lottery:big_pool:round') ?: 1); if ($small_round < $big_round) { $this->redis->set('lottery:small_pool:round', $big_round); } } /** * 缓存恢复:独立恢复大小轮次+对应金额 */ private function initRedisFromDb() { // 1. 恢复小奖池轮次(取pool_type=1的最大times) $maxSmallRound = Db::name('bb_lottery_pool_flow')->where('pool_type', 1)->max('times') ?: 1; if (!$this->redis->get('lottery:small_pool:round')) { $this->redis->set('lottery:small_pool:round', $maxSmallRound); } // 2. 恢复大奖池轮次(取pool_type=2的最大times) $maxBigRound = Db::name('bb_lottery_pool_flow')->where('pool_type', 2)->max('times') ?: 1; if (!$this->redis->get('lottery:big_pool:round')) { $this->redis->set('lottery:big_pool:round', $maxBigRound); } // 3. 恢复小奖池当前轮次的次数/金额 $small_round = intval($this->redis->get('lottery:small_pool:round')); if (!$this->redis->get('lottery:small_pool:total_times')) { $smallTotalTimes = Db::name('bb_lottery_pool_flow') ->where(['pool_type' => 1, 'type' => 1, 'times' => $small_round]) ->count(); $this->redis->set('lottery:small_pool:total_times', $smallTotalTimes); } if (!$this->redis->get('lottery:small_pool:total_gold')) { $smallTotalGold = Db::name('bb_lottery_pool_flow') ->where(['pool_type' => 1, 'type' => 1, 'times' => $small_round]) ->sum('amount') ?: 0; $this->redis->set('lottery:small_pool:total_gold', $smallTotalGold); } // 4. 恢复大奖池当前轮次的金额 $big_round = intval($this->redis->get('lottery:big_pool:round')); if (!$this->redis->get('lottery:big_pool:total_gold')) { $bigAddGold = Db::name('bb_lottery_pool_flow') ->where(['pool_type' => 2, 'type' => 3, 'times' => $big_round]) ->sum('amount') ?: 0; $this->redis->set('lottery:big_pool:total_gold', $bigAddGold ); } } /** * 处理送礼抽奖逻辑 * @param int $send_uid 送礼用户ID * @param float $gift_gold 礼物金币数 * @param int $giftId 礼物ID * @return array 处理结果 * @throws Exception */ public function handleGift($send_uid, $gift_gold, $giftId) { // 参数校验 if ($gift_gold <= 0 || !$send_uid) { throw new Exception('参数错误'); } // 读取配置+独立轮次+大奖池金额 $small_trigger_times = intval($this->config['small_pool_trigger_times'] ?? 200); $big_threshold = floatval($this->config['big_pool_threshold'] ?? 1000); $small_round = intval($this->redis->get('lottery:small_pool:round') ?: 1); $big_round = intval($this->redis->get('lottery:big_pool:round') ?: 1); $big_total_gold = floatval($this->redis->get('lottery:big_pool:total_gold') ?: 0); // 加载Lua脚本 $luaSha = LotteryGiftLua::getLotteryLuaScripts(); // 执行Lua脚本(入参:small_round + big_round) $result = $this->redis->eval($luaSha, [ $send_uid, 0, $gift_gold, $small_trigger_times, $big_threshold, $small_round, $big_round, $big_total_gold, $this->bigPrizeWeights['60'], $this->bigPrizeWeights['70'], $this->bigPrizeWeights['80'], $this->bigPrizeWeights['total'] ], 0); // var_dump($result);die; $result = json_decode($result, true); if (json_last_error() !== JSON_ERROR_NONE) { throw new Exception('Lua脚本执行失败'); } // 开启数据库事务 Db::startTrans(); try { // . 1记录小奖池累计流水(未开奖时) if ($result['is_small_prize'] == 0) { $this->addPoolFlow( 1, // 小奖池 1, // 累计 $result['small_pool_add'], $result['small_total_gold'] - $result['small_pool_add'], $result['small_total_gold'], $giftId, $result['small_round'], // 新增:传入轮次 "小奖池累计:用户{$send_uid}送礼,轮次{$result['small_round']}" ); } else { $winnerUid = $send_uid; // 奖默认给当前送礼用户 //2.开小奖剩余划入大奖后 大奖够开奖 //开大奖 if ($result['is_big_prize'] == 1) { //开小奖,小奖剩余划入大奖池流水 $this->addPoolFlow( 2, // 大奖池 3, // 划转 $result['small_remain_amount'],//小奖剩余金额 $result['big_total_gold'] - $result['small_remain_amount'], $result['big_total_gold'], $giftId, $result['big_round'] - ($result['is_big_prize'] ? 1 : 0), "小奖剩余划转大奖池:{$result['small_remain_amount']}金币" ); //2.1 大奖中奖记录 $this->addWinnerRecord( $winnerUid, 2, // 大奖 $result['big_prize_amount'],//中奖金额 $result['big_pool_total_before_open'], // 开奖时大奖池金额 $result['big_ratio'], $result['big_release_amount']//释放金额 ); // 大奖释放流水 $this->addPoolFlow( 2, // 大奖池 4, // 释放 $result['big_release_amount'],//释放金额 $result['big_pool_total_before_open'],// 开奖时大奖池金额 0, $giftId, $result['big_round'] - 1, // 关联已结束的小奖池轮次 "大奖释放金额:{$result['big_release_amount']}金币" ); // 4. 小奖开奖金额划转下一次大奖池流水 if ($result['small_prize_to_big_next_round'] > 0) { $this->addPoolFlow( 2,// 大奖池 3,// 划转 $result['small_prize_to_big_next_round'], 0, $result['small_prize_to_big_next_round'], $giftId, $result['big_round'], "小奖开奖金额划入大奖池下一轮:大轮次{$result['big_round']},金额{$result['small_prize_to_big_next_round']}"); } } else {//只有小奖中奖 // 小奖中奖记录 $this->addWinnerRecord( $winnerUid, 1, // 小奖 $result['small_prize_amount'],//中奖金额 $result['small_total_gold'],//奖池总金额 $this->getSmallRatio($result['small_prize_amount'], $result['small_total_gold']),//中奖比例 0 //释放金额 ); // 3. 小奖池开奖流水 $this->addPoolFlow( 1, // 小奖池 2, // 开奖扣除 $result['small_prize_amount'], $result['small_total_gold'], $result['small_total_gold'] - $result['small_prize_amount'], $giftId, $result['small_round'] - 1, // 开奖轮次为当前轮次-1(已结束的轮次) "小奖池开奖:轮次" . ($result['small_round'] - 1).",中奖金额:{$result['small_prize_amount']}金币" ); //开小奖,小奖剩余划入大奖池流水 $this->addPoolFlow( 2, // 大奖池 3, // 划转 $result['small_remain_amount'],//小奖剩余金额 $result['big_total_gold'] - $result['small_remain_amount'], $result['big_total_gold'], $giftId, $result['big_round'] - ($result['is_big_prize'] ? 1 : 0), "小奖剩余划转大奖池:{$result['small_remain_amount']}金币" ); } } Db::commit(); return [ 'code' => 1, 'msg' => '处理成功', 'data' => $result ]; } catch (Exception $e) { Db::rollback(); throw new Exception($e->getMessage()); } } /** * 添加奖池流水 * @param int $pool_type 奖池类型:1-小 2-大 * @param int $type 流水类型:1-累计 2-开奖 3-划转 4-释放 * @param float $amount 金额 * @param float $before_amount 操作前金额 * @param float $after_amount 操作后金额 * @param int $relate_id 关联ID * @param int $times 轮次 * @param string $remark 备注 */ private function addPoolFlow($pool_type, $type, $amount, $before_amount, $after_amount, $relate_id, $times, $remark) { Db::name('bb_lottery_pool_flow')->insert([ 'pool_type' => $pool_type, 'type' => $type, 'amount' => $amount, 'before_amount' => $before_amount, 'after_amount' => $after_amount, 'relate_id' => $relate_id, 'times' => $times, // 新增:写入轮次 'remark' => $remark, 'create_time' => time() ]); } /** * 添加中奖记录 * @param int $uid 中奖用户ID * @param int $prize_type 奖项类型:1-小 2-大 * @param float $prize_amount 中奖金额 * @param float $pool_amount 奖池总金额 * @param int $ratio 中奖比例 * @param float $release_amount 释放金额 */ private function addWinnerRecord($uid, $prize_type, $prize_amount, $pool_amount, $ratio, $release_amount) { Db::name('bb_lottery_winner_record')->insert([ 'uid' => $uid, 'prize_type' => $prize_type, 'prize_amount' => $prize_amount, 'pool_amount' => $pool_amount, 'ratio' => $ratio, 'release_amount' => $release_amount, 'create_time' => time(), 'status' => 1 // 已发放 ]); // 此处可添加用户金币入账逻辑(如更新用户金币表) } /** * 计算小奖中奖比例 * @param float $prize_amount 中奖金额 * @param float $pool_amount 奖池金额 * @return int 比例(%) */ private function getSmallRatio($prize_amount, $pool_amount) { return intval(round($prize_amount / $pool_amount * 100)); } /** * 计算大奖中奖比例 * @param float $prize_amount 中奖金额 * @param float $pool_amount 奖池金额 * @return int 比例(%) */ private function getBigRatio($prize_amount, $pool_amount) { return intval(round($prize_amount / $pool_amount * 100)); } /** * 统计中奖数据 * @param array $where 筛选条件(如uid、prize_type、time) * @return array 统计结果 */ public function statWinner($where = []) { $query = Db::name('bb_lottery_winner_record'); if (!empty($where['uid'])) { $query->where('uid', $where['uid']); } if (!empty($where['prize_type'])) { $query->where('prize_type', $where['prize_type']); } if (!empty($where['start_time']) && !empty($where['end_time'])) { $query->whereBetween('create_time', [$where['start_time'], $where['end_time']]); } // 总中奖金额、总释放金额、中奖次数 $stat = $query->field([ 'SUM(prize_amount) as total_prize', 'SUM(release_amount) as total_release', 'COUNT(id) as total_times' ])->find(); return [ 'total_prize' => $stat['total_prize'] ?? 0, 'total_release' => $stat['total_release'] ?? 0, 'total_times' => $stat['total_times'] ?? 0 ]; } }