天猫整站(简易版)SSM(四)

后台管理分为三大模块:商品分类管理、用户管理和订单管理。商品分类下包含属性管理、产品管理,而产品管理下又包含图片管理和属性值设置功能。核心就是CRUD。

目录

一、分类管理

1.1 前端需求

1.2 前端代码

1.2.1 数据表格

1.2.2 分页条

1.2.3 新增表单

1.3 后端接口

1.3.1 查询

1.3.2 增加

1.3.3 删除

1.3.4 编辑

1.3.5 修改

二、属性管理

2.1 前端需求

2.2 前端代码

2.3 后端接口

2.3.1 查询

2.3.2 增加

2.3.3 编辑

2.3.4 修改

2.3.5 删除

2.3.6 分页

三、产品管理

3.1 前端需求

3.2 前端代码

3.3 后端接口

3.3.1 查询

3.3.2 增加

3.3.3 编辑

3.3.4 修改

3.3.5 删除

3.4 category属性的用途

3.4.1 前端页面展示需要

3.4.2 商品编辑页面需要

3.5 存在的问题

四、产品图片管理

4.1 前端需求

4.2 前端代码

4.3 后端接口

4.3.1 查询

4.3.2 增加

4.3.3 删除

4.4 产品管理重构

4.4.1 新增属性

4.4.2 ProductService中新增方法

4.4.3 ProductServiceImpl中实现新增方法

4.4.4 前端页面使用


一、分类管理

1.1 前端需求

需要展示的分类数据有:ID、分类对应的图片、分类名称。

操作:编辑和删除。编辑是跳转到下一个页面进行的。

分类管理下包含分类对应属性的管理和分类下所有商品的管理,在后边介绍。

1.2 前端代码

分为三部分:数据表格、分页条、新增表单。

<div class="workingArea">
        <h1 class="label label-info">分类管理</h1>
        <br>
        <br>
        <div class="listDataTableDiv">
            <table class="table table-striped table-bordered table-hover  table-condensed">
                <c:if test="${cs.size() != 0}">
                    <thead>
                        <tr class="success">
                            <th>ID</th>
                            <th>图片</th>
                            <th>分类名称</th>
                            <th>属性管理</th>
                            <th>产品管理</th>
                            <th>编辑</th>
                            <th>删除</th>
                        </tr>
                    </thead>
                    <tbody>
                        <c:forEach items="${cs}" var="c">
                            <tr>
                                <td>${c.id}</td>
                                <td><img height="40px" src="${pageContext.request.contextPath}/image/category/${c.id}.jpg"></td>
                                <td>${c.name}</td>
                                <td><a href="admin_property_list?cid=${c.id}"><span class="glyphicon glyphicon-th-list"></span></a></td>
                                <td><a href="admin_product_list?cid=${c.id}"><span class="glyphicon glyphicon-shopping-cart"></span></a></td>
                                <td><a href="admin_category_edit?id=${c.id}"><span class="glyphicon glyphicon-edit"></span></a></td>
                                <td><a deleteLink="true" href="admin_category_delete?id=${c.id}"><span class="glyphicon glyphicon-trash"></span></a></td>
                            </tr>
                        </c:forEach>
                    </tbody>
                </c:if>
                <c:if test="${cs.size() == 0}">
                    <thead>
                        <tr class="success">
                            <th colspan="7" class="text-center">请添加数据</th>
                        </tr>
                    </thead>
                </c:if>
            </table>
        </div>

    <div class="pageDiv">
        <%@include file="../include/admin/adminPage.jsp" %>
    </div>
    <div class="panel panel-warning addDiv">
        <div class="panel-heading">新增分类</div>
        <div class="panel-body">
            <form method="post" id="addForm" action="admin_category_add" enctype="multipart/form-data">
                <table class="addTable">
                    <tr>
                        <td>分类名称</td>
                        <td><input  id="name" name="name" type="text" class="form-control"></td>
                    </tr>
                    <tr>
                        <td>分类圖片</td>
                        <td>
                            <input id="categoryPic" accept="image/*" type="file" name="image" />
                        </td>
                    </tr>
                    <tr class="submitTR">
                        <td colspan="2" align="center">
                            <button type="submit" class="btn btn-success">提 交</button>
                        </td>
                    </tr>
                </table>
            </form>
        </div>
    </div>
</div>

1.2.1 数据表格

ID、图片、分类名称等基本信息是通过<c:forEach>语句渲染后台传来的数据对象,也就是这里的cs,它里面包含了所有的分类数据。

删除操作是通过点击超链接向后台发起请求,在请求路径中添加对应的分类id。

编辑操作是跳转到一个新的页面,代码如下:

<div class="workingArea">

    <ol class="breadcrumb">
        <li><a href="admin_category_list">所有分类</a></li>
        <li class="active">编辑分类</li>
    </ol>

    <div class="panel panel-warning editDiv">
        <div class="panel-heading">编辑分类</div>
        <div class="panel-body">
            <form method="post" id="editForm" action="admin_category_update"  enctype="multipart/form-data">
                <table class="editTable">
                    <tr>
                        <td>分类名称</td>
                        <td><input  id="name" name="name" value="${c.name}" type="text" class="form-control"></td>
                    </tr>
                    <tr>
                        <td>分类圖片</td>
                        <td>
                            <input id="categoryPic" accept="image/*" type="file" name="image" />
                        </td>
                    </tr>
                    <tr class="submitTR">
                        <td colspan="2" align="center">
                            <input type="hidden" name="id" value="${c.id}">
                            <button type="submit" class="btn btn-success">提 交</button>
                        </td>
                    </tr>
                </table>
            </form>
        </div>
    </div>
</div>

在发起编辑请求时,也会向后台传递对应分类的id,查询得到对应分类的信息后,在渲染编辑视图时保存到对象c中,最后在表单中相应位置显示要修改的分类信息。当修改完成后再发起更新请求。

1.2.2 分页条

为了提高复用性,将分页条单独抽取到一个jsp页面中,在有需要的时候直接引入即可。使用时,只需在页面中添加一个分类对象page即可,具体请参考

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8" isELIgnored="false"%>

	
<script>
$(function(){
	$("ul.pagination li.disabled a").click(function(){
		return false;
	});
});

</script>


<nav>
  <ul class="pagination">
    <li <c:if test="${!page.hasPreviouse}">class="disabled"</c:if>>
      <a  href="?start=0${page.param}" aria-label="Previous" >
        <span aria-hidden="true">&laquo;</span>
      </a>
    </li>

    <li <c:if test="${!page.hasPreviouse}">class="disabled"</c:if>>
      <a  href="?start=${page.start-page.count}${page.param}" aria-label="Previous" >
        <span aria-hidden="true">&lsaquo;</span>
      </a>
    </li>    

    <c:forEach begin="0" end="${page.totalPage-1}" varStatus="status">
        <li <c:if test="${status.index*page.count==page.start}">class="disabled"</c:if>>
            <a href="?start=${status.index*page.count}${page.param}"
            <c:if test="${status.index*page.count==page.start}">class="current"</c:if>>${status.count}</a>
        </li>
    </c:forEach>

    <li <c:if test="${!page.hasNext}">class="disabled"</c:if>>
      <a href="?start=${page.start+page.count}${page.param}" aria-label="Next">
        <span aria-hidden="true">&rsaquo;</span>
      </a>
    </li>
    <li <c:if test="${!page.hasNext}">class="disabled"</c:if>>
      <a href="?start=${page.last}${page.param}" aria-label="Next">
        <span aria-hidden="true">&raquo;</span>
      </a>
    </li>
  </ul>
</nav>

1.2.3 新增表单

填好数据直接提交即可,注意input中的name值一定与对应的pojo对象一致。

1.3 后端接口

有关Page对象请参考《工具类》

后端中的pojo、mapper和mapper对应的xml文件都是通过逆向工具自动生成的。

1.3.1 查询

Controller

    @RequestMapping("admin_category_list")
    public String list(Model model, Page page){
        //页面中的start会自动注入到page对象当中
        PageHelper.offsetPage(page.getStart(),page.getCount());
        System.out.println("开始:"+page.getStart()+","+"数量:"+page.getCount());
        List<Category> categoryList = categoryService.list();
        for (Category c:categoryList) {
            System.out.println(c);
        }
        int total =(int) new PageInfo<>(categoryList).getTotal();
        page.setTotal(total);
        model.addAttribute("cs",categoryList);
        model.addAttribute("page",page);
        return "admin/listCategory";
    }

在model中添加要进行渲染的cs和page。

Service

接口

    /**
     * 分页查询
     * @return
     */
    List<Category> list();

实现类

    @Override
    public List<Category> list() {
        CategoryExample example = new CategoryExample();
        example.setOrderByClause("id desc");
        return categoryMapper.selectByExample(example);
    }

1.3.2 增加

在增加中涉及到了图片的上传,相关工具请参考《工具类》

Controller

    @RequestMapping("admin_category_add")
    public String add(Category category, HttpSession httpSession, UploadedImageFile uploadedImageFile) throws IOException {
        categoryService.add(category);

        File imageFolder = new File(httpSession.getServletContext().getRealPath("image/category"));
        System.out.println(httpSession.getServletContext().getRealPath("image/category"));
        File file = new File(imageFolder, category.getId()+".jpg");
        if(!file.getParentFile().exists()){
            file.getParentFile().mkdirs();
        }
        uploadedImageFile.getImage().transferTo(file);
        BufferedImage image = ImageUtil.change2jpg(file);
        ImageIO.write(image,"jpg",file);
        return "redirect:/admin_category_list";
    }

在这里强调一下,本项目对于图片的保存方案:

对于那些需要图片属性的对象,在它的pojo对象中不会直接创建image属性字段的,而是在上传图片时以该对象的唯一id来重新命名图片,将图片保存在独立的文件夹中,前端页面渲染时直接从文件夹中根据id显示即可。

Service

增加功能直接调用mapper中提供的insert方法即可。

1.3.3 删除

此时删除还有一些问题,因为与分类表中的主键id是其他表的外键,所以删除时会有约束,需要级联删除。

Controller

    @RequestMapping("admin_category_delete")
    public String delete(int id,HttpSession session){
        categoryService.delete(id);
        File  imageFolder= new File(session.getServletContext().getRealPath("image/category"));
        File file = new File(imageFolder,id+".jpg");
        file.delete();

        return "redirect:/admin_category_list";
    }

Service

直接根据主键删除即可。

1.3.4 编辑

主要功能就是数据回显。

Controller

    @RequestMapping("admin_category_edit")
    public String edit(int id,Model model){
        Category category= categoryService.get(id);
        model.addAttribute("c", category);
        return "admin/editCategory";
    }

1.3.5 修改

分类信息修改以及图片修改

Controller

    @RequestMapping("admin_category_update")
    public String update(Category c, HttpSession session, UploadedImageFile uploadedImageFile) throws IOException {
        categoryService.update(c);
        MultipartFile image = uploadedImageFile.getImage();
        if(null!=image &&!image.isEmpty()){
            File  imageFolder= new File(session.getServletContext().getRealPath("image/category"));
            File file = new File(imageFolder,c.getId()+".jpg");
            image.transferTo(file);
            BufferedImage img = ImageUtil.change2jpg(file);
            ImageIO.write(img, "jpg", file);
        }
        return "redirect:/admin_category_list";
    }

二、属性管理

2.1 前端需求

页面与分类页面大体相似,只是多了一个导航条,俗称"面包屑"。

点击编辑时跳转到编辑页面

2.2 前端代码

<div class="workingArea">
    <ol class="breadcrumb">
        <li><a href="admin_category_list">所有分类</a></li>
        <li><a href="admin_property_list?cid=${category.id}">${category.name}</a></li>
        <li class="active">属性管理</li>
    </ol>

    <div class="listDataTableDiv">
        <table class="table table-striped table-bordered table-hover  table-condensed">
            <c:if test="${propertyList.size() != 0}">
                <thead>
                    <tr class="success">
                        <th>ID</th>
                        <th>属性名称</th>
                        <th>编辑</th>
                        <th>删除</th>
                    </tr>
                </thead>
                <tbody>
                    <c:forEach items="${propertyList}" var="p">
                        <tr>
                            <td>${p.id}</td>
                            <td>${p.name}</td>
                            <td><a href="admin_property_edit?id=${p.id}"><span class="glyphicon glyphicon-edit"></span></a></td>
                            <td><a deleteLink="true" href="admin_property_delete?id=${p.id}"><span class="glyphicon glyphicon-trash"></span></a></td>
                        </tr>
                    </c:forEach>
                </tbody>
            </c:if>
            <c:if test="${propertyList.size() == 0}">
                <thead>
                    <tr class="success">
                        <th colspan="4" class="text-center">请添加数据</th>
                    </tr>
                </thead>
            </c:if>
        </table>
    </div>

    <div class="pageDiv">
        <%@include file="../include/admin/adminPage.jsp"%>
    </div>

    <div class="panel panel-warning addDiv">
        <div class="panel-heading">新增属性</div>
        <div class="panel-body">
            <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="${category.id}">
                            <button type="submit" class="btn btn-success">提 交</button>
                        </td>
                    </tr>
                </table>
            </form>
        </div>
    </div>
</div>

代码分为四部分:面包屑、数据表格、分页条、新增表单。页面中需要渲染的对象有:

分类对象(category):在面包屑中使用,用来显示层次关系,属性管理是针对某一分类的属性进行管理,所以在面包屑中显示对应分类的名称。

属性对象(propertyList):它是一个列表,因为属性有多种

分页对象(page):分页。

2.3 后端接口

根据实际业务需求,需要对Pojo中Property实体类进行修改,添加一个Category字段,以及get和set方法,方便以后使用。

2.3.1 查询

    @RequestMapping("admin_property_list")
    public String list(int cid, Model model, Page page){
        Category category = categoryService.get(cid);
        PageHelper.offsetPage(page.getStart(),page.getCount());
        List<Property> propertyList = propertyService.list(cid);
        int total =(int)new PageInfo<>(propertyList).getTotal();
        page.setTotal(total);
        page.setParam("&cid="+category.getId());
        model.addAttribute("propertyList",propertyList);
        model.addAttribute("category",category);
        model.addAttribute("page",page);
        return "admin/listProperty";
    }

查询地址admin_property_list映射的是PropertyController的list()方法
1. 获取分类 cid,和分页对象page
2. 通过PageHelper设置分页参数
3. 基于cid,获取当前分类下的属性集合
4. 通过PageInfo获取属性总数
5. 把总数设置给分页page对象
6. 拼接字符串"&cid="+c.getId(),设置给page对象的Param值。 因为属性分页都是基于当前分类下的分页,所以分页的时候需要传递这个cid
7. 把属性集合设置到 request的 "propertyList" 属性上
8. 把分类对象设置到 request的 "category" 属性上。(面包屑中使用)
9. 把分页对象设置到 request的 "page" 对象上
10. 服务端跳转到admin/listProperty.jsp页面
11. 在listProperty.jsp页面上使用c:forEach 遍历ps集合,并显示

2.3.2 增加

    @RequestMapping("admin_property_add")
    public String add(Property property){
        propertyService.add(property);
        return "redirect:admin_property_list?cid="+property.getCid();
    }

2.3.3 编辑

1. 在PropertyController的edit方法中,根据id获取Property对象
2. 根据properoty对象的cid属性获取Category对象,并把其设置在Property对象的category属性上,面包屑上使用。
3. 把Property对象放在request的 "property" 属性中
4. 服务端跳转到admin/editProperty.jsp
5. 在editProperty.jsp中显示属性名称
6. 在editProperty.jsp中隐式提供id和cid( cid 通过 p.category.id 获取),这样才能确定是对哪一个分类下的属性进行的修改。

property表结构:

其中cid就是对应的分类id。

    @RequestMapping("admin_property_edit")
    public String edit(Model model,Integer id){
        Property property = propertyService.get(id);
        Category category = categoryService.get(property.getCid());
        property.setCategory(category);
        model.addAttribute("property",property);
        return "admin/editProperty";
    }

2.3.4 修改

    @RequestMapping("admin_property_update")
    public String update(Property property){
        propertyService.update(property);
        return "redirect:admin_property_list?cid="+property.getCid();
    }

2.3.5 删除

    @RequestMapping("admin_property_delete")
    public String delete(Integer id){
        Property property = propertyService.get(id);
        propertyService.delete(id);
        return "redirect:admin_property_list?cid="+property.getCid();
    }

2.3.6 分页

这里的分页比起分类管理中的分页多了一个参数cid,因为属性分页查询时要确定是哪一个分类下的属性,所以需要cid。

1. 在PropertyController.list() 方法中,把&cid= 参数设置到在page对象的param属性上

page.setParam("&cid="+c.getId());

2. 在adminPage.jsp页面中通过${page.param}取出这个参数

三、产品管理

3.1 前端需求

编辑时需要跳转页面

3.2 前端代码

<div class="workingArea">

    <ol class="breadcrumb">
        <li><a href="admin_category_list">所有分类</a></li>
        <li><a href="admin_product_list?cid=${category.id}">${category.name}</a></li>
        <li class="active">产品管理</li>
    </ol>

    <div class="listDataTableDiv">
        <table class="table table-striped table-bordered table-hover  table-condensed">
            <c:if test="${products.size() != 0}">
                <thead>
                    <tr class="success">
                        <th>ID</th>
                        <th>图片</th>
                        <th>产品名称</th>
                        <th>产品小标题</th>
                        <th width="53px">原价格</th>
                        <th width="80px">优惠价格</th>
                        <th width="80px">库存数量</th>
                        <th width="80px">图片管理</th>
                        <th width="80px">设置属性</th>
                        <th width="42px">编辑</th>
                        <th width="42px">删除</th>
                    </tr>
                </thead>
                <tbody>
                <c:forEach items="${products}" var="p">
                    <tr>
                        <td>${p.id}</td>
                        <td>
                            <c:if test="${!empty p.productImage}">
                                <img height="60px" width="100px"  src="${pageContext.request.contextPath}/image/productSingle/${p.productImage.id}.jpg">
                            </c:if>
                        </td>
                        <td>${p.name}</td>
                        <td>${p.subTitle}</td>
                        <td>${p.originalPrice}</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_propertyValue_edit?pid=${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>
                </tbody>
            </c:if>
            <c:if test="${products.size() == 0}">
                <thead>
                    <tr class="success">
                        <th colspan="11" class="text-center">请添加数据</th>
                    </tr>
                </thead>
            </c:if>
        </table>
    </div>

    <div class="pageDiv">
        <%@include file="../include/admin/adminPage.jsp"%>
    </div>

    <div class="panel panel-warning addDiv">
        <div class="panel-heading">新增产品</div>
        <div class="panel-body">
            <form method="post" id="addForm" action="admin_product_add">
                <table class="addTable">
                    <tr>
                        <td>产品名称</td>
                        <td><input id="name" name="name" type="text" class="form-control"></td>
                    </tr>
                    <tr>
                        <td>产品小标题</td>
                        <td><input id="subTitle" name="subTitle" type="text" class="form-control"></td>
                    </tr>
                    <tr>
                        <td>原价格</td>
                        <td><input id="originalPrice" value="99.98" name="originalPrice" type="text" class="form-control"></td>
                    </tr>
                    <tr>
                        <td>优惠价格</td>
                        <td><input id="promotePrice"  value="19.98" name="promotePrice" type="text" class="form-control"></td>
                    </tr>
                    <tr>
                        <td>库存</td>
                        <td><input id="stock"  value="99" name="stock" type="text" class="form-control"></td>
                    </tr>
                    <tr class="submitTR">
                        <td colspan="2" align="center">
                            <input type="hidden" name="cid" value="${category.id}">
                            <button type="submit" class="btn btn-success">提 交</button>
                        </td>
                    </tr>
                </table>
            </form>
        </div>
    </div>

</div>

前端和前面的模块大同小异。

3.3 后端接口

在Product中添加category属性,以后的操作中会用到。

product表:

表中有cid。

3.3.1 查询

查询地址admin_product_list映射的是ProductController的list()方法
1. 获取分类 cid,和分页对象page
2. 通过PageHelper设置分页参数
3. 基于cid,获取当前分类下的产品集合
4. 通过PageInfo获取产品总数
5. 把总数设置给分页page对象
6. 拼接字符串"&cid="+c.getId(),设置给page对象的Param值。 因为产品分页都是基于当前分类下的分页,所以分页的时候需要传递这个cid
7. 把产品集合设置到 request的 "products" 产品上
8. 把分类对象设置到 request的 "category" 产品上,面包屑上使用,新增产品时需要。
9. 把分页对象设置到 request的 "page" 对象上
10. 服务端跳转到admin/listProduct.jsp页面
11. 在listProduct.jsp页面上使用c:forEach 遍历ps集合,并显示

    @RequestMapping("admin_product_list")
    public String list(int cid, Model model, Page page){
        Category category = categoryService.get(cid);
        PageHelper.offsetPage(page.getStart(),page.getCount());
        List<Product> products = productService.list(cid);
        int total = (int) new PageInfo<>(products).getTotal();
        page.setTotal(total);
        page.setParam("&cid="+category.getId());

        model.addAttribute("category",category);
        model.addAttribute("products",products);
        model.addAttribute("page",page);
        return "admin/listProduct";
    }

3.3.2 增加

1. 在listProduct.jsp提交数据的时候,除了提交产品名称,小标题,原价格,优惠价格,库存外还会提交cid
2. 在ProductController中获取Product对象,并插入到数据库
3. 客户端跳转到admin_product_list,并带上参数cid

    @RequestMapping("admin_product_add")
    public String add(Product product){
        product.setCreateDate(new Date());
        productService.add(product);
        return "redirect:admin_product_list?cid="+product.getCid();
    }

3.3.3 编辑

1. 在ProductController的edit方法中,根据id获取product对象
2. 根据product对象的cid产品获取Category对象,并把其设置在product对象的category产品上
3. 把product对象放在request的 "product" 产品中
3. 服务端跳转到admin/editProduct.jsp
4. 在editProduct.jsp中显示产品名称
5. 在editProduct.jsp中隐式提供id和cid( cid 通过 p.category.id 获取)

    @RequestMapping("admin_product_edit")
    public String edit(Model model, int id){
        Product product = productService.get(id);
        Category category = categoryService.get(product.getCid());
        product.setCategory(category);
        model.addAttribute("product",product);
        return "admin/editProduct";
    }

3.3.4 修改

    @RequestMapping("admin_product_update")
    public String update(Product product){
        productService.update(product);
        return "redirect:admin_product_list?cid="+product.getCid();
    }

3.3.5 删除

这里面也存在外键约束,所以要级联删除。

    @RequestMapping("admin_product_delete")
    public String delete(Integer id){
        Product product = productService.get(id);
        productService.delete(id);
        return "redirect:admin_product_list?cid="+product.getCid();
    }

3.4 category属性的用途

3.4.1 前端页面展示需要

3.4.2 商品编辑页面需要

编辑页面中面包屑要显示分类信息

修改完产品信息后,提交时要提交cid。

3.5 存在的问题

此时产品管理页面还无法显示具体的图片信息,此功能放在下一节介绍。

四、产品图片管理

4.1 前端需求

前端功能比较简单:添加和删除

4.2 前端代码

<div class="workingArea">
    <ol class="breadcrumb">
        <li><a href="admin_category_list">所有分类</a></li>
        <li><a href="admin_product_list?cid=${product.category.id}">${product.category.name}</a></li>
        <li class="active">${product.name}</li>
        <li class="active">产品图片管理</li>
    </ol>

    <table class="addPictureTable" align="center">
        <tr>
            <td class="addPictureTableTD">
                <div>
                    <div class="panel panel-warning addPictureDiv">
                        <div class="panel-heading">
                            新增产品<b class="text-primary">单个</b>图片
                        </div>
                        <div class="panel-body">
                            <form method="post" class="addFormSingle" action="admin_productImage_add" enctype="multipart/form-data">
                                <table class="addTable">
                                    <tr>
                                        <td>请选择本地图片尺寸400X400为佳</td>
                                    </tr>
                                    <tr>
                                        <td>
                                            <input id="filepathSingle" type="file" name="image" />
                                        </td>
                                    </tr>
                                    <tr class="submitTR">
                                        <td align="center">
                                            <input type="hidden" name="type" value="type_single" />
                                            <input type="hidden" name="pid" value="${product.id}" />
                                            <button type="submit" class="btn btn-success">提 交</button>
                                        </td>
                                    </tr>
                                </table>
                            </form>
                        </div>
                    </div>
                    <table class="table table-striped table-bordered table-hover  table-condensed">
                        <c:if test="${imageSingle.size() != 0}">
                            <thead>
                                <tr class="success">
                                    <th>ID</th>
                                    <th>产品单个图片缩略图</th>
                                    <th>删除</th>
                                </tr>
                            </thead>
                            <tbody>
                                <c:forEach items="${imageSingle}" var="pi">
                                    <tr>
                                        <td>${pi.id}</td>
                                        <td>
                                            <a title="点击查看原图" href="${pageContext.request.contextPath}/image/productSingle/${pi.id}.jpg"><img height="50px" src="${pageContext.request.contextPath}/image/productSingle/${pi.id}.jpg"></a>
                                        </td>
                                        <td>
                                            <a deleteLink="true" href="admin_productImage_delete?id=${pi.id}"><span class="glyphicon glyphicon-trash"></span></a>
                                        </td>

                                    </tr>
                                </c:forEach>
                            </tbody>
                        </c:if>
                        <c:if test="${imageSingle.size() != 0}">
                            <thead>
                                <tr class="success">
                                    <th colspan="3" class="text-center">请添加图片</th>
                                </tr>
                            </thead>
                        </c:if>
                    </table>
                </div>
            </td>
            <td class="addPictureTableTD">
                <div>
                    <div class="panel panel-warning addPictureDiv">
                        <div class="panel-heading">
                            新增产品<b class="text-primary">详情</b>图片
                        </div>
                        <div class="panel-body">
                            <form method="post" class="addFormDetail" action="admin_productImage_add" enctype="multipart/form-data">
                                <table class="addTable">
                                    <tr>
                                        <td>请选择本地图片宽度790为佳</td>
                                    </tr>
                                    <tr>
                                        <td>
                                            <input id="filepathDetail"  type="file" name="image" />
                                        </td>
                                    </tr>
                                    <tr class="submitTR">
                                        <td align="center">
                                            <input type="hidden" name="type" value="type_detail" />
                                            <input type="hidden" name="pid" value="${product.id}" />
                                            <button type="submit" class="btn btn-success">提 交</button>
                                        </td>
                                    </tr>
                                </table>
                            </form>
                        </div>
                        <table class="table table-striped table-bordered table-hover  table-condensed">
                            <c:if test="${imageDetail.size() != 0}">
                                <thead>
                                <tr class="success">
                                    <th>ID</th>
                                    <th>产品详情图片缩略图</th>
                                    <th>删除</th>
                                </tr>
                                </thead>
                                <tbody>
                                <c:forEach items="${imageDetail}" var="pi">
                                    <tr>
                                        <td>${pi.id}</td>
                                        <td>
                                            <a title="点击查看原图" href="${pageContext.request.contextPath}/image/productDetail/${pi.id}.jpg"><img height="50px" src="${pageContext.request.contextPath}/image/productDetail/${pi.id}.jpg"></a>
                                        </td>
                                        <td><a deleteLink="true" href="admin_productImage_delete?id=${pi.id}"><span class="glyphicon glyphicon-trash"></span></a></td>

                                    </tr>
                                </c:forEach>
                                </tbody>
                            </c:if>
                            <c:if test="${imageDetail.size() == 0}">
                                <thead>
                                    <tr class="success">
                                        <th colspan="3" class="text-center">请添加图片</th>
                                    </tr>
                                </thead>
                            </c:if>
                        </table>
                    </div>
                </div>
            </td>
        </tr>
    </table>
</div>

4.3 后端接口

4.3.1 查询

通过产品页面的图片管理访问ProductImageController的list()方法
1. 获取参数pid
2. 根据pid获取Product对象
3. 根据pid对象获取单个图片的集合ImageSingle 
4. 根据pid对象获取详情图片的集合ImageDetail
5. 把product 对象,ImageSingle ,ImageDetail放在model上
6. 服务端跳转到admin/listProductImage.jsp页面
7. 在listProductImage.jsp,使用c:forEach 遍历pisSingle 
8. 在listProductImage.jsp,使用c:forEach 遍历pisDetail

    @RequestMapping("admin_productImage_list")
    public String list(Integer pid, Model model){
        Product product = productService.get(pid);

        List<ProductImage> imageSingle = productImageService.list(pid,ProductImageService.TYPE_SINGLE);
        List<ProductImage> imageDetail = productImageService.list(pid,ProductImageService.TYPE_DETAIL);

        model.addAttribute("product",product);
        model.addAttribute("imageSingle",imageSingle);
        model.addAttribute("imageDetail",imageDetail);

        return "admin/listProductImage";
    }

4.3.2 增加

增加产品图片分单个和详情两种,其区别在于增加所提交的type类型不一样。将处理图片单独抽取为一个函数ImageAddProcess。

首先, 在listProductImage.jsp准备一个form,提交到admin_productImage_add

<form method="post" class="addFormSingle" action="admin_productImage_add" enctype="multipart/form-data">
接着在ProductImageController的add()方法中进行处理
1. 通过pi对象接受type和pid的注入
2. 借助productImageService,向数据库中插入数据。

    @RequestMapping("admin_productImage_add")
    public String add(ProductImage productImage, HttpSession session, UploadedImageFile uploadedImageFile) throws IOException {
        productImageService.add(productImage);
        ImageAddProcess(productImage,session,uploadedImageFile);
        return "redirect:admin_productImage_list?pid="+productImage.getPid();
    }

ImageAddProcess函数中
3. 根据session().getServletContext().getRealPath( "img/productSingle"),定位到存放单个产品图片的目录除了productSingle,还有productSingle/middle和productSingle/small。 因为每上传一张图片,都会有对应的正常,中等和小的三种大小图片,并且放在3个不同的目录下
4. 文件命名以保存到数据库的产品图片对象的id+".jpg"的格式命名
5. 通过uploadedImageFile保存文件
6. 借助ImageUtil.change2jpg()方法把格式真正转化为jpg,而不仅仅是后缀名为.jpg
7. 再借助ImageUtil.resizeImage把正常大小的图片,改变大小之后,分别复制到productSingle_middle和productSingle_small目录下。
8. 处理完毕之后,客户端条跳转到admin_productImage_list?pid=,并带上pid。

详情图片做的是一样的事情,区别在于复制到目录productDetail下,并且不需要改变大小

    public void ImageAddProcess(ProductImage productImage,HttpSession session,UploadedImageFile uploadedImageFile) throws IOException{
        String fileName = productImage.getId() + ".jpg";
        String imageFolder;
        String imageFolder_small = null;
        String imageFolder_middle = null;

        //如果上传的是产品单个图片的缩略图,将图片转换成两种不同规格大小的图片
        if (ProductImageService.TYPE_SINGLE.equals(productImage.getType())){
            imageFolder = session.getServletContext().getRealPath("image/productSingle");
            imageFolder_small = session.getServletContext().getRealPath("image/productSingle/small");
            imageFolder_middle = session.getServletContext().getRealPath("image/productSingle/middle");
        }else {
            //如果上传的是产品详情图片缩略图
            imageFolder = session.getServletContext().getRealPath("image/productDetail");
        }

        //创建文件夹
        File file = new File(imageFolder,fileName);
        if(!file.getParentFile().exists()){
            file.getParentFile().mkdirs();
        }

        uploadedImageFile.getImage().transferTo(file);
        BufferedImage image = ImageUtil.change2jpg(file);
        ImageIO.write(image,"jpg",file);

        if (ProductImageService.TYPE_SINGLE.equals(productImage.getType())){
            File file_small = new File(imageFolder_small,fileName);
            File file_middle = new File(imageFolder_middle,fileName);

            ImageUtil.resizeImage(file,56,56,file_small);
            ImageUtil.resizeImage(file,217,190,file_middle);
        }
    }

4.3.3 删除

点击删除超链,进入ProductImageController的delete方法
1. 获取id
2. 根据id获取ProductImage 对象pi
3. 借助productImageService,删除数据
具体删除图片操作在函数ImageDeleteProcess中

    @RequestMapping("admin_productImage_delete")
    public String delete(Integer id,HttpSession session){
        ProductImage productImage = productImageService.get(id);
        ImageDeleteProcess(productImage,session);
        productImageService.delete(id);
        return "redirect:admin_productImage_list?pid="+productImage.getPid();
    }

4. 如果是单个图片,那么删除3张正常,中等,小号图片
5. 如果是详情图片,那么删除一张图片
6. 客户端跳转到admin_productImage_list地址

    public void ImageDeleteProcess(ProductImage productImage,HttpSession session){
        String fileName = productImage.getId() + ".jpg";
        String imageFolder;
        String imageFolder_small = null;
        String imageFolder_middle = null;

        if (ProductImageService.TYPE_SINGLE.equals(productImage.getType())){
            imageFolder = session.getServletContext().getRealPath("image/productSingle");
            imageFolder_small = session.getServletContext().getRealPath("image/productSingle/small");
            imageFolder_middle = session.getServletContext().getRealPath("image/productSingle/middle");

            File imageFile = new File(imageFolder,fileName);
            File file_small = new File(imageFolder_small,fileName);
            File file_middle = new File(imageFolder_middle,fileName);

            imageFile.delete();
            file_small.delete();
            file_middle.delete();
        }else {
            imageFolder = session.getServletContext().getRealPath("image/productDetail");
            File imageFile = new File(imageFolder,fileName);
            imageFile.delete();
        }
    }

4.4 产品管理重构

回到产品管理页面,在产品列表页面,是没有图片的。 因为当时还没有产品图片管理功能,现在支持了,所以需要对Product做一些调整。

4.4.1 新增属性

4.4.2 ProductService中新增方法

4.4.3 ProductServiceImpl中实现新增方法

增加方法 setFirstProductImage(Product p):
根据pid和图片类型查询出所有的单个图片,然后把第一个取出来放在firstProductImage上。

增加方法 setFirstProductImage(List<Product> ps)
给多个产品设置图片

在get方法中调用setFirstProductImage(Product p) 为单个产品设置图片
在list方法中调用setFirstProductImage(List<Product> ps) 为多个产品设置图片

package com.li.tmall.service.impl;

import com.li.tmall.mapper.ProductMapper;
import com.li.tmall.pojo.Category;
import com.li.tmall.pojo.Product;
import com.li.tmall.pojo.ProductExample;
import com.li.tmall.pojo.ProductImage;
import com.li.tmall.service.CategoryService;
import com.li.tmall.service.ProductImageService;
import com.li.tmall.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @Author: 98050
 * Time: 2018-09-18 20:31
 * Feature:CRUD
 */
@Service
public class ProductServiceImpl implements ProductService {

    @Autowired
    private ProductMapper productMapper;

    @Autowired
    private CategoryService categoryService;

    @Autowired
    private ProductImageService productImageService;

    @Override
    public void add(Product product) {
        productMapper.insert(product);
    }

    @Override
    public void delete(Integer id) {
        productMapper.deleteByPrimaryKey(id);
    }

    @Override
    public void update(Product product) {
        productMapper.updateByPrimaryKeySelective(product);
    }

    @Override
    public Product get(Integer id) {
        Product product = productMapper.selectByPrimaryKey(id);
        set(product);
        return product;
    }

    @Override
    public List<Product> list(Integer cid) {
        ProductExample example = new ProductExample();
        example.createCriteria().andCidEqualTo(cid);
        example.setOrderByClause("id desc");
        List<Product> products = productMapper.selectByExample(example);
        setCategory(products);
        setFirstProductImage(products);
        return products;
    }

    @Override
    public void setFirstProductImage(Product product) {
        List<ProductImage> list = productImageService.list(product.getId(),ProductImageService.TYPE_SINGLE);
        if (!list.isEmpty()){
            ProductImage productImage = list.get(0);
            product.setProductImage(productImage);
        }
    }

    public void setFirstProductImage(List<Product> products){
        for (Product product : products){
            setFirstProductImage(product);
        }
    }

    public void setCategory(List<Product> products){
        for (Product p:products){
            set(p);
        }
    }

    public void set(Product product){
        int cid = product.getCid();
        Category category = categoryService.get(cid);
        product.setCategory(category);
    }

}

4.4.4 前端页面使用

 

猜你喜欢

转载自blog.csdn.net/lyj2018gyq/article/details/82765081