项目Gitee地址:https://gitee.com/aidianfirst/aidianfirst

项目简介

项目分为前后台两个大板块。

前台面向客户,提供客房展示,只展示状态时可预约的,用户可选择酒店相应楼层的房间进行预约。被预约的客房状态会改变。而必须是登录用户才可进行预约操作,前台还提供了简单的登录注册模块。

后台面向员工管理:

  • 首先是系统管理板块,可以设置员工的基础信息,以及角色分配,员工角色也就是后台管理的菜单权限,如超级管理员可以查看全部模块,业务管理员只能查看订单业务。还包括了菜单管理,可管理后台的一级二级菜单使用了Layui的dtree模板

  • 客房管理板块,房间类型管理,酒店楼层管理,房间管理,房间有不同类型

  • 订单业务管理,主要是预订管理和入住管理,这里就涉及到客房状态的联动管理问题。客房状态有1可预定、2不可预定。订单状态有1待确认、2已确认、3入住中、4已退房。

    当前台客户预订房间后,会预订管理新增一个状态为 1待确认 的订单,若添加订单成功,则将房间状态改为 2不可预定,且前端判断房间状态不为1的都不展示。然后由员工在后台确认订单,确认后的订单会变为2已确认。

    当客户到酒店时会办理入住手续,我们来到入住管理进行点击登记入住功能,该功能只查询处于 2已确认 的订单,然后将 2已确认 的订单变为 3已入住,最后当客户退房时执行退房操作,将 3已入住 的订单状态变为 4已退房,同时修改房间状态变为 1可预定,由于房间空出来了,前台也会展示可预定的房间。

数据库设计思想:订单与房间、房型信息相关,需要进行订单表房间表、房型表的联表查询获取字段信息,但订单表没有设置房间id或房型id,也就是外键,所以缺少了联表查询的字段,导致在mapper写sql联表查询时,要对订单表自定义resultmap结构引入房间、房型id,操作冗余,数据库设计要考虑全面。

登录注册逻辑:

使用SpringMVC拦截器 + Session + md5完成页面拦截和登录状态判断。

继承HandlerInterceptorAdapter重写preHandle方法,在请求处理前调用,方法内获取http请求的session(HttpServletRequest.getSession),判断session是否为空,为空则跳转到登录页面进行拦截,不为空放行。拦截器通过mvc:interceptor注解配置。一开始会屏蔽所有后台界面,然后放行登录。

1
2
3
4
5
6
7
8
9
10
<mvc:interceptors>
<mvc:interceptor>
<!-- 拦截admin下全部访问路径,除了登录的相关路径 -->
<mvc:mapping path="/admin/**"/>
<mvc:exclude-mapping path="/admin/login.html"/>
<mvc:exclude-mapping path="/admin/employee/login"/>
<!-- 注入拦截器 -->
<bean class="com.tang.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>

注册只有姓名和密码,姓名是直接添加,用户有盐值属性是为了md5服务,我们处理密码前先用uuid获取随机盐值并添加到用户,然后通过md5对密码加密再添加,这样数据库存放的不是用户的直接密码,安全性高。

登录时,业务会根据用户名先进行查询用户,获取当前用户的盐值属性,然后再对输入的密码进行md5加密,将加密后的随机码和用户数据库记录的随机码进行比较,相同则说明密码验证通过,可以登录,并返回用户。因为md5时不可逆的,所以用户会存放随机的盐值,输入的密码直接加密比较,也就是反正不行正着来。

这些是Sercvice层的逻辑操作,在Controller层调用Service,会先创建一个map,然后判断Service业务返回值是否为空,不为空说明登录验证通过,map存入键值对,(status,true)(status,false)(message,”xxxxxxx”),使用restController返回将map信息转为json返回。登录成功时还会session.setAttribute(),键值对存储 用户(前后台),用户对象。

前端登录时会点击提交按钮,首先调用Controller路径的请求,获取json的键值对应字段,判断存放的status字段的T/F,如果为T则进入后台首页,为F输出Message键的值。

https://zhuanlan.zhihu.com/p/121492822,md5不可逆,破解只能暴力

分页查询:

使用pagehelper插件进行分页。

权限管理:

当前用户登录后,后台session键确定,通过getAttribute键拿到员工对象(值),进而获取员工id,然后连接员工角色表、菜单角色表、菜单三表查询,通过员工id找到对应角色id,角色id找到多个菜单id,然后查询多个菜单id内容进行页面渲染。菜单的渲染还需要工具类 父子层次的菜单结构类 和 树形结构类,定义map,把信息传入,最后转json传到前端。

2021.11.23,突然看到这篇总结,特地更新一下项目精华,也是秋招面试后的总结,现在看来这个管理系统是真的拉,发现之前对项目理解没把握重点,别人问登录逻辑直接把我乱杀😥,麻中麻,不过最近搞完微服务商城了,可以聊点别的了,把功能组件逻辑理通顺即可,虚空业务可没人听。。。




个人感受

数据库

其实这次项目做完后,最大的感想就是数据库太重要了,因为是跟着视频学的,所以表的设计都是用别人的,但有的字段直到项目完成都没有用到,很明显是垃圾字段,而有的重要关联字段在相关表中却没有,导致联表查询时还需要去实体类声明变量。

我们的业务说白了就是在操作数据,所以数据库的重要性是不言而喻的,最底层的数据库没有设计好,那么项目后序可能会进行频繁的修改,这对于我们开发而言是很不好的。

之后的学习我会去了解一下MySQL的优化以及Redis的相关知识,增强自己数据库的能力。


视图层

这次项目第二大的感想就是前端很重要,毕竟一般人都是看外在包装的,谁会想直接看数据交流呢?美术资源和前端的页面设计就是我们项目拿出去最直观的感受,不过前端代码确实调的很烦人,一层套一层,后面我会去看看vue的使用,体验一下前后端分离的感觉。毕竟这次项目并不是完全的前后端分离,主要使用的还是Jsp文件,其他用的最多的是EL表达式,Jsp的语句就更不用提了,太臃肿了。


项目业务

业务其实基本上就是CURD,有一些业务会复杂一点,如订单状态的处理。这时就需要涉及多个数据库的改动,还是比较麻烦的,而且这种关联改动往往考虑不周全,有时候会少改动几个字段,导致数据出现差错。


工具类

Shiro-MD5

这次用了加密我才知道企业数据库应该不会存储用户的原密码,都是二次加密后存储到数据库的,加密的格式也比较复杂,这样密码安全性确实高了不少。

类型工具类

在使用Layui框架时,我们返回的数据必须符合官方的格式要求,也就是必须要有规定的返回字段。如使用分页查询时的表单数据、树型结构的dtree等,我们可以将它们的返回类型封装一个类,方便后续的调用。

常量类

由于向页面发送请求时常常需要用到字符串,且有大量的重复内容,所以我将重复内容放入一个接口中,用变量名声明,后面再使用时直接调用变量名即可,以防字符串打错。


细节处理

pojo实体类

实体类一定要对应数据库,无论是属性还是字段名,由于属性出错,导致我卡在登录界面跳转很久,而且要学会用浏览器控制台看问题,在network中查看请求的response,然后针对404、500等问题进行解决。(我是万万没想到pojo出错了,还找了半天配置文件和java类)

前端导入依赖

statics的依赖,我们要用到时应该在对应jsp中进行js引用,切记使用${pageContext.request.contextPath}相对路径。注意src和href区别,src是绝对路径,href是相对路径。

applicationContext.xml

切记spring-dao、spring-service、spring-mvc三层整合到一起,且service一定要先于controller被调用,毕竟是递进关系,谨防调用顺序不同的500报错。

注解

项目中有很多类不用走视图解析器,可直接返回JSON格式,这时我们控制器注解就可以优先使用@RestController,若JSON格式和视图器解析混合的控制器,可以使用@Controller+@ResponseBody

静态资源过滤

由于之前学习静态资源只见过HTML、CSS、JavaScript以及媒体资源,这些常规的过滤我们都有配置。而这次涉及小图标的展示使用ttf文件就出了问题。

在使用dtree树形菜单,图标不显示,报错Failed to decode downloaded font

然后找了一圈,在浏览器点击事件发现了报错:Failed to decode downloaded font

然后在捣鼓半天后搜到了一个帖子:https://blog.csdn.net/uiguion/article/details/105945346

总结一下原因

  • 使用maven,静态资源拦截
  • 使用shiro、security安全框架资源被拦截
  • 文件资源损坏

我这里就是maven资源拦截的原因,和之前的约定大于配置一样,我们需要配置资源过滤在build-resources标签下,这里的文件路径根据项目的情况修改。而且我的资源也损坏了,所以如果不是拦截问题,可以打开在文件夹打开资源看看是否损坏,win10是支持ttf字体文件的。(就不要用idea看了,反正也看不了)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<resource>
<directory>web</directory>
<filtering>true</filtering>
<excludes>
<exclude>static/**/*.woff</exclude>
<exclude>static/**/*.woff2</exclude>
<exclude>static/**/*.ttf</exclude>
</excludes>
</resource>
<resource>
<directory>web</directory>
<filtering>false</filtering>
<includes>
<include>static/**/*.woff</include>
<include>static/**/*.woff2</include>
<include>static/**/*.ttf</include>
</includes>
</resource>

转义 CDATA

在mapper.xml中写sql时,是不允许写>=这样的操作符,需要转义

我们可以在以下模块中写sql语句,且可以使用>=等操作符

1
<![CDATA[   ]]>

Mapper联表查询

  • 使用resultType,这时我们需要联表的字段是当前表中没有的,所以需要去pojo中加上其他pojo的对应字段。
  • 使用resultMap,这时我们只需要在pojo中加上其他联表的对象即可,然后在mapper文件中的resultMap区域自行添加需要用到的字段

我们联表查询量少时可以使用resultType,添加几个字段影响不会很大,而且写resultMap还再写一遍字段键值对应,麻烦;但量大时应使用resultMap,不然底层字段加的太多很冗杂,使用Map方便管理维护。

图片路径设置问题

由于项目是本地项目,所以如房间选型的默认图片是直接存放在项目中的,而后续下载添加的网络图片需要自定义文件夹,这也要改动相应的Java文件

项目业务流程

项目中每一个基础功能的构建过程。

pojo—dao层接口—Mapper写SQL语句—Service层接口—Impl实现接口,撰写业务逻辑—Controller调用业务,走视图解析—前端界面调用参数

在写登录业务时,如果需要登录的信息,可用Session来传递。

客人和房间状态的变化逻辑

房间:可预定、已预定(总共预定数)、已入住

客人:入住中(已入住)、已离店

项目中订单(客人)和房间涉及多种状态的转换,这里的逻辑比较复杂,要涉及多个页面的对应状态转换,所以业务层就要考虑的多一点,需要把所有涉及的表都更新一遍。在项目中我是通过checkin表拿到了其他表的ID,然后再根据ID更新的,没有这个数据集合的表还真不好搞,这也体现了数据库设计的重要性。