一、購(gòu)物車分析
1、購(gòu)物車需求分析
首先搭建好數(shù)據(jù)庫(kù)
1、考慮客戶端傳來(lái)的商品數(shù)據(jù)有哪些
2、考慮添加到購(gòu)物車中商品的信息有哪些
3、是根據(jù)哪個(gè)字段進(jìn)行查找購(gòu)物車商品
2、購(gòu)物車實(shí)現(xiàn)思路
使用redis存儲(chǔ)購(gòu)物車數(shù)據(jù),每次查看購(gòu)物車的時(shí)候直接從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級(jí)分類',
`category_id2` int(11) DEFAULT NULL COMMENT '2級(jí)分類',
`category_id3` int(11) DEFAULT NULL COMMENT '3級(jí)分類',
`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 '單價(jià)',
`num` int(10) DEFAULT NULL COMMENT '數(shù)量',
`money` int(20) DEFAULT NULL COMMENT '總金額',
`pay_money` int(11) DEFAULT NULL COMMENT '實(shí)付金額',
`image` varchar(200) COLLATE utf8_bin DEFAULT NULL COMMENT '圖片地址',
`weight` int(11) DEFAULT NULL COMMENT '重量',
`post_fee` int(11) DEFAULT NULL COMMENT '運(yùn)費(fèi)',
`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;
購(gòu)物車詳情表其實(shí)就是訂單詳情表結(jié)構(gòu),只是目前臨時(shí)存儲(chǔ)數(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 '消費(fèi)金額',
`pre_money` int(11) DEFAULT NULL COMMENT '優(yōu)惠金額',
`start_time` date DEFAULT NULL COMMENT '活動(dòng)開(kāi)始日期',
`end_time` date DEFAULT NULL COMMENT '活動(dòng)截至日期',
`type` char(1) DEFAULT NULL COMMENT '類型,1:普通訂單,2:限時(shí)活動(dòng)',
`state` char(1) DEFAULT NULL COMMENT '狀態(tài),1:有效,0:無(wú)效',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
二、訂單購(gòu)物車微服務(wù)搭建
1、引入依賴
2、通過(guò)逆向工程創(chuàng)建實(shí)體類,server,serverlmp,controller
3、application.yml文件完善配置(eureka、feign、redis、jdbc)
1、添加商品到redis
1.1、實(shí)現(xiàn)思路
(1).用戶添加購(gòu)物車,只需要將要加入購(gòu)物車的商品存入到Redis中即可。一個(gè)用戶可以將多件商品加入購(gòu)物車,存儲(chǔ)到Redis中的數(shù)據(jù)可以采用Hash類型。
(2).選Hash類型可以將用戶的用戶名作為namespace的一部分,將指定商品加入購(gòu)物車,則往對(duì)應(yīng)的namespace中增加一個(gè)key和value,key是商品ID,value是加入購(gòu)物車的商品詳情,如下圖:
1.2、代碼實(shí)現(xiàn)將數(shù)據(jù)存入redis
1、需要使用feign來(lái)調(diào)用商品服務(wù)的接口來(lái)獲取商品的信息——需要在訂單服務(wù)開(kāi)啟客戶端@EnableFeignClients
2、order服務(wù)的業(yè)務(wù)層添加add(商品數(shù)量,商品id,用戶名)方法來(lái)實(shí)現(xiàn)將商品添加到redis
a、根據(jù)id查詢庫(kù)存表sku,判斷該商品庫(kù)存數(shù)量和是否上架出售
b、從sku表中查出來(lái)的信息中獲取spu的id到spu表中查詢商品的其他信息(如銷售價(jià)格,優(yōu)惠)
c、將查到的兩個(gè)商品對(duì)象,創(chuàng)建一個(gè)實(shí)體類其包含了購(gòu)物車所需的屬性,用這個(gè)實(shí)體類將他們封裝起來(lái)
3、將封裝好的對(duì)象存儲(chǔ)到redis中
1.2.1、feign
- sku庫(kù)存
/***
* 根據(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;
/***
* 加入購(gòu)物車
* @param num:購(gòu)買商品數(shù)量
* @param id:購(gòu)買ID
* @param username:購(gòu)買用戶
* @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);
/******
* 購(gòu)物車數(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()); //單價(jià)*數(shù)量
orderItem.setPayMoney(num*orderItem.getPrice()); //實(shí)付金額
orderItem.setImage(sku.getImage());
orderItem.setWeight(sku.getWeight()*num); //重量=單個(gè)重量*數(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;
/***
* 加入購(gòu)物車
* @param num:購(gòu)買的數(shù)量
* @param id:購(gòu)買的商品(SKU)ID
* @return
*/
@RequestMapping(value = "/add")
public Result add(Integer num, Long id){
//用戶名
String username="szitheima";
//將商品加入購(gòu)物車
cartService.add(num,id,username);
return new Result(true, StatusCode.OK,"加入購(gòu)物車成功!");
}
}
注意:此處的用戶名是寫(xiě)死的,如何獲取用戶名成為一個(gè)問(wèn)題
2、從redis中獲取商品數(shù)據(jù)
2.1、思路分析
存入數(shù)據(jù)時(shí)是以hash的形式存儲(chǔ)的,且鍵位“Cart_”+用戶名,值為商品詳情對(duì)象,因此
2.2、業(yè)務(wù)層
redis中拿去數(shù)據(jù)的方法:注入redisTemplate,調(diào)用boundHashOps(鍵值).value()獲取值
/***
* 查詢用戶購(gòu)物車數(shù)據(jù)
* @param username
* @return
*/
@Override
public List<OrderItem> list(String username) {
//查詢所有購(gòu)物車數(shù)據(jù)
List<OrderItem> orderItems = redisTemplate.boundHashOps("Cart_"+username).values();
return orderItems;
}
3、存在的問(wèn)題
3.1、商品數(shù)量正負(fù)問(wèn)題
我們發(fā)現(xiàn)個(gè)問(wèn)題,就是用戶將商品加入購(gòu)物車,無(wú)論數(shù)量是正負(fù),都會(huì)執(zhí)行添加購(gòu)物車,如果數(shù)量<=0,應(yīng)該移除該商品的。
- 修改方法
- 只需在添加商品到購(gòu)物車的方法中增加一條判斷邏輯商品數(shù)量為負(fù)便return
注意:在判斷為負(fù)時(shí),若redis存在該值,則需redisTemplate.boundHashOps(“Cart_”+username).delete(id);刪除redis中的記錄,這樣便在查詢購(gòu)物車列表時(shí)不會(huì)出現(xiàn)該數(shù)據(jù)
3.2、數(shù)據(jù)精度丟失問(wèn)題
SkuId是Long類型,在頁(yè)面輸出的時(shí)候會(huì)存在精度丟失問(wèn)題,我們只需要在OrderItem的SkuId上加上字符串序列化類型(即@JsonSerialize)就可以了,代碼如下