88 lines
3.5 KiB
Python
88 lines
3.5 KiB
Python
"""CRUD API for the global business → agent / chain mapping (业务链条管理).
|
|
|
|
Binds a fixed business code (3Q / 6BF / 7BF / 8BF) to an optional 智能体 and an
|
|
optional 业务链条 (a **public** ``roundtable_chains`` row). Used to auto-pick the
|
|
business chain when a task page launches the multi-agent roundtable.
|
|
|
|
Global / shared — there is no user scoping; all accounts see and edit the same
|
|
rows. The four business rows are seeded on startup; this API only lists them and
|
|
updates the agent / chain columns (no create/delete).
|
|
|
|
Routes (prefix ``/api/business-mapping``):
|
|
GET "" list all business mappings (display order)
|
|
PUT "/{business_code}" update one row's agent / chain selection
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from datetime import datetime
|
|
|
|
from fastapi import APIRouter, HTTPException, Request
|
|
from pydantic import BaseModel, Field
|
|
|
|
logger = logging.getLogger(__name__)
|
|
router = APIRouter(prefix="/api/business-mapping", tags=["business-mapping"])
|
|
|
|
|
|
# ── schemas ────────────────────────────────────────────────────────────────
|
|
|
|
|
|
class BusinessMappingResponse(BaseModel):
|
|
business_code: str
|
|
label: str = ""
|
|
agent_id: str | None = None
|
|
agent_name: str | None = None
|
|
chain_id: str | None = None
|
|
chain_title: str | None = None
|
|
sort_order: int = 0
|
|
created_at: datetime | str | None = None
|
|
updated_at: datetime | str | None = None
|
|
|
|
|
|
class BusinessMappingListResponse(BaseModel):
|
|
mappings: list[BusinessMappingResponse]
|
|
|
|
|
|
class BusinessMappingUpdateRequest(BaseModel):
|
|
# All optional → callers send only what changed; explicit null clears a field.
|
|
agent_id: str | None = Field(default=None, max_length=64)
|
|
agent_name: str | None = Field(default=None, max_length=191)
|
|
chain_id: str | None = Field(default=None, max_length=64)
|
|
chain_title: str | None = Field(default=None, max_length=512)
|
|
|
|
|
|
# ── helpers ──────────────────────────────────────────────────────────────
|
|
|
|
|
|
def _get_store(request: Request):
|
|
store = getattr(request.app.state, "business_mapping_store", None)
|
|
if store is None:
|
|
raise HTTPException(status_code=503, detail="Business mapping store not available")
|
|
return store
|
|
|
|
|
|
# ── routes ───────────────────────────────────────────────────────────────
|
|
|
|
|
|
@router.get("", response_model=BusinessMappingListResponse)
|
|
async def list_mappings(request: Request) -> BusinessMappingListResponse:
|
|
store = _get_store(request)
|
|
rows = await store.list_mappings()
|
|
return BusinessMappingListResponse(
|
|
mappings=[BusinessMappingResponse(**r) for r in rows]
|
|
)
|
|
|
|
|
|
@router.put("/{business_code}", response_model=BusinessMappingResponse)
|
|
async def update_mapping(
|
|
request: Request, business_code: str, body: BusinessMappingUpdateRequest
|
|
) -> BusinessMappingResponse:
|
|
store = _get_store(request)
|
|
# Only forward fields the caller explicitly sent (distinguish omitted vs null).
|
|
fields = body.model_dump(exclude_unset=True)
|
|
row = await store.update_mapping(business_code, **fields)
|
|
if row is None:
|
|
raise HTTPException(status_code=404, detail="Business mapping not found")
|
|
return BusinessMappingResponse(**row)
|