feat(baccarat): 实现百家乐实时下注人数统计功能
- 新增 BaccaratPoolUpdated 事件,用于通过 WebSocket 广播实时下注数据更新 - 增加数据库迁移以在 baccarat_rounds 表中添加对应的下注人数统计字段 - 更新 BaccaratRound 模型以及 BaccaratController,支持实时下注统计更新与 WebSocket 事件分发 - 更新前端 chat.js 以及 baccarat-panel.blade.php,利用 Alpine.js 和 Echo 接收事件并动态渲染 "大"、"小"、"豹子" 的实时下注计数
This commit is contained in:
66
app/Events/BaccaratPoolUpdated.php
Normal file
66
app/Events/BaccaratPoolUpdated.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 文件功能:百家乐押注人数实时广播事件
|
||||
*
|
||||
* 当有用户成功下注时,向房间内所有用户广播最新的
|
||||
* 各选项下注总人次,供前端实时更新面板。
|
||||
*
|
||||
* @author ChatRoom Laravel
|
||||
*
|
||||
* @version 1.0.0
|
||||
*/
|
||||
|
||||
namespace App\Events;
|
||||
|
||||
use App\Models\BaccaratRound;
|
||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||
use Illuminate\Broadcasting\PresenceChannel;
|
||||
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class BaccaratPoolUpdated implements ShouldBroadcastNow
|
||||
{
|
||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||
|
||||
/**
|
||||
* @param BaccaratRound $round 本局信息
|
||||
*/
|
||||
public function __construct(
|
||||
public readonly BaccaratRound $round,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 广播至房间公共频道。
|
||||
*
|
||||
* @return array<int, \Illuminate\Broadcasting\Channel>
|
||||
*/
|
||||
public function broadcastOn(): array
|
||||
{
|
||||
return [new PresenceChannel('room.1')];
|
||||
}
|
||||
|
||||
/**
|
||||
* 广播事件名(前端监听 .baccarat.pool_updated)。
|
||||
*/
|
||||
public function broadcastAs(): string
|
||||
{
|
||||
return 'baccarat.pool_updated';
|
||||
}
|
||||
|
||||
/**
|
||||
* 广播数据。
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function broadcastWith(): array
|
||||
{
|
||||
return [
|
||||
'round_id' => $this->round->id,
|
||||
'bet_count_big' => $this->round->bet_count_big,
|
||||
'bet_count_small' => $this->round->bet_count_small,
|
||||
'bet_count_triple' => $this->round->bet_count_triple,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -56,6 +56,9 @@ class BaccaratController extends Controller
|
||||
'total_bet_big' => $round->total_bet_big,
|
||||
'total_bet_small' => $round->total_bet_small,
|
||||
'total_bet_triple' => $round->total_bet_triple,
|
||||
'bet_count_big' => $round->bet_count_big,
|
||||
'bet_count_small' => $round->bet_count_small,
|
||||
'bet_count_triple' => $round->bet_count_triple,
|
||||
'my_bet' => $myBet ? [
|
||||
'bet_type' => $myBet->bet_type,
|
||||
'amount' => $myBet->amount,
|
||||
@@ -139,9 +142,14 @@ class BaccaratController extends Controller
|
||||
|
||||
// 更新局次汇总统计
|
||||
$field = 'total_bet_'.$data['bet_type'];
|
||||
$countField = 'bet_count_'.$data['bet_type'];
|
||||
$round->increment($field, $data['amount']);
|
||||
$round->increment($countField);
|
||||
$round->increment('bet_count');
|
||||
|
||||
// 广播各选项的最新押注人数
|
||||
event(new \App\Events\BaccaratPoolUpdated($round));
|
||||
|
||||
$betLabel = match ($data['bet_type']) {
|
||||
'big' => '大', 'small' => '小', default => '豹子'
|
||||
};
|
||||
|
||||
@@ -24,6 +24,7 @@ class BaccaratRound extends Model
|
||||
'bet_opens_at', 'bet_closes_at', 'settled_at',
|
||||
'total_bet_big', 'total_bet_small', 'total_bet_triple',
|
||||
'total_payout', 'bet_count',
|
||||
'bet_count_big', 'bet_count_small', 'bet_count_triple',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -44,6 +45,9 @@ class BaccaratRound extends Model
|
||||
'total_bet_triple' => 'integer',
|
||||
'total_payout' => 'integer',
|
||||
'bet_count' => 'integer',
|
||||
'bet_count_big' => 'integer',
|
||||
'bet_count_small' => 'integer',
|
||||
'bet_count_triple' => 'integer',
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 文件功能:在百家乐局次表增加下注人数分项统计
|
||||
*
|
||||
* 为支持前端实时显示“押大”、“押小”、“押豹子”的人数,增加记录各类型的押注人次。
|
||||
*/
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* 执行迁移。
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('baccarat_rounds', function (Blueprint $table) {
|
||||
$table->unsignedInteger('bet_count_big')->default(0)->after('bet_count')->comment('押大人数');
|
||||
$table->unsignedInteger('bet_count_small')->default(0)->after('bet_count_big')->comment('押小人数');
|
||||
$table->unsignedInteger('bet_count_triple')->default(0)->after('bet_count_small')->comment('押豹子人数');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 回滚迁移。
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('baccarat_rounds', function (Blueprint $table) {
|
||||
$table->dropColumn(['bet_count_big', 'bet_count_small', 'bet_count_triple']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -116,6 +116,12 @@ export function initChat(roomId) {
|
||||
new CustomEvent("chat:baccarat.opened", { detail: e }),
|
||||
);
|
||||
})
|
||||
.listen(".baccarat.pool_updated", (e) => {
|
||||
console.log("百家乐押注更新:", e);
|
||||
window.dispatchEvent(
|
||||
new CustomEvent("chat:baccarat.pool_updated", { detail: e }),
|
||||
);
|
||||
})
|
||||
.listen(".baccarat.settled", (e) => {
|
||||
console.log("百家乐结算:", e);
|
||||
window.dispatchEvent(
|
||||
|
||||
@@ -124,7 +124,10 @@
|
||||
'border:2px solid #1d4ed8; background:#1d4ed8; color:#fff; transform:scale(1.05); box-shadow:0 4px 14px rgba(29,78,216,.3);' :
|
||||
'border:2px solid #bfdbfe; background:#eff6ff; color:#1d4ed8;'">
|
||||
<div style="font-size:22px;">🔵</div>
|
||||
<div style="font-size:13px; margin-top:4px;">大</div>
|
||||
<div style="font-size:13px; margin-top:4px; display:flex; align-items:center; justify-content:center; gap:4px;">
|
||||
<span>大</span>
|
||||
<span x-show="betCountBig > 0" x-text="'👤 ' + betCountBig" style="font-size:11px; opacity:0.85; font-weight:normal;"></span>
|
||||
</div>
|
||||
<div style="font-size:10px; margin-top:2px; opacity:.75;">11~17点 • 1:1</div>
|
||||
</button>
|
||||
{{-- 小 --}}
|
||||
@@ -135,7 +138,10 @@
|
||||
'border:2px solid #d97706; background:#d97706; color:#fff; transform:scale(1.05); box-shadow:0 4px 14px rgba(217,119,6,.3);' :
|
||||
'border:2px solid #fde68a; background:#fffbeb; color:#b45309;'">
|
||||
<div style="font-size:22px;">🟡</div>
|
||||
<div style="font-size:13px; margin-top:4px;">小</div>
|
||||
<div style="font-size:13px; margin-top:4px; display:flex; align-items:center; justify-content:center; gap:4px;">
|
||||
<span>小</span>
|
||||
<span x-show="betCountSmall > 0" x-text="'👤 ' + betCountSmall" style="font-size:11px; opacity:0.85; font-weight:normal;"></span>
|
||||
</div>
|
||||
<div style="font-size:10px; margin-top:2px; opacity:.75;">4~10点 • 1:1</div>
|
||||
</button>
|
||||
{{-- 豹子 --}}
|
||||
@@ -146,7 +152,10 @@
|
||||
'border:2px solid #7c3aed; background:#7c3aed; color:#fff; transform:scale(1.05); box-shadow:0 4px 14px rgba(124,58,237,.3);' :
|
||||
'border:2px solid #ddd6fe; background:#f5f3ff; color:#7c3aed;'">
|
||||
<div style="font-size:22px;">💥</div>
|
||||
<div style="font-size:13px; margin-top:4px;">豹子</div>
|
||||
<div style="font-size:13px; margin-top:4px; display:flex; align-items:center; justify-content:center; gap:4px;">
|
||||
<span>豹子</span>
|
||||
<span x-show="betCountTriple > 0" x-text="'👤 ' + betCountTriple" style="font-size:11px; opacity:0.85; font-weight:normal;"></span>
|
||||
</div>
|
||||
<div style="font-size:10px; margin-top:2px; opacity:.75;">三同 • 1:24</div>
|
||||
</button>
|
||||
</div>
|
||||
@@ -165,20 +174,21 @@
|
||||
</template>
|
||||
</div>
|
||||
|
||||
{{-- 自定义金额 --}}
|
||||
<input type="number" x-model.number="betAmount" min="100" placeholder="自定义金额"
|
||||
{{-- 自定义金额 (带有 id 和 name 防止 Bitwarden 等密码管理插件检索 DOM 时因属性为 null 报错) --}}
|
||||
<input type="number" id="baccarat-bet-amount" name="bet_amount" x-model.number="betAmount" min="100" placeholder="自定义金额"
|
||||
autocomplete="off" data-bwignore="true" data-lpignore="true" data-1p-ignore="true"
|
||||
style="width:100%; background:#f6faff; border:1.5px solid #d0e4f5;
|
||||
border-radius:8px; padding:8px 12px; color:#225588; font-size:13px;
|
||||
box-sizing:border-box; margin-bottom:10px;"
|
||||
x-on:focus="$event.target.select()">
|
||||
|
||||
{{-- 下注按钮 --}}
|
||||
<button x-on:click="submitBet()" :disabled="!selectedType || betAmount < 100 || submitting"
|
||||
:style="(!selectedType || betAmount < 100 || submitting) ?
|
||||
<button x-on:click="submitBet()" :disabled="!roundId || !selectedType || betAmount < 100 || submitting"
|
||||
:style="(!roundId || !selectedType || betAmount < 100 || submitting) ?
|
||||
'display:block; width:100%; border:none; border-radius:12px; padding:13px 0; font-size:14px; font-weight:bold; cursor:not-allowed; transition:all .2s; background:#e0e8f0; color:#99a8b8; box-shadow:none; font-family:inherit;' :
|
||||
'display:block; width:100%; border:none; border-radius:12px; padding:13px 0; font-size:14px; font-weight:bold; cursor:pointer; transition:all .2s; background:linear-gradient(135deg,#336699,#5a8fc0); color:#fff; box-shadow:0 4px 14px rgba(51,102,153,.3); font-family:inherit;'">
|
||||
<span
|
||||
x-text="submitting ? '提交中…' : (!selectedType ? '请先选择大/小/豹子' : '🎲 押注「' + betTypeLabel(selectedType) + '」 ' + Number(betAmount).toLocaleString() + ' 金币')"></span>
|
||||
x-text="!roundId ? '尚未开局' : (submitting ? '提交中…' : (!selectedType ? '请先选择大/小/豹子' : '🎲 押注「' + betTypeLabel(selectedType) + '」 ' + Number(betAmount).toLocaleString() + ' 金币'))"></span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -408,6 +418,11 @@
|
||||
totalBetSmall: 0,
|
||||
totalBetTriple: 0,
|
||||
|
||||
// 押注人数统计
|
||||
betCountBig: 0,
|
||||
betCountSmall: 0,
|
||||
betCountTriple: 0,
|
||||
|
||||
// 本人下注
|
||||
myBet: false,
|
||||
myBetType: '',
|
||||
@@ -448,6 +463,9 @@
|
||||
this.settledDice = [];
|
||||
this.selectedType = '';
|
||||
this.betAmount = 100;
|
||||
this.betCountBig = 0;
|
||||
this.betCountSmall = 0;
|
||||
this.betCountTriple = 0;
|
||||
this.show = true;
|
||||
|
||||
this.loadCurrentRound();
|
||||
@@ -466,6 +484,9 @@
|
||||
this.totalBetBig = data.round.total_bet_big;
|
||||
this.totalBetSmall = data.round.total_bet_small;
|
||||
this.totalBetTriple = data.round.total_bet_triple;
|
||||
this.betCountBig = data.round.bet_count_big;
|
||||
this.betCountSmall = data.round.bet_count_small;
|
||||
this.betCountTriple = data.round.bet_count_triple;
|
||||
if (data.round.my_bet) {
|
||||
this.myBet = true;
|
||||
this.myBetType = data.round.my_bet.bet_type;
|
||||
@@ -493,7 +514,7 @@
|
||||
* 提交下注
|
||||
*/
|
||||
async submitBet() {
|
||||
if (!this.selectedType || this.betAmount < 100 || this.submitting) return;
|
||||
if (!this.roundId || !this.selectedType || this.betAmount < 100 || this.submitting) return;
|
||||
this.submitting = true;
|
||||
|
||||
try {
|
||||
@@ -512,11 +533,15 @@
|
||||
});
|
||||
const data = await res.json();
|
||||
|
||||
if (data.ok) {
|
||||
if (res.ok && data.ok) {
|
||||
this.myBet = true;
|
||||
this.myBetType = data.bet_type;
|
||||
this.myBetAmount = data.amount;
|
||||
window.chatDialog?.alert(data.message, '下注成功', '#336699');
|
||||
} else if (res.status === 422 && data.errors) {
|
||||
// 取出第一条 Laravel 验证失败原因
|
||||
const firstError = Object.values(data.errors)[0][0];
|
||||
window.chatDialog?.alert(firstError, '下注验证失败', '#ef4444');
|
||||
} else {
|
||||
window.chatDialog?.alert(data.message || '下注失败', '提示', '#ef4444');
|
||||
}
|
||||
@@ -627,6 +652,21 @@
|
||||
if (panel) Alpine.$data(panel).showResult(e.detail);
|
||||
});
|
||||
|
||||
/** 收到下注池更新:更新押注人数 */
|
||||
window.addEventListener('chat:baccarat.pool_updated', (e) => {
|
||||
const panel = document.getElementById('baccarat-panel');
|
||||
if (panel) {
|
||||
const pd = Alpine.$data(panel);
|
||||
const data = e.detail;
|
||||
// 判断 round_id 是否一致
|
||||
if (pd.roundId === data.round_id) {
|
||||
pd.betCountBig = data.bet_count_big;
|
||||
pd.betCountSmall = data.bet_count_small;
|
||||
pd.betCountTriple = data.bet_count_triple;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/** 页面加载时:检查是否有进行中的局,有则自动恢复面板 */
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
try {
|
||||
@@ -656,6 +696,9 @@
|
||||
panelData.totalBetBig = round.total_bet_big;
|
||||
panelData.totalBetSmall = round.total_bet_small;
|
||||
panelData.totalBetTriple = round.total_bet_triple;
|
||||
panelData.betCountBig = round.bet_count_big;
|
||||
panelData.betCountSmall = round.bet_count_small;
|
||||
panelData.betCountTriple = round.bet_count_triple;
|
||||
|
||||
if (round.my_bet) {
|
||||
panelData.myBet = true;
|
||||
|
||||
Reference in New Issue
Block a user