1.商品下单优化秒杀订单以秒杀价格为准
2.修改水票套票释放逻辑,个人水票发放以次月以1日凌晨为时间节点 3.增加以订单号形式发送水票套票信息
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 ? "商品规格库存不足" : "商品库存不足";
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user