訂單功能模塊設計與實現


在商城項目中,之前我們介紹了購物車功能模塊的實現,商品加入到購物車之后,就是到購物車結算,然后顯示購物車的商品列表,點擊去結算,然后到了未提交前的訂單列表

點擊提交訂單后,生成此訂單,返回訂單的訂單號,付款金額,訂單預計到達時間。訂單系統是一個非常重要的系統,我們的移動端、PC端都需要訂單系統,所以這里我們將訂單系統單獨作為一個服務來,留出接口供客戶單來調用

 

今天我們來看下這個訂單系統到底是如何實現的:

一、訂單系統功能

訂單系統主要包含哪些功能模塊呢?

創建訂單功能、查看訂單列表、根據訂單id查詢訂單的詳細信息、訂單修改、訂單取消、訂單狀態、訂單評價等功能的實現。

今天我們來看下創建訂單的流程:

二、訂單系統的數據庫表的設計

創建訂單說到底就是向訂單表中添加數據,即insert這些信息。

下單功能一定要使用關系型數據庫表,保證數據的一致性,因為創建訂單要保證在一個事務(一個事務就是指向數據庫中進行的一種操作:比如插入,刪除等等)里面,nosql數據庫不支持事務,可能會丟失數據。

我們在網上購物的時候通常這個訂單包含的信息比較多,所以對於訂單系統如何創建它的數據庫也是非常重要的。創建數據庫遵循數據庫設計的三大范式原則來設計。

我們創建了三個表:tb_order(訂單信息表),tb_order_item(訂單詳情表),tb_order_shipping(訂單配送表).

tb_order:這里包含了訂單的基本信息

  

tb_order_item:訂單詳情表:訂單的詳情主要就是購買商品的信息,通過訂單的id來實現關聯

tb_order_shipping:訂單配送表:

這是三個基本的表,其實還可以有物流信息表,訂單交易信息表。這里我們采用這三張表足夠。

三、訂單系統接口文檔,一般我們開發的時候會收到已經寫好的接口文檔,比如創建訂單的接口文檔。

從上面這個表中,我們可以看到該接口的url,接口的傳入參數和返回值。

接下來我們針對這三個來進行代碼的編寫:

url屬於controller層,

傳入參數這里我們可以看到是數據庫建立的三張表信息:第一個是tb_order,第二個是一個集合式的訂單明細List,第三個是訂單的配送信息表。

所以傳入參數就是這三個對象。這里我們是編寫接口,供客戶端調用,至於客戶端怎么將這些參數傳遞過來,那是客戶端團隊考慮的事情。

返回值這里使用了taotaoresult來包裝了下,因為我們提交訂單成功后,返回的是訂單號,即訂單的id所以,我們需要向客戶端傳遞訂單id過去,並顯示在訂單創建成功的頁面。

下面看下訂單服務接口的service層的實現:

service層的主要實現是將訂單信息添加到數據庫中,即接收controller傳遞過來的對象,然后補全頁面沒有的字段,insert數據庫,這里可以使用逆向工程生成的dao。

另外還有個問題:

訂單編號:訂單編號用什么形式比較好呢?

 

解決方案一(不能使用):

 

使用mysql的自增長。

 

優點:不需要我們自己生成訂單號,mysql會自動生成。

 

缺點:如果訂單表數量太大時需要分庫分表,此時訂單號會重復。如果數據備份后再恢復,訂單號會變。

 

方案二:日期+隨機數

 

采用毫秒+隨機數。

 

缺點:仍然有重復的可能。不建議采用此方案。在沒有更好的解決方案之前可以使用。

 

方案三:使用UUID

 

優點:不會重復。

 

缺點:長。可讀性查。不建議使用。 

 

方案四:可讀性好,不能太長。一般訂單都是全數字的。可以使用redis的incr命令生成訂單號。

 

優點:可讀性好,不會重復

 

缺點:需要搭建redis服務器。

所以我們選取方案四作為生成訂單號的方案。

那么service層的編碼如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
package  com.taotao.order.service.impl;
 
import  java.util.Date;
import  java.util.List;
 
import  org.apache.commons.lang3.StringUtils;
import  org.springframework.beans.factory.annotation.Autowired;
import  org.springframework.beans.factory.annotation.Value;
import  org.springframework.stereotype.Service;
 
import  com.taotao.common.utils.TaotaoResult;
import  com.taotao.mapper.TbOrderItemMapper;
import  com.taotao.mapper.TbOrderMapper;
import  com.taotao.mapper.TbOrderShippingMapper;
import  com.taotao.order.dao.JedisClient;
import  com.taotao.order.service.OrderService;
import  com.taotao.pojo.TbOrder;
import  com.taotao.pojo.TbOrderItem;
import  com.taotao.pojo.TbOrderShipping;
//訂單模塊實現
@Service
public  class  OrderServiceImpl  implements  OrderService {
     @Autowired
     private  TbOrderMapper orderMapper;
     @Autowired
     private  TbOrderItemMapper orderItemMapper;
     @Autowired
     private  TbOrderShippingMapper shippingMapper;
     @Autowired
     private  JedisClient jedisClient;
     @Value ( "${ORDER_GEN_KEY}" )
     private  String ORDER_GEN_KEY;
     @Value ( "${ORDER_DEFAULT_KEY}" )
     private  String ORDER_DEFAULT_KEY;
     @Value ( "${ORDER_ITEM_KEY}" )
     private  String ORDER_ITEM_KEY;
     
//創建訂單功能實現
     @Override
     public  TaotaoResult createOrder(TbOrder order,List<TbOrderItem> orderItem, TbOrderShipping orderShipping) {
         //邏輯步驟:創建訂單即向數據庫中三個表中插入數據,所以講數據庫中的字段補全即可。頁面傳遞過來的就不需要補全了,直接可以寫入數據庫
         /*第一步:訂單號的生成方式:因為訂單號的特殊性,訂單后最好采用數字組成,且不能重復,可以采用redis中自增長的方式來實現,
          * 在redis中,如果無key,則redis自動創建你寫的key的名字。采用incr的命令來實現訂單號的自增。默認自增是從1開始的,因為考慮到用戶的原因,在redis中設置一個默認的key值
          * 這樣用戶體驗會好些,不會因為看到簡單的1,2,3,所以設置一個默認的key值
         */
         String string = jedisClient.get(ORDER_GEN_KEY);
         if  (StringUtils.isBlank(string)) {
             //如果redis中的key為空,則利用默認值
             String defaultKey = jedisClient.set(ORDER_GEN_KEY,ORDER_DEFAULT_KEY);  
         }
         //如果存在此key則,采用incr命令自增
         long  orderId= jedisClient.incr(ORDER_GEN_KEY);
         //然后向訂單表中插入數據
         order.setOrderId(orderId+ "" );
         //訂單狀態:狀態:1、未付款,2、已付款,3、未發貨,4、已發貨
         order.setStatus( 1 );
         order.setCreateTime( new  Date());
         order.setUpdateTime( new  Date());
         order.setBuyerRate( 0 );
         //補全完信息后,插入數據庫表
         orderMapper.insert(order);
         //補全完訂單表后,將訂單明細表補全,因為之前訂單寫入redis,訂單明細的id也寫入redis吧,自動創建這個id的key。這個不需要默認編號了。對比頁面傳遞的參數,將剩下的補全
        String string2 = jedisClient.get(ORDER_ITEM_KEY);
        long  orderItemId = jedisClient.incr(string2);
        //因為傳遞過來的是一個集合列表,所以遍歷列表
        for  (TbOrderItem tbOrderItem : orderItem) {
            tbOrderItem.setId(orderItemId+ "" );
            tbOrderItem.setOrderId(orderId+ "" ); 
            orderItemMapper.insert( tbOrderItem);
    
         //接下來補全訂單配送表
         orderShipping.setOrderId(orderId+ "" );
         orderShipping.setCreated( new  Date());
         orderShipping.setUpdated( new  Date());
         shippingMapper.insert(orderShipping);
         
         return  TaotaoResult.ok(orderId);
     }
 
}

 controller:層實現

controller需要將對象傳遞給service層:(客戶端向服務器端傳入的參數格式,詳見后面博文)

我們看到接口文檔中,controller接收的參數是一個json格式的字符串,也就是說客戶端傳遞過來的是json格式的字符串。

這就涉及到springMVC是如何接收json字符串的,需要用到@RequestBody注解。這里多說幾句:

@ResponseBody注解的原理是response只能響應一個字符串,當我們的返回值是java對象的時候,它有一個默認行為,即利用jackson包將java對象轉為字符串響應。這是一個默認自動的行為,不需要我們設置,只要這個注解即可。

@RequestBody注解同理:利用這個注解告訴springMVC我們現在接收的是一個json字符串,需要采取默認行為利用jackson包將json字符串轉換為java對象,所以controller層我們需要一個java對象的pojo。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package  com.taotao.order.pojo;
 
import  java.util.List;
 
import  com.taotao.pojo.TbOrder;
import  com.taotao.pojo.TbOrderItem;
import  com.taotao.pojo.TbOrderShipping;
 
public  class  Order  extends  TbOrder {
     private  List<TbOrderItem> orderItems;
     private  TbOrderShipping orderShipping;
     public  List<TbOrderItem> getOrderItems() {
         return  orderItems;
     }
     public  void  setOrderItems(List<TbOrderItem> orderItems) {
         this .orderItems = orderItems;
     }
     public  TbOrderShipping getOrderShipping() {
         return  orderShipping;
     }
     public  void  setOrderShipping(TbOrderShipping orderShipping) {
         this .orderShipping = orderShipping;
     }
 
}

  controller層實現:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package  com.taotao.order.controller;
 
import  org.springframework.beans.factory.annotation.Autowired;
import  org.springframework.stereotype.Controller;
import  org.springframework.web.bind.annotation.RequestBody;
import  org.springframework.web.bind.annotation.RequestMapping;
import  org.springframework.web.bind.annotation.ResponseBody;
 
import  com.taotao.common.utils.ExceptionUtil;
import  com.taotao.common.utils.TaotaoResult;
import  com.taotao.order.pojo.Order;
import  com.taotao.order.service.OrderService;
//訂單管理模塊
@Controller
@RequestMapping ( "/order" )
public  class  OrderController {
     @Autowired
     private  OrderService orderService;
     @RequestMapping ( "/create" )
     @ResponseBody
     public  TaotaoResult createOrder( @RequestBody  Order order ){ //創建訂單模塊實現
         try  {
             TaotaoResult taotaoResult = orderService.createOrder(order ,order.getOrderItems(), order.getOrderShipping());
             return  taotaoResult;
         catch  (Exception e) {
             e.printStackTrace();
             return  TaotaoResult.build( 500 ,ExceptionUtil.getStackTrace(e));
         }
     
     }
 
}

  以上代碼是訂單服務接口的創建訂單接口代碼實現。

接下來我們看下客戶端如何調用訂單服務層的:

客戶端當我們點擊去購物車結算的時候,顯示購物車列表。

在購物車列表頁面,當點擊去結算的時候,跳轉到未提交訂單的頁面。

點擊提交訂單,跳轉到顯示成功頁面。

controller層實現:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package  com.taotao.portal.controller;
 
import  java.util.List;
 
import  javax.servlet.http.HttpServletRequest;
import  javax.servlet.http.HttpServletResponse;
 
import  org.joda.time.DateTime;
import  org.springframework.beans.factory.annotation.Autowired;
import  org.springframework.stereotype.Controller;
import  org.springframework.ui.Model;
import  org.springframework.web.bind.annotation.RequestMapping;
 
import  com.taotao.portal.pojo.CartItem;
import  com.taotao.portal.pojo.Order;
import  com.taotao.portal.service.CartService;
import  com.taotao.portal.service.OrderService;
 
@Controller
@RequestMapping ( "/order" )
public  class  OrderController {
     @Autowired
     private  CartService cartService;
     @Autowired
     private  OrderService orderService;
     @RequestMapping ( "/order-cart" )
     //點擊去結算,顯示訂單的頁面
     public  String showOrderPage(HttpServletRequest request,HttpServletResponse response,Model model){
         List<CartItem> list= cartService.showCartList(request);
         model.addAttribute( "cartList" , list);
         return  "order-cart" ;   
     }
     //點擊提交訂單,顯示訂單號,訂單金額,預計送達時間
     @RequestMapping ( "/create" )
     public  String showSuccessOrder(Order order,Model model){
      try  {
 
         String orderId= orderService.createOrder(order);
         model.addAttribute( "orderId " ,orderId);
         model.addAttribute( "payment" ,order.getPayment());
         model.addAttribute( "date" new  DateTime().plusDays( 3 ).toString( "yyyy-mm-dd" ));
         return  "success" ;
     
     catch  (Exception e) {
     e.printStackTrace();
     model.addAttribute( "message" "創建訂單出錯" );
     return  "error/exception" ;
    }
     }
}

  service層實現:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package  com.taotao.portal.service.impl;
 
import  org.springframework.beans.factory.annotation.Value;
import  org.springframework.stereotype.Service;
 
import  com.taotao.common.utils.HttpClientUtil;
import  com.taotao.common.utils.JsonUtils;
import  com.taotao.common.utils.TaotaoResult;
import  com.taotao.portal.pojo.Order;
import  com.taotao.portal.service.OrderService;
@Service
public  class  OrderServiceImpl  implements  OrderService {
     @Value ( "${ORDER_BASE_URL}" )
     private  String ORDER_BASE_URL;
     @Value ( "${ORDER_CREATE_URL}" )
     private  String ORDER_CREATE_URL;
     
//創建訂單的客戶端實現
     @Override
     public  String createOrder(Order order) {
     //因為httpclientUtil中dopost傳遞的是json格式的數據,所以需要將order這個java對象轉換成Json格式 
         String json = HttpClientUtil.doPost(ORDER_BASE_URL+ORDER_CREATE_URL+JsonUtils.objectToJson(order));
         //為了獲取里面的訂單id,我們需要將獲得的字符串轉換為taotaoresult,然后獲取數據,因為服務層傳遞過來的就是訂單的id,所以獲得數據也是獲得的id
         TaotaoResult result= TaotaoResult.format(json);
         if  (result.getStatus()== 200 ) {
             Object orderId = result.getData();
             return  orderId.toString();
         }
         return  "" ;
     }
 
}

  這里同樣用了pojo類Order類。

攔截器的問題:因為提交訂單我們需要用戶登錄,所以在springMVC.xml文件中配置上:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM