IT频道
美团买菜系统优惠券设计:架构、功能、接口与关键考量
来源:     阅读:26
网站管理员
发布于 2025-10-12 01:05
查看主页
  
   一、功能概述
  
  美团买菜系统的优惠券通用功能需要支持多种类型的优惠券(满减券、折扣券、无门槛券等)在买菜业务场景下的灵活使用,包括商品级、品类级、全平台级的优惠券应用。
  
   二、系统架构设计
  
   1. 优惠券服务模块
  ```
  优惠券服务
  ├── 优惠券模板管理
  ├── 优惠券实例管理
  ├── 优惠券发放服务
  ├── 优惠券核销服务
  └── 优惠券计算引擎
  ```
  
   2. 数据库设计
  
  优惠券模板表(coupon_template)
  ```sql
  CREATE TABLE coupon_template (
   id BIGINT PRIMARY KEY AUTO_INCREMENT,
   name VARCHAR(100) NOT NULL,
   type TINYINT NOT NULL COMMENT 1-满减券 2-折扣券 3-无门槛券,
   discount_type TINYINT COMMENT 1-固定金额 2-百分比,
   discount_value DECIMAL(10,2) NOT NULL,
   min_order_amount DECIMAL(10,2) DEFAULT 0,
   valid_days INT COMMENT 有效期天数,
   expire_time DATETIME,
   range_type TINYINT NOT NULL COMMENT 1-全平台 2-指定品类 3-指定商品,
   range_ids VARCHAR(1000) COMMENT 适用范围ID集合,
   total_count INT DEFAULT 0 COMMENT 总发放量,
   remaining_count INT DEFAULT 0 COMMENT 剩余量,
   status TINYINT DEFAULT 1 COMMENT 1-有效 0-无效,
   create_time DATETIME,
   update_time DATETIME
  );
  ```
  
  用户优惠券表(user_coupon)
  ```sql
  CREATE TABLE user_coupon (
   id BIGINT PRIMARY KEY AUTO_INCREMENT,
   user_id BIGINT NOT NULL,
   template_id BIGINT NOT NULL,
   coupon_no VARCHAR(32) NOT NULL UNIQUE,
   status TINYINT DEFAULT 0 COMMENT 0-未使用 1-已使用 2-已过期,
   order_id BIGINT COMMENT 使用订单ID,
   get_time DATETIME,
   use_time DATETIME,
   expire_time DATETIME,
   FOREIGN KEY (template_id) REFERENCES coupon_template(id)
  );
  ```
  
   三、核心功能实现
  
   1. 优惠券发放
  
  ```java
  public class CouponService {
  
   // 发放优惠券给用户
   public UserCoupon grantCoupon(Long userId, Long templateId) {
   CouponTemplate template = couponTemplateRepository.findById(templateId)
   .orElseThrow(() -> new RuntimeException("优惠券模板不存在"));
  
   if (template.getRemainingCount() <= 0) {
   throw new RuntimeException("优惠券已发放完毕");
   }
  
   // 创建用户优惠券实例
   UserCoupon userCoupon = new UserCoupon();
   userCoupon.setUserId(userId);
   userCoupon.setTemplateId(templateId);
   userCoupon.setCouponNo(generateCouponNo());
   userCoupon.setStatus(0); // 未使用
   userCoupon.setExpireTime(calculateExpireTime(template));
  
   // 更新模板剩余数量
   couponTemplateRepository.decreaseRemainingCount(templateId);
  
   return userCouponRepository.save(userCoupon);
   }
  
   private String generateCouponNo() {
   // 生成唯一优惠券码
   return UUID.randomUUID().toString().replace("-", "").substring(0, 16);
   }
  
   private Date calculateExpireTime(CouponTemplate template) {
   if (template.getExpireTime() != null) {
   return template.getExpireTime();
   }
   // 按有效期天数计算
   Calendar calendar = Calendar.getInstance();
   calendar.add(Calendar.DAY_OF_YEAR, template.getValidDays());
   return calendar.getTime();
   }
  }
  ```
  
   2. 优惠券适用范围检查
  
  ```java
  public class CouponRangeChecker {
  
   public boolean isCouponApplicable(UserCoupon userCoupon, List cartItems) {
   CouponTemplate template = getCouponTemplate(userCoupon.getTemplateId());
  
   // 检查是否过期或已使用
   if (userCoupon.getStatus() != 0 || new Date().after(userCoupon.getExpireTime())) {
   return false;
   }
  
   // 全平台通用券
   if (template.getRangeType() == 1) {
   return true;
   }
  
   // 检查购物车商品是否在适用范围内
   Set rangeIds = parseRangeIds(template.getRangeIds());
   return cartItems.stream().anyMatch(item -> {
   if (template.getRangeType() == 2) { // 品类适用
   return rangeIds.contains(item.getCategoryId());
   } else if (template.getRangeType() == 3) { // 商品适用
   return rangeIds.contains(item.getProductId());
   }
   return false;
   });
   }
  
   private Set parseRangeIds(String rangeIdsStr) {
   if (StringUtils.isBlank(rangeIdsStr)) {
   return Collections.emptySet();
   }
   return Arrays.stream(rangeIdsStr.split(","))
   .map(Long::parseLong)
   .collect(Collectors.toSet());
   }
  }
  ```
  
   3. 优惠券计算引擎
  
  ```java
  public class CouponCalculator {
  
   public OrderDiscount calculateDiscount(UserCoupon userCoupon, List cartItems) {
   CouponTemplate template = getCouponTemplate(userCoupon.getTemplateId());
  
   // 检查适用性
   if (!new CouponRangeChecker().isCouponApplicable(userCoupon, cartItems)) {
   throw new RuntimeException("优惠券不适用当前商品");
   }
  
   OrderDiscount discount = new OrderDiscount();
  
   // 计算总金额(满足最小订单金额)
   BigDecimal totalAmount = calculateTotalAmount(cartItems);
   if (template.getMinOrderAmount() > 0 &&
   totalAmount.compareTo(template.getMinOrderAmount()) < 0) {
   throw new RuntimeException("订单金额不满足优惠券使用条件");
   }
  
   // 根据优惠券类型计算折扣
   switch (template.getType()) {
   case 1: // 满减券
   discount.setDiscountType(1);
   discount.setDiscountAmount(template.getDiscountValue());
   break;
   case 2: // 折扣券
   discount.setDiscountType(2);
   if (template.getDiscountType() == 1) { // 固定金额折扣
   discount.setDiscountAmount(template.getDiscountValue());
   } else { // 百分比折扣
   BigDecimal discountPercent = BigDecimal.valueOf(template.getDiscountValue())
   .divide(BigDecimal.valueOf(100));
   BigDecimal discountAmount = totalAmount.multiply(discountPercent)
   .setScale(2, RoundingMode.HALF_UP);
   discount.setDiscountAmount(discountAmount);
   }
   break;
   case 3: // 无门槛券
   discount.setDiscountType(3);
   discount.setDiscountAmount(template.getDiscountValue());
   break;
   }
  
   return discount;
   }
  
   private BigDecimal calculateTotalAmount(List cartItems) {
   return cartItems.stream()
   .map(item -> item.getPrice().multiply(BigDecimal.valueOf(item.getQuantity())))
   .reduce(BigDecimal.ZERO, BigDecimal::add);
   }
  }
  ```
  
   4. 优惠券核销
  
  ```java
  public class CouponRedeemService {
  
   @Transactional
   public Order redeemCoupon(Long userId, Long orderId, String couponNo) {
   // 1. 验证优惠券
   UserCoupon userCoupon = userCouponRepository.findByCouponNoAndUserId(couponNo, userId)
   .orElseThrow(() -> new RuntimeException("优惠券不存在或不属于当前用户"));
  
   if (userCoupon.getStatus() != 0) {
   throw new RuntimeException("优惠券不可用");
   }
  
   if (new Date().after(userCoupon.getExpireTime())) {
   throw new RuntimeException("优惠券已过期");
   }
  
   // 2. 获取订单信息
   Order order = orderRepository.findById(orderId)
   .orElseThrow(() -> new RuntimeException("订单不存在"));
  
   if (!order.getUserId().equals(userId)) {
   throw new RuntimeException("订单不属于当前用户");
   }
  
   // 3. 计算优惠券折扣
   List cartItems = order.getCartItems();
   OrderDiscount discount = new CouponCalculator().calculateDiscount(userCoupon, cartItems);
  
   // 4. 更新订单金额
   BigDecimal originalAmount = order.getTotalAmount();
   BigDecimal discountedAmount = originalAmount.subtract(discount.getDiscountAmount());
   order.setTotalAmount(discountedAmount.max(BigDecimal.ZERO));
   order.setDiscountAmount(discount.getDiscountAmount());
   order.setCouponId(userCoupon.getId());
  
   // 5. 更新优惠券状态
   userCoupon.setStatus(1); // 已使用
   userCoupon.setUseTime(new Date());
   userCoupon.setOrderId(orderId);
   userCouponRepository.save(userCoupon);
  
   // 6. 保存订单
   return orderRepository.save(order);
   }
  }
  ```
  
   四、API接口设计
  
   1. 优惠券领取接口
  ```
  POST /api/coupons/receive
  参数:
  - userId: 用户ID
  - templateId: 优惠券模板ID
  
  响应:
  {
   "code": 200,
   "message": "领取成功",
   "data": {
   "couponId": 12345,
   "couponNo": "ABC12345678",
   "expireTime": "2023-12-31 23:59:59"
   }
  }
  ```
  
   2. 用户优惠券列表接口
  ```
  GET /api/coupons/my
  参数:
  - userId: 用户ID
  - status: 优惠券状态(0-未使用 1-已使用 2-已过期)
  
  响应:
  {
   "code": 200,
   "message": "成功",
   "data": [
   {
   "couponId": 12345,
   "couponNo": "ABC12345678",
   "templateId": 1001,
   "templateName": "满100减20",
   "type": 1,
   "discountValue": 20,
   "minOrderAmount": 100,
   "expireTime": "2023-12-31 23:59:59",
   "status": 0
   }
   ]
  }
  ```
  
   3. 优惠券核销接口
  ```
  POST /api/orders/{orderId}/redeem-coupon
  参数:
  - userId: 用户ID
  - couponNo: 优惠券码
  
  响应:
  {
   "code": 200,
   "message": "优惠券使用成功",
   "data": {
   "orderId": 12345,
   "originalAmount": 150.00,
   "discountAmount": 20.00,
   "finalAmount": 130.00
   }
  }
  ```
  
   五、关键考虑因素
  
  1. 并发控制:优惠券领取和核销需要处理高并发场景,防止超发
  2. 分布式锁:在优惠券发放时使用Redis分布式锁确保原子性
  3. 幂等性设计:优惠券核销接口需要保证重复调用不会产生副作用
  4. 过期处理:使用定时任务清理过期优惠券
  5. 性能优化:优惠券适用范围检查需要高效,避免影响购物车结算性能
  6. 数据一致性:确保优惠券状态和订单状态的最终一致性
  
   六、扩展功能
  
  1. 优惠券叠加使用:支持多张优惠券按规则叠加使用
  2. 优惠券分享:用户可以将优惠券分享给好友
  3. 优惠券裂变:邀请好友使用可获得额外优惠券
  4. 优惠券组合:创建优惠券包,包含多种类型优惠券
  5. 数据分析:统计优惠券领取、使用情况,优化营销策略
  
  以上实现方案涵盖了美团买菜系统优惠券通用功能的核心逻辑,可根据实际业务需求进行调整和扩展。
免责声明:本文为用户发表,不代表网站立场,仅供参考,不构成引导等用途。 IT频道
购买生鲜系统联系18310199838
广告
相关推荐
采摘时间可视化方案:万象源码赋能,强化新鲜直供体验
万象订货系统:数据驱动,优化流程,升级体验促复购
优化流程、技术赋能、人员协同:万象生鲜异常处理升级方案
万象采购系统:数字化赋能,降本增效控风险
蔬东坡系统:全链路提速,助力生鲜企业降本增效增质