feat: migrate app routes and business modules
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
import { createRequestError } from './request-client.js'
|
||||
import {
|
||||
normalizeNormalDisableResponseStatus,
|
||||
normalizeOptionalNonnegativeInteger,
|
||||
normalizeOptionalNumericId,
|
||||
normalizeResponseText
|
||||
} from './response-normalizers.js'
|
||||
import {
|
||||
assertPlainPayload,
|
||||
normalizeOptionalNormalDisableStatus,
|
||||
normalizeOptionalSafeInteger,
|
||||
normalizeOptionalText,
|
||||
normalizeOssIdString,
|
||||
normalizeResourcePathId
|
||||
} from './request-normalizers.js'
|
||||
import {
|
||||
normalizeBusinessFileAccess,
|
||||
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 normalizePreviewFamilyArticles = (value, expectedGenealogyId) => {
|
||||
if (!Array.isArray(value)) {
|
||||
throw createRequestError('谱文响应不是列表', 'ARTICLE_RESPONSE_INVALID')
|
||||
}
|
||||
const rows = value.map((item) => {
|
||||
if (!item || typeof item !== 'object' || Array.isArray(item)) {
|
||||
throw createRequestError('谱文响应包含无效条目', 'ARTICLE_RESPONSE_INVALID')
|
||||
}
|
||||
const genealogyId = normalizeOptionalNumericId(item.genealogyId, '谱文家谱标识', 'ARTICLE_RESPONSE_INVALID')
|
||||
if (genealogyId && genealogyId !== expectedGenealogyId) {
|
||||
throw createRequestError('谱文归属与请求不匹配', 'ARTICLE_RESPONSE_INVALID')
|
||||
}
|
||||
const id = normalizeOptionalNumericId(item.id, '谱文标识', 'ARTICLE_RESPONSE_INVALID')
|
||||
const title = normalizeResponseText(item.title, 'title')
|
||||
if (!id || !title) {
|
||||
throw createRequestError('谱文响应缺少标识或标题', 'ARTICLE_RESPONSE_INVALID')
|
||||
}
|
||||
return {
|
||||
id,
|
||||
genealogyId: expectedGenealogyId,
|
||||
title,
|
||||
summary: normalizeResponseText(item.summary, 'summary'),
|
||||
content: '',
|
||||
author: normalizeResponseText(item.author, 'author') || '家族成员',
|
||||
time: normalizeResponseText(item.updatedAt, 'updatedAt'),
|
||||
category: normalizeResponseText(item.category, 'category'),
|
||||
coverOssId: normalizeOptionalNumericId(item.coverOssId, 'coverOssId', 'ARTICLE_RESPONSE_INVALID'),
|
||||
viewCount: normalizeOptionalNonnegativeInteger(item.viewCount, '谱文阅读数', 'ARTICLE_RESPONSE_INVALID')
|
||||
}
|
||||
})
|
||||
if (new Set(rows.map((item) => item.id)).size !== rows.length) {
|
||||
throw createRequestError('谱文响应包含重复标识', 'ARTICLE_RESPONSE_INVALID')
|
||||
}
|
||||
return rows
|
||||
}
|
||||
|
||||
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'),
|
||||
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',
|
||||
'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 (payload.coverOssId !== undefined && payload.coverOssId !== null && payload.coverOssId !== '') {
|
||||
normalizedPayload.coverOssId = normalizeOssIdString(payload.coverOssId, 'coverOssId')
|
||||
}
|
||||
return normalizedPayload
|
||||
}
|
||||
Reference in New Issue
Block a user