一、购物车分析
1、购物车需求分析
首先搭建好数据库
1、考虑客户端传来的商品数据有哪些
2、考虑添加到购物车中商品的信息有哪些
3、是根据哪个字段进行查找购物车商品
Copy
2、购物车实现思路
使用redis存储购物车数据,每次查看购物车的时候直接从Redis中获取。
3、表结构分析
1.订单表参考样例
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;
Copy
购物车详情表其实就是订单详情表结构,只是目前临时存储数据到Redis,等用户下单后才将数据从Redis取出存入到MySQL中。
2.折扣表
CREATE TABLE `tb_pref` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID',
`cate_id` int(11) DEFAULT NULL COMMENT '分类ID',
`buy_money` int(11) DEFAULT NULL COMMENT '消费金额',
`pre_money` int(11) DEFAULT NULL COMMENT '优惠金额',
`start_time` date DEFAULT NULL COMMENT '活动开始日期',
`end_time` date DEFAULT NULL COMMENT '活动截至日期',
`type` char(1) DEFAULT NULL COMMENT '类型,1:普通订单,2:限时活动',
`state` char(1) DEFAULT NULL COMMENT '状态,1:有效,0:无效',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
Copy
二、订单购物车微服务搭建
1、引入依赖
2、通过逆向工程创建实体类,server,serverlmp,controller
3、application.yml文件完善配置(eureka、feign、redis、jdbc)
Copy
1、添加商品到redis
1.1、实现思路
(1).用户添加购物车,只需要将要加入购物车的商品存入到Redis中即可。一个用户可以将多件商品加入购物车,存储到Redis中的数据可以采用Hash类型。
(2).选Hash类型可以将用户的用户名作为namespace的一部分,将指定商品加入购物车,则往对应的namespace中增加一个key和value,key是商品ID,value是加入购物车的商品详情,如下图:
1.2、代码实现将数据存入redis
1、需要使用feign来调用商品服务的接口来获取商品的信息——需要在订单服务开启客户端@EnableFeignClients
2、order服务的业务层添加add(商品数量,商品id,用户名)方法来实现将商品添加到redis
a、根据id查询库存表sku,判断该商品库存数量和是否上架出售
b、从sku表中查出来的信息中获取spu的id到spu表中查询商品的其他信息(如销售价格,优惠)
c、将查到的两个商品对象,创建一个实体类其包含了购物车所需的属性,用这个实体类将他们封装起来
3、将封装好的对象存储到redis中
Copy
1.2.1、feign
- sku库存
/***
* 根据ID查询SKU信息
* @param id : sku的ID
*/
@GetMapping(value = "/{id}")
public Result<Sku> findById(@PathVariable(value = "id", required = true) Long id);
Copy
- spu商品销售的信息表
/***
* 根据SpuID查询Spu信息
* @param id
* @return
*/
@GetMapping("/{id}")
public Result<Spu> findById(@PathVariable(name = "id") Long id);
Copy
1.2.2、业务层
添加到redis中数据的格式
redisTemplate.boundHashOps().put();
Copy
@Service
public class CartServiceImpl implements CartService {
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private SkuFeign skuFeign;
@Autowired
private SpuFeign spuFeign;
/***
* 加入购物车
* @param num:购买商品数量
* @param id:购买ID
* @param username:购买用户
* @return
*/
@Override
public void add(Integer num, Long id, String username) {
//查询SKU
Result<Sku> resultSku = skuFeign.findById(id);
if(resultSku!=null && resultSku.isFlag()){
//获取SKU
Sku sku = resultSku.getData();
//获取SPU
Result<Spu> resultSpu = spuFeign.findById(sku.getSpuId());
//将SKU转换成OrderItem
OrderItem orderItem = sku2OrderItem(sku,resultSpu.getData(), num);
/******
* 购物车数据存入到Redis
* namespace = Cart_[username]
* key=id(sku)
* value=OrderItem
*/
redisTemplate.boundHashOps("Cart_"+username).put(id,orderItem);
}
}
/***
* SKU转成OrderItem
* @param sku
* @param num
* @return
*/
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;
}
}
Copy
1.2.3、控制层
@RestController
@CrossOrigin
@RequestMapping(value = "/cart")
public class CartController {
@Autowired
private CartService cartService;
/***
* 加入购物车
* @param num:购买的数量
* @param id:购买的商品(SKU)ID
* @return
*/
@RequestMapping(value = "/add")
public Result add(Integer num, Long id){
//用户名
String username="szitheima";
//将商品加入购物车
cartService.add(num,id,username);
return new Result(true, StatusCode.OK,"加入购物车成功!");
}
}
Copy
注意:此处的用户名是写死的,如何获取用户名成为一个问题
2、从redis中获取商品数据
2.1、思路分析
存入数据时是以hash的形式存储的,且键位“Cart_”+用户名,值为商品详情对象,因此
2.2、业务层
redis中拿去数据的方法:注入redisTemplate,调用boundHashOps(键值).value()获取值
/***
* 查询用户购物车数据
* @param username
* @return
*/
@Override
public List<OrderItem> list(String username) {
//查询所有购物车数据
List<OrderItem> orderItems = redisTemplate.boundHashOps("Cart_"+username).values();
return orderItems;
}
Copy
3、存在的问题
3.1、商品数量正负问题
我们发现个问题,就是用户将商品加入购物车,无论数量是正负,都会执行添加购物车,如果数量<=0,应该移除该商品的。
- 修改方法
- 只需在添加商品到购物车的方法中增加一条判断逻辑商品数量为负便return
注意:在判断为负时,若redis存在该值,则需redisTemplate.boundHashOps(“Cart_”+username).delete(id);删除redis中的记录,这样便在查询购物车列表时不会出现该数据
3.2、数据精度丢失问题
SkuId是Long类型,在页面输出的时候会存在精度丢失问题,我们只需要在OrderItem的SkuId上加上字符串序列化类型(即@JsonSerialize)就可以了,代码如下