购物车设计是每个电商项目中必不可缺的一环,不管登录与否,购物车都可以使用.那么购物车的数据我们存在哪里呢?
登录时,购物车的数据理所应当的存在redis数据库中,可是未登录时呢?
在这里我们给出了三个方案
1、当用户在未登录的情况下,将此购物车存入Cookie中, 在用户登陆的情况下,将购物车数据存入redis 。如果用户登陆时,Cookie中存在购物车数据,需要将Redis的购物车合并到redis中存储.
缺点:Cookie最大存放:4K;
2、当用户在未登录的情况下,将此购物车存入LocalStorage中, 在用户登陆的情况下,将购物车数据存入redis 。如果用户登陆时,LocalStorage中存在购物车数据,需要将Redis的购物车合并到redis中存储.
缺点:当用户清空浏览器缓存时,LocalStorage数据就没有了;
3、不管用户是否登录,都保存在Redis中;(1)当用户未登录的情况下,获取SessionID,以SessionID作为Redis的Key保存;(2)如果用户登录了,根据用户名来保存到Redis;(3)如果用户登录时,把SessionID中的Redis数据和用户名获取的Redis数据合并;
很明显第三种解决方案是最好的,在这里我们就按照第三种解决方案来设计未登录时的购物车
作为购物车页面,按照一般电商网站的设计,不同的卖家应该分为不同的区域,也就是说要按照商家的sellerId来分块展示购物车数据.在这里我们已sellerId来区分购物车数据,购物车实体类分为三个属性
private String sellerId;//商家ID
private String sellerName;//商家名称
private List orderItemList;//购物车明细
在商品详情页面的添加购物车追加点击事件,点击则触发添加购物车的方法,登录与否只是存入redis中的key是sessionId还是userId的区别而已
无论是否登录都要先判断cookie中是否有保存的sessionId,以此来判断未登录时是否购物车有数据,如果有在判断是否登录,如果登录则需要合并购物车
根据之前的设计,当用户未登录时我们以sessionId作为key保存购物车数据到redis数据库中,而为了避免sessionId关闭浏览器后丢失,我们采用把sessionId存入cookie中这个方法来保存sessionId.当用户登录后,应该把未登录时的购物车对象集合添加到登录时的购物车集合中,具体逻辑需要遍历未登录时的购物车集合把每个购物车对象依次判断是否在登录的购物车集合中已存在相同的sellerId,如果存在,在判断商品是否相同,如果商品相同,仅仅叠加数量,如果商品不同,则在orderItemList追加一个orderItem对象,如果sellerId都不同,则添加一个新的Cart对象
@RestController
@RequestMapping("/cart")
public class CartController {
@Reference
private CartService cartService;
@Autowired
private HttpSession session;
@Autowired
private HttpServletRequest request;
@Autowired
private HttpServletResponse response;
@RequestMapping("/findCartList")
public List findCartList(){
String userId = SecurityContextHolder.getContext().getAuthentication().getName();
String sessionId = getSessionId();
List cartList_sessionId = cartService.findCartList(sessionId);
//判断当前用户id是不是匿名用户,如果不是匿名用户则用userId作为key去查询redis数据库中的数据
if(!"anonymousUser".equals(userId)) {
List cartList_userId = cartService.findCartList(userId);
//购物车合并
//如果cartList_sessionId的size不为0,则证明曾经未登录添加过购物车,这时我们要进行购物车合并
if(cartList_sessionId.size()!=0) {
//合并购物车
cartList_userId = cartService.mergeCartList(cartList_userId, cartList_sessionId);
//删除sessionId为key的数据库值
cartService.clearRedisByKey(sessionId);
//并且保存新的数据到数据库
cartService.saveCartListBySessionToRedis(userId, cartList_userId);
}
return cartList_userId;
}
return cartList_sessionId;
}
private String getSessionId() {
//直接从cookie中拿sessionId如果没有则创建一个sessionId扔到cookie中;
String sessionId = CookieUtil.getCookieValue(request, "user_key", "utf-8");
if(sessionId==null) {
sessionId = session.getId();
CookieUtil.setCookie(request, response, "user_key", sessionId, 24*60*60, "utf-8");
}
return sessionId;
}
@RequestMapping("/addCartList/{itemId}/{num}")
@CrossOrigin({"http://item.pinyougou.com","www.pinyougou.com"})
public Result addCartList(@PathVariable("itemId")Long itemId, @PathVariable("num")Integer num){
//查询添加前购物车对象集合,传递参数使用
try {
List oldCartList = findCartList();
//调用方法进行添加
List cartList = cartService.addCartList(itemId,num,oldCartList);
String userId = SecurityContextHolder.getContext().getAuthentication().getName();
//同样根据是否登录来判断使用什么key来保存购物车列表到数据库
if(!"anonymousUser".equals(userId)) {
cartService.saveCartListBySessionToRedis(userId, cartList);
}else {
String sessionId = getSessionId();
cartService.saveCartListBySessionToRedis(sessionId, cartList);
}
//调用方法保存购物车集合
return new Result(true, "添加购物车成功");
} catch (RuntimeException e) {
return new Result(false, e.getMessage());
} catch (Exception e) {
e.printStackTrace();
return new Result(false, "添加购物车失败");
}
}
}
@Service
public class CartServiceImpl implements CartService {
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private TbItemMapper itemMapper;
@Override
public List findCartList(String sessionId) {
//从redis数据库中根据sessionId或者userId作为key去取集合
List cartList = (List) redisTemplate.boundHashOps("cartlist").get(sessionId);
//如果为空则判断一下,因为如果集合为null则在页面遍历集合会报错
if(cartList==null) {
cartList = new ArrayList(0);
}
return cartList;
}
@Override
public List addCartList(Long itemId, Integer num, List cartList) {
TbItem tbItem = itemMapper.selectByPrimaryKey(itemId);
//这里加一个判断,判断一下传过来的itemId是否是私自传过来的非法数据
if(tbItem==null) {
throw new RuntimeException("itemId数据非法");
}
String sellerId = tbItem.getSellerId();
// 1.添加的商品的sellerId与之前CartList列表中的某一个Cart对象中的sellerId作对比
Cart cart = findCartFromCartListBySellerId(sellerId, cartList);
// 1.1如果存在某个Cart对象的sellerId与这个商品的sellerId相同时,获得这个Cart对象中的orderItemList集合,
if(cart!=null) {
// 遍历这个集合并判断其中每一个orderItem对象的itemId是否相同
List orderItemList = cart.getOrderItemList();
TbOrderItem orderItem =findOrderItemFromOrderItemListByItemId(itemId, orderItemList);
// 1.1.1如果相同,则证明之前添加过这个商品,并将传过来的num叠加上去,并更新小计价格
if(orderItem!=null) {
orderItem.setNum(orderItem.getNum()+num);
orderItem.setTotalFee(new BigDecimal(orderItem.getNum()*orderItem.getPrice().doubleValue()));
//如果此时的num属性为0则判断移除改orderItem
if(orderItem.getNum()<=0) {
orderItemList.remove(orderItem);
//这是判断移除orderItem后orderItemList中是否还有数据,如果没有则移除整个Cart对象
if(orderItemList.size()==0) {
cartList.remove(cart);
}
}
}else {
// 1.1.2如果不同,则证明没有添加过这个商品,把这个商品作为一个orderItem对象添加到orderItemList集合中
orderItem = createOrderItem(orderItem, tbItem, itemId, num);
cart.getOrderItemList().add(orderItem);
}
}else {
// 1.2如果不存在某个Cart对象的sellerId与之相同,则创建一个新的Cart对象并赋予其sellerId, sellerName,
// orderItemList,并把这个Cart对象添加到CartList列表中
cart = new Cart();
cart.setSellerId(sellerId);
cart.setSellerName(tbItem.getSeller());
List orderItemList = new ArrayList<>();
TbOrderItem orderItem = null;
orderItem = createOrderItem(orderItem, tbItem, itemId, num);
cart.setOrderItemList(orderItemList);
orderItemList.add(orderItem);
cartList.add(cart);
}
return cartList;
}
private TbOrderItem createOrderItem(TbOrderItem orderItem, TbItem tbItem, Long itemId, Integer num) {
//当第一次添加商品时,如果通过非法途径传入一个小于1的数字,则抛出一个异常
if(num<1) {
throw new RuntimeException("数量非法");
}
orderItem=new TbOrderItem();
orderItem.setGoodsId(tbItem.getGoodsId());
orderItem.setItemId(itemId);
orderItem.setNum(num);
orderItem.setPicPath(tbItem.getImage());
orderItem.setPrice(tbItem.getPrice());
orderItem.setSellerId(tbItem.getSellerId());
orderItem.setTitle(tbItem.getTitle());
orderItem.setTotalFee(new BigDecimal(orderItem.getNum()*orderItem.getPrice().doubleValue()));
return orderItem;
}
//用来遍历这个集合并判断其中每一个orderItem对象的itemId是否相同,如果有则返回一个orderItem对象
private TbOrderItem findOrderItemFromOrderItemListByItemId(Long itemId, List orderItemList) {
for (TbOrderItem orderItem : orderItemList) {
if(itemId.equals(orderItem.getItemId())) {
return orderItem;
}
}
return null;
}
//用来遍历这个集合并判断其中每一个Cart对象的SellerId是否相同,如果有则返回一个Cart对象
private Cart findCartFromCartListBySellerId(String sellerId, List cartList) {
for (Cart cart : cartList) {
if(sellerId.equals(cart.getSellerId())){
return cart;
}
}
return null;
}
@Override
public void saveCartListBySessionToRedis(String sessionId, List cartList) {
redisTemplate.boundHashOps("cartlist").put(sessionId, cartList);
}
@Override
public List mergeCartList(List cartList_userId, List cartList_sessionId) {
//遍历这个未登录是添加的购物车集合
for (Cart cart : cartList_sessionId) {
//获得每个cart对象,并再获取其orderItemList
List orderItemList = cart.getOrderItemList();
//遍历这个orderItemList,获取每一个orderItem
for (TbOrderItem orderItem : orderItemList) {
//调用addCartList方法,把每一个orderItem对象添加到cartList中并返回
cartList_userId = addCartList(orderItem.getItemId(), orderItem.getNum(), cartList_userId);
}
}
return cartList_userId;
}
@Override
public void clearRedisByKey(String sessionId) {
redisTemplate.boundHashOps("cartlist").delete(sessionId);
}
}