"""站内通知(M2 起用,M4 会接入外部推送)。 三条原则: 1. **不通知自己**。绝大多数通知都是由自己的操作触发的, 不排除自己会让"未读"迅速变成一个没人看的数字。 2. **通知里写清"谁、做了什么、在哪件事上"**。只写"状态已更新"等于没写, 用户还得点进去猜。 3. **通知只是提示,不是凭证**。点开后仍要按 `item_id` 重新拉数据, 因为通知生成之后事情可能又被改过。 """ from __future__ import annotations import uuid from collections.abc import Iterable from sqlalchemy.ext.asyncio import AsyncSession from ..models_ticket import Notification TICKET_STATUS_CN = { "open": "待接收", "accepted": "待办理", "in_progress": "办理中", "pending_verify": "待验收", "done": "已完成", "canceled": "已取消", } TASK_STATUS_CN = { "inbox": "收件箱", "todo": "待办", "doing": "进行中", "blocked": "被阻塞", "done": "已完成", "canceled": "已取消", } async def notify( session: AsyncSession, *, ws_id: uuid.UUID, user_ids: Iterable[uuid.UUID | None], kind: str, item_id: uuid.UUID | None, title: str, body: str = "", actor_id: uuid.UUID | None = None, ) -> int: """给若干人各写一条通知,返回实际写入条数。 去重与"跳过自己"在这里统一做一次——每个调用点都手写一遍迟早漏一个。 """ seen: set[uuid.UUID] = set() count = 0 for user_id in user_ids: if user_id is None or user_id == actor_id or user_id in seen: continue seen.add(user_id) session.add( Notification( workspace_id=ws_id, user_id=user_id, kind=kind, item_id=item_id, title=title[:120], body=body[:300], ) ) count += 1 return count