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 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<String, Object> 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(Long userId, Long unitId, CartItemDTO cartItemDTO) {
|
try {
|
// 验证商品定价是否存在
|
ProductPricing pricing = productPricingMapper.selectById(cartItemDTO.getPricingId());
|
if (pricing == null) {
|
throw new BusinessException("商品定价不存在");
|
}
|
|
// 构建购物车key
|
String cartKey = buildCartKey(userId, unitId);
|
String cartItemKey = buildCartItemKey(userId, unitId, 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.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(Long userId, Long unitId, Long pricingId) {
|
try {
|
String cartItemKey = buildCartItemKey(userId, unitId, pricingId);
|
|
// 从Redis中删除商品项
|
Boolean removed = redisTemplate.delete(cartItemKey);
|
if (Boolean.TRUE.equals(removed)) {
|
// 更新购物车商品列表
|
updateCartItemList(userId, unitId, pricingId, false);
|
|
// 异步从数据库中删除(根据配置)
|
if (Boolean.TRUE.equals(cartProperties.getEnablePersistence()) && "realtime".equalsIgnoreCase(cartProperties.getSyncStrategy())) {
|
cartPersistenceService.remove(userId, unitId, 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(Long userId, Long unitId, Long pricingId, Integer quantity) {
|
try {
|
if (quantity <= 0) {
|
return removeFromCart(userId, unitId, pricingId);
|
}
|
|
String cartItemKey = buildCartItemKey(userId, unitId, 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 clearCart(Long userId, Long unitId) {
|
try {
|
String cartKey = buildCartKey(userId, unitId);
|
List<Long> pricingIds = getCartItemPricingIds(userId, unitId);
|
|
// 删除所有商品项
|
for (Long pricingId : pricingIds) {
|
String cartItemKey = buildCartItemKey(userId, unitId, pricingId);
|
redisTemplate.delete(cartItemKey);
|
}
|
|
// 删除购物车列表
|
redisTemplate.delete(cartKey);
|
|
// 异步清空数据库中的购物车数据(根据配置)
|
if (Boolean.TRUE.equals(cartProperties.getEnablePersistence()) && "realtime".equalsIgnoreCase(cartProperties.getSyncStrategy())) {
|
cartPersistenceService.clear(userId, unitId);
|
}
|
|
log.info("用户{}成功清空购物车", userId);
|
return true;
|
} catch (Exception e) {
|
log.error("清空购物车失败", e);
|
throw new BusinessException("清空购物车失败:" + e.getMessage());
|
}
|
}
|
|
@Override
|
public CartVO getCart(Long userId, Long unitId) {
|
try {
|
CartVO cartVO = new CartVO();
|
cartVO.setUserId(userId);
|
cartVO.setUnitId(unitId);
|
|
List<CartItemVO> items = getCartItems(userId, unitId);
|
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<CartItemVO> getCartItems(Long userId, Long unitId) {
|
try {
|
// 优先从Redis获取
|
List<CartItemVO> items = getCartItemsFromRedis(userId, unitId);
|
if (items != null && !items.isEmpty()) {
|
return items;
|
}
|
|
// Redis中没有数据,从数据库加载
|
log.info("Redis中无购物车数据,从数据库加载用户{}的购物车", userId);
|
return loadCartFromDatabase(userId, unitId) ? getCartItemsFromRedis(userId, unitId) : new ArrayList<>();
|
} catch (Exception e) {
|
log.error("获取购物车商品列表失败", e);
|
// 降级到数据库查询
|
return getCartItemsFromDatabase(userId, unitId);
|
}
|
}
|
|
@Override
|
public boolean checkCartItemStock(Long userId, Long unitId, Long pricingId) {
|
// TODO: 实现库存检查逻辑
|
return true;
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public boolean batchRemoveFromCart(Long userId, Long unitId, List<Long> pricingIds) {
|
try {
|
if (CollectionUtils.isEmpty(pricingIds)) {
|
return true;
|
}
|
|
for (Long pricingId : pricingIds) {
|
removeFromCart(userId, unitId, pricingId);
|
}
|
|
return true;
|
} catch (Exception e) {
|
log.error("批量删除购物车商品失败", e);
|
throw new BusinessException("批量删除购物车商品失败:" + e.getMessage());
|
}
|
}
|
|
@Override
|
public Integer getCartItemCount(Long userId, Long unitId) {
|
try {
|
// 优先从Redis获取
|
String cartKey = buildCartKey(userId, unitId);
|
List<Long> pricingIds = (List<Long>) redisTemplate.opsForValue().get(cartKey);
|
if (pricingIds != null) {
|
return pricingIds.size();
|
}
|
|
// 从数据库获取
|
return cartMapper.countByUserIdAndUnitId(userId, unitId);
|
} catch (Exception e) {
|
log.error("获取购物车商品数量失败", e);
|
return 0;
|
}
|
}
|
|
@Override
|
public boolean loadCartFromDatabase(Long userId, Long unitId) {
|
try {
|
List<Cart> cartItems = cartMapper.selectByUserIdAndUnitId(userId, unitId);
|
if (CollectionUtils.isEmpty(cartItems)) {
|
return false;
|
}
|
|
String cartKey = buildCartKey(userId, unitId);
|
List<Long> pricingIds = new ArrayList<>();
|
|
for (Cart cartItem : cartItems) {
|
CartItemVO itemVO = convertCartToCartItemVO(cartItem);
|
String cartItemKey = buildCartItemKey(userId, unitId, 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(Long userId, Long unitId) {
|
try {
|
List<CartItemVO> redisItems = getCartItemsFromRedis(userId, unitId);
|
if (CollectionUtils.isEmpty(redisItems)) {
|
return true;
|
}
|
|
// 清空数据库中的购物车数据
|
clearCartFromDatabase(userId, unitId);
|
|
// 同步Redis数据到数据库
|
for (CartItemVO item : redisItems) {
|
syncCartItemToDatabase(userId, unitId, item);
|
}
|
|
log.info("成功同步Redis购物车数据到数据库,用户{}", userId);
|
return true;
|
} catch (Exception e) {
|
log.error("同步购物车数据到数据库失败", e);
|
return false;
|
}
|
}
|
|
@Override
|
public boolean checkCartConsistency(Long userId, Long unitId) {
|
try {
|
List<CartItemVO> redisItems = getCartItemsFromRedis(userId, unitId);
|
List<Cart> dbItems = cartMapper.selectByUserIdAndUnitId(userId, unitId);
|
|
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(Long userId, Long unitId) {
|
return CART_KEY_PREFIX + userId + ":" + unitId;
|
}
|
|
private String buildCartItemKey(Long userId, Long unitId, Long pricingId) {
|
return CART_ITEM_KEY_PREFIX + userId + ":" + unitId + ":" + pricingId;
|
}
|
|
private void updateCartItemList(Long userId, Long unitId, Long pricingId, boolean add) {
|
String cartKey = buildCartKey(userId, unitId);
|
List<Long> pricingIds = (List<Long>) 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<Long> getCartItemPricingIds(Long userId, Long unitId) {
|
String cartKey = buildCartKey(userId, unitId);
|
List<Long> pricingIds = (List<Long>) redisTemplate.opsForValue().get(cartKey);
|
return pricingIds != null ? pricingIds : new ArrayList<>();
|
}
|
|
private List<CartItemVO> getCartItemsFromRedis(Long userId, Long unitId) {
|
try {
|
String cartKey = buildCartKey(userId, unitId);
|
List<Long> pricingIds = (List<Long>) redisTemplate.opsForValue().get(cartKey);
|
|
if (CollectionUtils.isEmpty(pricingIds)) {
|
return new ArrayList<>();
|
}
|
|
List<CartItemVO> items = new ArrayList<>();
|
for (Long pricingId : pricingIds) {
|
String cartItemKey = buildCartItemKey(userId, unitId, 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<CartItemVO> getCartItemsFromDatabase(Long userId, Long unitId) {
|
try {
|
List<Cart> cartItems = cartMapper.selectByUserIdAndUnitId(userId, unitId);
|
List<CartItemVO> 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(Long userId, Long unitId, 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, 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(Long userId, Long unitId, Long pricingId) {
|
try {
|
Cart existingCart = cartMapper.selectByUserIdUnitIdAndPricingId(userId, unitId, pricingId);
|
if (existingCart != null) {
|
cartMapper.deleteByCustomerCondition(existingCart.getId());
|
}
|
} catch (Exception e) {
|
log.error("从数据库删除购物车商品失败", e);
|
}
|
}
|
|
private void clearCartFromDatabase(Long userId, Long unitId) {
|
try {
|
// 使用逻辑删除
|
List<Cart> cartItems = cartMapper.selectByUserIdAndUnitId(userId, unitId);
|
for (Cart item : cartItems) {
|
cartMapper.deleteById(item.getId());
|
}
|
} catch (Exception e) {
|
log.error("清空数据库购物车失败", e);
|
}
|
}
|
}
|