【电商项目实战】商品详情显示与Redis存储购物车信息

欢迎来到我的CSDN主页!

我是Java方文山,一个在CSDN分享笔记的博主。

推荐给大家我的专栏《电商项目实战》。

点击这里,就可以查看我的主页啦!

Java方文山的个人主页

如果感觉还不错的话请给我点赞吧!

期待你的加入,一起学习,一起进步!

请添加图片描述

一、商品详情

当我们点击商品的时候需要展示对应的商品详情,首先在首页商品页写入后端的接口

controller代码

@Controller
@RequestMapping("/goods")
public class GoodsController {
    @Autowired
    private IGoodsService goodsService;


    @RequestMapping("/ByIdSelect")
    public String ByIdSelect(GoodsVo vo, Model model){
        Goods g = goodsService.getById(vo.getGid());
        model.addAttribute("g",g);
        return "proDetail";
    }

}

 在对应的页面显示相应的数据



	
		<#include "common/head.html">
		
		
	
	
		
		<#include "common/top.html">
		
		
首页 / 装饰摆件 / 干花花艺 / <#-- 注意: 1)${goods.goodsTitle!}:只能判断goodsTitle属性是否为空,不能判断goods对象是否为空 2)${(goods.goodsTitle)!}:既可以判断goods对象是否为空,也可以判断goodsTitle属性是否为空 --> ${(g.goodsTitle)!}
<#--
]
-->

${(g.goodsTitle)!}

${(g.goodsName)!}

¥${(g.goodsPrice)!}

颜色分类

白瓷花瓶+20支快乐花

白瓷花瓶+20支兔尾巴草

20支快乐花

20支兔尾巴草

数量  库存${(g.goodsStock)!}

1

请选择商品属性!

馨***呀

不好意思评价晚了,产品很好,价格比玻璃品便宜,没有我担心的杂色,发货快,包装好,全5分

2016年12月27日08:31颜色分类:大中小三件套(不含花)

么***周

花瓶超级棒,我看图以为是光面的,收货发现是磨砂,但感觉也超有质感,很喜欢。磨砂上面还有点纹路,不过觉得挺自然的,不影响美观。包装也很好,绝对不会磕碎碰坏,好评!

2016年12月27日08:31颜色分类:大中小三件套(不含花)

馨***呀

不好意思评价晚了,产品很好,价格比玻璃品便宜,没有我担心的杂色,发货快,包装好,全5分

2016年12月27日08:31颜色分类:大中小三件套(不含花)

么***周

花瓶超级棒,我看图以为是光面的,收货发现是磨砂,但感觉也超有质感,很喜欢。磨砂上面还有点纹路,不过觉得挺自然的,不影响美观。包装也很好,绝对不会磕碎碰坏,好评!

2016年12月27日08:31颜色分类:大中小三件套(不含花)

馨***呀

不好意思评价晚了,产品很好,价格比玻璃品便宜,没有我担心的杂色,发货快,包装好,全5分

2016年12月27日08:31颜色分类:大中小三件套(不含花)

么***周

花瓶超级棒,我看图以为是光面的,收货发现是磨砂,但感觉也超有质感,很喜欢。磨砂上面还有点纹路,不过觉得挺自然的,不影响美观。包装也很好,绝对不会磕碎碰坏,好评!

2016年12月27日08:31颜色分类:大中小三件套(不含花)

馨***呀

不好意思评价晚了,产品很好,价格比玻璃品便宜,没有我担心的杂色,发货快,包装好,全5分

2016年12月27日08:31颜色分类:大中小三件套(不含花)

么***周

花瓶超级棒,我看图以为是光面的,收货发现是磨砂,但感觉也超有质感,很喜欢。磨砂上面还有点纹路,不过觉得挺自然的,不影响美观。包装也很好,绝对不会磕碎碰坏,好评!

2016年12月27日08:31颜色分类:大中小三件套(不含花)

馨***呀

不好意思评价晚了,产品很好,价格比玻璃品便宜,没有我担心的杂色,发货快,包装好,全5分

2016年12月27日08:31颜色分类:大中小三件套(不含花)

么***周

花瓶超级棒,我看图以为是光面的,收货发现是磨砂,但感觉也超有质感,很喜欢。磨砂上面还有点纹路,不过觉得挺自然的,不影响美观。包装也很好,绝对不会磕碎碰坏,好评!

2016年12月27日08:31颜色分类:大中小三件套(不含花)

<#include "common/footer.html">

效果展示 

二、加入购物车

1.前端方法编写

商品详情页面引入了一个cart.js,里面是有关该所有的点击事件方法

$(function(){
	/**************数量加减***************/
	$(".num .sub").click(function(){
		var num = parseInt($(this).siblings("span").text());
		if(num<=1){
			$(this).attr("disabled","disabled");
		}else{
			num--;
			$(this).siblings("span").text(num);
			//获取除了货币符号以外的数字
			var price = $(this).parents(".number").prev().text().substring(1);
			//单价和数量相乘并保留两位小数
			$(this).parents(".th").find(".sAll").text('¥'+(num*price).toFixed(2));
			jisuan();
			zg();
		}
	});
	$(".num .add").click(function(){
		var num = parseInt($(this).siblings("span").text());
		if(num>=5){
			confirm("限购5件");
		}else{
			num++;
			$(this).siblings("span").text(num);
			var price = $(this).parents(".number").prev().text().substring(1);
			$(this).parents(".th").find(".sAll").text('¥'+(num*price).toFixed(2));
			jisuan();
			zg();
		}
	});
	//计算总价
	function jisuan(){
		var all=0;
		var len =$(".th input[type='checkbox']:checked").length;
		if(len==0){
			 $("#all").text('¥'+parseFloat(0).toFixed(2));
		}else{
			 $(".th input[type='checkbox']:checked").each(function(){
			 	//获取小计里的数值
	        	var sAll = $(this).parents(".pro").siblings('.sAll').text().substring(1);
	        	//累加
	        	all+=parseFloat(sAll);
	        	//赋值
	        	$("#all").text('¥'+all.toFixed(2));
	        })
		}
		
	}
	//计算总共几件商品
	function zg(){
		var zsl = 0;
		var index = $(".th input[type='checkbox']:checked").parents(".th").find(".num span");
		var len =index.length;
		if(len==0){
			$("#sl").text(0);
		}else{
			index.each(function(){
				zsl+=parseInt($(this).text());
				$("#sl").text(zsl);
			})
		}
		if($("#sl").text()>0){
			$(".count").css("background","#c10000");
		}else{
			$(".count").css("background","#8e8e8e");
		}
	}
	/*****************商品全选***********************/
	$("input[type='checkbox']").on('click',function(){
		var sf = $(this).is(":checked");
		var sc= $(this).hasClass("checkAll");
		if(sf){
			if(sc){
				 $("input[type='checkbox']").each(function(){  
	                this.checked=true;  
	           }); 
				zg();
	           	jisuan();
			}else{
				$(this).checked=true; 
	            var len = $("input[type='checkbox']:checked").length;
	            var len1 = $("input").length-1;
				if(len==len1){
					 $("input[type='checkbox']").each(function(){  
		                this.checked=true;  
		            }); 
				}
				zg();
				jisuan();
			}
		}else{
			if(sc){
				 $("input[type='checkbox']").each(function(){  
	                this.checked=false;  
	           }); 
				zg();
				jisuan();
			}else{
				$(this).checked=false;
				var len = $(".th input[type='checkbox']:checked").length;
	            var len1 = $("input").length-1;
				if(len{

		},"json")

	});
	
	//删除购物车商品
	$('.del').click(function(){
		//单个删除
		if($(this).parent().parent().hasClass("th")){
			$(".mask").show();
			$(".tipDel").show();
			index = $(this).parents(".th").index()-1;
			$('.cer').click(function(){
				$(".mask").hide();
				$(".tipDel").hide();
				$(".th").eq(index).remove();
				$('.cer').off('click');
				if($(".th").length==0){
					$(".table .goOn").show();
				}
			})
		}else{
			//选中多个一起删除
			if($(".th input[type='checkbox']:checked").length==0){
				$(".mask").show();
				$(".pleaseC").show();
			}
			else{
				$(".mask").show();
				$(".tipDel").show();
				$('.cer').click(function(){
					$(".th input[type='checkbox']:checked").each(function(j){
						index = $(this).parents('.th').index()-1;
						$(".th").eq(index).remove();
						if($(".th").length==0){
							$(".table .goOn").show();
						}
					})
					$(".mask").hide();
					$(".tipDel").hide();
					zg();
					jisuan();
				})
			}
		}
	})
	$('.cancel').click(function(){
		$(".mask").hide();
		$(".tipDel").hide();
	})
	//改变商品规格
//	$(".pro dd").hover(function(){
//		var html='';
//		html='修改';
//		$(this).addClass("on").append(html).parents(".th").siblings(".th").find(".pro dd").removeClass("on").find('.edit').remove();
//		$(".edit").each(function(i){
//			$(this).attr("id",'edit'+i);
//			$("#edit"+i).click(function(){
//				$(".proDets").show();
//				$(".mask").show();
//				$(".changeBtn .buy").attr("data-id",i);
//			})
//		})
//	},function(){
//		$(this).removeClass("on");
//	})
//	$(".changeBtn .buy").click(function(){
//		var index = $(this).attr("data-id");
//		var result = $(".smallImg .on").find("img").attr("alt");
//		$("#edit"+index).prev().text(result);
//		$(".proDets").hide();
//		$(".mask").hide();
//		$("#edit"+index).parent("dd").removeClass("on").find(".edit").remove();
//	});
//	$(".changeBtn .cart").click(function(){
//		$(".proDets").hide();
//		$(".mask").hide();
//	})
})

2.后端代码编写 

我们的用户购物车数据放在数据库太耗费数据库的资源了,我们可以将数据放入redis,进行一个时限判断,如果一周内没有进行数据访问就加入数据库并清除缓存。

我们需要编写新增和查询Redis中购物车数据方法

IRedisService

    // 保存用户购物车信息
    void saveCart(User user, GoodsVo vo);

    // 查询用户购物车信息
    List loadCart(User user);

 IRedisService实现类

    @Override
    public  List loadCart(User user) {
        HashOperations operations=redisTemplate.opsForHash();
        String bigKey=Constants.REDIS_CART_PREFIX + user.getId();
        //根据用户Id查询所有的购物车信息
        List values = operations.values(bigKey);
        return values;
    }

controller 

    @RequestMapping("/getCart")
    public String getCart(GoodsVo vo, HttpServletRequest request, Model model) {
        //根据键获取token
        String token = CookieUtils.getCookieValue(request, "userToken");
        //根据token获取用户
        User user = redisService.loadUser(token);
        //获取用户购物车商品信息
        List goodsVos = redisService.loadCart(user);
        //根据商品Id查询对应商品
        List ids = goodsVos.stream().map(GoodsVo::getGid).collect(Collectors.toList());
        List goods = goodsService.listByIds(ids);
//        进行遍历筛选合适的数据
        for (Goods g : goods) {
            //找到对应属性的商品
            GoodsVo gv = goodsVos.stream()
                    .filter(v -> Objects.equals(v.getGid(), g.getGid()))
                    .findFirst()
                    .orElse(null);
            if (gv != null) {
                //将该商品g的属性赋值给GoodsVos
                BeanUtils.copyProperties(g, gv);
            }
        }
        System.out.println(goodsVos);
        model.addAttribute("item", goodsVos);
        return "cart";
    }

因为我们redis中只有商品的id所以需要将所有商品的id拿到数据库中查询,将该商品的图片信息标题等查询出来利用BeanUtils将属性加入到goodsVos中,方便前端页面显示

三、优化细节部分

1.购物车显示数据




	<#include "common/head.html">
	
	



购物车 继续购物>

商品
单价
数量
小计
操作
<#--有数据就显示--> <#if item??> <#list item as g>
¥${(g.goodsPrice)!}

${(g.num)!}

¥20.00
<#else>
空空如也~去逛逛

全选 删除

0件商品 合计: ¥0.00 结算

确定要删除该商品吗?

确定 取消

<#include "common/footer.html">

颜色分类

白瓷花瓶+20支快乐花

白瓷花瓶+20支兔尾巴草

20支快乐花

20支兔尾巴草

请选择宝贝

这里直接将我们后端所拿到的数据进行判断遍历展示即可

【电商项目实战】商品详情显示与Redis存储购物车信息_第1张图片

2.退出登录

首先我们不止需要清除浏览器上的cookie还需要清除redis中保存的用户信息,所以在Redis的service层定义一个删除的方法

  // 清除用户数据
    void logout(String token);

实现类编写相应代码

    @Override
    public void logout(String token) {
        //根据token删除对应的键
        redisTemplate.delete(Constants.REDIS_USER_PREFIX +token);
    }

 这时候,我们就可以编写退出的controller代码了

   @RequestMapping("/logout")
    public String  login( HttpServletRequest request, HttpServletResponse response){
        //清除redis缓存
        String token = CookieUtils.getCookieValue(request, "userToken");
        redisService.logout(token);
        //清除cookie
        CookieUtils.deleteCookie(request,response,"userToken");
        CookieUtils.deleteCookie(request,response,"nickname");
        return "redirect:/";
    }

清除浏览器cookie的同时也清除redis中的数据避免资源浪费

3.参数解析器 

通过这几天的编写,不知道大家发现没有,我们很多方法中都有获取用户的token信息,可以说这段代码是重复的,我们可以将其优化掉

 UserArgumentResolver 

@Component
public class UserArgumentResolver implements HandlerMethodArgumentResolver {
    @Autowired
    private IRedisService redisService;

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.getGenericParameterType()== User.class;
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
        //从请求中获取cookie
        String token = CookieUtils.getCookieValue(request, "userToken");
        User user = redisService.loadUser(token);
        return user;

    }
}

这个解析器的作用是判断控制器方法的参数是否为User类型,如果是,则会在处理请求之前执行resolveArgument方法。在resolveArgument方法中,它会通过HttpServletRequest获取请求中的cookie,然后调用redisServiceloadUser方法根据cookie中的token加载对应的用户信息。最后,返回解析得到的User对象作为控制器方法的参数。

这里还需要一个config配置类,用于加载自定义解析器

WebConfig

@Component
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private UserArgumentResolver userArgumentResolver;

    @Override
    public void addArgumentResolvers(List resolvers) {
        resolvers.add(userArgumentResolver);
    }

}

请添加图片描述

到这里我的分享就结束了,欢迎到评论区探讨交流!!

如果觉得有用的话还请点个赞吧

你可能感兴趣的:(电商项目实战,开发语言,Java)