[接口]新增抽奖接口/api/game/playStart
This commit is contained in:
208
server/app/api/logic/PlayStartLogic.php
Normal file
208
server/app/api/logic/PlayStartLogic.php
Normal file
@@ -0,0 +1,208 @@
|
||||
<?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 数据;余额不足时抛 ApiException,message 为约定文案
|
||||
*/
|
||||
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_number(5~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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user