import { createRequestError } from './request-client.js' import { normalizeResponseText } from './response-normalizers.js' import { assertPlainPayload, normalizeOptionalText, normalizeResourcePathId } from './request-normalizers.js' import { normalizeGenealogyPathId } from './genealogy-contract.js' export const JOIN_APPLICATION_STATUS = Object.freeze({ PENDING: '0', APPROVED: '1', REJECTED: '2', CANCELLED: '3' }) export const JOIN_APPLICATION_STATUS_LABELS = Object.freeze({ [JOIN_APPLICATION_STATUS.PENDING]: '审核中', [JOIN_APPLICATION_STATUS.APPROVED]: '已通过', [JOIN_APPLICATION_STATUS.REJECTED]: '未通过', [JOIN_APPLICATION_STATUS.CANCELLED]: '已撤回' }) const joinApplicationAuditStatuses = new Set([ JOIN_APPLICATION_STATUS.APPROVED, JOIN_APPLICATION_STATUS.REJECTED ]) export const normalizeJoinApplicationPayload = (payload) => { assertPlainPayload(payload, new Set(['applicantName', 'phone', 'relationDesc', 'applyReason']), '加入申请请求') const normalizedPayload = {} for (const field of ['applicantName', 'phone', 'relationDesc', 'applyReason']) { const normalizedText = normalizeOptionalText(payload[field], field) if (normalizedText) normalizedPayload[field] = normalizedText } return normalizedPayload } export const normalizeGenealogyInvitationToken = (value) => { if (typeof value !== 'string') throw new TypeError('邀请码必须是文本') const token = value.trim() if (!token) throw new TypeError('请输入邀请码') if (token.length > 128) throw new TypeError('邀请码长度不能超过128个字符') return token } export const normalizeGenealogyInvitationRedeemPayload = (payload) => { assertPlainPayload(payload, new Set(['token']), '家谱邀请码兑换请求') return { token: normalizeGenealogyInvitationToken(payload.token) } } export const normalizeGenealogyInvitation = (value, expectedStatus = '') => { if (!value || typeof value !== 'object' || Array.isArray(value)) { throw createRequestError('家谱邀请码响应无效', 'GENEALOGY_INVITATION_RESPONSE_INVALID') } const genealogyId = normalizeGenealogyPathId(value.genealogyId) const genealogyName = normalizeResponseText(value.genealogyName, 'genealogyName') const status = normalizeResponseText(value.status, 'status') const expiresAt = normalizeResponseText(value.expiresAt, 'expiresAt') if (!genealogyName || !status || !expiresAt) { throw createRequestError('家谱邀请码响应缺少可展示信息', 'GENEALOGY_INVITATION_RESPONSE_INVALID') } if (expectedStatus && status !== expectedStatus) { throw createRequestError('家谱邀请码状态与当前操作不匹配', 'GENEALOGY_INVITATION_RESPONSE_INVALID') } const redemptionResult = normalizeResponseText(value.redemptionResult, 'redemptionResult') if (expectedStatus === 'REDEEMED' && !['DIRECT_MEMBER', 'PENDING_APPLY'].includes(redemptionResult)) { throw createRequestError('家谱邀请码兑换结果无效', 'GENEALOGY_INVITATION_RESPONSE_INVALID') } return { genealogyId, genealogyName, status, expiresAt, redemptionResult } } export const normalizeIssuedGenealogyInvitation = (value) => { const invitation = normalizeGenealogyInvitation(value, 'ACTIVE') return { ...invitation, token: normalizeGenealogyInvitationToken(value.token) } } const genealogyInvitationStatuses = new Set(['ACTIVE', 'REDEEMED', 'REVOKED', 'EXPIRED']) export const normalizeMyGenealogyInvitations = (value) => { if (!Array.isArray(value)) { throw createRequestError('我的家谱邀请响应不是列表', 'GENEALOGY_INVITATION_RESPONSE_INVALID') } const invitations = value.map((item) => { const invitation = normalizeGenealogyInvitation(item) const id = normalizeResourcePathId(item.inviteId, '家谱邀请标识') if (!genealogyInvitationStatuses.has(invitation.status)) { throw createRequestError('家谱邀请状态无效', 'GENEALOGY_INVITATION_RESPONSE_INVALID') } return { id, ...invitation } }) if (new Set(invitations.map((item) => item.id)).size !== invitations.length) { throw createRequestError('我的家谱邀请响应包含重复标识', 'GENEALOGY_INVITATION_RESPONSE_INVALID') } return invitations } export const normalizeJoinAuditPayload = (payload) => { assertPlainPayload(payload, new Set(['status', 'auditRemark']), '加入申请审核请求') const status = normalizeOptionalText(payload.status, '审核状态') if (!joinApplicationAuditStatuses.has(status)) { throw new TypeError('加入申请审核状态只能是通过或拒绝') } const normalizedPayload = { status } const auditRemark = normalizeOptionalText(payload.auditRemark, '审核备注') if (auditRemark) normalizedPayload.auditRemark = auditRemark return normalizedPayload }