From f7a96724c6f26be910fdaa8a49c4497a025ce3ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E5=BF=A0=E6=9E=97?= <170083662@qq.com> Date: Mon, 9 Feb 2026 17:30:43 +0800 Subject: [PATCH] =?UTF-8?q?feat(task):=20=E6=9B=B4=E6=96=B0=E5=88=86?= =?UTF-8?q?=E9=94=80=E4=BD=A3=E9=87=91=E8=A7=A3=E5=86=BB=E5=92=8C=E5=A5=97?= =?UTF-8?q?=E7=A5=A8=E5=8F=91=E6=94=BE=E4=BB=BB=E5=8A=A1=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 引入GltTicketTemplate和GltTicketTemplateService用于动态获取水票模板 - 将硬编码的formId=10074替换为从水票模板表动态获取的goodsId集合 - 修改经销商佣金解冻规则适配新的模板配置方式 - 更新套票发放任务支持多商品ID配置 - 添加水票模板数据加载和验证逻辑 - 增强任务执行前的模板配置检查机制 --- .../glt/service/GltTicketIssueService.java | 38 +++++++++++++--- .../DealerCommissionUnfreeze10584Task.java | 44 ++++++++++++++----- .../glt/task/GltTicketIssue10584Task.java | 29 +++++++++--- 3 files changed, 88 insertions(+), 23 deletions(-) diff --git a/src/main/java/com/gxwebsoft/glt/service/GltTicketIssueService.java b/src/main/java/com/gxwebsoft/glt/service/GltTicketIssueService.java index 6ac9261..55a0473 100644 --- a/src/main/java/com/gxwebsoft/glt/service/GltTicketIssueService.java +++ b/src/main/java/com/gxwebsoft/glt/service/GltTicketIssueService.java @@ -19,8 +19,10 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Objects; +import java.util.Set; /** * 套票发放(从订单生成用户套票 + 释放计划)的业务逻辑。 @@ -57,13 +59,35 @@ public class GltTicketIssueService { * 扫描“今日订单”,执行套票发放。 */ public void issueTodayOrders(Integer tenantId, Integer formId) { + if (formId == null) { + return; + } + issueTodayOrders(tenantId, List.of(formId)); + } + + /** + * 扫描“今日订单”,执行套票发放(支持多个商品/模板)。 + */ + public void issueTodayOrders(Integer tenantId, List goodsIds) { + if (tenantId == null || goodsIds == null || goodsIds.isEmpty()) { + return; + } + List uniqueGoodsIds = goodsIds.stream() + .filter(Objects::nonNull) + .distinct() + .toList(); + if (uniqueGoodsIds.isEmpty()) { + return; + } + Set goodsIdSet = new HashSet<>(uniqueGoodsIds); + LocalDateTime todayStart = LocalDate.now().atStartOfDay(); LocalDateTime tomorrowStart = todayStart.plusDays(1); List orders = shopOrderService.list( new LambdaQueryWrapper() .eq(ShopOrder::getTenantId, tenantId) - .eq(ShopOrder::getFormId, formId) + .in(ShopOrder::getFormId, uniqueGoodsIds) .eq(ShopOrder::getPayStatus, true) .eq(ShopOrder::getOrderStatus, 0) // 今日订单(兼容:以 create_time 或 pay_time 任一落在今日即可) @@ -77,7 +101,7 @@ public class GltTicketIssueService { ); if (orders.isEmpty()) { - log.debug("套票发放扫描:今日无符合条件的订单 tenantId={}, formId={}", tenantId, formId); + log.debug("套票发放扫描:今日无符合条件的订单 tenantId={}, goodsIds={}", tenantId, uniqueGoodsIds); return; } @@ -87,7 +111,7 @@ public class GltTicketIssueService { for (ShopOrder order : orders) { try { - int issuedCount = issueForOrder(tenantId, formId, order); + int issuedCount = issueForOrder(tenantId, goodsIdSet, order); if (issuedCount > 0) { success += issuedCount; } else { @@ -100,11 +124,11 @@ public class GltTicketIssueService { } } - log.info("套票发放扫描完成 - tenantId={}, formId={}, 订单数={}, 发放成功={}, 跳过={}, 失败={}", - tenantId, formId, orders.size(), success, skipped, failed); + log.info("套票发放扫描完成 - tenantId={}, goodsIds={}, 订单数={}, 发放成功={}, 跳过={}, 失败={}", + tenantId, uniqueGoodsIds, orders.size(), success, skipped, failed); } - private int issueForOrder(Integer tenantId, Integer formId, ShopOrder order) { + private int issueForOrder(Integer tenantId, Set goodsIds, ShopOrder order) { List goodsList = shopOrderGoodsService.getListByOrderIdIgnoreTenant(order.getOrderId()); if (goodsList == null || goodsList.isEmpty()) { return 0; @@ -114,7 +138,7 @@ public class GltTicketIssueService { boolean shouldCompleteOrder = false; for (ShopOrderGoods og : goodsList) { - if (!Objects.equals(og.getGoodsId(), formId)) { + if (og.getGoodsId() == null || !goodsIds.contains(og.getGoodsId())) { continue; } diff --git a/src/main/java/com/gxwebsoft/glt/task/DealerCommissionUnfreeze10584Task.java b/src/main/java/com/gxwebsoft/glt/task/DealerCommissionUnfreeze10584Task.java index aca651a..7fc6d1f 100644 --- a/src/main/java/com/gxwebsoft/glt/task/DealerCommissionUnfreeze10584Task.java +++ b/src/main/java/com/gxwebsoft/glt/task/DealerCommissionUnfreeze10584Task.java @@ -3,8 +3,10 @@ package com.gxwebsoft.glt.task; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.gxwebsoft.common.core.annotation.IgnoreTenant; import com.gxwebsoft.glt.entity.GltTicketOrder; +import com.gxwebsoft.glt.entity.GltTicketTemplate; import com.gxwebsoft.glt.entity.GltUserTicket; import com.gxwebsoft.glt.service.GltTicketOrderService; +import com.gxwebsoft.glt.service.GltTicketTemplateService; import com.gxwebsoft.glt.service.GltUserTicketService; import com.gxwebsoft.shop.entity.ShopDealerCapital; import com.gxwebsoft.shop.entity.ShopDealerUser; @@ -34,8 +36,8 @@ import java.util.concurrent.atomic.AtomicBoolean; * 租户10584:分销佣金解冻任务 * *

规则:

- *

1) 送水套餐(formId=10074):订单号关联的水票产生了第一次送水订单,且该第一次送水订单状态=已完成(40) -> 解冻。

- *

2) 非送水套餐(formId!=10074):订单已确认收货(orderStatus=1) -> 解冻。

+ *

1) 送水套餐(formId in 水票模板 goodsId):订单号关联的水票产生了第一次送水订单,且该第一次送水订单状态=已完成(40) -> 解冻。

+ *

2) 非送水套餐(formId not in 水票模板 goodsId):订单已确认收货(orderStatus=1) -> 解冻。

* *

实现策略:以 ShopDealerCapital(flowType=10) 的“佣金明细”为解冻粒度, * 每条佣金明细对应生成一条 ShopDealerCapital(flowType=50) 作为幂等标记,并执行 @@ -47,7 +49,6 @@ import java.util.concurrent.atomic.AtomicBoolean; public class DealerCommissionUnfreeze10584Task { private static final int TENANT_ID = 10584; - private static final int WATER_FORM_ID = 10074; private static final int ORDER_STATUS_CONFIRMED_RECEIVE = 1; @@ -73,6 +74,9 @@ public class DealerCommissionUnfreeze10584Task { @Resource private GltTicketOrderService gltTicketOrderService; + @Resource + private GltTicketTemplateService gltTicketTemplateService; + private final AtomicBoolean running = new AtomicBoolean(false); @Scheduled(cron = "${dealer.commission.unfreeze10584.cron:0 */1 * * * ?}") @@ -84,10 +88,17 @@ public class DealerCommissionUnfreeze10584Task { } try { + Set waterFormIds = loadWaterFormIds(); + if (waterFormIds.isEmpty()) { + // 送水/非送水的判断依赖模板 goodsId;拿不到会导致误判,宁可跳过本轮。 + log.warn("分销佣金解冻任务跳过:未找到水票模板 goodsId - tenantId={}", TENANT_ID); + return; + } + // 先按“最近确认收货”的订单扫描,避免总是卡在很早的历史订单上。 Set eligibleOrderNos = new HashSet<>(); - eligibleOrderNos.addAll(findEligibleNonWaterOrderNos(true)); - eligibleOrderNos.addAll(findEligibleWaterOrderNosByFirstFinishedTicketOrder()); + eligibleOrderNos.addAll(findEligibleNonWaterOrderNos(waterFormIds, true)); + eligibleOrderNos.addAll(findEligibleWaterOrderNosByFirstFinishedTicketOrder(waterFormIds)); if (eligibleOrderNos.isEmpty()) { return; @@ -97,8 +108,8 @@ public class DealerCommissionUnfreeze10584Task { if (capitals.isEmpty()) { // 若本轮没有取到佣金明细,回退再按“最早确认收货”的订单扫一轮,尽量覆盖历史遗留未解冻。 eligibleOrderNos.clear(); - eligibleOrderNos.addAll(findEligibleNonWaterOrderNos(false)); - eligibleOrderNos.addAll(findEligibleWaterOrderNosByFirstFinishedTicketOrder()); + eligibleOrderNos.addAll(findEligibleNonWaterOrderNos(waterFormIds, false)); + eligibleOrderNos.addAll(findEligibleWaterOrderNosByFirstFinishedTicketOrder(waterFormIds)); capitals = findCapitalsByEligibleOrderNos(eligibleOrderNos); } @@ -128,13 +139,24 @@ public class DealerCommissionUnfreeze10584Task { } } - private List findEligibleNonWaterOrderNos(boolean newestFirst) { + private Set loadWaterFormIds() { + return gltTicketTemplateService.list( + new LambdaQueryWrapper() + .eq(GltTicketTemplate::getTenantId, TENANT_ID) + .eq(GltTicketTemplate::getDeleted, 0) + .isNotNull(GltTicketTemplate::getGoodsId) + ).stream() + .map(GltTicketTemplate::getGoodsId) + .collect(java.util.stream.Collectors.toSet()); + } + + private List findEligibleNonWaterOrderNos(Set waterFormIds, boolean newestFirst) { LambdaQueryWrapper qw = new LambdaQueryWrapper() .eq(ShopOrder::getTenantId, TENANT_ID) .eq(ShopOrder::getDeleted, 0) .eq(ShopOrder::getPayStatus, true) .eq(ShopOrder::getOrderStatus, ORDER_STATUS_CONFIRMED_RECEIVE) - .and(w -> w.ne(ShopOrder::getFormId, WATER_FORM_ID).or().isNull(ShopOrder::getFormId)) + .and(w -> w.notIn(ShopOrder::getFormId, waterFormIds).or().isNull(ShopOrder::getFormId)) .isNotNull(ShopOrder::getOrderNo); if (newestFirst) { @@ -150,7 +172,7 @@ public class DealerCommissionUnfreeze10584Task { .toList(); } - private Set findEligibleWaterOrderNosByFirstFinishedTicketOrder() { + private Set findEligibleWaterOrderNosByFirstFinishedTicketOrder(Set waterFormIds) { // 缓存减少 DB 往返:userTicketId -> 是否“第一次送水单已完成” Map firstFinishedCache = new HashMap<>(); Map userTicketOrderNoCache = new HashMap<>(); @@ -191,7 +213,7 @@ public class DealerCommissionUnfreeze10584Task { .eq(ShopOrder::getOrderNo, orderNo) .last("limit 1") ); - if (shopOrder == null || shopOrder.getFormId() == null || shopOrder.getFormId() != WATER_FORM_ID) { + if (shopOrder == null || shopOrder.getFormId() == null || !waterFormIds.contains(shopOrder.getFormId())) { continue; } diff --git a/src/main/java/com/gxwebsoft/glt/task/GltTicketIssue10584Task.java b/src/main/java/com/gxwebsoft/glt/task/GltTicketIssue10584Task.java index 292acb2..2cc7b68 100644 --- a/src/main/java/com/gxwebsoft/glt/task/GltTicketIssue10584Task.java +++ b/src/main/java/com/gxwebsoft/glt/task/GltTicketIssue10584Task.java @@ -1,18 +1,22 @@ package com.gxwebsoft.glt.task; import com.gxwebsoft.common.core.annotation.IgnoreTenant; +import com.gxwebsoft.glt.entity.GltTicketTemplate; import com.gxwebsoft.glt.service.GltTicketIssueService; +import com.gxwebsoft.glt.service.GltTicketTemplateService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; +import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; /** * GLT 套票发放任务: - * - 每分钟扫描一次今日订单(tenantId=10584, formId=10074, payStatus=1, orderStatus=0) + * - 每分钟扫描一次今日订单(tenantId=10584, formId in 套票模板 goodsId, payStatus=1, orderStatus=0) * - 为订单生成用户套票账户 + 释放计划(幂等) */ @Slf4j @@ -22,9 +26,9 @@ import java.util.concurrent.atomic.AtomicBoolean; public class GltTicketIssue10584Task { private static final int TENANT_ID = 10584; - private static final int FORM_ID = 10074; private final GltTicketIssueService gltTicketIssueService; + private final GltTicketTemplateService gltTicketTemplateService; private final AtomicBoolean running = new AtomicBoolean(false); @@ -32,15 +36,30 @@ public class GltTicketIssue10584Task { @IgnoreTenant("定时任务无登录态,需忽略租户隔离;内部使用 tenantId=10584 精确过滤") public void run() { if (!running.compareAndSet(false, true)) { - log.warn("套票发放任务仍在执行中,本轮跳过 - tenantId={}, formId={}", TENANT_ID, FORM_ID); + log.warn("套票发放任务仍在执行中,本轮跳过 - tenantId={}", TENANT_ID); return; } try { - gltTicketIssueService.issueTodayOrders(TENANT_ID, FORM_ID); + List goodsIds = gltTicketTemplateService.list( + new LambdaQueryWrapper() + .eq(GltTicketTemplate::getTenantId, TENANT_ID) + .eq(GltTicketTemplate::getDeleted, 0) + .eq(GltTicketTemplate::getEnabled, true) + .isNotNull(GltTicketTemplate::getGoodsId) + ).stream() + .map(GltTicketTemplate::getGoodsId) + .distinct() + .toList(); + + if (goodsIds.isEmpty()) { + log.warn("套票发放任务跳过:未配置/未启用套票模板 - tenantId={}", TENANT_ID); + return; + } + + gltTicketIssueService.issueTodayOrders(TENANT_ID, goodsIds); } finally { running.set(false); } } } -