【J2EE】模仿天猫商城(后台篇)

之前学习了使用J2EE开发一个模仿天猫商城整站的项目,期间学习到了不少知识。但是隔了一段时间再回看代码,居然有点生疏了~所以写下这篇博客,方便日后回顾,温故而知新,也可以和大家交流学习。

本篇介绍项目的后台管理开发,模块主要分为:分类管理,用户管理,订单管理,分类下的产品管理,分类属性管理,产品属性管理和产品图片管理等等。。。

浏览地址:http://how2j.cn/tmall/admin_category_list

下载地址:链接:https://pan.baidu.com/s/12EnpGy8gr-TC6KL_JRSiCg 密码:lrxu 

 

涉及到的知识

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

后端:j2ee

数据库:mysql

数据库表关系图

【J2EE】模仿天猫商城(后台篇)_第1张图片

模块开发

这里使用的是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 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代码片段

属性名称

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 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代码片段


    
        ${p.id}
        
         
        
            
        
         
        
        ${p.name}
        ${p.subTitle}
        ${p.orignalPrice}
        ${p.promotePrice}
        ${p.stock}
        
        
         
        
        
 
    

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 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开发增删查改第一步先是获取到传递过来的数据,在根据实际业务对数据进行操作,到这里后台部分就写完了

你可能感兴趣的:(J2EE)