baseUrl = rtrim($baseUrl, '/'); $this->token = $token; } public function setBaseUrl(string $url): self { $this->baseUrl = rtrim($url, '/'); return $this; } public function setToken(string $token): self { $this->token = $token; return $this; } public function setTimeout(int $seconds): self { $this->timeout = $seconds; return $this; } public function setConnectTimeout(int $seconds): self { $this->connectTimeout = $seconds; return $this; } public function setRetry(int $times, int $delayMs = 100): self { $this->retryTimes = $times; $this->retryDelay = $delayMs; return $this; } public function setFallbackHandler(?Closure $handler): self { $this->fallbackHandler = $handler; return $this; } public function enableLogging(bool $value = true): self { $this->enableLogging = $value; return $this; } protected function withDefaultHeaders(): PendingRequest { $request = Http::connectTimeout($this->connectTimeout) ->timeout($this->timeout) ->withHeaders([ 'Accept' => 'application/json', 'Authorization' => 'Bearer ' . $this->token, ]); if ($this->retryTimes > 0) { $request = $request->retry($this->retryTimes, $this->retryDelay, function ($exception, $request) { if ($this->enableLogging) { Log::warning('API retrying...', [ 'url' => $request->url(), 'exception' => $exception->getMessage(), ]); } return true; }); } return $request; } public function requestWithCatch(callable $fn) { try { return $fn(); } catch (RequestException $e) { if ($this->enableLogging) { Log::error('API Request Failed', [ 'message' => $e->getMessage(), 'url' => $e->request?->url(), 'code' => $e->getCode(), ]); } if ($this->fallbackHandler instanceof \Closure) { return call_user_func($this->fallbackHandler, $e); } // 智能 fallback,直接寫在這裡 $status = $e->response?->status(); if ($status >= 500) { return ApiResponse::error('伺服器錯誤,請稍後再試','SERVER_ERROR',503); } if ($status === 404) { return ApiResponse::error('遠端 API 路徑不存在','API_NOT_FOUND',404); } if ($status === 401 || $status === 403) { return ApiResponse::unauthorized(); } if ($e->getCode() === 0) { return ApiResponse::error('無法連線遠端 API(可能超時或 DNS 錯誤)','CONNECTION_ERROR',504); } return ApiResponse::error('API 請求失敗: ' . $e->getMessage(), 'HTTP_CLIENT_ERROR',$status ?? 400); } } public function get(string $endpoint, array $query = []) { return $this->requestWithCatch(fn () => $this->withDefaultHeaders()->get("{$this->baseUrl}{$endpoint}", $query) ); } public function post(string $endpoint, array $data = []) { return $this->requestWithCatch(fn () => $this->withDefaultHeaders()->post("{$this->baseUrl}{$endpoint}", $data) ); } public function put(string $endpoint, array $data = []) { return $this->requestWithCatch(fn () => $this->withDefaultHeaders()->put("{$this->baseUrl}{$endpoint}", $data) ); } public function patch(string $endpoint, array $data = []) { return $this->requestWithCatch(fn () => $this->withDefaultHeaders()->patch("{$this->baseUrl}{$endpoint}", $data) ); } public function delete(string $endpoint, array $data = []) { return $this->requestWithCatch(fn () => $this->withDefaultHeaders()->delete("{$this->baseUrl}{$endpoint}", $data) ); } public function upload(string $endpoint, array $files = [], array $data = []) { return $this->requestWithCatch(function () use ($endpoint, $files, $data) { $request = $this->timeout(300) ->connectTimeout(300) ->withDefaultHeaders(); foreach ($files as $key => $filePath) { $filename = basename($filePath); $request = $request->attach($key, fopen($filePath, 'r'), $filename); } return $request->withoutVerifying()->post("{$this->baseUrl}{$endpoint}", $data); }); } }