优化抽奖方式,以及记录相关信息

This commit is contained in:
2026-03-26 18:10:41 +08:00
parent 77ec0dcade
commit e32f3890f1
32 changed files with 304 additions and 244 deletions

View File

@@ -76,28 +76,28 @@ class GameController extends BaseController
* header: token由 TokenMiddleware 注入 request->player_id
* body: count = 1 | 5 | 101次/100coin, 5次/500coin, 10次/1000coin
*/
public function buyLotteryTickets(Request $request): Response
{
$userId = (int) ($request->player_id ?? 0);
$count = (int) $request->post('count', 0);
if (!in_array($count, [1, 5, 10], true)) {
return $this->fail('Invalid lottery ticket purchase', ReturnCode::PARAMS_ERROR);
}
try {
$logic = new GameLogic();
$data = $logic->buyLotteryTickets($userId, $count);
return $this->success($data);
} catch (ApiException $e) {
$msg = $e->getMessage();
if ($msg === '平台币不足') {
$player = DicePlayer::find($userId);
$coin = $player ? (float) $player->coin : 0;
return $this->success(['coin' => $coin], $msg);
}
return $this->fail($msg, ReturnCode::BUSINESS_ERROR);
}
}
// public function buyLotteryTickets(Request $request): Response
// {
// $userId = (int) ($request->player_id ?? 0);
// $count = (int) $request->post('count', 0);
// if (!in_array($count, [1, 5, 10], true)) {
// return $this->fail('Invalid lottery ticket purchase', ReturnCode::PARAMS_ERROR);
// }
//
// try {
// $logic = new GameLogic();
// $data = $logic->buyLotteryTickets($userId, $count);
// return $this->success($data);
// } catch (ApiException $e) {
// $msg = $e->getMessage();
// if ($msg === '平台币不足') {
// $player = DicePlayer::find($userId);
// $coin = $player ? (float) $player->coin : 0;
// return $this->success(['coin' => $coin], $msg);
// }
// return $this->fail($msg, ReturnCode::BUSINESS_ERROR);
// }
// }
/**
* 获取彩金池(中奖配置表)
@@ -194,10 +194,11 @@ class GameController extends BaseController
$langLower = strtolower($lang);
$isEn = $langLower === 'en' || str_starts_with($langLower, 'en-');
if (is_array($data) && array_key_exists('reward_config_id', $data)) {
$rewardConfigId = (int) $data['reward_config_id'];
if ($rewardConfigId > 0) {
$configRow = DiceRewardConfig::getCachedById($rewardConfigId);
if (is_array($data)) {
$rewardTier = array_key_exists('reward_tier', $data) ? (string) ($data['reward_tier'] ?? '') : '';
$targetIndex = array_key_exists('target_index', $data) ? (int) ($data['target_index'] ?? 0) : 0;
if ($rewardTier !== 'BIGWIN' && $targetIndex > 0) {
$configRow = DiceRewardConfig::getCachedById($targetIndex);
if ($configRow !== null) {
$uiText = '';
$uiTextEn = '';
@@ -247,9 +248,8 @@ class GameController extends BaseController
'win_coin' => 0,
'super_win_coin' => 0,
'reward_win_coin' => 0,
'use_coins' => 0,
'direction' => $direction,
'reward_config_id' => 0,
'reward_tier' => '',
'start_index' => 0,
'target_index' => 0,
'roll_array' => '[]',

View File

@@ -91,7 +91,7 @@ class UserController extends BaseController
if (empty($user)) {
return $this->fail('User not found', ReturnCode::NOT_FOUND);
}
$fields = ['id', 'username', 'phone', 'uid', 'name', 'coin', 'total_ticket_count'];
$fields = ['id', 'username', 'phone', 'uid', 'name', 'coin', 'total_ticket_count', 'free_ticket'];
$info = [];
foreach ($fields as $field) {
if (array_key_exists($field, $user)) {

View File

@@ -24,6 +24,8 @@ use support\think\Db;
*/
class PlayStartLogic
{
/** 钱包流水类型:购买抽奖次数 */
public const WALLET_TYPE_BUY_DRAW = 2;
/** 抽奖类型:付费 */
public const LOTTERY_TYPE_PAID = 0;
/** 抽奖类型:免费 */
@@ -74,12 +76,32 @@ class PlayStartLogic
throw new ApiException('当前注数不合规,请选择正确的注数');
}
// 免费抽奖:不再使用抽奖券作为开始条件,仅用 free_ticket_count 表示“免费抽奖次数”
$freeCount = (int) ($player->free_ticket_count ?? 0);
$isFree = $freeCount > 0;
// 免费抽奖:优先使用 free_ticket带 ante 与 count兼容旧字段 free_ticket_count
$freeTicket = $player->free_ticket ?? null;
$freeTicketAnte = null;
$freeTicketCount = 0;
if (is_array($freeTicket)) {
$a = $freeTicket['ante'] ?? null;
$c = $freeTicket['count'] ?? null;
if ($a !== null && $a !== '' && is_numeric($a)) {
$freeTicketAnte = (int) $a;
}
if ($c !== null && $c !== '' && is_numeric($c)) {
$freeTicketCount = (int) $c;
}
}
$legacyFreeCount = (int) ($player->free_ticket_count ?? 0);
$isFree = ($freeTicketAnte !== null && $freeTicketCount > 0) || $legacyFreeCount > 0;
$ticketType = $isFree ? self::LOTTERY_TYPE_FREE : self::LOTTERY_TYPE_PAID;
// 若为免费抽奖:注数必须与上一次触发免费抽奖时的注数一致
// 若为 free_ticket 免费抽奖:注数必须与券的 ante 一致
if ($ticketType === self::LOTTERY_TYPE_FREE && $freeTicketAnte !== null && $freeTicketCount > 0) {
if ($ante !== $freeTicketAnte) {
throw new ApiException('您有一张底注为' . $freeTicketAnte . '的免费抽奖券');
}
}
// 若为免费抽奖(旧逻辑):注数必须与上一次触发免费抽奖时的注数一致
if ($isFree) {
$requiredAnte = Cache::get(self::FREE_ANTE_KEY_PREFIX . $playerId);
if ($requiredAnte !== null && $requiredAnte !== '' && (int) $requiredAnte !== $ante) {
@@ -231,7 +253,6 @@ class PlayStartLogic
$adminId,
$configId,
$type0ConfigId,
$rewardId,
$configName,
$ticketType,
$ante,
@@ -247,8 +268,10 @@ class PlayStartLogic
$targetIndex,
$rollArray,
$isTierT5,
$tier,
&$record
) {
$rewardTier = ($isWin === 1 && $superWinCoin > 0) ? 'BIGWIN' : (string) ($tier ?? '');
$record = DicePlayRecord::create([
'player_id' => $playerId,
'admin_id' => $adminId,
@@ -260,14 +283,12 @@ class PlayStartLogic
'win_coin' => $winCoin,
'super_win_coin' => $superWinCoin,
'reward_win_coin' => $rewardWinCoin,
'use_coins' => $paidAmount,
'direction' => $direction,
'reward_config_id' => $rewardId,
'reward_tier' => $rewardTier,
'start_index' => $startIndex,
'target_index' => $targetIndex,
'roll_array' => is_array($rollArray) ? json_encode($rollArray) : $rollArray,
'roll_number' => is_array($rollArray) ? array_sum($rollArray) : 0,
'lottery_name' => $configName,
'status' => self::RECORD_STATUS_SUCCESS,
]);
@@ -279,9 +300,31 @@ class PlayStartLogic
// 开始前先扣付费金额,再加中奖金额(免费抽奖 paid_amount=0
$coinAfter = $coinBefore - $paidAmount + $winCoin;
$p->coin = $coinAfter;
// 不再使用抽奖券作为抽奖条件:付费不扣抽奖次数;免费抽奖消耗 free_ticket_count
// 免费抽奖消耗:优先消耗 free_ticket.count耗尽则清空 free_ticket否则兼容旧 free_ticket_count
if ($ticketType === self::LOTTERY_TYPE_FREE) {
$p->free_ticket_count = max(0, (int) $p->free_ticket_count - 1);
$ft = $p->free_ticket ?? null;
$ftAnte = null;
$ftCount = 0;
if (is_array($ft)) {
$a = $ft['ante'] ?? null;
$c = $ft['count'] ?? null;
if ($a !== null && $a !== '' && is_numeric($a)) {
$ftAnte = (int) $a;
}
if ($c !== null && $c !== '' && is_numeric($c)) {
$ftCount = (int) $c;
}
}
if ($ftAnte !== null && $ftCount > 0) {
$next = $ftCount - 1;
if ($next <= 0) {
$p->free_ticket = null;
} else {
$p->free_ticket = ['ante' => $ftAnte, 'count' => $next];
}
} else {
$p->free_ticket_count = max(0, (int) $p->free_ticket_count - 1);
}
}
// 记录每次游玩:写入抽奖券记录(用于后台“抽奖券记录”追踪付费/免费游玩与消耗)
@@ -299,9 +342,32 @@ class PlayStartLogic
'remark' => ($isPaidPlay ? '付费游玩' : '免费游玩') . '|play_record_id=' . $record->id,
]);
// 若本局中奖档位为 T5则额外赠送 1 次免费抽奖次数(总次数也 +1并记录抽奖券获取记录
// 若本局中奖档位为 T5则额外赠送 1 次免费抽奖次数
// - 新结构:写入 free_ticketante=本局注数count+1
// - 兼容旧结构free_ticket_count +1
if ($isTierT5) {
$p->free_ticket_count = (int) $p->free_ticket_count + 1;
$ft = $p->free_ticket ?? null;
$ftAnte = null;
$ftCount = 0;
if (is_array($ft)) {
$a = $ft['ante'] ?? null;
$c = $ft['count'] ?? null;
if ($a !== null && $a !== '' && is_numeric($a)) {
$ftAnte = (int) $a;
}
if ($c !== null && $c !== '' && is_numeric($c)) {
$ftCount = (int) $c;
}
}
if ($ftAnte === null) {
$ftAnte = $ante;
}
if ($ftAnte === $ante) {
$p->free_ticket = ['ante' => $ante, 'count' => $ftCount + 1];
} else {
// 若已有不同注数的免费券,则仍保留旧字段累加,避免覆盖玩家已有券
$p->free_ticket_count = (int) $p->free_ticket_count + 1;
}
DicePlayerTicketRecord::create([
'player_id' => $playerId,
@@ -314,7 +380,15 @@ class PlayStartLogic
Cache::set(self::FREE_ANTE_KEY_PREFIX . $playerId, $ante, self::FREE_ANTE_TTL);
} else {
// 若本次消耗了最后一次免费抽奖,则清理注数锁
if ($ticketType === self::LOTTERY_TYPE_FREE && (int) $p->free_ticket_count <= 0) {
$ft = $p->free_ticket ?? null;
$ftCount = 0;
if (is_array($ft)) {
$c = $ft['count'] ?? null;
if ($c !== null && $c !== '' && is_numeric($c)) {
$ftCount = (int) $c;
}
}
if ($ticketType === self::LOTTERY_TYPE_FREE && $ftCount <= 0 && (int) $p->free_ticket_count <= 0) {
Cache::delete(self::FREE_ANTE_KEY_PREFIX . $playerId);
}
}
@@ -338,15 +412,30 @@ class PlayStartLogic
]);
}
// 钱包流水拆分:先记录购券扣费,再记录抽奖结果(中奖/惩罚)
if ($paidAmount > 0) {
$walletAfterBuy = $coinBefore - $paidAmount;
DicePlayerWalletRecord::create([
'player_id' => $playerId,
'admin_id' => $adminId,
'coin' => -$paidAmount,
'type' => self::WALLET_TYPE_BUY_DRAW,
'wallet_before' => $coinBefore,
'wallet_after' => $walletAfterBuy,
'remark' => '抽奖购券扣费|play_record_id=' . $record->id,
]);
}
$walletBeforeDraw = $coinBefore - $paidAmount;
$drawRemark = ($winCoin >= 0 ? '抽奖中奖' : '抽奖惩罚') . '|play_record_id=' . $record->id;
DicePlayerWalletRecord::create([
'player_id' => $playerId,
'admin_id' => $adminId,
// 钱包流水记录本局净变化:-付费金额 + 中奖金额(免费抽奖付费金额为 0
'coin' => $winCoin - (float) $paidAmount,
'coin' => $winCoin,
'type' => self::WALLET_TYPE_DRAW,
'wallet_before' => $coinBefore,
'wallet_before' => $walletBeforeDraw,
'wallet_after' => $coinAfter,
'remark' => '抽奖|play_record_id=' . $record->id,
'remark' => $drawRemark,
]);
});
} catch (\Throwable $e) {
@@ -361,9 +450,8 @@ class PlayStartLogic
'win_coin' => 0,
'super_win_coin' => 0,
'reward_win_coin' => 0,
'use_coins' => 0,
'direction' => $direction,
'reward_config_id' => 0,
'reward_tier' => '',
'start_index' => $startIndex,
'target_index' => 0,
'roll_array' => '[]',
@@ -390,7 +478,7 @@ class PlayStartLogic
$arr['roll_array'] = json_decode($arr['roll_array'], true) ?? [];
}
$arr['roll_number'] = is_array($arr['roll_array'] ?? null) ? array_sum($arr['roll_array']) : 0;
$arr['tier'] = $tier ?? '';
$arr['reward_tier'] = ($isWin === 1 && $superWinCoin > 0) ? 'BIGWIN' : (string) ($tier ?? '');
// 记录完数据后返回当前玩家余额与抽奖次数
$arr['coin'] = $updated ? (float) $updated->coin : 0;
return $arr;
@@ -619,10 +707,10 @@ class PlayStartLogic
$winCoin = $superWinCoin + $rewardWinCoin;
$configId = $config !== null ? (int) $config->id : 0;
$rewardId = ($isWin === 1 && $superWinCoin > 0) ? 0 : $targetIndex;
$configName = $config !== null ? (string) ($config->name ?? '') : '自定义';
$costRealEv = $realEv + ($isWin === 1 ? $bigWinRealEv : 0.0);
$paidAmount = $lotteryType === 0 ? ($ante * self::UNIT_COST) : 0;
$rewardTier = ($isWin === 1 && $superWinCoin > 0) ? 'BIGWIN' : (string) ($tier ?? '');
return [
'player_id' => 0,
@@ -635,14 +723,12 @@ class PlayStartLogic
'paid_amount' => $paidAmount,
'super_win_coin' => $superWinCoin,
'reward_win_coin' => $rewardWinCoin,
'use_coins' => $paidAmount,
'direction' => $direction,
'reward_config_id' => $rewardId,
'reward_tier' => $rewardTier,
'start_index' => $startIndex,
'target_index' => $targetIndex,
'roll_array' => json_encode($rollArray),
'roll_number' => array_sum($rollArray),
'lottery_name' => $configName,
'status' => self::RECORD_STATUS_SUCCESS,
'tier' => $tier,
'roll_number_for_count' => $rollNumber,