feat: migrate app routes and business modules
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
import { createRequestError } from './request-client.js'
|
||||
import {
|
||||
normalizeOptionalNumericId,
|
||||
normalizeOptionalResponseText
|
||||
} from './response-normalizers.js'
|
||||
import {
|
||||
assertPlainPayload,
|
||||
normalizeOptionalNormalDisableStatus,
|
||||
normalizeOptionalText,
|
||||
normalizeResourcePathId
|
||||
} from './request-normalizers.js'
|
||||
|
||||
const normalizePersonDocumentText = (value, field) =>
|
||||
normalizeOptionalResponseText(value, field, '重要证件响应', 'PERSON_DOCUMENT_RESPONSE_INVALID')
|
||||
|
||||
export const PERSON_DOCUMENT_TYPE_OPTIONS = Object.freeze([
|
||||
Object.freeze({ value: 'id_card', label: '身份证' }),
|
||||
Object.freeze({ value: 'household_register', label: '户口簿' }),
|
||||
Object.freeze({ value: 'birth_certificate', label: '出生证明' }),
|
||||
Object.freeze({ value: 'marriage_certificate', label: '结婚证' }),
|
||||
Object.freeze({ value: 'other', label: '其他证件' })
|
||||
])
|
||||
|
||||
export const PERSON_DOCUMENT_RESOURCE_USAGE = Object.freeze({
|
||||
FRONT: 'FRONT',
|
||||
BACK: 'BACK',
|
||||
ATTACHMENT: 'ATTACHMENT'
|
||||
})
|
||||
|
||||
export const PERSON_DOCUMENT_RESOURCE_USAGE_LABELS = Object.freeze({
|
||||
[PERSON_DOCUMENT_RESOURCE_USAGE.FRONT]: '正面',
|
||||
[PERSON_DOCUMENT_RESOURCE_USAGE.BACK]: '反面',
|
||||
[PERSON_DOCUMENT_RESOURCE_USAGE.ATTACHMENT]: '附件'
|
||||
})
|
||||
|
||||
const personDocumentTypes = new Set(
|
||||
PERSON_DOCUMENT_TYPE_OPTIONS.map(({ value }) => value)
|
||||
)
|
||||
const personDocumentResourceUsages = new Set(
|
||||
Object.values(PERSON_DOCUMENT_RESOURCE_USAGE)
|
||||
)
|
||||
|
||||
export const normalizePersonDocumentResource = (value) => {
|
||||
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
||||
throw createRequestError('证件文件响应无效', 'PERSON_DOCUMENT_RESPONSE_INVALID')
|
||||
}
|
||||
const usageType = normalizePersonDocumentText(value.usageType, 'usageType')
|
||||
if (!personDocumentResourceUsages.has(usageType)) {
|
||||
throw createRequestError('证件文件用途无效', 'PERSON_DOCUMENT_RESPONSE_INVALID')
|
||||
}
|
||||
return {
|
||||
resourceId: normalizeResourcePathId(value.resourceId, '证件文件标识'),
|
||||
usageType,
|
||||
sortOrder: Number.isSafeInteger(value.sortOrder) ? value.sortOrder : null,
|
||||
status: normalizePersonDocumentText(value.status, 'status')
|
||||
}
|
||||
}
|
||||
|
||||
export const normalizePersonDocument = (value, expectedGenealogyId) => {
|
||||
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
||||
throw createRequestError('重要证件响应无效', 'PERSON_DOCUMENT_RESPONSE_INVALID')
|
||||
}
|
||||
const genealogyId = normalizeResourcePathId(value.genealogyId, '家谱标识')
|
||||
if (genealogyId !== expectedGenealogyId) {
|
||||
throw createRequestError('重要证件归属与请求不匹配', 'PERSON_DOCUMENT_RESPONSE_INVALID')
|
||||
}
|
||||
const documentType = normalizePersonDocumentText(value.documentType, 'documentType')
|
||||
if (!personDocumentTypes.has(documentType)) {
|
||||
throw createRequestError('重要证件类型无效', 'PERSON_DOCUMENT_RESPONSE_INVALID')
|
||||
}
|
||||
const documentTitle = normalizePersonDocumentText(value.documentTitle, 'documentTitle')
|
||||
if (!documentTitle) throw createRequestError('重要证件缺少标题', 'PERSON_DOCUMENT_RESPONSE_INVALID')
|
||||
for (const field of ['contentProtected', 'contentUnlocked', 'canEdit', 'canDelete']) {
|
||||
if (typeof value[field] !== 'boolean') {
|
||||
throw createRequestError(`重要证件权限字段 ${field} 无效`, 'PERSON_DOCUMENT_RESPONSE_INVALID')
|
||||
}
|
||||
}
|
||||
if (!Array.isArray(value.resources)) {
|
||||
throw createRequestError('重要证件文件列表无效', 'PERSON_DOCUMENT_RESPONSE_INVALID')
|
||||
}
|
||||
return {
|
||||
documentId: normalizeResourcePathId(value.documentId, '重要证件标识'),
|
||||
genealogyId,
|
||||
lineagePersonId: normalizeResourcePathId(value.lineagePersonId, '世系人物标识'),
|
||||
lineagePersonName: normalizePersonDocumentText(value.lineagePersonName, 'lineagePersonName'),
|
||||
documentType,
|
||||
documentTitle,
|
||||
maskedIdentifier: normalizePersonDocumentText(value.maskedIdentifier, 'maskedIdentifier'),
|
||||
description: normalizePersonDocumentText(value.description, 'description'),
|
||||
uploaderUserId: normalizeOptionalNumericId(value.uploaderUserId, '上传人标识', 'PERSON_DOCUMENT_RESPONSE_INVALID'),
|
||||
sortOrder: Number.isSafeInteger(value.sortOrder) ? value.sortOrder : null,
|
||||
status: normalizePersonDocumentText(value.status, 'status'),
|
||||
contentProtected: value.contentProtected,
|
||||
contentUnlocked: value.contentUnlocked,
|
||||
canEdit: value.canEdit,
|
||||
canDelete: value.canDelete,
|
||||
resources: value.resources.map(normalizePersonDocumentResource)
|
||||
}
|
||||
}
|
||||
|
||||
export const normalizePersonDocuments = (value, expectedGenealogyId) => {
|
||||
if (!Array.isArray(value)) throw createRequestError('重要证件响应不是列表', 'PERSON_DOCUMENT_RESPONSE_INVALID')
|
||||
const rows = value.map((item) => normalizePersonDocument(item, expectedGenealogyId))
|
||||
if (new Set(rows.map((item) => item.documentId)).size !== rows.length) {
|
||||
throw createRequestError('重要证件包含重复标识', 'PERSON_DOCUMENT_RESPONSE_INVALID')
|
||||
}
|
||||
return rows
|
||||
}
|
||||
|
||||
export const normalizePersonDocumentPayload = (payload) => {
|
||||
assertPlainPayload(payload, new Set(['lineagePersonId', 'documentType', 'documentTitle', 'maskedIdentifier', 'description', 'sortOrder', 'status']), '重要证件请求')
|
||||
const documentType = normalizeOptionalText(payload.documentType, 'documentType')
|
||||
if (!personDocumentTypes.has(documentType)) throw new TypeError('请选择有效的证件类型')
|
||||
const documentTitle = normalizeOptionalText(payload.documentTitle, 'documentTitle')
|
||||
if (!documentTitle) throw new TypeError('请填写证件标题')
|
||||
if (documentTitle.length > 100) throw new TypeError('证件标题不能超过100个字符')
|
||||
const normalizedPayload = {
|
||||
lineagePersonId: normalizeResourcePathId(payload.lineagePersonId, '世系人物标识'),
|
||||
documentType,
|
||||
documentTitle
|
||||
}
|
||||
for (const [field, limit] of [['maskedIdentifier', 100], ['description', 1000]]) {
|
||||
if (!Object.prototype.hasOwnProperty.call(payload, field)) continue
|
||||
if (typeof payload[field] !== 'string') throw new TypeError(`${field}必须是字符串`)
|
||||
const normalizedText = payload[field].trim()
|
||||
if (normalizedText.length > limit) throw new TypeError(`${field} 超出长度限制`)
|
||||
normalizedPayload[field] = normalizedText
|
||||
}
|
||||
if (payload.sortOrder !== undefined && payload.sortOrder !== null && payload.sortOrder !== '') {
|
||||
const sortOrder = typeof payload.sortOrder === 'number' ? payload.sortOrder : Number(payload.sortOrder)
|
||||
if (!Number.isSafeInteger(sortOrder)) throw new TypeError('sortOrder必须是安全整数')
|
||||
normalizedPayload.sortOrder = sortOrder
|
||||
}
|
||||
const status = normalizeOptionalNormalDisableStatus(payload.status, '证件状态')
|
||||
if (status) normalizedPayload.status = status
|
||||
return normalizedPayload
|
||||
}
|
||||
|
||||
export const normalizePersonDocumentResourcePayload = (payload) => {
|
||||
assertPlainPayload(payload, new Set(['ossId', 'usageType', 'sortOrder']), '证件文件请求')
|
||||
if (!personDocumentResourceUsages.has(payload.usageType)) throw new TypeError('请选择有效的证件文件用途')
|
||||
const ossId = normalizeResourcePathId(payload.ossId, 'OSS文件标识')
|
||||
const normalizedPayload = { ossId, usageType: payload.usageType }
|
||||
if (payload.sortOrder !== undefined && payload.sortOrder !== null && payload.sortOrder !== '') {
|
||||
const sortOrder = typeof payload.sortOrder === 'number' ? payload.sortOrder : Number(payload.sortOrder)
|
||||
if (!Number.isSafeInteger(sortOrder)) throw new TypeError('sortOrder必须是安全整数')
|
||||
normalizedPayload.sortOrder = sortOrder
|
||||
}
|
||||
return normalizedPayload
|
||||
}
|
||||
Reference in New Issue
Block a user