import { createRequestError } from './request-client.js' import { normalizeOptionalNumericId, normalizeOptionalResponseText } from './response-normalizers.js' import { assertPlainPayload, normalizeOptionalText, normalizeResourcePathId } from './request-normalizers.js' import { normalizeLineagePersonIdentity } from './lineage-person-contract.js' export const GENEALOGY_MEMBER_ROLE = Object.freeze({ OWNER: 'owner', ADMIN: 'admin', EDITOR: 'editor', MEMBER: 'member', VISITOR: 'visitor' }) export const GENEALOGY_MEMBER_ROLE_LABELS = Object.freeze({ [GENEALOGY_MEMBER_ROLE.OWNER]: '谱主', [GENEALOGY_MEMBER_ROLE.ADMIN]: '管理员', [GENEALOGY_MEMBER_ROLE.EDITOR]: '可编辑成员', [GENEALOGY_MEMBER_ROLE.MEMBER]: '普通成员', [GENEALOGY_MEMBER_ROLE.VISITOR]: '访客' }) const genealogyMemberRoles = new Set(Object.values(GENEALOGY_MEMBER_ROLE)) const editableGenealogyMemberRoles = new Set([ GENEALOGY_MEMBER_ROLE.ADMIN, GENEALOGY_MEMBER_ROLE.EDITOR, GENEALOGY_MEMBER_ROLE.MEMBER ]) const normalizeMemberOptionText = (value, field) => normalizeOptionalResponseText(value, field, '成员候选', 'MEMBER_OPTIONS_RESPONSE_INVALID') const normalizeMemberText = (value, field) => normalizeOptionalResponseText(value, field, '家谱成员', 'MEMBER_LIST_RESPONSE_INVALID') export const normalizeMemberUpdatePayload = (payload) => { assertPlainPayload(payload, new Set(['memberName', 'relationName', 'roleType', 'lineagePersonId']), '成员更新请求') const normalizedPayload = {} const memberName = normalizeOptionalText(payload.memberName, 'memberName') if (memberName && memberName.length > 50) throw new TypeError('memberName 超出长度限制') if (memberName) normalizedPayload.memberName = memberName if (Object.prototype.hasOwnProperty.call(payload, 'relationName')) { if (typeof payload.relationName !== 'string') throw new TypeError('relationName必须是字符串') const relationName = payload.relationName.trim() if (relationName.length > 100) throw new TypeError('relationName 超出长度限制') normalizedPayload.relationName = relationName } const roleType = normalizeOptionalText(payload.roleType, 'roleType') if (roleType) { if (!editableGenealogyMemberRoles.has(roleType)) { throw new TypeError('roleType 仅允许 admin、editor 或 member') } normalizedPayload.roleType = roleType } if (payload.lineagePersonId !== undefined && payload.lineagePersonId !== null && payload.lineagePersonId !== '') { normalizedPayload.lineagePersonId = normalizeResourcePathId(payload.lineagePersonId, '世系人物标识') } if (!Object.keys(normalizedPayload).length) throw new TypeError('请至少填写一项要更新的成员信息') return normalizedPayload } export const normalizeGenealogyMemberOptions = (value) => { if (!Array.isArray(value)) { throw createRequestError('成员候选响应不是数组', 'MEMBER_OPTIONS_RESPONSE_INVALID') } const seenUserIds = new Set() return value.map((item) => { if (!item || typeof item !== 'object' || Array.isArray(item)) { throw createRequestError('成员候选包含无效条目', 'MEMBER_OPTIONS_RESPONSE_INVALID') } const memberId = normalizeResourcePathId(item.memberId, '成员候选标识') const appUserId = normalizeLineagePersonIdentity(item.appUserId, '绑定用户标识') if (seenUserIds.has(appUserId)) { throw createRequestError('成员候选包含重复业务用户', 'MEMBER_OPTIONS_RESPONSE_INVALID') } seenUserIds.add(appUserId) const memberName = normalizeMemberOptionText(item.memberName, 'memberName') if (!memberName) { throw createRequestError('成员候选缺少显示名称', 'MEMBER_OPTIONS_RESPONSE_INVALID') } if (typeof item.eligible !== 'boolean') { throw createRequestError('成员候选 eligible 无效', 'MEMBER_OPTIONS_RESPONSE_INVALID') } return { memberId, appUserId, memberName, relationName: normalizeMemberOptionText(item.relationName, 'relationName'), roleType: normalizeMemberOptionText(item.roleType, 'roleType'), eligible: item.eligible, disabledReason: normalizeMemberOptionText(item.disabledReason, 'disabledReason') } }) } const normalizeGenealogyMemberCapabilities = (value) => { if (!value || typeof value !== 'object' || Array.isArray(value)) { throw createRequestError('成员操作权限无效', 'MEMBER_LIST_RESPONSE_INVALID') } const capabilities = {} for (const field of ['canEdit', 'canRemove', 'canLeave', 'canTransferOwner']) { if (typeof value[field] !== 'boolean') { throw createRequestError(`成员操作权限字段 ${field} 无效`, 'MEMBER_LIST_RESPONSE_INVALID') } capabilities[field] = value[field] } return capabilities } export const normalizeGenealogyMembers = (value, expectedGenealogyId) => { if (!Array.isArray(value)) { throw createRequestError('家谱成员响应不是数组', 'MEMBER_LIST_RESPONSE_INVALID') } const seenIds = new Set() return value.map((item) => { if (!item || typeof item !== 'object' || Array.isArray(item)) { throw createRequestError('家谱成员包含无效条目', 'MEMBER_LIST_RESPONSE_INVALID') } const memberId = normalizeResourcePathId(item.memberId, '成员标识') if (seenIds.has(memberId)) { throw createRequestError('家谱成员包含重复标识', 'MEMBER_LIST_RESPONSE_INVALID') } seenIds.add(memberId) const genealogyId = normalizeResourcePathId(item.genealogyId, '家谱标识') if (genealogyId !== expectedGenealogyId) { throw createRequestError('家谱成员归属与请求不匹配', 'MEMBER_LIST_RESPONSE_INVALID') } const roleType = normalizeMemberText(item.roleType, 'roleType') if (!genealogyMemberRoles.has(roleType)) { throw createRequestError('家谱成员角色无效', 'MEMBER_LIST_RESPONSE_INVALID') } const memberName = normalizeMemberText(item.memberName, 'memberName') if (!memberName) { throw createRequestError('家谱成员缺少显示名称', 'MEMBER_LIST_RESPONSE_INVALID') } return { memberId, genealogyId, genealogyNo: normalizeMemberText(item.genealogyNo, 'genealogyNo'), genealogyName: normalizeMemberText(item.genealogyName, 'genealogyName'), surname: normalizeMemberText(item.surname, 'surname'), appUserId: normalizeOptionalNumericId(item.appUserId, '成员用户标识', 'MEMBER_LIST_RESPONSE_INVALID'), appUserNickName: normalizeMemberText(item.appUserNickName, 'appUserNickName'), lineagePersonId: normalizeOptionalNumericId(item.lineagePersonId, '世系人物标识', 'MEMBER_LIST_RESPONSE_INVALID'), lineagePersonNo: normalizeMemberText(item.lineagePersonNo, 'lineagePersonNo'), lineagePersonName: normalizeMemberText(item.lineagePersonName, 'lineagePersonName'), memberName, roleType, relationName: normalizeMemberText(item.relationName, 'relationName'), joinSource: normalizeMemberText(item.joinSource, 'joinSource'), inviterUserId: normalizeOptionalNumericId(item.inviterUserId, '邀请人用户标识', 'MEMBER_LIST_RESPONSE_INVALID'), inviterNickName: normalizeMemberText(item.inviterNickName, 'inviterNickName'), joinTime: normalizeMemberText(item.joinTime, 'joinTime'), status: normalizeMemberText(item.status, 'status'), capabilities: normalizeGenealogyMemberCapabilities(item.capabilities) } }) }