[消耗coin, 购买次数paid, 赠送次数free] * 仅支持 1、5、10 档 */ class GameLogic { public const PACKAGES = [ 1 => ['coin' => 1, 'paid' => 1, 'free' => 0], // 1次/1coin 5 => ['coin' => 5, 'paid' => 5, 'free' => 1], // 5张/5coin(5购买+1赠送,共6次) 10 => ['coin' => 10, 'paid' => 10, 'free' => 3], // 10张/10coin(10购买+3赠送,共13次) ]; /** 钱包流水类型:购买抽奖次数 */ public const WALLET_TYPE_BUY_DRAW = 2; /** * 购买抽奖券 * 先更新 Redis 玩家信息(后续游玩从 Redis 读),再用事务更新数据库;事务失败则回滚 Redis * @param int $playerId 玩家ID * @param int $count 购买档位:1 / 5 / 10 * @return array 更新后的 coin, total_ticket_count, paid_ticket_count, free_ticket_count */ public function buyLotteryTickets(int $playerId, int $count): array { if (!isset(self::PACKAGES[$count])) { throw new ApiException('Invalid lottery ticket purchase'); } $pack = self::PACKAGES[$count]; $cost = $pack['coin']; $addPaid = $pack['paid']; $addFree = $pack['free']; $addTotal = $addPaid + $addFree; $player = DicePlayer::find($playerId); if (!$player) { throw new ApiException('User not found'); } $coinBefore = (float) $player->coin; if ($coinBefore < $cost) { throw new ApiException('Insufficient balance'); } $coinAfter = round($coinBefore - $cost, 2); $totalBefore = (int) ($player->total_ticket_count ?? 0); $paidBefore = (int) ($player->paid_ticket_count ?? 0); $freeBefore = (int) ($player->free_ticket_count ?? 0); $totalAfter = $totalBefore + $addTotal; $paidAfter = $paidBefore + $addPaid; $freeAfter = $freeBefore + $addFree; $oldUserArr = $player->hidden(['password'])->toArray(); $updatedUserArr = $oldUserArr; $updatedUserArr['coin'] = $coinAfter; $updatedUserArr['total_ticket_count'] = $totalAfter; $updatedUserArr['paid_ticket_count'] = $paidAfter; $updatedUserArr['free_ticket_count'] = $freeAfter; UserCache::setUser($playerId, $updatedUserArr); $adminId = ($player->admin_id ?? null) ? (int) $player->admin_id : null; try { Db::transaction(function () use ( $player, $playerId, $adminId, $cost, $coinBefore, $coinAfter, $addTotal, $addPaid, $addFree, $totalAfter, $paidAfter, $freeAfter ) { $player->coin = $coinAfter; $player->total_ticket_count = $totalAfter; $player->paid_ticket_count = $paidAfter; $player->free_ticket_count = $freeAfter; $player->save(); DicePlayerWalletRecord::create([ 'player_id' => $playerId, 'admin_id' => $adminId, 'coin' => round(-$cost, 2), 'type' => self::WALLET_TYPE_BUY_DRAW, 'wallet_before' => $coinBefore, 'wallet_after' => $coinAfter, 'total_ticket_count' => $addTotal, 'paid_ticket_count' => $addPaid, 'free_ticket_count' => $addFree, 'remark' => "购买抽奖券{$addTotal}次(付费{$addPaid}次+赠送{$addFree}次)", ]); DicePlayerTicketRecord::create([ 'player_id' => $playerId, 'admin_id' => $adminId, 'use_coins' => round($cost, 2), 'ante' => 1, 'total_ticket_count' => $addTotal, 'paid_ticket_count' => $addPaid, 'free_ticket_count' => $addFree, 'remark' => "购买抽奖券{$addTotal}次(付费{$addPaid}次+赠送{$addFree}次)", ]); }); } catch (\Throwable $e) { UserCache::setUser($playerId, $oldUserArr); throw $e; } return [ 'coin' => round((float) $coinAfter, 2), 'total_ticket_count' => (int) $totalAfter, 'paid_ticket_count' => (int) $paidAfter, 'free_ticket_count' => (int) $freeAfter, ]; } }