訂單功能模塊設計與實現


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

點擊提交訂單后,生成此訂單,返回訂單的訂單號,付款金額,訂單預計到達時間。訂單系統是一個非常重要的系統,我們的移動端、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層的編碼如下:

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。

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層實現:

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層實現:

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層實現:

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