package com.webmanage.service.impl; import com.webmanage.common.BusinessException; import com.webmanage.dto.CartItemDTO; import com.webmanage.entity.Cart; import com.webmanage.entity.ProductPricing; import com.webmanage.mapper.CartMapper; import com.webmanage.mapper.ProductPricingMapper; import com.webmanage.service.CartService; import com.webmanage.service.CartPersistenceService; import com.webmanage.vo.CartItemVO; import com.webmanage.vo.CartVO; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; import com.webmanage.config.CartProperties; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.format.datetime.DateFormatter; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import javax.annotation.Resource; import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; /** * 购物车服务实现类 */ @Slf4j @Service public class CartServiceImpl implements CartService { @Resource private RedisTemplate redisTemplate; @Resource private CartMapper cartMapper; @Resource private ProductPricingMapper productPricingMapper; @Resource private CartPersistenceService cartPersistenceService; @Resource private CartProperties cartProperties; // Redis key前缀 private static final String CART_KEY_PREFIX = "cart:"; private static final String CART_ITEM_KEY_PREFIX = "cart_item:"; private static final int CART_EXPIRE_DAYS = 30; // 购物车过期时间30天 @Override @Transactional(rollbackFor = Exception.class) public boolean addToCart(String userId, String unitId, CartItemDTO cartItemDTO) { try { // 验证商品定价是否存在 ProductPricing pricing = productPricingMapper.selectById(cartItemDTO.getPricingId()); if (pricing == null) { throw new BusinessException("商品定价不存在"); } // 构建购物车key String cartKey = buildCartKey(userId,unitId, cartItemDTO.getProductId()); String cartItemKey = buildCartItemKey(userId, unitId, cartItemDTO.getProductId(),cartItemDTO.getPricingId()); // 检查商品是否已在购物车中 CartItemVO existingItem = (CartItemVO) redisTemplate.opsForValue().get(cartItemKey); if (existingItem != null) { // 如果已存在,更新数量 existingItem.setQuantity(existingItem.getQuantity() + cartItemDTO.getQuantity()); existingItem.setTotalPrice(existingItem.getUnitPrice().multiply(BigDecimal.valueOf(existingItem.getQuantity()))); existingItem.setUpdateTime(LocalDateTime.now()); } else { // 如果不存在,创建新的购物车项 existingItem = new CartItemVO(); BeanUtils.copyProperties(cartItemDTO, existingItem); existingItem.setAddTime(LocalDateTime.now()); existingItem.setUpdateTime(LocalDateTime.now()); // 计算小计金额 if (existingItem.getTotalPrice() == null) { existingItem.setTotalPrice(existingItem.getUnitPrice().multiply(BigDecimal.valueOf(existingItem.getQuantity()))); } } // 保存到Redis redisTemplate.opsForValue().set(cartItemKey, existingItem, (cartProperties.getExpireDays() != null ? cartProperties.getExpireDays() : CART_EXPIRE_DAYS), TimeUnit.DAYS); // 更新购物车商品列表 updateCartItemList(userId, unitId, cartItemDTO.getProductId(), cartItemDTO.getPricingId(), true); // 设置购物车过期时间 redisTemplate.expire(cartKey, (cartProperties.getExpireDays() != null ? cartProperties.getExpireDays() : CART_EXPIRE_DAYS), TimeUnit.DAYS); // 异步持久化到数据库(根据配置) if (Boolean.TRUE.equals(cartProperties.getEnablePersistence()) && "realtime".equalsIgnoreCase(cartProperties.getSyncStrategy())) { cartPersistenceService.saveOrUpdate(userId, unitId, existingItem); } log.info("用户{}成功添加商品{}到购物车", userId, cartItemDTO.getProductName()); return true; } catch (Exception e) { log.error("添加商品到购物车失败", e); throw new BusinessException("添加商品到购物车失败:" + e.getMessage()); } } @Override @Transactional(rollbackFor = Exception.class) public boolean removeFromCart(String userId, String unitId, String productId, Long pricingId) { try { String cartItemKey = buildCartItemKey(userId, unitId, productId, pricingId); // 从Redis中删除商品项 Boolean removed = redisTemplate.delete(cartItemKey); if (Boolean.TRUE.equals(removed)) { // 更新购物车商品列表 updateCartItemList(userId, unitId, productId, pricingId, false); // 异步从数据库中删除(根据配置) if (Boolean.TRUE.equals(cartProperties.getEnablePersistence()) && "realtime".equalsIgnoreCase(cartProperties.getSyncStrategy())) { cartPersistenceService.remove(userId, unitId, productId,pricingId); } log.info("用户{}成功从购物车移除商品{}", userId, pricingId); return true; } return false; } catch (Exception e) { log.error("从购物车移除商品失败", e); throw new BusinessException("从购物车移除商品失败:" + e.getMessage()); } } @Override @Transactional(rollbackFor = Exception.class) public boolean updateCartItemQuantity(String userId, String unitId, String productId, Long pricingId, Integer quantity) { try { if (quantity <= 0) { return removeFromCart(userId, unitId, productId, pricingId); } String cartItemKey = buildCartItemKey(userId, unitId, productId, pricingId); CartItemVO cartItem = (CartItemVO) redisTemplate.opsForValue().get(cartItemKey); if (cartItem == null) { throw new BusinessException("购物车商品不存在"); } cartItem.setQuantity(quantity); cartItem.setTotalPrice(cartItem.getUnitPrice().multiply(BigDecimal.valueOf(quantity))); cartItem.setUpdateTime(LocalDateTime.now()); // 更新到Redis redisTemplate.opsForValue().set(cartItemKey, cartItem, (cartProperties.getExpireDays() != null ? cartProperties.getExpireDays() : CART_EXPIRE_DAYS), TimeUnit.DAYS); // 异步持久化到数据库(根据配置) if (Boolean.TRUE.equals(cartProperties.getEnablePersistence()) && "realtime".equalsIgnoreCase(cartProperties.getSyncStrategy())) { cartPersistenceService.saveOrUpdate(userId, unitId, cartItem); } log.info("用户{}成功更新购物车商品{}数量为{}", userId, pricingId, quantity); return true; } catch (Exception e) { log.error("更新购物车商品数量失败", e); throw new BusinessException("更新购物车商品数量失败:" + e.getMessage()); } } @Override @Transactional(rollbackFor = Exception.class) public boolean updateCartItemDuration(String userId, String unitId, String productId, Long pricingId, Integer duration) { try { String cartItemKey = buildCartItemKey(userId, unitId, productId, pricingId); CartItemVO cartItem = (CartItemVO) redisTemplate.opsForValue().get(cartItemKey); if (cartItem == null) { throw new BusinessException("购物车商品不存在"); } cartItem.setDuration(duration); cartItem.setUpdateTime(LocalDateTime.now()); // 更新到Redis redisTemplate.opsForValue().set(cartItemKey, cartItem, (cartProperties.getExpireDays() != null ? cartProperties.getExpireDays() : CART_EXPIRE_DAYS), TimeUnit.DAYS); // 异步持久化到数据库(根据配置) if (Boolean.TRUE.equals(cartProperties.getEnablePersistence()) && "realtime".equalsIgnoreCase(cartProperties.getSyncStrategy())) { cartPersistenceService.saveOrUpdate(userId, unitId, cartItem); } log.info("用户{}成功更新购物车商品{}年限为{}", userId, pricingId, duration); return true; } catch (Exception e) { log.error("更新购物车商品数量失败", e); throw new BusinessException("更新购物车商品数量失败:" + e.getMessage()); } } @Override @Transactional(rollbackFor = Exception.class) public boolean clearCart(String userId, String unitId, String productId) { try { String cartKey = buildCartKey(userId, unitId, productId); List pricingIds = getCartItemPricingIds(userId, unitId, productId); // 删除所有商品项 for (Long pricingId : pricingIds) { String cartItemKey = buildCartItemKey(userId, unitId, productId,pricingId); redisTemplate.delete(cartItemKey); } // 删除购物车列表 redisTemplate.delete(cartKey); // 异步清空数据库中的购物车数据(根据配置) if (Boolean.TRUE.equals(cartProperties.getEnablePersistence()) && "realtime".equalsIgnoreCase(cartProperties.getSyncStrategy())) { cartPersistenceService.clear(userId, unitId, productId); } log.info("用户{}成功清空购物车", userId); return true; } catch (Exception e) { log.error("清空购物车失败", e); throw new BusinessException("清空购物车失败:" + e.getMessage()); } } @Override public CartVO getCart(String userId, String unitId, String productId) { try { CartVO cartVO = new CartVO(); cartVO.setUserId(userId); if (StringUtils.hasText(unitId)){ cartVO.setUnitId(unitId); } List items = getCartItems(userId, unitId, productId); cartVO.setItems(items); // 计算总数量和总金额 int totalQuantity = items.stream().mapToInt(item -> item.getQuantity()).sum(); BigDecimal totalAmount = items.stream() .map(item -> item.getTotalPrice()) .reduce(BigDecimal.ZERO, BigDecimal::add); cartVO.setTotalQuantity(totalQuantity); cartVO.setTotalAmount(totalAmount); cartVO.setLastUpdateTime(LocalDateTime.now()); return cartVO; } catch (Exception e) { log.error("获取购物车信息失败", e); throw new BusinessException("获取购物车信息失败:" + e.getMessage()); } } @Override public List getCartItems(String userId, String unitId,String productId) { try { // 优先从Redis获取 List items = getCartItemsFromRedis(userId, unitId,productId); if (items != null && !items.isEmpty()) { return items; } // Redis中没有数据,从数据库加载 log.info("Redis中无购物车数据,从数据库加载用户{}的购物车", userId); return loadCartFromDatabase(userId, unitId, productId) ? getCartItemsFromRedis(userId, unitId, productId) : new ArrayList<>(); } catch (Exception e) { log.error("获取购物车商品列表失败", e); // 降级到数据库查询 return getCartItemsFromDatabase(userId, unitId, productId); } } @Override public boolean checkCartItemStock(String userId, String unitId, Long pricingId) { // TODO: 实现库存检查逻辑 return true; } @Override @Transactional(rollbackFor = Exception.class) public boolean batchRemoveFromCart(String userId, String unitId, String productId,List pricingIds) { try { if (CollectionUtils.isEmpty(pricingIds)) { return true; } for (Long pricingId : pricingIds) { removeFromCart(userId, unitId, productId,pricingId); } return true; } catch (Exception e) { log.error("批量删除购物车商品失败", e); throw new BusinessException("批量删除购物车商品失败:" + e.getMessage()); } } @Override public Integer getCartItemCount(String userId, String unitId, String productId) { try { // 优先从Redis获取 String cartKey = buildCartKey(userId, unitId, productId); List pricingIds = (List) redisTemplate.opsForValue().get(cartKey); if (pricingIds != null) { return pricingIds.size(); } // 从数据库获取 return cartMapper.countByUserIdAndUnitId(userId, unitId, productId); } catch (Exception e) { log.error("获取购物车商品数量失败", e); return 0; } } @Override public boolean loadCartFromDatabase(String userId, String unitId, String productId) { try { List cartItems = cartMapper.selectByUserIdAndUnitId(userId, unitId, productId); if (CollectionUtils.isEmpty(cartItems)) { return false; } String cartKey = buildCartKey(userId, unitId, productId); List pricingIds = new ArrayList<>(); for (Cart cartItem : cartItems) { CartItemVO itemVO = convertCartToCartItemVO(cartItem); String cartItemKey = buildCartItemKey(userId, unitId,cartItem.getProductId(), cartItem.getPricingId()); // 保存到Redis redisTemplate.opsForValue().set(cartItemKey, itemVO, CART_EXPIRE_DAYS, TimeUnit.DAYS); pricingIds.add(cartItem.getPricingId()); } // 保存购物车列表到Redis redisTemplate.opsForValue().set(cartKey, pricingIds, CART_EXPIRE_DAYS, TimeUnit.DAYS); log.info("成功从数据库加载用户{}的购物车数据到Redis", userId); return true; } catch (Exception e) { log.error("从数据库加载购物车数据失败", e); return false; } } @Override public boolean syncCartToDatabase(String userId, String unitId, String productId) { try { List redisItems = getCartItemsFromRedis(userId, unitId, productId); if (CollectionUtils.isEmpty(redisItems)) { return true; } // 清空数据库中的购物车数据 clearCartFromDatabase(userId, unitId, productId); // 同步Redis数据到数据库 for (CartItemVO item : redisItems) { syncCartItemToDatabase(userId, unitId,productId, item); } log.info("成功同步Redis购物车数据到数据库,用户{}", userId); return true; } catch (Exception e) { log.error("同步购物车数据到数据库失败", e); return false; } } @Override public boolean checkCartConsistency(String userId, String unitId, String productId) { try { List redisItems = getCartItemsFromRedis(userId, unitId, productId); List dbItems = cartMapper.selectByUserIdAndUnitId(userId, unitId, productId); if (redisItems.size() != dbItems.size()) { log.warn("购物车数据不一致:Redis数量{},数据库数量{}", redisItems.size(), dbItems.size()); return false; } // 检查每个商品项是否一致 for (CartItemVO redisItem : redisItems) { boolean found = false; for (Cart dbItem : dbItems) { if (redisItem.getPricingId().equals(dbItem.getPricingId()) && redisItem.getQuantity().equals(dbItem.getQuantity())) { found = true; break; } } if (!found) { log.warn("购物车商品项不一致:pricingId={}", redisItem.getPricingId()); return false; } } return true; } catch (Exception e) { log.error("检查购物车数据一致性失败", e); return false; } } // ==================== 私有方法 ==================== private String buildCartKey(String userId, String unitId,String productId) { if(StringUtils.hasText(unitId)){ return CART_KEY_PREFIX + userId + ":" + unitId + ":" + productId; } return CART_KEY_PREFIX + userId + ":" + productId; } private String buildCartItemKey(String userId, String unitId,String productId, Long pricingId) { if(StringUtils.hasText(unitId)){ return CART_ITEM_KEY_PREFIX + userId + ":" + unitId + ":" + productId + ":" + pricingId; } return CART_ITEM_KEY_PREFIX + userId + ":" + productId + ":" + pricingId; } private void updateCartItemList(String userId, String unitId,String productId ,Long pricingId, boolean add) { String cartKey = buildCartKey(userId, unitId, productId); List pricingIds = (List) redisTemplate.opsForValue().get(cartKey); if (pricingIds == null) { pricingIds = new ArrayList<>(); } if (add && !pricingIds.contains(pricingId)) { pricingIds.add(pricingId); } else if (!add) { pricingIds.remove(pricingId); } redisTemplate.opsForValue().set(cartKey, pricingIds, CART_EXPIRE_DAYS, TimeUnit.DAYS); } private List getCartItemPricingIds(String userId, String unitId, String productId) { String cartKey = buildCartKey(userId, unitId, productId); List pricingIds = (List) redisTemplate.opsForValue().get(cartKey); return pricingIds != null ? pricingIds : new ArrayList<>(); } private List getCartItemsFromRedis(String userId, String unitId, String productId) { try { String cartKey = buildCartKey(userId, unitId, productId); List pricingIds = (List) redisTemplate.opsForValue().get(cartKey); if (CollectionUtils.isEmpty(pricingIds)) { return new ArrayList<>(); } List items = new ArrayList<>(); for (Long pricingId : pricingIds) { String cartItemKey = buildCartItemKey(userId, unitId, productId,pricingId); CartItemVO item = (CartItemVO) redisTemplate.opsForValue().get(cartItemKey); if (item != null) { items.add(item); } } return items; } catch (Exception e) { log.error("从Redis获取购物车商品列表失败", e); return new ArrayList<>(); } } private List getCartItemsFromDatabase(String userId, String unitId, String productId) { try { List cartItems = cartMapper.selectByUserIdAndUnitId(userId, unitId, productId); List items = new ArrayList<>(); for (Cart cartItem : cartItems) { items.add(convertCartToCartItemVO(cartItem)); } return items; } catch (Exception e) { log.error("从数据库获取购物车商品列表失败", e); return new ArrayList<>(); } } private CartItemVO convertCartToCartItemVO(Cart cart) { CartItemVO itemVO = new CartItemVO(); BeanUtils.copyProperties(cart, itemVO); return itemVO; } private void syncCartItemToDatabase(String userId, String unitId, String productId,CartItemVO item) { try { Cart cart = new Cart(); BeanUtils.copyProperties(item, cart); cart.setUserId(userId); cart.setUnitId(unitId); cart.setUpdateTime(LocalDateTime.now()); // 检查是否已存在 Cart existingCart = cartMapper.selectByUserIdUnitIdAndPricingId(userId, unitId, productId,item.getPricingId()); if (existingCart != null) { // 更新 cart.setId(existingCart.getId()); cartMapper.updateById(cart); } else { // 新增 cart.setAddTime(LocalDateTime.now()); cartMapper.insert(cart); } } catch (Exception e) { log.error("同步购物车商品到数据库失败", e); } } private void removeCartItemFromDatabase(String userId, String unitId, String productId, Long pricingId) { try { Cart existingCart = cartMapper.selectByUserIdUnitIdAndPricingId(userId, unitId, productId,pricingId); if (existingCart != null) { cartMapper.deleteByCustomerCondition(existingCart.getId()); } } catch (Exception e) { log.error("从数据库删除购物车商品失败", e); } } private void clearCartFromDatabase(String userId, String unitId, String productId) { try { // 使用逻辑删除 List cartItems = cartMapper.selectByUserIdAndUnitId(userId, unitId, productId); for (Cart item : cartItems) { cartMapper.deleteById(item.getId()); } } catch (Exception e) { log.error("清空数据库购物车失败", e); } } }