Files
jiapuapp/services/api/video-comment-contract.js

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
}