485 lines
20 KiB
JavaScript
485 lines
20 KiB
JavaScript
import {
|
|
fromApiGenealogyAccess,
|
|
isGenealogyJoinMode,
|
|
isGenealogyVisibility
|
|
} from '@/utils/genealogy/access-policy.js'
|
|
import { normalizeBusinessFileAccess } from './business-file-contract.js'
|
|
import { createRequestError } from './request-client.js'
|
|
import { assertPlainPayload, normalizeOssIdString } from './request-normalizers.js'
|
|
import {
|
|
normalizeOptionalNonnegativeInteger,
|
|
normalizeOptionalNumericId,
|
|
normalizeResponseText
|
|
} from './response-normalizers.js'
|
|
|
|
const normalizeGenealogyResponseText = (value, label) => normalizeResponseText(value, label, {
|
|
code: 'GENEALOGY_RESPONSE_INVALID',
|
|
subject: '我的家谱响应'
|
|
})
|
|
|
|
const normalizeGenealogySettingsResponseText = (value, label) => normalizeResponseText(value, label, {
|
|
code: 'GENEALOGY_SETTINGS_RESPONSE_INVALID',
|
|
subject: '家谱设置响应'
|
|
})
|
|
|
|
export const GENEALOGY_LIFECYCLE_STATUS = Object.freeze({
|
|
NORMAL: 'normal',
|
|
ARCHIVED: 'archived',
|
|
DELETE_PENDING: 'delete_pending'
|
|
})
|
|
|
|
const genealogyLifecycleStatuses = new Set(
|
|
Object.values(GENEALOGY_LIFECYCLE_STATUS)
|
|
)
|
|
|
|
const normalizeGenealogyLifecycleStatus = (value, code) => {
|
|
const status = normalizeResponseText(value, 'lifecycleStatus', {
|
|
code,
|
|
subject: '家谱响应'
|
|
})
|
|
if (!genealogyLifecycleStatuses.has(status)) {
|
|
throw createRequestError('家谱生命周期状态无效', code)
|
|
}
|
|
return status
|
|
}
|
|
|
|
const normalizeGenealogyAccessValue = (value, field, isValid, code) => {
|
|
const normalized = normalizeResponseText(value, field, {
|
|
code,
|
|
subject: '家谱响应'
|
|
})
|
|
if (normalized && !isValid(normalized)) {
|
|
throw createRequestError(`家谱字段 ${field} 无效`, code)
|
|
}
|
|
return normalized
|
|
}
|
|
|
|
const normalizeGenealogyCapabilities = (value, code) => {
|
|
const capabilities = {}
|
|
for (const field of ['canManage', 'canEditContent', 'canArchive', 'canRestore']) {
|
|
if (typeof value[field] !== 'boolean') {
|
|
throw createRequestError(`家谱权限字段 ${field} 无效`, code)
|
|
}
|
|
capabilities[field] = value[field]
|
|
}
|
|
return capabilities
|
|
}
|
|
|
|
export const normalizeGenealogyPermanentDeletionCapability = (value) => {
|
|
if (!value || typeof value !== 'object' || Array.isArray(value) || typeof value.canDeletePermanently !== 'boolean') {
|
|
throw createRequestError('家谱永久删除能力响应无效', 'GENEALOGY_DELETION_RESPONSE_INVALID')
|
|
}
|
|
if (!Array.isArray(value.disabledReasons) || value.disabledReasons.some((reason) => typeof reason !== 'string')) {
|
|
throw createRequestError('家谱永久删除阻断原因无效', 'GENEALOGY_DELETION_RESPONSE_INVALID')
|
|
}
|
|
const phoneMasked = normalizeResponseText(value.verifiedMobileMasked, 'verifiedMobileMasked', {
|
|
code: 'GENEALOGY_DELETION_RESPONSE_INVALID',
|
|
subject: '家谱永久删除能力响应'
|
|
})
|
|
if (value.canDeletePermanently && !phoneMasked) {
|
|
throw createRequestError('家谱永久删除能力缺少验证手机号', 'GENEALOGY_DELETION_RESPONSE_INVALID')
|
|
}
|
|
return {
|
|
canDeletePermanently: value.canDeletePermanently,
|
|
phoneMasked,
|
|
disabledReasons: value.disabledReasons.map((reason) => reason.trim()).filter(Boolean)
|
|
}
|
|
}
|
|
|
|
export const normalizeGenealogyPermanentDeletionPayload = (payload) => {
|
|
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
|
|
throw new TypeError('家谱永久删除请求无效')
|
|
}
|
|
if (typeof payload.confirmationName !== 'string' || !payload.confirmationName.trim() || payload.confirmationName.trim().length > 24) {
|
|
throw new TypeError('家谱永久删除确认名称无效')
|
|
}
|
|
if (typeof payload.smsCode !== 'string' || !/^\d{4}$/.test(payload.smsCode)) {
|
|
throw new TypeError('家谱永久删除验证码必须是4位数字')
|
|
}
|
|
return { genealogyName: payload.confirmationName.trim(), smsCode: payload.smsCode }
|
|
}
|
|
|
|
export const normalizeGenealogyDeletionTask = (value, expectedGenealogyId) => {
|
|
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
throw createRequestError('家谱删除任务响应无效', 'GENEALOGY_DELETION_RESPONSE_INVALID')
|
|
}
|
|
const taskId = normalizeResponseText(String(value.taskId ?? ''), 'taskId', {
|
|
code: 'GENEALOGY_DELETION_RESPONSE_INVALID',
|
|
subject: '家谱删除任务'
|
|
})
|
|
const genealogyId = String(value.genealogyId ?? '')
|
|
if (!/^[1-9]\d*$/.test(taskId) || genealogyId !== expectedGenealogyId) {
|
|
throw createRequestError('家谱删除任务缺少稳定归属字段', 'GENEALOGY_DELETION_RESPONSE_INVALID')
|
|
}
|
|
return { taskId, genealogyId, status: normalizeResponseText(value.status, 'status') }
|
|
}
|
|
|
|
const normalizeMyGenealogyId = (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 createRequestError('我的家谱响应包含无效标识', 'GENEALOGY_RESPONSE_INVALID')
|
|
}
|
|
|
|
export const normalizeGenealogyPathId = (value) => {
|
|
const id = typeof value === 'number' && Number.isSafeInteger(value) ? String(value) : value
|
|
if (typeof id !== 'string' || !/^[1-9]\d*$/.test(id)) {
|
|
throw createRequestError('家谱标识无效', 'GENEALOGY_ID_INVALID')
|
|
}
|
|
return id
|
|
}
|
|
|
|
export const normalizeCreatedGenealogy = (value) => {
|
|
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
throw createRequestError('创建家谱响应无效', 'GENEALOGY_RESPONSE_INVALID')
|
|
}
|
|
const id = value.genealogyId
|
|
if (typeof id === 'string' && /^[1-9]\d*$/.test(id)) return { id }
|
|
if (typeof id === 'number' && Number.isSafeInteger(id) && id > 0) return { id: String(id) }
|
|
throw createRequestError('创建家谱响应缺少有效标识', 'GENEALOGY_RESPONSE_INVALID')
|
|
}
|
|
|
|
export const normalizeGenealogySettings = (value, expectedGenealogyId) => {
|
|
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
throw createRequestError('家谱设置响应无效', 'GENEALOGY_SETTINGS_RESPONSE_INVALID')
|
|
}
|
|
const id = normalizeMyGenealogyId(value.genealogyId)
|
|
if (id !== expectedGenealogyId) {
|
|
throw createRequestError('家谱设置响应标识不匹配', 'GENEALOGY_SETTINGS_RESPONSE_INVALID')
|
|
}
|
|
const genealogyName = normalizeGenealogySettingsResponseText(value.genealogyName, 'genealogyName')
|
|
const surname = normalizeGenealogySettingsResponseText(value.surname, 'surname')
|
|
const regionCode = normalizeGenealogySettingsResponseText(value.regionCode, 'regionCode')
|
|
if (!genealogyName || !surname || !regionCode) {
|
|
throw createRequestError('家谱设置响应缺少名称、姓氏或地区', 'GENEALOGY_SETTINGS_RESPONSE_INVALID')
|
|
}
|
|
const visibility = normalizeGenealogyAccessValue(
|
|
value.visibility,
|
|
'visibility',
|
|
isGenealogyVisibility,
|
|
'GENEALOGY_SETTINGS_RESPONSE_INVALID'
|
|
)
|
|
const joinMode = normalizeGenealogyAccessValue(
|
|
value.joinMode,
|
|
'joinMode',
|
|
isGenealogyJoinMode,
|
|
'GENEALOGY_SETTINGS_RESPONSE_INVALID'
|
|
)
|
|
const capabilities = normalizeGenealogyCapabilities(
|
|
value,
|
|
'GENEALOGY_SETTINGS_RESPONSE_INVALID'
|
|
)
|
|
return {
|
|
id,
|
|
genealogyName,
|
|
surname,
|
|
ancestralHall: normalizeGenealogySettingsResponseText(value.ancestralHall, 'ancestralHall'),
|
|
originPlace: normalizeGenealogySettingsResponseText(value.originPlace, 'originPlace'),
|
|
regionCode,
|
|
regionName: normalizeGenealogySettingsResponseText(value.regionName, 'regionName'),
|
|
regionFullName: normalizeGenealogySettingsResponseText(value.regionFullName, 'regionFullName'),
|
|
addressDetail: normalizeGenealogySettingsResponseText(value.addressDetail, 'addressDetail'),
|
|
coverFile: normalizeBusinessFileAccess(
|
|
value.coverFile,
|
|
'家谱封面',
|
|
'GENEALOGY_SETTINGS_RESPONSE_INVALID'
|
|
),
|
|
intro: normalizeGenealogySettingsResponseText(value.intro, 'intro'),
|
|
accessPreset: fromApiGenealogyAccess({ visibility, joinMode }),
|
|
visibility,
|
|
joinMode,
|
|
memberCount: normalizeOptionalNonnegativeInteger(value.memberCount, '家谱成员数量', 'GENEALOGY_SETTINGS_RESPONSE_INVALID'),
|
|
personCount: normalizeOptionalNonnegativeInteger(value.personCount, '家谱人物数量', 'GENEALOGY_SETTINGS_RESPONSE_INVALID'),
|
|
...capabilities,
|
|
lifecycleStatus: normalizeGenealogyLifecycleStatus(
|
|
value.lifecycleStatus,
|
|
'GENEALOGY_SETTINGS_RESPONSE_INVALID'
|
|
),
|
|
archivedAt: normalizeGenealogySettingsResponseText(value.archivedAt, 'archivedAt')
|
|
}
|
|
}
|
|
|
|
export const normalizeAppGenealogy = (item) => {
|
|
if (!item || typeof item !== 'object' || Array.isArray(item)) {
|
|
throw createRequestError('我的家谱响应包含无效条目', 'GENEALOGY_RESPONSE_INVALID')
|
|
}
|
|
const name = normalizeGenealogyResponseText(item.genealogyName, 'genealogyName')
|
|
if (!name) {
|
|
throw createRequestError('我的家谱响应缺少家谱名称', 'GENEALOGY_RESPONSE_INVALID')
|
|
}
|
|
const capabilities = normalizeGenealogyCapabilities(item, 'GENEALOGY_RESPONSE_INVALID')
|
|
if (!Number.isSafeInteger(item.memberCount) || item.memberCount < 0) {
|
|
throw createRequestError('我的家谱响应成员数量无效', 'GENEALOGY_RESPONSE_INVALID')
|
|
}
|
|
const personCount = item.personCount ?? null
|
|
if (personCount !== null && (!Number.isSafeInteger(personCount) || personCount < 0)) {
|
|
throw createRequestError('我的家谱响应世系人数无效', 'GENEALOGY_RESPONSE_INVALID')
|
|
}
|
|
const visibility = normalizeGenealogyAccessValue(
|
|
item.visibility,
|
|
'visibility',
|
|
isGenealogyVisibility,
|
|
'GENEALOGY_RESPONSE_INVALID'
|
|
)
|
|
const joinMode = normalizeGenealogyAccessValue(
|
|
item.joinMode,
|
|
'joinMode',
|
|
isGenealogyJoinMode,
|
|
'GENEALOGY_RESPONSE_INVALID'
|
|
)
|
|
return {
|
|
id: normalizeMyGenealogyId(item.genealogyId),
|
|
genealogyNo: normalizeGenealogyResponseText(item.genealogyNo, 'genealogyNo'),
|
|
name,
|
|
firstAncestorName: normalizeGenealogyResponseText(item.firstAncestorName, 'firstAncestorName'),
|
|
rootPersonId: normalizeOptionalNumericId(item.rootPersonId, '始迁祖人物标识', 'GENEALOGY_RESPONSE_INVALID'),
|
|
surname: normalizeGenealogyResponseText(item.surname, 'surname'),
|
|
hall: normalizeGenealogyResponseText(item.ancestralHall, 'ancestralHall'),
|
|
location:
|
|
normalizeGenealogyResponseText(item.regionFullName, 'regionFullName') ||
|
|
normalizeGenealogyResponseText(item.regionName, 'regionName') ||
|
|
normalizeGenealogyResponseText(item.originPlace, 'originPlace') ||
|
|
normalizeGenealogyResponseText(item.addressDetail, 'addressDetail') ||
|
|
'地区待补',
|
|
memberCount: item.memberCount,
|
|
personCount,
|
|
accessPreset: fromApiGenealogyAccess({ visibility, joinMode }),
|
|
...capabilities,
|
|
lifecycleStatus: normalizeGenealogyLifecycleStatus(
|
|
item.lifecycleStatus,
|
|
'GENEALOGY_RESPONSE_INVALID'
|
|
),
|
|
archivedAt: normalizeGenealogyResponseText(item.archivedAt, 'archivedAt'),
|
|
intro: normalizeGenealogyResponseText(item.intro, 'intro'),
|
|
createTime: normalizeGenealogyResponseText(item.createTime, 'createTime'),
|
|
joinTime: normalizeGenealogyResponseText(item.joinTime, 'joinTime')
|
|
}
|
|
}
|
|
|
|
export const normalizeMyGenealogies = (value) => {
|
|
if (!Array.isArray(value)) {
|
|
throw createRequestError('我的家谱响应不是列表', 'GENEALOGY_RESPONSE_INVALID')
|
|
}
|
|
const normalized = value.map((item) => {
|
|
const genealogy = normalizeAppGenealogy(item)
|
|
return {
|
|
id: genealogy.id,
|
|
name: genealogy.name,
|
|
surname: genealogy.surname,
|
|
hall: genealogy.hall,
|
|
location: genealogy.location,
|
|
memberCount: genealogy.memberCount,
|
|
canManage: genealogy.canManage,
|
|
canEditContent: genealogy.canEditContent,
|
|
createTime: genealogy.createTime,
|
|
joinTime: genealogy.joinTime,
|
|
lifecycleStatus: genealogy.lifecycleStatus,
|
|
archivedAt: genealogy.archivedAt,
|
|
canArchive: genealogy.canArchive,
|
|
canRestore: genealogy.canRestore
|
|
}
|
|
})
|
|
if (new Set(normalized.map((genealogy) => genealogy.id)).size !== normalized.length) {
|
|
throw createRequestError('我的家谱响应包含重复标识', 'GENEALOGY_RESPONSE_INVALID')
|
|
}
|
|
return normalized
|
|
}
|
|
|
|
export const normalizeGenealogyOrderIds = (value) => {
|
|
if (!Array.isArray(value)) {
|
|
throw createRequestError('家谱排序必须是列表', 'GENEALOGY_ORDER_INVALID')
|
|
}
|
|
const ids = value.map((item) => normalizeMyGenealogyId(item))
|
|
if (new Set(ids).size !== ids.length) {
|
|
throw createRequestError('家谱排序不能包含重复家谱', 'GENEALOGY_ORDER_INVALID')
|
|
}
|
|
return ids
|
|
}
|
|
|
|
export const normalizePublicGenealogyQuery = (query = {}) => {
|
|
assertPlainPayload(query, new Set(['keyword']), '公开家谱查询')
|
|
if (query.keyword === undefined || query.keyword === null) return {}
|
|
if (typeof query.keyword !== 'string') throw new TypeError('公开家谱关键词必须是字符串')
|
|
const keyword = query.keyword.trim()
|
|
if (keyword.length > 50) throw new TypeError('公开家谱关键词不能超过50个字符')
|
|
return keyword ? { keyword } : {}
|
|
}
|
|
|
|
export const normalizePublicGenealogies = (value) => {
|
|
if (!Array.isArray(value)) {
|
|
throw createRequestError('公开家谱响应不是列表', 'PUBLIC_GENEALOGY_RESPONSE_INVALID')
|
|
}
|
|
const genealogies = value.map((item) => {
|
|
if (!item || typeof item !== 'object' || Array.isArray(item)) {
|
|
throw createRequestError('公开家谱响应包含无效条目', 'PUBLIC_GENEALOGY_RESPONSE_INVALID')
|
|
}
|
|
const id = normalizeMyGenealogyId(item.genealogyId)
|
|
const name = normalizeGenealogyResponseText(item.genealogyName, 'genealogyName')
|
|
if (!name) {
|
|
throw createRequestError('公开家谱响应缺少家谱名称', 'PUBLIC_GENEALOGY_RESPONSE_INVALID')
|
|
}
|
|
const memberCount = normalizeOptionalNonnegativeInteger(
|
|
item.memberCount,
|
|
'公开家谱成员数量',
|
|
'PUBLIC_GENEALOGY_RESPONSE_INVALID'
|
|
)
|
|
if (item.canManage !== undefined && typeof item.canManage !== 'boolean') {
|
|
throw createRequestError('公开家谱管理权限无效', 'PUBLIC_GENEALOGY_RESPONSE_INVALID')
|
|
}
|
|
const memberStatus = normalizeGenealogyResponseText(item.memberStatus, 'memberStatus')
|
|
return {
|
|
id,
|
|
name,
|
|
surname: normalizeGenealogyResponseText(item.surname, 'surname'),
|
|
location:
|
|
normalizeGenealogyResponseText(item.regionFullName, 'regionFullName') ||
|
|
normalizeGenealogyResponseText(item.regionName, 'regionName') ||
|
|
normalizeGenealogyResponseText(item.originPlace, 'originPlace') ||
|
|
normalizeGenealogyResponseText(item.addressDetail, 'addressDetail'),
|
|
intro: normalizeGenealogyResponseText(item.intro, 'intro'),
|
|
memberCount,
|
|
canManage: item.canManage === true,
|
|
memberStatus,
|
|
hasMembership: item.canManage === true || Boolean(memberStatus)
|
|
}
|
|
})
|
|
if (new Set(genealogies.map((genealogy) => genealogy.id)).size !== genealogies.length) {
|
|
throw createRequestError('公开家谱响应包含重复标识', 'PUBLIC_GENEALOGY_RESPONSE_INVALID')
|
|
}
|
|
return genealogies
|
|
}
|
|
|
|
const normalizeGenealogyWriteText = (value, field, { required = false } = {}) => {
|
|
if (value === undefined || value === null) {
|
|
if (required) throw new TypeError(`创建家谱缺少 ${field}`)
|
|
return ''
|
|
}
|
|
if (typeof value !== 'string') throw new TypeError(`创建家谱字段 ${field} 必须是字符串`)
|
|
const normalized = value.trim()
|
|
if (required && !normalized) throw new TypeError(`创建家谱缺少 ${field}`)
|
|
return normalized
|
|
}
|
|
|
|
const GENEALOGY_WRITE_FIELDS = new Set([
|
|
'genealogyName',
|
|
'surname',
|
|
'regionCode',
|
|
'ancestralHall',
|
|
'originPlace',
|
|
'addressDetail',
|
|
'intro',
|
|
'coverOssId',
|
|
'visibility',
|
|
'joinMode'
|
|
])
|
|
|
|
export const normalizeGenealogyCreatePayload = (payload) => {
|
|
assertPlainPayload(
|
|
payload,
|
|
new Set([...GENEALOGY_WRITE_FIELDS, 'firstAncestorName', 'requestId', 'ownerIsFirstAncestor']),
|
|
'创建家谱请求'
|
|
)
|
|
const requestId = normalizeGenealogyWriteText(payload.requestId, 'requestId', { required: true })
|
|
if (requestId.length > 64) throw new TypeError('创建家谱请求号不能超过64个字符')
|
|
const firstAncestorName = normalizeGenealogyWriteText(payload.firstAncestorName, '始迁祖姓名', { required: true })
|
|
if (firstAncestorName.length > 30) throw new TypeError('始迁祖姓名不能超过30个字符')
|
|
if (payload.ownerIsFirstAncestor !== undefined && typeof payload.ownerIsFirstAncestor !== 'boolean') {
|
|
throw new TypeError('谱主是否为始迁祖必须是布尔值')
|
|
}
|
|
const normalizedPayload = {
|
|
genealogyName: normalizeGenealogyWriteText(payload.genealogyName, '谱名', { required: true }),
|
|
surname: normalizeGenealogyWriteText(payload.surname, '姓氏', { required: true }),
|
|
regionCode: normalizeGenealogyWriteText(payload.regionCode, '地区代码', { required: true }),
|
|
firstAncestorName,
|
|
requestId,
|
|
ownerIsFirstAncestor: payload.ownerIsFirstAncestor === true
|
|
}
|
|
for (const field of ['ancestralHall', 'originPlace', 'addressDetail', 'intro']) {
|
|
const normalizedText = normalizeGenealogyWriteText(payload[field], field)
|
|
if (normalizedText) normalizedPayload[field] = normalizedText
|
|
}
|
|
const visibility = normalizeGenealogyWriteText(payload.visibility, 'visibility')
|
|
if (visibility) {
|
|
if (!isGenealogyVisibility(visibility)) throw new TypeError('创建家谱字段 visibility 无效')
|
|
normalizedPayload.visibility = visibility
|
|
}
|
|
const joinMode = normalizeGenealogyWriteText(payload.joinMode, 'joinMode')
|
|
if (joinMode) {
|
|
if (!isGenealogyJoinMode(joinMode)) throw new TypeError('创建家谱字段 joinMode 无效')
|
|
normalizedPayload.joinMode = joinMode
|
|
}
|
|
if (payload.coverOssId !== undefined && payload.coverOssId !== null && payload.coverOssId !== '') {
|
|
normalizedPayload.coverOssId = normalizeOssIdString(payload.coverOssId, '创建家谱字段 coverOssId')
|
|
}
|
|
return normalizedPayload
|
|
}
|
|
|
|
export const normalizeGenealogyUpdatePayload = (payload) => {
|
|
assertPlainPayload(payload, GENEALOGY_WRITE_FIELDS, '更新家谱请求')
|
|
const normalizedPayload = {}
|
|
for (const field of [
|
|
'genealogyName',
|
|
'surname',
|
|
'regionCode',
|
|
'ancestralHall',
|
|
'originPlace',
|
|
'addressDetail',
|
|
'intro'
|
|
]) {
|
|
if (!Object.prototype.hasOwnProperty.call(payload, field)) continue
|
|
const normalizedText = normalizeGenealogyWriteText(payload[field], field)
|
|
if (normalizedText) normalizedPayload[field] = normalizedText
|
|
}
|
|
if (Object.prototype.hasOwnProperty.call(payload, 'visibility')) {
|
|
const visibility = normalizeGenealogyWriteText(payload.visibility, 'visibility')
|
|
if (!isGenealogyVisibility(visibility)) throw new TypeError('更新家谱字段 visibility 无效')
|
|
normalizedPayload.visibility = visibility
|
|
}
|
|
if (Object.prototype.hasOwnProperty.call(payload, 'joinMode')) {
|
|
const joinMode = normalizeGenealogyWriteText(payload.joinMode, 'joinMode')
|
|
if (!isGenealogyJoinMode(joinMode)) throw new TypeError('更新家谱字段 joinMode 无效')
|
|
normalizedPayload.joinMode = joinMode
|
|
}
|
|
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.keys(normalizedPayload).length) throw new TypeError('请至少填写一项需要更新的家谱信息')
|
|
return normalizedPayload
|
|
}
|
|
|
|
export const normalizeGenealogyQuota = (value) => {
|
|
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
throw createRequestError('家谱配额响应无效', 'GENEALOGY_QUOTA_RESPONSE_INVALID')
|
|
}
|
|
const normalizeUsage = (field) => {
|
|
if (!Number.isSafeInteger(value[field]) || value[field] < 0) {
|
|
throw createRequestError(`家谱配额字段 ${field} 无效`, 'GENEALOGY_QUOTA_RESPONSE_INVALID')
|
|
}
|
|
return value[field]
|
|
}
|
|
const normalizeLimit = (field) => {
|
|
if (!Number.isSafeInteger(value[field]) || value[field] < -1) {
|
|
throw createRequestError(`家谱配额字段 ${field} 无效`, 'GENEALOGY_QUOTA_RESPONSE_INVALID')
|
|
}
|
|
return value[field]
|
|
}
|
|
const normalizePermission = (field) => {
|
|
if (typeof value[field] !== 'boolean') {
|
|
throw createRequestError(`家谱配额字段 ${field} 无效`, 'GENEALOGY_QUOTA_RESPONSE_INVALID')
|
|
}
|
|
return value[field]
|
|
}
|
|
return {
|
|
createUsed: normalizeUsage('createUsed'),
|
|
createLimit: normalizeLimit('createLimit'),
|
|
createRemaining: normalizeLimit('createRemaining'),
|
|
canCreate: normalizePermission('canCreate'),
|
|
joinUsed: normalizeUsage('joinUsed'),
|
|
joinLimit: normalizeLimit('joinLimit'),
|
|
joinRemaining: normalizeLimit('joinRemaining'),
|
|
canJoin: normalizePermission('canJoin')
|
|
}
|
|
}
|