import { normalizeGenealogyPathId } from './genealogy-contract.js' import { normalizeResourcePathId } from './request-normalizers.js' import { createRequestError, requestStrict } from './request-client.js' import { normalizePlatformVideoPlacement, normalizePlatformVideos } from './family-media-contract.js' import { VIDEO_COMMENT_LEVEL, normalizeCreatedPlatformVideoComment, normalizeCreatedVideoComment, normalizePlatformVideoComments, normalizeVideoCommentPayload, normalizeVideoComments } from './video-comment-contract.js' import { normalizeMemberPermissionAssignment, normalizeMemberPermissionPayload, normalizePermissionCatalog } from './genealogy-permission-contract.js' const assertPage = (value, label) => { if (!value || typeof value !== 'object' || !Array.isArray(value.rows) || !Number.isSafeInteger(value.total)) { throw createRequestError(`${label}响应无效`, 'PAGE_RESPONSE_INVALID') } return value } const assertCompleteness = (value) => { const isCount = (count) => Number.isSafeInteger(count) && count >= 0 if ( !value || typeof value !== 'object' || !isCount(value.totalCount) || value.totalCount === 0 || !isCount(value.completedCount) || value.completedCount > value.totalCount || !Number.isSafeInteger(value.completionRate) || value.completionRate < 0 || value.completionRate > 100 || !Array.isArray(value.missingItems) ) { throw createRequestError('资料完整度响应无效', 'COMPLETENESS_RESPONSE_INVALID') } return value } export const genealogyCapabilityApi = { async getCompleteness(genealogyId, requestOptions = {}) { const id = normalizeGenealogyPathId(genealogyId) const completeness = await requestStrict({ url: `/genealogy/app/genealogies/${id}/completeness`, method: 'GET' }, { requestController: requestOptions.requestController ?? null }) return assertCompleteness(completeness) }, async getRecyclePage(genealogyId, query = {}, requestOptions = {}) { const id = normalizeGenealogyPathId(genealogyId) const page = await requestStrict({ url: `/genealogy/app/genealogies/${id}/recycle-bin/page`, method: 'GET', data: query }, { requireData: false, requestController: requestOptions.requestController ?? null }) return assertPage(page, '回收站') }, async restoreRecycleItem(genealogyId, resourceType, resourceId, requestOptions = {}) { if (typeof resourceType !== 'string' || !resourceType.trim()) throw new TypeError('资源类型不能为空') const id = normalizeGenealogyPathId(genealogyId) const resource = normalizeResourcePathId(resourceId, '资源标识') return requestStrict({ url: `/genealogy/app/genealogies/${id}/recycle-bin/${encodeURIComponent(resourceType.trim())}/${resource}/restore`, method: 'PUT' }, { requestController: requestOptions.requestController ?? null }) }, async getPermissionCatalog(genealogyId, requestOptions = {}) { const id = normalizeGenealogyPathId(genealogyId) const catalog = await requestStrict({ url: `/genealogy/app/genealogies/${id}/permission-catalog`, method: 'GET' }, { requestController: requestOptions.requestController ?? null }) return normalizePermissionCatalog(catalog) }, async getMemberPermissions(genealogyId, memberId, enabledCodes, requestOptions = {}) { const id = normalizeGenealogyPathId(genealogyId); const member = normalizeResourcePathId(memberId, '成员标识') const assignment = await requestStrict({ url: `/genealogy/app/genealogies/${id}/members/${member}/permissions`, method: 'GET' }, { requestController: requestOptions.requestController ?? null }) return normalizeMemberPermissionAssignment(assignment, enabledCodes, { expectedGenealogyId: id, expectedMemberId: member }) }, async saveMemberPermissions(genealogyId, memberId, permissionCodes, reason = '', enabledCodes = [], requestOptions = {}) { const id = normalizeGenealogyPathId(genealogyId); const member = normalizeResourcePathId(memberId, '成员标识') const assignment = await requestStrict({ url: `/genealogy/app/genealogies/${id}/members/${member}/permissions`, method: 'PUT', data: normalizeMemberPermissionPayload(permissionCodes, reason, enabledCodes) }, { requestController: requestOptions.requestController ?? null }) return normalizeMemberPermissionAssignment(assignment, enabledCodes, { expectedGenealogyId: id, expectedMemberId: member }) }, async setVideoLike(genealogyId, videoId, liked, requestOptions = {}) { if (typeof liked !== 'boolean') throw new TypeError('点赞状态无效') const id = normalizeGenealogyPathId(genealogyId); const video = normalizeResourcePathId(videoId, '视频标识') return requestStrict({ url: `/genealogy/app/genealogies/${id}/videos/${video}/likes`, method: liked ? 'POST' : 'DELETE' }, { requireData: false, requestController: requestOptions.requestController ?? null }) }, async getVideoComments(genealogyId, videoId, requestOptions = {}) { const id = normalizeGenealogyPathId(genealogyId); const video = normalizeResourcePathId(videoId, '视频标识') const page = assertPage(await requestStrict({ url: `/genealogy/app/genealogies/${id}/videos/${video}/comments/page`, method: 'GET', data: { pageNum: 1, pageSize: 100 } }, { requireData: false, requestController: requestOptions.requestController ?? null }), '视频评论') return normalizeVideoComments(page.rows, video, { expectedGenealogyId: id }) }, async getVideoCommentReplies(genealogyId, videoId, commentId, requestOptions = {}) { const id = normalizeGenealogyPathId(genealogyId) const video = normalizeResourcePathId(videoId, '视频标识') const comment = normalizeResourcePathId(commentId, '父评论标识') const page = assertPage(await requestStrict({ url: `/genealogy/app/genealogies/${id}/videos/${video}/comments/${comment}/replies/page`, method: 'GET', data: { pageNum: 1, pageSize: 100 } }, { requireData: false, requestController: requestOptions.requestController ?? null }), '视频评论回复') return normalizeVideoComments(page.rows, video, { expectedGenealogyId: id, expectedLevel: VIDEO_COMMENT_LEVEL.REPLY, expectedParentCommentId: comment }) }, async createVideoComment(genealogyId, videoId, content, parentCommentId = null, requestOptions = {}) { const id = normalizeGenealogyPathId(genealogyId); const video = normalizeResourcePathId(videoId, '视频标识') const payload = normalizeVideoCommentPayload({ commentContent: content, ...(parentCommentId ? { parentCommentId } : {}) }) const comment = await requestStrict({ url: `/genealogy/app/genealogies/${id}/videos/${video}/comments`, method: 'POST', data: payload }, { requestController: requestOptions.requestController ?? null }) return normalizeCreatedVideoComment(comment, video, { expectedGenealogyId: id, expectedLevel: parentCommentId ? VIDEO_COMMENT_LEVEL.REPLY : VIDEO_COMMENT_LEVEL.ROOT, expectedParentCommentId: payload.parentCommentId || '' }) }, async deleteVideoComment(genealogyId, videoId, commentId, requestOptions = {}) { const id = normalizeGenealogyPathId(genealogyId); const video = normalizeResourcePathId(videoId, '视频标识'); const comment = normalizeResourcePathId(commentId, '评论标识') return requestStrict({ url: `/genealogy/app/genealogies/${id}/videos/${video}/comments/${comment}`, method: 'DELETE' }, { requireData: false, requestController: requestOptions.requestController ?? null }) }, async getPlatformVideos(placement, requestOptions = {}) { const normalizedPlacement = normalizePlatformVideoPlacement(placement) const videos = await requestStrict({ url: '/genealogy/app/platform-videos', method: 'GET', data: { placement: normalizedPlacement } }, { requestController: requestOptions.requestController ?? null }) return normalizePlatformVideos(videos, normalizedPlacement) }, async setPlatformVideoLike(videoId, liked, requestOptions = {}) { if (typeof liked !== 'boolean') throw new TypeError('点赞状态无效') return requestStrict({ url: `/genealogy/app/platform-videos/${normalizeResourcePathId(videoId, '平台视频标识')}/likes`, method: liked ? 'POST' : 'DELETE' }, { requireData: false, requestController: requestOptions.requestController ?? null }) }, async getPlatformVideoComments(videoId, requestOptions = {}) { const video = normalizeResourcePathId(videoId, '平台视频标识') const comments = await requestStrict({ url: `/genealogy/app/platform-videos/${video}/comments`, method: 'GET' }, { requestController: requestOptions.requestController ?? null }) return normalizePlatformVideoComments(comments, video) }, async createPlatformVideoComment(videoId, content, requestOptions = {}) { const video = normalizeResourcePathId(videoId, '平台视频标识') const payload = normalizeVideoCommentPayload({ commentContent: content }) const comment = await requestStrict({ url: `/genealogy/app/platform-videos/${video}/comments`, method: 'POST', data: payload }, { requestController: requestOptions.requestController ?? null }) return normalizeCreatedPlatformVideoComment(comment, video) }, async deletePlatformVideoComment(videoId, commentId, requestOptions = {}) { const video = normalizeResourcePathId(videoId, '平台视频标识') const comment = normalizeResourcePathId(commentId, '平台视频评论标识') await requestStrict({ url: `/genealogy/app/platform-videos/${video}/comments/${comment}`, method: 'DELETE' }, { requireData: false, requestController: requestOptions.requestController ?? null }) return null } }