需求分析和设计

产品原型

字段名称 输入要求 提示信息
账号 必填,唯一 账号必须是唯一的
员工姓名 必填 请输入员工姓名
手机号 必填,合法11位手机号 手机号为合法的11位手机号码
性别 必选 男 / 女
身份证号 可选,合法18位身份证号码 身份证号为合法的18位身份证号码

图片

接口设计

图片
本项目约定

  1. 管理端发出的请求,统一使用/admin作为前缀
  2. 用户端发出的请求,统一使用/user作为前缀

代码开发

根据新员工接口设计对应的DTO:

⚠️ 注意:当前端提交的数据和实体中对应的属性差别比较大时,建议使用DTO来封装数据

EmployeeController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 新增员工
* 因为提交过来的employeeDTO是json格式 所以要添加 @RequestBody
* @param employeeDTO
* @return
*/
@PostMapping
@ApiOperation(value = "新增员工")
public Result save(@RequestBody EmployeeDTO employeeDTO) {
log.info("新增员工: {}", employeeDTO);//{}算是一个占位符
employeeService.save(employeeDTO);
return Result.success();
}

EmployeeServicelmpl.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* 新增员工业务方法
* @param employeeDTO
*/
public void save(EmployeeDTO employeeDTO) {
//将emploeeDTO转换为employee实体对象
Employee employee=new Employee();
//开始设置属性
//对象属性拷贝 要求两个对象的属性名要一样 在employeeDTO中是name则在employee中也应该是name
BeanUtils.copyProperties(employeeDTO,employee);//将employeeDTO里面的内容拷贝到employee中
//因为employee中的属性更多一点 所以需要设置其他的属性值
//设置状态
employee.setStatus(StatusConstant.ENABLE);//设置状态 1-激活 0-停用 为了方便管理就用StatusConstant中定义的常量
//设置密码 默认密码
employee.setPassword(DigestUtils.md5DigestAsHex(PasswordConstant.DEFAULT_PASSWORD.getBytes()));
//设置修改时间
employee.setCreateTime(LocalDateTime.now());
employee.setUpdateTime(LocalDateTime.now());
//设置修改人
// TODO 后期需要获得当前登录的用户的id
employee.setCreateUser(10L);
employee.setUpdateUser(10L);

//调用持久层将这条数据插入
employeeMapper.insert(employee);
}

EmployeeMapper.java

1
2
3
4
5
6
7
8
/**
* 插入员工数据 因为是一个单表的插入操作 没必要将这条sql写进一个xml文件中了
* @param employee
*/
@Insert("insert into employee (name, username, password, phone, sex, id_number, create_time, updata_time, create_user, update_user, status) "+
"values "+
"(#{name},#{username},#{password},#{phone},#{sex},#{idNumber},#{createTime},#{updateTime},#{createUser},#{updateUser},#{status})")
void insert(Employee employee);

代码完善

  1. 录入用户名已存在,抛出异常后没有处理
    GlobalExceptionHandler.class
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    /**
    *处理账号已存在的异常
    * @param ex
    * @return
    */
    @ExceptionHandler
    public Result exceptionHandler(SQLIntegrityConstraintViolationException ex){
    //Duplicate entry 'xiaoluo' for key 'employee.idx_username'
    String message = ex.getMessage();
    if(message.contains("Duplicate entry")){
    String[] split = message.split(" ");
    String msg=split[2]+ MessageConstant.ALREADY_EXISTS;
    return Result.error(msg);
    }else{
    return Result.error(MessageConstant.UNKNOWN_ERROR);
    }
    }
  2. 创建员工时,创建人id和修改人id
    员工登录成功后会生成JWT令牌,并响应给前端
    在后续的请求中,前端会携带JWT令牌,通过JWT令牌可以解析出当前登录的员工id
    JwtTokenAdminInterceptor.class
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    //1、从请求头中获取令牌
    String token = request.getHeader(jwtProperties.getAdminTokenName());
    //2、校验令牌
    try {
    log.info("jwt校验:{}", token);
    Claims claims = JwtUtil.parseJWT(jwtProperties.getAdminSecretKey(), token);
    Long empId = Long.valueOf(claims.get(JwtClaimsConstant.EMP_ID).toString());
    log.info("当前员工id:", empId);
    //将emptd存入到存储空间中 存入线程中
    BaseContext.setCurrentId(empId);
    //3、通过,放行
    return true;
    } catch (Exception ex) {
    //4、不通过,响应401状态码
    response.setStatus(401);
    return false;
    }
    解析出的登录员id后,如何传递给Service的save方法?
    ThreadLocal 并不是一个Treal,而是Treal的局部变量
    ThrealLocal 为每一个线程都提供了一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问。

    客户端发起的每一次请求都是一个单独的线程
    只要是在一个线程生命周期内就可以共享一个 存储空间

ThreadLocal常用方法 解释
public void set(T value) 设置当前线程的线程局部变量的值
public T get() 返回当前线程对应的线程局部的变量的值
public void remove() 移除当前线程局部变量

EmployeeServicelmpl.class

1
2
3
//将empid拿出来
employee.setCreateUser(BaseContext.getCurrentId());
employee.setUpdateUser(BaseContext.getCurrentId());