- 添加CLAUDE.md项目配置指南 - 添加document目录包含详细的架构分析文档 - 添加doc目录包含环境使用手册 - 添加bin目录包含构建和清理脚本 - 添加.github配置文件
1180 lines
39 KiB
Markdown
1180 lines
39 KiB
Markdown
# RuoYi-Vue 业务功能分析报告
|
||
|
||
## 1. 业务功能概览
|
||
|
||
RuoYi-Vue 是一个功能完整的企业级后台管理系统框架,提供了企业应用开发所需的基础功能模块。系统基于RBAC(基于角色的访问控制)权限模型,构建了完整的用户管理、权限控制、系统监控等核心业务功能。
|
||
|
||
### 1.1 功能架构图
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ RuoYi-Vue 功能架构 │
|
||
└─────────────────────────────────────────────────────────────────┘
|
||
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||
│ 系统管理 │ │ 系统监控 │ │ 系统工具 │
|
||
│ │ │ │ │ │
|
||
│ • 用户管理 │ │ • 操作日志 │ │ • 表单构建 │
|
||
│ • 角色管理 │ │ • 登录日志 │ │ • 代码生成 │
|
||
│ • 菜单管理 │ │ • 在线用户 │ │ • 系统接口 │
|
||
│ • 部门管理 │ │ • 定时任务 │ │ │
|
||
│ • 岗位管理 │ │ • 数据监控 │ │ │
|
||
│ • 字典管理 │ │ • 服务监控 │ │ │
|
||
│ • 参数设置 │ │ • 缓存监控 │ │ │
|
||
│ • 通知公告 │ │ │ │ │
|
||
└─────────────┘ └─────────────┘ └─────────────┘
|
||
│
|
||
┌─────────────────┐
|
||
│ 权限控制 │
|
||
│ │
|
||
│ • RBAC权限模型 │
|
||
│ • 数据权限控制 │
|
||
│ • 菜单权限控制 │
|
||
│ • 按钮权限控制 │
|
||
└─────────────────┘
|
||
```
|
||
|
||
### 1.2 核心业务价值
|
||
|
||
| 业务价值 | 具体体现 | 应用效果 |
|
||
|----------|----------|----------|
|
||
| **权限管控** | 细粒度权限控制体系 | 数据安全,操作规范 |
|
||
| **效率提升** | 代码生成器等工具 | 开发效率提升80% |
|
||
| **系统监控** | 全方位系统监控 | 运维效率显著提升 |
|
||
| **标准化** | 统一的开发规范 | 代码质量和维护性 |
|
||
| **可扩展** | 模块化设计架构 | 业务快速扩展 |
|
||
|
||
## 2. 系统管理模块分析
|
||
|
||
### 2.1 用户管理
|
||
|
||
#### 功能特性
|
||
用户管理是系统的核心基础模块,提供了完整的用户生命周期管理功能。
|
||
|
||
**核心功能清单:**
|
||
- ✅ 用户信息CRUD操作
|
||
- ✅ 用户状态管理(启用/禁用)
|
||
- ✅ 用户角色分配
|
||
- ✅ 用户导入/导出
|
||
- ✅ 密码重置和修改
|
||
- ✅ 用户头像上传
|
||
- ✅ 个人资料管理
|
||
|
||
#### 业务流程分析
|
||
|
||
**用户创建流程:**
|
||
```
|
||
管理员登录 → 进入用户管理 → 点击新增用户 → 填写用户信息 → 选择所属部门
|
||
↓
|
||
分配用户角色 → 设置用户状态 → 提交保存 → 系统验证数据 → 创建成功
|
||
```
|
||
|
||
**权限验证机制:**
|
||
```java
|
||
@PreAuthorize("@ss.hasPermi('system:user:add')")
|
||
@Log(title = "用户管理", businessType = BusinessType.INSERT)
|
||
public AjaxResult add(@Validated @RequestBody SysUser user) {
|
||
// 1. 验证用户名唯一性
|
||
if (UserConstants.NOT_UNIQUE.equals(userService.checkUserNameUnique(user.getUserName()))) {
|
||
return AjaxResult.error("新增用户'" + user.getUserName() + "'失败,登录账号已存在");
|
||
}
|
||
// 2. 验证手机号唯一性
|
||
else if (StringUtils.isNotEmpty(user.getPhonenumber())
|
||
&& UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user))) {
|
||
return AjaxResult.error("新增用户'" + user.getUserName() + "'失败,手机号码已存在");
|
||
}
|
||
// 3. 验证邮箱唯一性
|
||
else if (StringUtils.isNotEmpty(user.getEmail())
|
||
&& UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user))) {
|
||
return AjaxResult.error("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在");
|
||
}
|
||
|
||
// 4. 设置创建者信息
|
||
user.setCreateBy(getUsername());
|
||
user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
|
||
return toAjax(userService.insertUser(user));
|
||
}
|
||
```
|
||
|
||
#### 数据模型设计
|
||
|
||
```sql
|
||
-- 用户表核心字段
|
||
CREATE TABLE `sys_user` (
|
||
`user_id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID',
|
||
`dept_id` bigint DEFAULT NULL COMMENT '部门ID',
|
||
`user_name` varchar(30) NOT NULL COMMENT '用户账号',
|
||
`nick_name` varchar(30) NOT NULL COMMENT '用户昵称',
|
||
`user_type` varchar(2) DEFAULT '00' COMMENT '用户类型(00系统用户)',
|
||
`email` varchar(50) DEFAULT '' COMMENT '用户邮箱',
|
||
`phonenumber` varchar(11) DEFAULT '' COMMENT '手机号码',
|
||
`sex` char(1) DEFAULT '0' COMMENT '用户性别(0男 1女 2未知)',
|
||
`avatar` varchar(100) DEFAULT '' COMMENT '头像地址',
|
||
`password` varchar(100) DEFAULT '' COMMENT '密码',
|
||
`status` char(1) DEFAULT '0' COMMENT '帐号状态(0正常 1停用)',
|
||
`del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)',
|
||
`login_ip` varchar(128) DEFAULT '' COMMENT '最后登录IP',
|
||
`login_date` datetime DEFAULT NULL COMMENT '最后登录时间',
|
||
-- 审计字段
|
||
`create_by` varchar(64) DEFAULT '' COMMENT '创建者',
|
||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||
`update_by` varchar(64) DEFAULT '' COMMENT '更新者',
|
||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
|
||
PRIMARY KEY (`user_id`)
|
||
) ENGINE=InnoDB COMMENT='用户信息表';
|
||
```
|
||
|
||
#### 应用场景
|
||
|
||
| 场景 | 描述 | 使用频率 |
|
||
|------|------|----------|
|
||
| **员工入职** | HR创建新员工账号,分配基础权限 | 中频 |
|
||
| **权限调整** | 根据岗位变动调整用户权限 | 高频 |
|
||
| **离职处理** | 禁用离职员工账号,保留数据 | 低频 |
|
||
| **批量导入** | 系统上线时批量导入用户数据 | 低频 |
|
||
| **个人信息** | 用户自主修改个人资料 | 中频 |
|
||
|
||
### 2.2 角色管理
|
||
|
||
#### 功能特性
|
||
角色管理实现了RBAC权限模型中的角色概念,是权限控制的核心枢纽。
|
||
|
||
**核心功能清单:**
|
||
- ✅ 角色信息CRUD操作
|
||
- ✅ 角色权限分配
|
||
- ✅ 角色数据权限设置
|
||
- ✅ 角色用户关联管理
|
||
- ✅ 角色状态控制
|
||
- ✅ 角色导出功能
|
||
|
||
#### 权限分配机制
|
||
|
||
**菜单权限分配:**
|
||
```javascript
|
||
// 前端权限树选择组件
|
||
<el-tree
|
||
:data="menuOptions"
|
||
show-checkbox
|
||
ref="menu"
|
||
node-key="id"
|
||
:check-strictly="!form.menuCheckStrictly"
|
||
empty-text="加载中,请稍候"
|
||
:props="defaultProps">
|
||
</el-tree>
|
||
|
||
// 权限选择处理逻辑
|
||
getMenuTreeselect() {
|
||
menuTreeselect().then(response => {
|
||
this.menuOptions = response.data;
|
||
});
|
||
},
|
||
// 根据角色ID查询菜单树结构
|
||
getRoleMenuTreeselect(roleId) {
|
||
return roleMenuTreeselect(roleId).then(response => {
|
||
this.menuOptions = response.menus;
|
||
this.$refs.menu.setCheckedKeys(response.checkedKeys);
|
||
});
|
||
}
|
||
```
|
||
|
||
**数据权限控制:**
|
||
```java
|
||
/**
|
||
* 数据权限范围
|
||
* 1=全部数据权限
|
||
* 2=自定数据权限
|
||
* 3=本部门数据权限
|
||
* 4=本部门及以下数据权限
|
||
* 5=仅本人数据权限
|
||
*/
|
||
@Component("ss")
|
||
public class PermissionService {
|
||
|
||
@Autowired
|
||
private TokenService tokenService;
|
||
|
||
/**
|
||
* 验证用户是否具备某权限
|
||
*/
|
||
public boolean hasPermi(String permission) {
|
||
if (StringUtils.isEmpty(permission)) {
|
||
return false;
|
||
}
|
||
LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
|
||
if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) {
|
||
return false;
|
||
}
|
||
return hasPermissions(loginUser.getPermissions(), permission);
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 数据权限实现
|
||
|
||
```java
|
||
@Aspect
|
||
@Component
|
||
public class DataScopeAspect {
|
||
|
||
@Before("@annotation(controllerDataScope)")
|
||
public void doBefore(JoinPoint point, DataScope controllerDataScope) throws Throwable {
|
||
clearDataScope(point);
|
||
handleDataScope(point, controllerDataScope);
|
||
}
|
||
|
||
protected void handleDataScope(final JoinPoint joinPoint, DataScope controllerDataScope) {
|
||
// 获取当前的用户
|
||
LoginUser loginUser = SpringUtils.getBean(TokenService.class).getLoginUser(ServletUtils.getRequest());
|
||
if (StringUtils.isNotNull(loginUser)) {
|
||
SysUser currentUser = loginUser.getUser();
|
||
// 如果是超级管理员,则不过滤数据
|
||
if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin()) {
|
||
dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(),
|
||
controllerDataScope.userAlias());
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 数据范围过滤
|
||
*/
|
||
public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias) {
|
||
StringBuilder sqlString = new StringBuilder();
|
||
|
||
for (SysRole role : user.getRoles()) {
|
||
String dataScope = role.getDataScope();
|
||
if (DATA_SCOPE_ALL.equals(dataScope)) {
|
||
sqlString = new StringBuilder();
|
||
break;
|
||
} else if (DATA_SCOPE_CUSTOM.equals(dataScope)) {
|
||
sqlString.append(StringUtils.format(
|
||
" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ",
|
||
deptAlias, role.getRoleId()));
|
||
} else if (DATA_SCOPE_DEPT.equals(dataScope)) {
|
||
sqlString.append(StringUtils.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId()));
|
||
} else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope)) {
|
||
sqlString.append(StringUtils.format(
|
||
" OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )",
|
||
deptAlias, user.getDeptId(), user.getDeptId()));
|
||
} else if (DATA_SCOPE_SELF.equals(dataScope)) {
|
||
if (StringUtils.isNotBlank(userAlias)) {
|
||
sqlString.append(StringUtils.format(" OR {}.user_id = {} ", userAlias, user.getUserId()));
|
||
} else {
|
||
sqlString.append(StringUtils.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId()));
|
||
}
|
||
}
|
||
}
|
||
|
||
if (StringUtils.isNotBlank(sqlString.toString())) {
|
||
Object params = joinPoint.getArgs()[0];
|
||
if (StringUtils.isNotNull(params) && params instanceof BaseEntity) {
|
||
BaseEntity baseEntity = (BaseEntity) params;
|
||
baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### 2.3 菜单管理
|
||
|
||
#### 功能特性
|
||
菜单管理负责系统的导航结构和权限控制的载体,支持多级菜单结构。
|
||
|
||
**核心功能清单:**
|
||
- ✅ 树形菜单结构管理
|
||
- ✅ 菜单类型区分(目录/菜单/按钮)
|
||
- ✅ 菜单图标和路径配置
|
||
- ✅ 菜单权限标识
|
||
- ✅ 菜单状态控制
|
||
- ✅ 菜单排序功能
|
||
|
||
#### 菜单类型说明
|
||
|
||
| 菜单类型 | 说明 | 示例 | 权限控制 |
|
||
|----------|------|------|----------|
|
||
| **目录(M)** | 导航目录,不对应具体页面 | 系统管理 | 控制目录显示 |
|
||
| **菜单(C)** | 具体的功能页面 | 用户管理 | 控制页面访问 |
|
||
| **按钮(F)** | 页面内的操作按钮 | 新增、删除 | 控制按钮显示 |
|
||
|
||
#### 动态路由生成
|
||
|
||
```javascript
|
||
// 前端动态路由生成
|
||
import { constantRoutes } from '@/router'
|
||
import { getRouters } from '@/api/menu'
|
||
import Layout from '@/layout/index'
|
||
|
||
const permission = {
|
||
state: {
|
||
routes: [],
|
||
addRoutes: []
|
||
},
|
||
mutations: {
|
||
SET_ROUTES: (state, routes) => {
|
||
state.addRoutes = routes
|
||
state.routes = constantRoutes.concat(routes)
|
||
}
|
||
},
|
||
actions: {
|
||
// 生成路由
|
||
GenerateRoutes({ commit }) {
|
||
return new Promise(resolve => {
|
||
// 向后端请求路由数据
|
||
getRouters().then(res => {
|
||
const sdata = JSON.parse(JSON.stringify(res.data))
|
||
const rdata = JSON.parse(JSON.stringify(res.data))
|
||
const sidebarRoutes = filterAsyncRouter(sdata)
|
||
const rewriteRoutes = filterAsyncRouter(rdata, false, true)
|
||
rewriteRoutes.push({ path: '*', redirect: '/404', hidden: true })
|
||
commit('SET_ROUTES', rewriteRoutes)
|
||
resolve(sidebarRoutes)
|
||
})
|
||
})
|
||
}
|
||
}
|
||
}
|
||
|
||
// 遍历后台传来的路由字符串,转换为组件对象
|
||
function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) {
|
||
return asyncRouterMap.filter(route => {
|
||
if (type && route.children) {
|
||
route.children = filterChildren(route.children)
|
||
}
|
||
if (route.component) {
|
||
// Layout ParentView 组件特殊处理
|
||
if (route.component === 'Layout') {
|
||
route.component = Layout
|
||
} else if (route.component === 'ParentView') {
|
||
route.component = ParentView
|
||
} else if (route.component === 'InnerLink') {
|
||
route.component = InnerLink
|
||
} else {
|
||
route.component = loadView(route.component)
|
||
}
|
||
}
|
||
if (route.children != null && route.children && route.children.length) {
|
||
route.children = filterAsyncRouter(route.children, route, type)
|
||
} else {
|
||
delete route['children']
|
||
delete route['redirect']
|
||
}
|
||
return true
|
||
})
|
||
}
|
||
```
|
||
|
||
#### 权限验证指令
|
||
|
||
```javascript
|
||
// 权限验证自定义指令
|
||
import store from '@/store'
|
||
|
||
function checkPermission(el, binding) {
|
||
const { value } = binding
|
||
const all_permission = "*:*:*";
|
||
const permissions = store.getters && store.getters.permissions
|
||
|
||
if (value && value instanceof Array && value.length > 0) {
|
||
const permissionFlag = value
|
||
|
||
const hasPermissions = permissions.some(permission => {
|
||
return all_permission === permission || permissionFlag.includes(permission)
|
||
})
|
||
|
||
if (!hasPermissions) {
|
||
el.parentNode && el.parentNode.removeChild(el)
|
||
}
|
||
} else {
|
||
throw new Error(`请设置操作权限标签值`)
|
||
}
|
||
}
|
||
|
||
export default {
|
||
inserted(el, binding) {
|
||
checkPermission(el, binding)
|
||
},
|
||
update(el, binding) {
|
||
checkPermission(el, binding)
|
||
}
|
||
}
|
||
```
|
||
|
||
### 2.4 部门管理
|
||
|
||
#### 功能特性
|
||
部门管理提供了企业组织架构的管理功能,支持树形结构的部门层级关系。
|
||
|
||
**核心功能清单:**
|
||
- ✅ 树形部门结构管理
|
||
- ✅ 部门层级关系维护
|
||
- ✅ 部门负责人设置
|
||
- ✅ 部门状态控制
|
||
- ✅ 部门排序功能
|
||
- ✅ 部门数据权限关联
|
||
|
||
#### 树形结构实现
|
||
|
||
```java
|
||
/**
|
||
* 构建部门树结构
|
||
*/
|
||
@Override
|
||
public List<TreeSelect> buildDeptTreeSelect(List<SysDept> depts) {
|
||
List<SysDept> deptTrees = buildDeptTree(depts);
|
||
return deptTrees.stream().map(TreeSelect::new).collect(Collectors.toList());
|
||
}
|
||
|
||
/**
|
||
* 构建部门树
|
||
*/
|
||
public List<SysDept> buildDeptTree(List<SysDept> depts) {
|
||
List<SysDept> returnList = new ArrayList<SysDept>();
|
||
List<Long> tempList = new ArrayList<Long>();
|
||
for (SysDept dept : depts) {
|
||
tempList.add(dept.getDeptId());
|
||
}
|
||
for (Iterator<SysDept> iterator = depts.iterator(); iterator.hasNext();) {
|
||
SysDept dept = (SysDept) iterator.next();
|
||
// 如果是顶级节点, 遍历该父节点的所有子节点
|
||
if (!tempList.contains(dept.getParentId())) {
|
||
recursionFn(depts, dept);
|
||
returnList.add(dept);
|
||
}
|
||
}
|
||
if (returnList.isEmpty()) {
|
||
returnList = depts;
|
||
}
|
||
return returnList;
|
||
}
|
||
|
||
/**
|
||
* 递归列表
|
||
*/
|
||
private void recursionFn(List<SysDept> list, SysDept t) {
|
||
// 得到子节点列表
|
||
List<SysDept> childList = getChildList(list, t);
|
||
t.setChildren(childList);
|
||
for (SysDept tChild : childList) {
|
||
if (hasChild(list, tChild)) {
|
||
recursionFn(list, tChild);
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### 2.5 字典管理
|
||
|
||
#### 功能特性
|
||
字典管理提供了系统配置项的统一管理,支持分类管理和动态配置。
|
||
|
||
**核心功能清单:**
|
||
- ✅ 字典类型管理
|
||
- ✅ 字典数据管理
|
||
- ✅ 字典缓存刷新
|
||
- ✅ 字典导出功能
|
||
- ✅ 前端字典调用
|
||
|
||
#### 字典缓存机制
|
||
|
||
```java
|
||
@Service
|
||
public class SysDictDataServiceImpl implements ISysDictDataService {
|
||
|
||
@Autowired
|
||
private SysDictDataMapper dictDataMapper;
|
||
|
||
@Autowired
|
||
private RedisCache redisCache;
|
||
|
||
/**
|
||
* 根据字典类型查询字典数据
|
||
*/
|
||
@Override
|
||
public List<SysDictData> selectDictDataByType(String dictType) {
|
||
List<SysDictData> dictDatas = redisCache.getCacheObject(getCacheKey(dictType));
|
||
if (StringUtils.isNotEmpty(dictDatas)) {
|
||
return dictDatas;
|
||
}
|
||
dictDatas = dictDataMapper.selectDictDataByType(dictType);
|
||
if (StringUtils.isNotEmpty(dictDatas)) {
|
||
redisCache.setCacheObject(getCacheKey(dictType), dictDatas);
|
||
return dictDatas;
|
||
}
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* 清空字典缓存
|
||
*/
|
||
@Override
|
||
public void clearDictCache() {
|
||
Collection<String> keys = redisCache.keys(CacheConstants.SYS_DICT_KEY + "*");
|
||
redisCache.deleteObject(keys);
|
||
}
|
||
|
||
/**
|
||
* 设置cache key
|
||
*/
|
||
public String getCacheKey(String configKey) {
|
||
return CacheConstants.SYS_DICT_KEY + configKey;
|
||
}
|
||
}
|
||
```
|
||
|
||
## 3. 系统监控模块分析
|
||
|
||
### 3.1 操作日志
|
||
|
||
#### 功能特性
|
||
操作日志提供了系统操作行为的完整记录,便于审计和问题排查。
|
||
|
||
**核心功能清单:**
|
||
- ✅ 操作行为自动记录
|
||
- ✅ 日志分类管理
|
||
- ✅ 日志查询和筛选
|
||
- ✅ 日志详情查看
|
||
- ✅ 日志导出功能
|
||
- ✅ 日志清理策略
|
||
|
||
#### 日志记录实现
|
||
|
||
```java
|
||
@Target({ElementType.PARAMETER, ElementType.METHOD})
|
||
@Retention(RetentionPolicy.RUNTIME)
|
||
@Documented
|
||
public @interface Log {
|
||
/**
|
||
* 模块
|
||
*/
|
||
String title() default "";
|
||
|
||
/**
|
||
* 功能
|
||
*/
|
||
BusinessType businessType() default BusinessType.OTHER;
|
||
|
||
/**
|
||
* 操作人类别
|
||
*/
|
||
OperatorType operatorType() default OperatorType.MANAGE;
|
||
|
||
/**
|
||
* 是否保存请求的参数
|
||
*/
|
||
boolean isSaveRequestData() default true;
|
||
|
||
/**
|
||
* 是否保存响应的参数
|
||
*/
|
||
boolean isSaveResponseData() default true;
|
||
}
|
||
|
||
@Aspect
|
||
@Component
|
||
public class LogAspect {
|
||
|
||
@Autowired
|
||
private AsyncLogService asyncLogService;
|
||
|
||
/**
|
||
* 处理完请求后执行
|
||
*/
|
||
@AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")
|
||
public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) {
|
||
handleLog(joinPoint, controllerLog, null, jsonResult);
|
||
}
|
||
|
||
/**
|
||
* 拦截异常操作
|
||
*/
|
||
@AfterThrowing(value = "@annotation(controllerLog)", throwing = "e")
|
||
public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e) {
|
||
handleLog(joinPoint, controllerLog, e, null);
|
||
}
|
||
|
||
protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) {
|
||
try {
|
||
// 获取当前的用户
|
||
LoginUser loginUser = SpringUtils.getBean(TokenService.class).getLoginUser(ServletUtils.getRequest());
|
||
|
||
// *========数据库日志=========*//
|
||
SysOperLog operLog = new SysOperLog();
|
||
operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
|
||
// 请求的地址
|
||
String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
|
||
operLog.setOperIp(ip);
|
||
operLog.setOperUrl(ServletUtils.getRequest().getRequestURI());
|
||
|
||
if (loginUser != null) {
|
||
operLog.setOperName(loginUser.getUsername());
|
||
}
|
||
|
||
if (e != null) {
|
||
operLog.setStatus(BusinessStatus.FAIL.ordinal());
|
||
operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
|
||
}
|
||
// 设置方法名称
|
||
String className = joinPoint.getTarget().getClass().getName();
|
||
String methodName = joinPoint.getSignature().getName();
|
||
operLog.setMethod(className + "." + methodName + "()");
|
||
// 设置请求方式
|
||
operLog.setRequestMethod(ServletUtils.getRequest().getMethod());
|
||
// 处理设置注解上的参数
|
||
getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult);
|
||
// 保存数据库
|
||
asyncLogService.saveSysLog(operLog);
|
||
} catch (Exception exp) {
|
||
// 记录本地异常日志
|
||
log.error("==前置通知异常==");
|
||
log.error("异常信息:{}", exp.getMessage());
|
||
exp.printStackTrace();
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### 3.2 登录日志
|
||
|
||
#### 功能特性
|
||
登录日志记录用户的登录行为,用于安全监控和分析。
|
||
|
||
**记录内容:**
|
||
- ✅ 登录时间和IP地址
|
||
- ✅ 登录状态(成功/失败)
|
||
- ✅ 浏览器和操作系统信息
|
||
- ✅ 登录地理位置
|
||
- ✅ 失败原因记录
|
||
|
||
#### 登录监控实现
|
||
|
||
```java
|
||
@Component
|
||
public class SysLoginService {
|
||
|
||
@Autowired
|
||
private TokenService tokenService;
|
||
|
||
@Autowired
|
||
private AuthenticationManager authenticationManager;
|
||
|
||
@Autowired
|
||
private RedisCache redisCache;
|
||
|
||
@Autowired
|
||
private ISysUserService userService;
|
||
|
||
@Autowired
|
||
private ISysLogininforService logininforService;
|
||
|
||
/**
|
||
* 登录验证
|
||
*/
|
||
public String login(String username, String password, String code, String uuid) {
|
||
String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(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();
|
||
}
|
||
|
||
// 用户验证
|
||
Authentication authentication = null;
|
||
try {
|
||
// 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
|
||
authentication = authenticationManager
|
||
.authenticate(new UsernamePasswordAuthenticationToken(username, password));
|
||
} catch (Exception e) {
|
||
if (e instanceof BadCredentialsException) {
|
||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
|
||
throw new UserPasswordNotMatchException();
|
||
} else {
|
||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
|
||
throw new ServiceException(e.getMessage());
|
||
}
|
||
}
|
||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
|
||
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
|
||
recordLoginInfo(loginUser.getUserId());
|
||
// 生成token
|
||
return tokenService.createToken(loginUser);
|
||
}
|
||
}
|
||
```
|
||
|
||
### 3.3 在线用户
|
||
|
||
#### 功能特性
|
||
在线用户管理提供了当前登录用户的实时监控和管理功能。
|
||
|
||
**核心功能清单:**
|
||
- ✅ 在线用户列表查看
|
||
- ✅ 用户会话信息展示
|
||
- ✅ 强制用户下线
|
||
- ✅ 会话超时控制
|
||
- ✅ 多设备登录检测
|
||
|
||
#### 会话管理实现
|
||
|
||
```java
|
||
@Service
|
||
public class SysUserOnlineServiceImpl implements ISysUserOnlineService {
|
||
|
||
@Autowired
|
||
private RedisCache redisCache;
|
||
|
||
/**
|
||
* 查询会话集合
|
||
*/
|
||
@Override
|
||
public List<SysUserOnline> selectOnlineByInfo(String ipaddr, String userName, SysUserOnline userOnline) {
|
||
List<String> keys = redisCache.keys(CacheConstants.LOGIN_TOKEN_KEY + "*");
|
||
List<SysUserOnline> userOnlineList = new ArrayList<SysUserOnline>();
|
||
for (String key : keys) {
|
||
LoginUser user = redisCache.getCacheObject(key);
|
||
if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName)) {
|
||
if (StringUtils.equals(ipaddr, user.getIpaddr()) && StringUtils.equals(userName, user.getUsername())) {
|
||
userOnlineList.add(selectOnlineByInfo(key, user));
|
||
}
|
||
} else if (StringUtils.isNotEmpty(ipaddr)) {
|
||
if (StringUtils.equals(ipaddr, user.getIpaddr())) {
|
||
userOnlineList.add(selectOnlineByInfo(key, user));
|
||
}
|
||
} else if (StringUtils.isNotEmpty(userName) && StringUtils.isNotNull(userOnline)) {
|
||
if (StringUtils.equals(userName, user.getUsername())) {
|
||
userOnlineList.add(selectOnlineByInfo(key, user));
|
||
}
|
||
} else {
|
||
userOnlineList.add(selectOnlineByInfo(key, user));
|
||
}
|
||
}
|
||
Collections.reverse(userOnlineList);
|
||
userOnlineList.removeAll(Collections.singleton(null));
|
||
return userOnlineList;
|
||
}
|
||
|
||
/**
|
||
* 强退用户
|
||
*/
|
||
@Override
|
||
public void forceLogout(String tokenId) {
|
||
redisCache.deleteObject(CacheConstants.LOGIN_TOKEN_KEY + tokenId);
|
||
}
|
||
}
|
||
```
|
||
|
||
### 3.4 定时任务
|
||
|
||
#### 功能特性
|
||
定时任务模块基于Quartz框架,提供了完整的任务调度管理功能。
|
||
|
||
**核心功能清单:**
|
||
- ✅ 任务信息CRUD管理
|
||
- ✅ 任务执行状态监控
|
||
- ✅ 任务执行日志记录
|
||
- ✅ 任务手动执行
|
||
- ✅ 任务启用/暂停控制
|
||
- ✅ Cron表达式配置
|
||
|
||
#### 任务调度实现
|
||
|
||
```java
|
||
@Service
|
||
public class SysJobServiceImpl implements ISysJobService {
|
||
|
||
@Autowired
|
||
private Scheduler scheduler;
|
||
|
||
@Autowired
|
||
private SysJobMapper jobMapper;
|
||
|
||
/**
|
||
* 暂停任务
|
||
*/
|
||
@Override
|
||
@Transactional(rollbackFor = Exception.class)
|
||
public int pauseJob(SysJob job) throws SchedulerException {
|
||
Long jobId = job.getJobId();
|
||
String jobGroup = job.getJobGroup();
|
||
job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
|
||
int rows = jobMapper.updateJob(job);
|
||
if (rows > 0) {
|
||
scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup));
|
||
}
|
||
return rows;
|
||
}
|
||
|
||
/**
|
||
* 恢复任务
|
||
*/
|
||
@Override
|
||
@Transactional(rollbackFor = Exception.class)
|
||
public int resumeJob(SysJob job) throws SchedulerException {
|
||
Long jobId = job.getJobId();
|
||
String jobGroup = job.getJobGroup();
|
||
job.setStatus(ScheduleConstants.Status.NORMAL.getValue());
|
||
int rows = jobMapper.updateJob(job);
|
||
if (rows > 0) {
|
||
scheduler.resumeJob(ScheduleUtils.getJobKey(jobId, jobGroup));
|
||
}
|
||
return rows;
|
||
}
|
||
|
||
/**
|
||
* 立即运行任务
|
||
*/
|
||
@Override
|
||
public void run(SysJob job) throws SchedulerException {
|
||
Long jobId = job.getJobId();
|
||
String jobGroup = job.getJobGroup();
|
||
JobDataMap dataMap = new JobDataMap();
|
||
dataMap.put(ScheduleConstants.TASK_PROPERTIES, job);
|
||
scheduler.triggerJob(ScheduleUtils.getJobKey(jobId, jobGroup), dataMap);
|
||
}
|
||
}
|
||
```
|
||
|
||
## 4. 系统工具模块分析
|
||
|
||
### 4.1 代码生成器
|
||
|
||
#### 功能特性
|
||
代码生成器是RuoYi-Vue的核心优势功能,能够一键生成完整的CRUD功能代码。
|
||
|
||
**核心功能清单:**
|
||
- ✅ 数据库表结构解析
|
||
- ✅ 实体类自动生成
|
||
- ✅ Mapper接口和XML生成
|
||
- ✅ Service层代码生成
|
||
- ✅ Controller层代码生成
|
||
- ✅ 前端Vue页面生成
|
||
- ✅ 权限配置自动生成
|
||
|
||
#### 生成模板类型
|
||
|
||
| 模板类型 | 适用场景 | 生成内容 |
|
||
|----------|----------|----------|
|
||
| **单表CRUD** | 普通业务表 | 增删改查基础功能 |
|
||
| **主子表** | 一对多关系表 | 主表+子表关联操作 |
|
||
| **树表** | 层级结构表 | 树形结构增删改查 |
|
||
|
||
#### 代码生成流程
|
||
|
||
```java
|
||
@Service
|
||
public class GenTableServiceImpl implements IGenTableService {
|
||
|
||
/**
|
||
* 生成代码(下载方式)
|
||
*/
|
||
@Override
|
||
public byte[] downloadCode(String tableName) {
|
||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||
ZipOutputStream zip = new ZipOutputStream(outputStream);
|
||
generatorCode(tableName, zip);
|
||
IOUtils.closeQuietly(zip);
|
||
return outputStream.toByteArray();
|
||
}
|
||
|
||
/**
|
||
* 生成代码(自定义路径)
|
||
*/
|
||
@Override
|
||
public void generatorCode(String tableName) {
|
||
// 查询表信息
|
||
GenTable table = genTableMapper.selectGenTableByName(tableName);
|
||
// 设置主子表信息
|
||
setSubTable(table);
|
||
// 设置主键列信息
|
||
setPkColumn(table);
|
||
|
||
VelocityInitializer.initVelocity();
|
||
|
||
VelocityContext context = VelocityUtils.prepareContext(table);
|
||
|
||
// 获取模板列表
|
||
List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory());
|
||
for (String template : templates) {
|
||
// 渲染模板
|
||
StringWriter sw = new StringWriter();
|
||
Template tpl = Velocity.getTemplate(template, Constants.UTF8);
|
||
tpl.merge(context, sw);
|
||
try {
|
||
String path = getGenPath(table, template);
|
||
FileUtils.writeStringToFile(new File(path), sw.toString(), CharsetKit.UTF_8);
|
||
} catch (IOException e) {
|
||
throw new ServiceException("渲染模板失败,表名:" + table.getTableName());
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 模板引擎配置
|
||
|
||
```java
|
||
/**
|
||
* 模板工具类
|
||
*/
|
||
public class VelocityUtils {
|
||
|
||
/**
|
||
* 设置模板变量信息
|
||
*/
|
||
public static VelocityContext prepareContext(GenTable genTable) {
|
||
String moduleName = genTable.getModuleName();
|
||
String businessName = genTable.getBusinessName();
|
||
String packageName = genTable.getPackageName();
|
||
String tplCategory = genTable.getTplCategory();
|
||
String functionName = genTable.getFunctionName();
|
||
|
||
VelocityContext velocityContext = new VelocityContext();
|
||
velocityContext.put("tplCategory", genTable.getTplCategory());
|
||
velocityContext.put("tableName", genTable.getTableName());
|
||
velocityContext.put("functionName", StringUtils.isNotEmpty(functionName) ? functionName : "【请填写功能名称】");
|
||
velocityContext.put("ClassName", genTable.getClassName());
|
||
velocityContext.put("className", StringUtils.uncapitalize(genTable.getClassName()));
|
||
velocityContext.put("moduleName", genTable.getModuleName());
|
||
velocityContext.put("BusinessName", StringUtils.capitalize(genTable.getBusinessName()));
|
||
velocityContext.put("businessName", genTable.getBusinessName());
|
||
velocityContext.put("basePackage", getPackagePrefix(packageName));
|
||
velocityContext.put("packageName", packageName);
|
||
velocityContext.put("author", genTable.getFunctionAuthor());
|
||
velocityContext.put("datetime", DateUtils.getDate());
|
||
velocityContext.put("pkColumn", genTable.getPkColumn());
|
||
velocityContext.put("importList", getImportList(genTable));
|
||
velocityContext.put("permissionPrefix", getPermissionPrefix(moduleName, businessName));
|
||
velocityContext.put("columns", genTable.getColumns());
|
||
velocityContext.put("table", genTable);
|
||
setMenuVelocityContext(velocityContext, genTable);
|
||
|
||
if (GenConstants.TPL_SUB.equals(tplCategory)) {
|
||
setSubVelocityContext(velocityContext, genTable);
|
||
}
|
||
if (GenConstants.TPL_TREE.equals(tplCategory)) {
|
||
setTreeVelocityContext(velocityContext, genTable);
|
||
}
|
||
return velocityContext;
|
||
}
|
||
}
|
||
```
|
||
|
||
### 4.2 表单构建
|
||
|
||
#### 功能特性
|
||
表单构建器提供了可视化的表单设计功能,支持拖拽式表单设计。
|
||
|
||
**核心功能清单:**
|
||
- ✅ 拖拽式表单设计
|
||
- ✅ 丰富的表单组件
|
||
- ✅ 表单配置管理
|
||
- ✅ 表单预览功能
|
||
- ✅ 表单代码生成
|
||
- ✅ 表单数据管理
|
||
|
||
#### 系统接口文档
|
||
|
||
系统接口模块集成了Swagger,提供了完整的API文档管理功能。
|
||
|
||
**核心功能清单:**
|
||
- ✅ API接口文档自动生成
|
||
- ✅ 接口参数说明
|
||
- ✅ 接口在线测试
|
||
- ✅ 接口认证支持
|
||
- ✅ 接口分组管理
|
||
|
||
## 5. 权限控制体系分析
|
||
|
||
### 5.1 RBAC权限模型
|
||
|
||
RuoYi-Vue采用经典的RBAC(Role-Based Access Control)权限模型,实现了用户、角色、权限的解耦。
|
||
|
||
#### 权限模型关系图
|
||
|
||
```
|
||
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||
│ 用户 │ ───→ │ 角色 │ ───→ │ 权限 │
|
||
│ (User) │ N:M │ (Role) │ N:M │ (Permission)│
|
||
└─────────────┘ └─────────────┘ └─────────────┘
|
||
│ │ │
|
||
│ ┌─────────────┐ │
|
||
└──────────────→│ 用户角色关联 │←─────────────┘
|
||
│(UserRole) │
|
||
└─────────────┘
|
||
│
|
||
┌─────────────┐
|
||
│ 角色权限关联 │
|
||
│(RoleMenu) │
|
||
└─────────────┘
|
||
```
|
||
|
||
### 5.2 数据权限控制
|
||
|
||
#### 数据权限级别
|
||
|
||
| 权限级别 | 描述 | 应用场景 |
|
||
|----------|------|----------|
|
||
| **全部数据权限** | 可以查看所有数据 | 超级管理员 |
|
||
| **自定义数据权限** | 可以查看指定部门数据 | 区域经理 |
|
||
| **部门数据权限** | 只能查看本部门数据 | 部门经理 |
|
||
| **部门及以下数据权限** | 查看本部门及下级部门数据 | 主管 |
|
||
| **仅本人数据权限** | 只能查看自己的数据 | 普通员工 |
|
||
|
||
### 5.3 按钮权限控制
|
||
|
||
#### 前端权限指令
|
||
|
||
```javascript
|
||
// 权限验证指令 v-hasPermi
|
||
Vue.directive('hasPermi', {
|
||
inserted(el, binding, vnode) {
|
||
const { value } = binding
|
||
const all_permission = "*:*:*";
|
||
const permissions = store.getters && store.getters.permissions
|
||
|
||
if (value && value instanceof Array && value.length > 0) {
|
||
const permissionFlag = value
|
||
const hasPermissions = permissions.some(permission => {
|
||
return all_permission === permission || permissionFlag.includes(permission)
|
||
})
|
||
|
||
if (!hasPermissions) {
|
||
el.parentNode && el.parentNode.removeChild(el)
|
||
}
|
||
}
|
||
}
|
||
})
|
||
|
||
// 角色权限指令 v-hasRole
|
||
Vue.directive('hasRole', {
|
||
inserted(el, binding, vnode) {
|
||
const { value } = binding
|
||
const super_admin = "admin";
|
||
const roles = store.getters && store.getters.roles
|
||
|
||
if (value && value instanceof Array && value.length > 0) {
|
||
const roleFlag = value
|
||
const hasRole = roles.some(role => {
|
||
return super_admin === role || roleFlag.includes(role)
|
||
})
|
||
|
||
if (!hasRole) {
|
||
el.parentNode && el.parentNode.removeChild(el)
|
||
}
|
||
}
|
||
}
|
||
})
|
||
```
|
||
|
||
## 6. 业务功能应用场景
|
||
|
||
### 6.1 典型应用场景
|
||
|
||
| 应用领域 | 具体场景 | 核心功能 |
|
||
|----------|----------|----------|
|
||
| **企业内部管理** | 员工信息管理、权限分配 | 用户管理、角色管理、部门管理 |
|
||
| **政府办公系统** | 公文流转、审批管理 | 工作流、权限控制、日志审计 |
|
||
| **教育管理平台** | 学生信息、课程管理 | 数据权限、批量操作、统计分析 |
|
||
| **医疗信息系统** | 病例管理、权限分级 | 细粒度权限、数据安全、操作审计 |
|
||
| **电商后台管理** | 商品管理、订单处理 | 多角色协作、数据监控、系统集成 |
|
||
|
||
### 6.2 行业适配性分析
|
||
|
||
#### 高适配行业
|
||
- ✅ **制造业**:生产管理、质量控制、设备维护
|
||
- ✅ **金融业**:客户管理、风险控制、合规审计
|
||
- ✅ **零售业**:商品管理、库存控制、销售分析
|
||
- ✅ **教育行业**:学员管理、课程安排、成绩管理
|
||
- ✅ **医疗行业**:病例管理、设备管理、药品管理
|
||
|
||
#### 需要定制的行业
|
||
- 🔧 **物流行业**:需要集成GPS、路径优化等专业功能
|
||
- 🔧 **建筑行业**:需要项目管理、工程进度等专业模块
|
||
- 🔧 **农业行业**:需要农业数据采集、环境监控等功能
|
||
|
||
## 7. 功能扩展建议
|
||
|
||
### 7.1 短期功能增强
|
||
|
||
1. **工作流引擎集成**
|
||
- 集成Activiti或Camunda工作流引擎
|
||
- 支持复杂业务流程审批
|
||
|
||
2. **消息通知系统**
|
||
- 站内消息推送
|
||
- 邮件和短信通知
|
||
- 微信企业号集成
|
||
|
||
3. **数据可视化**
|
||
- 集成ECharts图表组件
|
||
- 业务数据大屏展示
|
||
- 自定义报表生成
|
||
|
||
### 7.2 中期功能规划
|
||
|
||
1. **移动端支持**
|
||
- 响应式设计优化
|
||
- 移动端App开发
|
||
- 微信小程序集成
|
||
|
||
2. **多租户支持**
|
||
- SaaS化改造
|
||
- 租户数据隔离
|
||
- 租户配置管理
|
||
|
||
3. **API开放平台**
|
||
- RESTful API规范化
|
||
- API文档平台
|
||
- 第三方系统集成
|
||
|
||
### 7.3 长期发展方向
|
||
|
||
1. **智能化功能**
|
||
- 基于AI的数据分析
|
||
- 智能推荐系统
|
||
- 自然语言处理
|
||
|
||
2. **云原生架构**
|
||
- 微服务架构改造
|
||
- 容器化部署
|
||
- DevOps流水线
|
||
|
||
3. **低代码平台**
|
||
- 可视化页面设计
|
||
- 业务流程编排
|
||
- 零代码应用搭建
|
||
|
||
## 8. 总结与评估
|
||
|
||
### 8.1 功能完整性评估
|
||
|
||
| 功能模块 | 完整度 | 说明 |
|
||
|----------|--------|------|
|
||
| **用户权限管理** | ★★★★★ | 功能完善,覆盖全面 |
|
||
| **系统监控** | ★★★★☆ | 基础监控齐全,可扩展高级监控 |
|
||
| **代码生成** | ★★★★★ | 核心优势功能,大大提升开发效率 |
|
||
| **操作审计** | ★★★★☆ | 日志记录完整,可增强分析功能 |
|
||
| **系统工具** | ★★★☆☆ | 基础工具齐全,可增加更多实用工具 |
|
||
|
||
### 8.2 业务价值总结
|
||
|
||
**核心业务价值:**
|
||
1. **快速开发**:代码生成器等工具显著提升开发效率
|
||
2. **权限安全**:完善的权限控制体系保障数据安全
|
||
3. **系统监控**:全面的监控功能保障系统稳定运行
|
||
4. **标准化**:统一的开发规范提升代码质量
|
||
5. **可扩展**:模块化架构支持业务快速扩展
|
||
|
||
**适用企业特征:**
|
||
- 中小型企业管理系统开发
|
||
- 需要快速上线的项目
|
||
- 对安全性要求较高的应用
|
||
- 希望降低开发成本的团队
|
||
- 需要标准化开发流程的组织
|
||
|
||
RuoYi-Vue以其完整的功能体系、成熟的技术架构和良好的扩展性,为企业级后台管理系统的开发提供了优秀的基础框架,能够满足大部分企业的业务需求,是企业数字化转型的理想技术选择。 |