From 5b434085676fd5c701a4a9ed86047b2143cae2a1 Mon Sep 17 00:00:00 2001
From: longz <longzhaoz@qq.com>
Date: 星期二, 04 八月 2026 20:09:21 +0800
Subject: [PATCH] fix: 退款列表查询优化(MyCat 分片下推/逆反查/时间筛选)
---
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++----
1 files changed, 100 insertions(+), 8 deletions(-)
diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java
index 215f032..bf9d7c5 100644
--- a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java
+++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java
@@ -3347,17 +3347,40 @@
.divide(BigDecimal.valueOf(3600), 2, RoundingMode.HALF_UP);
}
+ private static final int SERIAL_ORDER_ID_SEARCH_LIMIT = 5000;
+ private static final int SITE_ORDER_ID_SEARCH_LIMIT = 50000;
+
@Override
public R<PageInfo<TChargingOrderRefund>> getRefundList(ChargingRefundDto chargingRefundDto) {
- PageInfo<TChargingOrderRefund> pageInfo = new PageInfo<>(chargingRefundDto.getPageCurr(), chargingRefundDto.getPageSize());
- boolean withoutOrderJoin = !StringUtils.hasLength(chargingRefundDto.getSerialNumber())
- && (chargingRefundDto.getSiteIds() == null || chargingRefundDto.getSiteIds().isEmpty());
- List<TChargingOrderRefund> list = withoutOrderJoin
- ? this.baseMapper.getRefundListWithoutOrderJoin(pageInfo, chargingRefundDto)
- : this.baseMapper.getRefundList(pageInfo, chargingRefundDto);
- if (withoutOrderJoin) {
- enrichRefundOrderInfo(list);
+ int pageCurr = chargingRefundDto.getPageCurr() == null ? 1 : chargingRefundDto.getPageCurr();
+ int pageSize = chargingRefundDto.getPageSize() == null ? 50 : chargingRefundDto.getPageSize();
+ PageInfo<TChargingOrderRefund> pageInfo = new PageInfo<>(pageCurr, pageSize);
+ long offset = (long) (pageCurr - 1) * pageSize;
+ // 每个 UNION 分支须取 offset+pageSize 行,外层再按 offset,pageSize 切页,否则翻页时池子不足导致丢数据
+ int branchLimit = (int) Math.min(offset + pageSize, Integer.MAX_VALUE);
+ boolean filterBySerial = StringUtils.hasLength(chargingRefundDto.getSerialNumber());
+ boolean filterBySite = chargingRefundDto.getSiteIds() != null && !chargingRefundDto.getSiteIds().isEmpty();
+
+ List<TChargingOrderRefund> list;
+ if (!filterBySerial && !filterBySite) {
+ // 无筛选:手工分页。分页拦截器会对 UNION 查询生成全量 COUNT 且无法把 ORDER BY/LIMIT 下推到分片,
+ // 这里改为分支内下推(getRefundListPage)加分支 COUNT 求和(countRefundList)
+ pageInfo.setTotal(this.baseMapper.countRefundList(chargingRefundDto));
+ list = this.baseMapper.getRefundListPage(chargingRefundDto, offset, pageSize, branchLimit);
+ } else {
+ // 筛选:逆反查。先按条件查订单 id(t_charging_order 等可下推/走索引),
+ // 再按退款表分片键 IN 过滤(可路由),替代跨分片 LEFT JOIN
+ RefundOrderIds ids = resolveRefundOrderIds(chargingRefundDto);
+ if (ids.isEmpty()) {
+ pageInfo.setRecords(Collections.emptyList());
+ return R.ok(pageInfo);
+ }
+ pageInfo.setTotal(this.baseMapper.countRefundListByOrderIds(chargingRefundDto,
+ ids.type1OrderIds, ids.type2OrderIds, ids.type3OrderIds));
+ list = this.baseMapper.getRefundListByOrderIds(chargingRefundDto,
+ ids.type1OrderIds, ids.type2OrderIds, ids.type3OrderIds, offset, pageSize, branchLimit);
}
+ enrichRefundOrderInfo(list);
for (TChargingOrderRefund refund : list) {
refund.setUid(refund.getType() + "-" + refund.getId());
}
@@ -3366,6 +3389,75 @@
return R.ok(pageInfo);
}
+ /**
+ * 逆反查订单 id:serialNumber 匹配充电/商城/会员订单 id(各自 LIMIT 防爆),
+ * siteIds 匹配充电订单 id;两者同时存在时取交集。
+ * 返回约定:null = 该类型不过滤;空列表 = 该类型无匹配(SQL 侧 AND 1=0)。
+ */
+ private RefundOrderIds resolveRefundOrderIds(ChargingRefundDto chargingRefundDto) {
+ List<Long> type1OrderIds = null;
+ List<Long> type2OrderIds = null;
+ List<Long> type3OrderIds = null;
+ if (StringUtils.hasLength(chargingRefundDto.getSerialNumber())) {
+ // 模糊匹配结果可能很大(短流水号),截断上限防 IN 列表爆炸;匹配不到则列表为空
+ type1OrderIds = safeIds(this.baseMapper.listChargingOrderIdsBySerialNumber(
+ chargingRefundDto.getSerialNumber(), SERIAL_ORDER_ID_SEARCH_LIMIT));
+ type2OrderIds = safeIds(this.baseMapper.listShoppingOrderIdsBySerialNumber(
+ chargingRefundDto.getSerialNumber(), SERIAL_ORDER_ID_SEARCH_LIMIT));
+ type3OrderIds = safeIds(this.baseMapper.listVipOrderIdsBySerialNumber(
+ chargingRefundDto.getSerialNumber(), SERIAL_ORDER_ID_SEARCH_LIMIT));
+ warnIfTruncated(chargingRefundDto.getSerialNumber(), type1OrderIds,
+ SERIAL_ORDER_ID_SEARCH_LIMIT, "充电订单");
+ warnIfTruncated(chargingRefundDto.getSerialNumber(), type2OrderIds,
+ SERIAL_ORDER_ID_SEARCH_LIMIT, "商城订单");
+ warnIfTruncated(chargingRefundDto.getSerialNumber(), type3OrderIds,
+ SERIAL_ORDER_ID_SEARCH_LIMIT, "会员订单");
+ }
+ if (chargingRefundDto.getSiteIds() != null && !chargingRefundDto.getSiteIds().isEmpty()) {
+ List<Long> siteMatched = safeIds(this.baseMapper.listChargingOrderIdsBySiteIds(
+ chargingRefundDto.getSiteIds(), SITE_ORDER_ID_SEARCH_LIMIT));
+ warnIfTruncated(String.valueOf(chargingRefundDto.getSiteIds()), siteMatched,
+ SITE_ORDER_ID_SEARCH_LIMIT, "站点订单");
+ if (type1OrderIds == null) {
+ type1OrderIds = siteMatched;
+ } else {
+ type1OrderIds.retainAll(new HashSet<>(siteMatched));
+ }
+ }
+ return new RefundOrderIds(type1OrderIds, type2OrderIds, type3OrderIds);
+ }
+
+ /** 反查结果达到上限说明可能被截断,告警便于排查(截断会导致部分退款不在列表内) */
+ private void warnIfTruncated(String condition, List<Long> ids, int limit, String orderType) {
+ if (ids.size() >= limit) {
+ log.warn("退款列表逆反查:条件[{}]匹配{}数达到上限{},结果可能被截断", condition, orderType, limit);
+ }
+ }
+
+ private static List<Long> safeIds(List<Long> ids) {
+ return ids == null ? new ArrayList<>() : new ArrayList<>(ids);
+ }
+
+ /** 逆反查结果:null = 不过滤;空列表 = 无匹配 */
+ private static class RefundOrderIds {
+ private final List<Long> type1OrderIds;
+ private final List<Long> type2OrderIds;
+ private final List<Long> type3OrderIds;
+
+ private RefundOrderIds(List<Long> type1OrderIds, List<Long> type2OrderIds, List<Long> type3OrderIds) {
+ this.type1OrderIds = type1OrderIds;
+ this.type2OrderIds = type2OrderIds;
+ this.type3OrderIds = type3OrderIds;
+ }
+
+ /** 三个类型全部明确无匹配(非 null 且为空)时,整页为空,可短路 */
+ private boolean isEmpty() {
+ return type1OrderIds != null && type1OrderIds.isEmpty()
+ && type2OrderIds != null && type2OrderIds.isEmpty()
+ && type3OrderIds != null && type3OrderIds.isEmpty();
+ }
+ }
+
private void enrichRefundOrderInfo(List<TChargingOrderRefund> refunds) {
if (refunds == null || refunds.isEmpty()) {
return;
--
Gitblit v1.7.1