190 lines
7.8 KiB
JavaScript
190 lines
7.8 KiB
JavaScript
import { createRequestError } from './request-client.js'
|
|
import {
|
|
normalizeNormalDisableResponseStatus,
|
|
normalizeOptionalNonnegativeInteger
|
|
} from './response-normalizers.js'
|
|
import { assertPlainPayload } from './request-normalizers.js'
|
|
|
|
export const VIDEO_COMMENT_LEVEL = Object.freeze({
|
|
ROOT: 'root',
|
|
REPLY: 'reply'
|
|
})
|
|
|
|
const commentLevels = new Set(Object.values(VIDEO_COMMENT_LEVEL))
|
|
|
|
const normalizeCommentId = (value, label) => {
|
|
if (typeof value === 'string' && /^[1-9]\d*$/.test(value)) return value
|
|
if (typeof value === 'number' && Number.isSafeInteger(value) && value > 0) return String(value)
|
|
throw createRequestError(`视频评论${label}无效`, 'VIDEO_COMMENT_RESPONSE_INVALID')
|
|
}
|
|
|
|
const normalizeCommentText = (value, label, { required = false } = {}) => {
|
|
if (value === undefined || value === null) {
|
|
if (required) throw createRequestError(`视频评论缺少${label}`, 'VIDEO_COMMENT_RESPONSE_INVALID')
|
|
return ''
|
|
}
|
|
if (typeof value !== 'string') {
|
|
throw createRequestError(`视频评论${label}无效`, 'VIDEO_COMMENT_RESPONSE_INVALID')
|
|
}
|
|
const normalized = value.trim()
|
|
if (required && !normalized) {
|
|
throw createRequestError(`视频评论缺少${label}`, 'VIDEO_COMMENT_RESPONSE_INVALID')
|
|
}
|
|
return normalized
|
|
}
|
|
|
|
const normalizeParentCommentId = (value) => {
|
|
if (value === undefined || value === null || value === 0) return null
|
|
return normalizeCommentId(value, '父评论标识')
|
|
}
|
|
|
|
const normalizeVideoComment = (
|
|
item,
|
|
expectedVideoId,
|
|
{ expectedGenealogyId = '', expectedLevel = '', expectedParentCommentId = '' } = {}
|
|
) => {
|
|
if (!item || typeof item !== 'object' || Array.isArray(item)) {
|
|
throw createRequestError('视频评论响应包含无效条目', 'VIDEO_COMMENT_RESPONSE_INVALID')
|
|
}
|
|
const videoId = normalizeCommentId(item.videoId, '视频标识')
|
|
if (videoId !== expectedVideoId) {
|
|
throw createRequestError('视频评论归属与请求视频不匹配', 'VIDEO_COMMENT_RESPONSE_INVALID')
|
|
}
|
|
if (expectedGenealogyId) {
|
|
const genealogyId = normalizeCommentId(item.genealogyId, '家谱标识')
|
|
if (genealogyId !== expectedGenealogyId) {
|
|
throw createRequestError('视频评论归属与请求家谱不匹配', 'VIDEO_COMMENT_RESPONSE_INVALID')
|
|
}
|
|
} else if (item.genealogyId !== undefined && item.genealogyId !== null) {
|
|
throw createRequestError('平台视频评论不能携带家谱归属', 'VIDEO_COMMENT_RESPONSE_INVALID')
|
|
}
|
|
|
|
const parentCommentId = normalizeParentCommentId(item.parentCommentId)
|
|
const declaredLevel = normalizeCommentText(item.commentLevel, '层级')
|
|
const level = declaredLevel || (parentCommentId ? VIDEO_COMMENT_LEVEL.REPLY : VIDEO_COMMENT_LEVEL.ROOT)
|
|
if (!commentLevels.has(level) || (expectedLevel && level !== expectedLevel)) {
|
|
throw createRequestError('视频评论层级无效', 'VIDEO_COMMENT_RESPONSE_INVALID')
|
|
}
|
|
if (
|
|
(level === VIDEO_COMMENT_LEVEL.ROOT && parentCommentId !== null) ||
|
|
(level === VIDEO_COMMENT_LEVEL.REPLY && parentCommentId === null) ||
|
|
(expectedParentCommentId && parentCommentId !== expectedParentCommentId)
|
|
) {
|
|
throw createRequestError('视频评论父级与层级不匹配', 'VIDEO_COMMENT_RESPONSE_INVALID')
|
|
}
|
|
const deletedStatus = item.userDeleted
|
|
if (deletedStatus !== '0' && deletedStatus !== '1') {
|
|
throw createRequestError('视频评论删除状态无效', 'VIDEO_COMMENT_RESPONSE_INVALID')
|
|
}
|
|
if (typeof item.canDelete !== 'boolean') {
|
|
throw createRequestError('视频评论删除权限无效', 'VIDEO_COMMENT_RESPONSE_INVALID')
|
|
}
|
|
const userDeleted = deletedStatus === '1'
|
|
return {
|
|
id: normalizeCommentId(item.commentId, '标识'),
|
|
author: normalizeCommentText(item.appUserNickName, '用户昵称') || '未署名成员',
|
|
content: userDeleted
|
|
? '该评论已删除'
|
|
: normalizeCommentText(item.commentContent, '内容', { required: true }),
|
|
time: normalizeCommentText(item.createTime, '创建时间', { required: true }),
|
|
parentCommentId,
|
|
parentAuthor: normalizeCommentText(item.parentAppUserNickName, '被回复用户昵称'),
|
|
level,
|
|
userDeleted,
|
|
canDelete: item.canDelete,
|
|
replyCount: normalizeOptionalNonnegativeInteger(
|
|
item.replyCount,
|
|
'视频评论回复数',
|
|
'VIDEO_COMMENT_RESPONSE_INVALID'
|
|
),
|
|
status: normalizeNormalDisableResponseStatus(
|
|
item.status,
|
|
'视频评论状态',
|
|
'VIDEO_COMMENT_RESPONSE_INVALID'
|
|
)
|
|
}
|
|
}
|
|
|
|
export const normalizeVideoComments = (
|
|
value,
|
|
expectedVideoId,
|
|
options = {}
|
|
) => {
|
|
if (!Array.isArray(value)) {
|
|
throw createRequestError('视频评论响应不是列表', 'VIDEO_COMMENT_RESPONSE_INVALID')
|
|
}
|
|
const comments = value.map((item) => normalizeVideoComment(item, expectedVideoId, options))
|
|
if (new Set(comments.map((comment) => comment.id)).size !== comments.length) {
|
|
throw createRequestError('视频评论响应包含重复标识', 'VIDEO_COMMENT_RESPONSE_INVALID')
|
|
}
|
|
return comments
|
|
}
|
|
|
|
export const normalizeCreatedVideoComment = (
|
|
value,
|
|
expectedVideoId,
|
|
options = {}
|
|
) => normalizeVideoComment(value, expectedVideoId, options)
|
|
|
|
const normalizePlatformVideoComment = (item, expectedVideoId) => {
|
|
if (!item || typeof item !== 'object' || Array.isArray(item)) {
|
|
throw createRequestError('平台视频评论响应包含无效条目', 'VIDEO_COMMENT_RESPONSE_INVALID')
|
|
}
|
|
const videoId = normalizeCommentId(item.platformVideoId, '平台视频标识')
|
|
if (videoId !== expectedVideoId || normalizeParentCommentId(item.parentCommentId) !== null) {
|
|
throw createRequestError('平台视频评论归属或层级无效', 'VIDEO_COMMENT_RESPONSE_INVALID')
|
|
}
|
|
if (item.userDeleted !== '0' && item.userDeleted !== '1') {
|
|
throw createRequestError('平台视频评论删除状态无效', 'VIDEO_COMMENT_RESPONSE_INVALID')
|
|
}
|
|
if (typeof item.canDelete !== 'boolean') {
|
|
throw createRequestError('平台视频评论删除权限无效', 'VIDEO_COMMENT_RESPONSE_INVALID')
|
|
}
|
|
const userDeleted = item.userDeleted === '1'
|
|
return {
|
|
id: normalizeCommentId(item.commentId, '标识'),
|
|
author: normalizeCommentText(item.appUserNickName, '用户昵称') || '未署名用户',
|
|
content: userDeleted
|
|
? '该评论已删除'
|
|
: normalizeCommentText(item.commentContent, '内容', { required: true }),
|
|
time: normalizeCommentText(item.createTime, '创建时间', { required: true }),
|
|
parentCommentId: null,
|
|
parentAuthor: '',
|
|
level: VIDEO_COMMENT_LEVEL.ROOT,
|
|
userDeleted,
|
|
canDelete: item.canDelete,
|
|
replyCount: 0,
|
|
status: '0'
|
|
}
|
|
}
|
|
|
|
export const normalizePlatformVideoComments = (value, expectedVideoId) => {
|
|
if (!Array.isArray(value)) {
|
|
throw createRequestError('平台视频评论响应不是列表', 'VIDEO_COMMENT_RESPONSE_INVALID')
|
|
}
|
|
const comments = value.map((item) => normalizePlatformVideoComment(item, expectedVideoId))
|
|
if (new Set(comments.map((comment) => comment.id)).size !== comments.length) {
|
|
throw createRequestError('平台视频评论响应包含重复标识', 'VIDEO_COMMENT_RESPONSE_INVALID')
|
|
}
|
|
return comments
|
|
}
|
|
|
|
export const normalizeCreatedPlatformVideoComment = (value, expectedVideoId) =>
|
|
normalizePlatformVideoComment(value, expectedVideoId)
|
|
|
|
export const normalizeVideoCommentPayload = (payload) => {
|
|
assertPlainPayload(payload, new Set(['parentCommentId', 'commentContent']), '视频评论请求')
|
|
if (typeof payload.commentContent !== 'string' || !payload.commentContent.trim()) {
|
|
throw new TypeError('视频评论内容不能为空')
|
|
}
|
|
const commentContent = payload.commentContent.trim()
|
|
if (Array.from(commentContent).length > 1000) {
|
|
throw new TypeError('视频评论不能超过 1000 个字符')
|
|
}
|
|
const normalizedPayload = { commentContent }
|
|
if (payload.parentCommentId !== undefined && payload.parentCommentId !== null) {
|
|
normalizedPayload.parentCommentId = normalizeCommentId(payload.parentCommentId, '父评论标识')
|
|
}
|
|
return normalizedPayload
|
|
}
|