Files
lotteryLaravel/app/Services/Agent/AgentUsedCreditSyncService.php
kang f1f68d09d3
Some checks failed
lotterLaravel CI / test (push) Has been cancelled
lotterLaravel E2E / e2e-api (push) Has been cancelled
feat(credit): 实时同步代理已用额度,覆盖玩家下注/结算全流程
- PlayerCreditService 在下注占用、输赢结算、撤单释放等关键节点触发 AgentUsedCreditSyncService
- AgentProfileService::present 与 AgentDashboardOverviewBuilder 查询前主动同步 used_credit
- 确保代理链 used_credit(团队已用风险)实时反映下级玩家信用占用状态
2026-06-30 15:15:15 +08:00

115 lines
3.0 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
namespace App\Services\Agent;
use App\Models\AgentNode;
use App\Models\AgentProfile;
use App\Support\PlayerFundingMode;
use Illuminate\Support\Facades\DB;
/**
* 同步代理「团队当前已用风险」agent_profiles.used_credit
* 聚合该代理子树下所有信用玩家的 player_credit_accounts.used_credit + frozen_credit。
*
* 不参与 available_credit 计算available = credit_limit - allocated_credit
* 仅作风控展示指标。
*/
final class AgentUsedCreditSyncService
{
/**
* 为指定代理节点重算 used_credit。
*/
public function syncForAgent(AgentNode $agent): void
{
$profile = AgentProfile::query()->where('agent_node_id', $agent->id)->first();
if ($profile === null) {
return;
}
$expected = $this->calculateUsedCredit($agent);
if ((int) $profile->used_credit === $expected) {
return;
}
$profile->used_credit = $expected;
$profile->save();
}
public function syncForAgentId(int $agentNodeId): void
{
$agent = AgentNode::query()->find($agentNodeId);
if ($agent === null) {
return;
}
$this->syncForAgent($agent);
}
/**
* 玩家信用变化后:同步直属代理及所有祖先代理的 used_credit。
*/
public function syncForPlayerAgentChain(int $playerAgentNodeId): void
{
if ($playerAgentNodeId <= 0) {
return;
}
$agent = AgentNode::query()->find($playerAgentNodeId);
if ($agent === null) {
return;
}
$ancestorIds = $this->ancestorAndSelfIds($agent);
foreach ($ancestorIds as $nodeId) {
$this->syncForAgentId($nodeId);
}
}
/**
* 计算代理子树下所有信用玩家的 used_credit + frozen_credit 之和。
*/
public function calculateUsedCredit(AgentNode $agent): int
{
$subtreeIds = $this->subtreeNodeIds($agent);
if ($subtreeIds === []) {
return 0;
}
return (int) DB::table('player_credit_accounts as pca')
->join('players as p', 'p.id', '=', 'pca.player_id')
->whereIn('p.agent_node_id', $subtreeIds)
->where('p.funding_mode', PlayerFundingMode::CREDIT)
->sum(DB::raw('pca.used_credit + pca.frozen_credit'));
}
/**
* @return list<int>
*/
private function subtreeNodeIds(AgentNode $agent): array
{
return AgentNode::query()
->where('path', 'like', $agent->path.'%')
->pluck('id')
->map(static fn ($id): int => (int) $id)
->all();
}
/**
* @return list<int>
*/
private function ancestorAndSelfIds(AgentNode $agent): array
{
$path = (string) $agent->path;
if ($path === '') {
return [(int) $agent->id];
}
$parts = explode('/', trim($path, '/'));
$ancestorIds = array_map('intval', $parts);
$ancestorIds[] = (int) $agent->id;
return $ancestorIds;
}
}