大家好,我是小冷
上一篇介绍了CRM是什么?以及CRM系统有哪些模块组成。需要详细的地址点击:地址
接下来继续介绍CRM系统的具体模块逻辑信息
基础模块
每个系统都有自己的基础模块,CRM系统也有,主要包括登录,退出,记住密码,忘记密码等等。
登录功能
UserController层写的都是用户相关的接口,控制层定义接口,对接前台。Controller 层调用 Service 层 userLogin 方法,捕获 service 方法的异常,获取登录结果,并将 ResultInfo 对象通过 JSON 格式响应给客户端。
可以看下源代码:
@Controller
@RequestMapping("user")
public class UserController extends BaseController {
@Resource
private UserService userService;
/**
* 用户登录
*
*
* @param userName
* @param userPwd
*/
@PostMapping("login")
@ResponseBody
public ResultInfo userLogin(String userName, String userPwd) {
ResultInfo resultInfo = new ResultInfo();
// 调用service层登录方法
UserModel userModel = userService.userLogin(userName, userPwd);
// 设置ResultInfo的result的值 (将数据返回给请求)
resultInfo.setResult(userModel);
return resultInfo;
}
}
postMan测试结果如下:
前端登录功能实现
index.ftl 添加对应 index.js,使用 layui 表单组件实现表单提交操作。登录成功后,如果
参考API:https://www.layui.com/doc/modules/form.html#onsubmit
layui.use(['form','jquery','jquery_cookie'], function () {
var form = layui.form,
layer = layui.layer,
$ = layui.jquery,
$ = layui.jquery_cookie($);
/**
* 用户登录 表单提交
*/
form.on("submit(login)", function(data){
// 获取表单元素的值 (用户名 + 密码)
var fieldData = data.field;
// 判断参数是否为空
if (fieldData.username == "undefined" || fieldData.username.trim() == "") {
layer.msg("用户名称不能为空!");
return false;
}
if (fieldData.password == "undefined" || fieldData.password.trim() == "") {
layer.msg("用户密码不能为空!");
return false;
}
// 发送 ajax 请求,请求用户登录
$.ajax({
type:"post",
url:ctx + "/user/login",
data:{
userName:fieldData.username,
userPwd:fieldData.password
},
dataType:"json",
success:function(data){
// 判断是否登录成功
if (data.code == 200) {
layer.msg("登录成功!", function () {
// 将用户信息存到cookie中
var result = data.result;
$.cookie("userId", result.userId);
$.cookie("userName", result.userName);
$.cookie("trueName", result.trueName);
// 登录成功后,跳转到首页
window.location.href = ctx + "/main";
});
} else {
// 提示信息
layer.msg(data.msg);
}
}
});
// 阻止表单跳转
return false;
});
});
密码修改
核心思路分析
用户密码修改
1. 参数校验
userId 非空 用户对象必须存在
oldPassword 非空 与数据库中密文密码保持一致
newPassword 非空 与原始密码不能相同
confirmPassword 非空 与新密码保持一致
2. 设置用户新密码
新密码进行加密处理
3. 执行更新操作
受影响的行数小于1,则表示修改失败
UserService中updateUserPassword 方法实现
@Transactional(propagation = Propagation.REQUIRED)
public void updateUserPassword (Integer userId, String oldPassword,
String newPassword, String confirmPassword ) {
// 通过userId获取用户对象
User user = userMapper.selectByPrimaryKey(userId);
// 1. 参数校验
checkPasswordParams(user, oldPassword, newPassword, confirmPassword);
// 2. 设置用户新密码
user.setUserPwd(Md5Util.encode(newPassword));
// 3. 执行更新操作
AssertUtil.isTrue(userMapper.updateByPrimaryKeySelective(user) < 1, "用户密码更新失败!");
}
准备密码修改页对应 JS 文件。添加表单提交代码,使用 ajax 对接后端密码修改接口实现密码修改操
作, 当密码修改后清除客户端 cookie 信息并跳转至登录页面。
在 src/main/resources/public/ js 目录中,新建 user 目录,添加 password.js 文件
layui.use(['form','jquery','jquery_cookie'], function () {
var form = layui.form,
layer = layui.layer,
$ = layui.jquery,
$ = layui.jquery_cookie($);
/**
* 用户密码修改 表单提交
*/
form.on("submit(saveBtn)", function(data) {
// 获取表单元素的内容
var fieldData = data.field;
// 发送ajax请求,修改用户密码
$.ajax({
type:"post",
url:ctx + "/user/updatePassword",
data:{ oldPassword:fieldData.old_password,
newPassword:fieldData.new_password,
confirmPassword:fieldData.again_password
},
dataType:"json",
success:function (data) {
// 判断是否成功
if (data.code == 200) {
// 修改成功后,用户自动退出系统
layer.msg("用户密码修改成功,系统将在3秒钟后退出...", function () {
// 退出系统后,删除对应的cookie
$.removeCookie("userIdStr",{domain:"localhost",path:"/crm"});
$.removeCookie("userName", {domain:"localhost",path:"/crm"});
$.removeCookie("trueName", {domain:"localhost",path:"/crm"});
// 跳转到登录页面 (父窗口跳转)
window.parent.location.href = ctx + "/index";
});
} else {
layer.msg(data.msg);
}
}
});
});
});
退出登录
找到 “退出登录” 的元素,并绑定点击事件。当用户点击退出时,清空cookie信息
在 main.js 中,通过类选择器绑定元素的点击事件
/**
* 用户退出
* 删除cookie
*/
$(".login-out").click(function () {
// 删除 cookie
$.removeCookie("userIdStr", {domain:"localhost",path:"/crm"});
$.removeCookie("userName", {domain:"localhost",path:"/crm"});
$.removeCookie("trueName", {domain:"localhost",path:"/crm"});
// 跳转到登录页面 (父窗口跳转)
window.parent.location.href = ctx + "/index";
});
全局异常处理封装
实现思路:
控制层的方法返回的内容两种情况:1. 视图:视图异常 2. Json:方法执行错误 返回错误json信息
全局异常拦截器实现
实现 HandlerExceptionResolver 接口 ,处理应用程序异常信息
/**
* 全局异常统一处理
*
*/
@Component
public class GlobalExceptionResolver implements HandlerExceptionResolver {
/**
* 异常处理方法
* 方法的返回值:
* 1. 返回视图
* 2. 返回数据(JSON数据)
*
* 如何判断方法的返回值?
* 通过方法上是否声明@ResponseBody注解
* 如果未声明,则表示返回视图
* 如果声明了,则表示返回数据
* @param request request请求对象
* @param response response响应对象
* @param handler 方法对象
* @param ex 异常对象
* @return org.springframework.web.servlet.ModelAndView
*/
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
/**
* 非法请求拦截
* 判断是否抛出未登录异常
* 如果抛出该异常,则要求用户登录,重定向跳转到登录页面
*/
if (ex instanceof NoLoginException) {
// 重定向到登录页面
ModelAndView mv = new ModelAndView("redirect:/index");
return mv;
}
/**
* 设置默认异常处理(返回视图)
*/
ModelAndView modelAndView = new ModelAndView("error");
// 设置异常信息
modelAndView.addObject("code",500);
modelAndView.addObject("msg","系统异常,请重试...");
// 判断HandlerMethod
if (handler instanceof HandlerMethod) {
// 类型转换
HandlerMethod handlerMethod = (HandlerMethod) handler;
// 获取方法上声明的@ResponseBody注解对象
ResponseBody responseBody = handlerMethod.getMethod().getDeclaredAnnotation(ResponseBody.class);
// 判断ResponseBody对象是否为空 (如果对象为空,则表示返回的事视图;如果不为空,则表示返回的事数据)
if (responseBody == null) {
/**
* 方法返回视图
*/
// 判断异常类型
if (ex instanceof ParamsException) {
ParamsException p = (ParamsException) ex;
// 设置异常信息
modelAndView.addObject("code",p.getCode());
modelAndView.addObject("msg",p.getMsg());
} else if (ex instanceof AuthException) { // 认证异常
AuthException a = (AuthException) ex;
// 设置异常信息
modelAndView.addObject("code",a.getCode());
modelAndView.addObject("msg",a.getMsg());
}
return modelAndView;
} else {
/**
* 方法返回数据
*/
// 设置默认的异常处理
ResultInfo resultInfo = new ResultInfo();
resultInfo.setCode(500);
resultInfo.setMsg("异常异常,请重试!");
// 判断异常类型是否是自定义异常
if (ex instanceof ParamsException) {
ParamsException p = (ParamsException) ex;
resultInfo.setCode(p.getCode());
resultInfo.setMsg(p.getMsg());
} else if (ex instanceof AuthException) { // 认证异常
AuthException a = (AuthException) ex;
resultInfo.setCode(a.getCode());
resultInfo.setMsg(a.getMsg());
}
// 设置响应类型及编码格式(响应JSON格式的数据)
response.setContentType("application/json;charset=UTF-8");
// 得到字符输出流
PrintWriter out = null;
try {
// 得到输出流
out = response.getWriter();
// 将需要返回的对象转换成JOSN格式的字符
String json = JSON.toJSONString(resultInfo);
// 输出数据
out.write(json);
} catch (IOException e) {
e.printStackTrace();
} finally {
// 如果对象不为空,则关闭
if (out != null) {
out.close();
}
}
return null;
}
}
return modelAndView;
}
}
非法请求拦截
对于后端菜单资源,这里要求用户必须进行登录来保护 web 资源的安全性,此时引入非法请求拦截功
能。
实现思路:
-
判断用户是否是登录状态
-
获取Cookie对象,解析用户ID的值
-
如果用户ID不为空,且在数据库中存在对应的用户记录,表示请求合法
-
否则,请求不合法,进行拦截,重定向到登录页面
定义拦截器
在新建 interceptors 包,创建 NoLoginInterceptor 类,并继承 HandlerInterceptorAdapter 适配器,
实现拦截器功能。
public class NoLoginInterceptor extends HandlerInterceptorAdapter {
// 注入UserMapper
@Resource
private UserMapper userMapper;
/**
* 拦截用户是否是登录状态
* 在目标方法(目标资源)执行前,执行的方法
*
* 方法返回布尔类型:
* 如果返回true,表示目标方法可以被执行
* 如果返回false,表示阻止目标方法执行
*
* 如果判断用户是否是登录状态:
* 1. 判断cookie中是否存在用户信息(获取用户ID)
* 2. 数据库中是否存在指定用户ID的值
*
* 如果用户是登录状态,则允许目标方法执行;如果用户是非登录状态,则抛出未登录异常 (在全局异常中做判断,如果是未登录异常,则跳转到登录页面)
* @param request
* @param response
* @param handler
* @return boolean
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 获取cookie中的用户ID
Integer userId = LoginUserUtil.releaseUserIdFromCookie(request);
// 判断用户ID是否为空,且数据库中存在该ID的用户记录
if (null == userId || userMapper.selectByPrimaryKey(userId) == null) {
// 抛出未登录异常
throw new NoLoginException();
}
return true;
}
}
写到最后,小冷一直在技术路上前行…你的关注,评论,收藏都是对我的支持。
昨天,删去;今天,争取;明天,努力。
点下方名片关注就可以获取资料。