You've already forked FrameTour-BE
feat(pricing): 实现首次打印自动发券功能
- 新增自动发券服务接口 IAutoCouponService- 实现自动发券逻辑,包括参数校验、优惠券配置查询和发券记录检查 - 在打印服务中集成自动发券调用,确保首次打印时触发发券- 添加异常处理,避免发券失败影响主流程 - 支持通过优惠券名称和商品类型匹配规则查找目标优惠券
This commit is contained in:
@@ -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);
|
||||||
|
}
|
||||||
@@ -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<PriceCouponConfig> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,6 +33,7 @@ import com.ycwl.basic.pricing.dto.PriceCalculationRequest;
|
|||||||
import com.ycwl.basic.pricing.dto.PriceCalculationResult;
|
import com.ycwl.basic.pricing.dto.PriceCalculationResult;
|
||||||
import com.ycwl.basic.pricing.dto.ProductItem;
|
import com.ycwl.basic.pricing.dto.ProductItem;
|
||||||
import com.ycwl.basic.pricing.enums.ProductType;
|
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.pricing.service.IPriceCalculationService;
|
||||||
import com.ycwl.basic.model.pc.printer.entity.MemberPrintEntity;
|
import com.ycwl.basic.model.pc.printer.entity.MemberPrintEntity;
|
||||||
import com.ycwl.basic.model.pc.printer.entity.PrintTaskEntity;
|
import com.ycwl.basic.model.pc.printer.entity.PrintTaskEntity;
|
||||||
@@ -109,6 +110,8 @@ public class PrinterServiceImpl implements PrinterService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private IPriceCalculationService priceCalculationService;
|
private IPriceCalculationService priceCalculationService;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
private IAutoCouponService autoCouponService;
|
||||||
|
@Autowired
|
||||||
private ScenicRepository scenicRepository;
|
private ScenicRepository scenicRepository;
|
||||||
@Autowired
|
@Autowired
|
||||||
private OrderRepository orderRepository;
|
private OrderRepository orderRepository;
|
||||||
@@ -586,6 +589,21 @@ public class PrinterServiceImpl implements PrinterService {
|
|||||||
throw new BaseException("没有可打印的照片");
|
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();
|
OrderEntity order = new OrderEntity();
|
||||||
Long orderId = SnowFlakeUtil.getLongId();
|
Long orderId = SnowFlakeUtil.getLongId();
|
||||||
redisTemplate.opsForValue().set("printer_size:"+orderId, printer.getPreferPaper(), 60, TimeUnit.SECONDS);
|
redisTemplate.opsForValue().set("printer_size:"+orderId, printer.getPreferPaper(), 60, TimeUnit.SECONDS);
|
||||||
|
|||||||
Reference in New Issue
Block a user