Files
jiapuapp/services/api/genealogy-member-contract.js

165 lines
7.3 KiB
JavaScript

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)
}
})
}