From 7c83fa46642a04704e4c396a884d8c894eaee8eb Mon Sep 17 00:00:00 2001 From: Nick Zeng Date: Wed, 1 Apr 2026 12:44:52 +0800 Subject: [PATCH] update P15 --- .../Controller/api/v1/DataScopeController.php | 4 +- .../app/Controller/api/v1/UserController.php | 11 +- .../User/DataScopeControllerTest.php | 170 ++++++++++++++++++ .../Integration/User/UserControllerTest.php | 25 +++ 4 files changed, 206 insertions(+), 4 deletions(-) create mode 100644 backend/test/Cases/Integration/User/DataScopeControllerTest.php diff --git a/backend/app/Controller/api/v1/DataScopeController.php b/backend/app/Controller/api/v1/DataScopeController.php index d9e32ce..28fc64e 100644 --- a/backend/app/Controller/api/v1/DataScopeController.php +++ b/backend/app/Controller/api/v1/DataScopeController.php @@ -287,7 +287,7 @@ class DataScopeController extends AbstractController ? Company::query()->whereIn('id', $company_ids)->pluck('label', 'id')->toArray() : []; $platform_names = !empty($platform_ids) - ? Platform::query()->whereIn('id', $platform_ids)->pluck('id', 'id')->toArray() + ? Platform::query()->whereIn('id', $platform_ids)->pluck('name', 'id')->toArray() : []; $store_names = !empty($store_ids) ? Store::query()->whereIn('id', $store_ids)->pluck('label', 'id')->toArray() @@ -297,7 +297,7 @@ class DataScopeController extends AbstractController return array_map(function (array $scope) use ($company_names, $platform_names, $store_names): array { $name = match ($scope['scope_type']) { 'company' => $company_names[$scope['scope_id']] ?? null, - 'platform' => isset($platform_names[$scope['scope_id']]) ? "Platform #{$scope['scope_id']}" : null, + 'platform' => $platform_names[$scope['scope_id']] ?? null, 'store' => $store_names[$scope['scope_id']] ?? null, default => null, }; diff --git a/backend/app/Controller/api/v1/UserController.php b/backend/app/Controller/api/v1/UserController.php index d650462..0a7ab8a 100644 --- a/backend/app/Controller/api/v1/UserController.php +++ b/backend/app/Controller/api/v1/UserController.php @@ -22,12 +22,12 @@ class UserController extends AbstractController /** * 用户列表 * - * 支持分页、按 username/email 模糊搜索、按 status 精确筛选 + * 支持分页、按 username/email 模糊搜索、按 status/role_id 精确筛选 */ #[OA\Get( path: '/users', summary: '用户列表', - description: '获取用户列表,支持分页、按 username/email 模糊搜索、按 status 精确筛选', + description: '获取用户列表,支持分页、按 username/email 模糊搜索、按 status/role_id 精确筛选', security: [['bearerAuth' => []]], tags: ['Users'], parameters: [ @@ -36,6 +36,7 @@ class UserController extends AbstractController new OA\Parameter(name: 'username', in: 'query', required: false, description: '用户名模糊搜索', schema: new OA\Schema(type: 'string')), new OA\Parameter(name: 'email', in: 'query', required: false, description: '邮箱模糊搜索', schema: new OA\Schema(type: 'string')), new OA\Parameter(name: 'status', in: 'query', required: false, description: '状态筛选(0=禁用,1=启用)', schema: new OA\Schema(type: 'integer', enum: [0, 1])), + new OA\Parameter(name: 'role_id', in: 'query', required: false, description: '按角色筛选', schema: new OA\Schema(type: 'integer')), ], responses: [ new OA\Response( @@ -83,6 +84,12 @@ class UserController extends AbstractController $query->where('status', (int) $status); } + // 按 role_id 精确筛选 + $role_id = $this->request->input('role_id'); + if ($role_id !== null && $role_id !== '') { + $query->where('role_id', (int) $role_id); + } + // 按 created_at 降序排序 $query->orderBy('created_at', 'desc'); diff --git a/backend/test/Cases/Integration/User/DataScopeControllerTest.php b/backend/test/Cases/Integration/User/DataScopeControllerTest.php new file mode 100644 index 0000000..be38c5c --- /dev/null +++ b/backend/test/Cases/Integration/User/DataScopeControllerTest.php @@ -0,0 +1,170 @@ +fetchAdminRole(); + $user = $this->fetchUser(static function ($query) use ($admin_role): void { + $query->where('status', 1)->where('role_id', $admin_role->id); + }); + if (!$user) { + $this->markTestSkipped('没有可用的 administrator 用户,无法测试'); + } + + $auth = make(AuthManager::class); + return $auth->guard('jwt')->login($user); + } + + protected function fetchAdminRole(): Role + { + if (\Swoole\Coroutine::getCid() > 0) { + return Role::query()->where('name', 'administrator')->firstOrFail(); + } + + $role = null; + \Swoole\Coroutine\run(static function () use (&$role): void { + $role = Role::query()->where('name', 'administrator')->firstOrFail(); + }); + + return $role; + } + + protected function authHeaders(): array + { + return ['Authorization' => 'Bearer ' . $this->getAuthToken()]; + } + + protected function fetchUser(?callable $callback = null): ?User + { + if (\Swoole\Coroutine::getCid() > 0) { + $query = User::query(); + if ($callback !== null) { + $callback($query); + } + + return $query->first(); + } + + $user = null; + + \Swoole\Coroutine\run(static function () use ($callback, &$user): void { + $query = User::query(); + if ($callback !== null) { + $callback($query); + } + + $user = $query->first(); + }); + + return $user; + } + + /** + * 在协程内创建测试用户及 platform scope 数据 + * + * @return array{user_id: int, platform_name: string} + */ + protected function createUserWithPlatformScope(): array + { + $run = static function (): array { + $suffix = bin2hex(random_bytes(4)); + $user = User::query()->create([ + 'username' => 'scope_test_' . $suffix, + 'password' => 'Pass_' . $suffix, + 'email' => 'scope_test_' . $suffix . '@example.com', + 'status' => 1, + ]); + + $platform = Platform::query()->first(); + + UserDataScope::query()->insert([ + 'user_id' => $user->id, + 'scope_type' => 'platform', + 'scope_id' => $platform->id, + 'created_at' => date('Y-m-d H:i:s'), + ]); + + return ['user_id' => $user->id, 'platform_name' => $platform->name]; + }; + + if (\Swoole\Coroutine::getCid() > 0) { + return $run(); + } + + $result = null; + \Swoole\Coroutine\run(static function () use ($run, &$result): void { + $result = $run(); + }); + + return $result; + } + + /** + * 测试 platform scope 返回真实平台名称(而非 "Platform #ID") + */ + public function test_data_scope_returns_real_platform_name(): void + { + $setup = $this->createUserWithPlatformScope(); + + $response = $this->get('/api/v1/users/' . $setup['user_id'] . '/data-scope', [], $this->authHeaders()); + + $response->assertStatus(200); + $response->assertJsonPath('code', 0); + + $body = json_decode($response->getContent(), true); + $scopes = $body['data']['scopes']; + + $this->assertNotEmpty($scopes); + + $platform_scope = null; + foreach ($scopes as $scope) { + if ($scope['scope_type'] === 'platform') { + $platform_scope = $scope; + break; + } + } + + $this->assertNotNull($platform_scope, '应包含 platform 类型的 scope'); + $this->assertSame($setup['platform_name'], $platform_scope['name'], '平台名称应为真实名称,而非 "Platform #ID"'); + $this->assertStringNotContainsString('Platform #', (string) $platform_scope['name']); + } + + /** + * 测试未认证请求返回 401 + */ + public function test_data_scope_without_token_returns_401(): void + { + $response = $this->get('/api/v1/users/1/data-scope'); + + $response->assertStatus(401); + } + + /** + * 测试不存在的用户返回 404 + */ + public function test_data_scope_not_found_returns_404(): void + { + $response = $this->get('/api/v1/users/999999/data-scope', [], $this->authHeaders()); + + $response->assertStatus(404); + $response->assertJsonPath('code', 404); + } +} diff --git a/backend/test/Cases/Integration/User/UserControllerTest.php b/backend/test/Cases/Integration/User/UserControllerTest.php index 8074bdb..12d2914 100644 --- a/backend/test/Cases/Integration/User/UserControllerTest.php +++ b/backend/test/Cases/Integration/User/UserControllerTest.php @@ -201,6 +201,31 @@ class UserControllerTest extends TestCase } } + public function test_list_users_filter_by_role_id(): void + { + $admin_role = $this->fetchAdminRole(); + + $response = $this->get('/api/v1/users', ['role_id' => $admin_role->id], $this->authHeaders()); + + $response->assertStatus(200); + $response->assertJsonPath('code', 0); + + $body = json_decode($response->getContent(), true); + $this->assertGreaterThanOrEqual(1, $body['data']['total']); + foreach ($body['data']['items'] as $item) { + $this->assertSame($admin_role->id, $item['role_id']); + } + } + + public function test_list_users_filter_by_role_id_empty(): void + { + $response = $this->get('/api/v1/users', ['role_id' => 999999], $this->authHeaders()); + + $response->assertStatus(200); + $response->assertJsonPath('code', 0); + $response->assertJsonPath('data.total', 0); + } + // ========== 详情接口测试 ========== public function test_show_user_returns_user_data(): void