优化中心联系人展示名称解析

- 新增中心联系人展示名解析服务,支持从逗号分隔用户 ID 解析姓名\n- 中心列表接口返回 contact_display,避免前端依赖项目成员权限才能展示联系人姓名\n- 中心管理和立项会议授权页面优先使用后端联系人展示名\n- 补充联系人解析服务测试和立项会议授权页面展示顺序测试
This commit is contained in:
Cheng Zhou
2026-06-04 16:30:17 +08:00
parent d6452e3f9d
commit 4ea0e88c98
7 changed files with 118 additions and 1 deletions
+13 -1
View File
@@ -10,8 +10,10 @@ from app.crud import audit as audit_crud
from app.crud import site as site_crud
from app.crud import startup as startup_crud
from app.crud import study as study_crud
from app.crud import user as user_crud
from app.schemas.site import SiteCreate, SiteRead, SiteUpdate
from app.schemas.startup import StartupEthicsCreate, StartupFeasibilityCreate
from app.services.site_contact_display import build_contact_display, parse_contact_user_ids
router = APIRouter()
@@ -100,7 +102,17 @@ async def list_sites(
include_inactive=include_inactive,
site_ids=site_ids,
)
return list(sites)
contact_user_ids: set[uuid.UUID] = set()
for site in sites:
contact_user_ids.update(parse_contact_user_ids(site.contact))
users_map = await user_crud.get_users_by_ids(db, contact_user_ids)
result: list[SiteRead] = []
for site in sites:
item = SiteRead.model_validate(site)
item.contact_display = build_contact_display(site.contact, users_map)
result.append(item)
return result
@router.patch(
+1
View File
@@ -37,6 +37,7 @@ class SiteRead(BaseModel):
pi_name: Optional[str]
phone: Optional[str]
contact: Optional[str]
contact_display: Optional[str] = None
is_active: bool
enrollment_target: Optional[int]
enrollment_plan_start_date: Optional[date]
@@ -0,0 +1,45 @@
from __future__ import annotations
import uuid
from typing import Mapping
def parse_contact_user_ids(contact: str | None) -> set[uuid.UUID]:
ids: set[uuid.UUID] = set()
for token in _contact_tokens(contact):
try:
ids.add(uuid.UUID(token))
except ValueError:
continue
return ids
def build_contact_display(contact: str | None, users: Mapping[uuid.UUID, object]) -> str | None:
labels: list[str] = []
for token in _contact_tokens(contact):
try:
user_id = uuid.UUID(token)
except ValueError:
labels.append(token)
continue
user = users.get(user_id)
label = _user_display_name(user)
if label:
labels.append(label)
return "".join(labels) if labels else None
def _contact_tokens(contact: str | None) -> list[str]:
return [part.strip() for part in str(contact or "").split(",") if part.strip()]
def _user_display_name(user: object | None) -> str | None:
if user is None:
return None
for attr in ("full_name", "username", "email"):
value = getattr(user, attr, None)
if value:
return str(value)
return None