练手的一个小项目,能做 快递、快递员、用户、管理员 的管理(增删改查),支持多用户访问。
技术选型
前后台:
文件结构
这里我还没有部署到服务器,以后会部署到服务器,直接访问服务器即可体验到。
环境:jdk1.8+、mysql 5.7、maven 3.0+、Internet、浏览器。
项目地址:https://gitee.com/pdh_gitee/express-e.git
把此项目git clone到本地后,打开项目。
之后运行sql脚本,即sql包下的express_manager.sql
文件。做出必要的数据库连接配置,修改druid.properties
文件里面的username和password
username=root
password=
修改结束以后,启动项目。
localhost:8080
(验证码登陆,验证码会自动传送到输入框)localhost:8080/admin
(账号admin,密码123)此项目未使用任何现在留下的框架,而是自己手撸了一套以MVC架构为基础的自定义框架(参考SpringMVC,当然有很多不足),把 Controller类里的所有请求和请求对应的方法全部加载到一个map容器当中统一进行管理,新编写的Controller类只需要写入配置文件application.properties
中即可实现容器加载和管理。
容器初始化加载所有的方法之后,每次接收到用户发起的请求,直接访问map容器,获取到对应的封装对象,执行相应的方法,返回结果。
这种MVC架构模式使得项目具有 低耦合、易管理、易扩展、层次清晰 等优点,我们的项目代码在搭建完MVC加架构,编写起来就变得非常的简单。
这里针对这一核心技术点就不在多说,精华干货都在这里面:MVC架构搭建详解点击我查看。
前端使用html页面,后端传递给前端的数据全部是的以JSON数据格式的,所以,就必须有一个工具类 JSONUtil 把后端待传输的数据全部转换为JSON的格式,这可以借助Gson工具类,具体代码实现如下:
public class JSONUtil {
// 需要引入Gson依赖 项目中都已经实现
private static Gson gson = new Gson依赖();
public static String toJSON(Object obj){
return gson.toJson(obj);
}
}
如BooStrapTableExpress、BooStrapTableUser等。
封装数据查询到的数据,比如时间格式,从数据库查询到的时间是UTC格式,不符合国人的阅读习惯。状态码是数字,而展示到前端页面需要以文字的方式进行展示。
所有的数据全部封装为String和int格式,都符合国人的阅读习惯。这就要在合适的地方使用实体类转换为BooStrapTableXxx类,转换的结果得符合我们的预期。
后端与数据库交互后得到的数据结果集统一使用Message封装,这样在解析数据的时候,我们拥有统一的数据格式,方便很多。
Message
这里的状态码尚且这样处理,之后学习到更前言的技术,会有更好的解决方案。
public class Message {
//数据:{status:0,result:"",data{}}
//状态码 0表示成功 -1表示未知异常 -2表示快递code重复 -3表示快递单号重复 -4表示电话重复
private int status;
//消息内容
private String result;
//消息的数据 数据可能是任何类型的
private Object data;
// get,set,构造等方法
}
以为删除数据为例数据封装示例
后端的Service层的所有方法放回值类型都设置为了Message
public static Message delete(int id){
Message msg =null;
try {
boolean flag = dao.delete(id);
if(flag){
msg = new Message(0,"删除成功");
} else {
msg = new Message(-1,"删除失败");
}
} catch (UnHandleException e) {
msg = new Message(-1,"删除失败,未知异常");
}
return msg;
}
前端采用基于bootstrap
的轻量级表格插件bootstrap-table
后端接收前端传递的分页数据进行查询实现分页。
当然bootstrap插件的引入是必不可少的
<table id="user_list"></table>
$("#user_list").bootstrapTable({
url:"/user/list.do", //请求地址
striped:true, //是否显示行的间隔
pageNumber:1, //初始化加载第几页
pagination:true, //是否分页
sidePagination:'server',//选择服务器分页
pageSize:5, //每一页显示数据
pageList:[5,10,20], //可选的单页显示数
showRefresh:true, //是否显示刷新按钮
queryParams:function(params){
//通过params传递参数
var obj = {
paging:this.pagination,
offset:params.offset,
limit:params.limit
};
return obj;
},
columns:[
{
title:"编号",
field:"id",
sortable:false
}, {
title:"昵称",
field:"username",
sortable:false
},
... ...
]
});
借助工具类ResultData,前端的bootstrap-table插件能直接识别JSON格式的ResultData类型的数据,直接显示出total和rows数据。
public class ResultData<T> {
/**
* 每次查询的数据集合
*/
private List<T> rows = new ArrayList<>();
/**
* 查询到的总数量
*/
private int total;
// get,set,构造等方法需创建
}
ResultData数据也是封装到Message里面由service返回给controller层的。
一下是后端与前端的交互的代码示例:
@ResponseBody("/user/list.do")
public String list(HttpServletRequest req, HttpServletResponse resp){
//0.获取是否分页标志
boolean paging = Boolean.parseBoolean(req.getParameter("paging"));
//1.获取查询数据的起始索引值(偏移量)
int offset = Integer.parseInt(req.getParameter("offset"));
//2.获取当前页要查询的数量,注意不是当前页码 计算机不需要页码
int limit = Integer.parseInt(req.getParameter("limit"));
//3.查询数据
Message msg = UserService.findAll(paging,offset,limit);
String json = JSONUtil.toJSON(msg.getData());
return json;
}
借助Google的二维码生成插件、汉化的脚本,使用一些简单的代码,生成二维码。点击我下载js插件资源文件
步骤:
1. 引入Jquery.js文件
2. 引入jquery.qrcode.js文件
3. 引入支持中文的编码js文件 (utf.js)
4. 在网页中编写一个div 用于显示二维码
5. 准备二维码的规格对象(JSON)
var config = {
width:数字,//值是number类型, 表示的单位是px 必须传递
height:数字,//值是number类型, 表示的单位是px 必须传递
text:"内容",//text就表示二维码中存储的数据 必须传递
correctLevel:数字,//取值为0|1|2|3 表示二维码的纠错级别0:L/1:M/2:Q/3:H,默认0 可选参数
background:"#rrggbb",//默认白色, 表示二维码的后景颜色 可选参数
foreground:"#rrggbb",//默认黑色, 表示二维码的前景颜色 可选参数
render:"绘制模式"//取值:table/canvas , 默认table 可选参数
};
6. 通过选择器, 查找到上述的div ,得到Jquery对象, 通过jquery对象的qrcode函数生成二维码
$("#div1").qrcode(config);*
示例:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>二维码生成title>
<script src="js/jquery2.1.4.js">script>
<script src="js/jquery.qrcode.js">script>
<script src="js/utf.js">script>
head>
<body>
<div id="qrcode">div>
<script>
var config = {
width:200,//不能写成px
height:200,
text:"君不见黄河之水天上来"
};
$("#qrcode").qrcode(config);
script>
body>
html>
针对于异常而言,能给出一个统一处理是非常有意义的。比如在插入快递的时候,快递单号已经存在,就不能再次插入此单号,此时就会报出一个 单号重复异常 ,那么当我们捕捉到的时候,就抛出一个我们实现的异常类,这样就可以做出对于的操作(比如提示用户检测单号重新输入)。
异常类需要继承Exception类,以DuplicateNumberException为例:
public class DuplicateNumberException extends Exception{
public DuplicateNumberException() {
}
public DuplicateNumberException(String message) {
super(message);
}
}
使用示例:
dao层
try {
// 访问数据库
} catch (SQLException e) {
//对异常进行处理
if(e.getMessage().endsWith("for key 'uk_number'")) {
DuplicateNumberExceptionyi numberException = new DuplicateNumberException(e.getMessage());
// 抛出DuplicateNumberException异常
throw numberException;
}else {
throw Exception;
}
}
service层
Message msg = null;
try {
// 调用dao
} catch (Exception e) {
if (e instanceof DuplicateNumberException) {
msg = new Message(-3, "快递单号重复,请检查单号");
//因为是快递单号重复,需要提示用户再次输入正确的快递单号
} else {
// Other exception
}
}
当账号没有登陆的时候,不允许访问服务器的其他模块(除登陆模块外),跳转到登陆页面等待登陆。
实现思路:
在session里面存储标记需要在controller层实现,因为controller层能很好的获取到session对象,以AdminController的login()方法为例:
public class AdminController {
@ResponseBody("/admin/login.do")
public String login(HttpServletRequest request, HttpServletResponse response) {
//1.接参数
String adminName = request.getParameter("admin_name");
String password = request.getParameter("password");
//2.调用service传参数 并获取结果
Message msg = AdminService.login(adminName, password);
String ip = request.getRemoteAddr();//ip等于请求对象地址
//过滤器实现未登录不允许访问某些页面,还可在插入快递时获取到此adminPhone作为插入人手机号
if(msg != null){
// 存储msg到session作为登陆标记
request.getSession().setAttribute("adminInfoMsg",msg);
}
//4.将数据转换为json
String json = JSONUtil.toJSON(msg);
//5.将JOSN数据回复给ajax
return json;
}
}
下面就是 Filter 过滤器的使用方法(必须重写三个方法,否则报错):
过滤器 Filter接口 有三个方法(更加详细的信息查看源码注解即可):
- init():过滤器初始化执行的方法,此方法在实例化过滤器之后只执行一次。
- doFilter():过滤每一个请求,通过此过滤器后会去寻找下一个过滤器,知道FilterChain执行完毕。
- destory():当过滤器关闭或过期之后,会执行此方法。
@WebFilter({"/admin/index.html","/admin/views/*","/express/*"})//过滤器还未启用
public class AccessControlFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
Message adminInfo = LoginUtil.getAdminInfoMsg(request.getSession());
if(adminInfo != null){
//去到下一个过滤器,知道过滤器全部执行结束
filterChain.doFilter(servletRequest,servletResponse);
}else {
response.sendRedirect("/admin/login.html");//选择跳转到admin/login.html页面
}
}
@Override
public void destroy() {
}
}
用户权限做的很粗糙,之后学习新技术,引入Token等操作更为完备。
这个模块功能已经实现,但是 阿里云短信的密匙申请原因 ,未通过申请,无法使用此功能,固此处不写出。
这个项目的很多步骤处理并不规范,奈何学识、眼界、资源有限,以我现在的水平,能做到的自己满意即可。
技术点都是几年前用的东西,现在又非常的新技术新框架新解决方案。我想做这么一个项目,希望能扩展自己的视野,从一个初学者的角度看待一个Javaweb项目运行起来的样子。其中有不懂的点,找了很多资料,看了很多的教程视频,做了很多笔记,也写了一些博客。
之后我会学习SpringBoot+vue,并打算做一个个人博客项目,敬请期待~