1.代码生成器
1.1源码分析
代码生成器分为两个部分:
第一部分涉及将业务表结构导入到系统中
第二部分是点击生成按钮,系统将根据表结构生成相应的前后端代码,并提供下载。
1.表结构说明
gen_table
:存储业务表的基本信息 ,它对应于配置代码基本信息和生成信息的页面
gen_table_column
:存储业务表的字段信息 它对应于配置代码字段信息的页面。
这两张表是一对多的关系,一张业务表可以有多个字段的信息,所以在字段信息表中有个外键table_id指向
2.目录结构
3.查询数据库列表
当管理员在界面上点击导入按钮时,会弹出一个对话框,此时,前端需要向后端发送请求,查询数据库并返回到前端,展示当前项目库中所有待导入的业务表。
<select id="selectDbTableList" parameterType="GenTable" resultMap="GenTableResult">
select table_name, table_comment, create_time, update_time from information_schema.tables
where table_schema = (select database())
AND table_name NOT LIKE 'qrtz_%' AND table_name NOT LIKE 'gen_%'
AND table_name NOT IN (select table_name from gen_table)
<if test="tableName != null and tableName != ''">
AND lower(table_name) like lower(concat('%', #{tableName}, '%'))
</if>
<if test="tableComment != null and tableComment != ''">
AND lower(table_comment) like lower(concat('%', #{tableComment}, '%'))
</if>
<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
AND date_format(create_time,'%y%m%d') >= date_format(#{params.beginTime},'%y%m%d')
</if>
<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
AND date_format(create_time,'%y%m%d') <= date_format(#{params.endTime},'%y%m%d')
</if>
order by create_time desc
</select>
4.导入表结构
当管理员对话框中选中需要导入的业务表,点击确定按钮,此时,前端需要向后端发送请求,保存业务表的基本信息和字段信息
5.生成代码
6.问题分析
(1)每次生成代码都需要修改作者,去除实体类前缀过于繁琐,现在我们可以修改generator.yml
配置文件来调整为自己项目的
(2)实体类支持Lombok,Controller类支持Swagger
需要Velocity模板引擎
1.2Velocity模版引擎
Velocity是一个基于Java的模板引擎,可以通过特定的语法获取在java对象的数据 , 填充到模板中,从而实现界面和java代码的分离 !
1.基础语法
Velocity中的变量有两类
-
在模板中定义变量:
#set
开头,比如#set($name = "velocity")
-
获取变量的的值:
$name
或者${name}
获取对象中的数据
循环
循环遍历普通集合
循环遍历对象
if判断
在条件判断中,velocity支持常见的关系操作符,比如:&&(与), ||(或), !(非)
2.Lombok集成
(1)导入坐标(已完成)
在dkd-common
模块的pom.xml
中添加lombok坐标
(2)修改模板
在dkd-generator
模块的domain.java.vm
模板中添加lombok注解
修改完成之后,重启项目,找到代码生成的功能,通过代码预览可以查看实体类的代码:
3.Swagger集成
(1)修改模板
在dkd-generator
模块的 controller.java.vm
模板中添加Swagger注解
返回结果改为R<>
2.RBAC权限控制
Spring Security是一个功能强大的Java安全框架,它提供了全面的安全认证和授权的支持。
1)认证(Authentication) 在Spring Security的世界里,认证就像用户登录时提交的用户名和密码,系统通过这些信息来验证“你是谁”。
2)授权(Authorization) 授权是确认用户在通过认证之后,是否有权限执行某些操作或访问特定资源。
2.1SpringSecurity配置
Spring Security的配置类是实现安全控制的核心部分
开启Spring Security各种功能,以确保Web应用程序的安全性,包括认证、授权、会话管理、过滤器添加等。
2.2用户登录流程
在ruoyi-framework
模块中com.ruoyi.framework.web.service.SysLoginService
类的login
方法处理登录逻辑
-
验证码校验
-
登录前置校验
-
SS认证管理器用户校验,调用执行UserDetailsServiceImpl.loadUserByUsername
-
认证通过后,创建登录用户对象LoginUser包括用户ID、部门ID、用户信息和用户权限信息
-
-
登录成功,记录日志
-
修改用户表更新登录信息
-
生成token
2.3获取用户角色和权限
在ruoyi-framework
模块中com.ruoyi.framework.web.service.SysPermissionService
类
-
getRolePermission查询该用户角色集合
-
getMenuPermission查询该用户权限(菜单)集合用set集合,一个用户可能有多个角色,多个角色的权限可能有重复的
页面权限
2.4获取动态菜单路由
2.5权限注解
1.源码分析
在若依框架中,权限的验证最核心的是使用的Spring Security的提供的权限注解`@PreAuthorize `
- @PreAuthorize 是 Spring Security 框架中提供的一个安全注解,用于实现基于注解的访问控制。它允许开发者在**方法级别**上声明特定的安全约束,以确保只有满足指定条件的用户才能调用该方法
- 当 @PreAuthorize 注解被应用于某个方法时,Spring Security 在**该方法执行前**会先对当前认证的用户进行权限检查。如果检查通过,方法调用得以继续;否则,框架会抛出相应的权限异常(如 AccessDeniedException),阻止方法执行。
2.权限方法
3.公开接口
如果有些接口是不需要验证权限可以公开访问的,这个时候就需要我们给接口放行。
使用注解方式,只需要在Controller
的类或方法上加入@Anonymous
该注解即可
3.异步任务管理器
主要用于处理一些不需要即时返回结果的后台任务,从而提高应用程序的整体性能
// 多线程执行任务me()创建单例对象(饿汉式)
AsyncManager.me().execute(AsyncFactory.createTimerTask());
若依异步任务管理器是一个单例对象使用了线程池+异步工厂(产生任务用)
-
com.dkd.framework.manager.AsyncManager 异步任务管理器
-
com.dkd.framework.manager.factory.AsyncFactory 异步线程工厂
1、 AsyncManager.me()获取AsyncManager对象
2、调用execute方法,执行TimerTask任务(记录登录日志),它实现了runnable接口,由线程Thread去执行
3、execute方法内部调用ScheduledExecutorService异步操作任务调度线程池的schedule方法用于延迟10毫秒执行一个任务
4.操作日志
在需要被记录日志的controller
方法上添加@Log
注解,使用方法如下:
@Log(title = "订单管理", businessType = BusinessType.INSERT)
public AjaxResult add(...)
{
return toAjax(...);
}
若依操作日志使用了自定义注解+AOP切面+异步任务管理器
-
com.ruoyi.common.annotation.Log 自定义注解
-
com.ruoyi.framework.aspectj.LogAspect 在一个aop切面类
-
通过实现AOP切面编程,对目标方法进行拦截(标注Log注解的方法),实现了操作日志的自动记录
-
异步任务管理器来将任务(记录操作日志到数据库)交给线程池来完成
-
5.定时任务
5.1源码分析
1.表结构说明
sys_job
表(一):这是核心的定时任务表,用于存储定时任务的配置信息,如任务名称、任务组、执行的类全名、执行的参数、cron表达式等
sys_job_log
表(多):用于记录定时任务的执行日志,包括任务的开始执行时间、结束执行时间、执行结果等
2.Quartz体系结构
3.Quartz核心API
4.定时任务执行
5.添加定时任务
6.定时任务状态修改
刚才我们分析新增定时任务的源码时,发现了任务在初始化时是处于暂停状态的。
如果要启动任务,可以在页面进行任务状态的开关控制,所以接下来我们对此功能的源码进行分析
5.2集群模式
首先我们来聊下为什么需要quartz集群
在单机模式下,默认所有的jobDetail
和trigger
都存储在内存中。这样做的好处是读取速度快,但缺点也很明显:一旦服务器故障,所有的任务数据就会丢失,这就是所谓的单点故障问题。
还有如果在一个高峰时段,比如上午9点,需要触发500个任务,这将给服务器带来巨大的负载压力。这不仅影响性能,还可能引发服务中断。
缺点:单点故障、负载压力大
为了解决这些问题,我们可以部署多个服务器节点,将任务信息存储到数据库中。这样,多个节点就可以通过共享数据库来协调任务的执行,形成Quartz集群模式。
这种方式不仅解决了单点故障问题,还能通过负载均衡提升效率。
集群模式的优势
-
高可用性:即使某个节点出现问题,其他节点仍然可以正常运行。
-
负载均衡:将任务分散到不同的节点执行,避免单个节点过载。
实现
1.导入sql
将若依提供的quartz.sql
导入到数据库中
2.开启配置
打开dkd-quartz
模块中ScheduleConfig
配置类注释
3.节点复制
首先修改当前SpringBoot的启动类的名称
我们再添加(复制)一个SpringBoot的启动配置
-Dserver.port=8081
4.观察数据库
重启项目即可,观察数据库,已存入jobDetail和trigger,多个服务器节点可以实现共享
6.数据权限
在系统中,权限的分配和控制主要依赖于角色。每个角色可以被赋予不同的菜单权限和数据权限,用户则通过他们的角色来继承这些权限,进而决定他们能访问哪些系统资源。
目前,系统支持以下五种数据权限类型:
-
全部数据权限:无限制访问所有数据,相当于拥有最高权限的通行证。
-
自定数据权限:用户可以根据自己的需求设定访问特定数据的规则。
-
部门数据权限:只能访问自己所在部门的数据,限制在本部门范围内。
-
部门及以下数据权限:可以访问自己部门及下属部门的数据,适用于管理层级。
-
仅本人数据权限:只能访问和操作自己的数据,保障个人隐私和数据隔离。
1.源码
1、若依系统的数据权限设计主要通过用户、角色、部门表建立关系,实现对数据的访问控制:
2、在需要数据权限控制方法上添加@DataScope
注解,其中d
和u
用来表示表的别名
3、在mybatis
查询底部标签添加数据范围过滤
其作用就是相当于在一个 select 语句后面拼接一个 and 条件语句,来实现查询限制
若依数据权限底层使用了自定义注解+AOP切面+SQL拼接
-
com.dkd.common.annotation.DataScope 自定义注解
-
com.ruoyi.framework.aspectj.DataScopeAspect:切面类
-
通过实现AOP编程,对目标方法进行拦截(标注DataScope 注解的方法),实现了构建数据范围SQL过滤条件
-
仅实体继承`BaseEntity`才会进行处理,`SQL`语句会存放到`BaseEntity`对象中的`params`属性中,然后在`xml`中通过`${params.dataScope}`获取拼接后的语句。
2.改造
需求
我们有一个系统登录日志,里面记录了所有用户的登录信息。
但是,并不是所有人都应该看到所有的日志数据。所以,我们需要根据用户的角色来控制他们能查看的数据范围。
(1)添加权限注解
在dkd-system
模块的com.dkd.system.service.impl.SysLogininforServiceImpl
在服务层的方法上使用 @DataScope
注解
(2)添加表字段
如果sys_logininfo
业务表需要实现数据权限,需要有dept_id
和user_id
这两个字段。
(3)添加实体属性
在dkd-system
模块的com.dkd.system.domain.SysLogininfor
实体类中,需要有deptId
和userId
这两个属性。
(4)修改映射文件
在dkd-system
模块的com.dkd.system.domain.SysLogininforMapper.xml
映射文件中,通过动态拼接 SQL,实现数据范围的过滤
(5)异步工厂调整
在dkd-framework
模块的com.dkd.framework.manager.factory.AsyncFactory
异步工厂创建登录日志任务时,需要有deptId
和userId
这两个属性。