一、功能概述
美团买菜系统的优惠券通用功能需要支持多种类型的优惠券(满减、折扣、无门槛等)在买菜业务场景下的灵活使用,包括商品级、品类级、全平台级的优惠券发放、领取、使用和结算。
二、系统架构设计
1. 核心模块划分
```
优惠券系统
├── 优惠券模板管理
├── 优惠券实例管理
├── 用户优惠券管理
├── 优惠券发放中心
├── 优惠券使用规则引擎
├── 优惠券结算服务
└── 优惠券统计与分析
```
2. 数据库设计
优惠券模板表(coupon_template)
```
id (主键)
name (优惠券名称)
type (满减/折扣/无门槛)
discount_type (百分比折扣/固定金额)
discount_value (折扣值)
min_order_amount (使用门槛金额)
valid_start_time (生效时间)
valid_end_time (失效时间)
publish_count (发放总量)
remain_count (剩余数量)
status (草稿/已发布/已失效)
scope_type (全平台/品类/商品)
scope_ids (适用范围ID集合)
created_at (创建时间)
updated_at (更新时间)
```
用户优惠券表(user_coupon)
```
id (主键)
user_id (用户ID)
template_id (模板ID)
coupon_code (优惠券码)
status (未使用/已使用/已过期)
order_id (使用订单ID)
received_time (领取时间)
used_time (使用时间)
expiry_time (过期时间)
```
三、核心功能实现
1. 优惠券发放功能
```java
// 发放优惠券接口示例
public CouponIssueResult issueCoupon(Long userId, Long templateId, Integer quantity) {
// 1. 验证模板有效性
CouponTemplate template = couponTemplateDao.findById(templateId);
if (template == null || template.getStatus() != TemplateStatus.PUBLISHED) {
throw new BusinessException("优惠券模板无效");
}
// 2. 检查发放数量限制
if (template.getPublishCount() - template.getIssuedCount() < quantity) {
throw new BusinessException("优惠券库存不足");
}
// 3. 生成用户优惠券实例
List userCoupons = new ArrayList<>();
for (int i = 0; i < quantity; i++) {
UserCoupon coupon = new UserCoupon();
coupon.setUserId(userId);
coupon.setTemplateId(templateId);
coupon.setCouponCode(generateCouponCode());
coupon.setStatus(CouponStatus.UNUSED);
coupon.setExpiryTime(calculateExpiryTime(template));
userCoupons.add(coupon);
}
// 4. 批量插入数据库
userCouponDao.batchInsert(userCoupons);
// 5. 更新模板发放数量
couponTemplateDao.increaseIssuedCount(templateId, quantity);
return new CouponIssueResult(userCoupons);
}
```
2. 优惠券使用规则引擎
```java
// 优惠券适用性检查
public boolean isCouponApplicable(UserCoupon coupon, Order order) {
// 1. 检查状态
if (coupon.getStatus() != CouponStatus.UNUSED) {
return false;
}
// 2. 检查有效期
if (LocalDateTime.now().isAfter(coupon.getExpiryTime())) {
return false;
}
// 3. 检查订单金额门槛
CouponTemplate template = couponTemplateDao.findById(coupon.getTemplateId());
if (template.getMinOrderAmount() > 0
&& order.getTotalAmount().compareTo(template.getMinOrderAmount()) < 0) {
return false;
}
// 4. 检查适用范围
if (template.getScopeType() == ScopeType.CATEGORY) {
// 检查订单中是否包含指定品类的商品
boolean hasCategory = order.getItems().stream()
.anyMatch(item -> template.getScopeIds().contains(item.getCategoryId()));
if (!hasCategory) {
return false;
}
} else if (template.getScopeType() == ScopeType.PRODUCT) {
// 检查订单中是否包含指定商品
boolean hasProduct = order.getItems().stream()
.anyMatch(item -> template.getScopeIds().contains(item.getProductId()));
if (!hasProduct) {
return false;
}
}
return true;
}
```
3. 优惠券结算服务
```java
// 计算订单优惠金额
public OrderDiscountResult calculateDiscount(Order order, UserCoupon coupon) {
CouponTemplate template = couponTemplateDao.findById(coupon.getTemplateId());
BigDecimal discountAmount = BigDecimal.ZERO;
switch (template.getType()) {
case FIXED_DISCOUNT:
// 固定金额满减
discountAmount = template.getDiscountValue();
// 不能超过订单金额
if (discountAmount.compareTo(order.getTotalAmount()) > 0) {
discountAmount = order.getTotalAmount();
}
break;
case PERCENTAGE_DISCOUNT:
// 百分比折扣
BigDecimal discountRate = template.getDiscountValue().divide(new BigDecimal(100));
discountAmount = order.getTotalAmount().multiply(discountRate)
.setScale(2, RoundingMode.DOWN);
// 可能有最小/最大折扣限制
if (template.getMaxDiscount() != null
&& discountAmount.compareTo(template.getMaxDiscount()) > 0) {
discountAmount = template.getMaxDiscount();
}
break;
case NO_THRESHOLD:
// 无门槛优惠券
discountAmount = template.getDiscountValue();
break;
}
return new OrderDiscountResult(discountAmount, coupon);
}
```
四、关键业务场景实现
1. 优惠券领取流程
1. 用户进入优惠券中心
2. 系统展示可领取的优惠券列表(按状态过滤:未开始/可领取/已领完)
3. 用户点击领取
4. 系统验证领取资格(新用户/特定条件)
5. 生成用户优惠券记录
6. 返回领取结果
2. 优惠券使用流程
1. 用户选择商品加入购物车
2. 进入结算页面,系统自动匹配适用的优惠券
3. 用户选择使用某张优惠券
4. 系统验证优惠券有效性
5. 重新计算订单金额
6. 完成支付
7. 更新优惠券状态为已使用
3. 优惠券过期处理
```java
// 定时任务处理过期优惠券
@Scheduled(cron = "0 0 0 * * ?") // 每天0点执行
public void processExpiredCoupons() {
LocalDateTime now = LocalDateTime.now();
// 查询所有已过期未使用的优惠券
List expiredCoupons = userCouponDao.findByStatusAndExpiryTime(
CouponStatus.UNUSED, now);
for (UserCoupon coupon : expiredCoupons) {
// 更新状态为已过期
coupon.setStatus(CouponStatus.EXPIRED);
userCouponDao.update(coupon);
// 可选:记录过期日志或通知用户
}
}
```
五、性能优化与扩展性考虑
1. 缓存策略:
- 热门优惠券模板缓存
- 用户可用优惠券缓存
- 优惠券规则缓存
2. 分布式锁:
- 优惠券领取接口加锁,防止超发
- 使用Redis分布式锁或数据库行锁
3. 异步处理:
- 优惠券发放采用消息队列异步处理
- 优惠券使用后状态更新异步处理
4. 规则引擎:
- 使用Drools等规则引擎实现复杂优惠券规则
- 支持动态规则配置
5. AB测试支持:
- 不同用户群体发放不同优惠券
- 优惠券效果对比分析
六、测试要点
1. 单元测试:
- 优惠券适用性检查逻辑
- 折扣金额计算逻辑
- 边界条件测试(刚好满足门槛、刚好不满足等)
2. 集成测试:
- 优惠券领取到使用的完整流程
- 并发领取测试
- 过期处理流程
3. 性能测试:
- 高并发领取场景
- 大规模优惠券数据查询
七、监控与运维
1. 监控指标:
- 优惠券发放成功率
- 优惠券使用率
- 优惠券过期率
- 系统接口响应时间
2. 告警设置:
- 发放失败率过高告警
- 关键接口响应超时告警
- 库存不足告警
3. 日志记录:
- 优惠券操作日志
- 规则匹配日志
- 异常情况日志
通过以上设计实现,美团买菜系统的优惠券功能可以支持复杂的业务场景,同时保证系统的高可用性和性能。