| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495 |
- """站内通知服务:列表、未读数、全部已读与待办处理。"""
- from __future__ import annotations
- from datetime import UTC, datetime
- from sqlalchemy import func, select, update
- from dms.common.errors import ConflictError, InvalidArgumentError, ResourceNotFoundError
- from dms.database.transaction import transaction
- from dms.extensions import db
- from dms.models.notification import Notification
- # 列表返回条数上限:通知为待办提醒而非历史归档,固定取最近一批即可
- LIST_LIMIT = 200
- HANDLE_ACTIONS = ("AGREE", "REJECT")
- def _format_utc(value: datetime | None) -> str | None:
- if value is None:
- return None
- return (
- value.replace(tzinfo=UTC)
- .isoformat(timespec="milliseconds")
- .replace("+00:00", "Z")
- )
- def _serialize(item: Notification) -> dict[str, object]:
- return {
- "id": str(item.id),
- "title": item.title,
- "content": item.content,
- "notifyType": item.notify_type,
- "action": item.action,
- "status": item.status,
- "createdAt": _format_utc(item.created_at),
- "handledAt": _format_utc(item.handled_at),
- }
- def list_notifications(user_id: int) -> dict[str, object]:
- """当前用户通知列表(id 倒序,最近 LIMIT 条)。"""
- items = db.session.scalars(
- select(Notification)
- .where(Notification.user_id == user_id)
- .order_by(Notification.id.desc())
- .limit(LIST_LIMIT)
- ).all()
- return {"items": [_serialize(item) for item in items]}
- def unread_count(user_id: int) -> dict[str, object]:
- """未读数(红圈徽标数据源)。"""
- count = db.session.scalar(
- select(func.count())
- .select_from(Notification)
- .where(Notification.user_id == user_id, Notification.status == "UNREAD")
- )
- return {"count": int(count or 0)}
- def mark_all_read(user_id: int) -> dict[str, object]:
- """全部标记已读(打开通知页后清红圈)。"""
- with transaction() as session:
- result = session.execute(
- update(Notification)
- .where(Notification.user_id == user_id, Notification.status == "UNREAD")
- .values(status="READ")
- )
- return {"updated": int(result.rowcount or 0)}
- def handle_notification(user_id: int, notification_id: int, action: str) -> dict[str, object]:
- """处理待办通知:仅 PENDING 且归属本人的通知可同意/拒绝。"""
- if action not in HANDLE_ACTIONS:
- raise InvalidArgumentError("action必须是AGREE或REJECT")
- with transaction() as session:
- item = session.scalar(
- select(Notification).where(
- Notification.id == notification_id,
- Notification.user_id == user_id,
- )
- )
- if item is None:
- raise ResourceNotFoundError("通知不存在")
- if item.action != "PENDING":
- raise ConflictError("该通知已处理或无需处理")
- item.action = action
- item.status = "READ"
- item.handled_at = datetime.now(UTC).replace(tzinfo=None)
- handled = _serialize(item)
- return handled
|