推送模块
This commit is contained in:
@@ -13,6 +13,12 @@ final class GameLiveService
|
||||
private const BASE_ODDS = 33;
|
||||
private const CHANNEL = 'game-live';
|
||||
private const EVENT = 'bet-updated';
|
||||
|
||||
/** 与《36字花-移动端接口设计草案》7.1 对齐:公共对局频道 */
|
||||
private const CHANNEL_PUBLIC_GAME_PERIOD = 'public-game-period';
|
||||
private const EVT_PERIOD_TICK = 'period.tick';
|
||||
private const EVT_PERIOD_LOCKED = 'period.locked';
|
||||
private const EVT_PERIOD_OPENED = 'period.opened';
|
||||
private const KEY_PERIOD_SECONDS = 'period_seconds';
|
||||
private const KEY_BET_SECONDS = 'bet_seconds';
|
||||
private const KEY_PICK_MAX_NUMBER_COUNT = 'pick_max_number_count';
|
||||
@@ -203,6 +209,8 @@ final class GameLiveService
|
||||
return ['ok' => false, 'msg' => $e->getMessage()];
|
||||
}
|
||||
|
||||
self::publishPublicPeriodOpened((string) $record['period_no'], $finalNumber, $now);
|
||||
|
||||
self::publishSnapshot(null);
|
||||
return [
|
||||
'ok' => true,
|
||||
@@ -227,6 +235,7 @@ final class GameLiveService
|
||||
'update_time' => time(),
|
||||
]);
|
||||
$record['status'] = 1;
|
||||
self::publishPublicPeriodLocked($record);
|
||||
}
|
||||
if ($elapsed < $periodSeconds) {
|
||||
return;
|
||||
@@ -238,16 +247,121 @@ final class GameLiveService
|
||||
{
|
||||
try {
|
||||
$payload = self::buildSnapshot($recordId);
|
||||
$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')
|
||||
);
|
||||
$api = self::createPushApi();
|
||||
$api->trigger(self::CHANNEL, self::EVENT, $payload);
|
||||
self::publishPublicPeriodTick($payload, $api);
|
||||
} catch (Throwable) {
|
||||
}
|
||||
}
|
||||
|
||||
private static function createPushApi(): Api
|
||||
{
|
||||
return 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')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移动端公共频道:每秒心跳,含期号、倒计时、阶段(对齐 lobbyInit/periodCurrent 语义)
|
||||
*/
|
||||
private static function publishPublicPeriodTick(array $snapshot, Api $api): void
|
||||
{
|
||||
$record = $snapshot['record'] ?? null;
|
||||
$serverTime = (int) ($snapshot['server_time'] ?? time());
|
||||
$remaining = (int) ($snapshot['remaining_seconds'] ?? 0);
|
||||
$betCloseIn = (int) ($snapshot['bet_remaining_seconds'] ?? 0);
|
||||
$periodNo = '';
|
||||
$dbStatus = 0;
|
||||
$resultNumber = null;
|
||||
if (is_array($record)) {
|
||||
$periodNo = (string) ($record['period_no'] ?? '');
|
||||
$dbStatus = (int) ($record['status'] ?? 0);
|
||||
$rn = $record['result_number'] ?? null;
|
||||
$resultNumber = is_numeric((string) $rn) ? (int) $rn : null;
|
||||
}
|
||||
if ($record === null || $periodNo === '') {
|
||||
$status = 'idle';
|
||||
} else {
|
||||
$status = self::mapPublicPeriodStatus($dbStatus, $betCloseIn);
|
||||
}
|
||||
$payload = [
|
||||
'server_time' => $serverTime,
|
||||
'period_no' => $periodNo,
|
||||
'status' => $status,
|
||||
'countdown' => $remaining,
|
||||
'bet_close_in'=> $betCloseIn,
|
||||
];
|
||||
if ($periodNo !== '' && $record !== null) {
|
||||
$start = (int) ($record['period_start_at'] ?? 0);
|
||||
$betSeconds = (int) ($snapshot['bet_seconds'] ?? 20);
|
||||
$periodSeconds = (int) ($snapshot['period_seconds'] ?? 30);
|
||||
if ($start > 0) {
|
||||
$payload['lock_at'] = $start + $betSeconds;
|
||||
$payload['open_at'] = $start + $periodSeconds;
|
||||
}
|
||||
}
|
||||
if ($resultNumber !== null) {
|
||||
$payload['result_number'] = $resultNumber;
|
||||
}
|
||||
$api->trigger(self::CHANNEL_PUBLIC_GAME_PERIOD, self::EVT_PERIOD_TICK, $payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $record game_record 行
|
||||
*/
|
||||
private static function publishPublicPeriodLocked(array $record): void
|
||||
{
|
||||
try {
|
||||
$start = (int) ($record['period_start_at'] ?? 0);
|
||||
$betSeconds = self::getConfigInt(self::KEY_BET_SECONDS, 20);
|
||||
$periodNo = (string) ($record['period_no'] ?? '');
|
||||
$payload = [
|
||||
'period_no' => $periodNo,
|
||||
'lock_at' => $start > 0 ? $start + $betSeconds : time(),
|
||||
];
|
||||
$api = self::createPushApi();
|
||||
$api->trigger(self::CHANNEL_PUBLIC_GAME_PERIOD, self::EVT_PERIOD_LOCKED, $payload);
|
||||
} catch (Throwable) {
|
||||
}
|
||||
}
|
||||
|
||||
private static function publishPublicPeriodOpened(string $periodNo, int $resultNumber, int $openTime): void
|
||||
{
|
||||
try {
|
||||
$payload = [
|
||||
'period_no' => $periodNo,
|
||||
'result_number' => $resultNumber,
|
||||
'open_time' => $openTime,
|
||||
];
|
||||
$api = self::createPushApi();
|
||||
$api->trigger(self::CHANNEL_PUBLIC_GAME_PERIOD, self::EVT_PERIOD_OPENED, $payload);
|
||||
} catch (Throwable) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 与文档 3.1/4.1 中 status 字符串对齐:betting / locked / settling / finished
|
||||
*/
|
||||
private static function mapPublicPeriodStatus(int $dbStatus, int $betCloseIn): string
|
||||
{
|
||||
if ($dbStatus === 0) {
|
||||
return $betCloseIn > 0 ? 'betting' : 'locked';
|
||||
}
|
||||
if ($dbStatus === 1) {
|
||||
return 'locked';
|
||||
}
|
||||
if ($dbStatus === 4) {
|
||||
return 'finished';
|
||||
}
|
||||
if ($dbStatus === 2 || $dbStatus === 3) {
|
||||
return 'settling';
|
||||
}
|
||||
|
||||
return 'finished';
|
||||
}
|
||||
|
||||
private static function resolveRecord(?int $recordId): ?array
|
||||
{
|
||||
if ($recordId !== null && $recordId > 0) {
|
||||
|
||||
Reference in New Issue
Block a user