*/ public function formValues(string $processInstanceId): array { $processInstanceId = trim($processInstanceId); if ($processInstanceId === '') { throw new RuntimeException('DingTalk process instance ID is required.'); } try { $response = Http::acceptJson() ->timeout($this->timeout()) ->withHeaders([ 'x-acs-dingtalk-access-token' => $this->appAccessToken(), ]) ->get(self::PROCESS_INSTANCE_URL, [ 'processInstanceId' => $processInstanceId, ]); } catch (RequestException $exception) { throw new RuntimeException( 'DingTalk process instance lookup failed.', 0, $exception, ); } if (! $response->successful()) { throw new RuntimeException( 'DingTalk process instance lookup returned HTTP ' . $response->status() . '.', ); } $result = $response->json('result'); if (! is_array($result)) { throw new RuntimeException('DingTalk process instance response was incomplete.'); } $values = []; foreach ((array) ($result['formComponentValues'] ?? []) as $component) { if (! is_array($component)) { continue; } $name = trim((string) ($component['name'] ?? '')); if ($name !== '') { $values[$name] = $component['value'] ?? null; } } return $values; } private function appAccessToken(): string { if ($this->accessToken !== null && time() < $this->accessTokenExpiresAt) { return $this->accessToken; } if (! $this->isConfigured()) { throw new RuntimeException('DingTalk process API credentials are not configured.'); } try { $response = Http::acceptJson() ->timeout($this->timeout()) ->get((string) config('dingtalk.app_token_url'), [ 'appkey' => (string) config('dingtalk.client_id'), 'appsecret' => (string) config('dingtalk.client_secret'), ]) ->throw(); } catch (RequestException $exception) { throw new RuntimeException('DingTalk application token lookup failed.', 0, $exception); } $token = trim((string) $response->json('access_token')); if ((int) $response->json('errcode', -1) !== 0 || $token === '') { throw new RuntimeException('DingTalk application token was unavailable.'); } $expiresIn = max(300, (int) $response->json('expires_in', 7200)); $this->accessToken = $token; $this->accessTokenExpiresAt = time() + max( 120, $expiresIn - self::TOKEN_SAFETY_SECONDS, ); return $token; } private function timeout(): int { return max(3, min(30, (int) config('dingtalk.timeout', 10))); } }