Tích hợp Thông báo vào AI Chat
Hướng dẫn tích hợp menu thông báo và luồng Notification-to-Chat cho ứng dụng mobile/web.
Tổng quan
Khi người dùng click vào một thông báo, ứng dụng mở màn hình chat với nội dung thông báo được stream trực tiếp, kèm theo các nút CTA (Call-to-Action) để người dùng tương tác tiếp.
┌─────────────────────┐ ┌──────────────────────────────┐ ┌────────────────────────┐
│ Notification List │────▶│ Chat Draft (stream content) │────▶│ Chat Session (agent) │
│ GET /list │ │ WS: fetch_context │ │ WS: chat + source │
└─────────────────────┘ └──────────────────────────────┘ └────────────────────────┘
API Endpoints
1. Lấy danh sách thông báo
GET /api/notification/v2/{appCode}/message/list
| Param | Type | Required | Description |
|---|---|---|---|
| appCode | string | path | Mã ứng dụng: nebula |
| groupType | string | query | Lọc theo nhóm: signal (sự kiện), other (hệ thống), trống = tất cả |
| page | number | query | Trang (default: 0) |
| size | number | query | Số lượng (default: 20, max: 100) |
Response:
{
"status": 200,
"data": [
{
"id": "69cb3e99ef4af67a8ae4d72a",
"type": "company_insider_transaction",
"title": "Nebula AI",
"summary": "Chủ tịch HQC gom thêm 23,6 triệu cổ phiếu khi giá ở đáy 52 tuần",
"readStatus": false,
"isLatest": true,
"createdDate": 1774060800,
"ticker": "HQC",
"iconType": "company_insider_transaction"
}
],
"total": 42
}
2. Đếm thông báo chưa đọc (Badge)
GET /api/notification/v2/{appCode}/message/count
Response: { "status": 200, "data": 5 }
3. Đánh dấu đã xem (Clear badge)
POST /api/notification/v2/{appCode}/message/update-view-all
Gọi khi người dùng mở panel thông báo. Xóa badge count.
4. Đánh dấu đã đọc (1 item)
POST /api/notification/v2/{appCode}/message/update-read/{id}
5. Đánh dấu tất cả đã đọc
POST /api/notification/v2/{appCode}/message/update-read-all
Tích hợp Chat
Flow chi tiết
Bước 1: Mở Notification Draft
Khi click vào notification item, navigate tới chat với source params:
Web: /chat?source=notification&source_id={notification_id}
App: Mở màn hình chat, truyền source="notification", source_id={notification_id}
Bước 2: Stream nội dung qua WebSocket
Sau khi WebSocket connected + authenticated, gửi event fetch_context:
{
"type": "fetch_context",
"source": "notification",
"source_id": "69cb3e99ef4af67a8ae4d72a"
}
Server sẽ stream response theo SSE protocol (có event_id):
// 1. Message start
{
"type": "message_start",
"message_type": "context",
"source": "notification",
"source_id": "69cb3e99ef4af67a8ae4d72a",
"message_id": "0b1a7d03-6e81-49d8-aa60-41683860c178",
"event_id": "evt-1"
}
// 2. Text content (chunked - typing effect)
{ "type": "content_block_start", "index": 0, "content_block": { "type": "text" }, "event_id": "evt-2" }
{ "type": "content_block_delta", "index": 0, "delta": { "type": "text_delta", "text": "Chủ tịch HQC " }, "event_id": "evt-3" }
{ "type": "content_block_delta", "index": 0, "delta": { "type": "text_delta", "text": "gom thêm 23,6 " }, "event_id": "evt-4" }
// ... more text chunks
{ "type": "content_block_stop", "index": 0, "event_id": "evt-N" }
// 3. CTA block (single event, full JSON)
{
"type": "content_block_start",
"index": 1,
"content_block": {
"type": "cta",
"intro": "Bạn muốn tìm hiểu thêm gì?",
"items": [
{ "label": "Định giá: Cổ phiếu đang đắt hay rẻ?", "description": "Biết giá trị thực để mua/bán có cơ sở" },
{ "label": "Xu hướng giá & Thời điểm hành động", "description": "Xác định vùng hỗ trợ, kháng cự" },
{ "label": "Gợi ý phân bổ vốn & quản lý rủi ro", "description": "Xác định tỷ trọng an toàn" }
]
},
"event_id": "evt-CTA"
}
{ "type": "content_block_stop", "index": 1, "event_id": "evt-CTA-stop" }
// 4. Message stop
{ "type": "message_stop", "event_id": "evt-end" }
Lưu ý quan trọng:
- Text chunks stream giống agent response bình thường (typing effect)
- CTA block chỉ chứa
label+description, không chứaprompt(prompt được resolve ở backend) - CTA items render dạng buttons cho user chọn
Bước 3: User tương tác
Khi user click CTA hoặc gõ text, gửi WS chat message kèm source và source_id:
{
"type": "chat",
"message": "Định giá: Cổ phiếu đang đắt hay rẻ?\nBiết giá trị thực để mua/bán có cơ sở",
"source": "notification",
"source_id": "69cb3e99ef4af67a8ae4d72a"
}
Backend sẽ:
- Tạo session mới
- Lưu notification content vào history (AI message)
- Inject notification context vào agent (hidden system message)
- Match CTA prompt nếu user chọn CTA
- Agent xử lý và stream response
Sau khi session tạo, nhận new_session_id từ stream events → cập nhật URL/state.
Bước 4: Chat tiếp tục
Sau khi agent respond, user có thể chat tiếp bình thường trong session đó. Notification context đã được lưu trong checkpoint.
UI Guidelines
Notification List Panel
┌──────────────────────────────┐
│ ← Thông báo ② ☑ │ ← Back, title, badge, mark-all-read
├──────────────────────────────┤
│ [Tất cả] [Sự kiện] [Hệ thống] │ ← Tabs (groupType filter)
├──────────────────────────────┤
│ Summary notification 1 ● │ ← Unread dot
│ 23 phút trước │
├──────────────────────────────┤
│ Summary notification 2 │ ← Read (no dot)
│ 2 giờ trước │
├──────────────────────────────┤
│ ... │
└──────────────────────────────┘
Notification Draft View
┌──────────────────────────────┐
│ [Notification content text │ ← Streamed with typing effect
│ displayed as AI message] │
│ │
│ ┌────────────────────────┐ │
│ │ Định giá: Cổ phiếu... │ │ ← CTA buttons
│ │ Biết giá trị thực... │ │
│ └────────────────────────┘ │
│ ┌────────────────────────┐ │
│ │ Xu hướng giá & ... │ │
│ │ Xác định vùng hỗ trợ │ │
│ └────────────────────────┘ │
│ │
│ [Input: Hỏi bất kỳ điều gì] │ ← Chat input (user có thể gõ tự do)
└──────────────────────────────┘
Badge
- Bell icon hiện badge count khi > 0
- Count > 10 hiện
10+ - Mở panel → gọi
update-view-all→ badge reset về 0
Tab Filters
| Tab | groupType param | Mô tả |
|---|---|---|
| Tất cả | (trống) | Tất cả thông báo |
| Sự kiện | signal | Tín hiệu, sự kiện doanh nghiệp |
| Hệ thống | other | Thông báo hệ thống |
Xử lý Edge Cases
| Case | Xử lý |
|---|---|
| Notification bị xóa/hết hạn | fetch_context trả text lỗi, không có CTA |
| User gõ text thay vì click CTA | Vẫn hoạt động, source_id gửi kèm → agent có context |
F5 trên URL ?source=notification | Show skeleton loading → init xong → stream content |
| Click notification khác khi đang stream | Reset state + navigate mới + stream content mới |
| Notification không có CTA | Chỉ hiện content text, user gõ tự do |