修改完成待测试
This commit is contained in:
@@ -8,6 +8,7 @@ import {
|
||||
import {
|
||||
assertPlainPayload,
|
||||
normalizeOptionalNormalDisableStatus,
|
||||
normalizeOptionalOssIdList,
|
||||
normalizeOptionalSafeInteger,
|
||||
normalizeOptionalText,
|
||||
normalizeOssIdString,
|
||||
@@ -15,6 +16,7 @@ import {
|
||||
} from './request-normalizers.js'
|
||||
import {
|
||||
normalizeBusinessFileAccess,
|
||||
normalizeBusinessFileAccessRows,
|
||||
normalizeContentProtectionCapabilities,
|
||||
normalizeResourceCapabilities
|
||||
} from './business-file-contract.js'
|
||||
@@ -67,6 +69,7 @@ export const normalizeAppArticle = (value, expectedGenealogyId, expectedArticleI
|
||||
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'),
|
||||
@@ -90,6 +93,7 @@ export const normalizeArticleCreatePayload = (payload) => {
|
||||
'articleTitle',
|
||||
'articleSummary',
|
||||
'coverOssId',
|
||||
'mediaOssIds',
|
||||
'articleContent',
|
||||
'authorName',
|
||||
'sortOrder',
|
||||
@@ -116,5 +120,12 @@ export const normalizeArticleCreatePayload = (payload) => {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -33,13 +33,14 @@ const requestArticleContentProtection = async ({
|
||||
export const familyArticleApi = {
|
||||
async createArticle(genealogyId, payload, requestOptions = {}) {
|
||||
const normalizedGenealogyId = normalizeGenealogyPathId(genealogyId)
|
||||
return requestStrict({
|
||||
const article = await requestStrict({
|
||||
url: `/genealogy/app/genealogies/${normalizedGenealogyId}/articles`,
|
||||
method: 'POST',
|
||||
data: normalizeArticleCreatePayload(payload)
|
||||
}, {
|
||||
requestController: requestOptions.requestController ?? null
|
||||
})
|
||||
return normalizeAppArticle(article, normalizedGenealogyId)
|
||||
},
|
||||
|
||||
async getArticles(genealogyId, requestOptions = {}) {
|
||||
|
||||
@@ -8,6 +8,7 @@ import { createRequestError } from './request-client.js'
|
||||
import { assertPlainPayload, normalizeOssIdString } from './request-normalizers.js'
|
||||
import {
|
||||
normalizeOptionalNonnegativeInteger,
|
||||
normalizeOptionalNumericId,
|
||||
normalizeResponseText
|
||||
} from './response-normalizers.js'
|
||||
|
||||
@@ -227,6 +228,7 @@ export const normalizeAppGenealogy = (item) => {
|
||||
)
|
||||
return {
|
||||
id: normalizeMyGenealogyId(item.genealogyId),
|
||||
genealogyNo: normalizeGenealogyResponseText(item.genealogyNo, 'genealogyNo'),
|
||||
name,
|
||||
firstAncestorName: normalizeGenealogyResponseText(item.firstAncestorName, 'firstAncestorName'),
|
||||
rootPersonId: normalizeOptionalNumericId(item.rootPersonId, '始迁祖人物标识', 'GENEALOGY_RESPONSE_INVALID'),
|
||||
@@ -248,6 +250,7 @@ export const normalizeAppGenealogy = (item) => {
|
||||
),
|
||||
archivedAt: normalizeGenealogyResponseText(item.archivedAt, 'archivedAt'),
|
||||
intro: normalizeGenealogyResponseText(item.intro, 'intro'),
|
||||
createTime: normalizeGenealogyResponseText(item.createTime, 'createTime'),
|
||||
joinTime: normalizeGenealogyResponseText(item.joinTime, 'joinTime')
|
||||
}
|
||||
}
|
||||
@@ -267,6 +270,8 @@ export const normalizeMyGenealogies = (value) => {
|
||||
memberCount: genealogy.memberCount,
|
||||
canManage: genealogy.canManage,
|
||||
canEditContent: genealogy.canEditContent,
|
||||
createTime: genealogy.createTime,
|
||||
joinTime: genealogy.joinTime,
|
||||
lifecycleStatus: genealogy.lifecycleStatus,
|
||||
archivedAt: genealogy.archivedAt,
|
||||
canArchive: genealogy.canArchive,
|
||||
|
||||
@@ -89,11 +89,12 @@ export const lifeRecordApi = {
|
||||
|
||||
async createGrowthRecord(genealogyId, payload, requestOptions = {}) {
|
||||
const normalizedGenealogyId = normalizeGenealogyPathId(genealogyId)
|
||||
return requestStrict({
|
||||
const growthRecord = await requestStrict({
|
||||
url: `/genealogy/app/genealogies/${normalizedGenealogyId}/growth-records`,
|
||||
method: 'POST',
|
||||
data: normalizeGrowthRecordCreatePayload(payload)
|
||||
}, { requestController: requestOptions.requestController ?? null })
|
||||
return normalizeAppGrowthRecord(growthRecord, normalizedGenealogyId)
|
||||
},
|
||||
|
||||
async updateGrowthRecord(genealogyId, recordId, payload, requestOptions = {}) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { normalizeOptionalSafeInteger } from './request-normalizers.js'
|
||||
import { createRequestError } from './request-client.js'
|
||||
import { normalizeOptionalNumericId } from './response-normalizers.js'
|
||||
import { LINEAGE_PERSON_OPTIONS } from './lineage-person-options.js'
|
||||
import { normalizeBusinessOptionProjection } from './business-dictionary-contract.js'
|
||||
|
||||
@@ -10,6 +11,16 @@ const optionLabels = (options) => Object.freeze(
|
||||
const lineagePersonError = (message) =>
|
||||
createRequestError(message, 'LINEAGE_PERSON_RESPONSE_INVALID')
|
||||
|
||||
const lineageRankError = (message) =>
|
||||
createRequestError(message, 'LINEAGE_RANK_RESPONSE_INVALID')
|
||||
|
||||
const normalizeLineageRankText = (value, label) => {
|
||||
if (typeof value !== 'string' || !value.trim()) {
|
||||
throw lineageRankError(`世系排行选项缺少${label}`)
|
||||
}
|
||||
return value.trim()
|
||||
}
|
||||
|
||||
export const normalizeLineagePersonIdentity = (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)
|
||||
@@ -146,6 +157,12 @@ export const normalizeLineagePersonDetail = (value, expectedGenealogyId, expecte
|
||||
throw lineagePersonError('成员详情敏感病史权限无效')
|
||||
}
|
||||
const relationName = normalizeLineagePersonText(value.relationName, '关系显示名称')
|
||||
const rankId = normalizeOptionalNumericId(
|
||||
value.rankId,
|
||||
'成员排行标识',
|
||||
'LINEAGE_PERSON_RESPONSE_INVALID'
|
||||
)
|
||||
const rankName = normalizeLineagePersonText(value.rankName, '排行名称')
|
||||
for (const field of ['canDisable', 'canCreateDocument', 'canManageDocuments']) {
|
||||
if (value[field] !== undefined && typeof value[field] !== 'boolean') {
|
||||
throw lineagePersonError(`成员详情权限字段 ${field} 无效`)
|
||||
@@ -221,6 +238,8 @@ export const normalizeLineagePersonDetail = (value, expectedGenealogyId, expecte
|
||||
remark,
|
||||
canManageSensitiveMedicalHistory,
|
||||
relationName,
|
||||
rankId,
|
||||
rankName,
|
||||
sortOrder,
|
||||
status: personStatus === '1' ? 'deceased' : 'normal',
|
||||
canDisable: value.canDisable === true,
|
||||
@@ -231,6 +250,40 @@ export const normalizeLineagePersonDetail = (value, expectedGenealogyId, expecte
|
||||
}
|
||||
}
|
||||
|
||||
export const normalizeLineageRankOptions = (value) => {
|
||||
if (!Array.isArray(value)) throw lineageRankError('世系排行选项响应不是列表')
|
||||
const rankIds = new Set()
|
||||
return value.map((option) => {
|
||||
if (!option || typeof option !== 'object' || Array.isArray(option)) {
|
||||
throw lineageRankError('世系排行选项包含无效记录')
|
||||
}
|
||||
const rankId = normalizeOptionalNumericId(
|
||||
option.rankId,
|
||||
'世系排行标识',
|
||||
'LINEAGE_RANK_RESPONSE_INVALID'
|
||||
)
|
||||
if (!rankId || rankIds.has(rankId)) {
|
||||
throw lineageRankError(rankId ? '世系排行选项包含重复标识' : '世系排行选项缺少标识')
|
||||
}
|
||||
rankIds.add(rankId)
|
||||
const rankCode = normalizeLineageRankText(option.rankCode, '排行编码')
|
||||
const rankName = normalizeLineageRankText(option.rankName, '排行名称')
|
||||
const rankType = normalizeLineageRankText(option.rankType, '排行类型')
|
||||
const genderScope = normalizeLineageRankText(option.genderScope, '排行适用性别')
|
||||
if (!['ANCESTOR', 'GENERATION'].includes(rankType)) {
|
||||
throw lineageRankError('世系排行选项类型无效')
|
||||
}
|
||||
if (!['0', '1', '2'].includes(genderScope)) {
|
||||
throw lineageRankError('世系排行选项适用性别无效')
|
||||
}
|
||||
const rankOrder = normalizeOptionalSafeInteger(option.rankOrder, '排行顺序')
|
||||
if (rankOrder !== undefined && rankOrder < 0) {
|
||||
throw lineageRankError('世系排行选项顺序无效')
|
||||
}
|
||||
return { rankId, rankCode, rankName, rankType, genderScope, rankOrder }
|
||||
})
|
||||
}
|
||||
|
||||
export const normalizeLineageSensitiveProfile = (value, expectedPersonId) => {
|
||||
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
||||
throw lineagePersonError('成员敏感健康资料响应不是对象')
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
normalizeLineagePersonIdentity,
|
||||
normalizeLineagePersonOptions,
|
||||
normalizeLineagePersonPage,
|
||||
normalizeLineageRankOptions,
|
||||
normalizeLineageSensitiveProfile
|
||||
} from './lineage-person-contract.js'
|
||||
import { normalizeLineageTree } from './lineage-tree-contract.js'
|
||||
@@ -26,6 +27,24 @@ export const lineageApi = {
|
||||
return normalizeLineageTree(tree)
|
||||
},
|
||||
|
||||
async getRankOptions(genealogyId, generation, sex, requestOptions = {}) {
|
||||
const normalizedGenealogyId = normalizeGenealogyPathId(genealogyId)
|
||||
if (!Number.isSafeInteger(generation) || generation < 1) {
|
||||
throw new TypeError('排行选项世代必须是正安全整数')
|
||||
}
|
||||
if (!['0', '1', '2'].includes(sex)) {
|
||||
throw new TypeError('排行选项性别必须是 0、1 或 2')
|
||||
}
|
||||
const options = await requestStrict({
|
||||
url: `/genealogy/app/genealogies/${normalizedGenealogyId}/lineage/ranks`,
|
||||
method: 'GET',
|
||||
data: { generation, sex }
|
||||
}, {
|
||||
requestController: requestOptions.requestController ?? null
|
||||
})
|
||||
return normalizeLineageRankOptions(options)
|
||||
},
|
||||
|
||||
async getPerson(genealogyId, personId, requestOptions = {}) {
|
||||
const normalizedGenealogyId = normalizeGenealogyPathId(genealogyId)
|
||||
const normalizedPersonId = normalizeLineagePersonIdentity(personId, '人物标识')
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { createRequestError } from './request-client.js'
|
||||
import { normalizeBusinessFileAccess } from './business-file-contract.js'
|
||||
|
||||
const lineageTreeError = (message) =>
|
||||
createRequestError(message, 'LINEAGE_TREE_RESPONSE_INVALID')
|
||||
@@ -60,6 +61,7 @@ export const normalizeLineageTree = (value) => {
|
||||
throw lineageTreeError('世系树人物世代无效')
|
||||
}
|
||||
const generationName = normalizeLineageText(node.generationName, '字辈')
|
||||
const rankName = normalizeLineageText(node.rankName, '排行名称')
|
||||
const relationName = normalizeLineageText(node.relationName, '人物关系')
|
||||
const birthDate = lineageDatePart(node.birthDate, '出生日期')
|
||||
const deathDate = lineageDatePart(node.deathDate, '逝世日期')
|
||||
@@ -70,15 +72,22 @@ export const normalizeLineageTree = (value) => {
|
||||
name: normalizeLineageText(node.name, '人物姓名', { required: true }),
|
||||
relation:
|
||||
relationOverride ||
|
||||
rankName ||
|
||||
(parentId
|
||||
? (relationName && relationName !== '配偶' ? relationName : '后代')
|
||||
? (relationName && relationName !== '配偶' ? relationName : '排行待补')
|
||||
: relationName || '始祖'),
|
||||
rankName,
|
||||
generation: node.generation,
|
||||
branch: generationName
|
||||
? (generationName.endsWith('字辈') ? generationName : `${generationName}字辈`)
|
||||
: '字辈待补',
|
||||
years: birthDate || deathDate ? `${birthDate}—${deathDate}` : '生卒待补',
|
||||
treeYears: compactLineageYears(birthDate, deathDate),
|
||||
avatarFile: normalizeBusinessFileAccess(
|
||||
node.avatarFile,
|
||||
'世系人物头像',
|
||||
'LINEAGE_TREE_RESPONSE_INVALID'
|
||||
),
|
||||
sex: normalizeLineageText(node.sex, '性别'),
|
||||
personStatus: normalizeLineageText(node.personStatus, '人物状态')
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ export const normalizeLineageWritePayload = (payload) => {
|
||||
'aliasName',
|
||||
'sex',
|
||||
'generationName',
|
||||
'rankId',
|
||||
'fatherId',
|
||||
'motherId',
|
||||
'avatarOssId',
|
||||
@@ -77,6 +78,11 @@ export const normalizeLineageWritePayload = (payload) => {
|
||||
if (payload[field] === undefined || payload[field] === null || payload[field] === '') continue
|
||||
normalizedPayload[field] = normalizeLineagePersonIdentity(payload[field], label)
|
||||
}
|
||||
if (Object.prototype.hasOwnProperty.call(payload, 'rankId')) {
|
||||
normalizedPayload.rankId = payload.rankId === null || payload.rankId === ''
|
||||
? null
|
||||
: normalizeLineagePersonIdentity(payload.rankId, '排行标识')
|
||||
}
|
||||
if (payload.avatarOssId !== undefined && payload.avatarOssId !== null && payload.avatarOssId !== '') {
|
||||
normalizedPayload.avatarOssId = normalizeOssIdString(payload.avatarOssId, '头像文件标识')
|
||||
}
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
import { createRequestError } from './request-client.js'
|
||||
import {
|
||||
normalizeOptionalNonnegativeInteger,
|
||||
normalizeOptionalNumericId,
|
||||
normalizeResponseText
|
||||
} from './response-normalizers.js'
|
||||
import { normalizeBusinessFileAccess } from './business-file-contract.js'
|
||||
|
||||
const promotionPlacements = new Set(['home_banner', 'home_bottom', 'message_bottom', 'profile_bottom'])
|
||||
const siteArticleTypeLabels = Object.freeze({
|
||||
news: '资讯',
|
||||
notice: '公告'
|
||||
})
|
||||
|
||||
export const normalizePromotionPlacement = (value) => {
|
||||
if (!promotionPlacements.has(value)) throw new TypeError('推广位无效')
|
||||
@@ -50,6 +55,64 @@ export const normalizeAppPromotions = (value, expectedPlacement) => {
|
||||
return promotions
|
||||
}
|
||||
|
||||
export const normalizeSiteArticles = (value) => {
|
||||
if (!Array.isArray(value)) {
|
||||
throw createRequestError('官网文章响应不是列表', 'SITE_ARTICLE_RESPONSE_INVALID')
|
||||
}
|
||||
const articles = value.map((item) => {
|
||||
if (!item || typeof item !== 'object' || Array.isArray(item)) {
|
||||
throw createRequestError('官网文章响应包含无效条目', 'SITE_ARTICLE_RESPONSE_INVALID')
|
||||
}
|
||||
const id = normalizeOptionalNumericId(item.articleId, '官网文章标识', 'SITE_ARTICLE_RESPONSE_INVALID')
|
||||
const title = normalizeResponseText(item.articleTitle, 'articleTitle', {
|
||||
code: 'SITE_ARTICLE_RESPONSE_INVALID',
|
||||
subject: '官网文章响应'
|
||||
})
|
||||
if (!id || !title) {
|
||||
throw createRequestError('官网文章响应缺少稳定字段', 'SITE_ARTICLE_RESPONSE_INVALID')
|
||||
}
|
||||
const externalUrl = normalizeResponseText(item.externalUrl, 'externalUrl', {
|
||||
code: 'SITE_ARTICLE_RESPONSE_INVALID',
|
||||
subject: '官网文章响应'
|
||||
})
|
||||
if (externalUrl && !/^https:\/\/[^\s/?#]+(?:[/?#][^\s]*)?$/.test(externalUrl)) {
|
||||
throw createRequestError('官网文章外部链接无效', 'SITE_ARTICLE_RESPONSE_INVALID')
|
||||
}
|
||||
const type = normalizeResponseText(item.articleType, 'articleType', {
|
||||
code: 'SITE_ARTICLE_RESPONSE_INVALID',
|
||||
subject: '官网文章响应'
|
||||
})
|
||||
return {
|
||||
id,
|
||||
title,
|
||||
type,
|
||||
typeLabel: siteArticleTypeLabels[type] || '资讯',
|
||||
summary: normalizeResponseText(item.articleSummary, 'articleSummary', {
|
||||
code: 'SITE_ARTICLE_RESPONSE_INVALID',
|
||||
subject: '官网文章响应'
|
||||
}),
|
||||
content: normalizeResponseText(item.articleContent, 'articleContent', {
|
||||
code: 'SITE_ARTICLE_RESPONSE_INVALID',
|
||||
subject: '官网文章响应'
|
||||
}),
|
||||
publishTime: normalizeResponseText(item.publishTime, 'publishTime', {
|
||||
code: 'SITE_ARTICLE_RESPONSE_INVALID',
|
||||
subject: '官网文章响应'
|
||||
}),
|
||||
externalUrl,
|
||||
sortOrder: normalizeOptionalNonnegativeInteger(
|
||||
item.sortOrder,
|
||||
'官网文章排序值',
|
||||
'SITE_ARTICLE_RESPONSE_INVALID'
|
||||
)
|
||||
}
|
||||
})
|
||||
if (new Set(articles.map((article) => article.id)).size !== articles.length) {
|
||||
throw createRequestError('官网文章响应包含重复标识', 'SITE_ARTICLE_RESPONSE_INVALID')
|
||||
}
|
||||
return articles
|
||||
}
|
||||
|
||||
const normalizeHelpArticleText = (value, field) => {
|
||||
if (typeof value !== 'string' || !value.trim()) {
|
||||
throw createRequestError(`帮助文章缺少 ${field}`, 'HELP_ARTICLE_RESPONSE_INVALID')
|
||||
|
||||
@@ -3,11 +3,33 @@ import {
|
||||
normalizeComplianceDocument,
|
||||
normalizeComplianceDocumentKey,
|
||||
normalizeHelpArticles,
|
||||
normalizePromotionPlacement
|
||||
normalizePromotionPlacement,
|
||||
normalizeSiteArticles
|
||||
} from './site-content-contract.js'
|
||||
import { requestStrict } from './request-client.js'
|
||||
|
||||
export const siteContentApi = {
|
||||
async getSiteArticles(requestOptions = {}) {
|
||||
const limit = requestOptions.limit ?? 20
|
||||
if (!Number.isSafeInteger(limit) || limit < 1 || limit > 100) {
|
||||
throw new TypeError('官网文章数量上限无效')
|
||||
}
|
||||
const articleType = typeof requestOptions.articleType === 'string'
|
||||
? requestOptions.articleType.trim()
|
||||
: ''
|
||||
const articles = await requestStrict({
|
||||
url: '/genealogy/app/site/articles',
|
||||
method: 'GET',
|
||||
data: {
|
||||
limit,
|
||||
...(articleType ? { articleType } : {})
|
||||
}
|
||||
}, {
|
||||
requestController: requestOptions.requestController ?? null
|
||||
})
|
||||
return normalizeSiteArticles(articles)
|
||||
},
|
||||
|
||||
async getHelpArticles(requestOptions = {}) {
|
||||
const helpArticles = await requestStrict({
|
||||
url: '/genealogy/app/help-articles',
|
||||
@@ -25,6 +47,7 @@ export const siteContentApi = {
|
||||
method: 'GET',
|
||||
data: { platform: 'app', placement }
|
||||
}, {
|
||||
authenticated: false,
|
||||
requestController: requestOptions.requestController ?? null
|
||||
})
|
||||
return normalizeAppPromotions(promotions, placement)
|
||||
|
||||
Reference in New Issue
Block a user