課程計划
- 1、訂單系統搭建
- 2、訂單確認頁面展示
- 3、用戶認證
- 4、創建訂單
- 5、創建訂單成功后顯示訂單號
1、訂單系統搭建
1.1、功能分析
1、在購物車頁面點擊【去結算】按鈕跳轉到訂單確認頁面。
a) 展示商品列表
b) 配送地址列表
c) 選擇支付方式
2、展示訂單確認頁面之前,應該確認用戶身份。
a) 使用攔截器實現。
b) Cookie中取token。
c) 取不到token跳轉到登錄頁面。
d) 取到token,根據token查詢用戶信息。
e) 如果沒有用戶信息,登錄過期跳轉到登錄頁面。
f) 取到用戶信息,放行。
3、提交訂單
a) 生成訂單。
b) 展示訂單提交成功頁面。
1.2、工程搭建

1.2.1、創建訂單服務層工程
taotao-order(pom)
|--taotao-order-interface(jar)
|--taotao-order-service(war)
taotao-order

pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.taotao</groupId>
<artifactId>taotao-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>taotao-order</artifactId>
<packaging>pom</packaging>
<modules>
<module>taotao-order-interface</module>
<module>taotao-order-service</module>
</modules>
<dependencies>
<!-- 配置對taotao-common的依賴 -->
<dependency>
<groupId>com.taotao</groupId>
<artifactId>taotao-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- 配置Tomcat插件 -->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<configuration>
<port>8091</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
</project>
taotao-order-interface

pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.taotao</groupId>
<artifactId>taotao-order</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>taotao-order-interface</artifactId>
<dependencies>
<!-- 配置對taotao-manager-pojo的依賴 -->
<dependency>
<groupId>com.taotao</groupId>
<artifactId>taotao-manager-pojo</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
taotao-order-service

pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.taotao</groupId>
<artifactId>taotao-order</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>taotao-order-service</artifactId>
<packaging>war</packaging>
<dependencies>
<!-- 配置對taotao-manager-dao的依賴 -->
<dependency>
<groupId>com.taotao</groupId>
<artifactId>taotao-manager-dao</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- 配置對taotao-order-interface的依賴:服務層發布服務要通過該接口 -->
<dependency>
<groupId>com.taotao</groupId>
<artifactId>taotao-order-interface</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- 配置對spring的依賴 -->
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<!-- 配置對dubbo的依賴 -->
<!-- dubbo相關 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<!-- 排除對低版本jar包的依賴 -->
<exclusions>
<exclusion>
<artifactId>spring</artifactId>
<groupId>org.springframework</groupId>
</exclusion>
<exclusion>
<artifactId>netty</artifactId>
<groupId>org.jboss.netty</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</dependency>
<dependency>
<groupId>com.github.sgroschupf</groupId>
<artifactId>zkclient</artifactId>
</dependency>
</dependencies>
</project>
可以參考taotao-manager整合。

web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>taotao-order-service</display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<!-- 初始化spring容器:也即加載spring容器 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/applicationContext*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
1.2.2、創建訂單表現層工程
表現層工程處理訂單的確認頁面
和訂單提交后產生的訂單號
。
taotao-order-web打包方式war。

pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.taotao</groupId>
<artifactId>taotao-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>taotao-order-web</artifactId>
<packaging>war</packaging>
<dependencies>
<!-- 配置對taotao-common的依賴 -->
<dependency>
<groupId>com.taotao</groupId>
<artifactId>taotao-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- 配置對taotao-order-interface的依賴:表現層調用服務要通過該接口 -->
<dependency>
<groupId>com.taotao</groupId>
<artifactId>taotao-order-interface</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<!-- JSP相關 -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<scope>provided</scope>
</dependency>
<!-- 配置對dubbo的依賴 -->
<!-- dubbo相關 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<!-- 排除對低版本jar包的依賴 -->
<exclusions>
<exclusion>
<artifactId>spring</artifactId>
<groupId>org.springframework</groupId>
</exclusion>
<exclusion>
<artifactId>netty</artifactId>
<groupId>org.jboss.netty</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</dependency>
<dependency>
<groupId>com.github.sgroschupf</groupId>
<artifactId>zkclient</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- 配置Tomcat插件 -->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<configuration>
<port>8092</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
</project>
可以參考taotao-portal-web整合。

2、展示訂單確認頁面
2.1、導入靜態頁面

修改taotao-order-web工程中的src/main/resources/spring/下的springmvc.xml文件,
添加配置資源映射標簽
:

2.2、功能分析
在購物車表現層工程中的,有我們訂單確認頁面的URL:
/taotao-cart-web/src/main/webapp/WEB-INF/jsp/cart.jsp

由於訂單確認頁面展示在taotao-order-web系統,所以修改跳轉地址為:http://localhost:8092/order/order-cart.html
在購物車頁面點擊“去結算”按鈕跳轉到訂單確認頁面。
我們打開訂單確認頁面order-cart.jsp,看看需要准備什么數據。我們發現只需要准備購物車列表
List<TbItem>
,就可以了,但是還需要將
${cart.images[0]}
改為
${cart.image}

order-cart.jsp的第191行還需要修改:

不然會報錯!
請求分析:
請求的url:/order/order-cart
參數:沒有參數。
返回值:邏輯視圖String(order-cart.jsp),展示訂單確認頁面。
業務邏輯:
1、首先用戶先登錄,從cookie中獲取token,根據token調用SSO服務獲取登錄的用戶信息。
2、從cookie中獲取購物車的商品的列表數據。
3、從redis中獲取該用戶的購物車的商品列表數據。
4、將這兩者的數據進行合並,展示商品數據。並清除cookie中的數據。
5、展示配送地址列表,需要根據用戶id從數據庫中查詢收貨地址列表,這里暫時使用靜態數據。
6、展示支付方式,也需要從數據庫中查詢支付的方式列表,這里暫時使用靜態數據。
2.3、Dao層、Service層(無)
需要根據用戶id查詢收貨地址列表。
需要查詢支付方式。
但是由於這兩個都使用的靜態數據
,所以服務層不需要編寫。
靜態數據,做靜態頁面處理。
業務邏輯:
直接調用其他的服務層的服務即可。(需要添加對應的依賴,並引用服務。)
2.4、發布服務
由於表現層需要調服務層的SSO服務和購物車服務,所以需要在taotao-sso-service和taotao-cart-service工程中發布服務。
2.5、引用服務
在taotao-order-web工程中的pom.xml文件中配置對taotao-sso-interface和taotao-cart-interface的依賴:
<!-- 配置對taotao-sso-interface的依賴:表現層調用服務要通過該接口 -->
<dependency>
<groupId>com.taotao</groupId>
<artifactId>taotao-sso-interface</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- 配置對taotao-cart-interface的依賴:表現層調用服務要通過該接口 -->
<dependency>
<groupId>com.taotao</groupId>
<artifactId>taotao-cart-interface</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
在taotao-order-web工程中的springmvc.xml文件中引用服務:

2.6、Controller
參考功能分析的業務邏輯。
請求的url:/order/order-cart
參數:無
@Autowired
private UserLoginService userLoginService;
@Autowired
private CartService cartService;
@Value("${COOKIE_TOKEN_KEY}")
private String COOKIE_TOKEN_KEY;
@Value("${COOKIE_CART_KEY}")
private String COOKIE_CART_KEY;
@RequestMapping("/order/order-cart")
public String showOrderCart(HttpServletRequest request, HttpServletResponse response) {
// 1、從cookie中獲取token
String token = CookieUtils.getCookieValue(request, COOKIE_TOKEN_KEY);
// 2、根據token調用SSO服務獲取登錄的用戶信息,查看用戶是否已經過期
TbUser tbUser = null;
if (StringUtils.isNotBlank(token)) {
TaotaoResult result = userLoginService.getUserByToken(token);
if (result.getStatus() == 200) {
tbUser = (TbUser) result.getData();
}
}
// 3、用戶必須登錄才展示訂單頁面
// 4、展示用戶的配送地址列表,根據用戶id從數據庫中查詢收貨地址列表,這里暫時使用靜態數據
// 5、展示支付方式列表,從數據庫中查詢支付的方式列表,這里暫時使用靜態數據
// 6、從cookie中獲取購物車的商品列表
List<TbItem> cartList2 = getCartListFromCookie(request);
// 7、調用購物車服務從redis數據庫中獲取購物車的商品列表
List<TbItem> cartList1 = cartService.queryCartListByUserId(tbUser == null ? -1 : tbUser.getId());
// 8、合並數據到redis中
for (TbItem tbItem2 : cartList2) { // 遍歷cookie的購物車數據
boolean flag = false;
if (cartList1 != null) { // redis數據庫中有購物車數據
for (TbItem tbItem1 : cartList1) { // 遍歷redis數據庫中購物車的數據
if (tbItem1.getId() == tbItem2.getId().longValue()) {
// 商品數量相加
tbItem1.setNum(tbItem1.getNum() + tbItem2.getNum());
// 更新redis數據庫
cartService.updateTbItemCartByUserIdAndItemId(tbUser.getId(), tbItem1.getId(), tbItem1.getNum());
flag = true; // 表示找到
}
}
}
if (flag == false) { // 如果找了redis數據庫還沒有找到,說明cookie中的商品是新的,需要添加至redis數據庫
cartService.addItemCart(tbUser.getId(), tbItem2, tbItem2.getNum());
}
}
// 9、合並數據后刪除cookie
if (!cartList2.isEmpty()) {
CookieUtils.deleteCookie(request, response, COOKIE_CART_KEY);
}
// 10、再次調用購物車服務從redis數據庫中獲取新的購物車的商品列表
List<TbItem> cartList = cartService.queryCartListByUserId(tbUser.getId());
request.setAttribute("cartList", cartList);
return "order-cart";
}
/**
* 從cookie中獲取購物車的商品列表的方法
* @param request
* @return
*/
private List<TbItem> getCartListFromCookie(HttpServletRequest request) {
// 1、從cookie中獲取商品列表字符串
String cartJson = CookieUtils.getCookieValue(request, COOKIE_CART_KEY, true);
// 2、將字符串轉換成java對象
List<TbItem> list = new ArrayList<>();
if (StringUtils.isNotBlank(cartJson)) {
list = JsonUtils.jsonToList(cartJson, TbItem.class);
}
return list;
}
屬性文件內容如下:

2.7、訪問測試
首先安裝taotao-order工程,再啟動taotao-order-web。需要先登錄,再去訪問。
3、用戶身份認證
在展示訂單確認頁面之前,需要對用戶身份進行認證,要求用戶必須是登錄裝態。
3.1、功能分析
1、使用springmvc的攔截器實現。需要一個實現類實現HandlerInterceptor接口。
2、業務邏輯
a) 從cookie中取token。
b) 沒有token,需要跳轉到登錄頁面。
c) 有token,調用sso系統的服務,根據token查詢用戶信息。
d) 如果查不到用戶信息、用戶登錄已經過期。需要跳轉到登錄頁面。
e) 查詢到用戶信息,放行。
3、在springmvc.xml中配置攔截器。
3.2、攔截器實現

攔截器代碼如下:
/**
* 判斷用戶是否登錄的攔截器
* @author chenmingjun
* @date 2018年12月7日 下午3:41:52
* @version V1.0
*/
public class LoginInterceptor implements HandlerInterceptor {
@Autowired
private UserLoginService userLoginService;
@Value("${COOKIE_TOKEN_KEY}")
private String COOKIE_TOKEN_KEY;
@Value("${SSO_URL}")
private String SSO_URL;
/**
* 在進入目標方法之前執行該方法。
* 如果返回為false表示攔截,不讓訪問;如果返回為true,表示放行。
* 我們在這里進行用戶身份的認證。
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// 1、從cookie中獲取token
String token = CookieUtils.getCookieValue(request, COOKIE_TOKEN_KEY);
// 2、判斷token是否存在
if (StringUtils.isEmpty(token)) {
// 3、如果token不存在,說明用戶沒登錄,重定向到SSO系統的登錄頁面,在SSO系統登錄成功之后跳轉回訂單確認頁面
// http://localhost:8088/page/login?redirect=http://localhost:8092/order/order-cart.html
response.sendRedirect(SSO_URL + "/page/login?redirect=" + request.getRequestURL().toString());
// 攔截
return false;
}
// 4、如果token存在,則調用SSO服務判斷用戶信息,查看用戶登錄是否已經過期(加入依賴,引入服務,注入服務,使用)
TaotaoResult result = userLoginService.getUserByToken(token);
if (result.getStatus() != 200) {
// 5、如果用戶登錄已過期,重定向到SSO系統的登錄頁面,在SSO系統登錄成功之后跳轉回訂單確認頁面
// http://localhost:8088/page/login?redirect=http://localhost:8092/order/order-cart.html
response.sendRedirect(SSO_URL + "/page/login?redirect=" + request.getRequestURL().toString());
// 攔截
return false;
}
// 6、用戶登錄且沒過期,放行
return true;
}
/**
* 在進入目標方法之后,在返回ModelAndView之前執行該方法。
* 我們在這里可以對公用變量進行設置。比如用戶名。
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception {
}
/**
* 在返回ModelAndView之后執行該方法。
* 我們在這里可以進行異常的處理、日志的清理等。
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
}
屬性文件中的SSO_URL:

屬性文件代碼如下:
#cookie中存放token的key
COOKIE_TOKEN_KEY=COOKIE_TOKEN_KEY
#cookie中存放購物車的key
COOKIE_CART_KEY=COOKIE_CART_KEY
#SSO系統的URL
SSO_URL=http://localhost:8088
3.3、配置攔截器
在taotao-order-web工程中的springmvc.xml文件中配置攔截器:

配置文件代碼如下:
<!-- 配置用戶身份認證的攔截器,用於攔截訂單和訂單相關的請求-->
<mvc:interceptors>
<mvc:interceptor>
<!-- **表示當前路徑及其子路徑,*表示當前路徑 -->
<mvc:mapping path="/order/**"/><!-- 攔截訂單和訂單相關的請求 -->
<bean class="com.taotao.order.interceptor.LoginInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
3.4、訪問測試
訪問地址:http://localhost:8092/order/order-cart.html
發現被攔截后,重定向到登陸界面,登錄成功后,跳轉到商城首頁了。
3.5、實現攔截器后存在的問題
存在兩個問題:
1、SSO服務被調用了兩次,先在攔截器類
中的handler方法
中調用了一次,然后進入Controller類
中的目標方法
獲取用戶信息時又調用了一次。
2、登錄成功后跳轉到了商城首頁,應當跳轉到訂單確認頁面才對。
3.5.1、解決調用SSO服務兩次的問題
實際上,只需要在攔截器類
中的handler方法
中調用了一次便可以了,調用之后,將用戶信息的數據存放在request域中,等進入目標的方法后,可以直接通過request域獲取用戶信息。
在攔截器中,將登錄的用戶信息放到request域中:
攔截器類的代碼修改如下:

從request域中獲取登錄的用戶信息。
Controller類的方法修改如下:

3.5.2、解決登錄之后跳轉到商城首頁的問題
這里訪問訂單確認頁面,需要登錄,登錄之后應當跳轉到訂單確認頁面,不應該回到商城首頁。
下面是正常的流程圖:

在taotao-sso-web的login.jsp,我們可以看到一個叫做redirectUrl的變量,如果redirectUrl不為空,就會location.href=redirectUrl,
跳轉到這個redirectUrl,所以我們可以在攔截器攔截未登錄用戶跳轉http://localhost:8088/page/login時,設置一個參數redirect=http://localhost:8092/order/order-cart.html
比如:
http://localhost:8088/page/login?redirect=http://localhost:8092/order/order-cart.html
然后在taotao-sso-web的Controller中將redirect接收,並放到Model中。

修改taotao-order-web的未登錄用戶攔截器,在跳轉/page/login時加上參數redirect=url
這個地方是request.getRequestURL()不是request.getRequestURI(),小心看走眼!!!
request.getRequestURL() 獲取當前請求的全名,包括協議、域名、ip、端口等。
request.getRequestURI() 只能獲取端口后的相對路徑。

在taotao-sso-web系統中的Controller中接收參數redirect,並放到Model中:

3.5.3、訪問測試
特別注意:要安裝taota-sso工程和taota-order工程,然后重啟所有工程
,詳情省略。。。
4、實現提交訂單功能
用戶在購物車點擊【去結算】按鈕后,再點擊【提交訂單】按鈕,會創建一個訂單,如下:

4.1、數據庫表分析
在數據庫中涉及到三張表:tb_order、tb_order_item、tb_order_shipping
在tb_order表中
CREATE TABLE `tb_order` (
`order_id` varchar(50) COLLATE utf8_bin NOT NULL DEFAULT '' COMMENT '訂單id',
`payment` varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT '實付金額。精確到2位小數;單位:元。如:200.07,表示:200元7分',
`payment_type` int(2) DEFAULT NULL COMMENT '支付類型,1、在線支付,2、貨到付款',
`post_fee` varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT '郵費。精確到2位小數;單位:元。如:200.07,表示:200元7分',
`status` int(10) DEFAULT NULL COMMENT '狀態:1、未付款,2、已付款,3、未發貨,4、已發貨,5、交易成功,6、交易關閉',
`create_time` datetime DEFAULT NULL COMMENT '訂單創建時間',
`update_time` datetime DEFAULT NULL COMMENT '訂單更新時間',
`payment_time` datetime DEFAULT NULL COMMENT '付款時間',
`consign_time` datetime DEFAULT NULL COMMENT '發貨時間',
`end_time` datetime DEFAULT NULL COMMENT '交易完成時間',
`close_time` datetime DEFAULT NULL COMMENT '交易關閉時間',
`shipping_name` varchar(20) COLLATE utf8_bin DEFAULT NULL COMMENT '物流名稱',
`shipping_code` varchar(20) COLLATE utf8_bin DEFAULT NULL COMMENT '物流單號',
`user_id` bigint(20) DEFAULT NULL COMMENT '用戶id',
`buyer_message` varchar(100) COLLATE utf8_bin DEFAULT NULL COMMENT '買家留言',
`buyer_nick` varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT '買家昵稱',
`buyer_rate` int(2) DEFAULT NULL COMMENT '買家是否已經評價',
PRIMARY KEY (`order_id`),
KEY `create_time` (`create_time`),
KEY `buyer_nick` (`buyer_nick`),
KEY `status` (`status`),
KEY `payment_type` (`payment_type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
詳解如下:
主鍵order_id是字符串類型,不是自增長的,因此我們需要自己生成訂單編號,我們平時使用京東、天貓等購物網站,發現人家的訂單號都是用`數字`組成的,我們也使用數字作為訂單號,但是怎樣才能使訂單號不重復呢?用`時間加隨機數`的方案生成的訂單其實還是可能會重復的,當同一時刻生成的訂單越多越有可能出現訂單號一樣的情況,因此我們不能使用這種方案。比較好的方案是什么呢?是`用redis的incr方法`,由於redis中每一個操作都是`單線程`的,所以每一個操作都是具有`原子性`的,因此不會出現編號重復的問題。
payment 字段是實付金額,需要從前台傳過來,`保留小數點后2位`。
payment_type 是支付類型,分為在線支付和貨到付款,也需要從前台頁面傳過來。
post_free 字段是郵費,郵費得由前台傳過來,因為很多電商都搞活動,買夠多少錢的東西就免郵費,因此`郵費是動態變化`的。
status 字段是訂單狀態,訂單狀態我們暫且定義了6種狀態,未付款、已付款、未發貨、已發貨、交易成功、交易關閉。
create_time 字段是訂單創建時間,這沒什么可說的。
update_time 字段是訂單更新時間,這個通常是訂單狀態發生了變化。
payment_time 字段是付款時間。
consign_time 字段是發貨時間。
end_time 字段是交易完成時間,這個通常是用戶點確認收貨的時間。
close_time 字段是交易關閉時間,交易關閉時間則是該訂單的所有流程都走完后的時間。
shipping_name 字段是物流名稱,即用的誰家的快遞。
shipping_code 字段是物流單號,這個不用廢話。
user_id 字段當然是指`購買者ID`。
buyer_message 字段是指買家留言。
buyer_nick 字段指`買家昵稱`。
buyer_rate 字段記錄買家是否已經評價。
表中還可以看到create_time、buyer_nick、status、payment_type這四個字段由key修飾,說明為這四個字段建立了索引。
設計要求:
1、訂單號需要手動生成。
2、要求訂單號不能重復。
3、訂單號可讀性好。
4、訂單號不能太長。20位。
5、可以使用`redis的incr命令生成訂單號`。訂單號需要一個`初始值`。
在tb_order_item表中
CREATE TABLE `tb_order_item` (
`id` varchar(20) COLLATE utf8_bin NOT NULL,
`item_id` varchar(50) COLLATE utf8_bin NOT NULL COMMENT '商品id',
`order_id` varchar(50) COLLATE utf8_bin NOT NULL COMMENT '訂單id',
`num` int(10) DEFAULT NULL COMMENT '商品購買數量',
`title` varchar(200) COLLATE utf8_bin DEFAULT NULL COMMENT '商品標題',
`price` bigint(50) DEFAULT NULL COMMENT '商品單價',
`total_fee` bigint(50) DEFAULT NULL COMMENT '商品總金額',
`pic_path` varchar(200) COLLATE utf8_bin DEFAULT NULL COMMENT '商品圖片地址',
PRIMARY KEY (`id`),
KEY `item_id` (`item_id`),
KEY `order_id` (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
詳解如下:
從訂單表中可以看到訂單表中並沒有購買`商品詳情信息`,那么商品詳情信息在哪兒存放呢?
答:它被存放到了tb_order_item表中,主鍵id字段也是個字符串,我們也需要為其生成主鍵,同樣使用`redis的incr`。
設計要求:
1、展示商品的數據,`增加字段冗余`(目的:`減少關聯查詢,提升查詢性能`)。
2、逆規范化與反三范式參考鏈接:https://blog.csdn.net/liuyifeng1920/article/details/54136385
在tb_order_shipping表中
CREATE TABLE `tb_order_shipping` (
`order_id` varchar(50) NOT NULL COMMENT '訂單ID',
`receiver_name` varchar(20) DEFAULT NULL COMMENT '收貨人全名',
`receiver_phone` varchar(20) DEFAULT NULL COMMENT '固定電話',
`receiver_mobile` varchar(30) DEFAULT NULL COMMENT '移動電話',
`receiver_state` varchar(10) DEFAULT NULL COMMENT '省份',
`receiver_city` varchar(10) DEFAULT NULL COMMENT '城市',
`receiver_district` varchar(20) DEFAULT NULL COMMENT '區/縣',
`receiver_address` varchar(200) DEFAULT NULL COMMENT '收貨地址,如:xx路xx號',
`receiver_zip` varchar(6) DEFAULT NULL COMMENT '郵政編碼,如:310001',
`created` datetime DEFAULT NULL,
`updated` datetime DEFAULT NULL,
PRIMARY KEY (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
詳解如下:
這張表存放的是訂單物流信息,包括收貨人姓名、固定電話、移動電話、省、市、區/縣、街道門牌號、郵政編碼,而且收貨人信息與訂單是一對一的關系,因此收貨地址表的主鍵是order_id。
4.2、前端如何傳遞三張表的數據
點擊【提交訂單】按鈕時,會觸發$('#orderForm').submit()函數
,使用id選擇器
來得到表單,並且將該表單提交。

那么,表單在哪兒呢?我們搜索"orderForm",如下圖所示,可以看到這個表單所有的標簽都是
隱藏
的,是不會被用戶看到的,用戶看到的只是表單下面展示的信息(這些信息只是做展示用,不會被提交,真正提交的是被隱藏的表單)。表單要提交的話,我們一般
用pojo來接收
比較合適,那么這個表單我們應該用什么樣的pojo來接收呢?先來看看這個表單都有那些數據。
在這個表單中包含了
tb_order
、
tb_order_item
、
tb_order_shipping
三張表的數據,其中
紅色線框
起來的支付方式paymentType、支付金額payment屬於
tb_order
,
黑色線框
起來的商品信息屬於
tb_order_item
,
黃色線框
起來的訂單物流信息屬於
tb_order_shipping
表。這里暫時將
tb_order_shipping
表的數據寫死,支付類型也默認使用“1”。

綜合以上情況,我們來
寫個包裝的pojo類包含這些表單信息
,那么我們這個pojo應該放到哪兒比較合適呢?我們不能把它放到taotao-common當中,因為我們的pojo類要繼承TbOrder類,TbOrder類屬於taotao-manager-dao工程,即taotao-common工程要依賴taotao-manager-dao了,而taotao-manager-dao工程已經依賴了taotao-common工程,那么便成了
相互依賴
了,這是斷不可行的。我們還想該pojo類要
盡可能的共用
,則把它放到taotao-order-interface工程比較合適,因為taotao-order工程及taotao-order-web工程都依賴taotao-order-interface,因此把pojo寫到taotao-order-interface工程比較合適。
包裝pojo類如下所示,這里用到了一個技巧,那就是
繼承了TbOrder類
,這樣OrderInfo便直接擁有了
TbOrder的屬性
。即擴展TbOrder,在子類中添加兩個屬性一個是
商品明細列表
,一個是
配送信息表
。為了讓該pojo在網絡中傳輸,我們需要讓它
實現序列化接口
。
/**
* 訂單信息包裝類
* @author chenmingjun
* @date 2018年12月7日 下午9:10:56
* @version V1.0
*/
public class OrderInfo extends TbOrder implements Serializable {
private static final long serialVersionUID = 1L;
// 訂單明細表
private List<TbOrderItem> orderItems; // springmvc屬性綁定的命名要求:與頁面上的一致
// 訂單物流表
private TbOrderShipping orderShipping; // springmvc屬性綁定的命名要求:與頁面上的一致
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;
}
}
4.3、功能分析
URL: /order/create
參數:表單的數據(包含:訂單信息、訂單明細列表、訂單物流信息)
參數:提交的是表單的數據。保存的數據:訂單、訂單明細、配送地址。
a) 向tb_order中插入記錄。
i.訂單號需要手動生成。
要求訂單號不能重復。
訂單號可讀性好。
可以使用redis的incr命令生成訂單號。訂單號需要一個初始值。
ii.payment:表單數據
iii.payment_type:表單數據
iv.user_id:用戶信息
v.buyer_nick:用戶名
vi.其他字段null
b) 向tb_order_item訂單明細表中插入數據。
i.id:使用incr生成
ii.order_id:生成的訂單號
iii.其他的都是表單中的數據
c) 向tb_order_shipping訂單配送信息中插入數據。
i.order_id:生成的訂單號
ii.其他字段都是表單中的數據
d) 使用pojo接收表單的數據。
可以擴展TbOrder,在子類中添加兩個屬性一個是商品明細列表,一個是配送信息表。
把pojo放到taotao-order-interface工程中。
返回值:邏輯視圖(包含:訂單的ID)
業務邏輯:
1、接收表單的數據。
2、生成訂單ID。
3、向訂單表插入數據。(注意:不要忘記用戶ID設置)
4、向訂單明細表插入數據。
5、向訂單物流表插入數據。
6、返回TaotaoResult。(包含訂單的ID)
返回值:TaotaoResult 包含訂單ID
4.4、Dao層
既然表和接收表單的pojo都有了,代碼就好寫了。
直接使用逆向工程生成的代碼。
4.5、Service層
在taotao-order-interface創建接口:
/**
* 訂單管理的接口
* @author chenmingjun
* @date 2018年12月7日 下午10:57:07
* @version V1.0
*/
public interface OrderService {
/**
* 生成訂單
* @param orderInfo 包含了表單提交的所有數據
* @return
*/
TaotaoResult createOrder(OrderInfo orderInfo);
}
在taotao-order-service中創建實現類:
由於要操作redis所以需要jedis的依賴,在pom.xml中添加依賴:
<!-- 配置對Redis的Java客戶端jedis的依賴 -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
在taotao-order-service工程的/src/main/java中添加jedis的工具包,如下圖所示:

在taotao-order-service工程的/src/main/resources/spring/目錄下添加applicationContext-redis.xml配置文件。
代碼中還用到了常量,我們把常量放到配置文件中,如下所示:
#訂單號生成的key
ORDER_ID_GEN_KEY=ORDER_ID_GEN
#訂單號的初始值
ORDER_ID_INIT_VALUE=100888
#訂單明細表主鍵生成的key
ORDER_ITEM_ID_GEN_KEY=ORDER_ITEM_ID_GEN
還需要在taotao-order-service中applicationContext-dao.xml文件配置對resources.properties文件掃描,如下圖所示:

實現類代碼如下:
/**
* 訂單管理的Service
* @author chenmingjun
* @date 2018年12月7日 下午10:58:44
* @version V1.0
*/
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private JedisClient jedisClient;
@Autowired
private TbOrderMapper tbOrderMapper;
@Autowired
private TbOrderItemMapper tbOrderItemMapper;
@Autowired
private TbOrderShippingMapper tbOrderShippingMapper;
@Value("${ORDER_ID_GEN_KEY}")
private String ORDER_ID_GEN_KEY;
@Value("${ORDER_ID_INIT_VALUE}")
private String ORDER_ID_INIT_VALUE;
@Value("${ORDER_ITEM_ID_GEN_KEY}")
private String ORDER_ITEM_ID_GEN_KEY;
@Override
public TaotaoResult createOrder(OrderInfo orderInfo) {
// 1、接收表單的數據。
// 2、向訂單表插入數據。
if (!jedisClient.exists(ORDER_ID_GEN_KEY)) { // 說明訂單生成的key不存在
// 設置訂單號的初始值
jedisClient.set(ORDER_ID_GEN_KEY, ORDER_ID_INIT_VALUE);
}
// 2.1、生成訂單的ID。通過redis的incr來生成。
String orderId = jedisClient.incr(ORDER_ID_GEN_KEY).toString();
// 2.2、補全訂單表的其他屬性。
// 設置訂單id
orderInfo.setOrderId(orderId);
// 設置訂單狀態:1、未付款,2、已付款,3、未發貨,4、已發貨,5、交易成功,6、交易關閉
orderInfo.setStatus(1);
// 設置訂單郵費
orderInfo.setPostFee("0");
// 設置訂單創建日期
orderInfo.setCreateTime(new Date());
// 設置訂單更新日期
orderInfo.setUpdateTime(new Date());
// 在Controller中設置
// 設置買家昵稱
// orderInfo.setBuyerNick(buyerNick);
// 設置用戶id
// orderInfo.setUserId(userId);
tbOrderMapper.insert(orderInfo);
// 3、向訂單明細表插入數據。
List<TbOrderItem> orderItems = orderInfo.getOrderItems();
for (TbOrderItem tbOrderItem : orderItems) {
// 3.1、生成訂單明細表的ID。通過redis的incr來生成。
String orderItemId = jedisClient.incr(ORDER_ITEM_ID_GEN_KEY).toString();
// 3.2、補全訂單明細的其他屬性。
// 設置訂單明細id
tbOrderItem.setId(orderItemId);
// 設置訂單明細所屬訂單id
tbOrderItem.setOrderId(orderId);
tbOrderItemMapper.insert(tbOrderItem);
}
// 4、向訂單物流表插入數據。
TbOrderShipping orderShipping = orderInfo.getOrderShipping();
// 4.1、設置訂單的ID。
orderShipping.setOrderId(orderId);
// 4.2、補全訂單物流表的其他屬性。
orderShipping.setCreated(new Date());
orderShipping.setUpdated(new Date());
tbOrderShippingMapper.insert(orderShipping);
// 5、返回TaotaoResult。(要包含訂單的ID)
return TaotaoResult.ok(orderId);
}
}
4.6、發布服務
在applicationContext-service.xml發布服務:

4.7、引用服務
在springmvc.xml中引入服務:

4.8、Controller
請求的url:/order/create
參數:使用OrderInfo接收
返回值:邏輯視圖。(頁面應該顯示訂單號)
業務邏輯:
1、接收表單提交的數據OrderInfo。
2、補全用戶信息。
3、調用Service創建訂單。
4、返回邏輯視圖展示訂單提交成功頁面:
4.1、需要Service返回訂單號。
4.2、當前日期加三天。
第一步:首先加入時間操作組件的依賴:已經在taotao-common中依賴。
<!-- 時間操作組件 -->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
</dependency>
Controller代碼如下:
@RequestMapping(value="/order/create", method=RequestMethod.POST)
public String createOrder(OrderInfo orderInfo, HttpServletRequest request) {
// 0、引入服務,注入服務
// 1、接收表單提交的數據OrderInfo。
// 從request域中獲取登錄的用戶信息。
TbUser tbUser = (TbUser) request.getAttribute("USER_INFO");
// 2、補全OrderInfo信息。
orderInfo.setUserId(tbUser.getId());
orderInfo.setBuyerNick(tbUser.getUsername());
// 3、調用Service創建訂單。
TaotaoResult result = orderService.createOrder(orderInfo);
// 4、返回邏輯視圖展示訂單提交成功頁面
// 4.1、需要Service返回訂單號。
request.setAttribute("orderId", result.getData().toString());
request.setAttribute("payment", orderInfo.getPayment());
// 4.2、返回當前日期加三天。
DateTime dateTime = new DateTime();
dateTime = dateTime.plusDays(3);
request.setAttribute("date", dateTime.toString("yyyy-MM-dd"));
return "success";
}
4.9、訪問測試
安裝taotao-order,啟動,用戶在購物車點擊去結算,點擊提交訂單,會創建一個訂單,查看數據庫中已存在數據。
5、參考文章
本文參考:
https://blog.csdn.net/pdsu161530247/article/details/82312620
https://blog.csdn.net/u012453843/article/details/73368967