民宿预订和通用电商订单存在明显差异,房源绑定、入住时段限制、多维度核销规则,让优惠券模块更容易出现业务漏洞。

本模块采用SpringBoot+MySQL+Redis技术栈,分层设计解耦业务逻辑,整体分为四层:

为防止超领与重复领券,在user_coupon表建立userId+batchId联合唯一索引,作为最后的数据兜底。
@Service
public class CouponReceiveService {
@Autowired
private StringRedisTemplate redisTemplate;
@Autowired
private CouponBatchMapper batchMapper;
@Autowired
private UserCouponMapper userCouponMapper;
// 分布式锁前缀
private static final String LOCK_PREFIX = "coupon:lock:";
// 库存缓存key
private static final String STOCK_PREFIX = "coupon:stock:";
@Transactional(rollbackFor = Exception.class)
public ResultDTO receiveCoupon(Long userId, Long batchId) {
String lockKey = LOCK_PREFIX + userId + ":" + batchId;
// 1. 获取Redis分布式锁
Boolean lock = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 30, TimeUnit.SECONDS);
if (!lock) {
return ResultDTO.fail("请勿重复领取优惠券");
}
try {
// 2. 校验缓存库存
String stockStr = redisTemplate.opsForValue().get(STOCK_PREFIX + batchId);
if (StringUtils.isEmpty(stockStr) || Integer.parseInt(stockStr) <= 0) {
return ResultDTO.fail("优惠券已领完");
}
// 3. 扣减缓存库存
Long decrement = redisTemplate.opsForValue().decrement(STOCK_PREFIX + batchId);
if (decrement < 0) {
redisTemplate.opsForValue().increment(STOCK_PREFIX + batchId);
return ResultDTO.fail("优惠券已领完");
}
// 4. 写入用户优惠券记录,唯一索引防止重复入库
UserCoupon userCoupon = new UserCoupon();
userCoupon.setUserId(userId);
userCoupon.setBatchId(batchId);
userCoupon.setStatus(0);
userCouponMapper.insert(userCoupon);
return ResultDTO.success("领券成功");
} finally {
// 释放分布式锁
redisTemplate.delete(lockKey);
}
}
}民宿优惠券不能直接套用电商满减逻辑,必须增加房源与入住时段校验,采用策略模式区分不同券类型:满减券、折扣券、周末专享券、连住专享券。
public interface CouponRuleStrategy {
/**
* 校验优惠券是否可用
* @param userCoupon 用户券
* @param orderDTO 预订订单信息
* @return 校验结果
*/
CheckResult check(UserCoupon userCoupon, HomestayOrderDTO orderDTO);
}
// 连住优惠券实现类
@Component
public class NightCouponStrategy implements CouponRuleStrategy {
@Override
public CheckResult check(UserCoupon userCoupon, HomestayOrderDTO orderDTO) {
CouponBatch batch = batchMapper.selectById(userCoupon.getBatchId());
// 校验入住晚数
if (orderDTO.getNightCount() < batch.getMinNights()) {
return CheckResult.fail("未达到最低入住晚数要求");
}
// 校验民宿匹配
if (!batch.getHomestayIdList().contains(orderDTO.getHomestayId())) {
return CheckResult.fail("该优惠券不适用于当前民宿");
}
return CheckResult.success();
}
}
民宿优惠券模块的核心难点,不在于简单的金额计算,而是贴合民宿预订的房源、入住时段、连住条件等业务约束,同时兼顾高并发领券下的数据安全。通过缓存+分布式锁+数据库唯一索引三层防护,配合策略模式管理多样化优惠规则,既可以支撑大促峰值流量,又能严格控制营销成本,避免超发、错用、重复核销等线上问题。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。