Compare commits

...

3 Commits

Author SHA1 Message Date
c1efeef8c7 refactor(ticket): 更新用户票务实体字段映射
- 将用户票务中的购买数量字段从buyQty替换为orderGoodsQty
- 移除GltUserTicket实体中的buyQty字段定义
- 添加orderGoodsQty字段到GltUserTicket实体中
- 修改服务层代码以使用订单商品总数量进行赋值
- 调整数据库映射关系以匹配新的业务逻辑
2026-02-09 20:28:32 +08:00
76aec53bae Merge remote-tracking branch 'origin/master'
# Conflicts:
#	src/main/java/com/gxwebsoft/glt/service/GltTicketIssueService.java
2026-02-09 18:56:33 +08:00
33f9f07037 feat(ticket): 添加购买数量字段到用户票务实体
- 在GltUserTicket实体中新增buyQty字段用于存储购买数量
- 在票务发放服务中设置购买数量到用户票务记录
- 更新实体注解以包含购买数量的描述信息
2026-02-09 18:55:17 +08:00
2 changed files with 38 additions and 87 deletions

View File

@@ -55,6 +55,9 @@ public class GltUserTicket implements Serializable {
@Schema(description = "订单商品ID")
private Integer orderGoodsId;
@Schema(description = "订单商品数量")
private Integer orderGoodsQty;
@Schema(description = "总数量")
private Integer totalQty;

View File

@@ -3,7 +3,6 @@ package com.gxwebsoft.glt.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.gxwebsoft.glt.entity.GltTicketTemplate;
import com.gxwebsoft.glt.entity.GltTicketOrder;
import com.gxwebsoft.glt.entity.GltUserTicket;
import com.gxwebsoft.glt.entity.GltUserTicketLog;
import com.gxwebsoft.glt.entity.GltUserTicketRelease;
@@ -19,7 +18,6 @@ import org.springframework.transaction.support.TransactionTemplate;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
@@ -57,7 +55,6 @@ public class GltTicketIssueService {
private final GltUserTicketService gltUserTicketService;
private final GltUserTicketReleaseService gltUserTicketReleaseService;
private final GltUserTicketLogService gltUserTicketLogService;
private final GltTicketOrderService gltTicketOrderService;
private final TransactionTemplate transactionTemplate;
/**
@@ -220,38 +217,29 @@ public class GltTicketIssueService {
return IssueOutcome.ALREADY_ISSUED;
}
// water 套餐场景:订单表 totalNum 往往更可信(有些订单商品行会出现 totalNum != 订单总数的情况)
int rawBuyQty = og.getTotalNum() != null ? og.getTotalNum() : 0;
if (order.getTotalNum() != null && order.getTotalNum() > 0 && Objects.equals(order.getFormId(), og.getGoodsId())) {
rawBuyQty = order.getTotalNum();
}
if (rawBuyQty <= 0) {
int buyQty = og.getTotalNum() != null ? og.getTotalNum() : 0;
if (buyQty <= 0) {
log.warn("套票发放跳过:购买数量无效 - tenantId={}, orderNo={}, orderGoodsId={}, totalNum={}",
tenantId, order.getOrderNo(), og.getId(), og.getTotalNum());
return IssueOutcome.SKIPPED;
}
Integer minBuyQty = template.getMinBuyQty();
if (minBuyQty != null && minBuyQty > 0 && rawBuyQty < minBuyQty) {
if (minBuyQty != null && minBuyQty > 0 && buyQty < minBuyQty) {
log.info("套票发放跳过:未达到最小购买数量 - tenantId={}, orderNo={}, buyQty={}, minBuyQty={}",
tenantId, order.getOrderNo(), rawBuyQty, minBuyQty);
tenantId, order.getOrderNo(), buyQty, minBuyQty);
return IssueOutcome.SKIPPED;
}
int giftMultiplier = template.getGiftMultiplier() != null ? template.getGiftMultiplier() : 0;
int giftQty = rawBuyQty * Math.max(giftMultiplier, 0);
int giftQty = buyQty * Math.max(giftMultiplier, 0);
// 默认仅把“赠送量”计入水票账户;如需把购买量也纳入,设置 includeBuyQty=true
int buyQtyForTicket = Boolean.TRUE.equals(template.getIncludeBuyQty()) ? rawBuyQty : 0;
// 购买量buyQtyForTicket应立即可用赠送量giftQty进入冻结并按计划释放。
int availableQty = buyQtyForTicket;
int frozenQty = giftQty;
int totalQty = availableQty + frozenQty;
// 购买量buyQty应立即可用赠送量giftQty进入冻结并按计划释放
int totalQty = buyQty + giftQty;
if (totalQty <= 0) {
log.info("套票发放跳过计算结果为0 - tenantId={}, orderNo={}, buyQty={}, giftMultiplier={}, includeBuyQty={}",
tenantId, order.getOrderNo(), rawBuyQty, giftMultiplier, template.getIncludeBuyQty());
tenantId, order.getOrderNo(), buyQty, giftMultiplier, template.getIncludeBuyQty());
return IssueOutcome.SKIPPED;
}
@@ -264,11 +252,12 @@ public class GltTicketIssueService {
userTicket.setOrderNo(order.getOrderNo());
userTicket.setOrderGoodsId(og.getId());
userTicket.setTotalQty(totalQty);
userTicket.setAvailableQty(availableQty);
userTicket.setFrozenQty(frozenQty);
userTicket.setAvailableQty(buyQty);
userTicket.setFrozenQty(giftQty);
userTicket.setUsedQty(0);
// 已释放数量仅用于统计“从冻结释放到可用”的累计值初始为0释放任务会递增它
userTicket.setReleasedQty(0);
// 初始可用量来自“购买量”,视为已释放
userTicket.setReleasedQty(buyQty);
userTicket.setOrderGoodsQty(og.getTotalNum());
userTicket.setUserId(order.getUserId());
userTicket.setSortNumber(0);
userTicket.setComments("订单发放套票");
@@ -280,15 +269,25 @@ public class GltTicketIssueService {
gltUserTicketService.save(userTicket);
// 生成释放计划(按月)
LocalDateTime baseTime = order.getPayTime() != null ? order.getPayTime() : order.getCreateTime();
if (baseTime == null) {
baseTime = now;
}
List<GltUserTicketRelease> releases = buildReleasePlan(template, userTicket, baseTime, giftQty, now);
if (!releases.isEmpty()) {
gltUserTicketReleaseService.saveBatch(releases);
}
// 发放流水
GltUserTicketLog issueLog = new GltUserTicketLog();
issueLog.setUserTicketId(userTicket.getId());
issueLog.setChangeType(CHANGE_TYPE_ISSUE);
issueLog.setChangeAvailable(availableQty);
issueLog.setChangeFrozen(frozenQty);
issueLog.setChangeAvailable(buyQty);
issueLog.setChangeFrozen(giftQty);
issueLog.setChangeUsed(0);
issueLog.setAvailableAfter(availableQty);
issueLog.setFrozenAfter(frozenQty);
issueLog.setAvailableAfter(buyQty);
issueLog.setFrozenAfter(giftQty);
issueLog.setUsedAfter(0);
issueLog.setOrderId(order.getOrderId());
issueLog.setOrderNo(order.getOrderNo());
@@ -302,69 +301,31 @@ public class GltTicketIssueService {
issueLog.setUpdateTime(now);
gltUserTicketLogService.save(issueLog);
if (order.getTotalNum() != null && og.getTotalNum() != null && !Objects.equals(order.getTotalNum(), og.getTotalNum())) {
log.info("套票发放提示:订单总数量与订单商品数量不一致 - tenantId={}, orderNo={}, orderTotalNum={}, orderGoodsId={}, goodsId={}, orderGoodsTotalNum={}",
tenantId, order.getOrderNo(), order.getTotalNum(), og.getId(), og.getGoodsId(), og.getTotalNum());
}
// 按模板配置:自动“使用掉第一次水票”(起始送水数量)
Integer startSendQtyObj = template.getStartSendQty();
int startSendQty = startSendQtyObj != null ? startSendQtyObj : 0;
if (startSendQty > 0) {
int availableBefore = userTicket.getAvailableQty() != null ? userTicket.getAvailableQty() : 0;
int frozenBefore = userTicket.getFrozenQty() != null ? userTicket.getFrozenQty() : 0;
int usedBefore = userTicket.getUsedQty() != null ? userTicket.getUsedQty() : 0;
int toUse = Math.min(startSendQty, availableBefore + frozenBefore);
int toUse = Math.min(startSendQty, availableBefore);
if (toUse > 0) {
int useFromAvailable = Math.min(toUse, availableBefore);
int useFromFrozen = Math.min(toUse - useFromAvailable, frozenBefore);
userTicket.setAvailableQty(availableBefore - useFromAvailable);
userTicket.setFrozenQty(frozenBefore - useFromFrozen);
userTicket.setAvailableQty(availableBefore - toUse);
userTicket.setUsedQty(usedBefore + toUse);
userTicket.setUpdateTime(now);
if (!gltUserTicketService.updateById(userTicket)) {
throw new IllegalStateException("起始送水核销失败:更新用户水票失败 userTicketId=" + userTicket.getId());
}
// 同步生成一条“送水订单”(待配送),用于承载这次起始送水的核销。
// 这里不走 createWithWriteOff它会再次扣减水票而是复用本次已完成的扣减结果。
GltTicketOrder firstOrder = new GltTicketOrder();
firstOrder.setUserTicketId(userTicket.getId());
firstOrder.setStoreId(order.getStoreId());
firstOrder.setWarehouseId(order.getWarehouseId());
firstOrder.setAddressId(order.getAddressId());
firstOrder.setAddress(order.getAddress());
firstOrder.setBuyerRemarks(order.getBuyerRemarks());
firstOrder.setPrice(order.getPrice());
firstOrder.setTotalNum(toUse);
LocalDateTime sendTimeBase = order.getPayTime() != null ? order.getPayTime() : (order.getCreateTime() != null ? order.getCreateTime() : now);
firstOrder.setSendTime(sendTimeBase.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
firstOrder.setDeliveryStatus(GltTicketOrderService.DELIVERY_STATUS_WAITING);
firstOrder.setUserId(order.getUserId());
firstOrder.setSortNumber(0);
firstOrder.setComments("起始送水自动生成");
firstOrder.setStatus(0);
firstOrder.setDeleted(0);
firstOrder.setTenantId(tenantId);
firstOrder.setCreateTime(now);
firstOrder.setUpdateTime(now);
if (!gltTicketOrderService.save(firstOrder)) {
throw new IllegalStateException("起始送水核销失败:生成送水订单失败 userTicketId=" + userTicket.getId());
}
gltUserTicketService.updateById(userTicket);
GltUserTicketLog writeOffLog = new GltUserTicketLog();
writeOffLog.setUserTicketId(userTicket.getId());
writeOffLog.setChangeType(CHANGE_TYPE_START_SEND_WRITE_OFF);
writeOffLog.setChangeAvailable(-useFromAvailable);
writeOffLog.setChangeFrozen(-useFromFrozen);
writeOffLog.setChangeAvailable(-toUse);
writeOffLog.setChangeFrozen(0);
writeOffLog.setChangeUsed(toUse);
writeOffLog.setAvailableAfter(userTicket.getAvailableQty());
writeOffLog.setFrozenAfter(userTicket.getFrozenQty() != null ? userTicket.getFrozenQty() : 0);
writeOffLog.setUsedAfter(userTicket.getUsedQty());
// 关联本次生成的送水订单(与 createWithWriteOff 行为一致)
writeOffLog.setOrderId(firstOrder.getId());
writeOffLog.setOrderNo(firstOrder.getId() == null ? null : String.valueOf(firstOrder.getId()));
// 关联原始商城订单,便于追溯该次自动核销来源
writeOffLog.setOrderId(order.getOrderId());
writeOffLog.setOrderNo(order.getOrderNo());
writeOffLog.setUserId(order.getUserId());
writeOffLog.setSortNumber(0);
writeOffLog.setComments("起始送水自动核销");
@@ -377,19 +338,6 @@ public class GltTicketIssueService {
}
}
// 生成释放计划(按月):以“当前冻结量”为准(若起始送水消耗了冻结票,则相应减少释放计划)
int frozenToRelease = userTicket.getFrozenQty() != null ? userTicket.getFrozenQty() : 0;
if (frozenToRelease > 0) {
LocalDateTime baseTime = order.getPayTime() != null ? order.getPayTime() : order.getCreateTime();
if (baseTime == null) {
baseTime = now;
}
List<GltUserTicketRelease> releases = buildReleasePlan(template, userTicket, baseTime, frozenToRelease, now);
if (!releases.isEmpty()) {
gltUserTicketReleaseService.saveBatch(releases);
}
}
log.info("套票发放成功 - tenantId={}, orderNo={}, orderGoodsId={}, templateId={}, userTicketId={}, totalQty={}",
tenantId, order.getOrderNo(), og.getId(), template.getId(), userTicket.getId(), totalQty);