gateway.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. import { get, post } from '@/core/http/http-client'
  2. import { buildRestPath } from '@/core/config/api-paths'
  3. import { extractBroadcastPage, extractBroadcastRows } from '@/features/live/list-normalize'
  4. const live = (suffix) => buildRestPath(`live/${suffix}`)
  5. /**
  6. * 兼容拦截器解包前后两种结构:
  7. * A. `{ state, liveurl, ... }` 已解包
  8. * B. `{ code:1, msg, data: { state, ... } }` 仍带 envelope
  9. * 命中 `state/liveurl/recordurl` 任一字段视为已是业务对象。
  10. */
  11. function unwrapBroadcast(raw) {
  12. if (!raw || typeof raw !== 'object') return null
  13. const looksLikeRoom = (obj) =>
  14. obj && typeof obj === 'object'
  15. && (
  16. 'state' in obj
  17. || 'liveurl' in obj
  18. || 'recordurl' in obj
  19. || 'liveid' in obj
  20. )
  21. if (looksLikeRoom(raw)) return raw
  22. if (looksLikeRoom(raw.data)) return raw.data
  23. if (raw.data && looksLikeRoom(raw.data.data)) return raw.data.data
  24. return raw
  25. }
  26. export async function pullBroadcastDetail(roomId) {
  27. const { data, raw } = await get(live('play'), { id: roomId })
  28. const room = unwrapBroadcast(data)
  29. if (room) return room
  30. return unwrapBroadcast(raw?.res) || {}
  31. }
  32. function unwrapBroadcastListPage(data, raw) {
  33. const pack = extractBroadcastPage(data)
  34. if (pack.rows.length) return pack
  35. if (raw?.res) return extractBroadcastPage(raw.res)
  36. return extractBroadcastPage(data)
  37. }
  38. export async function pullBroadcastCatalog() {
  39. const { data, raw } = await get(live('livelist'))
  40. const pack = unwrapBroadcastListPage(data, raw)
  41. return pack.rows
  42. }
  43. /**
  44. * 搜索直播间(GET live/livelist?page=&search=)
  45. * @param {{ page?: number, search?: string }} opts
  46. */
  47. export async function searchBroadcastCatalog(opts = {}) {
  48. const page = Number(opts.page) || 1
  49. const search = String(opts.search || '').trim()
  50. const params = { page }
  51. if (search) params.search = search
  52. const { data, raw } = await get(live('livelist'), params)
  53. return unwrapBroadcastListPage(data, raw)
  54. }
  55. /**
  56. * 查询直播间状态(live-lists golive → getLiveStatus)
  57. * @returns {'0'|'1'|'2'|'3'|null} 0 未开播 1 直播中 2 已结束 3 封禁;失败返回 null
  58. */
  59. export async function fetchBroadcastStatus(roomId) {
  60. if (!roomId) return null
  61. try {
  62. const { data } = await get(live('getLiveStatus'), { id: roomId })
  63. const status = data?.status ?? data?.data?.status
  64. if (status === undefined || status === null) return null
  65. return String(status)
  66. } catch (_) {
  67. return null
  68. }
  69. }
  70. export async function probeBroadcastAlive(roomId) {
  71. const status = await fetchBroadcastStatus(roomId)
  72. if (status === null) return true
  73. return status === '1'
  74. }
  75. export async function pullTrialSeconds(roomId) {
  76. const { data, raw } = await get(live('getLastTime'), { id: roomId })
  77. const candidates = [
  78. data != null && typeof data === 'object' ? data.data?.last_time : undefined,
  79. data != null && typeof data === 'object' ? data.last_time : undefined,
  80. typeof data === 'number' || typeof data === 'string' ? data : undefined,
  81. raw?.res?.data?.last_time,
  82. raw?.res?.data?.data?.last_time,
  83. raw?.data?.last_time,
  84. raw?.data?.data?.last_time
  85. ]
  86. for (let i = 0; i < candidates.length; i++) {
  87. const rawVal = candidates[i]
  88. if (rawVal === undefined || rawVal === null || rawVal === '') continue
  89. const n = Number(rawVal)
  90. if (!Number.isNaN(n)) return n
  91. }
  92. return null
  93. }
  94. /**
  95. * 解锁付费直播
  96. * POST /wanlshop/pay/unlocklive body: { liveId }
  97. */
  98. export async function unlockLiveRoom(liveId) {
  99. if (liveId == null || liveId === '') throw new Error('直播间参数缺失')
  100. const { data, raw } = await post('/wanlshop/pay/unlocklive', {
  101. liveId: String(liveId)
  102. })
  103. return { data, raw }
  104. }
  105. export async function notifyRoomExit(groupId) {
  106. if (!groupId) return
  107. await get(live('unload'), { group: groupId, type: 'play' })
  108. }
  109. export async function pushRoomComment(groupId, text) {
  110. if (!groupId || !text) return
  111. await get(live('send'), { group: groupId, message: text })
  112. }
  113. export async function pushRoomLike(roomId) {
  114. await get(live('like'), { id: roomId })
  115. }
  116. /**
  117. * 求讲解(live/seek)
  118. * @param {string|number} groupId 直播群组 liveid
  119. * @param {number} goodsIndex 商品序号(从 1 开始)
  120. */
  121. export async function requestLiveGoodsExplain(groupId, goodsIndex) {
  122. if (!groupId) return
  123. await get(live('seek'), {
  124. group: groupId,
  125. goods_index: goodsIndex
  126. })
  127. }
  128. /**
  129. * 直播间关注(live-room.nvue onShopLike:GET live/follow,user_no + liveid)
  130. */
  131. export async function followLiveAnchor({ userNo, groupId }) {
  132. if (!userNo) return
  133. await get(live('follow'), {
  134. user_no: userNo,
  135. group: groupId || ''
  136. })
  137. }