Spring Boot 实现员工信息管理demo

Spring Boot 实现员工信息管理demo

目录

员工管理系统DEMO

一、安装lombok插件

这里我们使用lombok帮助我们自动生成pojo包的getter和setter等函数结构

在IDEA中安装lombok插件

等待插件安装完成后,在项目pom中导入lombok

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>

二、构建数据库(模拟)

在整合mybets之前,我们手动构建pojo和dao层进行模拟数据库

使用相应的注解来完善pojo结构

  • @Data 注解用于生成属性的getter与setter
  • @AllArgsConstructor 用于生成有参构造函数
  • @NoArgsConstructor 用于生成无参构造函数

0x01 pojo层

Department.java

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Department {
    private Integer id;
    private String departmentName;
}

Employee.java

@NoArgsConstructor
@AllArgsConstructor
@Data
public class Employee {
    private Integer id;
    private String lastName;
    private String email;
    private Integer gender;
    private Department department;
    private Date birth;
}

0x02 dao层

二、登录页面实现

在自定义MVC配置中重写一个 addViewControllers ,用来添加一些基本的视图控制器

@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("index");
        registry.addViewController("/index.html").setViewName("index");
        registry.addViewController("/index/").setViewName("index");
    }
}

测试访问 http://localhost:8080/index

三、页面国际化配置(多语言)

0x01 添加配置文件

设置IDEA的文件编码

在 resources 目录下创建 i18n 目录,用来存放多语言的配置文件,并创建如下文件结构

login.properties 是我们的主配置文件, login_en_XX.properties 是我们的指定的各个语言的配置文件

点击可视化配置 Resource Bundle ,添加一个配置键值对,这里我们命名login.tip

在编辑器的右边输入对 login.tip 进行各个语言的配置,会分别映射到我们刚才创建好的几个语言的配置文件中

按照如上步骤,我们把页面的几个标签对应的也配置好

在配置文件 application.properties 中绑定多语言配置文件位置

spring.messages.basename=i18n.login

0x02 替换HTML中的标签

使用 thymeleaf 模板语法中的 th:XX="#{}" 对各个标签进行接管,例如

<label class="sr-only" th:text="#{login.username}">Username</label>

#{} 中填写刚才我们在配置文件中添加的键

0x03 自定义一个转换器

在config中自定义一个转换器 MyLocaleResolver ,根据用户请求的参数进行设置页面的语言

public class MyLocaleResolver implements LocaleResolver {

    //解析请求
    @Override
    public Locale resolveLocale(HttpServletRequest request) {
        //获取请求中的语言参数
        String language = request.getParameter("lang");
        Locale locale = Locale.getDefault(); //如果没有就使用默认的

        //如果请求的链接携带了国际化的参数
        if (!StringUtils.isEmpty(language)){
            //分割字符串
            String[] lang_split = language.split("_");
        	locale = new Locale(lang_split[0], lang_split[1]);
    	}
        return locale;
}

    @Override
    public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {

    }
}

在我们之前自定义的mvc类中,使用 @Bean 注解将我们的转换器 MyLocaleResolver 配置到spring容器内

@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("index");
        registry.addViewController("/index.html").setViewName("index");
        registry.addViewController("/index/").setViewName("index");
    }

    //将自定义的国际化组件注册到spring bean中
    @Bean
    public LocaleResolver localeResolver(){
        return new MyLocaleResolver();
    }
}

接下来在HTML模板中使用thymeleaf模板语法中的 th:href="@{xxx.html(key=value)}",设置跳转请求

<a class="btn btn-sm" th:href="@{/index.html(lang='zh_CN')}">中文</a>
<a class="btn btn-sm" th:href="@{/index.html(lang='en_US')}">English</a>

重新启动项目,查看效果

成功实现多语言的切换

四、登录功能实现

0x01 接口测试

创建一个Controller,我这里命名为 LoginController ,编写基本的结构,返回一个字符串进行测试

@Controller
public class LoginController {
    @RequestMapping("/user/login")
    public String Login(){
        return "OK";
    }
}

在HTML中提交的表单中修改请求url

   <form class="form-signin" action="/user/login" method="POST">

测试

0x02 编写登录逻辑

@Controller
public class LoginController {

    @RequestMapping("/user/login")
    public String Login(
            @RequestParam("username") String username,
            @RequestParam("password") String password,
            Model model
    ){
        if(("admin".equals(username) && "123123".equals(password))){
            session.setAttribute("loginUser", username);
            //重定向至main页面
            return "redirect:/main";
        }else{  
            model.addAttribute("msg", "用户名或密码错误");  //将错误信息渲染至页面
            return "index";
        }
    }
}

这里暂时没有整合数据库,所以直接判断页面提交的值是否等于预定义的值,如果等于则重定向到main页面,否则返回登录页并渲染错误信息

前端页面新增一个p标签用于显示错误信息,使用 thymeleaf 模板引擎进行渲染

<!--如果msg值为空则不显示错误信息-->
<p style="color: red" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>

添加一个main页面的路由,这里我在自定义的mvc配置类中重写了一个视图控制器,也可以自己新增一个controller进行路由

registry.addViewController("/main").setViewName("dashboard");

0x03 添加登录拦截器

自定义一个拦截器LoginHandlerInterceptor,并实现 HandlerInterceptorpreHandle方法,自定义拦截的逻辑

public class LoginHandlerInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //拦截逻辑
        Object loginUser = request.getSession().getAttribute("loginUser");
        if(loginUser == null){
            //设置提示信息
            request.setAttribute("msg","没有权限,请先登录");
            //跳转回到主页
            request.getRequestDispatcher("/").forward(request, response);
            return false;
        }
        return true;
    }
}

在我们自定义的springmvc配置类中注册我们的拦截器 LoginHandlerInterceptor

//注册拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
    //设置拦截的黑名单和白名单
    //我们还要给静态资源设置白名单,不然显示不出来
    registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**")
            .excludePathPatterns("/", "/index*","/user/login", "/css/**", "/img/**", "/js/**");
}

登录成功后取session中的用户名显示到main页面

<a>[[${session.loginUser}]]</a>

0x04 测试

测试拦截器、登录验证、主页显示

五、展示员工信息

0x01 定义页面模板

我们从main页面种可以看出,页面的顶部栏和侧边栏的样式的固定的,所以我们可以将这两个部分独立成模块,在创建新的页面时我们可以直接复用预定义好的模块,减少代码量

我们新建一个 base.html 的页面,用于定义我们模块的代码,使用 th:fragment 标签定义模块的名称,在其他页面使用 th:insertth:replace 引用模板

  • th:insert :保留自己的主标签,保留th:fragment的主标签。
  • th:replace :不要自己的主标签,保留th:fragment的主标签。

定义模板

<!--顶部栏-->
<nav th:fragment="topBar" class="navbar"></nav>
<!--侧边栏-->
<nav th:fragment="leftBar" class="col-md-2"></nav>

引用模板

<!--顶部栏-->
<div th:replace="~{commons/dashboard_base :: topBar}"></div>
<!--侧边栏-->
<div th:replace="~{commons/dashboard_base :: leftBar}"></div>

0x02 侧边栏高亮

需求

在点击侧边栏的功能时候,需要在跳转页面后将指定的项高亮显示,例如下图

具体实现

在引用模板时候增加一个页面变量作为标识,如下代码

<div th:replace="~{commons/dashboard_base :: leftBar(barType='emps')}"></div>

在上面的代码当中,我们定义了一个barType的变量,作为页面类型的标识

接下来我们在base模板中对页面传递过来的变量进行识别

<li class="nav-item">
     <a th:class="${barType == 'main'? 'nav-link active': 'nav-link'}" th:href="@{/main}"></a>
 </li>
<li class="nav-item">
   <a th:class="${barType == 'emps'? 'nav-link active': 'nav-link'}" th:href="@{/emps/info}"></a>
</li>

在上面的代码当中,我们使用了thymeleaf 的三元运算符进行渲染,如果传递过来的barType 符合预期设定的值,则在标签的class中增加active实现高亮

0x03 控制器

定义一个Controller,使用Autowired 标识将EmployeeDao注入到当前Controller中,用于实现对employee的pojo的一些操作,调用了getEmpsInfo函数获取所有员工信息的值,并将所有员工信息作为model传递到页面中,代码如下

EmployeeController.java

@Controller
public class EmployeeController {

    @Autowired
    EmployeeDao employeeDao;    //注入

    @GetMapping("/emps/info")
    public String showInfo(Model model){
        Collection<Employee> empsInfo = employeeDao.getEmpsInfo();
        model.addAttribute("empsInfo", empsInfo);
        return "employee/info";
    }
}

在info页面中定义一个table标签,使用 th:each 对controller传递到页面的员工信息进行遍历,并渲染到页面,代码实现如下

emps/info.html

<table class="table table-hover">
    <thead>
        <tr>
            <th>ID</th>
            <th>姓名</th>
            <th>邮箱</th>
            <th>性别</th>
            <th>部门</th>
            <th>生日</th>
            <th>操作</th>
        </tr>
    </thead>
    <tr th:each="emp:${empsInfo}">
        <th th:text="${emp.id}"></th>
        <td th:text="${emp.lastName}">2</td>
        <td th:text="${emp.email}">3</td>
        <td th:text="${emp.gender}">4</td>
        <td th:text="${emp.department.departmentName}">5</td>
        <!--格式化日期-->
        <td th:text="${#dates.format(emp.birth, 'yyyy-MM-dd HH:mm')}"></td>
        <td>
            <button type="button" class="btn btn-primary btn-sm">编辑</button>
            <button type="button" class="btn btn-danger btn-sm">删除</button>
        </td>
    </tr>
</table>

0x05 实现效果

六、添加员工信息

0x01 实现思路

  1. 点击添加员工按钮,提交GET请求至控制器,并携带部门信息渲染至add页面
  2. 用户填写信息,提交POST请求至controller
  3. 控制器将表单提交的employee对象保存至对象数据中

0x02 提交GET请求

/emps/info.html

<h2><a style="color: white;" class="btn btn-sm btn-info" th:href="@{/emps/add}">添加员工</a></h2>

控制器返回add页面,并携带部门名称信息

@Autowired
EmployeeDao employeeDao;    //注入Dao
@Autowired
DepartmentDao departmentDao;

@GetMapping("/emps/add")
    public String addEmp(Model model){
        Collection<Department> departments = departmentDao.getDepartments();    //传递部门信息到前端
        model.addAttribute("departments", departments);
        return "employee/add";
}

add.html页面主体数据

<!--添加员工表单-->
<form th:action="@{/emps/add}" th:method="POST">
   <div class="form-group">
      <label>LastName</label>
      <input name="lastName" type="text" class="form-control" placeholder="b5ck">
   </div>
   <div class="form-group">
      <label>Email</label>
      <input name="email" type="email" class="form-control" placeholder="XXXXX@qq.com">
   </div>
   <div class="form-group">
      <label>Gender</label><br/>
      <div class="form-check form-check-inline">
         <input class="form-check-input " type="radio" name="gender"  value="1" checked="true">
         <label class="form-check-label">男</label>
      </div>
      <div class="form-check form-check-inline">
         <input class="form-check-input" type="radio" name="gender"  value="0">
         <label class="form-check-label">女</label>
      </div>
   </div>
   <div class="form-group">
      <label>department</label>
      <select class="form-control" name="department.id">
         <option th:each="dep:${departments}" th:value="${dep.getId()}" th:text="${dep.getDepartmentName()}"></option>
      </select>
   </div>
   <div class="form-group">
      <label>Birth</label>
      <input NAME="birth" type="text" class="form-control date-cell" placeholder="2020-01-01">
   </div>
   <button type="submit" class="btn btn-primary">添加</button>

页面效果

0x03 提交POST请求

携带用户填写的数据,控制器接收请求

   @PostMapping("/emps/add")
    public String addEmpPost(Employee employee, RedirectAttributes model){
        employeeDao.save(employee);
        model.addFlashAttribute("add",true);
        return "redirect:/emps/info";
//        return "employee/info";
    }

在上述代码中,接收到了用户提交表单的数据,转换为Employee对象,并调用前面代码中注入的employeeDao中的save函数将对象写入现有的数据中。

写入成功后,我们传递一个名称为add的model值至页面,用于标识添加成功,在页面添加相应的提示,因为这里使用的是redirect进行重定向页面,所以需要使用RedirectAttributes对象来传递model值至页面。

在info页面中添加一个提示框标签

<!--添加成功提示框-->
<div th:if="${add == true}" class="alert alert-success alert-dismissable">
   <button type="button" class="close" data-dismiss="alert"
         aria-hidden="true">
      &times;
   </button>
   员工信息添加成功!
</div>

该标签中使用th:if语句对model对象add进行判断,值为true时才对该div标签进行渲染,页面效果如下

controller全部代码

@Controller
public class EmployeeController {
    @Autowired
    EmployeeDao employeeDao;    //注入Dao
    @Autowired
    DepartmentDao departmentDao;

    @GetMapping("/emps/info")
    public String showInfo(Model model){
        Collection<Employee> empsInfo = employeeDao.getEmpsInfo();
        model.addAttribute("empsInfo", empsInfo);
        return "employee/info";
    }

    @GetMapping("/emps/add")
    public String addEmp(Model model){
        Collection<Department> departments = departmentDao.getDepartments();    //传递部门信息到前端
        model.addAttribute("departments", departments);
        return "employee/add";
    }

    @PostMapping("/emps/add")
    public String addEmpPost(Employee employee, RedirectAttributes model){
        employeeDao.save(employee);
        model.addFlashAttribute("add",true);
        return "redirect:/emps/info";
    }
}

七、删除员工信息

0x01 实现思路

  1. 用户点击删除按钮,触发模态框再次确认
  2. 点击删除,发送删除请求,请求路径中包含员工ID值

0x02 实现过程

确认框

<!-- 模态框(Modal) -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
   <div class="modal-dialog">
      <div class="modal-content">
         <div class="modal-header">
            <h4 class="modal-title" id="myModalLabel">确定要删除该员工的信息吗?</h4>
         </div>
         <div class="modal-body">
            <label>员工ID</label>
            <input th:value="${emp.id}" disabled="true" class="form-control" type="text" name="" >
            <label style="margin-top: 10px">员工姓名</label>
            <input th:value="${emp.lastName}"  disabled="true" class="form-control" type="text" name="">
         </div>
         <div class="modal-footer">
            <button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
            <a class="btn btn-danger btn-default" th:href="@{/emps/delete/}+${emp.id}">确定删除</a>
         </div>
      </div><!-- /.modal-content -->
   </div><!-- /.modal -->
</div>

点击删除按钮,触发模态框

<button class="btn btn-danger btn-sm" data-target="#myModal" data-toggle="modal">删除</button>

控制器

//删除员工信息
@GetMapping("/emps/delete/{id}")
public String toDelete(@PathVariable("id") Integer id, RedirectAttributes model) {
    String lastName = employeeDao.getEmployeeById(id).getLastName();
    employeeDao.delete(id);
    model.addFlashAttribute("msg",  "员工 " + lastName + " 信息已被删除");
    return "redirect:/emps/info";
}

测试删除

八、更新员工信息

0x01 实现思路

  1. 修改编辑按钮:INFO页面在渲染时修改编辑按钮的a标签链接,链接包含该行数据的ID值
  2. 渲染update页面:页面包含该id对应的员工信息数据
  3. 提交update请求

0x02 实现过程

修改编辑按钮

使用th:herf渲染链接,并携带当前行的员工的id号

<a class="btn btn-primary btn-sm" th:href="@{/emps/update/}+${emp.id}">编辑</a>

渲染update页面

控制器代码如下

@GetMapping("/emps/update/{id}")
public String updateEmpInfo(@PathVariable("id") Integer id, Model model){
    Employee employeeById = employeeDao.getEmployeeById(id);
    Collection<Department> departments = departmentDao.getDepartments();    //获取所有部门信息
    System.out.println(employeeById);
    model.addAttribute("emp", employeeById);
    model.addAttribute("deps", departments);
    return "employee/update";
}

使用{}在请求路径中取id值,通过@PathVariable 将id值赋值给变量

根据员工id获取该员工的所有信息,获取所有部门的信息,并渲染至页面中;

update.html页面主体代码如下

<!--更新员工表单-->
<form th:action="@{/emps/update/} + ${emp.id}" th:method="POST">
   <div class="form-group">
      <label>LastName</label>
      <input th:value="${emp.lastName}" name="lastName" type="text" class="form-control" placeholder="b5ck">
      </div>
      <div class="form-group">
         <label>Email</label>
         <input th:value="${emp.email}" name="email" type="email" class="form-control" placeholder="123@qq.com">
      </div>
      <div class="form-group">
         <label>Gender</label><br/>
         <div class="form-check form-check-inline">
            <input th:checked="${emp.gender == 1}" class="form-check-input " type="radio" name="gender"  value="1" checked="true">
            <label class="form-check-label">男</label>
      </div>
      <div class="form-check form-check-inline">
          <!-- 自动选中lable -->
         <input th:checked="${emp.gender == 0}" class="form-check-input" type="radio" name="gender"  value="0">
         <label class="form-check-label">女</label>
      </div>
   </div>
   <div class="form-group">
      <label >department</label>
      <select   class="form-control" name="department.id">
         <option th:each="dep:${deps}" th:selected="${emp.department.id == dep.getId()}" th:value="${dep.getId()}" th:text="${dep.getDepartmentName()}"></option>
      </select>
   </div>
   <div class="form-group">
      <label>Birth</label>
      <input th:value="${#dates.format(emp.birth, 'yyyy-MM-dd HH:mm:ss')}" NAME="birth" type="text" class="form-control date-cell" placeholder="2020-01-01">
   </div>
   <button type="submit" class="btn btn-primary">更新</button>
</form>

前端代码与add.html页面相似,不同的是表单中的值使用th:value从控制器中传递的model取出赋值搭到对应的位置供用户修改;

性别lable中使用th:checked="${emp.gender == 1}"gender值进行判断,使得lable标签能自动选中,而部门select下拉框使用th:selected对当前emp对象的部门ID值与dep值相比较,使得option标签能被自动选择。

实现效果

定义一个用于接收修改请求的控制器

@PostMapping("/emps/update/{id}")
public String toUpdateEmpInfo(@PathVariable("id") Integer id, Employee employee, RedirectAttributes model){
     //由于前端传递过来的只有部门ID号,需要重新将部门名称赋值
    employee.setDepartment(departmentDao.getDepartmentsById(employee.getDepartment().getId()));
    employeeDao.updateInfo(id, employee);
    model.addFlashAttribute("msg", employee.getLastName() +" 信息更新成功");
    return "redirect:/emps/info";
}

同样的从URL中取ID值,调用employeeDao中的updateInfo方法进行员工信息的更新。

修改用户AA的邮箱为123@qq.com,部门为后勤部,点击更新。

更新成功!

九、注销

0x01 实现思路

  1. 定义控制器,获取用户session并清空
  2. 在模板页面内添加一个注销按钮
  3. 重定向页面至index

0x02 实现过程

添加控制器

Controller/LoginController.java

@RequestMapping("/user/logout")
public String Logout(HttpSession session){
    session.invalidate();   //清除该session会话信息
    return "redirect:/index";
}

在模板页面 dashboard_base.html 中添加注销按钮样式,并提交请求

模板路径

<a th:href="@{/user/logout}" class="btn btn-warning">Sign out</a>

十、错误页面配置

templates目录下创建一个error目录,创建一个404.html页面,当请求发生404状态时,springboot会自动重定向到404.html页面中


测试404

其他错误状态也是如此,例如500.html403.html

Copyright: 采用 知识共享署名4.0 国际许可协议进行许可

Links: https://codeyee.com/archives/springboot-demo-employee.html