feat(task): 更新分销佣金解冻和套票发放任务逻辑
- 引入GltTicketTemplate和GltTicketTemplateService用于动态获取水票模板 - 将硬编码的formId=10074替换为从水票模板表动态获取的goodsId集合 - 修改经销商佣金解冻规则适配新的模板配置方式 - 更新套票发放任务支持多商品ID配置 - 添加水票模板数据加载和验证逻辑 - 增强任务执行前的模板配置检查机制
This commit is contained in:
@@ -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<Integer> goodsIds) {
|
||||
if (tenantId == null || goodsIds == null || goodsIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
List<Integer> uniqueGoodsIds = goodsIds.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.distinct()
|
||||
.toList();
|
||||
if (uniqueGoodsIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Set<Integer> goodsIdSet = new HashSet<>(uniqueGoodsIds);
|
||||
|
||||
LocalDateTime todayStart = LocalDate.now().atStartOfDay();
|
||||
LocalDateTime tomorrowStart = todayStart.plusDays(1);
|
||||
|
||||
List<ShopOrder> orders = shopOrderService.list(
|
||||
new LambdaQueryWrapper<ShopOrder>()
|
||||
.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<Integer> goodsIds, ShopOrder order) {
|
||||
List<ShopOrderGoods> 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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:分销佣金解冻任务
|
||||
*
|
||||
* <p>规则:</p>
|
||||
* <p>1) 送水套餐(formId=10074):订单号关联的水票产生了第一次送水订单,且该第一次送水订单状态=已完成(40) -> 解冻。</p>
|
||||
* <p>2) 非送水套餐(formId!=10074):订单已确认收货(orderStatus=1) -> 解冻。</p>
|
||||
* <p>1) 送水套餐(formId in 水票模板 goodsId):订单号关联的水票产生了第一次送水订单,且该第一次送水订单状态=已完成(40) -> 解冻。</p>
|
||||
* <p>2) 非送水套餐(formId not in 水票模板 goodsId):订单已确认收货(orderStatus=1) -> 解冻。</p>
|
||||
*
|
||||
* <p>实现策略:以 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<Integer> waterFormIds = loadWaterFormIds();
|
||||
if (waterFormIds.isEmpty()) {
|
||||
// 送水/非送水的判断依赖模板 goodsId;拿不到会导致误判,宁可跳过本轮。
|
||||
log.warn("分销佣金解冻任务跳过:未找到水票模板 goodsId - tenantId={}", TENANT_ID);
|
||||
return;
|
||||
}
|
||||
|
||||
// 先按“最近确认收货”的订单扫描,避免总是卡在很早的历史订单上。
|
||||
Set<String> 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<String> findEligibleNonWaterOrderNos(boolean newestFirst) {
|
||||
private Set<Integer> loadWaterFormIds() {
|
||||
return gltTicketTemplateService.list(
|
||||
new LambdaQueryWrapper<GltTicketTemplate>()
|
||||
.eq(GltTicketTemplate::getTenantId, TENANT_ID)
|
||||
.eq(GltTicketTemplate::getDeleted, 0)
|
||||
.isNotNull(GltTicketTemplate::getGoodsId)
|
||||
).stream()
|
||||
.map(GltTicketTemplate::getGoodsId)
|
||||
.collect(java.util.stream.Collectors.toSet());
|
||||
}
|
||||
|
||||
private List<String> findEligibleNonWaterOrderNos(Set<Integer> waterFormIds, boolean newestFirst) {
|
||||
LambdaQueryWrapper<ShopOrder> qw = new LambdaQueryWrapper<ShopOrder>()
|
||||
.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<String> findEligibleWaterOrderNosByFirstFinishedTicketOrder() {
|
||||
private Set<String> findEligibleWaterOrderNosByFirstFinishedTicketOrder(Set<Integer> waterFormIds) {
|
||||
// 缓存减少 DB 往返:userTicketId -> 是否“第一次送水单已完成”
|
||||
Map<Integer, Boolean> firstFinishedCache = new HashMap<>();
|
||||
Map<Integer, String> 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Integer> goodsIds = gltTicketTemplateService.list(
|
||||
new LambdaQueryWrapper<GltTicketTemplate>()
|
||||
.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user