若依学习笔记
# 若依(前后端分离版)
# 如何快速上手开源项目?
- 先看前端,再看后端
- 打开浏览器,检查网络,粘贴请求的路径
- 看前端代码
- 到后端代码按全局文件搜索:
Ctrl + Shift + F该路径,快速找到对应代码
# 文件结构
# 后端结构
com.ruoyi
├── common // 工具类
│ └── annotation // 自定义注解
│ └── config // 全局配置
│ └── constant // 通用常量
│ └── core // 核心控制
│ └── enums // 通用枚举
│ └── exception // 通用异常
│ └── filter // 过滤器处理
│ └── utils // 通用类处理
├── framework // 框架核心
│ └── aspectj // 注解实现
│ └── config // 系统配置
│ └── datasource // 数据权限
│ └── interceptor // 拦截器
│ └── manager // 异步处理
│ └── security // 权限控制
│ └── web // 前端控制
├── ruoyi-generator // 代码生成(可移除)
├── ruoyi-quartz // 定时任务(可移除)
├── ruoyi-system // 系统代码
├── ruoyi-admin // 后台服务
├── ruoyi-xxxxxx // 其他模块
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 前端结构
├── build // 构建相关
├── bin // 执行脚本
├── public // 公共文件
│ ├── favicon.ico // favicon图标
│ └── index.html // html模板
│ └── robots.txt // 反爬虫
├── src // 源代码
│ ├── api // 所有请求
│ ├── assets // 主题 字体等静态资源
│ ├── components // 全局公用组件
│ ├── directive // 全局指令
│ ├── layout // 布局
│ ├── plugins // 通用方法
│ ├── router // 路由
│ ├── store // 全局 store管理
│ ├── utils // 全局公用方法
│ ├── views // view
│ ├── App.vue // 入口页面
│ ├── main.js // 入口 加载组件 初始化等
│ ├── permission.js // 权限管理
│ └── settings.js // 系统配置
├── .editorconfig // 编码格式
├── .env.development // 开发环境配置
├── .env.production // 生产环境配置
├── .env.staging // 测试环境配置
├── .eslintignore // 忽略语法检查
├── .eslintrc.js // eslint 配置项
├── .gitignore // git 忽略项
├── babel.config.js // babel.config.js
├── package.json // package.json
└── vue.config.js // vue.config.js
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
27
28
29
30
31
# 1.1 什么是若依?
开源项目,学习开源项目的目标:
- 用,减少自己的工作量
- 学习优秀开源项目底层的编程思想,设计思路,提高自己的编程能力
官方文档:[介绍 | RuoYi (opens new window)](http://doc.ruoyi.vip/ruoyi-vue/)
更系统的学习,建议看官方文档
基础:Spring Boot + Vue
环境要求:
- JDK 1.8+
- MySQL
- Redis
- Maven
- Vue
# 1.2 使用若依
使用开源项目的步骤:
- 下载并运行
- 看懂业务流程
- 进行二次开发
具体操作:
- 去官网下载源代码
- 配置数据库 MySQL、Redis
- 本地执行 SQL 脚本
- 在工程中配置数据库 ——
application-druid.yml文件中修改 - Redis 配置
启动前端:
- 安装依赖
npm install - 启动
npm run dev
# 1.3 登录
# 1.3.1 生成验证码
基本思路:
后端生成一个表达式,如 1+1=2:
1+1=?@2:1+1=?转成图片,传到前端进行展示2(答案)存入 Redis
前端:
- 请求地址:
http://localhost/dev-api/captchaImage - 页面信息都在
views文件夹里面 - Vue 获取图片,前端还是后端?—— 后端
- 反向代理,url 请求前端,进行代理,映射到后端,解决跨域问题

/dev-api 替换成'' 再映射到
http://localhost:8080
2
即:http://localhost:8080/captchaImage
后端:
到后端代码按全局文件搜索:Ctrl + Shift + F 该路径,快速找到对应代码

# 1.3.2 登录的具体流程
后端:
- 校验验证码
- 校验用户名和密码
- 生成 Token
使用异步任务管理器,结合线程池,实现了异步的操作。日志记录和业务逻辑实现异步解耦合。
# 1.3.3 getInfo
获取当前用户的角色和权限信息,存储到 Vuex 中。
* 是通配符,格式为 模块:菜单:功能:
// spring security 里面的东西
// 代表所有权限
*:*:*
2
3

后端相应代码:

# 1.3.4 getRouters
根据当前用户的权限获取动态路由,不同权限的用户,展现的菜单不一样。
parent_id 为 0 的是最高级菜单,其他为子菜单。

先查询出的是父节点菜单,子菜单包含在 children 里。

# 1.4 用户管理
流程:加载 Vue 页面 → 请求后台数据
- getList —— 加载用户管理列表

1.1、startPage() —— 分页
PageHelper 中的 reasonable 对参数进行逻辑处理,保证参数的正确性,当 pageNum = 0/-1 时,自动填写 pageNum = 1。
1.2、userService.selectUserList(user)
注解 @DataScope(deptAlias = "d", userAlias = "u") 给表设置别名,sys_dept d,sys_user u。
treeselect —— 加载左边的树状图列表
(1)查出所有的部门数据
(2)组装成树状结构
buildDeptTreeSelect:将 10 条记录组装成一个树状图。recursionFn(depts, dept):2.1、先找到顶级节点,找到它的子节点
2.2、遍历顶级节点的子节点,再找到它的子节点

点击树状图中的数据:

# 1.5 添加数据
前端 —— 添加的方法:

reset是表单重置getTreeselect获取部门树状图getUser获取角色和部门信息
后端 —— User 业务:
插入用户的方法包含插入三张表,目的是增添关联表的记录。

# 1.6 修改数据

这里的 getUser 方法除了要获取所有的岗位和角色信息之外,还要获取当前用户已经拥有的岗位和角色。

数据库:
- 修改 user
- 重新维护
user_post和user_role
修改的逻辑是:先删后增。这样子更加安全,不用判断是否存在。

# 1.7 删除数据

做判断,不能删除当前用户,并使用注解记录删除操作的日志信息到数据库中。
# 1.8 异步任务管理器

AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
通过异步任务管理器记录登录日志:
AsyncManager.me()获取一个AsyncManager对象

- 执行
execute方法,执行任务,传入的是一个Task对象(在异步工厂的类里写),实现了Runnable接口,是一个任务,由线程Thread去执行。
/**
* 记录登录信息
*
* @param username 用户名
* @param status 状态
* @param message 消息
* @param args 列表
* @return 任务task
*/
public static TimerTask recordLogininfor(final String username, final String status, final String message,
final Object... args)
{
final UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
final String ip = IpUtils.getIpAddr();
return new TimerTask()
{
@Override
public void run()
{
String address = AddressUtils.getRealAddressByIP(ip);
StringBuilder s = new StringBuilder();
s.append(LogUtils.getBlock(ip));
s.append(address);
s.append(LogUtils.getBlock(username));
s.append(LogUtils.getBlock(status));
s.append(LogUtils.getBlock(message));
// 打印信息到日志
sys_user_logger.info(s.toString(), args);
// 获取客户端操作系统
String os = userAgent.getOperatingSystem().getName();
// 获取客户端浏览器
String browser = userAgent.getBrowser().getName();
// 封装对象
SysLogininfor logininfor = new SysLogininfor();
logininfor.setUserName(username);
logininfor.setIpaddr(ip);
logininfor.setLoginLocation(address);
logininfor.setBrowser(browser);
logininfor.setOs(os);
logininfor.setMsg(message);
// 日志状态
if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER))
{
logininfor.setStatus(Constants.SUCCESS);
}
else if (Constants.LOGIN_FAIL.equals(status))
{
logininfor.setStatus(Constants.FAIL);
}
// 插入数据
SpringUtils.getBean(ISysLogininforService.class).insertLogininfor(logininfor);
}
};
}
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
封装了登录用户的信息,执行添加操作,这里不会执行,而是将任务交给线程对象来执行。
异步任务管理器,内部定义了一个线程池,然后根据业务创建添加日志的任务,交给线程池来处理,这样做到日志和业务的抽象,解耦合,日志全部统一处理。
使用线程池有什么用?
提高线程利用率
# 1.9 代码自动生成
# 1、创建数据表
use ruoyi_vue;
create table test_user(
id int primary key auto_increment,
name varchar(11),
password varchar(11)
);
2
3
4
5
6
7
# 2、系统工具 -> 代码生成

# 3、编辑
基本信息:

字段信息:

生成信息:

# 4、点击生成代码

# 5、解压
生成代码后下载压缩包,里面有三个文件:
main(Java 后端代码)vue(Vue 前端代码)SQL(菜单 SQL)
# 6、导入代码,重启项目
- 如果后端抛出 404 异常,可能是因为代码刚导进项目,还未被扫描到
rebuild project,重新启动即可