Files
wzdj/lib/api/live.ts
卡若 b95d1a94c6 feat(live): 直播与主播工作台 API、云厂商解析与文档调研
- 新增直播评论/上下文/礼物等 API 与回放组件
- 主播开播、云端推流配置与 streamer 校验
- 依赖与 Mongo 集合/种子更新
- 需求与调研文档补充

Made-with: Cursor
2026-04-14 16:24:17 +08:00

117 lines
3.6 KiB
TypeScript

/**
* 直播上下文 API 客户端(房间、橱窗、风控词、公会分成提示)
*/
export interface LiveShowcaseItemDTO {
id: string
title: string
subtitle?: string
landingPath: string
refQuery: string
}
export interface LiveContextData {
streamer: {
id: string
name: string
isLive: boolean
commissionRate: number
guildId: string | null
}
guild: { id: string; name: string; commissionRate: number } | null
room: {
id: string
title: string
status: string
game: string
viewerCount: number
pullPlaybackUrl: string
roomMode: string
} | null
showcase: LiveShowcaseItemDTO[]
forbiddenWords: string[]
revenueHint: string
}
export function buildShowcaseHref(origin: string, item: LiveShowcaseItemDTO): string {
const path = item.landingPath.startsWith("/") ? item.landingPath : `/${item.landingPath}`
const q = item.refQuery.startsWith("?") ? item.refQuery.slice(1) : item.refQuery
return `${origin.replace(/\/$/, "")}${path}?${q}`
}
export async function getLiveContext(streamerId: string): Promise<LiveContextData | null> {
const res = await fetch(`/api/live/context/${streamerId}`, { cache: "no-store" })
const json = (await res.json().catch(() => ({}))) as {
success?: boolean
data?: LiveContextData
message?: string
}
if (!res.ok || !json.success || !json.data) return null
return json.data
}
export interface LiveCommentDTO {
id: string
userId: string
userName: string
content: string
type: "normal" | "enter" | "gift" | "system"
level: number
createdAt: string
}
export async function getLiveComments(streamerId: string): Promise<LiveCommentDTO[]> {
const res = await fetch(`/api/live/comments/${streamerId}`, { cache: "no-store" })
const json = (await res.json().catch(() => ({}))) as {
success?: boolean
data?: { comments?: LiveCommentDTO[] }
}
if (!res.ok || !json.success || !json.data?.comments) return []
return json.data.comments
}
export async function postLiveComment(
streamerId: string,
body: { content: string; type?: "normal" },
): Promise<{ ok: boolean; message?: string }> {
const res = await fetch(`/api/live/comments/${streamerId}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ content: body.content, type: body.type ?? "normal" }),
})
const json = (await res.json().catch(() => ({}))) as { success?: boolean; message?: string }
if (!res.ok || !json.success) {
return { ok: false, message: json.message ?? "发送失败" }
}
return { ok: true }
}
/** 进房提示(需登录;失败静默) */
export async function postLiveEnter(streamerId: string): Promise<boolean> {
const res = await fetch(`/api/live/comments/${streamerId}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ type: "enter", content: "" }),
})
const json = (await res.json().catch(() => ({}))) as { success?: boolean }
return res.ok && !!json.success
}
/** 直播送礼:服务端扣币 + liveGifts + commissionLedger + 公屏 gift */
export async function postLiveGift(
streamerId: string,
giftId: number,
): Promise<{ ok: boolean; message?: string }> {
const res = await fetch(`/api/live/gift`, {
method: "POST",
headers: { "Content-Type": "application/json" },
credentials: "include",
body: JSON.stringify({ streamerId, giftId }),
})
const json = (await res.json().catch(() => ({}))) as { success?: boolean; message?: string }
if (!res.ok || !json.success) {
return { ok: false, message: json.message ?? "送礼失败" }
}
return { ok: true }
}