点击指定的商品去到指定的商品详情页面:点击加入购物车,会将指定的商品加入该账号的购物车中去:
前台传递商品id
后台接收:
GoodsController?
package com.lya.lyaspshop.controller; import com.lya.lyaspshop.pojo.Goods; import com.lya.lyaspshop.service.impl.GoodsServiceImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; /** * <p> * 商品信息表 前端控制器 * </p> * * @author lya * @since 2023-12-27 */ @RequestMapping("/goods") @Controller//跳页面 //@RestController//显示数据 public class GoodsController { @Autowired private GoodsServiceImpl goodsService; // 首页商品 @RequestMapping("/list") public Object index() { List<Goods> g1 = goodsService.list(); return g1; } @RequestMapping("/query") public String query(Goods goods , Model model) { // 前台带一个gid,我们来查询 Goods g = goodsService.getById(goods.getGid()); model.addAttribute("g",g); return "proDetail" ; } }
购物车界面:
<!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">【最家】非洲菊仿真花干花</a> </div> </div> <!-----------------------Detail------------------------------> <div class="detCon"> <div class="proDet wrapper"> <div class="proCon clearfix"> <div class="proImg fl"> <img class="det" src="${(g.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>${(g.goodsName)!}</h4> <p>${(g.goodsDetail)!}</p> <span>¥${(g.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>${(g.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="${(g.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>
将商品id绑定到data-gid属性上面去:
为其设置点击事件:
/****************************proDetail 加入购物车*******************************/ $(".btns .cart").click(function(){ //获得添加购物车的id data-gid let gid = this.dataset.gid //发送添加购物车的数量 let num = $("div.num span.fl").text() //发起请求添加购物车 $.post('/cart/add',{gid,num},resp=>{ if(resp.code===200){ alert('增加成功') } },"json") });
这里我们后端要思考:购物车的东西我们因该放在哪里?
Redis 可以作为缓存系统,将热点数据存储在内存中,提高读写性能和响应速度,减少对后端数据存储的压力。
@RequestMapping("/add") @ResponseBody public JsonResponseBody<?> add(User user, CartItemVo vo, HttpServletRequest request){ String token = CookieUtils.getCookieValue(request, "userToken"); User userByToken = redisService.getUserByToken(token); //将购物车的商品放到缓存数据库中 redisService.saveCart(userByToken,vo); return JsonResponseBody.success(); }
这里写一个类用来放入保存到redis中去:
注意这个类要实现实现Serializable接口!!!
在Java中,实现Serializable接口是为了让一个对象可以被序列化和反序列化。序列化指的是将对象转换成字节流,以便可以在网络上传输或者保存在磁盘上;而反序列化则是将字节流重新转换成对象。
CartItemVo:
package com.lya.lyaspshop.vo; import lombok.Data; import java.io.Serializable; import java.math.BigDecimal; @Data public class CartItemVo implements Serializable { private Long gid; private String goodsName; private String goodsTitle; private BigDecimal goodsPrice; private String goodsImg; private String goodsType; private Integer num; public BigDecimal cartprice(){ return goodsPrice.multiply(BigDecimal.valueOf(num.doubleValue())); } }
redis中有许多的数据结构:
- String(字符串类型)常见使用场景是:存储 Session 信息、存储缓存信息(如详情页的缓存)、存储整数信息,可使用 incr 实现整数+1,和使用 decr 实现整数 -1;
- List(列表类型)常见使用场景是:实现简单的消息队列、存储某项列表数据;
- Hash(哈希表类型)常见使用场景是:存储 Session 信息、存储商品的购物车,购物车非常适合用哈希字典表示,使用人员唯一编号作为字典的 key,value 值可以存储商品的 id 和数量等信息、存储详情页信息;
- Set(集合类型)是一个无序并唯一的键值集合,它的常见使用场景是:关注功能,比如关注我的人和我关注的人,使用集合存储,可以保证人员不会重复;
- Sorted Set(有序集合类型)相比于 Set 集合类型多了一个排序属性 score(分值),它的常见使用场景是:可以用来存储排名信息、关注列表功能,这样就可以根据关注实现排序展示了。
@Override public void saveCart(User user, CartItemVo vo) { HashOperations<String,String, CartItemVo> operations = redisTemplate.opsForHash(); //获取购物车key String bigKey=REDIS_CART_PREFIX + user.getId(); //获取购物车中商品的gid String hashKey=vo.getGid().toString(); //判断购物车中是否有该商品 Boolean has = operations.hasKey(bigKey, hashKey); //如果有数据 修改购物车 if(has){ CartItemVo item = operations.get(bigKey, hashKey); vo.setNum(item.getNum()+vo.getNum()); } //添加缓存 operations.put(bigKey, hashKey,vo); }
展示购物车:
@Override public List<CartItemVo> loadCart(User user) { //获取购物车中的商品 HashOperations<String,String,CartItemVo> operations = redisTemplate.opsForHash(); String bigKey=REDIS_CART_PREFIX + user.getId(); //根据用户id获取购物车数据 List<CartItemVo> values = operations.values(bigKey); return values; }
前台:
后台:
清除缓存就行:
@RequestMapping("/userLogout") // 指定返回JsonResponseBody格式 public String logout (UserVo userVo , HttpServletRequest request, HttpServletResponse response){ String userToken = CookieUtils.getCookieValue(request, "userToken"); redisService.removeUser(userToken); CookieUtils.deleteCookie(request,response,"userToken"); CookieUtils.deleteCookie(request,response,"nickname"); return "redirect:/"; }
调用redis内置方法delete
HandlerMethodArgumentResolver
是Spring框架中的一个接口,用于解析处理方法参数。在Spring MVC中,当一个请求到达时,框架会尝试将请求参数映射到控制器方法的参数上。
HandlerMethodArgumentResolver
接口定义了一组方法,用于解析不同类型的方法参数。实现
HandlerMethodArgumentResolver
接口的类可以自定义参数解析逻辑,并告诉Spring如何将请求参数转换为方法参数。通常,开发人员可以根据业务需求实现自己的
HandlerMethodArgumentResolver
,以支持自定义的请求参数类型或者处理逻辑。使用
HandlerMethodArgumentResolver
可以帮助简化控制器方法的参数处理过程,提高代码的可读性和可维护性。
当您使用参数解析器时,您可以将输入的参数进行解析,并根据解析后的结果执行相应的操作。参数解析器可以帮助您从输入中提取所需的信息,并对其进行处理。
package com.lya.lyaspshop.core; import com.lya.lyaspshop.exception.BusinessException; import com.lya.lyaspshop.pojo.User; import com.lya.lyaspshop.service.impl.RedisServiceImpl; import com.lya.lyaspshop.utils.CookieUtils; 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; import static com.lya.lyaspshop.core.Constants.USER_CACHE_TOKEN; import static com.lya.lyaspshop.resp.JsonResponseStatus.NO_LOGIN; @Component public class UserArgumentResolver implements HandlerMethodArgumentResolver { @Autowired private RedisServiceImpl redisService; @Override public boolean supportsParameter(MethodParameter parameter) { return parameter.getParameterType() == User.class; } @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest(); String token = CookieUtils.getCookieValue(request, USER_CACHE_TOKEN); if (token == null) throw new BusinessException(NO_LOGIN); return redisService.getUserByToken(token); } }
配置:配置类才能使用
package com.lya.lyaspshop.core; 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; @Component public class WebConfig implements WebMvcConfigurer { @Autowired private UserArgumentResolver userArgumentResolver; @Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) { resolvers.add(userArgumentResolver); } }
使用解析类: