JavaWeb项目—快递e栈(后台部分)

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

写在前面

   这个部分对于我而言是一个重要的学习分水岭,学习了JavaWeb知识的之后,自己动手写一个demo的话可以更好的有助于之后阶段的学习。(应该)框架固然可以简化我们的开发流程,但如基本的JavaWeb都不熟练,又怎么会体验到框架的快捷性呢? (快递e站本来是有微信端和后台两部分组成的,我微信端忘了好多,就不写了。)    本demo实现了以下功能: 在这里插入图片描述

一,项目介绍

1.1项目背景

真实的:开课吧课程学习需要,培训进度到了。 虚假的:    随着互联网和通信技术的高速发展,使电子商务迅速普及,网购成为人们购物的重要手段之一。据统计2019年双十一,11月11日~16日,受电商平台集中促销影响,业务量达全年高峰,6天内共处理邮(快)件23.09亿件。    疫情期间配送快递成了一件难事。疫情防控已经趋于常态化,很多小区出台了新的治安管理条例,开始禁止快递人员进入。这给民众收发快件造成了极大的不便,因进不去小区,快递人员只能在小区门口周边摆摊设点,也影响了小区周边环境。    除了快递,外卖也一样面临相同的问题。除了小区,写字楼、校园也面临着相同的问题。正是基于对疫情期间配送最后一公里深刻的认识,我们探索出一条解决之道,那就是社区快递e栈(类似快递柜)。

1.2项目目的

   1、锻炼编码能力    2、融会贯通JavaWeb技术    3、体验项目开发流程    4、做出上线级别的微信端+JavaWeb后端项目(我微信端做的有点水)    5、为框架阶段打下前后端交互的基础

1.3项目涉及技术点

html、css、js、Jquery、bootstrap、layui、ajax、mysql、tomcat、servlet、filter、jsp,Javaweb相关知识点。

1.4需求分析

   该快递项目按照用户分为三层,管理员,普通用户和快递员,管理员可以对用户和快递员的数据进行增删改查操作,以及对快递的增删改查。管理员可以对快递进行增删改查,用户可以取件,也可以查看自己的快递情况。    按照功能模块分为登录模块,用户模块,快递员模块,快递模块,控制台模块,本系统主要是面向管理员端的后台关联系统。

二,搭建项目前准备

2.1编写MVC框架

   服务器启动时,会加载application.properties配置文件,配置文件中有多个类,配置文件是由HandlerMapping来加载。    HandlerMapping根据配置文件中的每个类,获取到每个类的方法,获取到方法之后再循环遍历这些方法,再找每个带有ResponseBody或ResponseView的方法,再把这些方法都存放在HandlerMapping中,之后用于处理给用户响应,所以服务器启动之后HandlerMapping中会存一堆方法。    当用户去请求DispatcherServlet时,首先去HandlerMapping中找有没有处理这个请求的方法,没有的话抛404异常。请求servlet的路径是:*.do

2.1.1编写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>DispatherServlet</servlet-name>
        <servlet-class>com.kaikeba.mvc.DispatherServlet</servlet-class>
        <!--初始化配置文件-->
        <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>DispatherServlet</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>
</web-app>
复制代码

2.1.2定义两个注解

package com.kaikeba.mvc;

import java.lang.annotation.*;

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

import java.lang.annotation.*;

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

再定义枚举类对应上面的两个注解:

package com.kaikeba.mvc;

/**
 * 枚举:用于描述响应的类型:文字;ResponseBody,视图;ResponseView
 */
public enum ResponseType {
    TEXT,VIEW;
}
复制代码

2.1.3 编写映射器 HandlerMapping

package com.kaikeba.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;

/**
 * 映射器 包含了大量的网址与方法的对应关系)
 */
public class HandlerMapping {

    // 静态的map集合,字符串加上mvc映射对象
    private static Map<String,MVCMapping> data = new HashMap<>();

    // 获取map集合中的对应方法
    public static MVCMapping get(String uri){
        return data.get(uri);
    }


    public static void load(InputStream is ){
        // 常规方法加载配置文件
        Properties ppt = new Properties();
        try {
            ppt.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //获取配置文件中描述的一个个的类
        Collection<Object> values = ppt.values();
        for (Object cla:values) { // 遍历配置文件中的所有类
            String className = (String) cla;
            try {
                //加载配置文件中描述的每一个类
                Class c = Class.forName(className);
                //创建这个类的对象
                Object obj = c.getConstructor().newInstance();
                //获取这个类的所有方法
                Method[] methods = c.getMethods();
                for (Method m:methods) { // 遍历一个类中的所有方法
                    Annotation[] as = m.getAnnotations(); // 获取方法里的所有注解
                    if(as != null){
                        for(Annotation annotation:as){ // 遍历一个方法中的所有注解
                            if(annotation instanceof ResponseBody){ // 如果注解的类型是ResponseBody
                                //说明此方法,用于返回字符串给客户端
                                MVCMapping mapping = new MVCMapping(obj,m,ResponseType.TEXT);
                                Object o = data.put(((ResponseBody) annotation).value(),mapping);
                                if(o != null){
                                    //存在了重复的请求地址
                                    throw new RuntimeException("请求地址重复:"+((ResponseBody) annotation).value());
                                }
                            }else if(annotation instanceof ResponseView){ // 如果注解的类型是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() {
        }

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

        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;
        }
    }
}
复制代码

2.1.4 定义分发Servlet

package com.kaikeba.mvc;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
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;
import java.util.Properties;
public class DispatherServlet extends javax.servlet.http.HttpServlet {

    @Override
    public void init(ServletConfig config) throws ServletException {
        // 读取配置文件的文件名称
        String path = config.getInitParameter("contentConfigLocation");
        // 加载配置文件
        InputStream is = DispatherServlet.class.getClassLoader().getResourceAsStream(path);
        // 常规方法加载
        /* //Properties可以用来保存属性集,主要用于读取Java的配置文件
        Properties ppt = new Properties();
        try {
            ppt.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }*/
        // 使用映射器加载
        HandlerMapping.load(is);

    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.    获取用户请求的uri   /xx.do
        String uri = req.getRequestURI();
        // 根据url得到MVCMapping映射对象
        HandlerMapping.MVCMapping mapping = HandlerMapping.get(uri);
        if( mapping == null){
            resp.sendError(404,"MVC:映射地址不存在:"+uri);
            return;
        }
        Object obj = mapping.getObj();
        Method method = mapping.getMethod();
        Object result = null;
        try {
            // 参数1.obj对象要执行method方法
            // 参数2.调用方法时传递的参数 req, resp 请求,响应对象
            result = method.invoke(obj, req, resp);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        switch (mapping.getType()){
            case TEXT:
                resp.getWriter().write((String)result);
                break;
            case VIEW:
                resp.sendRedirect((String)result);
                break;
        }
    }

}
复制代码

2.2layui介绍

layui官网地址

2.2.1布局部分

   与BootStrap很像,就是展示一些界面效果。

2.2.2 弹出层

步骤:    1.下载layer , 并将解压后的layer文件夹 移动到项目中    2.引入jquery.js    3.引入layer.js layer - msg函数 用于弹出信息提示框    格式1:

layer.msg("文本");
复制代码

   格式2 抖动显示:

 layer.msg("文本",function(){
        //弹窗结束后会执行
    });
复制代码

layer - load函数    格式1      弹出loading:

  var index = layer.load(数字0-2); 
// 参数表示 loading的图表
//loading窗口在弹出时, 不允许进行操作.
复制代码

          关闭loading:

layer.close(index);
复制代码

   格式2      超时自动关闭的loading

var index = layer.load(数字0-2,{time:毫秒数字})
//在指定的毫秒后 ,如果没有使用layer.close关闭, 则自动关闭
复制代码

layer - msg函数(load效果)    格式:      弹出的格式:

var index = layer.msg("文本",{icon:16,shade:0.01})
//因为是msg函数, 所以此窗口会自动消失
复制代码

   关闭的格式:

layer.close(index);
复制代码

layer - alert函数 信息提示窗    格式:

layer.alert("文本内容",{icon:图片编号});
//图片编号: 0-16之间 
复制代码

layer - tips函数 提示层    格式:

layer.tips("文本内容","选择器",{tipsMore:true,tips:数字});
复制代码

参数:    参数: tipsMore : 是否允许同时存在多个弹出的tips    参数: tips : 取值为数字1-4 , 分别表示弹出在元素的 上/右/下/左 . 默认弹出在右边 layer 所有弹出层的关闭

layer.closeAll();
复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="js/jquery2.1.4.js"></script>
    <script src="layer/layer.js"></script>
    
    <script>
        function msgTest() {
            // layer.msg("提示的文字");
            layer.msg("提示的文字", function () {
                // 窗口关系时执行,和上面的区别还有会添加一个抖动
            });
        }
        
        function loadTest() {
            // 返回弹出层的id
            var windowId = layer.load(0);  // 0 - 16的数字,每一个代表不同效果
            // 通过窗口id关闭窗口
            setTimeout(function () {
                layer.close(windowId);
            }, 2000);
        }

        function msg_loadTest() {
            // icon不同的值对应不同的图标
            layer.msg("提示的文字", {icon:16, shade:0.01});
        }
        
        function alertTest() {
            layer.alert("文字内容", {icon : 10});
        }

        function tipsTest() {
            // tipsMore表示
            layer.tips("这里有秘密", "#s1", {tipsMore:true, tips:2});
        }
    </script>
</head>
<body>
    <button onclick="msgTest()">msg函数(*)</button><br>
    <button onclick="loadTest()">load函数(*)</button><br>
    <button onclick="msg_loadTest()">load函数(load效果)</button><br>
    <button onclick="alertTest()">alert函数</button><br>
    <button onclick="tipsTest()">tips函数</button><br>
    <p>
        锄禾日<span id="s1">当午</span>,汗滴禾下土
    </p>
</body>
</html>
复制代码

2.3工具类介绍

2.3.1DruidUtil

进行数据库连接池的工具类

package com.kaikeba.util;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

public class DruidUtil {

    private static DataSource ds;
    
    static{
        try {
            Properties ppt = new Properties();
		ppt.load(DruidUtil.class.getClassLoader().getResourceAsStream("druid.properties"));
            ds = DruidDataSourceFactory.createDataSource(ppt);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 从连接池中取出一个连接给用户
     * @return
     */
    public static Connection getConnection(){
        try {
            return ds.getConnection();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return null;
    }

    public static void close(Connection conn, Statement state, ResultSet rs){
        try {
            if(rs!=null)
                rs.close();
        } catch (Exception throwables) {
            throwables.printStackTrace();
        }
        try {
            if(state!=null)
                state.close();
        } catch (Exception throwables) {
            throwables.printStackTrace();
        }
        try {
            if(conn!=null)
                conn.close();
        } catch (Exception throwables) {
            throwables.printStackTrace();
        }
    }
}
复制代码

2.3.2DateFormatUtil

时间格式化工具类

package com.kaikeba.util;

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

/**
 * 时间日期格式化工具
 */
public class DataFormatUtil {

    private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //字符串转日期类型

    public static String format(Date date){ // 把传入的Date类型转换为我们定义的Date类型
        return format.format(date);
    }
}
复制代码

2.3.3随机数工具类

package com.kaikeba.util;

import java.util.Random;

/**
 * @Description : 生成随机取件码
 * @Author : Administrator
 * @Date : 2021/11/17 7:56
 * @Version : 1.0
 **/
public class RandomUtil {
    private static Random r = new Random();
    public static int getCode(){
        return r.nextInt(900000)+100000;
    };
}
复制代码

2.3.4Json操作类

package com.kaikeba.util;

import com.google.gson.Gson;

/**
 * @Description : 为了更好的把数据返回给用户,返回Gson类型的数据给用户
 * @Author : Administrator
 * @Date : 2021/11/15 10:24
 * @Version : 1.0
 **/
public class JSONUtil {

    private static Gson g = new Gson();
    public static String toJSON(Object obj){ // 把对象变为json字符串
        return g.toJson(obj);
    }
}
复制代码

2.3.5用户的工具类

package com.kaikeba.util;

import com.kaikeba.bean.User;
import javax.servlet.http.HttpSession;

/**
 * @Description : 用户的工具类,
 * @Author : Administrator
 * @Date : 2021/11/17 22:05
 * @Version : 1.0
 **/
public class UserUtil {
    public static String getUserName(HttpSession session){// 根据session获取当前登录的用户名
        return (String) session.getAttribute("adminUserName");
    }
    public static String getUserPhone(HttpSession session){// 根据session获取当前登录的用户手机号
        // TODO : 还没有编写柜子端,未存储任何的录入人信息
        return "18888888888";
    }
}
复制代码

2.3.6前后端用于通信的消息类

package com.kaikeba.bean;

/**
 * @Description : 专门一个发消息的类
 * @Author : Administrator
 * @Date : 2021/11/15 10:13
 * @Version : 1.0
 **/
public class Message {

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


    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;
    }

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

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

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

    public Message() {
    }

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

2.3.7包装分页数据的类

package com.kaikeba.bean;

import java.util.ArrayList;
import java.util.List;

/**
 * @Description : 包装分页数据的一种格式
 * @Author : Administrator
 * @Date : 2021/11/17 16:20
 * @Version : 1.0
 **/
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;
    }
}
复制代码

2.3.8自定义的异常类

取件码重复异常:

package com.kaikeba.exception;

/**
 * @Description : 异常类:取件码重复异常
 * @Author : Administrator
 * @Date : 2021/11/17 7:20
 * @Version : 1.0
 **/
public class DuplicateCodeException extends Exception{
    /**
     * Constructs a new exception with {@code null} as its detail message.
     * The cause is not initialized, and may subsequently be initialized by a
     * call to {@link #initCause}.
     */
    public DuplicateCodeException() {
    }

    /**
     * Constructs a new exception with the specified detail message.  The
     * cause is not initialized, and may subsequently be initialized by
     * a call to {@link #initCause}.
     *
     * @param message the detail message. The detail message is saved for
     *                later retrieval by the {@link #getMessage()} method.
     */
    public DuplicateCodeException(String message) {
        super(message);
    }
}
复制代码

2.4数据库设计+实体类编写

2.4.1数据库分析

   之前的需求分析中有说到。要实现登录模块,用户模块,快递员模块,快递模块,控制台模块。那么就要创建自少四张表。管理员表,用户表,快递员表,快递员表。 管理员表 在这里插入图片描述 用户表 在这里插入图片描述 快递员表 在这里插入图片描述 快递表 在这里插入图片描述 其实单看这几张表的话,也能感觉到这是一个很简单的练手项目。甚至还不如他的前置项目表复杂。基本都是单表操作。具体的表内容就大家自己创建了。

2.4.2实体类

根据所创建的sql创建对应的类 类名=表名,属性名=列名 1,管理员类不用创建对应的实体类。 2,再创建时间类型的属性时,把他定义为String类型的,方便用对应的工具类显示 User:

package com.kaikeba.bean;

import java.sql.Timestamp;

/**
 * @Description : 同样也是用户实体类,不过这里用了字符类型来结束时间参数
 * @Author : Administrator
 * @Date : 2021/11/18 16:55
 * @Version : 1.0
 **/
public class BootStrapTableUser {
    private int id;
    private String username;
    private String userphone;
    private String idcard;
    private String userpassword;

    private String registeradtime;
    private String login_time;
    private int status;

    public int getStatus() {
        return status;
    }

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

    public BootStrapTableUser() {
    }

    public BootStrapTableUser(int id, String username, String userphone, String idcard, String userpassword, String registeradtime, String login_time, int status) {
        this.id = id;
        this.username = username;
        this.userphone = userphone;
        this.idcard = idcard;
        this.userpassword = userpassword;
        this.registeradtime = registeradtime;
        this.login_time = login_time;
        this.status = status;
    }

    public BootStrapTableUser(String username, String userphone, String idcard, String userpassword, int operation) {
        this.username = username;
        this.userphone = userphone;
        this.idcard = idcard;
        this.userpassword = userpassword;
    }

    public BootStrapTableUser(int id, String username, String userphone, String idcard, String userpassword, String registeradtime, String login_time) {
    }

    @Override
    public String toString() {
        return "BootStrapTableUser{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", userphone='" + userphone + '\'' +
                ", idcard='" + idcard + '\'' +
                ", userpassword='" + userpassword + '\'' +
                ", registeradtime='" + registeradtime + '\'' +
                ", login_time='" + login_time + '\'' +
                ", status=" + status +
                '}';
    }

    public int getId() {
        return id;
    }

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

    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 getIdcard() {
        return idcard;
    }

    public void setIdcard(String idcard) {
        this.idcard = idcard;
    }

    public String getUserpassword() {
        return userpassword;
    }

    public void setUserpassword(String userpassword) {
        this.userpassword = userpassword;
    }

    public String getRegisteradtime() {
        return registeradtime;
    }

    public void setRegisteradtime(String registeradtime) {
        this.registeradtime = registeradtime;
    }

    public String getLogin_time() {
        return login_time;
    }

    public void setLogin_time(String login_time) {
        this.login_time = login_time;
    }

}
复制代码

Express:

package com.kaikeba.bean;

/**
 * @Description : 同样也是快递实体类,不过这里用了字符类型来结束时间参数
 * @Author : Administrator
 * @Date : 2021/11/17 16:20
 * @Version : 1.0
 **/
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;

    private Courier Courier;

    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 BootStrapTableExpress(int id, String number, String username, String userPhone, String company, String code, String inTime, String outTime, String status, String sysPhone, com.kaikeba.bean.Courier courier) {
        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;
        Courier = courier;
    }

    @Override
    public String toString() {
        return "BootStrapTableExpress{" +
                "id=" + id +
                ", number='" + number + '\'' +
                ", username='" + username + '\'' +
                ", userPhone='" + userPhone + '\'' +
                ", company='" + company + '\'' +
                ", code='" + code + '\'' +
                ", inTime='" + inTime + '\'' +
                ", outTime='" + outTime + '\'' +
                ", status='" + status + '\'' +
                ", sysPhone='" + sysPhone + '\'' +
                ", Courier=" + Courier +
                '}';
    }

    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;
    }

    public com.kaikeba.bean.Courier getCourier() {
        return Courier;
    }

    public void setCourier(com.kaikeba.bean.Courier courier) {
        Courier = courier;
    }
}
复制代码

Courier:

package com.kaikeba.bean;

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

/**
 * @Description : 同样也是快递员实体类,不过这里用了字符类型来结束时间参数
 * @Author : Administrator
 * @Date : 2021/11/23 22:37
 * @Version : 1.0
 **/
public class BootStrapTableCourier {

    private int id;
    private String username;
    private String userphone;
    private String idcard;
    private String userpassword;
    private int sendnumber;


    private String registeradtime;
    private String login_time;
    private int status;

    // 一个角色对应多个用户
    private List<User> usersList;

    public BootStrapTableCourier() {
    }

    public BootStrapTableCourier(String username, String userphone, String idcard, String userpassword) {
        this.username = username;
        this.userphone = userphone;
        this.idcard = idcard;
        this.userpassword = userpassword;
    }

    public BootStrapTableCourier(int id, String username, String userphone, String idcard, String userpassword, int sendnumber, String registeradtime, String login_time, int status) {
        this.id = id;
        this.username = username;
        this.userphone = userphone;
        this.idcard = idcard;
        this.userpassword = userpassword;
        this.sendnumber = sendnumber;
        this.registeradtime = registeradtime;
        this.login_time = login_time;
        this.status = status;
    }

    public BootStrapTableCourier(int id, String username, String userphone, String idcard, String userpassword, int sendnumber, String registeradtime, String login_time, int status, List<User> usersList) {
        this.id = id;
        this.username = username;
        this.userphone = userphone;
        this.idcard = idcard;
        this.userpassword = userpassword;
        this.sendnumber = sendnumber;
        this.registeradtime = registeradtime;
        this.login_time = login_time;
        this.status = status;
        this.usersList = usersList;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof BootStrapTableCourier)) return false;
        BootStrapTableCourier that = (BootStrapTableCourier) o;
        return getId() == that.getId() &&
                getSendnumber() == that.getSendnumber() &&
                getStatus() == that.getStatus() &&
                Objects.equals(getUsername(), that.getUsername()) &&
                Objects.equals(getUserphone(), that.getUserphone()) &&
                Objects.equals(getIdcard(), that.getIdcard()) &&
                Objects.equals(getUserpassword(), that.getUserpassword()) &&
                Objects.equals(getRegisteradtime(), that.getRegisteradtime()) &&
                Objects.equals(getLogin_time(), that.getLogin_time()) &&
                Objects.equals(usersList, that.usersList);
    }

    @Override
    public int hashCode() {
        return Objects.hash(getId(), getUsername(), getUserphone(), getIdcard(), getUserpassword(), getSendnumber(), getRegisteradtime(), getLogin_time(), getStatus(), usersList);
    }

    @Override
    public String toString() {
        return "BootStrapTableCourier{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", userphone='" + userphone + '\'' +
                ", idcard='" + idcard + '\'' +
                ", userpassword='" + userpassword + '\'' +
                ", sendnumber=" + sendnumber +
                ", registeradtime='" + registeradtime + '\'' +
                ", login_time='" + login_time + '\'' +
                ", status=" + status +
                ", usersList=" + usersList +
                '}';
    }

    public int getId() {
        return id;
    }

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

    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 getIdcard() {
        return idcard;
    }

    public void setIdcard(String idcard) {
        this.idcard = idcard;
    }

    public String getUserpassword() {
        return userpassword;
    }

    public void setUserpassword(String userpassword) {
        this.userpassword = userpassword;
    }

    public int getSendnumber() {
        return sendnumber;
    }

    public void setSendnumber(int sendnumber) {
        this.sendnumber = sendnumber;
    }

    public String getRegisteradtime() {
        return registeradtime;
    }

    public void setRegisteradtime(String registeradtime) {
        this.registeradtime = registeradtime;
    }

    public String getLogin_time() {
        return login_time;
    }

    public void setLogin_time(String login_time) {
        this.login_time = login_time;
    }

    public int getStatus() {
        return status;
    }

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

    public List<User> getUsersList() {
        return usersList;
    }

    public void setUsersList(List<User> usersList) {
        this.usersList = usersList;
    }
}
复制代码

三,流程编写

3.1整体流程框图

大概流程如下:差了一个单词,猜猜看是哪个。 在这里插入图片描述

3.2配置文件(properties,过滤器)

数据库配置文件:druid.properties

url=jdbc:mysql://localhost:3306/javaweb_e?useUnicode=true&characterEncoding=utf-8
username=kaikeba
password=kaikeba
driverClassName=com.mysql.jdbc.Driver
复制代码

初始化配置文件:application.properties

admin=com.kaikeba.controller.AdminController
express=com.kaikeba.controller.ExpressController
user=com.kaikeba.controller.UserController
Courier=com.kaikeba.controller.CourierController
复制代码

再web.xml里,我们定义了*.do走DispatherServlet。

<servlet-mapping>
    <servlet-name>DispatherServlet</servlet-name>
    <url-pattern>*.do</url-pattern>
</servlet-mapping>
复制代码

所以编写一个过滤器,让所有的*.do请求格式都为utf-8

package com.kaikeba.filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
// 过滤器
@WebFilter("*.do")
public class CharSetFilter implements Filter {
    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        resp.setContentType("text/json;charset=utf-8");
        resp.setCharacterEncoding("utf-8");
        chain.doFilter(req, resp);
    }

    public void init(FilterConfig config) throws ServletException {

    }

}
复制代码

3.3结构代码编写(controller,service,dao)

结构就是这么一个结构。 在这里插入图片描述 我以管理员的方法举例: Controller:

public class AdminController {
}
复制代码

Service:

public class AdminService {
}
复制代码

Dao:

public interface BaseAdminDao {
}
复制代码
public class AdminDaoMysql implements BaseAdminDao {
}
复制代码

四,具体功能和详细代码

4.1管理员的登录退出功能。

service:

package com.kaikeba.service;
import com.kaikeba.dao.BaseAdminDao;
import com.kaikeba.dao.impl.AdminDaoMysql;

import java.util.Date;
/**
 * @Description : 管理员操作的方法
 * @Author : Administrator
 * @Date : 2021/11/15 11:09
 * @Version : 1.0
 **/
public class AdminService {

    private static BaseAdminDao dao = new AdminDaoMysql();

    /**
     * 更新登陆时间与ip
     * @param username
     * @param date
     * @param ip
     */
    public static void updateLoginTimeAndIP(String username, Date date, String ip){
        dao.updateLoginTime(username,date,ip);
    }

    /**
     * 登陆
     * @param username
     * @param password
     * @return true表示成功,false表示登陆失败
     */
    public static boolean login(String username,String password){
        return dao.login(username,password);
    }
}
复制代码

dao:

package com.kaikeba.dao;

import java.util.Date;

/**
 * 用于定义eadmin表格的操作规范
 */
public interface BaseAdminDao {

    /**
     * 根据用户名,更新登陆时间和登录ip
     * @param username
     */
    void updateLoginTime(String username, Date date, String ip);

    /**
     * 管理员根据账号密码登陆
     * @param username 账号
     * @param password 密码
     * @return 登陆的结果, true表示登陆成功
     */
    boolean login(String username, String password);
}
复制代码
package com.kaikeba.dao.impl;

import com.kaikeba.dao.BaseAdminDao;
import com.kaikeba.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.    填充参数
            state.setDate(1,new java.sql.Date(date.getTime()));
            state.setString(2,ip);
            state.setString(3,username);
            //4.    执行
            state.executeUpdate();

        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            //5.    释放资源
            DruidUtil.close(conn,state,null);
        }
    }

    /**
     * 管理员根据账号密码登陆
     * @param username 账号
     * @param password 密码
     * @return 登陆的结果, true表示登陆成功
     */
    @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.    填充参数
            state.setString(1,username);
            state.setString(2,password);
            //4.    执行并获取结果
            rs = state.executeQuery();
            //5.    根据查询结果,返回 有结果-true,无结果-false
            return rs.next();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            //6.    释放资源
            DruidUtil.close(conn,state,rs);
        }
        return false;
    }
}
复制代码

4.1.1管理员的登录功能。

(1)前端页面

<!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="../js/jquery2.1.4.js"></script>
<script src="../layer/layer.js"></script>
<!--<script src="../layer/layer.js"></script>-->

<script>
    $(function(){
        $(".login-button").click(function(){
            // TODO 记得对于用户名和密码进行判断
            var layer = layui.layer;
            var username = $("#username").val();
            var password = $("#password").val();
            if(username == null || password == null || username == "" || password == ""){
                alert("用户名或密码不能为空");
            }else {
                //1.    先使用layer,弹出load(提示加载中...)
                var windowId = layer.load();
                //2.    ajax与服务器交互
                $.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>
复制代码

(2)后端controller层方法

package com.kaikeba.controller;

import com.kaikeba.bean.Message;
import com.kaikeba.mvc.ResponseBody;
import com.kaikeba.service.AdminService;
import com.kaikeba.util.JSONUtil;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;

/**
 * @Description : TODO
 * @Author : Administrator
 * @Date : 2021/11/15 20:47
 * @Version : 1.0
 **/
public class AdminController {

    @ResponseBody("/admin/login.do")
    public String login(HttpServletRequest request, HttpServletResponse response){
        //1.    接参数
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        //2.    调用Service传参数,并获取结果
        boolean result = AdminService.login(username, password);
        //3.    根据结果,准备不同的返回数据
        Message msg = null;
        if(result){
            msg = new Message(0,"登录成功");//{status:0,result:"登录成功"}
            //登录时间 和 ip的更新
            Date date = new Date();
            String ip = request.getRemoteAddr(); // getRemoteAddr:获取客户端的IP地址
            AdminService.updateLoginTimeAndIP(username,date,ip);
            request.getSession().setAttribute("adminUserName","username");
        }else{
            msg = new Message(-1,"登录失败");//{status:-1,result:"登录失败"}
        }
        //4.    将数据转换为JSON
        String json = JSONUtil.toJSON(msg);
        //5.    将JSON回复给ajax
        return json;
    }
}
复制代码

4.1.2显示管理员用户名功能

AdminController中添加获取姓名方法:

@ResponseBody("/admin/getName.do")
public String getName(HttpServletRequest request, HttpServletResponse response){
    String userName = UserUtil.getUserName(request.getSession());
    Message msg = new Message();
    msg.setStatus(0);
    msg.setResult("查询成功");
    msg.setData(userName);
    String json = JSONUtil.toJSON(msg);
    return json;
}
复制代码

index.html部分:

$.post("/admin/getName.do",{},function (data) {
    $("#adminName").text(data.data);
});
复制代码

4.1.3管理员的退出功能

AdminController中添加退出登录方法:

@ResponseBody("/admin/logout.do")
public String logout(HttpServletRequest request, HttpServletResponse response){
    HttpSession session = request.getSession();
    session.removeAttribute("adminUserName");
    Message msg = new Message();
    msg.setStatus(0);
    msg.setResult("退出成功");
    String json = JSONUtil.toJSON(msg);
    return json;
}
复制代码

index.html完整代码:

<!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/admin.css">
    <link rel="icon" href="/favicon.ico">
    <title>快递e栈管理后台</title>
</head>
<body class="layui-layout-body">
    <div class="layui-layout layui-layout-admin">
        <div class="layui-header custom-header">
            
            <ul class="layui-nav layui-layout-left">
                <li class="layui-nav-item slide-sidebar" lay-unselect>
                    <a href="javascript:;" class="icon-font"><i class="ai ai-menufold"></i></a>
                </li>
            </ul>

            <ul class="layui-nav layui-layout-right">
                <li class="layui-nav-item">
                    <a><span id="adminName">aaaaaaa</span></a>
                    <dl class="layui-nav-child">
                        <dd><a href="#">帮助中心</a></dd>
                        <dd><a id="logout" href="login.html">退出</a></dd>
                    </dl>
                </li>
            </ul>
        </div>

        <div class="layui-side custom-admin">
            <div class="layui-side-scroll">

                <div class="custom-logo">
                    <img src="assets/images/logo.gif" alt=""/>
                    <h1>快递e栈</h1>
                </div>
                <ul id="Nav" class="layui-nav layui-nav-tree">
                    <li class="layui-nav-item">
                        <a href="javascript:;">
                            <i class="layui-icon">&#xe68e;</i>
                            <em>主页</em>
                        </a>
                        <dl class="layui-nav-child">
                            <dd><a href="views/console.html"><i class="layui-icon">&#xe665;</i> 控制台</a></dd>
                        </dl>
                    </li>
                    <li class="layui-nav-item">
                        <a href="javascript:;">
                            <i class="layui-icon">&#xe664;</i>
                            <em>快递员管理</em>
                        </a>
                        <dl class="layui-nav-child">
                            <dd><a href="views/courier/list.html"><i class="layui-icon">&#xe60a;</i> 快递员列表</a></dd>
                            <dd><a href="views/courier/add.html"><i class="layui-icon">&#xe654;</i> 快递员录入</a></dd>
                            <dd><a href="views/courier/update.html"><i class="layui-icon">&#xe642;</i> 快递员修改</a></dd>
                            <dd><a href="views/courier/delete.html"><i class="layui-icon">&#xe640;</i> 快递员删除</a></dd>
                        </dl>
                    </li>
                    <li class="layui-nav-item">
                        <a href="javascript:;">
                            <i class="layui-icon">&#xe857;</i>
                            <em>快件管理</em>
                        </a>
                        <dl class="layui-nav-child">
                            <dd><a href="views/express/list.html"><i class="layui-icon">&#xe60a;</i> 快件列表</a></dd>
                            <dd><a href="views/express/add.html"><i class="layui-icon">&#xe654;</i> 快件录入</a></dd>
                            <dd><a href="views/express/update.html"><i class="layui-icon">&#xe642;</i> 快件修改</a></dd>
                            <dd><a href="views/express/delete.html"><i class="layui-icon">&#xe640;</i> 快件删除</a></dd>
                        </dl>
                    </li>
                     <li class="layui-nav-item">
                        <a href="javascript:;">
                            <i class="layui-icon">&#xe770;</i>
                            <em>用户管理</em>
                        </a>
                        <dl class="layui-nav-child">
                            <dd><a href="views/user/list.html"><i class="layui-icon">&#xe60a;</i> 用户列表</a></dd>
                            <dd><a href="views/user/add.html"><i class="layui-icon">&#xe654;</i> 用户增加</a></dd>
                            <dd><a href="views/user/update.html"><i class="layui-icon">&#xe642;</i> 用户修改</a></dd>
                            <dd><a href="views/user/delete.html"><i class="layui-icon">&#xe640;</i> 用户删除</a></dd>
                        </dl>
                    </li>
                </ul>

            </div>
        </div>

        <div class="layui-body">
             <div class="layui-tab app-container" lay-allowClose="true" lay-filter="tabs">
                <ul id="appTabs" class="layui-tab-title custom-tab"></ul>
                <div id="appTabPage" class="layui-tab-content"></div>
            </div>
        </div>

        <div class="layui-footer">
            <p>©2020 <a href="http://www.itdage.com/" target="_blank">李伟杰版权声明</a></p>
        </div>

        <div class="mobile-mask"></div>
    </div>
    <script src="assets/layui.js"></script>
    <script src="js/index.js" data-main="home"></script>

    <script>
        $(function () {
            $.post("/admin/getName.do",{},function (data) {
                $("#adminName").text(data.data);
            });

            $("#logout").click(function () {
                var windowId = layer.load();
                $.post("/admin/logout.do",{},function (data) {
                    layer.msg(data.result);
                    layer.close(windowId);
                    location.href = "/admin/login.html";
                })
            });
        });
    </script>
</body>
</html>
复制代码

4.2快递的CRUD及对应控制台的显示

因为感觉快递员的CRUD和用户的CRUD都与快递的CRUD没什么差别。所以哪两个就不写了。 这里涉及到了我们自定义的异常类。 本来想把控制台的显示拿出来的,但是仔细想想,没必要。

4.2.1API编写流程

#### 快递 部分

##### 1. 用于获取控制台所需的快递数据
请求地址:express/console.do
参数列表:无
返回的格式示例:
	{
		status:0,
		reuslt:"获取成功",
		data:[
			{//全部的快递
				size:1000,//快递总数
				day:100//今日新增
			},{//待取件快递
				size:500,//待取件数
				day:100//今日新增
			}
		]
	}

##### 2. 快件列表(分页)
请求地址:express/findAll.do
参数列表:
	1.	limit:
			值:0,表示开启分页(默认)
			值:1,表示查询所有
	2.	offset:
			值:数字,表示SQL语句起始索引
	3.	pageNumber:
			值:数字,表示获取的快递数量		
返回的格式示例:

##### 3.根据单号查询快递信息
请求地址:express/findByNumber.do
参数列表:
	1.	number:快递单号
返回的格式示例:

##### 4. 根据取件码查询快递信息
请求地址:express/findByCode.do
参数列表:
	1.	code:取件码
返回的格式示例:

##### 5. 根据用户的手机号,查询快递信息
请求地址:express/findByUserPhone.do
参数列表:
	1.	phoneNumber:手机号码
	2.	status:
			值:0表示查询待取件的快递(默认)
			值:1表示查询已取件的快递
			值:2表示查询用户的所有快递
返回的格式示例:

##### 6.根据录入人的手机号,查询快递信息(快递员/柜子的历史记录)
请求地址:express/findBySysPhone.do
参数列表:
	1.	sysPhone:手机号码
返回的格式示例:

##### 7.进行快递数量的排序查询(用户表)
请求地址:express/lazyboard.do
参数列表:
	1.	type:
			值:0,表示查询总排名
			值:1,表示查询年排名
			值:2,表示查询月排名
返回的格式示例:

##### 8.快件录入
请求地址:express/insert.do
参数列表:
	1.	number:快递单号
	2.	company:快递公司
	3.	username:收件人姓名
	4.	userPhone:收件人手机号码
录入成功返回的格式示例:
录入失败返回的格式示例:

##### 9. 修改快递信息
请求地址:express/update.do
参数列表:
	1.	id:要修改的快递id
	2.	number:新的快递单号
	3.	company:新的快递公司
	4.	username:新的收货人姓名
	5.	userPhone:新的收件人手机号码,(手机号码更新,重新生成取件码,并发送短信)
	6.	status:新的快递的状态
返回的格式示例:

##### 10. 根据id删除快递信息
请求地址:express/delete.do
参数列表:
	1.	id:	要删除的快递的id
返回的格式示例:

##### 11.确认取件
请求地址:express/updateStatus.do
参数列表:
	number:要更改状态为已取件的快递单号
返回的格式示例:
复制代码

4.2.2sql语句编写

  //用于查询数据库中的全部快递(总数+新增),待取件快递(总数+新增)
    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=?";
复制代码

4.2.3快递相关的dao

步骤:    1.获取数据库连接    2.预编译sql    3.填充参数(可选)    4.执行sql    5.获取执行结果    6.资源释放 dao 接口:

package com.kaikeba.dao;

import com.kaikeba.bean.Express;
import com.kaikeba.exception.DuplicateCodeException;

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

/**
 * 跟快递有关的dao层接口
 */
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);
}
复制代码

完整实现:

package com.kaikeba.dao.impl;

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

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

/**
 * @Description : TODO
 * @Author : Administrator
 * @Date : 2021/11/16 15:31
 * @Version : 1.0
 **/
public class ExpressDaoMysql implements BaseExpressDao {

    //用于查询数据库中的全部快递(总数+新增),待取件快递(总数+新增)
    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=?";

    /**
     * 用于查询数据库中的全部快递(总数+新增),待取件快递(总数+新增)
     *
     * @return [{size:总数,day:新增},{size:总数,day:新增}]
     */
    @Override
    public List<Map<String, Integer>> console() {
        ArrayList<Map<String,Integer>> data = new ArrayList<>();
        //1.    获取数据库的连接
        Connection conn = DruidUtil.getConnection();
        PreparedStatement state = null;
        ResultSet result = null;
        //2.    预编译SQL语句
        try {
            state = (PreparedStatement) 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);
            }
            //6.    资源的释放
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            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 = (PreparedStatement) conn.prepareStatement(SQL_FIND_LIMIT);
                //3.    填充参数(可选)
                state.setInt(1,offset);
                state.setInt(2,pageNumber);
            }else {
                state = (PreparedStatement) 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);
            }
            //6.    资源的释放
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            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;
        //2.    预编译SQL语句
        try {
            state = (PreparedStatement) 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;
            }
            //6.    资源的释放
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            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;
        //2.    预编译SQL语句
        try {
            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 username = result.getString("username");
                String number = result.getString("number");
                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;
            }
            //6.    资源的释放
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            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;
        //2.    预编译SQL语句
        try {
            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);
            }
            //6.    资源的释放
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            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;
        //2.    预编译SQL语句
        try {
            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);
            }
            //6.    资源的释放
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            DruidUtil.close(conn,state,result);
        }
        return data;
    }

    /**
     * 快递的录入
     * INSERT INTO EXPRESS (NUMBER,USERNAME,USERPHONE,COMPANY,CODE,INTIME,STATUS,SYSPHONE) VALUES(?,?,?,?,?,NOW(),0,?)
     * @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 'code'")){
                //是因为取件码重复,而出现了异常
                DuplicateCodeException e2 = new DuplicateCodeException(e1.getMessage());
                throw e2;
            }else{
                e1.printStackTrace();
            }
        }finally {
            //5.    释放资源
            DruidUtil.close(conn,state,null);
        }
        return false;
    }

    /**
     * 快递的修改
     * UPDATE EXPRESS SET NUMBER=?,USERNAME=?,COMPANY=?,STATUS=? WHERE ID=?
     * @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.getCompany());
            state.setInt(4,newExpress.getStatus());
            state.setInt(5,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?true:false;
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            DruidUtil.close(conn,state,null);
        }
        return false;
    }
}
复制代码

4.2.4service

package com.kaikeba.service;

import com.kaikeba.bean.Express;
import com.kaikeba.dao.BaseExpressDao;
import com.kaikeba.dao.impl.ExpressDaoMysql;
import com.kaikeba.exception.DuplicateCodeException;
import com.kaikeba.util.RandomUtil;

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

/**
 * @Description : 快递相关服务层
 * @Author : Administrator
 * @Date : 2021/11/17 7:48
 * @Version : 1.0
 **/
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){
        //1.    生成了取件码
        e.setCode(RandomUtil.getCode()+"");
        try {
            boolean flag = dao.insert(e);
            return flag;
        } catch (DuplicateCodeException duplicateCodeException) {
            return insert(e);
        }
    }

    /**
     * 快递的修改
     *
     * @param id         要修改的快递id
     * @param newExpress 新的快递对象(number,company,username,userPhone)
     * @return 修改的结果,true表示成功,false表示失败
     *
     *
     *  逻辑 BUG ,
     */
    public static boolean update(int id, Express newExpress) {
        if(newExpress.getUserPhone()!=null){// 如果用户的手机号不等于空,(修改的时候直接带手机号不好吗)
            dao.delete(id);
            return insert(newExpress);
        }else{
            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);
    }
}
复制代码

4.2.5控制台显示

ExpressController :

package com.kaikeba.controller;

import com.kaikeba.bean.BootStrapTableExpress;
import com.kaikeba.bean.Express;
import com.kaikeba.bean.Message;
import com.kaikeba.bean.ResultData;
import com.kaikeba.mvc.ResponseBody;
import com.kaikeba.service.ExpressService;
import com.kaikeba.util.DataFormatUtil;
import com.kaikeba.util.JSONUtil;
import com.kaikeba.util.UserUtil;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @Description : TODO
 * @Author : Administrator
 * @Date : 2021/11/17 11:17
 * @Version : 1.0
 **/
public class ExpressController {

    @ResponseBody("/express/console.do")
    public String console(HttpServletRequest request, HttpServletResponse response){
        List<Map<String, Integer>> data = ExpressService.console();
        Message msg = new Message();
        if(data.size() == 0){
            msg.setStatus(-1);
        }else {
            msg.setStatus(0);
        }
        msg.setData(data);
        String json = JSONUtil.toJSON(msg);
        return json;
    }
}
复制代码

前端页面:

<!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"/>
    <script src="../assets/echarts.min.js"></script>
    <script src="../../js/jquery2.1.4.js"></script>
    <title></title>
</head>
<body class="layui-view-body">
    <div class="layui-content">
        <div class="layui-row layui-col-space20">

            <div class="layui-col-sm6 layui-col-md6">
                <div class="layui-card">
                    <div class="layui-card-body chart-card">
                        <div class="chart-header">
                            <div class="metawrap">
                                <div class="meta">
                                    <span>用户人数</span>
                                </div>
                                <div id="data_size" class="total">-</div>
                            </div>
                        </div>
                        <div class="chart-body">
                            <div class="contentwrap">
                            </div>
                        </div>
                        <div class="chart-footer">
                            <div class="field">
                                <span>日注册量</span>
                                <span id="data_day">-</span>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            <div class="layui-col-sm6 layui-col-md6">
                <div class="layui-card">
                    <div class="layui-card-body chart-card">
                        <div class="chart-header">
                            <div class="metawrap">
                                <div class="meta">
                                    <span>快递员人数</span>
                                </div>
                                <div id="data_size3" class="total">-</div>
                            </div>
                        </div>
                        <div class="chart-body">
                            <div class="contentwrap">
                            </div>
                        </div>
                        <div class="chart-footer">
                            <div class="field">
                                <span>日注册量</span>
                                <span id="data_day3">-</span>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            <div class="layui-col-sm6 layui-col-md6">
                <div class="layui-card">
                    <div class="layui-card-body chart-card">
                        <div class="chart-header">
                            <div class="metawrap">
                                <div class="meta">
                                    <span>总快件数</span>
                                </div>
                                <div id="data1_size" class="total">-</div>
                            </div>
                        </div>
                        <div class="chart-body">
                            <div class="contentwrap">
                            </div>
                        </div>
                        <div class="chart-footer">
                            <div class="field">
                                <span>日派单量</span>
                                <span id="data1_day">-</span>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            <div class="layui-col-sm6 layui-col-md6">
                <div class="layui-card">
                    <div class="layui-card-body chart-card">
                        <div class="chart-header">
                            <div class="metawrap">
                                <div class="meta">
                                    <span>待取件数</span>
                                </div>
                                <div id="data2_size" class="total">-</div>
                            </div>
                        </div>
                        <div class="chart-body">
                            <div class="contentwrap">
                            </div>
                        </div>
                        <div class="chart-footer">
                            <div class="field">
                                <span>新增数量</span>
                                <span id="data2_day">-</span>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
           
            <div class="layui-col-sm12 layui-col-md12">
                <div class="layui-card">
                    <div class="layui-tab layui-tab-brief">
                        <ul class="layui-tab-title">
                            <li class="layui-this">实时快件区域分布图</li>
                        </ul>
                        <div class="layui-tab-content">
                            <div class="layui-tab-item layui-show">
                               <iframe src="map.html" style="width:100%;height:600px;"></iframe>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <script src="../assets/layui.all.js"></script>
    <script>
     var element = layui.element;
    </script>
    <script>
        $(function(){
            //1.    ajax与服务器交互
            $.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");
            $.post("/user/console.do",null,function(data){
                if(data.status == 0){
                    //[{size:总数,day:新增},{size:总数,day:新增}]
                    $("#data_size").html(data.data[0].data_size);
                    $("#data_day").html(data.data[0].data_day);
                }
            },"JSON");
            $.post("/courier/console.do",null,function(data){
                if(data.status == 0){
                    //[{size:总数,day:新增},{size:总数,day:新增}]
                    $("#data_size3").html(data.data[0].data_size3);
                    $("#data_day3").html(data.data[0].data_day3);
                }
            },"JSON");
        });

    </script>
</body>
</html>
复制代码

4.2.6分页列表

ExpressController :

   @ResponseBody("/express/list.do")
    public String list(HttpServletRequest request, HttpServletResponse response){
        //1.    获取查询数据的起始索引值
        int offset = Integer.parseInt(request.getParameter("offset"));
        //2.    获取当前页要查询的数据量
        int pageNumber = Integer.parseInt(request.getParameter("pageNumber"));
        //3.    进行查询
        List<Express> list = ExpressService.findAll(true, offset, pageNumber);
        List<BootStrapTableExpress> list2 = new ArrayList<>();
        for(Express e:list){
            String inTime = DataFormatUtil.format(e.getInTime());
            String outTime = e.getOutTime()==null?"未出库":DataFormatUtil.format(e.getOutTime());
            String status = e.getStatus()==0?"待取件":"已取件";
            String code = e.getCode()==null?"已取件":e.getCode();
            BootStrapTableExpress e2 = new BootStrapTableExpress(e.getId(),e.getNumber(),e.getUsername(),e.getUserPhone(),e.getCompany(),code,inTime,outTime,status,e.getSysPhone());
            list2.add(e2);
        }
        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;
    }
复制代码

前端页面

<!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"/>
		<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/[email protected]/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/[email protected]/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>

复制代码

4.2.7CRUD方法

ExpressController :

   @ResponseBody("/express/insert.do")
    public String insert(HttpServletRequest request, HttpServletResponse response){
        String number = request.getParameter("number");
        String company = request.getParameter("company");
        String username = request.getParameter("username");
        String userPhone = request.getParameter("userPhone");

        Express e = new Express(number,username,userPhone,company, UserUtil.getUserPhone(request.getSession()));
        boolean flag = ExpressService.insert(e);
        Message msg = new Message();
        if(flag){
            //录入成功
            msg.setStatus(0);
            msg.setResult("快递录入成功!");
        }else{
            //录入失败
            msg.setStatus(-1);
            msg.setResult("快递录入失败!");
        }
        String json = JSONUtil.toJSON(msg);
        return json;
    }

   @ResponseBody("/express/find.do")
    public String find(HttpServletRequest request,HttpServletResponse response){
        String number = request.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 request,HttpServletResponse response){
        int id = Integer.parseInt(request.getParameter("id"));
        String number = request.getParameter("number");
        String company = request.getParameter("company");
        String username = request.getParameter("username");
        String userPhone = request.getParameter("userPhone");
        int status = Integer.parseInt(request.getParameter("status"));
        Express newExpress = new Express();
        newExpress.setNumber(number);
        newExpress.setCompany(company);
        newExpress.setUsername(username);
        newExpress.setUserPhone(userPhone);
        newExpress.setStatus(status);
        boolean flag = ExpressService.update(id, newExpress);
        Message msg = new Message();
        if(flag){
            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;
    }
复制代码

前端页面: 新增页面

<!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 >FedEx中国</option>
                            <option >FedEx国际</option>
                            <option >FedEx美国</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" id="phone" name="phonenumber" required  lay-verify="required" placeholder="请输入手机号码" autocomplete="off" class="layui-input" onblur="validatephone()">
                        <span id="phoneMsg"></span>
                    </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="/js/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(); // 获取到窗口的id
            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("顺丰速运");
                }else{
                    //录入失败
                }
            });
            return false;
        });
    });

    /*校验手机号*/
    function isPhoneNum(str) {
        //如果你要精确验证手机号码,那个你可以使用第一个正则。这是根据电信,移动,联通目前发行的号码来的。验证比较精确。
        var reg = /^1[3|4|5|7|8][0-9]{9}$/;
        // 如果因为现有的号码不能满足市场需求,电信服务商会增大号码范围。所以一般情况下我们只要验证手机号码为11位,且以1开头。
        var reg = /^^1[0-9]{10}$$/;
        return reg.test(str);
    }
    function validatephone(){
        var str=document.getElementById("phone").value;
        var msg=document.getElementById("phoneMsg");
        if(isPhoneNum(str)==false){
            msg.innerHTML="phone格式不合法!";
            msg.style.color="red";
            return false;
        }
        msg.innerHTML="phone合法!";
        msg.style.color="green";
        return true;
    }
</script>
</body>
</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" placeholder="请输入运单号码" id="number">
				<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 id="phone" class="input" placeholder="请输入手机号码" onblur="validatephone()"><span id="phoneMsg"></span></td>
			</tr>
			<tr>
				<td class="text-right">快递状态</td><td class="content_right">
				<input type="radio" class="status" name="status" value="1">已签收
				<input class="status"  name="status" type="radio" 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="/js/jquery2.1.4.js"></script>
<script src="/layer/layer.js"></script>
<script>
	$(".header:eq(1),.content:eq(1)").hide();
	var expressId = null;
	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);
				//data:{"status":0,"result":"查询成功","data":{"id":2,"number":"124","username":"张三","userPhone":"13843838438","company":"京东快递","code":"213456","inTime":"Jul 21, 2020, 4:26:21 PM","status":0,"sysPhone":"18888888888"}}
				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();
			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();
				}
			})
		});
	});

	/*校验手机号*/
	function isPhoneNum(str) {
		//如果你要精确验证手机号码,那个你可以使用第一个正则。这是根据电信,移动,联通目前发行的号码来的。验证比较精确。
		var reg = /^1[3|4|5|7|8][0-9]{9}$/;
		// 如果因为现有的号码不能满足市场需求,电信服务商会增大号码范围。所以一般情况下我们只要验证手机号码为11位,且以1开头。
		var reg = /^^1[0-9]{10}$$/;
		return reg.test(str);
	}
	function validatephone(){
		var str=document.getElementById("phone").value;
		var msg=document.getElementById("phoneMsg");
		if(isPhoneNum(str)==false){
			msg.innerHTML="phone格式不合法!";
			msg.style.color="red";
			return false;
		}
		msg.innerHTML="phone合法!";
		msg.style.color="green";
		return true;
	}

</script>
</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 id="number" class="input inline-input" 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 class="content_right" id="status">已签收</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="/js/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);
				//data:{"status":0,"result":"查询成功","data":{"id":2,"number":"124","username":"张三","userPhone":"13843838438","company":"京东快递","code":"213456","inTime":"Jul 21, 2020, 4:26:21 PM","status":0,"sysPhone":"18888888888"}}
				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>

复制代码

参考: blog.csdn.net/weixin_4414… blog.csdn.net/HNU_Csee_wj… 项目源码+各种参考资料: 链接:pan.baidu.com/s/1tmX2Q6ek… 提取码:1111

猜你喜欢

转载自juejin.im/post/7080520790092972046