第二十四篇:訂單系統(tǒng)的實現(xiàn)

前言:
我們已經(jīng)實現(xiàn)了加入購物車的功能,加入購物車以后悉默,我們下一步一般都是形成訂單準(zhǔn)備付款啦;那么如何形成訂單呢苟穆?下面就實現(xiàn)訂單系統(tǒng)

1.訂單系統(tǒng)的搭建

先來分析一波抄课,我們可以看到訂單系統(tǒng)和訂單服務(wù)是分開來的,即我們要建一個服務(wù)工程和一個前臺工程雳旅;

image.png

建立的過程可以參考taotao-manager和taotao-manager-web
image.png

具體的一些web.xml跟磨,pom.xml,還有mybatis等配置可以參考工程里面的~這里就不貼出來了
git傳送門
https://github.com/AslanYJ/shopping.git

2.實現(xiàn)登錄攔截器

搭建完工程后攒盈,我們知道京東的話雖然可以加入購物車但是在結(jié)算的時候是一定要登錄抵拘,也就是說購物車列表頁面直接跳轉(zhuǎn)到登錄,這里要用到攔截器
攔截器我們采取Springmvc提供的攔截器

image.png

下面貼出邏輯代碼

package com.taotao.order.interceptor;

import com.taotao.common.pojo.TaotaoResult;
import com.taotao.pojo.TbUser;
import com.taotao.sso.service.UserService;
import com.taotao.utils.CookieUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class LoginInterceptor implements HandlerInterceptor {
    @Value("${TOKEN_KEY}")
    private String TOKEN_KEY;
    @Value("${SSO_URL}")
    private String SSO_URL;
    @Autowired
    private UserService userService;
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        //執(zhí)行Handler之前執(zhí)行此方法型豁,攔截請求讓用戶登錄就在這個方法攔截
        //1.從cookie中取token僵蛛。
        String token = CookieUtils.getCookieValue(httpServletRequest,TOKEN_KEY);
        //2.沒有token尚蝌,需要跳轉(zhuǎn)到登錄頁面。
        if(StringUtils.isBlank(token)) {
            //取當(dāng)前請求的url
            String url = httpServletRequest.getRequestURL().toString();
            //跳轉(zhuǎn)到登錄頁面充尉,用redirect比較合適飘言,登錄之后還要回到當(dāng)前頁面,因此要在請求url中添加一個回調(diào)地址
            httpServletResponse.sendRedirect(SSO_URL + "/page/login?url=" + url);
            //由于沒有登錄驼侠,攔截
            return false;
        }
        //3.有token姿鸿。調(diào)用sso系統(tǒng)的服務(wù),根據(jù)token查詢用戶信息泪电。
        TaotaoResult taotaoResult = userService.getUserMessageByToken(token);
        //4.如果查不到用戶信息般妙。用戶登錄已經(jīng)過期纪铺。需要跳轉(zhuǎn)到登錄頁面相速。
        if(taotaoResult.getStatus() != 200) {
            //取當(dāng)前請求的url
            String url = httpServletRequest.getRequestURL().toString();
            //跳轉(zhuǎn)到登錄頁面,用redirect比較合適鲜锚,登錄之后還要回到當(dāng)前頁面突诬,因此要在請求url中添加一個回調(diào)地址
            httpServletResponse.sendRedirect(SSO_URL + "/page/login?url=" + url);
            //由于沒有登錄,攔截
            return false;
        }
        TbUser user = (TbUser) taotaoResult.getData();
        httpServletRequest.setAttribute("user",user);
        //5.查詢到用戶信息芜繁。放行
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
        // handler執(zhí)行之后旺隙,modelAndView返回之前,可以對返回值進(jìn)行處理
    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
        // 在ModelAndView返回之后骏令,這時只能做些異常處理了
    }
}

用到的常量在配置文件里面配置

image.png

記得配置在springmvc.xml里面加載攔截器和攔截器的請求
image.png

3.訂單展示確認(rèn)界面

  • 功能分析
    我們在購物的界面里面cart.jsp中可以看到請求的url蔬捷,根據(jù)這個請求的地址我們可以寫個Controller


    image.png
  • Controller
    請求的url:/order/order-cart
    參數(shù):無
    業(yè)務(wù)邏輯:
    從cookie中取商品列表展示到頁面。
    返回值:邏輯視圖榔袋。

@Controller
public class OrderController {
    
    @Value("${TT_CART}")
    private String TT_CART;

    /**
     * 展示訂單確認(rèn)頁面周拐。
     * <p>Title: showOrderCart</p>
     * <p>Description: </p>
     * @return
     */
    @RequestMapping("/order/order-cart")
    public String showOrderCart(HttpServletRequest request) {
        //取用戶id
        //從cookie中取token,然后根據(jù)token查詢用戶信息凰兑。需要調(diào)用sso系統(tǒng)的服務(wù)妥粟。
        //根據(jù)用戶id查詢收貨地址列表
        //從cookie中取商品列表
        List<TbItem> cartList = getCartList(request);
        //傳遞給頁面
        request.setAttribute("cartList", cartList);
        //返回邏輯視圖
        return "order-cart";
    }
    
    /**
     * 從cookie中取購物車列表
     * <p>Title: getCartList</p>
     * <p>Description: </p>
     * @param request
     * @return
     */
    private List<TbItem> getCartList(HttpServletRequest request) {
        //取購物車列表
        String json = CookieUtils.getCookieValue(request, TT_CART, true);
        //判斷json是否為null
        if (StringUtils.isNotBlank(json)) {
            //把json轉(zhuǎn)換成商品列表返回
            List<TbItem> list = JsonUtils.jsonToList(json, TbItem.class);
            return list;
        }
        return new ArrayList<>();
    }
}

但是測試的時候會跳回主頁,這不是我們想要的吏够,不信你試試~
如果是的話勾给,請往下看!
在taotao-sso-web中可以看到這一段代碼锅知;js首先會去嘗試獲取從Controller端傳過來的回調(diào)地址播急,如果取到了回調(diào)地址,那么登錄成功后會跳轉(zhuǎn)到回調(diào)地址售睹,如果沒有取到回調(diào)地址旅择,那么登錄成功后直接訪問的便是淘淘商城首頁,在上圖中之所以我登錄成功后訪問到的是淘淘商城首頁就是因為我們沒有在PageController 當(dāng)中添加回調(diào)地址侣姆。


image.png
image.png

那么我們改造一下PageController

@Controller
public class PageController {


    @RequestMapping("/page/register")
    public String showRegister(){
        return "register";
    }

    @RequestMapping("/page/login")
    public String showLogin(String url, Model model){
        model.addAttribute("redirect",url);
        return "login";
    }
}

OK.跳轉(zhuǎn)就完成了生真!

4.生產(chǎn)訂單

  • 訂單數(shù)據(jù)庫分析
    我們可以看到訂單的生產(chǎn)主要涉及三張表沉噩,一張是存訂單的主要信息,一張是存儲訂單的明細(xì)表柱蟀,一張是訂單的收貨人地址的表川蒙;
    我們先來看下tb_order表,如下圖所示长已,可以看到畜眨,主鍵order_id是字符串類型,不是自增長的术瓮,因此我們需要自己生成訂單編號康聂,我們平時使用京東、天貓等購物網(wǎng)站胞四,發(fā)現(xiàn)人家的訂單號都是用數(shù)字組成的恬汁,我們也使用數(shù)字作為訂單號,但是怎樣才能使訂單號不重復(fù)呢辜伟?用時間加隨機(jī)數(shù)的方案生成的訂單其實還是可能會重復(fù)的氓侧,當(dāng)同一時刻生成的訂單越多越有可能出現(xiàn)訂單號一樣的情況,因此我們不能使用這種方案导狡。比較好的方案是什么呢约巷?是用redis的incr方法,由于redis是單線程的旱捧,因此無論多少個線程共同訪問也不會出現(xiàn)訂單編號一樣的情況独郎。payment字段是實付金額,需要從前臺傳過來枚赡,保留小數(shù)點后2位氓癌,payment_type是支付類型,分為在線支付和貨到付款标锄,也需要從前臺頁面?zhèn)鬟^來顽铸,post_free字段是郵費,郵費得由前臺傳過來料皇,因為很多電商都搞活動谓松,買夠多少錢的東西就免郵費,因此郵費是動態(tài)變化的践剂。status字段是訂單狀態(tài)鬼譬,訂單狀態(tài)我們暫且定義了6種狀態(tài),未付款逊脯、已付款优质、未發(fā)貨、已發(fā)貨、交易成功巩螃、交易關(guān)閉演怎。create_time字段是訂單創(chuàng)建時間,這沒什么可說的避乏,update_time字段是訂單更新時間爷耀,這個通常是訂單狀態(tài)發(fā)生了變化,payment_time字段是付款時間拍皮,consign_time字段是發(fā)貨時間歹叮,end_time字段是交易完成時間,這個通常是用戶點確認(rèn)收貨的時間铆帽,交易關(guān)閉時間則是該訂單的所有流程都走完后的時間咆耿。shipping_name字段是物流名稱,即用的誰家的快遞爹橱。shipping_code字段是物流單號萨螺,這個不用廢話。user_id字段當(dāng)然是指購買者ID宅荤。buyer_message字段是指買家留言屑迂,buyer_nick字段指買家昵稱浸策。buyer_rate字段記錄買家是否已經(jīng)評價冯键。表中還可以看到create_time、buyer_nick庸汗、status惫确、payment_type四個字段由key修飾,說明為這四個字段建立了索引蚯舱。


    image.png

    可以看到訂單表中并沒有購買商品詳情信息改化,那么商品詳情信息在哪兒存放呢?它被存放到了tb_order_item表中枉昏,主鍵id字段也是個字符串陈肛,我們也需要為其生成主鍵,不過我倒是覺得兄裂,如果id用Long類型并且主鍵自增長會更好點句旱。如下圖所示


    image.png

    接著我們看tb_order_shipping,這張表存放的是用戶的收貨信息晰奖,包括收貨人姓名谈撒、固定電話、移動電話匾南、省、市、區(qū)/縣夹厌、街道門牌號裆悄、郵政編碼尊流,而且收貨人信息與訂單是一對一的,因此收貨地址表的主鍵是order_id崖技。
    image.png
  • 訂單生成功能分析
    生成訂單是在訂單確認(rèn)頁面進(jìn)行的钟哥,如下圖所示迎献,可以看到"提交訂單"按鈕。


    image.png

    我們找到這個頁面對應(yīng)的jsp文件腻贰,那就是order-cart.jsp,搜索"提交訂單"冀瓦,可以看到如下圖所示搜索結(jié)果写烤,可以看到這是個button按鈕,該按鈕的onclick事件中使用id選擇器來得到表單感局,并且將該表單提交暂衡。

image.png

可以看到表單的內(nèi)容是隱藏的。我們可以根據(jù)表單的內(nèi)容撑毛,來用一個對象來存儲


image.png

我們分析下上圖的表單藻雌,這個表單中包含了三張表的信息疹吃,其中<input type="hidden" name="paymentType" value="1"/>便是tb_order表中的付款類型字段,這里默認(rèn)是1了歉摧,<c:forEach>遍歷的是購物車列表,var="cart"表示單個購物車對象再悼,varStatus="status"的用法如下所示

     varStatus屬性可以方便我們實現(xiàn)一些與行數(shù)相關(guān)的功能,如:奇數(shù)行膝但、偶數(shù)行差異;最后一行特殊處理等等跟束。先就varStatus屬性常用參數(shù)總結(jié)下:
${status.index}      輸出行號冀宴,從0開始。
${status.count}      輸出行號甚疟,從1開始逃延。
${status.current}   當(dāng)前這次迭代的(集合中的)項
${status.first}  判斷當(dāng)前項是否為集合中的第一項,返回值為true或false
${status.last}   判斷當(dāng)前項是否為集合中的最后一項讽膏,返回值為true或false
begin盔然、end愈案、step分別表示:起始序號鹅搪,結(jié)束序號,跳躍步伐恢准。

我們可以用一個List集合來表示商品列表的集合甫题,一個orderShipping來表示坠非;
我們在taotao-order-interface中存放這個對象,因為如果放common-utils的話盟迟,我們taotao-order已經(jīng)依賴了common-utils工程了,如果再依賴就變成了相互依賴迫皱,這是行不通的辖众!所以放taotao-order-interface中


image.png
public class OrderInfo extends TbOrder implements Serializable {

    //訂單商品表實體的集合
    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;
    }
}

  • 功能分析(生產(chǎn)訂單)

業(yè)務(wù)邏輯:
1凹炸、接收表單的數(shù)據(jù)
2、生成訂單id
3饲握、向訂單表插入數(shù)據(jù)救欧。
4锣光、向訂單明細(xì)表插入數(shù)據(jù)
5、向訂單物流表插入數(shù)據(jù)蹬刷。
6频丘、返回TaotaoResult。
返回值:TaotaoResult

  • Dao層
    可以使用逆向工程迂卢。
  • Service層
    參數(shù):OrderInfo
    返回值:TaotaoResult
@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private TbOrderMapper orderMapper;
    @Autowired
    private TbOrderItemMapper orderItemMapper;
    @Autowired
    private TbOrderShippingMapper orderShippingMapper;
    @Autowired
    private JedisClient jedisClient;
    
    @Value("${ORDER_GEN_KEY}")
    private String ORDER_GEN_KEY;
    @Value("${ORDER_ID_BEGIN}")
    private String ORDER_ID_BEGIN;
    @Value("${ORDER_ITEM_ID_GEN_KEY}")
    private String ORDER_ITEM_ID_GEN_KEY;
    
    @Override
    public TaotaoResult createOrder(OrderInfo orderInfo) {
        // 1而克、接收表單的數(shù)據(jù)
        // 2怔毛、生成訂單id
        if (!jedisClient.exists(ORDER_GEN_KEY)) {
            //設(shè)置初始值
            jedisClient.set(ORDER_GEN_KEY, ORDER_ID_BEGIN);
        }
        String orderId = jedisClient.incr(ORDER_GEN_KEY).toString();
        orderInfo.setOrderId(orderId);
        orderInfo.setPostFee("0");
        //1拣度、未付款蜂莉,2混卵、已付款幕随,3、未發(fā)貨辕录,4梢卸、已發(fā)貨,5蚣旱、交易成功戴陡,6恤批、交易關(guān)閉
        orderInfo.setStatus(1);
        Date date = new Date();
        orderInfo.setCreateTime(date);
        orderInfo.setUpdateTime(date);
        // 3、向訂單表插入數(shù)據(jù)诀浪。
        orderMapper.insert(orderInfo);
        // 4延都、向訂單明細(xì)表插入數(shù)據(jù)
        List<TbOrderItem> orderItems = orderInfo.getOrderItems();
        for (TbOrderItem tbOrderItem : orderItems) {
            //生成明細(xì)id
            Long orderItemId = jedisClient.incr(ORDER_ITEM_ID_GEN_KEY);
            tbOrderItem.setId(orderItemId.toString());
            tbOrderItem.setOrderId(orderId);
            //插入數(shù)據(jù)
            orderItemMapper.insert(tbOrderItem);
        }
        // 5窄潭、向訂單物流表插入數(shù)據(jù)。
        TbOrderShipping orderShipping = orderInfo.getOrderShipping();
        orderShipping.setOrderId(orderId);
        orderShipping.setCreated(date);
        orderShipping.setUpdated(date);
        orderShippingMapper.insert(orderShipping);
        // 6、返回TaotaoResult躏惋。
        return TaotaoResult.ok(orderId);
    }

}

image.png
  • 發(fā)布服務(wù)


    image.png
  • 引用服務(wù)(taotao-order-web)


    image.png
  • Controller
    請求的url:/order/create
    參數(shù):使用OrderInfo接收
    返回值:邏輯視圖距误。
    業(yè)務(wù)邏輯:
    1、接收表單提交的數(shù)據(jù)OrderInfo趁俊。
    2刑然、補全用戶信息泼掠。
    3、調(diào)用Service創(chuàng)建訂單挡逼。
    4腻豌、返回邏輯視圖展示成功頁面
    a) 需要Service返回訂單號
    b) 當(dāng)前日期加三天。
  • 測試


    image.png

    添加訂單成功的畫面.png

可以到我們的攔截器,訂單系統(tǒng)都完全跑起來了~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末订框,一起剝皮案震驚了整個濱河市兜叨,隨后出現(xiàn)的幾起案子国旷,更是在濱河造成了極大的恐慌,老刑警劉巖跪但,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件屡久,死亡現(xiàn)場離奇詭異,居然都是意外死亡糙及,警方通過查閱死者的電腦和手機(jī)筛欢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來迟郎,“玉大人聪蘸,你說我怎么就攤上這事⌒偻ィ” “怎么了浑劳?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵魔熏,是天一觀的道長蒜绽。 經(jīng)常有香客問我,道長鼎姊,這世上最難降的妖魔是什么相赁? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮唤衫,結(jié)果婚禮上绵脯,老公的妹妹穿的比我還像新娘蛆挫。我一直安慰自己,他們只是感情好楣导,可當(dāng)我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布筒繁。 她就那樣靜靜地躺著巴元,像睡著了一般。 火紅的嫁衣襯著肌膚如雪呕缭。 梳的紋絲不亂的頭發(fā)上修己,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天睬愤,我揣著相機(jī)與錄音,去河邊找鬼砂豌。 笑死光督,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的筐摘。 我是一名探鬼主播船老,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼努隙,長吁一口氣:“原來是場噩夢啊……” “哼荸镊!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起张惹,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤岭洲,失蹤者是張志新(化名)和其女友劉穎盾剩,沒想到半個月后替蔬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體屎暇,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡根悼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年挤巡,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片喉恋。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡瀑晒,死狀恐怖徘意,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情玖详,我是刑警寧澤勤讽,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布脚牍,位于F島的核電站,受9級特大地震影響券膀,放射性物質(zhì)發(fā)生泄漏驯遇。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一舒帮、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧肢执,春花似錦蔚万、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽淳蔼。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間晌坤,已是汗流浹背骤菠。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工商乎, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留祭阀,地道東北人。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像却桶,于是被迫代替她去往敵國和親颖系。 傳聞我的和親對象是個殘疾皇子嘁扼,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,933評論 2 355

推薦閱讀更多精彩內(nèi)容