From bca4733e4b0fcd9b6496e608304549a09f982463 Mon Sep 17 00:00:00 2001
From: yanderong <307678080@qq.com>
Date: 星期五, 02 十月 2026 19:56:27 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/2.3' into 2.3
---
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/task/TaskUtil.java | 93 +++++++++++++++++++++++++++++++++++-----------
1 files changed, 71 insertions(+), 22 deletions(-)
diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/task/TaskUtil.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/task/TaskUtil.java
index 22b88b7..e15a804 100644
--- a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/task/TaskUtil.java
+++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/task/TaskUtil.java
@@ -465,6 +465,16 @@
&& ChronoUnit.MILLIS.between(createTime, LocalDateTime.now()) > NO_DEVICE_DATA_TIMEOUT_MILLIS;
}
+ /**
+ * 企业账户订单是否已完成闭环(已结算 settle_status=1 或锁定额度已释放 lock_released=1):
+ * 兜底扫描不回退这类订单重跑,避免结算完成后被回退重跑造成重复结算与重复扣款。
+ */
+ static boolean isEnterpriseOrderSettledOrReleased(TChargingOrder order) {
+ return null != order
+ && (Integer.valueOf(1).equals(order.getSettleStatus())
+ || Integer.valueOf(1).equals(order.getLockReleased()));
+ }
+
//十分钟循环执行的定时任务
@Scheduled(fixedRate = 1000 * 60 * 10)
@@ -482,22 +492,28 @@
log.info("定时任务执行,查询到启动失败的订单,执行退款:{}", order.getCode());
if(1 == order.getOrderSource()){
chargingOrderService.refund(order.getCode());
- int num = 0;
- while (true){
- TChargingOrderRefund one = chargingOrderRefundService.getOne(new LambdaQueryWrapper<TChargingOrderRefund>().eq(TChargingOrderRefund::getChargingOrderId, order.getId()));
- if(null != one && 2 == one.getRefundStatus()){
- order.setStatus(-1);
- chargingOrderService.updateById(order);
- break;
- }
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- num++;
- if(num > 30){
- break;
+ //企业账户订单启动失败不落退款明细(资金来自企业额度,仅释放额度并终结),无需等待退款记录,直接终结
+ if (chargingOrderService.isWhitelistEnterpriseOrder(order)) {
+ order.setStatus(-1);
+ chargingOrderService.updateById(order);
+ } else {
+ int num = 0;
+ while (true){
+ TChargingOrderRefund one = chargingOrderRefundService.getOne(new LambdaQueryWrapper<TChargingOrderRefund>().eq(TChargingOrderRefund::getChargingOrderId, order.getId()));
+ if(null != one && 2 == one.getRefundStatus()){
+ order.setStatus(-1);
+ chargingOrderService.updateById(order);
+ break;
+ }
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ num++;
+ if(num > 30){
+ break;
+ }
}
}
}else{
@@ -533,7 +549,13 @@
R r = chargingOrderService.endChargeBillingCharge(vo);
log.info("定时任务执行,停止中的订单处理结果:{}", JSON.toJSONString(r));
}else{
- endOrder(order);
+ if (chargingOrderService.isWhitelistEnterpriseOrder(order)
+ && shouldFallbackSettle(null, order.getCreateTime(), false)) {
+ // 企业账户订单停止中且长时间无任何设备数据与账单:释放锁定额度并兜底关单(金额0、状态5),避免悬挂
+ chargingOrderService.closeEnterpriseOrderWithoutData(order);
+ } else {
+ endOrder(order);
+ }
}
}
@@ -572,6 +594,11 @@
if(System.currentTimeMillis() - createTime.getTime() - (8 * 60 * 60 * 1000) > 1000 * 60 * 10){
endOrder(order);
}
+ } else if (chargingOrderService.isWhitelistEnterpriseOrder(order)
+ && shouldFallbackSettle(null, order.getCreateTime(), false)) {
+ // 企业账户订单长时间无任何实时监控数据(设备未上报/报文丢失)且无账单:
+ // 释放企业账户锁定额度并兜底关单(金额0、状态5、结算完成标记),避免订单长期悬挂被反复扫描
+ chargingOrderService.closeEnterpriseOrderWithoutData(order);
}
}
}
@@ -580,6 +607,13 @@
List<TChargingOrder> endChargingOrder = chargingOrderService.findEndChargingOrder();
log.info("定时任务执行,查询到已结束未退款的订单数量:{}", endChargingOrder.size());
for (TChargingOrder order : endChargingOrder) {
+ // 企业账户订单:已结算(settle_status=1)或锁定额度已释放(lock_released=1)说明已完成闭环,
+ // 不再回退状态重跑,避免重复结算与重复扣款(企业账户订单结算不落退款记录,无法靠退款记录过滤)
+ if (chargingOrderService.isWhitelistEnterpriseOrder(order)
+ && isEnterpriseOrderSettledOrReleased(order)) {
+ log.info("定时任务执行,企业账户订单已结算/已终结,跳过回退:{}", order.getCode());
+ continue;
+ }
if(-1 == order.getStatus()){
order.setStatus(2);
}
@@ -691,12 +725,12 @@
}
//企业账户流程:白名单企业账户订单按企业优惠结算(方式2=服务费比例优惠,方式3=按计费模板同阶段单价)
+ //优惠口径优先取订单快照(建单时固化,策略后续变更不影响补偿重算),快照为空的历史订单实时查询并回填
boolean enterpriseSettlement = chargingOrderService.isWhitelistEnterpriseOrder(order);
- CompanyPreferentialDTO enterprisePreferential = enterpriseSettlement
- ? chargeAccountSupport.loadCompanyPreferential(order.getCompanyId()) : null;
+ ChargeAccountSupport.PreferentialSnapshot enterprisePreferential = enterpriseSettlement
+ ? chargeAccountSupport.resolvePreferential(order) : null;
Integer enterprisePreferentialType = null == enterprisePreferential ? null : enterprisePreferential.getPreferentialType();
- TBillingTemplateDTO enterpriseTemplate = enterpriseSettlement
- ? chargeAccountSupport.loadBillingTemplate(enterprisePreferential) : null;
+ TBillingTemplateDTO enterpriseTemplate = null == enterprisePreferential ? null : enterprisePreferential.getTemplate();
BigDecimal enterpriseElectricDiscount = BigDecimal.ZERO;
BigDecimal enterpriseServiceDiscount = BigDecimal.ZERO;
@@ -947,12 +981,27 @@
if(null != desc){
order.setEndSoc(new BigDecimal(desc.getSoc()));
}
+ //企业账户流程:历史订单(快照为空)实时解析的优惠结果回填订单快照列,保证后续补偿重算口径一致;
+ //同时置结算完成标记,兜底扫描(findEndChargingOrder)不再回退已结算订单,防止重复结算与重复扣款
+ if (enterpriseSettlement) {
+ if (null != enterprisePreferential && enterprisePreferential.needBackfill()) {
+ order.setPreferentialType(enterprisePreferential.getPreferentialType());
+ order.setServiceDiscountRate(enterprisePreferential.getServiceDiscountRate());
+ order.setBillingTemplateId(enterprisePreferential.getBillingTemplateId());
+ order.setBillingTemplateSnapshot(
+ ChargeAccountSupport.serializeTemplateSnapshot(enterprisePreferential.getTemplate()));
+ order.setPreferentialSnapshotTime(LocalDateTime.now());
+ }
+ order.setSettleStatus(1);
+ }
chargingOrderService.updateById(order);
//企业账户流程:结算企业额度(按实际消费扣减锁定额度、差额退回企业账户)并写消费流水
if (enterpriseSettlement && !chargingOrderService.settleWhitelistEnterpriseAccount(order, payAmount)) {
- // 结算失败回退为停止中,交由补偿任务下一轮重新结算,避免企业额度长期冻结
+ // 结算失败回退为停止中,交由补偿任务下一轮重新结算,避免企业额度长期冻结;
+ // 同时清除结算完成标记,保证下一轮补偿仍会处理该订单
order.setStatus(4);
+ order.setSettleStatus(0);
chargingOrderService.updateById(order);
log.error("[CD补偿结算] 企业账户额度结算失败,订单回退停止中等待重试 code={}", order.getCode());
return;
--
Gitblit v1.7.1