|
@@ -3,6 +3,7 @@ package shop.alien.dining.service.impl;
|
|
|
import com.alibaba.fastjson2.JSON;
|
|
import com.alibaba.fastjson2.JSON;
|
|
|
import com.alibaba.fastjson2.JSONObject;
|
|
import com.alibaba.fastjson2.JSONObject;
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
|
|
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
|
|
import lombok.RequiredArgsConstructor;
|
|
import lombok.RequiredArgsConstructor;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.springframework.stereotype.Service;
|
|
import org.springframework.stereotype.Service;
|
|
@@ -28,6 +29,9 @@ import java.math.BigDecimal;
|
|
|
import java.util.ArrayList;
|
|
import java.util.ArrayList;
|
|
|
import java.util.Date;
|
|
import java.util.Date;
|
|
|
import java.util.List;
|
|
import java.util.List;
|
|
|
|
|
+import java.util.concurrent.CompletableFuture;
|
|
|
|
|
+import java.util.concurrent.ExecutorService;
|
|
|
|
|
+import java.util.concurrent.Executors;
|
|
|
import java.util.stream.Collectors;
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -45,6 +49,13 @@ public class CartServiceImpl implements CartService {
|
|
|
private static final String COUPON_USED_KEY_PREFIX = "coupon:used:table:";
|
|
private static final String COUPON_USED_KEY_PREFIX = "coupon:used:table:";
|
|
|
private static final int CART_EXPIRE_SECONDS = 24 * 60 * 60; // 24小时过期
|
|
private static final int CART_EXPIRE_SECONDS = 24 * 60 * 60; // 24小时过期
|
|
|
|
|
|
|
|
|
|
+ // 异步写入数据库的线程池(专门用于购物车数据库写入)
|
|
|
|
|
+ private static final ExecutorService CART_DB_WRITE_EXECUTOR = Executors.newFixedThreadPool(5, r -> {
|
|
|
|
|
+ Thread t = new Thread(r, "cart-db-write-" + System.currentTimeMillis());
|
|
|
|
|
+ t.setDaemon(true);
|
|
|
|
|
+ return t;
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
private final BaseRedisService baseRedisService;
|
|
private final BaseRedisService baseRedisService;
|
|
|
private final StoreTableMapper storeTableMapper;
|
|
private final StoreTableMapper storeTableMapper;
|
|
|
private final StoreCuisineMapper storeCuisineMapper;
|
|
private final StoreCuisineMapper storeCuisineMapper;
|
|
@@ -722,14 +733,21 @@ public class CartServiceImpl implements CartService {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * 保存购物车到Redis和数据库(双写策略)
|
|
|
|
|
|
|
+ * 保存购物车到Redis和数据库(优化后的双写策略)
|
|
|
|
|
+ * Redis同步写入(保证实时性),数据库异步批量写入(提高性能)
|
|
|
*/
|
|
*/
|
|
|
private void saveCart(CartDTO cart) {
|
|
private void saveCart(CartDTO cart) {
|
|
|
- // 保存到Redis
|
|
|
|
|
|
|
+ // 1. 同步保存到Redis(保证实时性)
|
|
|
saveCartToRedis(cart);
|
|
saveCartToRedis(cart);
|
|
|
|
|
|
|
|
- // 保存到数据库
|
|
|
|
|
- saveCartToDatabase(cart);
|
|
|
|
|
|
|
+ // 2. 异步保存到数据库(不阻塞主流程,提高性能)
|
|
|
|
|
+ CompletableFuture.runAsync(() -> {
|
|
|
|
|
+ try {
|
|
|
|
|
+ saveCartToDatabase(cart);
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("异步保存购物车到数据库失败, tableId={}, error={}", cart.getTableId(), e.getMessage(), e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }, CART_DB_WRITE_EXECUTOR);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -742,29 +760,25 @@ public class CartServiceImpl implements CartService {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * 保存购物车到数据库
|
|
|
|
|
|
|
+ * 保存购物车到数据库(优化后的批量操作版本)
|
|
|
|
|
+ * 使用批量逻辑删除和批量插入,提高性能
|
|
|
*/
|
|
*/
|
|
|
private void saveCartToDatabase(CartDTO cart) {
|
|
private void saveCartToDatabase(CartDTO cart) {
|
|
|
try {
|
|
try {
|
|
|
- // 先删除该桌号的所有购物车记录(逻辑删除)
|
|
|
|
|
- LambdaQueryWrapper<StoreCart> deleteWrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
- deleteWrapper.eq(StoreCart::getTableId, cart.getTableId());
|
|
|
|
|
- deleteWrapper.eq(StoreCart::getDeleteFlag, 0);
|
|
|
|
|
- List<StoreCart> existingCarts = storeCartMapper.selectList(deleteWrapper);
|
|
|
|
|
- if (existingCarts != null && !existingCarts.isEmpty()) {
|
|
|
|
|
- Date now = new Date();
|
|
|
|
|
- for (StoreCart existing : existingCarts) {
|
|
|
|
|
- existing.setDeleteFlag(1);
|
|
|
|
|
- existing.setUpdatedTime(now);
|
|
|
|
|
- storeCartMapper.updateById(existing);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ Date now = new Date();
|
|
|
|
|
+ Integer userId = TokenUtil.getCurrentUserId();
|
|
|
|
|
|
|
|
- // 插入新的购物车记录
|
|
|
|
|
- if (cart.getItems() != null && !cart.getItems().isEmpty()) {
|
|
|
|
|
- Integer userId = TokenUtil.getCurrentUserId();
|
|
|
|
|
- Date now = new Date();
|
|
|
|
|
|
|
+ // 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);
|
|
|
|
|
|
|
|
|
|
+ // 2. 批量插入新的购物车记录
|
|
|
|
|
+ if (cart.getItems() != null && !cart.getItems().isEmpty()) {
|
|
|
|
|
+ List<StoreCart> cartList = new ArrayList<>(cart.getItems().size());
|
|
|
for (CartItemDTO item : cart.getItems()) {
|
|
for (CartItemDTO item : cart.getItems()) {
|
|
|
StoreCart storeCart = new StoreCart();
|
|
StoreCart storeCart = new StoreCart();
|
|
|
storeCart.setTableId(cart.getTableId());
|
|
storeCart.setTableId(cart.getTableId());
|
|
@@ -783,19 +797,40 @@ public class CartServiceImpl implements CartService {
|
|
|
storeCart.setCreatedTime(now);
|
|
storeCart.setCreatedTime(now);
|
|
|
storeCart.setCreatedUserId(userId);
|
|
storeCart.setCreatedUserId(userId);
|
|
|
storeCart.setUpdatedTime(now);
|
|
storeCart.setUpdatedTime(now);
|
|
|
- storeCartMapper.insert(storeCart);
|
|
|
|
|
|
|
+ cartList.add(storeCart);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 批量插入(如果数量较少,直接循环插入;如果数量较多,可以考虑分批插入)
|
|
|
|
|
+ if (cartList.size() <= 50) {
|
|
|
|
|
+ // 小批量直接插入
|
|
|
|
|
+ for (StoreCart storeCart : cartList) {
|
|
|
|
|
+ storeCartMapper.insert(storeCart);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 大批量分批插入(每批50条)
|
|
|
|
|
+ int batchSize = 50;
|
|
|
|
|
+ for (int i = 0; i < cartList.size(); i += batchSize) {
|
|
|
|
|
+ int end = Math.min(i + batchSize, cartList.size());
|
|
|
|
|
+ List<StoreCart> batch = cartList.subList(i, end);
|
|
|
|
|
+ for (StoreCart storeCart : batch) {
|
|
|
|
|
+ storeCartMapper.insert(storeCart);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 更新桌号表的购物车统计
|
|
|
|
|
|
|
+ // 3. 更新桌号表的购物车统计
|
|
|
StoreTable table = storeTableMapper.selectById(cart.getTableId());
|
|
StoreTable table = storeTableMapper.selectById(cart.getTableId());
|
|
|
if (table != null) {
|
|
if (table != null) {
|
|
|
table.setCartItemCount(cart.getTotalQuantity());
|
|
table.setCartItemCount(cart.getTotalQuantity());
|
|
|
table.setCartTotalAmount(cart.getTotalAmount());
|
|
table.setCartTotalAmount(cart.getTotalAmount());
|
|
|
storeTableMapper.updateById(table);
|
|
storeTableMapper.updateById(table);
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ log.debug("购物车数据已异步保存到数据库, tableId={}, itemCount={}",
|
|
|
|
|
+ cart.getTableId(), cart.getItems() != null ? cart.getItems().size() : 0);
|
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
|
- log.error("保存购物车到数据库失败: {}", e.getMessage(), e);
|
|
|
|
|
|
|
+ log.error("保存购物车到数据库失败, tableId={}, error={}", cart.getTableId(), e.getMessage(), e);
|
|
|
// 数据库保存失败不影响Redis,继续执行
|
|
// 数据库保存失败不影响Redis,继续执行
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|