import { createRequestError } from './request-client.js' import { normalizeNormalDisableResponseStatus, normalizeOptionalNonnegativeInteger, normalizeOptionalNumericId, normalizeResponseText } from './response-normalizers.js' import { assertPlainPayload, normalizeOptionalNormalDisableStatus, normalizeOptionalOssIdList, normalizeOptionalSafeInteger, normalizeOptionalText, normalizeOssIdString, normalizeResourcePathId } from './request-normalizers.js' import { normalizeBusinessFileAccess, normalizeBusinessFileAccessRows, normalizeContentProtectionCapabilities, normalizeResourceCapabilities } from './business-file-contract.js' export const normalizeArticleCategoryOptions = (value) => { if (!Array.isArray(value)) { throw createRequestError('谱文分类响应不是列表', 'ARTICLE_CATEGORY_RESPONSE_INVALID') } const categories = value.map((item) => { if (!item || typeof item !== 'object' || Array.isArray(item)) { throw createRequestError('谱文分类响应包含无效条目', 'ARTICLE_CATEGORY_RESPONSE_INVALID') } const id = normalizeOptionalNumericId(item.categoryId, '谱文分类标识', 'ARTICLE_CATEGORY_RESPONSE_INVALID') const name = normalizeResponseText(item.categoryName, 'categoryName') if (!id || !name || typeof item.enabled !== 'boolean') { throw createRequestError('谱文分类响应缺少稳定字段', 'ARTICLE_CATEGORY_RESPONSE_INVALID') } return { id, name, enabled: item.enabled, sortOrder: normalizeOptionalNonnegativeInteger(item.sortOrder, '谱文分类排序值', 'ARTICLE_CATEGORY_RESPONSE_INVALID') } }) if (new Set(categories.map((item) => item.id)).size !== categories.length) { throw createRequestError('谱文分类响应包含重复标识', 'ARTICLE_CATEGORY_RESPONSE_INVALID') } return categories } export const normalizeAppArticle = (value, expectedGenealogyId, expectedArticleId = '') => { if (!value || typeof value !== 'object' || Array.isArray(value)) { throw createRequestError('谱文响应无效', 'ARTICLE_RESPONSE_INVALID') } const id = normalizeOptionalNumericId(value.articleId, '谱文标识', 'ARTICLE_RESPONSE_INVALID') const genealogyId = normalizeOptionalNumericId(value.genealogyId, '谱文家谱标识', 'ARTICLE_RESPONSE_INVALID') const title = normalizeResponseText(value.articleTitle, 'articleTitle') if (!id || genealogyId !== expectedGenealogyId || !title || (expectedArticleId && id !== expectedArticleId)) { throw createRequestError('谱文响应缺少稳定归属字段', 'ARTICLE_RESPONSE_INVALID') } return { id, genealogyId, title, summary: normalizeResponseText(value.articleSummary, 'articleSummary'), content: normalizeResponseText(value.articleContent, 'articleContent'), authorName: normalizeResponseText(value.authorName, 'authorName'), author: normalizeResponseText(value.authorName, 'authorName') || '家族成员', time: normalizeResponseText(value.publishTime, 'publishTime'), categoryId: normalizeOptionalNumericId(value.categoryId, '谱文分类标识', 'ARTICLE_RESPONSE_INVALID'), category: normalizeResponseText(value.categoryName, 'categoryName'), coverFile: normalizeBusinessFileAccess(value.coverFile, '谱文封面', 'ARTICLE_RESPONSE_INVALID'), mediaFiles: normalizeBusinessFileAccessRows(value.mediaFiles, '谱文正文图片', 'ARTICLE_RESPONSE_INVALID'), viewCount: normalizeOptionalNonnegativeInteger(value.viewCount, '谱文阅读数', 'ARTICLE_RESPONSE_INVALID'), sortOrder: normalizeOptionalNonnegativeInteger(value.sortOrder, '谱文排序值', 'ARTICLE_RESPONSE_INVALID'), status: normalizeNormalDisableResponseStatus(value.status, '谱文状态', 'ARTICLE_RESPONSE_INVALID'), ...normalizeContentProtectionCapabilities(value, '谱文', 'ARTICLE_RESPONSE_INVALID'), ...normalizeResourceCapabilities(value, '谱文', 'ARTICLE_RESPONSE_INVALID') } } export const normalizeAppArticles = (value, expectedGenealogyId) => { if (!Array.isArray(value)) throw createRequestError('谱文响应不是列表', 'ARTICLE_RESPONSE_INVALID') const articles = value.map((item) => normalizeAppArticle(item, expectedGenealogyId)) if (new Set(articles.map((item) => item.id)).size !== articles.length) { throw createRequestError('谱文响应包含重复标识', 'ARTICLE_RESPONSE_INVALID') } return articles } export const normalizeArticleCreatePayload = (payload) => { const allowedFields = new Set([ 'categoryId', 'articleTitle', 'articleSummary', 'coverOssId', 'mediaOssIds', 'articleContent', 'authorName', 'sortOrder', 'status' ]) assertPlainPayload(payload, allowedFields, '谱文请求') const articleTitle = typeof payload.articleTitle === 'string' ? payload.articleTitle.trim() : '' const articleContent = typeof payload.articleContent === 'string' ? payload.articleContent.trim() : '' if (!articleTitle) throw new TypeError('谱文标题不能为空') if (!articleContent) throw new TypeError('谱文正文不能为空') const normalizedPayload = { articleTitle, articleContent } for (const field of ['articleSummary', 'authorName']) { const normalizedText = normalizeOptionalText(payload[field], field) if (normalizedText) normalizedPayload[field] = normalizedText } const status = normalizeOptionalNormalDisableStatus(payload.status, '谱文状态') if (status) normalizedPayload.status = status if (payload.categoryId !== undefined && payload.categoryId !== null && payload.categoryId !== '') { normalizedPayload.categoryId = normalizeResourcePathId(payload.categoryId, '谱文分类标识') } const sortOrder = normalizeOptionalSafeInteger(payload.sortOrder, 'sortOrder') if (sortOrder !== undefined) normalizedPayload.sortOrder = sortOrder if (Object.prototype.hasOwnProperty.call(payload, 'coverOssId')) { if (payload.coverOssId === null) normalizedPayload.coverOssId = null else if (payload.coverOssId !== '') normalizedPayload.coverOssId = normalizeOssIdString(payload.coverOssId, 'coverOssId') } if (Object.prototype.hasOwnProperty.call(payload, 'mediaOssIds')) { const mediaOssIds = normalizeOptionalOssIdList(payload.mediaOssIds) if (mediaOssIds && mediaOssIds.length > 1000) { throw new TypeError('谱文正文图片标识总长度不能超过1000个字符') } normalizedPayload.mediaOssIds = mediaOssIds || '' } return normalizedPayload }