IT频道
美团买菜系统优惠券设计:功能、数据库、实现与优化全解析
来源:     阅读:30
网站管理员
发布于 2025-12-12 10:30
查看主页
  
   一、功能概述
  
  美团买菜系统的优惠券通用功能需要支持多种类型的优惠券(满减券、折扣券、无门槛券等)在多个业务场景(商品购买、配送费减免、新用户专享等)下的通用使用。
  
   二、数据库设计
  
   1. 优惠券表(coupon)
  ```sql
  CREATE TABLE `coupon` (
   `id` bigint NOT NULL AUTO_INCREMENT,
   `coupon_name` varchar(100) NOT NULL COMMENT 优惠券名称,
   `coupon_type` tinyint NOT NULL COMMENT 1-满减券 2-折扣券 3-无门槛券,
   `discount_amount` decimal(10,2) DEFAULT NULL COMMENT 满减金额/折扣金额,
   `discount_rate` decimal(5,2) DEFAULT NULL COMMENT 折扣率(0-100),
   `min_order_amount` decimal(10,2) DEFAULT NULL COMMENT 最低订单金额,
   `valid_start_time` datetime NOT NULL COMMENT 有效期开始时间,
   `valid_end_time` datetime NOT NULL COMMENT 有效期结束时间,
   `total_count` int DEFAULT NULL COMMENT 发放总量,
   `remaining_count` int DEFAULT NULL COMMENT 剩余数量,
   `status` tinyint NOT NULL DEFAULT 1 COMMENT 1-有效 0-无效,
   `create_time` datetime NOT NULL,
   `update_time` datetime NOT NULL,
   PRIMARY KEY (`id`)
  ) ENGINE=InnoDB;
  ```
  
   2. 用户优惠券表(user_coupon)
  ```sql
  CREATE TABLE `user_coupon` (
   `id` bigint NOT NULL AUTO_INCREMENT,
   `user_id` bigint NOT NULL COMMENT 用户ID,
   `coupon_id` bigint NOT NULL COMMENT 优惠券ID,
   `status` tinyint NOT NULL DEFAULT 1 COMMENT 1-可用 0-已使用 2-已过期,
   `get_time` datetime NOT NULL COMMENT 领取时间,
   `use_time` datetime DEFAULT NULL COMMENT 使用时间,
   `order_id` bigint DEFAULT NULL COMMENT 关联订单ID,
   PRIMARY KEY (`id`),
   UNIQUE KEY `uk_user_coupon` (`user_id`,`coupon_id`)
  ) ENGINE=InnoDB;
  ```
  
   3. 优惠券范围表(coupon_scope)
  ```sql
  CREATE TABLE `coupon_scope` (
   `id` bigint NOT NULL AUTO_INCREMENT,
   `coupon_id` bigint NOT NULL COMMENT 优惠券ID,
   `scope_type` tinyint NOT NULL COMMENT 1-商品分类 2-商品SKU 3-全场,
   `scope_id` varchar(50) DEFAULT NULL COMMENT 分类ID或SKU_ID,
   PRIMARY KEY (`id`)
  ) ENGINE=InnoDB;
  ```
  
   三、核心功能实现
  
   1. 优惠券发放服务
  
  ```java
  public class CouponService {
  
   @Autowired
   private CouponRepository couponRepository;
  
   @Autowired
   private UserCouponRepository userCouponRepository;
  
   /
   * 发放优惠券给用户
   */
   public boolean grantCoupon(Long userId, Long couponId) {
   Coupon coupon = couponRepository.findById(couponId)
   .orElseThrow(() -> new RuntimeException("优惠券不存在"));
  
   if (coupon.getStatus() != 1) {
   throw new RuntimeException("优惠券不可用");
   }
  
   // 检查用户是否已领取过该优惠券(根据业务需求决定是否允许重复领取)
   if (userCouponRepository.existsByUserIdAndCouponIdAndStatus(userId, couponId, 1)) {
   return false; // 或根据业务需求抛出异常
   }
  
   UserCoupon userCoupon = new UserCoupon();
   userCoupon.setUserId(userId);
   userCoupon.setCouponId(couponId);
   userCoupon.setStatus(1);
   userCoupon.setGetTime(new Date());
  
   userCouponRepository.save(userCoupon);
   return true;
   }
  }
  ```
  
   2. 优惠券可用性检查
  
  ```java
  public class CouponValidator {
  
   @Autowired
   private CouponRepository couponRepository;
  
   @Autowired
   private UserCouponRepository userCouponRepository;
  
   @Autowired
   private CouponScopeService couponScopeService;
  
   /
   * 检查优惠券是否可用
   */
   public boolean isCouponAvailable(Long userId, Long couponId,
   BigDecimal orderAmount,
   List skuIds) {
   // 1. 检查用户是否拥有该优惠券且有效
   UserCoupon userCoupon = userCouponRepository.findByUserIdAndCouponIdAndStatus(userId, couponId, 1)
   .orElseThrow(() -> new RuntimeException("优惠券不可用"));
  
   Coupon coupon = couponRepository.findById(couponId)
   .orElseThrow(() -> new RuntimeException("优惠券不存在"));
  
   // 2. 检查优惠券是否在有效期内
   Date now = new Date();
   if (now.before(coupon.getValidStart()) || now.after(coupon.getValidEnd())) {
   return false;
   }
  
   // 3. 检查订单金额是否满足最低要求
   if (coupon.getMinOrderAmount() != null &&
   orderAmount.compareTo(coupon.getMinOrderAmount()) < 0) {
   return false;
   }
  
   // 4. 检查商品是否在优惠券适用范围内
   if (!couponScopeService.isSkuInScope(couponId, skuIds)) {
   return false;
   }
  
   return true;
   }
  }
  ```
  
   3. 优惠券使用服务
  
  ```java
  public class CouponUsageService {
  
   @Autowired
   private CouponValidator couponValidator;
  
   @Autowired
   private OrderService orderService;
  
   @Autowired
   private UserCouponRepository userCouponRepository;
  
   /
   * 使用优惠券
   */
   public Order useCoupon(Long userId, Long couponId, Long orderId) {
   // 1. 获取订单信息
   Order order = orderService.getOrderById(orderId);
   if (!order.getUserId().equals(userId)) {
   throw new RuntimeException("订单不属于当前用户");
   }
  
   // 2. 验证优惠券是否可用
   if (!couponValidator.isCouponAvailable(userId, couponId, order.getTotalAmount(), order.getItems())) {
   throw new RuntimeException("优惠券不可用");
   }
  
   // 3. 获取优惠券详情
   Coupon coupon = couponRepository.findById(couponId)
   .orElseThrow(() -> new RuntimeException("优惠券不存在"));
  
   // 4. 计算折扣后的金额
   BigDecimal discountedAmount = calculateDiscountedAmount(order.getTotalAmount(), coupon);
  
   // 5. 更新订单金额
   order.setTotalAmount(discountedAmount);
   order.setCouponId(couponId);
   order.setDiscountAmount(order.getTotalAmount().subtract(discountedAmount));
  
   // 6. 更新用户优惠券状态
   UserCoupon userCoupon = userCouponRepository.findByUserIdAndCouponId(userId, couponId)
   .orElseThrow(() -> new RuntimeException("用户优惠券记录不存在"));
   userCoupon.setStatus(2); // 已使用
   userCoupon.setUsedTime(new Date());
   userCouponRepository.save(userCoupon);
  
   // 7. 保存订单
   return orderService.saveOrder(order);
   }
  
   private BigDecimal calculateDiscount(BigDecimal amount, Coupon coupon) {
   switch (coupon.getCouponType()) {
   case 1: // 满减券
   return amount.subtract(coupon.getDiscountAmount());
   case 2: // 折扣券
   return amount.multiply(coupon.getDiscountRate()).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);
   case 3: // 无门槛券
   return amount.subtract(coupon.getDiscountAmount());
   default:
   return amount;
   }
   }
  }
  ```
  
   四、API接口设计
  
   1. 领取优惠券接口
  ```
  POST /api/coupon/receive
  参数:
  - userId: 用户ID
  - couponId: 优惠券ID
  
  返回:
  {
   "code": 200,
   "message": "领取成功",
   "data": null
  }
  ```
  
   2. 查询可用优惠券接口
  ```
  GET /api/coupon/available
  参数:
  - userId: 用户ID
  - orderAmount: 订单金额(可选)
  - skuIds: 商品ID列表(可选)
  
  返回:
  {
   "code": 200,
   "message": "成功",
   "data": [
   {
   "couponId": 123,
   "couponName": "满100减20",
   "couponType": 1,
   "discountAmount": 20.00,
   "minOrderAmount": 100.00
   },
   ...
   ]
  }
  ```
  
   3. 使用优惠券接口
  ```
  POST /api/coupon/use
  参数:
  - userId: 用户ID
  - couponId: 优惠券ID
  - orderId: 订单ID
  
  返回:
  {
   "code": 200,
   "message": "使用成功",
   "data": {
   "orderId": 456,
   "finalAmount": 80.00,
   "discountAmount": 20.00
   }
  }
  ```
  
   五、关键业务规则
  
  1. 优惠券有效期:优惠券必须在有效期内使用,系统需自动过滤过期优惠券
  
  2. 最低消费金额:满减券和折扣券需满足最低消费金额才能使用
  
  3. 优惠券叠加规则:
   - 默认不允许叠加使用
   - 可配置是否允许与其他优惠券叠加
   - 可配置是否允许与促销活动叠加
  
  4. 使用范围限制:
   - 可限制特定商品类别
   - 可限制特定商品
   - 可限制新用户/老用户
  
  5. 退款处理:
   - 订单部分退款时,已使用的优惠券通常不予退还
   - 订单全额退款时,可配置是否返还优惠券
  
   六、性能优化考虑
  
  1. 优惠券查询缓存:对用户可用优惠券列表进行缓存,减少数据库查询
  
  2. 异步处理:优惠券发放可采用消息队列异步处理,避免高峰期系统压力
  
  3. 分布式锁:在高并发场景下,优惠券使用需加分布式锁防止超发
  
  4. 批量操作:优惠券发放支持批量操作,提高效率
  
   七、测试要点
  
  1. 边界值测试:测试刚好满足/不满足优惠券使用条件的场景
  
  2. 并发测试:测试高并发下优惠券的发放和使用
  
  3. 异常场景测试:测试优惠券过期、已使用、库存不足等场景
  
  4. 组合测试:测试优惠券与促销活动、会员折扣等组合使用场景
  
  以上是美团买菜系统优惠券通用功能的基本实现方案,可根据实际业务需求进行调整和扩展。
免责声明:本文为用户发表,不代表网站立场,仅供参考,不构成引导等用途。 IT频道
购买生鲜系统联系18310199838
广告
相关推荐
观麦系统:以全链路数字化,破生鲜配送难题,构建行业新优势
生鲜配送软件全解析:多场景推荐与选型指南
小象买菜转型生活服务平台:融场景、强互动,以科技构建差异化竞争力
蔬东坡生鲜配送系统:数字化赋能,降本增效提质
生鲜配送系统:破信息孤岛、提效率、降成本,实现全流程协同