Files
dafuweng-saiadmin6.x/server/app/api/logic/PlayStartLogic.php

209 lines
8.1 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
namespace app\api\logic;
use app\api\cache\UserCache;
use app\api\service\LotteryService;
use app\dice\model\lottery_config\DiceLotteryConfig;
use app\dice\model\play_record\DicePlayRecord;
use app\dice\model\player\DicePlayer;
use app\dice\model\player_wallet_record\DicePlayerWalletRecord;
use app\dice\model\reward_config\DiceRewardConfig;
use plugin\saiadmin\exception\ApiException;
use support\think\Cache;
use support\think\Db;
/**
* 开始游戏 / 抽奖一局
*/
class PlayStartLogic
{
/** 抽奖类型:付费 */
public const LOTTERY_TYPE_PAID = 0;
/** 抽奖类型:免费 */
public const LOTTERY_TYPE_FREE = 1;
/** 钱包流水类型:抽奖 */
public const WALLET_TYPE_DRAW = 5;
/** 对局状态:成功 */
public const RECORD_STATUS_SUCCESS = 1;
/** 对局状态:超时/失败 */
public const RECORD_STATUS_TIMEOUT = 0;
/** 开启对局最低余额 = |DiceRewardConfig 最小 real_ev + 100| */
private const MIN_COIN_EXTRA = 100;
/**
* 执行一局游戏
* @param int $playerId 玩家ID
* @param int $direction 方向 0=无/顺时针 1=中奖/逆时针(前端 rediction
* @return array 成功返回 DicePlayRecord 数据;余额不足时抛 ApiExceptionmessage 为约定文案
*/
public function run(int $playerId, int $direction): array
{
$player = DicePlayer::find($playerId);
if (!$player) {
throw new ApiException('用户不存在');
}
$minEv = (float) DiceRewardConfig::min('real_ev');
$minCoin = abs($minEv + self::MIN_COIN_EXTRA);
$coin = (float) $player->coin;
if ($coin < $minCoin) {
throw new ApiException('当前玩家余额小于DiceRewardConfigMin.real_ev+100无法继续游戏');
}
$paid = (int) ($player->paid_draw_count ?? 0);
$free = (int) ($player->free_draw_count ?? 0);
if ($paid + $free <= 0) {
throw new ApiException('抽奖券不足');
}
$lotteryService = LotteryService::getOrCreate($playerId);
$ticketType = LotteryService::drawTicketType($paid, $free);
$config = $ticketType === self::LOTTERY_TYPE_PAID
? ($lotteryService->getConfigType0Id() ? DiceLotteryConfig::find($lotteryService->getConfigType0Id()) : null)
: ($lotteryService->getConfigType1Id() ? DiceLotteryConfig::find($lotteryService->getConfigType1Id()) : null);
if (!$config) {
throw new ApiException('奖池配置不存在');
}
$tier = LotteryService::drawTierByWeights($config);
$rewards = DiceRewardConfig::where('tier', $tier)->select();
if ($rewards->isEmpty()) {
throw new ApiException('该档位暂无奖励配置');
}
$rewardList = $rewards->all();
$reward = $rewardList[array_rand($rewardList)];
$realEv = (float) $reward->real_ev;
$winCoin = 100 + $realEv; // 赢取平台币 = 100 + DiceRewardConfig.real_ev
$gridNumber = (int) $reward->grid_number;
$startIndex = (int) Cache::get(LotteryService::getStartIndexKey($playerId), 0);
$targetIndex = (int) $reward->id;
$rollArray = $this->generateRollArray($gridNumber);
$record = null;
$configId = (int) $config->id;
$rewardId = (int) $reward->id;
$configName = (string) ($config->name ?? '');
try {
Db::transaction(function () use (
$playerId,
$configId,
$rewardId,
$configName,
$ticketType,
$winCoin,
$realEv,
$direction,
$startIndex,
$targetIndex,
$rollArray,
&$record
) {
$record = DicePlayRecord::create([
'player_id' => $playerId,
'lottery_config_id' => $configId,
'lottery_type' => $ticketType,
'win_coin' => $winCoin,
'direction' => $direction,
'reward_config_id' => $rewardId,
'start_index' => $startIndex,
'target_index' => $targetIndex,
'roll_array' => is_array($rollArray) ? json_encode($rollArray) : $rollArray,
'lottery_name' => $configName,
'status' => self::RECORD_STATUS_SUCCESS,
]);
$p = DicePlayer::find($playerId);
if (!$p) {
throw new \RuntimeException('玩家不存在');
}
$coinBefore = (float) $p->coin;
$coinAfter = $coinBefore + $winCoin;
$p->coin = $coinAfter;
$p->total_draw_count = max(0, (int) $p->total_draw_count - 1);
if ($ticketType === self::LOTTERY_TYPE_PAID) {
$p->paid_draw_count = max(0, (int) $p->paid_draw_count - 1);
} else {
$p->free_draw_count = max(0, (int) $p->free_draw_count - 1);
}
$p->save();
// 累加彩金池盈利额度(累加值为 -real_ev。若 dice_lottery_config 表有 ev 字段则执行
try {
DiceLotteryConfig::where('id', $configId)->update([
'ev' => Db::raw('IFNULL(ev,0) - ' . (float) $realEv),
]);
} catch (\Throwable $_) {
}
DicePlayerWalletRecord::create([
'player_id' => $playerId,
'coin' => $winCoin,
'type' => self::WALLET_TYPE_DRAW,
'wallet_before' => $coinBefore,
'wallet_after' => $coinAfter,
'remark' => '抽奖|play_record_id=' . $record->id,
]);
Cache::set(LotteryService::getStartIndexKey($playerId), $targetIndex, 86400 * 30);
});
} catch (\Throwable $e) {
if ($record === null) {
try {
$record = DicePlayRecord::create([
'player_id' => $playerId,
'lottery_config_id' => $configId ?? 0,
'lottery_type' => $ticketType,
'win_coin' => 0,
'direction' => $direction,
'reward_config_id' => 0,
'start_index' => $startIndex,
'target_index' => 0,
'roll_array' => '[]',
'status' => self::RECORD_STATUS_TIMEOUT,
]);
} catch (\Throwable $_) {
// 表可能无 status 字段时忽略
}
}
throw $e;
}
$updated = DicePlayer::find($playerId);
if ($updated) {
UserCache::setUser($playerId, $updated->hidden(['password'])->toArray());
}
if (!$record instanceof DicePlayRecord) {
throw new \RuntimeException('对局记录创建失败');
}
$arr = $record->toArray();
if (isset($arr['roll_array']) && is_string($arr['roll_array'])) {
$arr['roll_array'] = json_decode($arr['roll_array'], true) ?? [];
}
return $arr;
}
/** 生成 5 个 1-6 的点数,和为 grid_number5~30严格不超范围 */
private function generateRollArray(int $gridNumber): array
{
$minSum = 5;
$maxSum = 30;
$n = max($minSum, min($maxSum, $gridNumber));
$dice = [1, 1, 1, 1, 1];
$remain = $n - 5;
while ($remain > 0) {
$i = array_rand($dice);
if ($dice[$i] < 6) {
$add = min($remain, 6 - $dice[$i]);
$dice[$i] += $add;
$remain -= $add;
}
}
shuffle($dice);
return $dice;
}
}