一、功能概述
美团买菜系统的优惠券通用功能需要支持多种类型的优惠券(满减券、折扣券、无门槛券等)在多个业务场景(买菜、配送、会员服务等)下的通用使用,同时要保证系统的性能和用户体验。
二、系统架构设计
1. 优惠券服务分层架构
```
优惠券系统
├── 接口层(API Gateway)
│ ├── 用户接口(领券、查券、用券)
│ ├── 订单接口(优惠券核销)
│ └── 管理接口(运营后台操作)
├── 业务逻辑层
│ ├── 优惠券规则引擎
│ ├── 优惠券分发服务
│ ├── 优惠券核销服务
│ └── 优惠券计算服务
├── 数据访问层
│ ├── 优惠券模板DAO
│ ├── 用户优惠券DAO
│ └── 优惠券使用记录DAO
└── 存储层
├── MySQL(关系型数据)
├── Redis(缓存)
└── MongoDB(日志数据)
```
2. 核心数据模型
```java
// 优惠券模板表
public class CouponTemplate {
private Long id;
private String name; // 优惠券名称
private String type; // 类型:满减、折扣、无门槛
private BigDecimal value; // 优惠值
private BigDecimal condition; // 使用条件(满减金额)
private Date startTime; // 生效时间
private Date endTime; // 失效时间
private Integer total; // 发行总量
private Integer remaining; // 剩余数量
private String scope; // 使用范围:全平台/特定品类
private String userType; // 适用用户类型
private String status; // 状态
// getters & setters
}
// 用户优惠券表
public class UserCoupon {
private Long id;
private Long userId; // 用户ID
private Long templateId; // 模板ID
private String status; // 状态:未使用/已使用/已过期
private Date obtainTime; // 领取时间
private Date expireTime; // 过期时间
private String orderNo; // 关联订单号(使用后)
// getters & setters
}
```
三、核心功能实现
1. 优惠券领取功能
```java
public class CouponService {
@Transactional
public ResultVO receiveCoupon(Long userId, Long templateId) {
// 1. 验证优惠券模板
CouponTemplate template = couponTemplateDao.selectById(templateId);
if (template == null || template.getRemaining() <= 0) {
return ResultVO.error("优惠券已领完");
}
// 2. 验证用户领取资格
if (!checkUserEligibility(userId, template)) {
return ResultVO.error("您不符合领取条件");
}
// 3. 创建用户优惠券记录
UserCoupon userCoupon = new UserCoupon();
userCoupon.setUserId(userId);
userCoupon.setTemplateId(templateId);
userCoupon.setExpireTime(template.getEndTime());
userCoupon.setStatus("UNUSED");
userCouponDao.insert(userCoupon);
// 4. 减少模板剩余数量
couponTemplateDao.decreaseRemaining(templateId);
return ResultVO.success("领取成功");
}
private boolean checkUserEligibility(Long userId, CouponTemplate template) {
// 实现用户资格验证逻辑(新用户、会员等级等)
return true;
}
}
```
2. 优惠券核销功能
```java
public class OrderService {
public Order createOrder(OrderRequest request) {
// 1. 查询用户可用优惠券
List availableCoupons = userCouponDao.selectAvailable(
request.getUserId(),
request.getGoodsType() // 根据商品类型筛选可用券
);
// 2. 应用优惠券(如果有选择)
if (request.getCouponId() != null) {
UserCoupon userCoupon = userCouponDao.selectById(request.getCouponId());
if (userCoupon != null && "UNUSED".equals(userCoupon.getStatus())) {
// 验证优惠券是否适用于当前订单
if (isCouponApplicable(userCoupon, request)) {
// 计算优惠金额
BigDecimal discount = calculateDiscount(userCoupon, request);
// 更新订单金额
request.setTotalAmount(request.getTotalAmount().subtract(discount));
// 标记优惠券为已使用
userCoupon.setStatus("USED");
userCoupon.setOrderNo(request.getOrderNo());
userCouponDao.update(userCoupon);
}
}
}
// 3. 创建订单...
// ...
}
private boolean isCouponApplicable(UserCoupon coupon, OrderRequest request) {
// 检查商品类型、金额等是否满足优惠券条件
// 实现具体业务逻辑
return true;
}
private BigDecimal calculateDiscount(UserCoupon coupon, OrderRequest request) {
// 根据优惠券类型计算折扣金额
switch (coupon.getType()) {
case "FIXED": // 固定金额
return coupon.getValue();
case "PERCENT": // 百分比
return request.getTotalAmount().multiply(coupon.getValue().divide(new BigDecimal("100"));
// 其他类型...
default:
return BigDecimal.ZERO;
}
}
}
```
3. 优惠券规则引擎实现
```java
public class CouponRuleEngine {
// 定义规则接口
public interface CouponRule {
boolean isApplicable(OrderContext context, CouponTemplate coupon);
}
// 具体规则实现
public static class MinAmountRule implements CouponRule {
@Override
public boolean isApplicable(OrderContext context, CouponTemplate coupon) {
return context.getTotalAmount().compareTo(coupon.getCondition()) >= 0;
}
}
public static class GoodsTypeRule implements CouponRule {
@Override
public boolean isApplicable(OrderContext context, CouponTemplate coupon) {
// 检查订单商品类型是否在优惠券适用范围内
return true; // 简化示例
}
}
// 规则引擎执行
public boolean evaluateRules(OrderContext context, CouponTemplate coupon) {
List rules = new ArrayList<>();
rules.add(new MinAmountRule());
rules.add(new GoodsTypeRule());
// 可以添加更多规则...
return rules.stream().allMatch(rule -> rule.isApplicable(context, coupon));
}
}
```
四、关键技术实现
1. 优惠券状态管理
使用Redis实现优惠券状态的高效管理:
```java
// 使用Redis的Hash结构存储用户优惠券状态
public class RedisCouponService {
private static final String USER_COUPON_KEY = "user:coupon:%d"; // %d为用户ID
public void setCouponStatus(Long userId, Long couponId, String status) {
String key = String.format(USER_COUPON_KEY, userId);
redisTemplate.opsForHash().put(key, couponId.toString(), status);
}
public String getCouponStatus(Long userId, Long couponId) {
String key = String.format(USER_COUPON_KEY, userId);
return (String) redisTemplate.opsForHash().get(key, couponId.toString());
}
}
```
2. 优惠券过期处理
使用Redis的TTL机制和定时任务处理过期优惠券:
```java
// 使用Redis的EXPIRE命令设置过期时间
public void setCouponExpire(Long couponId, long expireSeconds) {
redisTemplate.expire("coupon:" + couponId, expireSeconds, TimeUnit.SECONDS);
}
// 定时任务扫描即将过期的优惠券
@Scheduled(fixedRate = 3600000) // 每小时执行一次
public void scanExpiringCoupons() {
List expiringSoon = couponDao.findExpiringSoon(System.currentTimeMillis() + 3600000); // 1小时内过期
expiringSoon.forEach(coupon -> {
// 发送过期提醒等操作
});
}
```
3. 分布式锁防止优惠券超发
```java
public class CouponLock {
private static final String LOCK_PREFIX = "coupon_lock_";
public boolean tryLock(Long couponId) {
String lockKey = LOCK_PREFIX + couponId;
return redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);
}
public void unlock(Long couponId) {
String lockKey = LOCK_PREFIX + couponId;
redisTemplate.delete(lockKey);
}
}
// 使用示例
public void distributeCoupon(Long couponId) {
CouponLock lock = new CouponLock();
try {
if (lock.tryLock(couponId)) {
// 执行业务逻辑
couponDao.decreaseStock(couponId);
} else {
throw new RuntimeException("获取锁失败,请稍后重试");
}
} finally {
lock.unlock(couponId);
}
}
```
五、性能优化措施
1. 缓存策略:
- 缓存优惠券模板信息
- 缓存用户可用优惠券列表
- 使用本地缓存减少数据库访问
2. 异步处理:
- 优惠券发放使用消息队列异步处理
- 优惠券使用记录异步写入数据库
3. 数据库优化:
- 优惠券表按用户ID和状态分表
- 使用读写分离
- 关键查询加索引
4. 限流措施:
- 领券接口限流
- 核销接口限流
六、测试方案
1. 单元测试:
- 优惠券规则引擎测试
- 优惠券状态转换测试
- 边界条件测试
2. 集成测试:
- 领券-用券全流程测试
- 并发场景测试
- 异常场景测试
3. 性能测试:
- 高并发领券测试
- 大数据量查询测试
- 长时间运行稳定性测试
七、部署与监控
1. 部署方案:
- 优惠券服务独立部署
- 灰度发布策略
- 回滚机制
2. 监控指标:
- 优惠券领取成功率
- 优惠券核销率
- 系统响应时间
- 错误率监控
3. 告警策略:
- 领取失败率过高告警
- 核销失败率过高告警
- 系统响应时间过长告警
通过以上设计,美团买菜系统的优惠券通用功能可以实现高可用、高性能的优惠券管理,支持多种业务场景下的优惠券使用,同时保证系统的稳定性和用户体验。