【wiki知识库】07.用户管理后端SpringBoot部分

目录

一、今日目标

二、??SpringBoot部分类的添加

[2.1 使用逆向工程新增User模块](#2.1 使用逆向工程新增User模块)

[2.2 UserQueryParam添加](#2.2 UserQueryParam添加)

[2.3 UserSaveParam添加](#2.3 UserSaveParam添加)

[2.4 UserResetPasswordParam添加](#2.4 UserResetPasswordParam添加)

[2.5 UserQueryVo添加](#2.5 UserQueryVo添加)

[2.6 SnowFlake工具类](#2.6 SnowFlake工具类)

三、??后端新增接口?

[3.1 /user/list接口添加](#3.1 /user/list接口添加)

[3.2 /user/save接口添加](#3.2 /user/save接口添加)

[3.3 /user/delete接口添加](#3.3 /user/delete接口添加)

[3.4 /user/reset-password接口添加](#3.4 /user/reset-password接口添加)


一、今日目标

上一篇文章我把前端部分的代码给大家了,这篇文章就来实现上一篇文章没有完成的接口。

二、??SpringBoot部分类的添加

2.1 使用逆向工程新增User模块

这一块的代码和之前的相同,我们找到逆向工程的工具类后,把类的部分改为user即可。

2.2 UserQueryParam添加

这个类看名字也知道是用来用户查询的,要继承之前的分页类。

复制代码
@Data
public class UserQueryParam extends PageParam {
    private String loginName;
}

2.3 UserSaveParam添加

这个类是用来作为用户新增参数接收用的。这个类作为用户信息保存的参数类,在这个类中对于属性的值做了一些限制。用户名、昵称和密码不能为空,同时密码要匹配正则表达式,这个正则表达式限制了密码由数字和字母组成,并且长度在6-32位,如果上边有条件不满足,那么就会抛出message中的错误。

复制代码
@Data
public class UserSaveParam {
    private Long id;

    @NotNull(message = "【用户名】不能为空")
    private String loginName;

    @NotNull(message = "【昵称】不能为空")
    private String name;

    @NotNull(message = "【密码】不能为空")
    @Pattern(regexp = "^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,32}$", message = "【密码】至少包含 数字和英文,长度6-32")
    private String password;

}

2.4 UserResetPasswordParam添加

这个类用于重置用户密码,传入账号的id还有用户的新密码。同样也做了密码的安全性校验。

复制代码
@Data
public class UserResetPasswordParam {
    private Long id;

    @NotNull(message = "【密码】不能为空")
    @Pattern(regexp = "^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,32}$", message = "【密码】至少包含 数字和英文,长度6-32")
    private String password;
}

2.5 UserQueryVo添加

这个类作为用户查询结果返回。

复制代码
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserQueryVo {
    private Long id;

    private String loginName;

    private String name;

    private String password;

}

2.6 SnowFlake工具类

这个工具类的作用就是生成一个不会重复的id值,想要了解具体内容的小伙伴可以搜索一下雪花算法。现在我先不过多介绍,可能之后会补上这一部分。现在只要记住这个类可以帮你生成一个不会重复的id值就可以了。

复制代码
/**
 * Twitter的分布式自增ID雪花算法
 **/
@Component
public class SnowFlake {

    /**
     * 起始的时间戳
     */
    private final static long START_STMP = 1609459200000L; // 2021-01-01 00:00:00

    /**
     * 每一部分占用的位数
     */
    private final static long SEQUENCE_BIT = 12; //序列号占用的位数
    private final static long MACHINE_BIT = 5;   //机器标识占用的位数
    private final static long DATACENTER_BIT = 5;//数据中心占用的位数

    /**
     * 每一部分的最大值
     */
    private final static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT);
    private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);
    private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);

    /**
     * 每一部分向左的位移
     */
    private final static long MACHINE_LEFT = SEQUENCE_BIT;
    private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
    private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;

    private long datacenterId = 1;  //数据中心
    private long machineId = 1;     //机器标识
    private long sequence = 0L; //序列号
    private long lastStmp = -1L;//上一次时间戳

    public SnowFlake() {
    }

    public SnowFlake(long datacenterId, long machineId) {
        if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) {
            throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");
        }
        if (machineId > MAX_MACHINE_NUM || machineId < 0) {
            throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");
        }
        this.datacenterId = datacenterId;
        this.machineId = machineId;
    }

    /**
     * 产生下一个ID
     *
     * @return
     */
    public synchronized long nextId() {
        long currStmp = getNewstmp();
        if (currStmp < lastStmp) {
            throw new RuntimeException("Clock moved backwards.  Refusing to generate id");
        }

        if (currStmp == lastStmp) {
            //相同毫秒内,序列号自增
            sequence = (sequence + 1) & MAX_SEQUENCE;
            //同一毫秒的序列数已经达到最大
            if (sequence == 0L) {
                currStmp = getNextMill();
            }
        } else {
            //不同毫秒内,序列号置为0
            sequence = 0L;
        }

        lastStmp = currStmp;

        return (currStmp - START_STMP) << TIMESTMP_LEFT //时间戳部分
                | datacenterId << DATACENTER_LEFT       //数据中心部分
                | machineId << MACHINE_LEFT             //机器标识部分
                | sequence;                             //序列号部分
    }

    private long getNextMill() {
        long mill = getNewstmp();
        while (mill <= lastStmp) {
            mill = getNewstmp();
        }
        return mill;
    }

    private long getNewstmp() {
        return System.currentTimeMillis();
    }
}

上边的代码都没有什么难度,下面就开始实现用户管理的逻辑部分。

三、??后端新增接口

在UserController类中需要小小的修改一下。

复制代码
@RestController
@RequestMapping("/user")
public class UserController {
  @Autowired
  private UserService userService;
}

3.1 /user/list接口添加

其实这样的代码已经写了不少了,这样的list查询并没有什么难度,

复制代码
@RequestMapping("/list")
  public CommonResp list(@Validated UserQueryParam userQueryParam){
    PageVo<UserQueryVo> list = userService.list(userQueryParam);
    return new CommonResp(true,"查找成功", list);
  }

UserServiceImpl中的list接口。

复制代码
 public PageVo<UserQueryVo> list(UserQueryParam userQueryParam) {
        // 构建一个表达式来筛选用户
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(StringUtils.isNotBlank(userQueryParam.getLoginName()),
                User::getLoginName,userQueryParam.getLoginName());
        // 创建分页查询的对象
        Page<User> page = new Page<>(userQueryParam.getPage(),userQueryParam.getSize());
        // 接收分页查询的结果
        Page<User> resultPage = userMapper.selectPage(page, queryWrapper);
        // 这个对象用于返回给前端
        PageVo<UserQueryVo> pageVo = new PageVo<>();
        // 将分页查询的结果转换一下 User-》UserQueryVo
        List<UserQueryVo> users = CopyUtil.copyList(resultPage.getRecords(),UserQueryVo.class);
        pageVo.setList(users);
        pageVo.setTotal(resultPage.getTotal());
        return pageVo;
    }

3.2 /user/save接口添加

值得注意的是,在数据库当中,用户的密码我们不在进行明文存储了,我们存储的都是加密后的代码,这里仅仅使用了简单的md5加密算法,实际的加密码算法有很多种类型。

复制代码
@PostMapping("/save")
  public CommonResp save(@Valid @RequestBody UserSaveParam userSaveParam) {
    userSaveParam.setPassword(DigestUtils.md5DigestAsHex(userSaveParam.getPassword().getBytes()));
    boolean save = userService.save(userSaveParam);
    String message = Boolean.TRUE.equals(save) ? "添加成功":"添加失败";
    return new CommonResp<>(save,message,null);
  }

UserServiceImpl中的save接口。

复制代码
 public boolean save(UserSaveParam userSaveParam) {
        User user = CopyUtil.copy(userSaveParam, User.class);
        if (ObjectUtils.isEmpty(userSaveParam.getId())) {
            User userDB = selectByLoginName(userSaveParam.getLoginName());
            if (ObjectUtils.isEmpty(userDB)) {
                // 新增
                user.setId(snowFlake.nextId());
                userMapper.insert(user);
            } else {
                // 用户名已存在
                throw new RuntimeException("用户存在");
            }
        } else {
            // 更新
            user.setLoginName(null);
            user.setPassword(null);
            userMapper.updateById(user);
        }
        return true;
    }

 private User selectByLoginName(String loginName) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("login_name", loginName);
        return userMapper.selectOne(queryWrapper);
    }

3.3 /user/delete接口添加

这一部分就更简单了。

复制代码
  @DeleteMapping("/delete/{id}")
  public CommonResp delete(@PathVariable Long id) {
    boolean res = userService.removeById(id);
    String message = Boolean.TRUE.equals(res) ? "删除成功":"删除失败";
    return new CommonResp<>(res,message,null);
  }

3.4 /user/reset-password接口添加

复制代码
  @PostMapping("/reset-password")
  public CommonResp resetPassword(@Valid @RequestBody UserResetPasswordParam req) {
    req.setPassword(DigestUtils.md5DigestAsHex(req.getPassword().getBytes()));
    userService.resetPassword(req);
    return new CommonResp(true,"密码重置成功",null);
  }

UserServiceImpl中的resetPassword接口。

复制代码
    public void resetPassword(UserResetPasswordParam req) {
        User user = CopyUtil.copy(req, User.class);
        userMapper.updateById(user);
    }
相关推荐
Rust研习社11 小时前
组合真的优于继承吗?为什么 Rust 和 Go 都拥抱组合舍弃继承?
后端·rust·编程语言
IT_陈寒11 小时前
JavaScript的闭包把我坑惨了,说好的内存会自动回收呢?
前端·人工智能·后端
CaffeinePro12 小时前
Pydantic深度使用:数据校验、枚举、ORM映射
后端·fastapi
Chenyiax12 小时前
从 Chat 到 Responses:OpenAI API 抽象为什么变了?
后端
MariaH12 小时前
Koa和Express的区别
后端
MariaH13 小时前
Koa框架的使用
后端
luckdewei14 小时前
那个用 passlib 做认证的新同事,上线第一天就把用户密码写进了日志
后端
ping某15 小时前
为什么 Nginx 明明监听了 80,转发后端时却用了 4xxxx 端口?
后端·nginx
JustHappy15 小时前
我汇总了身边朋友的经历才发现,其实第一份实习是最难找的......
前端·后端·面试
uhakadotcom15 小时前
在python 的 工程化架构中 ,什么是 薄包装器层?
后端·面试·github