第八章WEB实训项目_快递驿站后台管理

第八章WEB实训项目_快递驿站后台管理

第一节编写mvc框架

1.基本概念

​ 在项目编写时,如果使用servlet/jsp等技术来做较繁琐。举个例子,我们要写一个demo,需实现注册、登录用户信息获取三个方法,原来我们需要给每一个方法都配一个servlet,流程不太清晰,概念也容易混淆。如果利用mvc框架进行编写,让他们都去找一个类,这个类有三个方法,当需要某个方法时,就去对应不同的servlet,如下图所示,这样就可以很好地优化代码,便于理解维护。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vjOFFgOX-1634895151780)(C:\Users\chenzhiyuan4\AppData\Roaming\Typora\typora-user-images\image-20211019151811499.png)]

​ 更重要的一个原因是这样做可以提前建立框架的认知,本章项目代码大致参考SpringMVC,对其关键性步骤进行处理,非关键性步骤弱化,以便于理解。

​ 我们去编写一个servlet,所有的*.do文件都去找这个servlet。servlet里面去重写初始化方法init,去加载配置文件,去建立映射地址的池(某个方法对应到某个类的某个方法)

流程:所有用户请求的入口servlet–>映射池–>调用方法–>将结果返回给用户

2.项目创建

​ 在idea创建javaweb项目;

​ 在src下新建一个servlet,名为DispatcherServlet,放于com.czy中;

​ 编写在web/WEB-INF下面的web.xml的配置文件,代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
   
    <!--自己加的-->
    <servlet>
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>com.czy.mvc.DispatcherServlet</servlet-class>
        <!--模拟spring MVC框架,需要添加一些配置参数,配置文件(.properties)默认在src目录下。servlet启动时会加载一个名为contentConfigLocation的初始化参数,参数里面描述了一个配置文件,它的名称为application.properties,我们需要编写其他代码去加载这个文件-->
        <init-param>
            <param-name>contentConfigLocation</param-name>
            <param-value>application.properties</param-value>
        </init-param>
        <!--项目启动时加载此servlet-->
        <load-on-startup>0</load-on-startup>
    </servlet>
    <!--映射地址-->
    <servlet-mapping>
        <servlet-name>DispatcherServlet</servlet-name>
        <!--所有以.do结尾的请求都会去找上面那行的DispatcherServlet-->
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>
    <!--自己加的-->
    
</web-app>

​ 接下来,给我们的servlet(即刚创建的DispatcherServlet)重写它的初始化方法;

    //DispatcherServlet的初识化方法init
    @Override
    public void init(ServletConfig config) throws ServletException {
        //利用ServletConfig类的名为config的对象,调用它的getInitParameter方法,
        //传入参数contentConfigLocation,这个是web.xml配置文件中初始化配置参数的名字
        //getInitParameter方法取到了名为contentConfigLocation的配置文件路径,将其命名为path
        String path = config.getInitParameter("contentConfigLocation");
        //通过上面得到的路径path,我们调用DispatcherServlet.class.getClassLoader()方法,获得一个输入流is
        InputStream is = DispatcherServlet.class.getClassLoader().getResourceAsStream(path);
        //在初识化servlet的时候就要加载配置文件的输入流,使用 HandlerMapping.load方法
        HandlerMapping.load(is);
    }

​ 通过反射技术,我们去读取这个配置文件application.properties中所有类的注解情况。除此之外,我们先要编写两个注解和一个枚举。

package com.czy.mvc;

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
/**
 * 注解的作用:
 *   被此注解添加的方法,会被用于处理请求
 *   方法返回的内容,会以文字方式返回到客户端
 */
public @interface ResponseBody {
    String value();
}
package com.czy.mvc;

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
/**
 * 注解的作用:
 *   被此注解添加的方法,会被用于处理请求
 *   方法返回的内容,会直接重定向
 */
public @interface ResponseView {
    String value();
}
package com.czy.mvc;

public enum ResponseType {
    TEXT, VIEW;
}

​ 编写DispatcherServlet:

package com.czy.mvc;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

@WebServlet(name = "DispatcherServlet")
public class DispatcherServlet extends HttpServlet {
    //DispatcherServlet的初识化方法init
    @Override
    public void init(ServletConfig config) throws ServletException {
        //利用ServletConfig类的名为config的对象,调用它的getInitParameter方法,
        //传入参数contentConfigLocation,这个是web.xml配置文件中初始化配置参数的名字
        //getInitParameter方法取到了名为contentConfigLocation的配置文件路径,将其命名为path
        String path = config.getInitParameter("contentConfigLocation");
        //通过上面得到的路径path,我们调用DispatcherServlet.class.getClassLoader()方法,获得一个输入流is
        InputStream is = DispatcherServlet.class.getClassLoader().getResourceAsStream(path);
        //在初识化servlet的时候就要加载配置文件的输入流,使用 HandlerMapping.load方法
        HandlerMapping.load(is);
    }

    @Override
    public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.获取用户的请求uri
        String uri = req.getRequestURI();
        //调用HandlerMapping类的get方法,传入uri,得到一个MVCMapping类的mapping对象
        HandlerMapping.MVCMapping mapping = HandlerMapping.get(uri);
        //若映射地址不存在,则返回null
        if (mapping == null) {
            resp.sendError(404, "自定义MVC:映射地址不存在" + uri);
            return;
        }

        Object obj = mapping.getObj();//用这个MVCMapping类的mapping对象,调用它的getOBJ方法,得到对象
        Method method = mapping.getMethod();//用这个MVCMapping类的mapping对象,调用它的getMethod方法,得到方法

        Object result = null;//向上抽取result,先新建出来
        try {
            result = method.invoke(obj, req, resp);//调用method的invoke方法,得到注释中事先约定好的返回结果(String类型的)
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

        //判断这个mapping返回类型是text还是view,向前台回复处理的结果
        switch (mapping.getType()) {
            case TEXT:
                resp.getWriter().write((String) result);
                break;
            case VIEW:
                resp.sendRedirect((String) result);
                break;
        }
    }

}

​ 编写HandlerMapping:

package com.czy.mvc;

import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * 映射器:包含了大量网址和方法的对应关系
 * 这个HandlerMapping类会包含n个MVCMapping对象
 * 用户每次发起请求的时候,我们就去HandlerMapping里面的Map找,用哪个方法来给他做处理
 * 这个类主要作用:用来描述一些处理请求的方法
 */
public class HandlerMapping {
    //我们读取配置文件,就会把配置文件所指向的那些类里面,所有用了那两个注解的方法封装成这样一个对象
    private static Map<String,MVCMapping> data = new HashMap<>();
    //从data这个对象里面,获取网址所对应的所有方法
    public static MVCMapping get(String uri){
        return data.get(uri);
    }

    public static void load(InputStream is){
    //is这个输入流能得到很多的类名,接下来获取这些很多的类名
        Properties ppt = new Properties();
        try {
            ppt.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //获取的values是application.properties里面的等号的右边的值(获取配置文件中的一个个类)
        Collection<Object> values = ppt.values();
        //遍历,一个个进行反射处理,把类进行加载
        for (Object cla:values){
            //获取的是一个个类对象,名为cla,下面一行代码是将其强转成类名字,类型为String
            String className = (String) cla;
            try {
                //用拿到的类的名称,去得到一个个具体的类(加载配置文件中描述的每一个类)
                Class c = Class.forName(className);
                //创建这个类的对象(用一个个具体的类,调用其无参的构造函数去创建具体的对象)
                Object obj = c.getConstructor().newInstance();
                //获取这个类的所有方法,名为methods
                Method[] methods = c.getMethods();
                //遍历每一个类中方法们的具体某个方法
                for (Method m: methods) {
                    //把每个方法的注解拿到,命名为as
                    Annotation[] as = m.getAnnotations();
                    if (as!=null){
                        for (Annotation annotation : as) {
                            if (annotation instanceof ResponseBody) {//说明该方法返回字符串给客户端
                                //为这个方法新建一个MVCMapping映射对象,名为mapping,把相关参数传进去(该方法当前在的类,该方法,返回值类型)
                                MVCMapping mapping = new MVCMapping(obj, m, ResponseType.TEXT);
                                //把相关数据放入HandlerMapping 这个大类中最前面的属性data,调用它的put方法
                                Object o = data.put((((ResponseBody) annotation).value()), mapping);
                                if (o != null){
                                    //存在了重复的地址
                                    throw new RuntimeException("请求地址重复"+((ResponseBody) annotation).value());
                                }
                            } else if (annotation instanceof  ResponseView) {//说明该方法返回页面给客户端
                                MVCMapping mapping = new MVCMapping(obj, m, ResponseType.VIEW);
                                Object o = data.put((((ResponseView) annotation).value()), mapping);
                                if (o != null){
                                    //存在了重复的地址
                                    throw new RuntimeException("请求地址重复"+((ResponseView) annotation).value());
                                }
                            }
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 映射对象:每一个映射对象封装了一个方法,用于处理请求
     */
    public static class MVCMapping {
        private Object obj;
        private Method method;
        private ResponseType type;

        public MVCMapping(Object obj, Method method, ResponseType type) {
            this.obj = obj;
            this.method = method;
            this.type = type;
        }

        public MVCMapping() {
        }

        public Object getObj() {
            return obj;
        }

        public void setObj(Object obj) {
            this.obj = obj;
        }

        public Method getMethod() {
            return method;
        }

        public void setMethod(Method method) {
            this.method = method;
        }

        public ResponseType getType() {
            return type;
        }

        public void setType(ResponseType type) {
            this.type = type;
        }
    }

}

​ 当服务器启动的时候,DispatcherServlet 就会去加载配置文件,会在HandlerMapping里面存储一堆的方法。

​ 当用户去请求DispatcherServlet 的时候,我们就去HandlerMapping里面去找有没有处理这个请求的方法,若无则404,若有,我们的方法开头一定会有一段注解,再进行后续处理。

第二节项目搭建与工具整理

​ 二维码生成和Layer部分省略,阿里云短信业务砍掉。

​ 将德鲁伊的配置文件放于src下

url=jdbc:mysql://localhost:3306/e?useUnicode=true&characterEncoding=utf-8&serverTimezone =Asia/Shanghai
username=root
password=123456
driverClassName=com.mysql.jdbc.Driver
initialSize=5
# 最大连接池数量
maxActive=10
# 最小连接池数量
minIdle=5
# 获取连接时最大等待时间,单位毫秒
maxWait=5000

​ 在web/WEB-INF下新建lib文件夹,放入相关jar包并引入,如下图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bnwn5ZtQ-1635139481038)(C:\Users\chenzhiyuan4\AppData\Roaming\Typora\typora-user-images\image-20211020163118054.png)]

​ 在src下新建bean包,在里面新建Message对象,作为后续消息:

package com.czy.bean;

public class Message {
    // 每次给用户回复的是这样一段json格式{status:0, result:"", data:{}}

    // 状态码 0成功 -1失败
    private int status;
    // 消息内容
    private String result;
    // 消息携带的数据
    private Object data;

    public Message(int status, String result, Object data) {
        this.status = status;
        this.result = result;
        this.data = data;
    }

    public Message() {
    }

    public Message(int status) {
        this.status = status;
    }

    public Message(String result) {
        this.result = result;
    }

    public Message(int status, String result) {
        this.status = status;
        this.result = result;
    }

    public int getStatus() {
        return status;
    }

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

    public String getResult() {
        return result;
    }

    public void setResult(String result) {
        this.result = result;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }
}
	

​ 在src下新建util包,先把之前JDBC项目中的DruidUtil复制过来,再新建两个工具类:1.DateFormatUtil用于后续格式化日期;2.JSONUtil作为转换为json格式的工具类。

package com.czy.util;

import java.text.SimpleDateFormat;
import java.util.Date;

public class DateFormatUtil {
    private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public static String format(Date date) {
        return format.format(date);
    }

}
package com.czy.util;

import com.google.gson.Gson;

//转换为json格式的工具类
public class JSONUtil {
    private static Gson g = new Gson();

    public static String toJSON(Object obj) {
        return g.toJson(obj);
    }
}

第三节管理员数据操作部分

​ 在web目录下创建admin文件夹,将素材模板粘贴进去。接下来正式编写后台管理部分,我们计划分为五部分进行编写:1.管理员登录;2.快递的管理;3.用户的管理;4.快递员的管理;5.控制台。整体的编写我们参考前后端分离的架构去实现,后端在编写时映射的地址没有遵循相应规范,后面的框架学习才会学习到。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9HR0WjlZ-1634895075444)(C:\Users\chenzhiyuan4\AppData\Roaming\Typora\typora-user-images\image-20211020170310853.png)]

​ 管理员登录部分我们需要做两个事情:1.检查账号密码是否正确,正确则进行登录;2.写入本次登录的ip和时间

​ 在src下新建dao包,新建BaseAdminMysql接口,在包内再新建一个包imp,里面存放dao包中接口的实现类。

​ BaseAdminMysql接口:

package com.czy.dao;import java.util.Date;/** * 用于定义eadmin表格的规范操作 */public interface BaseAdminDao {    /**     * 根据用户名更新登陆时间和登录ip     * @param username     * @param date     * @param ip     */    void updateLoginTime(String username, Date date, String ip);    /**     * 管理员根据账号密码登录     * @param username     * @param password     * @return 登录的结果,true表示登录成功     */    boolean login(String username, String password);}

​ AdminDaoMysql实现类:

package com.czy.dao.imp;import com.czy.dao.BaseAdminDao;import com.czy.util.DruidUtil;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;import java.util.Date;public class AdminDaoMysql implements BaseAdminDao {    private static final String SQL_UPDATE_LOGIN_TIME = "UPDATE EADMIN SET LOGINTIME=?,LOGINIP=? WHERE USERNAME=?";    private static final String SQL_LOGIN = "SELECT ID FROM EADMIN WHERE USERNAME=? AND PASSWORD=?";    /**     * 根据用户名更新登陆时间和登录ip     *     * @param username     * @param date     * @param ip     */    @Override    public void updateLoginTime(String username, Date date, String ip) {        //1.    获取连接        Connection conn = DruidUtil.getConnection();        PreparedStatement state = null;        //2.    预编译SQL语句        try {            state = conn.prepareStatement(SQL_UPDATE_LOGIN_TIME);            //3.    填充参数            //这里的date是util包中的类,作为参数传递方便处理,传入时需要转换为sql包中的Date类。            state.setDate(1, new java.sql.Date(date.getTime()));            state.setString(2, ip);            state.setString(3, username);            //4.    执行            state.executeUpdate();        } catch (SQLException e) {            e.printStackTrace();        } finally {            //5.    释放资源            DruidUtil.close(conn, state, null);// 这里不需要返回结果,所以传入null        }    }    /**     * 判断登录是否成功     *     * @param username     * @param password     * @return     */    @Override    public boolean login(String username, String password) {        //1.    获取连接        Connection conn = DruidUtil.getConnection();        PreparedStatement state = null;        ResultSet rs = null;        //2.    预编译SQL语句        try {            state = conn.prepareStatement(SQL_LOGIN);            //3.    填充参数            // 这里的date是util包中的类,作为参数传递方便处理。传入时需要转换为sql包中的Date类            state.setString(1, username);            state.setString(2, password);            //4.    执行并获取结果            rs = state.executeQuery();            //5.    根据查询结果返回(如果没有异常,到下面这行,next能出来就true,不能就false)            return rs.next();        } catch (SQLException e) {            e.printStackTrace();        } finally {            //6.    释放资源            DruidUtil.close(conn, state, rs);// 这里不需要返回结果,所以传入null        }        //如果代码出现异常最后就到这一行        return false;    }}

​ 接下来在controller(还没写)和Dao中编写一个Service,避免controller层和dao层紧耦合。(这两层比较重要,若发生改动对程序影响较大)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-du3DXe3e-1634895075446)(C:\Users\chenzhiyuan4\AppData\Roaming\Typora\typora-user-images\image-20211020180602944.png)]

​ 编写AdminService:

package com.czy.dao;import java.util.Date;public interface BaseAdminDao {    /**     * 根据用户名更新登陆时间和登录ip     * @param username     * @param date     * @param ip     */    void updateLoginTime(String username, Date date, String ip);    /**     * 管理员根据账号密码登录     * @param username     * @param password     * @return 登录的结果,true表示登录成功     */    boolean login(String username, String password);}

第四节管理员登录前后端交互

​ 修改login.html,位于web/admin下。将登录按钮的类型改为button(原先是submit),这样可以通过ajax在前端拿到登录时的用户名和密码引入jQuery和layer(注意引入的顺序)编写脚本,取得用户名和密码

<!DOCTYPE html><html lang="zh-CN"><head>    <meta charset="UTF-8">    <meta http-equiv="X-UA-Compatible" content="IE=edge">    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">    <link rel="stylesheet" href="assets/css/layui.css">    <link rel="stylesheet" href="assets/css/login.css">    <link rel="icon" href="/favicon.ico">    <title>快递e栈管理后台</title></head><body class="login-wrap">    <div class="login-container">    	<h3>快递e栈后台管理</h3>        <form class="login-form" action="index.html">            <div class="input-group">                <input type="text" id="username" class="input-field">                <label for="username" class="input-label">                    <span class="label-title">用户名</span>                </label>            </div>            <div class="input-group">                <input type="password" id="password" class="input-field">                <label for="password" class="input-label">                    <span class="label-title">密码</span>                </label>            </div>            <button type="button" class="login-button">登录<i class="ai ai-enter"></i></button>        </form>    </div></body><script src="assets/layui.js"></script><script src="js/index.js" data-main="login"></script><script src="js/login.js" data-main="login"></script><script src="../layer/layer.js"></script><script src="../qrcode/jquery2.1.4.js"></script><script>    $(function () {        $(".login-button").click(function () {            var username = $("#username").val();            var password = $("#password").val();            //1.    先试用layer,弹出load(提示加载中)            var windowID = layer.load();            //2.    ajax与服务器交互(密码的提交建议用post)            // 成功时进入function函数            // post里面前台输入这个地址login.do,我们就去后台找注解了这个地址的方法            $.post("login.do",{username:username,password:password},function (data) {                //3.    关闭load窗口                layer.close(windowID);                //4.    将服务器回复的结果进行显示                layer.msg(data.result);                if (data.status == 0){                    //跳转到主页                    location.assign("index.html");                }            },"JSON");        })    })</script></html>

​ 编写controller包,先写AdminController :

package com.czy.controller;import com.czy.bean.Message;import com.czy.mvc.ResponseBody;import com.czy.service.AdminService;import com.czy.util.JSONUtil;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.util.Date;public class AdminController {    @ResponseBody("/admin/login.do")    public String login(HttpServletRequest req, HttpServletResponse resp) {        // 1, 接收参数        String username = req.getParameter("username");        String password = req.getParameter("password");        // 2, 调用service传递参数,并获取结果        boolean result = AdminService.login(username, password);        // 3, 根据结果准备不同的返回数据        Message msg = null;        if (result) {            msg = new Message(0, "登录成功");            //登录时间 和 ip 的更新            Date date = new Date();            String ip = req.getRemoteAddr();//如果用本机进行登录,会获取到一堆的0            AdminService.updateLoginTimeAndIP(username, date, ip);            req.getSession().setAttribute("adminUsername", "username");        } else {            msg = new Message(-1, "登录失败");        }        // 4, 将数据转换为JSON        String json = JSONUtil.toJSON(msg);        // 5, 将JSON返回给ajax        return json;    }}

​ 处理字符乱码问题,在src中创建filter目录,编写CharsetFilter。

package com.xxy.filter; import javax.servlet.*;import javax.servlet.annotation.WebFilter;import java.io.IOException; @WebFilter("*.do")public class CharsetFilter implements Filter {     @Override    public void init(FilterConfig filterConfig) throws ServletException {     }     @Override    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {        servletRequest.setCharacterEncoding("utf-8");        servletResponse.setContentType("text/json;charset=utf-8");        servletResponse.setCharacterEncoding("utf-8");        filterChain.doFilter(servletRequest, servletResponse);    }     @Override    public void destroy() {     }}

第五节流程与快递表格创建

快递管理编写流程

​ 1. 快递的列表展示

  • 分页查询的列表

    2.新增快递

  • 用户输入内容,后台接收参数,向数据库存储

    3.删除快递

  • 用户输入快递单号查询到快递信息

  • 浏览快递信息的最后,可以点击删除按钮 ,删除快递

    4.修改快递

  • 用户输入快递单号查询到快递信息

  • 浏览(可修改)快递信息的最后,可以点击确认按钮 ,确认修改快递

具体流程

创建数据库表格(Express)—> 编写DAO—> 编写Service—> 编写Controller—> 前后端的交互

前端发起ajax→DispatcherServlet→Controller→Service→DAO→数据库

​ 创建数据库的sql代码:

CREATE TABLE express(	id INT PRIMARY KEY auto_increment,	number VARCHAR(64) UNIQUE,	username VARCHAR(32),	userphone VARCHAR(32),	company VARCHAR(32),	code VARCHAR(32) UNIQUE,	intime TIMESTAMP,	outtime TIMESTAMP,	status int,	sysPhone VARCHAR(32));

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G6VPccxl-1634895075448)(C:\Users\chenzhiyuan4\AppData\Roaming\Typora\typora-user-images\image-20211020200849772.png)]

生成表格对应的实体类-快递express:

package com.czy.bean;

import java.sql.Timestamp;
import java.util.Objects;

public class Express {

    private int id;
    private String number;
    private String username;
    private String userphone;

    private String company;
    private String code;
    private Timestamp intime;
    private Timestamp outtime;
    private int status;
    private String sysPhone;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Express express = (Express) o;
        return id == express.id &&
                status == express.status &&
                Objects.equals(number, express.number) &&
                Objects.equals(username, express.username) &&
                Objects.equals(userphone, express.userphone) &&
                Objects.equals(company, express.company) &&
                Objects.equals(code, express.code) &&
                Objects.equals(intime, express.intime) &&
                Objects.equals(outtime, express.outtime) &&
                Objects.equals(sysPhone, express.sysPhone);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, number, username, userphone, company, code, intime, outtime, status, sysPhone);
    }

    @Override
    public String toString() {
        return "Express{" +
                "id=" + id +
                ", number='" + number + '\'' +
                ", username='" + username + '\'' +
                ", userphone='" + userphone + '\'' +
                ", company='" + company + '\'' +
                ", code='" + code + '\'' +
                ", intime=" + intime +
                ", outtime=" + outtime +
                ", status=" + status +
                ", sysPhone='" + sysPhone + '\'' +
                '}';
    }

    public int getId() {
        return id;
    }

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

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getUserPhone() {
        return userphone;
    }

    public void setuserphone(String userphone) {
        this.userphone = userphone;
    }

    public String getCompany() {
        return company;
    }

    public void setCompany(String company) {
        this.company = company;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public Timestamp getIntime() {
        return intime;
    }

    public void setIntime(Timestamp intime) {
        this.intime = intime;
    }

    public Timestamp getOuttime() {
        return outtime;
    }

    public void setOuttime(Timestamp outtime) {
        this.outtime = outtime;
    }

    public int getStatus() {
        return status;
    }

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

    public String getSysPhone() {
        return sysPhone;
    }

    public void setSysPhone(String sysPhone) {
        this.sysPhone = sysPhone;
    }

    public Express(String number, String username, String userphone, String company, String sysPhone) {
        this.number = number;
        this.username = username;
        this.userphone = userphone;
        this.company = company;
        this.sysPhone = sysPhone;
    }

    //由用户自己输入的参数形成的构造方法
    public Express(String number, String username, String userphone, String company, String sysPhone,String code) {
        this.number = number;
        this.username = username;
        this.userphone = userphone;
        this.company = company;
        this.sysPhone = sysPhone;
        this.code = code;
    }

    public Express() {
    }

    public Express(int id, String number, String username, String userphone, String company, String code, Timestamp intime, Timestamp outtime, int status, String sysPhone) {
        this.id = id;
        this.number = number;
        this.username = username;
        this.userphone = userphone;
        this.company = company;
        this.code = code;
        this.intime = intime;
        this.outtime = outtime;
        this.status = status;
        this.sysPhone = sysPhone;
    }
}

第六节快递的dao编写

​ api设计省略,快递的dao接口编写如下:

package com.czy.dao;import com.czy.bean.Express;import com.czy.exception.DuplicateCodeException;import java.util.List;import java.util.Map;public interface BaseExpressDao {    /**     * 用于查询数据库中的全部快递(总数+新增),待取件快递(总数+新增)     * @return [{size:总数,day:新增},{size:总数,day:新增}]     *     */    List<Map<String, Integer>> console();    /**     * 用于查询所有快递     * @param limit 是否分页的标记,true表示分页。false表示查询所有快递     * @param offset SQL语句的起始索引     * @param pageNumber 页查询的数量     * @return 快递的集合     */    List<Express> findAll(boolean limit, int offset, int pageNumber);    /**     * 根据快递单号,查询快递信息     * @param number 单号     * @return 查询的快递信息,单号不存在时返回null     */    Express findByNumber(String number);    /**     * 根据快递取件码,查询快递信息     * @param code 取件码     * @return 查询的快递信息,取件码不存在时返回null     */    Express findByCode(String code);    /**     * 根据用户手机号码,查询他所有的快递信息     * @param userPhone 手机号码     * @return 查询的快递信息列表     */    List<Express> findByUserPhone(String userPhone);    /**     * 根据录入人手机号码,查询录入的所有记录     * @param sysPhone 手机号码     * @return 查询的快递信息列表     */    List<Express> findBySysPhone(String sysPhone);    /**     * 快递的录入     * @param e 要录入的快递对象     * @return 录入的结果,true表示成功,false表示失败     */    boolean insert(Express e) throws DuplicateCodeException;    /**     * 快递的修改     * @param id 要修改的快递id     * @param newExpress 新的快递对象(number,company,username,userPhone)     * @return 修改的结果,true表示成功,false表示失败     */    boolean update(int id, Express newExpress);    /**     * 更改快递的状态为1,表示取件完成     * @param code 要修改的快递取件码     * @return 修改的结果,true表示成功,false表示失败     */    boolean updateStatus(String code);    /**     * 根据id,删除单个快递信息     * @param id 要删除的快递id     * @return 删除的结果,true表示成功,false表示失败     */    boolean delete(int id);}

​ 在ExpressDao的实现类ExpressDaoMysql,先在开头编写SQL语句:

    // 这里统一使用大写 如果不是大写的话后面会自动转换为大写
    // 用于查询数据库中的全部快递(总数+新增),待取件快递(总数+新增)
    public static final String SQL_CONSOLE = "SELECT COUNT(ID) data1_size, COUNT(TO_DAYS(INTIME)=TO_DAYS(NOW()) OR NULL) data1_day, COUNT(STATUS=0 OR NULL) data2_size, COUNT(TO_DAYS(INTIME)=TO_DAYS(NOW()) AND STATUS=0 OR NULL) data2_day FROM EXPRESS";
    // 用于查询数据库中的所有快递信息
    public static final String SQL_FIND_ALL = "SELECT * FROM EXPRESS";
    // 用于分页查询数据库中的快递信息
    public static final String SQL_FIND_LIMIT = "SELECT * FROM EXPRESS LIMIT ?,?";
    // 通过取件码查询快递信息
    public static final String SQL_FIND_BY_CODE = "SELECT * FROM EXPRESS WHERE CODE=?";
    // 通过快递单号查询快递信息
    public static final String SQL_FIND_BY_NUMBER = "SELECT * FROM EXPRESS WHERE NUMBER=?";
    // 通过录入人手机号查询快递信息
    public static final String SQL_FIND_BY_SYSPHONE = "SELECT * FROM EXPRESS WHERE SYSPHONE=?";
    // 通过用户手机号查询用户所有快递
    public static final String SQL_FIND_BY_USERPHONE = "SELECT * FROM EXPRESS WHERE USERPHONE=?";
    // 录入快递
    public static final String SQL_INSERT = "INSERT INTO EXPRESS (NUMBER,USERNAME,USERPHONE,COMPANY,CODE,INTIME,STATUS,SYSPHONE) VALUES(?,?,?,?,?,NOW(),0,?)";
    // 快递修改
    public static final String SQL_UPDATE = "UPDATE EXPRESS SET NUMBER=?,USERNAME=?,COMPANY=?,STATUS=? WHERE ID=?";
    // 快递的状态码改变(取件)
    public static final String SQL_UPDATE_STATUS = "UPDATE EXPRESS SET STATUS=1,OUTTIME=NOW(),CODE=NULL WHERE CODE=?";
    // 快递的删除
    public static final String SQL_DELETE = "DELETE FROM EXPRESS WHERE ID=?";

​ ExpressDao实现类编写

package com.czy.dao.imp;

import com.czy.bean.Express;
import com.czy.dao.BaseExpressDao;
import com.czy.exception.DuplicateCodeException;
import com.czy.util.DruidUtil;

import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ExpressDaoMysql implements BaseExpressDao {
    //sql语句这里省略,详情见前面那段代码

    /**
     * 用于查询数据库中的全部快递(总数+新增),待取件快递(总数+新增)
     *
     * @return [{size:总数,day:新增},{size:总数,day:新增}]
     */
    @Override
    public List<Map<String, Integer>> console() {
        List<Map<String, Integer>> data = new ArrayList<>();

        // 1 获取数据库连接
        Connection conn = DruidUtil.getConnection();
        PreparedStatement state = null;// 变量向上抽取,便于释放资源
        ResultSet result = null;

        try {
            // 2 预编译SQL语句
            state = conn.prepareStatement(SQL_CONSOLE);

            // 3 填充参数(可选)

            // 4 执行SQL语句
            result = state.executeQuery();

            // 5 获得执行结果
            //使用的是聚合函数,查到的是一行数据,我们先判断有没有查到数据
            if (result.next()) {
                int data1_size = result.getInt("data1_size");
                int data1_day = result.getInt("data1_day");
                int data2_size = result.getInt("data2_size");
                int data2_day = result.getInt("data2_day");
                Map data1 = new HashMap();
                data1.put("data1_size", data1_size);
                data1.put("data1_day", data1_day);
                Map data2 = new HashMap();
                data2.put("data2_size", data2_size);
                data2.put("data2_day", data2_day);
                data.add(data1);
                data.add(data2);
            }


        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 6 释放资源
            DruidUtil.close(conn, state, result);
        }
        return data;
    }

    /**
     * 用于查询所有快递
     *
     * @param limit      是否分页的标记,true表示分页。false表示查询所有快递
     * @param offset     SQL语句的起始索引
     * @param pageNumber 页查询的数量
     * @return 快递的集合
     */
    @Override
    public List<Express> findAll(boolean limit, int offset, int pageNumber) {
        ArrayList<Express> data = new ArrayList<>();
        //1.    获取数据库的连接
        Connection conn = DruidUtil.getConnection();
        PreparedStatement state = null;
        ResultSet result = null;
        //2.    预编译SQL语句
        try {
            //判断是否为分页查询,两条路径
            if(limit) {
                state = conn.prepareStatement(SQL_FIND_LIMIT);
                //3.    填充参数(可选)
                state.setInt(1,offset);
                state.setInt(2,pageNumber);
            } else {
                state = conn.prepareStatement(SQL_FIND_ALL);
            }

            //4.    执行SQL语句
            result = state.executeQuery();

            //5.    获取执行的结果
            while(result.next()) {
                int id = result.getInt("id");
                String number = result.getString("number");
                String username = result.getString("username");
                String userPhone = result.getString("userPhone");
                String company = result.getString("company");
                String code = result.getString("code");
                Timestamp inTime = result.getTimestamp("inTime");
                Timestamp outTime = result.getTimestamp("outTime");
                int status = result.getInt("status");
                String sysPhone = result.getString("sysPhone");
                Express e = new Express(id,number,username,userPhone,company,code,inTime,outTime,status,sysPhone);
                data.add(e);
            }

        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            //6.    资源的释放
            DruidUtil.close(conn,state,result);
        }
        return data;
    }

    /**
     * 根据快递单号,查询快递信息
     *
     * @param number 单号
     * @return 查询的快递信息,单号不存在时返回null
     */
    @Override
    public Express findByNumber(String number) {
        // 1 获取数据库连接
        Connection conn = DruidUtil.getConnection();
        PreparedStatement state = null;
        ResultSet result = null;

        try {
            // 2 预编译SQL语句
            state = conn.prepareStatement(SQL_FIND_BY_NUMBER);

            // 3 填充参数(可选)
            state.setString(1, number);

            // 4 执行SQL语句
            result = state.executeQuery();

            // 5 获得执行结果
            if (result.next()) {
                int id = result.getInt("id");
                String username = result.getString("username");
                String userPhone = result.getString("userPhone");
                String company = result.getString("company");
                String code = result.getString("code");
                Timestamp inTime = result.getTimestamp("inTime");
                Timestamp outTime = result.getTimestamp("outTime");
                int status = result.getInt("status");
                String sysPhone = result.getString("sysPhone");
                Express e = new Express(id,number,username,userPhone,company,code,inTime,outTime,status,sysPhone);
                return  e;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 6 释放资源
            DruidUtil.close(conn, state, result);
        }
        return null;
    }

    /**
     * 根据快递取件码,查询快递信息
     *
     * @param code 取件码
     * @return 查询的快递信息,取件码不存在时返回null
     */
    @Override
    public Express findByCode(String code) {
        // 1 获取数据库连接
        Connection conn = DruidUtil.getConnection();
        PreparedStatement state = null;
        ResultSet result = null;

        try {
            // 2 预编译SQL语句
            state = conn.prepareStatement(SQL_FIND_BY_CODE);

            // 3 填充参数(可选)
            state.setString(1, code);

            // 4 执行SQL语句
            result = state.executeQuery();

            // 5 获得执行结果
            if (result.next()) {
                int id = result.getInt("id");
                String number = result.getString("number");
                String username = result.getString("username");
                String userPhone = result.getString("userPhone");
                String company = result.getString("company");
                Timestamp inTime = result.getTimestamp("inTime");
                Timestamp outTime = result.getTimestamp("outTime");
                int status = result.getInt("status");
                String sysPhone = result.getString("sysPhone");
                Express e = new Express(id,number,username,userPhone,company,code,inTime,outTime,status,sysPhone);
                return  e;
            }

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 6 释放资源
            DruidUtil.close(conn, state, result);
        }
        return null;
    }

    /**
     * 根据用户手机号码,查询他所有的快递信息
     *
     * @param userPhone 手机号码
     * @return 查询的快递信息列表
     */
    @Override
    public List<Express> findByUserPhone(String userPhone) {
        ArrayList<Express> data = new ArrayList<>();
        // 1 获取数据库连接
        Connection conn = DruidUtil.getConnection();
        PreparedStatement state = null;
        ResultSet result = null;

        try {
            // 2 预编译SQL语句
            state = conn.prepareStatement(SQL_FIND_BY_USERPHONE);

            // 3 填充参数(可选)
            state.setString(1, userPhone);

            // 4 执行SQL语句
            result = state.executeQuery();

            // 5 获得执行结果
            while (result.next()) {
                int id = result.getInt("id");
                String number = result.getString("number");
                String username = result.getString("username");
                String company = result.getString("company");
                String code = result.getString("code");
                Timestamp inTime = result.getTimestamp("inTime");
                Timestamp outTime = result.getTimestamp("outTime");
                int status = result.getInt("status");
                String sysPhone = result.getString("sysPhone");
                Express e = new Express(id,number,username,userPhone,company,code,inTime,outTime,status,sysPhone);
                data.add(e);
            }

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 6 释放资源
            DruidUtil.close(conn, state, result);
        }
        return data;
    }


    /**
     * 根据录入人手机号码,查询录入的所有记录
     *
     * @param sysPhone 手机号码
     * @return 查询的快递信息列表
     */
    @Override
    public List<Express> findBySysPhone(String sysPhone) {
        ArrayList<Express> data = new ArrayList<>();
        // 1 获取数据库连接
        Connection conn = DruidUtil.getConnection();
        PreparedStatement state = null;
        ResultSet result = null;

        try {
            // 2 预编译SQL语句
            state = conn.prepareStatement(SQL_FIND_BY_SYSPHONE);

            // 3 填充参数(可选)
            state.setString(1, sysPhone);

            // 4 执行SQL语句
            result = state.executeQuery();

            // 5 获得执行结果
            while (result.next()) {
                int id = result.getInt("id");
                String number = result.getString("number");
                String username = result.getString("username");
                String company = result.getString("company");
                String userPhone = result.getString("userPhone");
                String code = result.getString("code");
                Timestamp inTime = result.getTimestamp("inTime");
                Timestamp outTime = result.getTimestamp("outTime");
                int status = result.getInt("status");
                Express e = new Express(id,number,username,userPhone,company,code,inTime,outTime,status,sysPhone);
                data.add(e);
            }

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 6 释放资源
            DruidUtil.close(conn, state, result);
        }
        return data;
    }

    /**
     * 快递的录入
     *
     * @param e 要录入的快递对象
     * @return 录入的结果,true表示成功,false表示失败
     */
    @Override
    public boolean insert(Express e) throws DuplicateCodeException {
        //1.    连接的获取
        Connection conn = DruidUtil.getConnection();
        //2.    预编译SQL语句
        PreparedStatement state = null;
        try {
            state = conn.prepareStatement(SQL_INSERT);
            //3.    填充参数
            //可能存在的异常:单号重复录入-->措施是直接告诉录入人,这个单子已经录入过了
            state.setString(1,e.getNumber());
            state.setString(2,e.getUsername());
            state.setString(3,e.getUserPhone());
            state.setString(4,e.getCompany());
            //可能存在的异常:取件码重复-->措施是找到相对应的异常类型,相应异常信息中的关键字,然后选择自定义一个异常向外抛出
            state.setString(5,e.getCode());
            state.setString(6,e.getSysPhone());
            //4.    执行SQL语句,并获取执行结果
            return state.executeUpdate()>0?true:false;
        } catch (SQLException e1) {
//            throwables.printStackTrace();
            if (e1.getMessage().endsWith("for key 'express.code'")){
                //是因为取件码重复,而出现了异常
                DuplicateCodeException e2 = new DuplicateCodeException(e1.getMessage());
                throw e2;
            }else{
                e1.printStackTrace();
            }
        }finally {
            //5.    释放资源
            DruidUtil.close(conn,state,null);
        }
        return false;
    }

    /**
     * 快递的修改
     *
     * @param id         要修改的快递id
     * @param newExpress 新的快递对象(number,company,username,userPhone)
     * @return 修改的结果,true表示成功,false表示失败
     */
    @Override
    public boolean update(int id, Express newExpress) {
        //1.    连接的获取
        Connection conn = DruidUtil.getConnection();
        PreparedStatement state = null;
        //2.    预编译SQL语句
        try {
            state = conn.prepareStatement(SQL_UPDATE);
            state.setString(1,newExpress.getNumber());
            state.setString(2,newExpress.getUsername());
            state.setString(3,newExpress.getUserPhone());
            state.setString(4,newExpress.getCompany());
            state.setInt(5,newExpress.getStatus());
            state.setInt(6,id);
            return state.executeUpdate()>0?true:false;
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            DruidUtil.close(conn,state,null);
        }
        return false;
    }


    /**
     * 更改快递的状态为1,表示取件完成
     *
     * @param code 要修改的快递取件码
     * @return 修改的结果,true表示成功,false表示失败
     */
    @Override
    public boolean updateStatus(String code) {
        //1.    连接的获取
        Connection conn = DruidUtil.getConnection();
        PreparedStatement state = null;
        //2.    预编译SQL语句
        try {
            state = conn.prepareStatement(SQL_UPDATE_STATUS);
            state.setString(1, code);
            return state.executeUpdate()>0?true:false;
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            DruidUtil.close(conn,state,null);
        }
        return false;
    }

    /**
     * 根据id,删除单个快递信息
     *
     * @param id 要删除的快递id
     * @return 删除的结果,true表示成功,false表示失败
     */
    @Override
    public boolean delete(int id) {
        //1.    连接的获取
        Connection conn = DruidUtil.getConnection();
        PreparedStatement state = null;
        //2.    预编译SQL语句
        try {
            state = conn.prepareStatement(SQL_DELETE);
            state.setInt(1,id);
            return state.executeUpdate()>0;
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            DruidUtil.close(conn,state,null);
        }
        return false;
    }
}

第七节快递的service编写

//为避免controller层和dao层紧耦合(这两层比较重要,若发生改动对程序影响较大),在两者中间加入service层。
package com.czy.service;

import com.czy.bean.Express;
import com.czy.dao.BaseExpressDao;
import com.czy.dao.imp.ExpressDaoMysql;
import com.czy.exception.DuplicateCodeException;
import com.czy.util.RandomUtil;

import java.util.List;
import java.util.Map;

public class ExpressService {
    private static BaseExpressDao dao = new ExpressDaoMysql();

    /**
     * 用于查询数据库中的全部快递(总数+新增),待取件快递(总数+新增)
     *
     * @return [{size:总数,day:新增},{size:总数,day:新增}]
     */
    public static List<Map<String, Integer>> console() {
        return dao.console();
    }

    /**
     * 用于查询所有快递
     *
     * @param limit      是否分页的标记,true表示分页。false表示查询所有快递
     * @param offset     SQL语句的起始索引
     * @param pageNumber 页查询的数量
     * @return 快递的集合
     */
    public static List<Express> findAll(boolean limit, int offset, int pageNumber) {
        return dao.findAll(limit, offset, pageNumber);
    }

    /**
     * 根据快递单号,查询快递信息
     *
     * @param number 单号
     * @return 查询的快递信息,单号不存在时返回null
     */
    public static Express findByNumber(String number) {
        return dao.findByNumber(number);
    }

    /**
     * 根据快递取件码,查询快递信息
     *
     * @param code 取件码
     * @return 查询的快递信息,取件码不存在时返回null
     */
    public static Express findByCode(String code) {
        return dao.findByCode(code);
    }

    /**
     * 根据用户手机号码,查询他所有的快递信息
     *
     * @param userphone 手机号码
     * @return 查询的快递信息列表
     */
    public static List<Express> findByuserphone(String userphone) {
        return dao.findByUserPhone((userphone));
    }

    /**
     * 根据录入人手机号码,查询录入的所有记录
     *
     * @param sysPhone 手机号码
     * @return 查询的快递信息列表
     */
    public static List<Express> findBySysPhone(String sysPhone) {
        return dao.findBySysPhone(sysPhone);
    }

    /**
     * 快递的录入
     *
     * @param e 要录入的快递对象
     * @return 录入的结果,true表示成功,false表示失败
     */
    public static boolean insert(Express e) {
        e.setCode(RandomUtil.getCode()+"");
        try {
            return dao.insert(e);
        } catch (DuplicateCodeException e1) {
            // 捕获到取件码重复后 递归调用插入
            return insert(e);
        }
    }

    /**
     * 快递的修改
     *
     * @param id         要修改的快递id
     * @param newExpress 新的快递对象(number,company,username,userphone)
     * @return 修改的结果,true表示成功,false表示失败
     */
    //这段的逻辑比较乱,不深究了
    public static boolean update(int id, Express newExpress) {
        // 个人理解:当有修改收件人号码的需求时,由于涉及到重发短信的业务,需要重新执行插入(插入时,会向用户发送短信)
        if (newExpress.getUserPhone() != null) {
            dao.delete(id);
            return insert(newExpress);
        } else {
            // 这里的逻辑感觉不是很清晰,注释上修改的是userphone,后面SQL语句中却替换为status,后面有问题再来修改吧
            boolean update = dao.update(id, newExpress);
            Express e = dao.findByNumber(newExpress.getNumber());
            if (newExpress.getStatus() == 1) {
                updateStatus(e.getCode());
            }
            return update;
        }
    }

    /**
     * 更改快递的状态为1,表示取件完成
     *
     * @param code 要修改的快递取件码
     * @return 修改的结果,true表示成功,false表示失败
     */
    public static boolean updateStatus(String code) {
        return dao.updateStatus(code);
    }

    /**
     * 根据id,删除单个快递信息
     *
     * @param id 要删除的快递id
     * @return 删除的结果,true表示成功,false表示失败
     */
    public static boolean delete(int id) {
        return dao.delete(id);
    }

}

第八节控制台部分

标准流程ajax→DispatcherServlet→Controller→Service→DAO→数据库

  • 1.前端发起ajax

    $("按钮选择器").click(function(){
        //1.    先使用layer,弹出load(提示加载中...)
        var windowId = layer.load();
        //2.    ajax与服务器交互
        $.post("服务器地址",参数JSON,function(data){
            //3.    关闭load窗口
            layer.close(windowId);
            //4.    将服务器回复的结果进行显示
            layer.msg(data.result);
        },"JSON");
    });
    
  • 2.编写Controller,用于处理ajax的请求

    • 在Controller中调用service处理
    • 处理完毕, 根据service返回的结果,给ajax返回

    找到console.html页面(内嵌在index.html中),将一些静态数据修改为-,并添加id标识

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1YvJCOEo-1634895075451)(C:\Users\chenzhiyuan4\AppData\Roaming\Typora\typora-user-images\image-20211021112543532.png)]

在该html最后部分添加js脚本:

	<script>
        $(function () {
            $.post("/express/console.do", null, function (data) {
                if (data.status == 0) {
                    // [{size:总数,day:新增},{size:总数,day:新增}]
                    $("#data1_size").html(data.data[0].data1_size);
                    $("#data1_day").html(data.data[0].data1_day);
                    $("#data2_size").html(data.data[1].data2_size);
                    $("#data2_day").html(data.data[1].data2_day);
                }
            }, "JSON");
        });
    </script>

第九节分页列表、快递录入、删除和修改

​ 1.分页列表

​ 在bean文件夹下新建一个ResultData实体类,用来存放分页的数据。

package com.czy.bean;
 
import java.util.ArrayList;
import java.util.List;
 
public class ResultData<T> {
    // 每次查询的数据集合
    private List<T> rows = new ArrayList<>();
    // 总数量
    private int total;
 
    public List<T> getRows() {
        return rows;
    }
 
    public void setRows(List<T> rows) {
        this.rows = rows;
    }
 
    public int getTotal() {
        return total;
    }
 
    public void setTotal(int total) {
        this.total = total;
    }
}

​ 新建BootstrapTableExpress实体类, 在获取到express列表后,将其逐个转换为BootstrapTableExpress对象。

package com.czy.bean;

public class BootstrapTableExpress {
    private int id;
    private String number;
    private String username;
    private String userphone;
    private String company;
    private String code;
    private String intime;
    private String outtime;
    private String status;
    private String sysPhone;

    public BootstrapTableExpress() {
    }


    public BootstrapTableExpress(int id, String number, String username, String userphone, String company, String code, String intime, String outtime, String status, String sysPhone) {
        this.id = id;
        this.number = number;
        this.username = username;
        this.userphone = userphone;
        this.company = company;
        this.code = code;
        this.intime = intime;
        this.outtime = outtime;
        this.status = status;
        this.sysPhone = sysPhone;
    }

    public int getId() {
        return id;
    }

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

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getUserphone() {
        return userphone;
    }

    public void setUserphone(String userphone) {
        this.userphone = userphone;
    }

    public String getCompany() {
        return company;
    }

    public void setCompany(String company) {
        this.company = company;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getIntime() {
        return intime;
    }

    public void setIntime(String intime) {
        this.intime = intime;
    }

    public String getOuttime() {
        return outtime;
    }

    public void setOuttime(String outtime) {
        this.outtime = outtime;
    }

    public String getStatus() {
        return status;
    }

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

    public String getSysPhone() {
        return sysPhone;
    }

    public void setSysPhone(String sysPhone) {
        this.sysPhone = sysPhone;
    }
}

​ 在ExpressController中编写处理列表请求的方法

@ResponseBody("/express/list.do")
    public String list(HttpServletRequest req, HttpServletResponse resp) {
        // 1,获取查询数据的起始索引值
        int offset = Integer.parseInt(req.getParameter("offset"));
        // 2,获取当前页要查询的数据量
        int pageNumber = Integer.parseInt(req.getParameter("pageNumber"));
        // 3,进行查询
        List<Express> list = ExpressService.findAll(true, offset, pageNumber);
        List<BootstrapTableExpress> list2 = new ArrayList<>();
        for (Express e : list) {
            String inTime = DateFormatUtil.format(e.getIntime());
            String outTime = e.getOuttime() == null ? "未出库" : DateFormatUtil.format(e.getOuttime());
            String status = e.getStatus() == 0 ? "待取件" : "已取件";
            String code = e.getCode() == null ? "已取件" : e.getCode();
            list2.add(new BootstrapTableExpress(e.getId(), e.getNumber(), e.getUsername(), e.getUserPhone(), e.getCompany(), code, inTime, outTime, status, e.getSysPhone()));

        }
        List<Map<String,Integer>> console = ExpressService.console();
        Integer total = console.get(0).get("data1_size");
        // 4,将集合封装为bootstrap-table识别的格式(用于前后端分离)
        ResultData<BootstrapTableExpress> data = new ResultData<>();
        data.setRows(list2);
        data.setTotal(total);
        String json = JSONUtil.toJSON(data);
        return json;
    }

​ 修改list.html内容

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<link rel="stylesheet" type="text/css" href="../../css/reset.css"/>
		<link rel="stylesheet" type="text/css" href="../../css/list.css"/>
		<!--从cdn中引入资源,适用于有网络的条件,也可以提前下载,自行引入-->
		<link href="https://cdn.bootcdn.net/ajax/libs/bootstrap-table/1.17.1/bootstrap-table.min.css" rel="stylesheet">
		<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
		<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
		<script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
		<script src="https://cdn.bootcdn.net/ajax/libs/bootstrap-table/1.17.1/bootstrap-table.min.js"></script>
		<script src="https://cdn.bootcdn.net/ajax/libs/bootstrap-table/1.17.1/locale/bootstrap-table-zh-CN.min.js"></script>
	</head>
	<body>
	<table id="express_list"></table>
	<script>
		$(function () {
			$("#express_list").bootstrapTable({
				url:"/express/list.do", 	// 数据地址
				striped:true,				// 是否显示行的间隔
				pageNumber:1,				// 初始化加载第几页
				pagination:true,			// 是否分页
				sidePagination:'server',	// 分页方式,分为服务器分页和前端分页
				pageSize:5,
				pageList:[5,10,20],
				showRefresh:true,			// 是否提供刷新功能
				queryParams:function (params) {
					var temp = {
						offset:params.offset,
						pageNumber:params.limit
					};
					return temp;
				},
				columns:[
					{
						title:"编号",
						field:"id",
						sortable:false		// 选择前端分页才能使用排序功能
					},
					{
						title:"单号",
						field:"number",
						sortable:false
					},
					{
						title:"姓名",
						field:"username",
						sortable:false
					},
					{
						title:"电话",
						field:"userphone",	// 注意与自己定义的字段名是否一致(大小写)
						sortable:false
					},
					{
						title:"快递公司",
						field:"company",
						sortable:false
					},
					{
						title:"取件码",
						field:"code",
						sortable:false
					},
					{
						title:"入库时间",
						field:"intime",
						sortable:false
					},
					{
						title:"出库时间",
						field:"outtime",
						sortable:false
					},
					{
						title:"状态",
						field:"status",
						sortable:false
					},
					{
						title:"录入人电话",
						field:"sysPhone",
						sortable:false
					}
				]
			});
		});
	</script>
	</body>
</html>

2.快递录入

​ 修改add.html:为表单添加id;引入jQuery、layer;通过js为表单添加提交方法,获取页面提供的参数、打包参数向后台传递、接收后台返回的数据。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <link rel="stylesheet" href="../../assets/css/layui.css">
    <link rel="stylesheet" href="../../assets/css/view.css"/>
    <title></title>
</head>
<body class="layui-view-body">
    <div class="layui-content">
        <div class="layui-row">
            <div class="layui-card">
                <div class="layui-card-header">录入快件</div>
                <form id="form" class="layui-form layui-card-body" >

                <div class="layui-form-item">
                    <label class="layui-form-label">单号</label>
                    <div class="layui-input-block">
                      <input type="text" name="code" required  lay-verify="required" placeholder="请输入快递单号" autocomplete="off" class="layui-input">
                    </div>
                  </div>
                  <div class="layui-form-item">
                    <label class="layui-form-label">快递公司</label>
                    <div class="layui-input-block">
                      <select name="company" lay-verify="" lay-search>
						<option >安能快递</option><option >TNT</option><option >UPS</option><option >智利邮政</option><option >中速快递</option>
					</select>

                    </div>
                  </div>
                  <div class="layui-form-item">
                    <label class="layui-form-label">收件人姓名</label>
                    <div class="layui-input-block">
                      <input type="text" name="username" required  lay-verify="required" placeholder="请输入收货人姓名" autocomplete="off" class="layui-input">
                    </div>
                  </div>
                  <div class="layui-form-item">
                    <label class="layui-form-label">手机号码</label>
                    <div class="layui-input-block">
                      <input type="text" name="phonenumber" required  lay-verify="required" placeholder="请输入手机号码" autocomplete="off" class="layui-input">
                    </div>
                  </div>
                  <div class="layui-form-item">
                    <div class="layui-input-block">
                      <button class="layui-btn layui-btn-blue" lay-submit lay-filter="formDemo">立即提交</button>
                      <button type="reset" class="layui-btn layui-btn-primary">重置</button>
                    </div>
                  </div>
                </form>  
            </div>
        </div>
    </div>
    <script src="../../assets/layui.all.js"></script>
    <script src="/qrcode/jquery2.1.4.js"></script>
    <script src="/layer/layer.js"></script>
    <script>
      var form = layui.form
        ,layer = layui.layer;
    </script>
    <script>
        $(function () {
            $("#form").submit(function () {
                var windowId = layer.load();
                var number = $("input:eq(0)").val();
                var company = $("input:eq(1)").val();
                var username = $("input:eq(2)").val();
                var userphone = $("input:eq(3)").val();

                $.post("/express/insert.do", {
                    number:number,
                    company:company,
                    username:username,
                    userphone:userphone
                }, function (data) {
                    layer.close(windowId);
                    layer.msg(data.result);
                    if (data.status == 0) {
                        // 恢复默认值
                        $("input").val("");
                        $("input:eq(1)").val("顺丰速运");
                    }
                });

                return false;// 不跳转页面
            });
        });
    </script>
</body>
</html>

​ 3.修改和删除

​ 引入jQuery、layer;为指定的html组件添加id、value等属性;编写脚本,提供快件查询、快件修改/删除功能;(编写过程中需要完善后台的地方,再转回去完善。里面的逻辑也有点复杂,已经尽量做好备注)

​ update.html

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title></title>
	<link rel="stylesheet" type="text/css" href="../../css/reset.css"/>
	<link rel="stylesheet" type="text/css" href="../../css/add.css"/>
</head>
<body>
<div id="app">
	<div class="header">
		<span>修改快递信息</span>
	</div>
	<div class="content">
		<table>
			<tr>
				<td class="text-right">运单号</td><td class="content_right"><input class="input inline-input" id="number" placeholder="请输入运单号码"> <span class="btn btn-info" id="find">立即查找</span></td>
			</tr>
		</table>
	</div>


	<div class="header">
		<span>查找信息如下</span>
	</div>
	<div class="content">
		<table>
			<tr>
				<td class="text-right">快递单号</td><td class="content_right"><input class="input" placeholder="请输入姓名"></td>
			</tr>
			<tr>
				<td class="text-right">快递公司</td><td class="content_right"><input class="input" placeholder="请输入姓名"></td>
			</tr>
			<tr>
				<td class="text-right">收货人姓名</td><td class="content_right"><input class="input" placeholder="请输入姓名"></td>
			</tr>
			<tr>
				<td class="text-right">手机号码</td><td class="content_right"><input class="input" placeholder="请输入姓名"></td>
			</tr>
			<tr>
				<td class="text-right">快递状态</td><td class="content_right"><input type="radio" name="status" class="status" value="1">已签收  <input name="status" type="radio" class="status" value="0">未签收</td>
			</tr>
			<tr>
				<td></td><td class="content_right"><span class="btn btn-info" id="update">立即修改</span> <span class="btn">重置</span> </td>
			</tr>
		</table>
	</div>
</div>
</body>

<script src="/qrcode/jquery2.1.4.js"></script>
<script src="/layer/layer.js"></script>
<script>
	$(".header:eq(1), .content:eq(1)").hide();
	var expressId = null;// 后面可能修改单号,但是id不会该,所以在这里进行标记
	var g_userPhone = null;
	$(function () {
		$("#find").click(function () {
			var windowId = layer.load();
			var number = $("#number").val();
			$.getJSON("/express/find.do", {number:number}, function (data) {
				layer.close(windowId);
				layer.msg(data.result);
				if (data.status == 0) {
					$(".header:eq(1), .content:eq(1)").fadeIn(1000);
					expressId = data.data.id;
					g_userPhone = data.data.userphone;
					$("input:eq(1)").val(data.data.number);
					$("input:eq(2)").val(data.data.company);
					$("input:eq(3)").val(data.data.username);
					$("input:eq(4)").val(data.data.userphone);
					if (data.data.status == 0) {
						// 未签收
						$(".status:eq(1)").prop("checked", true);
					} else {
						// 已签收
						$(".status:eq(0)").prop("checked", true);
					}
				}
			})
		});
	});

	$("#update").click(function () {
		var windowId = layer.load();
		var number = $("input:eq(1)").val();
		var company = $("input:eq(2)").val();
		var username = $("input:eq(3)").val();
		var userphone = $("input:eq(4)").val();
		var status = $(".status:checked").val();

		// 后面检测修改时,判断userPhone是否为空来决定是否重发短信,但是查到手机号后就不可能为空
		// 所以这里判断修改时手机号与查找时手机号不同时,表示手机号修改,将该字段赋值
		var express = {
			id:expressId,
			number:number,
			company:company,
			username:username,
			status:status
		}
		if (userphone != g_userPhone) {
			express.userphone = userphone;
		}

		$.getJSON("/express/update.do", express, function (data) {
			layer.close(windowId);
			layer.msg(data.result);
			if (data.status == 0) {//修改成功
				$(".header:eq(1), .content:eq(1)").hide();

			}
		})
	});
</script>
</html>

​ delete.html

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title></title>
	<link rel="stylesheet" type="text/css" href="../../css/reset.css"/>
	<link rel="stylesheet" type="text/css" href="../../css/add.css"/>
</head>
<body>
<div id="app">
	<div class="header">
		<span>删除快递信息</span>
	</div>
	<div class="content">
		<table>
			<tr>
				<td class="text-right">运单号</td><td class="content_right"><input class="input inline-input" id="number" placeholder="请输入运单号码"> <span id="find" class="btn btn-info">立即查找</span></td>
			</tr>
		</table>
	</div>


	<div class="header">
		<span>查找信息如下</span>
	</div>
	<div class="content">
		<table>
			<tr>
				<td class="text-right">快递单号</td><td class="content_right"><input class="input" placeholder="请输入姓名"></td>
			</tr>
			<tr>
				<td class="text-right">快递公司</td><td class="content_right"><input class="input" placeholder="请输入姓名"></td>
			</tr>
			<tr>
				<td class="text-right">收货人姓名</td><td class="content_right"><input class="input" placeholder="请输入姓名"></td>
			</tr>
			<tr>
				<td class="text-right">手机号码</td><td class="content_right"><input class="input" placeholder="请输入姓名"></td>
			</tr>
			<tr>
				<td class="text-right">快递状态</td><td id="status" class="content_right"><input type="radio" name="status">已签收  <input name="status" type="radio">未签收</td>
			</tr>
			<tr>
				<td></td><td class="content_right"><span class="btn btn-info" id="delete">立即删除</span> </td>
			</tr>
		</table>
	</div>
</div>
</body>
<script src="/qrcode/jquery2.1.4.js"></script>
<script src="/layer/layer.js"></script>
<script>
	$(".header:eq(1), .content:eq(1)").hide();
	var expressId = null;
	$(function () {
		$("#find").click(function () {
			var windowId = layer.load();
			var number = $("#number").val();
			$.getJSON("/express/find.do", {number:number}, function (data) {
				layer.close(windowId);
				layer.msg(data.result);
				if (data.status == 0) {
					$(".header:eq(1), .content:eq(1)").fadeIn(1000);
					expressId = data.data.id;
					$("input:eq(1)").val(data.data.number);
					$("input:eq(2)").val(data.data.company);
					$("input:eq(3)").val(data.data.username);
					$("input:eq(4)").val(data.data.userphone);
					//让输入框在查询成功后,不能修改
					$("input:eq(1),input:eq(2),input:eq(3),input:eq(4)").prop("disabled", true);
					if (data.data.status == 0) {
						// 未签收
						$("#status").html("未签收");
					} else {
						// 已签收
						$("#status").html("已签收");
					}
				}
			})
		});
	});

	$("#delete").click(function(){
		var windowId = layer.load();
		$.getJSON("/express/delete.do",{id:expressId},function(data){
			layer.close(windowId);
			layer.msg(data.result);
			if(data.status == 0){
				$(".header:eq(1),.content:eq(1)").hide();
			}
		});
	});
</script>

</html>

新增三个处理请求的方法

    @ResponseBody("/express/find.do")
    public String find(HttpServletRequest req, HttpServletResponse resp) {
        String number = req.getParameter("number");
        Express e = ExpressService.findByNumber(number);
        Message msg = new Message();
        if (e == null) {
            msg.setStatus(-1);
            msg.setResult("单号不存在");
        } else {
            msg.setStatus(0);
            msg.setResult("查找成功");
            msg.setData(e);
        }
        String json = JSONUtil.toJSON(msg);
        return json;
    }
 
    @ResponseBody("/express/update.do")
    public String update(HttpServletRequest req, HttpServletResponse resp) {
        int id = Integer.parseInt(req.getParameter("id"));
        String number = req.getParameter("number");
        String company = req.getParameter("company");
        String username = req.getParameter("username");
        String userphone = req.getParameter("userphone");
        int status = Integer.parseInt(req.getParameter("status"));
 
        Express e = new Express();
        e.setNumber(number);
        e.setCompany(company);
        e.setUsername(username);
        e.setUserphone(userphone);
        e.setStatus(status);
        boolean update = ExpressService.update(id, e);
        Message msg = new Message();
        if (update) {
            msg.setStatus(0);
            msg.setResult("修改成功");
        } else {
            msg.setStatus(-1);
            msg.setResult("修改失败");
        }
        String json = JSONUtil.toJSON(msg);
        return json;
    }
 
    @ResponseBody("/express/delete.do")
    public String delete(HttpServletRequest request,HttpServletResponse response){
        int id = Integer.parseInt(request.getParameter("id"));
        boolean flag = ExpressService.delete(id);
        Message msg = new Message();
        if(flag){
            msg.setStatus(0);
            msg.setResult("删除成功");
        }else{
            msg.setStatus(-1);
            msg.setResult("删除失败");
        }
        String json = JSONUtil.toJSON(msg);
        return json;
    }
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值