1.商品下单优化秒杀订单以秒杀价格为准

2.修改水票套票释放逻辑,个人水票发放以次月以1日凌晨为时间节点
3.增加以订单号形式发送水票套票信息
This commit is contained in:
1350250847@qq.com
2026-04-27 17:23:08 +08:00
parent 1ae7a76901
commit 818be01c7c
5 changed files with 124 additions and 13 deletions

View File

@@ -2,6 +2,7 @@ package com.gxwebsoft.glt.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.gxwebsoft.common.core.annotation.IgnoreTenant;
import com.gxwebsoft.glt.entity.GltTicketTemplate;
import com.gxwebsoft.glt.entity.GltUserTicket;
import com.gxwebsoft.glt.entity.GltUserTicketLog;
@@ -12,17 +13,18 @@ import com.gxwebsoft.shop.service.ShopOrderGoodsService;
import com.gxwebsoft.shop.service.ShopOrderService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
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;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* 套票发放(从订单生成用户套票 + 释放计划)的业务逻辑。
@@ -128,6 +130,57 @@ public class GltTicketIssueService {
tenantId, uniqueGoodsIds, orders.size(), success, skipped, failed);
}
@Async
// @Scheduled(cron = "0/1 * 4-22 * * ?") 没秒钟执行一次
public void paySuccessExecute(String orderNo, Integer tenantId){
suerTicketRelease(orderNo, tenantId);
}
/**
* 订单支付成功,直接发送水票【后期优化订单类型,为水票的订单才需要执行此业务】
* @param orderNo 订单号
* @param tenantId 租户ID
*/
public void suerTicketRelease(String orderNo, Integer tenantId){
//1.订单为空跳过执行
ShopOrder shopOrder = shopOrderService.getByOrderNo(orderNo, tenantId);
if(shopOrder == null){
return;
}
//2.跳过已完成发放套票订单
if(shopOrder.getOrderStatus() == 1){
return;
}
//3.订单商品为空跳过执行
List<ShopOrderGoods> goodsList = shopOrderGoodsService.getListByOrderIdIgnoreTenant(shopOrder.getOrderId());
if (CollectionUtils.isEmpty(goodsList)) {
return;
}
//4.执行水票发放业务【】
AtomicBoolean release = new AtomicBoolean(false);
goodsList.forEach(orderGood ->{
IssueOutcome outcome = transactionTemplate.execute(status -> doIssueOne(tenantId, shopOrder, orderGood));
if(Arrays.asList(IssueOutcome.ISSUED, IssueOutcome.ALREADY_ISSUED).contains(outcome)){
release.set(true);
}
});
//5.更新商品订单为已完成、已收到赠品状态
if (release.get()) {
shopOrderService.update(new LambdaUpdateWrapper<ShopOrder>()
.eq(ShopOrder::getOrderId, shopOrder.getOrderId())
.eq(ShopOrder::getTenantId, tenantId)
.eq(ShopOrder::getOrderStatus, 0)
.set(ShopOrder::getOrderStatus, 1)
.set(ShopOrder::getHasTakeGift, true)
.set(ShopOrder::getUpdateTime, LocalDateTime.now())
);
}
}
private int issueForOrder(Integer tenantId, Set<Integer> goodsIds, ShopOrder order) {
List<ShopOrderGoods> goodsList = shopOrderGoodsService.getListByOrderIdIgnoreTenant(order.getOrderId());
if (goodsList == null || goodsList.isEmpty()) {
@@ -304,12 +357,14 @@ public class GltTicketIssueService {
// 若启用了 releasePeriods 且首期释放时机为“支付成功当刻”,则将首期释放量直接计入可用,
// 避免用户刚购买后短时间内无可用水票;后续期数仍由自动释放任务按 release_time 释放。
if (useReleasePeriods && !releases.isEmpty() && !Objects.equals(template.getFirstReleaseMode(), 1)) {
// if (useReleasePeriods && !releases.isEmpty() && !Objects.equals(template.getFirstReleaseMode(), 1)) {
if (!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))) {
// if (firstQty > 0 && (firstTime == null || !firstTime.isAfter(now))) {
if (firstQty > 0) {
first.setStatus(1);
first.setUpdateTime(now);
@@ -376,10 +431,13 @@ public class GltTicketIssueService {
// 首期释放时间
LocalDateTime firstReleaseTime;
LocalDateTime referenceTime;
if (Objects.equals(template.getFirstReleaseMode(), 1)) {
firstReleaseTime = nextMonthSameDay(baseTime);
referenceTime = firstReleaseTime.withDayOfMonth(1).toLocalDate().atStartOfDay();
} else {
firstReleaseTime = baseTime;
referenceTime = firstReleaseTime.withDayOfMonth(1).toLocalDate().atStartOfDay();
}
// 每期释放数量计算
@@ -393,7 +451,11 @@ public class GltTicketIssueService {
if (qty <= 0) {
continue;
}
list.add(buildRelease(userTicket, i, qty, firstReleaseTime.plusMonths(i), now));
if(i == 0){
list.add(buildRelease(userTicket, i, qty, firstReleaseTime, now));
}else {
list.add(buildRelease(userTicket, i, qty, referenceTime.plusMonths(i), now));
}
}
return list;
}
@@ -410,7 +472,11 @@ public class GltTicketIssueService {
break;
}
remaining -= qty;
list.add(buildRelease(userTicket, i, qty, firstReleaseTime.plusMonths(i), now));
if(i == 0){
list.add(buildRelease(userTicket, i, qty, firstReleaseTime, now));
}else {
list.add(buildRelease(userTicket, i, qty, referenceTime.plusMonths(i), now));
}
}
return list;

View File

@@ -13,6 +13,7 @@ import com.gxwebsoft.common.core.utils.WechatCertAutoConfig;
import com.gxwebsoft.common.core.utils.WechatPayConfigValidator;
import com.gxwebsoft.common.core.web.BaseController;
import com.gxwebsoft.common.system.entity.Payment;
import com.gxwebsoft.glt.service.GltTicketIssueService;
import com.gxwebsoft.shop.entity.ShopOrderDelivery;
import com.gxwebsoft.shop.entity.ShopUserAddress;
import com.gxwebsoft.shop.service.*;
@@ -110,6 +111,8 @@ public class ShopOrderController extends BaseController {
private GltTicketRevokeService gltTicketRevokeService;
@Resource
private ShopDealerCommissionRollbackService shopDealerCommissionRollbackService;
@Resource
private GltTicketIssueService gltTicketIssueService;
@Operation(summary = "分页查询订单")
@GetMapping("/page")
@@ -763,7 +766,7 @@ public class ShopOrderController extends BaseController {
@Schema(description = "异步通知11")
@PostMapping("/notify/{tenantId}")
public String wxNotify(@RequestHeader Map<String, String> header, @RequestBody String body, @PathVariable("tenantId") Integer tenantId) {
logger.info("异步通知*************** = " + tenantId);
logger.info("异步通知*************** = " + body + ",租户:" +tenantId);
// 获取支付配置信息用于解密
String key = "Payment:1:".concat(tenantId.toString());
@@ -899,7 +902,7 @@ public class ShopOrderController extends BaseController {
logger.info("开始解析微信支付异步通知...");
Transaction transaction = parser.parse(requestParam, Transaction.class);
logger.info("✅ 异步通知解析成功 - 交易状态: {}, 商户订单号: {}",
transaction.getTradeStateDesc(), transaction.getOutTradeNo());
transaction.getTradeState(), transaction.getOutTradeNo());
// 使用枚举值判断支付状态,避免依赖状态描述字符串
if (Transaction.TradeStateEnum.SUCCESS.equals(transaction.getTradeState())) {
@@ -934,6 +937,10 @@ public class ShopOrderController extends BaseController {
System.out.println("实际付款金额 = " + order.getPayPrice());
// 更新订单状态并处理支付成功后的业务逻辑(包括累加商品销量)
shopOrderService.updateByOutTradeNo(order);
// //支付成功执行一步任务
// gltTicketIssueService.paySuccessExecute(order.getOrderNo(), tenantId);
return "SUCCESS";
}
}

View File

@@ -148,7 +148,7 @@ public class OrderCreateRequest {
private Integer tenantId;
@Schema(description = "秒杀活动ID")
private Long activityId;
private Integer activityId;
@Schema(description = "订单商品列表")
@Valid
@@ -161,6 +161,9 @@ public class OrderCreateRequest {
@Data
@Schema(name = "OrderGoodsItem", description = "订单商品项")
public static class OrderGoodsItem {
@Schema(description = "秒杀活动ID")
private Integer activityId;
@Schema(description = "商品ID", required = true)
@NotNull(message = "商品ID不能为空")
private Integer goodsId;

View File

@@ -3,10 +3,12 @@ package com.gxwebsoft.shop.service;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import com.gxwebsoft.common.core.exception.BusinessException;
import com.gxwebsoft.common.core.exception.enums.GlobalErrorCodeConstants;
import com.gxwebsoft.common.system.entity.User;
import com.gxwebsoft.shop.config.OrderConfigProperties;
import com.gxwebsoft.shop.dto.OrderCreateRequest;
import com.gxwebsoft.shop.entity.*;
import com.gxwebsoft.shop.mapper.ShopFlashSaleActivityMapper;
import com.gxwebsoft.shop.service.ShopStoreFenceService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
@@ -52,11 +54,16 @@ public class OrderBusinessService {
@Resource
private ShopUserAddressService shopUserAddressService;
@Resource
private ShopUserCouponService shopUserCouponService;
@Resource
private ShopStoreFenceService shopStoreFenceService;
@Resource
private ShopFlashSaleActivityMapper shopFlashSaleActivityMapper;
/**
* 创建订单
*
@@ -167,6 +174,8 @@ public class OrderBusinessService {
BigDecimal total = BigDecimal.ZERO;
for (OrderCreateRequest.OrderGoodsItem item : request.getGoodsItems()) {
Integer activityId = item.getActivityId();
// 验证商品ID
if (item.getGoodsId() == null) {
throw new BusinessException("商品ID不能为空");
@@ -216,6 +225,25 @@ public class OrderBusinessService {
productName = goods.getName() + "(" + (item.getSpecInfo() != null ? item.getSpecInfo() : sku.getSku()) + ")";
}
//秒杀商品价格以秒杀价为准
if(activityId != null){
request.setActivityId(activityId);
ShopFlashSaleActivity saleActivity = shopFlashSaleActivityMapper.selectById(activityId);
if(saleActivity == null){
throw new BusinessException("秒杀活动数据查询失败!");
}
if(saleActivity.getStatus() != 0){
throw new BusinessException("当前秒杀活动已失效!");
}
if(saleActivity.getStock() <= 0){
throw new BusinessException("当前秒杀活动商品已售罄!");
}
if(item.getQuantity() > saleActivity.getSaleLimit()){
throw new BusinessException("选购数量已超秒杀活动限购数量!");
}
actualPrice = saleActivity.getPrice();
}
// 验证实际价格
if (actualPrice == null || actualPrice.compareTo(BigDecimal.ZERO) <= 0) {
throw new BusinessException("商品价格异常:" + productName);
@@ -651,6 +679,13 @@ public class OrderBusinessService {
}
}
//秒杀商品价格以秒杀价为准
Integer activityId = item.getActivityId();
if(activityId != null){
ShopFlashSaleActivity saleActivity = shopFlashSaleActivityMapper.selectById(activityId);
actualPrice = saleActivity.getPrice();
}
// 验证库存
if (actualStock == null || actualStock < item.getQuantity()) {
String stockMsg = sku != null ? "商品规格库存不足" : "商品库存不足";

View File

@@ -9,7 +9,7 @@ spring:
datasource:
url: jdbc:mysql://47.107.249.41:13306/modules?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
username: modules
password: tYmmMGh5wpwXR3ae2
password: tYmmMGh5wpwXR3ae
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource