WELIFE Docs Swagger Admin Web

Hệ thống thông báo WELIFE

Domain: D-NOTIF · WP: WP-NOTIF-HUB-01 · Cập nhật: 2026-08-17 Surfaces: CON · PAR · DRV · ADM · API Audit + implement: reports/WP-NOTIF-HUB-01.md

Một cửa NotificationsService.dispatch. Vertical không tự appNotification.create. SMS không dùng cho booking (chỉ OTP D-AUTH).


1. Nguyên tắc thống nhất (call · chat · push · inbox)

Kênh Khi nào Không dùng cho
Inbox app_notifications Mọi sự kiện người cần đọc lại GPS ping (ride:location)
WS Socket.IO App đang mở, sự kiện realtime Promo / social (không có room)
Push Expo App nền / không trong conversation GPS; chat nếu muted; user tắt prefs (trừ call + bảo mật)
Chuông gọi call:ring + push high Cuộc gọi đến Booking / order
SMS OTP đăng nhập (D-AUTH) Booking, ride, chat, call nhỡ

Thứ tự quyết định (hiệu quả, tránh spam):

  1. Sự kiện thuộc lớp telemetry (GPS) → chỉ WS. Dừng.
  2. Ghi inbox nếu policy inbox=true.
  3. Emit WS (vertical giữ: message:new · call:ring · ride:status).
  4. Push nếu: policy.push AND (prefs bật HOẶC pushEvenIfDisabled) AND (không mute conversation).
  5. Không SMS.

Call và chat không trộn kind: cuộc gọi đến = CALL_INCOMING (không còn SYSTEM). Tin nhắn = CHAT_MESSAGE. Nhỡ = CALL_MISSED.


2. Sơ đồ kiến trúc

flowchart TB
  subgraph Events["Sự kiện nghiệp vụ"]
    B[Booking hotel/park/rent/med/school]
    R[Ride match / pickup / complete]
    O[Order FNB/grocery]
    C[Chat message]
    K[Call ring / missed]
    S[Social / bảo mật / admin]
  end

  H[Notif Hub dispatch]
  B --> H
  R --> H
  O --> H
  C --> H
  K --> H
  S --> H

  subgraph Policy["Catalog policy"]
    P1[inbox?]
    P2[push? mute prefs]
    P3[sfx + deepLink]
  end
  H --> Policy

  Inbox[(app_notifications)]
  Expo[Expo Push]
  WS[Socket.IO rooms]
  SMS[eSMS — chỉ OTP ngoài hub]

  P1 -->|yes| Inbox
  P2 -->|yes| Expo
  R -.->|ride:status / GPS| WS
  C -.->|message:new| WS
  K -.->|call:ring| WS
  SMS -.-> AuthOTP[Auth OTP riêng]

3. Sơ đồ tuần tự — đặt xe (mẫu mọi booking)

sequenceDiagram
  autonumber
  actor U as CON
  actor D as DRV
  participant API as Nest vertical
  participant Hub as Notif Hub
  participant WS as Socket.IO
  participant Push as Expo

  U->>API: POST /rides (hoặc booking)
  API->>API: persist Ride/Booking
  API->>WS: ride:status CONFIRMED
  API->>Hub: dispatch ride.matched CON+DRV
  Hub->>Hub: prefs + mute
  Hub->>Hub: insert inbox
  Hub->>Push: Expo high / channel welife-rides
  Push-->>U: banner + sfx notif_ride
  Push-->>D: banner (token DRV)
  U->>U: tap → deepLink RideDetail

4. Sơ đồ phân luồng call vs chat vs booking

flowchart TD
  E[Sự kiện] --> T{Loại?}
  T -->|GPS| WSonly[Chỉ WS ride:location]
  T -->|Call incoming| CallPath
  T -->|Chat message| ChatPath
  T -->|Ride status / booking / order| BizPath
  T -->|OTP| SmsPath[SmsService eSMS — ngoài hub]

  CallPath --> Inbox1[Inbox CALL_INCOMING]
  CallPath --> WS1[WS call:ring]
  CallPath --> Push1[Push high — kể cả tắt notif chung]
  CallPath --> Sfx1[call_ringtone_incoming]

  ChatPath --> Inbox2[Inbox CHAT_MESSAGE]
  ChatPath --> WS2[WS message:new]
  ChatPath --> Mute{Conversation muted?}
  Mute -->|yes| NoPush[Không push]
  Mute -->|no| Prefs1{prefs on?}
  Prefs1 -->|yes| Push2[Push notif_message]
  Prefs1 -->|no| NoPush

  BizPath --> Inbox3[Inbox RIDE/ORDER/BOOKING]
  BizPath --> WS3[WS nếu có room]
  BizPath --> Prefs2{prefs on?}
  Prefs2 -->|yes| Push3[Push channel theo lớp]
  Prefs2 -->|no| SkipPush[Inbox only]

5. Ma trận sự kiện × kênh × surface

Event Kind CON PAR DRV Inbox Push WS SFX
chat.message CHAT_MESSAGE ● ○ ○ ● nếu không mute message:new notif_message
call.incoming CALL_INCOMING ● — — ● luôn high call:ring call_ringtone_incoming
call.missed CALL_MISSED ● — — ● ● call:ended call_missed
ride.matched RIDE ● — ● ● ● ride:status ride_driver_assigned
ride.arrived RIDE ● — — ● ● ride:status ride_driver_assigned
ride.pickup RIDE ● — — ● ● ride:status ride_trip_started
ride.completed RIDE ● — ● ● ● ride:status ride_trip_completed
ride.cancelled RIDE ● — ● ● ● ride:status notif_ride
ride:location — ● — ● — — ride:location —
order.created ORDER ● ● owner — ● ● — notif_order
order.confirmed ORDER ● — — ● ● — notif_order
order.cancelled ORDER ● ● — ● ● — notif_order
booking.hotel.* BOOKING ● ● org — ● ● — notif_order
booking.parking.* BOOKING ● ● org — ● ● — notif_order
booking.rental.viewing* BOOKING ● ● landlord — ● ● — notif_order
booking.medical.created BOOKING ● ● org — ● ● — notif_order
booking.school.created BOOKING ● ● org — ● ● — notif_order
booking.homeservice.created BOOKING ● ● org — ● ● — notif_order
rental.deposit.* WALLET ● ● — ● ● — notif_payment
social.* SOCIAL_* ● — — ● ● — notif_default
security.device SYSTEM ● — — ● luôn — notif_urgent
admin.broadcast SYSTEM ● ● ● ● ● — notif_promo
Auth OTP — ● ● ● — — — SMS brand WELIFE

○ = cùng user JWT nếu họ dùng app đó (token chung theo userId).


6. Payload thống nhất (data)

{
  "event": "ride.matched",
  "kind": "RIDE",
  "notificationId": "uuid",
  "entityId": "ride-id",
  "sfx": "ride_driver_assigned",
  "deepLink": {
    "tab": "MapTab",
    "screen": "RideDetail",
    "params": { "rideId": "…" }
  },
  "surface": "CON"
}

FE CON: tap push / «Mở chi tiết» → openNotifDeepLink (không mở DriverInTrip). FE DRV: tap push / inbox Cài đặt → InTripScreen (driver/). OS sound Expo vẫn default (custom .caf phải bundle APK — SOUND-PACK). data.sfx để foreground play sau này.


7. Deep-link theo surface

Event CON DRV
chat / call ChatTab · ChatThread / Call (incoming) / CallDetail (missed) Chat inbox (app tài xế)
ride.matched MapTab · RideDetail InTripScreen (app tài xế)
ride.arrived MapTab · RideDetail — (chỉ khách)
ride.pickup MapTab · RideDetail — (chỉ khách)
ride.completed / cancelled RideDetail InTrip / lịch sử
order.* MapTab · OrderTrack —
hotel MapTab · HotelBookings —
parking MapTab · ParkingBookings —
rental viewing MapTab · RentalViewings —
rental deposit MapTab · RentalDeposit —
medical MapTab · MedicalAppointments —
school MapTab · SchoolConsultations —
homeservice MapTab · HomeServicesBookings —
social SocialTab —
security AccountTab · AuthSessions —

8. Code map

Vai trò Path
Catalog + policy api/src/modules/notifications/notif-catalog.ts
Dispatch api/src/modules/notifications/notifications.service.ts
REST GET/PATCH /notifications · POST /push-tokens
CON inbox mobile/src/features/notifications/
CON deep-link mobile/src/features/notifications/notifDeepLink.ts
CON/DRV/PAR token registerExpoPushToken sau login

Gọi từ vertical: notifications.dispatch / dispatchFanout (CON + partner owner + driver).


9. Việc còn lại (không chặn hub)

WP Việc
WP-NOTIF-PRESENCE-01 PARTIAL — skip push chat nếu đang trong conv: room. Ride room chưa
WP-NOTIF-SFX-BUNDLE-01 Bundle notif_*.caf vào EAS APK
WP-NOTIF-ANDROID-CH-01 IMP trong WP-CHAT-CALL-LOCK-01 (welife-calls/chat/rides/orders)
WP-NOTIF-PREFS-CH-01 Prefs theo kênh (chat / ride / order) thay vì 1 bit
WP-NOTIF-VOIP-01 BLOCKED — cấm thiết bị thật (CallKit / ConnectionService / EAS device)