- company.notifications 테이블 + NotificationType(SUCCESS/REGENERATED/FAILURE) - 백엔드 알림 조회/읽음 API + close_and_decide 결과 분기마다 알림 생성 - 헤더 알림 벨(안읽음 배지) + 알림 페이지(읽음·딥링크) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
89 lines
3.4 KiB
Python
89 lines
3.4 KiB
Python
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
|