修复前端:按真实门禁结果展示下发注销与对账状态
This commit is contained in:
1 parent
7d10968b50
commit
9922ae3aa4
2 files changed
+216
-38
No files matched your search
@@ -0,0 +1,188 @@
|
||||
/**
|
||||
* 门禁凭据与对账交互端点前端结果语义解析器 (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
|
||||
}
|
||||
}
|
||||
@@ -301,6 +301,7 @@
|
||||
|
||||
<script>
|
||||
import { listAuth, getOperationHistory, manualReconcile, issueAuth, revokeAuth, getCapabilities } from '@/api/train/access'
|
||||
import { resolveRevokeFeedback, resolveIssueFeedback, resolveReconcileFeedback } from './authFeedback'
|
||||
|
||||
export default {
|
||||
name: 'Auth',
|
||||
@@ -386,9 +387,18 @@ export default {
|
||||
).then(() => {
|
||||
return manualReconcile(row.enrollmentId)
|
||||
}).then(res => {
|
||||
this.$modal.msgSuccess(res.msg || '对账执行成功')
|
||||
const feedback = resolveReconcileFeedback(res.data)
|
||||
if (feedback.type === 'success') {
|
||||
this.$modal.msgSuccess(feedback.message)
|
||||
} else if (feedback.type === 'warning') {
|
||||
this.$modal.msgWarning(feedback.message)
|
||||
} else if (feedback.type === 'error') {
|
||||
this.$modal.msgError(feedback.message)
|
||||
} else {
|
||||
this.$modal.msgInfo(feedback.message)
|
||||
}
|
||||
this.getList()
|
||||
if (res.data && res.data.oneTimePlaintextCredential) {
|
||||
if (feedback.openCredentialDialog && res.data && res.data.oneTimePlaintextCredential) {
|
||||
this.oneTimeCredential = res.data.oneTimePlaintextCredential
|
||||
this.maskedMode = false
|
||||
this.oneTimeDialogOpen = true
|
||||
@@ -403,29 +413,17 @@ export default {
|
||||
this.$modal.confirm(`确认向学员【${row.studentName || row.studentId}】下发门禁凭据?`).then(() => {
|
||||
return issueAuth(row.authId)
|
||||
}).then(res => {
|
||||
const data = res.data || {}
|
||||
const decision = data.decision || data.status || ''
|
||||
const msg = res.msg || data.message || ''
|
||||
|
||||
if (decision === 'DISPATCHED' || data.status === 'ACTIVE' || data.status === 'GRANTING') {
|
||||
this.$modal.msgSuccess(msg || '凭据已提交下发,设备确认后生效')
|
||||
} else if (decision === 'WAITING_FOR_DMS' || msg.includes('DMS当前未启用')) {
|
||||
this.$modal.msgWarning(msg || 'DMS当前未启用,未执行门禁下发')
|
||||
} else if (decision === 'MISSING_STUDENT_PHONE') {
|
||||
this.$modal.msgWarning(msg || '缺少学员手机号,未下发门禁')
|
||||
} else if (decision === 'INVALID_VALIDITY_WINDOW') {
|
||||
this.$modal.msgWarning(msg || '授权起止时间无效,未下发门禁')
|
||||
} else if (decision === 'AMBIGUOUS_EXTERNAL_MUTATION' || decision === 'AMBIGUOUS') {
|
||||
this.$modal.msgError(msg || '上一次下发结果无法确认,禁止重复下发,请先核对厂商侧凭据')
|
||||
} else if (decision === 'FAILED' || !res.data) {
|
||||
this.$modal.msgError(msg || '凭据下发失败')
|
||||
const feedback = resolveIssueFeedback(res.data)
|
||||
if (feedback.type === 'success') {
|
||||
this.$modal.msgSuccess(feedback.message)
|
||||
} else if (feedback.type === 'warning') {
|
||||
this.$modal.msgWarning(feedback.message)
|
||||
} else {
|
||||
this.$modal.msgInfo(msg || '凭据处理完成')
|
||||
this.$modal.msgError(feedback.message)
|
||||
}
|
||||
|
||||
this.getList()
|
||||
if (data.oneTimePlaintextCredential) {
|
||||
this.oneTimeCredential = data.oneTimePlaintextCredential
|
||||
if (feedback.openCredentialDialog && res.data && res.data.oneTimePlaintextCredential) {
|
||||
this.oneTimeCredential = res.data.oneTimePlaintextCredential
|
||||
this.maskedMode = false
|
||||
this.oneTimeDialogOpen = true
|
||||
}
|
||||
@@ -442,23 +440,15 @@ export default {
|
||||
this.$modal.confirm(`确定要注销学员【${studentDesc}】在宿舍【${dormDesc}】的门禁授权(授权ID: ${row.authId})吗?注销后该学员将无法通行该门禁。`).then(() => {
|
||||
return revokeAuth(row.authId)
|
||||
}).then(res => {
|
||||
const data = res.data || {}
|
||||
const status = data.authStatus || data.status || ''
|
||||
const decision = data.decision || ''
|
||||
const msg = res.msg || data.message || ''
|
||||
|
||||
if (status === 'REVOKED') {
|
||||
this.$modal.msgSuccess(msg || '门禁注销成功')
|
||||
} else if (status === 'PENDING_REVOKE' || decision === 'WAITING_FOR_DMS') {
|
||||
this.$modal.msgWarning(msg || '已建立注销队列,等待DMS处理')
|
||||
} else if (status === 'REVOKING' || decision === 'DISPATCHED') {
|
||||
this.$modal.msgInfo(msg || '注销指令已提交,设备确认后生效')
|
||||
} else if (status === 'AMBIGUOUS') {
|
||||
this.$modal.msgError(msg || '外部注销结果不确定,请人工核对')
|
||||
} else if (status === 'ERROR') {
|
||||
this.$modal.msgError(msg || '注销操作失败')
|
||||
const feedback = resolveRevokeFeedback(res.data)
|
||||
if (feedback.type === 'success') {
|
||||
this.$modal.msgSuccess(feedback.message)
|
||||
} else if (feedback.type === 'warning') {
|
||||
this.$modal.msgWarning(feedback.message)
|
||||
} else if (feedback.type === 'error') {
|
||||
this.$modal.msgError(feedback.message)
|
||||
} else {
|
||||
this.$modal.msgInfo(msg || '注销请求已提交')
|
||||
this.$modal.msgInfo(feedback.message)
|
||||
}
|
||||
this.getList()
|
||||
}).catch(() => {})
|
||||
|
||||
Reference in new issue
Block a user