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

95 lines
4.3 KiB
JavaScript

import { createRequestError } from './request-client.js'
const permissionResponseError = (message) =>
createRequestError(message, 'GENEALOGY_PERMISSION_RESPONSE_INVALID')
const normalizePermissionCode = (value, subject = '权限编码') => {
if (typeof value !== 'string' || !/^[A-Z][A-Z0-9_]{2,63}$/.test(value.trim())) {
throw permissionResponseError(`${subject}无效`)
}
return value.trim()
}
const normalizePermissionIdentity = (value, subject) => {
if (typeof value === 'string' && /^[1-9]\d*$/.test(value)) return value
if (typeof value === 'number' && Number.isSafeInteger(value) && value > 0) return String(value)
throw permissionResponseError(`${subject}无效`)
}
const normalizePermissionText = (value, field, { required = false } = {}) => {
if (value === undefined || value === null) {
if (required) throw permissionResponseError(`权限响应缺少${field}`)
return ''
}
if (typeof value !== 'string') throw permissionResponseError(`权限响应${field}无效`)
const normalized = value.trim()
if (required && !normalized) throw permissionResponseError(`权限响应缺少${field}`)
return normalized
}
export const normalizePermissionCatalog = (value) => {
if (!Array.isArray(value)) throw permissionResponseError('权限目录不是列表')
const options = value.map((item) => {
if (!item || typeof item !== 'object' || Array.isArray(item) || typeof item.enabled !== 'boolean') {
throw permissionResponseError('权限目录包含无效条目')
}
return {
code: normalizePermissionCode(item.code),
label: normalizePermissionText(item.name, 'name', { required: true }),
groupCode: normalizePermissionText(item.groupCode, 'groupCode', { required: true }),
groupName: normalizePermissionText(item.groupName, 'groupName', { required: true }),
description: normalizePermissionText(item.description, 'description'),
sensitive: item.sensitive === true,
enabled: item.enabled,
disabledReason: normalizePermissionText(item.disabledReason, 'disabledReason')
}
})
if (new Set(options.map((item) => item.code)).size !== options.length) {
throw permissionResponseError('权限目录包含重复编码')
}
return options
}
export const normalizeMemberPermissionAssignment = (
value,
enabledCodes,
{ expectedGenealogyId = '', expectedMemberId = '' } = {}
) => {
if (!value || typeof value !== 'object' || Array.isArray(value) || !Array.isArray(value.permissionCodes)) {
throw permissionResponseError('成员权限响应无效')
}
const permissionCodes = value.permissionCodes.map((code) => normalizePermissionCode(code))
if (new Set(permissionCodes).size !== permissionCodes.length) {
throw permissionResponseError('成员权限响应包含重复编码')
}
const allowedCodes = new Set(enabledCodes)
if (permissionCodes.some((code) => !allowedCodes.has(code))) {
throw permissionResponseError('成员权限响应包含目录外或已停用权限')
}
const genealogyId = normalizePermissionIdentity(value.genealogyId, '权限响应家谱标识')
const memberId = normalizePermissionIdentity(value.memberId, '权限响应成员标识')
if (
(expectedGenealogyId && genealogyId !== expectedGenealogyId) ||
(expectedMemberId && memberId !== expectedMemberId)
) {
throw permissionResponseError('成员权限响应归属与请求不匹配')
}
return { genealogyId, memberId, permissionCodes }
}
export const normalizeMemberPermissionPayload = (permissionCodes, reason, enabledCodes) => {
if (!Array.isArray(permissionCodes)) throw new TypeError('权限项必须是数组')
const allowedCodes = new Set(enabledCodes)
const normalizedCodes = permissionCodes.map((code) => normalizePermissionCode(code, '提交权限编码'))
if (new Set(normalizedCodes).size !== normalizedCodes.length || normalizedCodes.some((code) => !allowedCodes.has(code))) {
throw new TypeError('提交权限包含重复、目录外或已停用权限')
}
if (typeof reason !== 'string') throw new TypeError('授权原因必须是字符串')
const normalizedReason = reason.trim()
if (normalizedReason.length > 200) throw new TypeError('授权原因不能超过200个字符')
return {
permissionCodes: normalizedCodes,
...(normalizedReason ? { reason: normalizedReason } : {})
}
}