feat: 完成前端业务闭环与后端联调
This commit is contained in:
@@ -17,6 +17,96 @@ import {
|
||||
normalizeResourceCapabilities
|
||||
} from './business-file-contract.js'
|
||||
|
||||
export const PLATFORM_VIDEO_PLACEMENT = Object.freeze({
|
||||
HOME_FEATURED: 'home_featured',
|
||||
VIDEO_CENTER: 'video_center',
|
||||
PROFILE_FEATURED: 'profile_featured'
|
||||
})
|
||||
|
||||
const platformVideoPlacements = new Set(Object.values(PLATFORM_VIDEO_PLACEMENT))
|
||||
|
||||
export const normalizePlatformVideoPlacement = (value) => {
|
||||
if (typeof value !== 'string' || !platformVideoPlacements.has(value.trim())) {
|
||||
throw new TypeError('平台视频投放位无效')
|
||||
}
|
||||
return value.trim()
|
||||
}
|
||||
|
||||
const normalizePlatformVideo = (value, expectedPlacement) => {
|
||||
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
||||
throw createRequestError('平台视频响应无效', 'PLATFORM_VIDEO_RESPONSE_INVALID')
|
||||
}
|
||||
const id = normalizeOptionalNumericId(
|
||||
value.platformVideoId,
|
||||
'平台视频标识',
|
||||
'PLATFORM_VIDEO_RESPONSE_INVALID'
|
||||
)
|
||||
const title = normalizeResponseText(value.videoTitle, 'videoTitle')
|
||||
if (
|
||||
!id ||
|
||||
!title ||
|
||||
value.placement !== expectedPlacement ||
|
||||
(value.platform !== 'app' && value.platform !== 'all')
|
||||
) {
|
||||
throw createRequestError('平台视频响应缺少稳定字段', 'PLATFORM_VIDEO_RESPONSE_INVALID')
|
||||
}
|
||||
if (typeof value.likedByCurrentUser !== 'boolean') {
|
||||
throw createRequestError('平台视频点赞状态无效', 'PLATFORM_VIDEO_RESPONSE_INVALID')
|
||||
}
|
||||
return {
|
||||
id,
|
||||
platform: value.platform,
|
||||
placement: expectedPlacement,
|
||||
title,
|
||||
description: normalizeResponseText(value.videoDesc, 'videoDesc'),
|
||||
startAt: normalizeResponseText(value.startAt, 'startAt'),
|
||||
endAt: normalizeResponseText(value.endAt, 'endAt'),
|
||||
coverFile: normalizeBusinessFileAccess(
|
||||
value.coverFile,
|
||||
'平台视频封面',
|
||||
'PLATFORM_VIDEO_RESPONSE_INVALID'
|
||||
),
|
||||
videoFile: normalizeBusinessFileAccess(
|
||||
value.videoFile,
|
||||
'平台视频文件',
|
||||
'PLATFORM_VIDEO_RESPONSE_INVALID',
|
||||
{ required: true }
|
||||
),
|
||||
durationSeconds: normalizeOptionalNonnegativeInteger(
|
||||
value.durationSeconds,
|
||||
'平台视频时长',
|
||||
'PLATFORM_VIDEO_RESPONSE_INVALID'
|
||||
),
|
||||
viewCount: normalizeOptionalNonnegativeInteger(
|
||||
value.viewCount,
|
||||
'平台视频播放量',
|
||||
'PLATFORM_VIDEO_RESPONSE_INVALID'
|
||||
),
|
||||
likeCount: normalizeOptionalNonnegativeInteger(
|
||||
value.likeCount,
|
||||
'平台视频点赞量',
|
||||
'PLATFORM_VIDEO_RESPONSE_INVALID'
|
||||
),
|
||||
commentCount: normalizeOptionalNonnegativeInteger(
|
||||
value.commentCount,
|
||||
'平台视频评论量',
|
||||
'PLATFORM_VIDEO_RESPONSE_INVALID'
|
||||
),
|
||||
likedByCurrentUser: value.likedByCurrentUser
|
||||
}
|
||||
}
|
||||
|
||||
export const normalizePlatformVideos = (value, expectedPlacement) => {
|
||||
if (!Array.isArray(value)) {
|
||||
throw createRequestError('平台视频响应不是列表', 'PLATFORM_VIDEO_RESPONSE_INVALID')
|
||||
}
|
||||
const videos = value.map((item) => normalizePlatformVideo(item, expectedPlacement))
|
||||
if (new Set(videos.map((item) => item.id)).size !== videos.length) {
|
||||
throw createRequestError('平台视频响应包含重复标识', 'PLATFORM_VIDEO_RESPONSE_INVALID')
|
||||
}
|
||||
return videos
|
||||
}
|
||||
|
||||
export const normalizeAppVideo = (value, expectedGenealogyId, expectedVideoId = '') => {
|
||||
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
||||
throw createRequestError('家族视频响应无效', 'VIDEO_RESPONSE_INVALID')
|
||||
@@ -27,8 +117,12 @@ export const normalizeAppVideo = (value, expectedGenealogyId, expectedVideoId =
|
||||
if (!id || genealogyId !== expectedGenealogyId || !title || (expectedVideoId && id !== expectedVideoId)) {
|
||||
throw createRequestError('家族视频响应缺少稳定字段', 'VIDEO_RESPONSE_INVALID')
|
||||
}
|
||||
if (typeof value.canEdit !== 'boolean' || typeof value.canDelete !== 'boolean') {
|
||||
throw createRequestError('家族视频响应缺少权限字段', 'VIDEO_RESPONSE_INVALID')
|
||||
if (
|
||||
typeof value.canEdit !== 'boolean' ||
|
||||
typeof value.canDelete !== 'boolean' ||
|
||||
typeof value.likedByCurrentUser !== 'boolean'
|
||||
) {
|
||||
throw createRequestError('家族视频响应缺少状态或权限字段', 'VIDEO_RESPONSE_INVALID')
|
||||
}
|
||||
return {
|
||||
id,
|
||||
@@ -40,6 +134,9 @@ export const normalizeAppVideo = (value, expectedGenealogyId, expectedVideoId =
|
||||
durationSeconds: normalizeOptionalNonnegativeInteger(value.durationSeconds, '视频时长', 'VIDEO_RESPONSE_INVALID'),
|
||||
publishTime: normalizeResponseText(value.publishTime, 'publishTime'),
|
||||
viewCount: normalizeOptionalNonnegativeInteger(value.viewCount, '视频播放量', 'VIDEO_RESPONSE_INVALID'),
|
||||
likeCount: normalizeOptionalNonnegativeInteger(value.likeCount, '视频点赞量', 'VIDEO_RESPONSE_INVALID'),
|
||||
commentCount: normalizeOptionalNonnegativeInteger(value.commentCount, '视频评论量', 'VIDEO_RESPONSE_INVALID'),
|
||||
likedByCurrentUser: value.likedByCurrentUser,
|
||||
sortOrder: normalizeOptionalNonnegativeInteger(value.sortOrder, '视频排序值', 'VIDEO_RESPONSE_INVALID'),
|
||||
status: normalizeNormalDisableResponseStatus(value.status, '视频状态', 'VIDEO_RESPONSE_INVALID'),
|
||||
canEdit: value.canEdit,
|
||||
@@ -72,6 +169,7 @@ export const normalizeAppAlbum = (value, expectedGenealogyId) => {
|
||||
description: normalizeResponseText(value.albumDesc, 'albumDesc'),
|
||||
coverFile: normalizeBusinessFileAccess(value.coverFile, '相册封面', 'ALBUM_RESPONSE_INVALID'),
|
||||
photoCount: normalizeOptionalNonnegativeInteger(value.photoCount, '相册照片数量', 'ALBUM_RESPONSE_INVALID'),
|
||||
createTime: normalizeResponseText(value.createTime, 'createTime'),
|
||||
sortOrder: normalizeOptionalNonnegativeInteger(value.sortOrder, '相册排序值', 'ALBUM_RESPONSE_INVALID'),
|
||||
status: normalizeNormalDisableResponseStatus(value.status, '相册状态', 'ALBUM_RESPONSE_INVALID'),
|
||||
...normalizeResourceCapabilities(value, '相册', 'ALBUM_RESPONSE_INVALID')
|
||||
@@ -87,18 +185,6 @@ export const normalizeAppAlbums = (value, expectedGenealogyId) => {
|
||||
return albums
|
||||
}
|
||||
|
||||
export const projectPreviewAlbums = (value, expectedGenealogyId) =>
|
||||
value.map((item) => ({
|
||||
id: String(item.id),
|
||||
genealogyId: expectedGenealogyId,
|
||||
name: String(item.name || '未命名相册'),
|
||||
description: String(item.description || ''),
|
||||
coverFile: item.cover ? { accessUrl: String(item.cover) } : null,
|
||||
photoCount: Number.isSafeInteger(item.photoCount) ? item.photoCount : 0,
|
||||
canEdit: false,
|
||||
canDelete: false
|
||||
}))
|
||||
|
||||
const normalizeAppAlbumPhoto = (value, expectedGenealogyId, expectedAlbumId) => {
|
||||
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
||||
throw createRequestError('相册照片响应无效', 'ALBUM_PHOTO_RESPONSE_INVALID')
|
||||
@@ -178,8 +264,9 @@ export const normalizeVideoPayload = (payload) => {
|
||||
const normalizedPayload = { videoTitle, videoOssId }
|
||||
const videoDesc = normalizeOptionalText(payload.videoDesc, '视频说明')
|
||||
if (videoDesc) normalizedPayload.videoDesc = videoDesc
|
||||
if (payload.coverOssId !== undefined && payload.coverOssId !== null && payload.coverOssId !== '') {
|
||||
normalizedPayload.coverOssId = normalizeOssIdString(payload.coverOssId, '视频封面 OSS ID')
|
||||
if (Object.prototype.hasOwnProperty.call(payload, 'coverOssId')) {
|
||||
if (payload.coverOssId === null) normalizedPayload.coverOssId = null
|
||||
else if (payload.coverOssId !== '') normalizedPayload.coverOssId = normalizeOssIdString(payload.coverOssId, '视频封面 OSS ID')
|
||||
}
|
||||
for (const field of ['durationSeconds', 'sortOrder']) {
|
||||
const normalizedInteger = normalizeOptionalSafeInteger(payload[field], field)
|
||||
|
||||
Reference in New Issue
Block a user