Files
jiapuapp/services/api/person-document-contract.js

154 lines
7.4 KiB
JavaScript

import { createRequestError } from './request-client.js'
import {
normalizeOptionalNumericId,
normalizeOptionalResponseText
} from './response-normalizers.js'
import {
assertPlainPayload,
normalizeOptionalNormalDisableStatus,
normalizeOptionalText,
normalizeResourcePathId
} from './request-normalizers.js'
import { normalizeBusinessOptionProjection } from './business-dictionary-contract.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 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 = normalizeBusinessOptionProjection(
value.documentType,
value.documentTypeLabel,
value.documentTypeOptionState,
'重要证件类型',
'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: documentType.value,
documentTypeLabel: documentType.label,
documentTypeOptionState: documentType.state,
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 (!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
}