从零学习一个基于Springboot的权限管理系统(三)用户管理

用户相关接口实现

一、用户分页数据

1.配置mybatis分页插件

利用mybatis本身的拦截器机制对beforeQuery方法进行了实现,根据指定的方言类型拼接上分页sql语句和对应的参数映射

 

java

代码解读

复制代码

@Configuration @EnableTransactionManagement//激活spring事务管理器 public class MybatisConfig { /** * 分页插件和数据权限插件 */ @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); //数据权限拦截器 interceptor.addInnerInterceptor(new DataPermissionInterceptor(new MyDataPermissionHandler())); //分页插件 interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } /** * 自动填充数据库创建人、创建时间、更新人、更新时间 */ @Bean public GlobalConfig globalConfig() { GlobalConfig globalConfig = new GlobalConfig(); globalConfig.setMetaObjectHandler(new MyMetaObjectHandler()); return globalConfig; } }

数据权限以及自动填充数据库某些公有字段后续再进行解释

2.定义分页响应结构体PageResult以及视图对象UserPageVO

  • 因为定义的是分页的响应结构,所以还需要在PageResult中添加一个构造。接收IPage<T>类型的参数。并将前端所需要的total以及list封装给PageResult对象

整理了一份Java面试题。包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题

 需要全套面试笔记的【点击此处】即可免费获取

java

代码解读

复制代码

@Data public class PageResult<T> implements Serializable { private String code; private Data<T> data; private String msg; public static <T> PageResult<T> success(IPage<T> page) { PageResult<T> result = new PageResult<>(); result.setCode(ResultCode.SUCCESS.getCode()); Data data = new Data<T>(); data.setList(page.getRecords()); data.setTotal(page.getTotal()) result.setData(data); result.setMsg(ResultCode.SUCCESS.getMsg()); return result; } @Data public static class Data<T> { private List<T> list; private long total; } }

  • 定义返回给前端的VO对象 (代码省略)需要注意的是可以添加@Schema注解,为接口文档添加描述。
  • 定义接收前端传入的query参数对象 UserPageQuery 注意添加@DateTimeFormat(pattern = "yyyy-MM-dd")注解以及接口文档的解释注解 (如果传入的是Json 还需要注意使用@JsonFormat注解)
  • SysUserController--->SysUserService--->SysUserServiceImpl
    • 根据前端传入的query参数封装成mybatis-plus的Page对象
    • 格式化为数据库的日期格式,避免日期比较格式化函数导致索引失效
    • 传入Page对象以及query参数调用mapper层进行查询
    • 查询出来的对象一般封装为BO
    • 利用MapStruct实现对象之间赋值(过于复杂的 直接set就好 推荐博客文章 blog.csdn.net/yangshangwe…)
    • 返回page给前端
    controller部分代码
 

java

代码解读

复制代码

@Operation(summary = "用户分页列表") @GetMapping("/page") //返回一个分页查询对象 泛型为UserPageVO public PageResult<UserPageVO> listPagedUsers( UserPageQuery queryParams ) { //返回mybatis-plus分页插件类型的接口 IPage<UserPageVO> result = userService.listPagedUsers(queryParams); return PageResult.success(result); }

service部分代码

 

java

代码解读

复制代码

@Override public IPage<UserPageVO> listPagedUsers(UserPageQuery queryParams) { // 参数构建 int pageNum = queryParams.getPageNum(); int pageSize = queryParams.getPageSize(); Page<UserBO> page = new Page<>(pageNum, pageSize); // 格式化为数据库日期格式,避免日期比较使用格式化函数导致索引失效 DateUtils.toDatabaseFormat(queryParams, "startTime", "endTime"); // 查询数据 Page<UserBO> userPage = this.baseMapper.listPagedUsers(page, queryParams); // 实体转换 return userConverter.toPageVo(userPage); }

二、用户信息的增删改操作

新增与修改

mybatis-plus提供了类似于saveOrUpdate这种方法 所以一般service层写到一起进行判断

新增与修改返��值都是boolean,表单提交上来的参数也都要进行校验@Valid(@NotNull @NotBlank等)。

1.逻辑实现
  • 根据前端传入的user_id判断是新增还是修改
  • 数据库查询用户名是否唯一
  • 新增时添加密码编码器对明文密码进行加密
  • 使用mapstruct将表单对象转换为entity
  • 调用saveOrUpdate方法 插入到sys_user表
  • 保存用户与角色相关的信息 插入到sys_user_role
  • 如果是修改 还需要删除已经不生效的关联信息
 

java

代码解读

复制代码

@Override @Transactional(rollbackFor = Exception.class) public boolean saveUserRoles(Long userId, List<Long> roleIds) { if (userId == null || CollectionUtil.isEmpty(roleIds)) { return false; } // 用户原角色ID集合 List<Long> userRoleIds = this.list(new LambdaQueryWrapper<SysUserRole>() .eq(SysUserRole::getUserId, userId)) .stream() .map(SysUserRole::getRoleId) .collect(Collectors.toList()); // 新增用户角色 List<Long> saveRoleIds; if (CollectionUtil.isEmpty(userRoleIds)) { saveRoleIds = roleIds; } else { saveRoleIds = roleIds.stream() .filter(roleId -> !userRoleIds.contains(roleId)) .collect(Collectors.toList()); } List<SysUserRole> saveUserRoles = saveRoleIds .stream() .map(roleId -> new SysUserRole(userId, roleId)) .collect(Collectors.toList()); this.saveBatch(saveUserRoles); // 删除用户角色 if (CollectionUtil.isNotEmpty(userRoleIds)) { List<Long> removeRoleIds = userRoleIds.stream() .filter(roleId -> !roleIds.contains(roleId)) .collect(Collectors.toList()); if (CollectionUtil.isNotEmpty(removeRoleIds)) { this.remove(new LambdaQueryWrapper<SysUserRole>() .eq(SysUserRole::getUserId, userId) .in(SysUserRole::getRoleId, removeRoleIds) ); } } return true; }

2.防止重复提交

利用redisson的分布式锁以及aop实现的防止重复提交

DuplicateSubmitAspect切面通过在被PreventRepeatSubmit注解标记的方法执行前尝试获取一个基于请求信息和JWT Token的分布式锁,有效地防止了重复提交。如果锁获取失败,表明有其他实例正在处理相同请求,从而阻止了重复执行,确保了操作的原子性和一致性

 

java

代码解读

复制代码

@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited public @interface PreventRepeatSubmit { /** * 锁过期时间(秒) * <p> * 默认5秒内不允许重复提交 */ int expire() default 5; }

 

java

代码解读

复制代码

@Aspect @Component @Slf4j @RequiredArgsConstructor public class DuplicateSubmitAspect { private final RedissonClient redissonClient; private static final String RESUBMIT_LOCK_PREFIX = "LOCK:RESUBMIT:"; /** * 防重复提交切点 */ @Pointcut("@annotation(preventRepeatSubmit)") public void preventDuplicateSubmitPointCut(PreventRepeatSubmit preventRepeatSubmit) { log.info("定义防重复提交切点"); } @Around("preventDuplicateSubmitPointCut(preventRepeatSubmit)") public Object doAround(ProceedingJoinPoint pjp, PreventRepeatSubmit preventRepeatSubmit) throws Throwable { String resubmitLockKey = generateResubmitLockKey(); if (resubmitLockKey != null) { int expire = preventRepeatSubmit.expire(); // 防重提交锁过期时间 RLock lock = redissonClient.getLock(resubmitLockKey); boolean lockResult = lock.tryLock(0, expire, TimeUnit.SECONDS); // 立刻尝试获取锁;失败,直接返回 false if (!lockResult) { throw new BusinessException(ResultCode.REPEAT_SUBMIT_ERROR); // 抛出重复提交提示信息 } } return pjp.proceed(); } /** * 获取重复提交锁的 key */ private String generateResubmitLockKey() { String resubmitLockKey = null; HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); String token = request.getHeader(HttpHeaders.AUTHORIZATION); if (StrUtil.isNotBlank(token) && token.startsWith(SecurityConstants.JWT_TOKEN_PREFIX)) { token = token.substring(SecurityConstants.JWT_TOKEN_PREFIX.length()); // 从 JWT Token 中获取 jti String jti = (String) JWTUtil.parseToken(token).getPayload(RegisteredPayload.JWT_ID); resubmitLockKey = RESUBMIT_LOCK_PREFIX + jti + ":" + request.getMethod() + "-" + request.getRequestURI(); } return resubmitLockKey; } }

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
很高兴为您提供这份SpringBoot的气象服务用户管理系统的需求文档。 1.系统介绍 本系统是一个基于SpringBoot的气象服务用户管理系统,旨在为气象服务所提供的用户提供一个便利的管理平台,包括用户注册、登录、信息修改、密码找回等功能。 2.系统功能 2.1 用户注册 用户可以通过注册功能注册自己的账号,输入用户名、密码、邮箱等信息,系统会对输入的信息进行验证,确保信息的正确性和完整性。 2.2 用户登录 已经注册的用户可以通过登录功能登录系统,输入用户名和密码即可进入系统,如果输入的信息有误,系统会给出相应的提示。 2.3 用户信息修改 用户可以通过用户信息修改功能修改自己的个人信息,包括用户名、密码、邮箱等,系统会对修改后的信息进行验证,确保信息的正确性。 2.4 密码找回 如果用户忘记了自己的密码,可以通过密码找回功能找回密码,用户需要输入自己注册时填写的邮箱地址,系统会向该邮箱发送一封包含密码重置链接的邮件,用户可以通过该链接重置密码。 3.系统架构 本系统采用SpringBoot框架进行开发,使用MySQL作为数据库,采用Maven进行依赖管理,使用Thymeleaf作为模板引擎,使用Bootstrap进行页面设计,使用SpringSecurity进行登录验证。 4.技术选型 4.1 SpringBoot SpringBoot一个基于Spring框架的快速开发框架,它提供了一系列的开箱即用的特性和约定,让开发人员能够更快、更方便地搭建一个可靠、高效的Web应用程序。 4.2 MySQL MySQL是一个开源的关系型数据库管理系统,它提供了高效的数据存储和查询功能,支持多种操作系统和编程语言,是Web应用程序常用的数据库之一。 4.3 Maven Maven是一个基于Java的项目管理工具,它提供了一系列的命令行工具和插件,可以自动化完成项目的编译、测试、打包和发布等过程,大大提高了开发效率。 4.4 Thymeleaf Thymeleaf是一个Java模板引擎,它能够将模板文件和数据源进行结合,生成最终的HTML文件,支持多种模板语法和标签库,是Web应用程序常用的模板引擎之一。 4.5 Bootstrap Bootstrap是一个开源的前端框架,它提供了丰富的UI组件和样式,可以快速搭建一个美观、响应式的Web界面,是Web应用程序常用的前端框架之一。 4.6 SpringSecurity SpringSecurity是一个基于Spring框架的安全框架,它提供了一系列的安全功能和过滤器,可以保护Web应用程序的安全性,支持多种身份验证和授权机制,是Web应用程序常用的安全框架之一。 5.总结 本文介绍了一个基于SpringBoot的气象服务用户管理系统的需求文档,包括系统功能、系统架构、技术选型等内容,希望对您的开发工作有所帮助。如果您需要更详细的内容或有任何疑问,请随时联系我们。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值