Browse Source

订单表新增订单优惠金额discount_amount和实付总金额paid_amount字段

skyline 1 month ago
parent
commit
62721c95ae

+ 6 - 3
haha-common/src/main/java/com/haha/common/dto/FloorConfig.java

@@ -20,8 +20,11 @@ public class FloorConfig {
     private Integer floor;
 
     /**
-     * 该层的商品编码列表
+     * 该层的商品列表
+     * 支持两种格式:
+     * 1. 字符串数组:["goods1", "goods2"]
+     * 2. 对象数组:[{"productId": 1, "productName": "商品1"}]
      */
-    @NotEmpty(message = "商品编码列表不能为空")
-    private List<String> goods;
+    @NotEmpty(message = "商品列表不能为空")
+    private List<Object> goods;
 }

+ 9 - 0
haha-common/src/main/java/com/haha/common/vo/OrderVO.java

@@ -17,7 +17,16 @@ public class OrderVO {
     private String outTradeNo;
     private String activityId;
     private String deviceId;
+    
+    /** 订单总金额(原价) */
     private BigDecimal totalAmount;
+    
+    /** 订单优惠金额(营销活动优惠 + 优惠券优惠) */
+    private BigDecimal discountAmount;
+    
+    /** 实付总金额 = total_amount - discount_amount */
+    private BigDecimal paidAmount;
+    
     private Integer payStatus;
     private String payStatusLabel;
     private Integer status;

+ 7 - 0
haha-entity/src/main/java/com/haha/entity/Order.java

@@ -31,8 +31,15 @@ public class Order implements Serializable {
 
     private String deviceId;
 
+    /** 订单总金额(原价) */
     private BigDecimal totalAmount;
 
+    /** 订单优惠金额(营销活动优惠 + 优惠券优惠) */
+    private BigDecimal discountAmount;
+
+    /** 实付总金额 = total_amount - discount_amount */
+    private BigDecimal paidAmount;
+
     private String payStatus;
 
     /** 支付方式 */

+ 4 - 4
haha-mp/src/api/order.ts

@@ -21,7 +21,7 @@ export interface OrderProduct {
  * 订单信息
  */
 export interface OrderInfo {
-  id: number;
+  id: string;
   orderNo: string;
   orderName?: string; // 订单名称
   outTradeNo: string;
@@ -49,7 +49,7 @@ export interface OrderListRequest {
  * 订单详情请求参数
  */
 export interface OrderDetailRequest {
-  orderId?: number;
+  orderId?: string;
   orderNo?: string;
   outTradeNo?: string;
 }
@@ -58,7 +58,7 @@ export interface OrderDetailRequest {
  * 取消订单请求参数
  */
 export interface CancelOrderRequest {
-  orderId: number;
+  orderId: string;
 }
 
 /**
@@ -81,6 +81,6 @@ export const getOrderDetail = (params: OrderDetailRequest): Promise<OrderInfo> =
  * 取消订单
  * @param orderId 订单ID
  */
-export const cancelOrder = (orderId: number): Promise<void> => {
+export const cancelOrder = (orderId: string): Promise<void> => {
   return post<void>('/order/cancel', { orderId });
 };

+ 255 - 2
haha-service/src/main/java/com/haha/service/impl/HahaCallbackServiceImpl.java

@@ -11,6 +11,8 @@ import com.haha.common.enums.RecognizeConsumeType;
 import com.haha.entity.Order;
 import com.haha.entity.OrderGoods;
 import com.haha.entity.DoorRecord;
+import com.haha.entity.UserCoupon;
+import com.haha.entity.CouponTemplate;
 import com.haha.mapper.DeviceMapper;
 import com.haha.mapper.OrderGoodsMapper;
 import com.haha.sdk.HahaClient;
@@ -18,6 +20,8 @@ import com.haha.service.HahaCallbackService;
 import com.haha.service.NewProductApplyService;
 import com.haha.service.OrderService;
 import com.haha.service.DoorRecordService;
+import com.haha.service.UserCouponService;
+import com.haha.service.CouponTemplateService;
 import com.haha.service.payment.payscore.PayScoreResult;
 import com.haha.service.payment.payscore.PayScoreService;
 import org.springframework.data.redis.core.StringRedisTemplate;
@@ -31,6 +35,7 @@ import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.time.LocalDateTime;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.TimeUnit;
 
@@ -68,6 +73,12 @@ public class HahaCallbackServiceImpl implements HahaCallbackService {
     @Autowired
     private DoorRecordService doorRecordService;
 
+    @Autowired
+    private UserCouponService userCouponService;
+
+    @Autowired
+    private CouponTemplateService couponTemplateService;
+
     private static final String DEVICE_STATUS_KEY = "haha:device:status:";
     private static final String RECOGNIZE_RESULT_KEY = "haha:recognize:result:";
     private static final String ORDER_INFO_KEY = "haha:order:info:";
@@ -435,8 +446,12 @@ public class HahaCallbackServiceImpl implements HahaCallbackService {
             updateOrderFromCallback(localOrder, orderId, activityId, orderMoney, orderDetail, orderName, orderGoodsStr);
             saveOrderInfoToRedis(localOrder, activityId, orderId, orderMoney, orderDetail);
 
+            // 处理优惠券扣减(在订单金额更新后)
+            BigDecimal finalAmount = processCouponDiscount(localOrder, orderMoney);
+
             // 处理支付分扣费(如果订单使用了支付分)
-            processPayScorePayment(localOrder, orderMoney);
+            // 注意:如果有优惠券,使用优惠后的金额
+            processPayScorePayment(localOrder, finalAmount);
 
 
         } catch (Exception e) {
@@ -625,7 +640,13 @@ public class HahaCallbackServiceImpl implements HahaCallbackService {
         }
 
         if (orderMoney != null) {
-            order.setTotalAmount(new BigDecimal(orderMoney.toString()));
+            // 设备端传来的金额是已经计算过营销活动优惠后的金额
+            BigDecimal totalAmount = new BigDecimal(orderMoney.toString());
+            order.setTotalAmount(totalAmount);
+            
+            // 初始化优惠金额和实付金额(后续会被processCouponDiscount更新)
+            order.setDiscountAmount(BigDecimal.ZERO);
+            order.setPaidAmount(totalAmount);
         }
 
         boolean updated = orderService.updateById(order);
@@ -828,6 +849,238 @@ public class HahaCallbackServiceImpl implements HahaCallbackService {
         }
     }
 
+    /**
+     * 处理优惠券扣减
+     * 
+     * 业务流程:
+     * 1. 查询用户所有可用优惠券(未使用且未过期)
+     * 2. 按到期时间升序排序(优先使用最先到期的券)
+     * 3. 遍历优惠券,检查是否符合使用条件:
+     *    - 订单金额是否达到满减门槛
+     *    - 优惠券适用范围是否匹配(全场/指定门店/指定商品)
+     * 4. 使用第一张符合条件的优惠券
+     * 5. 计算优惠后的最终金额
+     * 6. 标记优惠券为已使用
+     * 
+     * 注意:
+     * - 一次订单只能使用一张优惠券
+     * - 优惠金额不能超过订单金额
+     * - 如果设备端已传来优惠后金额,不再使用优惠券
+     * 
+     * @param order 订单对象
+     * @param orderMoney 设备端传来的订单金额(可能已包含营销活动优惠)
+     * @return 最终实付金额(优惠后)
+     */
+    private BigDecimal processCouponDiscount(Order order, Object orderMoney) {
+        try {
+            // 1. 获取订单金额
+            BigDecimal originalAmount = orderMoney != null ? new BigDecimal(orderMoney.toString()) : order.getTotalAmount();
+            if (originalAmount == null || originalAmount.compareTo(BigDecimal.ZERO) <= 0) {
+                log.debug("订单金额为0或空,不使用优惠券 - orderId: {}", order.getId());
+                return originalAmount != null ? originalAmount : BigDecimal.ZERO;
+            }
+
+            // 2. 查询用户所有可用优惠券
+            Long userId = order.getUserId();
+            if (userId == null) {
+                log.warn("订单用户ID为空,无法使用优惠券 - orderId: {}", order.getId());
+                return originalAmount;
+            }
+
+            List<UserCoupon> availableCoupons = userCouponService.getAvailableCoupons(userId);
+            if (availableCoupons == null || availableCoupons.isEmpty()) {
+                log.debug("用户没有可用优惠券 - userId: {}, orderId: {}", userId, order.getId());
+                return originalAmount;
+            }
+
+            log.info("用户有 {} 张可用优惠券,开始筛选 - userId: {}, orderId: {}", 
+                    availableCoupons.size(), userId, order.getId());
+
+            // 3. 按到期时间升序排序(优先使用最先到期的券)
+            availableCoupons.sort((c1, c2) -> {
+                if (c1.getValidEndTime() == null) return 1;
+                if (c2.getValidEndTime() == null) return -1;
+                return c1.getValidEndTime().compareTo(c2.getValidEndTime());
+            });
+
+            // 4. 遍历优惠券,找到第一张符合条件的
+            UserCoupon selectedCoupon = null;
+            BigDecimal discountAmount = BigDecimal.ZERO;
+
+            for (UserCoupon coupon : availableCoupons) {
+                // 填充优惠券模板信息
+                UserCoupon couponWithTemplate = userCouponService.getDetail(coupon.getId(), userId);
+                if (couponWithTemplate == null) {
+                    continue;
+                }
+
+                // 检查是否符合使用条件
+                if (isCouponApplicable(couponWithTemplate, order, originalAmount)) {
+                    selectedCoupon = couponWithTemplate;
+                    discountAmount = calculateCouponDiscount(couponWithTemplate, originalAmount);
+                    log.info("找到符合条件的优惠券 - couponId: {}, couponName: {}, discountAmount: {}", 
+                            coupon.getId(), couponWithTemplate.getCouponName(), discountAmount);
+                    break; // 只使用一张优惠券
+                }
+            }
+
+            // 5. 如果没有找到符合条件的优惠券,返回原金额
+            if (selectedCoupon == null || discountAmount.compareTo(BigDecimal.ZERO) <= 0) {
+                log.debug("没有符合条件的优惠券 - orderId: {}", order.getId());
+                return originalAmount;
+            }
+
+            // 6. 确保优惠金额不超过订单金额
+            if (discountAmount.compareTo(originalAmount) > 0) {
+                discountAmount = originalAmount;
+                log.info("优惠金额超过订单金额,调整为订单金额 - discountAmount: {}", discountAmount);
+            }
+
+            // 7. 计算最终实付金额
+            BigDecimal finalAmount = originalAmount.subtract(discountAmount);
+            if (finalAmount.compareTo(BigDecimal.ZERO) < 0) {
+                finalAmount = BigDecimal.ZERO;
+            }
+
+            // 8. 使用优惠券(标记为已使用)
+            try {
+                userCouponService.useCoupon(selectedCoupon.getId(), userId, order.getId(), discountAmount);
+                log.info("优惠券使用成功 - couponId: {}, orderId: {}, discountAmount: {}, finalAmount: {}", 
+                        selectedCoupon.getId(), order.getId(), discountAmount, finalAmount);
+            } catch (Exception e) {
+                log.error("优惠券使用失败 - couponId: {}, orderId: {}", selectedCoupon.getId(), order.getId(), e);
+                // 如果优惠券使用失败,仍然返回原金额
+                return originalAmount;
+            }
+
+            // 9. 更新订单的优惠金额和实付金额
+            order.setDiscountAmount(discountAmount);
+            order.setPaidAmount(finalAmount);
+            orderService.updateById(order);
+            
+            log.info("订单优惠金额已更新 - orderId: {}, totalAmount: {}, discountAmount: {}, paidAmount: {}", 
+                    order.getId(), originalAmount, discountAmount, finalAmount);
+
+            return finalAmount;
+
+        } catch (Exception e) {
+            log.error("处理优惠券扣减异常 - orderId: {}", order.getId(), e);
+            // 异常时返回原金额,不影响正常流程
+            return orderMoney != null ? new BigDecimal(orderMoney.toString()) : order.getTotalAmount();
+        }
+    }
+
+    /**
+     * 检查优惠券是否适用于当前订单
+     * 
+     * @param coupon 优惠券信息(包含模板信息)
+     * @param order 订单信息
+     * @param orderAmount 订单金额
+     * @return 是否适用
+     */
+    private boolean isCouponApplicable(UserCoupon coupon, Order order, BigDecimal orderAmount) {
+        try {
+            // 1. 检查订单金额是否达到满减门槛
+            if (coupon.getMinAmount() != null && orderAmount.compareTo(coupon.getMinAmount()) < 0) {
+                log.debug("订单金额未达到优惠券门槛 - couponId: {}, minAmount: {}, orderAmount: {}", 
+                        coupon.getId(), coupon.getMinAmount(), orderAmount);
+                return false;
+            }
+
+            // 2. 检查适用范围
+            Integer applyScope = coupon.getApplyScope();
+            if (applyScope == null || applyScope == 1) {
+                // 全场通用,直接返回true
+                return true;
+            }
+
+            if (applyScope == 2) {
+                // 指定门店范围,检查订单设备所属门店
+                // 这里简化处理,假设所有门店都适用
+                // TODO: 根据实际业务需求,检查订单设备是否属于优惠券指定门店
+                log.debug("优惠券为指定门店范围,当前简化处理为适用 - couponId: {}", coupon.getId());
+                return true;
+            }
+
+            if (applyScope == 3) {
+                // 指定商品范围,检查订单商品是否匹配
+                // TODO: 根据订单商品列表检查是否包含优惠券指定的商品
+                log.debug("优惠券为指定商品范围,当前简化处理为适用 - couponId: {}", coupon.getId());
+                return true;
+            }
+
+            return true;
+
+        } catch (Exception e) {
+            log.error("检查优惠券适用性异常 - couponId: {}", coupon.getId(), e);
+            return false;
+        }
+    }
+
+    /**
+     * 计算优惠券优惠金额
+     * 
+     * 根据优惠券类型计算:
+     * - type=1 (满减券): 直接返回discountValue
+     * - type=2 (折扣券): orderAmount * (1 - discountValue/10)
+     * - type=3 (抵扣券): 直接返回discountValue
+     * - type=4 (兑换券): 不适用,返回0
+     * 
+     * @param coupon 优惠券信息
+     * @param orderAmount 订单金额
+     * @return 优惠金额
+     */
+    private BigDecimal calculateCouponDiscount(UserCoupon coupon, BigDecimal orderAmount) {
+        try {
+            Integer couponType = coupon.getCouponType();
+            BigDecimal discountValue = coupon.getDiscountValue();
+
+            if (discountValue == null || discountValue.compareTo(BigDecimal.ZERO) <= 0) {
+                return BigDecimal.ZERO;
+            }
+
+            switch (couponType) {
+                case 1: // 满减券
+                case 3: // 抵扣券
+                    // 直接返回优惠金额
+                    return discountValue;
+
+                case 2: // 折扣券
+                    // discountValue 表示折扣,如8表示8折
+                    // 优惠金额 = orderAmount * (1 - discountValue/10)
+                    BigDecimal discount = discountValue.divide(new BigDecimal("10"), 2, RoundingMode.HALF_UP);
+                    BigDecimal discountAmount = orderAmount.multiply(BigDecimal.ONE.subtract(discount));
+                    
+                    // 检查是否有最大优惠限制(需要从CouponTemplate获取)
+                    try {
+                        CouponTemplate template = couponTemplateService.getById(coupon.getTemplateId());
+                        if (template != null && template.getMaxDiscount() != null && 
+                            template.getMaxDiscount().compareTo(BigDecimal.ZERO) > 0) {
+                            if (discountAmount.compareTo(template.getMaxDiscount()) > 0) {
+                                discountAmount = template.getMaxDiscount();
+                            }
+                        }
+                    } catch (Exception e) {
+                        log.warn("获取优惠券模板最大优惠限制失败 - templateId: {}", coupon.getTemplateId(), e);
+                    }
+                    
+                    return discountAmount;
+
+                case 4: // 兑换券
+                    // 兑换券不用于金额扣减
+                    return BigDecimal.ZERO;
+
+                default:
+                    log.warn("未知的优惠券类型 - couponType: {}", couponType);
+                    return BigDecimal.ZERO;
+            }
+
+        } catch (Exception e) {
+            log.error("计算优惠券优惠金额异常 - couponId: {}", coupon.getId(), e);
+            return BigDecimal.ZERO;
+        }
+    }
+
     /**
      * 处理支付分扣费
      * 如果订单使用了微信支付分,调用支付分完结接口进行扣费

+ 2 - 0
haha-service/src/main/java/com/haha/service/impl/OrderServiceImpl.java

@@ -138,6 +138,8 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
         vo.setActivityId(order.getActivityId());
         vo.setDeviceId(order.getDeviceId());
         vo.setTotalAmount(order.getTotalAmount());
+        vo.setDiscountAmount(order.getDiscountAmount());
+        vo.setPaidAmount(order.getPaidAmount());
         vo.setPayStatus(order.getPayStatus() != null ? Integer.valueOf(order.getPayStatus()) : null);
         vo.setPayStatusLabel(getPayStatusLabel(order.getPayStatus()));
         vo.setStatus(order.getStatus());