Đặc tả: Livestream Feature
Module: livestreamweb (port 8086) + batch + firebaselistener
Status: Production
1. Tổng quan
Feature livestream cung cấp APIs cho viewer xem và tương tác với streamer qua Agora video, quản lý gift aggregation qua Redis/ActiveMQ pipeline, và đồng bộ realtime data với Firebase RTDB.
2. Phạm vi
In-scope - REST APIs (livestreamweb module)
LivestreamController (/api/livestream)
| Endpoint |
Method |
Mô tả |
/ |
GET |
Lấy danh sách livestream (paginated, với filters) |
/viewer-get-livestream/{id} |
GET |
Chi tiết livestream + Agora info + Firebase data |
/get-by-user-id |
GET |
Streamer lấy livestream của mình |
/{id} |
GET |
Get livestream by ID |
/get-agora-info-by-channel-name/{channelName} |
GET |
Sub channel Agora info |
/get-agora-info-by-opponent-id/{id} |
GET |
Agora info của đối thủ |
/push-opponent/{id} |
PUT |
Streamer mời đối thủ vào livestream |
/remove-opponent/{id} |
PUT |
Xóa đối thủ khỏi livestream |
/room-sync-register/{id} |
PUT |
Viewer đăng ký sync room |
/sync-register/{id} |
PUT |
Sync register v2 |
/room-sync-cancel/{id} |
PUT |
Viewer cancel sync room |
/sync-room-info/{id} |
PUT |
Sync room info |
/sync-user-info |
PUT |
Sync user info |
/user-sync-balance/{id} |
PUT |
Sync user balance cho livestream |
/top-sync-viewers/{id} |
GET |
Top viewers theo total sync |
/agora/app-id |
GET |
Lấy Agora App ID |
/authorize-firebase-uid |
PUT |
Authorize/deauthorize Firebase UID |
LivestreamGiftController (/api/livestream-gift)
| Endpoint |
Method |
Mô tả |
| (gift sending endpoints) |
POST |
Gửi gift cho streamer |
In-scope - Async pipeline
- Gift aggregation: Redis counter →
GiftAggregationJob (every 5s) → ActiveMQ → JMSLivestreamViewerGiftFirebaseUpdateListener → Firebase
- Firebase listeners (module
firebaselistener): commands, event_types, livestream_events, yell_points_history
Out-of-scope
| Mục |
Lý do |
| Agora SDK integration setup |
SDK-level config |
| Firebase project setup |
Infrastructure |
| Streamer management (KYC) |
Admin domain |
3. User stories
| ID |
Role |
Story |
Acceptance |
| LIVE-US-01 |
Viewer |
Xem livestream list → click vào → nhận Agora token → xem video |
Agora token hợp lệ, channel connected |
| LIVE-US-02 |
Streamer |
Mở app → nhận Agora publisher token + OBS settings |
Token với ROLE_PUBLISHER |
| LIVE-US-03 |
Viewer |
Gửi gift → B-Coin trừ ngay, streamer counter tăng (real-time Firebase) |
Counter Firebase updated |
| LIVE-US-04 |
Viewer |
Burst gift trong 5 giây → Redis aggregate → 1 batch message per streamer |
Không race condition balance |
| LIVE-US-05 |
Viewer |
Đăng ký sync room → xem game realtime của streamer |
Sync data available |
| LIVE-US-06 |
Streamer |
Mời opponent vào Agora channel |
Opponent sub-token generated |
4. Functional requirements
| ID |
Requirement |
Chi tiết |
| LIVE-F-01 |
Agora token - viewer |
generateToken(channel, uid, ROLE_SUBSCRIBER), uid từ userService.getCurrentUserIndex(userId) |
| LIVE-F-02 |
Agora token - streamer |
4 UIDs: camera, screen, view, voice. ROLE_SUBSCRIBER (streamer subscribes multiple feeds) |
| LIVE-F-03 |
OBS settings |
getObsSetting: token với ROLE_PUBLISHER + appId |
| LIVE-F-04 |
Firebase data |
getLivestreamDataByChildNodes(id) fetch viewer count, gifts, etc. với caching |
| LIVE-F-05 |
Streamer points |
leaderboardLivestreamStreamerMonthlyService.getTotalPointsInMonth() inject vào response |
| LIVE-F-06 |
Gift aggregation |
Redis key: livestream:pending_gifts:{streamerId}, INCRBY per gift |
| LIVE-F-07 |
Gift batch |
GiftAggregationJob fixedDelay=5000ms, GETSET atomic, send với JMSXGroupID=streamerId |
| LIVE-F-08 |
Sequential per streamer |
JMSXGroupID=streamerId đảm bảo message order per streamer |
| LIVE-F-09 |
Room sync |
roomSyncRegisterCommandHandler.handleCore() + roomSyncCancelCommandHandler.handleCore() |
| LIVE-F-10 |
Firebase ban check |
Async fire-and-forget check getBannedLivestreamsByUserIdAsync() (non-blocking) |
5. Business rules
- Agora UID format:
- Camera:
userIndex (raw)
- Screen:
UID_SCREEN_PREFIX + userIndex
- View:
UID_VIEW_PREFIX + UID_SCREEN
- Voice:
UID_VOICE_PREFIX + UID_SCREEN
- Sub-camera (viewer):
UID_SUB_CAMERA_PREFIX + userIndex
- Gift aggregation fixedDelay:
${jobs.gift-aggregation.fixedDelay} (default 5000ms).
- Streamer points: tổng tháng hiện tại từ
leaderboard_livestream_streamer_monthly.
viewer-get-livestream/{id} endpoint có performance logging chi tiết (LOGGER.error level → review nên dùng level thấp hơn).
6. Data contracts
Response - ViewerGetLivestreamResp
{
"id": "<livestreamId>",
"title": "<title>",
"streamer_id": "<userId>",
"channel_name": "BTH_Live_<streamerEmail>",
"status": "NEW | STREAMING | ENDED",
"agora": {
"uid": 12345,
"token": "<agoraToken>",
"channel_name": "BTH_Live_..."
},
"livestream_viewer": {
"total_bcoins": 5000,
"streamer_points": 1200,
"wish_orb": { "current": 30, "max": 100 }
}
}
Response - StreamerGetLiveStreamRes
{
"id": "<livestreamId>",
"agora_streamer": {
"uid_camera": 100,
"uid_screen": 1000100,
"uid_view": 2000100,
"uid_vtube": 3000100,
"token_camera": "...",
"token_screen": "...",
"token_view": "...",
"token_voice": "...",
"channel_name": "BTH_Live_..."
},
"obs_setting": {
"token_host": "<publisherToken>",
"app_id": "<agoraAppId>"
}
}
Gift aggregation message (LivestreamGiftAggregateMessageDto)
{
"streamer_id": "<streamerId>",
"total_bcoin_value": 500,
"timestamp": "2024-01-01T00:00:00Z"
}
7. Acceptance criteria
- [ ]
viewer-get-livestream/{id} trả đúng Agora token viewer + Firebase data
- [ ] Streamer endpoint trả 4 Agora tokens đúng role/uid
- [ ] Gift burst 100 requests trong 5s → Redis aggregate → 1 JMS message per streamer
- [ ] Hai streamer nhận gift đồng thời → parallel processing (khác
JMSXGroupID)
- [ ] Cùng streamer → sequential processing (cùng
JMSXGroupID)
- [ ] Firebase counters update đúng sau gift JMS processed
- [ ] Ban check async không block
viewer-get-livestream response time
- [ ] OBS setting token có
ROLE_PUBLISHER, viewer token có ROLE_SUBSCRIBER
8. Constraints
- Agora credentials cached (
@Cacheable) để tránh DB call per request.
viewer-get-livestream có performance log toàn bộ steps (log level LOGGER.error đang dùng cho debug → cần cleanup).
getBannedLivestreamsByUserIdAsync() là fire-and-forget, không affect main response.
9. Code references
livestreamweb/
controllers/livestream/LivestreamController.java
controllers/livestream_gift/LivestreamGiftController.java
services/agora/AgoraTokenService.java
batch/
job/livestream/GiftAggregationJob.java
jms/JMSLivestreamViewerGiftFirebaseUpdateListener.java
firebaselistener/
listener/FirebaseCommandListener.java
listener/FirebaseEventTypesListener.java
listener/FirebaseLivestreamEventListener.java
listener/FirebaseLivestreamsYellPointListener.java
application-core/
service/livestream/LivestreamService.java
service/livestream_firebase/LivestreamFirebaseService.java
firebase/command/handler/RoomSyncRegisterCommandHandler.java
firebase/command/handler/RoomSyncCancelCommandHandler.java