黑马Tlias JavaWeb后台管理系统 10 后端实战 员工管理 删改

删除员工

需求

当我们勾选列表前面的复选框,然后点击 “批量删除” 按钮,就可以将这一批次的员工信息删除掉了。也可以只勾选一个复选框,仅删除一个员工信息。

问题:我们需要开发两个功能接口吗?一个删除单个员工,一个删除多个员工

答案:不需要。 只需要开发一个功能接口即可(删除多个员工包含只删除一个员工)

接口文档

**员工管理** -> **删除员工**

思路分析

功能开发

Controller接收参数

EmpController 中增加如下方法 delete ,来执行批量删除员工的操作

  1. 数组

多个参数,默认可以将其封装到一个数组中需要保证前端传递的参数名 与 方法形参名称保持一致

/**
* 批量删除员工
*/
@DeleteMapping
public Result delete(Integer[] ids){
    log.info("批量删除部门: ids={} ", Arrays.asList(ids));
    return Result.success();
}
  1. 集合

也可以将其封装到一个List集合中,需要在集合前面加上@RequestParam注解

/**
* 批量删除员工
*/
@DeleteMapping
public Result delete(@RequestParam List<Integer> ids){
    log.info("批量删除部门: ids={} ", ids);
    empService.deleteByIds(ids);
    return Result.success();
}

一般会选择集合,因为基于集合操作其中的元素会更加方便

Service

接口类

/**
* 批量删除员工
*/
void deleteByIds(List<Integer> ids);

实现类,删除emp表和emp_expr表中的信息

@Transactional
@Override
public void deleteByIds(List<Integer> ids) {
    //1. 根据ID批量删除员工基本信息
    empMapper.deleteByIds(ids);

    //2. 根据员工的ID批量删除员工的工作经历信息
    empExprMapper.deleteByEmpIds(ids);
}

Mapper

1). 在 EmpMapper 接口中增加 deleteByIds 方法实现批量删除员工基本信息

/**
* 批量删除员工信息
*/
void deleteByIds(List<Integer> ids);

2). 在 EmpMapper.xml 配置文件中, 配置对应的SQL语句

<!--批量删除员工信息-->
<delete id="deleteByIds">
  delete from emp where id in
  <foreach collection="ids" item="id" open="(" close=")" separator=",">
    #{id}
  </foreach>
</delete>

3). 在 EmpExprMapper 接口中增加 deleteByEmpIds 方法实现根据员工ID批量删除员工的工作经历信息

/**
* 根据员工的ID批量删除工作经历信息
*/
void deleteByEmpIds(List<Integer> empIds);

4). 在 EmpExprMapper.xml 配置文件中, 配置对应的SQL语句

<!--根据员工的ID批量删除工作经历信息-->
<delete id="deleteByEmpIds">
  delete from emp_expr where emp_id in
  <foreach collection="empIds" item="empId" open="(" close=")" separator=",">
    #{empId}
  </foreach>
</delete>

测试

发送DELETE请求,加入参数ids,值为38,39,40

控制台SQL语句

前后端联调

汤姆不见了

修改员工

在进行修改员工信息的时候,我们首先先要根据员工的ID查询员工的详细信息用于页面回显展示,然后用户修改员工数据之后,点击保存按钮,就可以将修改的数据提交到服务端,保存到数据库。 具体操作为:

  1. 根据ID查询员工信息

  2. 保存修改的员工信息

查询回显

接口描述

**员工管理** -> **根据ID查询**

思路

在查询回显时,既需要查询出员工的基本信息,又需要查询出该员工的工作经历信息。

我们可以先通过一条SQL语句,查询出指定员工的基本信息,及其员工的工作经历信息。SQL如下:

select e.*,
       ee.id ee_id,
       ee.begin ee_begin,
       ee.end ee_end,
       ee.company ee_company,
       ee.job ee_job
from emp e left join emp_expr ee on e.id = ee.emp_id where e.id = 39;

实现思路

代码实现

1). EmpController 添加 getInfo 用来根据ID查询员工数据,用于页面回显

/**
 * 查询回显
 */
@GetMapping("/{id}")
public Result getInfo(@PathVariable Integer id){
    log.info("根据id查询员工的详细信息");
    Emp emp  = empService.getInfo(id);
    return Result.success(emp);
}

2). EmpService 接口中增加 getInfo 方法

/**
 * 根据ID查询员工的详细信息
 */
Emp getInfo(Integer id);

3). EmpServiceImpl 实现类中实现 getInfo 方法

@Override
public Emp getInfo(Integer id) {
return empMapper.getById(id);
}

4). EmpMapper 接口中增加 getById 方法

/**
 * 根据ID查询员工详细信息
 */
Emp getById(Integer id);

5). EmpMapper.xml 配置文件中定义对应的SQL

<!--自定义结果集ResultMap-->
<resultMap id="empResultMap" type="com.itheima.pojo.Emp">
  <id column="id" property="id" />
  <result column="username" property="username" />
  <result column="password" property="password" />
  <result column="name" property="name" />
  <result column="gender" property="gender" />
  <result column="phone" property="phone" />
  <result column="job" property="job" />
  <result column="salary" property="salary" />
  <result column="image" property="image" />
  <result column="entry_date" property="entryDate" />
  <result column="dept_id" property="deptId" />
  <result column="create_time" property="createTime" />
  <result column="update_time" property="updateTime" />

  <!--封装exprList-->
  <collection property="exprList" ofType="com.itheima.pojo.EmpExpr">
    <id column="ee_id" property="id"/>
    <result column="ee_company" property="company"/>
    <result column="ee_job" property="job"/>
    <result column="ee_begin" property="begin"/>
    <result column="ee_end" property="end"/>
    <result column="ee_empid" property="empId"/>
  </collection>
</resultMap>

<!--根据ID查询员工的详细信息-->
<select id="getById" resultMap="empResultMap">
  select e.*,
  ee.id ee_id,
  ee.emp_id ee_empid,
  ee.begin ee_begin,
  ee.end ee_end,
  ee.company ee_company,
  ee.job ee_job
  from emp e left join emp_expr ee on e.id = ee.emp_id
  where e.id = #{id}
</select>

在这种一对多的查询中,我们要想成功的封装的结果,需要手动的基于 <resultMap> 来进行封装结果。

Mybatis中封装查询结果,什么时候用 resultType,什么时候用resultMap ?

  • 如果查询返回的字段名与实体的属性名可以直接对应上,用resultType 。
  • 如果查询返回的字段名与实体的属性名对应不上,或实体属性比较复杂,可以通过resultMap手动封装 。

Apifox测试

前后端联调测试

修改员工

查询回显之后,就可以在页面上修改员工的信息了。

  • 当用户修改完数据之后,点击保存按钮,就需要将数据提交到服务端,然后服务端需要将修改后的数据更新到数据库中 。
  • 而此次更新的时候,既需要更新员工的基本信息; 又需要更新员工的工作经历信息 。

接口文档

**员工管理** -> **修改员工**

实现思路

代码实现

1). EmpController 增加 update 方法接收请求参数,响应数据

/**
* 更新员工信息
*/
@PutMapping
public Result update(@RequestBody Emp emp){
    log.info("修改员工信息, {}", emp);
    empService.update(emp);
    return Result.success();
}

2). EmpService 接口增加 update 方法

/**
* 更新员工信息
* @param emp
*/
void update(Emp emp);

3). EmpServiceImpl实现类实现 update 方法

@Transactional
@Override
public void update(Emp emp) {
//1. 根据ID更新员工基本信息
emp.setUpdateTime(LocalDateTime.now());
empMapper.updateById(emp);

//2. 根据员工ID删除员工的工作经历信息 【删除老的】
empExprMapper.deleteByEmpIds(Arrays.asList(emp.getId()));

//3. 新增员工的工作经历数据 【新增新的】
Integer empId = emp.getId();
List<EmpExpr> exprList = emp.getExprList();
if(!CollectionUtils.isEmpty(exprList)){
    exprList.forEach(empExpr -> empExpr.setEmpId(empId));
    empExprMapper.insertBatch(exprList);
}
}

4). EmpMapper 接口中增加 updateById 方法

/**
* 更新员工基本信息
*/
void updateById(Emp emp);

5). EmpMapper.xml 配置文件中定义对应的SQL语句,基于动态SQL更新员工信息

<!--根据ID更新员工信息-->
<update id="updateById">
  update emp
  <set>
    <if test="username != null and username != ''">username = #{username},</if>
    <if test="password != null and password != ''">password = #{password},</if>
    <if test="name != null and name != ''">name = #{name},</if>
    <if test="gender != null">gender = #{gender},</if>
    <if test="phone != null and phone != ''">phone = #{phone},</if>
    <if test="job != null">job = #{job},</if>
    <if test="salary != null">salary = #{salary},</if>
    <if test="image != null and image != ''">image = #{image},</if>
    <if test="entryDate != null">entry_date = #{entryDate},</if>
    <if test="deptId != null">dept_id = #{deptId},</if>
    <if test="updateTime != null">update_time = #{updateTime},</if>
  </set>
  where id = #{id}
</update>

Apifox测试

前后端联调

把广智变成女孩子

异常处理

问题分析

八戒手机号改成已存在的手机号

返回错误结果,状态码为500,表示服务端异常。打开IDEA控制台查看。

上述错误的含义是emp员工标的phone手机号字段的值重复了,我们再设计表的时候为phone字段添加了唯一约束。

响应回来的数据是一个JSON格式的数据,但不是我们开发规范当中提到的统一响应结果Result。所以前端并不能解析出响应的JSON数据。

Mapper接口在操作数据库时出错了,把异常向上抛给Service,Service又把异常抛给Controller,Controller也没对异常做任何处理,最终异常会再往上抛,返回一个不符合我们开发规范的JSON数据。

解决方案

  1. 在所有Controller的所有方法中进行try…catch处理

代码臃肿,不推荐

  1. 全局异常处理器

推荐

全局异常处理器

  • 定义一个类,在类上加上@RestControllerAdvice这个注解,代表我们定义了一个全局处理器。
  • 在全局异常处理器中,需要定义一个方法来捕获异常,在这个方法上需要加上注解@ExceptionHandler,通过他当中的value属性来指定我们要捕获的是哪一类型的异常。
@RestControllerAdvice
public class GlobalExceptionHandler {
    
    //处理异常
    @ExceptionHandler
    public Result ex(Exception e){//方法形参中指定能够处理的异常类型
        e.printStackTrace();//打印堆栈中的异常信息
        //捕获到异常之后,响应一个标准的Result
        return Result.error("对不起,操作失败,请联系管理员");
    }
    
}

@RestControllerAdvice = @ControllerAdvice + @ResponseBody

处理异常的方法返回值会转换为JSON后再响应给前端。

测试

可以看到异常已经被全局异常处理器捕获,返回的错误信息被前端程序正常解析,提示出了对应的错误提示信息。

//全局异常处理器
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    //处理异常
    @ExceptionHandler
    public Result handleException(Exception e) {
        //方法形参中指定能够处理的异常类型
        e.printStackTrace();    //打印堆栈中的异常信息
        //捕获到异常之后,响应一个标准的Result
        log.error("程序出错",e);    //打印日志
        return Result.error("对不起,操作失败,请联系管理员");
    }

    //专门处理重复值异常
    @ExceptionHandler
    public Result handleDuplicateKeyException(DuplicateKeyException e) {
        log.error("程序出错",e);
        String message = e.getMessage();
        int i = message.indexOf("Duplicate entry");
        String errMsg = message.substring(i);
        String[] arr = errMsg.split(" ");
        return Result.error(arr[2] + "已存在");
        //处理返回信息
    }
}

将返回信息转换为字符串数组,以空格进行拆分,arr[2]也就是第三个位置就是我们需要的值。

员工信息统计

员工管理的增删改查功能我们已开发完成,接下来,我们再来完成员工信息统计的接口开发。 对于这些图形报表的开发,其实呢,都是基于现成的一些图形报表的组件开发的,比如:Echarts、HighCharts等。

而报表的制作,主要是前端人员开发,引入对应的组件(比如:ECharts)即可。 服务端开发人员仅为其提供数据即可。

https://echarts.apache.org/zh/index.html

职位统计

需求

通过官方示例,我们需要提供的就是X轴展示的信息和对应的数据。

接口描述

**数据统计** -> **员工职位统计**

代码实现

  1. 定义封装结果对象JobOption
package com.itheima.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class JobOption {
    private List jobList;
    private List dataList;
}
  1. ReportController
@Slf4j
@RequestMapping("/report")
@RestController
public class ReportController {

    @Autowired
    private ReportService reportService;

    /**
     * 统计各个职位的员工人数
     */
    @GetMapping("/empJobData")
    public Result getEmpJobData(){
        log.info("统计各个职位的员工人数");
        JobOption jobOption = reportService.getEmpJobData();
        return Result.success(jobOption);
    }
}
  1. ReportService
public interface ReportService {
    /**
     * 统计各个职位的员工人数
     * @return
     */
    JobOption getEmpJobData();
}
@Service
public class ReportServiceImpl implements ReportService {

    @Autowired
    private EmpMapper empMapper;

    @Override
    public JobOption getEmpJobData() {
        List<Map<String,Object>> list = empMapper.countEmpJobData();
        List<Object> jobList = list.stream().map(dataMap -> dataMap.get("pos")).toList();
        List<Object> dataList = list.stream().map(dataMap -> dataMap.get("total")).toList();
        return new JobOption(jobList, dataList);
    }
}
  1. 定义EmpMapper接口
/**
 * 统计各个职位的员工人数
 */
@MapKey("pos")
List<Map<String,Object>> countEmpJobData();

如果查询的记录往Map中封装,可以通过@MapKey注解指定返回map中的唯一标识是哪个字段。

  1. EmpMapper.xml
<!-- 统计各个职位的员工人数 -->
<select id="countEmpJobData" resultType="java.util.Map">
    select
    (case job when 1 then '班主任' 
     when 2 then '讲师' 
     when 3 then '学工主管' 
     when 4 then '教研主管' 
     when 5 then '咨询师' 
     else '其他' end)  pos,
    count(*) total
    from emp group by job
    order by total
</select>

case流程控制函数:

  • 语法一:case when cond1 then res1 [ when cond2 then res2 ] else res end;

  • 如果cond1成立,取res1;如果cond2成立,取res2。如果前面的条件都不成立,则取res。

  • 语法二(仅适用于等值匹配):case expr when val1 then res1 [when val2 then res2] else res end;

  • 如果expr的值为val1,取res1;如果expr的值为val2,取res2。如果前面的条件都不成立,则取res。

Apifox测试

前后端联调

性别统计

需求

需要提供一个JSON格式的数据。

**数据统计** -> **员工性别统计**

代码实现

1). 在ReportController,添加方法。

/**
 * 统计员工性别信息
 */
@GetMapping("/empGenderData")
public Result getEmpGenderData(){
    log.info("统计员工性别信息");
    List<Map> genderList = reportService.getEmpGenderData();
    return Result.success(genderList);
}

2). 在ReportService接口,添加接口方法。

/**
 * 统计员工性别信息
 */
List<Map> getEmpGenderData();

3). 在ReportServiceImpl实现类,实现方法

@Override
public List<Map> getEmpGenderData() {
    return empMapper.countEmpGenderData();
}

4). 定义EmpMapper 接口

统计的是员工的信息,所以需要操作的是员工表。 所以代码我们就写在 EmpMapper 接口中即可。

/**
 * 统计员工性别信息
 */
@MapKey("name")
List<Map> countEmpGenderData();

5). 定义EmpMapper.xml

<!-- 统计员工的性别信息 -->
<select id="countEmpGenderData" resultType="java.util.Map">
select
if(gender = 1, '男', '女') as name,
count(*) as value
from emp group by gender ;
</select>
  • if:if(条件,为true取值,为false取值)
  • ifnull:ifnull(expr,val1)如果expr不为null,取自身,否则取val1

Apifox测试

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值