回滚代码

This commit is contained in:
wuweihua
2026-10-16 20:28:49 +08:00
parent 9ff89e36ee
commit 52f714c760
29 changed files with 505 additions and 1413 deletions
-31
View File
@@ -1,31 +0,0 @@
import request from '@/utils/request'
export function listPrcOrder(query) {
return request({
url: '/monitor/prcOrder/list',
method: 'get',
params: query
})
}
export function getPrcOrder(orderId) {
return request({
url: '/monitor/prcOrder/' + orderId,
method: 'get'
})
}
export function retryPrcOrder(orderId) {
return request({
url: '/monitor/prcOrder/retry/' + orderId,
method: 'post'
})
}
export function retryPrcOrderBatch(orderIds) {
return request({
url: '/monitor/prcOrder/retry',
method: 'post',
data: orderIds
})
}
@@ -5,47 +5,65 @@
<template v-else>
<div class="detail-header">
<el-tag :type="statusTag(detail.successStatus)" effect="light">{{ statusLabel(detail.successStatus) }}</el-tag>
<span class="detail-header-time">计费时间:{{ detail.bizDate || '-' }}</span>
<span class="detail-header-type">{{ billTypeLabel(detail.billType) }}</span>
<span class="detail-header-time">计费时间:{{ detail.billTime || '-' }}</span>
</div>
<el-descriptions :column="1" border size="small" class="detail-desc">
<el-descriptions-item label="订单ID">{{ detail.orderId }}</el-descriptions-item>
<el-descriptions-item label="任务ID">{{ detail.taskId }}</el-descriptions-item>
<el-descriptions-item label="客户代码">{{ detail.customerCode || '-' }}</el-descriptions-item>
<el-descriptions-item label="订单号">{{ detail.orderNo || '-' }}</el-descriptions-item>
<el-descriptions-item label="运单号">{{ detail.trackingNo || '-' }}</el-descriptions-item>
<el-descriptions-item label="转单号">{{ detail.transferNo || '-' }}</el-descriptions-item>
<el-descriptions-item label="产品代码">{{ detail.productCode || '-' }}</el-descriptions-item>
<el-descriptions-item label="计费订单ID">{{ detail.billId || '-' }}</el-descriptions-item>
<el-descriptions-item label="失败码">{{ detail.failCode || '-' }}</el-descriptions-item>
<el-descriptions-item label="创建时间">{{ detail.createTime || '-' }}</el-descriptions-item>
<el-descriptions-item label="更新时间">{{ detail.updateTime || '-' }}</el-descriptions-item>
</el-descriptions>
<div v-if="detail.errorMessage" class="err-block">
<div class="err-block-head">
<span class="err-block-title">失败原因</span>
<el-button link type="primary" size="small" icon="CopyDocument" @click="copyError">复制</el-button>
</div>
<div class="err-block-body">{{ detail.errorMessage }}</div>
</div>
</template>
</div>
</el-drawer>
</template>
<script setup>
import { getPrcOrder } from '@/api/monitor/prcOrder'
import { statusLabel, statusTag } from './billingTaskOptions'
import { getBillingTask } from '@/api/monitor/billingTask'
import { billTypeLabel, statusLabel, statusTag } from './billingTaskOptions'
const { proxy } = getCurrentInstance()
const open = ref(false)
const loading = ref(false)
const detail = ref(null)
const drawerTitle = ref('订单计费详情')
const drawerTitle = ref('计费任务详情')
function openDrawer(row) {
if (!row || !row.orderId) return
drawerTitle.value = '订单 ' + (row.transferNo || row.orderId)
if (!row || !row.taskId) return
drawerTitle.value = '计费任务 #' + row.taskId
open.value = true
loading.value = true
detail.value = null
getPrcOrder(row.orderId).then(res => {
getBillingTask(row.taskId).then(res => {
detail.value = res.data || row
}).finally(() => {
loading.value = false
})
}
function copyError() {
const text = detail.value?.errorMessage || ''
if (!text) return
navigator.clipboard.writeText(text).then(() => {
proxy.$modal.msgSuccess('已复制')
}).catch(() => {
proxy.$modal.msgError('复制失败')
})
}
defineExpose({ open: openDrawer })
</script>
+162 -14
View File
@@ -1,15 +1,38 @@
<template>
<div class="app-container billing-task-page">
<div v-if="retryFloatVisible" class="retry-status-float">
<el-tooltip :content="retryFloatTooltip" placement="bottom">
<el-button class="retry-status-button" type="primary" plain size="small" @click="filterRetryRef.open()">
<span class="retry-progress-ring" :style="{ '--retry-progress': retryProgressDegree + 'deg' }" aria-hidden="true">
<span class="retry-progress-value">{{ retryProgressText }}</span>
</span>
<span class="retry-status-text">
<strong>{{ retryFloatTitle }}</strong>
<small>{{ retryFloatSummary }}</small>
</span>
</el-button>
</el-tooltip>
</div>
<basic-table
ref="tableRef"
:columns="columns"
:search-columns="searchColumns"
:query="queryList"
:default-params="defaultParams"
row-key="orderId"
row-key="taskId"
selection
>
<template #toolbar="{ ids }">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="Refresh"
v-hasPermi="['monitor:billingTask:retry']"
@click="filterRetryRef.open()"
>批量重新同步</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
@@ -22,16 +45,22 @@
>同步选中</el-button>
</el-col>
</template>
<template #col-billType="{ row }">
<el-tag size="small">{{ billTypeLabel(row.billType) }}</el-tag>
</template>
<template #col-successStatus="{ row }">
<el-tag size="small" :type="statusTag(row.successStatus)">{{ statusLabel(row.successStatus) }}</el-tag>
</template>
<template #col-errorMessage="{ row }">
<span class="err-text" :title="row.errorMessage || ''">{{ row.errorMessage || '-' }}</span>
</template>
<template #col-action="{ row }">
<el-button link type="primary" icon="View" @click="detailRef.open(row)" v-hasPermi="['monitor:billingTask:list']">查看</el-button>
<el-button
link
type="warning"
icon="Refresh"
:loading="retryingId === row.orderId"
:loading="retryingId === row.taskId"
v-hasPermi="['monitor:billingTask:retry']"
@click="handleRetry(row)"
>重新同步</el-button>
@@ -39,15 +68,23 @@
</basic-table>
<billing-task-detail-drawer ref="detailRef" />
<billing-task-filter-retry-dialog
ref="filterRetryRef"
@progress="onFilterRetryProgress"
@success="onFilterRetrySuccess"
/>
</div>
</template>
<script setup name="BillingTaskMonitor">
import { listPrcOrder, retryPrcOrder, retryPrcOrderBatch } from '@/api/monitor/prcOrder'
import { listBillingTask, retryBillingTask, retryBillingTaskBatch } from '@/api/monitor/billingTask'
import BillingTaskDetailDrawer from './components/BillingTaskDetailDrawer.vue'
import BillingTaskFilterRetryDialog from './components/BillingTaskFilterRetryDialog.vue'
import {
statusOptions,
billTypeOptions,
failCodeOptions,
billTypeLabel,
statusLabel,
statusTag
} from './components/billingTaskOptions'
@@ -55,13 +92,16 @@ import {
const { proxy } = getCurrentInstance()
const tableRef = ref()
const detailRef = ref()
const filterRetryRef = ref()
const retryingId = ref(null)
const batchLoading = ref(false)
const retryFloat = ref(null)
const defaultParams = { successStatus: 'FAILED' }
const searchColumns = [
{ label: '状态', prop: 'successStatus', component: 'select', options: statusOptions, placeholder: '请选择状态', width: '140px', props: { clearable: true } },
{ label: '计费类型', prop: 'billType', component: 'select', options: billTypeOptions, placeholder: '请选择类型', width: '140px', props: { clearable: true } },
{ label: '失败码', prop: 'failCode', component: 'select', options: failCodeOptions, placeholder: '请选择失败码', width: '220px', props: { clearable: true, filterable: true } },
{ label: '客户代码', prop: 'customerCode', component: 'input', placeholder: '客户代码', width: '160px' },
{ label: '订单号', prop: 'orderNo', component: 'input', placeholder: '订单号', width: '180px' },
@@ -71,34 +111,70 @@ const searchColumns = [
]
const columns = [
{ label: '任务ID', prop: 'taskId', align: 'center', width: 100 },
{ label: '客户代码', prop: 'customerCode', minWidth: 120, showOverflowTooltip: true },
{ label: '订单号', prop: 'orderNo', minWidth: 160, showOverflowTooltip: true },
{ label: '运单号', prop: 'trackingNo', minWidth: 150, showOverflowTooltip: true },
{ label: '转单号', prop: 'transferNo', minWidth: 160, showOverflowTooltip: true },
{ label: '产品', prop: 'productCode', minWidth: 120, showOverflowTooltip: true },
{ label: '计费类型', prop: 'billType', align: 'center', width: 100, slot: 'billType' },
{ label: '状态', prop: 'successStatus', align: 'center', width: 100, slot: 'successStatus' },
{ label: '计费时间', prop: 'bizDate', align: 'center', width: 170 },
{ label: '计费时间', prop: 'billTime', align: 'center', width: 170 },
{ label: '失败原因', prop: 'errorMessage', minWidth: 220, slot: 'errorMessage', showOverflowTooltip: true },
{ label: '失败码', prop: 'failCode', align: 'center', width: 180, showOverflowTooltip: true },
{ label: '创建时间', prop: 'createTime', align: 'center', width: 170 },
{ label: '操作', prop: 'action', align: 'center', width: 180, fixed: 'right', slot: 'action', showInToolbar: false }
]
const retryFloatVisible = computed(() => {
const p = retryFloat.value
return !!(p && (p.loading || p.finished))
})
const retryProgressDegree = computed(() => Math.round(((retryFloat.value?.percent || 0) / 100) * 360))
const retryProgressText = computed(() => (retryFloat.value?.percent || 0) + '%')
const retryFloatTitle = computed(() => {
const st = (retryFloat.value?.status || '').toUpperCase()
if (retryFloat.value?.loading) return '异步重新计费中'
if (st === 'SUCCESS') return '重新计费完成'
if (st === 'FAIL') return '重新计费失败'
return '异步重新计费'
})
const retryFloatSummary = computed(() => {
const p = retryFloat.value || {}
return '成功 ' + (p.ok || 0) + ' / 失败 ' + (p.fail || 0) + ' / 总数 ' + (p.total || 0)
})
const retryFloatTooltip = computed(() => retryFloat.value?.message || '点击查看详情')
function queryList(params) {
const p = proxy.addDateRange({ ...params }, params.dateRange)
delete p.dateRange
return listPrcOrder(p)
return listBillingTask(p)
}
function onFilterRetryProgress(payload) {
retryFloat.value = payload
if (payload && payload.finished && !payload.loading) {
window.setTimeout(() => {
if (retryFloat.value && retryFloat.value.finished && !retryFloat.value.loading) {
retryFloat.value = null
}
}, 8000)
}
}
function onFilterRetrySuccess() {
tableRef.value?.refresh()
}
function handleRetry(row) {
proxy.$modal.confirm('确认重新同步转单号 ' + (row.transferNo || row.orderId) + ' ?').then(() => {
retryingId.value = row.orderId
return retryPrcOrder(row.orderId)
proxy.$modal.confirm('确认重新同步任务 #' + row.taskId + ' ?').then(() => {
retryingId.value = row.taskId
return retryBillingTask(row.taskId)
}).then(res => {
const order = res.data || {}
if (order.successStatus === 'SUCCESS') {
const task = res.data || {}
if (task.successStatus === 'SUCCESS') {
proxy.$modal.msgSuccess('同步成功')
} else {
proxy.$modal.msgError(order.failCode || '同步失败')
proxy.$modal.msgError(task.errorMessage || '同步失败')
}
tableRef.value.refresh()
}).catch(() => {}).finally(() => {
@@ -108,9 +184,9 @@ function handleRetry(row) {
function handleBatchRetry(ids) {
if (!ids || !ids.length) return
proxy.$modal.confirm('确认同步选中的 ' + ids.length + ' 条订单?').then(() => {
proxy.$modal.confirm('确认同步选中的 ' + ids.length + ' 条任务?').then(() => {
batchLoading.value = true
return retryPrcOrderBatch(ids)
return retryBillingTaskBatch(ids)
}).then(res => {
const data = res.data || {}
proxy.$modal.msgSuccess('完成:成功 ' + (data.ok || 0) + ' / 失败 ' + (data.fail || 0))
@@ -122,4 +198,76 @@ function handleBatchRetry(ids) {
</script>
<style scoped>
.err-text {
color: #f56c6c;
word-break: break-all;
}
.billing-task-page .retry-status-float {
position: fixed;
top: 92px;
right: 24px;
z-index: 3001;
}
.billing-task-page .retry-status-button {
display: inline-flex;
align-items: center;
gap: 10px;
min-width: 200px;
min-height: 68px;
padding: 6px 16px 6px 7px;
border-radius: 36px;
}
.billing-task-page .retry-progress-ring {
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
flex: 0 0 54px;
width: 54px;
height: 54px;
border-radius: 50%;
background: conic-gradient(
var(--el-color-primary) 0deg var(--retry-progress, 45deg),
var(--el-color-primary-light-8) var(--retry-progress, 45deg) 360deg
);
}
.billing-task-page .retry-progress-ring::after {
position: absolute;
inset: 5px;
content: '';
border-radius: 50%;
background: var(--el-bg-color);
}
.billing-task-page .retry-progress-value {
position: relative;
z-index: 1;
color: var(--el-color-primary);
font-size: 12px;
font-weight: 600;
line-height: 1;
}
.billing-task-page .retry-status-text {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 3px;
line-height: 1.2;
white-space: nowrap;
}
.billing-task-page .retry-status-text strong {
font-size: 14px;
font-weight: 600;
}
.billing-task-page .retry-status-text small {
color: var(--el-text-color-secondary);
font-size: 11px;
}
</style>