From f52f3b27aa9792a38b8bd1e1b881f024595e8797 Mon Sep 17 00:00:00 2001 From: Xin Date: Thu, 8 May 2025 19:32:37 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E7=A7=9F=E6=88=B7=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E5=88=97=E8=A1=A8=E9=97=AE=E9=A2=98=E6=9C=AA=E8=A7=A3=E5=86=B3?= =?UTF-8?q?=EF=BC=8C=E5=BE=85=E8=BF=9B=E4=B8=80=E6=AD=A5=E8=B0=83=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/app.py | 28 ++++++++----- api/model_manager.py | 23 ++++++----- api/models.py | 2 +- api/tenant_manager.py | 35 +++++++++++++++- web/src/api/model/index.ts | 1 + web/src/api/model/types.ts | 27 +++++++++++- web/src/api/tenant/index.ts | 16 ++++---- web/src/axios/service.ts | 3 ++ web/src/views/Model/index.vue | 77 ++++++++++++++++++++++++++++------- 9 files changed, 164 insertions(+), 48 deletions(-) diff --git a/api/app.py b/api/app.py index c983fae..e6c7d41 100644 --- a/api/app.py +++ b/api/app.py @@ -1,5 +1,6 @@ import uuid from datetime import datetime, timedelta, timezone +from typing import List from fastapi import FastAPI, Depends, HTTPException, status, Request, Body, Response, UploadFile, File from fastapi.middleware.cors import CORSMiddleware from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm @@ -116,7 +117,9 @@ async def get_current_user(token: str = Depends(oauth2_scheme)): # 打印接收到的token用于调试 logger.info(f"Received token: {token[:10]}...{token[-10:]}") + print(f"Full token: {token}") # 调试日志 payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) + print(f"Decoded payload: {payload}") # 调试日志 username: str = payload.get("sub") if username is None: raise credentials_exception @@ -424,19 +427,21 @@ async def create_tenant(tenant: TenantCreate, current_user: dict = Depends(get_c logger.error(f"创建租户失败: {e}") raise HTTPException(status_code=400, detail="创建租户失败") -@app.get("/api/tenants/") -async def list_tenants(current_user: dict = Depends(get_current_user)): +@app.get("/api/tenants/", response_model=List[TenantResponse]) +async def list_tenants( + search: str = None, + current_user: dict = Depends(get_current_user) +): """查询租户列表""" try: - tenants = TenantManager.get_all_tenants() - return { - "tenants": [{ - "id": str(t["id"]), - "name": t["name"], - "description": t.get("description", ""), - "created_at": t.get("created_at", datetime.now(timezone.utc)) - } for t in tenants] - } + logger.info(f"Current user accessing tenants: {current_user['username']}") + tenants = TenantManager.search_tenants(search) if search else TenantManager.get_all_tenants() + if not tenants: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="未找到匹配的租户" + ) + return tenants except Exception as e: logger.error(f"查询租户列表失败: {e}") raise HTTPException(status_code=400, detail="查询租户列表失败") @@ -445,6 +450,7 @@ async def list_tenants(current_user: dict = Depends(get_current_user)): async def get_tenant(name: str, current_user: dict = Depends(get_current_user)): """查询特定租户""" try: + logger.info(f"Current user accessing tenant {name}: {current_user['username']}") tenant = TenantManager.get_tenant_by_name(name) if not tenant: raise HTTPException( diff --git a/api/model_manager.py b/api/model_manager.py index 183dd6b..e0870d9 100644 --- a/api/model_manager.py +++ b/api/model_manager.py @@ -61,16 +61,19 @@ class ModelManager: cursor.execute(query, (tenant_id,)) models = cursor.fetchall() - return [{ - "id": str(model[0]), - "provider_name": model[1], - "model_name": model[2], - "model_type": model[3], - "encrypted_config": json.loads(model[4]), - "is_valid": model[5], - "created_at": model[6], - "updated_at": model[7] - } for model in models] + return { + "tenant_id": tenant_id, + "models": [{ + "id": str(model[0]), + "provider_name": model[1], + "model_name": model[2], + "model_type": model[3], + "encrypted_config": json.loads(model[4]), + "is_valid": model[5], + "created_at": model[6], + "updated_at": model[7] + } for model in models] + } except Exception as e: logger.error(f"获取租户模型失败: {e}") raise diff --git a/api/models.py b/api/models.py index c9b81db..f4803b1 100644 --- a/api/models.py +++ b/api/models.py @@ -54,5 +54,5 @@ class TenantResponse(BaseModel): """租户响应模型""" id: str name: str - description: str + description: Optional[str] = None created_at: datetime diff --git a/api/tenant_manager.py b/api/tenant_manager.py index c657003..cdf4114 100644 --- a/api/tenant_manager.py +++ b/api/tenant_manager.py @@ -107,9 +107,42 @@ class TenantManager: def get_all_tenants(): """获取所有租户信息""" try: - query = "SELECT id, encrypt_public_key FROM tenants;" + query = """ + SELECT + id::text, + name, + encrypt_public_key, + created_at + FROM tenants; + """ tenants = execute_query(query, cursor_factory=RealDictCursor) + if not tenants: + return [] + + # 确保返回字段名正确 return tenants except Exception as e: logger.error(f"获取所有租户信息失败: {e}") return [] + + @staticmethod + def search_tenants(search_term: str): + """根据名称搜索租户""" + try: + query = """ + SELECT + id::text, + name, + encrypt_public_key, + created_at + FROM tenants + WHERE name ILIKE %s; + """ + tenants = execute_query(query, (f"%{search_term}%",), cursor_factory=RealDictCursor) + if not tenants: + return [] + + return tenants + except Exception as e: + logger.error(f"搜索租户失败: {e}") + return [] diff --git a/web/src/api/model/index.ts b/web/src/api/model/index.ts index fb89532..efdbe1e 100644 --- a/web/src/api/model/index.ts +++ b/web/src/api/model/index.ts @@ -29,6 +29,7 @@ export function assignModelToTenant( } export function getTenantModels(tenantId: string): Promise> { + console.log('请求模型列表API,路径:', `/api/models/${tenantId}`) return request({ url: `/api/models/${tenantId}`, method: 'get' diff --git a/web/src/api/model/types.ts b/web/src/api/model/types.ts index 8d6ee6d..c2fbda0 100644 --- a/web/src/api/model/types.ts +++ b/web/src/api/model/types.ts @@ -23,8 +23,31 @@ export interface ModelResponse { created_at: string } +export interface ModelItem { + id: string + model_name: string + provider_name: string + model_type: string + encrypted_config: any + is_valid: boolean + created_at: string + updated_at: string +} + +export interface ModelItem { + id: string + provider_name: string + model_name: string + model_type: string + encrypted_config: any + is_valid: boolean + created_at: string + updated_at: string +} + export interface TenantModelResponse { tenant_id: string - tenant_name: string - models: ModelResponse[] + models: { + models: ModelItem[] + } } diff --git a/web/src/api/tenant/index.ts b/web/src/api/tenant/index.ts index b33adc3..615ee66 100644 --- a/web/src/api/tenant/index.ts +++ b/web/src/api/tenant/index.ts @@ -7,17 +7,15 @@ import type { CreateTenantResponse } from './types' -export const fetchTenants = (params: { page?: number; pageSize?: number }) => +export const fetchTenants = (params: { search?: string; page?: number; pageSize?: number }) => request<{ - tenants: { - id: string - name: string - description: string - createdAt: string - }[] - }>({ + id: string + name: string + description: string + createdAt: string + }[]>({ method: 'GET', - url: '/api/tenants', + url: '/api/tenants/', params }) diff --git a/web/src/axios/service.ts b/web/src/axios/service.ts index 1177ec4..d675be8 100644 --- a/web/src/axios/service.ts +++ b/web/src/axios/service.ts @@ -8,11 +8,14 @@ const service = createAxios() service.interceptors.request.use( (config) => { const token = localStorage.getItem('access_token') + console.log('Request interceptor - token:', token) // 调试日志 if (token) { config.headers.Authorization = `Bearer ${token}` + console.log('Authorization header set:', config.headers.Authorization) // 调试日志 } config.headers['Content-Type'] = 'application/json' config.withCredentials = true + console.log('Final request config:', config) // 调试日志 return config }, (error) => { diff --git a/web/src/views/Model/index.vue b/web/src/views/Model/index.vue index f19c56c..5286bec 100644 --- a/web/src/views/Model/index.vue +++ b/web/src/views/Model/index.vue @@ -107,7 +107,7 @@ import { } from '@/api/model' import { fetchTenants } from '@/api/tenant' import { ElMessage } from 'element-plus' -import type { ModelResponse } from '@/api/model/types' +import type { ModelResponse, ModelItem } from '@/api/model/types' interface Tenant { id: string @@ -130,27 +130,67 @@ const assignForm = ref({ const searchTenants = async () => { try { - const res = await fetchTenants({ search: searchQuery.value }) - searchedTenants.value = res.tenants - } catch (error) { - ElMessage.error('查询租户失败') + const tenants = await fetchTenants({ search: searchQuery.value }) + searchedTenants.value = tenants + if (tenants.length === 0) { + ElMessage.warning('未找到匹配的租户') + } + } catch (error: any) { + if (error.response?.status === 404) { + searchedTenants.value = [] + ElMessage.warning('未找到匹配的租户') + } else { + ElMessage.error('查询租户失败') + } } } const showTenantModels = async (tenantId: string) => { try { - const res = await getTenantModels(tenantId) - models.value = res.data.models - modelDialogVisible.value = true - } catch (error) { - ElMessage.error('加载模型列表失败') + console.log('开始请求模型列表,租户ID:', tenantId) + const res = await getTenantModels(tenantId).catch(err => { + console.error('获取模型列表失败:', err) + console.error('错误详情:', err.response?.data || err.message) + throw err + }) + console.log('完整API响应:', JSON.stringify(res, null, 2)) + console.log('响应数据:', res.data) + console.log('响应数据.models:', res.data?.models) + if (res?.data?.models?.models) { + models.value = res.data.models.models.map((m: ModelItem) => ({ + id: m.id, + model_name: m.model_name, + provider_name: m.provider_name, + model_type: m.model_type, + created_at: m.created_at, + updated_at: m.updated_at + })) + modelDialogVisible.value = true + } else { + ElMessage.error('获取模型列表失败: 返回数据格式不正确') + } + } catch (error: any) { + ElMessage.error('加载模型列表失败: ' + + (error.response?.data?.message || + error.message || + '未知错误')) } } const handleTenantChange = async (tenantId: string) => { try { const res = await getTenantModels(tenantId) - models.value = res.data.models + if (res?.data?.models?.models) { + models.value = res.data.models.models.map((m: ModelItem) => ({ + id: m.id, + model_name: m.model_name, + provider_name: m.provider_name, + model_type: m.model_type, + created_at: m.created_at + })) + } else { + ElMessage.error('获取模型列表失败: 返回数据格式不正确') + } } catch (error) { ElMessage.error('加载模型列表失败') } @@ -182,7 +222,17 @@ const loadModels = async () => { return } const res = await getTenantModels(currentTenantId) - models.value = res.data.models + if (res?.data?.models?.models) { + models.value = res.data.models.models.map((m: ModelItem) => ({ + id: m.id, + model_name: m.model_name, + provider_name: m.provider_name, + model_type: m.model_type, + created_at: m.created_at + })) + } else { + ElMessage.error('获取模型列表失败: 返回数据格式不正确') + } } catch (error) { ElMessage.error('加载模型列表失败') } @@ -190,8 +240,7 @@ const loadModels = async () => { const loadTenants = async () => { try { - const res = await fetchTenants({}) - tenants.value = res.tenants + tenants.value = await fetchTenants({}) } catch (error) { ElMessage.error('加载租户列表失败') }