宜立方商城—— 购物车增加、删除、修改数量、删除选中商品和清空购物车的实现
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为准。