feat: migrate app routes and business modules
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
import { createRequestError } from './request-client.js'
|
||||
|
||||
const lineageTreeError = (message) =>
|
||||
createRequestError(message, 'LINEAGE_TREE_RESPONSE_INVALID')
|
||||
|
||||
const normalizeLineagePersonId = (value) => {
|
||||
if (typeof value === 'string' && /^[1-9]\d*$/.test(value)) return value
|
||||
if (typeof value === 'number' && Number.isSafeInteger(value) && value > 0) return String(value)
|
||||
throw lineageTreeError('世系树包含无效人物标识')
|
||||
}
|
||||
|
||||
const normalizeLineageText = (value, label, { required = false } = {}) => {
|
||||
if (value === undefined || value === null) {
|
||||
if (required) throw lineageTreeError(`世系树缺少${label}`)
|
||||
return ''
|
||||
}
|
||||
if (typeof value !== 'string') throw lineageTreeError(`世系树${label}无效`)
|
||||
const normalized = value.trim()
|
||||
if (required && !normalized) throw lineageTreeError(`世系树缺少${label}`)
|
||||
return normalized
|
||||
}
|
||||
|
||||
const lineageDatePart = (value, label) => {
|
||||
const normalized = normalizeLineageText(value, label)
|
||||
if (!normalized) return ''
|
||||
if (!/^\d{4}-\d{2}-\d{2}/.test(normalized)) {
|
||||
throw lineageTreeError(`世系树${label}无效`)
|
||||
}
|
||||
if (normalized.length === 10) return normalized
|
||||
const instant = new Date(normalized)
|
||||
if (Number.isNaN(instant.getTime())) throw lineageTreeError(`世系树${label}无效`)
|
||||
|
||||
const chinaTime = new Date(instant.getTime() + 8 * 60 * 60 * 1000)
|
||||
const year = chinaTime.getUTCFullYear()
|
||||
const month = String(chinaTime.getUTCMonth() + 1).padStart(2, '0')
|
||||
const day = String(chinaTime.getUTCDate()).padStart(2, '0')
|
||||
return `${year}-${month}-${day}`
|
||||
}
|
||||
|
||||
const compactLineageYears = (birthDate, deathDate) => {
|
||||
const birthYear = birthDate ? birthDate.slice(0, 4) : ''
|
||||
const deathYear = deathDate ? deathDate.slice(0, 4) : ''
|
||||
return birthYear || deathYear ? `${birthYear}—${deathYear}` : '生卒待补'
|
||||
}
|
||||
|
||||
export const normalizeLineageTree = (value) => {
|
||||
if (!Array.isArray(value)) throw lineageTreeError('世系树响应不是列表')
|
||||
const seen = new Set()
|
||||
const normalized = []
|
||||
const normalizedById = new Map()
|
||||
const appendNode = (node, parentId, relationOverride = '', spouseOf = '') => {
|
||||
if (!node || typeof node !== 'object' || Array.isArray(node)) {
|
||||
throw lineageTreeError('世系树包含无效节点')
|
||||
}
|
||||
const id = normalizeLineagePersonId(node.personId)
|
||||
if (seen.has(id)) throw lineageTreeError('世系树包含重复人物标识')
|
||||
seen.add(id)
|
||||
if (seen.size > 5000) throw lineageTreeError('世系树节点数量超出客户端上限')
|
||||
if (!Number.isSafeInteger(node.generation) || node.generation < 1) {
|
||||
throw lineageTreeError('世系树人物世代无效')
|
||||
}
|
||||
const generationName = normalizeLineageText(node.generationName, '字辈')
|
||||
const relationName = normalizeLineageText(node.relationName, '人物关系')
|
||||
const birthDate = lineageDatePart(node.birthDate, '出生日期')
|
||||
const deathDate = lineageDatePart(node.deathDate, '逝世日期')
|
||||
const normalizedNode = {
|
||||
id,
|
||||
parentId,
|
||||
...(spouseOf ? { spouseOf } : {}),
|
||||
name: normalizeLineageText(node.name, '人物姓名', { required: true }),
|
||||
relation:
|
||||
relationOverride ||
|
||||
(parentId
|
||||
? (relationName && relationName !== '配偶' ? relationName : '后代')
|
||||
: relationName || '始祖'),
|
||||
generation: node.generation,
|
||||
branch: generationName
|
||||
? (generationName.endsWith('字辈') ? generationName : `${generationName}字辈`)
|
||||
: '字辈待补',
|
||||
years: birthDate || deathDate ? `${birthDate}—${deathDate}` : '生卒待补',
|
||||
treeYears: compactLineageYears(birthDate, deathDate),
|
||||
sex: normalizeLineageText(node.sex, '性别'),
|
||||
personStatus: normalizeLineageText(node.personStatus, '人物状态')
|
||||
}
|
||||
normalized.push(normalizedNode)
|
||||
normalizedById.set(id, normalizedNode)
|
||||
return id
|
||||
}
|
||||
const walk = (node, parentId = null, depth = 0) => {
|
||||
if (depth > 64) throw lineageTreeError('世系树深度超出客户端上限')
|
||||
if (!node || typeof node !== 'object' || Array.isArray(node)) {
|
||||
throw lineageTreeError('世系树包含无效节点')
|
||||
}
|
||||
const spouses = node.spouses ?? []
|
||||
const children = node.children ?? []
|
||||
if (!Array.isArray(spouses) || !Array.isArray(children)) {
|
||||
throw lineageTreeError('世系树亲属集合无效')
|
||||
}
|
||||
const id = normalizeLineagePersonId(node.personId)
|
||||
const knownSpouseId = spouses
|
||||
.map((spouse) => normalizeLineagePersonId(spouse?.personId))
|
||||
.find((spouseId) => normalizedById.has(spouseId)) || ''
|
||||
if (seen.has(id)) {
|
||||
if (knownSpouseId) return id
|
||||
throw lineageTreeError('世系树包含重复人物标识')
|
||||
}
|
||||
const knownSpouse = knownSpouseId ? normalizedById.get(knownSpouseId) : null
|
||||
const nodeParentId = knownSpouse ? knownSpouse.parentId : parentId
|
||||
const appendedId = appendNode(node, nodeParentId, knownSpouse ? '配偶' : '', knownSpouseId)
|
||||
spouses.forEach((spouse) => {
|
||||
const spouseId = normalizeLineagePersonId(spouse?.personId)
|
||||
if (seen.has(spouseId)) return
|
||||
appendNode(spouse, nodeParentId, '配偶', appendedId)
|
||||
})
|
||||
children.forEach((child) => walk(child, knownSpouseId || appendedId, depth + 1))
|
||||
}
|
||||
value.forEach((root) => walk(root))
|
||||
return normalized
|
||||
}
|
||||
Reference in New Issue
Block a user