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 */ 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 */ 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; } }