$tenantIds * @param array $backupHealthByTenant * @param array> $recoveryEvidenceByTenant * @return array{ * rows: array, * recovery_evidence: array * }>, * summaries: array * } */ public function resolveMany( int $workspaceId, array $tenantIds, array $backupHealthByTenant = [], array $recoveryEvidenceByTenant = [], ): array { $tenantIds = array_values(array_unique(array_map(static fn (int|string $tenantId): int => (int) $tenantId, $tenantIds))); $emptySummary = [ PortfolioArrivalContextToken::FAMILY_BACKUP_HEALTH => $this->emptySummary(PortfolioArrivalContextToken::FAMILY_BACKUP_HEALTH), PortfolioArrivalContextToken::FAMILY_RECOVERY_EVIDENCE => $this->emptySummary(PortfolioArrivalContextToken::FAMILY_RECOVERY_EVIDENCE), ]; if ($workspaceId <= 0 || $tenantIds === []) { return [ 'rows' => [], 'summaries' => $emptySummary, ]; } $activeReviews = TenantTriageReview::query() ->with('reviewer:id,name,email') ->forWorkspace($workspaceId) ->whereIn('tenant_id', $tenantIds) ->active() ->orderByDesc('reviewed_at') ->orderByDesc('id') ->get() ->groupBy([ 'tenant_id', 'concern_family', ]); $rows = []; $summaries = $emptySummary; foreach ($tenantIds as $tenantId) { $rows[$tenantId] = [ PortfolioArrivalContextToken::FAMILY_BACKUP_HEALTH => $this->resolveFamily( tenantId: $tenantId, concernFamily: PortfolioArrivalContextToken::FAMILY_BACKUP_HEALTH, currentConcern: $this->fingerprints->forBackupHealth($backupHealthByTenant[$tenantId] ?? null), activeReview: $activeReviews[$tenantId][PortfolioArrivalContextToken::FAMILY_BACKUP_HEALTH][0] ?? null, ), PortfolioArrivalContextToken::FAMILY_RECOVERY_EVIDENCE => $this->resolveFamily( tenantId: $tenantId, concernFamily: PortfolioArrivalContextToken::FAMILY_RECOVERY_EVIDENCE, currentConcern: $this->fingerprints->forRecoveryEvidence($recoveryEvidenceByTenant[$tenantId] ?? null), activeReview: $activeReviews[$tenantId][PortfolioArrivalContextToken::FAMILY_RECOVERY_EVIDENCE][0] ?? null, ), ]; foreach ($rows[$tenantId] as $family => $row) { if (($row['current_concern_present'] ?? false) !== true) { continue; } $summaries[$family]['affected_total']++; match ($row['derived_state']) { TenantTriageReview::STATE_REVIEWED => $summaries[$family]['reviewed_count']++, TenantTriageReview::STATE_FOLLOW_UP_NEEDED => $summaries[$family]['follow_up_needed_count']++, TenantTriageReview::DERIVED_STATE_CHANGED_SINCE_REVIEW => $summaries[$family]['changed_since_review_count']++, default => $summaries[$family]['not_reviewed_count']++, }; } } return [ 'rows' => $rows, 'summaries' => $summaries, ]; } /** * @param array{ * concern_family: string, * concern_state: string, * fingerprint: string, * snapshot: array * }|null $currentConcern * @return array */ private function resolveFamily( int $tenantId, string $concernFamily, ?array $currentConcern, ?TenantTriageReview $activeReview, ): array { $reviewerName = null; if ($activeReview?->reviewer !== null) { $reviewerName = filled($activeReview->reviewer->name) ? (string) $activeReview->reviewer->name : (filled($activeReview->reviewer->email) ? (string) $activeReview->reviewer->email : null); } if ($currentConcern === null) { return [ 'tenant_id' => $tenantId, 'concern_family' => $concernFamily, 'current_concern_present' => false, 'current_state' => null, 'current_fingerprint' => null, 'review_fingerprint' => $activeReview?->review_fingerprint, 'derived_state' => null, 'reviewed_at' => $activeReview?->reviewed_at, 'reviewed_by_user_id' => $activeReview?->reviewed_by_user_id, 'reviewed_by_user_name' => $reviewerName, 'current_snapshot' => null, 'review_snapshot' => is_array($activeReview?->review_snapshot) ? $activeReview->review_snapshot : null, ]; } $derivedState = match (true) { ! $activeReview instanceof TenantTriageReview => TenantTriageReview::DERIVED_STATE_NOT_REVIEWED, $activeReview->review_fingerprint !== $currentConcern['fingerprint'] => TenantTriageReview::DERIVED_STATE_CHANGED_SINCE_REVIEW, default => (string) $activeReview->current_state, }; return [ 'tenant_id' => $tenantId, 'concern_family' => $concernFamily, 'current_concern_present' => true, 'current_state' => $currentConcern['concern_state'], 'current_fingerprint' => $currentConcern['fingerprint'], 'review_fingerprint' => $activeReview?->review_fingerprint, 'derived_state' => $derivedState, 'reviewed_at' => $activeReview?->reviewed_at, 'reviewed_by_user_id' => $activeReview?->reviewed_by_user_id, 'reviewed_by_user_name' => $reviewerName, 'current_snapshot' => $currentConcern['snapshot'], 'review_snapshot' => is_array($activeReview?->review_snapshot) ? $activeReview->review_snapshot : $currentConcern['snapshot'], ]; } /** * @return array{ * concern_family: string, * affected_total: int, * reviewed_count: int, * follow_up_needed_count: int, * changed_since_review_count: int, * not_reviewed_count: int * } */ private function emptySummary(string $concernFamily): array { return [ 'concern_family' => $concernFamily, 'affected_total' => 0, 'reviewed_count' => 0, 'follow_up_needed_count' => 0, 'changed_since_review_count' => 0, 'not_reviewed_count' => 0, ]; } }