1.重构实时消息WebSocket连接

2.MySQL备份
This commit is contained in:
2026-04-24 13:49:38 +08:00
parent d69412a0f7
commit fd324f2882
54 changed files with 2396 additions and 2638 deletions

View File

@@ -14,7 +14,6 @@ use app\common\model\DepositOrder;
use app\common\model\GameConfig;
use app\common\model\WithdrawOrder;
use app\common\service\DepositOrderExpireService;
use app\common\service\UserPushService;
use support\Response;
use support\think\Db;
use Throwable;
@@ -332,7 +331,7 @@ class Finance extends MobileBase
}
/**
* 模拟第三方异步通知:验签后调用 DepositSettlement::settle 入账,并推送 wallet.changed
* 模拟第三方异步通知:验签后调用 DepositSettlement::settle 入账。
*/
public function depositMockNotify(Request $request): Response
{
@@ -373,19 +372,6 @@ class Finance extends MobileBase
null,
'channel_code=' . $pc
);
$uid = intval(strval($order->user_id));
if ($uid > 0) {
$coinAfter = is_string($result['balance_after'] ?? null) ? $result['balance_after'] : strval($result['balance_after'] ?? '0');
$credit = is_string($result['credit'] ?? null) ? $result['credit'] : strval($result['credit'] ?? '0');
UserPushService::publish($uid, UserPushService::EVT_WALLET_CHANGED, [
'reason' => 'deposit',
'ref_type' => 'deposit_order',
'ref_id' => (string) $orderId,
'order_no' => $orderNo,
'delta' => $credit,
'balance_after' => $coinAfter,
]);
}
} catch (Throwable $e) {
return $this->mobileError(2000, $e->getMessage());
}

View File

@@ -11,7 +11,7 @@ use app\common\model\UserWalletRecord;
use app\common\service\GameHotDataCoordinator;
use app\common\service\GameHotDataRedis;
use app\common\service\GameRecordService;
use app\common\service\UserPushService;
use app\common\service\GameWebSocketEventBus;
use support\think\Db;
use Webman\Http\Request;
use support\Response;
@@ -139,12 +139,27 @@ class Game extends MobileBase
}
/**
* 提交下注入参极简——period_no + numbers + bet_amount整笔总金额 + idempotency_key。
*
* 下注判定:开奖号码 ∈ pick_numbers 即算中奖,赔付按整笔 total_amount × odds 计算
* (派彩 = 压注总额 × 连胜奖励表 odds_factorstreak_at_bet 为下注时快照)。
* 与前端文档对齐:/api/game/current_status
*/
public function currentStatus(Request $request): Response
{
return $this->periodCurrent($request);
}
/**
* 兼容旧路由:/api/game/betPlace
* 新语义与 place_bet 一致bet_amount 作为“单注金额”。
*/
public function betPlace(Request $request): Response
{
return $this->placeBet($request);
}
/**
* 提交下注:入参为 period_no + numbers + single_bet_amount + idempotency_key。
* 兼容前端传参 bet_amount作为 single_bet_amount 同义字段)。
*/
public function placeBet(Request $request): Response
{
$response = $this->initializeMobile($request);
if ($response !== null) {
@@ -152,16 +167,14 @@ class Game extends MobileBase
}
$periodNo = trim((string) $request->post('period_no', ''));
$numbersRaw = $request->post('numbers', '');
$betAmount = trim((string) $request->post('bet_amount', ''));
$singleBetAmount = trim((string) ($request->post('single_bet_amount', $request->post('bet_amount', ''))));
$idempotencyKey = trim((string) $request->post('idempotency_key', ''));
if ($periodNo === '' || $betAmount === '' || $idempotencyKey === '') {
if ($periodNo === '' || $singleBetAmount === '' || $idempotencyKey === '') {
return $this->mobileError(1001, 'Missing parameters');
}
if (!is_numeric($betAmount) || bccomp($betAmount, '0', 2) <= 0) {
if (!is_numeric($singleBetAmount) || bccomp($singleBetAmount, '0', 2) <= 0) {
return $this->mobileError(1003, 'Invalid parameter value');
}
$totalAmount = bcadd($betAmount, '0', 2);
$numbers = $this->parseBetNumbersFromRequest($numbersRaw);
if ($numbers === []) {
return $this->mobileError(1003, 'Invalid parameter value');
@@ -170,6 +183,9 @@ class Game extends MobileBase
if (count($numbers) > $maxSelect) {
return $this->mobileError(1003, 'Invalid parameter value');
}
$singleAmount = bcadd($singleBetAmount, '0', 2);
$numberCount = (string) count($numbers);
$totalAmount = bcmul($singleAmount, $numberCount, 2);
if (!GameRecordService::isLiveRuntimeEnabled()) {
return $this->mobileError(3001, 'Game is paused');
@@ -273,19 +289,28 @@ class Game extends MobileBase
}
GameHotDataCoordinator::afterUserCommitted($userId);
UserPushService::publish($userId, UserPushService::EVT_BET_ACCEPTED, [
'order_no' => $orderNo,
'period_no' => (string) $period->period_no,
'status' => 'accepted',
'balance_after' => $after,
'total_amount' => $totalAmount,
'current_streak' => $streakAtBet,
GameWebSocketEventBus::publish('bet.accepted', [
'user_id' => $userId,
'period_no' => $period->period_no,
'numbers' => $numbers,
'single_bet_amount' => $singleAmount,
'numbers_count' => count($numbers),
'total_amount' => $totalAmount,
'balance_after' => $after,
'accepted_at' => time(),
]);
GameWebSocketEventBus::publish('wallet.changed', [
'user_id' => $userId,
'balance_after' => $after,
'biz_type' => 'bet',
'changed_at' => time(),
]);
return $this->mobileSuccess([
'order_no' => $orderNo,
'period_no' => $period->period_no,
'status' => 'accepted',
'single_bet_amount' => $singleAmount,
'numbers_count' => count($numbers),
'locked_balance' => '0.00',
'balance_after' => $after,
'current_streak' => $streakAtBet,
@@ -295,6 +320,61 @@ class Game extends MobileBase
}
}
/**
* 自动托管(无推送模式):先落托管配置,实际执行仍由客户端轮询 current_status 驱动。
*/
public function autoSpin(Request $request): Response
{
$response = $this->initializeMobile($request);
if ($response !== null) {
return $response;
}
$action = trim((string) $request->post('action', 'start'));
if ($action === 'stop') {
return $this->mobileSuccess([
'status' => 'stopped',
'auto_mode' => false,
]);
}
$periodNo = trim((string) $request->post('period_no', ''));
$numbersRaw = $request->post('numbers', '');
$singleBetAmount = trim((string) ($request->post('single_bet_amount', $request->post('bet_amount', ''))));
$rounds = $this->intValue($request->post('rounds', 1));
if ($periodNo === '' || $singleBetAmount === '' || $rounds < 1) {
return $this->mobileError(1001, 'Missing parameters');
}
if (!is_numeric($singleBetAmount) || bccomp($singleBetAmount, '0', 2) <= 0) {
return $this->mobileError(1003, 'Invalid parameter value');
}
$numbers = $this->parseBetNumbersFromRequest($numbersRaw);
if ($numbers === []) {
return $this->mobileError(1003, 'Invalid parameter value');
}
$userIdValue = filter_var($this->auth->id ?? null, FILTER_VALIDATE_INT);
if ($userIdValue === false) {
$userIdValue = 0;
}
GameWebSocketEventBus::publish('auto.spin.progress', [
'user_id' => $userIdValue,
'period_no' => $periodNo,
'rounds' => $rounds,
'remaining_rounds' => $rounds,
'completed_rounds' => 0,
'status' => 'scheduled',
'server_time' => time(),
]);
return $this->mobileSuccess([
'status' => 'scheduled',
'auto_mode' => true,
'period_no' => $periodNo,
'numbers' => $numbers,
'single_bet_amount' => bcadd($singleBetAmount, '0', 2),
'rounds' => $rounds,
'remaining_rounds' => $rounds,
]);
}
public function betMyOrders(Request $request): Response
{
$response = $this->initializeMobile($request);