宜立方商城—— 购物车增加、删除、修改数量、删除选中商品和清空购物车的实现

1.  购物车的实现

1.1. 功能分析

1、购物车是一个独立的表现层工程。

2、添加购物车不要求登录。可以指定购买商品的数量。

3、展示购物车列表页面

4、修改购物车商品数量

5、删除购物车商品

模块划分结构

宜立方商城—— 购物车增加、删除、修改数量、删除选中商品和清空购物车的实现

1.1. 添加购物车

1.1.1.    功能分析

在不登陆的情况下也可以添加购物车。把购物车信息写入cookie。

优点:

1、不占用服务端存储空间

2、用户体验好。

3、代码实现简单。

缺点:

1、cookie中保存的容量有限。最大4k

2、把购物车信息保存在cookie中,更换设备购物车信息不能同步。

请求的url:/cart/add/{itemId}

参数:

1)商品id: Long itemId
2)商品数量: int num

业务逻辑:

1、从cookie中查询商品列表。

2、判断商品在商品列表中是否存在。

3、如果存在,商品数量相加。

4、不存在,根据商品id查询商品信息。

5、把商品添加到购车列表。

6、把购车商品列表写入cookie。

 

返回值:逻辑视图

Controller

/**
 * 购物车Controller
 * @Auther: jun
 * @Date: 2018/5/31 0031 19:44
 * @Description:
 */
@Controller
public class CartController {
    @Autowired
    private ItemService itemService;
    @Autowired
    private CartService cartService;
    @Value("${COOKIE_CART_EXIPERE}")
    private Integer COOKIE_CART_EXIPERE;
    /**
     *添加购物车商品
     * @auther: jun
     * @date: 2018/5/31 0031 22:16
     * @param itemId,num,request,response
     * @return: java.lang.String
     * @Description:
     */
    @RequestMapping("/cart/add/{itemId}")
    public String addCart(@PathVariable Long itemId, @RequestParam(defaultValue = "1") Integer num,
                          HttpServletRequest request, HttpServletResponse response){
        //判断用户是否登录
        //从request中获取到用户信息
        TbUser user = (TbUser) request.getAttribute("user");
        //判断是否有值有就是登陆状态把购物车写入redis中
        if (user !=null){
            //保存到服务端(redis)
            cartService.addCart(user.getId(),itemId,num);
            //返回逻辑视图
            return "cartSuccess";
        }
        //从cookie中获取到购物车列表
        List<TbItem> cartList = getCartListFromCookie(request);
        //用于判断购物车中是否有添加的商品信息
        boolean flag=false;
        //遍历购物车商品列表
        for (TbItem item:cartList) {
            //判断添加的商品是否在购物册列表中使用商品id判断
            if (itemId.equals(item.getId())){
                //如果在flag为true
                flag=true;
                //并且购物车中商品数量加上添加商品的的数量
                item.setNum(item.getNum()+num);
                //跳出循环
                break;
            }
        }
        //如果不存在
        if(!flag){
            //根据商品id查询得到一个TbItem对象
            TbItem item = itemService.getItemById(itemId);
            //设置商品数量
            item.setNum(num);
            //取一张图片
            String image = item.getImage();
            if (StringUtils.isNoneBlank(image)){
              //设置第一张图片
                item.setImage(image.split(",")[0]);
            }
            //添加商品到购物车商品列表中
            cartList.add(item);
        }
        //写入cookie中需要数据需要转换成json数据还有过期时间1个小时,还要编码实现
        CookieUtils.setCookie(request,response,"cart",JsonUtils.objectToJson(cartList),COOKIE_CART_EXIPERE,true);
        //返回逻辑视图页面
        return "cartSuccess";
    }
/**
 *从cookie中取购物车列表的处理
 * @auther: jun
 * @date: 2018/5/31 0031 19:47
 * @param request
 * @return: java.util.List<com.e3mall.pojo.TbItem>
 * @Description: 
 */
private List<TbItem> getCartListFromCookie(HttpServletRequest request){
    //从cookie中取出数据时需要转码
    String json = CookieUtils.getCookieValue(request, "cart", true);
    //判断json是否为空
    if (StringUtils.isBlank(json)){
        //避免空值
        return new ArrayList<>();
    }
    //返回List的商品列表
    return JsonUtils.jsonToList(json,TbItem.class);
}
 

Service层

配置文件resource.properties

宜立方商城—— 购物车增加、删除、修改数量、删除选中商品和清空购物车的实现

/**
 * 购物车Service
 * @Auther: jun
 * @Date: 2018/6/1 0001 15:23
 * @Description:
 */
@Service
public class CartServiceImpl implements CartService {
    @Autowired
    private JedisClient jedisClient;
    @Autowired
    private TbItemMapper itemMapper;
    @Value("${REDIS_CART_PRE}")
    private String REDIS_CART_PRE;

    @Override
    public E3Result addCart(Long userId, Long itemId,Integer num) {
        //向redis中添加购物车
        //数据类型:hash类型 key:用户id filed:商品id value :商品信息
        //判断商品是否存在
        Boolean hexists = jedisClient.hexists(REDIS_CART_PRE + ":" + userId, itemId.toString());
        if (hexists){
            //存在获取到redis中的存在的商品值
            String json = jedisClient.hget(REDIS_CART_PRE + ":" + userId, itemId.toString());
            //把json转换成pojo对象
            TbItem item = JsonUtils.jsonToPojo(json, TbItem.class);
            //数量相加
            item.setNum(item.getNum()+num);
            //重新写入redis中
            jedisClient.hset(REDIS_CART_PRE + ":" + userId,itemId.toString(),JsonUtils.objectToJson(item));
            //退出返回结果
            return E3Result.ok();
        }
        //不存在就是要查询数据库并且存入redis中
        //根据商品id查询商品信息
        TbItem item = itemMapper.selectByPrimaryKey(itemId);
        //设置数量
        item.setNum(num);
        //取一张图片存入item中
        String image = item.getImage();
        if (StringUtils.isNoneBlank(image)){
            item.setImage(image.split(",")[0]);
        }
        jedisClient.hset(REDIS_CART_PRE + ":" + userId,itemId.toString(),JsonUtils.objectToJson(item));
        return E3Result.ok();
    }

applicationContent-service.xml发布服务

宜立方商城—— 购物车增加、删除、修改数量、删除选中商品和清空购物车的实现

<!-- 使用dubbo发布服务 -->
<!-- 提供方应用信息,用于计算依赖关系 -->
<dubbo:application name="e3-sso" />
<dubbo:registry protocol="zookeeper" address="192.168.25.128:2181" />
<!-- 用dubbo协议在20880端口暴露服务 -->
<dubbo:protocol name="dubbo" port="20884" />
<!-- 声明需要暴露的服务接口 -->
<!--购物车服务-->
<dubbo:service interface="com.e3mall.cart.service.CartService" ref="cartServiceImpl" timeout="600000"/>

springmvc.xml 调用服务

宜立方商城—— 购物车增加、删除、修改数量、删除选中商品和清空购物车的实现

<!-- 引用dubbo服务 -->
<dubbo:application name="e3-cart-web"/>
<dubbo:registry protocol="zookeeper" address="192.168.25.128:2181"/>
<!--商品服务-->
<dubbo:reference interface="com.e3mall.service.ItemService" id="itemService" />
<!--sso服务-->
<dubbo:reference interface="com.e3mall.sso.service.TokenService" id="tokenService" />
<dobbo:reference interface="com.e3mall.cart.service.CartService" id="cartService"/>

配置用户拦截器判断用户是登录和未登录状态

/**
 * 用户登录拦截器实现
 *
 * @Auther: jun
 * @Date: 2018/6/1 0001 14:38
 * @Description:
 */
public class LoginInterceptor implements HandlerInterceptor {
    @Autowired
    private TokenService tokenService;
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler) throws Exception {
        //前处理,执行handler之前执行此方法
        // 返回true 放行 false 拦截
        //1.从cookie中取出token
        String token = CookieUtils.getCookieValue(httpServletRequest, "token");
        //2.如果没有token,未登录状态,直接放行
        if (StringUtils.isBlank(token)){
            //放行
            return true;
        }
        //3.取到token,炫耀调用sso系统的服务,根据token去用户信息
        E3Result result = tokenService.getUserByToker(token);
        //4.没有取到用户信息,登录过期,直接放行
        //判断状态为200 登录成功
        if (result.getStatus()!=200){
           return true;
        }
        //在token中取用户信息
        //5.取到用户信息,登录状态
        TbUser user = (TbUser) result.getData();
        //6.把用户信息放到request中,只需要在Controller中判断request中是否包含user信息
        httpServletRequest.setAttribute("user",user);
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler, ModelAndView modelAndView) throws Exception {
    //handler执行之后,返回ModelAndView之前
    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler, Exception e) throws Exception {
        //完成处理之后,返回ModelAndView之后
        //可以在此处理异常
    }

在springmvc.xml拦截器配置

宜立方商城—— 购物车增加、删除、修改数量、删除选中商品和清空购物车的实现

<mvc:interceptors>
    <mvc:interceptor>
        <!--拦截所有的url-->
        <mvc:mapping path="/**"/>
        <!--配置拦截器的实现类-->
        <bean class="com.e3mall.cart.interceptor.LoginInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

1.1. 展示购物车商品列表

请求的url:/cart/cart

参数:无

返回值:逻辑视图

业务逻辑:

需要考虑登录用户redis中的购物车数据和cookie中的数据和并

没有登录就展示cookie中的数据

1、从cookie中取商品列表。

2、把商品列表传递给页面。

    引用服务
/**
 *购物车展示
 * @auther: jun
 * @date: 2018/5/31 0031 20:38
 * @param request
 * @return: java.lang.String
 * @Description:
 */
@RequestMapping("/cart/cart")
public  String showCartList(HttpServletRequest request,HttpServletResponse response){
    //从cookie中取购物车列表
    List<TbItem> cartList = getCartListFromCookie(request);
    //判断用户是否为登录状态
    TbUser user = (TbUser) request.getAttribute("user");
    //如果是登录状态
    if (user!=null){
        //如果不为空,把cookie购物车和redis中的购物车商品信息合并
        cartService.mergeCart(cartList, user.getId());
        //把cookie中的购物车商品删除
        CookieUtils.deleteCookie(request,response,"cart");
        //从redis取购物车列表信息
        cartList = cartService.getCartList(user.getId());
    }

    //未登录状态
   // List<TbItem> cartList = cartList;
    //把列表转递给页面
    request.setAttribute("cartList",cartList);
    //返回逻辑视图
    return "cart";
}

Service

合并数据的实现方法

@Override
public E3Result mergeCart(List<TbItem> itemList, Long userId) {
    //遍历商品列表
    //把商品列表添加到购物车中
    //判断购物车中是否有该商品
    //如果有就商品数量相加
    //没有就添加新的商品信息
    for (TbItem item : itemList) {
        //调用添加购物车方法实现
        addCart(userId,item.getId(),item.getNum());
    }
    //返回成功
    return E3Result.ok();
}

展示购物车数据

@Override
public List<TbItem> getCartList(Long userId) {
    //根据用户id查询购物车商品列表(redis 中的键找值)
    List<String> jsonList = jedisClient.hvals(REDIS_CART_PRE + ":" + userId);
    //创建一个商品列表信息
    List<TbItem> itemList=new ArrayList<>();
    for (String string : jsonList) {
        //获取到json中一个数据并转换数据类型成pojo对象
        TbItem item = JsonUtils.jsonToPojo(string, TbItem.class);
        //添加到列表
        itemList.add(item);
    }
    //返回列表
    return itemList;
}

发布服务

1.1. 修改购物车商品数量

1.1.1.    功能分析

1、在页面中可以修改商品数量

2、重新计算小计和总计。

3、修改需要写入cookie。

4、每次修改都需要向服务端发送一个ajax请求,在服务端修改cookie中的商品数量。

请求的url:/cart/update/num/{itemId}/{num}

参数:long itemId、int num

业务逻辑:

1、接收两个参数

2、从cookie中取商品列表

3、遍历商品列表找到对应商品

4、更新商品数量

5、把商品列表写入cookie。

6、响应e3Result。Json数据。

返回值:

 e3Result。Json数据

Controller

引用服务

/**
 *修改商品数据功能
 * @auther: jun
 * @date: 2018/5/31 0031 22:16
 * @param itemId,num,request,response
 * @return: com.e3mall.common.utils.E3Result
 * @Description:
 */
@RequestMapping("/cart/update/num/{itemId}/{num}")
@ResponseBody
public E3Result updateCartNum(@PathVariable Long itemId,@PathVariable Integer num,
                              HttpServletRequest request,HttpServletResponse response){
    //获取登录信息
    TbUser user = (TbUser) request.getAttribute("user");
    //判断登录状态
    if (user!=null){
        //调用服务更新
        cartService.updateCartNum(user.getId(), itemId, num);
        return E3Result.ok();
    }
    //从cookie中获取到商品列表
    List<TbItem> cartList= getCartListFromCookie(request);
    //遍历列表
    for (TbItem item:cartList){
        //根据id修改商品列表中的数量信息
        if (itemId==item.getId().longValue()){
            item.setNum(num);
        }
        //退出
        break;
    }
    //把购物车写回cookie
    CookieUtils.setCookie(request,response,"cart",JsonUtils.objectToJson(cartList),COOKIE_CART_EXIPERE,true);
    //返回结果
    return E3Result.ok();
}

Service

@Override
public E3Result updateCartNum(Long userId, Long itemId, Integer num) {
    //获取到购物车中商品信息
    String json = jedisClient.hget(REDIS_CART_PRE + ":" + userId, itemId.toString());
        //转换json成pojo对象
        TbItem item = JsonUtils.jsonToPojo(json, TbItem.class);
        //写入数量
        item.setNum(num);
        //回写到redis中
        jedisClient.hset(REDIS_CART_PRE + ":" + userId,itemId.toString(),JsonUtils.objectToJson(item));
    //返回结果
    return E3Result.ok();
}

发布服务

2、重新计算小计和总计。jsp页面修改

1.修改cart.jsp

宜立方商城—— 购物车增加、删除、修改数量、删除选中商品和清空购物车的实现

cart.js

加减方法中添加的

CART.refreshTotal(_thisInput.val(),_thisInput.attr("itemId"));

宜立方商城—— 购物车增加、删除、修改数量、删除选中商品和清空购物车的实现

宜立方商城—— 购物车增加、删除、修改数量、删除选中商品和清空购物车的实现

1.1. 删除购物车商品

1.1.1.    功能分析

请求的url:/cart/delete/{itemId}

参数:商品id

返回值:展示购物车列表页面。Url需要做redirect跳转。

业务逻辑:

1、从url中取商品id

2、从cookie中取购物车商品列表

3、遍历列表找到对应的商品

4、删除商品。

5、把商品列表写入cookie。

6、返回逻辑视图:在逻辑视图中做redirect跳转。

Controller

引用服务

/**
 *删除购物车商品
 * @auther: jun
 * @date: 2018/6/1 0001 17:18
 * @param itemId,request,response
 * @return: java.lang.String
 * @Description:
 */
@RequestMapping("/cart/delete/{itemId}")
public  String deleteCartItem(@PathVariable Long itemId,
                              HttpServletRequest request,HttpServletResponse response){
    //获取登录信息
    TbUser user = (TbUser) request.getAttribute("user");
    //判断登录状态
    if (user!=null){
        //调用服务更新
        cartService.deleteCartItem(user.getId(), itemId);
        //返回逻辑视图
        return "redirect:/cart/cart.html";
    }
    //从cookie中获取商品列表
    List<TbItem> cartList = getCartListFromCookie(request);
    //遍历列表
    for (TbItem item:cartList){
        //根据id删除指定商品列表中的数据
        if (itemId==item.getId().longValue()){
            //移除cookie中的商品信息
            cartList.remove(item);
        }
        //退出
        break;
    }
Service

@Override
public E3Result deleteCartItem(Long userId, Long itemId) {
    //删除指定id的商品
    jedisClient.hdel(REDIS_CART_PRE + ":" + userId,itemId.toString());
    //返回结果
    return E3Result.ok();
}

发布服务

清空购物车

需要修改一下jsp并且还要写一段js代码

宜立方商城—— 购物车增加、删除、修改数量、删除选中商品和清空购物车的实现

Controller代码

/**
 *清空购物车
 * @auther: jun
 * @date: 2018/6/4 0004 13:10
 * @param request
 * @param response
 * @return: java.lang.String
 * @Description:
 */
@RequestMapping("/cart/clearCart")
public String clearCart(HttpServletRequest request, HttpServletResponse response) throws IOException {
    //获取用户信息
    TbUser user = (TbUser) request.getAttribute ("user");
    //是否登录状态
    if (user != null) {
        //调用服务清除购物车合并后的数据
        cartService.clearCartItem (user.getId ());
        return "redirect:/cart/cart.html";
    }
    //cookie清除
   CookieUtils.deleteCookie (request, response, "cart");
    return "redirect:/cart/cart.html";
}

调用服务

@Override
public E3Result clearCartItem(Long userId) {
    //删除购物车信息
    jedisClient.del (REDIS_CART_PRE+":"+userId);
    return E3Result.ok ();
}

发布服务

删除选中的商品的功能实现

jsp修改页面value

宜立方商城—— 购物车增加、删除、修改数量、删除选中商品和清空购物车的实现

js代码段

宜立方商城—— 购物车增加、删除、修改数量、删除选中商品和清空购物车的实现

Controller

这里我们考虑了登录和未登录状态问题所以我们操作了cookie数据和redis中的数据

/**
 * 批量删除购物车中的商品
 *
 * @param ids
 * @param request
 * @param response
 * @auther: jun
 * @date: 2018/6/4 0004 10:48
 * @return: com.e3mall.common.utils.E3Result
 * @Description:
 */
@RequestMapping(value = "/cart/cartDelMore")
@ResponseBody
public E3Result cartDelMore(String[] ids,
                            HttpServletRequest request, HttpServletResponse response) {
    //判断是否是登录状态
    TbUser user = (TbUser) request.getAttribute ("user");
    if (user != null) {
        //调用服务执行批量删除商品
        cartService.cartDelMore (user.getId (), ids);
    }
    //未登录状态
    //从cookie中获取商品列表
    List<TbItem> cartList = getCartListFromCookie (request);
    //遍历参数数组
    for (String id : ids) {
        //遍历商品列表
        for (TbItem item : cartList) {
            //判断
            if (Long.parseLong (id) ==item.getId ().longValue () ) {
                //移除
                cartList.remove (item);
                //退出当前商品列表集合
                break;
            }
        }
    }
    //重新写入列表到cookie
    CookieUtils.setCookie (request, response, "cart", JsonUtils.objectToJson (cartList), COOKIE_CART_EXIPERE, true);
    return E3Result.ok ();
}

Service

@Override
public E3Result cartDelMore(Long userId, String[] ids) {
    for (String itemId:ids){
        //删除指定id的商品
        jedisClient.hdel(REDIS_CART_PRE + ":" + userId,itemId);
    }
    return E3Result.ok ();
}

1.1. 小结

使用cookie实现购物车:

优点:

1、实现简单

2、不需要占用服务端存储空间。

缺点:

1、存储容量有限

2、更换设备购车信息不能同步。

 

实现购车商品数据同步:

1、要求用户登录。

2、把购物车商品列表保存到数据库中。推荐使用redis。

3、Key:用户id,value:购车商品列表。推荐使用hash,hash的field:商品id,value:商品信息。

4、在用户未登录情况下写cookie。当用户登录后,访问购物车列表时,

a)        把cookie中的数据同步到redis。

b)       把cookie中的数据删除

c)        展示购物车列表时以redis为准。

d)       如果redis中有数据cookie中也有数据,需要做数据合并。相同商品数量相加,不同商品添加一个新商品。

如果用户登录状态,展示购物车列表以redis为准。如果未登录,以cookie为准。