| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279 |
- import { get, post } from '@/core/http/http-client'
- import { buildRestPath } from '@/core/config/api-paths'
- import { extractFeedPage } from '@/features/feed/clip-normalize'
- // follow 接口跟视频点赞同一个 endpoint(setFindUser type=follow),
- // profile 模块里已经封过一份,feed 这边 re-export 复用即可,避免重复。
- import { toggleProfileFollow } from '@/features/profile/profile-gateway'
- const listPath = () => buildRestPath('find/find/getList')
- const likePath = () => buildRestPath('find/user/setFindUser')
- const commentListPath = () => buildRestPath('find/comments/getList')
- const commentAddPath = () => buildRestPath('find/comments/addData')
- const commentDelPath = () => buildRestPath('find/comments/delData')
- const commentLikePath = () => buildRestPath('find/comments/likeData')
- /**
- * 把 setFindUser 接口的不一致返回拍平成数字(0 = 取消,1 = 已点赞/关注)。
- *
- * 后端的实际形态(v5 上踩过):
- * - 点赞成功: { data: 1 } → parseApiResponse 后 data = 1
- * - 取消点赞成功:{ data: { data: 0 } } → parseApiResponse 后 data = { data: 0 }
- *
- * 不解开第二种,前端会拿到一个对象与 0 比较永远 falsy,
- * 导致"取消点赞"被误判为"已点赞",UI 状态错乱。
- */
- function unwrapFlag(value) {
- if (value == null) return null
- if (typeof value === 'number') return value
- if (typeof value === 'string') {
- const n = Number(value)
- return Number.isNaN(n) ? null : n
- }
- if (typeof value === 'object') {
- if ('data' in value) return unwrapFlag(value.data)
- if ('isLike' in value) return unwrapFlag(value.isLike)
- if ('isFollow' in value) return unwrapFlag(value.isFollow)
- }
- return null
- }
- const seriesItemListPath = () => buildRestPath('videoseriesitem/list')
- /** 解锁后刷新合辑可播列表 */
- const seriesVideoListPath = () => buildRestPath('videoseriesitem/seriesvideolist')
- async function parseFeedPack(data, raw) {
- let parsed = extractFeedPage(data)
- if (!parsed.rows.length) {
- const envelope = raw?.res?.data ?? raw?.data
- parsed = extractFeedPage(envelope)
- }
- if (!parsed.rows.length && Array.isArray(raw?.data)) {
- parsed = extractFeedPage(raw.data)
- }
- // 部分合辑接口:data 直接是分页对象,再包一层
- if (!parsed.rows.length && data && typeof data === 'object' && !Array.isArray(data)) {
- if (Array.isArray(data.rows)) parsed = extractFeedPage(data.rows)
- else if (data.result) parsed = extractFeedPage(data.result)
- }
- return parsed
- }
- /**
- * 解锁后拉取合辑视频列表(带可播 url)
- * GET /wanlshop/videoseriesitem/seriesvideolist
- * @param {{ seriesId: string|number, videoId: string|number, page?: number }} opts
- */
- export async function fetchSeriesVideoList({ seriesId, videoId, page = 1 } = {}) {
- if (seriesId == null || seriesId === '') {
- throw new Error('合辑信息缺失')
- }
- if (videoId == null || videoId === '') {
- throw new Error('视频 video_id 缺失')
- }
- const query = {
- page: String(page || 1),
- series_id: String(seriesId),
- video_id: String(videoId)
- }
- console.log('[feed.series] seriesvideolist request', query)
- const { data, raw } = await get(seriesVideoListPath(), query)
- let parsed = await parseFeedPack(data, raw)
- // 兼容直接返回数组
- if (!parsed.rows.length && Array.isArray(data)) {
- parsed = extractFeedPage(data)
- }
- console.log('[feed.series] seriesvideolist rows=', parsed.rows.length)
- return parsed
- }
- /**
- * 合辑分集列表
- * GET /wanlshop/videoseriesitem/list
- * 必填:id(=series_id)、find_id(=video.id,如 473)
- */
- export async function fetchSeriesItemPage(page = 1, { seriesId, findId } = {}) {
- if (seriesId == null || seriesId === '') {
- throw new Error('合辑信息缺失')
- }
- if (findId == null || findId === '') {
- throw new Error('视频 find_id 缺失')
- }
- const query = {
- type: 'video',
- page,
- id: seriesId,
- series_id: seriesId,
- find_id: findId
- }
- console.log('[feed.series] list request', query)
- const { data, raw } = await get(seriesItemListPath(), query)
- const parsed = await parseFeedPack(data, raw)
- console.log('[feed.series] list rows=', parsed.rows.length)
- return parsed
- }
- /**
- * 竖滑视频流分页(与参考工程同一接口:find/find/getList,type=video)
- * 有 compID 时改走合辑接口 videoseriesitem/list(需同时带 find_id=video.id)
- * @param {number} page
- * @param {Record<string, unknown>} [filters]
- */
- export async function fetchVideoFeedPage(page = 1, filters = {}) {
- const rawFilters = { ...(filters || {}) }
- const compID = rawFilters.compID
- const useCompilation = compID != null && String(compID) !== ''
- delete rawFilters.compID
- if (useCompilation) {
- const findId = rawFilters.find_id
- // 后端合辑列表强依赖 find_id(video.id),缺参会直接失败
- if (findId == null || findId === '') {
- console.warn('[feed.series] skip list: missing find_id (video.id)')
- return { rows: [], currentPage: 1, lastPage: 1 }
- }
- return fetchSeriesItemPage(page, { seriesId: compID, findId })
- }
- const { data, raw } = await get(listPath(), {
- type: 'video',
- page,
- ...rawFilters
- })
- return parseFeedPack(data, raw)
- }
- /**
- * 动态流分页(find/find/getList,type=find)
- * @param {number} page
- * @param {Record<string, unknown>} [filters]
- */
- export async function fetchDiscoverFeedPage(page = 1, filters = {}) {
- const { data, raw } = await get(listPath(), {
- type: 'find',
- page,
- ...filters
- })
- let parsed = extractFeedPage(data)
- if (!parsed.rows.length) {
- const envelope = raw?.res?.data ?? raw?.data
- parsed = extractFeedPage(envelope)
- }
- // 拦截器改写后 raw.data 有时已是列表项数组
- if (!parsed.rows.length && Array.isArray(raw?.data)) {
- parsed = extractFeedPage(raw.data)
- }
- return parsed
- }
- /** 点赞/取消点赞,返回服务端 isLike 状态值(0 取消 / 1 已赞) */
- export async function toggleClipLike(clipId) {
- const { data } = await post(likePath(), {
- id: clipId,
- type: 'likes'
- })
- return unwrapFlag(data)
- }
- /**
- * 关注/取消关注主播
- * 参考 HKLive 项目 components/find/play.vue#handleFollow:
- * POST /wanlshop/find/user/setFindUser body: {id: user_no, type:'follow'}
- * 返回 0 取消关注 / 1 已关注
- *
- * @param {string|number} userNo 视频所属主播的 user_no(不是数据库 id)
- */
- export async function toggleClipFollow(userNo) {
- return toggleProfileFollow(userNo)
- }
- /**
- * 拉取视频评论列表 + 总数
- * 参考 HKLive 项目 components/find/play.vue#handleComment:
- * GET /wanlshop/find/comments/getList?id={clipId}
- * 返回:{ count: 评论总数, list: [...] }
- *
- * @param {string|number} clipId 视频 id
- */
- export async function fetchClipComments(clipId) {
- const { data, raw } = await get(commentListPath(), { id: clipId })
- const envelope = (data && (Array.isArray(data) ? null : data))
- || raw?.res?.data
- || raw?.data
- || {}
- const list = Array.isArray(envelope.list) ? envelope.list : []
- const count = Number(envelope.count != null ? envelope.count : list.length) || 0
- return { count, list }
- }
- /**
- * 新增评论 / 回复
- * 参考 HKLive components/comment/comment-nvue.vue#handleAdd:
- * POST /wanlshop/find/comments/addData body: { find_id, content, pid? }
- *
- * @param {string|number} findId 视频 id
- * @param {string} content 评论内容
- * @param {string|number|null} pid 父评论 id(顶级评论传 null/0)
- */
- export async function addClipComment(findId, content, pid = null) {
- const payload = { find_id: findId, content }
- if (pid != null && pid !== '' && pid !== 0) payload.pid = pid
- const { data } = await post(commentAddPath(), payload)
- return data
- }
- /**
- * 删除评论
- * 参考 HKLive components/comment/comment-nvue.vue#handleDelete:
- * POST /wanlshop/find/comments/delData body: { id, find_id }
- *
- * @returns {{ count: number }} 删除后剩余评论数
- */
- export async function deleteClipComment(commentId, findId) {
- const { data } = await post(commentDelPath(), { id: commentId, find_id: findId })
- const count = Number(data && data.count != null ? data.count : (typeof data === 'number' ? data : 0)) || 0
- return { count }
- }
- /**
- * 点赞 / 取消点赞 评论
- * 参考 HKLive components/comment/comment-nvue.vue#handleLike:
- * GET /wanlshop/find/comments/likeData?id={commentId}
- * 返回:true / false(已点赞 / 已取消)
- */
- export async function toggleClipCommentLike(commentId) {
- const { data } = await get(commentLikePath(), { id: commentId })
- return unwrapFlag(data)
- }
- /**
- * 查询钻石余额(gift-panel / 解锁前校验)
- * @returns {number}
- */
- export async function fetchDiamondBalance() {
- const { data } = await get('/user/getdiamondnum')
- if (typeof data === 'string' || typeof data === 'number') {
- return Number(data) || 0
- }
- if (data && typeof data === 'object') {
- if (data.diamond != null) return Number(data.diamond) || 0
- if (data.data != null && typeof data.data === 'object' && data.data.diamond != null) {
- return Number(data.data.diamond) || 0
- }
- if (typeof data.data === 'string' || typeof data.data === 'number') {
- return Number(data.data) || 0
- }
- }
- return 0
- }
- /**
- * 解锁合辑付费视频
- * POST /wanlshop/pay/unlockvideo body: { id: series_item_id }
- */
- export async function unlockFeedVideo(seriesItemId) {
- if (seriesItemId == null || seriesItemId === '') {
- throw new Error('视频信息缺失')
- }
- const { data, raw } = await post('/wanlshop/pay/unlockvideo', { id: seriesItemId })
- return { data, raw }
- }
|