feat: migrate app routes and business modules
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user