美团买菜优惠券系统设计:涵盖功能、架构、实现、规则及性能优化
分类:IT频道
时间:2026-02-11 07:55
浏览:18
概述
一、功能概述 美团买菜系统的优惠券通用功能需要实现以下核心能力: 1.支持多种类型优惠券(满减券、折扣券、无门槛券等) 2.跨品类通用(蔬菜、水果、肉禽、日用品等) 3.灵活的发放与使用规则 4.精准的优惠计算与叠加规则 5.完善的优惠券生命周期管理 二、系统架构设计
内容
一、功能概述
美团买菜系统的优惠券通用功能需要实现以下核心能力:
1. 支持多种类型优惠券(满减券、折扣券、无门槛券等)
2. 跨品类通用(蔬菜、水果、肉禽、日用品等)
3. 灵活的发放与使用规则
4. 精准的优惠计算与叠加规则
5. 完善的优惠券生命周期管理
二、系统架构设计
1. 核心模块划分
```
优惠券系统
├── 优惠券模板管理
├── 优惠券实例管理
├── 优惠券发放服务
├── 优惠券使用服务
├── 优惠券计算引擎
└── 优惠券监控与统计
```
2. 数据库设计
优惠券模板表(coupon_template)
```sql
CREATE TABLE coupon_template (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL COMMENT 优惠券名称,
type TINYINT NOT NULL COMMENT 类型(1:满减 2:折扣 3:无门槛),
discount_type TINYINT COMMENT 折扣类型(1:百分比 2:固定金额),
discount_value DECIMAL(10,2) NOT NULL COMMENT 优惠值,
min_order_amount DECIMAL(10,2) COMMENT 最低订单金额,
valid_start_time DATETIME NOT NULL COMMENT 有效期开始时间,
valid_end_time DATETIME NOT NULL COMMENT 有效期结束时间,
total_count INT DEFAULT 0 COMMENT 总发放数量,
remaining_count INT DEFAULT 0 COMMENT 剩余数量,
user_limit INT DEFAULT 1 COMMENT 每人限领数量,
scope TINYINT DEFAULT 1 COMMENT 使用范围(1:全品类 2:指定品类),
category_ids VARCHAR(255) COMMENT 适用品类ID列表,
status TINYINT DEFAULT 1 COMMENT 状态(1:启用 2:禁用),
create_time DATETIME NOT NULL,
update_time DATETIME NOT NULL
);
```
用户优惠券表(user_coupon)
```sql
CREATE TABLE user_coupon (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL COMMENT 用户ID,
template_id BIGINT NOT NULL COMMENT 模板ID,
coupon_code VARCHAR(32) NOT NULL COMMENT 优惠券码,
status TINYINT DEFAULT 1 COMMENT 状态(1:未使用 2:已使用 3:已过期),
order_id BIGINT COMMENT 关联订单ID,
get_time DATETIME NOT NULL COMMENT 领取时间,
use_time DATETIME COMMENT 使用时间,
expire_time DATETIME NOT NULL COMMENT 过期时间,
INDEX idx_user (user_id),
INDEX idx_status (status),
INDEX idx_expire (expire_time)
);
```
三、核心功能实现
1. 优惠券发放服务
```java
public class CouponDistributionService {
@Autowired
private CouponTemplateRepository templateRepository;
@Autowired
private UserCouponRepository userCouponRepository;
@Transactional
public void distributeCoupon(Long templateId, List userIds) {
CouponTemplate template = templateRepository.findById(templateId)
.orElseThrow(() -> new RuntimeException("优惠券模板不存在"));
// 检查剩余数量
if (template.getRemainingCount() <= 0) {
throw new RuntimeException("优惠券已发放完毕");
}
// 生成用户优惠券
List coupons = new ArrayList<>();
Date now = new Date();
for (Long userId : userIds) {
// 检查用户已领取数量
int userCount = userCouponRepository.countByUserIdAndTemplateIdAndStatus(
userId, templateId, CouponStatus.UNUSED.getCode());
if (userCount >= template.getUserLimit()) {
continue; // 跳过已达上限的用户
}
UserCoupon coupon = new UserCoupon();
coupon.setUserId(userId);
coupon.setTemplateId(templateId);
coupon.setCouponCode(generateCouponCode());
coupon.setStatus(CouponStatus.UNUSED.getCode());
coupon.setGetTime(now);
coupon.setExpireTime(template.getValidEndTime());
coupons.add(coupon);
}
if (!coupons.isEmpty()) {
userCouponRepository.saveAll(coupons);
// 更新模板剩余数量
template.setRemainingCount(template.getRemainingCount() - coupons.size());
templateRepository.save(template);
}
}
private String generateCouponCode() {
// 生成唯一优惠券码逻辑
return UUID.randomUUID().toString().replace("-", "").substring(0, 16).toUpperCase();
}
}
```
2. 优惠券使用服务
```java
public class CouponUsageService {
@Autowired
private UserCouponRepository userCouponRepository;
@Autowired
private CouponTemplateRepository templateRepository;
@Autowired
private OrderRepository orderRepository;
public UserCoupon useCoupon(Long userId, String couponCode, Long orderId) {
// 验证订单属于当前用户
Order order = orderRepository.findByIdAndUserId(orderId, userId)
.orElseThrow(() -> new RuntimeException("订单不存在或不属于当前用户"));
// 查找用户优惠券
UserCoupon userCoupon = userCouponRepository.findByUserIdAndCouponCodeAndStatus(
userId, couponCode, CouponStatus.UNUSED.getCode())
.orElseThrow(() -> new RuntimeException("优惠券不存在或不可用"));
CouponTemplate template = templateRepository.findById(userCoupon.getTemplateId())
.orElseThrow(() -> new RuntimeException("优惠券模板不存在"));
// 验证有效期
if (new Date().after(template.getValidEndTime())) {
throw new RuntimeException("优惠券已过期");
}
// 验证使用范围
if (template.getScope() == CouponScope.SPECIFIC_CATEGORY.getCode()) {
List categoryIds = Arrays.asList(template.getCategoryIds().split(","))
.stream().map(Long::valueOf).collect(Collectors.toList());
// 检查订单商品是否都属于指定品类
List items = orderRepository.findOrderItemsByOrderId(orderId);
boolean allMatch = items.stream()
.allMatch(item -> categoryIds.contains(item.getCategoryId()));
if (!allMatch) {
throw new RuntimeException("该优惠券仅适用于指定品类商品");
}
}
// 验证最低订单金额
if (template.getMinOrderAmount() != null &&
order.getTotalAmount().compareTo(template.getMinOrderAmount()) < 0) {
throw new RuntimeException("订单金额未达到使用条件");
}
// 更新优惠券状态
userCoupon.setStatus(CouponStatus.USED.getCode());
userCoupon.setUseTime(new Date());
userCoupon.setOrderId(orderId);
userCouponRepository.save(userCoupon);
return userCoupon;
}
}
```
3. 优惠券计算引擎
```java
public class CouponCalculator {
public OrderDiscountResult calculateDiscount(Order order, UserCoupon userCoupon) {
CouponTemplate template = getTemplateById(userCoupon.getTemplateId());
OrderDiscountResult result = new OrderDiscountResult();
switch (CouponType.valueOf(template.getType())) {
case FIXED_DISCOUNT:
// 固定金额满减券
result.setDiscountAmount(template.getDiscountValue());
result.setDiscountType(DiscountType.FIXED);
break;
case PERCENTAGE_DISCOUNT:
// 百分比折扣券
BigDecimal discount = order.getTotalAmount()
.multiply(template.getDiscountValue().divide(new BigDecimal("100")));
// 通常折扣券有最大优惠金额限制
if (template.getMaxDiscount() != null &&
discount.compareTo(template.getMaxDiscount()) > 0) {
discount = template.getMaxDiscount();
}
result.setDiscountAmount(discount);
result.setDiscountType(DiscountType.PERCENTAGE);
break;
case NO_THRESHOLD:
// 无门槛券
result.setDiscountAmount(template.getDiscountValue());
result.setDiscountType(DiscountType.FIXED);
break;
}
// 确保优惠金额不超过订单总额
if (result.getDiscountAmount().compareTo(order.getTotalAmount()) > 0) {
result.setDiscountAmount(order.getTotalAmount());
}
result.setFinalAmount(order.getTotalAmount().subtract(result.getDiscountAmount()));
return result;
}
}
```
四、关键业务规则实现
1. 优惠券叠加规则
```java
public class CouponCombinationRule {
public boolean canCombine(List coupons) {
if (coupons.size() > 3) { // 最多叠加3张券
return false;
}
Set types = new HashSet<>();
for (UserCoupon coupon : coupons) {
CouponTemplate template = getTemplateById(coupon.getTemplateId());
types.add(CouponType.valueOf(template.getType()));
}
// 不能同时使用满减券和折扣券
if (types.contains(CouponType.FIXED_DISCOUNT) &&
types.contains(CouponType.PERCENTAGE_DISCOUNT)) {
return false;
}
// 无门槛券不能与其他券叠加
if (types.contains(CouponType.NO_THRESHOLD) && types.size() > 1) {
return false;
}
return true;
}
}
```
2. 优惠券有效期管理
```java
@Scheduled(cron = "0 0 0 * * ?") // 每天凌晨执行
public class CouponExpiryJob {
@Autowired
private UserCouponRepository userCouponRepository;
@Transactional
public void processExpiredCoupons() {
Date now = new Date();
List expiredCoupons = userCouponRepository.findByStatusAndExpireTimeBefore(
CouponStatus.UNUSED.getCode(), now);
if (!expiredCoupons.isEmpty()) {
for (UserCoupon coupon : expiredCoupons) {
coupon.setStatus(CouponStatus.EXPIRED.getCode());
}
userCouponRepository.saveAll(expiredCoupons);
}
}
}
```
五、前端交互设计
1. 优惠券领取流程
1. 用户进入优惠券中心页面
2. 系统展示可领取的优惠券列表(包括通用券和品类券)
3. 用户点击"立即领取"按钮
4. 后端验证并发放优惠券
5. 返回领取结果(成功/失败原因)
2. 优惠券使用流程
1. 用户进入订单确认页面
2. 系统自动匹配适用的优惠券(考虑商品品类、订单金额等)
3. 用户选择要使用的优惠券
4. 系统重新计算订单金额并显示优惠信息
5. 用户提交订单完成购买
六、性能优化考虑
1. 缓存策略:
- 缓存热门优惠券模板信息
- 缓存用户可用的优惠券列表
2. 数据库优化:
- 为常用查询字段建立索引(用户ID、状态、过期时间等)
- 考虑分表策略(如按用户ID分表存储用户优惠券)
3. 异步处理:
- 优惠券发放采用异步消息队列处理
- 优惠券使用后的统计数据异步更新
4. 限流措施:
- 对高并发领取场景实施限流
- 防止恶意刷券行为
七、测试用例示例
1. 正常场景测试:
- 用户领取全品类满减券并成功使用
- 用户领取指定品类折扣券并在符合条件的订单中使用
2. 异常场景测试:
- 尝试使用已过期的优惠券
- 在订单金额不足时使用满减券
- 在包含非指定品类商品时使用品类券
- 尝试叠加不兼容的优惠券类型
3. 边界条件测试:
- 刚好达到最低订单金额时使用满减券
- 使用折扣券时订单金额接近零的情况
- 优惠券刚好在领取后过期的情况
通过以上设计,美团买菜系统可以实现一个灵活、高效、可扩展的优惠券通用功能,支持各种营销活动需求,同时保证系统稳定性和用户体验。
评论