311 lines
8.7 KiB
Python
311 lines
8.7 KiB
Python
"""
|
|
Metadata Schema API.
|
|
动态元数据模式管理接口。
|
|
"""
|
|
|
|
import logging
|
|
from typing import Annotated, Any
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
from fastapi.responses import JSONResponse
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.core.database import get_session
|
|
from app.models.entities import (
|
|
MetadataField,
|
|
MetadataSchema,
|
|
MetadataSchemaCreate,
|
|
MetadataSchemaUpdate,
|
|
)
|
|
from app.services.metadata_schema_service import MetadataSchemaService
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
router = APIRouter(prefix="/admin/metadata-schemas", tags=["Metadata Schemas"])
|
|
|
|
|
|
def get_current_tenant_id() -> str:
|
|
"""Get current tenant ID from context."""
|
|
from app.core.tenant import get_tenant_id
|
|
tenant_id = get_tenant_id()
|
|
if not tenant_id:
|
|
from app.core.exceptions import MissingTenantIdException
|
|
raise MissingTenantIdException()
|
|
return tenant_id
|
|
|
|
|
|
@router.get(
|
|
"",
|
|
operation_id="listMetadataSchemas",
|
|
summary="List metadata schemas",
|
|
description="获取租户所有元数据模式配置",
|
|
)
|
|
async def list_schemas(
|
|
tenant_id: Annotated[str, Depends(get_current_tenant_id)],
|
|
session: Annotated[AsyncSession, Depends(get_session)],
|
|
include_disabled: bool = False,
|
|
) -> JSONResponse:
|
|
"""
|
|
列出租户所有元数据模式
|
|
"""
|
|
service = MetadataSchemaService(session)
|
|
schemas = await service.list_schemas(tenant_id, include_disabled)
|
|
|
|
return JSONResponse(
|
|
content={
|
|
"schemas": [
|
|
{
|
|
"id": str(s.id),
|
|
"name": s.name,
|
|
"description": s.description,
|
|
"fields": s.fields,
|
|
"isDefault": s.is_default,
|
|
"isEnabled": s.is_enabled,
|
|
"createdAt": s.created_at.isoformat(),
|
|
"updatedAt": s.updated_at.isoformat(),
|
|
}
|
|
for s in schemas
|
|
]
|
|
}
|
|
)
|
|
|
|
|
|
@router.get(
|
|
"/default",
|
|
operation_id="getDefaultMetadataSchema",
|
|
summary="Get default metadata schema",
|
|
description="获取租户默认的元数据模式配置",
|
|
)
|
|
async def get_default_schema(
|
|
tenant_id: Annotated[str, Depends(get_current_tenant_id)],
|
|
session: Annotated[AsyncSession, Depends(get_session)],
|
|
) -> JSONResponse:
|
|
"""
|
|
获取租户默认的元数据模式
|
|
"""
|
|
service = MetadataSchemaService(session)
|
|
schema = await service.get_schema(tenant_id)
|
|
|
|
if not schema:
|
|
return JSONResponse(
|
|
content={
|
|
"schema": None,
|
|
"message": "No default schema configured",
|
|
}
|
|
)
|
|
|
|
return JSONResponse(
|
|
content={
|
|
"schema": {
|
|
"id": str(schema.id),
|
|
"name": schema.name,
|
|
"description": schema.description,
|
|
"fields": schema.fields,
|
|
"isDefault": schema.is_default,
|
|
"isEnabled": schema.is_enabled,
|
|
"createdAt": schema.created_at.isoformat(),
|
|
"updatedAt": schema.updated_at.isoformat(),
|
|
}
|
|
}
|
|
)
|
|
|
|
|
|
@router.get(
|
|
"/{schema_id}",
|
|
operation_id="getMetadataSchema",
|
|
summary="Get metadata schema by ID",
|
|
description="根据 ID 获取元数据模式配置",
|
|
)
|
|
async def get_schema(
|
|
tenant_id: Annotated[str, Depends(get_current_tenant_id)],
|
|
session: Annotated[AsyncSession, Depends(get_session)],
|
|
schema_id: str,
|
|
) -> JSONResponse:
|
|
"""
|
|
根据 ID 获取元数据模式
|
|
"""
|
|
service = MetadataSchemaService(session)
|
|
schema = await service.get_schema(tenant_id, schema_id)
|
|
|
|
if not schema:
|
|
raise HTTPException(status_code=404, detail="Schema not found")
|
|
|
|
return JSONResponse(
|
|
content={
|
|
"schema": {
|
|
"id": str(schema.id),
|
|
"name": schema.name,
|
|
"description": schema.description,
|
|
"fields": schema.fields,
|
|
"isDefault": schema.is_default,
|
|
"isEnabled": schema.is_enabled,
|
|
"createdAt": schema.created_at.isoformat(),
|
|
"updatedAt": schema.updated_at.isoformat(),
|
|
}
|
|
}
|
|
)
|
|
|
|
|
|
@router.post(
|
|
"",
|
|
operation_id="createMetadataSchema",
|
|
summary="Create metadata schema",
|
|
description="创建新的元数据模式配置",
|
|
)
|
|
async def create_schema(
|
|
tenant_id: Annotated[str, Depends(get_current_tenant_id)],
|
|
session: Annotated[AsyncSession, Depends(get_session)],
|
|
schema_create: MetadataSchemaCreate,
|
|
) -> JSONResponse:
|
|
"""
|
|
创建元数据模式
|
|
"""
|
|
service = MetadataSchemaService(session)
|
|
|
|
for field in schema_create.fields:
|
|
if isinstance(field, MetadataField):
|
|
field_dict = field.model_dump()
|
|
else:
|
|
field_dict = field
|
|
|
|
field_type = field_dict.get("field_type", "string")
|
|
if field_type in ["select", "multi_select"]:
|
|
if not field_dict.get("options"):
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail=f"Field '{field_dict.get('name')}' is {field_type} type but has no options"
|
|
)
|
|
|
|
schema = await service.create_schema(tenant_id, schema_create)
|
|
await session.commit()
|
|
|
|
return JSONResponse(
|
|
status_code=201,
|
|
content={
|
|
"id": str(schema.id),
|
|
"name": schema.name,
|
|
"description": schema.description,
|
|
"fields": schema.fields,
|
|
"isDefault": schema.is_default,
|
|
"isEnabled": schema.is_enabled,
|
|
}
|
|
)
|
|
|
|
|
|
@router.put(
|
|
"/{schema_id}",
|
|
operation_id="updateMetadataSchema",
|
|
summary="Update metadata schema",
|
|
description="更新元数据模式配置",
|
|
)
|
|
async def update_schema(
|
|
tenant_id: Annotated[str, Depends(get_current_tenant_id)],
|
|
session: Annotated[AsyncSession, Depends(get_session)],
|
|
schema_id: str,
|
|
schema_update: MetadataSchemaUpdate,
|
|
) -> JSONResponse:
|
|
"""
|
|
更新元数据模式
|
|
"""
|
|
service = MetadataSchemaService(session)
|
|
schema = await service.update_schema(tenant_id, schema_id, schema_update)
|
|
|
|
if not schema:
|
|
raise HTTPException(status_code=404, detail="Schema not found")
|
|
|
|
await session.commit()
|
|
|
|
return JSONResponse(
|
|
content={
|
|
"id": str(schema.id),
|
|
"name": schema.name,
|
|
"description": schema.description,
|
|
"fields": schema.fields,
|
|
"isDefault": schema.is_default,
|
|
"isEnabled": schema.is_enabled,
|
|
}
|
|
)
|
|
|
|
|
|
@router.delete(
|
|
"/{schema_id}",
|
|
operation_id="deleteMetadataSchema",
|
|
summary="Delete metadata schema",
|
|
description="删除元数据模式配置",
|
|
)
|
|
async def delete_schema(
|
|
tenant_id: Annotated[str, Depends(get_current_tenant_id)],
|
|
session: Annotated[AsyncSession, Depends(get_session)],
|
|
schema_id: str,
|
|
) -> JSONResponse:
|
|
"""
|
|
删除元数据模式
|
|
"""
|
|
service = MetadataSchemaService(session)
|
|
success = await service.delete_schema(tenant_id, schema_id)
|
|
|
|
if not success:
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail="Cannot delete schema (not found or is default)"
|
|
)
|
|
|
|
await session.commit()
|
|
|
|
return JSONResponse(
|
|
content={
|
|
"success": True,
|
|
"message": "Schema deleted"
|
|
}
|
|
)
|
|
|
|
|
|
@router.get(
|
|
"/default/field-definitions",
|
|
operation_id="getFieldDefinitions",
|
|
summary="Get field definitions",
|
|
description="获取字段定义映射,用于前端动态渲染表单",
|
|
)
|
|
async def get_field_definitions(
|
|
tenant_id: Annotated[str, Depends(get_current_tenant_id)],
|
|
session: Annotated[AsyncSession, Depends(get_session)],
|
|
schema_id: str | None = None,
|
|
) -> JSONResponse:
|
|
"""
|
|
获取字段定义映射
|
|
"""
|
|
service = MetadataSchemaService(session)
|
|
field_defs = await service.get_field_definitions(tenant_id, schema_id)
|
|
|
|
return JSONResponse(
|
|
content={
|
|
"fieldDefinitions": field_defs
|
|
}
|
|
)
|
|
|
|
|
|
@router.post(
|
|
"/default/validate",
|
|
operation_id="validateMetadata",
|
|
summary="Validate metadata",
|
|
description="验证元数据是否符合模式定义",
|
|
)
|
|
async def validate_metadata(
|
|
tenant_id: Annotated[str, Depends(get_current_tenant_id)],
|
|
session: Annotated[AsyncSession, Depends(get_session)],
|
|
metadata: dict[str, Any],
|
|
schema_id: str | None = None,
|
|
) -> JSONResponse:
|
|
"""
|
|
验证元数据
|
|
"""
|
|
service = MetadataSchemaService(session)
|
|
is_valid, errors = await service.validate_metadata(tenant_id, metadata, schema_id)
|
|
|
|
return JSONResponse(
|
|
content={
|
|
"isValid": is_valid,
|
|
"errors": errors
|
|
}
|
|
)
|