Jelajahi Sumber

Merge branch 'master' of https://gitee.com/y_project/RuoYi-Vue

 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
疯狂的狮子li 5 tahun lalu
induk
melakukan
98941acd2a

+ 11 - 16
README.md

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

+ 8 - 0
ruoyi-ui/src/api/system/dept.js

@@ -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) {
   return request({

+ 6 - 0
ruoyi-ui/src/utils/errorCode.js

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

+ 21 - 12
ruoyi-ui/src/utils/request.js

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

+ 1 - 1
ruoyi-ui/src/views/monitor/job/index.vue

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

+ 1 - 1
ruoyi-ui/src/views/monitor/logininfor/index.vue

@@ -91,7 +91,7 @@
       <el-table-column label="访问编号" align="center" prop="infoId" />
       <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="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="os" />
       <el-table-column label="登录状态" align="center" prop="status" :formatter="statusFormat" />

+ 1 - 1
ruoyi-ui/src/views/monitor/operlog/index.vue

@@ -110,7 +110,7 @@
       <el-table-column label="请求方式" align="center" prop="requestMethod" />
       <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="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="operTime" width="180">
         <template slot-scope="scope">

+ 7 - 9
ruoyi-ui/src/views/system/dept/index.vue

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

+ 6 - 10
ruoyi/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java

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

+ 3 - 1
ruoyi/src/main/java/com/ruoyi/framework/aspectj/lang/annotation/DataSource.java

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

+ 13 - 9
ruoyi/src/main/java/com/ruoyi/framework/config/SwaggerConfig.java

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

+ 1 - 0
ruoyi/src/main/java/com/ruoyi/framework/web/controller/BaseController.java

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

+ 3 - 3
ruoyi/src/main/java/com/ruoyi/framework/web/page/TableDataInfo.java

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

+ 3 - 0
ruoyi/src/main/java/com/ruoyi/project/monitor/controller/SysJobLogController.java

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

+ 23 - 0
ruoyi/src/main/java/com/ruoyi/project/system/controller/SysDeptController.java

@@ -1,6 +1,8 @@
 package com.ruoyi.project.system.controller;
 
+import java.util.Iterator;
 import java.util.List;
+import org.apache.commons.lang3.ArrayUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.validation.annotation.Validated;
@@ -45,6 +47,27 @@ public class SysDeptController extends BaseController
         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);
+    }
+
     /**
      * 根据部门编号获取详细信息
      */

+ 1 - 0
ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysDeptServiceImpl.java

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

+ 3 - 0
ruoyi/src/main/java/com/ruoyi/project/tool/gen/controller/GenController.java

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

+ 4 - 1
ruoyi/src/main/resources/application.yml

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