You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
163 lines
6.0 KiB
163 lines
6.0 KiB
<?php
|
|
|
|
namespace App\Http\Controllers\Api\Admin;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Http\Requests\Admin\StoreReviewerScopeRequest;
|
|
use App\Models\Competition;
|
|
use App\Models\CompetitionTrack;
|
|
use App\Models\ReviewerScope;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
|
|
class ReviewerScopeController extends Controller
|
|
{
|
|
/**
|
|
* 各赛道已配置评审员人数(与原型「赛道 tab 角标」对齐)。
|
|
*/
|
|
public function countsByTrack(Competition $competition): JsonResponse
|
|
{
|
|
/** @var array<string, int|string> $pairs */
|
|
$pairs = ReviewerScope::query()
|
|
->where('competition_id', $competition->id)
|
|
->selectRaw('track_code, COUNT(*) as c')
|
|
->groupBy('track_code')
|
|
->pluck('c', 'track_code')
|
|
->all();
|
|
|
|
$out = [];
|
|
foreach ($pairs as $code => $n) {
|
|
$out[$code] = (int) $n;
|
|
}
|
|
|
|
return response()->json(['data' => $out]);
|
|
}
|
|
|
|
public function index(Request $request): JsonResponse
|
|
{
|
|
$validated = $request->validate([
|
|
'competition_id' => ['required', 'integer', 'exists:competitions,id'],
|
|
'reviewer_id' => ['sometimes', 'nullable', 'integer', 'exists:reviewers,id'],
|
|
'track_code' => ['sometimes', 'nullable', 'string', 'max:64'],
|
|
'per_page' => ['sometimes', 'integer', 'min:1', 'max:500'],
|
|
'page' => ['sometimes', 'integer', 'min:1'],
|
|
]);
|
|
|
|
$perPage = min((int) ($validated['per_page'] ?? 15), 500);
|
|
$competitionId = (int) $validated['competition_id'];
|
|
|
|
$q = ReviewerScope::query()
|
|
->with([
|
|
'reviewer:id,mobile,username,name,status,password_hash',
|
|
'competition:id,slug,name',
|
|
])
|
|
->where('competition_id', $competitionId)
|
|
->orderByDesc('id');
|
|
|
|
if (! empty($validated['reviewer_id'])) {
|
|
$q->where('reviewer_id', (int) $validated['reviewer_id']);
|
|
}
|
|
|
|
if (! empty($validated['track_code'])) {
|
|
$q->where('track_code', $validated['track_code']);
|
|
}
|
|
|
|
$paginator = $q->paginate($perPage);
|
|
|
|
$codes = $paginator->getCollection()->pluck('track_code')->unique()->filter()->values()->all();
|
|
$tracksByCode = CompetitionTrack::query()
|
|
->where('competition_id', $competitionId)
|
|
->whereIn('track_code', $codes)
|
|
->get()
|
|
->keyBy('track_code');
|
|
|
|
$paginator->getCollection()->transform(function (ReviewerScope $s) use ($tracksByCode): array {
|
|
$track = $tracksByCode->get($s->track_code);
|
|
|
|
return [
|
|
'id' => $s->id,
|
|
'reviewer_id' => $s->reviewer_id,
|
|
'competition_id' => $s->competition_id,
|
|
'track_code' => $s->track_code,
|
|
'track_title' => $track?->title,
|
|
'created_at' => $s->created_at?->toIso8601String(),
|
|
'reviewer' => $s->reviewer !== null ? [
|
|
'id' => $s->reviewer->id,
|
|
'mobile' => $s->reviewer->mobile,
|
|
'username' => $s->reviewer->username,
|
|
'name' => $s->reviewer->name,
|
|
'status' => $s->reviewer->status,
|
|
'password_display' => ($s->reviewer->password_hash !== null && $s->reviewer->password_hash !== '')
|
|
? '········'
|
|
: '—',
|
|
] : null,
|
|
'competition' => $s->competition !== null ? [
|
|
'id' => $s->competition->id,
|
|
'slug' => $s->competition->slug,
|
|
'name' => $s->competition->name,
|
|
] : null,
|
|
];
|
|
});
|
|
|
|
return response()->json($paginator);
|
|
}
|
|
|
|
public function store(StoreReviewerScopeRequest $request): JsonResponse
|
|
{
|
|
/** @var array{reviewer_id: int|string, competition_id: int|string, track_code: string} $validated */
|
|
$validated = $request->validated();
|
|
|
|
$scope = ReviewerScope::query()->create([
|
|
'reviewer_id' => (int) $validated['reviewer_id'],
|
|
'competition_id' => (int) $validated['competition_id'],
|
|
'track_code' => $validated['track_code'],
|
|
]);
|
|
|
|
$scope->load(['reviewer:id,mobile,username,name,status,password_hash', 'competition:id,slug,name']);
|
|
$track = CompetitionTrack::query()
|
|
->where('competition_id', $scope->competition_id)
|
|
->where('track_code', $scope->track_code)
|
|
->first();
|
|
|
|
return response()->json($this->singleRowPayload($scope, $track?->title), Response::HTTP_CREATED);
|
|
}
|
|
|
|
public function destroy(ReviewerScope $reviewer_scope): Response
|
|
{
|
|
$reviewer_scope->delete();
|
|
|
|
return response()->noContent();
|
|
}
|
|
|
|
/**
|
|
* @return array<string, mixed>
|
|
*/
|
|
private function singleRowPayload(ReviewerScope $s, ?string $trackTitle): array
|
|
{
|
|
return [
|
|
'id' => $s->id,
|
|
'reviewer_id' => $s->reviewer_id,
|
|
'competition_id' => $s->competition_id,
|
|
'track_code' => $s->track_code,
|
|
'track_title' => $trackTitle,
|
|
'created_at' => $s->created_at?->toIso8601String(),
|
|
'reviewer' => $s->reviewer !== null ? [
|
|
'id' => $s->reviewer->id,
|
|
'mobile' => $s->reviewer->mobile,
|
|
'username' => $s->reviewer->username,
|
|
'name' => $s->reviewer->name,
|
|
'status' => $s->reviewer->status,
|
|
'password_display' => ($s->reviewer->password_hash !== null && $s->reviewer->password_hash !== '')
|
|
? '········'
|
|
: '—',
|
|
] : null,
|
|
'competition' => $s->competition !== null ? [
|
|
'id' => $s->competition->id,
|
|
'slug' => $s->competition->slug,
|
|
'name' => $s->competition->name,
|
|
] : null,
|
|
];
|
|
}
|
|
}
|