B1: 项目脚手架 + 数据模型 + 租户管理 - Task 1.1: FastAPI 项目脚手架、SQLite + async SQLAlchemy - Task 1.2: 7 个数据模型 (Tenant, TenantConfig, DigitalEmployee, Conversation, Message, KnowledgeBase, Document) - Task 1.3: 租户 CRUD API + LLM 配置(含 API Key AES 加密) B2: 数字员工配置 + LLM Provider 抽象层 - Task 2.1: 数字员工 CRUD API(关联知识库) - Task 2.2: BaseLLMProvider 抽象接口 + OpenAI/Qwen Provider - Task 2.3: Provider 动态实例化 + test-provider 端点 验证: 26 个测试全部通过 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
129 lines
3.8 KiB
Python
129 lines
3.8 KiB
Python
from fastapi import APIRouter, Depends, status
|
|
from pydantic import BaseModel
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.api.deps import get_db
|
|
from app.schemas.tenant import (
|
|
TenantConfigCreate,
|
|
TenantConfigResponse,
|
|
TenantCreate,
|
|
TenantResponse,
|
|
TenantUpdate,
|
|
)
|
|
from app.services import tenant_service
|
|
|
|
router = APIRouter(prefix="/tenants", tags=["tenants"])
|
|
|
|
|
|
@router.post("", response_model=TenantResponse, status_code=status.HTTP_201_CREATED)
|
|
async def create_tenant(
|
|
data: TenantCreate, session: AsyncSession = Depends(get_db)
|
|
) -> TenantResponse:
|
|
tenant = await tenant_service.create_tenant(session, data.name, data.slug)
|
|
return TenantResponse(
|
|
id=tenant.id,
|
|
name=tenant.name,
|
|
slug=tenant.slug,
|
|
status=tenant.status.value,
|
|
created_at=tenant.created_at,
|
|
updated_at=tenant.updated_at,
|
|
)
|
|
|
|
|
|
@router.get("", response_model=list[TenantResponse])
|
|
async def list_tenants(session: AsyncSession = Depends(get_db)) -> list[TenantResponse]:
|
|
tenants = await tenant_service.list_tenants(session)
|
|
return [
|
|
TenantResponse(
|
|
id=t.id,
|
|
name=t.name,
|
|
slug=t.slug,
|
|
status=t.status.value,
|
|
created_at=t.created_at,
|
|
updated_at=t.updated_at,
|
|
)
|
|
for t in tenants
|
|
]
|
|
|
|
|
|
@router.get("/{tenant_id}", response_model=TenantResponse)
|
|
async def get_tenant(
|
|
tenant_id: str, session: AsyncSession = Depends(get_db)
|
|
) -> TenantResponse:
|
|
tenant = await tenant_service.get_tenant(session, tenant_id)
|
|
return TenantResponse(
|
|
id=tenant.id,
|
|
name=tenant.name,
|
|
slug=tenant.slug,
|
|
status=tenant.status.value,
|
|
created_at=tenant.created_at,
|
|
updated_at=tenant.updated_at,
|
|
)
|
|
|
|
|
|
@router.patch("/{tenant_id}", response_model=TenantResponse)
|
|
async def update_tenant(
|
|
tenant_id: str,
|
|
data: TenantUpdate,
|
|
session: AsyncSession = Depends(get_db),
|
|
) -> TenantResponse:
|
|
tenant = await tenant_service.update_tenant(session, tenant_id, data.name, data.slug)
|
|
return TenantResponse(
|
|
id=tenant.id,
|
|
name=tenant.name,
|
|
slug=tenant.slug,
|
|
status=tenant.status.value,
|
|
created_at=tenant.created_at,
|
|
updated_at=tenant.updated_at,
|
|
)
|
|
|
|
|
|
@router.delete("/{tenant_id}", status_code=status.HTTP_204_NO_CONTENT)
|
|
async def delete_tenant(tenant_id: str, session: AsyncSession = Depends(get_db)) -> None:
|
|
await tenant_service.delete_tenant(session, tenant_id)
|
|
|
|
|
|
@router.put("/{tenant_id}/config", response_model=TenantConfigResponse)
|
|
async def update_tenant_config(
|
|
tenant_id: str,
|
|
data: TenantConfigCreate,
|
|
session: AsyncSession = Depends(get_db),
|
|
) -> TenantConfigResponse:
|
|
config = await tenant_service.update_tenant_config(
|
|
session,
|
|
tenant_id,
|
|
data.llm_provider,
|
|
data.llm_api_key,
|
|
data.llm_model,
|
|
data.llm_base_url,
|
|
data.max_tokens_per_month or 1000000,
|
|
)
|
|
return TenantConfigResponse(
|
|
id=config.id,
|
|
tenant_id=config.tenant_id,
|
|
llm_provider=config.llm_provider,
|
|
llm_model=config.llm_model,
|
|
llm_base_url=config.llm_base_url,
|
|
max_tokens_per_month=config.max_tokens_per_month,
|
|
created_at=config.created_at,
|
|
updated_at=config.updated_at,
|
|
)
|
|
|
|
|
|
class ProviderTestResponse(BaseModel):
|
|
provider: str
|
|
model: str
|
|
status: str
|
|
|
|
|
|
@router.post("/{tenant_id}/test-provider", response_model=ProviderTestResponse)
|
|
async def test_provider(
|
|
tenant_id: str, session: AsyncSession = Depends(get_db)
|
|
) -> ProviderTestResponse:
|
|
provider = await tenant_service.get_provider_for_tenant(session, tenant_id)
|
|
return ProviderTestResponse(
|
|
provider=provider.__class__.__name__.replace("Provider", "").lower(),
|
|
model=provider.model,
|
|
status="ok",
|
|
)
|