diff --git a/src/main/java/com/gxwebsoft/glt/service/GltTicketIssueService.java b/src/main/java/com/gxwebsoft/glt/service/GltTicketIssueService.java index 70fd33b..eda3a30 100644 --- a/src/main/java/com/gxwebsoft/glt/service/GltTicketIssueService.java +++ b/src/main/java/com/gxwebsoft/glt/service/GltTicketIssueService.java @@ -258,7 +258,7 @@ public class GltTicketIssueService { int giftMultiplier = template.getGiftMultiplier() != null ? template.getGiftMultiplier() : 0; int giftQty = buyQty * Math.max(giftMultiplier, 0); - // 购买量(buyQty)应立即可用;赠送量(giftQty)进入冻结并按计划释放。 + // 总票数(购买量 + 赠送量) int totalQty = buyQty + giftQty; if (totalQty <= 0) { @@ -269,6 +269,15 @@ public class GltTicketIssueService { LocalDateTime now = LocalDateTime.now(); + boolean useReleasePeriods = template.getReleasePeriods() != null && template.getReleasePeriods() > 0; + + // 释放期数(releasePeriods)为高优先级: + // - 配置了期数:按期数平均分摊 totalQty,每期释放;不再“先把购买桶数一次性释放”。 + // - 未配置期数:保持原逻辑(购买量立即可用,赠送量冻结并按计划释放)。 + int initAvailableQty = useReleasePeriods ? 0 : buyQty; + int initFrozenQty = useReleasePeriods ? totalQty : giftQty; + int initReleasedQty = useReleasePeriods ? 0 : buyQty; + GltUserTicket userTicket = new GltUserTicket(); userTicket.setTemplateId(template.getId()); userTicket.setGoodsId(og.getGoodsId()); @@ -276,11 +285,10 @@ public class GltTicketIssueService { userTicket.setOrderNo(order.getOrderNo()); userTicket.setOrderGoodsId(og.getId()); userTicket.setTotalQty(totalQty); - userTicket.setAvailableQty(buyQty); - userTicket.setFrozenQty(giftQty); + userTicket.setAvailableQty(initAvailableQty); + userTicket.setFrozenQty(initFrozenQty); userTicket.setUsedQty(0); - // 初始可用量来自“购买量”,视为已释放 - userTicket.setReleasedQty(buyQty); + userTicket.setReleasedQty(initReleasedQty); userTicket.setOrderGoodsQty(og.getTotalNum()); userTicket.setUserId(order.getUserId()); userTicket.setSortNumber(0); @@ -293,12 +301,36 @@ public class GltTicketIssueService { gltUserTicketService.save(userTicket); - // 生成释放计划(按月) + // 生成释放计划: + // - 配置 releasePeriods:按 totalQty 生成每期释放量(periods 优先) + // - 未配置 releasePeriods:按 giftQty 生成每期释放量 LocalDateTime baseTime = order.getPayTime() != null ? order.getPayTime() : order.getCreateTime(); if (baseTime == null) { baseTime = now; } - List releases = buildReleasePlan(template, userTicket, baseTime, giftQty, now); + int planQty = useReleasePeriods ? totalQty : giftQty; + List releases = buildReleasePlan(template, userTicket, baseTime, planQty, now); + + // 若启用了 releasePeriods 且首期释放时机为“支付成功当刻”,则将首期释放量直接计入可用, + // 避免用户刚购买后短时间内无可用水票;后续期数仍由自动释放任务按 release_time 释放。 + if (useReleasePeriods && !releases.isEmpty() && !Objects.equals(template.getFirstReleaseMode(), 1)) { + GltUserTicketRelease first = releases.get(0); + Integer firstQtyObj = first.getReleaseQty(); + LocalDateTime firstTime = first.getReleaseTime(); + int firstQty = firstQtyObj != null ? firstQtyObj : 0; + if (firstQty > 0 && (firstTime == null || !firstTime.isAfter(now))) { + first.setStatus(1); + first.setUpdateTime(now); + + userTicket.setAvailableQty((userTicket.getAvailableQty() != null ? userTicket.getAvailableQty() : 0) + firstQty); + userTicket.setFrozenQty((userTicket.getFrozenQty() != null ? userTicket.getFrozenQty() : 0) - firstQty); + userTicket.setReleasedQty((userTicket.getReleasedQty() != null ? userTicket.getReleasedQty() : 0) + firstQty); + userTicket.setUpdateTime(now); + if (!gltUserTicketService.updateById(userTicket)) { + throw new IllegalStateException("首期释放:更新用户水票失败 userTicketId=" + userTicket.getId()); + } + } + } if (!releases.isEmpty()) { gltUserTicketReleaseService.saveBatch(releases); } @@ -307,11 +339,11 @@ public class GltTicketIssueService { GltUserTicketLog issueLog = new GltUserTicketLog(); issueLog.setUserTicketId(userTicket.getId()); issueLog.setChangeType(CHANGE_TYPE_ISSUE); - issueLog.setChangeAvailable(buyQty); - issueLog.setChangeFrozen(giftQty); + issueLog.setChangeAvailable(userTicket.getAvailableQty() != null ? userTicket.getAvailableQty() : 0); + issueLog.setChangeFrozen(userTicket.getFrozenQty() != null ? userTicket.getFrozenQty() : 0); issueLog.setChangeUsed(0); - issueLog.setAvailableAfter(buyQty); - issueLog.setFrozenAfter(giftQty); + issueLog.setAvailableAfter(userTicket.getAvailableQty() != null ? userTicket.getAvailableQty() : 0); + issueLog.setFrozenAfter(userTicket.getFrozenQty() != null ? userTicket.getFrozenQty() : 0); issueLog.setUsedAfter(0); issueLog.setOrderId(order.getOrderId()); issueLog.setOrderNo(order.getOrderNo()); @@ -402,12 +434,13 @@ public class GltTicketIssueService { if (releasePeriods != null && releasePeriods > 0) { int base = totalQty / releasePeriods; int remainder = totalQty % releasePeriods; - for (int i = 1; i <= releasePeriods; i++) { - int qty = base + (i <= remainder ? 1 : 0); + // periodNo 从 0 开始:第0期、第1期……(更贴近任务执行计数) + for (int i = 0; i < releasePeriods; i++) { + int qty = base + (i < remainder ? 1 : 0); if (qty <= 0) { continue; } - list.add(buildRelease(userTicket, i, qty, firstReleaseTime.plusMonths(i - 1), now)); + list.add(buildRelease(userTicket, i, qty, firstReleaseTime.plusMonths(i), now)); } return list; } @@ -417,13 +450,14 @@ public class GltTicketIssueService { : 10; int periods = (totalQty + monthlyReleaseQty - 1) / monthlyReleaseQty; int remaining = totalQty; - for (int i = 1; i <= periods; i++) { + // periodNo 从 0 开始:第0期、第1期…… + for (int i = 0; i < periods; i++) { int qty = Math.min(monthlyReleaseQty, remaining); if (qty <= 0) { break; } remaining -= qty; - list.add(buildRelease(userTicket, i, qty, firstReleaseTime.plusMonths(i - 1), now)); + list.add(buildRelease(userTicket, i, qty, firstReleaseTime.plusMonths(i), now)); } return list;