一、購物車分析
1、購物車需求分析
首先搭建好數(shù)據(jù)庫
1、考慮客戶端傳來的商品數(shù)據(jù)有哪些
2、考慮添加到購物車中商品的信息有哪些
3、是根據(jù)哪個字段進行查找購物車商品
2、購物車實現(xiàn)思路
使用redis存儲購物車數(shù)據(jù),每次查看購物車的時候直接從Redis中獲取。
3、表結(jié)構(gòu)分析
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 '數(shù)量',
`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;
購物車詳情表其實就是訂單詳情表結(jié)構(gòu),只是目前臨時存儲數(shù)據(jù)到Redis,等用戶下單后才將數(shù)據(jù)從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 '優(yōu)惠金額',
`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 '狀態(tài),1:有效,0:無效',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
二、訂單購物車微服務(wù)搭建
1、引入依賴
2、通過逆向工程創(chuàng)建實體類,server,serverlmp,controller
3、application.yml文件完善配置(eureka、feign、redis、jdbc)
1、添加商品到redis
1.1、實現(xiàn)思路
(1).用戶添加購物車,只需要將要加入購物車的商品存入到Redis中即可。一個用戶可以將多件商品加入購物車,存儲到Redis中的數(shù)據(jù)可以采用Hash類型。
(2).選Hash類型可以將用戶的用戶名作為namespace的一部分,將指定商品加入購物車,則往對應(yīng)的namespace中增加一個key和value,key是商品ID,value是加入購物車的商品詳情,如下圖:
1.2、代碼實現(xiàn)將數(shù)據(jù)存入redis
1、需要使用feign來調(diào)用商品服務(wù)的接口來獲取商品的信息——需要在訂單服務(wù)開啟客戶端@EnableFeignClients
2、order服務(wù)的業(yè)務(wù)層添加add(商品數(shù)量,商品id,用戶名)方法來實現(xiàn)將商品添加到redis
a、根據(jù)id查詢庫存表sku,判斷該商品庫存數(shù)量和是否上架出售
b、從sku表中查出來的信息中獲取spu的id到spu表中查詢商品的其他信息(如銷售價格,優(yōu)惠)
c、將查到的兩個商品對象,創(chuàng)建一個實體類其包含了購物車所需的屬性,用這個實體類將他們封裝起來
3、將封裝好的對象存儲到redis中
1.2.1、feign
- sku庫存
/***
* 根據(jù)ID查詢SKU信息
* @param id : sku的ID
*/
@GetMapping(value = "/{id}")
public Result<Sku> findById(@PathVariable(value = "id", required = true) Long id);
- spu商品銷售的信息表
/***
* 根據(jù)SpuID查詢Spu信息
* @param id
* @return
*/
@GetMapping("/{id}")
public Result<Spu> findById(@PathVariable(name = "id") Long id);
1.2.2、業(yè)務(wù)層
添加到redis中數(shù)據(jù)的格式
redisTemplate.boundHashOps().put();
@Service
public class CartServiceImpl implements CartService {
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private SkuFeign skuFeign;
@Autowired
private SpuFeign spuFeign;
/***
* 加入購物車
* @param num:購買商品數(shù)量
* @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轉(zhuǎn)換成OrderItem
OrderItem orderItem = sku2OrderItem(sku,resultSpu.getData(), num);
/******
* 購物車數(shù)據(jù)存入到Redis
* namespace = Cart_[username]
* key=id(sku)
* value=OrderItem
*/
redisTemplate.boundHashOps("Cart_"+username).put(id,orderItem);
}
}
/***
* SKU轉(zhuǎn)成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()); //單價*數(shù)量
orderItem.setPayMoney(num*orderItem.getPrice()); //實付金額
orderItem.setImage(sku.getImage());
orderItem.setWeight(sku.getWeight()*num); //重量=單個重量*數(shù)量
//分類ID設(shè)置
orderItem.setCategoryId1(spu.getCategory1Id());
orderItem.setCategoryId2(spu.getCategory2Id());
orderItem.setCategoryId3(spu.getCategory3Id());
return orderItem;
}
}
1.2.3、控制層
@RestController
@CrossOrigin
@RequestMapping(value = "/cart")
public class CartController {
@Autowired
private CartService cartService;
/***
* 加入購物車
* @param num:購買的數(shù)量
* @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,"加入購物車成功!");
}
}
注意:此處的用戶名是寫死的,如何獲取用戶名成為一個問題
2、從redis中獲取商品數(shù)據(jù)
2.1、思路分析
存入數(shù)據(jù)時是以hash的形式存儲的,且鍵位“Cart_”+用戶名,值為商品詳情對象,因此
2.2、業(yè)務(wù)層
redis中拿去數(shù)據(jù)的方法:注入redisTemplate,調(diào)用boundHashOps(鍵值).value()獲取值
/***
* 查詢用戶購物車數(shù)據(jù)
* @param username
* @return
*/
@Override
public List<OrderItem> list(String username) {
//查詢所有購物車數(shù)據(jù)
List<OrderItem> orderItems = redisTemplate.boundHashOps("Cart_"+username).values();
return orderItems;
}
3、存在的問題
3.1、商品數(shù)量正負問題
我們發(fā)現(xiàn)個問題,就是用戶將商品加入購物車,無論數(shù)量是正負,都會執(zhí)行添加購物車,如果數(shù)量<=0,應(yīng)該移除該商品的。
- 修改方法
- 只需在添加商品到購物車的方法中增加一條判斷邏輯商品數(shù)量為負便return
注意:在判斷為負時,若redis存在該值,則需redisTemplate.boundHashOps(“Cart_”+username).delete(id);刪除redis中的記錄,這樣便在查詢購物車列表時不會出現(xiàn)該數(shù)據(jù)
3.2、數(shù)據(jù)精度丟失問題
SkuId是Long類型,在頁面輸出的時候會存在精度丟失問題,我們只需要在OrderItem的SkuId上加上字符串序列化類型(即@JsonSerialize)就可以了,代碼如下