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 }