request($server, 'get', '/users')->json(); } public function user(ServerResource $server, string $username): array { return $this->request($server, 'get', '/users/'.$this->encodePath($username))->json(); } public function userExists(ServerResource $server, string $username): bool { try { $this->user($server, $username); return true; } catch (ValidationException $exception) { $errors = $exception->errors(); $messages = collect($errors)->flatten()->map(fn ($message): string => (string) $message); if ($messages->contains(fn (string $message): bool => str_contains(mb_strtolower($message), 'not found') || str_contains($message, '不存在'))) { return false; } throw $exception; } } public function createUser(ServerResource $server, array $payload): array { return $this->request($server, 'post', '/users', $payload)->json(); } public function deleteUser(ServerResource $server, string $username): array { return $this->request($server, 'delete', '/users/'.$this->encodePath($username))->json(); } public function updatePassword(ServerResource $server, string $username, string $passwordHash): array { return $this->request($server, 'patch', '/users/'.$this->encodePath($username).'/password', [ 'password_hash' => $passwordHash, ])->json(); } public function userEnvironment(ServerResource $server, string $username): array { return $this->request($server, 'get', '/users/'.$this->encodePath($username).'/environment')->json(); } public function updateUserEnvironment(ServerResource $server, string $username, string $content): array { return $this->request($server, 'put', '/users/'.$this->encodePath($username).'/environment', [ 'content' => $content, ])->json(); } public function updateAllUserEnvironments(ServerResource $server, string $content): array { return $this->request($server, 'put', '/users/environment', [ 'content' => $content, ])->json(); } public function groups(ServerResource $server): array { return $this->request($server, 'get', '/groups')->json(); } public function createGroup(ServerResource $server, string $groupname): array { return $this->request($server, 'post', '/groups', ['groupname' => $groupname])->json(); } public function deleteGroup(ServerResource $server, string $groupname): array { return $this->request($server, 'delete', '/groups/'.$this->encodePath($groupname))->json(); } public function userGroups(ServerResource $server, string $username): array { return $this->request($server, 'get', '/users/'.$this->encodePath($username).'/groups')->json(); } public function syncUserGroups(ServerResource $server, string $username, array $groups, string $mode = 'replace'): array { return $this->request($server, 'post', '/users/'.$this->encodePath($username).'/groups', [ 'groups' => array_values($groups), 'mode' => $mode, ])->json(); } public function removeUserGroups(ServerResource $server, string $username, array $groups): array { return $this->request($server, 'delete', '/users/'.$this->encodePath($username).'/groups', [ 'groups' => array_values($groups), 'mode' => 'append', ])->json(); } private function request(ServerResource $server, string $method, string $path, array $payload = []) { $target = $this->resolveServer($server); $baseUrl = rtrim((string) $target->user_api_base_url, '/'); $token = trim((string) $target->user_api_token); if ($baseUrl === '' || $token === '') { throw ValidationException::withMessages([ 'server' => ['该服务器未配置用户管理 API 地址或密钥。'], ]); } try { $pending = Http::baseUrl($baseUrl) ->acceptJson() ->asJson() ->timeout((int) config('services.server_user_management.timeout', 15)) ->connectTimeout((int) config('services.server_user_management.connect_timeout', 5)) ->retry(2, 200, throw: false) ->withToken($token) ->withOptions([ 'verify' => (bool) config('services.server_user_management.verify_ssl', false), ]); $response = match ($method) { 'post' => $pending->post($path, $payload), 'put' => $pending->put($path, $payload), 'patch' => $pending->patch($path, $payload), 'delete' => empty($payload) ? $pending->delete($path) : $pending->send('DELETE', $path, ['json' => $payload]), default => $pending->get($path), }; } catch (ConnectionException|RequestException $exception) { throw ValidationException::withMessages([ 'server' => ['服务器用户管理 API 调用失败:'.$exception->getMessage()], ]); } if (! $response->successful()) { $message = $this->errorMessage($response->json()); throw ValidationException::withMessages([ 'server' => [$message], ]); } return $response; } private function resolveServer(ServerResource $server): ServerResource { if (! $server->parent_id) { return $server; } return $server->parent()->firstOrFail(); } private function encodePath(string $value): string { return rawurlencode($value); } private function errorMessage(mixed $payload): string { if (! is_array($payload)) { return '服务器用户管理 API 返回异常'; } $message = data_get($payload, 'message') ?: data_get($payload, 'detail.message') ?: data_get($payload, 'detail'); if (is_string($message) && $message !== '') { return $message; } if (is_array($message)) { $messages = collect($message) ->map(function (mixed $item): string { if (is_string($item)) { return $item; } if (is_array($item)) { return (string) (data_get($item, 'message') ?: data_get($item, 'msg') ?: data_get($item, 'detail') ?: json_encode($item, JSON_UNESCAPED_UNICODE)); } return ''; }) ->filter() ->values(); if ($messages->isNotEmpty()) { return $messages->join(';'); } } $failedUsers = collect(data_get($payload, 'failed_users', [])) ->map(function (mixed $item): string { if (! is_array($item)) { return ''; } $username = (string) data_get($item, 'username', ''); $error = (string) (data_get($item, 'message') ?: data_get($item, 'code', '')); return trim($username.($error !== '' ? ': '.$error : '')); }) ->filter() ->values(); if ($failedUsers->isNotEmpty()) { return '部分服务器用户环境变量设置失败:'.$failedUsers->join(';'); } return '服务器用户管理 API 返回异常'; } }