162 lines
5.2 KiB
PHP
162 lines
5.2 KiB
PHP
<?php
|
||
declare(strict_types=1);
|
||
|
||
namespace app\api\service;
|
||
|
||
use app\dice\model\lottery_config\DiceLotteryConfig;
|
||
use app\dice\model\player\DicePlayer;
|
||
use support\think\Cache;
|
||
|
||
/**
|
||
* 彩金池实例,按玩家权重与奖池配置创建,存 Redis 便于增删改查
|
||
*/
|
||
class LotteryService
|
||
{
|
||
private const REDIS_KEY_PREFIX = 'api:game:lottery_pool:';
|
||
private const REDIS_KEY_START_INDEX = 'api:game:start_index:';
|
||
private const EXPIRE = 86400 * 7; // 7天
|
||
|
||
private int $playerId;
|
||
private ?int $configType0Id = null;
|
||
private ?int $configType1Id = null;
|
||
/** @var array{t1_weight?:int,t2_weight?:int,t3_weight?:int,t4_weight?:int,t5_weight?:int} */
|
||
private array $playerWeights = [];
|
||
|
||
public function __construct(int $playerId)
|
||
{
|
||
$this->playerId = $playerId;
|
||
}
|
||
|
||
public static function getRedisKey(int $playerId): string
|
||
{
|
||
return self::REDIS_KEY_PREFIX . $playerId;
|
||
}
|
||
|
||
public static function getStartIndexKey(int $playerId): string
|
||
{
|
||
return self::REDIS_KEY_START_INDEX . $playerId;
|
||
}
|
||
|
||
/** 从 Redis 加载或根据玩家与 DiceLotteryConfig 创建并保存 */
|
||
public static function getOrCreate(int $playerId): self
|
||
{
|
||
$key = self::getRedisKey($playerId);
|
||
$cached = Cache::get($key);
|
||
if ($cached && is_string($cached)) {
|
||
$data = json_decode($cached, true);
|
||
if (is_array($data)) {
|
||
$s = new self($playerId);
|
||
$s->configType0Id = (int) ($data['config_type_0_id'] ?? 0);
|
||
$s->configType1Id = (int) ($data['config_type_1_id'] ?? 0);
|
||
$s->playerWeights = $data['player_weights'] ?? [];
|
||
return $s;
|
||
}
|
||
}
|
||
$player = DicePlayer::find($playerId);
|
||
if (!$player) {
|
||
throw new \RuntimeException('玩家不存在');
|
||
}
|
||
$config0 = DiceLotteryConfig::where('type', 0)->find();
|
||
$config1 = DiceLotteryConfig::where('type', 1)->find();
|
||
$s = new self($playerId);
|
||
$s->configType0Id = $config0 ? (int) $config0->id : null;
|
||
$s->configType1Id = $config1 ? (int) $config1->id : null;
|
||
$s->playerWeights = [
|
||
't1_weight' => (int) ($player->t1_weight ?? 0),
|
||
't2_weight' => (int) ($player->t2_weight ?? 0),
|
||
't3_weight' => (int) ($player->t3_weight ?? 0),
|
||
't4_weight' => (int) ($player->t4_weight ?? 0),
|
||
't5_weight' => (int) ($player->t5_weight ?? 0),
|
||
];
|
||
$s->save();
|
||
return $s;
|
||
}
|
||
|
||
public function save(): void
|
||
{
|
||
$key = self::getRedisKey($this->playerId);
|
||
$data = [
|
||
'config_type_0_id' => $this->configType0Id,
|
||
'config_type_1_id' => $this->configType1Id,
|
||
'player_weights' => $this->playerWeights,
|
||
];
|
||
Cache::set($key, json_encode($data), self::EXPIRE);
|
||
}
|
||
|
||
/** 根据奖池配置的 t1_weight..t5_weight 权重随机抽取档位 T1-T5 */
|
||
public static function drawTierByWeights(DiceLotteryConfig $config): string
|
||
{
|
||
$tiers = ['T1', 'T2', 'T3', 'T4', 'T5'];
|
||
$weights = [
|
||
(int) ($config->t1_weight ?? 0),
|
||
(int) ($config->t2_weight ?? 0),
|
||
(int) ($config->t3_weight ?? 0),
|
||
(int) ($config->t4_weight ?? 0),
|
||
(int) ($config->t5_weight ?? 0),
|
||
];
|
||
return self::drawTierByWeightArray($tiers, $weights);
|
||
}
|
||
|
||
/**
|
||
* 根据玩家 t1_weight~t5_weight 权重随机抽取中奖档位 T1-T5
|
||
* t1_weight=T1, t2_weight=T2, t3_weight=T3, t4_weight=T4, t5_weight=T5
|
||
*/
|
||
public static function drawTierByPlayerWeights(DicePlayer $player): string
|
||
{
|
||
$tiers = ['T1', 'T2', 'T3', 'T4', 'T5'];
|
||
$weights = [
|
||
(int) ($player->t1_weight ?? 0),
|
||
(int) ($player->t2_weight ?? 0),
|
||
(int) ($player->t3_weight ?? 0),
|
||
(int) ($player->t4_weight ?? 0),
|
||
(int) ($player->t5_weight ?? 0),
|
||
];
|
||
return self::drawTierByWeightArray($tiers, $weights);
|
||
}
|
||
|
||
/** 按档位权重数组抽取 T1-T5 */
|
||
private static function drawTierByWeightArray(array $tiers, array $weights): string
|
||
{
|
||
$total = array_sum($weights);
|
||
if ($total <= 0) {
|
||
return $tiers[array_rand($tiers)];
|
||
}
|
||
$r = mt_rand(1, $total);
|
||
$acc = 0;
|
||
foreach ($weights as $i => $w) {
|
||
$acc += $w;
|
||
if ($r <= $acc) {
|
||
return $tiers[$i];
|
||
}
|
||
}
|
||
return $tiers[4];
|
||
}
|
||
|
||
/** 按 paid_ticket_count 与 free_ticket_count 权重随机抽取 0=付费 1=免费 */
|
||
public static function drawTicketType(int $paid, int $free): int
|
||
{
|
||
if ($paid <= 0 && $free <= 0) {
|
||
throw new \RuntimeException('抽奖券不足');
|
||
}
|
||
if ($paid <= 0) {
|
||
return 1;
|
||
}
|
||
if ($free <= 0) {
|
||
return 0;
|
||
}
|
||
$total = $paid + $free;
|
||
$r = mt_rand(1, $total);
|
||
return $r <= $paid ? 0 : 1;
|
||
}
|
||
|
||
public function getConfigType0Id(): ?int
|
||
{
|
||
return $this->configType0Id;
|
||
}
|
||
|
||
public function getConfigType1Id(): ?int
|
||
{
|
||
return $this->configType1Id;
|
||
}
|
||
}
|