input('per_page', 15); $perPage = min(max($perPage, 1), 100); // 限制每页数量在 1-100 之间 $search = $request->input('search'); $status = $request->input('status'); $query = Client::with('llmProvider:id,name,service_name') ->orderBy('created_at', 'desc'); // 搜索条件 if ($search) { $query->where(function ($q) use ($search) { $q->where('name', 'like', "%{$search}%") ->orWhere('email', 'like', "%{$search}%"); }); } // 状态筛选 if ($status && in_array($status, ['active', 'inactive'])) { $query->where('status', $status); } $clients = $query->paginate($perPage); return response()->json([ 'data' => $clients->items(), 'meta' => [ 'current_page' => $clients->currentPage(), 'per_page' => $clients->perPage(), 'total' => $clients->total(), 'total_pages' => $clients->lastPage(), ], ]); } catch (\Exception $e) { Log::error('Error fetching clients', [ 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString(), 'search' => $search, 'status' => $status, ]); return response()->json([ 'error' => 'server_error', 'message' => '服务器内部错误。', ], 500); } } /** * Create new client * * @param Request $request * @return JsonResponse * * Request body: * { * "name": "string", * "email": "string", * "llm_provider_id": "integer", * "rate_limit": "integer", * "timeout": "integer" * } * * Success response (201): * { * "id": 1001, * "name": "Client Name", * "email": "client@example.com", * "status": "active", * "llm_provider_id": 1, * "rate_limit": 100, * "timeout": 30, * "created_at": "2023-10-01T14:00:00Z" * } */ public function store(Request $request): JsonResponse { try { $validated = $request->validate([ 'name' => [ 'required', 'string', 'max:100', 'regex:/^[\w\-\s]+$/u', ], 'email' => [ 'required', 'string', 'email', 'max:255', 'unique:clients', ], 'llm_provider_id' => [ 'required', 'integer', 'exists:llm_providers,id', ], 'rate_limit' => [ 'required', 'integer', 'min:1', 'max:1000', ], 'timeout' => [ 'required', 'integer', 'min:1', 'max:300', ], ], [ 'name.regex' => '客户名称只能包含字母、数字、下划线、横线和空格。', 'email.unique' => '该邮箱地址已被使用。', 'llm_provider_id.exists' => '选择的 LLM 提供商不存在。', 'rate_limit.min' => '速率限制不能小于 1。', 'rate_limit.max' => '速率限制不能大于 1000。', 'timeout.min' => '超时时间不能小于 1 秒。', 'timeout.max' => '超时时间不能大于 300 秒。', ]); $validated['status'] = 'active'; $client = Client::create($validated); // 加载 LLM 提供商信息 $client->load('llmProvider:id,name,service_name'); $this->logService->logOperation( 'admin', $request->admin->id, "Created client: {$client->name}" ); return response()->json($client, 201); } catch (ValidationException $e) { return response()->json([ 'error' => 'validation_error', 'message' => '请求参数验证失败。', 'errors' => $e->errors(), ], 422); } catch (\Exception $e) { Log::error('Error creating client', [ 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString(), 'request_data' => $request->except(['api_token']), ]); return response()->json([ 'error' => 'server_error', 'message' => '服务器内部错误。', ], 500); } } /** * Update client * * @param Request $request * @param int $id * @return JsonResponse * * Request body: * { * "name": "string", * "email": "string", * "llm_provider_id": "integer", * "rate_limit": "integer", * "timeout": "integer", * "status": "string" * } * * Success response: * { * "id": 1001, * "name": "Client Name", * "email": "client@example.com", * "status": "active", * "llm_provider_id": 1, * "rate_limit": 100, * "timeout": 30, * "updated_at": "2023-10-02T14:00:00Z", * "llm_provider": { * "id": 1, * "name": "OpenAI", * "service_name": "openai" * } * } */ public function update(Request $request, int $id): JsonResponse { try { $client = Client::findOrFail($id); $validated = $request->validate([ 'name' => [ 'required', 'string', 'max:100', 'regex:/^[\w\-\s]+$/u', ], 'email' => [ 'required', 'string', 'email', 'max:255', "unique:clients,email,{$id}", ], 'llm_provider_id' => [ 'required', 'integer', 'exists:llm_providers,id', ], 'rate_limit' => [ 'required', 'integer', 'min:1', 'max:1000', ], 'timeout' => [ 'required', 'integer', 'min:1', 'max:300', ], 'status' => [ 'required', 'string', 'in:active,inactive', ], ], [ 'name.regex' => '客户名称只能包含字母、数字、下划线、横线和空格。', 'email.unique' => '该邮箱地址已被使用。', 'llm_provider_id.exists' => '选择的 LLM 提供商不存在。', 'rate_limit.min' => '速率限制不能小于 1。', 'rate_limit.max' => '速率限制不能大于 1000。', 'timeout.min' => '超时时间不能小于 1 秒。', 'timeout.max' => '超时时间不能大于 300 秒。', 'status.in' => '状态只能是 active 或 inactive。', ]); // 如果状态改为 inactive,检查是否有活跃的认证令牌 if ($validated['status'] === 'inactive' && $client->status === 'active') { if ($client->authTokens()->where('status', 'active')->exists()) { return response()->json([ 'error' => 'active_tokens_exist', 'message' => '该客户有活跃的认证令牌,请先停用所有令牌。', ], 422); } } $client->update($validated); // 加载 LLM 提供商信息 $client->load('llmProvider:id,name,service_name'); $this->logService->logOperation( 'admin', $request->admin->id, "Updated client: {$client->name}" ); return response()->json($client); } catch (ValidationException $e) { return response()->json([ 'error' => 'validation_error', 'message' => '请求参数验证失败。', 'errors' => $e->errors(), ], 422); } catch (ModelNotFoundException $e) { return response()->json([ 'error' => 'not_found', 'message' => '客户不存在。', ], 404); } catch (\Exception $e) { Log::error('Error updating client', [ 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString(), 'client_id' => $id, 'request_data' => $request->except(['api_token']), ]); return response()->json([ 'error' => 'server_error', 'message' => '服务器内部错误。', ], 500); } } /** * Delete client * * @param Request $request * @param int $id * @return JsonResponse * * Success response: * { * "message": "客户已删除。" * } * * Error responses: * - 404: 客户不存在 * - 422: 客户有活跃的认证令牌 * - 500: 服务器内部错误 */ public function destroy(Request $request, int $id): JsonResponse { try { $client = Client::findOrFail($id); // 检查是否有活跃的认证令牌 if ($client->authTokens()->where('status', 'active')->exists()) { return response()->json([ 'error' => 'client_in_use', 'message' => '该客户有活跃的认证令牌,请先停用或删除令牌。', ], 422); } $clientName = $client->name; // 软删除客户记录 $client->delete(); $this->logService->logOperation( 'admin', $request->admin->id, "Deleted client: {$clientName}" ); return response()->json([ 'message' => '客户已删除。', ]); } catch (ModelNotFoundException $e) { return response()->json([ 'error' => 'not_found', 'message' => '客户不存在。', ], 404); } catch (\Exception $e) { Log::error('Error deleting client', [ 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString(), 'client_id' => $id, ]); return response()->json([ 'error' => 'server_error', 'message' => '服务器内部错误。', ], 500); } } /** * Generate auth token for client * * @param Request $request * @param int $id * @return JsonResponse * * Request body: * { * "expires_in_days": int (optional) // 令牌有效期(天),默认 30 天 * } * * Success response: * { * "auth_token": "string", // 认证令牌 * "expires_at": "2024-10-01T14:00:00Z", // 过期时间 * "client": { // 客户信息 * "id": 1001, * "name": "Client Name", * "email": "client@example.com", * "status": "active" * } * } * * Error responses: * - 404: 客户不存在 * - 422: 客户状态非活跃 * - 500: 服务器内部错误 */ public function generateAuthToken(Request $request, int $id): JsonResponse { try { $client = Client::findOrFail($id); if ($client->status !== 'active') { return response()->json([ 'error' => 'client_inactive', 'message' => '客户状态为非活跃,无法生成认证令牌。', ], 422); } // 验证过期时间 $validated = $request->validate([ 'expires_in_days' => 'nullable|integer|min:1|max:365', ], [ 'expires_in_days.min' => '令牌有效期不能小于 1 天。', 'expires_in_days.max' => '令牌有效期不能超过 365 天。', ]); $token = $this->tokenService->generateAuthToken( $client, $validated['expires_in_days'] ?? 30 ); $this->logService->logOperation( 'admin', $request->admin->id, "Generated auth token for client: {$client->name}" ); return response()->json([ 'auth_token' => $token->token, 'expires_at' => $token->expires_at, 'client' => [ 'id' => $client->id, 'name' => $client->name, 'email' => $client->email, 'status' => $client->status, ], ]); } catch (ValidationException $e) { return response()->json([ 'error' => 'validation_error', 'message' => '请求参数验证失败。', 'errors' => $e->errors(), ], 422); } catch (ModelNotFoundException $e) { return response()->json([ 'error' => 'not_found', 'message' => '客户不存在。', ], 404); } catch (\Exception $e) { Log::error('Error generating auth token', [ 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString(), 'client_id' => $id, 'request_data' => $request->except(['api_token']), ]); return response()->json([ 'error' => 'server_error', 'message' => '服务器内部错误。', ], 500); } } }