feat: add ERP push failure recovery
This commit is contained in:
17
ruoyi-ui/src/api/erp/pushException.js
Normal file
17
ruoyi-ui/src/api/erp/pushException.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
export function listPushExceptions(query) {
|
||||
return request({
|
||||
url: '/erp/push-exception/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
export function retryPushException(data) {
|
||||
return request({
|
||||
url: '/erp/push-exception/retry',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
224
ruoyi-ui/src/views/erp/pushException/index.vue
Normal file
224
ruoyi-ui/src/views/erp/pushException/index.vue
Normal file
@@ -0,0 +1,224 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form
|
||||
ref="queryForm"
|
||||
:model="queryParams"
|
||||
size="small"
|
||||
:inline="true"
|
||||
v-show="showSearch"
|
||||
label-width="76px"
|
||||
>
|
||||
<el-form-item label="单据类型" prop="documentType">
|
||||
<el-select v-model="queryParams.documentType" placeholder="全部类型" clearable>
|
||||
<el-option label="入库" value="INBOUND" />
|
||||
<el-option label="出库" value="OUTBOUND" />
|
||||
<el-option label="移库" value="MOVE" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="单据编号" prop="documentNo">
|
||||
<el-input
|
||||
v-model="queryParams.documentNo"
|
||||
placeholder="请输入单据编号"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="错误信息" prop="errorMessage">
|
||||
<el-input
|
||||
v-model="queryParams.errorMessage"
|
||||
placeholder="请输入错误信息"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
icon="el-icon-refresh-right"
|
||||
size="mini"
|
||||
:disabled="selectedRows.length === 0 || retrying"
|
||||
v-hasPermi="['erp:pushException:retry']"
|
||||
@click="handleBatchRetry"
|
||||
>批量重推</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList" />
|
||||
</el-row>
|
||||
|
||||
<el-alert
|
||||
class="retry-alert"
|
||||
title="仅显示最近一个月的失败单据。通信中断时 ERP 可能已经收到单据,重推前请人工确认。"
|
||||
type="warning"
|
||||
:closable="false"
|
||||
show-icon
|
||||
/>
|
||||
|
||||
<el-table v-loading="loading" :data="exceptionList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="48" align="center" />
|
||||
<el-table-column label="类型" prop="documentType" width="84" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tag size="small" :type="typeTag(scope.row.documentType)">
|
||||
{{ typeLabel(scope.row.documentType) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="单据编号" prop="documentNo" min-width="170" show-overflow-tooltip />
|
||||
<el-table-column label="业务信息" prop="businessReference" min-width="210" show-overflow-tooltip />
|
||||
<el-table-column label="失败时间" prop="failureTime" width="168" />
|
||||
<el-table-column label="错误原因" prop="errorMessage" min-width="220" show-overflow-tooltip />
|
||||
<el-table-column label="操作" width="150" fixed="right" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
type="text"
|
||||
size="mini"
|
||||
icon="el-icon-refresh-right"
|
||||
:disabled="retrying"
|
||||
:loading="retryingId === rowKey(scope.row)"
|
||||
v-hasPermi="['erp:pushException:retry']"
|
||||
@click="handleRetry(scope.row)"
|
||||
>重推</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
:page.sync="queryParams.pageNum"
|
||||
:limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listPushExceptions, retryPushException } from '@/api/erp/pushException'
|
||||
|
||||
export default {
|
||||
name: 'ErpPushException',
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
retrying: false,
|
||||
retryingId: null,
|
||||
showSearch: true,
|
||||
total: 0,
|
||||
exceptionList: [],
|
||||
selectedRows: [],
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
documentType: null,
|
||||
documentNo: null,
|
||||
errorMessage: null
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getList()
|
||||
},
|
||||
methods: {
|
||||
getList() {
|
||||
this.loading = true
|
||||
return listPushExceptions(this.queryParams).then(response => {
|
||||
this.exceptionList = response.rows || []
|
||||
this.total = response.total || 0
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1
|
||||
this.getList()
|
||||
},
|
||||
resetQuery() {
|
||||
this.resetForm('queryForm')
|
||||
this.handleQuery()
|
||||
},
|
||||
handleSelectionChange(selection) {
|
||||
this.selectedRows = selection
|
||||
},
|
||||
handleRetry(row) {
|
||||
this.confirmRetry(1).then(() => {
|
||||
this.retrying = true
|
||||
this.retryingId = this.rowKey(row)
|
||||
return retryPushException(this.retryPayload(row))
|
||||
}).then(() => {
|
||||
this.$modal.msgSuccess('已加入重试队列')
|
||||
return this.getList()
|
||||
}).catch(() => {
|
||||
if (this.retryingId) {
|
||||
return this.getList().catch(() => {})
|
||||
}
|
||||
}).finally(() => {
|
||||
this.retrying = false
|
||||
this.retryingId = null
|
||||
})
|
||||
},
|
||||
async handleBatchRetry() {
|
||||
const rows = this.selectedRows.slice()
|
||||
try {
|
||||
await this.confirmRetry(rows.length)
|
||||
} catch (error) {
|
||||
return
|
||||
}
|
||||
this.retrying = true
|
||||
let succeeded = 0
|
||||
const failed = []
|
||||
try {
|
||||
for (const row of rows) {
|
||||
try {
|
||||
await retryPushException(this.retryPayload(row))
|
||||
succeeded += 1
|
||||
} catch (error) {
|
||||
failed.push(row.documentNo)
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
this.retrying = false
|
||||
await this.getList().catch(() => {})
|
||||
}
|
||||
if (failed.length === 0) {
|
||||
this.$modal.msgSuccess(`已将 ${succeeded} 条单据加入重试队列`)
|
||||
} else {
|
||||
this.$modal.msgWarning(`成功 ${succeeded} 条,失败 ${failed.length} 条:${failed.join('、')}`)
|
||||
}
|
||||
},
|
||||
confirmRetry(count) {
|
||||
return this.$modal.confirm(
|
||||
`确定重新推送 ${count} 条单据吗?如果 ERP 已收到但响应丢失,重复结果由 ERP 的单号校验规则决定。`
|
||||
)
|
||||
},
|
||||
retryPayload(row) {
|
||||
return {
|
||||
documentType: row.documentType,
|
||||
recordId: row.recordId
|
||||
}
|
||||
},
|
||||
rowKey(row) {
|
||||
return `${row.documentType}:${row.recordId}`
|
||||
},
|
||||
typeLabel(type) {
|
||||
return { INBOUND: '入库', OUTBOUND: '出库', MOVE: '移库' }[type] || type
|
||||
},
|
||||
typeTag(type) {
|
||||
return { INBOUND: 'success', OUTBOUND: 'danger', MOVE: 'warning' }[type] || 'info'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.retry-alert {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
</style>
|
||||
Reference in New Issue
Block a user