需求分析
当我们点击加入购物车的时候,将商品加入到我们的购物车,页面传递到后台的信息是商品的id和数量,我们根据id和数量将商品放进购物车,购物车数据存储到cookie或者是服务器端
购物车列表在cookie的存储指的是我们在不登录的情况下也能将商品添加到购物车,这时购物车中的商品信息存储在浏览器端的cookie,用户清除浏览器缓存或者cookie过期后,购物车中就不存在商品信息
创建购物车的单个商家对象Cart,List Cart构成了购物车,Cart对象存储的是像京东自营、xx专卖店所包含商品的数据
public class Cart implements Serializable {
private String sellerId;//商家id
private String sellerName;//商家名称
private List<TbOrderItem> orderItemList;//购物车明细
public Cart() {
}
public Cart(String sellerId, String sellerName, List<TbOrderItem> orderItemList) {
this.sellerId = sellerId;
this.sellerName = sellerName;
this.orderItemList = orderItemList;
}
public String getSellerId() {
return sellerId;
}
public void setSellerId(String sellerId) {
this.sellerId = sellerId;
}
public String getSellerName() {
return sellerName;
}
public void setSellerName(String sellerName) {
this.sellerName = sellerName;
}
public List<TbOrderItem> getOrderItemList() {
return orderItemList;
}
public void setOrderItemList(List<TbOrderItem> orderItemList) {
this.orderItemList = orderItemList;
}
}
存储实现
注意
Long类型数据的比较通过longValue()方法在进行比较,而long类型可以通过==比较
/*
添加商品到购物车
cartList 老的购物车
*/
@Override
public List<Cart> addCart(List<Cart> cartList, Long itemId, Integer num) {
TbItem item = itemMapper.selectByPrimaryKey(itemId);
//做安全校验
if (item==null){
throw new RuntimeException("该商品不存在");
}
if (!item.getStatus().equals("1")){
throw new RuntimeException("该商品暂不支持添加购物车");
}
String sellerId = item.getSellerId();
Cart cart = searchItemInCartList(cartList, sellerId);
if (cart!=null){//购物车中已经存在该商家
List<TbOrderItem> orderItems = cart.getOrderItemList();//获取用户在这个商家中的商品明细
//判断新商品在不在购物车的商品明细里
TbOrderItem orderItem = searchItemInOrderOtemList(orderItems, itemId);
if (orderItem!=null){//如果该商品已经在购物车中,改变数量和总价
orderItem.setNum(orderItem.getNum()+num);
orderItem.setTotalFee(new BigDecimal(orderItem.getPrice().doubleValue()*orderItem.getNum()));
//移除商品数量减到0的商品明细
if (orderItem.getNum()<=0){
orderItems.remove(orderItem);
}
if (orderItems.size()==0){//如果商家在购物车列表的商品数量为0,就移除该商家
cartList.remove(orderItems);
}
}
else{//如果该商品不在购物车中,添加该商品信息到新建的商品明细中
TbOrderItem newOrderItem = createOrderItem(item, num);
orderItems.add(newOrderItem);
}
}
else{//购物车中不存在该商家,创建该商家的购物明细
TbOrderItem orderItem = createOrderItem(item, num);
List<TbOrderItem> items = new ArrayList<>();//创建集合用来保存商品信息
items.add(orderItem);
Cart newCart = new Cart(item.getSellerId(),item.getSeller(),items);
cartList.add(newCart);
}
return cartList;
}
/*
检测商家商品明细是否存在于购物车列表
*/
private Cart searchItemInCartList(List<Cart> cartList,String sellerId){
for (Cart cart : cartList) {
if (cart.getSellerId().equals(sellerId)) {//购物车中已经存在该商家
return cart;
}
}
return null;
}
/*
判断商品是否存在于商家商品明细中
*/
private TbOrderItem searchItemInOrderOtemList(List<TbOrderItem> itemList,Long itemId){
for (TbOrderItem orderItem : itemList) {
if (orderItem.getItemId().longValue()==itemId.longValue()) {//如果该商品已经在购物车中
return orderItem;
}
}
return null;
}
/*
新建商品明细中的单个商品信息
*/
private TbOrderItem createOrderItem(TbItem item,Integer num){
TbOrderItem orderItem = new TbOrderItem();
orderItem.setGoodsId(item.getGoodsId());
orderItem.setItemId(item.getId());
orderItem.setTitle(item.getTitle());
orderItem.setNum(num);
orderItem.setPrice(item.getPrice());
orderItem.setTotalFee(new BigDecimal(orderItem.getPrice().doubleValue()*num));
orderItem.setPicPath(item.getImage());
return orderItem;
}
未登录保存在cookie中
在后端对cookie进行操作
注意
复杂对象是不能进行编码的,可以先转换成字符串,字符串是可编码的,这样就可以避免中文乱码
@Reference
private CartService cartService;
@Autowired
private HttpServletRequest request;
@Autowired
private HttpServletResponse response;
@RequestMapping("/add")
public Result addCart(Long itemId,Integer num){
try {
List<Cart> cartList = findCartList();
List<Cart> carts = cartService.addCart(cartList, itemId, num);
//存入cookie
//复杂对象不支持编码,转成字符串,字符串是可编码的,防止对象中中文乱码
String cartsJson = JSON.toJSONString(carts);
CookieUtil.setCookie(request,response,"cartList",cartsJson,3600*24,"utf-8");
return new Result(true,"加入购物车成功!");
} catch (Exception e) {
e.printStackTrace();
return new Result(false,"加入购物车失败!");
}
}
CookieUtil是我们自己封装的工具类
优化(登录存储在redis,未登录保存在cookie)
判断当前用户是否登陆,如果未登录采用Cookie存储,如果登录则采用Redis存储。登录后要进行Cookie购物车与Redis购物车的合并操作,并清除Cookie购物车。
修改spring-security.xml配置文件
之前我们是直接放行有关购物车操作的请求,这样的话spring-security就不会验证是否登录,也自然就没有登录相关信息
现在我们需要,spring-security里有登录相关信息,不登录也能执行有关购物车的操作,登录也可以操作
解决办法就是,修改拦截的权限
删除<http pattern="/cart/*.do" security="none"></http>
access=“IS_AUTHENTICATED_ANONYMOUSLY” 用于设置资源可以在不登陆时访问。
此配置与 security="none"的区别在于当用户未登陆时获取登陆人账号的值为anonymousUser,而security="none"的话,无论是否登陆都不能获取登录人账号的值。
否则:"/cart/*.do" security="none"表示不拦截,绕过spring security,此时SecurityContextHolder.getContext()上下文为空。
service层代码同上,修改controller代码如下
@Reference
private CartService cartService;
@Autowired
private HttpServletRequest request;
@Autowired
private HttpServletResponse response;
@Autowired
private RedisTemplate redisTemplate;
@RequestMapping("/addGoodsToCart")
public Result addCart(Long itemId,Integer num){
try {
List<Cart> cartList = findCartList();
List<Cart> carts = cartService.addCart(cartList, itemId, num);
String username = SecurityContextHolder.getContext().getAuthentication().getName();
if ("anonymousUser".equals(username)){//没有登录,将信息存入cookie
//存入cookie
//复杂对象不支持编码,转成字符串,字符串是可编码的,防止对象中中文乱码
String cartsJson = JSON.toJSONString(carts);
CookieUtil.setCookie(request,response,"cartList",cartsJson,3600*24,"utf-8");
System.out.println(">>>>>save cartList to cookie" );
}
else{//已登录,存入redis
redisTemplate.boundHashOps("cartList").put(username,carts);
System.out.println(">>>>>save cartList to Redis" );
}
return new Result(true,"加入购物车成功!");
} catch (Exception e) {
e.printStackTrace();
return new Result(false,"加入购物车失败!");
}
}
/*
获取到原来的购物车对象
*/
@RequestMapping("/findCartList")
public List<Cart> findCartList(){
String username = SecurityContextHolder.getContext().getAuthentication().getName();
if ("anonymousUser".equals(username)) {//没有登录,从cookie取出信息
String cartListStr = CookieUtil.getCookieValue(request, "cartList","utf-8");
if (cartListStr==null || cartListStr.equals("")){
cartListStr = "[]";
}
List<Cart> cartList = JSON.parseArray(cartListStr, Cart.class);
System.out.println(">>>>>get cartList from Cookie" );
return cartList;
}else{//已登录,从redis中取出信息
List<Cart> cartList = (List<Cart>) redisTemplate.boundHashOps("cartList").get(username);
if (cartList==null){
cartList = new ArrayList<>();
}
System.out.println(">>>>>get cartList from Redis" );
return cartList;
}
}
@RequestMapping("/test")
public List<Cart> testCartList(){
String cartListStr = CookieUtil.getCookieValue(request, "cartList","utf-8");
if (cartListStr==null || cartListStr.equals("")){
cartListStr = "[]";
}
List<Cart> cartList = JSON.parseArray(cartListStr, Cart.class);
return cartList;
}
这样我们就做到了,未登录时,购物车信息存储在本地浏览器的cookie中,已登录时购物车信息存储在redis服务器中
购物车的合并(将cookie中的购物车信息同步到redis中)
service层新添合并方法,会调用之前的添加方法
/**
* 将cookieList合并到redisList
* @param redisList
* @param cookieList
* @return
*/
@Override
public List<Cart> joinCartList(List<Cart> redisList, List<Cart> cookieList) {
for (Cart cart : cookieList) {
List<TbOrderItem> orderItemList = cart.getOrderItemList();
for (TbOrderItem orderItem : orderItemList) {
addCart(redisList,orderItem.getItemId(),orderItem.getNum());
}
}
return redisList;
}
修改controller层的获取方法
@RequestMapping("/findCartList")
public List<Cart> findCartList(){
String username = SecurityContextHolder.getContext().getAuthentication().getName();
// 从cookie中获取购物车列表,如果考虑将cookie同步到redis的情况
// 那么,不管登录与否,都需要获取本地cookie数据
String cartListStr = CookieUtil.getCookieValue(request, "cartList","utf-8");
if (cartListStr==null || cartListStr.equals("")){
cartListStr = "[]";
}
List<Cart> cookieList = JSON.parseArray(cartListStr, Cart.class);
//没有登录,从cookie取出信息
if ("anonymousUser".equals(username)) {
System.out.println(">>>>>get cartList from Cookie" );
return cookieList;
}
//已登录
else{
List<Cart> redisList = (List<Cart>) redisTemplate.boundHashOps("cartList").get(username);
if (redisList==null){
redisList = new ArrayList<>();
}
if (cookieList.size()>0){//如果cookie的购物车里有数据就进行合并,没有就直接返回redis中的数据
//合并购物车
redisList = cartService.joinCartList(redisList, cookieList);
// 将合并后的数据重新存回redis
redisTemplate.boundHashOps("cartList").put(username,redisList);
// 合并完成后,删除本地的cookie
CookieUtil.deleteCookie(request, response, "cartList");
System.out.println(">>>>>import cookie to redis");
}
return redisList;
}
}