Procházet zdrojové kódy

优化订单积分流程

wang před 1 týdnem
rodič
revize
52cf88cafa

+ 93 - 12
yami-shop-api/src/main/java/com/yami/shop/api/listener/SubmitOrderListener.java

@@ -10,8 +10,10 @@
 
 package com.yami.shop.api.listener;
 
+import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.lang.Snowflake;
 import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.yami.shop.bean.app.dto.ShopCartItemDiscountDto;
 import com.yami.shop.bean.app.dto.ShopCartItemDto;
 import com.yami.shop.bean.app.dto.ShopCartOrderDto;
@@ -22,7 +24,6 @@ import com.yami.shop.bean.event.SubmitOrderEvent;
 import com.yami.shop.bean.model.*;
 import com.yami.shop.bean.order.SubmitOrderOrder;
 import com.yami.shop.common.exception.GlobalException;
-import com.yami.shop.common.exception.YamiShopBindException;
 import com.yami.shop.common.util.Arith;
 import com.yami.shop.dao.*;
 import com.yami.shop.security.api.util.SecurityUtils;
@@ -36,7 +37,10 @@ import org.springframework.context.event.EventListener;
 import org.springframework.core.annotation.Order;
 import org.springframework.stereotype.Component;
 
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
 import java.util.*;
+import java.util.stream.Collectors;
 
 /**
  * 确认订单信息时的默认操作
@@ -58,6 +62,7 @@ public class SubmitOrderListener {
 
     private final Snowflake snowflake;
 
+    private final PointsRecordMapper pointsRecordMapper;
     private final OrderItemMapper orderItemMapper;
 
     private final SkuMapper skuMapper;
@@ -98,20 +103,57 @@ public class SubmitOrderListener {
         userAddrOrder.setUserId(userId);
         userAddrOrder.setCreateTime(now);
         userAddrOrderService.save(userAddrOrder);
-
+        //所有当前用户可用的积分记录
+        List<PointsRecord> pointsRecords = pointsRecordMapper.selectList(new LambdaQueryWrapper<PointsRecord>()
+                        .eq(PointsRecord::getUserId, userId)
+                        .eq(PointsRecord::getChannelId, mergerOrder.getPlatform())
+                        .in(PointsRecord::getPointsType, Arrays.asList(1, 3))
+                        .in(PointsRecord::getPointsAudit, Arrays.asList(1, 2)).and(wrapper -> wrapper
+                                .gt(PointsRecord::getExpiryDate, LocalDateTime.now())
+                                .or()
+                                .isNull(PointsRecord::getExpiryDate)
+                        )).stream()
+                .sorted(Comparator
+                        .comparing(PointsRecord::getExpiryDate,
+                                Comparator.nullsLast(Comparator.naturalOrder()))
+                        .thenComparing(PointsRecord::getId))
+                .collect(Collectors.toList());
+        ;
+        // 将记录添加到队列中
+        ArrayDeque<PointsRecord> expiryQueue = new ArrayDeque<>(pointsRecords);
         // 订单地址id
         Long addrOrderId = userAddrOrder.getAddrOrderId();
 
         if (CollectionUtils.isNotEmpty(shopCartOrders)) {
             // 每个店铺生成一个订单
             for (ShopCartOrderDto shopCartOrderDto : shopCartOrders) {
-                Double sumPlatformAmount = 0.0;
+                //兑换人民币是分,必须要实时统计计算
+                Integer point = pointsRecordMapper.statisticsPoint(userId, mergerOrder.getPlatform());
                 // 使用雪花算法生成的订单号
                 String orderNumber = String.valueOf(snowflake.nextId());
                 shopCartOrderDto.setOrderNumber(orderNumber);
 
                 Long shopId = shopCartOrderDto.getShopId();
-
+                // 订单信息
+                com.yami.shop.bean.model.Order order = new com.yami.shop.bean.model.Order();
+                Double actualTotal = shopCartOrderDto.getActualTotal();
+                //企业用户才进这个判断,并且该用户的积分必须大于0
+                if (1 == mergerOrder.getPlatform() && null != point && point > 0) {
+                    // 计算商品金额和积分
+                    Double mul = Arith.mul(actualTotal, 100);
+                    Double vp = Double.valueOf(point);
+                    if (vp >= mul) {
+                        //积分完全足够支付
+                        actualTotal = 0.0;//剩下需要付的钱
+                        order.setOffsetPoints(Long.valueOf(String.valueOf(mul)));
+                    } else {
+                        //积分不够抵扣
+                        double sub = Arith.sub(mul, vp);
+                        //剩下需要付的钱
+                        actualTotal = sub / 100;
+                        order.setOffsetPoints(Long.valueOf(String.valueOf(sub)));
+                    }
+                }
                 // 订单商品名称
                 StringBuilder orderProdName = new StringBuilder(100);
                 List<OrderItem> orderItems = new ArrayList<>();
@@ -138,9 +180,19 @@ public class SubmitOrderListener {
                         orderItem.setBasketDate(shopCartItem.getBasketDate());
                         //平台的补贴优惠金额
                         orderItem.setPlatformShareReduce(shopCartItem.getPlatformShareReduce());
+                        Double actualItem = shopCartItem.getActualTotal();
+                        //后台充值的积分
+                        if (1 == mergerOrder.getPlatform() && null != point && point > 0) {
+                            //把钱换算成积分
+                            actualItem = this.doGetOrderItemPoints(Arith.mul(actualItem, 100), expiryQueue, orderNumber);
+                            //把积分换算成钱
+                            if (actualItem > 0) {
+                                actualItem = Arith.div(actualItem, 100);
+                            }
+                        }
                         // 实际订单项支付金额
-                        //TODO wangjian 根据platform来计算是否优先扣减积分抵扣
-                        orderItem.setActualTotal(shopCartItem.getActualTotal());
+                        // 根据platform来计算是否优先扣减积分抵扣
+                        orderItem.setActualTotal(actualItem);
                         // 分摊优惠金额
                         orderItem.setShareReduce(shopCartItem.getShareReduce());
                         orderProdName.append(orderItem.getProdName()).append(",");
@@ -162,8 +214,6 @@ public class SubmitOrderListener {
                     orderProdName.deleteCharAt(orderProdName.length() - 1);
                 }
 
-                // 订单信息
-                com.yami.shop.bean.model.Order order = new com.yami.shop.bean.model.Order();
 
                 order.setShopId(shopId);
                 order.setOrderNumber(orderNumber);
@@ -174,8 +224,7 @@ public class SubmitOrderListener {
                 // 商品总额
                 order.setTotal(shopCartOrderDto.getTotal());
                 // 实际总额
-                //TODO wangjian 根据platform来计算是否优先扣减积分抵扣
-                order.setActualTotal(shopCartOrderDto.getActualTotal());
+                order.setActualTotal(actualTotal);
                 order.setStatus(OrderStatus.UNPAY.value());
                 order.setChannelId(Long.valueOf(mergerOrder.getPlatform()));
                 order.setUpdateTime(now);
@@ -217,7 +266,7 @@ public class SubmitOrderListener {
             basketMapper.deleteShopCartItemsByBasketIds(userId, basketIds);
         }
 
-//TODO wangjia 修改本地库存
+//        现在不用维护库存
 //        // 更新sku库存
 //        skuStocksMap.forEach((key, sku) -> {
 //
@@ -228,7 +277,7 @@ public class SubmitOrderListener {
 //            }
 //        });
 //
-////         更新商品库存
+//        //更新商品库存
 //        prodStocksMap.forEach((prodId, prod) -> {
 //
 //            if (productMapper.updateStocks(prod) == 0) {
@@ -331,4 +380,36 @@ public class SubmitOrderListener {
     }
 
 
+    private Double doGetOrderItemPoints(Double actualTotal, ArrayDeque<PointsRecord> expiryQueue, String orderNumber) {
+        PointsRecord pointsRecord = expiryQueue.poll();
+        //判定可用金额
+        BigDecimal points = pointsRecord.getPoints();
+        BigDecimal variablePoints = pointsRecord.getVariablePoints();
+        //充值记录中可用的记录
+        Double available = points.subtract(variablePoints).doubleValue();
+        actualTotal = Arith.sub(actualTotal, available);
+        pointsRecord.setVariablePoints(BigDecimal.valueOf(available));
+        if (available > actualTotal) {
+            //钱已经抵扣完,但是当条积分还有剩余,所以不要重新把剩余的放入队列
+            //更改pointsRecord的变动的值,并修改数据库
+            expiryQueue.addFirst(pointsRecord);
+        }
+        int update = pointsRecordMapper.updateById(pointsRecord);
+        if (update > 0) {
+            PointsRecord pr = new PointsRecord();
+            BeanUtil.copyProperties(pointsRecord, pr);
+            pr.setId(null);
+            pr.setOrderNumber(orderNumber);
+        }
+        // TODO 插入数据库
+        if (actualTotal > available) {
+            //把当前积分已完全够扣,但是还有钱
+            actualTotal = this.doGetOrderItemPoints(actualTotal, expiryQueue, orderNumber);
+        } else {
+            //恰好积分等于钱
+//            actualTotal = Arith.sub(actualTotal, available);
+        }
+        return actualTotal;
+    }
+
 }

+ 3 - 0
yami-shop-bean/src/main/java/com/yami/shop/bean/model/Order.java

@@ -267,6 +267,9 @@ public class Order implements Serializable {
     @TableField(exist = false)
     private Integer payScore;
 
+    @TableField(exist = false)
+    private Long offsetPoints;
+
 
 
 

+ 2 - 2
yami-shop-bean/src/main/java/com/yami/shop/bean/model/PointsRecord.java

@@ -38,9 +38,9 @@ public class PointsRecord implements Serializable {
     private Long channelId;
 
     /**
-     * 订单ID
+     * 订单编号
      */
-    private Long orderId;
+    private String orderNumber;
 
     /**
      * 充值积分ID

+ 2 - 0
yami-shop-service/src/main/java/com/yami/shop/dao/PointsRecordMapper.java

@@ -3,6 +3,7 @@ package com.yami.shop.dao;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.yami.shop.bean.model.PointsRecord;
+import org.springframework.data.repository.query.Param;
 
 /**
  * 积分记录详细 Mapper接口
@@ -11,5 +12,6 @@ import com.yami.shop.bean.model.PointsRecord;
  * @since 2025-09-XX
  */
 public interface PointsRecordMapper extends BaseMapper<PointsRecord> {
+    Integer statisticsPoint(@Param("userId") String userId, @Param("platform") Integer platform);
 
 }

+ 19 - 0
yami-shop-service/src/main/resources/mapper/PointsRecordMapper.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.yami.shop.dao.PointsRecordMapper">
+
+    <select id="statisticsPoint" resultType="java.lang.Integer">
+        SELECT SUM(
+                       CASE
+                           WHEN points_audit = 1 THEN points
+                           WHEN points_audit = 2 THEN points - variable_points
+                           END
+               ) AS total_available_points
+        FROM tz_points_record
+        WHERE user_id = #{userId}
+          AND channel_id = #{platform}
+          AND points_type IN (1, 3)
+          AND points_audit IN (1, 2)
+          AND (expiry_date > NOW() OR expiry_date IS NULL)
+    </select>
+</mapper>