🥳🥳Welcome Huihui's Code World ! !🥳🥳
接下来看看由辉辉所写的关于SpringBoot电商项目的相关操作吧
目录
🥳🥳Welcome Huihui's Code World ! !🥳🥳
一.功能需求
二.代码编写
1.商品详情的显示
2.加入购物车
3.参数解析器
4.购物车的前端优化
5.购物车的删除【单个&批量】
一.功能需求
①商品详情的显示(根据id查询单个商品)
②加入购物车(redis+cookie+数据库)
③需要通过用户信息来获取购物车【参数解析器】
④前端的显示【商品数量,小计】
⑤购物车的删除【全选与全不选,单个删除与批量删除】
二.代码编写
1.商品详情的显示
首页的商品点击跳转到商品详情页
<a href="${ctx}/goods/selectOne?gid=${g.gid}">
<!DOCTYPE html> <html> <head lang="en"> <#include "common/head.html"> <link rel="stylesheet" type="text/css" href="css/public.css"/> <link rel="stylesheet" type="text/css" href="css/index.css" /> </head> <body> <!------------------------------head------------------------------> <#include "common/top.html"> <!-------------------------banner---------------------------> <div class="block_home_slider"> <div id="home_slider" class="flexslider"> <ul class="slides"> <li> <div class="slide"> <img src="img/banner2.jpg"/> </div> </li> <li> <div class="slide"> <img src="img/banner1.jpg"/> </div> </li> </ul> </div> </div> <!------------------------------thImg------------------------------> <div class="thImg"> <div class="clearfix"> <a href="${ctx}/page/vase_proList.html"><img src="img/i1.jpg"/></a> <a href="${ctx}/page/proList.html"><img src="img/i2.jpg"/></a> <a href="#2"><img src="img/i3.jpg"/></a> </div> </div> <!------------------------------news------------------------------> <div class="news"> <div class="wrapper"> <h2><img src="img/ih1.jpg"/></h2> <div class="top clearfix"> <a href="${ctx}/page/proDetail.html"><img src="img/n1.jpg"/><p></p></a> <a href="${ctx}/page/proDetail.html"><img src="img/n2.jpg"/><p></p></a> <a href="${ctx}/page/proDetail.html"><img src="img/n3.jpg"/><p></p></a> </div> <div class="bott clearfix"> <a href="${ctx}/page/proDetail.html"><img src="img/n4.jpg"/><p></p></a> <a href="${ctx}/page/proDetail.html"><img src="img/n5.jpg"/><p></p></a> <a href="${ctx}/page/proDetail.html"><img src="img/n6.jpg"/><p></p></a> </div> <h2><img src="img/ih2.jpg"/></h2> <#list p1 as p> <div class="flower clearfix tran"> <#list p as g> <a href="${ctx}/goods/selectOne?gid=${g.gid}" class="clearfix"> <dl> <dt> <span class="abl"></span> <img src="${g.goodsImg}"/> <span class="abr"></span> </dt> <dd>${g.goodsTitle}</dd> <dd><span>¥${g.goodsPrice}</span></dd> </dl> </a> </#list> </div> </#list> </div> </div> <!------------------------------ad------------------------------> <a href="#" class="ad"><img src="img/ib1.jpg"/></a> <!------------------------------people------------------------------> <div class="people"> <div class="wrapper"> <h2><img src="img/ih3.jpg"/></h2> <#list p2 as p> <div class="pList clearfix tran"> <#list p as g> <a href="${ctx}/goods/selectOne?gid=${g.gid}"> <dl> <dt> <span class="abl"></span> <img src="${g.goodsImg}"/> <span class="abr"></span> </dt> <dd>${g.goodsTitle}</dd> <dd><span>¥${g.goodsPrice}</span></dd> </dl> </a> </#list> </div> </#list> </div> </div> </div> <#include "common/footer.html"/> <script src="js/public.js" type="text/javascript" charset="utf-8"></script> <script src="js/nav.js" type="text/javascript" charset="utf-8"></script> <script src="js/jquery.flexslider-min.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> $(function() { $('#home_slider').flexslider({ animation: 'slide', controlNav: true, directionNav: true, animationLoop: true, slideshow: true, slideshowSpeed:2000, useCSS: false }); }); </script> </body> </html>
效果预览
2.加入购物车
前端
<a href="javascript:void(0);"><p class="cart fr" data-gid="${(good.gid)!}">加入购物车</p></a>
完整代码
<!DOCTYPE html> <html> <head> <#include "common/head.html"> <link rel="stylesheet" type="text/css" href="css/public.css"/> <link rel="stylesheet" type="text/css" href="css/proList.css"/> </head> <body> <!------------------------------head------------------------------> <#include "common/top.html"> <!-----------------address-------------------------------> <div class="address"> <div class="wrapper clearfix"> <a href="${ctx}/">首页</a> <span>/</span> <a href="${ctx}/page/flowerDer.html">装饰摆件</a> <span>/</span> <a href="${ctx}/page/proList.html">干花花艺</a> <span>/</span> <#-- 注意: 1)${goods.goodsTitle!}:只能判断goodsTitle属性是否为空,不能判断goods对象是否为空 2)${(goods.goodsTitle)!}:既可以判断goods对象是否为空,也可以判断goodsTitle属性是否为空 --> <a href="#" class="on">${(good.goodsTitle)!}</a> </div> </div> <!-----------------------Detail------------------------------> <div class="detCon"> <div class="proDet wrapper"> <div class="proCon clearfix"> <div class="proImg fl"> <img class="det" src="${(good.goodsImg)!}" /> <#--<div class="smallImg clearfix">] <img src="img/temp/proDet01.jpg" data-src="img/temp/proDet01_big.jpg"> <img src="img/temp/proDet02.jpg" data-src="img/temp/proDet02_big.jpg"> <img src="img/temp/proDet03.jpg" data-src="img/temp/proDet03_big.jpg"> <img src="img/temp/proDet04.jpg" data-src="img/temp/proDet04_big.jpg"> </div>--> </div> <div class="fr intro"> <div class="title"> <h4>${(good.goodsName)!}</h4> <p>${(good.goodsDetail)!}</p> <span>¥${(good.goodsPrice)!}</span> </div> <div class="proIntro"> <p>颜色分类</p> <div class="smallImg clearfix categ"> <p class="fl"><img src="img/temp/prosmall01.jpg" alt="白瓷花瓶+20支快乐花" data-src="img/temp/proBig01.jpg"></p> <p class="fl"><img src="img/temp/prosmall02.jpg" alt="白瓷花瓶+20支兔尾巴草" data-src="img/temp/proBig02.jpg"></p> <p class="fl"><img src="img/temp/prosmall03.jpg" alt="20支快乐花" data-src="img/temp/proBig03.jpg"></p> <p class="fl"><img src="img/temp/prosmall04.jpg" alt="20支兔尾巴草" data-src="img/temp/proBig04.jpg"></p> </div> <p>数量 库存<span>${(good.goodsStock)!}</span>件</p> <div class="num clearfix"> <img class="fl sub" src="img/temp/sub.jpg"> <span class="fl" contentEditable="true">1</span> <img class="fl add" src="img/temp/add.jpg"> <p class="please fl">请选择商品属性!</p> </div> </div> <div class="btns clearfix"> <a href="#2"><p class="buy fl">立即购买</p></a> <a href="javascript:void(0);"><p class="cart fr" data-gid="${(good.gid)!}">加入购物车</p></a> </div> </div> </div> </div> </div> <div class="introMsg wrapper clearfix"> <div class="msgL fl"> <div class="msgTit clearfix"> <a class="on">商品详情</a> <a>所有评价</a> </div> <div class="msgAll"> <div class="msgImgs"> <img src="img/temp/det01.jpg"> <img src="img/temp/det02.jpg"> <img src="img/temp/det03.jpg"> <img src="img/temp/det04.jpg"> <img src="img/temp/det05.jpg"> <img src="img/temp/det06.jpg"> <img src="img/temp/det07.jpg"> </div> <div class="eva"> <div class="per clearfix"> <img class="fl" src="img/temp/per01.jpg"> <div class="perR fl"> <p>馨***呀</p> <p>不好意思评价晚了,产品很好,价格比玻璃品便宜,没有我担心的杂色,发货快,包装好,全5分</p> <div class="clearfix"> <p><img src="img/temp/eva01.jpg"></p> <p><img src="img/temp/eva02.jpg"></p> <p><img src="img/temp/eva03.jpg"></p> <p><img src="img/temp/eva04.jpg"></p> <p><img src="img/temp/eva05.jpg"></p> </div> <p><span>2016年12月27日08:31</span><span>颜色分类:大中小三件套(不含花)</span></p> </div> </div> <div class="per clearfix"> <img class="fl" src="img/temp/per02.jpg"> <div class="perR fl"> <p>么***周</p> <p>花瓶超级棒,我看图以为是光面的,收货发现是磨砂,但感觉也超有质感,很喜欢。磨砂上面还有点纹路,不过觉得挺自然的,不影响美观。包装也很好,绝对不会磕碎碰坏,好评!</p> <p><span>2016年12月27日08:31</span><span>颜色分类:大中小三件套(不含花)</span></p> </div> </div> <div class="per clearfix"> <img class="fl" src="img/temp/per01.jpg"> <div class="perR fl"> <p>馨***呀</p> <p>不好意思评价晚了,产品很好,价格比玻璃品便宜,没有我担心的杂色,发货快,包装好,全5分</p> <div class="clearfix"> <p><img src="img/temp/eva01.jpg"></p> <p><img src="img/temp/eva02.jpg"></p> <p><img src="img/temp/eva03.jpg"></p> <p><img src="img/temp/eva04.jpg"></p> <p><img src="img/temp/eva05.jpg"></p> </div> <p><span>2016年12月27日08:31</span><span>颜色分类:大中小三件套(不含花)</span></p> </div> </div> <div class="per clearfix"> <img class="fl" src="img/temp/per02.jpg"> <div class="perR fl"> <p>么***周</p> <p>花瓶超级棒,我看图以为是光面的,收货发现是磨砂,但感觉也超有质感,很喜欢。磨砂上面还有点纹路,不过觉得挺自然的,不影响美观。包装也很好,绝对不会磕碎碰坏,好评!</p> <p><span>2016年12月27日08:31</span><span>颜色分类:大中小三件套(不含花)</span></p> </div> </div> <div class="per clearfix"> <img class="fl" src="img/temp/per01.jpg"> <div class="perR fl"> <p>馨***呀</p> <p>不好意思评价晚了,产品很好,价格比玻璃品便宜,没有我担心的杂色,发货快,包装好,全5分</p> <div class="clearfix"> <p><img src="img/temp/eva01.jpg"></p> <p><img src="img/temp/eva02.jpg"></p> <p><img src="img/temp/eva03.jpg"></p> <p><img src="img/temp/eva04.jpg"></p> <p><img src="img/temp/eva05.jpg"></p> </div> <p><span>2016年12月27日08:31</span><span>颜色分类:大中小三件套(不含花)</span></p> </div> </div> <div class="per clearfix"> <img class="fl" src="img/temp/per02.jpg"> <div class="perR fl"> <p>么***周</p> <p>花瓶超级棒,我看图以为是光面的,收货发现是磨砂,但感觉也超有质感,很喜欢。磨砂上面还有点纹路,不过觉得挺自然的,不影响美观。包装也很好,绝对不会磕碎碰坏,好评!</p> <p><span>2016年12月27日08:31</span><span>颜色分类:大中小三件套(不含花)</span></p> </div> </div> <div class="per clearfix"> <img class="fl" src="img/temp/per01.jpg"> <div class="perR fl"> <p>馨***呀</p> <p>不好意思评价晚了,产品很好,价格比玻璃品便宜,没有我担心的杂色,发货快,包装好,全5分</p> <div class="clearfix"> <p><img src="img/temp/eva01.jpg"></p> <p><img src="img/temp/eva02.jpg"></p> <p><img src="img/temp/eva03.jpg"></p> <p><img src="img/temp/eva04.jpg"></p> <p><img src="img/temp/eva05.jpg"></p> </div> <p><span>2016年12月27日08:31</span><span>颜色分类:大中小三件套(不含花)</span></p> </div> </div> <div class="per clearfix"> <img class="fl" src="img/temp/per02.jpg"> <div class="perR fl"> <p>么***周</p> <p>花瓶超级棒,我看图以为是光面的,收货发现是磨砂,但感觉也超有质感,很喜欢。磨砂上面还有点纹路,不过觉得挺自然的,不影响美观。包装也很好,绝对不会磕碎碰坏,好评!</p> <p><span>2016年12月27日08:31</span><span>颜色分类:大中小三件套(不含花)</span></p> </div> </div> <div class="per clearfix"> <img class="fl" src="img/temp/per01.jpg"> <div class="perR fl"> <p>馨***呀</p> <p>不好意思评价晚了,产品很好,价格比玻璃品便宜,没有我担心的杂色,发货快,包装好,全5分</p> <div class="clearfix"> <p><img src="img/temp/eva01.jpg"></p> <p><img src="img/temp/eva02.jpg"></p> <p><img src="img/temp/eva03.jpg"></p> <p><img src="img/temp/eva04.jpg"></p> <p><img src="img/temp/eva05.jpg"></p> </div> <p><span>2016年12月27日08:31</span><span>颜色分类:大中小三件套(不含花)</span></p> </div> </div> <div class="per clearfix"> <img class="fl" src="img/temp/per02.jpg"> <div class="perR fl"> <p>么***周</p> <p>花瓶超级棒,我看图以为是光面的,收货发现是磨砂,但感觉也超有质感,很喜欢。磨砂上面还有点纹路,不过觉得挺自然的,不影响美观。包装也很好,绝对不会磕碎碰坏,好评!</p> <p><span>2016年12月27日08:31</span><span>颜色分类:大中小三件套(不含花)</span></p> </div> </div> </div> </div> </div> <div class="msgR fr"> <h4>为你推荐</h4> <div class="seeList"> <a href="#"> <dl> <dt><img src="img/temp/see01.jpg"></dt> <dd>【最家】复古文艺风玻璃花瓶</dd> <dd>¥193.20</dd> </dl> </a> <a href="#"> <dl> <dt><img src="img/temp/see02.jpg"></dt> <dd>【最家】复古文艺风玻璃花瓶</dd> <dd>¥193.20</dd> </dl> </a> <a href="#"> <dl> <dt><img src="img/temp/see03.jpg"></dt> <dd>【最家】复古文艺风玻璃花瓶</dd> <dd>¥193.20</dd> </dl> </a> <a href="#"> <dl> <dt><img src="img/temp/see04.jpg"></dt> <dd>【最家】复古文艺风玻璃花瓶</dd> <dd>¥193.20</dd> </dl> </a> </div> </div> </div> <div class="like"> <h4>猜你喜欢</h4> <div class="bottom"> <div class="hd"> <span class="prev"><img src="img/temp/prev.png"></span> <span class="next"><img src="img/temp/next.png"></span> </div> <div class="imgCon bd"> <div class="likeList clearfix"> <div> <a href="${ctx}/page/proDetail.html"> <dl> <dt><img src="img/temp/like01.jpg"></dt> <dd>【最家】复古文艺风玻璃花瓶</dd> <dd>¥193.20</dd> </dl> </a> <a href="${ctx}/page/proDetail.html"> <dl> <dt><img src="img/temp/like02.jpg"></dt> <dd>【最家】复古文艺风玻璃花瓶</dd> <dd>¥193.20</dd> </dl> </a> <a href="${ctx}/page/proDetail.html"> <dl> <dt><img src="img/temp/like03.jpg"></dt> <dd>【最家】复古文艺风玻璃花瓶</dd> <dd>¥193.20</dd> </dl> </a> <a href="${ctx}/page/proDetail.html"> <dl> <dt><img src="img/temp/like04.jpg"></dt> <dd>【最家】复古文艺风玻璃花瓶</dd> <dd>¥193.20</dd> </dl> </a> <a href="${ctx}/page/proDetail.html" class="last"> <dl> <dt><img src="img/temp/like05.jpg"></dt> <dd>【最家】复古文艺风玻璃花瓶</dd> <dd>¥193.20</dd> </dl> </a> </div> <div> <a href="${ctx}/page/proDetail.html"> <dl> <dt><img src="img/temp/like01.jpg"></dt> <dd>【最家】复古文艺风玻璃花瓶</dd> <dd>¥193.20</dd> </dl> </a> <a href="${ctx}/page/proDetail.html"> <dl> <dt><img src="img/temp/like02.jpg"></dt> <dd>【最家】复古文艺风玻璃花瓶</dd> <dd>¥193.20</dd> </dl> </a> <a href="${ctx}/page/proDetail.html"> <dl> <dt><img src="img/temp/like03.jpg"></dt> <dd>【最家】复古文艺风玻璃花瓶</dd> <dd>¥193.20</dd> </dl> </a> <a href="${ctx}/page/proDetail.html"> <dl> <dt><img src="img/temp/like04.jpg"></dt> <dd>【最家】复古文艺风玻璃花瓶</dd> <dd>¥193.20</dd> </dl> </a> <a href="${ctx}/page/proDetail.html" class="last"> <dl> <dt><img src="img/temp/like05.jpg"></dt> <dd>【最家】复古文艺风玻璃花瓶</dd> <dd>¥193.20</dd> </dl> </a> </div> </div> </div> </div> </div> <!--返回顶部--> <#include "common/footer.html"> <script src="js/jquery.SuperSlide.2.1.1.js" type="text/javascript" charset="utf-8"></script> <script src="js/public.js" type="text/javascript" charset="utf-8"></script> <script src="js/nav.js" type="text/javascript" charset="utf-8"></script> <script src="js/pro.js" type="text/javascript" charset="utf-8"></script> <script src="js/cart.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> jQuery(".bottom").slide({titCell:".hd ul",mainCell:".bd .likeList",autoPage:true,autoPlay:false,effect:"leftLoop",autoPlay:true,vis:1}); </script> </body> </html>
效果预览
js
/****************************proDetail 加入购物车*******************************/ $(".btns .cart").click(function(){ //获得需要添加到购物车中的商品id let gid=this.dataset.gid //获得添加的商品的数量 let num=$("div.num span.fl").text() //ajax请求 $.post('/cart/add',{gid,num},resp=>{ if(resp.code===200){ alert("添加成功") } },'json') });
service
package com.wh.easyshop.service; import com.wh.easyshop.model.User; import com.wh.easyshop.vo.CartVo; import java.util.List; public interface IRedisService { /** * 将购物车对象保存到Redis中 */ void saveCart(User user, CartVo cartVo); }
将购物车数据加到redis时,需要注意使用的类型,我把购物车的数据放到redis中的使用的是hash的数据类型,
因为购物车的数据是来自于多个用户的,这样的话肯定需要用一个标识去标识出特定的用户的数据
,其次用户的购物车数据肯定也是多条的,这样也需要用一个特殊的字段去拿到特定的购物车信息
【键:{键:值}】➡【用户标识:{商品id:商品信息}】,hash的数据类型就很符合这样的存储格式,【特别适合于存储具有多个成员的结构体或对象】
serviceimpl
package com.wh.easyshop.service; import com.wh.easyshop.model.User; import com.wh.easyshop.util.Constants; import com.wh.easyshop.vo.CartVo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.HashOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import java.util.List; import java.util.concurrent.TimeUnit; @Service public class RedisServiceImpl implements IRedisService { @Autowired private RedisTemplate<String, Object> redisTemplate; /** * 将购物车数据存入缓存中 * @param user * @param cartVo */ @Override public void saveCart(User user, CartVo cartVo) { //获取操Redi中hash类型数据对象--通过其将购物车存入到缓存中 HashOperations<String, String , CartVo> rediscart = redisTemplate.opsForHash(); //大键【hash的键】 String hashKey=Constants.REDIS_CART_PREFIX + user.getId(); //小键【hash中键值对的键】--存入的商品id String ValueKey=cartVo.getGid().toString(); //判断购物车中是否有这个【大键+小键】--去拿到键对应的购物车中的商品 Boolean hascart = rediscart.hasKey(hashKey, ValueKey); if(hascart){ //edit【购物车中有这个商品】 //拿到键对应的购物车中的商品 CartVo cart= rediscart.get(hashKey, ValueKey); cart.setNum(cart.getNum()+cartVo.getNum()); } //add【购物车中没有这个商品】 //往缓存中加入数据 rediscart.put(hashKey,ValueKey,cartVo); } }
controller
package com.wh.easyshop.controller; import com.wh.easyshop.model.Goods; import com.wh.easyshop.model.User; import com.wh.easyshop.resp.JsonResponseBody; import com.wh.easyshop.service.IGoodsService; import com.wh.easyshop.service.IRedisService; import com.wh.easyshop.util.CookieUtils; import com.wh.easyshop.vo.CartVo; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import javax.jws.WebParam; import javax.servlet.http.HttpServletRequest; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; /** * <p> * 购物车 前端控制器 * </p> * * @author wh * @since 2024-1-2 */ @Controller @RequestMapping("/cart") public class CartController { @Autowired IRedisService redisService; @Autowired IGoodsService goodsService; @RequestMapping("/add") @ResponseBody public JsonResponseBody<?> addCart(CartVo cartVo, HttpServletRequest request) { //拿到cookie中的用户令牌 String userToken = CookieUtils.getCookieValue(request, "UserToken"); //通过令牌拿到用户对象 User userByToken = redisService.getUserByToken(userToken); //将当前登录用户的购物车信息保存到redis中 redisService.saveCart(userByToken,cartVo); return JsonResponseBody.success() ; } }
把购物车的数据加到redis中时,我只加了商品的id以及商品的数量,又因为goods这个实体类中没有商品数量的这个字段,所以建了一个vo类
vo
package com.wh.easyshop.vo; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import lombok.Data; import java.io.Serializable; import java.math.BigDecimal; /** * @author是辉辉啦 * @create 2024-01-02-15:14 */ @Data public class CartVo implements Serializable { /** * 商品ID */ private Long gid; /** * 商品数量 */ private Integer num; }
如果把其他的数据字段都加入到redis中的话,会降低效率,所以后面还需要从数据库中拿一次数据显示到购物车的页面。
service
package com.wh.easyshop.service; import com.wh.easyshop.model.User; import com.wh.easyshop.vo.CartVo; import java.util.List; public interface IRedisService { /** * 根据大键【user:用户的id】拿出redis中的购物车对象 * @param user * @return */ List<CartVo> loadCart(User user); }
serviceimpl
package com.wh.easyshop.controller; import com.wh.easyshop.model.Goods; import com.wh.easyshop.model.User; import com.wh.easyshop.resp.JsonResponseBody; import com.wh.easyshop.service.IGoodsService; import com.wh.easyshop.service.IRedisService; import com.wh.easyshop.util.CookieUtils; import com.wh.easyshop.vo.CartVo; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import javax.jws.WebParam; import javax.servlet.http.HttpServletRequest; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; /** * <p> * 购物车 前端控制器 * </p> * * @author wh * @since 2024-1-2 */ @Controller @RequestMapping("/cart") public class CartController { @Autowired IRedisService redisService; @Autowired IGoodsService goodsService; @RequestMapping("/toCart") public String index (HttpServletRequest request, Model model) { //拿到cookie中的用户令牌 String userToken = CookieUtils.getCookieValue(request, "UserToken"); //通过令牌拿到用户对象 User userByToken = redisService.getUserByToken(userToken); //拿出对应用户的购物车中的所有的商品 List<CartVo> cartVos = redisService.loadCart(userByToken); //拿出所有的购物车中的商id--集合 List<Long> ids = cartVos.stream().map(CartVo::getGid).collect(Collectors.toList()); //根据这个id集合查询所有对应的商品 List<Goods> goods = goodsService.listByIds(ids); //遍历集合 赋值给对应的对象 for (Goods g : goods) { //找到对应id相同的元素 CartVo vo = cartVos.stream() .filter(v -> Objects.equals(v.getGid(), g.getGid())) .findFirst() .get(); //将商品g的属性赋值给vo【这样vo中的属性就有数据了】 BeanUtils.copyProperties(g,vo); } model.addAttribute("cart",cartVos); return "cart"; } }
controller
package com.wh.easyshop.controller; import com.wh.easyshop.model.Goods; import com.wh.easyshop.model.User; import com.wh.easyshop.resp.JsonResponseBody; import com.wh.easyshop.service.IGoodsService; import com.wh.easyshop.service.IRedisService; import com.wh.easyshop.util.CookieUtils; import com.wh.easyshop.vo.CartVo; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import javax.jws.WebParam; import javax.servlet.http.HttpServletRequest; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; /** * <p> * 购物车 前端控制器 * </p> * * @author wh * @since 2024-1-2 */ @Controller @RequestMapping("/cart") public class CartController { @Autowired IRedisService redisService; @Autowired IGoodsService goodsService; @RequestMapping("/toCart") public String index (HttpServletRequest request, Model model) { //拿到cookie中的用户令牌 String userToken = CookieUtils.getCookieValue(request, "UserToken"); //通过令牌拿到用户对象 User userByToken = redisService.getUserByToken(userToken); //拿出对应用户的购物车中的所有的商品 List<CartVo> cartVos = redisService.loadCart(userByToken); //拿出所有的购物车中的商id--集合 List<Long> ids = cartVos.stream().map(CartVo::getGid).collect(Collectors.toList()); //根据这个id集合查询所有对应的商品 List<Goods> goods = goodsService.listByIds(ids); //遍历集合 赋值给对应的对象 for (Goods g : goods) { //找到对应id相同的元素 CartVo vo = cartVos.stream() .filter(v -> Objects.equals(v.getGid(), g.getGid())) .findFirst() .get(); //将商品g的属性赋值给vo【这样vo中的属性就有数据了】 BeanUtils.copyProperties(g,vo); } model.addAttribute("cart",cartVos); return "cart"; } }
在购物车页面显示相应数据
这里还需要在首页的地方将跳转购物车页面的路径连接写好
<a href="${ctx}/cart/toCart"><img src="img/gwc.png"/></a>
<script type="text/javascript" src="js/jquery-1.12.4.min.js"></script> <script> $(function(){ let nickname=getCookie("nickname"); if(null!=nickname&&''!=nickname&&undefined!=nickname) { //设置昵称 $('#nickname').text("您好,"+nickname); //隐藏登录注册按钮 $('p.fl>span:eq(1)').css("display","none"); //显示昵称和退出按钮 $('p.fl>span:eq(0)').css("display","block"); }else{ //隐藏昵称 $('#nickname').text(""); //显示登录注册按钮 $('p.fl>span:eq(1)').css("display","block"); //隐藏昵称和退出按钮 $('p.fl>span:eq(0)').css("display","none"); } }); function getCookie(cname) { var name = cname + "="; var decodedCookie = decodeURIComponent(document.cookie); var ca = decodedCookie.split(';'); for(var i = 0; i <ca.length; i++) { var c = ca[i]; while (c.charAt(0) == ' ') { c = c.substring(1); } if (c.indexOf(name) == 0) { return c.substring(name.length, c.length); } } return ""; } </script> <div class="head"> <div class="wrapper clearfix"> <div class="clearfix" id="top"> <h1 class="fl"><a href="${ctx}/"><img src="img/logo.png"/></a></h1> <div class="fr clearfix" id="top1"> <p class="fl"> <span> <span id="nickname"></span> <a href="${ctx}/user/userLogout">退出</a> </span> <span style="display: none"> <a href="${ctx}/page/login.html" id="login">登录</a> <a href="${ctx}/page/reg.html" id="reg">注册</a> </span> </p> <form action="#" method="get" class="fl"> <input type="text" placeholder="热门搜索:干花花瓶" /> <input type="button" /> </form> <div class="btn fl clearfix"> <a href="${ctx}/page/mygxin.html"><img src="img/grzx.png"/></a> <a href="#" class="er1"><img src="img/ewm.png"/></a> <a href="${ctx}/cart/toCart"><img src="img/gwc.png"/></a> <p><a href="#"><img src="img/smewm.png"/></a></p> </div> </div> </div> <ul class="clearfix" id="bott"> <li><a href="${ctx}/">首页</a></li> <li> <a href="#">所有商品</a> <div class="sList"> <div class="wrapper clearfix"> <a href="${ctx}/page/paint.html"> <dl> <dt><img src="img/nav1.jpg"/></dt> <dd>浓情欧式</dd> </dl> </a> <a href="${ctx}/page/paint.html"> <dl> <dt><img src="img/nav2.jpg"/></dt> <dd>浪漫美式</dd> </dl> </a> <a href="${ctx}/page/paint.html"> <dl> <dt><img src="img/nav3.jpg"/></dt> <dd>雅致中式</dd> </dl> </a> <a href="${ctx}/page/paint.html"> <dl> <dt><img src="img/nav6.jpg"/></dt> <dd>简约现代</dd> </dl> </a> <a href="${ctx}/page/paint.html"> <dl> <dt><img src="img/nav7.jpg"/></dt> <dd>创意装饰</dd> </dl> </a> </div> </div> </li> <li> <a href="${ctx}/page/flowerDer.html">装饰摆件</a> <div class="sList2"> <div class="clearfix"> <a href="${ctx}/page/proList.html">干花花艺</a> <a href="${ctx}/page/vase_proList.html">花瓶花器</a> </div> </div> </li> <li> <a href="${ctx}/page/decoration.html">布艺软饰</a> <div class="sList2"> <div class="clearfix"> <a href="${ctx}/page/zbproList.html">桌布罩件</a> <a href="${ctx}/page/bzproList.html">抱枕靠垫</a> </div> </div> </li> <li><a href="${ctx}/page/paint.html">墙式壁挂</a></li> <li><a href="${ctx}/page/perfume.html">蜡艺香薰</a></li> <li><a href="${ctx}/page/idea.html">创意家居</a></li> </ul> </div> </div>
然后就是显示购物车的内容
<!DOCTYPE html> <html> <head lang="en"> <#include "common/head.html"> <link rel="stylesheet" type="text/css" href="css/public.css"/> <link rel="stylesheet" type="text/css" href="css/proList.css" /> </head> <body> <!--------------------------------------cart---------------------> <div class="head ding"> <div class="wrapper clearfix"> <div class="clearfix" id="top"> <h1 class="fl"><a href="${ctx}/"><img src="img/logo.png"/></a></h1> <div class="fr clearfix" id="top1"> <form action="#" method="get" class="fl"> <input type="text" placeholder="搜索" /> <input type="button" /> </form> </div> </div> </div> </div> <div class="cart mt"> <!-----------------logo-------------------> <!--<div class="logo"> <h1 class="wrapper clearfix"> <a href="${ctx}/"><img class="fl" src="img/temp/logo.png"></a> <img class="top" src="img/temp/cartTop01.png"> </h1> </div>--> <!-----------------site-------------------> <div class="site"> <p class=" wrapper clearfix"> <span class="fl">购物车</span> <img class="top" src="img/temp/cartTop01.png"> <a href="${ctx}/" class="fr">继续购物></a> </p> </div> <!-----------------table-------------------> <div class="table wrapper"> <div class="tr"> <div>商品</div> <div>单价</div> <div>数量</div> <div>小计</div> <div>操作</div> </div> <#list cart as c > <div class="th"> <div class="pro clearfix"> <label class="fl"> <input type="checkbox"/> <span></span> </label> <a class="fl" href="#"> <dl class="clearfix"> <dt class="fl"><img src="${(c.goodsImg)!}" style="width: 120px; height: 120px;"></dt> <dd class="fl"> <p >${(c.goodsTitle)!}</p> </dd> </dl> </a> </div> <div class="price" style="margin-left:100px;">${(c.goodsPrice)!}</div> <div class="number"> <p class="num clearfix"> <img class="fl sub" src="img/temp/sub.jpg"> <span class="fl">1</span> <img class="fl add" src="img/temp/add.jpg"> </p> </div> <div class="price sAll" style="margin-left:80px;">¥20.00</div> <div class="price"><a class="del" href="#2" style="margin-left:40px;">删除</a></div> </div> </#list> <div class="goOn">空空如也~<a href="${ctx}/">去逛逛</a></div> <div class="tr clearfix"> <label class="fl"> <input class="checkAll" type="checkbox"/> <span></span> </label> <p class="fl"> <a href="javascript:void(0);">全选</a> <a href="javascript:void(0);" class="del">删除</a> </p> <p class="fr"> <span>共<small id="sl">0</small>件商品</span> <span>合计: <small id="all">¥0.00</small></span> <a class="count">结算</a> </p> </div> </div> </div> <div class="mask"></div> <div class="tipDel"> <p>确定要删除该商品吗?</p> <p class="clearfix"> <a class="fl cer" href="javascript:void(0);">确定</a> <a class="fr cancel" href="javascript:void(0);">取消</a> </p> </div> <!--返回顶部--> <#include "common/footer.html"> <!----------------mask-------------------> <div class="mask"></div> <!-------------------mask内容-------------------> <div class="proDets"> <img class="off" src="img/temp/off.jpg" /> <div class="proCon clearfix"> <div class="proImg fr"> <img class="list" src="img/temp/proDet.jpg" /> <div class="smallImg clearfix"> <img src="img/temp/proDet01.jpg" data-src="img/temp/proDet01_big.jpg"> <img src="img/temp/proDet02.jpg" data-src="img/temp/proDet02_big.jpg"> <img src="img/temp/proDet03.jpg" data-src="img/temp/proDet03_big.jpg"> <img src="img/temp/proDet04.jpg" data-src="img/temp/proDet04_big.jpg"> </div> </div> <div class="fl"> <div class="proIntro change"> <p>颜色分类</p> <div class="smallImg clearfix"> <p class="fl on"><img src="img/temp/prosmall01.jpg" alt="白瓷花瓶+20支快乐花" data-src="img/temp/proBig01.jpg"></p> <p class="fl"><img src="img/temp/prosmall02.jpg" alt="白瓷花瓶+20支兔尾巴草" data-src="img/temp/proBig02.jpg"></p> <p class="fl"><img src="img/temp/prosmall03.jpg" alt="20支快乐花" data-src="img/temp/proBig03.jpg"></p> <p class="fl"><img src="img/temp/prosmall04.jpg" alt="20支兔尾巴草" data-src="img/temp/proBig04.jpg"></p> </div> </div> <div class="changeBtn clearfix"> <a href="#2" class="fl"><p class="buy">确认</p></a> <a href="#2" class="fr"><p class="cart">取消</p></a> </div> </div> </div> </div> <div class="pleaseC"> <p>请选择宝贝</p> <img class="off" src="img/temp/off.jpg" /> </div> <script src="js/public.js" type="text/javascript" charset="utf-8"></script> <script src="js/pro.js" type="text/javascript" charset="utf-8"></script> <script src="js/cart.js" type="text/javascript" charset="utf-8"></script> </body> </html>
3.参数解析器
先说明一下参数解析器说发挥的作用
参数解析器:它的主要作用是处理请求中的参数,无论是获取Cookie中的值,Header中的值,JSON参数解析器的主要作用是处理请求中的参数,无论是获取Cookie中的值,Header中的值,JSON格式的数据,URI中的值,还是请求体中的数据,都可以通过相应的参数解析器来提取。
其中我们的代码中,是将用户的数据放到了redis以及cookie中,用户的数据又需要再很多的地方获取到【购物车,订单...】,所以我们可以专门写一个user的参数解析器
package com.wh.easyshop.util; import com.wh.easyshop.model.User; import com.wh.easyshop.service.IRedisService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.MethodParameter; import org.springframework.stereotype.Component; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; import javax.servlet.http.HttpServletRequest; /** * 用户参数解析器,用于从请求中获取User对象 */ @Component public class UserArgumentResolver implements HandlerMethodArgumentResolver { @Autowired private IRedisService redisService; /** * 判断是否支持该类型的参数解析 * @param parameter 方法参数 * @return 如果支持返回true,否则返回false */ @Override public boolean supportsParameter(MethodParameter parameter) { return parameter.getParameterType() == User.class; } /** * 解析请求中的参数,并返回User对象 * @param parameter 方法参数 * @param mavContainer ModelAndView容器 * @param webRequest NativeWebRequest对象 * @param binderFactory WebDataBinderFactory对象 * @return User对象 * @throws Exception 解析过程中可能出现的异常 */ @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest(); // 从cookie中获得userToken String token = CookieUtils.getCookieValue(request, "UserToken"); // 使用Redis服务加载User对象 User user = redisService.getUserByToken(token); return user; } }
为了让参数解析器生效,我们还需要一个配置类
package com.wh.easyshop.util; import com.wh.easyshop.util.UserArgumentResolver; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import java.util.List; /** * WebConfig类,用于配置Spring MVC的参数解析器 */ @Component public class WebConfig implements WebMvcConfigurer { // 注入UserArgumentResolver实例 @Autowired private UserArgumentResolver userArgumentResolver; /** * 重写addArgumentResolvers方法,将UserArgumentResolver添加到参数解析器列表中 * @param resolvers 参数解析器列表 */ @Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) { resolvers.add(userArgumentResolver); } }
现在就可以使用参数解析器了
package com.wh.easyshop.controller; import com.wh.easyshop.model.Goods; import com.wh.easyshop.model.User; import com.wh.easyshop.resp.JsonResponseBody; import com.wh.easyshop.service.IGoodsService; import com.wh.easyshop.service.IRedisService; import com.wh.easyshop.util.CookieUtils; import com.wh.easyshop.vo.CartVo; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import javax.jws.WebParam; import javax.servlet.http.HttpServletRequest; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; /** * <p> * 购物车 前端控制器 * </p> * * @author wh * @since 2024-1-2 */ @Controller @RequestMapping("/cart") public class CartController { @Autowired IRedisService redisService; @Autowired IGoodsService goodsService; @RequestMapping("/toCart") public String index (User user,HttpServletRequest request, Model model) { List<CartVo> cartVos = redisService.loadCart(user);//使用参数解析器 //拿出所有的购物车中的商id--集合 List<Long> ids = cartVos.stream().map(CartVo::getGid).collect(Collectors.toList()); //根据这个id集合查询所有对应的商品 List<Goods> goods = goodsService.listByIds(ids); //遍历集合 赋值给对应的对象 for (Goods g : goods) { //找到对应id相同的元素 CartVo vo = cartVos.stream() .filter(v -> Objects.equals(v.getGid(), g.getGid())) .findFirst() .get(); //将商品g的属性赋值给vo【这样vo中的属性就有数据了】 BeanUtils.copyProperties(g,vo); } model.addAttribute("cart",cartVos); return "cart"; } }
4.购物车的前端优化
其中的小计和总计都还没有计算显示出来以及商品数量的加减,这里就把它优化一下
html
<!DOCTYPE html> <html> <head lang="en"> <#include "common/head.html"> <link rel="stylesheet" type="text/css" href="css/public.css"/> <link rel="stylesheet" type="text/css" href="css/proList.css" /> </head> <body> <!--------------------------------------cart---------------------> <div class="head ding"> <div class="wrapper clearfix"> <div class="clearfix" id="top"> <h1 class="fl"><a href="${ctx}/"><img src="img/logo.png"/></a></h1> <div class="fr clearfix" id="top1"> <form action="#" method="get" class="fl"> <input type="text" placeholder="搜索" /> <input type="button" /> </form> </div> </div> </div> </div> <div class="cart mt"> <!-----------------logo-------------------> <!--<div class="logo"> <h1 class="wrapper clearfix"> <a href="${ctx}/"><img class="fl" src="img/temp/logo.png"></a> <img class="top" src="img/temp/cartTop01.png"> </h1> </div>--> <!-----------------site-------------------> <div class="site"> <p class=" wrapper clearfix"> <span class="fl">购物车</span> <img class="top" src="img/temp/cartTop01.png"> <a href="${ctx}/" class="fr">继续购物></a> </p> </div> <!-----------------table-------------------> <div class="table wrapper"> <div class="tr"> <div>商品</div> <div>单价</div> <div>数量</div> <div>小计</div> <div>操作</div> </div> <#list cart as c > <div class="th"> <div class="pro clearfix"> <label class="fl"> <input type="checkbox" class="cartCheckBox"/> <span></span> </label> <a class="fl" href="#"> <dl class="clearfix"> <dt class="fl"><img src="${(c.goodsImg)!}" style="width: 100px; height: 100px;"></dt> <dd class="fl"> <p >${(c.goodsTitle)!}</p> </dd> </dl> </a> </div> <div class="price myprice" style="">${(c.goodsPrice)!}</div> <div class="number"> <p class="num clearfix"> <img class="fl sub" src="img/temp/sub.jpg"> <span class="fl mynum" data-gid="${c.gid}">${c.num}</span> <img class="fl add" src="img/temp/add.jpg"> </p> </div> <div class="price sAll" >¥${(c.xj())!}</div> <div class="price"><a class="del" data-gid="${c.gid}" >删除</a></div> </div> </#list> <div class="goOn">空空如也~<a href="${ctx}/">去逛逛</a></div> <div class="tr clearfix"> <label class="fl"> <input class="checkAll" type="checkbox"/> <span></span> </label> <p class="fl"> <a href="javascript:void(0);">全选</a> <a class="del" >删除</a> </p> <p class="fr"> <span>共<small id="sl">0</small>件商品</span> <span>合计: <small id="all">¥0.00</small></span> <a class="count">结算</a> </p> </div> </div> </div> <div class="mask"></div> <div class="tipDel"> <p>确定要删除该商品吗?</p> <p class="clearfix"> <a class="fl cer" href="javascript:void(0);">确定</a> <a class="fr cancel" href="javascript:void(0);">取消</a> </p> </div> <!--返回顶部--> <#include "common/footer.html"> <!----------------mask-------------------> <div class="mask"></div> <!-------------------mask内容-------------------> <div class="proDets"> <img class="off" src="img/temp/off.jpg" /> <div class="proCon clearfix"> <div class="proImg fr"> <img class="list" src="img/temp/proDet.jpg" /> <div class="smallImg clearfix"> <img src="img/temp/proDet01.jpg" data-src="img/temp/proDet01_big.jpg"> <img src="img/temp/proDet02.jpg" data-src="img/temp/proDet02_big.jpg"> <img src="img/temp/proDet03.jpg" data-src="img/temp/proDet03_big.jpg"> <img src="img/temp/proDet04.jpg" data-src="img/temp/proDet04_big.jpg"> </div> </div> <div class="fl"> <div class="proIntro change"> <p>颜色分类</p> <div class="smallImg clearfix"> <p class="fl on"><img src="img/temp/prosmall01.jpg" alt="白瓷花瓶+20支快乐花" data-src="img/temp/proBig01.jpg"></p> <p class="fl"><img src="img/temp/prosmall02.jpg" alt="白瓷花瓶+20支兔尾巴草" data-src="img/temp/proBig02.jpg"></p> <p class="fl"><img src="img/temp/prosmall03.jpg" alt="20支快乐花" data-src="img/temp/proBig03.jpg"></p> <p class="fl"><img src="img/temp/prosmall04.jpg" alt="20支兔尾巴草" data-src="img/temp/proBig04.jpg"></p> </div> </div> <div class="changeBtn clearfix"> <a href="#2" class="fl"><p class="buy">确认</p></a> <a href="#2" class="fr"><p class="cart">取消</p></a> </div> </div> </div> </div> <div class="pleaseC"> <p>请选择宝贝</p> <img class="off" src="img/temp/off.jpg" /> </div> <script src="js/public.js" type="text/javascript" charset="utf-8"></script> <script src="js/pro.js" type="text/javascript" charset="utf-8"></script> <script src="js/cart.js" type="text/javascript" charset="utf-8"></script> </body> </html>
js
$(function(){ //计算总价&计算小计 function jisuan(){ //小计和总价他们都放在一个大的盒子(th) //所以去拿到这个大和盒子,遍历得到每个的价格和数量=>小计和总价 let total=0;//计算总价 $(".th").each((i,el)=>{ //获得价格 let price=$(el).find(".myprice").text().replace("¥","") //获得价格 let count=$(el).find(".mynum").text()*1 //计算小计 $(el).find(".sAll").text("¥"+price*count) //子复选框--选中状态 let flag =$(el).find('input[type=checkbox]').prop('checked') //如果元素被选择了 if(flag){ total+=price*count } }) //把计算出来的总价给到相应的元素 $("#all").text("¥"+total); } /**************数量加减***************/ $(".num .sub").click(function () { update(this,-1) }); $(".num .add").click(function () { update(this,1) }); /** * 更新购物车中指定商品的数量 * @param ele * @param count */ function update(ele,count){ //存放数量的元素【mynum是自己定义的】 let el = $(ele).parent().find(".mynum") //拿到原来的内容 let num = el.text()*1 //加上变化的数量 num+=count; //判断是否是正确数量 if(num<=0) return //将数量改变到页面上去 el.text(num) //获得数量 let gid = el.attr('data-gid') //访问后端 $.post('/cart/edit',{gid,num},resp=>{ if(resp.code===200) jisuan() }) } })
更新数量的control后端处理
service
package com.wh.easyshop.service; import com.wh.easyshop.model.User; import com.wh.easyshop.vo.CartVo; import java.util.List; public interface IRedisService { /** * 修改购物车 * @param user * @param cartVo */ void editCart(CartVo cartVo,User user); void delCart(List<String> ids, User user); }
package com.wh.easyshop.service; import com.wh.easyshop.model.User; import com.wh.easyshop.util.Constants; import com.wh.easyshop.vo.CartVo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.HashOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import java.util.List; import java.util.concurrent.TimeUnit; @Service public class RedisServiceImpl implements IRedisService { @Autowired private RedisTemplate<String, Object> redisTemplate; @Override public void editCart(CartVo cartVo,User user) { //获取操Redi中hash类型数据对象--通过其将购物车存入到缓存中 HashOperations<String, String , CartVo> rediscart = redisTemplate.opsForHash(); //大键【hash的键】 String hashKey=Constants.REDIS_CART_PREFIX + user.getId(); //小键【hash中键值对的键】--存入的商品id String ValueKey=cartVo.getGid().toString(); //往缓存中加入数据(覆盖原来的数据) rediscart.put(hashKey,ValueKey,cartVo); } }
controller
package com.wh.easyshop.controller; import com.wh.easyshop.model.Goods; import com.wh.easyshop.model.User; import com.wh.easyshop.resp.JsonResponseBody; import com.wh.easyshop.service.IGoodsService; import com.wh.easyshop.service.IRedisService; import com.wh.easyshop.util.CookieUtils; import com.wh.easyshop.vo.CartVo; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; import javax.jws.WebParam; import javax.servlet.http.HttpServletRequest; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; /** * <p> * 购物车 前端控制器 * </p> * * @author wh * @since 2024-1-2 */ @Controller @RequestMapping("/cart") public class CartController { @Autowired IRedisService redisService; @Autowired IGoodsService goodsService; @RequestMapping("/edit") @ResponseBody public JsonResponseBody<?> editCart(CartVo cartVo,User user) { //修改用户的购物车信息 redisService.editCart(cartVo,user); return JsonResponseBody.success() ; } }
以上代码中都写有注释,而且逻辑也比较清晰,在这里就没有做过多的解析了
5.购物车的删除【单个&批量】
html
<!DOCTYPE html> <html> <head lang="en"> <#include "common/head.html"> <link rel="stylesheet" type="text/css" href="css/public.css"/> <link rel="stylesheet" type="text/css" href="css/proList.css" /> </head> <body> <!--------------------------------------cart---------------------> <div class="head ding"> <div class="wrapper clearfix"> <div class="clearfix" id="top"> <h1 class="fl"><a href="${ctx}/"><img src="img/logo.png"/></a></h1> <div class="fr clearfix" id="top1"> <form action="#" method="get" class="fl"> <input type="text" placeholder="搜索" /> <input type="button" /> </form> </div> </div> </div> </div> <div class="cart mt"> <!-----------------logo-------------------> <!--<div class="logo"> <h1 class="wrapper clearfix"> <a href="${ctx}/"><img class="fl" src="img/temp/logo.png"></a> <img class="top" src="img/temp/cartTop01.png"> </h1> </div>--> <!-----------------site-------------------> <div class="site"> <p class=" wrapper clearfix"> <span class="fl">购物车</span> <img class="top" src="img/temp/cartTop01.png"> <a href="${ctx}/" class="fr">继续购物></a> </p> </div> <!-----------------table-------------------> <div class="table wrapper"> <div class="tr"> <div>商品</div> <div>单价</div> <div>数量</div> <div>小计</div> <div>操作</div> </div> <#list cart as c > <div class="th"> <div class="pro clearfix"> <label class="fl"> <input type="checkbox" class="cartCheckBox"/> <span></span> </label> <a class="fl" href="#"> <dl class="clearfix"> <dt class="fl"><img src="${(c.goodsImg)!}" style="width: 100px; height: 100px;"></dt> <dd class="fl"> <p >${(c.goodsTitle)!}</p> </dd> </dl> </a> </div> <div class="price myprice" style="">${(c.goodsPrice)!}</div> <div class="number"> <p class="num clearfix"> <img class="fl sub" src="img/temp/sub.jpg"> <span class="fl mynum" data-gid="${c.gid}">${c.num}</span> <img class="fl add" src="img/temp/add.jpg"> </p> </div> <div class="price sAll" >¥${(c.xj())!}</div> <div class="price"><a class="del" data-gid="${c.gid}" >删除</a></div> </div> </#list> <div class="goOn">空空如也~<a href="${ctx}/">去逛逛</a></div> <div class="tr clearfix"> <label class="fl"> <input class="checkAll" type="checkbox"/> <span></span> </label> <p class="fl"> <a href="javascript:void(0);">全选</a> <a class="del" >删除</a> </p> <p class="fr"> <span>共<small id="sl">0</small>件商品</span> <span>合计: <small id="all">¥0.00</small></span> <a class="count">结算</a> </p> </div> </div> </div> <div class="mask"></div> <div class="tipDel"> <p>确定要删除该商品吗?</p> <p class="clearfix"> <a class="fl cer" href="javascript:void(0);">确定</a> <a class="fr cancel" href="javascript:void(0);">取消</a> </p> </div> <!--返回顶部--> <#include "common/footer.html"> <!----------------mask-------------------> <div class="mask"></div> <!-------------------mask内容-------------------> <div class="proDets"> <img class="off" src="img/temp/off.jpg" /> <div class="proCon clearfix"> <div class="proImg fr"> <img class="list" src="img/temp/proDet.jpg" /> <div class="smallImg clearfix"> <img src="img/temp/proDet01.jpg" data-src="img/temp/proDet01_big.jpg"> <img src="img/temp/proDet02.jpg" data-src="img/temp/proDet02_big.jpg"> <img src="img/temp/proDet03.jpg" data-src="img/temp/proDet03_big.jpg"> <img src="img/temp/proDet04.jpg" data-src="img/temp/proDet04_big.jpg"> </div> </div> <div class="fl"> <div class="proIntro change"> <p>颜色分类</p> <div class="smallImg clearfix"> <p class="fl on"><img src="img/temp/prosmall01.jpg" alt="白瓷花瓶+20支快乐花" data-src="img/temp/proBig01.jpg"></p> <p class="fl"><img src="img/temp/prosmall02.jpg" alt="白瓷花瓶+20支兔尾巴草" data-src="img/temp/proBig02.jpg"></p> <p class="fl"><img src="img/temp/prosmall03.jpg" alt="20支快乐花" data-src="img/temp/proBig03.jpg"></p> <p class="fl"><img src="img/temp/prosmall04.jpg" alt="20支兔尾巴草" data-src="img/temp/proBig04.jpg"></p> </div> </div> <div class="changeBtn clearfix"> <a href="#2" class="fl"><p class="buy">确认</p></a> <a href="#2" class="fr"><p class="cart">取消</p></a> </div> </div> </div> </div> <div class="pleaseC"> <p>请选择宝贝</p> <img class="off" src="img/temp/off.jpg" /> </div> <script src="js/public.js" type="text/javascript" charset="utf-8"></script> <script src="js/pro.js" type="text/javascript" charset="utf-8"></script> <script src="js/cart.js" type="text/javascript" charset="utf-8"></script> </body> </html>
js
$(function(){ /*****************商品全选--全选框***********************/ $(".checkAll").on('click',function(){ //当全选框被选中时,其他的子复选框也要被选中 $(".cartCheckBox").prop("checked", $(".checkAll").prop("checked")); jisuan() }); /*****************商品全选--子复选框***********************/ //给子复选框添加点击事件 $(".cartCheckBox").on('click',function() { // console.log($(".other").length) //如果被选中的子复选框的长度等于所有的已有的子复选框的长度 if ($(".cartCheckBox:checked").length === $(".cartCheckBox").length) { //就把全选框设置为选中状态 $(".checkAll").prop("checked", true); } else { //否则就把全选框设置为未选中状态 $(".checkAll").prop("checked", false); } jisuan() }) //删除购物车商品 $('.del').click(function() { //获取id let gid=$(this).attr('data-gid') //用一个容器来存放所有的商品id let ids=[] //如果带了id过来就是删除单个,没有带的话就是批量删除 if(gid){ ids.push(gid) }else{ $(".th").each((i,el)=>{ //子复选框--选中状态 let flag =$(el).find('input[type=checkbox]').prop('checked') //如果元素被选择了 if(flag){ let id=$(el).find('.mynum').attr('data-gid') ids.push(id) } }) } if(ids.length>0){ $.post('/cart/del',{ids},resp=>{ if(resp.code===200){ alert("删除成功") } },'json') } }) })
service
package com.wh.easyshop.service; import com.wh.easyshop.model.User; import com.wh.easyshop.vo.CartVo; import java.util.List; public interface IRedisService { void delCart(List<String> ids, User user); }
package com.wh.easyshop.service; import com.wh.easyshop.model.User; import com.wh.easyshop.util.Constants; import com.wh.easyshop.vo.CartVo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.HashOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import java.util.List; import java.util.concurrent.TimeUnit; @Service public class RedisServiceImpl implements IRedisService { @Autowired private RedisTemplate<String, Object> redisTemplate; /** * 删除购物车 * @param ids * @param user */ @Override public void delCart(List<String> ids, User user) { //获取操Redi中hash类型数据对象--通过其将购物车存入到缓存中 HashOperations<String, String , CartVo> rediscart = redisTemplate.opsForHash(); //大键【hash的键】 String hashKey=Constants.REDIS_CART_PREFIX + user.getId(); for (String id : ids) { rediscart.delete(hashKey,id); } } }
controller
package com.wh.easyshop.controller; import com.wh.easyshop.model.Goods; import com.wh.easyshop.model.User; import com.wh.easyshop.resp.JsonResponseBody; import com.wh.easyshop.service.IGoodsService; import com.wh.easyshop.service.IRedisService; import com.wh.easyshop.util.CookieUtils; import com.wh.easyshop.vo.CartVo; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; import javax.jws.WebParam; import javax.servlet.http.HttpServletRequest; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; /** * <p> * 购物车 前端控制器 * </p> * * @author wh * @since 2024-1-2 */ @Controller @RequestMapping("/cart") public class CartController { @Autowired IRedisService redisService; @Autowired IGoodsService goodsService; @RequestMapping("/del") @ResponseBody public JsonResponseBody<?> delCart(@RequestParam("ids[]") List<String> ids, User user) { //删除用户的购物车信息 redisService.delCart(ids,user); return JsonResponseBody.success() ; } }
其中删除的时候,从前端带过来的数据格式是作用的👇
但是后端接受的数据是这样的,数据之间不一致,就会出现错误
为了成功的接受的前端的数据,我们可以使用一个注解
@RequestParam("ids[]")
@RequestParam("ids[]"):
是一个Java注解,用于将请求参数绑定到方法的参数上。在这个例子中,它指示控制器方法需要从请求参数中获取名为"ids[]"的值并将其绑定到一个List类型的方法参数上。
具体来说,当你在URL或表单中发送一个请求时,你可以通过在请求中包含名为"ids[]"的参数来传递一个ID列表。@RequestParam("ids[]")告诉Spring框架去查找这个参数,并将其值绑定到方法参数上。这样,在方法内部,你就可以像使用普通的Java List一样使用这个参数了。
需要注意的是,注解中的参数名称应该和请求参数的名称一致,这样Spring才能正确地将参数值绑定到方法参数上
好啦,今天的分享就到这了,希望能够帮到你呢!😊😊