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 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 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), '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 = (string) (data_get($response->json(), 'message') ?: data_get($response->json(), 'detail.message') ?: data_get($response->json(), 'detail') ?: '服务器用户管理 API 返回异常'); 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); } }