From 661aa4567fa37c085909d2d3a78c148c6c03f265 Mon Sep 17 00:00:00 2001 From: Jerry Yan <792602257@qq.com> Date: Fri, 14 Nov 2025 01:08:12 +0800 Subject: [PATCH 1/4] =?UTF-8?q?feat(print):=20=E6=94=AF=E6=8C=81=E5=A4=9A?= =?UTF-8?q?=E7=A7=8D=E7=85=A7=E7=89=87=E6=89=93=E5=8D=B0=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E7=9A=84=E4=BB=B7=E6=A0=BC=E8=AE=A1=E7=AE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增手机照片打印(PHOTO_PRINT_MU)和特效照片打印(PHOTO_PRINT_FX)枚举类型 - 在价格计算服务中增加isPrintProduct方法统一判断打印类商品 - 修改订单服务跳过打印类商品重复购买检查逻辑 -优化打印机服务根据sourceId分类统计不同照片类型数量 - 分别计算普通、手机、特效照片打印的价格和数量- 更新价格计算逻辑以支持多种打印类型商品项 --- .../order/service/impl/OrderServiceImpl.java | 4 +- .../ycwl/basic/pricing/enums/ProductType.java | 2 + .../impl/PriceCalculationServiceImpl.java | 31 ++-- .../printer/impl/PrinterServiceImpl.java | 155 +++++++++++++++--- 4 files changed, 157 insertions(+), 35 deletions(-) diff --git a/src/main/java/com/ycwl/basic/order/service/impl/OrderServiceImpl.java b/src/main/java/com/ycwl/basic/order/service/impl/OrderServiceImpl.java index 11f83a7f..f2fdb9cb 100644 --- a/src/main/java/com/ycwl/basic/order/service/impl/OrderServiceImpl.java +++ b/src/main/java/com/ycwl/basic/order/service/impl/OrderServiceImpl.java @@ -912,9 +912,11 @@ public class OrderServiceImpl implements IOrderService { checkSetAlreadyPurchased(userId, faceId, scenicId, product.getProductType()); break; case PHOTO_PRINT: + case PHOTO_PRINT_MU: + case PHOTO_PRINT_FX: case MACHINE_PRINT: // 打印类商品允许重复购买,跳过检查 - log.debug("跳过打印类商品重复购买检查: productType={}, productId={}", + log.debug("跳过打印类商品重复购买检查: productType={}, productId={}", product.getProductType(), product.getProductId()); break; default: diff --git a/src/main/java/com/ycwl/basic/pricing/enums/ProductType.java b/src/main/java/com/ycwl/basic/pricing/enums/ProductType.java index bc3aa789..8f072514 100644 --- a/src/main/java/com/ycwl/basic/pricing/enums/ProductType.java +++ b/src/main/java/com/ycwl/basic/pricing/enums/ProductType.java @@ -14,6 +14,8 @@ public enum ProductType { RECORDING_SET("RECORDING_SET", "录像集"), PHOTO_SET("PHOTO_SET", "照相集"), PHOTO_PRINT("PHOTO_PRINT", "照片打印"), + PHOTO_PRINT_MU("PHOTO_PRINT_MU", "手机照片打印"), + PHOTO_PRINT_FX("PHOTO_PRINT_FX", "特效照片打印"), MACHINE_PRINT("MACHINE_PRINT", "一体机打印"); private final String code; diff --git a/src/main/java/com/ycwl/basic/pricing/service/impl/PriceCalculationServiceImpl.java b/src/main/java/com/ycwl/basic/pricing/service/impl/PriceCalculationServiceImpl.java index cbfee913..2b1231a0 100644 --- a/src/main/java/com/ycwl/basic/pricing/service/impl/PriceCalculationServiceImpl.java +++ b/src/main/java/com/ycwl/basic/pricing/service/impl/PriceCalculationServiceImpl.java @@ -29,7 +29,18 @@ public class PriceCalculationServiceImpl implements IPriceCalculationService { private final IPriceBundleService bundleService; private final IDiscountDetectionService discountDetectionService; private final IVoucherService voucherService; - + + /** + * 判断是否为打印类商品 + * 打印类商品的价格计算方式为:单价 × 数量 + */ + private boolean isPrintProduct(ProductType productType) { + return productType == ProductType.PHOTO_PRINT + || productType == ProductType.PHOTO_PRINT_MU + || productType == ProductType.PHOTO_PRINT_FX + || productType == ProductType.MACHINE_PRINT; + } + @Override public PriceCalculationResult calculatePrice(PriceCalculationRequest request) { if (request.getProducts() == null || request.getProducts().isEmpty()) { @@ -190,7 +201,7 @@ public class PriceCalculationServiceImpl implements IPriceCalculationService { try { PriceProductConfig baseConfig = productConfigService.getProductConfig(productType.getCode(), productId); if (baseConfig != null) { - if (productType == ProductType.PHOTO_PRINT || productType == ProductType.MACHINE_PRINT) { + if (isPrintProduct(productType)) { return baseConfig.getBasePrice().multiply(BigDecimal.valueOf(product.getQuantity())); } else { return baseConfig.getBasePrice(); @@ -205,7 +216,7 @@ public class PriceCalculationServiceImpl implements IPriceCalculationService { try { PriceProductConfig defaultConfig = productConfigService.getProductConfig(productType.getCode(), "default"); if (defaultConfig != null) { - if (productType == ProductType.PHOTO_PRINT || productType == ProductType.MACHINE_PRINT) { + if (isPrintProduct(productType)) { return defaultConfig.getBasePrice().multiply(BigDecimal.valueOf(product.getQuantity())); } else { return defaultConfig.getBasePrice(); @@ -219,7 +230,7 @@ public class PriceCalculationServiceImpl implements IPriceCalculationService { List configs = productConfigService.getProductConfig(productType.getCode()); if (!configs.isEmpty()) { PriceProductConfig baseConfig = configs.get(0); // 使用第一个配置作为默认 - if (productType == ProductType.PHOTO_PRINT || productType == ProductType.MACHINE_PRINT) { + if (isPrintProduct(productType)) { return baseConfig.getBasePrice().multiply(BigDecimal.valueOf(product.getQuantity())); } else { return baseConfig.getBasePrice(); @@ -252,8 +263,8 @@ public class PriceCalculationServiceImpl implements IPriceCalculationService { if (baseConfig != null) { actualPrice = baseConfig.getBasePrice(); originalPrice = baseConfig.getOriginalPrice(); - - if (productType == ProductType.PHOTO_PRINT || productType == ProductType.MACHINE_PRINT) { + + if (isPrintProduct(productType)) { actualPrice = actualPrice.multiply(BigDecimal.valueOf(product.getQuantity())); if (originalPrice != null) { originalPrice = originalPrice.multiply(BigDecimal.valueOf(product.getQuantity())); @@ -272,8 +283,8 @@ public class PriceCalculationServiceImpl implements IPriceCalculationService { if (defaultConfig != null) { actualPrice = defaultConfig.getBasePrice(); originalPrice = defaultConfig.getOriginalPrice(); - - if (productType == ProductType.PHOTO_PRINT || productType == ProductType.MACHINE_PRINT) { + + if (isPrintProduct(productType)) { actualPrice = actualPrice.multiply(BigDecimal.valueOf(product.getQuantity())); if (originalPrice != null) { originalPrice = originalPrice.multiply(BigDecimal.valueOf(product.getQuantity())); @@ -291,8 +302,8 @@ public class PriceCalculationServiceImpl implements IPriceCalculationService { PriceProductConfig baseConfig = configs.getFirst(); // 使用第一个配置作为默认 actualPrice = baseConfig.getBasePrice(); originalPrice = baseConfig.getOriginalPrice(); - - if (productType == ProductType.PHOTO_PRINT || productType == ProductType.MACHINE_PRINT) { + + if (isPrintProduct(productType)) { actualPrice = actualPrice.multiply(BigDecimal.valueOf(product.getQuantity())); if (originalPrice != null) { originalPrice = originalPrice.multiply(BigDecimal.valueOf(product.getQuantity())); 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 e37ce810..bc215339 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 @@ -354,37 +354,89 @@ public class PrinterServiceImpl implements PrinterService { @Override public PriceObj queryPrice(Long memberId, Long scenicId, Long faceId) { List userPhotoList = getUserPhotoList(memberId, scenicId, faceId); - // 计算照片总数量 - long count = userPhotoList.stream() - .filter(item -> Objects.nonNull(item.getQuantity())) - .mapToInt(MemberPrintResp::getQuantity) - .sum(); + PriceObj obj = new PriceObj(); obj.setScenicId(scenicId); obj.setGoodsId(faceId); obj.setFaceId(faceId); obj.setGoodsType(3); - if (count == 0) { + // 按照 sourceId 分类照片 + // sourceId > 0: 普通照片打印 (PHOTO_PRINT) + // sourceId == null: 手机照片打印 (PHOTO_PRINT_MU) + // sourceId == 0: 特效照片打印 (PHOTO_PRINT_FX) + long normalCount = userPhotoList.stream() + .filter(item -> Objects.nonNull(item.getQuantity()) + && item.getSourceId() != null && item.getSourceId() > 0) + .mapToInt(MemberPrintResp::getQuantity) + .sum(); + + long mobileCount = userPhotoList.stream() + .filter(item -> Objects.nonNull(item.getQuantity()) + && item.getSourceId() == null) + .mapToInt(MemberPrintResp::getQuantity) + .sum(); + + long effectCount = userPhotoList.stream() + .filter(item -> Objects.nonNull(item.getQuantity()) + && item.getSourceId() != null && item.getSourceId() == 0) + .mapToInt(MemberPrintResp::getQuantity) + .sum(); + + long totalCount = normalCount + mobileCount + effectCount; + + if (totalCount == 0) { // 如果没有照片,返回零价格 obj.setPrice(BigDecimal.ZERO); obj.setSlashPrice(BigDecimal.ZERO); obj.setFree(false); return obj; } + // 构建价格计算请求 PriceCalculationRequest request = new PriceCalculationRequest(); request.setUserId(memberId); - // 创建照片打印商品项 - ProductItem photoItem = new ProductItem(); - photoItem.setProductType(ProductType.PHOTO_PRINT); - photoItem.setProductId(scenicId.toString()); - photoItem.setQuantity(Long.valueOf(count).intValue()); - photoItem.setPurchaseCount(1); - photoItem.setScenicId(scenicId.toString()); + // 创建商品项列表 + List productItems = new ArrayList<>(); - request.setProducts(Collections.singletonList(photoItem)); + // 添加普通照片打印商品项 (sourceId > 0) + if (normalCount > 0) { + ProductItem normalPhotoItem = new ProductItem(); + normalPhotoItem.setProductType(ProductType.PHOTO_PRINT); + normalPhotoItem.setProductId(scenicId.toString()); + normalPhotoItem.setQuantity(Long.valueOf(normalCount).intValue()); + normalPhotoItem.setPurchaseCount(1); + normalPhotoItem.setScenicId(scenicId.toString()); + productItems.add(normalPhotoItem); + log.debug("普通照片打印数量: {}", normalCount); + } + + // 添加手机照片打印商品项 (sourceId == null) + if (mobileCount > 0) { + ProductItem mobilePhotoItem = new ProductItem(); + mobilePhotoItem.setProductType(ProductType.PHOTO_PRINT_MU); + mobilePhotoItem.setProductId(scenicId.toString()); + mobilePhotoItem.setQuantity(Long.valueOf(mobileCount).intValue()); + mobilePhotoItem.setPurchaseCount(1); + mobilePhotoItem.setScenicId(scenicId.toString()); + productItems.add(mobilePhotoItem); + log.debug("手机照片打印数量: {}", mobileCount); + } + + // 添加特效照片打印商品项 (sourceId == 0) + if (effectCount > 0) { + ProductItem effectPhotoItem = new ProductItem(); + effectPhotoItem.setProductType(ProductType.PHOTO_PRINT_FX); + effectPhotoItem.setProductId(scenicId.toString()); + effectPhotoItem.setQuantity(Long.valueOf(effectCount).intValue()); + effectPhotoItem.setPurchaseCount(1); + effectPhotoItem.setScenicId(scenicId.toString()); + productItems.add(effectPhotoItem); + log.debug("特效照片打印数量: {}", effectCount); + } + + request.setProducts(productItems); // 使用统一价格计算服务 PriceCalculationResult result = priceCalculationService.calculatePrice(request); @@ -505,8 +557,32 @@ public class PrinterServiceImpl implements PrinterService { } // 验证照片数量 List userPhotoList = getUserPhotoList(memberId, scenicId, faceId); - long count = userPhotoList.stream().filter(item -> Objects.nonNull(item.getQuantity())).mapToInt(MemberPrintResp::getQuantity).sum(); - if (count == 0) { + + // 按照 sourceId 分类照片 + // sourceId > 0: 普通照片打印 (PHOTO_PRINT) + // sourceId == null: 手机照片打印 (PHOTO_PRINT_MU) + // sourceId == 0: 特效照片打印 (PHOTO_PRINT_FX) + long normalCount = userPhotoList.stream() + .filter(item -> Objects.nonNull(item.getQuantity()) + && item.getSourceId() != null && item.getSourceId() > 0) + .mapToInt(MemberPrintResp::getQuantity) + .sum(); + + long mobileCount = userPhotoList.stream() + .filter(item -> Objects.nonNull(item.getQuantity()) + && item.getSourceId() == null) + .mapToInt(MemberPrintResp::getQuantity) + .sum(); + + long effectCount = userPhotoList.stream() + .filter(item -> Objects.nonNull(item.getQuantity()) + && item.getSourceId() != null && item.getSourceId() == 0) + .mapToInt(MemberPrintResp::getQuantity) + .sum(); + + long totalCount = normalCount + mobileCount + effectCount; + + if (totalCount == 0) { throw new BaseException("没有可打印的照片"); } @@ -536,15 +612,46 @@ public class PrinterServiceImpl implements PrinterService { request.setUserId(memberId); request.setScenicId(scenicId); - // 创建照片打印商品项 - ProductItem photoItem = new ProductItem(); - photoItem.setProductType(ProductType.PHOTO_PRINT); - photoItem.setProductId(scenicId.toString()); - photoItem.setQuantity(Long.valueOf(count).intValue()); - photoItem.setPurchaseCount(1); - photoItem.setScenicId(scenicId.toString()); + // 创建商品项列表 + List productItems = new ArrayList<>(); - request.setProducts(Collections.singletonList(photoItem)); + // 添加普通照片打印商品项 (sourceId > 0) + if (normalCount > 0) { + ProductItem normalPhotoItem = new ProductItem(); + normalPhotoItem.setProductType(ProductType.PHOTO_PRINT); + normalPhotoItem.setProductId(scenicId.toString()); + normalPhotoItem.setQuantity(Long.valueOf(normalCount).intValue()); + normalPhotoItem.setPurchaseCount(1); + normalPhotoItem.setScenicId(scenicId.toString()); + productItems.add(normalPhotoItem); + log.debug("创建订单-普通照片打印数量: {}", normalCount); + } + + // 添加手机照片打印商品项 (sourceId == null) + if (mobileCount > 0) { + ProductItem mobilePhotoItem = new ProductItem(); + mobilePhotoItem.setProductType(ProductType.PHOTO_PRINT_MU); + mobilePhotoItem.setProductId(scenicId.toString()); + mobilePhotoItem.setQuantity(Long.valueOf(mobileCount).intValue()); + mobilePhotoItem.setPurchaseCount(1); + mobilePhotoItem.setScenicId(scenicId.toString()); + productItems.add(mobilePhotoItem); + log.debug("创建订单-手机照片打印数量: {}", mobileCount); + } + + // 添加特效照片打印商品项 (sourceId == 0) + if (effectCount > 0) { + ProductItem effectPhotoItem = new ProductItem(); + effectPhotoItem.setProductType(ProductType.PHOTO_PRINT_FX); + effectPhotoItem.setProductId(scenicId.toString()); + effectPhotoItem.setQuantity(Long.valueOf(effectCount).intValue()); + effectPhotoItem.setPurchaseCount(1); + effectPhotoItem.setScenicId(scenicId.toString()); + productItems.add(effectPhotoItem); + log.debug("创建订单-特效照片打印数量: {}", effectCount); + } + + request.setProducts(productItems); PriceCalculationResult priceResult = priceCalculationService.calculatePrice(request); 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 2/4] =?UTF-8?q?feat(pricing):=20=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E9=A6=96=E6=AC=A1=E6=89=93=E5=8D=B0=E8=87=AA=E5=8A=A8=E5=8F=91?= =?UTF-8?q?=E5=88=B8=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); From 932081abf09d849a85a90652f256b99ed3dea171 Mon Sep 17 00:00:00 2001 From: Jerry Yan <792602257@qq.com> Date: Sat, 15 Nov 2025 14:28:56 +0800 Subject: [PATCH 3/4] =?UTF-8?q?refactor(pricing):=20=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E5=8F=91=E5=88=B8=E6=9C=8D=E5=8A=A1=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E5=91=BD=E5=90=8D=E5=8F=8A=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将 autoGrantFirstPrintCoupon 方法重命名为 autoGrantCoupon - 修改 findFirstPrintCouponId 方法名为 findFirstCouponId - 调整优惠券名称匹配逻辑,移除对"first"关键字的检查 - 更新调用方 PrinterServiceImpl 中的方法引用 - 优化自动发券异常处理,确保不影响主流程 --- .../pricing/service/IAutoCouponService.java | 2 +- .../service/impl/AutoCouponServiceImpl.java | 12 ++++---- .../printer/impl/PrinterServiceImpl.java | 29 +++++++++---------- 3 files changed, 20 insertions(+), 23 deletions(-) diff --git a/src/main/java/com/ycwl/basic/pricing/service/IAutoCouponService.java b/src/main/java/com/ycwl/basic/pricing/service/IAutoCouponService.java index c51ceed4..7df0c95f 100644 --- a/src/main/java/com/ycwl/basic/pricing/service/IAutoCouponService.java +++ b/src/main/java/com/ycwl/basic/pricing/service/IAutoCouponService.java @@ -17,5 +17,5 @@ public interface IAutoCouponService { * @param productType 商品类型 * @return 是否成功发券 */ - boolean autoGrantFirstPrintCoupon(Long memberId, Long faceId, Long scenicId, ProductType productType); + boolean autoGrantCoupon(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 index d2592700..f44d5889 100644 --- a/src/main/java/com/ycwl/basic/pricing/service/impl/AutoCouponServiceImpl.java +++ b/src/main/java/com/ycwl/basic/pricing/service/impl/AutoCouponServiceImpl.java @@ -27,7 +27,7 @@ public class AutoCouponServiceImpl implements IAutoCouponService { private final ICouponService couponService; @Override - public boolean autoGrantFirstPrintCoupon(Long memberId, Long faceId, Long scenicId, ProductType productType) { + public boolean autoGrantCoupon(Long memberId, Long faceId, Long scenicId, ProductType productType) { try { // 1. 校验参数 if (memberId == null || faceId == null || scenicId == null || productType == null) { @@ -37,7 +37,7 @@ public class AutoCouponServiceImpl implements IAutoCouponService { } // 2. 查找该景区、该商品类型的首次打印优惠券配置 - Long couponId = findFirstPrintCouponId(scenicId, productType); + Long couponId = findFirstCouponId(scenicId, productType); if (couponId == null) { log.debug("景区未配置首次打印优惠券: scenicId={}, productType={}", scenicId, productType); return false; @@ -60,7 +60,7 @@ public class AutoCouponServiceImpl implements IAutoCouponService { memberId, couponId, scenicId.toString(), - "AUTO_FIRST_PRINT" // 标记为自动发券来源 + "AUTO_GRANT" // 标记为自动发券来源 ); couponService.claimCoupon(request); @@ -85,7 +85,7 @@ public class AutoCouponServiceImpl implements IAutoCouponService { * @param productType 商品类型 * @return 优惠券ID,未找到返回null */ - private Long findFirstPrintCouponId(Long scenicId, ProductType productType) { + private Long findFirstCouponId(Long scenicId, ProductType productType) { try { // 查询该景区的有效优惠券 List coupons = couponConfigMapper.selectValidCouponsByScenicId( @@ -94,9 +94,7 @@ public class AutoCouponServiceImpl implements IAutoCouponService { for (PriceCouponConfig coupon : coupons) { // 检查优惠券名称是否包含"首次"关键字 - if (coupon.getCouponName() != null && - (coupon.getCouponName().contains("首次") || - coupon.getCouponName().contains("first"))) { + if (coupon.getCouponName() != null && (coupon.getCouponName().contains("首次"))) { // 检查适用商品类型 String applicableProducts = coupon.getApplicableProducts(); 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 b8971c30..090ac1b9 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 @@ -441,6 +441,20 @@ public class PrinterServiceImpl implements PrinterService { request.setProducts(productItems); + if (mobileCount > 0) { + try { + autoCouponService.autoGrantCoupon( + memberId, + faceId, + scenicId, + ProductType.PHOTO_PRINT_MU + ); + } catch (Exception e) { + log.warn("自动发券失败,不影响下单流程: memberId={}, faceId={}, scenicId={}, error={}", + memberId, faceId, scenicId, e.getMessage()); + } + } + // 使用统一价格计算服务 PriceCalculationResult result = priceCalculationService.calculatePrice(request); @@ -589,21 +603,6 @@ 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); From a1b0687526f745ead7ad396fa42ba623ee8d950b Mon Sep 17 00:00:00 2001 From: Jerry Yan <792602257@qq.com> Date: Sat, 15 Nov 2025 14:52:53 +0800 Subject: [PATCH 4/4] =?UTF-8?q?feat(pricing):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=8A=98=E6=89=A3=E4=BC=98=E5=85=88=E7=BA=A7=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E5=B9=B6=E4=BC=98=E5=8C=96=E4=BB=B7=E6=A0=BC=E8=AE=A1=E7=AE=97?= =?UTF-8?q?=E8=AF=B7=E6=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在折扣信息中增加优先级字段,提升折扣策略的灵活性 - 为价格计算请求默认启用自动使用优惠券功能 - 设置价格预览模式为非预览状态,确保实际计算准确性 - 完善打印服务中的价格计算逻辑,提高系统稳定性 --- .../pricing/service/impl/OnePricePurchaseDiscountProvider.java | 1 + .../ycwl/basic/service/printer/impl/PrinterServiceImpl.java | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/main/java/com/ycwl/basic/pricing/service/impl/OnePricePurchaseDiscountProvider.java b/src/main/java/com/ycwl/basic/pricing/service/impl/OnePricePurchaseDiscountProvider.java index 1c069a6a..ed7f0b5b 100644 --- a/src/main/java/com/ycwl/basic/pricing/service/impl/OnePricePurchaseDiscountProvider.java +++ b/src/main/java/com/ycwl/basic/pricing/service/impl/OnePricePurchaseDiscountProvider.java @@ -83,6 +83,7 @@ public class OnePricePurchaseDiscountProvider implements IDiscountProvider { discountInfo.setDiscountAmount(onePriceInfo.getActualDiscountAmount()); discountInfo.setDiscountDescription("景区一口价购买,价格更优惠"); discountInfo.setOnePriceInfo(onePriceInfo); + discountInfo.setPriority(getPriority()); discounts.add(discountInfo); 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 090ac1b9..94b16a28 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 @@ -454,6 +454,7 @@ public class PrinterServiceImpl implements PrinterService { memberId, faceId, scenicId, e.getMessage()); } } + request.setAutoUseCoupon(true); // 使用统一价格计算服务 PriceCalculationResult result = priceCalculationService.calculatePrice(request); @@ -669,6 +670,8 @@ public class PrinterServiceImpl implements PrinterService { } request.setProducts(productItems); + request.setAutoUseCoupon(true); + request.setPreviewOnly(false); PriceCalculationResult priceResult = priceCalculationService.calculatePrice(request);