day80_淘淘商城項目_13_訂單系統搭建 + 展現訂單確認頁面 + 用戶身份認證(SpringMVC攔截器) + 實現提交訂單功能_匠心筆記

課程計劃javascript

  • 一、訂單系統搭建
  • 二、訂單確認頁面展現
  • 三、用戶認證
  • 四、建立訂單
  • 五、建立訂單成功後顯示訂單號

一、訂單系統搭建

1.一、功能分析

一、在購物車頁面點擊【去結算】按鈕跳轉到訂單確認頁面。
  a) 展現商品列表
  b) 配送地址列表
  c) 選擇支付方式
二、展現訂單確認頁面以前,應該確認用戶身份。
  a) 使用攔截器實現。
  b) Cookie中取token。
  c) 取不到token跳轉到登陸頁面。
  d) 取到token,根據token查詢用戶信息。
  e) 若是沒有用戶信息,登陸過時跳轉到登陸頁面。
  f) 取到用戶信息,放行。
三、提交訂單
  a) 生成訂單。
  b) 展現訂單提交成功頁面。php

1.二、工程搭建

1.2.一、建立訂單服務層工程

  taotao-order(pom)
    |--taotao-order-interface(jar)
    |--taotao-order-service(war)
taotao-ordercss


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-interfacehtml


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整合。java


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.二、建立訂單表現層工程

表現層工程處理訂單的確認頁面和訂單提交後產生的訂單號
taotao-order-web打包方式war。git


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整合。github

二、展現訂單確認頁面

2.一、導入靜態頁面


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

2.二、功能分析

在購物車表現層工程中的,有咱們訂單確認頁面的URL:
/taotao-cart-web/src/main/webapp/WEB-INF/jsp/cart.jspweb


因爲訂單確認頁面展現在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),展現訂單確認頁面。
業務邏輯:
  一、首先用戶先登陸,從cookie中獲取token,根據token調用SSO服務獲取登陸的用戶信息。
  二、從cookie中獲取購物車的商品的列表數據。
  三、從redis中獲取該用戶的購物車的商品列表數據。
  四、將這二者的數據進行合併,展現商品數據。並清除cookie中的數據。
  五、展現配送地址列表,須要根據用戶id從數據庫中查詢收貨地址列表,這裏暫時使用靜態數據。
  六、展現支付方式,也須要從數據庫中查詢支付的方式列表,這裏暫時使用靜態數據。redis

2.三、Dao層、Service層(無)

  須要根據用戶id查詢收貨地址列表。
  須要查詢支付方式。
  可是因爲這兩個都使用的靜態數據,因此服務層不須要編寫。
  靜態數據,作靜態頁面處理。
  業務邏輯:
    直接調用其餘的服務層的服務便可。(須要添加對應的依賴,並引用服務。)

2.四、發佈服務

  因爲表現層須要調服務層的SSO服務和購物車服務,因此須要在taotao-sso-service和taotao-cart-service工程中發佈服務。

2.五、引用服務

  在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.六、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) {
        // 一、從cookie中獲取token
        String token = CookieUtils.getCookieValue(request, COOKIE_TOKEN_KEY);
        // 二、根據token調用SSO服務獲取登陸的用戶信息,查看用戶是否已通過期
        TbUser tbUser = null;
        if (StringUtils.isNotBlank(token)) {
            TaotaoResult result = userLoginService.getUserByToken(token);
            if (result.getStatus() == 200) {
                tbUser = (TbUser) result.getData();
            }
        }
        // 三、用戶必須登陸才展現訂單頁面
        // 四、展現用戶的配送地址列表,根據用戶id從數據庫中查詢收貨地址列表,這裏暫時使用靜態數據
        // 五、展現支付方式列表,從數據庫中查詢支付的方式列表,這裏暫時使用靜態數據
        // 六、從cookie中獲取購物車的商品列表
        List<TbItem> cartList2 = getCartListFromCookie(request);
        // 七、調用購物車服務從redis數據庫中獲取購物車的商品列表
        List<TbItem> cartList1 = cartService.queryCartListByUserId(tbUser == null ? -1 : tbUser.getId());
        // 八、合併數據到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());
            }
        }
        // 九、合併數據後刪除cookie
        if (!cartList2.isEmpty()) {
            CookieUtils.deleteCookie(request, response, COOKIE_CART_KEY);
        }
        // 十、再次調用購物車服務從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) {
        // 一、從cookie中獲取商品列表字符串
        String cartJson = CookieUtils.getCookieValue(request, COOKIE_CART_KEY, true);
        // 二、將字符串轉換成java對象
        List<TbItem> list = new ArrayList<>();
        if (StringUtils.isNotBlank(cartJson)) {
            list = JsonUtils.jsonToList(cartJson, TbItem.class);
        }
        return list;
    }

屬性文件內容以下:

2.七、訪問測試

  首先安裝taotao-order工程,再啓動taotao-order-web。須要先登陸,再去訪問。

三、用戶身份認證

  在展現訂單確認頁面以前,須要對用戶身份進行認證,要求用戶必須是登陸裝態。

3.一、功能分析

一、使用springmvc的攔截器實現。須要一個實現類實現HandlerInterceptor接口。
二、業務邏輯
  a) 從cookie中取token。
  b) 沒有token,須要跳轉到登陸頁面。
  c) 有token,調用sso系統的服務,根據token查詢用戶信息。
  d) 若是查不到用戶信息、用戶登陸已通過期。須要跳轉到登陸頁面。
  e) 查詢到用戶信息,放行。
三、在springmvc.xml中配置攔截器。

3.二、攔截器實現


攔截器代碼以下:
/**
 * 判斷用戶是否登陸的攔截器
 * @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 
{
        // 一、從cookie中獲取token
        String token = CookieUtils.getCookieValue(request, COOKIE_TOKEN_KEY);
        // 二、判斷token是否存在
        if (StringUtils.isEmpty(token)) {
            // 三、若是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;
        }
        // 四、若是token存在,則調用SSO服務判斷用戶信息,查看用戶登陸是否已通過期(加入依賴,引入服務,注入服務,使用)
        TaotaoResult result = userLoginService.getUserByToken(token);
        if (result.getStatus() != 200) {
            // 五、若是用戶登陸已過時,重定向到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;
        }
        // 六、用戶登陸且沒過時,放行
        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.三、配置攔截器

在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.四、訪問測試

  訪問地址:http://localhost:8092/order/order-cart.html
  發現被攔截後,重定向到登錄界面,登陸成功後,跳轉到商城首頁了。

3.五、實現攔截器後存在的問題

存在兩個問題:
  一、SSO服務被調用了兩次,先在攔截器類中的handler方法中調用了一次,而後進入Controller類中的目標方法獲取用戶信息時又調用了一次。
  二、登陸成功後跳轉到了商城首頁,應當跳轉到訂單確認頁面纔對。

3.5.一、解決調用SSO服務兩次的問題

實際上,只須要在攔截器類中的handler方法中調用了一次即可以了,調用以後,將用戶信息的數據存放在request域中,等進入目標的方法後,能夠直接經過request域獲取用戶信息。
在攔截器中,將登陸的用戶信息放到request域中:
攔截器類的代碼修改以下:


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

3.5.二、解決登陸以後跳轉到商城首頁的問題

這裏訪問訂單確認頁面,須要登陸,登陸以後應當跳轉到訂單確認頁面,不該該回到商城首頁。
下面是正常的流程圖:


在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.三、訪問測試

  特別注意:要安裝taota-sso工程和taota-order工程,而後重啓全部工程,詳情省略。。。

四、實現提交訂單功能

用戶在購物車點擊【去結算】按鈕後,再點擊【提交訂單】按鈕,會建立一個訂單,以下:

4.一、數據庫表分析

在數據庫中涉及到三張表:tb_order、tb_order_item、tb_order_shipping
在tb_order表中

CREATE TABLE `tb_order` (
  `order_id` varchar(50COLLATE utf8_bin NOT NULL DEFAULT '' COMMENT '訂單id',
  `payment` varchar(50COLLATE utf8_bin DEFAULT NULL COMMENT '實付金額。精確到2位小數;單位:元。如:200.07,表示:200元7分',
  `payment_type` int(2DEFAULT NULL COMMENT '支付類型,一、在線支付,二、貨到付款',
  `post_fee` varchar(50COLLATE utf8_bin DEFAULT NULL COMMENT '郵費。精確到2位小數;單位:元。如:200.07,表示:200元7分',
  `status` int(10DEFAULT NULL COMMENT '狀態:一、未付款,二、已付款,三、未發貨,四、已發貨,五、交易成功,六、交易關閉',
  `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(20COLLATE utf8_bin DEFAULT NULL COMMENT '物流名稱',
  `shipping_code` varchar(20COLLATE utf8_bin DEFAULT NULL COMMENT '物流單號',
  `user_id` bigint(20DEFAULT NULL COMMENT '用戶id',
  `buyer_message` varchar(100COLLATE utf8_bin DEFAULT NULL COMMENT '買家留言',
  `buyer_nick` varchar(50COLLATE utf8_bin DEFAULT NULL COMMENT '買家暱稱',
  `buyer_rate` int(2DEFAULT 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(20COLLATE utf8_bin NOT NULL,
  `item_id` varchar(50COLLATE utf8_bin NOT NULL COMMENT '商品id',
  `order_id` varchar(50COLLATE utf8_bin NOT NULL COMMENT '訂單id',
  `num` int(10DEFAULT NULL COMMENT '商品購買數量',
  `title` varchar(200COLLATE utf8_bin DEFAULT NULL COMMENT '商品標題',
  `price` bigint(50DEFAULT NULL COMMENT '商品單價',
  `total_fee` bigint(50DEFAULT NULL COMMENT '商品總金額',
  `pic_path` varchar(200COLLATE 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(50NOT NULL COMMENT '訂單ID',
  `receiver_name` varchar(20DEFAULT NULL COMMENT '收貨人全名',
  `receiver_phone` varchar(20DEFAULT NULL COMMENT '固定電話',
  `receiver_mobile` varchar(30DEFAULT NULL COMMENT '移動電話',
  `receiver_state` varchar(10DEFAULT NULL COMMENT '省份',
  `receiver_city` varchar(10DEFAULT NULL COMMENT '城市',
  `receiver_district` varchar(20DEFAULT NULL COMMENT '區/縣',
  `receiver_address` varchar(200DEFAULT NULL COMMENT '收貨地址,如:xx路xx號',
  `receiver_zip` varchar(6DEFAULT NULL COMMENT '郵政編碼,如:310001',
  `created` datetime DEFAULT NULL,
  `updated` datetime DEFAULT NULL,
  PRIMARY KEY (`order_id`)
ENGINE=InnoDB DEFAULT CHARSET=utf8;

詳解以下:

    這張表存放的是訂單物流信息,包括收貨人姓名、固定電話、移動電話、省、市、區/縣、街道門牌號、郵政編碼,並且收貨人信息與訂單是一對一的關係,所以收貨地址表的主鍵是order_id。

4.二、前端如何傳遞三張表的數據

  點擊【提交訂單】按鈕時,會觸發$('#orderForm').submit()函數,使用id選擇器來獲得表單,而且將該表單提交。


  那麼,表單在哪兒呢?咱們搜索"orderForm",以下圖所示,能夠看到這個表單全部的標籤都是 隱藏的,是不會被用戶看到的,用戶看到的只是表單下面展現的信息(這些信息只是作展現用,不會被提交,真正提交的是被隱藏的表單)。表單要提交的話,咱們通常 用pojo來接收比較合適,那麼這個表單咱們應該用什麼樣的pojo來接收呢?先來看看這個表單都有那些數據。
  在這個表單中包含了 tb_ordertb_order_itemtb_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.三、功能分析

URL: /order/create
參數:表單的數據(包含:訂單信息、訂單明細列表、訂單物流信息)

參數:提交的是表單的數據。保存的數據:訂單、訂單明細、配送地址。
a) 向tb_order中插入記錄。
    i.訂單號須要手動生成。
        要求訂單號不能重複。
        訂單號可讀性好。
        可使用redisincr命令生成訂單號。訂單號須要一個初始值。
    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)
業務邏輯:
  一、接收表單的數據。
  二、生成訂單ID。
  三、向訂單表插入數據。(注意:不要忘記用戶ID設置)
  四、向訂單明細表插入數據。
  五、向訂單物流表插入數據。
  六、返回TaotaoResult。(包含訂單的ID)

返回值:TaotaoResult 包含訂單ID

4.四、Dao層

  既然表和接收表單的pojo都有了,代碼就好寫了。
  直接使用逆向工程生成的代碼。

4.五、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) {
        // 一、接收表單的數據。
        // 二、向訂單表插入數據。
        if (!jedisClient.exists(ORDER_ID_GEN_KEY)) { // 說明訂單生成的key不存在
            // 設置訂單號的初始值
            jedisClient.set(ORDER_ID_GEN_KEY, ORDER_ID_INIT_VALUE);
        }
        // 2.一、生成訂單的ID。經過redis的incr來生成。
        String orderId = jedisClient.incr(ORDER_ID_GEN_KEY).toString();
        // 2.二、補全訂單表的其餘屬性。
        // 設置訂單id
        orderInfo.setOrderId(orderId);
        // 設置訂單狀態:一、未付款,二、已付款,三、未發貨,四、已發貨,五、交易成功,六、交易關閉
        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);

        // 三、向訂單明細表插入數據。
        List<TbOrderItem> orderItems = orderInfo.getOrderItems();
        for (TbOrderItem tbOrderItem : orderItems) {
            // 3.一、生成訂單明細表的ID。經過redis的incr來生成。
            String orderItemId = jedisClient.incr(ORDER_ITEM_ID_GEN_KEY).toString();
            // 3.二、補全訂單明細的其餘屬性。
            // 設置訂單明細id
            tbOrderItem.setId(orderItemId);
            // 設置訂單明細所屬訂單id
            tbOrderItem.setOrderId(orderId);
            tbOrderItemMapper.insert(tbOrderItem);
        }

        // 四、向訂單物流表插入數據。
        TbOrderShipping orderShipping = orderInfo.getOrderShipping();
        // 4.一、設置訂單的ID。
        orderShipping.setOrderId(orderId);
        // 4.二、補全訂單物流表的其餘屬性。
        orderShipping.setCreated(new Date());
        orderShipping.setUpdated(new Date());
        tbOrderShippingMapper.insert(orderShipping);

        // 五、返回TaotaoResult。(要包含訂單的ID)
        return TaotaoResult.ok(orderId);
    }
}

4.六、發佈服務

在applicationContext-service.xml發佈服務:

4.七、引用服務

在springmvc.xml中引入服務:

4.八、Controller

請求的url:/order/create
參數:使用OrderInfo接收
返回值:邏輯視圖。(頁面應該顯示訂單號)
業務邏輯:
  一、接收表單提交的數據OrderInfo。
  二、補全用戶信息。
  三、調用Service建立訂單。
  四、返回邏輯視圖展現訂單提交成功頁面:
    4.一、須要Service返回訂單號。
    4.二、當前日期加三天。
第一步:首先加入時間操做組件的依賴:已經在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、引入服務,注入服務
        // 一、接收表單提交的數據OrderInfo。
        // 從request域中獲取登陸的用戶信息。
        TbUser tbUser = (TbUser) request.getAttribute("USER_INFO");
        // 二、補全OrderInfo信息。
        orderInfo.setUserId(tbUser.getId());
        orderInfo.setBuyerNick(tbUser.getUsername());
        // 三、調用Service建立訂單。
        TaotaoResult result = orderService.createOrder(orderInfo);
        // 四、返回邏輯視圖展現訂單提交成功頁面
        // 4.一、須要Service返回訂單號。
        request.setAttribute("orderId", result.getData().toString());
        request.setAttribute("payment", orderInfo.getPayment());
        // 4.二、返回當前日期加三天。
        DateTime dateTime = new DateTime();
        dateTime = dateTime.plusDays(3);
        request.setAttribute("date", dateTime.toString("yyyy-MM-dd"));
        return "success";
    }

4.九、訪問測試

  安裝taotao-order,啓動,用戶在購物車點擊去結算,點擊提交訂單,會建立一個訂單,查看數據庫中已存在數據。

五、參考文章

本文參考:  https://blog.csdn.net/pdsu161530247/article/details/82312620  https://blog.csdn.net/u012453843/article/details/73368967

相關文章
相關標籤/搜索