import uuid from fastapi import Depends from common.database.db_session_manager import DB_SESSION_MNG from common.database.model.models import notifications from common.enums import DBWRType, ErrorType, NotificationType from common.logger import LOG from common.models.gmodel import PageParams from common.utils.gtime import GTime from crud.notification_crud import INotificationCRUD, NotificationCRUD from router.v1.notification.protocol import NotificationData, Res_NotificationList, Res_NotificationRead def _as_uuid(v): return uuid.UUID(v) if isinstance(v, str) else v async def create_notification( user_id, ntype: NotificationType, data: dict, ref_qt_id=None, ref_session_id=None, ) -> None: """알림 1건 기록(인박스). 수신자=user_id(견적 작성자), data=렌더 스냅샷(유형별 필드). 부가 효과라 실패해도 본 흐름을 막지 않는다(raise 안 함, 로그만).""" notif = notifications( user_id=_as_uuid(user_id), type=ntype.value, ref_qt_id=_as_uuid(ref_qt_id) if ref_qt_id else None, ref_session_id=_as_uuid(ref_session_id) if ref_session_id else None, data=data, ) err = await DB_SESSION_MNG.execute_lambda_run( [notifications.DBType()], [lambda s: DB_SESSION_MNG.insert(s, notif, raise_error=False)], ) if err != ErrorType.SUCCESS: LOG.e_no_callstack(f"[notify] 알림 기록 실패 user={user_id} type={ntype.name}") class NotificationService: """알림 조회/읽음 처리(헤더 벨·알림 페이지). 항상 로그인 유저(user_id)로 스코프.""" def __init__(self, crud: INotificationCRUD = Depends(NotificationCRUD)): self.crud = crud async def list_notifications(self, user_id: str, pg: PageParams) -> Res_NotificationList: res = Res_NotificationList(page=pg.page, size=pg.size) uid = uuid.UUID(user_id) err_type, rows, total = await DB_SESSION_MNG.execute_lambda( notifications.DBType(), DBWRType.DB_READ.value, lambda s: self.crud.list_for_user(s, uid, pg.skip, pg.size), ) if err_type != ErrorType.SUCCESS: res.result.SetResult(err_type) return res res.notifications = [NotificationData.model_validate(r) for r in rows] res.total = total _e, unread = await DB_SESSION_MNG.execute_lambda( notifications.DBType(), DBWRType.DB_READ.value, lambda s: self.crud.count_unread(s, uid), ) res.unread = unread if _e == ErrorType.SUCCESS else 0 return res async def mark_read(self, user_id: str, notification_id: str) -> Res_NotificationRead: res = Res_NotificationRead() err_type = await DB_SESSION_MNG.execute_lambda_run( [notifications.DBType()], [lambda s: self.crud.mark_read(s, uuid.UUID(user_id), uuid.UUID(notification_id), GTime.UTC())], ) if err_type != ErrorType.SUCCESS: res.result.SetResult(err_type) return res async def mark_all_read(self, user_id: str) -> Res_NotificationRead: res = Res_NotificationRead() err_type = await DB_SESSION_MNG.execute_lambda_run( [notifications.DBType()], [lambda s: self.crud.mark_all_read(s, uuid.UUID(user_id), GTime.UTC())], ) if err_type != ErrorType.SUCCESS: res.result.SetResult(err_type) return res