1. 程式人生 > >【J2EE】模仿天貓商城(前臺篇)-02購物

【J2EE】模仿天貓商城(前臺篇)-02購物

圍繞購物流程最重要的兩個表是OrderItem 和 Order表

Order表 

關於OrderItem的業務行為 1. 立即購買 —— 新增 OrderItem 2. 加入購物車 —— 新增 OrderItem 3. 檢視購物車 —— 顯示未和Order關聯的OrderItem 4. 選中購物車中的商品 —— 選中OrderItem 5. 結算頁面 —— 顯示選中的OrderItem 6. 生成訂單 —— 新增Order 7 .付款 —— 修改Order狀態 8. 我的訂單 —— 顯示Order 9. 確認收貨 —— 修改Order狀態

點選進入一個產品的詳情頁 ,如圖

點選立即購買

按鈕

那麼點選購買都做了什麼事情呢? 會在OrderItem表裡插入一條資料,這條資料會表示: 1. pid =844 購買的商品id 2. oid = -1, 這個訂單項還沒有生成對應的訂單,即還在購物車中 3. uid= 3,使用者的id是3 4. number=1, 購買了1件產品

如果沒有登入的話,那麼點選立即購買之前會彈出一個模態登入視窗

 具體程式碼

imgAndInfo.jsp中的一段js程式碼

$(".buyLink").click(function(){
    	//檢查是否登入
        var page = "forecheckLogin";
        $.get(
                page,
                function(result){
                	//登入了,則跳轉到購買連結
                    if("success"==result){
                        var num = $(".productNumberSetting").val();
                        location.href= $(".buyLink").attr("href")+"&num="+num;
                    }
                	//否則,彈出登入視窗
                    else{
                        $("#loginModal").modal('show');                    
                    }
                }
        );     
        return false;
    });

上面forecheckLogin是呼叫了ForeServlet中的checkLogin()方法

public String checkLogin(HttpServletRequest request, HttpServletResponse response, Page page) {
        User user =(User) request.getSession().getAttribute("user");
        if(null!=user)
            return "%success";
        return "%fail";
    }

在模態登入視窗中輸入賬號密碼後,使用jQuery的ajax來登入,具體程式碼如下

$("button.loginSubmitButton").click(function(){
        var name = $("#name").val();
        var password = $("#password").val();
         
        if(0==name.length||0==password.length){
            $("span.errorMessage").html("請輸入賬號密碼");
            $("div.loginErrorMessageDiv").show();          
            return false;
        }
         
        var page = "foreloginAjax";
        $.get(
                page,
                {"name":name,"password":password},
                function(result){
                    if("success"==result){
                        location.reload();
                    }
                    else{
                        $("span.errorMessage").html("賬號密碼錯誤");
                        $("div.loginErrorMessageDiv").show();                      
                    }
                }
        );         
         
        return true;
    });

同理,當我們點選加入購物車的時候,也會彈出登入視窗,這裡不再寫了。

接著當我們輸入賬號密碼登入之後,我們再點選立即購買,看這段“立即購買”按鈕的程式碼

href="forebuyone?pid=${p.id}帶著當前產品的id,同時再看上面imgAndInfo.jsp中的js程式碼 

這裡意思如果是登入了,則獲取數量,並且跳轉到/forebuyone同時帶著p.id和num,那麼根據上一節01的內容,這裡其實就去呼叫ForeServlet中的buyone()方法,並且帶著引數p.id和num。下面是buyone方法程式碼

public String buyone(HttpServletRequest request, HttpServletResponse response, Page page) {
    int pid = Integer.parseInt(request.getParameter("pid"));
    int num = Integer.parseInt(request.getParameter("num"));
    Product p = productDAO.get(pid);
    int oiid = 0;
     
    User user =(User) request.getSession().getAttribute("user");
    boolean found = false;
    List<OrderItem> ois = orderItemDAO.listByUser(user.getId());
    for (OrderItem oi : ois) {
        if(oi.getProduct().getId()==p.getId()){
            oi.setNumber(oi.getNumber()+num);
            orderItemDAO.update(oi);
            found = true;
            oiid = oi.getId();
            break;
        }
    }      
 
    if(!found){
        OrderItem oi = new OrderItem();
        oi.setUser(user);
        oi.setNumber(num);
        oi.setProduct(p);
        orderItemDAO.add(oi);
        oiid = oi.getId();
    }
    return "@forebuy?oiid="+oiid;
}

1. 獲取引數pid 2. 獲取引數num 3. 根據pid獲取產品物件p 4. 從session中獲取使用者物件user 接下來就是新增訂單項OrderItem, 新增訂單項要考慮兩個情況 a. 如果已經存在這個產品對應的OrderItem,並且還沒有生成訂單,即還在購物車中。 那麼就應該在對應的OrderItem基礎上,調整數量 a.1 基於使用者物件user,查詢沒有生成訂單的訂單項集合 a.2 遍歷這個集合 a.3 如果產品是一樣的話,就進行數量追加 a.4 獲取這個訂單項的 id b. 如果不存在對應的OrderItem,那麼就新增一個訂單項OrderItem b.1 生成新的訂單項 b.2 設定數量,使用者和產品 b.3 插入到資料庫 b.4 獲取這個訂單項的 id

注:這裡是根據使用者id查詢沒有生成訂單(即購物車中)的訂單項,其中sql語句有個oid= -1 當OrderItem表中的oid欄位等於 -1,即表示沒有對應的訂單;當我們插入訂單項到orderItem中時,orderItem的add方法會設定oid為-1,同時插入成功後還會返回一個id,即oiid。

    public List<OrderItem> listByUser(int uid) {
        return listByUser(uid, 0, Short.MAX_VALUE);
    }
  
    public List<OrderItem> listByUser(int uid, int start, int count) {
        List<OrderItem> beans = new ArrayList<OrderItem>();
  
        String sql = "select * from OrderItem where uid = ? and oid=-1 order by id desc limit ?,? ";
  
        try (Connection c = DBUtil.getConnection(); PreparedStatement ps = c.prepareStatement(sql);) {
  
            ps.setInt(1, uid);
            ps.setInt(2, start);
            ps.setInt(3, count);
  
            ResultSet rs = ps.executeQuery();
  
            while (rs.next()) {
                OrderItem bean = new OrderItem();
                int id = rs.getInt(1);
 
                int pid = rs.getInt("pid");
                int oid = rs.getInt("oid");
                int number = rs.getInt("number");
                 
                Product product = new ProductDAO().get(pid);
                if(-1!=oid){
                    Order order= new OrderDAO().get(oid);
                    bean.setOrder(order);                  
                }
 
                User user = new UserDAO().get(uid);
                bean.setProduct(product);
 
                bean.setUser(user);
                bean.setNumber(number);
                bean.setId(id);               
                beans.add(bean);
            }
        } catch (SQLException e) {
  
            e.printStackTrace();
        }
        return beans;
    }
public void add(OrderItem bean) {
 
        String sql = "insert into OrderItem values(null,?,?,?,?)";
        try (Connection c = DBUtil.getConnection(); PreparedStatement ps = c.prepareStatement(sql);) {
  
            ps.setInt(1, bean.getProduct().getId());
             
            //訂單項在建立的時候,是沒有訂單資訊的,故設定oid為-1
            if(null==bean.getOrder())
                ps.setInt(2, -1);
            else
                ps.setInt(2, bean.getOrder().getId()); 
             
            ps.setInt(3, bean.getUser().getId());
            ps.setInt(4, bean.getNumber());
            ps.execute();
  
            ResultSet rs = ps.getGeneratedKeys();
            if (rs.next()) {
                int id = rs.getInt(1);
                bean.setId(id);
            }
        } catch (SQLException e) {
  
            e.printStackTrace();
        }
    }

最後, 基於這個訂單項id客戶端跳轉到結算頁面/forebuy

結算介面: 

通過位址列上的地址

可以發現跳轉到/forebuy其實就是進行了伺服器端的跳轉,使得ForeServlet.buy()被呼叫了

buy方法程式碼

public String buy(HttpServletRequest request, HttpServletResponse response, Page page){
    String[] oiids=request.getParameterValues("oiid");
    List<OrderItem> ois = new ArrayList<>();
    float total = 0;
 
    for (String strid : oiids) {
        int oiid = Integer.parseInt(strid);
        OrderItem oi= orderItemDAO.get(oiid);
        total +=oi.getProduct().getPromotePrice()*oi.getNumber();
        ois.add(oi);
    }
     
    request.getSession().setAttribute("ois", ois);
    request.setAttribute("total", total);
    return "buy.jsp";
}  

1. 通過getParameterValues獲取引數oiid 為什麼這裡要用getParameterValues試圖獲取多個oiid,而不是getParameter僅僅獲取一個oiid? 因為結算頁面還需要顯示在購物車中選中的多條OrderItem資料,所以為了相容從購物車頁面跳轉過來的需求,要用getParameterValues獲取多個oiid 2. 準備一個泛型是OrderItem的集合ois 3. 根據前面步驟獲取的oiids,從資料庫中取出OrderItem物件,並放入ois集合中 4. 累計這些ois的價格總數,賦值在total上 5. 把訂單項集合放在session的屬性 "ois" 上 6. 把總價格放在 request的屬性 "total" 上 7. 服務端跳轉到buy.jsp

在buy.jsp中,除了遍歷出訂單項集合ois和顯示總價外,還需設定表單用於填寫使用者資訊,當用戶填寫好點選提交按鈕時,將跳轉到forecreateOrder,實際上也就是呼叫了foreServlet.createOrder,這裡是buy.jsp的程式碼片段。

 提交訂單訪問路徑 /forecreateOrder, 導致ForeServlet.createOrder 方法被呼叫 1. 從session中獲取user物件 2. 獲取地址,郵編,收貨人,使用者留言等資訊 3. 根據當前時間加上一個4位隨機數生成訂單號 4. 根據上述引數,建立訂單物件 5. 把訂單狀態設定為等待支付 6. 加入到資料庫 7. 從session中獲取訂單項集合 ( 在ForeServlet.buy中,訂單項集合被放到了session中 ) 8. 遍歷訂單項集合,設定每個訂單項的order,並更新到資料庫 9. 統計本次訂單的總金額 10. 客戶端跳轉到確認支付頁forealipay,並帶上訂單id和總金額

public String createOrder(HttpServletRequest request, HttpServletResponse response, Page page){
    User user =(User) request.getSession().getAttribute("user");
     
    List<OrderItem> ois= (List<OrderItem>) request.getSession().getAttribute("ois");
    if(ois.isEmpty())
        return "@login.jsp";
 
    String address = request.getParameter("address");
    String post = request.getParameter("post");
    String receiver = request.getParameter("receiver");
    String mobile = request.getParameter("mobile");
    String userMessage = request.getParameter("userMessage");
     
    Order order = new Order();
    String orderCode = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date()) +RandomUtils.nextInt(10000);
 
    order.setOrderCode(orderCode);
    order.setAddress(address);
    order.setPost(post);
    order.setReceiver(receiver);
    order.setMobile(mobile);
    order.setUserMessage(userMessage);
    order.setCreateDate(new Date());
    order.setUser(user);
    order.setStatus(OrderDAO.waitPay);
 
    orderDAO.add(order);
    float total =0;
    for (OrderItem oi: ois) {
        oi.setOrder(order);
        orderItemDAO.update(oi);
        total+=oi.getProduct().getPromotePrice()*oi.getNumber();
    }
     
    return "@forealipay?oid="+order.getId() +"&total="+total;
}

 在上一步客戶端跳轉到路徑/forealipay方法,導致ForeServlet.alipay()方法被呼叫。 alipay()沒做什麼事情,就是服務端跳轉到了alipay.jsp頁面。alipay.jsp也是包含了header.jsp,top.jsp,footer.jsp等公共頁面,其中還有一個確認支付業務頁面alipayPage.jsp,顯示總金額,並且讓確認支付按鈕跳轉到頁面 /forepayed頁面,並帶上oid和金額

 在上一步確認訪問按鈕提交資料到/forepayed,導致ForeServlet.payed方法被呼叫 1.1 獲取引數oid 1.2 根據oid獲取到訂單物件order 1.3 修改訂單物件的狀態和支付時間 1.4 更新這個訂單物件到資料庫 1.5 把這個訂單物件放在request的屬性"o"上 1.6 服務端跳轉到payed.jsp

public String payed(HttpServletRequest request, HttpServletResponse response, Page page) {
    int oid = Integer.parseInt(request.getParameter("oid"));
    Order order = orderDAO.get(oid);
    order.setStatus(OrderDAO.waitDelivery);
    order.setPayDate(new Date());
    new OrderDAO().update(order);
    request.setAttribute("o", order);
    return "payed.jsp";    
}  

payed.jsp是支付成功業務頁面 ,其中主要資訊由payedPage.jsp顯示

其中這裡可以點選 檢視交易詳情,跳轉到個人訂單頁面

 

/forebought導致ForeServlet.bought()方法被呼叫 1. 通過session獲取使用者user 2. 查詢user所有的狀態不是"delete" 的訂單集合os 3. 為這些訂單填充訂單項 4. 把os放在request的屬性"os"上 5. 服務端跳轉到bought.jsp

public String bought(HttpServletRequest request, HttpServletResponse response, Page page) {
    User user =(User) request.getSession().getAttribute("user");
    List<Order> os= orderDAO.list(user.getId(),OrderDAO.delete);
     
    orderItemDAO.fill(os);
     
    request.setAttribute("os", os);
     
    return "bought.jsp";       
}

 到這裡,一個購物的基本流程就走完了。