Forráskód Böngészése

优化清空购物车逻辑 之清空未下单部分

lutong 2 hónapja
szülő
commit
b2f6dfc30c

+ 1 - 11
alien-dining/src/main/java/shop/alien/dining/controller/StoreOrderController.java

@@ -142,17 +142,7 @@ public class StoreOrderController {
                 return R.fail("用户未登录");
             }
             
-            // 检查购物车中是否有锁定数量的商品(已下单的商品)
-            CartDTO cart = cartService.getCart(tableId);
-            if (cart.getItems() != null && !cart.getItems().isEmpty()) {
-                boolean hasLockedItems = cart.getItems().stream()
-                        .anyMatch(item -> item.getLockedQuantity() != null && item.getLockedQuantity() > 0);
-                if (hasLockedItems) {
-                    return R.fail("购物车中有已下单的商品,无法清空。请先取消订单或等待订单完成后再清空。");
-                }
-            }
-            
-            // 清空购物车
+            // 清空购物车(会自动保留已下单的商品)
             cartService.clearCart(tableId);
             
             // 创建空的购物车对象用于返回和推送

+ 130 - 49
alien-dining/src/main/java/shop/alien/dining/service/impl/CartServiceImpl.java

@@ -3,7 +3,6 @@ package shop.alien.dining.service.impl;
 import com.alibaba.fastjson2.JSON;
 import com.alibaba.fastjson2.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
@@ -206,17 +205,17 @@ public class CartServiceImpl implements CartService {
             // 商品已存在
             Integer lockedQuantity = existingItem.getLockedQuantity();
             if (lockedQuantity != null && lockedQuantity > 0) {
-                // 如果商品有锁定数量(已下单),将新数量叠加到当前数量和锁定数量上
+                // 如果商品有已下单数量,将新数量叠加到当前数量和已下单数量上
                 Integer newQuantity = existingItem.getQuantity() + dto.getQuantity();
                 Integer newLockedQuantity = lockedQuantity + dto.getQuantity();
                 existingItem.setQuantity(newQuantity);
                 existingItem.setLockedQuantity(newLockedQuantity);
                 existingItem.setSubtotalAmount(existingItem.getUnitPrice()
                         .multiply(BigDecimal.valueOf(newQuantity)));
-                log.info("商品已存在且有锁定数量,叠加数量, cuisineId={}, oldQuantity={}, newQuantity={}, oldLockedQuantity={}, newLockedQuantity={}", 
+                log.info("商品已存在且有已下单数量,叠加数量, cuisineId={}, oldQuantity={}, newQuantity={}, oldOrderedQuantity={}, newOrderedQuantity={}", 
                         dto.getCuisineId(), existingItem.getQuantity() - dto.getQuantity(), newQuantity, lockedQuantity, newLockedQuantity);
             } else {
-                // 商品已存在但没有锁定数量,不允许重复添加
+                // 商品已存在但没有已下单数量,不允许重复添加
                 throw new RuntimeException("已添加过本商品");
             }
         } else {
@@ -270,11 +269,11 @@ public class CartServiceImpl implements CartService {
                 .orElse(null);
 
         if (item != null) {
-            // 检查锁定数量:不允许将数量减少到小于锁定数量
+            // 检查已下单数量:不允许将数量减少到小于已下单数量
             Integer lockedQuantity = item.getLockedQuantity();
             if (lockedQuantity != null && lockedQuantity > 0) {
                 if (quantity < lockedQuantity) {
-                    throw new RuntimeException("商品数量不能少于锁定数量(" + lockedQuantity + "),该数量已下单");
+                    throw new RuntimeException("商品数量不能少于已下单数量(" + lockedQuantity + "),该数量已下单");
                 }
             }
             
@@ -304,7 +303,7 @@ public class CartServiceImpl implements CartService {
         CartDTO cart = getCart(tableId);
         List<CartItemDTO> items = cart.getItems();
         
-        // 检查是否有锁定数量,如果有则不允许删除
+        // 检查是否有已下单数量,如果有则不允许删除
         CartItemDTO item = items.stream()
                 .filter(i -> i.getCuisineId().equals(cuisineId))
                 .findFirst()
@@ -313,7 +312,7 @@ public class CartServiceImpl implements CartService {
         if (item != null) {
             Integer lockedQuantity = item.getLockedQuantity();
             if (lockedQuantity != null && lockedQuantity > 0) {
-                throw new RuntimeException("商品已下单,锁定数量为 " + lockedQuantity + ",不允许删除");
+                throw new RuntimeException("商品已下单,已下单数量为 " + lockedQuantity + ",不允许删除");
             }
         }
         
@@ -335,30 +334,107 @@ public class CartServiceImpl implements CartService {
 
     @Override
     public void clearCart(Integer tableId) {
-        log.info("清空购物车, tableId={}", tableId);
-        // 清空Redis
-        String cartKey = CART_KEY_PREFIX + tableId;
-        baseRedisService.delete(cartKey);
-
-        // 清空数据库(逻辑删除)
-        LambdaQueryWrapper<StoreCart> wrapper = new LambdaQueryWrapper<>();
-        wrapper.eq(StoreCart::getTableId, tableId);
-        wrapper.eq(StoreCart::getDeleteFlag, 0);
-        List<StoreCart> cartList = storeCartMapper.selectList(wrapper);
-        if (cartList != null && !cartList.isEmpty()) {
-            for (StoreCart cart : cartList) {
-                cart.setDeleteFlag(1);
-                cart.setUpdatedTime(new Date());
-                storeCartMapper.updateById(cart);
+        log.info("清空购物车(保留已下单商品), tableId={}", tableId);
+        
+        // 获取购物车
+        CartDTO cart = getCart(tableId);
+        List<CartItemDTO> items = cart.getItems();
+        
+        if (items == null || items.isEmpty()) {
+            log.info("购物车为空,无需清空, tableId={}", tableId);
+            return;
+        }
+        
+        // 分离已下单的商品和未下单的商品
+        List<CartItemDTO> orderedItems = new ArrayList<>(); // 已下单的商品(保留,数量恢复为已下单数量)
+        List<Integer> orderedCuisineIds = new ArrayList<>(); // 已下单的商品ID列表
+        List<CartItemDTO> unorderedItems = new ArrayList<>(); // 未下单的商品(删除)
+        boolean hasChanges = false; // 是否有变化(需要更新)
+        
+        for (CartItemDTO item : items) {
+            Integer lockedQuantity = item.getLockedQuantity();
+            if (lockedQuantity != null && lockedQuantity > 0) {
+                // 有已下单数量,保留该商品,但将当前数量恢复为已下单数量
+                Integer currentQuantity = item.getQuantity();
+                if (currentQuantity != null && !currentQuantity.equals(lockedQuantity)) {
+                    // 当前数量不等于已下单数量,需要恢复
+                    item.setQuantity(lockedQuantity);
+                    item.setSubtotalAmount(item.getUnitPrice().multiply(BigDecimal.valueOf(lockedQuantity)));
+                    hasChanges = true;
+                    log.info("恢复已下单商品数量, cuisineId={}, oldQuantity={}, orderedQuantity={}", 
+                            item.getCuisineId(), currentQuantity, lockedQuantity);
+                }
+                orderedItems.add(item);
+                orderedCuisineIds.add(item.getCuisineId());
+            } else {
+                // 没有已下单数量,标记为删除
+                unorderedItems.add(item);
+                hasChanges = true;
             }
         }
-
-        // 更新桌号表的购物车统计
-        StoreTable table = storeTableMapper.selectById(tableId);
-        if (table != null) {
-            table.setCartItemCount(0);
-            table.setCartTotalAmount(BigDecimal.ZERO);
-            storeTableMapper.updateById(table);
+        
+        // 如果有变化(有未下单的商品需要删除,或者已下单商品数量需要恢复),进行更新
+        if (hasChanges) {
+            // 1. 更新购物车(删除未下单商品,已下单商品数量已恢复)
+            cart.setItems(orderedItems);
+            // 重新计算总金额和总数量(只计算保留的商品,数量已恢复为已下单数量)
+            BigDecimal totalAmount = orderedItems.stream()
+                    .map(CartItemDTO::getSubtotalAmount)
+                    .reduce(BigDecimal.ZERO, BigDecimal::add);
+            Integer totalQuantity = orderedItems.stream()
+                    .mapToInt(CartItemDTO::getQuantity)
+                    .sum();
+            cart.setTotalAmount(totalAmount);
+            cart.setTotalQuantity(totalQuantity);
+            
+            // 更新Redis(保留已下单的商品,数量已恢复)
+            if (orderedItems.isEmpty()) {
+                // 如果所有商品都未下单,清空Redis
+                String cartKey = CART_KEY_PREFIX + tableId;
+                baseRedisService.delete(cartKey);
+            } else {
+                // 保存更新后的购物车到Redis(已下单商品数量已恢复为已下单数量)
+                saveCartToRedis(cart);
+            }
+            
+            // 2. 从数据库中逻辑删除未下单的商品
+            if (!unorderedItems.isEmpty()) {
+                LambdaQueryWrapper<StoreCart> wrapper = new LambdaQueryWrapper<>();
+                wrapper.eq(StoreCart::getTableId, tableId);
+                wrapper.eq(StoreCart::getDeleteFlag, 0);
+                if (!orderedCuisineIds.isEmpty()) {
+                    // 排除已下单的商品ID
+                    wrapper.notIn(StoreCart::getCuisineId, orderedCuisineIds);
+                }
+                List<StoreCart> cartListToDelete = storeCartMapper.selectList(wrapper);
+                if (cartListToDelete != null && !cartListToDelete.isEmpty()) {
+                    List<Integer> cartIds = cartListToDelete.stream()
+                            .map(StoreCart::getId)
+                            .collect(Collectors.toList());
+                    // 使用 deleteBatchIds 进行逻辑删除(MyBatis-Plus 会自动处理 @TableLogic)
+                    storeCartMapper.deleteBatchIds(cartIds);
+                    log.info("删除未下单商品, tableId={}, count={}", tableId, cartIds.size());
+                }
+            }
+            
+            // 3. 更新数据库中已下单商品的数量(恢复为已下单数量)
+            if (!orderedItems.isEmpty()) {
+                // 保存更新后的购物车到数据库(会更新已下单商品的数量)
+                saveCartToDatabase(cart);
+            }
+            
+            // 4. 更新桌号表的购物车统计
+            StoreTable table = storeTableMapper.selectById(tableId);
+            if (table != null) {
+                table.setCartItemCount(totalQuantity);
+                table.setCartTotalAmount(totalAmount);
+                storeTableMapper.updateById(table);
+            }
+            
+            log.info("清空购物车完成(保留已下单商品,数量恢复为已下单数量), tableId={}, 删除商品数={}, 保留商品数={}", 
+                    tableId, unorderedItems.size(), orderedItems.size());
+        } else {
+            log.info("购物车无需更新, tableId={}", tableId);
         }
     }
 
@@ -494,18 +570,18 @@ public class CartServiceImpl implements CartService {
         String couponUsedKey = COUPON_USED_KEY_PREFIX + tableId;
         baseRedisService.delete(couponUsedKey);
 
-        // 更新数据库(逻辑删除未下单的记录)
+        // 更新数据库(逻辑删除未下单的记录,使用 MyBatis-Plus 的 deleteBatchIds
         LambdaQueryWrapper<StoreCouponUsage> wrapper = new LambdaQueryWrapper<>();
         wrapper.eq(StoreCouponUsage::getTableId, tableId);
         wrapper.eq(StoreCouponUsage::getDeleteFlag, 0);
         wrapper.eq(StoreCouponUsage::getUsageStatus, 0); // 只删除已标记使用但未下单的
         List<StoreCouponUsage> usageList = storeCouponUsageMapper.selectList(wrapper);
         if (usageList != null && !usageList.isEmpty()) {
-            for (StoreCouponUsage usage : usageList) {
-                usage.setDeleteFlag(1);
-                usage.setUpdatedTime(new Date());
-                storeCouponUsageMapper.updateById(usage);
-            }
+            List<Integer> usageIds = usageList.stream()
+                    .map(StoreCouponUsage::getId)
+                    .collect(Collectors.toList());
+            // 使用 deleteBatchIds 进行逻辑删除(MyBatis-Plus 会自动处理 @TableLogic)
+            storeCouponUsageMapper.deleteBatchIds(usageIds);
         }
 
         // 更新桌号表的优惠券ID
@@ -690,7 +766,7 @@ public class CartServiceImpl implements CartService {
 
     @Override
     public CartDTO lockCartItems(Integer tableId) {
-        log.info("锁定购物车商品数量, tableId={}", tableId);
+        log.info("锁定购物车商品数量(设置已下单数量), tableId={}", tableId);
         
         // 获取购物车
         CartDTO cart = getCart(tableId);
@@ -701,7 +777,7 @@ public class CartServiceImpl implements CartService {
             return cart;
         }
         
-        // 遍历所有商品,将当前数量设置为锁定数量
+        // 遍历所有商品,将当前数量设置为已下单数量
         boolean hasChanges = false;
         for (CartItemDTO item : items) {
             Integer currentQuantity = item.getQuantity();
@@ -709,16 +785,16 @@ public class CartServiceImpl implements CartService {
             
             if (currentQuantity != null && currentQuantity > 0) {
                 if (lockedQuantity == null || lockedQuantity == 0) {
-                    // 如果还没有锁定数量,将当前数量设置为锁定数量
+                    // 如果还没有已下单数量,将当前数量设置为已下单数量
                     item.setLockedQuantity(currentQuantity);
                     hasChanges = true;
-                    log.info("锁定商品数量, cuisineId={}, quantity={}", item.getCuisineId(), currentQuantity);
+                    log.info("设置商品已下单数量, cuisineId={}, orderedQuantity={}", item.getCuisineId(), currentQuantity);
                 } else if (currentQuantity > lockedQuantity) {
-                    // 如果已有锁定数量,且当前数量大于锁定数量(再次下单的情况),将新增数量累加到锁定数量
+                    // 如果已有已下单数量,且当前数量大于已下单数量(再次下单的情况),将新增数量累加到已下单数量
                     Integer newLockedQuantity = lockedQuantity + (currentQuantity - lockedQuantity);
                     item.setLockedQuantity(newLockedQuantity);
                     hasChanges = true;
-                    log.info("更新锁定商品数量, cuisineId={}, oldLockedQuantity={}, newLockedQuantity={}", 
+                    log.info("更新商品已下单数量, cuisineId={}, oldOrderedQuantity={}, newOrderedQuantity={}", 
                             item.getCuisineId(), lockedQuantity, newLockedQuantity);
                 }
             }
@@ -768,13 +844,18 @@ public class CartServiceImpl implements CartService {
             Date now = new Date();
             Integer userId = TokenUtil.getCurrentUserId();
 
-            // 1. 批量逻辑删除该桌号的所有购物车记录(使用批量更新,而不是循环单个更新)
-            LambdaUpdateWrapper<StoreCart> deleteWrapper = new LambdaUpdateWrapper<>();
-            deleteWrapper.eq(StoreCart::getTableId, cart.getTableId())
-                    .eq(StoreCart::getDeleteFlag, 0)
-                    .set(StoreCart::getDeleteFlag, 1)
-                    .set(StoreCart::getUpdatedTime, now);
-            storeCartMapper.update(null, deleteWrapper);
+            // 1. 批量逻辑删除该桌号的所有购物车记录(使用 MyBatis-Plus 的 deleteBatchIds)
+            LambdaQueryWrapper<StoreCart> queryWrapper = new LambdaQueryWrapper<>();
+            queryWrapper.eq(StoreCart::getTableId, cart.getTableId())
+                    .eq(StoreCart::getDeleteFlag, 0);
+            List<StoreCart> existingCartList = storeCartMapper.selectList(queryWrapper);
+            if (existingCartList != null && !existingCartList.isEmpty()) {
+                List<Integer> cartIds = existingCartList.stream()
+                        .map(StoreCart::getId)
+                        .collect(Collectors.toList());
+                // 使用 deleteBatchIds 进行逻辑删除(MyBatis-Plus 会自动处理 @TableLogic)
+                storeCartMapper.deleteBatchIds(cartIds);
+            }
 
             // 2. 批量插入新的购物车记录
             if (cart.getItems() != null && !cart.getItems().isEmpty()) {

+ 1 - 1
alien-entity/src/main/java/shop/alien/entity/store/StoreCart.java

@@ -54,7 +54,7 @@ public class StoreCart {
     @TableField("quantity")
     private Integer quantity;
 
-    @ApiModelProperty(value = "锁定数量(下单时锁定的数量,不允许减少或删除)")
+    @ApiModelProperty(value = "已下单数量(下单时锁定的数量,不允许减少或删除)")
     @TableField("locked_quantity")
     private Integer lockedQuantity;
 

+ 1 - 1
alien-entity/src/main/java/shop/alien/entity/store/dto/CartItemDTO.java

@@ -34,7 +34,7 @@ public class CartItemDTO {
     @ApiModelProperty(value = "数量")
     private Integer quantity;
 
-    @ApiModelProperty(value = "锁定数量(下单时锁定的数量,不允许减少或删除)")
+    @ApiModelProperty(value = "已下单数量(下单时锁定的数量,不允许减少或删除)")
     private Integer lockedQuantity;
 
     @ApiModelProperty(value = "小计金额")