feat(ticket): 实现起始送水自动核销并生成送水订单功能
- 在自动核销成功后创建送水订单用于配送端跟踪 - 添加对用户水票更新失败的异常处理机制 - 添加对送水订单创建失败的异常处理机制 - 更新水票日志关联到送水订单并保留来源商城订单号 - 实现地址快照功能,优先使用地址表数据并兼容旧数据格式 - 添加安全字符串处理方法防止空指针异常 - 集成 ShopUserAddressService 和 GltTicketOrderService 服务
This commit is contained in:
@@ -2,22 +2,27 @@ package com.gxwebsoft.glt.service;
|
|||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||||
|
import com.gxwebsoft.glt.entity.GltTicketOrder;
|
||||||
import com.gxwebsoft.glt.entity.GltTicketTemplate;
|
import com.gxwebsoft.glt.entity.GltTicketTemplate;
|
||||||
import com.gxwebsoft.glt.entity.GltUserTicket;
|
import com.gxwebsoft.glt.entity.GltUserTicket;
|
||||||
import com.gxwebsoft.glt.entity.GltUserTicketLog;
|
import com.gxwebsoft.glt.entity.GltUserTicketLog;
|
||||||
import com.gxwebsoft.glt.entity.GltUserTicketRelease;
|
import com.gxwebsoft.glt.entity.GltUserTicketRelease;
|
||||||
import com.gxwebsoft.shop.entity.ShopOrder;
|
import com.gxwebsoft.shop.entity.ShopOrder;
|
||||||
import com.gxwebsoft.shop.entity.ShopOrderGoods;
|
import com.gxwebsoft.shop.entity.ShopOrderGoods;
|
||||||
|
import com.gxwebsoft.shop.entity.ShopUserAddress;
|
||||||
import com.gxwebsoft.shop.service.ShopOrderGoodsService;
|
import com.gxwebsoft.shop.service.ShopOrderGoodsService;
|
||||||
import com.gxwebsoft.shop.service.ShopOrderService;
|
import com.gxwebsoft.shop.service.ShopOrderService;
|
||||||
|
import com.gxwebsoft.shop.service.ShopUserAddressService;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.support.TransactionTemplate;
|
import org.springframework.transaction.support.TransactionTemplate;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.LocalTime;
|
import java.time.LocalTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -55,6 +60,8 @@ public class GltTicketIssueService {
|
|||||||
private final GltUserTicketService gltUserTicketService;
|
private final GltUserTicketService gltUserTicketService;
|
||||||
private final GltUserTicketReleaseService gltUserTicketReleaseService;
|
private final GltUserTicketReleaseService gltUserTicketReleaseService;
|
||||||
private final GltUserTicketLogService gltUserTicketLogService;
|
private final GltUserTicketLogService gltUserTicketLogService;
|
||||||
|
private final GltTicketOrderService gltTicketOrderService;
|
||||||
|
private final ShopUserAddressService shopUserAddressService;
|
||||||
private final TransactionTemplate transactionTemplate;
|
private final TransactionTemplate transactionTemplate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -312,7 +319,15 @@ public class GltTicketIssueService {
|
|||||||
userTicket.setAvailableQty(availableBefore - toUse);
|
userTicket.setAvailableQty(availableBefore - toUse);
|
||||||
userTicket.setUsedQty(usedBefore + toUse);
|
userTicket.setUsedQty(usedBefore + toUse);
|
||||||
userTicket.setUpdateTime(now);
|
userTicket.setUpdateTime(now);
|
||||||
gltUserTicketService.updateById(userTicket);
|
if (!gltUserTicketService.updateById(userTicket)) {
|
||||||
|
throw new IllegalStateException("起始送水自动核销:更新用户水票失败 userTicketId=" + userTicket.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 起始送水:自动核销成功后,生成一条送水订单(用于配送端/后台跟踪)
|
||||||
|
GltTicketOrder ticketOrder = buildStartSendTicketOrder(tenantId, order, userTicket, toUse, now);
|
||||||
|
if (!gltTicketOrderService.save(ticketOrder)) {
|
||||||
|
throw new IllegalStateException("起始送水自动核销:创建送水订单失败 userTicketId=" + userTicket.getId());
|
||||||
|
}
|
||||||
|
|
||||||
GltUserTicketLog writeOffLog = new GltUserTicketLog();
|
GltUserTicketLog writeOffLog = new GltUserTicketLog();
|
||||||
writeOffLog.setUserTicketId(userTicket.getId());
|
writeOffLog.setUserTicketId(userTicket.getId());
|
||||||
@@ -323,12 +338,12 @@ public class GltTicketIssueService {
|
|||||||
writeOffLog.setAvailableAfter(userTicket.getAvailableQty());
|
writeOffLog.setAvailableAfter(userTicket.getAvailableQty());
|
||||||
writeOffLog.setFrozenAfter(userTicket.getFrozenQty() != null ? userTicket.getFrozenQty() : 0);
|
writeOffLog.setFrozenAfter(userTicket.getFrozenQty() != null ? userTicket.getFrozenQty() : 0);
|
||||||
writeOffLog.setUsedAfter(userTicket.getUsedQty());
|
writeOffLog.setUsedAfter(userTicket.getUsedQty());
|
||||||
// 关联原始商城订单,便于追溯该次自动核销来源
|
// 关联送水订单(保持与“用户下单核销”的日志一致),并在备注里保留来源商城订单号便于追溯
|
||||||
writeOffLog.setOrderId(order.getOrderId());
|
writeOffLog.setOrderId(ticketOrder.getId());
|
||||||
writeOffLog.setOrderNo(order.getOrderNo());
|
writeOffLog.setOrderNo(ticketOrder.getId() == null ? null : String.valueOf(ticketOrder.getId()));
|
||||||
writeOffLog.setUserId(order.getUserId());
|
writeOffLog.setUserId(order.getUserId());
|
||||||
writeOffLog.setSortNumber(0);
|
writeOffLog.setSortNumber(0);
|
||||||
writeOffLog.setComments("起始送水自动核销");
|
writeOffLog.setComments("起始送水自动核销(来源商城订单:" + safe(order.getOrderNo()) + ")");
|
||||||
writeOffLog.setStatus(0);
|
writeOffLog.setStatus(0);
|
||||||
writeOffLog.setDeleted(0);
|
writeOffLog.setDeleted(0);
|
||||||
writeOffLog.setTenantId(tenantId);
|
writeOffLog.setTenantId(tenantId);
|
||||||
@@ -423,4 +438,78 @@ public class GltTicketIssueService {
|
|||||||
LocalDate adjusted = nextMonth.withDayOfMonth(day);
|
LocalDate adjusted = nextMonth.withDayOfMonth(day);
|
||||||
return LocalDateTime.of(adjusted, time);
|
return LocalDateTime.of(adjusted, time);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final DateTimeFormatter DATETIME_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||||
|
|
||||||
|
private GltTicketOrder buildStartSendTicketOrder(Integer tenantId,
|
||||||
|
ShopOrder shopOrder,
|
||||||
|
GltUserTicket userTicket,
|
||||||
|
int totalNum,
|
||||||
|
LocalDateTime now) {
|
||||||
|
GltTicketOrder o = new GltTicketOrder();
|
||||||
|
o.setUserTicketId(userTicket.getId());
|
||||||
|
o.setUserId(shopOrder.getUserId());
|
||||||
|
o.setStoreId(shopOrder.getStoreId());
|
||||||
|
o.setWarehouseId(shopOrder.getWarehouseId());
|
||||||
|
o.setRiderId(shopOrder.getRiderId());
|
||||||
|
o.setTotalNum(totalNum);
|
||||||
|
o.setPrice(BigDecimal.ZERO);
|
||||||
|
o.setBuyerRemarks(shopOrder.getBuyerRemarks());
|
||||||
|
|
||||||
|
// 地址快照:优先使用地址表快照;兜底使用商城订单上的 address 字段
|
||||||
|
Integer addressId = shopOrder.getAddressId();
|
||||||
|
o.setAddressId(addressId);
|
||||||
|
String addressSnapshot = null;
|
||||||
|
if (addressId != null) {
|
||||||
|
ShopUserAddress addr = shopUserAddressService.getOne(new LambdaQueryWrapper<ShopUserAddress>()
|
||||||
|
.eq(ShopUserAddress::getId, addressId)
|
||||||
|
.eq(ShopUserAddress::getUserId, shopOrder.getUserId())
|
||||||
|
.eq(ShopUserAddress::getTenantId, tenantId)
|
||||||
|
.last("limit 1"));
|
||||||
|
addressSnapshot = buildAddressSnapshot(addr);
|
||||||
|
}
|
||||||
|
if (addressSnapshot == null || addressSnapshot.isBlank()) {
|
||||||
|
addressSnapshot = shopOrder.getAddress();
|
||||||
|
}
|
||||||
|
o.setAddress(addressSnapshot);
|
||||||
|
|
||||||
|
String preferredSendTime = shopOrder.getSendStartTime();
|
||||||
|
if (preferredSendTime == null || preferredSendTime.isBlank()) {
|
||||||
|
preferredSendTime = now.format(DATETIME_FMT);
|
||||||
|
}
|
||||||
|
o.setSendTime(preferredSendTime);
|
||||||
|
o.setDeliveryStatus(GltTicketOrderService.DELIVERY_STATUS_WAITING);
|
||||||
|
o.setSortNumber(0);
|
||||||
|
o.setComments("起始送水自动下单(来源商城订单:" + safe(shopOrder.getOrderNo()) + ")");
|
||||||
|
o.setStatus(0);
|
||||||
|
o.setDeleted(0);
|
||||||
|
o.setTenantId(tenantId);
|
||||||
|
o.setCreateTime(now);
|
||||||
|
o.setUpdateTime(now);
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String buildAddressSnapshot(ShopUserAddress addr) {
|
||||||
|
if (addr == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (addr.getFullAddress() != null && !addr.getFullAddress().isBlank()) {
|
||||||
|
return addr.getFullAddress();
|
||||||
|
}
|
||||||
|
// 兼容旧数据:fullAddress 为空时,拼接省市区 + 详细地址
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
if (addr.getProvince() != null) sb.append(addr.getProvince());
|
||||||
|
if (addr.getCity() != null) sb.append(addr.getCity());
|
||||||
|
if (addr.getRegion() != null) sb.append(addr.getRegion());
|
||||||
|
if (addr.getAddress() != null) sb.append(addr.getAddress());
|
||||||
|
String s = sb.toString();
|
||||||
|
if (!s.isBlank()) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
return addr.getAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String safe(String s) {
|
||||||
|
return s == null ? "" : s;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user