userWithPermissions(['tickets.use']); $category = TicketCategory::query()->create(['name' => '账号问题', 'is_active' => true]); $create = $this->actingAs($user, 'api')->postJson('/tickets', [ 'ticket_category_id' => $category->id, 'title' => '无法登录', 'content' => '登录失败', ]); $create ->assertCreated() ->assertJsonPath('data.title', '无法登录') ->assertJsonPath('data.messages.0.content', '登录失败'); $ticketId = (int) $create->json('data.id'); $reply = $this->actingAs($user, 'api')->postJson('/tickets/'.$ticketId.'/messages', [ 'content' => '补充信息', ]); $reply ->assertCreated() ->assertJsonPath('data.sender_type', 'user'); $detail = $this->actingAs($user, 'api')->getJson('/tickets/'.$ticketId); $detail ->assertOk() ->assertJsonPath('data.messages.1.content', '补充信息'); } public function test_user_cannot_view_other_users_ticket(): void { $owner = $this->userWithPermissions(['tickets.use']); $other = $this->userWithPermissions(['tickets.use']); $category = TicketCategory::query()->create(['name' => '资源申请', 'is_active' => true]); $ticket = Ticket::query()->create([ 'user_id' => $owner->id, 'ticket_category_id' => $category->id, 'title' => '申请资源', 'content' => '需要资源', 'status' => Ticket::StatusOpen, ]); $response = $this->actingAs($other, 'api')->getJson('/tickets/'.$ticket->id); $response->assertStatus(403); } public function test_admin_can_manage_categories_update_and_reply_ticket(): void { $admin = $this->userWithPermissions(['platform.tickets.view', 'platform.tickets.manage']); $owner = $this->userWithPermissions(['tickets.use']); $category = TicketCategory::query()->create(['name' => '旧分类', 'is_active' => true]); $ticket = Ticket::query()->create([ 'user_id' => $owner->id, 'ticket_category_id' => $category->id, 'title' => '问题', 'content' => '内容', 'status' => Ticket::StatusOpen, ]); $createdCategory = $this->actingAs($admin, 'api')->postJson('/ticket-categories', [ 'name' => '新分类', 'description' => '分类说明', 'is_active' => true, ]); $createdCategory->assertCreated()->assertJsonPath('data.name', '新分类'); $update = $this->actingAs($admin, 'api')->putJson('/tickets/'.$ticket->id, [ 'status' => Ticket::StatusResolved, 'ticket_category_id' => $createdCategory->json('data.id'), ]); $update ->assertOk() ->assertJsonPath('data.status', Ticket::StatusResolved); $reply = $this->actingAs($admin, 'api')->postJson('/tickets/'.$ticket->id.'/messages', [ 'content' => '已处理', ]); $reply ->assertCreated() ->assertJsonPath('data.sender_type', 'admin'); $list = $this->actingAs($admin, 'api')->getJson('/tickets'); $list->assertOk(); $this->assertContains($ticket->id, collect($list->json('data.data'))->pluck('id')->all()); } public function test_admin_can_manage_nested_ticket_categories(): void { $admin = $this->userWithPermissions(['platform.tickets.manage']); $parent = TicketCategory::query()->create(['name' => '账号问题', 'is_active' => true]); $created = $this->actingAs($admin, 'api')->postJson('/ticket-categories', [ 'parent_id' => $parent->id, 'name' => '登录异常', 'description' => '登录相关问题', 'is_active' => true, ]); $created ->assertCreated() ->assertJsonPath('data.parent_id', $parent->id) ->assertJsonMissingPath('data.sort_order'); $list = $this->actingAs($admin, 'api')->getJson('/ticket-categories'); $list->assertOk(); $parentNode = collect($list->json('tree'))->firstWhere('id', $parent->id); $this->assertSame('账号问题', $parentNode['name']); $this->assertSame('登录异常', $parentNode['children'][0]['name']); } public function test_user_without_ticket_permission_is_forbidden(): void { $user = User::factory()->create(); $response = $this->actingAs($user, 'api')->getJson('/tickets'); $response->assertStatus(403); } private function userWithPermissions(array $permissions): User { $user = User::factory()->create(); $role = Role::query()->create([ 'name' => 'role-'.strtolower(str()->random(8)), 'guard_name' => 'api', ]); foreach ($permissions as $permissionName) { $permission = Permission::query()->firstOrCreate([ 'name' => $permissionName, 'guard_name' => 'api', ]); $role->givePermissionTo($permission); } $user->assignRole($role); return $user; } }