from __future__ import annotations from .shared import * def _utc_now_naive() -> datetime: return now_cn_naive() async def _ensure_project_signal_link( db: AsyncSession, *, tenant_id: int, project_id: int, signal_id: int, link_type: str = "related", ) -> None: existing = ( await db.execute( select(ProjectSignalLink.id).where( and_( ProjectSignalLink.tenant_id == int(tenant_id), ProjectSignalLink.project_id == int(project_id), ProjectSignalLink.signal_id == int(signal_id), ) ).limit(1) ) ).scalar_one_or_none() if existing is not None: return db.add( ProjectSignalLink( tenant_id=int(tenant_id), project_id=int(project_id), signal_id=int(signal_id), link_type=link_type, relevance_score=1.0, ) ) def _parse_related_users(raw_value: Any) -> List[str]: """兼容 involved_user_ids 的 list / json-string / 单值字符串。""" if raw_value is None: return [] if isinstance(raw_value, list): return [str(item) for item in raw_value if item is not None] if isinstance(raw_value, (bytes, bytearray)): raw_value = raw_value.decode("utf-8", errors="ignore") if isinstance(raw_value, str): text = raw_value.strip() if not text: return [] try: parsed = json.loads(text) except (TypeError, ValueError, json.JSONDecodeError): if "," in text: return [v.strip() for v in text.split(",") if v.strip()] return [text] if isinstance(parsed, list): return [str(item) for item in parsed if item is not None] if parsed is None: return [] return [str(parsed)] return [str(raw_value)]