where('owner_type', 'agent') ->where('owner_id', $agentNodeId) ->where('status', 'overdue') ->where('unpaid_amount', '>', 0) ->exists(); } public static function agentHasSevereOverdueBills(int $agentNodeId, int $days = 7): bool { if ($agentNodeId <= 0) { return false; } $cutoff = now()->subDays($days); return DB::table('settlement_bills') ->where('owner_type', 'agent') ->where('owner_id', $agentNodeId) ->where('status', 'overdue') ->where('unpaid_amount', '>', 0) ->where('updated_at', '<', $cutoff) ->exists(); } public static function agentLineHasSevereOverdueBills(int $agentNodeId, int $days = 7): bool { if ($agentNodeId <= 0) { return false; } $agent = DB::table('agent_nodes')->where('id', $agentNodeId)->first(); if ($agent === null) { return false; } // 获取该代理的所有祖先节点ID(包括自己) $path = (string) $agent->path; if ($path === '') { // 根节点,只检查自己 $ancestorIds = [$agentNodeId]; } else { // 解析 path 获取所有祖先ID $parts = explode('/', trim($path, '/')); $ancestorIds = array_map('intval', $parts); $ancestorIds[] = $agentNodeId; } $cutoff = now()->subDays($days); return DB::table('settlement_bills') ->where('owner_type', 'agent') ->where('status', 'overdue') ->where('unpaid_amount', '>', 0) ->where('updated_at', '<', $cutoff) ->whereIn('owner_id', $ancestorIds) ->exists(); } public static function assertAgentMayGrantCredit(int $agentNodeId): void { if (self::agentHasOverdueBills($agentNodeId)) { throw \Illuminate\Validation\ValidationException::withMessages([ 'credit' => ['agent_overdue'], ]); } } public static function assertAgentLineMayPlaceBet(int $agentNodeId, ?int $severeOverdueDays = null): void { $days = $severeOverdueDays ?? config('agent_line_defaults.overdue.severe_days_threshold', 7); if (self::agentLineHasSevereOverdueBills($agentNodeId, $days)) { throw \Illuminate\Validation\ValidationException::withMessages([ 'credit' => ['agent_line_severe_overdue'], ]); } } public static function parentHasOverdueBills(int $agentNodeId): bool { if ($agentNodeId <= 0) { return false; } $agent = DB::table('agent_nodes')->where('id', $agentNodeId)->first(); if ($agent === null || $agent->parent_id === null) { return false; } return DB::table('settlement_bills') ->where('owner_type', 'agent') ->where('owner_id', $agent->parent_id) ->where('status', 'overdue') ->where('unpaid_amount', '>', 0) ->exists(); } public static function assertMayOperateWhenParentOverdue(int $agentNodeId): void { if (! config('agent_line_defaults.overdue.cascade_freeze_on_parent_overdue', false)) { return; } if (self::parentHasOverdueBills($agentNodeId)) { throw \Illuminate\Validation\ValidationException::withMessages([ 'parent_id' => ['parent_overdue'], ]); } } }