publishedCompetition(); $this->platformAdmin('admin', 'Admin123456'); $this->postJson('/api/v1/manage/auth/login', [ 'username' => 'admin', 'password' => 'Admin123456', 'competition_slug' => $competition->slug, ])->assertOk() ->assertJsonPath('kind', 'platform') ->assertJsonPath('competition.slug', $competition->slug); } public function test_competition_admin_can_login_only_to_own_event(): void { $competition = $this->publishedCompetition(); $other = $this->publishedCompetition('other-2026'); $this->competitionAdmin($competition, 'hsxt_admin', 'Pass123456'); $this->postJson('/api/v1/manage/auth/login', [ 'username' => 'hsxt_admin', 'password' => 'Pass123456', 'competition_slug' => $competition->slug, ])->assertOk() ->assertJsonPath('kind', 'competition'); $this->postJson('/api/v1/manage/auth/login', [ 'username' => 'hsxt_admin', 'password' => 'Pass123456', 'competition_slug' => $other->slug, ])->assertUnprocessable(); } public function test_competition_admin_cannot_access_platform_admin_users(): void { $competition = $this->publishedCompetition(); Sanctum::actingAs($this->competitionAdmin($competition)); $this->getJson('/api/v1/admin/admin-users')->assertForbidden(); $this->getJson('/api/v1/admin/competitions')->assertForbidden(); } public function test_competition_admin_can_manage_own_audience_but_not_other_event(): void { $competition = $this->publishedCompetition(); $other = $this->publishedCompetition('other-2026'); Sanctum::actingAs($this->competitionAdmin($competition)); $this->getJson("/api/v1/admin/competitions/{$competition->id}/audience-registrations") ->assertOk(); $this->getJson("/api/v1/admin/competitions/{$other->id}/audience-registrations") ->assertForbidden(); } public function test_platform_admin_can_crud_competition_admins(): void { Sanctum::actingAs($this->platformAdmin()); $competition = $this->publishedCompetition(); $this->postJson("/api/v1/admin/competitions/{$competition->id}/admins", [ 'username' => 'event_admin', 'name' => '赛事管理员', 'password' => 'Pass123456', 'status' => 'active', ])->assertCreated() ->assertJsonPath('username', 'event_admin'); $this->assertDatabaseHas('competition_admins', [ 'competition_id' => $competition->id, 'username' => 'event_admin', ]); } public function test_competition_admin_can_checkin(): void { $competition = $this->publishedCompetition(); $session = $this->openSession($competition); $user = User::query()->create(['mobile' => '13800138200']); $registration = AudienceRegistration::query()->create([ 'competition_id' => $competition->id, 'audience_session_id' => $session->id, 'user_id' => $user->id, 'name' => '现场观众', 'phone' => '13800138200', 'purpose' => '项目交流', 'registered_at' => now(), ]); Sanctum::actingAs($this->competitionAdmin($competition)); $this->postJson("/api/v1/admin/competitions/{$competition->id}/audience-checkin", [ 'ticket_code' => $registration->ticket_code, ])->assertOk() ->assertJsonPath('already_checked_in', false); $this->assertNotNull($registration->fresh()->checked_in_by_competition_admin_id); } private function publishedCompetition(string $slug = 'husu-2026'): Competition { return Competition::query()->create([ 'slug' => $slug, 'name' => '沪苏协同新兴产业科创大赛', 'published' => true, 'status' => 'signup_open', ]); } /** * @param array $overrides */ private function openSession(Competition $competition, array $overrides = []): AudienceSession { return AudienceSession::query()->create(array_merge([ 'competition_id' => $competition->id, 'code' => 'shanghai-preliminary', 'name' => '上海预赛路演', 'event_date_text' => '2026年9月18日(星期五)', 'address' => '上海', 'capacity' => 300, 'signup_start_at' => Carbon::parse('2026-08-10 00:00:00', BizDateTime::TIMEZONE), 'signup_end_at' => Carbon::parse('2026-09-10 23:59:59', BizDateTime::TIMEZONE), 'is_final' => false, 'sort' => 1, 'enabled' => true, ], $overrides)); } private function platformAdmin(string $username = 'platform', string $password = 'Admin123456'): AdminUser { return AdminUser::query()->create([ 'username' => $username, 'password_hash' => $password, 'name' => '平台管理员', 'status' => 'active', ]); } private function competitionAdmin(Competition $competition, string $username = 'event_admin', string $password = 'Pass123456'): CompetitionAdmin { return CompetitionAdmin::query()->create([ 'competition_id' => $competition->id, 'username' => $username, 'password_hash' => $password, 'name' => '赛事管理员', 'status' => 'active', ]); } }