admin; if (!$admin) { throw new \RuntimeException('管理員信息未找到。'); } $this->admin = $admin; } /** * 獲取客戶列表 */ public function index(): JsonResponse { try { $query = Client::select([ 'id', 'name', 'llm_provider_id', 'created_at', ]); // 如果不是超級管理員,只能看到自己管理的客戶 if (!$this->admin->isSuperAdmin()) { $query->whereHas('admins', function ($query) { $query->where('admin_id', $this->admin->id); }); } $clients = $query->get(); return $this->success([ 'items' => $clients->map(fn($client) => [ 'id' => $client->id, 'name' => $client->name, 'llm_provider_id' => $client->llm_provider_id, 'created_at' => $client->created_at->toIso8601String(), ]) ]); } catch (\Exception $e) { Log::error('Error fetching clients', [ 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString(), ]); return $this->error( ErrorCode::SERVER_ERROR, ErrorCode::getMessage(ErrorCode::SERVER_ERROR) ); } } /** * 新增客戶 */ public function store(Request $request): JsonResponse { try { $validated = $request->validate([ 'name' => [ 'required', 'string', 'max:100', 'unique:clients', ], 'llm_provider_id' => [ 'required', 'integer', 'exists:llm_providers,id', ], ]); // 檢查是否有權限管理該提供商 if (!$this->admin->canManageLlmProvider($validated['llm_provider_id'])) { return $this->error( ErrorCode::FORBIDDEN, '您無權使用該 LLM 提供商。' ); } $validated['status'] = Client::STATUS_ACTIVE; $validated['rate_limit'] = config('llm.default_rate_limit', 60); $validated['timeout'] = config('llm.default_timeout', 30); $client = Client::create($validated); // 如果不是超級管理員,需要建立關聯 if (!$this->admin->isSuperAdmin()) { $client->admins()->attach($this->admin->id); } $this->logService->logOperation( 'admin', $this->admin->id, "Created client: {$client->name}" ); return $this->created([ 'id' => $client->id, 'name' => $client->name, 'llm_provider_id' => $client->llm_provider_id, 'created_at' => $client->created_at->toIso8601String(), ]); } catch (ValidationException $e) { return $this->error( ErrorCode::VALIDATION_ERROR, ErrorCode::getMessage(ErrorCode::VALIDATION_ERROR), $e->errors() ); } catch (\Exception $e) { Log::error('Error creating client', [ 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString(), 'request_data' => $request->all(), ]); return $this->error( ErrorCode::SERVER_ERROR, ErrorCode::getMessage(ErrorCode::SERVER_ERROR) ); } } /** * 修改客戶 */ public function update(Request $request, int $id): JsonResponse { try { $client = Client::findOrFail($id); if (!$this->admin->canManageClient($client->id)) { return $this->error( ErrorCode::FORBIDDEN, '您無權管理該客戶。' ); } $validated = $request->validate([ 'name' => [ 'required', 'string', 'max:100', "unique:clients,name,{$id}", ], 'llm_provider_id' => [ 'required', 'integer', 'exists:llm_providers,id', ], ]); $client->update($validated); $this->logService->logOperation( 'admin', $this->admin->id, "Updated client: {$client->name}" ); return $this->success([ 'id' => $client->id, 'name' => $client->name, 'llm_provider_id' => $client->llm_provider_id, 'updated_at' => $client->updated_at->toIso8601String(), ]); } catch (ValidationException $e) { return $this->error( ErrorCode::VALIDATION_ERROR, ErrorCode::getMessage(ErrorCode::VALIDATION_ERROR), $e->errors() ); } catch (ModelNotFoundException $e) { return $this->error( ErrorCode::CLIENT_NOT_FOUND, ErrorCode::getMessage(ErrorCode::CLIENT_NOT_FOUND) ); } catch (\Exception $e) { Log::error('Error updating client', [ 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString(), 'client_id' => $id, 'request_data' => $request->all(), ]); return $this->error( ErrorCode::SERVER_ERROR, ErrorCode::getMessage(ErrorCode::SERVER_ERROR) ); } } /** * 刪除客戶 */ public function destroy(int $id): JsonResponse { try { $client = Client::findOrFail($id); if (!$this->admin->canManageClient($client->id)) { return $this->error( ErrorCode::FORBIDDEN, '您無權管理該客戶。' ); } if ($this->hasActiveTokens($client)) { return $this->error( ErrorCode::CLIENT_HAS_ACTIVE_TOKENS, '該客戶有活躍的認證令牌,無法刪除。' ); } $client->delete(); $this->logService->logOperation( 'admin', $this->admin->id, "Deleted client: {$client->name}" ); return $this->success(null, '客戶已刪除。'); } catch (ModelNotFoundException $e) { return $this->error( ErrorCode::CLIENT_NOT_FOUND, ErrorCode::getMessage(ErrorCode::CLIENT_NOT_FOUND) ); } catch (\Exception $e) { Log::error('Error deleting client', [ 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString(), 'client_id' => $id, ]); return $this->error( ErrorCode::SERVER_ERROR, ErrorCode::getMessage(ErrorCode::SERVER_ERROR) ); } } /** * 為客戶生成認證令牌 */ public function generateAuthToken(int $id): JsonResponse { try { $client = Client::findOrFail($id); if (!$this->admin->canManageClient($client->id)) { return $this->error( ErrorCode::FORBIDDEN, '您無權管理該客戶。' ); } if (!$client->isActive()) { return $this->error( ErrorCode::CLIENT_INACTIVE, '客戶未啟用,無法生成令牌。' ); } $result = $this->tokenService->generateAuthToken($client); return $this->success([ 'client_id' => $client->id, 'auth_token' => $result['auth_token'], 'created_at' => now()->toIso8601String(), ]); } catch (ModelNotFoundException $e) { return $this->error( ErrorCode::CLIENT_NOT_FOUND, ErrorCode::getMessage(ErrorCode::CLIENT_NOT_FOUND) ); } catch (\Exception $e) { Log::error('Error generating auth token', [ 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString(), 'client_id' => $id, 'client_exists' => Client::find($id) ? 'yes' : 'no', 'redis_status' => Redis::ping() ? 'connected' : 'disconnected', 'file' => $e->getFile(), 'line' => $e->getLine() ]); return $this->error( ErrorCode::SERVER_ERROR, ErrorCode::getMessage(ErrorCode::SERVER_ERROR) ); } } /** * 檢查客戶是否有活躍的令牌 */ private function hasActiveTokens(Client $client): bool { $pattern = 'auth_token:*'; $keys = Redis::keys($pattern); foreach ($keys as $key) { $data = Redis::get($key); if ($data) { $tokenData = json_decode($data, true); if ($tokenData && $tokenData['client_id'] === $client->id && now()->isBefore($tokenData['expires_at'])) { return true; } } } return false; } }