189 lines
6.2 KiB
JavaScript
189 lines
6.2 KiB
JavaScript
/**
|
||
* 门禁凭据与对账交互端点前端结果语义解析器 (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
|
||
}
|
||
}
|