From 19fae4bd00c3e9b53a9ba9efb0cd6d1b679abbac Mon Sep 17 00:00:00 2001 From: Jerry Yan <792602257@qq.com> Date: Fri, 14 Nov 2025 09:10:06 +0800 Subject: [PATCH] =?UTF-8?q?feat(pricing):=20=E5=AE=9E=E7=8E=B0=E9=A6=96?= =?UTF-8?q?=E6=AC=A1=E6=89=93=E5=8D=B0=E8=87=AA=E5=8A=A8=E5=8F=91=E5=88=B8?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增自动发券服务接口 IAutoCouponService- 实现自动发券逻辑,包括参数校验、优惠券配置查询和发券记录检查 - 在打印服务中集成自动发券调用,确保首次打印时触发发券- 添加异常处理,避免发券失败影响主流程 - 支持通过优惠券名称和商品类型匹配规则查找目标优惠券 --- .../pricing/service/IAutoCouponService.java | 21 ++++ .../service/impl/AutoCouponServiceImpl.java | 118 ++++++++++++++++++ .../printer/impl/PrinterServiceImpl.java | 18 +++ 3 files changed, 157 insertions(+) create mode 100644 src/main/java/com/ycwl/basic/pricing/service/IAutoCouponService.java create mode 100644 src/main/java/com/ycwl/basic/pricing/service/impl/AutoCouponServiceImpl.java diff --git a/src/main/java/com/ycwl/basic/pricing/service/IAutoCouponService.java b/src/main/java/com/ycwl/basic/pricing/service/IAutoCouponService.java new file mode 100644 index 00000000..c51ceed4 --- /dev/null +++ b/src/main/java/com/ycwl/basic/pricing/service/IAutoCouponService.java @@ -0,0 +1,21 @@ +package com.ycwl.basic.pricing.service; + +import com.ycwl.basic.pricing.enums.ProductType; + +/** + * 自动发券服务接口 + * 负责在特定场景下自动为用户发放优惠券 + */ +public interface IAutoCouponService { + + /** + * 自动为用户发放首次打印优惠券 + * + * @param memberId 用户ID (member_id) + * @param faceId 人脸ID (face_id) + * @param scenicId 景区ID + * @param productType 商品类型 + * @return 是否成功发券 + */ + boolean autoGrantFirstPrintCoupon(Long memberId, Long faceId, Long scenicId, ProductType productType); +} diff --git a/src/main/java/com/ycwl/basic/pricing/service/impl/AutoCouponServiceImpl.java b/src/main/java/com/ycwl/basic/pricing/service/impl/AutoCouponServiceImpl.java new file mode 100644 index 00000000..d2592700 --- /dev/null +++ b/src/main/java/com/ycwl/basic/pricing/service/impl/AutoCouponServiceImpl.java @@ -0,0 +1,118 @@ +package com.ycwl.basic.pricing.service.impl; + +import com.ycwl.basic.pricing.dto.CouponClaimRequest; +import com.ycwl.basic.pricing.entity.PriceCouponClaimRecord; +import com.ycwl.basic.pricing.entity.PriceCouponConfig; +import com.ycwl.basic.pricing.enums.ProductType; +import com.ycwl.basic.pricing.mapper.PriceCouponClaimRecordMapper; +import com.ycwl.basic.pricing.mapper.PriceCouponConfigMapper; +import com.ycwl.basic.pricing.service.IAutoCouponService; +import com.ycwl.basic.pricing.service.ICouponService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 自动发券服务实现 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class AutoCouponServiceImpl implements IAutoCouponService { + + private final PriceCouponConfigMapper couponConfigMapper; + private final PriceCouponClaimRecordMapper couponClaimRecordMapper; + private final ICouponService couponService; + + @Override + public boolean autoGrantFirstPrintCoupon(Long memberId, Long faceId, Long scenicId, ProductType productType) { + try { + // 1. 校验参数 + if (memberId == null || faceId == null || scenicId == null || productType == null) { + log.warn("自动发券参数不完整: memberId={}, faceId={}, scenicId={}, productType={}", + memberId, faceId, scenicId, productType); + return false; + } + + // 2. 查找该景区、该商品类型的首次打印优惠券配置 + Long couponId = findFirstPrintCouponId(scenicId, productType); + if (couponId == null) { + log.debug("景区未配置首次打印优惠券: scenicId={}, productType={}", scenicId, productType); + return false; + } + + // 3. 检查用户是否已领取过该券(领券即消耗首次资格) + PriceCouponClaimRecord existingRecord = couponClaimRecordMapper.selectUserCouponRecord( + memberId, + couponId + ); + + if (existingRecord != null) { + log.debug("用户已领取过首次优惠券,不重复发券: memberId={}, couponId={}, claimTime={}", + memberId, couponId, existingRecord.getClaimTime()); + return false; + } + + // 4. 自动发券 + CouponClaimRequest request = new CouponClaimRequest( + memberId, + couponId, + scenicId.toString(), + "AUTO_FIRST_PRINT" // 标记为自动发券来源 + ); + + couponService.claimCoupon(request); + + log.info("成功自动发放首次打印优惠券: memberId={}, faceId={}, scenicId={}, productType={}, couponId={}", + memberId, faceId, scenicId, productType, couponId); + + return true; + + } catch (Exception e) { + log.error("自动发券失败: memberId={}, faceId={}, scenicId={}, productType={}", + memberId, faceId, scenicId, productType, e); + return false; + } + } + + /** + * 查找指定景区、指定商品类型的首次打印优惠券ID + * 规则:优惠券名称包含 "首次" 且 适用商品类型包含目标类型 + * + * @param scenicId 景区ID + * @param productType 商品类型 + * @return 优惠券ID,未找到返回null + */ + private Long findFirstPrintCouponId(Long scenicId, ProductType productType) { + try { + // 查询该景区的有效优惠券 + List coupons = couponConfigMapper.selectValidCouponsByScenicId( + scenicId.toString() + ); + + for (PriceCouponConfig coupon : coupons) { + // 检查优惠券名称是否包含"首次"关键字 + if (coupon.getCouponName() != null && + (coupon.getCouponName().contains("首次") || + coupon.getCouponName().contains("first"))) { + + // 检查适用商品类型 + String applicableProducts = coupon.getApplicableProducts(); + if (applicableProducts != null && + applicableProducts.contains(productType.getCode())) { + return coupon.getId(); + } + } + } + + log.debug("未找到匹配的首次打印优惠券: scenicId={}, productType={}", scenicId, productType); + return null; + + } catch (Exception e) { + log.error("查找首次打印优惠券失败: scenicId={}, productType={}", scenicId, productType, e); + return null; + } + } +} diff --git a/src/main/java/com/ycwl/basic/service/printer/impl/PrinterServiceImpl.java b/src/main/java/com/ycwl/basic/service/printer/impl/PrinterServiceImpl.java index bc215339..b8971c30 100644 --- a/src/main/java/com/ycwl/basic/service/printer/impl/PrinterServiceImpl.java +++ b/src/main/java/com/ycwl/basic/service/printer/impl/PrinterServiceImpl.java @@ -33,6 +33,7 @@ import com.ycwl.basic.pricing.dto.PriceCalculationRequest; import com.ycwl.basic.pricing.dto.PriceCalculationResult; import com.ycwl.basic.pricing.dto.ProductItem; import com.ycwl.basic.pricing.enums.ProductType; +import com.ycwl.basic.pricing.service.IAutoCouponService; import com.ycwl.basic.pricing.service.IPriceCalculationService; import com.ycwl.basic.model.pc.printer.entity.MemberPrintEntity; import com.ycwl.basic.model.pc.printer.entity.PrintTaskEntity; @@ -109,6 +110,8 @@ public class PrinterServiceImpl implements PrinterService { @Autowired private IPriceCalculationService priceCalculationService; @Autowired + private IAutoCouponService autoCouponService; + @Autowired private ScenicRepository scenicRepository; @Autowired private OrderRepository orderRepository; @@ -586,6 +589,21 @@ public class PrinterServiceImpl implements PrinterService { throw new BaseException("没有可打印的照片"); } + // 【新增】检测并自动发放首次打印优惠券 + if (mobileCount > 0) { + try { + autoCouponService.autoGrantFirstPrintCoupon( + memberId, + faceId, + scenicId, + ProductType.PHOTO_PRINT_MU + ); + } catch (Exception e) { + log.warn("自动发券失败,不影响下单流程: memberId={}, faceId={}, scenicId={}, error={}", + memberId, faceId, scenicId, e.getMessage()); + } + } + OrderEntity order = new OrderEntity(); Long orderId = SnowFlakeUtil.getLongId(); redisTemplate.opsForValue().set("printer_size:"+orderId, printer.getPreferPaper(), 60, TimeUnit.SECONDS);