/** * 门禁凭据与对账交互端点前端结果语义解析器 (Package A.3 - MAJ-05) * * 核心原则: * 1. 杜绝依赖 AjaxResult 顶层 res.msg (防止“操作成功”假成功); * 2. 严格按后端 DTO 真实契约解析 (ReconciliationResult, IssueResultVO); * 3. 严禁对排队、未执行、部分失败或人工对账展示“成功”。 */ /** * 解析门禁注销操作结果反馈 (POST /train/access/auth/{authId}/revoke) * @param {Object} data - res.data (ReconciliationResult) * @returns {{ type: 'success'|'info'|'warning'|'error', message: string }} */ export function resolveRevokeFeedback(data) { const d = data || {} const decision = d.decision || '' const success = d.success === true const externalInvoked = d.externalInvoked === true const reason = d.reason || '' // 1. decision == NO_ACTION && success == true -> 无需操作或已处于安全终态 (info/success) if (decision === 'NO_ACTION' && success) { return { type: 'info', message: reason || '无需操作或已处于安全终态' } } // 2. decision == REVOKE_REQUIRED && success == true && externalInvoked == true if (decision === 'REVOKE_REQUIRED' && success && externalInvoked) { return { type: 'info', message: '注销请求已提交,等待设备确认。' } } // 3. decision == REVOKE_REQUIRED && success == true && externalInvoked == false if (decision === 'REVOKE_REQUIRED' && success && !externalInvoked) { return { type: 'warning', message: reason || '当前未执行外部注销,等待DMS恢复或后续处理。' } } // 4. decision == FAILED || success == false -> error 直接展示 reason if (decision === 'FAILED' || !success) { return { type: 'error', message: reason || '门禁注销失败' } } // 安全兜底 return { type: 'info', message: reason || '注销请求已提交' } } /** * 解析门禁凭据显式下发结果反馈 (POST /train/access/auth/{authId}/issue) * @param {Object} data - res.data (IssueResultVO) * @returns {{ type: 'success'|'warning'|'error', message: string, openCredentialDialog: boolean }} */ export function resolveIssueFeedback(data) { const d = data || {} const status = d.status || '' const message = d.message || '' const hasCredential = Boolean(d.oneTimePlaintextCredential && String(d.oneTimePlaintextCredential).trim().length > 0) // 1. ACCEPTED: 仅在有 credential 时才允许弹窗;提示“凭据已提交下发,设备确认后生效。” if (status === 'ACCEPTED') { return { type: 'success', message: '凭据已提交下发,设备确认后生效。', openCredentialDialog: hasCredential } } // 2. WAITING_FOR_PREREQUISITE (包含 MISSING_STUDENT_PHONE, INVALID_VALIDITY_WINDOW, DMS_DISABLED 等真实 reason) if (status === 'WAITING_FOR_PREREQUISITE') { return { type: 'warning', message: message || '前置条件未满足,暂无法下发门禁凭据', openCredentialDialog: false } } // 3. AMBIGUOUS_EXTERNAL_MUTATION: 不得允许直接重发 if (status === 'AMBIGUOUS_EXTERNAL_MUTATION' || status === 'AMBIGUOUS') { return { type: 'error', message: message || '上一次下发结果无法确认,禁止重复下发,请先核对厂商侧凭据。(AMBIGUOUS_EXTERNAL_MUTATION)', openCredentialDialog: false } } // 4. FAILED 或其他错误 return { type: 'error', message: message || '门禁凭据下发失败', openCredentialDialog: false } } /** * 解析门禁手动对账结果反馈 (POST /train/access/reconcile/{enrollmentId}) * @param {Object} data - res.data (ReconciliationResult) * @returns {{ type: 'success'|'info'|'warning'|'error', message: string, openCredentialDialog: boolean }} */ export function resolveReconcileFeedback(data) { const d = data || {} const batchResult = d.batchResult || null const outcome = (batchResult && batchResult.outcome) || d.outcome || (d.decision === 'NO_ACTION' ? 'ALL_SETTLED' : '') const reason = d.reason || '' const hasCredential = Boolean(d.oneTimePlaintextCredential && String(d.oneTimePlaintextCredential).trim().length > 0) // 1. ALL_SETTLED -> success if (outcome === 'ALL_SETTLED') { return { type: 'success', message: reason || '对账完成:所有授权均已处于安全终态或无需操作', openCredentialDialog: hasCredential } } // 2. IN_PROGRESS -> info if (outcome === 'IN_PROGRESS') { return { type: 'info', message: reason || '对账处理中:指令已派发至外部设备,等待设备确认', openCredentialDialog: hasCredential } } // 3. WAITING_FOR_DMS -> warning if (outcome === 'WAITING_FOR_DMS') { return { type: 'warning', message: reason || '门禁系统当前未启用,操作已排队等待DMS恢复后处理', openCredentialDialog: hasCredential } } // 4. PARTIAL_FAILURE -> error (明确“部分处理失败”,严禁报对账成功) if (outcome === 'PARTIAL_FAILURE') { return { type: 'error', message: reason ? ('部分处理失败:' + reason) : '对账完成:部分授权处理失败,需关注处理', openCredentialDialog: false } } // 5. ALL_FAILED -> error if (outcome === 'ALL_FAILED') { return { type: 'error', message: reason ? ('对账处理失败:' + reason) : '对账执行失败:所有授权操作均失败', openCredentialDialog: false } } // 6. REQUIRES_MANUAL_SETTLEMENT -> error (明确“需要人工核对厂商侧状态”) if (outcome === 'REQUIRES_MANUAL_SETTLEMENT') { return { type: 'error', message: reason ? ('需要人工核对厂商侧状态:' + reason) : '需要人工核对厂商侧状态:外部凭据状态不确定,阻断自动处理', openCredentialDialog: false } } // 7. 兜底逻辑:若明确 success == false if (d.success === false) { return { type: 'error', message: reason || '对账执行未成功', openCredentialDialog: false } } // 8. 默认中性提示 return { type: 'info', message: reason || '对账执行完成', openCredentialDialog: hasCredential } }