好好學習,天天向上
本文已收錄至我的Github倉庫DayDayUP:github.com/RobodLee/DayDayUP,歡迎Star,更多文章請前往:目錄導航
- 暢購商城(一):環境搭建
- 暢購商城(二):分布式檔案系統FastDFS
- 暢購商城(三):商品管理
- 暢購商城(四):Lua、OpenResty、Canal實作廣告快取與同步
- 暢購商城(五):Elasticsearch實作商品搜索
- 暢購商城(六):商品搜索
- 暢購商城(七):Thymeleaf實作靜態頁
- 暢購商城(八):微服務網關和JWT令牌
- 暢購商城(九):Spring Security Oauth2
- 暢購商城(十):購物車
- 暢購商城(十一):訂單
識訓地址
不論是在京東還是淘寶下單,都會去選擇識訓地址,那這些識訓地址資訊都是保存在MySQL中的,所以第一步要實作查詢識訓地址的功能,思路就是通過用戶名去查詢出識訓地址,但并不是前端將用戶名傳到服務器,而是直接決議Token拿到用戶名,然后再去查詢資料庫,這樣做的好處就是安全,實作起來很簡單,直接上代碼,決議Token用的還是之前寫的工具類,
// AddressController
@GetMapping("/user/list")
public Result<List<Address>> list() {
String username = tokenDecodeUtil.getUserInfo().get("username");
List<Address> addresses = addressService.list(username);
return new Result<>(true,StatusCode.OK,"查詢成功",addresses);
}
------------------------------------------------------------------------
// AddressServiceImpl
@Override
public List<Address> list(String username) {
return addressMapper.list(username);
}
-------------------------------------------------------------------------
// AddressMapper
@Select("select * from tb_address where username = #{username}")
List<Address> list(String username);
下單
下單并不是前端直接將訂單的資訊傳給服務器,服務器直接創建訂單這么簡單,這里面有兩個需要注意的點:
-
價格校驗
比如現在有個商品做活動,原價99元,現在限時88元,我在點擊提交訂單按鈕之前價格就已經恢復到99元的,但是頁面上的內容沒有重繪,還是88元,所以訂單提到到服務器的時候,后端這邊應該從資料庫里面將價格再查詢一遍,做個校驗,以資料庫查詢出來的為準,這樣可以防止價格變動導致的損失,
-
庫存檢查
檢查庫存是為了防止超賣,比如我進入到商品詳情頁的時候顯示還剩5件,就在提交訂單之前,該商品賣完了,但是由于界面未重繪導致顯示還有貨,所以訂單提交到服務器的時候,后端先從資料庫里面查詢一下時候還有貨,如果沒有貨的話訂單就創建失敗,反之成功,
所以說,并不是所有的東西都是從前端傳過來的,具體的可以看一下和訂單相關的兩張表,
和訂單有關的有兩張表,訂單是點擊下單后生成的記錄,比如我一次性下單10件商品,就是一個訂單,但是生成了10個訂單明細,其中,支付型別,識訓人,識訓人手機,識訓人地址是前端傳過來的,其它的資訊則是由后端去完善的,
在介紹完了這些內容之后,就可以去撰寫代碼了,
@PostMapping
public Result add(@RequestBody Order order){
String username = tokenDecodeUtil.getUserInfo().get("username");
order.setUsername(username);
orderService.add(order);
return new Result(true,StatusCode.OK,"添加成功");
}
---------------------------------------------------------------------------
@Override
public synchronized void add(Order order) {
order.setId(String.valueOf(idWorker.nextId()));
BoundHashOperations boundHashOperations = redisTemplate.boundHashOps("Cart_" + order.getUsername());
int totalNum=0,totalMoney=0; //總數量,總金額
LocalDateTime localDateTime = LocalDateTime.now();
List<OrderItem> orderItems = boundHashOperations.values(); //從購物車中獲取訂單明細
if (orderItems == null || orderItems.size()==0) {
throw new RuntimeException("購物車資料例外,下單失敗");
}
List<Sku> skuList = skuFeign.findBySkuIds(order.getSkuIds()).getData(); //資料庫中對應的sku集合
//如果資料庫中查詢出來的sku集合數量與前端傳過來的sku數量不一致,說明資料有誤,下單失敗
if (skuList.size() != order.getSkuIds().size()){
throw new RuntimeException("sku資料庫資料例外,下單失敗");
}
Map<Long,Sku> skuMap = skuList.stream().collect(Collectors.toMap(Sku::getId,a -> a));
//遍歷購物車中的資料,判斷是否是選中的,將選中的訂單明細資料補充完整
for (OrderItem orderItem : orderItems) {
if (order.getSkuIds().contains(orderItem.getSkuId())) { //判斷當前遍歷到的orderItem是否是選中的
orderItem.setId(String.valueOf(idWorker.nextId()));
orderItem.setOrderId(order.getId());
orderItem.setIsReturn("0");
Sku sku = skuMap.get(orderItem.getSkuId()); //資料庫中的sku
if (orderItem.getNum() <= sku.getNum()) { //判斷庫存是否充足,不足則報例外訂單提交失敗
totalNum += orderItem.getNum();
} else {
throw new RuntimeException("庫存不足,下單失敗");
}
totalMoney += sku.getPrice();
}
}
//減庫存,刪購物車
for (OrderItem orderItem : orderItems) {
if (order.getSkuIds().contains(orderItem.getSkuId())) {
Sku sku = skuMap.get(orderItem.getSkuId()); //資料庫中的sku
sku.setNum(sku.getNum() - orderItem.getNum()); //減庫存
boundHashOperations.delete(orderItem.getSkuId()); //刪購物車
orderItemMapper.insertSelective(orderItem); //添加到訂單明細表
}
}
skuFeign.updateMap(skuMap); //將sku資訊提交到資料庫中的sku表
order.setCreateTime(localDateTime);
order.setUpdateTime(localDateTime);
order.setTotalNum(totalNum);
order.setTotalMoney(totalMoney);
order.setSourceType("1"); //1.web
order.setOrderStatus("0");
order.setPayStatus("0");
order.setIsDelete("0");
orderMapper.insertSelective(order); //添加到訂單表
}
這段代碼有點長,首先在Controller層,通過決議Token,拿到了用戶名,然后到了Service層,order物件里面有個欄位
private List<Long> skuIds; //選中的sku的id
這個是在頁面選中的商品的id的集合,因為下單的時候,可以選擇購物車中的部分商品,并不是所有的商品,用這個就可以判斷哪些是選中的,
在Service層中,先是通過skuIds去呼叫Feign拿到對應的商品集合資料,因為需要進行庫存判斷,減庫存以及價格查詢,所以才去拿到這些資料,為了方便使用,將其轉成Map集合,然后查詢出購物車中的所有資料,因為我們需要將下單的商品從購物車中移除,
然后就開始第一遍遍歷購物車中的商品,目的是進行庫存判斷防止超賣,計算總數量,計算總金額,如果下單的數量大于庫存,就下單失敗,
如果第一遍遍歷的時候沒有發生超賣的現象,就可以進行第二遍遍歷購物車,目的是減庫存,洗掉購物車中已下單的商品,將訂單明細添加到訂單明細表中,等遍歷完了之后,就將改過庫存的sku資訊提交到資料庫中,使用兩次回圈而不是一次的原因是只要有一件商品庫存不足,那么此次下單就應該不成功,購物車中的資料不做修改,一次回圈滿足不了需求,
最后將order資訊補充完整后添加到訂單表中,至此,訂單就算創建完成了,
在這段代碼中,對幾個可能會發生例外的情況做了處理,
- 如果購物車中的資料為0,說明可能是前端傳錯了令牌,決議出來的用戶名下沒有商品資訊,此時下單失敗,
- 拿著skuId集合去資料庫中查詢對應的sku集合,如果資料量不一致,說明可能前端傳過來的某個skuId有誤或者資料庫中的資料有誤,那么下單失敗,
- 庫存不足,下單失敗,
- 為了確保執行緒安全,加了synchronized關鍵字,
好了,下訂單的功能就完成了,
還有一個用戶積分的功能,這個就比較簡單了,就是通過Feign去呼叫UserController中的方法,然后根據用戶名修改積分資料,沒什么好說的,代碼就不貼了,
總結
這篇文章到這里就結束了,內容比較少,就兩個,一個是根據用戶名去查詢識訓地址,還有一個是下單的功能,下單功能不算復雜,就是有些可能會出現問題的幾個點需要處理,沒有支付的功能怎么能叫下單呢,下篇文章就去實作一下支付的功能,讓我們下期再見!
碼字不易,可以的話,給我來個點贊,收藏,關注
如果你喜歡我的文章,歡迎關注微信公眾號 『 R o b o d 』,第一時間閱讀,
代碼:https://github.com/RobodLee/changgou
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/30795.html
標籤:Java
