import { createPreviewMyJoinApplications, createPreviewPendingJoinApplications, previewCurrentUser } from '@/data/preview/genealogy-membership.js' import { hasRemoteConfig } from '@/utils/runtime-config.js' import { normalizeGenealogyPathId } from './genealogy-contract.js' import { JOIN_APPLICATION_STATUS, normalizeGenealogyInvitation, normalizeGenealogyInvitationRedeemPayload, normalizeGenealogyInvitationToken, normalizeIssuedGenealogyInvitation, normalizeJoinApplicationPayload, normalizeJoinAuditPayload, normalizeMyGenealogyInvitations } from './genealogy-membership-contract.js' import { normalizeResourcePathId } from './request-normalizers.js' import { createRequestError, requestStrict } from './request-client.js' const requireRemoteMembership = (label, operation) => { if (hasRemoteConfig()) return throw createRequestError( `${label}${operation}需要真实服务,当前本地预览不会伪造结果`, operation === '读取' ? 'REMOTE_READ_REQUIRED' : 'REMOTE_WRITE_REQUIRED' ) } const previewMyJoinApplications = createPreviewMyJoinApplications() const previewPendingJoinApplications = createPreviewPendingJoinApplications() let nextPreviewApplicationId = Date.now() const snapshotPreviewJoinApplications = (applications) => applications.map((application) => ({ ...application })) const createPreviewApplicationId = () => String(nextPreviewApplicationId++) const readMembershipList = async (url, label, requestOptions) => { requireRemoteMembership(label, '读取') const rows = await requestStrict({ url, method: 'GET' }, { requestController: requestOptions.requestController ?? null }) if (!Array.isArray(rows)) { throw createRequestError(`${label}响应不是列表`, 'LIST_RESPONSE_INVALID') } return rows } const writeMembership = (url, method, data, label, requestOptions) => { requireRemoteMembership(label, '写入') return requestStrict({ url, method, ...(data === undefined ? {} : { data }) }, { requestController: requestOptions.requestController ?? null }) } export const genealogyMembershipApi = { async previewGenealogyInvitation(token, requestOptions = {}) { const normalizedToken = normalizeGenealogyInvitationToken(token) requireRemoteMembership('家谱邀请码', '读取') const invitation = await requestStrict({ url: `/genealogy/app/genealogies/invitations/preview?token=${encodeURIComponent(normalizedToken)}`, method: 'GET' }, { requestController: requestOptions.requestController ?? null }) return normalizeGenealogyInvitation(invitation, 'ACTIVE') }, async issueGenealogyInvitation(genealogyId, requestOptions = {}) { const normalizedGenealogyId = normalizeGenealogyPathId(genealogyId) const invitationResponse = await writeMembership( `/genealogy/app/genealogies/${normalizedGenealogyId}/invitations`, 'POST', undefined, '家谱邀请码签发', requestOptions ) const invitation = normalizeIssuedGenealogyInvitation(invitationResponse) if (invitation.genealogyId !== normalizedGenealogyId) { throw createRequestError( '家谱邀请码归属与当前家谱不匹配', 'GENEALOGY_INVITATION_RESPONSE_INVALID' ) } return invitation }, async getMyGenealogyInvitations(requestOptions = {}) { requireRemoteMembership('我的家谱邀请', '读取') const invitations = await requestStrict({ url: '/genealogy/app/genealogies/invitations/mine', method: 'GET' }, { requestController: requestOptions.requestController ?? null }) return normalizeMyGenealogyInvitations(invitations) }, async revokeGenealogyInvitation(inviteId, requestOptions = {}) { const normalizedInviteId = normalizeResourcePathId(inviteId, '家谱邀请标识') requireRemoteMembership('家谱邀请码撤销', '写入') await requestStrict({ url: `/genealogy/app/genealogies/invitations/${normalizedInviteId}`, method: 'DELETE' }, { requireData: false, requestController: requestOptions.requestController ?? null }) return null }, async redeemGenealogyInvitation(payload, requestOptions = {}) { const invitation = await writeMembership( '/genealogy/app/genealogies/invitations/redeem', 'POST', normalizeGenealogyInvitationRedeemPayload(payload), '家谱邀请码兑换', requestOptions ) return normalizeGenealogyInvitation(invitation, 'REDEEMED') }, async applyToJoin(genealogyId, payload, requestOptions = {}) { const normalizedGenealogyId = normalizeGenealogyPathId(genealogyId) const applicationDraft = normalizeJoinApplicationPayload(payload) if (hasRemoteConfig()) { return requestStrict({ url: `/genealogy/app/genealogies/${normalizedGenealogyId}/join-applies`, method: 'POST', data: applicationDraft }, { requestController: requestOptions.requestController ?? null }) } previewMyJoinApplications.unshift({ id: createPreviewApplicationId(), genealogyId: normalizedGenealogyId, name: applicationDraft.applicantName || previewCurrentUser.name, phone: applicationDraft.phone || previewCurrentUser.phone, relation: applicationDraft.relationDesc || '关系待补充', reason: applicationDraft.applyReason || '', appliedAt: '刚刚', status: JOIN_APPLICATION_STATUS.PENDING }) return null }, async getMyJoinApplications(requestOptions = {}) { if (!hasRemoteConfig()) { return snapshotPreviewJoinApplications(previewMyJoinApplications) } return readMembershipList( '/genealogy/app/genealogies/join-applies/mine', '我的加入申请', requestOptions ) }, async withdrawJoinApplication(applicationId, requestOptions = {}) { const normalizedApplicationId = normalizeResourcePathId(applicationId, '加入申请标识') if (!hasRemoteConfig()) { const application = previewMyJoinApplications.find( (candidate) => candidate.id === normalizedApplicationId && candidate.status === JOIN_APPLICATION_STATUS.PENDING ) if (!application) { throw createRequestError( '待撤回申请不存在或已处理', 'PREVIEW_JOIN_APPLICATION_NOT_FOUND' ) } application.status = JOIN_APPLICATION_STATUS.CANCELLED return null } await requestStrict({ url: `/genealogy/app/genealogies/join-applies/${normalizedApplicationId}`, method: 'DELETE' }, { requireData: false, requestController: requestOptions.requestController ?? null }) return null }, async getPendingApplications(genealogyId, requestOptions = {}) { const normalizedGenealogyId = normalizeGenealogyPathId(genealogyId) if (!hasRemoteConfig()) { return snapshotPreviewJoinApplications( previewPendingJoinApplications.filter( (application) => application.genealogyId === normalizedGenealogyId && application.status === JOIN_APPLICATION_STATUS.PENDING ) ) } return readMembershipList( `/genealogy/app/genealogies/${normalizedGenealogyId}/join-applies/pending`, '待审核申请', requestOptions ) }, async auditApplication(genealogyId, applicationId, payload, requestOptions = {}) { const normalizedGenealogyId = normalizeGenealogyPathId(genealogyId) const normalizedApplicationId = normalizeResourcePathId(applicationId, '加入申请标识') const auditDecision = normalizeJoinAuditPayload(payload) if (hasRemoteConfig()) { await requestStrict({ url: `/genealogy/app/genealogies/${normalizedGenealogyId}/join-applies/${normalizedApplicationId}/audit`, method: 'PUT', data: auditDecision }, { requireData: false, requestController: requestOptions.requestController ?? null }) return null } const application = previewPendingJoinApplications.find( (candidate) => candidate.id === normalizedApplicationId && candidate.genealogyId === normalizedGenealogyId && candidate.status === JOIN_APPLICATION_STATUS.PENDING ) if (!application) { throw createRequestError( '待审核申请不存在、已处理或不属于当前家谱', 'PREVIEW_JOIN_APPLICATION_NOT_FOUND' ) } Object.assign(application, auditDecision) return null } }