# 快递e **Repository Path**: bloomcool/express-e ## Basic Information - **Project Name**: 快递e - **Description**: 简单的快递管理项目,分为前后端管理,使用原生JavaWeb技术实现,自定义注解,借助maven实现。未使用任何框架 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 4 - **Forks**: 1 - **Created**: 2021-09-01 - **Last Updated**: 2023-06-16 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 一、项目介绍 练手的一个小项目,能做 快递、快递员、用户、管理员 的管理(增删改查),支持多用户访问。 **技术选型** 1. 系统环境:Java EE 8、Servlet 3.0、Apache Maven 3、Mysql。 2. 技术点:Java反射,Durid,HTML、Ajax,JSON,XML等等基础知识点。 3. 所用框架:自己搭建的基于MVC架构的MVC框架(模拟SpringMVC框架)。**[MVC架构搭建详解点击我查看](https://blog.csdn.net/yeahPeng11/article/details/120330630)**。 **前后台:** 1. 前台:使用手机端操作。 2. 后台:使用pc浏览器操作。 **文件结构** ![](markdown/images/文件结构图.png)
# 二、快速使用 > 这里我还没有部署到服务器,以后会部署到服务器,直接访问服务器即可体验到。 环境: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 ```properties username=root password= ``` 修改结束以后,启动项目。 1. **访问前台:`localhost:8080`(验证码登陆,验证码会自动传送到输入框)** 2. **访问后台:`localhost:8080/admin` (账号admin,密码123)**
# 三、核心技术 ## 1.项目架构 **技术点博客:** 1. 【[MVC架构搭建框架](https://blog.csdn.net/yeahPeng11/article/details/120330630)】 2. 【[Java反射](https://blog.csdn.net/yeahPeng11/article/details/120394222)】 3. 【[Java注解](https://blog.csdn.net/yeahPeng11/article/details/120394276)】 4. 【[JVM类加载机制](https://blog.csdn.net/yeahPeng11/article/details/120402814)】
此项目未使用任何现在留下的框架,而是自己手撸了一套以MVC架构为基础的自定义框架(参考SpringMVC,当然有很多不足),把 **Controller类里的所有请求和请求对应的方法全部加载到一个map容器当中统一进行管理**,新编写的Controller类只需要写入配置文件`application.properties`中即可实现容器加载和管理。 容器初始化加载所有的方法之后,每次接收到用户发起的请求,直接访问map容器,获取到对应的封装对象,执行相应的方法,返回结果。 这种MVC架构模式使得项目具有 **低耦合、易管理、易扩展、层次清晰** 等优点,我们的项目代码在搭建完MVC加架构,编写起来就变得非常的简单。 这里针对这一核心技术点就不在多说,精华干货都在这里面:**[MVC架构搭建详解点击我查看](https://blog.csdn.net/yeahPeng11/article/details/120330630)**。
## 2.后端数据封装 ### 2.1 JSON 前端使用html页面,后端传递给前端的数据全部是的以JSON数据格式的,所以,就必须有一个工具类 **JSONUtil** 把后端待传输的数据全部转换为JSON的格式,这可以借助Gson工具类,具体代码实现如下: ```java public class JSONUtil { // 需要引入Gson依赖 项目中都已经实现 private static Gson gson = new Gson依赖(); public static String toJSON(Object obj){ return gson.toJson(obj); } } ``` ### 2.2 BooStrapTableXxx类 > 如BooStrapTableExpress、BooStrapTableUser等。 封装数据查询到的数据,比如**时间**格式,从数据库查询到的时间是UTC格式,不符合国人的阅读习惯。**状态码**是数字,而展示到前端页面需要以文字的方式进行展示。 所有的数据全部封装为String和int格式,都符合国人的阅读习惯。这就要在合适的地方使用实体类转换为BooStrapTableXxx类,转换的结果得符合我们的预期。 ### 2.2 Message类 **后端与数据库交互后得到的数据结果集统一使用Message封装**,这样在解析数据的时候,我们拥有统一的数据格式,方便很多。 Message > 这里的状态码尚且这样处理,之后学习到更前言的技术,会有更好的解决方案。 ```java 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** ```java 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; } ```
## 3.分页实现 前端采用基于`bootstrap`的轻量级表格插件[bootstrap-table](https://github.com/wenzhixin/bootstrap-table) 后端接收前端传递的分页数据进行查询实现分页。 ### 3.1 前端调用实现 > 当然bootstrap插件的引入是必不可少的 ```javascript
$("#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 }, ... ... ] }); ``` ### 3.2 后台逻辑实现 借助工具类**ResultData**,**前端的bootstrap-table插件能直接识别JSON格式的ResultData类型的数据**,直接显示出total和rows数据。 ```java public class ResultData { /** * 每次查询的数据集合 */ private List rows = new ArrayList<>(); /** * 查询到的总数量 */ private int total; // get,set,构造等方法需创建 } ``` >ResultData数据也是封装到Message里面由service返回给controller层的。 一下是后端与前端的交互的代码示例: ```java @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; } ```
## 4.二维码生成 借助Google的二维码生成插件、汉化的脚本,使用一些简单的代码,生成二维码。[点击我下载js插件资源文件](https://itdage.cn/file/qrcode.zip) **步骤:** ``` 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);* ``` 示例: ```html 二维码生成
```
## 5.异常的统一处理 针对于异常而言,能给出一个统一处理是非常有意义的。比如在插入快递的时候,快递单号已经存在,就不能再次插入此单号,此时就会报出一个 **单号重复异常** ,那么当我们捕捉到的时候,就抛出一个我们实现的异常类,这样就可以做出对于的操作(比如提示用户检测单号重新输入)。 异常类需要继承Exception类,以DuplicateNumberException为例: ```java public class DuplicateNumberException extends Exception{ public DuplicateNumberException() { } public DuplicateNumberException(String message) { super(message); } } ``` 使用示例: dao层 ```JAVA 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层 ```java Message msg = null; try { // 调用dao } catch (Exception e) { if (e instanceof DuplicateNumberException) { msg = new Message(-3, "快递单号重复,请检查单号"); //因为是快递单号重复,需要提示用户再次输入正确的快递单号 } else { // Other exception } } ```
## 6.用户权限处理 当账号没有登陆的时候,不允许访问服务器的其他模块(除登陆模块外),跳转到登陆页面等待登陆。 **实现思路**: 1. 用户登陆后使用 **session存储一个登陆标记**,用于表示用户已经登陆(当然可以设置失效时间)。当用户访问其他模块时,获取不到相应的标记,就跳转到登陆页面。 2. 在每次请求都会去寻找session里面的存储的标记,这就要用到 **过滤器Filter**,过滤每一次请求,每一次请求都去寻找session里面的标记,找到就放行,找不到就跳转登陆页面。 在session里面存储标记需要在controller层实现,因为controller层能很好的获取到session对象,以AdminController的login()方法为例: ```java 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** 过滤器的使用方法(只需要重写`doFilter()`即可): >过滤器 **Filter接口** 有三个方法(更加详细的信息查看源码注解即可): > >1. **init**():过滤器初始化执行的方法,此方法在**实例化**过滤器之后只执行一次。 >2. **doFilter**():过滤每一个请求,通过此过滤器后会去寻找下一个过滤器,知道FilterChain执行完毕。 >3. **destory**():当过滤器关闭或过期之后,会执行此方法。 > ```java @WebFilter({"/admin/index.html","/admin/views/*","/express/*"})//过滤器的请求 public class AccessControlFilter implements Filter { @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 { //选择跳转到admin/login.html页面 response.sendRedirect("/admin/login.html"); } } } ``` > 用户权限做的很粗糙,之后学习新技术,引入Token等操作更为完备。 ## 7.阿里云短信发送 这个模块功能已经实现,但是 **阿里云短信的密匙申请原因** ,未通过申请,无法使用此功能,固此处不写出。
# 四、拓展及展望 这个项目的很多步骤处理并不规范,奈何学识、眼界、资源有限,以我现在的水平,能做到的自己满意即可。 技术点都是几年前用的东西,现在又非常的新技术新框架新解决方案。我想做这么一个项目,希望能扩展自己的视野,从一个初学者的角度看待一个Javaweb项目运行起来的样子。其中有不懂的点,找了很多资料,看了很多的教程视频,做了很多笔记,也写了一些博客。 之后我会学习SpringBoot+vue,并打算做一个**个人博客项目**,敬请期待~