1. 程式人生 > >【J2EE】模仿天貓商城(後臺篇)

【J2EE】模仿天貓商城(後臺篇)

之前學習了使用J2EE開發一個模仿天貓商城整站的專案,期間學習到了不少知識。但是隔了一段時間再回看程式碼,居然有點生疏了~所以寫下這篇部落格,方便日後回顧,溫故而知新,也可以和大家交流學習。

本篇介紹專案的後臺管理開發,模組主要分為:分類管理,使用者管理,訂單管理,分類下的產品管理,分類屬性管理,產品屬性管理和產品圖片管理等等。。。

涉及到的知識

前端:html+css+JavaScript+jQuery+Bootstrap

後端:j2ee

資料庫:mysql

資料庫表關係圖

模組開發

這裡使用的是MVC開發模式,jsp作為顯示的層面,servlet充當控制層,bean和dao作為模型

如果一個功能對應一個servlet,那麼一個專案裡面就有很多很多的servlet,web.xml也要配置很多次。這裡的解決方案是使用Filter結合servlet。假設訪問路徑是 http://127.0.0.1:8080/tmall/admin_category_list,首先設定一個過濾器BackServletFilter,對所有請求進行攔截,判斷訪問的地址是否以/admin_開頭,如果是,那麼做如下操作

1.取出兩個下劃線之間的值 category

2.取出最後一個下劃線之後的值 list

3. 然後根據這個值,服務端跳轉到categoryServlet,並且把list這個值傳遞過去

package tmall.filter;
 
import java.io.IOException;
 
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.apache.commons.lang.StringUtils;
 
public class BackServletFilter implements Filter {
 
    public void destroy() {
         
    }
 
    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
         
        String contextPath=request.getServletContext().getContextPath();
        String uri = request.getRequestURI();
        uri =StringUtils.remove(uri, contextPath);
        if(uri.startsWith("/admin_")){     
            String servletPath = StringUtils.substringBetween(uri,"_", "_") + "Servlet";
            String method = StringUtils.substringAfterLast(uri,"_" );
            request.setAttribute("method", method);
            req.getRequestDispatcher("/" + servletPath).forward(request, response);
            return;
        }
         
        chain.doFilter(request, response);
    }
 
    public void init(FilterConfig arg0) throws ServletException {
     
    }
}

緊接著,定義一個BaseBackServlet進行抽取,原理是利用反射技術。讓categoryServlet 繼承BaseBackServlet,重寫BaseBackServlet中的service函式。在呼叫categoryServlet中的dopost或者doget方法前,BaseBackServlet會被呼叫,根據filter傳遞過來method的值,藉助反射,呼叫categoryServlet中對用的方法,最後根據servlet中返回的欄位,選擇進行伺服器端的跳轉或者是客戶端的跳轉。

package tmall.servlet;
 
import java.io.InputStream;
 
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
 
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
 
import tmall.dao.CategoryDAO;
import tmall.dao.OrderDAO;
import tmall.dao.OrderItemDAO;
import tmall.dao.ProductDAO;
import tmall.dao.ProductImageDAO;
import tmall.dao.PropertyDAO;
import tmall.dao.PropertyValueDAO;
import tmall.dao.ReviewDAO;
import tmall.dao.UserDAO;
import tmall.util.Page;
 
public abstract class BaseBackServlet extends HttpServlet {
 
    public abstract String add(HttpServletRequest request, HttpServletResponse response, Page page) ;
    public abstract String delete(HttpServletRequest request, HttpServletResponse response, Page page) ;
    public abstract String edit(HttpServletRequest request, HttpServletResponse response, Page page) ;
    public abstract String update(HttpServletRequest request, HttpServletResponse response, Page page) ;
    public abstract String list(HttpServletRequest request, HttpServletResponse response, Page page) ;
     
    protected CategoryDAO categoryDAO = new CategoryDAO();
    protected OrderDAO orderDAO = new OrderDAO();
    protected OrderItemDAO orderItemDAO = new OrderItemDAO();
    protected ProductDAO productDAO = new ProductDAO();
    protected ProductImageDAO productImageDAO = new ProductImageDAO();
    protected PropertyDAO propertyDAO = new PropertyDAO();
    protected PropertyValueDAO propertyValueDAO = new PropertyValueDAO();
    protected ReviewDAO reviewDAO = new ReviewDAO();
    protected UserDAO userDAO = new UserDAO();
 
    public void service(HttpServletRequest request, HttpServletResponse response) {
        try {
             
            /*獲取分頁資訊*/
            int start= 0;
            int count = 5;
            try {
                start = Integer.parseInt(request.getParameter("page.start"));
            } catch (Exception e) {
                 
            }
            try {
                count = Integer.parseInt(request.getParameter("page.count"));
            } catch (Exception e) {
            }
            Page page = new Page(start,count);
             
            /*藉助反射,呼叫對應的方法*/
            String method = (String) request.getAttribute("method");
            Method m = this.getClass().getMethod(method, javax.servlet.http.HttpServletRequest.class,
                    javax.servlet.http.HttpServletResponse.class,Page.class);
            String redirect = m.invoke(this,request, response,page).toString();
             
            /*根據方法的返回值,進行相應的客戶端跳轉,服務端跳轉,或者僅僅是輸出字串*/
             
            if(redirect.startsWith("@"))
                response.sendRedirect(redirect.substring(1));
            else if(redirect.startsWith("%"))
                response.getWriter().print(redirect.substring(1));
            else
                request.getRequestDispatcher(redirect).forward(request, response);
             
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

1.分類管理

分類管理是後臺開發的起始,是產品管理的父級,這裡實現了分類管理增刪查改的基本操作,同時提供分頁查詢,分類屬性的增刪查改,分類下產品管理入口(在跳轉時傳遞分類id查詢分類下的產品)。下面是分類下分頁查詢程式碼,分頁查詢在其他功能開發中都會用到,在page中設定start和count作為引數傳入categoryDao中,查詢出條件下的分類集合,用request傳到jsp頁面中去

public String list(HttpServletRequest request, HttpServletResponse response, Page page) {
        List<Category> cs = categoryDAO.list(page.getStart(),page.getCount());
        int total = categoryDAO.getTotal();
        page.setTotal(total);
         
        request.setAttribute("thecs", cs);
        request.setAttribute("page", page);
         
        return "admin/listCategory.jsp";
    }

2.分類屬性管理

每個分類都有對應屬性,例如電視機,會有產地,尺寸,顏色等屬性,所以我們得對它的這些屬性進行管理。例如做一個增加操作,提交資料是在istProperty.jsp頁面中的,除了提交屬性名稱,還會提交cid,在PropertyServlet中根據獲取到的cid,name引數,建立新的Property物件,並插入到資料庫,客戶端跳轉到admin_property_list,並帶上引數cid。

listProperty.jsp程式碼片段

<form method="post" id="addForm" action="admin_property_add">
                <table class="addTable">
                    <tr>
                        <td>屬性名稱</td>
                        <td><input id="name" name="name" type="text"
                            class="form-control"></td>
                    </tr>
                    <tr class="submitTR">
                        <td colspan="2" align="center">
                            <input type="hidden" name="cid" value="${c.id}">
                            <button type="submit" class="btn btn-success">提 交</button>
                        </td>
                    </tr>
                </table>
            </form>

propertyServlet程式碼片段

public String add(HttpServletRequest request, HttpServletResponse response, Page page) {
    int cid = Integer.parseInt(request.getParameter("cid"));
    Category c = categoryDAO.get(cid);
     
    String name= request.getParameter("name");
    Property p = new Property();
    p.setCategory(c);
    p.setName(name);
    propertyDAO.add(p);
    return "@admin_property_list?cid="+cid;
}

3.產品管理

產品管理這裡也是關於增刪查改的基本操作,有不同的是page下必須多夾帶一個引數,這個引數就是產品父級-分類的id,在產品的分頁查詢需要用到這個引數,查詢訪問的是ProductServlet的list()方法,首先獲取分類 cid,基於cid,獲取當前分類下的產品集合, 獲取當前分類下的產品總數,並且設定給分頁page物件,拼接字串"&cid="+c.getId(),設定給page物件的Param值。 因為產品分頁都是基於當前分類下的分頁,所以分頁的時候需要傳遞這個cid,封裝好資料,服務端跳轉到admin/listProduct.jsp頁面, 在listProduct.jsp頁面上使用c:forEach 遍歷ps集合,並顯示

public String list(HttpServletRequest request, HttpServletResponse response, Page page) {
    int cid = Integer.parseInt(request.getParameter("cid"));
    Category c = categoryDAO.get(cid);
     
    List<Product> ps = productDAO.list(cid, page.getStart(),page.getCount());
     
    int total = productDAO.getTotal(cid);
    page.setTotal(total);
    page.setParam("&cid="+c.getId());
     
    request.setAttribute("ps", ps);
    request.setAttribute("c", c);
    request.setAttribute("page", page);
     
    return "admin/listProduct.jsp";
}

listProduct.jsp程式碼片段

<c:forEach items="${ps}" var="p">
    <tr>
        <td>${p.id}</td>
        <td>
         
        <c:if test="${!empty p.firstProductImage}">
            <img width="40px" src="img/productSingle/${p.firstProductImage.id}.jpg">
        </c:if>
         
        </td>
        <td>${p.name}</td>
        <td>${p.subTitle}</td>
        <td>${p.orignalPrice}</td>
        <td>${p.promotePrice}</td>
        <td>${p.stock}</td>
        <td><a href="admin_productImage_list?pid=${p.id}"><span
                class="glyphicon glyphicon-picture"></span></a></td>
        <td><a href="admin_product_editPropertyValue?id=${p.id}"><span
                class="glyphicon glyphicon-th-list"></span></a></td>
         
        <td><a href="admin_product_edit?id=${p.id}"><span
                class="glyphicon glyphicon-edit"></span></a></td>
        <td><a deleteLink="true"
            href="admin_product_delete?id=${p.id}"><span
                class="     glyphicon glyphicon-trash"></span></a></td>
 
    </tr>
</c:forEach>

4.產品圖片管理

每個產品的圖片有分為單張圖片和詳細資訊圖片,單張圖片是為了展示產品外觀,而詳細資訊圖片是為了展示產品詳細資訊。parseUpload 獲取上傳檔案的輸入流,parseUpload 方法會修改params 引數,並且把瀏覽器提交的type,pid資訊放在其中,從params 中取出type,pid資訊,並根據這個type,pid,藉助productImageDAO,向資料庫中插入資料,根據request.getSession().getServletContext().getRealPath( "img/productSingle"),定位到存放分類圖片的目錄,除了productSingle,還有productSingle_middle和productSingle_small。 因為每上傳一張圖片,都會有對應的正常,中等和小的三種大小圖片,並且放在3個不同的目錄下,檔案命名以儲存到資料庫的分類物件的id+".jpg"的格式命名,根據步驟1獲取的輸入流,把瀏覽器提交的檔案,複製到目標檔案,再借助ImageUtil.resizeImage把正常大小的圖片,改變大小之後,分別複製到productSingle_middle和productSingle_small目錄下, 處理完畢之後,客戶端條跳轉到admin_productImage_list?pid=,並帶上pid。

productImageServlet程式碼片段

public String add(HttpServletRequest request, HttpServletResponse response, Page page) {
        //上傳檔案的輸入流
        InputStream is = null;
        //提交上傳檔案時的其他引數
        Map<String,String> params = new HashMap<>();
 
        //解析上傳
        is = parseUpload(request, params);     
         
        //根據上傳的引數生成productImage物件
        String type= params.get("type");
        int pid = Integer.parseInt(params.get("pid"));
        Product p =productDAO.get(pid);
         
        ProductImage pi = new ProductImage();      
        pi.setType(type);
        pi.setProduct(p);
        productImageDAO.add(pi);
         
        //生成檔案
        String fileName = pi.getId()+ ".jpg";
        String imageFolder;
        String imageFolder_small=null;
        String imageFolder_middle=null;
        if(ProductImageDAO.type_single.equals(pi.getType())){
            imageFolder= request.getSession().getServletContext().getRealPath("img/productSingle");
            imageFolder_small= request.getSession().getServletContext().getRealPath("img/productSingle_small");
            imageFolder_middle= request.getSession().getServletContext().getRealPath("img/productSingle_middle");
        }
             
        else
            imageFolder= request.getSession().getServletContext().getRealPath("img/productDetail");
        File f = new File(imageFolder, fileName);
        f.getParentFile().mkdirs();
         
        // 複製檔案
        try {
            if(null!=is && 0!=is.available()){
                try(FileOutputStream fos = new FileOutputStream(f)){
                    byte b[] = new byte[1024 * 1024];
                    int length = 0;
                    while (-1 != (length = is.read(b))) {
                        fos.write(b, 0, length);
                    }
                    fos.flush();
                    //通過如下程式碼,把檔案儲存為jpg格式
                    BufferedImage img = ImageUtil.change2jpg(f);
                    ImageIO.write(img, "jpg", f);      
                     
                    if(ProductImageDAO.type_single.equals(pi.getType())){
                        File f_small = new File(imageFolder_small, fileName);
                        File f_middle = new File(imageFolder_middle, fileName);
 
                        ImageUtil.resizeImage(f, 56, 56, f_small);
                        ImageUtil.resizeImage(f, 217, 190, f_middle);
                    }
                         
                }
                catch(Exception e){
                    e.printStackTrace();
                }
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
 
        return "@admin_productImage_list?pid="+p.getId();
    }

5.產品屬性管理

每個產品都有各自的屬性,比如電視機的顏色是紅色。所以對產品屬性的管理也無非是增刪查改,這裡用修改舉例,修改時候使用了post提交ajax非同步處理。使用監聽輸入框上的keyup事件,獲取輸入框裡的,還有定義的pvid,藉助JQuery的ajax函式 $.post,把id和值,提交到admin_product_updatePropertyValue,admin_product_updatePropertyValue導致ProductServlet的updatePropertyValue方法被呼叫, BaseBackServlet根據返回值"%success",直接輸出字串"success" 到瀏覽器, 瀏覽器判斷如果返回值是"success",那麼就把邊框設定為綠色,表示修改成功,否則設定為紅色,表示修改失敗。

editProperty.jsp程式碼片段

$("input.pvValue").keyup(function(){
    var value = $(this).val();
    var page = "admin_product_updatePropertyValue";
    var pvid = $(this).attr("pvid");
    var parentSpan = $(this).parent("span");
    parentSpan.css("border","1px solid yellow");
    $.post(
            page,
            {"value":value,"pvid":pvid},
            function(result){
                if("success"==result)
                    parentSpan.css("border","1px solid green");
                else
                    parentSpan.css("border","1px solid red");
            }
        );     
});

6.使用者管理

這裡就只是從資料庫取出來,分頁顯示~

7.訂單管理

後臺訂單管理只提供發貨和查詢的方法,下單和取消訂單是由前臺使用者自己進行的。當訂單狀態是waitDelivery的時候,就會出現發貨按鈕,發貨按鈕連結跳轉到admin_order_delivery,OrderServlet.delivery()方法被呼叫,獲取物件id,修改發貨時間,設定發貨狀態,更新資料庫,跳轉,完成了

orderServlet片段

public String delivery(HttpServletRequest request, HttpServletResponse response, Page page) {
    int id = Integer.parseInt(request.getParameter("id"));
    Order o = orderDAO.get(id);
    o.setDeliveryDate(new Date());
    o.setStatus(OrderDAO.waitConfirm);
    orderDAO.update(o);
    return "@admin_order_list";
}

總結下來,j2ee開發增刪查改第一步先是獲取到傳遞過來的資料,在根據實際業務對資料進行操作,到這裡後臺部分就寫完了