家居網購項目--項目總結

来源:https://www.cnblogs.com/zydevelop/p/18109181/zy_servletProject
-Advertisement-
Play Games

家居網購項目--項目總結 家居網購項目總結 本項目是基於java的前後端項目,使用原生的Servlet + jsp 開發。 主要的技術點: 1.登錄註冊功能:使用kaptcha去生成驗證碼,使用郵件完成註冊 2.使用攔截器攔截用戶請求,限制用戶訪問許可權 3.使用ThreadLocal 確保是同一線程 ...


家居網購項目--項目總結

家居網購項目總結

本項目是基於java的前後端項目,使用原生的Servlet + jsp 開發。
主要的技術點:
1.登錄註冊功能:使用kaptcha去生成驗證碼,使用郵件完成註冊
2.使用攔截器攔截用戶請求,限制用戶訪問許可權
3.使用ThreadLocal 確保是同一線程來完成事務的提交和回滾
4.使用MVC模式使用DAO-Serice-Servlet進行分層
5.使用數據模型entity Cart.java 將數據存入session中來進行購物車的存儲
6.使用ajax對頁面進行局部刷新
7.使用json格式進行前後端數據的交換
8.前端使用jsp進行伺服器渲染
9.資料庫使用Mysql

項目準備

1.一些工作環境的配置,IDE使用IntellijIDEA,jdk1.8,下麵介紹一些關於此項目的主要模塊後端代碼的實現部分

2.搭建項目的框架

註冊與登錄功能的實現

註冊和登錄功能是每個網站最基本的功能,實現的主要難點我覺得在於正則表達式的編寫。

//完成對用戶名的校驗
   var usernamePattern = /^\w{6,10}$/;
//驗證郵箱
    var emailVal = $("#email").val();
    var emailPattern = /^[\w-]+@([a-zA-Z]+\.)+[a-zA-Z]+$/
    if (!emailPattern.test(emailVal)) {
        $("span[class= 'errorMsg']").text("電子郵箱格式不正確");
        return false;
    }
//...

用戶表實現

密碼MD5加密

為了保證安全,密碼不能明文的在網路中進行傳輸,也不能以明文的形式存到資料庫中。
存在資料庫的密碼 = MD5( 密碼 ) 防止密碼泄露

Kaptcha 生成驗證碼

導入 Kaptcha.jar 後,伺服器會生成的驗證碼並保存在session中,我們需要通過 com.google.code.kaptcha下的 Constants.java類 的 屬性 拿到對應的驗證碼

public static final String KAPTCHA_SESSION_KEY = "KAPTCHA_SESSION_KEY";//靜態屬性

//獲取驗證碼
  String token = (String) request.getSession().getAttribute(KAPTCHA_SESSION_KEY);

並且為了防止驗證碼被重覆使用需要立即刪除驗證碼

//立即刪除驗證碼防止驗證碼被重覆使用
request.getSession().removeAttribute(KAPTCHA_SESSION_KEY);

過濾器的使用

本項目有兩個過濾器,分別是:

1.用於用戶許可權驗證的 AuthFilter.java

2.用於配合JDBC提交和回滾事務的 TransactionFilter.java

使用過濾器需要在web.xml文件中配置相關參數,並且過濾器一般配置的優先順序較高(高於Servlet的配置)

 <!--AuthFilter 過濾器-->
    <filter>
        <filter-name>AuthFilter</filter-name>
        <filter-class>com.code_study.furns.filter.AuthFilter</filter-class>
        <init-param>
            <!--3. 對於要攔截的目錄的某些要放行的資源,通過配置參數指定-->
            <param-name>excludedUrls</param-name>
            <param-value>/views/manage/manage_login.jsp,/views/member/login.jsp</param-value>
        </init-param>
        <init-param>
            <!--3. 攔截的url-->
            <param-name>interceptUrls</param-name>
            <param-value>
      /views/cart/*
      ,/views/manage/*
      ,/views/member/*
      ,/views/order/*
      ,/cartServlet
      ,/manage/furnServlet
      ,/orderServlet
            </param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>AuthFilter</filter-name>
        <!--配置要攔截的url-->
        <url-pattern>/views/cart/*</url-pattern>
        <url-pattern>/views/manage/*</url-pattern>
        <url-pattern>/views/member/*</url-pattern>
        <url-pattern>/views/order/*</url-pattern>
        <url-pattern>/cartServlet</url-pattern>
        <url-pattern>/manage/furnServlet</url-pattern>
        <url-pattern>/orderServlet</url-pattern>
    </filter-mapping>

使用攔截器AuthFilter來攔截所有的用戶請求,判斷請求中的session是否存在有效的member或者admin,如果都沒有就請求轉發到登陸頁面,如果有,根據member或者admin對應許可權判斷 放行的資源是否包含 請求的url。如果不包含配置的放行url 就走驗證

<param-name>excludedUrls</param-name>
<param-value>/views/manage/manage_login.jsp,/views/member/login.jsp</param-value>

Servlet合併

將Servlet合併成一個BasicServlet——利用了模板設計模式+動態綁定+反射

//解決接收到的數據中文亂碼問題
        request.setCharacterEncoding("utf-8");
//獲取action不同的值
        String action = request.getParameter("action");
        try {
//反射獲取servlet對應的方法的對象
            Method declaredMethod = this.getClass().getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class);
            declaredMethod.invoke(this, request, response);
        } catch (Exception e) {
//將發生的異常繼續throw
            throw new RuntimeException(e);
        }

分頁顯示

將分頁顯示抽象成一個數據模型entity Page.java

    //每頁顯示多少條記錄其他地方也可以使用
    public static final Integer PAGE_SIZE = 4;//表示 每頁顯示幾條記錄
    private Integer pageNo;//表示 第幾頁
    private Integer totalRow;//表示 多少條記錄
    private Integer pageTotalCount;//表示 共有多少頁 -> totalRow / pageSize
    private List<T> items;//當前頁顯示的數據
    private String url;//分頁導航的字元串
    private Integer pageSize = PAGE_SIZE;//每頁顯示幾條記錄

進行分頁的方法page

 public Page<Furn> page(int pageNo, int pageSize) {

        Page<Furn> page = new Page<>();
        page.setPageNo(pageNo);
        page.setPageSize(pageSize);
        int totalRow = furnDAO.getTotalRow();
        page.setTotalRow(totalRow);
        //pageTotalCount 計算得到
        int pageTotalCount = totalRow / pageSize;
        if (totalRow % pageSize > 0) {
            pageTotalCount += 1;
        }
        page.setPageTotalCount(pageTotalCount);

        //begin計算得到 ->int pageNo, int pageSize
        int begin = (pageNo - 1) * pageSize;
        List<Furn> pageItems = furnDAO.getPageItems(begin, pageSize);
        page.setItems(pageItems);
        String url = "/manage/furnServlet?action=page&pageNo=" + pageNo;
        page.setUrl(url);
        return page;
    }

從而在前端可以進行分頁導航欄的展示

 <li><a href="customerFurnServlet?action=pageByName&pageNo=1">首頁</a></li>
    <c:if test="${requestScope.page.pageNo > 1}">
        <li><a href="${requestScope.page.url}&pageNo=${requestScope.page.pageNo-1}">上一頁</a></li>
    </c:if>
    <c:choose>
        <c:when test="${requestScope.page.pageTotalCount<=5}">
            <c:set var="begin" value="1"/>
            <c:set var="end" value="${requestScope.page.pageTotalCount}"/>
        </c:when>
        <c:when test="${requestScope.page.pageTotalCount > 5}">
            <c:choose>
                <c:when test="${requestScope.page.pageNo<=3}">
                    <c:set var="begin" value="1"/>
                    <c:set var="end" value="5"/>
                </c:when>
                <c:when test="${requestScope.page.pageNo>requestScope.page.pageTotalCount-3}">
                    <c:set var="begin" value="${requestScope.page.pageTotalCount - 4}"/>
                    <c:set var="end" value="${requestScope.page.pageTotalCount}"/>
                </c:when>
                <c:otherwise>
                    <c:set var="begin" value="${requestScope.page.pageNo - 2}"/>
                    <c:set var="end" value="${requestScope.page.pageNo +2 }"/>
                </c:otherwise>
            </c:choose>
        </c:when>
    </c:choose>
    <c:forEach begin="${begin}" end="${end}" var="i">
        <c:if test="${i == requestScope.page.pageNo}">
            <li><a class="active" href="${requestScope.page.url}&pageNo=${i}">${i}</a></li>
        </c:if>
        <c:if test="${i != requestScope.page.pageNo}">
            <li><a href="${requestScope.page.url}&pageNo=${i}">${i}</a></li>
        </c:if>

    </c:forEach>

    <c:if test="${requestScope.page.pageNo < requestScope.page.pageTotalCount}">
        <li><a href="${requestScope.page.url}&pageNo=${requestScope.page.pageNo+1}">下一頁</a></li>
    </c:if>

    <li><a href="customerFurnServlet?action=pageByName&pageNo=${requestScope.page.pageTotalCount}">末頁</a></li>
    <li><a>共${requestScope.get("page").pageTotalCount}頁</a></li>
    <li><a>共${requestScope.get("page").totalRow}條</a></li>
    </ul>

搜索功能

在首頁我們需要根據用戶提供的家居名稱進行查詢相關家居信息

使用Mysql中的模糊查詢

public int getTotalRowByName(String name) {
        String sql = "SELECT COUNT(*) FROM furn WHERE`name` LIKE ?";
        return ((Number)queryScalar(sql,"%"+name+"%")).intValue();//模糊查詢
    }

使用Ajax局部刷新

ajax實現非同步請求有三種常用方法1) $.ajax 2)$.get和 $.post 3)$.getJson

這裡滿足發送的請求方式是get請求因此使用方便的$.getJson

 $("button.add-to-cart").click(function () {
                //獲取到點擊的furn.id
                var furnId = $(this).attr("furnId");

                //發出一個請求添加家居
                $.getJSON(
                    "cartServlet", {
                        "action": "addItemByAjax",
                        "id": furnId
                    },
                    function (data) {

                        if (data.url == undefined) {//沒有返回url 已經登錄過
                            //局部刷新
                            $("span.header-action-num").text(data.cartTotalCount);
                        } else {
                            // 說明當前伺服器返回url 要求定位
                            location.href = data.url;
                        }
                    }
                )
            })

文件上傳

文件上傳是 前端的form 表單里的enctype 需要修改成multipart/form-data 提交的才可以是文件和文本

為了讓文件可以按照年月日分類進行存放 我們可以提供一個工具方法

public static String getYearMonthDay() {
        int year = LocalDateTime.now().getYear();
        int month = LocalDateTime.now().getMonthValue();
        int day = LocalDateTime.now().getDayOfMonth();
        return year + "/" + month + "/" + day;
    }

並且在創建目錄時拼接到 fileRealPath中

File fileRealPathDirectory = new File(fileRealPath + "/" + yearMonthDay);

需要更新圖片的路徑

//更新圖片路徑
furn.setImgPath(WebUtils.FURN_IMG_DIRECTORY + "/" +yearMonthDay+ "/"+ name);

以下是部分代碼

 //1. 判斷是不是文件表單(enctype="multipart/form-data")
        if (ServletFileUpload.isMultipartContent(request)) {
            //2. 創建 DiskFileItemFactory 對象, 用於構建一個解析上傳數據的工具對象
            DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
            //3. 創建一個解析上傳數據的工具對象
            ServletFileUpload servletFileUpload =
                    new ServletFileUpload(diskFileItemFactory);
            //解決接收到文件名是中文亂碼問題
            servletFileUpload.setHeaderEncoding("utf-8");

            try {
                List<FileItem> list = servletFileUpload.parseRequest(request);
                for (FileItem fileItem : list) {
                    if (fileItem.isFormField()) {//如果是true就是文本 input text
                        if ("name".equals(fileItem.getFieldName())) {
                            furn.setName(fileItem.getString("utf-8"));
                        } else if ("maker".equals(fileItem.getFieldName())) {
                            furn.setMaker(fileItem.getString("utf-8"));
                        } else if ("price".equals(fileItem.getFieldName())) {
                            furn.setPrice(new BigDecimal(fileItem.getString()));
                        } else if ("sales".equals(fileItem.getFieldName())) {
                            furn.setSales(new Integer(fileItem.getString()));
                        } else if ("stock".equals(fileItem.getFieldName())) {
                            furn.setStock(new Integer(fileItem.getString()));
                        }
                    } else {
                        //用一個方法
                        //獲取上傳的文件的名字
                        String name = fileItem.getName();
                        System.out.println("上傳的文件名=" + name);

                        if (!"".equals(name)) {
                            //把這個上傳到 伺服器的 temp下的文件保存到你指定的目錄
                            //1.指定一個目錄 , 就是我們網站工作目錄下
                            String filePath = "/" + WebUtils.FURN_IMG_DIRECTORY ;
//
                            //2. 獲取到完整目錄
                            String fileRealPath =
                                    request.getServletContext().getRealPath(filePath);

                            //3. 創建這個上傳的目錄=> 創建目錄
                            String yearMonthDay = WebUtils.getYearMonthDay();
                            File fileRealPathDirectory = new File(fileRealPath + "/" + yearMonthDay);
                            if (!fileRealPathDirectory.exists()) {//不存在,就創建
                                fileRealPathDirectory.mkdirs();//創建
                            }

                            //4. 將文件拷貝到fileRealPathDirectory目錄
                            //   構建一個上傳文件的完整路徑 :目錄+文件名
                            name = UUID.randomUUID().toString() + "_" + System.currentTimeMillis() + "_" + name;
                            String fileFullPath = fileRealPathDirectory + "/" + name;
                            fileItem.write(new File(fileFullPath));

                            fileItem.getOutputStream().close();

                            //5. 提示信息
                            response.setContentType("text/html;charset=utf-8");
                            response.getWriter().write("上傳成功~");

                            //todo
                            //刪除原先的圖片
                            //1.獲取原先的圖片路徑
                            String imgPath = furnService.queryFurnById(id).getImgPath();
                            //2.判斷是否存在
                           String path =  "out/artifacts/jiaju_mall_war_exploded/"+imgPath;
                          
                            File file = new File(path);
                            if (file.exists()){
                                file.delete();
                                System.out.println("圖片刪除成功");
                            }else{
                                System.out.println("圖片並不存在,無需刪除");
                            }

                            path = "web/"+imgPath;
                            System.out.println("path= "+path);
                            file = new File(path);
                            if (file.exists()){
                                file.delete();
                                System.out.println("圖片刪除成功");
                            }else{
                                System.out.println("圖片並不存在,無需刪除");
                            }
                           
                            //更新圖片路徑
                            furn.setImgPath(WebUtils.FURN_IMG_DIRECTORY + "/" +yearMonthDay+ "/"+ name);
                        }
                    }
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            System.out.println("不是文件表單...");
        }

        //更新furn
        furnService.updateFurnById(furn);
        request.getRequestDispatcher("/views/manage/update_ok.jsp").forward(request, response);
    }

結語

以上只是對本項目的相對比較重要的功能進行了總結,當然了這個項目還有一些其他的功能,比如訂單生成/管理,購物車的生成/管理,統一錯誤404,500的頁面顯示等。總的來說,原生的Web項目對我幫助很大,在之後學習完框架之後很多的底層很多細節會被隱藏起來,不再讓我們看到,所以我覺得做完這個項目一定對之後學習框架有所幫助。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Entity主要用於ORM(對象關係映射)框架中,如Hibernate、MyBatis等,以便將資料庫中的數據映射為對象,方便進行業務操作。 Entity通常與資料庫表一一對應,代表業務數據的基本單元。 通常放在項目的model或entity包下。 DAO(數據訪問對象):DAO是連接業務邏輯和數據 ...
  • ★ 基本定義 一種用起來像是使用的實例屬性一樣的特殊屬性,可以對應於某個方法 ★ property屬性的兩種方式 裝飾器 => 在方法上應用裝飾器 類屬性 => 在類中定義值為property對象的類屬性 ★ 裝飾器方式 代碼示例 class Goods(object): def __init__( ...
  • ​FFmpeg內置了aac音頻格式,在《FFmpeg開發實戰:從零基礎到短視頻上線》一書的“5.2.2 Linux環境集成mp3lame”又介紹瞭如何給FFmpeg集成mp3格式,常見的音頻文件除了這兩種之外,還有ogg和amr兩種格式也較常用。其中ogg格式的編解碼依賴於libogg和libvor ...
  • getpass模塊提供了兩個函數getpass和getuser,隱式密碼輸入和獲取當前用戶,當你想要用戶輸入密碼,基於安全考慮,密碼又不能明文顯示出來的時候就可以使用這個模塊。 getpass(prompt='Password: ', stream=None) 沒有回顯地獲取用戶輸入的密碼。 使用 ...
  • 0 prompt engineer 就是prompt工程師它的底層透視。 1 學習大模型的重要性 底層邏輯 人工智慧大潮已來,不加入就可能被淘汰。就好像現在職場里誰不會用PPT和excel一樣,基本上你見不到。你問任何一個人問他會不會用PPT,他都會說會用,只是說好還是不好。你除非說這個崗位跟電腦完 ...
  • Spring Boot應用的jar包因其Fat JAR構建、自定義載入器、內嵌Web容器及自動配置特性,通過Maven或Gradle插件統一打包所有依賴,實現Main-Class指定的啟動器載入應用,簡化部署,實現跨平臺直接運行,大幅提升開發與運維效率。 ...
  • 1 需求 由於業務種種原因,現在需要將ftp中已存文件移動到其它文件夾。 2 初始策略 一開始走上彎路,直接翻看FTPClient API有無move方法,但沒發現: 於是曲線救國,想著採用先複製、再刪除,偽代碼: InputStream inputStream = ftpClient.retrie ...
  • 1. 匿名函數 傳統的函數的定義包括了:函數名 + 函數體。 def send_email(): pass # 1. 執行 send_email() # 2. 當做列表元素 data_list = [send_email, send_email, send_email ] # 3. 當做參數傳遞 o ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...