从零用 Java 实现小红书 SpringBoot Vue UniApp(17)TRTC 直播完整实现
·
移动端演示 http://8.146.211.120:8081/#/
管理端演示 http://8.146.211.120:8088/#/
项目整体介绍及演示
前言
商城解决「买」,IM 解决「聊」,直播则把两者拉到同一现场:主播边讲边卖、观众边看边互动。早期小红书教程系列通常停在内容与购物,直播是本章相对原教程全新加入的能力。
本章基于腾讯云 TRTC,实现:
- 视频直播 / 语音直播
- 公屏弹幕、在线人数、点赞
- 主播心跳与超时自动关播
- 轻量直播货架(讲解商品浮卡)
- 体验版时长限制 + 页面中部倒计时







一、业务架构
直播挂在「笔记」之上:一条直播笔记同时承载内容属性与房间属性。
创作者中心 / 发布入口
↓
创建直播笔记(liveStatus=直播中)
↓
GET /member/note/live/credentials
(sdkAppId / userId / userSig / roomId / maxMinutes)
↓
主播推流 / 观众拉流 + WebSocket 公屏
↓
主动结束 / 心跳超时 / 体验到期 → 结束页
为什么选 TRTC
- 音视频质量稳定,Web / App 都有成熟 SDK
- UserSig 必须后端签发,密钥不进前端
- 与下一章「聊天室」共用同一套 TRTC 配置,运维成本低
二、进房凭证(核心代码)
MemberNoteLiveServiceImpl#getCredentials:校验直播中 → 签发 UserSig → 返回体验到期时间。
public AppMemberNoteLiveCredentialsRespVO getCredentials(Long loginUserId, Long noteId) {
MemberNoteDO note = requireLiveNote(noteId);
boolean anchor = Objects.equals(note.getUserId(), loginUserId);
if (!Objects.equals(note.getLiveStatus(), MemberNoteLiveStatusEnum.LIVING.getStatus())) {
throw exception(NOTE_LIVE_NOT_LIVING);
}
// 体验时长到期:先关播再拒绝进房
if (endIfExperienceExpired(note)) {
throw exception(NOTE_LIVE_NOT_LIVING);
}
long sdkAppId = requireSdkAppId();
String secretKey = requireSecretKey();
String trtcUserId = toTrtcUserId(loginUserId);
String userSig = new TLSSigAPIv2(sdkAppId, secretKey)
.genUserSig(trtcUserId, USER_SIG_EXPIRE_SECONDS);
AppMemberNoteLiveCredentialsRespVO resp = new AppMemberNoteLiveCredentialsRespVO();
resp.setSdkAppId(sdkAppId);
resp.setUserId(trtcUserId);
resp.setUserSig(userSig);
resp.setRoomId(StrUtil.blankToDefault(note.getLiveRoomId(), buildRoomId(note.getId())));
resp.setAnchor(anchor);
// ...
int maxMinutes = getLiveMaxMinutes();
resp.setMaxMinutes(maxMinutes);
if (maxMinutes > 0 && note.getLiveStartTime() != null) {
resp.setLiveExpireTime(note.getLiveStartTime().plusMinutes(maxMinutes));
}
return resp;
}
主播心跳(杀进程后靠 Job 扫超时关播):
public void heartbeat(Long loginUserId, Long noteId) {
MemberNoteDO note = requireLiveNote(noteId);
if (!Objects.equals(note.getUserId(), loginUserId)) {
throw exception(NOTE_LIVE_ONLY_OWNER);
}
touchOwnerHeartbeat(note.getId());
presenceRedisDAO.add(BIZ_LIVE, String.valueOf(noteId), loginUserId);
}
对应 HTTP:
GET /member/note/live/credentialsPOST /member/note/live/heartbeatPOST /member/note/live/end
三、公屏走业务 WebSocket(不走私信)
直播互动类型:
String MEMBER_NOTE_LIVE_SUBSCRIBE = "member_note_live_subscribe_type";
String MEMBER_NOTE_LIVE_CHAT = "member_note_live_chat_type";
String MEMBER_NOTE_LIVE_ONLINE = "member_note_live_online_type";
String MEMBER_NOTE_LIVE_ENDED = "member_note_live_ended_type";
String MEMBER_NOTE_LIVE_GOODS = "member_note_live_goods_type";
String MEMBER_NOTE_LIVE_BUY = "member_note_live_buy_type";
流程:发送聊天 → 敏感词过滤 → 写入 live_chat_json → 推房间内在线用户。在线人数用 Redis 统计,主播可禁言。
四、体验倒计时(前端)
配置:member.live.max-minutes(默认 30,0=不限制)。
export function createExperienceCountdown(options) {
const expireAtMs = Number(options?.expireAtMs) || 0;
const onTick = options?.onTick;
const onExpire = options?.onExpire;
let timer = null;
const tick = () => {
const remain = Math.max(0, Math.ceil((expireAtMs - Date.now()) / 1000));
if (typeof onTick === 'function') onTick(remain, formatRemain(remain));
if (remain <= 0) {
clearInterval(timer);
if (typeof onExpire === 'function') onExpire();
}
};
return {
start() {
tick();
timer = setInterval(tick, 1000);
},
stop() {
if (timer) clearInterval(timer);
},
};
}
// 进房后
experienceCountdown = createExperienceCountdown({
expireAtMs: parseTimeMs(data.liveExpireTime),
onTick: (remain, text) => {
state.remainSec = remain;
state.countdownText = text;
},
onExpire: () => onForceEnded('体验时长已到,自动关闭。如需体验请联系管理员', true),
});
experienceCountdown.start();
页面中部浮层显示「体验剩余 MM:SS」,最后 1 分钟标红。
五、前端信息架构
| 区域 | 内容 |
|---|---|
| 顶栏 | 返回、主播胶囊、关注、在线人数 |
| 中部 | 视频画面 / 语音头像舞台;体验倒计时浮层 |
| 左侧 | 货架短轨(最多 3 个) |
| 下部 | 公屏滚动 + 输入框 |
| 底部操作 | 静音、摄像头、翻转、耳返、扬声器、分享、更多 |
- H5:TRTC Web SDK 直连
- App:
web-view加载hybrid/html/trtc-live.html
分享可复制链接,或发给互关好友(直播邀请卡片,详见(19))。
六、数据库与配置
member_note 直播字段(节选):
live_room_id/live_mode/live_statuslive_start_time/live_end_time/live_peak_onlinelive_heartbeat_time/live_chat_jsonlive_goods_json/live_explain_spu_id
| config_key | 含义 |
|---|---|
member.trtc.sdk-app-id | TRTC SDKAppID |
member.trtc.secret-key | TRTC 密钥(仅后端) |
member.live.max-minutes | 单场最长分钟 |
member.live.chat-sensitive-words | 公屏敏感词 |
一键部署:sql/mysql/xhs-init.sql
关键代码位置
- 后端:
MemberNoteLiveServiceImpl.java、TLSSigAPIv2.java、MemberLiveHeartbeatTimeoutJob.java - 移动端:
pages/note/live.vue、pages/note/live-end.vue - Hybrid:
hybrid/html/trtc-live.html - 倒计时:
sheep/helper/experienceCountdown.js
总结
- UserSig 后端签发,前端零密钥
- 「媒体用 TRTC + 互动用 WebSocket」分层清晰
- 体验倒计时把运营策略产品化,而不是只做硬关房
下一章预告
直播是「一人对多人」。下一章实现 音视频聊天室:8 麦位、上麦治理、视频/语音双模式。
演示地址
移动端:http://8.146.211.120:8081/#/
管理端:http://8.146.211.120:8088/#/
更多推荐


所有评论(0)