Appearance
Runtime And Delivery — Technical Map
FE runtime map
| Surface | File | Vai trò |
|---|---|---|
| Bell + badge | NotificationButton.tsx | Load unread aggregate, cache count, mở drawer |
| Personal list | NotificationList.tsx | Query notification_personal, mark read / read all, route resolve |
| Runtime popup | NotificationListener.tsx | Subcribe onMessage() rồi bắn QNotify |
| Vendor wrapper | useOneSignal.ts | init, subscribe, unsubscribe, sendTags, getTags, deleteTags |
| Boot entry | src/boot/onesignal.ts | Init OneSignal khi app khởi động |
| Local cache | useNotificationStore.ts | Persist unreadCount, lastRequest vào localStorage |
Backend runtime map
| Layer | File / object | Vai trò |
|---|---|---|
| Push delivery | notification-api/messenger/messenger.go | Apply template, chọn vendor, send/update |
| Push insert event | notification-api/event/message.go | notification_send trên insert bảng notification |
| Push cron | notification-api/scheduler/notification_send.go | Quét notification chưa sent_at |
| Cleanup cron | notification-api/scheduler/notification_cleanup.go | Xóa notification cũ theo batch |
| Email immediate/save | notification-api/action/send_email.go | Load template, apply vars, insert/send |
| SMS immediate/save | notification-api/action/send_sms.go | Load template, apply vars, insert/send |
| ZNS insert event | notification-v2-api/event/zns_request_insert.go | Quyết định gửi ngay hay schedule |
| Deferred queue | scheduled_tasks + message_scheduler/*.go | Heap-based in-memory scheduler cho task trong ngày |
Data / read model map
| Object | Vai trò |
|---|---|
notification_personal | Function lấy inbox cá nhân theo session |
notification_personal_stats | Aggregate unread/read stats |
notification_user | Read/delete state per user cho push/in-app |
message_user_view | Unified read model cho message logs |
notification_queue | Queue metadata-driven cho một số SMS/ZNS flows |
device_token | Device token DB-side, có event device_token_register |
scheduled_tasks | Persisted future task store cho v2 scheduler |
zns_request, zns_user | ZNS request + recipient logs |
sms_request, sms_user | SMS request + recipient logs |
Deep-link / routing map
| Kỹ thuật | Ghi chú |
|---|---|
NOTIFICATION_TYPE | Map subject_type -> route template cho customer, appointment, order, ticket, approval, project task, inventory transfer... |
NotificationList.handleView() | Switch lớn để build RouteLocationRaw từ payload data |
| Payload fields | Một số route cần thêm order_kind, customer_id, project_id, task_id, recordId ngoài subject_id |
Rủi ro / Findings kỹ thuật
| Mức | Finding |
|---|---|
| Cao | notification_send event trigger chạy trên mọi insert của notification, và sendNotification() gọi Messenger.SendAndUpdate() ngay lập tức. Scheduler notification_send cũng tồn tại cho bản ghi sent_at IS NULL. Điều này xác nhận runtime có hai cơ chế send chồng nhau, trong đó insert-event path đang bypass semantics send_after. |
| Cao | NotificationButton.reloadCount() throttling cứng 60 giây bằng localStorage; unread badge có thể stale ngắn hạn nếu cùng user thao tác trên tab/session khác. |
| Cao | NotificationList.handleView() là switch cross-module rất lớn; chỉ cần route constant đổi ở module khác là notification runtime có thể điều hướng sai mà không có compiler boundary rõ. |
| Trung bình | device_token và OneSignal external user binding là hai lớp identity/runtime khác nhau; source hiện không cho thấy một abstraction thống nhất giữa web push vendor và DB token registry. |
| Trung bình | scheduled_task_insert chỉ hot-load task trong ngày; task xa hơn được để lại cho lần day rollover, nên debugging deferred send phải nhìn cả DB lẫn in-memory queue. |