feat(pricing): 支持发放多个首次打印优惠券

- 修改自动发券逻辑,支持发放多个符合条件的首次优惠券
- 更新查找优惠券方法,返回所有匹配的优惠券ID列表
- 添加发券过程中的异常处理,确保部分失败不影响其他券发放
- 记录详细的发券日志,包括成功、跳过和失败的数量
- 优化日志输出,提供更清晰的调试信息
This commit is contained in:
2025-12-07 21:41:54 +08:00
parent 349b702fc3
commit a5fe00052d

View File

@@ -36,39 +36,62 @@ public class AutoCouponServiceImpl implements IAutoCouponService {
return false;
}
// 2. 查找该景区、该商品类型的首次打印优惠券配置
Long couponId = findFirstCouponId(scenicId, productType);
if (couponId == null) {
// 2. 查找该景区、该商品类型的所有首次打印优惠券配置
List<Long> couponIds = findFirstCouponIds(scenicId, productType);
if (couponIds == null || couponIds.isEmpty()) {
log.debug("景区未配置首次打印优惠券: scenicId={}, productType={}", scenicId, productType);
return false;
}
// 3. 检查用户是否已领取过该券(领券即消耗首次资格)
PriceCouponClaimRecord existingRecord = couponClaimRecordMapper.selectUserCouponRecord(
memberId,
couponId
);
log.info("找到{}张首次优惠券待发放: scenicId={}, productType={}, couponIds={}",
couponIds.size(), scenicId, productType, couponIds);
if (existingRecord != null) {
log.debug("用户已领取过首次优惠券,不重复发券: memberId={}, couponId={}, claimTime={}",
memberId, couponId, existingRecord.getClaimTime());
return false;
// 3. 遍历所有优惠券,逐一检查并发放
int successCount = 0;
int skipCount = 0;
int failCount = 0;
for (Long couponId : couponIds) {
try {
// 检查用户是否已领取过该券(领券即消耗首次资格)
PriceCouponClaimRecord existingRecord = couponClaimRecordMapper.selectUserCouponRecord(
memberId,
couponId
);
if (existingRecord != null) {
log.debug("用户已领取过优惠券,跳过: memberId={}, couponId={}, claimTime={}",
memberId, couponId, existingRecord.getClaimTime());
skipCount++;
continue;
}
// 自动发券
CouponClaimRequest request = new CouponClaimRequest(
memberId,
couponId,
scenicId.toString(),
"AUTO_GRANT" // 标记为自动发券来源
);
couponService.claimCoupon(request);
successCount++;
log.info("成功自动发放首次优惠券: memberId={}, faceId={}, scenicId={}, productType={}, couponId={}",
memberId, faceId, scenicId, productType, couponId);
} catch (Exception e) {
failCount++;
log.error("单张优惠券发放失败,继续处理其他券: memberId={}, couponId={}, error={}",
memberId, couponId, e.getMessage());
}
}
// 4. 自动发券
CouponClaimRequest request = new CouponClaimRequest(
memberId,
couponId,
scenicId.toString(),
"AUTO_GRANT" // 标记为自动发券来源
);
log.info("自动发券完成: memberId={}, 成功{}张, 跳过{}张, 失败{}张",
memberId, successCount, skipCount, failCount);
couponService.claimCoupon(request);
log.info("成功自动发放首次打印优惠券: memberId={}, faceId={}, scenicId={}, productType={}, couponId={}",
memberId, faceId, scenicId, productType, couponId);
return true;
// 只要有一张成功就返回true
return successCount > 0;
} catch (Exception e) {
log.error("自动发券失败: memberId={}, faceId={}, scenicId={}, productType={}",
@@ -78,14 +101,15 @@ public class AutoCouponServiceImpl implements IAutoCouponService {
}
/**
* 查找指定景区、指定商品类型的首次打印优惠券ID
* 查找指定景区、指定商品类型的所有首次打印优惠券ID
* 规则:优惠券名称包含 "首次" 且 适用商品类型包含目标类型
*
* @param scenicId 景区ID
* @param productType 商品类型
* @return 优惠券ID,未找到返回null
* @return 优惠券ID列表,未找到返回空列表
*/
private Long findFirstCouponId(Long scenicId, ProductType productType) {
private List<Long> findFirstCouponIds(Long scenicId, ProductType productType) {
List<Long> couponIds = new java.util.ArrayList<>();
try {
// 查询该景区的有效优惠券
List<PriceCouponConfig> coupons = couponConfigMapper.selectValidCouponsByScenicId(
@@ -100,17 +124,22 @@ public class AutoCouponServiceImpl implements IAutoCouponService {
String applicableProducts = coupon.getApplicableProducts();
if (applicableProducts != null &&
applicableProducts.contains(productType.getCode())) {
return coupon.getId();
couponIds.add(coupon.getId());
log.debug("找到匹配的首次优惠券: couponId={}, couponName={}, scenicId={}, productType={}",
coupon.getId(), coupon.getCouponName(), scenicId, productType);
}
}
}
log.debug("未找到匹配的首次打印优惠券: scenicId={}, productType={}", scenicId, productType);
return null;
if (couponIds.isEmpty()) {
log.debug("未找到匹配的首次打印优惠券: scenicId={}, productType={}", scenicId, productType);
}
return couponIds;
} catch (Exception e) {
log.error("查找首次打印优惠券失败: scenicId={}, productType={}", scenicId, productType, e);
return null;
return couponIds;
}
}
}