1. 程式人生 > >基於Java Web的網上圖書商城管理系統——(三)

基於Java Web的網上圖書商城管理系統——(三)

三、詳細設計

1.註冊

regist.jsp頁面------>UserServlet----->UserDao
                
                UserServlet中:
                    1.獲取驗證碼,判斷它是否正確,如果正確,向下執行.
                      如果不正確,跳轉到regist.jsp頁面,顯示錯誤資訊
                      
                    2.將所以請求引數封裝到User物件中.在User類中建立一個validateRegist,
                    這個方法會對請求引數進行校驗,將錯誤資訊封裝到一個Map集合,在Servlet中
                    判斷集合長度是否>0就可以判斷是否有錯誤資訊,如果有,跳轉到regist.jsp
                    顯示錯誤資訊.
                    
                    3.呼叫UserService去完成註冊操作  呼叫regist方法,傳遞User引數
                        
                    4.在regist方法中做了兩件事情
                        (1).呼叫UserDao完成註冊操作
                        (2).給註冊的使用者傳送了一封啟用郵件
                 
                關於md5加密:
                    在mysql資料庫中通過  md5(欄位);
                        update users set password=md5(password);
                        
                    在java中可以通過程式碼實現
                        MessageDigest.getInstance("md5")

2.登入

index.jsp---->page.jsp頁面 有登入視窗
            
            會有登入視窗,提交時會訪問UserServlet,會帶一個請求引數  method=login
            在UserServlet中就可以判斷當前操作是登入操作
            就會呼叫UserServlet中的login方法
        
            UserServlet---UserService----->UserDao
                
                1.userServlet中收集了使用者名稱與密碼
                2.UserService中呼叫UserDao中查詢使用者操作  findUserByUserNameAndPassword
                3.在UserService中判斷了一下得到的User物件是否為null,如果為null,直接丟擲一個自定義異常.
                4.如果查詢到了使用者,但是使用者未啟用,那麼也不能登入成功,丟擲了一個自定義異常.
                5.在UserServlet中捕獲自定義異常,在page.jsp頁面顯示錯誤資訊.
                6.在UserServlet中判斷使用者不為空,就將User儲存到session中,並跳轉到首面index.jsp,
                  自動跳轉到page.jsp頁面.
                 
            1.記住使用者名稱
                當用戶登入成功後,並且勾選了記住使用者名稱操作,我們將使用者的username儲存到cookie中,
                持久化儲存,並攜帶到瀏覽器端.
                在頁面上通過el表示式獲取username顯示出來.
                
                在cookie中是否能儲存中文,那麼要是使用者名稱是中文,我們可以儲存username的utf-8碼.
                在頁面上,通過js將utf-8碼解碼.
                
            2.自動登入操作
                當用戶登入成功後,並且勾選自動登入操作,我們將username,password都儲存到cookie中,
                持久化儲存,並攜帶到瀏覽器端.
                
                當下一次在訪問時,我們可以通過Filter來攔截我們請求,判斷cookie中是否有我們儲存username,
                passowrd的這個cookie
                
                注意:自動登入時,有以下情況是不需要進行自動登入的.
                    (1).使用者已經登入
                    (2).使用者訪問的路徑是  login  regist這樣的操作。
                 
            3.登出操作
                我們使用者登入成功後,會將使用者儲存到session中。
                登出操作就是將session銷燬就可以以。
                session.invalidate()方法.
                
                點選登出訪問UserServlet?method=logout

3.商品新增

新增商品操作其時是一個檔案上傳操作.新增商品時,需要新增一個商品圖片,我們使用檔案上傳.
            commons-fileupload
            
            瀏覽器:
                1.method=post
                2.encType="multipart/form-data"
                3.<input type="file" name="f">元件
                
            點選新增圖書連線,會訪問 addProduct.jsp頁面    
            
            AddProductServlet這個servlet中有兩個工作:
                1.完成書箱圖片的儲存(上傳操作)
                2.將資訊儲存到資料庫.
              
            建立了一個Map<String,String[]>它用於封裝所有請求引數
            通過BeanUtils.populate方法將請求引數直接封裝到Product類中。

            可以呼叫ProductService中的新增圖書的方法,完成圖書新增操作

            當圖片新增成功後,我們會跳轉到index.jsp頁面.

4.商品查詢

(1).查詢全部
                index.jsp頁面直接跳轉到ProductServlet中.
                執行findAll操作,也就是查詢出全部資訊.
                
                呼叫ProductService----ProductDao完成查詢操作,得到所有商品資訊List<Product>
                
                轉發到page.jsp頁面,在page.jsp頁面展示所有商品資訊.
            

(2).根據id查詢
                點選搶購書籍,會訪問ProductServlet,
                product?method=findById&id=xxx
                會將這本書籍的id也攜帶到伺服器端.
                
                會呼叫ProductService的findByid方法,根據id查詢書籍,也就是得到一個Product物件.
                
                查詢到商品後,跳轉到productInfo.jsp頁面,展示了商品資訊
                
                上傳的所有圖片大小不確定的,怎樣保證顯示商品時,它的大小?
                    
                通過一個工具類可以保證當前的商品的圖片大小一致
                
                
                PicUtils putils = new PicUtils(this.getServletContext()
                    .getRealPath(product.getImgurl()));
                putils.resize(200, 200);
                
                當新增商品時,會生成商品圖片的一個縮圖,以方便我們在頁面上顯示。
                 
                在Product類中提供了一個getImgurl_s方法 ,這個方法,會根據商品圖片的
                路徑,獲取到縮圖片的路徑,我們就可以直接在頁面上通過el表達工,獲取
                縮圖的路徑,顯示出這個縮圖.

5.檢視商品詳情

在page.jsp頁面會顯示所以商品資訊,它提供一個連線,可以點選檢視
        當前書籍的詳細資訊。
        
        <a href='${pageContext.request.contextPath}/product?method=findById&id=${p.id}'>速速搶購</a>
    
        會訪問一個servlet(ProductServlet)並且傳遞了method=findById,id=xxxx.
        
        在servlet中根據傳遞的method值,判斷要執行的是findById方法,也就根據
        id查詢書籍資訊.會呼叫ProductService中查詢操作的方法,在ProductService中
        呼叫ProductDao中查詢書籍方法,最後得到一個Product物件,也就是商品資訊封裝
        物件.將查詢到的Product物件封裝到request域中,請求轉發到productInfo.jsp頁面,
        在頁面上展示我們書籍詳細資訊.

6.新增到購物車

宣告:購物車,沒有資料庫,直接使用session儲存資訊.
      
      當productInfo.jsp頁面,點選新增商品到購物車時,會將商品的id傳遞到伺服器端
      
      在頁面上點選新增商品到購物車會呼叫一個js函式
      function addProductToCart(id) {
        location.href = "${pageContext.request.contextPath}/cart?method=add&id=" + id;
      }
    
      在伺服器端會有CartServlet,它就是用於處理我們購物車操作.
        1.得到商品id  request.getParameter("id");
        2.根據id查找出商品   Product物件.
        3.關於購物車的資料結構
            Map<Product,Integer> 它就是我們的購物車,最終Map集合會儲存到session中.
            
        4.第一次新增商品到購物車時,在伺服器端,根據就沒有購物車,也就是沒有map集合.
          得到的是null值,就可以知道是第一次購物,就可以將購物車創建出來,並且,將
          商品新增到Map集合中。    

        5.如果不是第一次購物,查詢後得到的map集合就不為null,就說明購物車中可能已經存
          在了商品,就需要考慮一個事情,就需要考慮一個事情,購物車中存在了當間要購買的
          商品。

            Map集合特點:
                key是唯一的,如果使用put方法儲存,那麼,當key重複時,put方法
                返回的就是原來的value值。
                
                可以根據put方法返回值,來判斷商品在購物車中是否存在,
                如果存在了,也就是說,put方法返回值不為null,這時就可以將返回值
                +1,在重新儲存到map集合中。

7.檢視購物車

當點選檢視購物車中商品時,會跳轉到一個jsp頁面,購物車是儲存在session中的,
    那麼在jsp頁面上就可以直接得到session中的商品資訊.
    
    <a href="${pageContext.request.contextPath}/showCart.jsp">
                
          使用jstl的forEach遍歷Map集合.
        <c:forEach items=${cart}  var="entry">
            
            ${entry.key} ----對於cart中的key它就是一個Product物件
            ${entry.value}---對於caft中的value它是一個Integer物件,其實就是商品數量
            
        </c:forEach>
        
        通過<c:set> 來完成商品總價計算操作.

8.購物車管理

1.關於資料修改問題
        
       (1)關於點選+ -按鈕完成商品數量修改操作
            當點選按鈕時會呼叫函式changeCount(商品的id,商品修改數量,商品的庫存)
            onclick="changeCount('${entry.key.id }','${entry.value-1}','${entry.key.pnum}')"
        
            在js中它是沒資料型別的,那麼當傳遞引數時,在函式中,可能認為它是一個字串,
            那麼就會引起問題。通過parseInt()函式將數值轉換成數字.
        
            在函式中處理資料後,會將資料傳遞到伺服器端
            location.href = "${pageContext.request.contextPath}/cart?method=update&id=" + id
                + "&count=" + count;
                
            在CartServlet中通過判斷method=update完成操作.
                1).得到要修改商品的id,在得到要修改的商品數量值count.
                2).直接對購物車中的商品進行操作.
                3).為什麼直接建立一個Product物件,將id值賦值給它,就可以
                  直接修改商品數量.
                  
                  原因:對Product類中的equals方法進行了重寫,只比較商品的id.
                       在重寫equalse方法時,也將hashCode方法重寫了.
                       
        (2).關於+號操作
            以後面的原理一樣.
            
            onclick="changeCount('${entry.key.id}','${entry.value+1}','${entry.key.pnum }')"
        
            區別:-號操作,是將商品的數量進行-1操作
                 +號操作,是將商品的數量進行+1操作
                 
        3.文字框失去焦點時,也呼叫         
            onblur="changeCount('${entry.key.id}',this.value,'${entry.key.pnum}')
            注意:傳遞了this.value,它代表的是文字框中的值
            
        4.數字文字框
            通過對文字新增onkeydown事件操作,當鍵盤按下時,會呼叫一個函式.numbText(event)
            在函式中通過判斷按下鍵的keyCode值,就是鍵碼值,來判斷當前是否按下的是指定的
            按鈕。
            
            注意:對於firefox或ie瀏覽器,它們獲取事件物件event有區別。
            code = e.which; 判斷firefox瀏覽器  得到鍵碼值.
            code = window.event.keyCode; 判斷是ie瀏覽器,得到鍵碼值.
            
            if (!(code >= 48 && code <= 57 || code == 8 || code == 46)) 
            這是判斷當前按下的不是0-9  delete  backspace
            這時就要阻止事件的預設行為.
            
            e.preventDefault();  firefox阻止預設行為執行
            window.event.returnValue = false; ie瀏覽器阻止預設行為執行.
            
        5.關於刪除操作
            <a href="${pageContext.request.contextPath}/cart?method=remove&id=${entry.key.id}" onclick="delConfirm(event)">刪除</a>
            
            這個超連線會訪問伺服器的一個CartServlet,method=remove代表要執行的是刪除操作。
            並且將商品的id傳遞到伺服器端.
            
            得到id後,new Product()這個物件的id值就是傳遞過去的。
            對於Product類來說,它已經重寫了equals方法,它比較的就是id。
            在map集合中就可以直接根據我們建立的Product物件,將商品刪除。
            
            最後會判斷當前購物車中是否有商品,如果沒有,直接將購物車刪除。
            
            確認刪除操作?
                
                function delConfirm(e){
                    var flag=window.confirm("確認刪除商品嗎");
                    
                    if(!flag){
                        //不刪除商品        
                        //要想不刪除商品,要阻止事件的預設行為執行.
                        if(e&&e.preventDefault){
                            // e物件存在,preventDefault方法存在 ---- 火狐瀏覽器
                            e.preventDefault();
                        }else{
                            // 不支援e物件,或者沒有preventDefault方法 ---- IE
                            window.event.returnValue = false;
                        }

                    }                    
                }
                我們阻止超連線的預設事件執行.這樣超連線就不會向href指定的路徑傳送請求。

9.生成訂單

1.showCart.jsp頁面,點選結算會生成訂單
2.會跳轉到order.jsp頁面,在頁面展示我們訂單中的資訊.
      需要輸入一上訂單的收貨地址.
    生成訂單的程式碼實現:
        (1).在order.jsp頁面表單會向 ${pageContext.request.contextPath}/order提交.
          表單中有一個隱藏域 <input type="hidden" name="method" value="add">
        (2).在 OrderServlet中有一個add方法,它是訂單新增操作.
            
            訂單的添加註意事項:
                當訂單生成後,需要對以下的表進行操作.
                1).訂單表中要插入資料
                2).商品表中的商品數量要進行修改(修改商品的庫存)
                3).訂單與使用者之間也存在關係,新增訂單時,也需要得到當前使用者的id.
                
            以上操作需要進行事務控制。
                1).獲取Connection時,要使用同一個,需要在DataSourceUtils中對獲取
                 Connection物件操作進行修改,將其放入到ThreadLocale中.
                2).Dbutils
                    QueryRunner 直接使用帶引數的 引數型別是DataSource型別。
                    new QueryRunner(DataSource ds);這個操作,在呼叫update,query方法,
                    一般不會帶Connection引數,這樣,它就是一條sql一個事務。
                    
                    而現在我們需要事務管理,所以我們在使用QueryRunner時,就會
                    不帶引數  new QueryRunner(),而使用帶Connection引數的update,query方法.
              
            ---------------------------------
            要注意訂單要包含商品資訊,這時就需要從session中獲取購物車,將購物車中的資訊封裝
            Order物件中.

            現在我們需要事務控制,所以我們在service層進行了事務的開啟
                
            在新增訂單項時,使用了批處理,因為訂單與商品之間存在多對多關係,那麼
            我們的中間表orderItem,它就有可能有多條資料,所以我們使用了QueryRuner
            的batch方法完成新增訂單項操作
            
            注意:當我們操作完成後,一定要將Connection物件從ThreadLocale中remove掉.      
     

10.檢視訂單

檢視訂單,會根據使用者的role去顯示出不同的訂單。
    如果role=admin  它查詢出所有的訂單.
    如果role=user   它只查詢出當前使用者的訂單.
    
    程式碼實現:
        檢視訂單的入口:
            1).在首頁,提供了檢視訂單連線
            2).當用戶新增完成訂單成功後,會顯示檢視訂單操作.            
            <a href="${pageContext.request.contextPath}/order?method=search">
            
        (1).當點選連線後會訪問OrderServlet,並提交一個引數 method=search;
        (2).在OrderServlet會首先得到當前使用者   request.getSession().getAttribute("user");    
          如果使用者沒有登入,會讓它登入,如果使用者登入了,會檢視訂單資訊.
          
        (3).當呼叫dao中查詢訂單操作時,會根據當前使用者的role,進行不同的sql語句操作。
          查詢出訂單後,訂單中不包含商品資訊,所以要根據訂單的資訊,在orderItem表與
          products表中查詢出商品資訊。

       (4).查詢出所有訂單後,會得到一個List<Order>,將集合儲存到request域中,最後請求轉發到
          showOrder.jsp頁面,在頁面上顯示出所有查詢出的訂單.

11.訂單管理

1.支付操作
        使用了線上支付操作   epay第三方支付平臺.
        
        在顯示訂單頁面上showOrder.jsp頁面,顯示訂單資訊中,包含了當前支付狀態。
        會顯示  "已支付"  "未支付",如果是未支付,會有一個連線訪問pay.jsp頁面,
        並將當前訂單的id,以及當前訂單的金額傳遞到pay.jsp頁面。
        
        (1).在pay.jsp頁面上可以選擇銀行,表單提交時,將訂單編號,金額,以及銀行,提交到OnlinepayServlet中。
        (2).在OnlinePayServlet中完成請求引數封裝.
        (3).第三方支付,會根據你提交的請求引數   p8_Url 向這個路徑傳送資訊,
        (4).可以指定p8_url為CallbackServlet,那麼我們在servlet中就可以得到支付結果資訊
        (5).通過判斷資訊是否正確,以及r9_BType=1  r9_BType=2 可以知道,是否支付成功
        (6).當判斷支付成功後,我們要修改訂單的狀態。
            1).在orders表中有一個欄位  paystate=0 代表未支付,我們支付成功後,要修改訂單的狀態。
                paystat=1  這個代表訂單已對付.
                
            2).修改訂單狀態要根據訂單編號修改,在返回的支付結果資訊中r6_Order,它就代表了
              我們的訂單編號。                
    ---------------------------------------------

  2.訂單取消
        在顯示訂單的頁面上,會提供一個刪除訂單的連線。
        
       (1).錄取消訂單時,這個超連線會攜帶當前訂單的編號傳遞到伺服器端.
            <a href="${pageContext.request.contextPath}/order?method=del&id=${order.id}">取消訂單</a>
        (2).這個連線會訪問OrderServlet,並且 method=del  id=訂單編號
            
        (3).OrderServlet中會根據傳遞method判斷執行取消訂單操作,會根據id知道要刪除哪一個訂單.
            
        (4).刪除訂單注意事項
            1).刪除訂單要將orders表中資料刪除---根據id刪除.
            2).需要刪除orderItem表中資料
            3).需要修改商品的數量  也就是說需要對products表進行update操作.
            
            程式碼:
                1).根據訂單id在orderitem表中查詢出相關的商品資訊.
                2).修改商品資訊
                3).刪除訂單項資訊
                4).刪除訂單.
            以上操作,也需要進行事務控制。                
          

12.許可權控制

1.做一個註解
            @Retention(RetentionPolicy.RUNTIME)  //說明當前註解在runtime階段有效果
            @Target(ElementType.METHOD) //當前註解是在方法上使用的
            @Inherited  //當前註解具有繼承性
            public @interface PrivilegeInfo {

                String value(); //許可權名稱
            }
        

 2.對所有的service層的類進行提取介面操作.
            ProductServiceImpl 類---------->ProductService 介面
            OrderServieImple類 ------------>OrderService介面.
            
            在介面的方法上添加註解,註解中的value值,就是當前方法要執行,所需要的許可權名稱。
            

3.在servlet中得到的service物件,我們不直接new出來,可以針對每一個Service提供一個
         工廠,在工廠中生產對應的service物件,並且,返回的是代理物件.

            OrderServiceFactory
            ProductServiceFactory
            
            它們用於生產不同的service物件。
            
            在工廠中創建出對應的service物件,在提供的getInstance()方法中,
            返回其代理物件.
            
            這樣我們在servlet中,就通過工廠獲取service物件。得到的其實是代理物件。
            

 4.在動態代理的  InvocationHandler的invoke方法中進行許可權控制.
           (1).得到當前方法上的註解
                1).判斷當前方法上是否有指定的註解
                2).如果沒有,代表這個操作不需要許可權控制.
                       如果有,就會存在我們的註解.
                3).得到方法上的註解,通過註解物件得到當前方法要執行時所需要的許可權名稱。
                4).得到註解後,還需要得到當前使用者,好麼我們在所有添加註解的方法上
                  添加了一個引數  User.
                  可以在invoke方法中通過args引數獲取User物件.
                5).可以判斷當前使用者是否存在,知道是否有許可權操作。
                    1).如果使用者不存在,throw new PrivilegeException();許可權不足.
                    2).如果使用者存在
                        1).根據user的role,在資料庫中查詢出使用者所具有的所有的許可權名稱,
                          與註解上提供的許可權名稱對比。如果包含,那麼具有許可權,
                          如果不包含,沒有許可權
                        2).不包含     throw new PrivilegeException();許可權不足.
                           包含  method.invoke();

13.銷售榜單匯出

獲得商品銷售情況,需要查詢orderitem表  ------- 統計已支付訂單項內容

1.榜單中存在哪些資訊?(已支付訂單中商品)

商品資訊 products表

銷售數量 orderitem表

訂單支付情況 orders表

select * from products,orderitem,orders where products.id = orderitem.product_id and orderitem.order_id = orders.id ;

進行商品分組查詢 group by

select products.* , sum(orderitem.buynum) totalSaleNum from products,orderitem,orders where products.id = orderitem.product_id and orderitem.order_id = orders.id and orders.paystate = 1 group by products.id order by totalSaleNum desc;

2.榜單檔案是什麼格式?

匯出Excel 使用 POI類庫

csv 格式檔案 , 逗號分隔檔案

    1) 資訊當中有,在兩端加 雙引號

    2) 資訊當中有" 在之前加雙引號 轉義

檔案下載

設定Content-Type、Content-Disposition 頭資訊

檔案流輸出 (輸出檔案內容)

Excel 預設讀取字符集gbk