目录
引出
基于SpringBoot的简单的小区物业后台管理系统,主要功能有报修的处理,楼宇信息和房屋信息的管理,业主信息的管理【核心】,以及数据统计分析模块Echarts绘图;此外采用用户-角色权限模型,结合自定义注解实现简单的权限管理功能,采用aop切面实现日志的存储,全局异常的使用。
技术栈:Spring、Spring MVC、Spring Boot、MyBatis、Echarts、Session、Vue、JavaScript、Bootstrap
本项目后端代码git仓库为:
https://gitee.com/pet365/spring-community-manager
一、应用到的技术栈
Spring、Spring MVC、Spring Boot基础
-
Spring基础(Web-MVC)——在idea中新建springWeb项目 & 浏览器请求 和 服务器响应 & SpringMvc文件相关
-
Spring基础(Data Access数据库)——Spring+SpringMVC & 集成mybatis & 拦截器
SpringBoot进阶、SpringMVC原理、AOP切面
-
SpringBoot基础——追根溯源servlet是啥,tomcat是啥,maven是啥 & springBoot项目初步,maven构建,打包 & 测试
-
SpringMvc框架——【深入】SpringMVC 的运行流程:从客户端发送请求request到springMvc框架返回响应response的全流程分析 & DispatcherServlet
SpringAOP,切面
MyBatis 数据库相关
【合集】MySQL的入门进阶强化——从 普通人 到 超级赛亚人 的 华丽转身
-
mybatis 是一个优秀的基于 java 的持久层框架,主要应用于关系型数据库(sql),它内部封装了 jdbc,使开发者只需要关注 sql 语句本身,而不需要花费精力去处理加载驱动、创建连接、创建 statement ,封装数据等繁杂的过程。
-
mybatis 通过 xml 或注解的方式将要执行的各种 statement 配置起来,并通过 java 对象和 statement 中sql 的动态参数进行映射生成最终执行的 sql 语句,最后由 mybatis 框架执行 sql 并将结果映射为 java 对象并返回。
-
采用 ORM 思想解决了实体和数据库映射的问题,对 jdbc 进行了封装,屏蔽了 jdbc api 底层访问细节,使我们不用与 jdbc api 打交道,就可以完成对数据库的持久化操作。
JavaWeb基础:Session等
Java网络开发(Tomcat)—— Servlet学习 & Web相关背景知识 + 网页状态码(304) & JavaWeb项目初步
Java网络开发(Session)—— 从http请求 到 cookie 到 session & 用 session控制 删改数据的权限
网页状态码——200正常、302重定向、304客户端有缓存、400浏览器请求传参异常、404未找到、405方法不允许、415不支持的媒体?、500服务器异常 & 跨域
前端Vue、JavaScript、Bootstrap
二、后台管理系统的功能
登录功能
1.用户名密码登录
能够实现用户名和密码登录以及手机验证码登录,其中手机验证码存储到session中,键为登录的用户的手机号。
当时对于各种情况的用户进行了处理
离职员工
账号锁定
首次登录改密码
密码修改
2.验证码的登录
后台逻辑,手机号校验,多线程使用
报修业务的处理
前端页面
图片用的本地文件资源映射
批量删除当时的操作:
1.真的delete删除了,其实应该是逻辑删除,把状态修改一下,比如未处理0,已处理1,已删除2,就暂时不显示;
2.在删除之前没有做数据校验,其实按理说删除之前应该检查一下状态,是否还没有维修完成等;
为了前期便于测试,给了一个默认的登录用户admin,便于前期进行接口测试,不需要每次登录才能测试。
楼宇信息管理
快排功能
用动态sql
房屋管理
多条件查询
后端代码
用动态sql,面积后端排序
入住日期排除,前端实现
业主管理【核心功能】
业主管理的极简页面
详情页面
买房的业务
添加业主页面
买买买
买!
添加新的业主成功
有几个小瑕疵:
- 身份证号码没有进行校验;
- 记录人没有和登录的管理员绑定;
实现可买的房子到买了的房子框框中的逻辑
意向用户信息登记
意向用户详情页面
后端相关代码
开始想用这种一个参数一个参数接收的方式,但是有一个参数是ist,一直报错,后面用对象接收
尝试用了可变长度参数传参
从service到mapper
动态SQL的处理,可变长度参数处理
楼栋 List 是否传入执行不同的sql语句查询
数据统计分析
采用Echarts进行数据统计分析图表绘制
进行了数据校验
后端数据校验
三、系统必备的要素
登录认证
采用拦截器,如果没有登录就去登录页面
权限管理
采用的模型和思路
用3张表实现简单权限管理,用户对应角色
自定义注解和实现
处理角色权限的注解
拦截器,获得controller层上的注解
package com.tianju.interceptor;
import com.alibaba.fastjson.JSON;
import com.tianju.anno.PreAuthorize;
import com.tianju.entity.ResData;
import com.tianju.entity.User;
import com.tianju.entity.UserAuth;
import com.tianju.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
@Component
public class AuthorizeInterceptor implements HandlerInterceptor {
@Autowired
private IUserService userService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
User user = (User) request.getSession().getAttribute("user");
UserAuth userAuth = userService.queryUserAuthByUsername(user.getUsername());
// 如果请求的资源是经过controller
if (handler instanceof HandlerMethod){
// 获取controller中方法上的注解
HandlerMethod handlerMethod = (HandlerMethod) handler;
PreAuthorize preAuthorize = handlerMethod.getMethod().getAnnotation(PreAuthorize.class);
// 方法上有这个注解
if (preAuthorize!=null){
String[] value = preAuthorize.value();
int index = Arrays.binarySearch(value, userAuth.getRoleName());
if (index >=0){
return true;
}else {
response.setContentType("application/json;charset=utf-8");
response.setCharacterEncoding("UTF-8");
response.getWriter()
.write(JSON.toJSONString(new ResData(300001, "没有权限访问该方法", null)));
return false;
}
}
}
return true;
}
}
配置拦截器
package com.tianju.config;
import com.tianju.interceptor.AuthorizeInterceptor;
import com.tianju.interceptor.LoginAuthorInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* springmvc的配置类
* 1.是配置类;@Configuration
* 2.是springmvc的配置类;implements WebMvcConfigurer
*/
@Configuration
public class SpringMvcConfig implements WebMvcConfigurer {
@Value("${repairImg}")
String repairImgLocation;
// 静态资源映射;
// 要点:1.浏览器访问的连接 /repair/**;2.映射的位置,file:D:/620/repair/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/repair/**")
.addResourceLocations("file:"+repairImgLocation); // 映射的位置
}
// 拦截器,拦截谁,放行谁
@Autowired
LoginAuthorInterceptor loginAuthorInterceptor;
@Autowired
AuthorizeInterceptor authorizeInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginAuthorInterceptor)
.addPathPatterns("/**") //拦截谁,表示都拦截
.excludePathPatterns(
"/user/loginPage","/user/login",
"/js/**","/css/**","/bootstrap/**","/img/**",
"/user/loginSms","/getSmsYzm"
);
registry.addInterceptor(authorizeInterceptor)
.addPathPatterns("/**") //拦截谁,表示都拦截
.excludePathPatterns(
"/user/loginPage","/user/login",
"/js/**","/css/**","/bootstrap/**","/img/**",
"/user/loginSms","/getSmsYzm"
);
}
}
另一种权限(页面不同)
普通员工–夸夸自己
我是boss–隐藏地图
日志管理
AOP切面,用来记录日志,什么时候,谁,访问了啥,干了啥
package com.tianju.aop;
import com.tianju.entity.User;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.aspectj.lang.annotation.Aspect;
import javax.servlet.http.HttpSession;
import java.util.Arrays;
import java.util.Date;
/**
* 记录日志,用增强方法,
* 要点:
* 0.是增强类 @Aspect,在容器中 @Component
* 1.给谁做增强,
* 2.怎么增强,@before @after @afterReturning @afterThrowing
*/
/**
* @Component 在容器中
* @Aspect 是增强方法
* @Before("@within(org.springframework.stereotype.Controller)") 给controller层增强
*/
@Component
@Aspect
@Slf4j // 用lombok.extern.slf4j.Slf4j;
public class LoggingAsp {
@Autowired // session也在容器里,所以直接可以注入
private HttpSession session;
// 给所有标注了@Controller注解的方法做增强
@Before("@within(org.springframework.stereotype.Controller)")
public void log(JoinPoint joinPoint){
String className = joinPoint.getTarget().getClass().getSimpleName(); // 获取类名
String methodName = joinPoint.getSignature().getName(); // 获取方法名
Object[] args = joinPoint.getArgs(); // 获取传的参数
// 获取当前登陆的人,从session中获取
User user = (User) session.getAttribute("user");
String username = (user==null)?"未登录人员":user.getUsername();
log.info("{}访问了{}类的{}方法,传的参数为{}",
new Date() + username,className,methodName, Arrays.toString(args));
}
}
持久化日志到本地
# 日志的相关配置
logging:
file:
name: D:\\620\\log\\community.log
level:
org.springframework.web: debug
com.tianju: debug
org.springframework.jdbc.support.JdbcTransactionManager: debug
全局异常
如果没有全局异常处理,则异常会一层一层,从dao到service到controller,最后抛给前端,然后用户看到一堆看不懂的东西
兜底异常,返回统一异常信息,更进一步的返回一个页面
总结
1.简单的小区物业后台管理系统,主要功能有报修的处理,楼宇信息和房屋信息的管理,业主信息的管理【核心】,以及数据统计分析模块Echarts绘图;
2.此外采用用户-角色权限模型,结合自定义注解实现简单的权限管理功能;
3.采用aop切面实现日志的存储;
4.全局异常的使用。