@
feat(圆桌): 业务链条显示配置人 + 配置页拆两步并新增精美流程图 - 列表/弹窗卡片显示「配置人」(后端把 owner user_id 解析为 email, ChainResponse 增 owner_id/owner_name,store 输出 owner_id) - 配置编辑器拆为两步:第一步基本信息(描述改多行文本),第二步链条编排 - 第一步有编排内容时,左侧基本信息 + 右侧 ChainFlowDiagram 流程图: 直角连线、点节点开详情抽屉、连线显示阶段目标(依赖)、滚轮缩放、 拖动平移、全屏 + 全屏右下角缩略图 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> @
This commit is contained in:
1 parent
e9f44c8f0f
commit
146c8e76f0
7 files changed
+693
-91
No files matched your search
@@ -23,7 +23,7 @@ from fastapi import APIRouter, Depends, HTTPException, Request
|
||||
from langchain_core.messages import HumanMessage, SystemMessage
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from app.gateway.deps import get_config
|
||||
from app.gateway.deps import get_config, get_local_provider
|
||||
from deerflow.config.app_config import AppConfig
|
||||
from deerflow.models import create_chat_model
|
||||
from deerflow.runtime.user_context import get_effective_user_id
|
||||
@@ -61,6 +61,10 @@ class ChainResponse(BaseModel):
|
||||
is_public: bool = False
|
||||
# 是否当前用户所有(仅本人可编辑 / 删除 / 发布;公共筛选里别人的链条 is_mine=False)。
|
||||
is_mine: bool = True
|
||||
# 配置者(owner)的 user_id 及解析后的可读用户名(email)。owner_name 由路由层
|
||||
# 用 LocalAuthProvider 解析;解析失败时为 None,前端回退到短 id 展示。
|
||||
owner_id: str | None = None
|
||||
owner_name: str | None = None
|
||||
created_at: datetime | str | None = None
|
||||
updated_at: datetime | str | None = None
|
||||
|
||||
@@ -103,6 +107,31 @@ def _current_user_id(request: Request) -> str:
|
||||
return get_effective_user_id()
|
||||
|
||||
|
||||
async def _enrich_owner_names(rows: list[dict]) -> None:
|
||||
"""把每行的 ``owner_id`` 解析成可读用户名(email),写入 ``owner_name``。
|
||||
|
||||
与 knowledge 路由的 ``_enrich_actor_names`` 同思路:批量解析、按 id 缓存、best-effort
|
||||
(解析失败 / provider 不可用时 ``owner_name`` 留空,前端回退到短 id)。
|
||||
"""
|
||||
try:
|
||||
provider = get_local_provider()
|
||||
except Exception:
|
||||
return
|
||||
cache: dict[str, str | None] = {}
|
||||
for row in rows:
|
||||
uid = row.get("owner_id")
|
||||
if not uid:
|
||||
row["owner_name"] = None
|
||||
continue
|
||||
if uid not in cache:
|
||||
try:
|
||||
user = await provider.get_user(uid)
|
||||
cache[uid] = user.email if user else None
|
||||
except Exception:
|
||||
cache[uid] = None
|
||||
row["owner_name"] = cache[uid]
|
||||
|
||||
|
||||
def _get_store(request: Request):
|
||||
store = getattr(request.app.state, "roundtable_chain_store", None)
|
||||
if store is None:
|
||||
@@ -211,6 +240,7 @@ async def list_chains(request: Request, scope: str = "mine") -> ChainListRespons
|
||||
store = _get_store(request)
|
||||
user_id = _current_user_id(request)
|
||||
rows = await store.list_chains(user_id, scope=normalized_scope)
|
||||
await _enrich_owner_names(rows)
|
||||
return ChainListResponse(chains=[ChainResponse(**r) for r in rows])
|
||||
|
||||
|
||||
@@ -233,6 +263,7 @@ async def create_chain(request: Request, body: ChainCreateRequest) -> ChainRespo
|
||||
"coordinator_prompt": body.coordinator_prompt,
|
||||
},
|
||||
)
|
||||
await _enrich_owner_names([row])
|
||||
return ChainResponse(**row)
|
||||
|
||||
|
||||
@@ -243,6 +274,7 @@ async def get_chain(request: Request, chain_id: str) -> ChainResponse:
|
||||
row = await store.get_chain(chain_id, user_id)
|
||||
if row is None:
|
||||
raise HTTPException(status_code=404, detail="Chain not found")
|
||||
await _enrich_owner_names([row])
|
||||
return ChainResponse(**row)
|
||||
|
||||
|
||||
@@ -265,6 +297,7 @@ async def update_chain(request: Request, chain_id: str, body: ChainUpdateRequest
|
||||
row = await store.update_chain(chain_id, user_id, **fields)
|
||||
if row is None:
|
||||
raise HTTPException(status_code=404, detail="Chain not found")
|
||||
await _enrich_owner_names([row])
|
||||
return ChainResponse(**row)
|
||||
|
||||
|
||||
|
||||
+2
@@ -59,6 +59,8 @@ class RoundtableChainRepository:
|
||||
# 发布状态 + 是否本人所有(前端据此显示「公共」徽标、决定能否编辑 / 发布)。
|
||||
"is_public": bool(getattr(row, "is_public", False)),
|
||||
"is_mine": (current_user_id is None) or (row.user_id == current_user_id),
|
||||
# 配置者(owner)的 user_id;路由层据此解析成可读用户名(owner_name)。
|
||||
"owner_id": row.user_id,
|
||||
"created_at": row.created_at.isoformat() if isinstance(row.created_at, datetime) else row.created_at,
|
||||
"updated_at": row.updated_at.isoformat() if isinstance(row.updated_at, datetime) else row.updated_at,
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user