牛骨文教育服务平台(让学习变的简单)
博文笔记

后台系统开发

创建时间:2017-06-02 投稿人: 浏览次数:3592

后台管理系统工程

1、采用maven构建项目

  • Maven定义了软件开发的整套流程体系,并进行了封装,开发人员只需要指定项目的构建流程,无需针对每个流程编写自己的构建脚本。除了项目构建,Maven最核心的功能是软件包的依赖管理,能够自动分析项目所需要的依赖软件包,并到Maven中心仓库去下载。并统一管理依赖的jar包。和工程之间的依赖关系。
  • 使用Maven的本地仓库:
    在当前系统用户的文件夹下。例如当前用户是Administrator那么本地仓库就是在C:UsersAdministrator.m2目录下。若要自定义仓库位置只需要修改Maven的配置文件即可。关于Maven的更多介绍可以参考这里。点我
  • Maven插件使用eclipse mars自带maven插件。只需要统一开发环境。

2、创建工程

  • 后台系统项目依赖关系

enjoyshop-parent 管理依赖jar包的版本,全局,公司级别,pom信息如下:

    <groupId>com.enjoyshop.parent</groupId>
    <artifactId>enjoyshop-parent</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>pom</packaging>

enjoyshop-common –通用组件、工具类

    <parent>
        <groupId>com.enjoyshop.parent</groupId>
        <artifactId>enjoyshop-parent</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <groupId>com.enjoyshop.common</groupId>
    <artifactId>enjoyshop-common</artifactId>
    <version>1.0.0-SNAPSHOT</version>

enjoyshop-manage –后台系统

    <parent>
        <groupId>com.enjoyshop.parent</groupId>
        <artifactId>enjoyshop-parent</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <groupId>com.enjoyshop.manage</groupId>
    <artifactId>enjoyshop-manage</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>pom</packaging>
    <modules>
        <module>enjoyshop-manage-pojo</module>
        <module>enjoyshop-manage-mapper</module>
        <module>enjoyshop-manage-service</module>
        <module>enjoyshop-manage-web</module>
    </modules>

    <dependencies>
        <dependency>
            <groupId>com.enjoyshop.common</groupId>
            <artifactId>enjoyshop-common</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>
        <!--省略了其他信息-->
    </dependencies>

enjoyshop.manage.pojo

    <parent>
        <groupId>com.enjoyshop.manage</groupId>
        <artifactId>enjoyshop-manage</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    <artifactId>enjoyshop-manage-pojo</artifactId>

enjoyshop.manage.mapper

    <parent>
        <groupId>com.enjoyshop.manage</groupId>
        <artifactId>enjoyshop-manage</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    <artifactId>enjoyshop-manage-mapper</artifactId>
    <dependencies>
        <dependency>
            <groupId>com.enjoyshop.manage</groupId>
            <artifactId>enjoyshop-manage-pojo</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>
        <!--省略了其他信息-->
    </dependencies>

enjoyshop.manage.service

    <parent>
        <groupId>com.enjoyshop.manage</groupId>
        <artifactId>enjoyshop-manage</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    <artifactId>enjoyshop-manage-service</artifactId>

    <dependencies>
        <dependency>
            <groupId>com.enjoyshop.manage</groupId>
            <artifactId>enjoyshop-manage-mapper</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>
        <!--省略了其他信息-->
    </dependencies>

enjoyshop.manage.web

    <parent>
        <groupId>com.enjoyshop.manage</groupId>
        <artifactId>enjoyshop-manage</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    <artifactId>enjoyshop-manage-web</artifactId>
    <packaging>war</packaging>

    <dependencies>
        <dependency>
            <groupId>com.enjoyshop.manage</groupId>
            <artifactId>enjoyshop-manage-service</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>
        <!--省略了其他信息-->
    </dependencies>
  • 导入依赖和tomcat插件

导入依赖的原则:
1、在使用依赖的最底层导入。
2、运行时所需要的依赖在web工程中加入。
3、所有的工程都需要的依赖应该在聚合工程(enjoyshop-manage)中导入。
导入SSM依赖:
Spring和SpringMVC的依赖在enjoysho-manage-service中导入;
导入tomcat插件:
聚合工程的tomcat插件要在聚合工程中导入,enjoysho-manage中导入。

    <build>
        <plugins>
            <!-- 配置Tomcat插件 -->
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <configuration>
                    <port>8081</port>
                    <path>/</path>
                </configuration>
            </plugin>
        </plugins>
    </build>

3、初步整合SSM

  • 在enjoyshop.manage.web项目的相应目录下创建web.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    id="WebApp_ID" version="2.5">
    <display-name>enjoyshop-manage</display-name>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring/applicationContext*.xml</param-value>
    </context-param>

    <!--Spring的ApplicationContext 载入 -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- 编码过滤器,以UTF8编码 -->
    <filter>
        <filter-name>encodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- 解决PUT请求无法提交表单数据的问题 -->
    <filter>
        <filter-name>HttpMethodFilter</filter-name>
        <filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>HttpMethodFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- 将POST请求转化为DELETE或者是PUT 要用_method指定真正的请求参数 -->
    <filter>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>


    <!-- 配置SpringMVC框架入口 -->
    <servlet>
        <servlet-name>enjoyshop-manage</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring/enjoyshop-manage-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>enjoyshop-manage</servlet-name>
        <url-pattern>/rest/*</url-pattern>
    </servlet-mapping>

    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>

</web-app>
  • 加入Sping的配置文件

applicationContext.xml(spring关于Bean的配置文件)

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
    http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">

    <!-- 使用spring自带的占位符替换功能 -->
    <bean
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <!-- 允许JVM参数覆盖 -->
        <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
        <!-- 忽略没有找到的资源文件 -->
        <property name="ignoreResourceNotFound" value="true" />
        <!-- 配置资源文件 -->
        <property name="locations">
            <list>
                <value>classpath:jdbc.properties</value>
            </list>
        </property>
    </bean>

    <!-- 扫描包 -->
    <context:component-scan base-package="com.enjoyshop"/>

     <!-- 定义数据源 -->
    <bean id="dataSource" class="com.jolbox.bonecp.BoneCPDataSource"
        destroy-method="close">
        <!-- 数据库驱动 -->
        <property name="driverClass" value="${jdbc.driverClassName}" />
        <!-- 相应驱动的jdbcUrl -->
        <property name="jdbcUrl" value="${jdbc.url}" />
        <!-- 数据库的用户名 -->
        <property name="username" value="${jdbc.username}" />
        <!-- 数据库的密码 -->
        <property name="password" value="${jdbc.password}" />
        <!-- 检查数据库连接池中空闲连接的间隔时间,单位是分,默认值:240,如果要取消则设置为0 -->
        <property name="idleConnectionTestPeriod" value="60" />
        <!-- 连接池中未使用的链接最大存活时间,单位是分,默认值:60,如果要永远存活设置为0 -->
        <property name="idleMaxAge" value="30" />
        <!-- 每个分区最大的连接数 -->
        <!-- 
            判断依据:请求并发数
         -->
        <property name="maxConnectionsPerPartition" value="100" />
        <!-- 每个分区最小的连接数 -->
        <property name="minConnectionsPerPartition" value="5" />
    </bean>

</beans>

applicationContext-transaction.xml(Spring关于事务的配置文件)

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
    http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">

    <!-- 定义事务管理器 -->
    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <!-- 定义事务策略 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!--所有以query开头的方法都是只读的 -->
            <tx:method name="query*" read-only="true" />
            <!--其他方法使用默认事务策略 -->
            <tx:method name="*" />
        </tx:attributes>
    </tx:advice>

    <aop:config>
        <!--pointcut元素定义一个切入点,execution中的第一个星号 用以匹配方法的返回类型,这里星号表明匹配所有返回类型。 -->
        <aop:pointcut id="myPointcut" expression="execution(* com.enjoyshop.manage.service.*.*(..))" />
        <!--将定义好的事务处理策略应用到上述的切入点 -->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut" />
    </aop:config>

</beans>
  • 加入SpringMVC的配置文件

enjoyshop-manage-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">

    <!-- 定义注解驱动 -->
    <mvc:annotation-driven/>

    <!-- 定义Controller的扫描包 -->
    <context:component-scan base-package="com.enjoyshop.manage.controller"/>

    <!-- 定义试图解析器 -->
    <!-- 
        Example: prefix="/WEB-INF/jsp/", suffix=".jsp", viewname="test" -> "/WEB-INF/jsp/test.jsp" 
     -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

</beans>
  • 加入Mybatis的配置文件

mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <plugins>
        <!-- 配置分页助手 -->
        <plugin interceptor="com.github.pagehelper.PageHelper">
            <property name="dialect" value="mysql" />
            <!-- 设置为true时,使用RowBounds分页会进行count查询 -->
            <property name="rowBoundsWithCount" value="true" />
        </plugin>

        <!-- 通用Mapper -->
        <plugin interceptor="com.github.abel533.mapperhelper.MapperInterceptor">
            <!--主键自增回写方法,默认值MYSQL,详细说明请看文档 -->
            <property name="IDENTITY" value="MYSQL" />
            <!--通用Mapper接口,多个通用接口用逗号隔开 -->
            <property name="mappers" value="com.github.abel533.mapper.Mapper" />
        </plugin>
    </plugins>

</configuration>
  • 关于SSM整合可以参考这里。点我
  • 关于分页助手和通用Mapper可以参考这里。点我

4、导入数据库数据

关于数据库设计可以参考这里。点我
也可以直接到数据库SQL这个目录下找到SQL脚本。

5、实现页面的通用跳转

package com.enjoyshop.manage.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@RequestMapping("page")//这个类拦截类似/page/*这种请求
@Controller  //标记为一个controller
public class PageController {
    @RequestMapping(value = "{pageName}", method = RequestMethod.GET)
    public String toPage(@PathVariable("pageName") String pageName) {
        return pageName;
    }

}
@RequestMapping(value = "{pageName}", method = RequestMethod.GET)

这行代码注解表示toPage这个方法接受一个占位符参数pageName(用大括号扩起来的就是占位符参数),并且指定了这个方法只响应GET请求。之后toPage通过
(@PathVariable("pageName") String pageName
这种形式来接受占位符参数,并把这个值赋给方法的输入参数pageName。
综合分析,如果请求的URL是/rest/page/index.jsp,那么首先SpringMVC会拦截/rest/*的请求,之后交给这个类来处理。此时传给toPage方法的占位符参数是“index”,那么该方法也就返回“index”这个字符串,经过springMVC解析后,返回WEB-INF/views/index.jsp这个视图。

6、登录逻辑的实现

  • URL:rest/page/login.jsp
  • 处理逻辑:
    <script type="text/javascript">
        $("#login").click(function(){
            var username = $("[name=username]").val();
            var password = $("[name=password]").val();

            if(username!="admin" || password!="admin"){
                $.messager.alert("错误","用户名密码不正确!");
                return ;
            }
            window.location.href="/rest/page/index";
        });
    </script>

这里简化了用户登录的功能,直接检测用户名和密码是否为“admin”。在实际应用中应当连接数据库进行查询再返回结果。

7、首页菜单树的实现

  • URL:rest/page/index.jsp
<body class="easyui-layout">
    <div data-options="region:"west",title:"菜单",split:true" style="width:180px;">
        <ul id="menu" class="easyui-tree" style="margin-top: 10px;margin-left: 5px;">
            <li>
                <span>商品管理</span>
                <ul>
                    <li data-options="attributes:{"url":"/rest/page/item-add"}">新增商品</li>
                    <li data-options="attributes:{"url":"/rest/page/item-list"}">查询商品</li>
                    <li data-options="attributes:{"url":"/rest/page/item-param-list"}">规格参数</li>
                </ul>
            </li>
            <li>
                <span>网站内容管理</span>
                <ul>
                    <li data-options="attributes:{"url":"/rest/page/content-category"}">内容分类管理</li>
                    <li data-options="attributes:{"url":"/rest/page/content"}">内容管理</li>
                </ul>
            </li>
         </ul>
    </div>
    <div data-options="region:"center",title:""">
        <div id="tabs" class="easyui-tabs">
            <div title="首页" style="padding:20px;">

            </div>
        </div>
    </div>

首页的布局通过easyui-layout来实现,而左侧菜单是通过定义一个easyui-tree来实现。

<ul id="menu" class="easyui-tree" style="margin-top: 10px;margin-left: 5px;">
  • 菜单点击事件
<script type="text/javascript">
$(function(){
    $("#menu").tree({
        onClick: function(node){//node是点击的节点对象
            if($("#menu").tree("isLeaf",node.target)){//判断当前节点是不是叶子节点,若是父节点则不作处理
                var tabs = $("#tabs");
                var tab = tabs.tabs("getTab",node.text);//通过叶子节点的名称查询tab
                if(tab){//tab已存在,表示页面已打开,则选中打开的tab
                    tabs.tabs("select",node.text);
                }else{
                    tabs.tabs("add",{
                        title:node.text,
                        href: node.attributes.url,
                        closable:true,
                        bodyCls:"content"
                    });
                }
            }
        }
    });
});

8、显示商品类目的功能实现

  • 功能描述:在新增商品时点击选择类目,弹出窗口,在窗口中显示商品类目数据。
  • 点击按钮描述
            <tr>
                <td>商品类目:</td>
                <td>
                    <a href="javascript:void(0)" class="easyui-linkbutton selectItemCat">选择类目</a>
                    <input type="hidden" name="cid" style="width: 280px;"></input>
                </td>
            </tr>

class="easyui-linkbutton selectItemCat
指明了这个按钮的属性是一个easyui-linkbutton,关联的点击事件是selectItemCat。而这个selectItemCat的定义在common.js中。

  • 点击弹出窗口的实现
// 初始化选择类目组件
    initItemCat : function(data){
        //i代表index索引,表示第几个元素
        //e代表element元素
        $(".selectItemCat").each(function(i,e){
            var _ele = $(e);//把dom对象转为JQuery对象
            if(data && data.cid){
                _ele.after("<span style="margin-left:10px;">"+data.cid+"</span>");
            }else{
                _ele.after("<span style="margin-left:10px;"></span>");
            }
            _ele.unbind("click").click(function(){//解绑事件再绑定事件,可以防止重复绑定(绑定之前清除所有其他绑定)
                $("<div>").css({padding:"5px"}).html("<ul>")//$("<div>")表示创建一个div,.css表示给予样式,.html写入ul元素
                .window({
                    width:"500",
                    height:"450",
                    modal:true,
                    closed:true,
                    iconCls:"icon-save",
                    title:"选择类目",
                    onOpen : function(){//窗口打开时执行
  • 加载并显示商品类目数据
 onOpen : function(){//窗口打开时执行
                        var _win = this;//this指的是$("<div>")
                        $("ul",_win).tree({//$("ul",_win)表示在$("<div>")下去查找ul元素
                            url:"/rest/item/cat",
                            animate:true,
                            method:"GET",
                            onClick : function(node){
                                if($(this).tree("isLeaf",node.target)){
                                    // 填写到cid中
                                    _ele.parent().find("[name=cid]").val(node.id);//设置隐藏框的值为节点id
                                    _ele.next().text(node.text).attr("cid",node.id);
                                    $(_win).window("close");
                                    if(data && data.fun){
                                        data.fun.call(this,node);
                                    }
                                }
                            }
                        });
                    },
                    onClose : function(){//窗口关闭事件
                        $(this).window("destroy");
                    }
  • 在item-add.jsp上的初始化
    以上的事件函数定义在一个名为ENJOYSHOP的对象中
var TT = ENJOYSHOP = {
·····
 // 初始化选择类目组件
    initItemCat : function(data){
    ······
    }
}

然后再jsp页面上通过ENJOYSHOP.ini的方法实现初始化和加载。

$(function(){
        itemAddEditor = ENJOYSHOP.createEditor("#itemAddForm [name=desc]");
        ENJOYSHOP.init({fun:function(node){
            ENJOYSHOP.changeItemParam(node, "itemAddForm");
        }});
    });

最后在TT.init中调用initItemCat方法,给class=”selectItemCat”绑定click事件,即可实现效果。

  • 编写商品类目的POJO类

首先编写一个BasePojo类,存放一些基础通用数据。

package com.enjoyshop.manage.pojo;

import java.util.Date;

public abstract class BasePojo {

    private Date created;
    private Date updated;
    public Date getCreated() {
        return created;
    }
    public void setCreated(Date created) {
        this.created = created;
    }
    public Date getUpdated() {
        return updated;
    }
    public void setUpdated(Date updated) {
        this.updated = updated;
    }



}

编写ItemCat类,继承BasePojo

package com.enjoyshop.manage.pojo;

import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Table(name = "tb_item_cat")
public class ItemCat extends BasePojo {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private Long parentId;

    private String name;

    private Integer status;

    private Integer sortOrder;

    private Boolean isParent;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Long getParentId() {
        return parentId;
    }

    public void setParentId(Long parentId) {
        this.parentId = parentId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    public Integer getSortOrder() {
        return sortOrder;
    }

    public void setSortOrder(Integer sortOrder) {
        this.sortOrder = sortOrder;
    }

    public Boolean getIsParent() {
        return isParent;
    }

    public void setIsParent(Boolean isParent) {
        this.isParent = isParent;
    }

    // 扩展text字段,用于easyui的tree组件使用
    public String getText() {
        return this.getName();
    }

    //如果是父节点则状态为closed,如果是不是父节点则直接显示
    public String getState() {
        return this.getIsParent() ? "closed" : "open";
    }

}

其中用到了JPA的注解。关于JPA注解请参考这里。点我
注意到该POJO类扩展了两个get方法:getText()和getState()方法,这是为了使得返回的数据满足EasyUI的tree的结构,否则EasyUI无法正确解析。

  • 编写Mapper接口

只需继承通用Mapper接口即可。

package com.enjoyshop.manage.mapper;

import com.enjoyshop.manage.pojo.ItemCat;
import com.github.abel533.mapper.Mapper;

public interface ItemCatMapper extends Mapper<ItemCat>{


}
  • 编写service层
    首先创建一个BaseService,通过spring4的泛型注入特性,抽取出一些通用的方法。
package com.enjoyshop.manage.service;

import java.util.Date;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;

import com.enjoyshop.manage.pojo.BasePojo;
import com.github.abel533.entity.Example;
import com.github.abel533.mapper.Mapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;

public abstract class BaseService<T extends BasePojo> {

    //public abstract Mapper<T> getMapper();

    //spring4支持泛型注入
    @Autowired
    private Mapper<T> mapper;

    /**
     * 根据id查询数据
     * 
     * @param id
     * @return
     */
    public T queryById(Long id) {
        return this.mapper.selectByPrimaryKey(id);
    }

    /**
     * 查询所有数据
     * 
     * @return
     */
    public List<T> queryAll() {
        return this.mapper.select(null);
    }

    /**
     * 根据条件查询一条数据
     * 
     * @param record
     * @return
     */
    public T queryOne(T record) {
        return this.mapper.selectOne(record);
    }

    /**
     * 根据条件查询数据列表
     * 
     * @param record
     * @return
     */
    public List<T> queryListByWhere(T record) {
        return this.mapper.select(record);
    }

    /**
     * 分页查询数据列表
     * 
     * @param page
     * @param rows
     * @param record
     * @return
     */
    public PageInfo<T> queryPageListByWhere(Integer page, Integer rows, T record) {
        // 设置分页参数
        PageHelper.startPage(page, rows);
        List<T> list = this.mapper.select(record);
        return new PageInfo<T>(list);
    }

    /**
     * 新增数据
     * 
     * @param t
     * @return
     */
    public Integer save(T t) {
        t.setCreated(new Date());
        t.setUpdated(t.getCreated());
        return this.mapper.insert(t);
    }

    /**
     * 有选择的保存,选择不为null的字段作为插入字段
     * 
     * @param t
     * @return
     */
    public Integer saveSelective(T t) {
        t.setCreated(new Date());
        t.setUpdated(t.getCreated());
        return this.mapper.insertSelective(t);
    }

    /**
     * 更新数据
     * 
     * @param t
     * @return
     */
    public Integer update(T t) {
        t.setUpdated(new Date());
        return this.mapper.updateByPrimaryKey(t);
    }

    /**
     * 有选择的更新,选择不为null的字段作为插入字段
     * 
     * @param t
     * @return
     */
    public Integer updateSelective(T t) {
        t.setUpdated(new Date());
        return this.mapper.updateByPrimaryKeySelective(t);
    }

    /**
     * 根据id删除数据
     * 
     * @param id
     * @return
     */
    public Integer deleteById(Long id) {
        return this.mapper.deleteByPrimaryKey(id);
    }

    /**
     * 批量删除
     * 
     * @param clazz
     * @param property
     * @param values
     * @return
     */
    public Integer deleteByIds(Class<T> clazz, String property, List<Object> values) {
        Example example = new Example(clazz);
        example.createCriteria().andIn(property, values);
        return this.mapper.deleteByExample(example);
    }

    /**
     * 根据条件删除数据
     * 
     * @param record
     * @return
     */
    public Integer deleteByWhere(T record){
        return this.mapper.delete(record);
    }
}

编写ItemCatService,只需继承BaseService,注入Mapper接口即可,不需要编写具体的业务处理逻辑,这些都已经抽取到BaseService中。

package com.enjoyshop.manage.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.enjoyshop.manage.mapper.ItemCatMapper;
import com.enjoyshop.manage.pojo.ItemCat;
import com.github.abel533.mapper.Mapper;

@Service
public class ItemCatService extends BaseService<ItemCat> {

     @Autowired
     private ItemCatMapper itemCatMapper;

    /**
     * 根据父节点id查询商品类目列表
     * 
     * @param parentId
     * @return
     */
//     public List<ItemCat> queryItemCatListByParentId(Long parentId) {
//     ItemCat record = new ItemCat();
//     record.setParentId(parentId);
//     return this.itemCatMapper.select(record);
//     }
     public Mapper<ItemCat> getMapper() {
     return this.itemCatMapper;
     }
}
  • 编写controller层
package com.enjoyshop.manage.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import com.enjoyshop.manage.pojo.ItemCat;
import com.enjoyshop.manage.service.ItemCatService;

@RequestMapping("item/cat")
@Controller
public class ItemCatController {

    @Autowired
    private ItemCatService itemCatService;

    @RequestMapping(method = RequestMethod.GET)
    public ResponseEntity<List<ItemCat>> queryItemCatListByParentId(
            @RequestParam(value = "id", defaultValue = "0") Long parentId) {
        try {
            // List<ItemCat> list = this.itemCatService.queryItemCatListByParentId(parentId);
            ItemCat record = new ItemCat();
            record.setParentId(parentId);
            List<ItemCat> list = this.itemCatService.queryListByWhere(record);
            if (null == list || list.isEmpty()) {
                // 资源不存在
                return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
            }
            return ResponseEntity.ok(list);
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 500
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
    }

}

因为在rest中,如果要抛出异常或者给出出錯信息,则需要返回相关的HTTP 状态码。所以最简单的方法就是使用ResponseEntity作为返回值。

9、新增商品规格参数和查询商品参数列表的实现

  • 前端实现
    1、当新增商品规格参数时,需要先确认对应的规格参数已经是否存在。若已存在则弹出提示窗口
ENJOYSHOP.initItemCat({
            fun:function(node){
            $(".addGroupTr").hide().find(".param").remove();
                //  判断选择的目录是否已经添加过规格    
              $.ajax({
                   type: "GET",
                   url: "/rest/item/param/" + node.id,
                   statusCode : {
                       200 : function(){
                           $.messager.alert("提示", "该类目已经添加,请选择其他类目。", undefined, function(){
                                 $("#itemParamAddTable .selectItemCat").click();
                              });
                       },
                       404 : function(){
                           $(".addGroupTr").show();
                       },
                       500 : function(){
                           alert("error");
                       }
                   }
                });
            }
        });

2、提交事件

$("#itemParamAddTable .submit").click(function(){
            var params = [];
            var groups = $("#itemParamAddTable [name=group]");
            groups.each(function(i,e){
                var p = $(e).parentsUntil("ul").parent().find("[name=param]");
                var _ps = [];
                p.each(function(_i,_e){
                    var _val = $(_e).siblings("input").val();
                    if($.trim(_val).length>0){
                        _ps.push(_val);                     
                    }
                });
                var _val = $(e).siblings("input").val();
                if($.trim(_val).length>0 && _ps.length > 0){
                    params.push({
                        "group":_val,
                        "params":_ps
                    });                 
                }
            });
            var url = "/rest/item/param/"+$("#itemParamAddTable [name=cid]").val();

            $.ajax({
                   type: "POST",
                   url: url,
                   //JSON.stringify(params)将参数序列化为json格式 
                   data: {"paramData":JSON.stringify(params)},
                   statusCode :{
                       201 : function(){
                           $.messager.alert("提示","新增商品规格成功!",undefined,function(){
                                $(".panel-tool-close").click();
                                $("#itemParamList").datagrid("reload");
                            });
                       },
                       400 : function(){
                           $.messager.alert("提示","提交的参数不合法!");
                       },
                       500 : function(){
                           $.messager.alert("提示","新增商品规格参数模板失败!");
                       }
                   }
                });
        });

这样提交的数据格式就是json格式。

3、查询规格参数列表的前端实现

<table class="easyui-datagrid" id="itemParamList" title="商品列表" 
       data-options="singleSelect:false,collapsible:true,pagination:true,url:"/rest/item/param",method:"get",pageSize:30,toolbar:itemParamListToolbar">
    <thead>
        <tr>
            <th data-options="field:"ck",checkbox:true"></th>
            <th data-options="field:"id",width:60">ID</th>
            <th data-options="field:"itemCatId",width:80">商品类目ID</th>
            <!-- <th data-options="field:"itemCatName",width:100">商品类目</th> -->
            <th data-options="field:"paramData",width:300">规格</th>
            <th data-options="field:"created",width:130,align:"center",formatter:ENJOYSHOP.formatDateTime">创建日期</th>
            <th data-options="field:"updated",width:130,align:"center",formatter:ENJOYSHOP.formatDateTime">更新日期</th>
        </tr>
    </thead>
</table>
  • 后台实现

service层关键代码

    @Autowired
    private ItemParamMapper itemParamMapper;

    public PageInfo<ItemParam> queryPageList(Integer page, Integer rows) {
        Example example=new Example(ItemParam.class);
        example.setOrderByClause("updated DESC");
        //设置分页参数
        PageHelper.startPage(page,rows);
        List<ItemParam> list=this.itemParamMapper.selectByExample(example);
        return new PageInfo<ItemParam>(list);
    }

controller层关键代码

    @Autowired
    private ItemParamService itemParamService;
    //对应的url是/rest/item/param/itemCatId
    //根据类目id查询模板数据
    @RequestMapping(value="{itemCatId}",method=RequestMethod.GET)
    public ResponseEntity<ItemParam> queryByItemCatId(@PathVariable("itemCatId") Long itemCatId){
        try {
            ItemParam record=new ItemParam();
            record.setItemCatId(itemCatId);
            ItemParam itemParam=this.itemParamService.queryOne(record);
            if(null==itemParam){
                return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
            }
            return ResponseEntity.ok(itemParam);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
    }

    //对应的url是/rest/item/param/itemCatId,和上面的url相同,但是请求方式不同。一个是get,一个是post,这就是rest的功能
    //保存商品规格参数模板
    @RequestMapping(value="{itemCatId}",method=RequestMethod.POST)
    public ResponseEntity<Void> saveItemParam(@RequestParam("paramData") String paramData,@PathVariable("itemCatId") Long itemCatId){
        try {
            ItemParam itemParam=new ItemParam();
            itemParam.setItemCatId(itemCatId);
            itemParam.setParamData(paramData);
            this.itemParamService.save(itemParam);
            return ResponseEntity.status(HttpStatus.CREATED).build();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
    }
    //查询所有商品规格参数列表
    @RequestMapping(method = RequestMethod.GET)
    public ResponseEntity<EasyUIResult> queryItemParamList(@RequestParam(value = "page", defaultValue = "1") Integer page,
            @RequestParam(value = "rows", defaultValue = "30") Integer rows) {
        try {
            PageInfo<ItemParam> pageInfo = this.itemParamService.queryPageList(page, rows);
            EasyUIResult easyUIResult = new EasyUIResult(pageInfo.getTotal(), pageInfo.getList());
            return ResponseEntity.ok(easyUIResult);
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 出错 500
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
    }

10、新增商品的功能实现

  • 商品价格的的处理
            <tr>
                <td>商品价格:</td>
                <td><input class="easyui-numberbox" type="text" name="priceView" data-options="min:1,max:99999999,precision:2,required:true" />
                    <input type="hidden" name="price"/>
                </td>
            </tr>

设置一个隐藏框,商品显示时以元为单位,提交时以分为单位。

        //处理商品的价格的单位,将元转化为分
        $("#itemAddForm [name=price]").val(eval($("#itemAddForm [name=priceView]").val()) * 100);
        //将编辑器中的内容同步到隐藏多行文本中
        itemAddEditor.sync();
  • 提交事件
//提交到后台的RESTful
        $.ajax({
           type: "POST",
           url: "/rest/item",
           data: $("#itemAddForm").serialize(),
           statusCode : {
               201 : function(){
                   $.messager.alert("提示","新增商品成功!");
               },
                400 : function(){
                   $.messager.alert("提示","提交的参数不合法!");
               },
               500 : function(){
                   $.messager.alert("提示","新增商品失败!");
               },
               404 : function(){
                   $.messager.alert("提示","页面不存在!");
               }
           }

        });
  • 显示商品规格参数
    前端页面实现
changeItemParam : function(node,formId){

        $.ajax({
               type: "GET",
               url: "/rest/item/param/" + node.id,
               statusCode : {
                   200 : function(data){
                       $("#"+formId+" .params").show();
                         var paramData = JSON.parse(data.paramData);
                         var html = "<ul>";
                         for(var i in paramData){
                             var pd = paramData[i];
                             html+="<li><table>";
                             html+="<tr><td colspan="2" class="group">"+pd.group+"</td></tr>";

                             for(var j in pd.params){
                                 var ps = pd.params[j];
                                 html+="<tr><td class="param"><span>"+ps+"</span>: </td><td><input autocomplete="off" type="text"/></td></tr>";
                             }

                             html+="</li></table>";
                         }
                         html+= "</ul>";
                         $("#"+formId+" .params td").eq(1).html(html);
                   },
                   404 : function(){
                       $("#"+formId+" .params").hide();
                         $("#"+formId+" .params td").eq(1).empty();
                   },
                   500 : function(){
                       alert("error");
                   }
               }
            });
    },
  • service层关键代码
 public void saveItem(Item item, String desc, String itemParams) {
        // 设置初始数据
        item.setStatus(1);

        item.setId(null);// 强制设置id为null

        super.save(item);

        ItemDesc itemDesc = new ItemDesc();
        itemDesc.setItemId(item.getId());
        itemDesc.setItemDesc(desc);
        // 保存描述数据
        this.itemDescService.save(itemDesc);

        // 保存规格参数数据
        ItemParamItem itemParamItem = new ItemParamItem();
        itemParamItem.setItemId(item.getId());
        itemParamItem.setParamData(itemParams);
        this.itemParamItemService.save(itemParamItem);
    }
  • controller层关键代码
 @RequestMapping(method = RequestMethod.POST)
    public ResponseEntity<Void> saveItem(Item item, @RequestParam("desc") String desc,
            @RequestParam("itemParams") String itemParams) {
        try {
            if (StringUtils.isEmpty(item.getTitle())) {
                // 响应400
                return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
            }

            // 保存商品的基本数据
            this.itemService.saveItem(item, desc, itemParams);
            }
            // 成功 201
            return ResponseEntity.status(HttpStatus.CREATED).build();
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 出错 500
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
    }

11、图片上传的实现

  • 前端实现
    前端图片的上传组件使用KindEditor的上传组件。通过如下方式指定了上传参数:
var TT = ENJOYSHOP = {
    // 编辑器文件上传参数
    kingEditorParams : {
        filePostName  : "uploadFile",
        uploadJson : "/rest/pic/upload",
        dir : "image"
    },

具体的实现逻辑如下:

 init : function(data){
        this.initPicUpload(data);
        this.initItemCat(data);
    },
    // 初始化图片上传组件
    initPicUpload : function(data){
        $(".picFileUpload").each(function(i,e){
            var _ele = $(e);
            _ele.siblings("div.pics").remove();
            _ele.after("
                <div class="pics">
                    <ul></ul>
                </div>");
            // 回显图片
            if(data && data.pics){
                var imgs = data.pics.split(",");
                for(var i in imgs){
                    if($.trim(imgs[i]).length > 0){
                        _ele.siblings(".pics").find("ul").append("<li><a href=""+imgs[i]+"" target="_blank"><img src=""+imgs[i]+"" width="80" height="50" /></a></li>");
                    }
                }
            }
            $(e).click(function(){
                var form = $(this).parentsUntil("form").parent("form");//从按钮向上查找到最近的一个form表单
                KindEditor.editor(TT.kingEditorParams).loadPlugin("multiimage",function(){
                    var editor = this;
                    editor.plugin.multiImageDialog({
                        //点击“全部插入”时执行
                        clickFn : function(urlList) {
                            var imgArray = [];
                            KindEditor.each(urlList, function(i, data) {
                                imgArray.push(data.url);
                                //小图片预览
                                form.find(".pics ul").append("<li><a href=""+data.url+"" target="_blank"><img src=""+data.url+"" width="80" height="50" /></a></li>");
                            });
                            //将URL通过逗号分隔写入隐藏域中
                            form.find("[name=image]").val(imgArray.join(","));

                            editor.hideDialog();
                        }
                    });
                });
            });
        });
    },
  • 通过Nginx配置图片服务器
    修改Nginx的配置文件nginx.conf,添加如下配置:
server {
        listen       80;
        server_name  manage.enjoyshop.com;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

    proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Server $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        location / {
        proxy_pass http://127.0.0.1:8081;
        proxy_connect_timeout 600;
        proxy_read_timeout 600;
        }

    }
server {
        listen       80;
        server_name  image.enjoyshop.com;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

    proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Server $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        location / {
        root  E:\picture-upload;
        }

    }

上面的配置中第一个server配置了后台系统的代理,使得URL:http://manage.enjoyshop.com指向http://127.0.0.1:8081。第二个server则配置了一个图片访问服务器,使得URL:http://image.enjoyshop.com指向了本地的静态资源E:picture-upload。与之相应的需要在本地的host文件中添加如下两个映射:

127.0.0.1 manage.enjoyshop.com
127.0.0.1 image.enjoyshop.com

关于Nginx的更多介绍请参考这里。点我

  • 后台实现

1、首先在SpringMVC的配置文件中添加一个文件上传解析器。

<!-- 定义文件上传解析器 -->
    <bean id="multipartResolver"
        class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!-- 设定默认编码 -->
        <property name="defaultEncoding" value="UTF-8"></property>
        <!-- 设定文件上传的最大值5MB,5*1024*1024 -->
        <property name="maxUploadSize" value="5242880"></property>
    </bean>

2、在类路径下新建一个外部属性文件env.properties,内容如下

REPOSITORY_PATH=E:\picture-upload
IMAGE_BASE_URL=http://image.enjoyshop.com

REPOSITORY_PATH是文件存储的物理路径。
IMAGE_BASE_UR是图片服务器的基础URL。
3、配置完成后在Spring的配置文件中加载该属性文件

        <!-- 配置资源文件 -->
        <property name="locations">
            <list>
                <value>classpath:jdbc.properties</value>
                <value>classpath:env.properties</value>
            </list>
        </property>

4、创建对象来进行图片的存储和校验,只需存储其URL地址即可。通过宽和高两个属性来鉴别文件是否是图片。

package com.enjoyshop.manage.bean;

public class PicUploadResult {

    //上传是否成功的判断标识,0-成功,1-失败
    private Integer error;

    private String url;

    private String width;

    private String height;

    public Integer getError() {
        return error;
    }

    public void setError(Integer error) {
        this.error = error;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getWidth() {
        return width;
    }

    public void setWidth(String width) {
        this.width = width;
    }

    public String getHeight() {
        return height;
    }

    public void setHeight(String height) {
        this.height = height;
    }



}

5、定义service层

package com.enjoyshop.manage.service;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Service
public class PropertieService {
    @Value("${REPOSITORY_PATH}")
    public String REPOSITORY_PATH;

    @Value("${IMAGE_BASE_URL}")
    public String IMAGE_BASE_URL;

}

service层中通过 @Value(“${xxxx}”)的注解获取到了Spring加载的外部属性文件中的值。
需要注意的是不能将这两个属性放入controller中,因为@Value作用是在Spring容器初始化(所有的bean)之后,在当前的所在容器中获取值,然后注入。假如把这两个属性放入controller中,那么相应的当前容器就是SpringMVC容器,而SpringMVC容器中并没有注入相应的值。因为我们是在Spring的配置文件中加载的外部属性文件,相应的值实际上是在Spring容器中。
关于Spring的父子容器:
Spring容器是父容器,SpringMVC容器是子容器。
父子容器的关系:
(1)、 子容器能够访问父容器的资源(bean)
示例:Controller(SpringMVC容器)可以注入Service(Spring容器)
(2)、 父容器不能访问子容器的资源(bean)
示例:无法在service层中注入controller

6、定义controller层


/**
 * 图片上传
 */
@Controller
@RequestMapping("/pic")
public class PicUploadController {

    private static final Logger LOGGER = LoggerFactory.getLogger(PicUploadController.class);

    private static final ObjectMapper mapper = new ObjectMapper();

    @Autowired
    private PropertieService propertieService;

    // 允许上传的格式
    private static final String[] IMAGE_TYPE = new String[] { ".bmp", ".jpg", ".jpeg", ".gif", ".png" };

    //produces: 指定响应的类型
    @RequestMapping(value = "/upload", method = RequestMethod.POST, produces = MediaType.TEXT_PLAIN_VALUE)
    @ResponseBody
    public String upload(@RequestParam("uploadFile") MultipartFile uploadFile, HttpServletResponse response)
            throws Exception {

        // 校验图片格式
        boolean isLegal = false;
        for (String type : IMAGE_TYPE) {
            if (StringUtils.endsWithIgnoreCase(uploadFile.getOriginalFilename(), type)) {
                isLegal = true;
                break;
            }
        }

        // 封装Result对象,并且将文件的byte数组放置到result对象中
        PicUploadResult fileUploadResult = new PicUploadResult();

        // 状态
        fileUploadResult.setError(isLegal ? 0 : 1);

        // 文件新路径
        String filePath = getFilePath(uploadFile.getOriginalFilename());

        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Pic file upload .[{}] to [{}] .", uploadFile.getOriginalFilename(), filePath);
        }

        // 生成图片的绝对引用地址
        String picUrl = StringUtils.replace(StringUtils.substringAfter(filePath, propertieService.REPOSITORY_PATH),
                "\", "/");
        fileUploadResult.setUrl(propertieService.IMAGE_BASE_URL + picUrl);

        File newFile = new File(filePath);

        // 写文件到磁盘
        uploadFile.transferTo(newFile);

        // 校验图片是否合法
        isLegal = false;
        try {
            BufferedImage image = ImageIO.read(newFile);
            if (image != null) {
                fileUploadResult.setWidth(image.getWidth() + "");
                fileUploadResult.setHeight(image.getHeight() + "");
                isLegal = true;
            }
        } catch (IOException e) {
        }

        // 状态
        fileUploadResult.setError(isLegal ? 0 : 1);

        if (!isLegal) {
            // 不合法,将磁盘上的文件删除
            newFile.delete();
        }

        //将java对象转化成(序列化)json字符串
        return mapper.writeValueAsString(fileUploadResult);
    }

    private String getFilePath(String sourceFileName) {
        String baseFolder = propertieService.REPOSITORY_PATH + File.separator + "images";
        Date nowDate = new Date();
        // yyyy/MM/dd
        String fileFolder = baseFolder + File.separator + new DateTime(nowDate).toString("yyyy")
                + File.separator + new DateTime(nowDate).toString("MM") + File.separator
                + new DateTime(nowDate).toString("dd");
        File file = new File(fileFolder);
        if (!file.isDirectory()) {
            // 如果目录不存在,则创建目录
            file.mkdirs();
        }
        // 生成新的文件名
        String fileName = new DateTime(nowDate).toString("yyyyMMddhhmmssSSSS")
                + RandomUtils.nextInt(100, 9999) + "." + StringUtils.substringAfterLast(sourceFileName, ".");
        return fileFolder + File.separator + fileName;
    }

}

因为需要把上传文件返回的数据设为文本类型的json数据,所以这里使用String作为方法的返回值类型,并且用@ResponseBody来注解该方法使其返回JSON数据。

12、实现商品列表的显示

  • 前台页面
<table class="easyui-datagrid" id="itemList" title="商品列表" 
       data-options="singleSelect:false,collapsible:true,pagination:true,url:"/rest/item",method:"get",pageSize:30,toolbar:toolbar">
    <thead>
        <tr>
            <th data-options="field:"ck",checkbox:true"></th>
            <th data-options="field:"id",width:60">商品ID</th>
            <th data-options="field:"title",width:200">商品标题</th>
            <th data-options="field:"cid",width:100">叶子类目</th>
            <th data-options="field:"sellPoint",width:100">卖点</th>
            <th data-options="field:"price",width:70,align:"right",formatter:ENJOYSHOP.formatPrice">价格</th>
            <th data-options="field:"num",width:70,align:"right"">库存数量</th>
            <th data-options="field:"barcode",width:100">条形码</th>
            <th data-options="field:"status",width:60,align:"center",formatter:ENJOYSHOP.formatItemStatus">状态</th>
            <th data-options="field:"created",width:130,align:"center",formatter:ENJOYSHOP.formatDateTime">创建日期</th>
            <th data-options="field:"updated",width:130,align:"center",formatter:ENJOYSHOP.formatDateTime">更新日期</th>
        </tr>
    </thead>
</table>

可以看到请求的URL为/rest/item,请求方式method是get。默认情况下,会直接显示返回的数据,但是有些属性下不能直接显示,如:价格、日期、性别,需要指定formatter函数。在common.js中有如下定义:

Date.prototype.format = function(format){ 
    var o =  { 
    "M+" : this.getMonth()+1, //month 
    "d+" : this.getDate(), //day 
    "h+" : this.getHours(), //hour 
    "m+" : this.getMinutes(), //minute 
    "s+" : this.getSeconds(), //second 
    "q+" : Math.floor((this.getMonth()+3)/3), //quarter 
    "S" : this.getMilliseconds() //millisecond 
    };
    if(/(y+)/.test(format)){ 
        format = format.replace(RegExp.$1, (this.getFullYear()+"").substr(4 - RegExp.$1.length)); 
    }
    for(var k in o)  { 
        if(new RegExp("("+ k +")").test(format)){ 
            format = format.replace(RegExp.$1, RegExp.$1.length==1 ? o[k] : ("00"+ o[k]).substr((""+ o[k]).length)); 
        } 
    } 
    return format; 
};
    // 格式化时间
    formatDateTime : function(val,row){
        var now = new Date(val);
        return now.format("yyyy-MM-dd hh:mm:ss");
    },
    // 格式化连接
    formatUrl : function(val,row){
        if(val){
            return "<a href=""+val+"" target="_blank">查看</a>";          
        }
        return "";
    },
    // 格式化价格
    formatPrice : function(val,row){
        return (val/1000).toFixed(2);
    },
    /
声明:该文观点仅代表作者本人,牛骨文系教育信息发布平台,牛骨文仅提供信息存储空间服务。