From 6ba8b4fa00ad3635488605ffd4af909f8e849370 Mon Sep 17 00:00:00 2001 From: zjf <1799650030@qq.com> Date: Sun, 22 Feb 2026 18:28:34 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=9B=9E=E8=B0=83=E9=97=AE?= =?UTF-8?q?=E9=A2=98=EF=BC=8C=E9=80=9A=E9=81=93=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/channel/ChannelServiceImpl.java | 4 +- .../order/TradeOrderUpdateServiceImpl.java | 105 +++++++++++------- .../order/bo/DispenseResultStatusEnum.java | 2 +- .../handler/TradeDeviceOrderHandler.java | 48 +++++--- .../handler/TradeGiftVoucherOrderHandler.java | 10 +- 5 files changed, 104 insertions(+), 65 deletions(-) diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/channel/ChannelServiceImpl.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/channel/ChannelServiceImpl.java index f13ca5b..3456512 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/channel/ChannelServiceImpl.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/channel/ChannelServiceImpl.java @@ -406,14 +406,14 @@ public class ChannelServiceImpl implements ChannelService { throw exception0(1_008_015_001, "出货失败!原因: " + body.getMsg()); } } else { - deviceLog.setDeviceStatus(501); + deviceLog.setDeviceStatus(99); deviceLog.setContent("操作失败!原因: 请求出货响应为空"); productDeviceLogService.createDeviceLog(deviceLog); throw exception(CHANNEL_ERROR); } /*} catch (Exception e) { System.err.println("请求异常: " + e.getMessage()); - deviceLog.setDeviceStatus(500); + deviceLog.setDeviceStatus(99); deviceLog.setContent("请求异常: " + e.getMessage()); productDeviceLogService.createDeviceLog(deviceLog); throw exception0(1_008_016_001, "请求异常: " + e.getMessage()); diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java index 43655fa..4c3a595 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java @@ -30,7 +30,7 @@ import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; -import cn.iocoder.yudao.module.product.enums.sku.ProductSkuPurchaseBizTypeEnum; +import cn.iocoder.yudao.module.product.enums.spu.PurchaseLimitTypeEnum; import cn.iocoder.yudao.module.promotion.api.giftvoucher.GiftVoucherApi; import cn.iocoder.yudao.module.promotion.api.giftvoucher.dto.GiftVoucherRespDTO; import cn.iocoder.yudao.module.system.api.social.SocialClientApi; @@ -78,12 +78,9 @@ import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import javax.validation.constraints.NotNull; -import java.time.LocalDate; import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.Set; +import java.time.temporal.TemporalAdjusters; +import java.util.*; import java.util.stream.Collectors; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; @@ -152,47 +149,73 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { // =================== Order =================== - public List findUserSuccessOrderItem(Long payUserId, List skuIdList, ProductSkuPurchaseBizTypeEnum status) { - List statusList = new ArrayList<>(); - statusList.add(TradeOrderStatusEnum.UNPAID.getStatus()); - statusList.add(TradeOrderStatusEnum.UNDELIVERED.getStatus()); - statusList.add(TradeOrderStatusEnum.DELIVERED.getStatus()); - statusList.add(TradeOrderStatusEnum.COMPLETED.getStatus()); - // 查询用户订单 - LambdaQueryWrapperX orderQuery = new LambdaQueryWrapperX().eq(TradeOrderDO::getUserId, payUserId).in(TradeOrderDO::getStatus, statusList); - // 限购一天一次 - if (ObjectUtil.equals(status, ProductSkuPurchaseBizTypeEnum.ONCE_A_DAY)) { - orderQuery.between(TradeOrderDO::getCreateTime, LocalDate.now(), LocalDate.now().plusDays(1)); - } - List orderList = tradeOrderMapper.selectList(orderQuery); - if (ObjectUtil.isEmpty(orderList)) return null; - List orderIds = orderList.stream().map(TradeOrderDO::getId).collect(Collectors.toList()); - // 查询订单项 - List orderItemList = tradeOrderItemMapper.selectList(new LambdaQueryWrapperX() - .eq(TradeOrderItemDO::getUserId, payUserId) - .in(TradeOrderItemDO::getOrderId, orderIds) - ); - if (ObjectUtil.isEmpty(orderItemList)) return null; - orderItemList = orderItemList.stream().filter(v -> skuIdList.contains(v.getSkuId())).collect(Collectors.toList()); - return orderItemList; - } - /** - * 判断sku是否限购 + * 结算阶段限购预校验(按 SPU 限购规则) */ @Override public void isSkuPurchaseLimit(Long payUserId, List skuIdList) { List skuList = productSkuApi.getSkuList(skuIdList); - // 检查每天限购一次 - ProductSkuPurchaseBizTypeEnum onceADayStatus = ProductSkuPurchaseBizTypeEnum.ONCE_A_DAY; - List onceADay = skuList.stream().filter((v) -> ObjectUtil.equals(v.getPurchaseBizType(), onceADayStatus.getStatus())).collect(Collectors.toList()); - if (ObjectUtil.isNotEmpty(onceADay)) { - List targetIdList = onceADay.stream().map(ProductSkuRespDTO::getId).collect(Collectors.toList()); - List targetSkuList = findUserSuccessOrderItem(payUserId, targetIdList, onceADayStatus); - if (ObjectUtil.isNotEmpty(targetSkuList)) { - String spuName = targetSkuList.stream().map(TradeOrderItemDO::getSpuName).collect(Collectors.joining()); - throw exception(ORDER_RECEIVE_FAIL_STATUS_PURCHASE, "【" + spuName + "】", onceADayStatus.getName()); + if (CollUtil.isEmpty(skuList)) { + return; + } + + // 本次请求每个 SPU 的购买数量(按 skuList 聚合;结算页通常每条 sku 一项) + Map currentPurchaseBySpu = new HashMap<>(); + for (ProductSkuRespDTO sku : skuList) { + if (sku == null || sku.getSpuId() == null) { + continue; } + currentPurchaseBySpu.merge(sku.getSpuId(), 1, Integer::sum); + } + if (currentPurchaseBySpu.isEmpty()) { + return; + } + + List spuList = productSpuApi.getSpuList(new ArrayList<>(currentPurchaseBySpu.keySet())); + if (CollUtil.isEmpty(spuList)) { + return; + } + + for (ProductSpuRespDTO spu : spuList) { + if (spu == null || spu.getId() == null) { + continue; + } + Integer limitType = spu.getPurchaseLimitType(); + Integer limitCount = spu.getPurchaseLimitCount(); + if (limitType == null || Objects.equals(limitType, PurchaseLimitTypeEnum.NONE.getType()) + || limitCount == null || limitCount <= 0) { + continue; // 不限购 + } + + PurchaseLimitTypeEnum limitTypeEnum = PurchaseLimitTypeEnum.valueOf(limitType); + if (limitTypeEnum == null) { + continue; + } + LocalDateTime startTime = getStartTimeByLimitType(limitTypeEnum); + if (startTime == null) { + startTime = LocalDateTime.of(2000, 1, 1, 0, 0); + } + + int historyPurchaseCount = tradeOrderItemMapper.selectPurchaseCountByUserAndSpu(payUserId, spu.getId(), startTime); + int currentPurchaseCount = currentPurchaseBySpu.getOrDefault(spu.getId(), 0); + if (historyPurchaseCount + currentPurchaseCount > limitCount) { + throw exception(ORDER_CREATE_PURCHASE_LIMIT_EXCEED, spu.getName(), limitTypeEnum.getName(), limitCount); + } + } + } + + private LocalDateTime getStartTimeByLimitType(PurchaseLimitTypeEnum limitType) { + LocalDateTime now = LocalDateTime.now(); + switch (limitType) { + case DAILY: + return now.toLocalDate().atStartOfDay(); + case WEEKLY: + return now.with(TemporalAdjusters.previousOrSame(java.time.DayOfWeek.MONDAY)) + .toLocalDate().atStartOfDay(); + case LIFETIME: + return null; + default: + return null; } } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/bo/DispenseResultStatusEnum.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/bo/DispenseResultStatusEnum.java index 5e18766..4ed38a4 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/bo/DispenseResultStatusEnum.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/bo/DispenseResultStatusEnum.java @@ -22,7 +22,7 @@ public enum DispenseResultStatusEnum { CHANNEL_ERROR(6, "通道号错误"), CHANNEL_TYPE_ERROR(7, "通道类型错误"), /** 系统/业务异常(如网络异常、参数错误等) */ - EXCEPTION(500, "出货异常"), + EXCEPTION(99, "出货异常"), ; private final int value; diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeDeviceOrderHandler.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeDeviceOrderHandler.java index 956aa11..09fee4a 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeDeviceOrderHandler.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeDeviceOrderHandler.java @@ -107,7 +107,7 @@ public class TradeDeviceOrderHandler implements TradeOrderHandler { } catch (Throwable ex) { // 兜底保护:这里不再向上抛异常,仅记录日志,避免影响支付回调流程 log.error("订单--{}:[设备出货处理异常]:原因: {}", order.getNo(), ex.getMessage(), ex); - deviceLogSaveReqVO.setDeviceStatus(500); + deviceLogSaveReqVO.setDeviceStatus(99); deviceLogSaveReqVO.setContent("[出货异常]原因:" + ex.getMessage()); deviceLogService.createDeviceLog(deviceLogSaveReqVO); } @@ -137,7 +137,7 @@ public class TradeDeviceOrderHandler implements TradeOrderHandler { List spuList = productSpuApi.getSpuList(spuIds); if (CollUtil.isEmpty(spuList)) { log.error("订单:{}, 提货柜订单spu列表为空", order.getNo()); - deviceLogSaveReqVO.setDeviceStatus(500); + deviceLogSaveReqVO.setDeviceStatus(99); deviceLogSaveReqVO.setContent("[出货异常]原因:提货柜订单spu列表为空"); deviceLogService.createDeviceLog(deviceLogSaveReqVO); return; @@ -148,6 +148,8 @@ public class TradeDeviceOrderHandler implements TradeOrderHandler { log.error("订单:{}, 中包含非提货柜商品,跳过设备出货处理", order.getNo()); return; } + Map spuNameMap = spuList.stream() + .collect(Collectors.toMap(ProductSpuRespDTO::getId, ProductSpuRespDTO::getName, (a, b) -> a)); String deviceId = findUniqueDeviceId(order, spuList, deviceLogSaveReqVO); if (deviceId == null) { @@ -160,13 +162,13 @@ public class TradeDeviceOrderHandler implements TradeOrderHandler { if (CollUtil.isEmpty(skuListBySpu)) { log.warn("订单:{} 商品下无货道(SKU),跳过设备出货处理", order.getNo()); deviceLogSaveReqVO.setDeviceId(deviceId); - deviceLogSaveReqVO.setDeviceStatus(500); + deviceLogSaveReqVO.setDeviceStatus(99); deviceLogSaveReqVO.setContent("[出货异常]原因:商品下无货道(SKU)"); deviceLogService.createDeviceLog(deviceLogSaveReqVO); return; } - String cabinetType = findUniqueCabinetType(order, skuListBySpu, deviceId, deviceLogSaveReqVO); + String cabinetType = findUniqueCabinetType(order, skuListBySpu, spuNameMap, deviceId, deviceLogSaveReqVO); if (cabinetType == null) { // 已在方法内部记录日志 return; @@ -176,7 +178,7 @@ public class TradeDeviceOrderHandler implements TradeOrderHandler { DispenseRequestBo request = buildDispenseRequest(order, orderItems, deviceId, cabinetType); if (request == null) { deviceLogSaveReqVO.setDeviceId(deviceId); - deviceLogSaveReqVO.setDeviceStatus(500); + deviceLogSaveReqVO.setDeviceStatus(99); deviceLogSaveReqVO.setContent("[出货异常]原因:存在商品无可用货道(有库存且通道类型匹配)"); deviceLogService.createDeviceLog(deviceLogSaveReqVO); return; @@ -213,7 +215,7 @@ public class TradeDeviceOrderHandler implements TradeOrderHandler { .count() == 1; if (!isSingleDevice) { log.error("订单:{}, 明细中包含多个设备的商品", order.getNo()); - deviceLogSaveReqVO.setDeviceStatus(500); + deviceLogSaveReqVO.setDeviceStatus(99); deviceLogSaveReqVO.setContent("[出货异常]原因:订单明细中包含多台贩卖机商品"); deviceLogService.createDeviceLog(deviceLogSaveReqVO); return null; @@ -223,7 +225,7 @@ public class TradeDeviceOrderHandler implements TradeOrderHandler { if (deviceId == null) { // 设备编号为空,无法出货 log.error("订单:{}, 商品的设备编号为空", order.getNo()); - deviceLogSaveReqVO.setDeviceStatus(500); + deviceLogSaveReqVO.setDeviceStatus(99); deviceLogSaveReqVO.setContent("[出货异常]原因:商品的设备编号为空"); deviceLogService.createDeviceLog(deviceLogSaveReqVO); return null; @@ -236,6 +238,7 @@ public class TradeDeviceOrderHandler implements TradeOrderHandler { */ private String findUniqueCabinetType(TradeOrderDO order, List skuList, + Map spuNameMap, String deviceId, DeviceLogSaveReqVO deviceLogSaveReqVO) { boolean isSingleCabinetType = skuList.stream() @@ -243,15 +246,28 @@ public class TradeDeviceOrderHandler implements TradeOrderHandler { .distinct() .count() == 1; if (!isSingleCabinetType) { - log.error("订单:{}, 明细中包含多个通道类型", order.getNo()); - deviceLogSaveReqVO.setDeviceId(deviceId); - deviceLogSaveReqVO.setDeviceStatus(500); - deviceLogSaveReqVO.setContent("[出货异常]原因:订单明细中包含多个通道类型"); - deviceLogService.createDeviceLog(deviceLogSaveReqVO); - return null; + // 临时放开校验:不再因“多个通道类型”中断出货流程,仅记录告警日志便于排查 + log.warn("订单:{}, 明细中包含多个通道类型,已跳过该校验并继续出货", order.getNo()); + Map> skuByCabinetType = skuList.stream() + .collect(Collectors.groupingBy(sku -> StrUtil.blankToDefault(sku.getCabinetType(), ""))); + skuByCabinetType.forEach((type, skus) -> { + String details = skus.stream() + .map(sku -> String.format("spuId=%s,spuName=%s,skuId=%s,cabinetType=%s,cabinetChannel=%s", + sku.getSpuId(), + spuNameMap.getOrDefault(sku.getSpuId(), ""), + sku.getId(), + sku.getCabinetType(), + sku.getCabinetChannel())) + .collect(Collectors.joining(" | ")); + log.warn("订单:{}, 通道类型分组[{}] -> {}", order.getNo(), type, details); + }); } - return skuList.get(0).getCabinetType(); + return skuList.stream() + .map(ProductSkuRespDTO::getCabinetType) + .filter(StrUtil::isNotBlank) + .findFirst() + .orElse(skuList.get(0).getCabinetType()); } /** @@ -334,7 +350,7 @@ public class TradeDeviceOrderHandler implements TradeOrderHandler { if (body == null) { log.error("订单--{}:[出货失败]:响应体为空", order.getNo()); deviceLogSaveReqVO.setDeviceId(request.getDeviceId()); - deviceLogSaveReqVO.setDeviceStatus(500); + deviceLogSaveReqVO.setDeviceStatus(99); deviceLogSaveReqVO.setContent("[出货失败]原因:响应为空"); deviceLogService.createDeviceLog(deviceLogSaveReqVO); return; @@ -357,7 +373,7 @@ public class TradeDeviceOrderHandler implements TradeOrderHandler { // HTTP 请求异常 log.error("订单--{}:[出货异常]:原因: {}", order.getNo(), e.getMessage(), e); deviceLogSaveReqVO.setDeviceId(request.getDeviceId()); - deviceLogSaveReqVO.setDeviceStatus(500); + deviceLogSaveReqVO.setDeviceStatus(99); deviceLogSaveReqVO.setContent("[出货异常]原因:" + e.getMessage()); deviceLogService.createDeviceLog(deviceLogSaveReqVO); } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeGiftVoucherOrderHandler.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeGiftVoucherOrderHandler.java index 1f9db40..843cf78 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeGiftVoucherOrderHandler.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeGiftVoucherOrderHandler.java @@ -117,7 +117,7 @@ public class TradeGiftVoucherOrderHandler implements TradeOrderHandler { if (deviceId == null){ //imei为空 log.error("订单:{},商品的设备编号为空",order.getNo()); - deviceLogSaveReqVO.setDeviceStatus(500); + deviceLogSaveReqVO.setDeviceStatus(99); deviceLogSaveReqVO.setContent("[出货异常]原因:商品的设备编号为空"); deviceLogService.createDeviceLog(deviceLogSaveReqVO); return; @@ -203,7 +203,7 @@ public class TradeGiftVoucherOrderHandler implements TradeOrderHandler { System.err.println("请求异常: " + e.getMessage()); log.error("订单--{}:[出货异常]:原因: {}",order.getNo(), e.getMessage()); deviceLogSaveReqVO.setDeviceId(deviceId); - deviceLogSaveReqVO.setDeviceStatus(500); + deviceLogSaveReqVO.setDeviceStatus(99); deviceLogSaveReqVO.setContent("[出货异常]原因:"+e.getMessage()); deviceLogService.createDeviceLog(deviceLogSaveReqVO); } @@ -211,14 +211,14 @@ public class TradeGiftVoucherOrderHandler implements TradeOrderHandler { System.out.println("订单明细中包含多个通道类型"); log.error("订单:{},明细中包含多个通道类型",order.getNo()); deviceLogSaveReqVO.setDeviceId(deviceId); - deviceLogSaveReqVO.setDeviceStatus(500); + deviceLogSaveReqVO.setDeviceStatus(99); deviceLogSaveReqVO.setContent("[出货异常]原因:订单明细中包含多个通道类型"); deviceLogService.createDeviceLog(deviceLogSaveReqVO); } } else { System.out.println("订单明细中包含多个商品创建人"); log.error("订单:{},明细中包含多个商品创建人",order.getNo()); - deviceLogSaveReqVO.setDeviceStatus(500); + deviceLogSaveReqVO.setDeviceStatus(99); deviceLogSaveReqVO.setContent("[出货异常]原因:订单明细中包含多台贩卖机商品"); deviceLogService.createDeviceLog(deviceLogSaveReqVO); } @@ -226,7 +226,7 @@ public class TradeGiftVoucherOrderHandler implements TradeOrderHandler { }else { System.out.println("提货柜订单spu列表为空"); log.error("订单:{},提货柜订单spu列表为空",order.getNo()); - deviceLogSaveReqVO.setDeviceStatus(500); + deviceLogSaveReqVO.setDeviceStatus(99); deviceLogSaveReqVO.setContent("[出货异常]原因:提货柜订单spu列表为空"); deviceLogService.createDeviceLog(deviceLogSaveReqVO); }