购物车
购物车分为用户登录购物车和未登录购物车操作,国内知名电商京东用户登录和不登录都可以操作购物车,如果用户不登录,操作购物车可以将数据存储到Cookie,用户登录后购物车数据可以存储到Redis中,再将之前未登录加入的购物车合并到Redis中即可。
淘宝天猫则采用了另外一种实现方案,用户要想将商品加入购物车,必须先登录才能操作购物车。
我们今天实现的购物车是天猫解决方案,即用户必须先登录才能使用购物车功能。
购物车业务分析
(1)需求分析
用户在商品详细页点击加入购物车,提交商品SKU编号和购买数量,添加到购物车。购物车展示页面如下:
(2)购物车实现思路
我们实现的是用户登录后的购物车,用户将商品加入购物车的时候,直接将要加入购物车的详情存入到Redis即可。每次查看购物车的时候直接从Redis中获取。
(3)表结构分析
用户登录后将商品加入购物车,需要存储商品详情以及购买数量,购物车详情表如下:
changgou_order数据中tb_order_item表:
CREATE TABLE `tb_order_item` ( `id` varchar(20) COLLATE utf8_bin NOT NULL COMMENT 'ID', `category_id1` int(11) DEFAULT NULL COMMENT '1级分类', `category_id2` int(11) DEFAULT NULL COMMENT '2级分类', `category_id3` int(11) DEFAULT NULL COMMENT '3级分类', `spu_id` varchar(20) COLLATE utf8_bin DEFAULT NULL COMMENT 'SPU_ID', `sku_id` bigint(20) NOT NULL COMMENT 'SKU_ID', `order_id` bigint(20) NOT NULL COMMENT '订单ID', `name` varchar(200) COLLATE utf8_bin DEFAULT NULL COMMENT '商品名称', `price` int(20) DEFAULT NULL COMMENT '单价', `num` int(10) DEFAULT NULL COMMENT '数量', `money` int(20) DEFAULT NULL COMMENT '总金额', `pay_money` int(11) DEFAULT NULL COMMENT '实付金额', `image` varchar(200) COLLATE utf8_bin DEFAULT NULL COMMENT '图片地址', `weight` int(11) DEFAULT NULL COMMENT '重量', `post_fee` int(11) DEFAULT NULL COMMENT '运费', `is_return` char(1) COLLATE utf8_bin DEFAULT NULL COMMENT '是否退货', PRIMARY KEY (`id`), KEY `item_id` (`sku_id`), KEY `order_id` (`order_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
添加购物车
获取sku数据
goods服务中定义根据id查询sku对象实现
@RestController @CrossOrigin @RequestMapping("/sku") public class SkuController { @Autowired private SkuService skuService; @GetMapping("/{id}") public Result<Sku> findById(@PathVariable("id") String id){ Sku sku = skuService.findById(id); return new Result(true,StatusCode.OK,"查询成功",sku); } }
定义feign接口
goods-api工程中定义skuFeign接口,并定义查询方法
/** * 根据id查询sku信息 * @param id * @return */ @GetMapping("/{id}") public Result<Sku> findById(@PathVariable("id") String id);
订单服务添加依赖
<dependency> <groupId>com.changgou</groupId> <artifactId>changgou_service_goods_api</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
订单服务启动类添加feign接口扫描
@EnableFeignClients(basePackages = "com.changgou.goods.feign")
订单服务新建CartController
@RestController @CrossOrigin @RequestMapping("/cart") public class CartController { @Autowired private CartService cartService; /** * 添加购物车 * @param skuId * @param num * @return */ @GetMapping("/add") public Result add(@RequestParam("skuId") String skuId, @RequestParam("num") Integer num){ //暂时静态,后续动态获取 String username = "itcast"; cartService.add(skuId,num,username); return new Result(true, StatusCode.OK,"加入购物车成功"); } }
订单服务添加cartService,实现添加购物车
代码如下:
@Service public class CartServiceImpl implements CartService { private static final String CART="Cart_"; @Autowired private RedisTemplate redisTemplate; @Autowired private SkuFeign skuFeign; @Autowired private SpuFeign spuFeign; /** * 添加购物车 * @param skuId * @param num */ @Override public void add(String skuId, Integer num,String username) { /** * 1)查询redis中的数据 * 2)如果redis中已经有了,则追加数量,重新计算金额 * 3)如果没有,将商品添加到缓存 */ OrderItem orderItem = (OrderItem) redisTemplate.boundHashOps(CART+username).get(skuId); if (orderItem != null){ //存在,刷新购物车 orderItem.setNum(orderItem.getNum()+num); orderItem.setMoney(orderItem.getNum()*orderItem.getPrice()); orderItem.setPayMoney(orderItem.getNum()*orderItem.getPrice()); }else{ //不存在,新增购物车 Result<Sku> skuResult = skuFeign.findById(skuId); Sku sku = skuResult.getData(); Spu spu = spuFeign.findByspuId(sku.getSpuId()); //将SKU转换成OrderItem orderItem = this.sku2OrderItem(sku,spu,num); } //存入redis redisTemplate.boundHashOps(CART+username).put(skuId,orderItem); } //sku转换为orderItem private OrderItem sku2OrderItem(Sku sku, Spu spu, Integer num) { OrderItem orderItem = new OrderItem(); orderItem.setSpuId(sku.getSpuId()); orderItem.setSkuId(sku.getId()); orderItem.setName(sku.getName()); orderItem.setPrice(sku.getPrice()); orderItem.setNum(num); orderItem.setMoney(num*orderItem.getPrice()); //单价*数量 orderItem.setPayMoney(num*orderItem.getPrice()); //实付金额 orderItem.setImage(sku.getImage()); orderItem.setWeight(sku.getWeight()*num); //重量=单个重量*数量 //分类ID设置 orderItem.setCategoryId1(spu.getCategory1Id()); orderItem.setCategoryId2(spu.getCategory2Id()); orderItem.setCategoryId3(spu.getCategory3Id()); return orderItem; } }
测试添加购物车,效果如下:
请求地址localhost:9004/cart/add?skuId=100000022652&num=1
Redis缓存中已经有商品了
购物车列表
思路分析
接着我们实现一次购物车列表操作。因为存的时候是根据用户名往Redis中存储用户的购物车数据的,所以我们这里可以将用户的名字作为key去Redis中查询对应的数据。
代码实现
(1)控制层
com.changgou.order.controller.CartController类,添加购物车列表查询方法,代码如下:
/*** * 查询用户购物车列表 * @return */ @GetMapping(value = "/list") public Map list(){ //暂时静态,后续修改 String username = "itcast"; return cartService.list(username); }
(2)业务层
业务层接口
com.changgou.order.service.CartService接口,添加购物车列表方法,代码如下:
/*** * 查询用户的购物车数据 * @return */ Map list(String username);
业务层接口实现类
com.changgou.order.service.impl.CartServiceImpl类,添加购物车列表实现方法,代码如下:
/** * 获取购物车列表数据 * @param username * @return */ @Override public Map list(String username) { Map map = new HashMap(); List<OrderItem> orderItemList = redisTemplate.boundHashOps(CART+username).values(); map.put("orderItemList",orderItemList); //商品数量与总价格 Integer totalNum = 0; Integer totalPrice = 0; for (OrderItem orderItem : orderItemList) { totalNum +=orderItem.getNum(); totalPrice+=orderItem.getMoney(); } map.put("totalNum",totalNum); map.put("totalPrice",totalPrice); return map; }
(3)测试
使用Postman访问 GET http://localhost:9004/cart/list ,效果如下: