206 lines
5.9 KiB
PHP
206 lines
5.9 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
namespace app\common\library\game;
|
||
|
||
use support\think\Db;
|
||
|
||
/**
|
||
* 连胜奖励(game_config.streak_win_reward):按「连胜档位」1~10 配置赔率系数与是否大奖。
|
||
*
|
||
* 派彩:total_amount × odds_factor(与后台「连胜奖励」表一致,不再额外 ×33)。
|
||
*/
|
||
final class StreakWinReward
|
||
{
|
||
public const CONFIG_KEY = 'streak_win_reward';
|
||
|
||
/** @var list<array{streak: int, odds_factor: int, is_jackpot: bool}>|null */
|
||
private static ?array $cache = null;
|
||
|
||
public static function clearCache(): void
|
||
{
|
||
self::$cache = null;
|
||
}
|
||
|
||
/**
|
||
* @return list<array{streak: int, odds_factor: int, is_jackpot: bool}>
|
||
*/
|
||
public static function defaultRows(): array
|
||
{
|
||
$out = [];
|
||
for ($s = 1; $s <= 10; $s++) {
|
||
$out[] = [
|
||
'streak' => $s,
|
||
'odds_factor' => $s,
|
||
'is_jackpot' => $s === 10,
|
||
];
|
||
}
|
||
|
||
return $out;
|
||
}
|
||
|
||
/**
|
||
* @param mixed $raw
|
||
*
|
||
* @return list<array{streak: int, odds_factor: int, is_jackpot: bool}>
|
||
*/
|
||
public static function parseFromConfigValue($raw): array
|
||
{
|
||
if (!is_string($raw) || trim($raw) === '') {
|
||
return self::defaultRows();
|
||
}
|
||
$decoded = json_decode($raw, true);
|
||
if (!is_array($decoded)) {
|
||
return self::defaultRows();
|
||
}
|
||
$list = $decoded['rows'] ?? $decoded;
|
||
if (!is_array($list)) {
|
||
return self::defaultRows();
|
||
}
|
||
$byStreak = [];
|
||
foreach ($list as $row) {
|
||
if (!is_array($row)) {
|
||
continue;
|
||
}
|
||
$streak = isset($row['streak']) && is_numeric($row['streak']) ? (int) $row['streak'] : 0;
|
||
if ($streak < 1 || $streak > 10) {
|
||
continue;
|
||
}
|
||
$factor = isset($row['odds_factor']) && is_numeric($row['odds_factor']) ? (int) $row['odds_factor'] : $streak;
|
||
if ($factor < 1) {
|
||
$factor = 1;
|
||
}
|
||
$jack = !empty($row['is_jackpot']);
|
||
$byStreak[$streak] = [
|
||
'streak' => $streak,
|
||
'odds_factor' => $factor,
|
||
'is_jackpot' => $jack,
|
||
];
|
||
}
|
||
$out = [];
|
||
for ($s = 1; $s <= 10; $s++) {
|
||
$out[] = $byStreak[$s] ?? [
|
||
'streak' => $s,
|
||
'odds_factor' => $s,
|
||
'is_jackpot' => $s === 10,
|
||
];
|
||
}
|
||
|
||
return $out;
|
||
}
|
||
|
||
/**
|
||
* 从库加载并缓存
|
||
*
|
||
* @return list<array{streak: int, odds_factor: int, is_jackpot: bool}>
|
||
*/
|
||
public static function loadRows(): array
|
||
{
|
||
if (self::$cache !== null) {
|
||
return self::$cache;
|
||
}
|
||
$row = Db::name('game_config')->where('config_key', self::CONFIG_KEY)->find();
|
||
self::$cache = self::parseFromConfigValue($row['config_value'] ?? null);
|
||
|
||
return self::$cache;
|
||
}
|
||
|
||
/**
|
||
* streak_at_bet 为下注时快照(0 表示尚未连胜);档位取 min(streak_at_bet+1, 10)。
|
||
*/
|
||
public static function levelFromStreakAtBet(int $streakAtBet): int
|
||
{
|
||
$level = $streakAtBet + 1;
|
||
if ($level < 1) {
|
||
$level = 1;
|
||
}
|
||
if ($level > 10) {
|
||
$level = 10;
|
||
}
|
||
|
||
return $level;
|
||
}
|
||
|
||
/**
|
||
* @return array{streak: int, odds_factor: int, is_jackpot: bool}
|
||
*/
|
||
public static function rowForStreakAtBet(int $streakAtBet): array
|
||
{
|
||
$level = self::levelFromStreakAtBet($streakAtBet);
|
||
foreach (self::loadRows() as $row) {
|
||
if ((int) ($row['streak'] ?? 0) === $level) {
|
||
return $row;
|
||
}
|
||
}
|
||
|
||
return [
|
||
'streak' => $level,
|
||
'odds_factor' => $level,
|
||
'is_jackpot' => $level === 10,
|
||
];
|
||
}
|
||
|
||
public static function isJackpotForStreakAtBet(int $streakAtBet): bool
|
||
{
|
||
return self::rowForStreakAtBet($streakAtBet)['is_jackpot'] === true;
|
||
}
|
||
|
||
/**
|
||
* 返回该注单适用的「赔率乘数」字符串(= 配置档位的 odds_factor),供 bcmul(total_amount, ..., 4)。
|
||
*/
|
||
public static function totalOddsMultiplierForStreakAtBet(int $streakAtBet): string
|
||
{
|
||
$factor = (int) self::rowForStreakAtBet($streakAtBet)['odds_factor'];
|
||
if ($factor < 1) {
|
||
$factor = 1;
|
||
}
|
||
|
||
return bcadd((string) $factor, '0', 4);
|
||
}
|
||
|
||
/**
|
||
* @param list<array{streak: int, odds_factor: int, is_jackpot: bool}> $rows
|
||
*/
|
||
public static function encodeForDb(array $rows): string
|
||
{
|
||
$normalized = [];
|
||
for ($s = 1; $s <= 10; $s++) {
|
||
$found = null;
|
||
foreach ($rows as $r) {
|
||
if (!is_array($r)) {
|
||
continue;
|
||
}
|
||
$st = isset($r['streak']) && is_numeric($r['streak']) ? (int) $r['streak'] : 0;
|
||
if ($st === $s) {
|
||
$found = $r;
|
||
break;
|
||
}
|
||
}
|
||
if ($found === null) {
|
||
$normalized[] = [
|
||
'streak' => $s,
|
||
'odds_factor' => $s,
|
||
'is_jackpot' => $s === 10,
|
||
];
|
||
} else {
|
||
$f = isset($found['odds_factor']) && is_numeric($found['odds_factor']) ? (int) $found['odds_factor'] : $s;
|
||
if ($f < 1) {
|
||
$f = 1;
|
||
}
|
||
$normalized[] = [
|
||
'streak' => $s,
|
||
'odds_factor' => $f,
|
||
'is_jackpot' => !empty($found['is_jackpot']),
|
||
];
|
||
}
|
||
}
|
||
$json = json_encode(['rows' => $normalized], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||
if ($json === false) {
|
||
return '{"rows":[]}';
|
||
}
|
||
|
||
return $json;
|
||
}
|
||
}
|