Conflicts:
	ruoyi-ui/src/api/system/dept.js
	ruoyi-ui/src/utils/request.js
	ruoyi-ui/src/views/monitor/job/index.vue
	ruoyi-ui/src/views/monitor/logininfor/index.vue
	ruoyi-ui/src/views/monitor/operlog/index.vue
	ruoyi-ui/src/views/system/dept/index.vue
	ruoyi/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java
	ruoyi/src/main/java/com/ruoyi/framework/aspectj/lang/annotation/DataSource.java
	ruoyi/src/main/java/com/ruoyi/framework/config/SwaggerConfig.java
	ruoyi/src/main/java/com/ruoyi/framework/web/controller/BaseController.java
	ruoyi/src/main/java/com/ruoyi/framework/web/page/TableDataInfo.java
	ruoyi/src/main/java/com/ruoyi/project/system/controller/SysDeptController.java
	ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysDeptServiceImpl.java
	ruoyi/src/main/java/com/ruoyi/project/tool/gen/controller/GenController.java
	ruoyi/src/main/resources/application.yml
This commit is contained in:
疯狂的狮子li 2020-05-20 09:15:19 +08:00
commit 98941acd2a
18 changed files with 116 additions and 64 deletions

View File

@ -1,18 +1,12 @@
## 平台简介 ## 平台简介
一直想做一款后台管理系统看了很多优秀的开源项目但是发现没有合适的。于是利用空闲休息时间开始自己写了一套后台系统。如此有了若依。她可以用于所有的Web应用程序如网站管理后台网站会员中心CMSCRMOA。所有前端后台代码封装过后十分精简易上手出错概率低。同时支持移动客户端访问。系统会陆续更新一些实用功能。 * 前端采用Vue、Element UI、Vue-Element-Admin。
* 后端采用Spring Boot、Spring Security、Redis & Jwt。
性别男,若依是给还没有出生女儿取的名字(寓意:你若不离不弃,我必生死相依) * 权限认证使用Jwt支持多终端认证系统。
* 支持加载动态权限菜单,多方式轻松权限控制。
参考后台模板[vue-element-admin](https://github.com/PanJiaChen/vue-element-admin) * 高效率开发,使用代码生成器可以一键生成前后端代码。
* 不分离版本,请移步[RuoYi](https://gitee.com/y_project/RuoYi),微服务版本,请移步[RuoYi-Cloud](https://gitee.com/y_project/RuoYi-Cloud)
> 阿里云服务器89元/年双12年末特惠爆款产品限时1折 [点我进入](https://www.aliyun.com/minisite/goods?userCode=brki8iof&share_source=copy_link) * 阿里云优惠券:[点我进入](https://www.aliyun.com/minisite/goods?userCode=brki8iof&share_source=copy_link),腾讯云优惠券:[点我领取](https://cloud.tencent.com/redirect.php?redirect=1025&cps_key=198c8df2ed259157187173bc7f4f32fd&from=console)  
> 如需不分离应用,请移步 [RuoYi](https://gitee.com/y_project/RuoYi) `(保持同步更新)`,如需其他版本,请移步 [项目扩展](http://doc.ruoyi.vip/ruoyi/document/xmkz.html) `(不定时更新)`
> 阿里云通用云产品1888优惠券 [点我领取](https://promotion.aliyun.com/ntms/yunparter/invite.html?userCode=brki8iof)    腾讯云通用云产品2860优惠券 [点我领取](https://cloud.tencent.com/redirect.php?redirect=1025&cps_key=198c8df2ed259157187173bc7f4f32fd&from=console)  `(仅限新用户)`
> 阿里云Hi拼购 限量爆款 低至199元/年 [点我进入](https://www.aliyun.com/acts/hi-group-buying?userCode=brki8iof)  `(仅限新用户)`
## 内置功能 ## 内置功能
@ -33,12 +27,13 @@
15. 服务监控监视当前系统CPU、内存、磁盘、堆栈等相关信息。 15. 服务监控监视当前系统CPU、内存、磁盘、堆栈等相关信息。
16. 在线构建器拖动表单元素生成相应的HTML代码。 16. 在线构建器拖动表单元素生成相应的HTML代码。
17. 连接池监视监视当前系统数据库连接池状态可进行分析SQL找出系统性能瓶颈。 17. 连接池监视监视当前系统数据库连接池状态可进行分析SQL找出系统性能瓶颈。
## 在线体验 ## 在线体验
> admin/admin123
> 陆陆续续收到一些打赏,为了更好的体验已用于演示服务器升级。谢谢各位小伙伴。 - admin/admin123
- 陆陆续续收到一些打赏,为了更好的体验已用于演示服务器升级。谢谢各位小伙伴。
演示地址http://vue.ruoyi.vip 演示地址http://vue.ruoyi.vip
文档地址http://doc.ruoyi.vip 文档地址http://doc.ruoyi.vip
## 演示图 ## 演示图

View File

@ -9,6 +9,14 @@ export function listDept(query) {
}) })
} }
// 查询部门列表(排除节点)
export function listDeptExcludeChild(deptId) {
return request({
url: '/system/dept/list/exclude/' + deptId,
method: 'get'
})
}
// 查询部门详细 // 查询部门详细
export function getDept(deptId) { export function getDept(deptId) {
return request({ return request({

View File

@ -0,0 +1,6 @@
export default {
'401': '认证失败,无法访问系统资源',
'403': '当前操作没有权限',
'404': '访问资源不存在',
'default': '系统未知错误,请反馈给管理员'
}

View File

@ -2,6 +2,7 @@ import axios from 'axios'
import { Notification, MessageBox, Message } from 'element-ui' import { Notification, MessageBox, Message } from 'element-ui'
import store from '@/store' import store from '@/store'
import { getToken } from '@/utils/auth' import { getToken } from '@/utils/auth'
import errorCode from '@/utils/errorCode'
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8' axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
// 创建axios实例 // 创建axios实例
@ -12,22 +13,24 @@ const service = axios.create({
timeout: 10000 timeout: 10000
}) })
// request拦截器 // request拦截器
service.interceptors.request.use( service.interceptors.request.use(config => {
config => { // 是否需要设置 token
if (getToken()) { const isToken = (config.headers || {}).isToken === false
config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改 if (getToken() && !isToken) {
} config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
return config }
}, return config
error => { }, error => {
console.log(error) console.log(error)
Promise.reject(error) Promise.reject(error)
} })
)
// 响应拦截器 // 响应拦截器
service.interceptors.response.use(res => { service.interceptors.response.use(res => {
const code = res.data.code // 未设置状态码则默认成功状态
const code = res.data.code || 200;
// 获取错误信息
const message = errorCode[code] || res.data.msg || errorCode['default']
if (code === 401) { if (code === 401) {
MessageBox.confirm( MessageBox.confirm(
'登录状态已过期,您可以继续留在该页面,或者重新登录', '登录状态已过期,您可以继续留在该页面,或者重新登录',
@ -42,9 +45,15 @@ service.interceptors.response.use(res => {
location.reload() // 为了重新实例化vue-router对象 避免bug location.reload() // 为了重新实例化vue-router对象 避免bug
}) })
}) })
} else if (code === 500) {
Message({
message: message,
type: 'error'
})
return Promise.reject(new Error(message))
} else if (code !== 200) { } else if (code !== 200) {
Notification.error({ Notification.error({
title: res.data.msg title: message
}) })
return Promise.reject('error') return Promise.reject('error')
} else { } else {

View File

@ -397,7 +397,7 @@ export default {
type: "warning" type: "warning"
}).then(function() { }).then(function() {
return runJob(row.jobId, row.jobGroup); return runJob(row.jobId, row.jobGroup);
}).then(function() { }).then(() => {
this.msgSuccess("执行成功"); this.msgSuccess("执行成功");
}).catch(function() {}); }).catch(function() {});
}, },

View File

@ -91,7 +91,7 @@
<el-table-column label="访问编号" align="center" prop="infoId" /> <el-table-column label="访问编号" align="center" prop="infoId" />
<el-table-column label="用户名称" align="center" prop="userName" /> <el-table-column label="用户名称" align="center" prop="userName" />
<el-table-column label="登录地址" align="center" prop="ipaddr" width="130" :show-overflow-tooltip="true" /> <el-table-column label="登录地址" align="center" prop="ipaddr" width="130" :show-overflow-tooltip="true" />
<el-table-column label="登录地点" align="center" prop="loginLocation" /> <el-table-column label="登录地点" align="center" prop="loginLocation" :show-overflow-tooltip="true" />
<el-table-column label="浏览器" align="center" prop="browser" /> <el-table-column label="浏览器" align="center" prop="browser" />
<el-table-column label="操作系统" align="center" prop="os" /> <el-table-column label="操作系统" align="center" prop="os" />
<el-table-column label="登录状态" align="center" prop="status" :formatter="statusFormat" /> <el-table-column label="登录状态" align="center" prop="status" :formatter="statusFormat" />

View File

@ -110,7 +110,7 @@
<el-table-column label="请求方式" align="center" prop="requestMethod" /> <el-table-column label="请求方式" align="center" prop="requestMethod" />
<el-table-column label="操作人员" align="center" prop="operName" /> <el-table-column label="操作人员" align="center" prop="operName" />
<el-table-column label="主机" align="center" prop="operIp" width="130" :show-overflow-tooltip="true" /> <el-table-column label="主机" align="center" prop="operIp" width="130" :show-overflow-tooltip="true" />
<el-table-column label="操作地点" align="center" prop="operLocation" /> <el-table-column label="操作地点" align="center" prop="operLocation" :show-overflow-tooltip="true" />
<el-table-column label="操作状态" align="center" prop="status" :formatter="statusFormat" /> <el-table-column label="操作状态" align="center" prop="status" :formatter="statusFormat" />
<el-table-column label="操作日期" align="center" prop="operTime" width="180"> <el-table-column label="操作日期" align="center" prop="operTime" width="180">
<template slot-scope="scope"> <template slot-scope="scope">

View File

@ -138,7 +138,7 @@
</template> </template>
<script> <script>
import { listDept, getDept, delDept, addDept, updateDept } from "@/api/system/dept"; import { listDept, getDept, delDept, addDept, updateDept, listDeptExcludeChild } from "@/api/system/dept";
import Treeselect from "@riophae/vue-treeselect"; import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css"; import "@riophae/vue-treeselect/dist/vue-treeselect.css";
@ -220,12 +220,6 @@ export default {
children: node.children children: node.children
}; };
}, },
/** 查询部门下拉树结构 */
getTreeselect() {
listDept().then(response => {
this.deptOptions = this.handleTree(response.data, "deptId");
});
},
// //
statusFormat(row, column) { statusFormat(row, column) {
return this.selectDictLabel(this.statusOptions, row.status); return this.selectDictLabel(this.statusOptions, row.status);
@ -256,22 +250,26 @@ export default {
/** 新增按钮操作 */ /** 新增按钮操作 */
handleAdd(row) { handleAdd(row) {
this.reset(); this.reset();
this.getTreeselect();
if (row != undefined) { if (row != undefined) {
this.form.parentId = row.deptId; this.form.parentId = row.deptId;
} }
this.open = true; this.open = true;
this.title = "添加部门"; this.title = "添加部门";
listDept().then(response => {
this.deptOptions = this.handleTree(response.data, "deptId");
});
}, },
/** 修改按钮操作 */ /** 修改按钮操作 */
handleUpdate(row) { handleUpdate(row) {
this.reset(); this.reset();
this.getTreeselect();
getDept(row.deptId).then(response => { getDept(row.deptId).then(response => {
this.form = response.data; this.form = response.data;
this.open = true; this.open = true;
this.title = "修改部门"; this.title = "修改部门";
}); });
listDeptExcludeChild(row.deptId).then(response => {
this.deptOptions = this.handleTree(response.data, "deptId");
});
}, },
/** 提交按钮 */ /** 提交按钮 */
submitForm: function() { submitForm: function() {

View File

@ -1,6 +1,6 @@
package com.ruoyi.framework.aspectj; package com.ruoyi.framework.aspectj;
import java.lang.reflect.Method; import java.util.Objects;
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Aspect;
@ -8,6 +8,7 @@ import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature; import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.StringUtils;
@ -60,17 +61,12 @@ public class DataSourceAspect
public DataSource getDataSource(ProceedingJoinPoint point) public DataSource getDataSource(ProceedingJoinPoint point)
{ {
MethodSignature signature = (MethodSignature) point.getSignature(); MethodSignature signature = (MethodSignature) point.getSignature();
Class<? extends Object> targetClass = point.getTarget().getClass(); DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class);
DataSource targetDataSource = targetClass.getAnnotation(DataSource.class); if (Objects.nonNull(dataSource))
if (StringUtils.isNotNull(targetDataSource))
{ {
return targetDataSource;
}
else
{
Method method = signature.getMethod();
DataSource dataSource = method.getAnnotation(DataSource.class);
return dataSource; return dataSource;
} }
return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class);
} }
} }

View File

@ -11,6 +11,8 @@ import com.ruoyi.framework.aspectj.lang.enums.DataSourceType;
/** /**
* 自定义多数据源切换注解 * 自定义多数据源切换注解
* *
* 优先级先方法后类如果方法覆盖了类上的数据源类型以方法的为准否则以类上的为准
*
* @author ruoyi * @author ruoyi
*/ */
@Target({ ElementType.METHOD, ElementType.TYPE }) @Target({ ElementType.METHOD, ElementType.TYPE })

View File

@ -33,9 +33,13 @@ public class SwaggerConfig
@Autowired @Autowired
private RuoYiConfig ruoyiConfig; private RuoYiConfig ruoyiConfig;
/** Swagger开关配置 */ /** 是否开启swagger */
@Value("${swagger.enable}") @Value("${swagger.enabled}")
private boolean swaggerEnable; private boolean enabled;
/** 设置请求的统一前缀 */
@Value("${swagger.pathMapping}")
private String pathMapping;
/** /**
* 创建API * 创建API
@ -45,8 +49,7 @@ public class SwaggerConfig
{ {
return new Docket(DocumentationType.SWAGGER_2) return new Docket(DocumentationType.SWAGGER_2)
// 是否启用Swagger // 是否启用Swagger
.enable(swaggerEnable) .enable(enabled)
.pathMapping("/dev-api")
// 用来创建该API的基本信息展示在文档的页面中自定义展示的信息 // 用来创建该API的基本信息展示在文档的页面中自定义展示的信息
.apiInfo(apiInfo()) .apiInfo(apiInfo())
// 设置哪些接口暴露给Swagger展示 // 设置哪些接口暴露给Swagger展示
@ -54,13 +57,14 @@ public class SwaggerConfig
// 扫描所有有注解的api用这种方式更灵活 // 扫描所有有注解的api用这种方式更灵活
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
// 扫描指定包中的swagger注解 // 扫描指定包中的swagger注解
//.apis(RequestHandlerSelectors.basePackage("com.ruoyi.project.tool.swagger")) // .apis(RequestHandlerSelectors.basePackage("com.ruoyi.project.tool.swagger"))
// 扫描所有 .apis(RequestHandlerSelectors.any()) // 扫描所有 .apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any()) .paths(PathSelectors.any())
.build() .build()
/* 设置安全模式swagger可以设置访问token */ /* 设置安全模式swagger可以设置访问token */
.securitySchemes(securitySchemes()) .securitySchemes(securitySchemes())
.securityContexts(securityContexts()); .securityContexts(securityContexts())
.pathMapping(pathMapping);
} }
/** /**

View File

@ -67,6 +67,7 @@ public class BaseController
{ {
TableDataInfo rspData = new TableDataInfo(); TableDataInfo rspData = new TableDataInfo();
rspData.setCode(HttpStatus.SUCCESS); rspData.setCode(HttpStatus.SUCCESS);
rspData.setMsg("查询成功");
rspData.setRows(list); rspData.setRows(list);
rspData.setTotal(new PageInfo(list).getTotal()); rspData.setTotal(new PageInfo(list).getTotal());
return rspData; return rspData;

View File

@ -22,7 +22,7 @@ public class TableDataInfo implements Serializable
private int code; private int code;
/** 消息内容 */ /** 消息内容 */
private int msg; private String msg;
/** /**
* 表格数据对象 * 表格数据对象
@ -73,12 +73,12 @@ public class TableDataInfo implements Serializable
this.code = code; this.code = code;
} }
public int getMsg() public String getMsg()
{ {
return msg; return msg;
} }
public void setMsg(int msg) public void setMsg(String msg)
{ {
this.msg = msg; this.msg = msg;
} }

View File

@ -76,6 +76,9 @@ public class SysJobLogController extends BaseController
return toAjax(jobLogService.deleteJobLogByIds(jobLogIds)); return toAjax(jobLogService.deleteJobLogByIds(jobLogIds));
} }
/**
* 清空定时任务调度日志
*/
@PreAuthorize("@ss.hasPermi('monitor:job:remove')") @PreAuthorize("@ss.hasPermi('monitor:job:remove')")
@Log(title = "调度日志", businessType = BusinessType.CLEAN) @Log(title = "调度日志", businessType = BusinessType.CLEAN)
@DeleteMapping("/clean") @DeleteMapping("/clean")

View File

@ -1,6 +1,8 @@
package com.ruoyi.project.system.controller; package com.ruoyi.project.system.controller;
import java.util.Iterator;
import java.util.List; import java.util.List;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@ -45,6 +47,27 @@ public class SysDeptController extends BaseController
return AjaxResult.success(depts); return AjaxResult.success(depts);
} }
/**
* 查询部门列表排除节点
*/
@PreAuthorize("@ss.hasPermi('system:dept:list')")
@GetMapping("/list/exclude/{deptId}")
public AjaxResult excludeChild(@PathVariable(value = "deptId", required = false) Long deptId)
{
List<SysDept> depts = deptService.selectDeptList(new SysDept());
Iterator<SysDept> it = depts.iterator();
while (it.hasNext())
{
SysDept d = (SysDept) it.next();
if (d.getDeptId().intValue() == deptId
|| ArrayUtils.contains(StringUtils.split(d.getAncestors(), ","), deptId + ""))
{
it.remove();
}
}
return AjaxResult.success(depts);
}
/** /**
* 根据部门编号获取详细信息 * 根据部门编号获取详细信息
*/ */

View File

@ -114,6 +114,7 @@ public class SysDeptServiceImpl implements ISysDeptService
* @param deptId 部门ID * @param deptId 部门ID
* @return 子部门数 * @return 子部门数
*/ */
@Override
public int selectNormalChildrenDeptById(Long deptId) public int selectNormalChildrenDeptById(Long deptId)
{ {
return deptMapper.selectNormalChildrenDeptById(deptId); return deptMapper.selectNormalChildrenDeptById(deptId);

View File

@ -124,6 +124,9 @@ public class GenController extends BaseController
return AjaxResult.success(); return AjaxResult.success();
} }
/**
* 删除代码生成
*/
@PreAuthorize("@ss.hasPermi('tool:gen:remove')") @PreAuthorize("@ss.hasPermi('tool:gen:remove')")
@Log(title = "代码生成", businessType = BusinessType.DELETE) @Log(title = "代码生成", businessType = BusinessType.DELETE)
@DeleteMapping("/{tableIds}") @DeleteMapping("/{tableIds}")

View File

@ -118,7 +118,10 @@ pagehelper:
# Swagger配置 # Swagger配置
swagger: swagger:
enable: true # 是否开启swagger
enabled: true
# 请求前缀
pathMapping: /dev-api
# 防止XSS攻击 # 防止XSS攻击
xss: xss: