一、购物车的存储形式
1、cookie存放数据
无须登录、无须查库、保存在浏览器端
优点:性能好、访问快、没有和数据库交互。
缺点1:换浏览器购物车数据会丢失。
缺点2:电脑被他人使用,存在隐私安全。
2、session存放数据
用户登录后,购物车数据放入用户会话
优点:初期性能较好、访问快。
缺点1:session基于内存、用户量庞大影响服务器性能。
缺点2:只能存在于当前回话,不适用集群与分布式系统。
3、数据库存放数据
用户登录后,购物车数据存入数据库
优点:数据持久化,可在任何地点任何时间访问。
缺点:频繁读写数据库,造成数据库压力。
4、Redis存放数据
用户登录后,购物车数据存入redis缓存
优点1:数据持久化,可以在任何地点任何时间访问。
优点2:频繁读写只基于缓存,不会造成数据库压力。
优点3:适用于集群与分布式系统,可扩展性强
系统使用的是cookie+redis的形式,用户未登陆用的是cookie,登陆后用的cookie+redis相互结合
二、未登录已登录加入购物车业务代码
1、前端vue构建购物车的商品对象
2、前端购物车的数据覆盖本地的cookie
3、前端是在这里调用后端服务的。传递了shopcartItem这个对象。
3、服务端
1) 创建ShopcartBO对象

package com.imooc.pojo.bo; public class ShopcartBO { private String itemId; private String itemImgUrl; private String itemName; private String specId; private String specName; private Integer buyCounts; private String priceDiscount; private String priceNormal; public String getItemId() { return itemId; } public void setItemId(String itemId) { this.itemId = itemId; } public String getItemImgUrl() { return itemImgUrl; } public void setItemImgUrl(String itemImgUrl) { this.itemImgUrl = itemImgUrl; } public String getItemName() { return itemName; } public void setItemName(String itemName) { this.itemName = itemName; } public String getSpecId() { return specId; } public void setSpecId(String specId) { this.specId = specId; } public String getSpecName() { return specName; } public void setSpecName(String specName) { this.specName = specName; } public Integer getBuyCounts() { return buyCounts; } public void setBuyCounts(Integer buyCounts) { this.buyCounts = buyCounts; } public String getPriceDiscount() { return priceDiscount; } public void setPriceDiscount(String priceDiscount) { this.priceDiscount = priceDiscount; } public String getPriceNormal() { return priceNormal; } public void setPriceNormal(String priceNormal) { this.priceNormal = priceNormal; } @Override public String toString() { return "ShopcartBO{" + "itemId='" + itemId + '\'' + ", itemImgUrl='" + itemImgUrl + '\'' + ", itemName='" + itemName + '\'' + ", specId='" + specId + '\'' + ", specName='" + specName + '\'' + ", buyCounts=" + buyCounts + ", priceDiscount='" + priceDiscount + '\'' + ", priceNormal='" + priceNormal + '\'' + '}'; } }
2)添加商品到购物车
3、 后端方法设计到cookie。那么就会用到request对象。

package com.imooc.controller; import com.imooc.pojo.bo.ShopcartBO; import com.imooc.utils.IMOOCJSONResult; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.apache.commons.lang3.StringUtils; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Api(value = "购物车接口controller",tags = {"购物车接口相关的api"}) @RequestMapping("shopcart") @RestController public class ShopcartController { @ApiOperation(value = "添加商品到购物车",notes = "添加商品到购物车",httpMethod = "POST") @PostMapping("/add") public IMOOCJSONResult add( @RequestParam String userId, @RequestBody ShopcartBO shopcartBO, HttpServletRequest request, HttpServletResponse response ) { if (StringUtils.isBlank(userId)) { return IMOOCJSONResult.errorMsg(""); } System.out.println(shopcartBO); //TODO 前端用户在登录的情况下,添加商品到购物车,会同时在后端同步购物车到redis缓存 return IMOOCJSONResult.ok(); } @ApiOperation(value = "从购物车中删除商品",notes = "从购物车中删除商品",httpMethod = "POST") @PostMapping("/del") public IMOOCJSONResult del( @RequestParam String userId, @RequestParam String itemSpecId, HttpServletRequest request, HttpServletResponse response ) { if (StringUtils.isBlank(userId) || StringUtils.isBlank(itemSpecId)) { return IMOOCJSONResult.errorMsg(""); } //TODO 用户在页面删除购物车中的商品数据,如果此时用户已经登录,则需要同步删除后端购物车中的数据 return IMOOCJSONResult.ok(); } }
待完善方法体
登陆后后端的接口要判断相应的权限等等,那么这个权限又和redis,分布式会话有关,并且还涉及到拦截器。这些内容我们都会在分布式缓存里面所涉及到。
完善用户登陆的TODO

package com.imooc.controller; import com.imooc.pojo.Users; import com.imooc.pojo.bo.UserBO; import com.imooc.service.UserService; import com.imooc.utils.CookieUtils; import com.imooc.utils.IMOOCJSONResult; import com.imooc.utils.JsonUtils; import com.imooc.utils.MD5Utils; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Api(value = "注册登录",tags = "用于注册和登录的相关接口") @RestController @RequestMapping("passport") public class PassportController { @Autowired private UserService userService; @ApiOperation(value="用户名是否存在",notes = "用户名是否存在",httpMethod = "GET") @GetMapping("/usernameIsExist") public IMOOCJSONResult usernameIsExist(@RequestParam String username){ //1. if(StringUtils.isBlank(username)) { return IMOOCJSONResult.errorMsg("用户名不能为空"); } //2. boolean isExist=userService.queryUsernameIsExist(username); if (isExist) { return IMOOCJSONResult.errorMsg("用户名已经存在"); } //3.请求成功 return IMOOCJSONResult.ok(); } @ApiOperation(value="用户注册",notes = "用户注册",httpMethod = "POST") @PostMapping("/regist") public IMOOCJSONResult regist(@RequestBody UserBO userBO){ String username = userBO.getUsername(); String password = userBO.getPassword(); String confirmPwd=userBO.getConfirmPassword(); //0 if(StringUtils.isBlank(username) || StringUtils.isBlank(password)|| StringUtils.isBlank(confirmPwd)){ return IMOOCJSONResult.errorMsg("用户名或密码不能为空"); } //1 boolean isExist=userService.queryUsernameIsExist(username); if (isExist) { return IMOOCJSONResult.errorMsg("用户名已经存在"); } //2 if(password.length()<6) { return IMOOCJSONResult.errorMsg("用户密码不能少于6位"); } //3 //2 if(!password.equals(confirmPwd)) { return IMOOCJSONResult.errorMsg("两次密码不一致"); } //4 userService.creatUser(userBO); //TODO 生成用户token,存入redis //TODO 同步购物车数据 return IMOOCJSONResult.ok(); } @ApiOperation(value="用户登录",notes = "用户登录",httpMethod = "POST") @PostMapping("/login") public IMOOCJSONResult login(@RequestBody UserBO userBO, HttpServletRequest request, HttpServletResponse response) throws Exception{ String username = userBO.getUsername(); String password = userBO.getPassword(); //String confirmPwd=userBO.getConfirmPassword(); //0 if(StringUtils.isBlank(username) || StringUtils.isBlank(password) ){ return IMOOCJSONResult.errorMsg("用户名或密码不能为空"); } Users user= userService.queryUserForLogin(username, MD5Utils.getMD5Str(password)); if(user==null) { return IMOOCJSONResult.errorMsg("用户名和密码不正确"); } user=setNullProperty(user); CookieUtils.setCookie(request, response,"user", JsonUtils.objectToJson(user),true); //TODO 生成用户token,存入redis //TODO 同步购物车数据 return IMOOCJSONResult.ok(user); } @ApiOperation(value="用户退出登录",notes = "用户退出登录",httpMethod = "POST") @PostMapping("/logout") public IMOOCJSONResult logout(@RequestParam String userId,HttpServletRequest request, HttpServletResponse response){ //1 CookieUtils.deleteCookie(request, response, "user"); //TODO 用户退出登录,需要清空购物车 //TODO 分布式会话中需要清除用户数据 //2 return IMOOCJSONResult.ok(); } private Users setNullProperty(Users user) { user.setPassword(null); user.setNickname(null); user.setCreatedTime(null); return user; } }
三、刷新购物车
cookie中的数据拿出来在页面上做渲染是可以的吗?答案是不可以。这是因为我们的数据保存在前端,它只是一种临时的数据。购物车里面的数据,它不可能马上去结算去买单的。他有可能明天后天甚至过一个礼拜再打开。那么再打开购物车的时候,价格还是cookie里面的价格。当用户进入到购物车这个页面的时候,我们一定要刷新一下购物车里面的商品数据。如果说价格发生了更改,商品的图片、规则、名称都发生更改的话,我们就需要重新的刷新相关的数据。商品的数量是不需要做更新。尤其是金额需要做刷新。所以我们要把相应的数据,尤其是购物车里规格的id发发送到后端。让后端去查询出来购物车相关的数据,再放到页面上展示。
1、前端业务
(1)在声明周期函数里面找到renderShopcart方法,首先获取cookie中购物车的数据,如果没有就返回空
(2)for循环拼接购物车的数据
拼接完就是以逗号分隔开的字符串,如下: 1001,1002,3003,4004
(3)请求后端的接口
2、后端接口
(1)我们要查的就是购物车里面的对象。除了buyCounts其余的内容我们都需要。
(2)自定义sql查询语句

SELECT i.id AS itemId, ii.url AS itemImgUrl, i.item_name AS itemName, sp.id AS specId, sp.`name` AS specName, sp.price_discount AS priceDiscount, sp.price_normal AS priceNormal FROM items_spec sp LEFT JOIN items i ON i.id = sp.item_id LEFT JOIN items_img ii ON i.id = ii.item_id WHERE ii.is_main = 1 AND sp.id IN ( 'bingan-1001-spec-1', 'bingan-1001-spec-2', 'bingan-1001-spec-3')
(3)把sql复制到自定义的mapper里面。我们要返回的VO类型和之前写好的ShopcartBO基本是一样的,除了bugCounts这个属性没有。
传入的结合对象命名为paramsList,标签的闭合,循环的内容要在括号内部。里面的每一项元素使用逗号进行间隔。

<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="com.imooc.mapper.ItemsMapperCustom" > <select id="queryItemComments" parameterType="Map" resultType="com.imooc.pojo.vo.ItemCommentVO"> SELECT ic.comment_level AS commentLevel, ic.content AS content, ic.sepc_name AS specName, ic.created_time AS createdTime, u.face AS userFace, u.nickname AS nickName FROM items_comments ic LEFT JOIN users u ON ic.user_id = u.id WHERE ic.item_id = #{paramsMap.itemId} <if test=" paramsMap.level !=null and paramsMap.level !='' "> AND ic.comment_level = #{paramsMap.level} </if> </select> <select id="searchItems" parameterType="Map" resultType="com.imooc.pojo.vo.SearchItemsVO"> SELECT i.id AS itemId, i.item_name AS itemName, i.sell_counts AS sellCounts, ii.url AS imgUrl, tempSpec.priceDiscount AS price FROM items i LEFT JOIN items_img ii ON i.id = ii.item_id LEFT JOIN ( SELECT item_id, MIN( price_discount ) AS priceDiscount FROM items_spec GROUP BY item_id ) tempSpec ON i.id = tempSpec.item_id WHERE ii.is_main = 1 <if test=" paramsMap.keywords !=null and paramsMap.keywords !='' "> AND i.item_name like '%${paramsMap.keywords}%' </if> order by <choose> <when test=" paramsMap.sort == "c " "> i.sell_counts desc </when> <when test=" paramsMap.sort == "p " "> tempSpec.priceDiscount desc </when> <otherwise> i.item_name asc </otherwise> </choose> </select> <select id="searchItemsByThirdCat" parameterType="Map" resultType="com.imooc.pojo.vo.SearchItemsVO"> SELECT i.id AS itemId, i.item_name AS itemName, i.sell_counts AS sellCounts, ii.url AS imgUrl, tempSpec.priceDiscount AS price FROM items i LEFT JOIN items_img ii ON i.id = ii.item_id LEFT JOIN ( SELECT item_id, MIN( price_discount ) AS priceDiscount FROM items_spec GROUP BY item_id ) tempSpec ON i.id = tempSpec.item_id WHERE ii.is_main = 1 AND i.cat_id = #{paramsMap.catId} order by <choose> <when test=" paramsMap.sort == "c " "> i.sell_counts desc </when> <when test=" paramsMap.sort == "p " "> tempSpec.priceDiscount desc </when> <otherwise> i.item_name asc </otherwise> </choose> </select> <!-- k:默认,代表默认排序,根据name --> <!-- c:根据销量排序 --> <!-- p:根据价格排序 --> <select id="queryItemsBySpecIds" parameterType="List" resultType="com.imooc.pojo.vo.ShopcartVO"> SELECT i.id AS itemId, ii.url AS itemImgUrl, i.item_name AS itemName, sp.id AS specId, sp.`name` AS specName, sp.price_discount AS priceDiscount, sp.price_normal AS priceNormal FROM items_spec sp LEFT JOIN items i ON i.id = sp.item_id LEFT JOIN items_img ii ON i.id = ii.item_id WHERE ii.is_main = 1 AND sp.id IN <foreach collection="paramsList" index="index" item="specId" open="(" separator="," close=")"> #{specId} </foreach> </select> </mapper>

package com.imooc.pojo.vo; public class ShopcartVO { private String itemId; private String itemImgUrl; private String itemName; private String specId; private String specName; private String priceDiscount; private String priceNormal; public String getItemId() { return itemId; } public void setItemId(String itemId) { this.itemId = itemId; } public String getItemImgUrl() { return itemImgUrl; } public void setItemImgUrl(String itemImgUrl) { this.itemImgUrl = itemImgUrl; } public String getItemName() { return itemName; } public void setItemName(String itemName) { this.itemName = itemName; } public String getSpecId() { return specId; } public void setSpecId(String specId) { this.specId = specId; } public String getSpecName() { return specName; } public void setSpecName(String specName) { this.specName = specName; } public String getPriceDiscount() { return priceDiscount; } public void setPriceDiscount(String priceDiscount) { this.priceDiscount = priceDiscount; } public String getPriceNormal() { return priceNormal; } public void setPriceNormal(String priceNormal) { this.priceNormal = priceNormal; } }
(4)定义接口方法
注意参数名称和xml内保持一致

package com.imooc.mapper; import com.imooc.pojo.vo.ItemCommentVO; import com.imooc.pojo.vo.SearchItemsVO; import com.imooc.pojo.vo.ShopcartVO; import org.apache.ibatis.annotations.Param; import java.util.List; import java.util.Map; public interface ItemsMapperCustom { public List<ItemCommentVO> queryItemComments (@Param("paramsMap") Map<String, Object> map); public List<SearchItemsVO> searchItems(@Param("paramsMap") Map<String, Object> map); public List<SearchItemsVO> searchItemsByThirdCat(@Param("paramsMap") Map<String, Object> map); public List<ShopcartVO> queryItemsBySpecIds(@Param("paramsList") List<String> specIds); }
(5)service层

package com.imooc.service; import com.imooc.pojo.Items; import com.imooc.pojo.ItemsImg; import com.imooc.pojo.ItemsParam; import com.imooc.pojo.ItemsSpec; import com.imooc.pojo.vo.CommentLevelCountsVO; import com.imooc.pojo.vo.ItemCommentVO; import com.imooc.pojo.vo.SearchItemsVO; import com.imooc.pojo.vo.ShopcartVO; import com.imooc.utils.PagedGridResult; import org.apache.ibatis.annotations.Param; import java.util.List; import java.util.Map; public interface ItemService { /** * 根据商品ID查询详情 * @param itemId * @return */ public Items queryItemById(String itemId); /** * 根据商品ID查询图片列表 * @param itemId * @return */ public List<ItemsImg> queryItemImgList(String itemId); /** * 根据商品ID查询商品规格列表 * @param itemId * @return */ public List<ItemsSpec> queryItemSpecList(String itemId); /** * 根据商品ID查询商品参数 * @param itemId * @return */ public ItemsParam queryItemParam(String itemId); /** * 根据商品id查询商品的评价等级数量 * @param itemId */ public CommentLevelCountsVO queryItemCommentCounts(String itemId); /** * 根据商品id查询商品评价(分页) * @param itemId * @param leve * @return */ public PagedGridResult queryPagedComments (String itemId, Integer leve,Integer page,Integer pageSize); /** * 搜索商品列表 * @param keyWords * @param sort * @param page * @param pageSize * @return */ public PagedGridResult searchItems(String keyWords,String sort,Integer page,Integer pageSize); /** * 三级分类商品列表 * @param catId * @param sort * @param page * @param pageSize * @return */ public PagedGridResult searchItemsByThirdCat(Integer catId,String sort,Integer page,Integer pageSize); /** * 根据规格ids查询最新的购物车中商品数据(用于刷新渲染购物车中的商品数据) * @param specIds * @return */ public List<ShopcartVO> queryItemsBySpecIds(String specIds); }

package com.imooc.service.impl; import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; import com.imooc.enums.CommentLevel; import com.imooc.mapper.*; import com.imooc.pojo.*; import com.imooc.pojo.vo.CommentLevelCountsVO; import com.imooc.pojo.vo.ItemCommentVO; import com.imooc.pojo.vo.SearchItemsVO; import com.imooc.pojo.vo.ShopcartVO; import com.imooc.service.ItemService; import com.imooc.utils.DesensitizationUtil; import com.imooc.utils.PagedGridResult; import io.swagger.annotations.ApiParam; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import tk.mybatis.mapper.entity.Example; import java.util.*; @Service public class ItemServiceImpl implements ItemService { @Autowired ItemsMapper itemsMapper; @Autowired ItemsImgMapper itemsImgMapper; @Autowired ItemsSpecMapper itemsSpecMapper; @Autowired ItemsParamMapper itemsParamMapper; @Autowired ItemsCommentsMapper itemsCommentsCommentsMapper; @Autowired ItemsMapperCustom itemsMapperCustom; @Transactional(propagation = Propagation.SUPPORTS) @Override public Items queryItemById(String itemId) { return itemsMapper.selectByPrimaryKey(itemId); } @Transactional(propagation = Propagation.SUPPORTS) @Override public List<ItemsImg> queryItemImgList(String itemId) { Example itemsImgExp = new Example(ItemsImg.class); Example.Criteria criteria =itemsImgExp.createCriteria(); criteria.andEqualTo("itemId",itemId); return itemsImgMapper.selectByExample(itemsImgExp); } @Transactional(propagation = Propagation.SUPPORTS) @Override public List<ItemsSpec> queryItemSpecList(String itemId) { Example itemsSpecExp = new Example(ItemsSpec.class); Example.Criteria criteria =itemsSpecExp.createCriteria(); criteria.andEqualTo("itemId",itemId); return itemsSpecMapper.selectByExample(itemsSpecExp); } @Transactional(propagation = Propagation.SUPPORTS) @Override public ItemsParam queryItemParam(String itemId) { Example itemsParamExp = new Example(ItemsParam.class); Example.Criteria criteria =itemsParamExp.createCriteria(); criteria.andEqualTo("itemId",itemId); return itemsParamMapper.selectOneByExample(itemsParamExp); } @Transactional(propagation = Propagation.SUPPORTS) @Override public CommentLevelCountsVO queryItemCommentCounts(String itemId) { //Integer totalCounts=getCommentCounts(itemId); Integer goodCounts=getCommentCounts(itemId, CommentLevel.Good.type); Integer normalCounts=getCommentCounts(itemId, CommentLevel.NORMAL.type); Integer badCounts=getCommentCounts(itemId, CommentLevel.BAD.type); Integer totalCounts=goodCounts+normalCounts+badCounts; CommentLevelCountsVO commentLevelCountsVO=new CommentLevelCountsVO(); commentLevelCountsVO.setTotalCounts(totalCounts); commentLevelCountsVO.setGoodCounts(goodCounts); commentLevelCountsVO.setNormalCounts(normalCounts); commentLevelCountsVO.setBadCounts(badCounts); return commentLevelCountsVO; } @Transactional(propagation = Propagation.SUPPORTS) Integer getCommentCounts(String itemId,Integer level){ ItemsComments confdition =new ItemsComments(); confdition.setItemId(itemId); if (level != null) { confdition.setCommentLevel(level); } return itemsCommentsCommentsMapper.selectCount(confdition); } @Transactional(propagation = Propagation.SUPPORTS) @Override public PagedGridResult queryPagedComments(String itemId, Integer level, Integer page, Integer pageSzie) { Map<String,Object> map =new HashMap<>(); map.put("itemId",itemId); map.put("level",level); /** * page:第几页 * pageSize:每页显示多少条 */ PageHelper.startPage(page,pageSzie); List<ItemCommentVO> list=itemsMapperCustom.queryItemComments(map); for (ItemCommentVO vo : list ) { vo.setNickName(DesensitizationUtil.commonDisplay(vo.getNickName())); } return setterPagedGrid(list,page); } private PagedGridResult setterPagedGrid(List<?> list,Integer page){ PageInfo<?> pageList = new PageInfo<>(list); PagedGridResult grid = new PagedGridResult(); grid.setPage(page); grid.setRows(list); grid.setTotal(pageList.getPages()); grid.setRecords(pageList.getTotal()); return grid; } @Transactional(propagation = Propagation.SUPPORTS) @Override public PagedGridResult searchItems(String keywords, String sort, Integer page, Integer pageSize) { Map<String,Object> map =new HashMap<>(); map.put("keywords",keywords); map.put("sort",sort); /** * page:第几页 * pageSize:每页显示多少条 */ PageHelper.startPage(page,pageSize); List<SearchItemsVO> list=itemsMapperCustom.searchItems(map); return setterPagedGrid(list,page); } @Transactional(propagation = Propagation.SUPPORTS) @Override public PagedGridResult searchItemsByThirdCat(Integer catId, String sort, Integer page, Integer pageSize) { Map<String,Object> map =new HashMap<>(); map.put("catId",catId); map.put("sort",sort); /** * page:第几页 * pageSize:每页显示多少条 */ PageHelper.startPage(page,pageSize); List<SearchItemsVO> list=itemsMapperCustom.searchItemsByThirdCat(map); return setterPagedGrid(list,page); } @Transactional(propagation = Propagation.SUPPORTS) @Override public List<ShopcartVO> queryItemsBySpecIds(String specIds) { String ids[] =specIds.split(","); List<String> specIdList = new ArrayList<>(); Collections.addAll(specIdList,ids); return itemsMapperCustom.queryItemsBySpecIds(specIdList); } }
(6)Api子模块 (controller层)
、

package com.imooc.controller; import com.imooc.enums.YesOrNo; import com.imooc.pojo.*; import com.imooc.pojo.vo.*; import com.imooc.service.CarouselService; import com.imooc.service.CategoryService; import com.imooc.service.ItemService; import com.imooc.utils.IMOOCJSONResult; import com.imooc.utils.PagedGridResult; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; @Api(value = "商品接口",tags = "商品信息展示的相关接口") @RestController @RequestMapping("item") public class ItemController { @Autowired private ItemService itemService; @ApiOperation(value="查询商品详情",notes = "查询商品详情",httpMethod = "GET") @GetMapping("/info/{itemId}") public IMOOCJSONResult info( @ApiParam(name = "itemId",value = "商品ID",required = true) @PathVariable() String itemId) { if (StringUtils.isBlank(itemId)) { return IMOOCJSONResult.errorMsg(""); } Items item = itemService.queryItemById(itemId); List<ItemsImg> itemImgList=itemService.queryItemImgList(itemId); List<ItemsSpec> itemSpecList=itemService.queryItemSpecList(itemId); ItemsParam itemParam=itemService.queryItemParam(itemId); ItemInfoVO itemInfoVO=new ItemInfoVO(); itemInfoVO.setItem(item); itemInfoVO.setItemImgList(itemImgList); itemInfoVO.setItemSpecList(itemSpecList); itemInfoVO.setItemParam(itemParam); return IMOOCJSONResult.ok(itemInfoVO); } @ApiOperation(value="查询商品评价分页",notes = "查询商品评价分页",httpMethod = "GET") @GetMapping("/comments") public IMOOCJSONResult comments( @ApiParam(name = "itemId",value = "商品ID",required = true) @RequestParam() String itemId, @ApiParam(name = "level",value = "评价等级",required = false) @RequestParam() Integer level, @ApiParam(name = "page",value = "查询下一页的第几页",required = false) @RequestParam() Integer page, @ApiParam(name = "pageSize",value = "分页每一页显示的条数",required = false) @RequestParam() Integer pageSize ) { if (StringUtils.isBlank(itemId)) { return IMOOCJSONResult.errorMsg(""); } if(page==null) { page=1; } if(pageSize==null) { pageSize=10; } PagedGridResult grid = itemService.queryPagedComments(itemId,level,page,pageSize); return IMOOCJSONResult.ok(grid); } @ApiOperation(value="查询商品评价等级",notes = "查询商品评价等级",httpMethod = "GET") @GetMapping("/commentLevel") public IMOOCJSONResult commentLevel( @ApiParam(name = "itemId",value = "商品ID",required = true) @RequestParam() String itemId) { if (StringUtils.isBlank(itemId)) { return IMOOCJSONResult.errorMsg(""); } CommentLevelCountsVO countsVO = itemService.queryItemCommentCounts(itemId); return IMOOCJSONResult.ok(countsVO); } @ApiOperation(value="搜索商品列表分页",notes = "搜索商品列表分页",httpMethod = "GET") @GetMapping("/search") public IMOOCJSONResult search( @ApiParam(name = "keywords",value = "关键字",required = true) @RequestParam() String keywords, @ApiParam(name = "sort",value = "排序",required = false) @RequestParam() String sort, @ApiParam(name = "page",value = "查询下一页的第几页",required = false) @RequestParam() Integer page, @ApiParam(name = "pageSize",value = "分页每一页显示的条数",required = false) @RequestParam() Integer pageSize ) { if (StringUtils.isBlank(keywords)) { return IMOOCJSONResult.errorMsg(""); } if (page == null) { page = 1; } if(pageSize==null) { pageSize=20; } PagedGridResult grid = itemService.searchItems(keywords,sort,page,pageSize); return IMOOCJSONResult.ok(grid); } @ApiOperation(value="通过三级分类Id搜索商品列表分页",notes = "通过三级分类Id搜索商品列表分页",httpMethod = "GET") @GetMapping("/catItems") public IMOOCJSONResult catItems( @ApiParam(name = "catId",value = "三级分类id",required = true) @RequestParam() Integer catId, @ApiParam(name = "sort",value = "排序",required = false) @RequestParam() String sort, @ApiParam(name = "page",value = "查询下一页的第几页",required = false) @RequestParam() Integer page, @ApiParam(name = "pageSize",value = "分页每一页显示的条数",required = false) @RequestParam() Integer pageSize ) { if (catId==null) { return IMOOCJSONResult.errorMsg(""); } if (page == null) { page = 1; } if(pageSize==null) { pageSize=20; } PagedGridResult grid = itemService.searchItemsByThirdCat(catId,sort,page,pageSize); return IMOOCJSONResult.ok(grid); } //用于用户长时间未登录网站,刷新购物车中的数据(主要是商品价格),类似京东淘宝 @ApiOperation(value="根据规格ids查询最新的购物车中商品数据",notes = "根据规格ids查询最新的购物车中商品数据",httpMethod = "GET") @GetMapping("/refresh") public IMOOCJSONResult refresh( @ApiParam(name = "itemSpecIds",value = "拼接的规格ids",required = true,example = "1001,1002,1103") @RequestParam() String itemSpecIds ) { if(StringUtils.isBlank(itemSpecIds)) { return IMOOCJSONResult.ok(); } List<ShopcartVO> list=itemService.queryItemsBySpecIds(itemSpecIds); return IMOOCJSONResult.ok(list); } }
7、前端代码
四、删除商品
购物车内商品的删除,考虑两种情况,一个是用户未登陆,一个是用户已登录。
如果未登陆直接在前端删除,如果用户已登录,那么就要拿着这条数据到后端的购物车里面删除。这样也是保证我们前后端数据的同步。
1、前端代码分析
删除的方法传入的是商品的规格id。购物车里面,商品是以规格作为单位的。
删除前端对象中的这个规格的商品
新的list重新放到cookie里面
2、用户登陆的情况,把商品的规格id传递到后端
3.后端接口

package com.imooc.controller; import com.imooc.pojo.bo.ShopcartBO; import com.imooc.utils.IMOOCJSONResult; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.apache.commons.lang3.StringUtils; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Api(value = "购物车接口controller",tags = {"购物车接口相关的api"}) @RequestMapping("shopcart") @RestController public class ShopcartController { @ApiOperation(value = "添加商品到购物车",notes = "添加商品到购物车",httpMethod = "POST") @PostMapping("/add") public IMOOCJSONResult add( @RequestParam String userId, @RequestBody ShopcartBO shopcartBO, HttpServletRequest request, HttpServletResponse response ) { if (StringUtils.isBlank(userId)) { return IMOOCJSONResult.errorMsg(""); } System.out.println(shopcartBO); //TODO 前端用户在登录的情况下,添加商品到购物车,会同时在后端同步购物车到redis缓存 return IMOOCJSONResult.ok(); } @ApiOperation(value = "从购物车中删除商品",notes = "从购物车中删除商品",httpMethod = "POST") @PostMapping("/del") public IMOOCJSONResult del( @RequestParam String userId, @RequestParam String itemSpecId, HttpServletRequest request, HttpServletResponse response ) { if (StringUtils.isBlank(userId) || StringUtils.isBlank(itemSpecId)) { return IMOOCJSONResult.errorMsg(""); } //TODO 用户在页面删除购物车中的商品数据,如果此时用户已经登录,则需要同步删除后端购物车中的数据 return IMOOCJSONResult.ok(); } }
五、提交购物车至结算页
判断用户是否登陆
支付页面的声明周期函数
最终的商品数据
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理