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 }