$globalReviewerIds * @param array> $reviewerIdsByLocation * @param array $reviewers */ public function __construct( private readonly array $globalReviewerIds, private readonly array $reviewerIdsByLocation, private readonly array $reviewers, ) {} public static function forCompetition(int $competitionId): self { $global = []; $byLocation = []; $reviewers = []; ReviewerScope::query() ->with('reviewer:id,name') ->where('competition_id', $competitionId) ->orderBy('id') ->get() ->each(function (ReviewerScope $scope) use (&$global, &$byLocation, &$reviewers): void { $reviewerId = (int) $scope->reviewer_id; if ($reviewerId <= 0) { return; } $name = trim((string) ($scope->reviewer?->name ?? '')); if ($name === '') { $name = '评委#'.$reviewerId; } $reviewers[$reviewerId] = ['id' => $reviewerId, 'name' => $name]; $raw = trim((string) ($scope->event_location ?? '')); if ($raw === '') { $global[$reviewerId] = true; return; } $normalized = SignupOptionCatalog::normalize('event_location', $raw) ?: $raw; $byLocation[$normalized][$reviewerId] = true; }); return new self($global, $byLocation, $reviewers); } /** * @return list */ public function reviewerIdsFor(Application $app): array { $ids = $this->globalReviewerIds; $raw = trim((string) ($app->event_location ?? '')); if ($raw === '') { if ($ids === []) { foreach ($this->reviewers as $id => $_) { $ids[$id] = true; } } } else { $normalized = SignupOptionCatalog::normalize('event_location', $raw) ?: $raw; foreach ($this->reviewerIdsByLocation[$normalized] ?? [] as $id => $_) { $ids[$id] = true; } } $out = array_map('intval', array_keys($ids)); sort($out); return $out; } /** * @return list */ public function reviewersFor(Application $app): array { $out = []; foreach ($this->reviewerIdsFor($app) as $id) { $out[] = $this->reviewers[$id] ?? ['id' => $id, 'name' => '评委#'.$id]; } return $out; } /** * @param list $scoredReviewerIds * @return array{required: int, completed: int, fully_completed: bool, progress: string} */ public function progress(Application $app, array $scoredReviewerIds = []): array { $assigned = $this->reviewerIdsFor($app); $required = count($assigned); $scoredSet = []; foreach ($scoredReviewerIds as $id) { $scoredSet[(int) $id] = true; } $completed = 0; foreach ($assigned as $id) { if (isset($scoredSet[$id])) { $completed++; } } return [ 'required' => $required, 'completed' => $completed, 'fully_completed' => $required > 0 && $completed >= $required, 'progress' => $required > 0 ? ($completed.'/'.$required) : '-', ]; } }