Files
chatroom/app/Jobs/AiBaccaratBetJob.php
lkddi 887fc5c7ef feat(百家乐): AI小班长改用AI接口预测路单走势下注
- 新增 BaccaratPredictionService,调用 AI 厂商(OpenAI 兼容协议)
  根据近期 20 局路单给出预测(大/小/豹子)
- AiBaccaratBetJob 优先使用 AI 预测结果;
  AI 不可用(超时/无配置)时自动回退本地路单统计决策
- 复用 AiProviderConfig 多厂商配置与故障转移逻辑
- AI 调用结果写入 ai_usage_logs(action=baccarat_predict)
2026-03-28 20:35:43 +08:00

161 lines
5.3 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* 文件功能AI小班长自动参与百家乐下注
*
* 在每局百家乐开启时延迟调度执行:
* 1. 检查是否存在连输休息惩罚1小时
* 2. 检查可用金币,确保留存底金
* 3. 调用 AI 接口预测路单走势决定下注方向AI 不可用时回退本地决策)
* 4. 提交下注
*
* @author ChatRoom Laravel
*
* @version 1.0.0
*/
namespace App\Jobs;
use App\Enums\CurrencySource;
use App\Models\BaccaratBet;
use App\Models\BaccaratRound;
use App\Models\GameConfig;
use App\Models\Sysparam;
use App\Models\User;
use App\Services\BaccaratPredictionService;
use App\Services\UserCurrencyService;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis;
class AiBaccaratBetJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function __construct(
public readonly BaccaratRound $round,
) {}
public function handle(UserCurrencyService $currency): void
{
// 1. 检查总开关与游戏开关
if (Sysparam::getValue('chatbot_enabled', '0') !== '1' || ! GameConfig::isEnabled('baccarat')) {
return;
}
$round = $this->round->fresh();
if (! $round || ! $round->isBettingOpen()) {
return;
}
$user = User::where('username', 'AI小班长')->first();
if (! $user) {
return;
}
// 2. 检查连输惩罚超时
if (Redis::exists('ai_baccarat_timeout')) {
return; // 还在禁赛期
}
// 3. 检查余额与限额
$config = GameConfig::forGame('baccarat')?->params ?? [];
$minBet = (int) ($config['min_bet'] ?? 100);
$maxBet = (int) ($config['max_bet'] ?? 50000);
// 至少保留 2000 金币底仓
$availableGold = ($user->jjb ?? 0) - 2000;
if ($availableGold < $minBet) {
return; // 资金不足以支撑最小下注
}
// 下注金额:可用余额的 2% ~ 5%,并在 min_bet 和 max_bet 之间
$percent = rand(2, 5) / 100.0;
$amount = (int) round($availableGold * $percent);
$amount = max($minBet, min($amount, $maxBet));
// 如果依然大于实际 jjb (保险兜底),则放弃
if ($amount > $user->jjb) {
return;
}
// 4. 获取近期路单(最多取 20 局)
$recentResults = BaccaratRound::query()
->where('status', 'settled')
->orderByDesc('id')
->limit(20)
->pluck('result')
->toArray();
// 5. 优先调用 AI 接口预测下注方向
$predictionService = app(BaccaratPredictionService::class);
$betType = $predictionService->predict($recentResults);
// AI 不可用时回退本地路单决策(保底逻辑)
if ($betType === null) {
$bigCount = count(array_filter($recentResults, fn (string $r) => $r === 'big'));
$smallCount = count(array_filter($recentResults, fn (string $r) => $r === 'small'));
$strategy = rand(1, 100);
if ($strategy <= 10) {
$betType = 'triple'; // 10% 概率博豹子
} elseif ($bigCount > $smallCount) {
// 大偏热70% 概率顺势买大30% 逆势买小
$betType = rand(1, 100) <= 70 ? 'big' : 'small';
} elseif ($smallCount > $bigCount) {
$betType = rand(1, 100) <= 70 ? 'small' : 'big';
} else {
$betType = rand(0, 1) ? 'big' : 'small';
}
}
// 5. 执行下注 (同 BaccaratController::bet 逻辑)
DB::transaction(function () use ($user, $round, $betType, $amount, $currency) {
// 幂等:同一局只能下一注
$existing = BaccaratBet::query()
->where('round_id', $round->id)
->where('user_id', $user->id)
->lockForUpdate()
->exists();
if ($existing) {
return;
}
// 扣除金币
$currency->change(
$user,
'gold',
-$amount,
CurrencySource::BACCARAT_BET,
"AI小班长百家乐 #{$round->id}".match ($betType) {
'big' => '大', 'small' => '小', default => '豹子'
},
);
// 写入下注记录
BaccaratBet::create([
'round_id' => $round->id,
'user_id' => $user->id,
'bet_type' => $betType,
'amount' => $amount,
'status' => 'pending',
]);
// 更新局次汇总统计
$field = 'total_bet_'.$betType;
$countField = 'bet_count_'.$betType;
$round->increment($field, $amount);
$round->increment($countField);
$round->increment('bet_count');
// 广播各选项的最新押注人数(让前台看到 AI 下注的人数增长)
event(new \App\Events\BaccaratPoolUpdated($round));
});
}
}