(该文章整理于网络,仅用于学习记录,如有侵权,请联系删除)
RuoYi-Vue 是一个 Java EE 企业级快速开发平台,基于经典技术组合(Spring Boot、Spring Security、MyBatis、Jwt、Vue),内置模块如:部门管理、角色用户、菜单及按钮授权、数据权限、系统参数、日志管理、代码生成等。在线定时任务配置;支持集群,支持多数据源,支持分布式事务。比较好用。
可参考官网。
部署Java:多模块springboot ,通过宝塔创建springboot项目,再通过nginx反向代理即可。
部署Vue :打包部署。
war部署方式
ruoyi/pom.xml
中的packaging
修改为war
,放入tomcat
服务器webapps
SpringBoot
去除内嵌Tomcat
(PS:此步骤不重要,因为不排除也能在容器中部署war
)
1. 注解:需要通过反射来进行解析,参考excle注解。如果没解析器就没有任何意义。
2. ruoyi配置,通过该类读取yml配置文件:(
@Component
@ConfigurationProperties(prefix = "ruoyi")
3. constant常量
4. core: 基本的control 封装公共。
5.公共的实体:如loginbody.
6.封装的page.
7. redis操作的方法。
bbbub{},“aaa”====》
debug讲解
登陆讲解:
获取验证码,获取配置默认的验证码类型。
验证码存放到redis ,并且返回一个uuuid到前台用于验证(有时效),如果要使用,可以临时去取。
登陆逻辑梳理:
public void validateCaptcha(String username, String code, String uuid)
{
String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;
String captcha = redisCache.getCacheObject(verifyKey);
redisCache.deleteObject(verifyKey);
if (captcha == null)
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")));
throw new CaptchaExpireException();
}
if (!code.equalsIgnoreCase(captcha))
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));
throw new CaptchaException();
}
}
登陆debug:
注意是封装的第三方组件。
模拟登陆若依
通过API POST 或者 IDEA 的HTTP客户端发起请求,获取图片后 ,解析到答案。
或者直接根据UUID去redis查询到结果:
-
获取验证码,得到UUID
-
向后端发送login请求,带上username,password,uuid,code(验证码)
-
登录成功 获取TOKEN token解密后,并缓存到redis.
4. 发起其他请求,需要带上token进行认证。(两种均可)
复习反射注解
Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。
Java 语言中的类、方法、变量、参数和包等都可以被标注。和 Javadoc 不同,Java 标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容 。 当然它也支持自定义 Java 标注。
内置的注解
Java 定义了一套注解,共有 7 个,3 个在 java.lang 中,剩下 4 个在 java.lang.annotation 中。
作用在代码的注解是
-
@Override - 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
-
@Deprecated - 标记过时方法。如果使用该方法,会报编译警告。
-
@SuppressWarnings - 指示编译器去忽略注解中声明的警告。
作用在其他注解的注解(或者说 元注解)是:
-
@Retention - 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。
-
@Documented - 标记这些注解是否包含在用户文档中。
-
@Target - 标记这个注解应该是哪种 Java 成员。
-
@Inherited - 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)
从 Java 7 开始,额外添加了 3 个注解:
获取注解,比如或用户名称上的注解信息:(注意soutv的使用)
通过反射获取构造函数并实例化,获取成员方法并调用:如下:
导入导出
分页
-
前端实现
// 一般在查询参数中定义分页变量
queryParams: {
pageNum: 1,
pageSize: 10
},
// 页面添加分页组件,传入分页变量
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
// 调用后台方法,传入参数 获取结果
listUser(this.queryParams).then(response => {
this.userList = response.rows;
this.total = response.total;
}
);
-
后端实现
@PostMapping("/list")
@ResponseBody
public TableDataInfo list(User user)
{
startPage(); // 此方法配合前端完成自动分页
List<User> list = userService.selectUserList(user);
return getDataTable(list);
}
上传下载
-
上传
drop table if exists sys_file_info;
create table sys_file_info (
file_id int(11) not null auto_increment comment '文件id',
file_name varchar(50) default '' comment '文件名称',
file_path varchar(255) default '' comment '文件路径',
primary key (file_id)
) engine=innodb auto_increment=1 default charset=utf8 comment = '文件信息表';
<el-upload
ref="upload"
:limit="1"
accept=".jpg, .png"
:action="upload.url"
:headers="upload.headers"
:file-list="upload.fileList"
:on-progress="handleFileUploadProgress"
:on-success="handleFileSuccess"
:auto-upload="false">
<el-button slot="trigger" size="small" type="primary">选取文件</el-button>
<el-button style="margin-left: 10px;" size="small" type="success" :loading="upload.isUploading" @click="submitUpload">上传到服务器</el-button>
<div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过500kb</div>
</el-upload>
import { getToken } from "@/utils/auth";
// 上传参数
upload: {
// 是否禁用上传
isUploading: false,
// 设置上传的请求头部
headers: { Authorization: "Bearer " + getToken() },
// 上传的地址
url: process.env.VUE_APP_BASE_API + "/common/upload",
// 上传的文件列表
fileList: []
},
handleAdd() {
...
this.upload.fileList = [];
}
handleUpdate(row) {
...
this.upload.fileList = [{ name: this.form.fileName, url: this.form.filePath }];
}
// 文件提交处理
submitUpload() {
this.$refs.upload.submit();
},
// 文件上传中处理
handleFileUploadProgress(event, file, fileList) {
this.upload.isUploading = true;
},
// 文件上传成功处理
handleFileSuccess(response, file, fileList) {
this.upload.isUploading = false;
this.form.filePath = response.url;
this.msgSuccess(response.msg);
}
-
下载
<template>
<div class="app-container">
<el-row :gutter="20">
<el-col xl="24" md="24" lg="24" sm="24">
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleDownload"
>下载
</el-button>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
name: "index",
methods: {
// 文件下载处理
handleDownload() {
var name = "/profile/upload/2021/01/18/4d6b8f1f-5b10-4e76-b5b4-675bd90856b6.jpg";
var url = "http://localhost:8080/profile/upload/2021/01/18/4d6b8f1f-5b10-4e76-b5b4-675bd90856b6.jpg";
var suffix = url.substring(url.lastIndexOf("."), url.length);
const a = document.createElement('a')
a.setAttribute('download', name + suffix)
a.setAttribute('target', '_blank')
a.setAttribute('href', url)
a.click()
}
}
}
</script>
<style scoped>
</style>
权限控制
事务管理
同SpringBoot,主要注解:@Transactional
异常处理
全局异常处理。通过一个全局异常处理类来进行处理GlobalExceptionHandler,这里面有两个重要的注解叫:@RestControllerAdvice,@ExceptionHandler(BaseException.class),用AOP的方法去增强方法,若发生异常将由ExceptionHandler注解的方法去处理这个异常,一般都是给前端返回一个错误的信息。这里BaseException.class就是指定某个异常,当这个异常发生,就会执行相关方法,例如:
/**
* 基础异常
*/
@ExceptionHandler(BaseException.class)
public AjaxResult baseException(BaseException e)
{
return AjaxResult.error(e.getMessage());
}
这个方法就会去处理发生了BaseException的控制器。
系统日志
-
登录日志(需要线程池知识)
-
登录日志带大家debug,在com/ruoyi/framework/web/service/SysLoginService.java这个类中的login方法中能找到相关的日志打印信息,例如:
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")));
-
控制器操作日志
-
AOP切面实现逻辑-debug。
-
案例-自定义自己的操作日志。
数据权限
在实际开发中,需要设置用户只能查看哪些部门的数据,这种情况一般称为数据权限。 例如对于销售,财务的数据,它们是非常敏感的,因此要求对数据权限进行控制, 对于基于集团性的应用系统而言,就更多需要控制好各自公司的数据了。如设置只能看本公司、或者本部门的数据,对于特殊的领导,可能需要跨部门的数据, 因此程序不能硬编码那个领导该访问哪些数据,需要进行后台的权限和数据权限的控制。
针对角色设置的-去角色管理进行设置每一个角色的数据权限。
这个数据权限是AOP来实现的,主要注解是@Before(前置切入),通过对维护的param属性对应的HashMap中dataScope健存入一段SQL,这段SQL最终将加在查询语句的后边,mybatis中引用:${param.dataScope}。如何拼装的SQL在切面类(DataScopeAspect)中debug可以看到。
多数据源
前置知识 -> 如何实现多数据源?,实现多数据源的ThreadLocal类作用是什么呢?
主要通过继承AbstractRoutingDataSource类来实现。
ThreadLocal是存储线程独立局部变量用的,多个线程之间互不干扰。
其它
公共基础:
获取当前用户逻辑
前端获取路由逻辑
Excel导入逻辑 * 讲过
Excel导出逻辑 不讲
登录逻辑 * 讲过
鉴权(权限)逻辑 -- 讲了
sys_config表是个什么玩意?
对应前端参数设置的值。
redis存的一些值是什么玩意?怎么项目一启动就有值的?
值来自sys-config表
作业:
Excel导入,顺带导入角色,在模板中给用户以提示。