From 818be01c7c3aabf83dbb5497553b3433459e2071 Mon Sep 17 00:00:00 2001 From: "1350250847@qq.com" Date: Mon, 27 Apr 2026 17:23:08 +0800 Subject: [PATCH] =?UTF-8?q?1.=E5=95=86=E5=93=81=E4=B8=8B=E5=8D=95=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E7=A7=92=E6=9D=80=E8=AE=A2=E5=8D=95=E4=BB=A5=E7=A7=92?= =?UTF-8?q?=E6=9D=80=E4=BB=B7=E6=A0=BC=E4=B8=BA=E5=87=86=202.=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E6=B0=B4=E7=A5=A8=E5=A5=97=E7=A5=A8=E9=87=8A=E6=94=BE?= =?UTF-8?q?=E9=80=BB=E8=BE=91=EF=BC=8C=E4=B8=AA=E4=BA=BA=E6=B0=B4=E7=A5=A8?= =?UTF-8?q?=E5=8F=91=E6=94=BE=E4=BB=A5=E6=AC=A1=E6=9C=88=E4=BB=A51?= =?UTF-8?q?=E6=97=A5=E5=87=8C=E6=99=A8=E4=B8=BA=E6=97=B6=E9=97=B4=E8=8A=82?= =?UTF-8?q?=E7=82=B9=203.=E5=A2=9E=E5=8A=A0=E4=BB=A5=E8=AE=A2=E5=8D=95?= =?UTF-8?q?=E5=8F=B7=E5=BD=A2=E5=BC=8F=E5=8F=91=E9=80=81=E6=B0=B4=E7=A5=A8?= =?UTF-8?q?=E5=A5=97=E7=A5=A8=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../glt/service/GltTicketIssueService.java | 84 +++++++++++++++++-- .../shop/controller/ShopOrderController.java | 11 ++- .../shop/dto/OrderCreateRequest.java | 5 +- .../shop/service/OrderBusinessService.java | 35 ++++++++ src/main/resources/application-dev.yml | 2 +- 5 files changed, 124 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/gxwebsoft/glt/service/GltTicketIssueService.java b/src/main/java/com/gxwebsoft/glt/service/GltTicketIssueService.java index 5b4ccae..75c20c8 100644 --- a/src/main/java/com/gxwebsoft/glt/service/GltTicketIssueService.java +++ b/src/main/java/com/gxwebsoft/glt/service/GltTicketIssueService.java @@ -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 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() + .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 goodsIds, ShopOrder order) { List 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; diff --git a/src/main/java/com/gxwebsoft/shop/controller/ShopOrderController.java b/src/main/java/com/gxwebsoft/shop/controller/ShopOrderController.java index d156858..2b241e0 100644 --- a/src/main/java/com/gxwebsoft/shop/controller/ShopOrderController.java +++ b/src/main/java/com/gxwebsoft/shop/controller/ShopOrderController.java @@ -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 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"; } } diff --git a/src/main/java/com/gxwebsoft/shop/dto/OrderCreateRequest.java b/src/main/java/com/gxwebsoft/shop/dto/OrderCreateRequest.java index 3553a59..07455ef 100644 --- a/src/main/java/com/gxwebsoft/shop/dto/OrderCreateRequest.java +++ b/src/main/java/com/gxwebsoft/shop/dto/OrderCreateRequest.java @@ -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; diff --git a/src/main/java/com/gxwebsoft/shop/service/OrderBusinessService.java b/src/main/java/com/gxwebsoft/shop/service/OrderBusinessService.java index ed8b43c..1e9a1b9 100644 --- a/src/main/java/com/gxwebsoft/shop/service/OrderBusinessService.java +++ b/src/main/java/com/gxwebsoft/shop/service/OrderBusinessService.java @@ -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 ? "商品规格库存不足" : "商品库存不足"; diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 34972e5..7c3a897 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -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