第八章WEB实训项目_快递驿站后台管理
第一节编写mvc框架
1.基本概念
在项目编写时,如果使用servlet/jsp等技术来做较繁琐。举个例子,我们要写一个demo,需实现注册、登录用户信息获取三个方法,原来我们需要给每一个方法都配一个servlet,流程不太清晰,概念也容易混淆。如果利用mvc框架进行编写,让他们都去找一个类,这个类有三个方法,当需要某个方法时,就去对应不同的servlet,如下图所示,这样就可以很好地优化代码,便于理解维护。
更重要的一个原因是这样做可以提前建立框架的认知,本章项目代码大致参考SpringMVC,对其关键性步骤进行处理,非关键性步骤弱化,以便于理解。
我们去编写一个servlet,所有的*.do文件都去找这个servlet。servlet里面去重写初始化方法init,去加载配置文件,去建立映射地址的池(某个方法对应到某个类的某个方法)
流程:所有用户请求的入口servlet–>映射池–>调用方法–>将结果返回给用户
2.项目创建
在idea创建javaweb项目;
在src下新建一个servlet,名为DispatcherServlet,放于com.czy中;
编写在web/WEB-INF下面的web.xml的配置文件,代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--自己加的-->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>com.czy.mvc.DispatcherServlet</servlet-class>
<!--模拟spring MVC框架,需要添加一些配置参数,配置文件(.properties)默认在src目录下。servlet启动时会加载一个名为contentConfigLocation的初始化参数,参数里面描述了一个配置文件,它的名称为application.properties,我们需要编写其他代码去加载这个文件-->
<init-param>
<param-name>contentConfigLocation</param-name>
<param-value>application.properties</param-value>
</init-param>
<!--项目启动时加载此servlet-->
<load-on-startup>0</load-on-startup>
</servlet>
<!--映射地址-->
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<!--所有以.do结尾的请求都会去找上面那行的DispatcherServlet-->
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<!--自己加的-->
</web-app>
接下来,给我们的servlet(即刚创建的DispatcherServlet)重写它的初始化方法;
//DispatcherServlet的初识化方法init
@Override
public void init(ServletConfig config) throws ServletException {
//利用ServletConfig类的名为config的对象,调用它的getInitParameter方法,
//传入参数contentConfigLocation,这个是web.xml配置文件中初始化配置参数的名字
//getInitParameter方法取到了名为contentConfigLocation的配置文件路径,将其命名为path
String path = config.getInitParameter("contentConfigLocation");
//通过上面得到的路径path,我们调用DispatcherServlet.class.getClassLoader()方法,获得一个输入流is
InputStream is = DispatcherServlet.class.getClassLoader().getResourceAsStream(path);
//在初识化servlet的时候就要加载配置文件的输入流,使用 HandlerMapping.load方法
HandlerMapping.load(is);
}
通过反射技术,我们去读取这个配置文件application.properties中所有类的注解情况。除此之外,我们先要编写两个注解和一个枚举。
package com.czy.mvc;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
/**
* 注解的作用:
* 被此注解添加的方法,会被用于处理请求
* 方法返回的内容,会以文字方式返回到客户端
*/
public @interface ResponseBody {
String value();
}
package com.czy.mvc;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
/**
* 注解的作用:
* 被此注解添加的方法,会被用于处理请求
* 方法返回的内容,会直接重定向
*/
public @interface ResponseView {
String value();
}
package com.czy.mvc;
public enum ResponseType {
TEXT, VIEW;
}
编写DispatcherServlet:
package com.czy.mvc;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@WebServlet(name = "DispatcherServlet")
public class DispatcherServlet extends HttpServlet {
//DispatcherServlet的初识化方法init
@Override
public void init(ServletConfig config) throws ServletException {
//利用ServletConfig类的名为config的对象,调用它的getInitParameter方法,
//传入参数contentConfigLocation,这个是web.xml配置文件中初始化配置参数的名字
//getInitParameter方法取到了名为contentConfigLocation的配置文件路径,将其命名为path
String path = config.getInitParameter("contentConfigLocation");
//通过上面得到的路径path,我们调用DispatcherServlet.class.getClassLoader()方法,获得一个输入流is
InputStream is = DispatcherServlet.class.getClassLoader().getResourceAsStream(path);
//在初识化servlet的时候就要加载配置文件的输入流,使用 HandlerMapping.load方法
HandlerMapping.load(is);
}
@Override
public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.获取用户的请求uri
String uri = req.getRequestURI();
//调用HandlerMapping类的get方法,传入uri,得到一个MVCMapping类的mapping对象
HandlerMapping.MVCMapping mapping = HandlerMapping.get(uri);
//若映射地址不存在,则返回null
if (mapping == null) {
resp.sendError(404, "自定义MVC:映射地址不存在" + uri);
return;
}
Object obj = mapping.getObj();//用这个MVCMapping类的mapping对象,调用它的getOBJ方法,得到对象
Method method = mapping.getMethod();//用这个MVCMapping类的mapping对象,调用它的getMethod方法,得到方法
Object result = null;//向上抽取result,先新建出来
try {
result = method.invoke(obj, req, resp);//调用method的invoke方法,得到注释中事先约定好的返回结果(String类型的)
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
//判断这个mapping返回类型是text还是view,向前台回复处理的结果
switch (mapping.getType()) {
case TEXT:
resp.getWriter().write((String) result);
break;
case VIEW:
resp.sendRedirect((String) result);
break;
}
}
}
编写HandlerMapping:
package com.czy.mvc;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
/**
* 映射器:包含了大量网址和方法的对应关系
* 这个HandlerMapping类会包含n个MVCMapping对象
* 用户每次发起请求的时候,我们就去HandlerMapping里面的Map找,用哪个方法来给他做处理
* 这个类主要作用:用来描述一些处理请求的方法
*/
public class HandlerMapping {
//我们读取配置文件,就会把配置文件所指向的那些类里面,所有用了那两个注解的方法封装成这样一个对象
private static Map<String,MVCMapping> data = new HashMap<>();
//从data这个对象里面,获取网址所对应的所有方法
public static MVCMapping get(String uri){
return data.get(uri);
}
public static void load(InputStream is){
//is这个输入流能得到很多的类名,接下来获取这些很多的类名
Properties ppt = new Properties();
try {
ppt.load(is);
} catch (IOException e) {
e.printStackTrace();
}
//获取的values是application.properties里面的等号的右边的值(获取配置文件中的一个个类)
Collection<Object> values = ppt.values();
//遍历,一个个进行反射处理,把类进行加载
for (Object cla:values){
//获取的是一个个类对象,名为cla,下面一行代码是将其强转成类名字,类型为String
String className = (String) cla;
try {
//用拿到的类的名称,去得到一个个具体的类(加载配置文件中描述的每一个类)
Class c = Class.forName(className);
//创建这个类的对象(用一个个具体的类,调用其无参的构造函数去创建具体的对象)
Object obj = c.getConstructor().newInstance();
//获取这个类的所有方法,名为methods
Method[] methods = c.getMethods();
//遍历每一个类中方法们的具体某个方法
for (Method m: methods) {
//把每个方法的注解拿到,命名为as
Annotation[] as = m.getAnnotations();
if (as!=null){
for (Annotation annotation : as) {
if (annotation instanceof ResponseBody) {//说明该方法返回字符串给客户端
//为这个方法新建一个MVCMapping映射对象,名为mapping,把相关参数传进去(该方法当前在的类,该方法,返回值类型)
MVCMapping mapping = new MVCMapping(obj, m, ResponseType.TEXT);
//把相关数据放入HandlerMapping 这个大类中最前面的属性data,调用它的put方法
Object o = data.put((((ResponseBody) annotation).value()), mapping);
if (o != null){
//存在了重复的地址
throw new RuntimeException("请求地址重复"+((ResponseBody) annotation).value());
}
} else if (annotation instanceof ResponseView) {//说明该方法返回页面给客户端
MVCMapping mapping = new MVCMapping(obj, m, ResponseType.VIEW);
Object o = data.put((((ResponseView) annotation).value()), mapping);
if (o != null){
//存在了重复的地址
throw new RuntimeException("请求地址重复"+((ResponseView) annotation).value());
}
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 映射对象:每一个映射对象封装了一个方法,用于处理请求
*/
public static class MVCMapping {
private Object obj;
private Method method;
private ResponseType type;
public MVCMapping(Object obj, Method method, ResponseType type) {
this.obj = obj;
this.method = method;
this.type = type;
}
public MVCMapping() {
}
public Object getObj() {
return obj;
}
public void setObj(Object obj) {
this.obj = obj;
}
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
public ResponseType getType() {
return type;
}
public void setType(ResponseType type) {
this.type = type;
}
}
}
当服务器启动的时候,DispatcherServlet 就会去加载配置文件,会在HandlerMapping里面存储一堆的方法。
当用户去请求DispatcherServlet 的时候,我们就去HandlerMapping里面去找有没有处理这个请求的方法,若无则404,若有,我们的方法开头一定会有一段注解,再进行后续处理。
第二节项目搭建与工具整理
二维码生成和Layer部分省略,阿里云短信业务砍掉。
将德鲁伊的配置文件放于src下
url=jdbc:mysql://localhost:3306/e?useUnicode=true&characterEncoding=utf-8&serverTimezone =Asia/Shanghai
username=root
password=123456
driverClassName=com.mysql.jdbc.Driver
initialSize=5
# 最大连接池数量
maxActive=10
# 最小连接池数量
minIdle=5
# 获取连接时最大等待时间,单位毫秒
maxWait=5000
在web/WEB-INF下新建lib文件夹,放入相关jar包并引入,如下图:
在src下新建bean包,在里面新建Message对象,作为后续消息:
package com.czy.bean;
public class Message {
// 每次给用户回复的是这样一段json格式{status:0, result:"", data:{}}
// 状态码 0成功 -1失败
private int status;
// 消息内容
private String result;
// 消息携带的数据
private Object data;
public Message(int status, String result, Object data) {
this.status = status;
this.result = result;
this.data = data;
}
public Message() {
}
public Message(int status) {
this.status = status;
}
public Message(String result) {
this.result = result;
}
public Message(int status, String result) {
this.status = status;
this.result = result;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
在src下新建util包,先把之前JDBC项目中的DruidUtil复制过来,再新建两个工具类:1.DateFormatUtil用于后续格式化日期;2.JSONUtil作为转换为json格式的工具类。
package com.czy.util;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateFormatUtil {
private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static String format(Date date) {
return format.format(date);
}
}
package com.czy.util;
import com.google.gson.Gson;
//转换为json格式的工具类
public class JSONUtil {
private static Gson g = new Gson();
public static String toJSON(Object obj) {
return g.toJson(obj);
}
}
第三节管理员数据操作部分
在web目录下创建admin文件夹,将素材模板粘贴进去。接下来正式编写后台管理部分,我们计划分为五部分进行编写:1.管理员登录;2.快递的管理;3.用户的管理;4.快递员的管理;5.控制台。整体的编写我们参考前后端分离的架构去实现,后端在编写时映射的地址没有遵循相应规范,后面的框架学习才会学习到。
管理员登录部分我们需要做两个事情:1.检查账号密码是否正确,正确则进行登录;2.写入本次登录的ip和时间
在src下新建dao包,新建BaseAdminMysql接口,在包内再新建一个包imp,里面存放dao包中接口的实现类。
BaseAdminMysql接口:
package com.czy.dao;import java.util.Date;/** * 用于定义eadmin表格的规范操作 */public interface BaseAdminDao { /** * 根据用户名更新登陆时间和登录ip * @param username * @param date * @param ip */ void updateLoginTime(String username, Date date, String ip); /** * 管理员根据账号密码登录 * @param username * @param password * @return 登录的结果,true表示登录成功 */ boolean login(String username, String password);}
AdminDaoMysql实现类:
package com.czy.dao.imp;import com.czy.dao.BaseAdminDao;import com.czy.util.DruidUtil;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;import java.util.Date;public class AdminDaoMysql implements BaseAdminDao { private static final String SQL_UPDATE_LOGIN_TIME = "UPDATE EADMIN SET LOGINTIME=?,LOGINIP=? WHERE USERNAME=?"; private static final String SQL_LOGIN = "SELECT ID FROM EADMIN WHERE USERNAME=? AND PASSWORD=?"; /** * 根据用户名更新登陆时间和登录ip * * @param username * @param date * @param ip */ @Override public void updateLoginTime(String username, Date date, String ip) { //1. 获取连接 Connection conn = DruidUtil.getConnection(); PreparedStatement state = null; //2. 预编译SQL语句 try { state = conn.prepareStatement(SQL_UPDATE_LOGIN_TIME); //3. 填充参数 //这里的date是util包中的类,作为参数传递方便处理,传入时需要转换为sql包中的Date类。 state.setDate(1, new java.sql.Date(date.getTime())); state.setString(2, ip); state.setString(3, username); //4. 执行 state.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } finally { //5. 释放资源 DruidUtil.close(conn, state, null);// 这里不需要返回结果,所以传入null } } /** * 判断登录是否成功 * * @param username * @param password * @return */ @Override public boolean login(String username, String password) { //1. 获取连接 Connection conn = DruidUtil.getConnection(); PreparedStatement state = null; ResultSet rs = null; //2. 预编译SQL语句 try { state = conn.prepareStatement(SQL_LOGIN); //3. 填充参数 // 这里的date是util包中的类,作为参数传递方便处理。传入时需要转换为sql包中的Date类 state.setString(1, username); state.setString(2, password); //4. 执行并获取结果 rs = state.executeQuery(); //5. 根据查询结果返回(如果没有异常,到下面这行,next能出来就true,不能就false) return rs.next(); } catch (SQLException e) { e.printStackTrace(); } finally { //6. 释放资源 DruidUtil.close(conn, state, rs);// 这里不需要返回结果,所以传入null } //如果代码出现异常最后就到这一行 return false; }}
接下来在controller(还没写)和Dao中编写一个Service,避免controller层和dao层紧耦合。(这两层比较重要,若发生改动对程序影响较大)
编写AdminService:
package com.czy.dao;import java.util.Date;public interface BaseAdminDao { /** * 根据用户名更新登陆时间和登录ip * @param username * @param date * @param ip */ void updateLoginTime(String username, Date date, String ip); /** * 管理员根据账号密码登录 * @param username * @param password * @return 登录的结果,true表示登录成功 */ boolean login(String username, String password);}
第四节管理员登录前后端交互
修改login.html,位于web/admin下。将登录按钮的类型改为button(原先是submit),这样可以通过ajax在前端拿到登录时的用户名和密码引入jQuery和layer(注意引入的顺序)编写脚本,取得用户名和密码
<!DOCTYPE html><html lang="zh-CN"><head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <link rel="stylesheet" href="assets/css/layui.css"> <link rel="stylesheet" href="assets/css/login.css"> <link rel="icon" href="/favicon.ico"> <title>快递e栈管理后台</title></head><body class="login-wrap"> <div class="login-container"> <h3>快递e栈后台管理</h3> <form class="login-form" action="index.html"> <div class="input-group"> <input type="text" id="username" class="input-field"> <label for="username" class="input-label"> <span class="label-title">用户名</span> </label> </div> <div class="input-group"> <input type="password" id="password" class="input-field"> <label for="password" class="input-label"> <span class="label-title">密码</span> </label> </div> <button type="button" class="login-button">登录<i class="ai ai-enter"></i></button> </form> </div></body><script src="assets/layui.js"></script><script src="js/index.js" data-main="login"></script><script src="js/login.js" data-main="login"></script><script src="../layer/layer.js"></script><script src="../qrcode/jquery2.1.4.js"></script><script> $(function () { $(".login-button").click(function () { var username = $("#username").val(); var password = $("#password").val(); //1. 先试用layer,弹出load(提示加载中) var windowID = layer.load(); //2. ajax与服务器交互(密码的提交建议用post) // 成功时进入function函数 // post里面前台输入这个地址login.do,我们就去后台找注解了这个地址的方法 $.post("login.do",{username:username,password:password},function (data) { //3. 关闭load窗口 layer.close(windowID); //4. 将服务器回复的结果进行显示 layer.msg(data.result); if (data.status == 0){ //跳转到主页 location.assign("index.html"); } },"JSON"); }) })</script></html>
编写controller包,先写AdminController :
package com.czy.controller;import com.czy.bean.Message;import com.czy.mvc.ResponseBody;import com.czy.service.AdminService;import com.czy.util.JSONUtil;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.util.Date;public class AdminController { @ResponseBody("/admin/login.do") public String login(HttpServletRequest req, HttpServletResponse resp) { // 1, 接收参数 String username = req.getParameter("username"); String password = req.getParameter("password"); // 2, 调用service传递参数,并获取结果 boolean result = AdminService.login(username, password); // 3, 根据结果准备不同的返回数据 Message msg = null; if (result) { msg = new Message(0, "登录成功"); //登录时间 和 ip 的更新 Date date = new Date(); String ip = req.getRemoteAddr();//如果用本机进行登录,会获取到一堆的0 AdminService.updateLoginTimeAndIP(username, date, ip); req.getSession().setAttribute("adminUsername", "username"); } else { msg = new Message(-1, "登录失败"); } // 4, 将数据转换为JSON String json = JSONUtil.toJSON(msg); // 5, 将JSON返回给ajax return json; }}
处理字符乱码问题,在src中创建filter目录,编写CharsetFilter。
package com.xxy.filter; import javax.servlet.*;import javax.servlet.annotation.WebFilter;import java.io.IOException; @WebFilter("*.do")public class CharsetFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { servletRequest.setCharacterEncoding("utf-8"); servletResponse.setContentType("text/json;charset=utf-8"); servletResponse.setCharacterEncoding("utf-8"); filterChain.doFilter(servletRequest, servletResponse); } @Override public void destroy() { }}
第五节流程与快递表格创建
快递管理编写流程
1. 快递的列表展示
-
分页查询的列表
2.新增快递
-
用户输入内容,后台接收参数,向数据库存储
3.删除快递
-
用户输入快递单号查询到快递信息
-
浏览快递信息的最后,可以点击删除按钮 ,删除快递
4.修改快递
-
用户输入快递单号查询到快递信息
-
浏览(可修改)快递信息的最后,可以点击确认按钮 ,确认修改快递
具体流程
创建数据库表格(Express)—> 编写DAO—> 编写Service—> 编写Controller—> 前后端的交互
前端发起ajax→DispatcherServlet→Controller→Service→DAO→数据库
创建数据库的sql代码:
CREATE TABLE express( id INT PRIMARY KEY auto_increment, number VARCHAR(64) UNIQUE, username VARCHAR(32), userphone VARCHAR(32), company VARCHAR(32), code VARCHAR(32) UNIQUE, intime TIMESTAMP, outtime TIMESTAMP, status int, sysPhone VARCHAR(32));
生成表格对应的实体类-快递express:
package com.czy.bean;
import java.sql.Timestamp;
import java.util.Objects;
public class Express {
private int id;
private String number;
private String username;
private String userphone;
private String company;
private String code;
private Timestamp intime;
private Timestamp outtime;
private int status;
private String sysPhone;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Express express = (Express) o;
return id == express.id &&
status == express.status &&
Objects.equals(number, express.number) &&
Objects.equals(username, express.username) &&
Objects.equals(userphone, express.userphone) &&
Objects.equals(company, express.company) &&
Objects.equals(code, express.code) &&
Objects.equals(intime, express.intime) &&
Objects.equals(outtime, express.outtime) &&
Objects.equals(sysPhone, express.sysPhone);
}
@Override
public int hashCode() {
return Objects.hash(id, number, username, userphone, company, code, intime, outtime, status, sysPhone);
}
@Override
public String toString() {
return "Express{" +
"id=" + id +
", number='" + number + '\'' +
", username='" + username + '\'' +
", userphone='" + userphone + '\'' +
", company='" + company + '\'' +
", code='" + code + '\'' +
", intime=" + intime +
", outtime=" + outtime +
", status=" + status +
", sysPhone='" + sysPhone + '\'' +
'}';
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getUserPhone() {
return userphone;
}
public void setuserphone(String userphone) {
this.userphone = userphone;
}
public String getCompany() {
return company;
}
public void setCompany(String company) {
this.company = company;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public Timestamp getIntime() {
return intime;
}
public void setIntime(Timestamp intime) {
this.intime = intime;
}
public Timestamp getOuttime() {
return outtime;
}
public void setOuttime(Timestamp outtime) {
this.outtime = outtime;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public String getSysPhone() {
return sysPhone;
}
public void setSysPhone(String sysPhone) {
this.sysPhone = sysPhone;
}
public Express(String number, String username, String userphone, String company, String sysPhone) {
this.number = number;
this.username = username;
this.userphone = userphone;
this.company = company;
this.sysPhone = sysPhone;
}
//由用户自己输入的参数形成的构造方法
public Express(String number, String username, String userphone, String company, String sysPhone,String code) {
this.number = number;
this.username = username;
this.userphone = userphone;
this.company = company;
this.sysPhone = sysPhone;
this.code = code;
}
public Express() {
}
public Express(int id, String number, String username, String userphone, String company, String code, Timestamp intime, Timestamp outtime, int status, String sysPhone) {
this.id = id;
this.number = number;
this.username = username;
this.userphone = userphone;
this.company = company;
this.code = code;
this.intime = intime;
this.outtime = outtime;
this.status = status;
this.sysPhone = sysPhone;
}
}
第六节快递的dao编写
api设计省略,快递的dao接口编写如下:
package com.czy.dao;import com.czy.bean.Express;import com.czy.exception.DuplicateCodeException;import java.util.List;import java.util.Map;public interface BaseExpressDao { /** * 用于查询数据库中的全部快递(总数+新增),待取件快递(总数+新增) * @return [{size:总数,day:新增},{size:总数,day:新增}] * */ List<Map<String, Integer>> console(); /** * 用于查询所有快递 * @param limit 是否分页的标记,true表示分页。false表示查询所有快递 * @param offset SQL语句的起始索引 * @param pageNumber 页查询的数量 * @return 快递的集合 */ List<Express> findAll(boolean limit, int offset, int pageNumber); /** * 根据快递单号,查询快递信息 * @param number 单号 * @return 查询的快递信息,单号不存在时返回null */ Express findByNumber(String number); /** * 根据快递取件码,查询快递信息 * @param code 取件码 * @return 查询的快递信息,取件码不存在时返回null */ Express findByCode(String code); /** * 根据用户手机号码,查询他所有的快递信息 * @param userPhone 手机号码 * @return 查询的快递信息列表 */ List<Express> findByUserPhone(String userPhone); /** * 根据录入人手机号码,查询录入的所有记录 * @param sysPhone 手机号码 * @return 查询的快递信息列表 */ List<Express> findBySysPhone(String sysPhone); /** * 快递的录入 * @param e 要录入的快递对象 * @return 录入的结果,true表示成功,false表示失败 */ boolean insert(Express e) throws DuplicateCodeException; /** * 快递的修改 * @param id 要修改的快递id * @param newExpress 新的快递对象(number,company,username,userPhone) * @return 修改的结果,true表示成功,false表示失败 */ boolean update(int id, Express newExpress); /** * 更改快递的状态为1,表示取件完成 * @param code 要修改的快递取件码 * @return 修改的结果,true表示成功,false表示失败 */ boolean updateStatus(String code); /** * 根据id,删除单个快递信息 * @param id 要删除的快递id * @return 删除的结果,true表示成功,false表示失败 */ boolean delete(int id);}
在ExpressDao的实现类ExpressDaoMysql,先在开头编写SQL语句:
// 这里统一使用大写 如果不是大写的话后面会自动转换为大写
// 用于查询数据库中的全部快递(总数+新增),待取件快递(总数+新增)
public static final String SQL_CONSOLE = "SELECT COUNT(ID) data1_size, COUNT(TO_DAYS(INTIME)=TO_DAYS(NOW()) OR NULL) data1_day, COUNT(STATUS=0 OR NULL) data2_size, COUNT(TO_DAYS(INTIME)=TO_DAYS(NOW()) AND STATUS=0 OR NULL) data2_day FROM EXPRESS";
// 用于查询数据库中的所有快递信息
public static final String SQL_FIND_ALL = "SELECT * FROM EXPRESS";
// 用于分页查询数据库中的快递信息
public static final String SQL_FIND_LIMIT = "SELECT * FROM EXPRESS LIMIT ?,?";
// 通过取件码查询快递信息
public static final String SQL_FIND_BY_CODE = "SELECT * FROM EXPRESS WHERE CODE=?";
// 通过快递单号查询快递信息
public static final String SQL_FIND_BY_NUMBER = "SELECT * FROM EXPRESS WHERE NUMBER=?";
// 通过录入人手机号查询快递信息
public static final String SQL_FIND_BY_SYSPHONE = "SELECT * FROM EXPRESS WHERE SYSPHONE=?";
// 通过用户手机号查询用户所有快递
public static final String SQL_FIND_BY_USERPHONE = "SELECT * FROM EXPRESS WHERE USERPHONE=?";
// 录入快递
public static final String SQL_INSERT = "INSERT INTO EXPRESS (NUMBER,USERNAME,USERPHONE,COMPANY,CODE,INTIME,STATUS,SYSPHONE) VALUES(?,?,?,?,?,NOW(),0,?)";
// 快递修改
public static final String SQL_UPDATE = "UPDATE EXPRESS SET NUMBER=?,USERNAME=?,COMPANY=?,STATUS=? WHERE ID=?";
// 快递的状态码改变(取件)
public static final String SQL_UPDATE_STATUS = "UPDATE EXPRESS SET STATUS=1,OUTTIME=NOW(),CODE=NULL WHERE CODE=?";
// 快递的删除
public static final String SQL_DELETE = "DELETE FROM EXPRESS WHERE ID=?";
ExpressDao实现类编写
package com.czy.dao.imp;
import com.czy.bean.Express;
import com.czy.dao.BaseExpressDao;
import com.czy.exception.DuplicateCodeException;
import com.czy.util.DruidUtil;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ExpressDaoMysql implements BaseExpressDao {
//sql语句这里省略,详情见前面那段代码
/**
* 用于查询数据库中的全部快递(总数+新增),待取件快递(总数+新增)
*
* @return [{size:总数,day:新增},{size:总数,day:新增}]
*/
@Override
public List<Map<String, Integer>> console() {
List<Map<String, Integer>> data = new ArrayList<>();
// 1 获取数据库连接
Connection conn = DruidUtil.getConnection();
PreparedStatement state = null;// 变量向上抽取,便于释放资源
ResultSet result = null;
try {
// 2 预编译SQL语句
state = conn.prepareStatement(SQL_CONSOLE);
// 3 填充参数(可选)
// 4 执行SQL语句
result = state.executeQuery();
// 5 获得执行结果
//使用的是聚合函数,查到的是一行数据,我们先判断有没有查到数据
if (result.next()) {
int data1_size = result.getInt("data1_size");
int data1_day = result.getInt("data1_day");
int data2_size = result.getInt("data2_size");
int data2_day = result.getInt("data2_day");
Map data1 = new HashMap();
data1.put("data1_size", data1_size);
data1.put("data1_day", data1_day);
Map data2 = new HashMap();
data2.put("data2_size", data2_size);
data2.put("data2_day", data2_day);
data.add(data1);
data.add(data2);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 6 释放资源
DruidUtil.close(conn, state, result);
}
return data;
}
/**
* 用于查询所有快递
*
* @param limit 是否分页的标记,true表示分页。false表示查询所有快递
* @param offset SQL语句的起始索引
* @param pageNumber 页查询的数量
* @return 快递的集合
*/
@Override
public List<Express> findAll(boolean limit, int offset, int pageNumber) {
ArrayList<Express> data = new ArrayList<>();
//1. 获取数据库的连接
Connection conn = DruidUtil.getConnection();
PreparedStatement state = null;
ResultSet result = null;
//2. 预编译SQL语句
try {
//判断是否为分页查询,两条路径
if(limit) {
state = conn.prepareStatement(SQL_FIND_LIMIT);
//3. 填充参数(可选)
state.setInt(1,offset);
state.setInt(2,pageNumber);
} else {
state = conn.prepareStatement(SQL_FIND_ALL);
}
//4. 执行SQL语句
result = state.executeQuery();
//5. 获取执行的结果
while(result.next()) {
int id = result.getInt("id");
String number = result.getString("number");
String username = result.getString("username");
String userPhone = result.getString("userPhone");
String company = result.getString("company");
String code = result.getString("code");
Timestamp inTime = result.getTimestamp("inTime");
Timestamp outTime = result.getTimestamp("outTime");
int status = result.getInt("status");
String sysPhone = result.getString("sysPhone");
Express e = new Express(id,number,username,userPhone,company,code,inTime,outTime,status,sysPhone);
data.add(e);
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
//6. 资源的释放
DruidUtil.close(conn,state,result);
}
return data;
}
/**
* 根据快递单号,查询快递信息
*
* @param number 单号
* @return 查询的快递信息,单号不存在时返回null
*/
@Override
public Express findByNumber(String number) {
// 1 获取数据库连接
Connection conn = DruidUtil.getConnection();
PreparedStatement state = null;
ResultSet result = null;
try {
// 2 预编译SQL语句
state = conn.prepareStatement(SQL_FIND_BY_NUMBER);
// 3 填充参数(可选)
state.setString(1, number);
// 4 执行SQL语句
result = state.executeQuery();
// 5 获得执行结果
if (result.next()) {
int id = result.getInt("id");
String username = result.getString("username");
String userPhone = result.getString("userPhone");
String company = result.getString("company");
String code = result.getString("code");
Timestamp inTime = result.getTimestamp("inTime");
Timestamp outTime = result.getTimestamp("outTime");
int status = result.getInt("status");
String sysPhone = result.getString("sysPhone");
Express e = new Express(id,number,username,userPhone,company,code,inTime,outTime,status,sysPhone);
return e;
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 6 释放资源
DruidUtil.close(conn, state, result);
}
return null;
}
/**
* 根据快递取件码,查询快递信息
*
* @param code 取件码
* @return 查询的快递信息,取件码不存在时返回null
*/
@Override
public Express findByCode(String code) {
// 1 获取数据库连接
Connection conn = DruidUtil.getConnection();
PreparedStatement state = null;
ResultSet result = null;
try {
// 2 预编译SQL语句
state = conn.prepareStatement(SQL_FIND_BY_CODE);
// 3 填充参数(可选)
state.setString(1, code);
// 4 执行SQL语句
result = state.executeQuery();
// 5 获得执行结果
if (result.next()) {
int id = result.getInt("id");
String number = result.getString("number");
String username = result.getString("username");
String userPhone = result.getString("userPhone");
String company = result.getString("company");
Timestamp inTime = result.getTimestamp("inTime");
Timestamp outTime = result.getTimestamp("outTime");
int status = result.getInt("status");
String sysPhone = result.getString("sysPhone");
Express e = new Express(id,number,username,userPhone,company,code,inTime,outTime,status,sysPhone);
return e;
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 6 释放资源
DruidUtil.close(conn, state, result);
}
return null;
}
/**
* 根据用户手机号码,查询他所有的快递信息
*
* @param userPhone 手机号码
* @return 查询的快递信息列表
*/
@Override
public List<Express> findByUserPhone(String userPhone) {
ArrayList<Express> data = new ArrayList<>();
// 1 获取数据库连接
Connection conn = DruidUtil.getConnection();
PreparedStatement state = null;
ResultSet result = null;
try {
// 2 预编译SQL语句
state = conn.prepareStatement(SQL_FIND_BY_USERPHONE);
// 3 填充参数(可选)
state.setString(1, userPhone);
// 4 执行SQL语句
result = state.executeQuery();
// 5 获得执行结果
while (result.next()) {
int id = result.getInt("id");
String number = result.getString("number");
String username = result.getString("username");
String company = result.getString("company");
String code = result.getString("code");
Timestamp inTime = result.getTimestamp("inTime");
Timestamp outTime = result.getTimestamp("outTime");
int status = result.getInt("status");
String sysPhone = result.getString("sysPhone");
Express e = new Express(id,number,username,userPhone,company,code,inTime,outTime,status,sysPhone);
data.add(e);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 6 释放资源
DruidUtil.close(conn, state, result);
}
return data;
}
/**
* 根据录入人手机号码,查询录入的所有记录
*
* @param sysPhone 手机号码
* @return 查询的快递信息列表
*/
@Override
public List<Express> findBySysPhone(String sysPhone) {
ArrayList<Express> data = new ArrayList<>();
// 1 获取数据库连接
Connection conn = DruidUtil.getConnection();
PreparedStatement state = null;
ResultSet result = null;
try {
// 2 预编译SQL语句
state = conn.prepareStatement(SQL_FIND_BY_SYSPHONE);
// 3 填充参数(可选)
state.setString(1, sysPhone);
// 4 执行SQL语句
result = state.executeQuery();
// 5 获得执行结果
while (result.next()) {
int id = result.getInt("id");
String number = result.getString("number");
String username = result.getString("username");
String company = result.getString("company");
String userPhone = result.getString("userPhone");
String code = result.getString("code");
Timestamp inTime = result.getTimestamp("inTime");
Timestamp outTime = result.getTimestamp("outTime");
int status = result.getInt("status");
Express e = new Express(id,number,username,userPhone,company,code,inTime,outTime,status,sysPhone);
data.add(e);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 6 释放资源
DruidUtil.close(conn, state, result);
}
return data;
}
/**
* 快递的录入
*
* @param e 要录入的快递对象
* @return 录入的结果,true表示成功,false表示失败
*/
@Override
public boolean insert(Express e) throws DuplicateCodeException {
//1. 连接的获取
Connection conn = DruidUtil.getConnection();
//2. 预编译SQL语句
PreparedStatement state = null;
try {
state = conn.prepareStatement(SQL_INSERT);
//3. 填充参数
//可能存在的异常:单号重复录入-->措施是直接告诉录入人,这个单子已经录入过了
state.setString(1,e.getNumber());
state.setString(2,e.getUsername());
state.setString(3,e.getUserPhone());
state.setString(4,e.getCompany());
//可能存在的异常:取件码重复-->措施是找到相对应的异常类型,相应异常信息中的关键字,然后选择自定义一个异常向外抛出
state.setString(5,e.getCode());
state.setString(6,e.getSysPhone());
//4. 执行SQL语句,并获取执行结果
return state.executeUpdate()>0?true:false;
} catch (SQLException e1) {
// throwables.printStackTrace();
if (e1.getMessage().endsWith("for key 'express.code'")){
//是因为取件码重复,而出现了异常
DuplicateCodeException e2 = new DuplicateCodeException(e1.getMessage());
throw e2;
}else{
e1.printStackTrace();
}
}finally {
//5. 释放资源
DruidUtil.close(conn,state,null);
}
return false;
}
/**
* 快递的修改
*
* @param id 要修改的快递id
* @param newExpress 新的快递对象(number,company,username,userPhone)
* @return 修改的结果,true表示成功,false表示失败
*/
@Override
public boolean update(int id, Express newExpress) {
//1. 连接的获取
Connection conn = DruidUtil.getConnection();
PreparedStatement state = null;
//2. 预编译SQL语句
try {
state = conn.prepareStatement(SQL_UPDATE);
state.setString(1,newExpress.getNumber());
state.setString(2,newExpress.getUsername());
state.setString(3,newExpress.getUserPhone());
state.setString(4,newExpress.getCompany());
state.setInt(5,newExpress.getStatus());
state.setInt(6,id);
return state.executeUpdate()>0?true:false;
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
DruidUtil.close(conn,state,null);
}
return false;
}
/**
* 更改快递的状态为1,表示取件完成
*
* @param code 要修改的快递取件码
* @return 修改的结果,true表示成功,false表示失败
*/
@Override
public boolean updateStatus(String code) {
//1. 连接的获取
Connection conn = DruidUtil.getConnection();
PreparedStatement state = null;
//2. 预编译SQL语句
try {
state = conn.prepareStatement(SQL_UPDATE_STATUS);
state.setString(1, code);
return state.executeUpdate()>0?true:false;
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
DruidUtil.close(conn,state,null);
}
return false;
}
/**
* 根据id,删除单个快递信息
*
* @param id 要删除的快递id
* @return 删除的结果,true表示成功,false表示失败
*/
@Override
public boolean delete(int id) {
//1. 连接的获取
Connection conn = DruidUtil.getConnection();
PreparedStatement state = null;
//2. 预编译SQL语句
try {
state = conn.prepareStatement(SQL_DELETE);
state.setInt(1,id);
return state.executeUpdate()>0;
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
DruidUtil.close(conn,state,null);
}
return false;
}
}
第七节快递的service编写
//为避免controller层和dao层紧耦合(这两层比较重要,若发生改动对程序影响较大),在两者中间加入service层。
package com.czy.service;
import com.czy.bean.Express;
import com.czy.dao.BaseExpressDao;
import com.czy.dao.imp.ExpressDaoMysql;
import com.czy.exception.DuplicateCodeException;
import com.czy.util.RandomUtil;
import java.util.List;
import java.util.Map;
public class ExpressService {
private static BaseExpressDao dao = new ExpressDaoMysql();
/**
* 用于查询数据库中的全部快递(总数+新增),待取件快递(总数+新增)
*
* @return [{size:总数,day:新增},{size:总数,day:新增}]
*/
public static List<Map<String, Integer>> console() {
return dao.console();
}
/**
* 用于查询所有快递
*
* @param limit 是否分页的标记,true表示分页。false表示查询所有快递
* @param offset SQL语句的起始索引
* @param pageNumber 页查询的数量
* @return 快递的集合
*/
public static List<Express> findAll(boolean limit, int offset, int pageNumber) {
return dao.findAll(limit, offset, pageNumber);
}
/**
* 根据快递单号,查询快递信息
*
* @param number 单号
* @return 查询的快递信息,单号不存在时返回null
*/
public static Express findByNumber(String number) {
return dao.findByNumber(number);
}
/**
* 根据快递取件码,查询快递信息
*
* @param code 取件码
* @return 查询的快递信息,取件码不存在时返回null
*/
public static Express findByCode(String code) {
return dao.findByCode(code);
}
/**
* 根据用户手机号码,查询他所有的快递信息
*
* @param userphone 手机号码
* @return 查询的快递信息列表
*/
public static List<Express> findByuserphone(String userphone) {
return dao.findByUserPhone((userphone));
}
/**
* 根据录入人手机号码,查询录入的所有记录
*
* @param sysPhone 手机号码
* @return 查询的快递信息列表
*/
public static List<Express> findBySysPhone(String sysPhone) {
return dao.findBySysPhone(sysPhone);
}
/**
* 快递的录入
*
* @param e 要录入的快递对象
* @return 录入的结果,true表示成功,false表示失败
*/
public static boolean insert(Express e) {
e.setCode(RandomUtil.getCode()+"");
try {
return dao.insert(e);
} catch (DuplicateCodeException e1) {
// 捕获到取件码重复后 递归调用插入
return insert(e);
}
}
/**
* 快递的修改
*
* @param id 要修改的快递id
* @param newExpress 新的快递对象(number,company,username,userphone)
* @return 修改的结果,true表示成功,false表示失败
*/
//这段的逻辑比较乱,不深究了
public static boolean update(int id, Express newExpress) {
// 个人理解:当有修改收件人号码的需求时,由于涉及到重发短信的业务,需要重新执行插入(插入时,会向用户发送短信)
if (newExpress.getUserPhone() != null) {
dao.delete(id);
return insert(newExpress);
} else {
// 这里的逻辑感觉不是很清晰,注释上修改的是userphone,后面SQL语句中却替换为status,后面有问题再来修改吧
boolean update = dao.update(id, newExpress);
Express e = dao.findByNumber(newExpress.getNumber());
if (newExpress.getStatus() == 1) {
updateStatus(e.getCode());
}
return update;
}
}
/**
* 更改快递的状态为1,表示取件完成
*
* @param code 要修改的快递取件码
* @return 修改的结果,true表示成功,false表示失败
*/
public static boolean updateStatus(String code) {
return dao.updateStatus(code);
}
/**
* 根据id,删除单个快递信息
*
* @param id 要删除的快递id
* @return 删除的结果,true表示成功,false表示失败
*/
public static boolean delete(int id) {
return dao.delete(id);
}
}
第八节控制台部分
标准流程ajax→DispatcherServlet→Controller→Service→DAO→数据库
-
1.前端发起ajax
$("按钮选择器").click(function(){ //1. 先使用layer,弹出load(提示加载中...) var windowId = layer.load(); //2. ajax与服务器交互 $.post("服务器地址",参数JSON,function(data){ //3. 关闭load窗口 layer.close(windowId); //4. 将服务器回复的结果进行显示 layer.msg(data.result); },"JSON"); });
-
2.编写Controller,用于处理ajax的请求
- 在Controller中调用service处理
- 处理完毕, 根据service返回的结果,给ajax返回
找到console.html页面(内嵌在index.html中),将一些静态数据修改为-,并添加id标识
在该html最后部分添加js脚本:
<script>
$(function () {
$.post("/express/console.do", null, function (data) {
if (data.status == 0) {
// [{size:总数,day:新增},{size:总数,day:新增}]
$("#data1_size").html(data.data[0].data1_size);
$("#data1_day").html(data.data[0].data1_day);
$("#data2_size").html(data.data[1].data2_size);
$("#data2_day").html(data.data[1].data2_day);
}
}, "JSON");
});
</script>
第九节分页列表、快递录入、删除和修改
1.分页列表
在bean文件夹下新建一个ResultData实体类,用来存放分页的数据。
package com.czy.bean;
import java.util.ArrayList;
import java.util.List;
public class ResultData<T> {
// 每次查询的数据集合
private List<T> rows = new ArrayList<>();
// 总数量
private int total;
public List<T> getRows() {
return rows;
}
public void setRows(List<T> rows) {
this.rows = rows;
}
public int getTotal() {
return total;
}
public void setTotal(int total) {
this.total = total;
}
}
新建BootstrapTableExpress实体类, 在获取到express列表后,将其逐个转换为BootstrapTableExpress对象。
package com.czy.bean;
public class BootstrapTableExpress {
private int id;
private String number;
private String username;
private String userphone;
private String company;
private String code;
private String intime;
private String outtime;
private String status;
private String sysPhone;
public BootstrapTableExpress() {
}
public BootstrapTableExpress(int id, String number, String username, String userphone, String company, String code, String intime, String outtime, String status, String sysPhone) {
this.id = id;
this.number = number;
this.username = username;
this.userphone = userphone;
this.company = company;
this.code = code;
this.intime = intime;
this.outtime = outtime;
this.status = status;
this.sysPhone = sysPhone;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getUserphone() {
return userphone;
}
public void setUserphone(String userphone) {
this.userphone = userphone;
}
public String getCompany() {
return company;
}
public void setCompany(String company) {
this.company = company;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getIntime() {
return intime;
}
public void setIntime(String intime) {
this.intime = intime;
}
public String getOuttime() {
return outtime;
}
public void setOuttime(String outtime) {
this.outtime = outtime;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getSysPhone() {
return sysPhone;
}
public void setSysPhone(String sysPhone) {
this.sysPhone = sysPhone;
}
}
在ExpressController中编写处理列表请求的方法
@ResponseBody("/express/list.do")
public String list(HttpServletRequest req, HttpServletResponse resp) {
// 1,获取查询数据的起始索引值
int offset = Integer.parseInt(req.getParameter("offset"));
// 2,获取当前页要查询的数据量
int pageNumber = Integer.parseInt(req.getParameter("pageNumber"));
// 3,进行查询
List<Express> list = ExpressService.findAll(true, offset, pageNumber);
List<BootstrapTableExpress> list2 = new ArrayList<>();
for (Express e : list) {
String inTime = DateFormatUtil.format(e.getIntime());
String outTime = e.getOuttime() == null ? "未出库" : DateFormatUtil.format(e.getOuttime());
String status = e.getStatus() == 0 ? "待取件" : "已取件";
String code = e.getCode() == null ? "已取件" : e.getCode();
list2.add(new BootstrapTableExpress(e.getId(), e.getNumber(), e.getUsername(), e.getUserPhone(), e.getCompany(), code, inTime, outTime, status, e.getSysPhone()));
}
List<Map<String,Integer>> console = ExpressService.console();
Integer total = console.get(0).get("data1_size");
// 4,将集合封装为bootstrap-table识别的格式(用于前后端分离)
ResultData<BootstrapTableExpress> data = new ResultData<>();
data.setRows(list2);
data.setTotal(total);
String json = JSONUtil.toJSON(data);
return json;
}
修改list.html内容
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<link rel="stylesheet" type="text/css" href="../../css/reset.css"/>
<link rel="stylesheet" type="text/css" href="../../css/list.css"/>
<!--从cdn中引入资源,适用于有网络的条件,也可以提前下载,自行引入-->
<link href="https://cdn.bootcdn.net/ajax/libs/bootstrap-table/1.17.1/bootstrap-table.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/bootstrap-table/1.17.1/bootstrap-table.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/bootstrap-table/1.17.1/locale/bootstrap-table-zh-CN.min.js"></script>
</head>
<body>
<table id="express_list"></table>
<script>
$(function () {
$("#express_list").bootstrapTable({
url:"/express/list.do", // 数据地址
striped:true, // 是否显示行的间隔
pageNumber:1, // 初始化加载第几页
pagination:true, // 是否分页
sidePagination:'server', // 分页方式,分为服务器分页和前端分页
pageSize:5,
pageList:[5,10,20],
showRefresh:true, // 是否提供刷新功能
queryParams:function (params) {
var temp = {
offset:params.offset,
pageNumber:params.limit
};
return temp;
},
columns:[
{
title:"编号",
field:"id",
sortable:false // 选择前端分页才能使用排序功能
},
{
title:"单号",
field:"number",
sortable:false
},
{
title:"姓名",
field:"username",
sortable:false
},
{
title:"电话",
field:"userphone", // 注意与自己定义的字段名是否一致(大小写)
sortable:false
},
{
title:"快递公司",
field:"company",
sortable:false
},
{
title:"取件码",
field:"code",
sortable:false
},
{
title:"入库时间",
field:"intime",
sortable:false
},
{
title:"出库时间",
field:"outtime",
sortable:false
},
{
title:"状态",
field:"status",
sortable:false
},
{
title:"录入人电话",
field:"sysPhone",
sortable:false
}
]
});
});
</script>
</body>
</html>
2.快递录入
修改add.html:为表单添加id;引入jQuery、layer;通过js为表单添加提交方法,获取页面提供的参数、打包参数向后台传递、接收后台返回的数据。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="stylesheet" href="../../assets/css/layui.css">
<link rel="stylesheet" href="../../assets/css/view.css"/>
<title></title>
</head>
<body class="layui-view-body">
<div class="layui-content">
<div class="layui-row">
<div class="layui-card">
<div class="layui-card-header">录入快件</div>
<form id="form" class="layui-form layui-card-body" >
<div class="layui-form-item">
<label class="layui-form-label">单号</label>
<div class="layui-input-block">
<input type="text" name="code" required lay-verify="required" placeholder="请输入快递单号" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">快递公司</label>
<div class="layui-input-block">
<select name="company" lay-verify="" lay-search>
<option >安能快递</option><option >TNT</option><option >UPS</option><option >智利邮政</option><option >中速快递</option>
</select>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">收件人姓名</label>
<div class="layui-input-block">
<input type="text" name="username" required lay-verify="required" placeholder="请输入收货人姓名" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">手机号码</label>
<div class="layui-input-block">
<input type="text" name="phonenumber" required lay-verify="required" placeholder="请输入手机号码" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn layui-btn-blue" lay-submit lay-filter="formDemo">立即提交</button>
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
</div>
</div>
</form>
</div>
</div>
</div>
<script src="../../assets/layui.all.js"></script>
<script src="/qrcode/jquery2.1.4.js"></script>
<script src="/layer/layer.js"></script>
<script>
var form = layui.form
,layer = layui.layer;
</script>
<script>
$(function () {
$("#form").submit(function () {
var windowId = layer.load();
var number = $("input:eq(0)").val();
var company = $("input:eq(1)").val();
var username = $("input:eq(2)").val();
var userphone = $("input:eq(3)").val();
$.post("/express/insert.do", {
number:number,
company:company,
username:username,
userphone:userphone
}, function (data) {
layer.close(windowId);
layer.msg(data.result);
if (data.status == 0) {
// 恢复默认值
$("input").val("");
$("input:eq(1)").val("顺丰速运");
}
});
return false;// 不跳转页面
});
});
</script>
</body>
</html>
3.修改和删除
引入jQuery、layer;为指定的html组件添加id、value等属性;编写脚本,提供快件查询、快件修改/删除功能;(编写过程中需要完善后台的地方,再转回去完善。里面的逻辑也有点复杂,已经尽量做好备注)
update.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<link rel="stylesheet" type="text/css" href="../../css/reset.css"/>
<link rel="stylesheet" type="text/css" href="../../css/add.css"/>
</head>
<body>
<div id="app">
<div class="header">
<span>修改快递信息</span>
</div>
<div class="content">
<table>
<tr>
<td class="text-right">运单号</td><td class="content_right"><input class="input inline-input" id="number" placeholder="请输入运单号码"> <span class="btn btn-info" id="find">立即查找</span></td>
</tr>
</table>
</div>
<div class="header">
<span>查找信息如下</span>
</div>
<div class="content">
<table>
<tr>
<td class="text-right">快递单号</td><td class="content_right"><input class="input" placeholder="请输入姓名"></td>
</tr>
<tr>
<td class="text-right">快递公司</td><td class="content_right"><input class="input" placeholder="请输入姓名"></td>
</tr>
<tr>
<td class="text-right">收货人姓名</td><td class="content_right"><input class="input" placeholder="请输入姓名"></td>
</tr>
<tr>
<td class="text-right">手机号码</td><td class="content_right"><input class="input" placeholder="请输入姓名"></td>
</tr>
<tr>
<td class="text-right">快递状态</td><td class="content_right"><input type="radio" name="status" class="status" value="1">已签收 <input name="status" type="radio" class="status" value="0">未签收</td>
</tr>
<tr>
<td></td><td class="content_right"><span class="btn btn-info" id="update">立即修改</span> <span class="btn">重置</span> </td>
</tr>
</table>
</div>
</div>
</body>
<script src="/qrcode/jquery2.1.4.js"></script>
<script src="/layer/layer.js"></script>
<script>
$(".header:eq(1), .content:eq(1)").hide();
var expressId = null;// 后面可能修改单号,但是id不会该,所以在这里进行标记
var g_userPhone = null;
$(function () {
$("#find").click(function () {
var windowId = layer.load();
var number = $("#number").val();
$.getJSON("/express/find.do", {number:number}, function (data) {
layer.close(windowId);
layer.msg(data.result);
if (data.status == 0) {
$(".header:eq(1), .content:eq(1)").fadeIn(1000);
expressId = data.data.id;
g_userPhone = data.data.userphone;
$("input:eq(1)").val(data.data.number);
$("input:eq(2)").val(data.data.company);
$("input:eq(3)").val(data.data.username);
$("input:eq(4)").val(data.data.userphone);
if (data.data.status == 0) {
// 未签收
$(".status:eq(1)").prop("checked", true);
} else {
// 已签收
$(".status:eq(0)").prop("checked", true);
}
}
})
});
});
$("#update").click(function () {
var windowId = layer.load();
var number = $("input:eq(1)").val();
var company = $("input:eq(2)").val();
var username = $("input:eq(3)").val();
var userphone = $("input:eq(4)").val();
var status = $(".status:checked").val();
// 后面检测修改时,判断userPhone是否为空来决定是否重发短信,但是查到手机号后就不可能为空
// 所以这里判断修改时手机号与查找时手机号不同时,表示手机号修改,将该字段赋值
var express = {
id:expressId,
number:number,
company:company,
username:username,
status:status
}
if (userphone != g_userPhone) {
express.userphone = userphone;
}
$.getJSON("/express/update.do", express, function (data) {
layer.close(windowId);
layer.msg(data.result);
if (data.status == 0) {//修改成功
$(".header:eq(1), .content:eq(1)").hide();
}
})
});
</script>
</html>
delete.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<link rel="stylesheet" type="text/css" href="../../css/reset.css"/>
<link rel="stylesheet" type="text/css" href="../../css/add.css"/>
</head>
<body>
<div id="app">
<div class="header">
<span>删除快递信息</span>
</div>
<div class="content">
<table>
<tr>
<td class="text-right">运单号</td><td class="content_right"><input class="input inline-input" id="number" placeholder="请输入运单号码"> <span id="find" class="btn btn-info">立即查找</span></td>
</tr>
</table>
</div>
<div class="header">
<span>查找信息如下</span>
</div>
<div class="content">
<table>
<tr>
<td class="text-right">快递单号</td><td class="content_right"><input class="input" placeholder="请输入姓名"></td>
</tr>
<tr>
<td class="text-right">快递公司</td><td class="content_right"><input class="input" placeholder="请输入姓名"></td>
</tr>
<tr>
<td class="text-right">收货人姓名</td><td class="content_right"><input class="input" placeholder="请输入姓名"></td>
</tr>
<tr>
<td class="text-right">手机号码</td><td class="content_right"><input class="input" placeholder="请输入姓名"></td>
</tr>
<tr>
<td class="text-right">快递状态</td><td id="status" class="content_right"><input type="radio" name="status">已签收 <input name="status" type="radio">未签收</td>
</tr>
<tr>
<td></td><td class="content_right"><span class="btn btn-info" id="delete">立即删除</span> </td>
</tr>
</table>
</div>
</div>
</body>
<script src="/qrcode/jquery2.1.4.js"></script>
<script src="/layer/layer.js"></script>
<script>
$(".header:eq(1), .content:eq(1)").hide();
var expressId = null;
$(function () {
$("#find").click(function () {
var windowId = layer.load();
var number = $("#number").val();
$.getJSON("/express/find.do", {number:number}, function (data) {
layer.close(windowId);
layer.msg(data.result);
if (data.status == 0) {
$(".header:eq(1), .content:eq(1)").fadeIn(1000);
expressId = data.data.id;
$("input:eq(1)").val(data.data.number);
$("input:eq(2)").val(data.data.company);
$("input:eq(3)").val(data.data.username);
$("input:eq(4)").val(data.data.userphone);
//让输入框在查询成功后,不能修改
$("input:eq(1),input:eq(2),input:eq(3),input:eq(4)").prop("disabled", true);
if (data.data.status == 0) {
// 未签收
$("#status").html("未签收");
} else {
// 已签收
$("#status").html("已签收");
}
}
})
});
});
$("#delete").click(function(){
var windowId = layer.load();
$.getJSON("/express/delete.do",{id:expressId},function(data){
layer.close(windowId);
layer.msg(data.result);
if(data.status == 0){
$(".header:eq(1),.content:eq(1)").hide();
}
});
});
</script>
</html>
新增三个处理请求的方法
@ResponseBody("/express/find.do")
public String find(HttpServletRequest req, HttpServletResponse resp) {
String number = req.getParameter("number");
Express e = ExpressService.findByNumber(number);
Message msg = new Message();
if (e == null) {
msg.setStatus(-1);
msg.setResult("单号不存在");
} else {
msg.setStatus(0);
msg.setResult("查找成功");
msg.setData(e);
}
String json = JSONUtil.toJSON(msg);
return json;
}
@ResponseBody("/express/update.do")
public String update(HttpServletRequest req, HttpServletResponse resp) {
int id = Integer.parseInt(req.getParameter("id"));
String number = req.getParameter("number");
String company = req.getParameter("company");
String username = req.getParameter("username");
String userphone = req.getParameter("userphone");
int status = Integer.parseInt(req.getParameter("status"));
Express e = new Express();
e.setNumber(number);
e.setCompany(company);
e.setUsername(username);
e.setUserphone(userphone);
e.setStatus(status);
boolean update = ExpressService.update(id, e);
Message msg = new Message();
if (update) {
msg.setStatus(0);
msg.setResult("修改成功");
} else {
msg.setStatus(-1);
msg.setResult("修改失败");
}
String json = JSONUtil.toJSON(msg);
return json;
}
@ResponseBody("/express/delete.do")
public String delete(HttpServletRequest request,HttpServletResponse response){
int id = Integer.parseInt(request.getParameter("id"));
boolean flag = ExpressService.delete(id);
Message msg = new Message();
if(flag){
msg.setStatus(0);
msg.setResult("删除成功");
}else{
msg.setStatus(-1);
msg.setResult("删除失败");
}
String json = JSONUtil.toJSON(msg);
return json;
}