springboot登陆前后端及动态菜单

目录

一. 开发公共模块,数据封装和全局异常处理及application.properties

​编辑

二. 开发登陆要用的接收前端数据的dto,和封装查询结果的model

三. 开发登陆的控制层

四. 开发存储当前用户的model和常量类

五. 开发登陆的业务类

六. 开发持久化组件

七. 开发登录前端(vue+ajax)

八. 动态菜单的开发

8.1 首先看数据库查出来的数据

8.2 定义封装查询结果的model

8.3 定义封装树状菜单结构的bean

8.4 开发控制层

8.5 开发业务层

8.6 开发持久层

8.7 编写映射文件

8.8 编写前端

一. 开发公共模块,数据封装和全局异常处理及application.properties

package com.xupt.ygq.oa.common;

public class Result<T> {
    public static final int CODE_OK = 200;
    public static final int CODE_ERR_BUSINESS = 500;
    public static final int CODE_ERR_SYS = 530;
    public static final int CODE_ERR_UNLOGINED = 520;

    public static <T>Result<T> ok(){
        return new Result(true,CODE_OK,null,null);
    }
    public static <T>Result<T> ok(String message){
        return new Result(true,CODE_OK,message,null);
    }
    public static <T>Result<T> ok(T data){
        return new Result(true,CODE_OK,null,data);
    }
    public static <T>Result<T> ok(String message,T data){
        return new Result(true,CODE_OK,message,data);
    }

    public static <T>Result<T> err(int errCode ,String message){
        return new Result(false,errCode,message,null);
    }
    public static <T>Result<T> err(int errCode ,String message,T data){
        return new Result(false,errCode,message,data);
    }

    private boolean success;//是否成功
    private int code;//200 成功 500 业务失败,530 系统错误,520 未登录
    private String message;//概要信息
    private T data;

    public Result(boolean success, int code, String message, T data) {
        this.success = success;
        this.code = code;
        this.message = message;
        this.data = data;
    }

    public boolean isSuccess() {
        return success;
    }

    public int getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }

    public T getData() {
        return data;
    }
}
import com.xupt.ygq.oa.exception.BusinessException;
import com.xupt.ygq.oa.exception.SysException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.TypeMismatchException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@Slf4j
@RestControllerAdvice//该注解说明本类是一个RestController的拦截器,
// 还可以对controller处理方法添加额外逻辑,做增强处理
public class DemoControllerAdvice {
    //该注解说明本方法是一个异常处理器,即当被拦截的controller处理方法发生指定的异常时,即由本方法处理
    @ExceptionHandler(ConstraintViolationException.class)
    public Result handleConstraintViolationException(ConstraintViolationException e){
        log.error("参数不匹配",e);
        Set<ConstraintViolation<?>> set = e.getConstraintViolations();//获取验证错误集合
        //将验证错误集合中,每一个错误的信息取出来,组成一个新的集合,再将新集合中的元素用”,“连接成一个字符串,返回这个字符串
        String msg = set.stream().map(item->item.getMessage()).collect(Collectors.joining(","));
        msg.substring(0,msg.length()-1);
        return Result.err(Result.CODE_ERR_SYS,msg);
    }
    //邦json没绑好异常(post请求)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result handleMethodArgumentNotValidException(MethodArgumentNotValidException e){
        log.error("参数不匹配",e);
        //获取参数绑定结果(包括绑定错误信息)
        BindingResult bindingResult = e.getBindingResult();
        //获取属性错误集合
        List<FieldError> fieldErrorList = bindingResult.getFieldErrors();
        String errMsg = fieldErrorList.stream()
                .map(fieldError->fieldError.getField()+":"+fieldError.getDefaultMessage())
                .collect(Collectors.joining(","));
        return Result.err(Result.CODE_ERR_SYS,errMsg);
    }
    //绑普通字符串没绑好异常
    @ExceptionHandler(BindException.class)
    public Result handleBindException(BindException e){
        log.error("参数不匹配",e);
        //获取参数绑定结果(包括绑定错误信息)
        BindingResult bindingResult = e.getBindingResult();
        //获取属性错误集合
        List<FieldError> fieldErrorList = bindingResult.getFieldErrors();
        String errMsg = fieldErrorList.stream()
                .map(fieldError->fieldError.getField()+":"+fieldError.getDefaultMessage())
                .collect(Collectors.joining(","));
        return Result.err(Result.CODE_ERR_SYS,errMsg);
    }
    //处理自定义系统异常
    @ExceptionHandler(SysException.class)
    public Result handleSysException(SysException e){
        log.error("系统错误",e);
        return Result.err(Result.CODE_ERR_SYS,"系统维护中");
    }
    //处理自定义业务异常
    @ExceptionHandler(BusinessException.class)
    public Result handleBusinessException(BusinessException e){
        return Result.err(Result.CODE_ERR_BUSINESS,e.getMessage());
    }
    //参数格式异常
    @ExceptionHandler(TypeMismatchException.class)
    public Result handleTypeMismatchException(TypeMismatchException e){
        log.error("系统错误",e);
        return Result.err(Result.CODE_ERR_BUSINESS,"系统维护中");
    }
    //处理遗留的异常
    @ExceptionHandler(Exception.class)
    public Result handleException(Exception e){
        log.error("系统错误",e);
        return Result.err(Result.CODE_ERR_BUSINESS,"系统维护中");
    }
}

二. 开发登陆要用的接收前端数据的dto,和封装查询结果的model

public class User {
    private String u_id;
    private String u_name;
    private String u_pwd;

    public String getU_id() {
        return u_id;
    }

    public void setU_id(String u_id) {
        this.u_id = u_id;
    }

    public String getU_name() {
        return u_name;
    }

    public void setU_name(String u_name) {
        this.u_name = u_name;
    }

    public String getU_pwd() {
        return u_pwd;
    }

    public void setU_pwd(String u_pwd) {
        this.u_pwd = u_pwd;
    }
}
import javax.validation.constraints.NotEmpty;

public class LoginDto {
    //@NotEmpty仅针对String ,集合等
    @NotEmpty(message = "账号不得为空")
    private String u_id;
    @NotEmpty(message = "密码不得为空")
    private String u_pwd;

    public String getU_id() {
        return u_id;
    }

    public void setU_id(String u_id) {
        this.u_id = u_id;
    }

    public String getU_pwd() {
        return u_pwd;
    }

    public void setU_pwd(String u_pwd) {
        this.u_pwd = u_pwd;
    }
}

dto里面加了数据验证,所以在作为参数接收前端传回来的数据的时候,前面要加@Validated注解

三. 开发登陆的控制层

import com.xupt.ygq.oa.common.Result;
import com.xupt.ygq.oa.common.page.CurrentUser;
import com.xupt.ygq.oa.common.page.OaConstants;
import com.xupt.ygq.oa.dto.LoginDto;
import com.xupt.ygq.oa.fun.security.service.LoginService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpSession;

@RestController
@RequestMapping("/security/login")//为本控制器处理方法的所有映射地址加统一前缀
public class LoginController {
    @Autowired
    private LoginService loginService;
    @PostMapping("")//此方法地址:/security/login
    public Result login(@Validated @RequestBody LoginDto dto,HttpSession session){
        CurrentUser currentUser = loginService.checkLogin(dto);
        //当前用户信息存放在session中
        session.setAttribute(OaConstants.SESSION_NAME_CURRENT_USER,currentUser);
        return Result.ok();
    }
}

六个注解:

①:@Restcontroller

②:@RequestMapping("+++") :为本控制器处理方法的所有映射地址统一加前缀

③:@Autowired

④:@PostMapping("") :此方法地址为项目根地址加上前面的前缀

⑤:@Validated:表示这个用来接收参数的javaBean里面添加了数据验证

⑥:@RequestBody:表示这个Post请求传递的是json串的请求体

一个对象:

CurrentUser:表示当前登录用户

一个重要参数:

session:用来把查到的当前登录用户存到session中,本次会话都可以访问的到

注意:session.setAttribute()存数据的时候,键用了一个常量来表示,可以避免后续使用出错

四. 开发存储当前用户的model和常量类


public class CurrentUser {
    private String UserId;
    private String UserName;

    public String getUserId() {
        return UserId;
    }

    public void setUserId(String userId) {
        UserId = userId;
    }

    public String getUserName() {
        return UserName;
    }

    public void setUserName(String userName) {
        UserName = userName;
    }
}
//常量类
public class OaConstants {

    /*
    * session中用于存放当前用户对象的属性名称
    * */
    public static final String SESSION_NAME_CURRENT_USER = "SESSION_NAME_CURRENT_USER";
}

五. 开发登陆的业务类

public interface LoginService {
    //验证用户账号或密码是否正常,若正确返回表示当前用户的对象,否则抛出业务异常(BusinessException)
    public CurrentUser checkLogin(LoginDto loginDto);
}
@Service
@Transactional
public class LoginServiceImpl implements LoginService {
    @Autowired
    private LoginDao loginDao;
    @Override
    public CurrentUser checkLogin(LoginDto dto) {
        User user = loginDao.findUserByIdAndPwd(dto);
        if (user == null ){
            throw new BusinessException("账号或密码错误!!");
        }
        CurrentUser currentUser = new CurrentUser();
        currentUser.setUserId(user.getU_id());
        currentUser.setUserName(user.getU_name());
        return currentUser;
    }
}

业务实现类中:

两个注解:

①:@Service

②:@Transactional

要对当前有没有用户做出判断,没有的话抛出异常,到Controller里面再由专门处理异常的类去处理。有的话,把查到的用户(User)封装到当前用户(CurrentUser)里面。

六. 开发持久化组件

import com.xupt.ygq.oa.dto.LoginDto;
import com.xupt.ygq.oa.model.User;
import org.apache.ibatis.annotations.Select;

public interface LoginDao {
    @Select("select u_id,u_name from t_user where u_id=#{u_id} and u_pwd=#{u_pwd}")
    public User findUserByIdAndPwd(LoginDto dto);
}

七. 开发登录前端(vue+ajax)

<script>
    //1.定义登陆数据
    const loginData = {
        u_id:'',//账号
        u_pwd:''//密码
    };
    //2.定义登陆函数
    const login = () =>{
        //向服务端发送post请求,提交数据为loginData
        //只有成功的情况下的success=true,同时code值为200时,才能执行then
        ajax.post("/security/login",loginData).then(result=>{
            //这里意味着成功
            //then表示提示信息显示完成后在进行的动作
            xTip.success('登陆成功').then(()=>{
                window.location.href='home.html';
            });
        });
    };
    const cfg = {
        setup() {
            return {
                //3.导出数据在页面上使用
                loginData,
                login,
            };
        }
    };
    Vue.createApp(cfg).use(XModal).use(XPagination).mount('#app');
</script>
<!-- 4. 将登录数据与账号框和密码框绑定 -->
<div class="form-floating">
            <input  type="text" class="form-control" placeholder="账号" v-model="loginData.u_id">
            <label>账号:</label>
        </div>
        <div class="form-floating">
            <input  type="password" class="form-control" placeholder="密码" v-model="loginData.u_pwd">
            <label>密码:</label>
        </div>
 <!-- 5. 在登录按钮上设置事件绑定函数login -->
        <button  class="w-100 btn btn-lg btn-primary" type="button" @click="login">登录</button>

八. 动态菜单的开发

8.1 首先看数据库查出来的数据

 明显的p代表的是父菜单,f代表子菜单

8.2 定义封装查询结果的model

public class Fun {
    private Integer p_id;
    private String p_name;
    private Integer f_id;
    private String f_name;
    private String f_url;

    public Integer getP_id() {
        return p_id;
    }

    public void setP_id(Integer p_id) {
        this.p_id = p_id;
    }

    public String getP_name() {
        return p_name;
    }

    public void setP_name(String p_name) {
        this.p_name = p_name;
    }

    public Integer getF_id() {
        return f_id;
    }

    public void setF_id(Integer f_id) {
        this.f_id = f_id;
    }

    public String getF_name() {
        return f_name;
    }

    public void setF_name(String f_name) {
        this.f_name = f_name;
    }

    public String getF_url() {
        return f_url;
    }

    public void setF_url(String f_url) {
        this.f_url = f_url;
    }
}

8.3 定义封装树状菜单结构的bean

//封装树状菜单结构的bean
public class MenuDto {
    private Integer menuId;
    private String menuName;
    private String menuUrl;

    //子菜单集合
    private List<MenuDto> chidren;

    public Integer getMenuId() {
        return menuId;
    }

    public void setMenuId(Integer menuId) {
        this.menuId = menuId;
    }

    public String getMenuName() {
        return menuName;
    }

    public void setMenuName(String menuName) {
        this.menuName = menuName;
    }

    public String getMenuUrl() {
        return menuUrl;
    }

    public void setMenuUrl(String menuUrl) {
        this.menuUrl = menuUrl;
    }

    public List<MenuDto> getChidren() {
        return chidren;
    }

    public void setChidren(List<MenuDto> chidren) {
        this.chidren = chidren;
    }
}

8.4 开发控制层

@RestController
@RequestMapping("/security/home")
public class HomeController {
    @Autowired
    private HomeService homeService;
    @GetMapping("/menu")
    public Result<List<MenuDto>> userMenuList(HttpSession session){
        //sesssion中取出来的数据都是object的
        CurrentUser currentUser = (CurrentUser) session.getAttribute(OaConstants.SESSION_NAME_CURRENT_USER);
        List<MenuDto> menuDtoList = homeService.getUserMenuList(currentUser.getUserId());
        return Result.ok(menuDtoList);
    }

}

数据是由用户id查出来的,所以需要从session中取出当前用户传给业务层。

8.5 开发业务层

import com.xupt.ygq.oa.dto.MenuDto;

import java.util.List;

public interface HomeService {
    public List<MenuDto> getUserMenuList(String userId);
}

@Service
@Transactional
public class HomeServiceImpl implements HomeService {
    @Autowired
    private HomeDao homeDao;

    @Override
    public List<MenuDto> getUserMenuList(String userId) {
        List<Fun> funList = homeDao.findUserFunList(userId);
        //主菜单集合
        List<MenuDto> mainList = new ArrayList<>();
        MenuDto currentMainMenu = null;//当前主菜单
        for (Fun fun:funList){
            if (currentMainMenu == null || !fun.getP_id().equals(currentMainMenu.getMenuId())){
                /*如果当前主菜单不存在,或者当前遍历的数据中的父菜单编号与当前主菜单编号不相符
                应当新创建一个当前主菜单,并放入主菜单集合*/
                currentMainMenu = new MenuDto();
                currentMainMenu.setMenuId(fun.getP_id());//设置主菜单编号
                currentMainMenu.setMenuName(fun.getP_name());//设置主菜单名称
                currentMainMenu.setChidren(new ArrayList<MenuDto>());//设置子菜单集合
                mainList.add(currentMainMenu);
            }
            //将当前遍历数据中的子菜单部分分离出来,封装到MenuDto对象中,并将该对象放入当前主菜单的子菜单集合中
            MenuDto subMenu = new MenuDto();
            subMenu.setMenuId(fun.getF_id());
            subMenu.setMenuName(fun.getF_name());
            subMenu.setMenuUrl(fun.getF_url());
            currentMainMenu.getChidren().add(subMenu);
        }
        /*funList.forEach(fun->{
            if (currentMainMenu == null || !fun.getP_id().equals(currentMainMenu.getMenuId())){
                *//*
                * 如果当前主菜单不存在,或者当前遍历的数据中的父菜单编号与当前主菜单编号不相符
                * 应当新创建一个当前主菜单,并放入主菜单集合
                * *//*
                currentMainMenu = new MenuDto();//内部类不能访问外部的局部变量
            }
        });*/
        return mainList;
    }
}

业务类里面要把查询到的结果封装成MenuDto对象,再由控制器带着MenuDto返回给前端。

8.6 开发持久层

import com.xupt.ygq.oa.model.Fun;

import java.util.List;

public interface HomeDao {
    public List<Fun> findUserFunList(String userId);
}

8.7 编写映射文件

<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xupt.ygq.oa.fun.security.dao.HomeDao">
    <select id="findUserFunList" resultType="com.xupt.ygq.oa.model.Fun">
        select
            p.f_id p_id,
            p.f_name p_name,
            c.f_id,
            c.f_name,
            c.f_url
        from
            t_fun c join t_fun p on c.f_pid = p.f_id
                    join t_rf rf on c.f_id = rf.f_id
                    join t_role r on rf.ro_id = r.ro_id
                    join t_ur ur on r.ro_id = ur.ro_id
        where
            c.f_ismenu = 1 and ur.u_id = #{userId}
    </select>
</mapper>

8.8 编写前端

<script>
    //1.定义菜单数据
    const menuList = Vue.ref([]);

    //2.定义获取菜单数据的函数
    const getMenuList =()=>{
        ajax.get('/security/home/menu').then(result=>{
            menuList.value = result.data;
        });
    };
    //3.执行获取菜单数据的函数
    getMenuList();
    const cfg = {
        setup(){
            
            return {
                menuList,//4.导出菜单数据(在页面上使用)
            };
        }
    };
    Vue.createApp(cfg).use(XModal).use(XPagination).mount('#app');
</script>
<li v-for="main in menuList" class="mb-1">
                        <button class="btn btn-toggle align-items-center rounded collapsed" data-bs-toggle="collapse"
                                :data-bs-target=" '#main-' + main.menuId" aria-expanded="false">
                           {{main.menuName}}
                        </button>
                        <div class="collapse" :id=" 'main-' + main.menuId">
                            <ul class="btn-toggle-nav list-unstyled fw-normal pb-1 small">
                                <li v-for="sub in main.chidren">
                                    <a :href="sub.menuUrl" target="mainFrame" class="link-dark rounded">
                                        {{sub.menuName}}
                                    </a>
                                </li>
                            </ul>
                        </div>
                    </li>

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值