diff --git a/bms-back/bms-system/src/main/java/com/bms/price/task/PrcOrderBillLocalRebillAsyncRunner.java b/bms-back/bms-system/src/main/java/com/bms/price/task/PrcOrderBillLocalRebillAsyncRunner.java index c2905a14..6f46763a 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/task/PrcOrderBillLocalRebillAsyncRunner.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/task/PrcOrderBillLocalRebillAsyncRunner.java @@ -28,11 +28,14 @@ import com.bms.price.config.BillingProperties; import com.bms.price.domain.BatchAddIncomeBillFeeTask; import com.bms.price.domain.PrcOrderBill; import com.bms.price.domain.vo.LocalRebillRequest; +import com.bms.price.domain.vo.TmsPriceQuoteResult; +import com.bms.price.enums.BillBillingFailStatus; import com.bms.price.mapper.PrcOrderBillMapper; import com.bms.price.search.EsBizLogSupport; import com.bms.price.search.IBillSearchStore; import com.bms.price.search.TabOrderSyncEsLog; import com.bms.price.service.IPrcOrderBillService; +import com.bms.price.service.support.OrderBillExportSupport; /** * Async local rebill from prc_order_bill only (no Oracle / MQ). @@ -122,16 +125,33 @@ public class PrcOrderBillLocalRebillAsyncRunner String one = ids.get(i); try { - orderBillService.rebillLocalByBillId(one); - success.incrementAndGet(); + TmsPriceQuoteResult result = orderBillService.rebillLocalByBillId(one); + // Soft-fail still persists the order bill; count as fail in progress. + if (BillBillingFailStatus.isFail(result == null ? null : result.getBillingFailStatus())) + { + fail.incrementAndGet(); + String tip = softFailMessage(result); + String transferLabel = failTransferLabel(one, + result == null ? null : result.getTransferNo()); + appendFail(failTips, transferLabel, tip); + collectFailDetail(failDetails, one, tip); + log.warn("local rebill soft-fail taskId={} billId={} transferNo={} status={} msg={}", + taskId, one, transferLabel, + result == null ? null : result.getBillingFailStatus(), tip); + } + else + { + success.incrementAndGet(); + } } catch (Exception e) { fail.incrementAndGet(); - appendFail(failTips, one, e); + String transferLabel = failTransferLabel(one, null); + appendFail(failTips, transferLabel, e); collectFailDetail(failDetails, one, e); - log.warn("local rebill failed taskId={} billId={} msg={}", - taskId, one, e.getMessage()); + log.warn("local rebill failed taskId={} billId={} transferNo={} msg={}", + taskId, one, transferLabel, e.getMessage()); } finally { @@ -250,6 +270,11 @@ public class PrcOrderBillLocalRebillAsyncRunner } private void collectFailDetail(List> failDetails, String billId, Exception e) + { + collectFailDetail(failDetails, billId, errorMessage(e)); + } + + private void collectFailDetail(List> failDetails, String billId, String message) { if (failDetails == null) { @@ -284,7 +309,7 @@ public class PrcOrderBillLocalRebillAsyncRunner item.put("orderId", StringUtils.isEmpty(orderNo) ? billId : orderNo); item.put("transferOrderId", transferNo); item.put("trackingNo", trackingNo); - item.put("errorMessage", errorMessage(e)); + item.put("errorMessage", StringUtils.isEmpty(message) ? "fail" : message); failDetails.add(item); } } @@ -427,7 +452,12 @@ public class PrcOrderBillLocalRebillAsyncRunner return v; } - private static void appendFail(StringBuilder failTips, String billId, Exception e) + private static void appendFail(StringBuilder failTips, String orderLabel, Exception e) + { + appendFail(failTips, orderLabel, errorMessage(e)); + } + + private static void appendFail(StringBuilder failTips, String orderLabel, String message) { synchronized (failTips) { @@ -439,10 +469,57 @@ public class PrcOrderBillLocalRebillAsyncRunner { failTips.append("; "); } - failTips.append(billId).append(":").append(errorMessage(e)); + failTips.append(StringUtils.isEmpty(orderLabel) ? "-" : orderLabel) + .append(":").append(StringUtils.isEmpty(message) ? "fail" : message); } } + /** Prefer transferNo in UI tips; fall back to orderNo, then billId. */ + private String failTransferLabel(String billId, String transferNoHint) + { + if (StringUtils.isNotEmpty(transferNoHint)) + { + return transferNoHint.trim(); + } + if (StringUtils.isEmpty(billId) || orderBillMapper == null) + { + return billId; + } + try + { + PrcOrderBill bill = orderBillMapper.selectPrcOrderBillById(billId.trim()); + if (bill != null) + { + if (StringUtils.isNotEmpty(bill.getTransferNo())) + { + return bill.getTransferNo().trim(); + } + if (StringUtils.isNotEmpty(bill.getOrderNo())) + { + return bill.getOrderNo().trim(); + } + } + } + catch (Exception ignore) + { + // tip label is best-effort + } + return billId; + } + + private static String softFailMessage(TmsPriceQuoteResult result) + { + if (result == null) + { + return "billing fail"; + } + if (StringUtils.isNotEmpty(result.getMatchMessage())) + { + return result.getMatchMessage(); + } + return OrderBillExportSupport.billingFailStatusLabel(result.getBillingFailStatus()); + } + private static String errorMessage(Exception e) { return StringUtils.isEmpty(e.getMessage()) ? e.getClass().getSimpleName() : e.getMessage(); diff --git a/bms-front/src/views/price/orderBill/index.vue b/bms-front/src/views/price/orderBill/index.vue index 34123d7e..35b0b11f 100644 --- a/bms-front/src/views/price/orderBill/index.vue +++ b/bms-front/src/views/price/orderBill/index.vue @@ -192,7 +192,7 @@
{{ rebillProgressMessage }}
-
+
+
+ 总数 + {{ rebillProgressTotal }} +
+
+ 成功 + {{ rebillProgressSuccess }} +
+
+ 失败 + {{ rebillProgressFail }} +
+
+
总数 {{ rebillProgressTotal }} / 成功 {{ rebillProgressSuccess }} / 失败 {{ rebillProgressFail }}
+

计费在后台进行,可收起弹窗继续操作;请勿关闭或刷新本页

o.value).filter(v => v !== 0) +} + +function viewRebillFailedOrders() { + rebillProgressVisible.value = false + const fails = billingFailOnlyStatuses() + tableRef.value?.setQueryParams?.({ billingFailStatuses: fails }) + tableRef.value?.handleQuery?.() +} + +async function exportRebillFailedOrders() { + if (exportLoading.value) return + const fails = billingFailOnlyStatuses() + // Keep current list filters, force fail statuses for export. + tableRef.value?.setQueryParams?.({ billingFailStatuses: fails }) + rebillProgressVisible.value = false + await handleExport() +} + function finishRebillFromTask(task, cancelled) { const success = Number(task && task.success) || 0 const fail = Number(task && task.fail) || 0 + const total = Number(task && task.total) || rebillProgressTotal.value || 0 rebillProgressPercent.value = Math.min(100, Number(task && task.percent) || 100) rebillProgressStatus.value = cancelled ? 'warning' : (fail > 0 ? 'warning' : 'success') - rebillProgressTotal.value = Number(task && task.total) || rebillProgressTotal.value + rebillProgressTotal.value = total rebillProgressSuccess.value = success rebillProgressFail.value = fail - rebillProgressMessage.value = cancelled - ? ('已取消:成功 ' + success + ' / 失败 ' + fail) - : ('完成:成功 ' + success + ' / 失败 ' + fail) + rebillFailDetail.value = fail > 0 ? String((task && (task.error || task.message)) || '').trim() : '' + if (cancelled) { + rebillProgressMessage.value = '重新计费已取消:成功 ' + success + ' / 失败 ' + fail + ' / 总数 ' + total + } else if (fail > 0) { + rebillProgressMessage.value = '重新计费完成(含失败):成功 ' + success + ' / 失败 ' + fail + ' / 总数 ' + total + } else { + rebillProgressMessage.value = '重新计费完成:成功 ' + success + ' / 失败 ' + fail + ' / 总数 ' + total + } rebillFinished.value = true clearRebillTaskId() tableRef.value?.refresh?.() + // Always popup result dialog when billing finishes (even if progress was collapsed). + rebillProgressVisible.value = true if (cancelled) ElMessage.warning(rebillProgressMessage.value) + else if (fail > 0) ElMessage.warning(rebillProgressMessage.value) else ElMessage.success(rebillProgressMessage.value) scheduleHideRebillFloat() } @@ -799,6 +867,7 @@ async function resumeRebillTask(taskId) { rebillProgressTotal.value = 0 rebillProgressSuccess.value = 0 rebillProgressFail.value = 0 + rebillFailDetail.value = '' let superseded = false try { const result = await pollLocalRebillTask(taskId, applyRebillProgress) @@ -811,6 +880,7 @@ async function resumeRebillTask(taskId) { const task = e && e.task rebillProgressStatus.value = 'exception' rebillProgressMessage.value = (e && e.message) || '重新计费失败' + rebillFailDetail.value = String((task && task.error) || (e && e.message) || '').trim() rebillFinished.value = true if (task) { rebillProgressPercent.value = Math.min(100, Number(task.percent) || 100) @@ -875,6 +945,7 @@ async function runLocalRebill(payload, confirmText) { rebillProgressTotal.value = 0 rebillProgressSuccess.value = 0 rebillProgressFail.value = 0 + rebillFailDetail.value = '' window.setTimeout(() => { if (batchLoading.value) rebillProgressVisible.value = false }, 600) @@ -894,6 +965,7 @@ async function runLocalRebill(payload, confirmText) { const task = e && e.task rebillProgressStatus.value = 'exception' rebillProgressMessage.value = (e && e.message) || '重新计费失败' + rebillFailDetail.value = String((task && task.error) || (e && e.message) || '').trim() rebillFinished.value = true if (task) { rebillProgressPercent.value = Math.min(100, Number(task.percent) || 100) @@ -1049,6 +1121,39 @@ onBeforeUnmount(() => { } .order-identifier-search { width: 100%; } .rebill-progress__msg { margin-top: 12px; color: var(--el-text-color-regular); } +.rebill-result-stats { + display: flex; + gap: 12px; + margin-top: 14px; +} +.rebill-result-stats__item { + flex: 1; + padding: 10px 12px; + border-radius: 8px; + background: var(--el-fill-color-light); + text-align: center; +} +.rebill-result-stats__item.is-success { + background: var(--el-color-success-light-9); +} +.rebill-result-stats__item.is-fail { + background: var(--el-color-warning-light-9); +} +.rebill-result-stats__label { + display: block; + font-size: 12px; + color: var(--el-text-color-secondary); + margin-bottom: 4px; +} +.rebill-result-stats__item strong { + font-size: 20px; + line-height: 1.2; + color: var(--el-text-color-primary); +} +.rebill-progress__error { + margin-top: 12px; +} + .rebill-progress__text { margin-top: 8px; font-size: 13px; color: var(--el-text-color-secondary); } .rebill-progress__hint { margin-top: 12px; font-size: 12px; color: var(--el-color-warning); } .order-bill-query .export-status-float {