From 24aab111b50aa4c95297f50f003de1127a79b211 Mon Sep 17 00:00:00 2001 From: zhenhui <1276357500@qq.com> Date: Mon, 20 Apr 2026 10:02:27 +0800 Subject: [PATCH] =?UTF-8?q?1.=E4=BC=98=E5=8C=96=E5=BC=80=E5=A5=96=E5=92=8C?= =?UTF-8?q?=E6=8E=A8=E9=80=81=202.=E6=96=B0=E5=A2=9E=E6=8E=A7=E5=88=B6?= =?UTF-8?q?=E8=BF=9E=E7=BB=AD=E5=BC=80=E5=A5=96=E8=B5=94=E7=8E=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/controller/config/GameConfig.php | 65 +++++- .../controller/config/StreakWinReward.php | 121 +++++++++++ app/api/controller/Game.php | 2 +- app/common/library/game/StreakWinReward.php | 205 ++++++++++++++++++ app/common/service/GameBetSettleService.php | 79 ++++++- app/common/service/GameLiveService.php | 14 +- app/common/service/GameRecordStatService.php | 7 +- app/common/service/JackpotPushService.php | 72 ++++++ app/common/service/UserPushService.php | 3 + .../lang/backend/en/config/streakWinReward.ts | 8 + .../lang/backend/en/test/pushGamePeriod.ts | 2 +- .../backend/zh-cn/config/streakWinReward.ts | 8 + .../lang/backend/zh-cn/test/pushGamePeriod.ts | 2 +- .../backend/zh-cn/test/pushOperationNotice.ts | 2 +- web/src/utils/backend/pushChannelTest.ts | 1 + .../backend/config/depositTier/index.vue | 53 +++-- .../views/backend/config/gameConfig/index.vue | 1 + .../backend/config/streakWinReward/index.vue | 141 ++++++++++++ 18 files changed, 749 insertions(+), 37 deletions(-) create mode 100644 app/admin/controller/config/StreakWinReward.php create mode 100644 app/common/library/game/StreakWinReward.php create mode 100644 app/common/service/JackpotPushService.php create mode 100644 web/src/lang/backend/en/config/streakWinReward.ts create mode 100644 web/src/lang/backend/zh-cn/config/streakWinReward.ts create mode 100644 web/src/views/backend/config/streakWinReward/index.vue diff --git a/app/admin/controller/config/GameConfig.php b/app/admin/controller/config/GameConfig.php index 136452c..850987a 100644 --- a/app/admin/controller/config/GameConfig.php +++ b/app/admin/controller/config/GameConfig.php @@ -3,6 +3,8 @@ namespace app\admin\controller\config; use app\common\controller\Backend; +use app\common\library\game\DepositTier; +use app\common\library\game\StreakWinReward; use app\common\library\game\ZiHuaDictionary as ZiHuaDictionaryLib; use support\Response; use Webman\Http\Request as WebmanRequest; @@ -33,7 +35,19 @@ class GameConfig extends Backend } /** - * 列表:排除独立表单维护的 36 字花字典 + * @return list + */ + protected function excludedConfigKeys(): array + { + return [ + ZiHuaDictionaryLib::CONFIG_KEY, + DepositTier::CONFIG_KEY, + StreakWinReward::CONFIG_KEY, + ]; + } + + /** + * 列表:排除独立表单维护的配置键 */ protected function _index(): Response { @@ -45,7 +59,7 @@ class GameConfig extends Backend $table = strtolower($this->model->getTable()); $mainShort = $alias[$table] ?? ''; if ($mainShort !== '') { - $where[] = [$mainShort . '.config_key', '<>', ZiHuaDictionaryLib::CONFIG_KEY]; + $where[] = [$mainShort . '.config_key', 'not in', $this->excludedConfigKeys()]; } $res = $this->model @@ -63,4 +77,51 @@ class GameConfig extends Backend 'remark' => get_route_remark(), ]); } + + /** + * 远程下拉:排除独立维护的配置键 + */ + protected function _select(): Response + { + if (empty($this->model)) { + return $this->success('', [ + 'list' => [], + 'total' => 0, + ]); + } + + $pk = $this->model->getPk(); + + $fields = [$pk]; + $quickSearchArr = is_array($this->quickSearchField) ? $this->quickSearchField : explode(',', (string) $this->quickSearchField); + foreach ($quickSearchArr as $f) { + $f = trim((string) $f); + if ($f === '') { + continue; + } + $f = str_contains($f, '.') ? substr($f, strrpos($f, '.') + 1) : $f; + if ($f !== '' && !in_array($f, $fields, true)) { + $fields[] = $f; + } + } + + list($where, $alias, $limit, $order) = $this->queryBuilder(); + $table = strtolower($this->model->getTable()); + $mainShort = $alias[$table] ?? ''; + if ($mainShort !== '') { + $where[] = [$mainShort . '.config_key', 'not in', $this->excludedConfigKeys()]; + } + + $res = $this->model + ->field($fields) + ->alias($alias) + ->where($where) + ->order($order) + ->paginate($limit); + + return $this->success('', [ + 'list' => $res->items(), + 'total' => $res->total(), + ]); + } } diff --git a/app/admin/controller/config/StreakWinReward.php b/app/admin/controller/config/StreakWinReward.php new file mode 100644 index 0000000..77d4eec --- /dev/null +++ b/app/admin/controller/config/StreakWinReward.php @@ -0,0 +1,121 @@ +auth) { + return false; + } + $controllerPath = get_controller_path($request); + if (!$controllerPath) { + return false; + } + $paths = []; + $paths[] = $controllerPath . '/' . $action; + $parts = explode('/', $controllerPath); + foreach ($parts as &$part) { + if (str_contains($part, '_')) { + $part = lcfirst(str_replace(' ', '', ucwords(str_replace('_', ' ', $part)))); + } + } + $paths[] = implode('/', $parts) . '/' . $action; + foreach (array_values(array_unique($paths)) as $path) { + if ($this->auth->check($path)) { + return true; + } + } + + return false; + } + + protected function initController(WebmanRequest $request): ?Response + { + return null; + } + + public function index(WebmanRequest $request): Response + { + $response = $this->initializeBackend($request); + if ($response !== null) { + return $response; + } + if (!$this->hasNodePermission($request, 'index')) { + return $this->error(__('You have no permission'), [], 401); + } + if ($request->method() !== 'GET') { + return $this->error(__('Parameter error')); + } + $row = Db::name('game_config')->where('config_key', StreakWinRewardLib::CONFIG_KEY)->find(); + $rows = StreakWinRewardLib::parseFromConfigValue($row['config_value'] ?? null); + + return $this->success('', [ + 'rows' => $rows, + ]); + } + + public function save(WebmanRequest $request): Response + { + $response = $this->initializeBackend($request); + if ($response !== null) { + return $response; + } + if (!$this->hasNodePermission($request, 'save')) { + return $this->error(__('You have no permission'), [], 401); + } + if ($request->method() !== 'POST') { + return $this->error(__('Parameter error')); + } + $payload = $request->post('rows'); + if (!is_array($payload)) { + return $this->error('参数错误'); + } + $encoded = StreakWinRewardLib::encodeForDb($payload); + $now = time(); + Db::startTrans(); + try { + $exists = Db::name('game_config')->where('config_key', StreakWinRewardLib::CONFIG_KEY)->find(); + if ($exists) { + Db::name('game_config')->where('config_key', StreakWinRewardLib::CONFIG_KEY)->update([ + 'config_value' => $encoded, + 'update_time' => $now, + ]); + } else { + Db::name('game_config')->insert([ + 'config_key' => StreakWinRewardLib::CONFIG_KEY, + 'config_value' => $encoded, + 'value_type' => 'json', + 'remark' => '连胜奖励', + 'create_time' => $now, + 'update_time' => $now, + ]); + } + Db::commit(); + } catch (Throwable $e) { + Db::rollback(); + + return $this->error($e->getMessage()); + } + StreakWinRewardLib::clearCache(); + + return $this->success('保存成功'); + } +} diff --git a/app/api/controller/Game.php b/app/api/controller/Game.php index 6bdf66b..1069109 100644 --- a/app/api/controller/Game.php +++ b/app/api/controller/Game.php @@ -138,7 +138,7 @@ class Game extends MobileBase * 提交下注:入参极简——period_no + numbers + bet_amount(整笔总金额) + idempotency_key。 * * 下注判定:开奖号码 ∈ pick_numbers 即算中奖,赔付按整笔 total_amount × odds 计算 - * (odds 定义见 GameBetSettleService::BASE_ODDS 与 streak_at_bet)。 + * (派彩 = 压注总额 × 连胜奖励表 odds_factor;streak_at_bet 为下注时快照)。 */ public function betPlace(Request $request): Response { diff --git a/app/common/library/game/StreakWinReward.php b/app/common/library/game/StreakWinReward.php new file mode 100644 index 0000000..e469823 --- /dev/null +++ b/app/common/library/game/StreakWinReward.php @@ -0,0 +1,205 @@ +|null */ + private static ?array $cache = null; + + public static function clearCache(): void + { + self::$cache = null; + } + + /** + * @return list + */ + 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 + */ + 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 + */ + 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 $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; + } +} diff --git a/app/common/service/GameBetSettleService.php b/app/common/service/GameBetSettleService.php index 839d7ac..68dffd1 100644 --- a/app/common/service/GameBetSettleService.php +++ b/app/common/service/GameBetSettleService.php @@ -4,25 +4,27 @@ declare(strict_types=1); namespace app\common\service; +use app\common\library\game\StreakWinReward; use support\think\Db; use Throwable; /** * 开奖后结算注单:写入 win_amount、status=已结算;中奖时入账并记 user_wallet_record(biz_type=payout)。 + * 连胜赔率来自 game_config.streak_win_reward;结算后更新 user.current_streak(未中奖则连胜归 0)。 */ final class GameBetSettleService { - private const BASE_ODDS = 33; - /** * 对指定期次按开奖号码结算所有「待开奖」注单;同一注单幂等(仅 status=1 会更新)。 * + * @return array{jackpot_hits: list} + * * @throws Throwable */ - public static function settleBetsForDraw(int $recordId, int $resultNumber): void + public static function settleBetsForDraw(int $recordId, int $resultNumber): array { if ($recordId <= 0 || $resultNumber < 1) { - return; + return ['jackpot_hits' => []]; } $now = time(); @@ -36,12 +38,26 @@ final class GameBetSettleService /** @var array}> */ $aggregateByUser = []; + /** @var array */ + $userOutcome = []; + + /** @var array */ + $jackpotNotify = []; + foreach ($bets as $bet) { $betId = (int) ($bet['id'] ?? 0); if ($betId <= 0) { continue; } + $userId = (int) ($bet['user_id'] ?? 0); + if ($userId > 0 && !isset($userOutcome[$userId])) { + $userOutcome[$userId] = [ + 'streak_at' => (int) ($bet['streak_at_bet'] ?? 0), + 'had_win' => false, + ]; + } + $win = self::computeWinAmount($bet, $resultNumber); $jackpot = '0.0000'; @@ -59,10 +75,17 @@ final class GameBetSettleService continue; } - // 结算刚刚成功(status 1 → 2):把本单下注总额 1:1 累加到用户打码量 self::creditUserBetFlow($bet, $now); - $userId = (int) ($bet['user_id'] ?? 0); + if ($userId > 0) { + if (bccomp($win, '0', 4) > 0) { + $userOutcome[$userId]['had_win'] = true; + } + if (bccomp($win, '0', 4) > 0 && StreakWinReward::isJackpotForStreakAtBet((int) ($bet['streak_at_bet'] ?? 0))) { + $jackpotNotify[$userId] = true; + } + } + if ($userId <= 0) { continue; } @@ -93,6 +116,23 @@ final class GameBetSettleService ]; } + foreach ($userOutcome as $userId => $info) { + $streakAt = (int) ($info['streak_at'] ?? 0); + $hadWin = (bool) ($info['had_win'] ?? false); + if ($hadWin) { + $next = $streakAt + 1; + if ($next > 10) { + $next = 10; + } + } else { + $next = 0; + } + Db::name('user')->where('id', $userId)->update([ + 'current_streak' => $next, + 'update_time' => $now, + ]); + } + foreach ($aggregateByUser as $userId => $agg) { $hitOrderCount = 0; foreach ($agg['orders'] as $o) { @@ -119,6 +159,25 @@ final class GameBetSettleService ]); } } + + $jackpotHits = []; + foreach ($jackpotNotify as $uid => $_) { + if (!isset($aggregateByUser[$uid])) { + continue; + } + $agg = $aggregateByUser[$uid]; + if (bccomp($agg['total_win'], '0', 4) <= 0) { + continue; + } + $jackpotHits[] = [ + 'user_id' => (int) $uid, + 'period_no' => (string) ($agg['period_no'] ?? ''), + 'total_win' => (string) $agg['total_win'], + 'result_number' => $resultNumber, + ]; + } + + return ['jackpot_hits' => $jackpotHits]; } /** @@ -150,8 +209,9 @@ final class GameBetSettleService } Db::startTrans(); try { - self::settleBetsForDraw($rid, $rn); + $out = self::settleBetsForDraw($rid, $rn); Db::commit(); + JackpotPushService::publishHits($out['jackpot_hits'] ?? []); $count++; } catch (Throwable $e) { Db::rollback(); @@ -163,7 +223,7 @@ final class GameBetSettleService } /** - * 应付派彩:开奖号码 ∈ pick_numbers 即中奖;整笔 total_amount × (连胜+1) × 33(与 GameLiveService 一致)。 + * 应付派彩:开奖号码 ∈ pick_numbers 即中奖;整笔 total_amount × odds_factor(odds_factor 来自连胜奖励表对应档位)。 */ public static function computeWinAmount(array $bet, int $resultNumber): string { @@ -180,7 +240,7 @@ final class GameBetSettleService } $total = (string) ($bet['total_amount'] ?? '0'); $streak = (int) ($bet['streak_at_bet'] ?? 0); - $odds = (string) (($streak + 1) * self::BASE_ODDS); + $odds = StreakWinReward::totalOddsMultiplierForStreakAtBet($streak); return bcmul($total, $odds, 4); } @@ -206,7 +266,6 @@ final class GameBetSettleService if (bccomp($flow, '0', 4) <= 0) { return; } - // 原子加法:避免读-改-写导致的并发覆盖;$flow 已由 bcadd 归一化为纯数字字符串,不存在 SQL 注入 Db::name('user') ->where('id', $userId) ->update([ diff --git a/app/common/service/GameLiveService.php b/app/common/service/GameLiveService.php index b1764d9..494672f 100644 --- a/app/common/service/GameLiveService.php +++ b/app/common/service/GameLiveService.php @@ -4,13 +4,13 @@ declare(strict_types=1); namespace app\common\service; +use app\common\library\game\StreakWinReward; use support\think\Db; use Throwable; use Webman\Push\Api; final class GameLiveService { - private const BASE_ODDS = 33; private const CHANNEL = 'game-live'; private const EVENT = 'bet-updated'; @@ -327,6 +327,7 @@ final class GameLiveService $now = time(); $payoutUntil = $now + self::PAYOUT_GRACE_SECONDS; + $settleOut = ['jackpot_hits' => []]; Db::startTrans(); try { Db::name('game_record')->where('id', (int) $record['id'])->update([ @@ -337,14 +338,19 @@ final class GameLiveService 'payout_until' => $payoutUntil, 'update_time' => $now, ]); - GameBetSettleService::settleBetsForDraw((int) $record['id'], $finalNumber); + $settleOut = GameBetSettleService::settleBetsForDraw((int) $record['id'], $finalNumber); Db::commit(); - GameRecordStatService::refreshForRecordId((int) $record['id']); } catch (Throwable $e) { Db::rollback(); return ['ok' => false, 'msg' => $e->getMessage()]; } + try { + GameRecordStatService::refreshForRecordId((int) $record['id']); + } catch (Throwable) { + } + JackpotPushService::publishHits($settleOut['jackpot_hits'] ?? []); + self::publishPublicPeriodOpened((string) $record['period_no'], $finalNumber, $now); self::publishPublicPeriodPayout((string) $record['period_no'], $finalNumber, $payoutUntil); self::publishSnapshot(null); @@ -690,7 +696,7 @@ final class GameLiveService } $total = (string) ($bet['total_amount'] ?? '0'); $streak = (int) ($bet['streak_at_bet'] ?? 0); - $odds = (string) (($streak + 1) * self::BASE_ODDS); + $odds = StreakWinReward::totalOddsMultiplierForStreakAtBet($streak); $orderPayout = bcmul($total, $odds, 4); $payout = bcadd($payout, $orderPayout, 4); } diff --git a/app/common/service/GameRecordStatService.php b/app/common/service/GameRecordStatService.php index 3d94175..7270288 100644 --- a/app/common/service/GameRecordStatService.php +++ b/app/common/service/GameRecordStatService.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace app\common\service; +use app\common\library\game\StreakWinReward; use support\think\Db; /** @@ -11,8 +12,6 @@ use support\think\Db; */ final class GameRecordStatService { - private const BASE_ODDS = 33; - /** * 根据注单与开奖号码回写 game_record 统计字段(已结束对局)。 */ @@ -82,7 +81,7 @@ final class GameRecordStatService } /** - * 与 GameLiveService::estimateLossForNumber 中派彩一致:命中号码时 total_amount × (streak+1) × 33。 + * 与 GameLiveService::estimateLossForNumber 一致:命中号码时 total_amount × odds_factor。 */ private static function estimatePayoutForBet(array $bet, int $resultNumber): string { @@ -99,7 +98,7 @@ final class GameRecordStatService } $total = (string) ($bet['total_amount'] ?? '0'); $streak = (int) ($bet['streak_at_bet'] ?? 0); - $odds = (string) (($streak + 1) * self::BASE_ODDS); + $odds = StreakWinReward::totalOddsMultiplierForStreakAtBet($streak); return bcmul($total, $odds, 4); } diff --git a/app/common/service/JackpotPushService.php b/app/common/service/JackpotPushService.php new file mode 100644 index 0000000..d12865a --- /dev/null +++ b/app/common/service/JackpotPushService.php @@ -0,0 +1,72 @@ + $hits + */ + public static function publishHits(array $hits): void + { + foreach ($hits as $h) { + $uid = (int) ($h['user_id'] ?? 0); + if ($uid <= 0) { + continue; + } + $periodNo = (string) ($h['period_no'] ?? ''); + $totalWin = (string) ($h['total_win'] ?? '0'); + $rn = (int) ($h['result_number'] ?? 0); + UserPushService::publish($uid, UserPushService::EVT_JACKPOT_HIT, [ + 'period_no' => $periodNo, + 'total_win_amount' => $totalWin, + 'result_number' => $rn, + 'is_jackpot' => true, + ]); + self::publishPublicChannels($periodNo, $uid, $totalWin, $rn); + } + } + + /** + * @param array $payload + */ + private static function triggerChannel(Api $api, string $channel, array $payload): void + { + $api->trigger($channel, self::EVT_JACKPOT_HIT, $payload); + } + + private static function publishPublicChannels(string $periodNo, int $userId, string $totalWin, int $resultNumber): void + { + try { + $api = new Api( + str_replace('0.0.0.0', '127.0.0.1', (string) config('plugin.webman.push.app.api')), + (string) config('plugin.webman.push.app.app_key'), + (string) config('plugin.webman.push.app.app_secret') + ); + $payload = [ + 'period_no' => $periodNo, + 'user_id' => $userId, + 'total_win_amount' => $totalWin, + 'result_number' => $resultNumber, + 'message' => '恭喜玩家命中大奖派彩', + ]; + self::triggerChannel($api, self::CHANNEL_GAME_PERIOD, $payload); + self::triggerChannel($api, self::CHANNEL_OPERATION_NOTICE, $payload); + } catch (Throwable) { + } + } +} diff --git a/app/common/service/UserPushService.php b/app/common/service/UserPushService.php index 24851bf..96ba6c0 100644 --- a/app/common/service/UserPushService.php +++ b/app/common/service/UserPushService.php @@ -20,6 +20,9 @@ final class UserPushService public const EVT_WALLET_CHANGED = 'wallet.changed'; + /** 命中配置为「大奖」的连胜档派彩(私有频道) */ + public const EVT_JACKPOT_HIT = 'jackpot.hit'; + private static function channelName(string $uuid): string { return 'private-user-' . $uuid; diff --git a/web/src/lang/backend/en/config/streakWinReward.ts b/web/src/lang/backend/en/config/streakWinReward.ts new file mode 100644 index 0000000..a335dfc --- /dev/null +++ b/web/src/lang/backend/en/config/streakWinReward.ts @@ -0,0 +1,8 @@ +export default { + desc: 'Streak levels 1–10: payout = bet total × odds_factor. Jackpot rows trigger jackpot.hit on the user private channel, public-game-period, and public-operation-notice when won.', + btn_save: 'Save', + streak: 'Streak (rounds)', + odds_factor: 'Odds factor', + is_jackpot: 'Jackpot', + err_factor: 'Row {no}: odds factor must be ≥ 1', +} diff --git a/web/src/lang/backend/en/test/pushGamePeriod.ts b/web/src/lang/backend/en/test/pushGamePeriod.ts index 9d4533b..0b34ff5 100644 --- a/web/src/lang/backend/en/test/pushGamePeriod.ts +++ b/web/src/lang/backend/en/test/pushGamePeriod.ts @@ -1,3 +1,3 @@ export default { - tip: 'Subscribe to public-game-period (global period channel) for period.tick / period.locked / period.opened / period.payout. The server must publish to this channel.', + tip: 'Subscribe to public-game-period (global period channel) for period.tick / period.locked / period.opened / period.payout / jackpot.hit. The server must publish to this channel.', } diff --git a/web/src/lang/backend/zh-cn/config/streakWinReward.ts b/web/src/lang/backend/zh-cn/config/streakWinReward.ts new file mode 100644 index 0000000..8efba56 --- /dev/null +++ b/web/src/lang/backend/zh-cn/config/streakWinReward.ts @@ -0,0 +1,8 @@ +export default { + desc: '1~10 档连胜:派彩 = 压注总额 × 赔率系数(odds_factor)。勾选「大奖」的档位在中奖时会对玩家私有频道、public-game-period 与 public-operation-notice 推送 jackpot.hit。', + btn_save: '保存', + streak: '连胜档(局)', + odds_factor: '赔率系数', + is_jackpot: '是否大奖', + err_factor: '第 {no} 行赔率系数须 ≥1', +} diff --git a/web/src/lang/backend/zh-cn/test/pushGamePeriod.ts b/web/src/lang/backend/zh-cn/test/pushGamePeriod.ts index 49f4a61..49463c0 100644 --- a/web/src/lang/backend/zh-cn/test/pushGamePeriod.ts +++ b/web/src/lang/backend/zh-cn/test/pushGamePeriod.ts @@ -1,3 +1,3 @@ export default { - tip: '订阅文档中的「全局对局频道」public-game-period,用于验证 period.tick / period.locked / period.opened / period.payout 等公共事件(需服务端向该频道推送)。', + tip: '订阅文档中的「全局对局频道」public-game-period,用于验证 period.tick / period.locked / period.opened / period.payout / jackpot.hit 等公共事件(需服务端向该频道推送)。', } diff --git a/web/src/lang/backend/zh-cn/test/pushOperationNotice.ts b/web/src/lang/backend/zh-cn/test/pushOperationNotice.ts index 6075fb4..2823334 100644 --- a/web/src/lang/backend/zh-cn/test/pushOperationNotice.ts +++ b/web/src/lang/backend/zh-cn/test/pushOperationNotice.ts @@ -1,3 +1,3 @@ export default { - tip: '订阅文档中的「公告广播频道」public-operation-notice,用于验证 notice.popout 等全站公告类推送(需服务端向该频道推送)。', + tip: '订阅「公告广播频道」public-operation-notice:全站公告 notice.popout 与本游戏 jackpot.hit(连胜大奖广播)均会发往此频道;需服务端推送。', } diff --git a/web/src/utils/backend/pushChannelTest.ts b/web/src/utils/backend/pushChannelTest.ts index 87d1986..d494fe9 100644 --- a/web/src/utils/backend/pushChannelTest.ts +++ b/web/src/utils/backend/pushChannelTest.ts @@ -7,6 +7,7 @@ const DOC_EVENTS = [ 'period.locked', 'period.opened', 'period.payout', + 'jackpot.hit', 'bet.accepted', 'bet.settled', 'wallet.changed', diff --git a/web/src/views/backend/config/depositTier/index.vue b/web/src/views/backend/config/depositTier/index.vue index 570ea57..0ed6eb0 100644 --- a/web/src/views/backend/config/depositTier/index.vue +++ b/web/src/views/backend/config/depositTier/index.vue @@ -14,32 +14,45 @@ - - + + - + - + - + - +